🎮ArcadeLab

Block Puzzle

by DriftFalcon72
204 lines7.9 KB
▶ Play
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Block Puzzle</title>
<style>
*{box-sizing:border-box}
body{margin:0;background:#101426;color:white;font-family:Arial,sans-serif;text-align:center}
h1{color:#45d6f5;margin:18px 0 8px}
button{border:0;border-radius:10px;padding:13px 25px;margin:7px;background:#268dce;color:white;font-size:17px;cursor:pointer}
button:hover{filter:brightness(1.2)}
.panel{max-width:520px;margin:15px auto;padding:15px}
#board{display:grid;grid-template-columns:repeat(10,1fr);gap:3px;width:min(94vw,460px);margin:15px auto;background:#263451;padding:5px;border-radius:9px;touch-action:none}
.cell{aspect-ratio:1;background:#1a253b;border-radius:3px}
.filled{background:var(--c);box-shadow:inset 0 3px 5px #ffffff33}
#pieces{display:flex;justify-content:center;gap:8px;flex-wrap:wrap}
.piece{width:30%;min-width:95px;min-height:105px;background:#263451;border-radius:9px;display:flex;align-items:center;justify-content:center;touch-action:none}
.mini{display:grid;gap:3px}
.mini-cell{width:19px;height:19px;border-radius:3px}
.hidden{display:none}
input{width:80%;margin:12px}
#message{min-height:24px;color:#f4e45b}
</style>
</head>
<body>
<h1>BLOCK PUZZLE</h1>

<div id="menu" class="panel">
<p>A colourful block challenge</p>
<button onclick="startGame()">PLAY</button><br>
<button onclick="showSettings()">SETTINGS</button>
</div>

<div id="settings" class="panel hidden">
<h2>SETTINGS</h2>
<p><label><input id="musicToggle" type="checkbox" checked> Music</label></p>
<p><label><input id="soundToggle" type="checkbox" checked> Sound effects</label></p>
<p>Music volume <input id="musicVolume" type="range" min="0" max="1" step=".05" value=".3"></p>
<p>Sound volume <input id="soundVolume" type="range" min="0" max="1" step=".05" value=".6"></p>
<button onclick="showMenu()">BACK</button>
</div>

<div id="game" class="panel hidden">
<div>
Score: <b id="score">0</b> |
Best: <b id="best">0</b>
<button onclick="pauseGame()">PAUSE</button>
</div>
<div id="board"></div>
<p id="message">Choose a block and place it on the board.</p>
<h3>YOUR BLOCKS</h3>
<div id="pieces"></div>
</div>

<div id="pause" class="panel hidden">
<h2>GAME PAUSED</h2>
<button onclick="resumeGame()">RESUME</button>
<button onclick="showSettings()">SETTINGS</button>
<button onclick="startGame()">RESTART</button>
<button onclick="showMenu()">MAIN MENU</button>
</div>

<div id="over" class="panel hidden">
<h2>GAME OVER!</h2>
<p>Final score: <b id="finalScore">0</b></p>
<button onclick="startGame()">PLAY AGAIN</button>
<button onclick="showMenu()">MAIN MENU</button>
</div>

<script>
const N=10;
const colors=["#45D6F5","#FFB84D","#A78BFA","#58D68D","#FF6B81","#F4E45B"];
const shapes=[
 [[0,0]],[[0,0],[1,0]],[[0,0],[0,1]],
 [[0,0],[1,0],[2,0]],[[0,0],[0,1],[0,2]],
 [[0,0],[1,0],[0,1],[1,1]],
 [[0,0],[1,0],[2,0],[1,1]],
 [[0,0],[0,1],[1,1]],[[1,0],[0,1],[1,1]],
 [[0,0],[1,0],[2,0],[3,0]],
 [[0,0],[0,1],[0,2],[0,3]],
 [[0,0],[1,0],[2,0],[0,1],[1,1],[2,1]],
 [[0,0],[1,0],[2,0],[2,1],[2,2]],
 [[0,0],[0,1],[0,2],[1,2],[2,2]],
 [[0,0],[1,0],[1,1],[2,1]]
];

let board=[],pieces=[],score=0,best=0,selected=-1;
let audioCtx=null,musicTimer=null,musicStep=0;

function show(id){
 ["menu","settings","game","pause","over"].forEach(x=>
 document.getElementById(x).classList.toggle("hidden",x!==id));
}
function showMenu(){stopMusic();show("menu")}
function showSettings(){show("settings")}
function startGame(){
 board=Array.from({length:N},()=>Array(N).fill(-1));
 score=0;pieces=[];selected=-1;
 for(let i=0;i<3;i++)newPiece();
 update();show("game");startMusic();
}
function pauseGame(){stopMusic();show("pause")}
function resumeGame(){show("game");startMusic()}
function newPiece(){
 let shape=shapes[Math.floor(Math.random()*shapes.length)];
 pieces.push({shape:shape.map(p=>[...p]),color:Math.floor(Math.random()*colors.length)});
}
function drawBoard(){
 const el=document.getElementById("board");el.innerHTML="";
 for(let y=0;y<N;y++)for(let x=0;x<N;x++){
  const c=document.createElement("div");c.className="cell";
  if(board[y][x]>=0){c.classList.add("filled");c.style.setProperty("--c",colors[board[y][x]])}
  c.dataset.x=x;c.dataset.y=y;
  c.addEventListener("pointerup",()=>placeAt(x,y));
  el.appendChild(c);
 }
}
function drawPieces(){
 const el=document.getElementById("pieces");el.innerHTML="";
 pieces.forEach((p,i)=>{
  const card=document.createElement("div");card.className="piece";
  card.style.outline=selected===i?"3px solid #45d6f5":"none";
  const maxX=Math.max(...p.shape.map(c=>c[0]))+1;
  const maxY=Math.max(...p.shape.map(c=>c[1]))+1;
  const mini=document.createElement("div");mini.className="mini";
  mini.style.gridTemplateColumns=`repeat(${maxX},19px)`;
  for(let y=0;y<maxY;y++)for(let x=0;x<maxX;x++){
   const b=document.createElement("div");b.className="mini-cell";
   if(p.shape.some(c=>c[0]===x&&c[1]===y))b.style.background=colors[p.color];
   mini.appendChild(b);
  }
  card.appendChild(mini);card.onclick=()=>{selected=i;drawPieces()};
  el.appendChild(card);
 });
}
function update(){
 document.getElementById("score").textContent=score;
 best=Math.max(best,score);
 document.getElementById("best").textContent=best;
 drawBoard();drawPieces();
}
function canPlace(p,x,y){
 return p.shape.every(([dx,dy])=>
 x+dx>=0&&x+dx<N&&y+dy>=0&&y+dy<N&&board[y+dy][x+dx]===-1);
}
function placeAt(x,y){
 if(selected<0||!pieces[selected])return;
 const p=pieces[selected];
 if(!canPlace(p,x,y)){document.getElementById("message").textContent="That spot is blocked. Try another!";beep(180);return}
 p.shape.forEach(([dx,dy])=>board[y+dy][x+dx]=p.color);
 score+=p.shape.length;
 pieces.splice(selected,1);selected=-1;
 clearLines();
 if(pieces.length===0){for(let i=0;i<3;i++)newPiece()}
 update();beep(620);
 if(!pieces.some(p=>board.some((row,y)=>row.some((_,x)=>canPlace(p,x,y))))) {
  document.getElementById("finalScore").textContent=score;
  stopMusic();show("over");
 }
}
function clearLines(){
 let rows=[],cols=[];
 for(let y=0;y<N;y++)if(board[y].every(v=>v!==-1))rows.push(y);
 for(let x=0;x<N;x++)if(board.every(row=>row[x]!==-1))cols.push(x);
 rows.forEach(y=>board[y].fill(-1));
 cols.forEach(x=>board.forEach(row=>row[x]=-1));
 const cleared=rows.length+cols.length;
 if(cleared){score+=cleared*10;document.getElementById("message").textContent="Great! "+cleared+" line(s) cleared!";beep(850)}
 else document.getElementById("message").textContent="Nice move!";
}
function beep(freq){
 if(!document.getElementById("soundToggle").checked)return;
 try{
  audioCtx??=new(window.AudioContext||window.webkitAudioContext)();
  const o=audioCtx.createOscillator(),g=audioCtx.createGain();
  o.frequency.value=freq;
  g.gain.value=Number(document.getElementById("soundVolume").value)*.08;
  o.connect(g);g.connect(audioCtx.destination);o.start();o.stop(audioCtx.currentTime+.12);
 }catch(e){}
}
function startMusic(){
 stopMusic();
 if(!document.getElementById("musicToggle").checked)return;
 try{
  audioCtx??=new(window.AudioContext||window.webkitAudioContext)();
  const notes=[262,330,392,330,294,349,440,349];
  musicTimer=setInterval(()=>{
   if(!document.getElementById("musicToggle").checked){stopMusic();return}
   const o=audioCtx.createOscillator(),g=audioCtx.createGain();
   o.frequency.value=notes[musicStep++%notes.length];
   g.gain.value=Number(document.getElementById("musicVolume").value)*.025;
   o.connect(g);g.connect(audioCtx.destination);
   o.start();o.stop(audioCtx.currentTime+.22);
  },350);
 }catch(e){}
}
function stopMusic(){if(musicTimer){clearInterval(musicTimer);musicTimer=null}}
document.getElementById("musicToggle").onchange=()=>{if(document.getElementById("musicToggle").checked&& !document.getElementById("game").classList.contains("hidden"))startMusic();else stopMusic()};
</script>
</body>
</html>

Game Source: Block Puzzle

Creator: DriftFalcon72

Libraries: none

Complexity: complex (204 lines, 7.9 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: block-puzzle-driftfalcon72" to link back to the original. Then publish at arcadelab.ai/publish.