🎮ArcadeLab

Block Blast

by AtomicBolt46
471 lines8.8 KB
▶ Play

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Block Blast</title>

<style>
* { box-sizing:border-box; }

body {
  margin:0;
  background:linear-gradient(135deg,#10152d,#222d52);
  color:white;
  font-family:Arial,sans-serif;
  text-align:center;
  min-height:100vh;
}

h1 {
  color:#70eaff;
  font-size:38px;
  margin:20px 0 5px;
  text-shadow:0 0 15px #27aaff;
}

.subtitle { color:#aab9dd; }

.stats {
  display:flex;
  justify-content:center;
  gap:35px;
  margin:20px;
}

.stat {
  background:#26365e;
  padding:12px 25px;
  border-radius:12px;
  min-width:115px;
}

.stat small {
  color:#aab9dd;
  display:block;
}

.stat strong {
  font-size:27px;
  color:#ffe66d;
}

#board {
  display:grid;
  grid-template-columns:repeat(8,1fr);
  gap:5px;
  width:min(92vw,440px);
  aspect-ratio:1;
  margin:15px auto;
  padding:8px;
  background:#101a35;
  border-radius:12px;
  box-shadow:0 0 25px #0008;
}

.cell {
  background:#26365b;
  border-radius:5px;
  border:1px solid #35466c;
}

.cell.filled {
  background:var(--color);
  border:2px solid #ffffff55;
  box-shadow:inset 0 -4px 0 #0002;
}

#pieces {
  display:flex;
  justify-content:center;
  align-items:center;
  gap:15px;
  margin:20px auto;
  max-width:450px;
}

.piece {
  width:30%;
  height:115px;
  background:#26365e;
  border:3px solid transparent;
  border-radius:12px;
  display:flex;
  justify-content:center;
  align-items:center;
  cursor:pointer;
}

.piece.selected {
  border-color:#ffe66d;
  background:#394d7c;
}

.piece.used { opacity:0.18; }

.mini {
  display:grid;
  gap:3px;
}

.mini-cell {
  width:21px;
  height:21px;
  border-radius:3px;
  background:var(--color);
  border:1px solid #ffffff55;
}

button {
  padding:12px 25px;
  border:0;
  border-radius:10px;
  background:#55d6ff;
  font-size:17px;
  font-weight:bold;
  cursor:pointer;
  margin:8px;
}

button:hover { background:#a3ecff; }

#message {
  min-height:25px;
  color:#ffe66d;
  font-weight:bold;
}

#gameover {
  display:none;
  position:fixed;
  inset:0;
  background:#080d20ed;
  z-index:5;
  align-items:center;
  justify-content:center;
  flex-direction:column;
}

#gameover h2 {
  font-size:45px;
  color:#ff6969;
}

.instructions {
  color:#aab9dd;
  font-size:14px;
  margin-bottom:25px;
}
</style>
</head>

<body>

<h1>🟦 BLOCK BLAST</h1>
<p class="subtitle">Place blocks. Clear lines. Beat your high score!</p>

<div class="stats">
  <div class="stat">
    <small>SCORE</small>
    <strong id="score">0</strong>
  </div>
  <div class="stat">
    <small>BEST</small>
    <strong id="best">0</strong>
  </div>
</div>

<div id="board"></div>

<p id="message">Select a block to start!</p>

<div id="pieces"></div>

<p class="instructions">
  Select a block, then click an empty space on the board.
  <br>Complete rows or columns to clear them!
</p>

<button onclick="restart()">🔄 NEW GAME</button>

<div id="gameover">
  <h2>GAME OVER!</h2>
  <h3>Your score: <span id="finalscore">0</span></h3>
  <button onclick="restart()">PLAY AGAIN</button>
</div>

<script>
const SIZE = 8;

const colors = [
  "#38bdf8",
  "#f472b6",
  "#a78bfa",
  "#facc15",
  "#4ade80",
  "#fb923c",
  "#f87171"
];

const shapes = [
  [[0,0]],
  [[0,0],[0,1]],
  [[0,0],[0,1],[0,2]],
  [[0,0],[1,0],[2,0]],
  [[0,0],[0,1],[1,0],[1,1]],
  [[0,0],[0,1],[0,2],[0,3]],
  [[0,0],[1,0],[2,0],[3,0]],
  [[0,0],[0,1],[0,2],[0,3],[0,4]],
  [[0,0],[1,0],[2,0],[3,0],[4,0]],
  [[0,0],[1,0],[1,1]],
  [[0,0],[0,1],[1,1]],
  [[0,0],[1,0],[2,0],[2,1]],
  [[0,0],[1,0],[2,0],[2,-1]],
  [[0,0],[0,1],[0,2],[1,2]],
  [[0,0],[1,0],[1,1],[2,1]],
  [[0,0],[0,1],[1,1],[1,2]],
  [[0,0],[1,0],[2,0],[1,1]],
  [[0,0],[1,0],[2,0],[1,-1],[1,1]],
  [[0,0],[1,0],[2,0],[0,1],[1,1],[2,1]]
];

let grid;
let pieces;
let selected = -1;
let score = 0;
let best = 0;
let gameEnded = false;

const board = document.getElementById("board");
const piecesBox = document.getElementById("pieces");

function makeBoard() {
  board.innerHTML = "";

  for(let r=0;r<SIZE;r++) {
    for(let c=0;c<SIZE;c++) {
      const cell = document.createElement("div");
      cell.className = "cell";
      cell.dataset.r = r;
      cell.dataset.c = c;

      cell.onclick = () => placeBlock(r,c);

      board.appendChild(cell);
    }
  }
}

function randomPiece() {
  const shape = shapes[Math.floor(Math.random()*shapes.length)];

  return {
    shape: shape.map(p=>[...p]),
    color: colors[Math.floor(Math.random()*colors.length)]
  };
}

function newPieces() {
  pieces = [randomPiece(),randomPiece(),randomPiece()];
  selected = -1;
  renderPieces();
}

function renderBoard() {
  [...board.children].forEach(cell=>{
    const r = Number(cell.dataset.r);
    const c = Number(cell.dataset.c);

    cell.classList.toggle("filled",grid[r][c] !== null);

    if(grid[r][c]) {
      cell.style.setProperty("--color",grid[r][c]);
    } else {
      cell.style.removeProperty("--color");
    }
  });
}

function renderPieces() {
  piecesBox.innerHTML = "";

  pieces.forEach((piece,index)=>{
    const button = document.createElement("div");
    button.className = "piece";

    if(index===selected) button.classList.add("selected");
    if(!piece) button.classList.add("used");

    if(piece) {
      const mini = document.createElement("div");
      mini.className = "mini";

      const minR = Math.min(...piece.shape.map(p=>p[0]));
      const maxR = Math.max(...piece.shape.map(p=>p[0]));
      const minC = Math.min(...piece.shape.map(p=>p[1]));
      const maxC = Math.max(...piece.shape.map(p=>p[1]));

      mini.style.gridTemplateColumns =
        `repeat(${maxC-minC+1},21px)`;

      for(let r=minR;r<=maxR;r++) {
        for(let c=minC;c<=maxC;c++) {
          const block = document.createElement("div");

          if(piece.shape.some(p=>p[0]===r && p[1]===c)) {
            block.className = "mini-cell";
            block.style.setProperty("--color",piece.color);
          } else {
            block.style.width="21px";
            block.style.height="21px";
          }

          mini.appendChild(block);
        }
      }

      button.appendChild(mini);
      button.onclick = () => {
        if(gameEnded) return;
        selected=index;
        renderPieces();
        document.getElementById("message").textContent =
          "Now choose a place for your block!";
      };
    }

    piecesBox.appendChild(button);
  });
}

function canPlace(piece,r,c) {
  return piece.shape.every(([dr,dc])=>{
    const y=r+dr;
    const x=c+dc;

    return y>=0 && y<SIZE &&
           x>=0 && x<SIZE &&
           grid[y][x]===null;
  });
}

function placeBlock(r,c) {
  if(gameEnded) return;

  if(selected===-1 || !pieces[selected]) {
    document.getElementById("message").textContent =
      "Select a block first!";
    return;
  }

  const piece=pieces[selected];

  if(!canPlace(piece,r,c)) {
    document.getElementById("message").textContent =
      "That block doesn't fit there!";
    return;
  }

  piece.shape.forEach(([dr,dc])=>{
    grid[r+dr][c+dc]=piece.color;
  });

  score+=piece.shape.length*10;
  pieces[selected]=null;
  selected=-1;

  const cleared=clearLines();

  if(cleared>0) {
    score+=cleared*100;
    document.getElementById("message").textContent =
      "💥 AMAZING! "+cleared+" lines cleared!";
  } else {
    document.getElementById("message").textContent =
      "Nice move!";
  }

  updateStats();
  renderBoard();
  renderPieces();

  if(pieces.every(p=>p===null)) {
    newPieces();
  }

  checkGameOver();
}

function clearLines() {
  const rows=[];
  const cols=[];

  for(let r=0;r<SIZE;r++) {
    if(grid[r].every(cell=>cell!==null)) rows.push(r);
  }

  for(let c=0;c<SIZE;c++) {
    if(grid.every(row=>row[c]!==null)) cols.push(c);
  }

  rows.forEach(r=>{
    for(let c=0;c<SIZE;c++) grid[r][c]=null;
  });

  cols.forEach(c=>{
    for(let r=0;r<SIZE;r++) grid[r][c]=null;
  });

  return rows.length+cols.length;
}

function checkGameOver() {
  const available=pieces.filter(p=>p!==null);

  if(available.length===0) return;

  const possible=available.some(piece=>{
    for(let r=0;r<SIZE;r++) {
      for(let c=0;c<SIZE;c++) {
        if(canPlace(piece,r,c)) return true;
      }
    }
    return false;
  });

  if(!possible) {
    gameEnded=true;
    document.getElementById("finalscore").textContent=score;
    document.getElementById("gameover").style.display="flex";
  }
}

function updateStats() {
  best=Math.max(best,score);

  document.getElementById("score").textContent=score;
  document.getElementById("best").textContent=best;
}

function restart() {
  grid=Array.from({length:SIZE},()=>Array(SIZE).fill(null));
  score=0;
  selected=-1;
  gameEnded=false;

  document.getElementById("gameover").style.display="none";
  document.getElementById("message").textContent=
    "Select a block to start!";

  updateStats();
  makeBoard();
  newPieces();
  renderBoard();
}

restart();
</script>

</body>
</html>

Game Source: Block Blast

Creator: AtomicBolt46

Libraries: none

Complexity: complex (471 lines, 8.8 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-blast-atomicbolt46" to link back to the original. Then publish at arcadelab.ai/publish.