1.效果演示
这是一个用js写的动态沙漏程序,在文本框中输入数字,回车,即可以看到一个相应层数的沙漏在不停的变化。
难度在于上半部分缺失的沙子形成的镂空图形、下半部分多出的沙子形成的图形,如何尽量接近一个正三角形的形状。上面的沙子“一右一左”地消失,下面的沙子“一左一右”地多出,题目原文中的说法是“one left and one right”。
2.源代码
<!DOCTYPE html> <html> <head> <meta charset="UTF-16"> <style type="text/css"> .floor{height:15px;} .solid{width:15px; height:15px; float:left} </style> </head>
<body> <p>在文本框中输入数字,回车,尽量不超过10</p> <script type="text/javascript" defer="defer"> var row1 = document.createElement("div"); var row2 = document.createElement("div");
var input = document.createElement("input"); input.addEventListener("keyup", drawImg);
document.body.appendChild(row1); document.body.appendChild(row2); row1.appendChild(input);
var rowsNum = null; //行数 var rowSum = null; //矩形整体宽度 var lowerSolidSum = 0; //下半部分的沙子总数
/**沙子落下*/ function drop(){ var judge = flip(); if(judge){ lowerSolidSum=-1; } lowerSolidSum++;//下半部分的沙子总数加1 var floorNum = Math.floor(Math.sqrt(lowerSolidSum));//上面少了的沙子 或 下面多出来的沙子,能组成的图形的层数 var floorArr = new Array();//沙堆的层数数组 //初始化floorArr,每一层都是0粒沙 for(var i=0; i<floorNum; i++){ floorArr[i] = 0; } var count = 0; while(count<lowerSolidSum){ count++; var upperArr = getUpperArr(count, floorNum); for(var i=floorArr.length-1; i>=0; i--){ //floorArr[i]标示floorArr的某层,其值代表当前层的沙子数 //标示当前层的沙子数上限 if(floorArr[i] < upperArr[i]){ floorArr[i]++; break; } } } //将floorArr补充至rowsNum行 for(var i=floorNum; i<rowsNum; i++){ floorArr.unshift(0); } redraw(floorArr); }
/**翻转*/ function flip(){ var count = 0; for(var i=1; i<=rowsNum; i++){ count += 2*i-1; } if(count == lowerSolidSum){ return true; } return false; }
/**根据代表沙堆的floorArr,重绘沙漏*/ function redraw(triangleArr){ var imgArr = new Array(); //"上半部分"缺失沙子,镂空形成图形 for(var i=1; i<=rowsNum; i++){ var rowSolidSum = i*2-1; //这一层最多能容纳的沙子总数 var currRowSolidSum = rowSolidSum-triangleArr[i-1]; //这一行当前的沙子总数 var res = drawUpperRow(rowSolidSum, currRowSolidSum); imgArr.unshift(res); } //"下半部分"落下沙子,堆成图形 for(var i=1; i<=rowsNum; i++){ var rowSolidSum = i*2-1; //这一层最多能容纳的沙子总数 var currRowSolidSum = triangleArr[i-1]; //这一行当前的沙子总数 var res = drawLowerRow(rowSolidSum, currRowSolidSum); imgArr.push(res); } var floorArr = row2.childNodes;//沙漏的所有层 for(var i=0; i<floorArr.length; i++){ var floor = floorArr[i]; var img = imgArr[i]; for(var j=0; j<floor.childNodes.length; j++){ var div = floor.childNodes[j]; var ch = img.charAt(j); div.innerText = ch; } } }
/**获取这count粒沙子,组成的arrNum层图形,每层的沙子数量上限*/ function getUpperArr(count, arrNum){ var upperArr = new Array(); if( count == arrNum*arrNum ){ for(var i=1; i<=arrNum; i++){ upperArr.push(2*i-1); } }else{ for(var i=1; i<=arrNum; i++){ upperArr.push(2*i+1); } } var temp = Math.floor(Math.sqrt(count)); if(temp < arrNum){ upperArr.pop(); } for(var i=upperArr.length; i<arrNum; i++){ upperArr.unshift(0); } return upperArr; }
function drawImg(event){ if(event.keyCode == 13){ rowsNum = parseInt(input.value); if(!rowsNum){ input.value = ""; return; } row2.innerHTML = ""; rowSum = rowsNum*2-1;//沙漏的每一行应有的总数 init(); auto(); } }
function auto(){ setTimeout( function(){ drop(); setTimeout(arguments.callee, 500); },500); }
function init(){ //上半部分 for(var i=rowsNum; i>0; i--){//行数 var rowNo = document.createElement("div"); rowNo.className="floor"; row2.appendChild(rowNo); generateRow(rowNo, i, "*");//在这一行后面画沙漏的第i行,填充物为* } //下半部分 for(var i=1; i<=rowsNum; i++){ var rowNo = document.createElement("div"); rowNo.className="floor"; row2.appendChild(rowNo); generateRow(rowNo, i, "-");//在这一行后面画沙漏的第i行,填充物为- } }
function generateRow(rowNo, i, filler){ var rowSolidSum = 2*i-1; //这一行的沙子总数 var spaceSum = rowSum-rowSolidSum; //这一行的空格总数 var div = null; var index1 = spaceSum/2;//一半的空格 var index2 = spaceSum/2 + rowSolidSum;//一半的空格+全部的沙子 for(var i=1; i<=rowSum; i++){ if(i<=index1 || i>index2){//在两个index之外的是· div = document.createElement("div"); div.innerText="·"; }else { div = document.createElement("div"); div.innerText=filler; } div.className = "solid"; rowNo.appendChild(div); } }
function drawUpperRow(rowSolidSum, currRowSolidSum){ //rowSolidSum 这一行最多能容纳的沙子总数 //currRowSolidSum 这一行当前的沙子总数 //rowSum 每一行的图形宽度 var str = ""; var space1 = (rowSum-rowSolidSum)/2; //一半的空格 var space2 = rowSum - space1; //对称的另一半的空格起始位置 var miss1 = space1+Math.ceil(currRowSolidSum/2); //漏掉的沙子的起始位置 var miss2 = space2-Math.floor(currRowSolidSum/2); //漏掉的沙子的终止位置 for(var i=1; i<=rowSum; i++){ if(i<=space1 || i>space2){//在两个index之外的是· str+="·"; }else if(i>space1 && i<=miss1 || i>miss2 && i<=space2){ str+="*"; }else{ str+="-"; } }
return str; }
function drawLowerRow(rowSolidSum, currRowSolidSum){ currRowSolidSum = rowSolidSum-currRowSolidSum; //rowSolidSum 这一行最多能容纳的沙子总数 //currRowSolidSum 这一行当前的沙子总数 //rowSum 每一行的图形宽度 var str = ""; var space1 = (rowSum-rowSolidSum)/2; //一半的空格 var space2 = rowSum - space1; //对称的另一半的空格起始位置 var miss1 = space1+Math.floor(currRowSolidSum/2); //漏下的沙子的起始位置 var miss2 = space2-Math.ceil(currRowSolidSum/2); //漏掉的沙子的终止位置 for(var i=1; i<=rowSum; i++){ if(i<=space1 || i>space2){//在两个index之外的是· str+="·"; }else if(i>space1 && i<=miss1 || i>miss2 && i<=space2){ str+="-"; }else{ str+="*"; } }
return str; } </script> </body> </html> |
3.创意来源
1. Create a new class called Hourglass.
2. Create an attribute to store the height, and one to store the amount of sand in the bottom half of the hourglass.
3. Write the general constructor so that it creates a Hourglass instance of the specified height. When a new hourglass is created, all the sand should be in the top half. The amount of sand in an hourglass is exactly enough to fill one half of the hourglass.
4. Write the default constructor so that it creates a Hourglass instance of height 3.
5. Write a method dropGrain that simulates one grain of sand falling into the bottom half of the Hourglass. If all the sand is already at the bottom before a grain is dropped, this method should cause the hourglass to be flipped, meaning that all the sand will be in the top again. Then, one grain of sand should fall. Hint: this method can be quite short. All you need to do is update one attribute.
6. Write a method getHeapHeight() which returns the height of the heap of sand in the bottom of the hourglass. Hint: a triangle of height h contains h*h grains (=1+3+5+...+(2*h-1)). So determining the height when the amount of sand in the bottom half is a square (1,4,9,16,...) is easy. Think about what happens if the amount of sand is not exactly a square.
7. Write a class method starMinusLine which takes two arguments specifying a number of stars and minuses respectively and returns a String with the stars centered between the minuses. Example: starMinusLine(3,4) should return "--***--".
8. Write a class method starMinusLine which takes three arguments specifying a number of stars and minuses as well as a true/false value and returns a String with the stars centered between the minuses, or, if the boolean is false, the minuses should be centered between the stars. Example: starMinusLine(3,4,true) should return "--*(3 stars)--". starMinusLine(3,4,false) should return "---**". Thus, the first argument is always the number of symbols in the centre. Hint: determine at the start of the method what character will be in the center and what will be at the sides, and put these into 1-character Strings containing "*" and "-" respectively.
9. Write a method getExtraGrains that calculates how much sand has fallen since the last time that the heap of sand was a triangle, and how much of that sand has ended up at or above the line that is the argument of this method. Hint: You know the height and the total amount of grains in the bottom half of the hourglass. (Note that the height of the heap is the same as the height of the triangle at the last time that the heap was exactly a triangle.) In every line below the current line (i.e., a line with a higher line number), two extra grains can be placed, one left and one right.
10. Write a method getSandInLine that calculates how much sand there is on a particular POSITIVE line of the picture of the hourglass (that is, the bottom half). The javadoc notes above show how the lines are numbered. Hints: Above the heap, there is no sand, so check for this first (how?). In the heap, you can first calculate the width of the triangle at the current line (The triangle that formed the heap the last time that the number of grains in the bottom half of the hourglass was a square.) Then you need to check if there are still grains to the left and to the right at this height. How many grains are not in the triangle? See the method getExtraGrains(int).
11. Write a method toString that produces a String containing a picture of the current state of the HourGlass according to the description given in the javadoc. Each line should be drawn using the starMinusLine and getSandInLine methods. Use '.' for the background (outside the hourglass). Hint: you start a new line inside a String with the code \n. If you can print the positive lines of the hourglass (the bottom half) correctly, then you only need to make a small change for the top half.
The Hourglass should look like this
An hourglass of size 3 when it is just created. sand starts to fall into the bottom half, the heap of sand at the bottom always consists of a triangle with some extra grains of sand at the sides. We assume that grains fall to the left and to the right of the heap in an alternating fashion, beginning with the left. At the top of the hourglass, the sand will disappear from the center first; there will be an empty space which exactly mirrors the heap at the bottom. Example: after the first grain has dropped, the hourglass looks as follows.
After one grain has dropped:
|
4.网上出处
https://coderanch.com/t/642633/java/Beginner-sand-timer-program