用到2个库:JQ用来操作DOM,awesome用一些图标,都是在HTML里通过URL引用

 

 

得分原则:

  • 单次消除N行比分多次消除N 行得分高
  • 消除后,上面的自动落下又导致有行被消除,有比较多的额外奖励,因为创造条件有一定风险

游戏有4个状态:

  • 终止:GAMEOVER的时候,这个状态只能refresh 别的操作都无效
  • 正常游戏:自动下落,可以暂停、旋转、左右移动、加速下落
  • 消除或自动下落:播放动画,自动消除,下落,再消除。不响应操作
  • 暂停:不响应操作,不会自动往下落

附上代码,只有一个HTML 直接打开就能玩

  1 <html>
  2     <head>
  3         <title>tetris</title>
  4         <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
  5         <style type="text/css">
  6         .data {
  7             width: 35px;
  8             height: 35px;
  9             display: inline-block;
 10             margin: 1px;
 11             background-color: #999999;
 12             color: #333333;
 13             text-align: center;
 14             vertical-align: middle;
 15         }
 16         .small-data {
 17             width: 20px;
 18             height: 20px;
 19         }
 20         .fill {
 21             background-color: #333333;
 22             color: #999999;
 23         }
 24         .clear {
 25             animation-name: clearLine;
 26             animation-duration: 200ms;
 27         }
 28         .inline {
 29             display: inline-block;
 30         }
 31         .top {
 32             width: 200px;
 33             vertical-align: top;
 34         }
 35         .left {
 36             text-align: right;
 37             display: inline-grid;
 38             grid-template-columns: 30px 30px;
 39             grid-template-rows: repeat(6,30px);
 40             align-items: center;
 41             justify-content: center;
 42         }
 43         .right {
 44             text-align: left;
 45         }
 46         .label {
 47             float: left;
 48             width: 100px;
 49             text-align: left;
 50         }
 51         body {
 52             background-color: #666666;
 53             color: #999999;
 54             font-size: 24px;
 55             font-weight: bold;
 56         }
 57         @keyframes clearLine {
 58             from{background-color: #333333;}
 59             to{background-color: #999999;}
 60         }
 61         </style>
 62         <link href="https://netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
 63         <meta http-equiv="content-type" content="text/html;charset=utf-8">
 64     </head>
 65     <body>
 66         <div style="text-align: center;" >
 67             <div class="inline top left">
 68                     <span>W:</span>
 69                     <i class="fa fa-rotate-left" title="rotate"></i>
 70                     <span>A:</span>
 71                     <i class="fa fa-arrow-left" title="move left"></i>
 72                     <span>S:</span>
 73                     <i class="fa fa-arrow-right" title="move right"></i>
 74                     <span>D:</span>
 75                     <i class="fa fa-arrow-down" title="drop fast"></i>
 76                     <span>Q:</span>
 77                     <i class="fa fa-pause pause" title="pause/play"></i>
 78                     <span>F:</span>
 79                     <i class="fa fa-refresh" title="refresh"></i>
 80             </div>
 81             <div class="inline" id="main"></div>
 82             <div class="inline top right">
 83                 <div>
 84                     <span class="label">score</span>
 85                     <span id="score">0</span>
 86                 </div>
 87                 <div>
 88                     <span class="label">next</span>
 89                     <div style="display: inline-block;vertical-align: middle;" id="next"></div>
 90                 </div>
 91             </div>
 92         </div>
 93     </body>
 94     <script>
 95         var width=10;
 96         var height=20;
 97         var data=new Array(width * height).fill(false);
 98         var rowIndexList=indexArray(height);
 99         var columnIndexList=indexArray(width);
100         var shape=[
101             [1*width+1,1*width+2,2*width+1,2*width+2],//O
102             [1,width+1,2*width+1,3*width+1],//I
103             [1*width+1,1*width+2,2*width,2*width+1],//S
104             [1*width,1*width+1,2*width+1,2*width+2],//Z
105             [width+1,2*width+1,3*width+1,3*width+2],//L
106             [width+2,2*width+2,3*width+1,3*width+2],//J
107             [1*width,1*width+1,1*width+2,2*width+1]//T
108         ];
109         var next;
110         var current;
111         var gameState=0;//0:game over, 1:normal, 2:clear or drop, 3:pause
112         function indexArray(length){
113             return Array.from(new Array(length)).map((v,k)=>k);
114         }
115         function newBlock(){
116             current=next.slice();
117             next=shape[parseInt(Math.random()*shape.length)];
118             $('#next').html(indexArray(16).map(k=>(k%4==0?'<div>':'')+'<div class="data small-data'+(next.includes(k+parseInt(k/4)*(width-4))?' fill':'')+'"></div>'+(k%4==3?'</div>':'')).join(''));
119             while(current.every(u=>u>=width))
120                 current=current.map(u=>u-width);
121             current=current.map(u=>u+parseInt(width/2-1));
122             if(current.some(u=>data[u])){
123                 while(current.some(u=>data[u]))
124                     current=current.map(u=>u-width).filter(u=>u>=0);
125                 gameOver();
126             }
127             setData([],current);
128         }
129         function init(){
130             next=shape[parseInt(Math.random()*shape.length)];
131             setData(Array.from(data.keys()),[]);
132             data.fill(false);
133             $('.data').text('');
134             newBlock();
135             gameState=1;
136         }
137         function pause(){
138             if(gameState==3){
139                 gameState=1;
140                 $('.pause').removeClass('fa-Play');
141                 $('.pause').addClass('fa-pause');
142             }
143             else if(gameState==1){
144                 gameState=3;
145                 $('.pause').removeClass('fa-pause');
146                 $('.pause').addClass('fa-Play');
147             }
148         }
149         function setData(setFalse,setTrue){
150             setFalse.forEach(i=>data[i]=false);
151             setTrue.forEach(i=>data[i]=true);
152             new Set(setFalse.concat(setTrue)).forEach(i=>$('.data[data-index='+i+']').toggleClass('fill', data[i]));
153         }
154         function canMoveDown(){
155             return current.every(u=>current.includes(u+width)||data[u+width]===false);
156         }
157         function moveDown(){
158             setData(current,current.map(i=>i+width));
159             current=current.map(u=>u+width);
160         }
161         function moveLeft(){
162             setData(current,current.map(i=>i-1));
163             current=current.map(u=>u-1);
164         }
165         function moveRight(){
166             setData(current,current.map(i=>i+1));
167             current=current.map(u=>u+1);
168         }
169         function rotate(){
170             let target=current.map(u=>parseInt(u/width)-u%width*height);
171             while(target.every(u=>u<parseInt(Math.max(...current)/width)*height))
172                 target=target.map(u=>u+height);
173             while(target.some(u=>u%height>=width)||Math.min(...target.map(u=>u%height))>Math.min(...current.map(u=>u%width)))
174                 target=target.map(u=>u-1);
175             while(target.every(u=>u%height<width-1)&&Math.min(...target.map(u=>u%height))<Math.min(...current.map(u=>u%width)))
176                 target=target.map(u=>u+1);
177             target=target.map(u=>u-parseInt(u/height)*(height-width));
178             if(target.every(u=>current.includes(u)||data[u]===false)){
179                 setData(current,target);
180                 current=target;
181             }
182         }
183         function clearLine(){
184             let lines=rowIndexList.filter(i=>columnIndexList.every(j=>data[i*width+j]));
185             if(lines.length==0)
186                 return false;
187             let score=lines.length*20-10+(gameState-1)*100;
188             $('#score').text(parseInt($('#score').text())+score);
189             let cells=lines.flatMap(i=>columnIndexList.map(j=>i*width+j));
190             cells.forEach(i=>$('.data[data-index='+i+']').toggleClass('clear', true));
191             setTimeout(()=>cells.forEach(i=>$('.data[data-index='+i+']').toggleClass('clear', false)),200);
192             setData(cells,[]);
193             return true;
194         }
195         function dropDown(){
196             let lines=rowIndexList.filter(i=>i>0&&columnIndexList.every(j=>!data[i*width+j])&&columnIndexList.some(j=>data[i*width+j-width]));
197             if(lines.length==0)
198                 return false;
199             for(let i=lines.length-1;i>=0;i--){
200                 let startIndex=i==0?0:lines[i-1]*width+width;
201                 var tmpData=data.slice(startIndex,lines[i]*width);
202                 var blockList=[];
203                 var tmpSet=[];
204                 function getBlock(i){
205                     tmpSet.push(i);
206                     tmpData[i]=false;
207                     if(i%width>0&&tmpData[i-1])
208                         getBlock(i-1);
209                     if(i%width<width-1&&tmpData[i+1])
210                         getBlock(i+1);
211                     if(i>=width&&tmpData[i-width])
212                         getBlock(i-width);
213                     if(i+width<tmpData.length&&tmpData[i+width])
214                         getBlock(i+width);
215                 }
216                 while(tmpData.some(u=>u)){
217                     getBlock(tmpData.findIndex(u=>u));
218                     blockList.push(tmpSet.map(u=>u+startIndex));
219                     tmpSet=[];
220                 }
221                 while(blockList.some(u=>u.length>0)){
222                     for(let j=0;j<blockList.length;j++){
223                         if(blockList[j].every(u=>blockList[j].includes(u+width)||data[u+width]===false)){
224                             setData(blockList[j],blockList[j].map(i=>i+width));
225                             blockList[j]=blockList[j].map(i=>i+width);
226                         }
227                         else
228                             blockList[j]=[];
229                     }
230                 }
231             }
232             return true;
233         }
234         function gameOver(){
235             gameState=0;
236             'GAMEOVER'.split('').forEach((v,i)=>$('.data[data-index='+((parseInt(height/2)+parseInt(i/4)-2)*width+parseInt(width/2)-2+i%4)+']').text(v));
237         }
238         const interval=500;
239         setInterval(()=>{
240             if(gameState==0||gameState==3)
241                 return;
242             if(gameState==1&&canMoveDown())
243                 moveDown();
244             else{
245                 if(clearLine()||dropDown()){
246                     gameState=2;
247                     return;
248                 }
249                 gameState=1;
250                 if(current.some(u=>u<width))
251                     gameOver();
252                 else
253                     newBlock();
254             }
255         },interval);
256         $('body').keydown(e=>{
257             switch(e.which){
258                 case 70:
259                     init();
260                     break;
261                 case 81:
262                     pause();
263                     break;
264                 case 65:
265                     if(gameState==1&&current.every(u=>current.includes(u-1)||(u%width>0&&data[u-1]===false)))
266                         moveLeft();
267                     break;
268                 case 87:
269                     if(gameState==1)
270                         rotate();
271                     break;
272                 case 68:
273                     if(gameState==1&&current.every(u=>current.includes(u+1)||(u%width<width-1&&data[u+1]===false)))
274                         moveRight();
275                     break;
276                 case 83:
277                     if(gameState==1&&canMoveDown())
278                         moveDown();
279                     break;
280             }
281         });
282         $('#main').html(data.map((v,k)=>(k%width==0?'<div>':'')+'<div class="data" data-index='+k+'></div>'+(k%width==width-1?'</div>':'')).join(''));
283         init();
284     </script>
285 </html>