Block Puzzle
by DriftFalcon72204 lines7.9 KB
<!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.