36 lines
3.2 KiB
JavaScript
36 lines
3.2 KiB
JavaScript
|
|
const canvas = document.getElementById('game');
|
||
|
|
const ctx = canvas.getContext('2d');
|
||
|
|
const COLS = 10, ROWS = 20, BLOCK = 30;
|
||
|
|
const COLORS = ['#000','#00f0f0','#0000f0','#f0a000','#f0f000','#00f000','#a000f0','#f00000'];
|
||
|
|
const SHAPES = [
|
||
|
|
[],
|
||
|
|
[[1,1,1,1]],
|
||
|
|
[[2,0,0],[2,2,2]],
|
||
|
|
[[0,0,3],[3,3,3]],
|
||
|
|
[[4,4],[4,4]],
|
||
|
|
[[0,5,5],[5,5,0]],
|
||
|
|
[[0,6,0],[6,6,6]],
|
||
|
|
[[7,7,0],[0,7,7]]
|
||
|
|
];
|
||
|
|
let board, piece, score, lines, level, dropCounter, dropInterval, last, paused, over;
|
||
|
|
function newBoard(){return Array.from({length:ROWS},()=>Array(COLS).fill(0));}
|
||
|
|
function randPiece(){const id = 1 + Math.floor(Math.random()*7);return {x:3,y:0,id,shape:SHAPES[id].map(r=>[...r])};}
|
||
|
|
function collide(p){for(let y=0;y<p.shape.length;y++)for(let x=0;x<p.shape[y].length;x++)if(p.shape[y][x]){const nx=p.x+x, ny=p.y+y; if(nx<0||nx>=COLS||ny>=ROWS|| (ny>=0 && board[ny][nx])) return true;} return false;}
|
||
|
|
function merge(){piece.shape.forEach((r,y)=>r.forEach((v,x)=>{if(v&&piece.y+y>=0)board[piece.y+y][piece.x+x]=v;}));}
|
||
|
|
function rotate(s){return s[0].map((_,i)=>s.map(r=>r[i]).reverse());}
|
||
|
|
function clearLines(){let c=0; outer: for(let y=ROWS-1;y>=0;y--){for(let x=0;x<COLS;x++)if(!board[y][x])continue outer; board.splice(y,1); board.unshift(Array(COLS).fill(0)); c++; y++;}
|
||
|
|
if(c){lines+=c; score += [0,40,100,300,1200][c]*level; level = 1 + Math.floor(lines/10); dropInterval = Math.max(80, 700 - (level-1)*60);}}
|
||
|
|
function spawn(){piece=randPiece(); if(collide(piece)){over=true;}}
|
||
|
|
function move(dx){piece.x+=dx; if(collide(piece))piece.x-=dx;}
|
||
|
|
function drop(){piece.y++; if(collide(piece)){piece.y--; merge(); clearLines(); spawn();} dropCounter=0;}
|
||
|
|
function hardDrop(){while(!collide(piece))piece.y++; piece.y--; drop();}
|
||
|
|
function tryRotate(){const old=piece.shape; piece.shape=rotate(piece.shape); if(collide(piece)){piece.x++; if(collide(piece)){piece.x-=2; if(collide(piece)){piece.x++; piece.shape=old;}}}}
|
||
|
|
function drawCell(x,y,v){ctx.fillStyle=COLORS[v];ctx.fillRect(x*BLOCK,y*BLOCK,BLOCK-1,BLOCK-1);}
|
||
|
|
function draw(){ctx.fillStyle='#10152b';ctx.fillRect(0,0,canvas.width,canvas.height); board.forEach((r,y)=>r.forEach((v,x)=>v&&drawCell(x,y,v))); piece.shape.forEach((r,y)=>r.forEach((v,x)=>v&&drawCell(piece.x+x,piece.y+y,v))); if(paused||over){ctx.fillStyle='rgba(0,0,0,.55)';ctx.fillRect(0,0,300,600);ctx.fillStyle='#fff';ctx.font='bold 28px Arial';ctx.fillText(over?'Game Over':'Pause',80,300);} }
|
||
|
|
function ui(){scoreEl.textContent=score; levelEl.textContent=level; linesEl.textContent=lines;}
|
||
|
|
const scoreEl=document.getElementById('score'), levelEl=document.getElementById('level'), linesEl=document.getElementById('lines');
|
||
|
|
function loop(t=0){const dt=t-last; last=t; if(!paused&&!over){dropCounter+=dt; if(dropCounter>dropInterval)drop();} draw(); ui(); requestAnimationFrame(loop);}
|
||
|
|
function reset(){board=newBoard(); score=0; lines=0; level=1; dropCounter=0; dropInterval=700; last=0; paused=false; over=false; spawn();}
|
||
|
|
document.addEventListener('keydown',e=>{if(e.key==='p'||e.key==='P'){paused=!paused;return;} if(paused||over)return; if(e.key==='ArrowLeft')move(-1); else if(e.key==='ArrowRight')move(1); else if(e.key==='ArrowDown')drop(); else if(e.key==='ArrowUp')tryRotate(); else if(e.code==='Space'){e.preventDefault(); hardDrop();}});
|
||
|
|
document.getElementById('restart').onclick=reset;
|
||
|
|
reset(); loop();
|