JSFiddle - React, Tailwind, and code Playground
by Jiaming0324
HTML
<div id="spookitonMaze">
<section id="gamePanel" class="game-panel">
<header class="game-header">
<div>
<span class="small-label">SPOOKITON VILLAGE</span>
<h1>Candy Street Maze</h1>
</div>
<button id="newGameBtn" class="new-game-btn" type="button">↻ New Game</button>
</header>
<div class="status-bar">
<div><small>STAGE</small><strong id="stageText">1 / 3</strong></div>
<div><small>SCORE</small><strong id="scoreText">0</strong></div>
<div><small>COLLECTED</small><strong id="collectedText">0</strong></div>
</div>
<p id="stageMessage" class="stage-message">Find the glowing village gate.</p>
<div class="maze-frame">
<div id="mazeBoard" class="maze-board" aria-label="Spookiton Village maze"></div>
</div>
<div class="controls" aria-label="Direction controls">
<button type="button" class="move-btn up" data-direction="up" aria-label="Move up">▲</button>
<div class="control-row">
<button type="button" class="move-btn" data-direction="left" aria-label="Move left">◀</button>
<button type="button" class="move-btn" data-direction="down" aria-label="Move down">▼</button>
<button type="button" class="move-btn" data-direction="right" aria-label="Move right">▶</button>
</div>
</div>
<p class="control-help"><b>PHONE:</b> Tap the buttons · <b>COMPUTER:</b> Arrow Keys or W A S D</p>
<p class="score-help">🍬 +3 🎃 +1 👻 −2</p>
</section>
<section id="finishPanel" class="finish-panel" aria-live="polite">
<div class="finish-stars">✦ 🎃 ✦</div>
<span class="small-label">ALL THREE STAGES COMPLETE</span>
<h2>You Found Your Way Through Spookiton!</h2>
<div class="result-box">
<small>FINAL SCORE</small>
<strong id="finalScore">0</strong>
<code>VILLAGE-FOUND · 3/3 MAZES</code>
</div>
<button id="playAgainBtn" class="play-again-btn" type="button">Play...
CSS
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
min-height: 100%;
background: #f7ead7;
overscroll-behavior: contain;
}
body {
font-family: Arial, Helvetica, sans-serif;
color: #39243f;
}
button {
font: inherit;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
#spookitonMaze {
width: 100%;
min-height: 100vh;
padding: 12px;
background:
radial-gradient(circle at 8% 8%, rgba(255, 188, 88, .28), transparent 22%),
radial-gradient(circle at 92% 18%, rgba(108, 62, 128, .15), transparent 25%),
#f7ead7;
}
.game-panel {
width: min(100%, 650px);
margin: 0 auto;
}
.game-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 8px;
}
.small-label {
color: #8d4a69;
font-size: 10px;
font-weight: 800;
letter-spacing: .2em;
}
h1 {
margin: 2px 0 0;
color: #5d2671;
font-family: Georgia, serif;
font-size: clamp(25px, 6vw, 38px);
line-height: 1;
}
.new-game-btn,
.play-again-btn {
border: 0;
border-radius: 13px;
padding: 10px 15px;
background: #e46d2f;
box-shadow: 0 4px 0 #a94320;
color: #fff;
font-weight: 800;
cursor: pointer;
}
.new-game-btn:active,
.play-again-btn:active,
.move-btn:active {
transform: translateY(3px);
box-shadow: 0 1px 0 #4a205a;
}
.status-bar {
display: grid;
grid-template-columns: repeat(3, 1fr);
overflow: hidden;
border: 3px solid #5d2671;
border-radius: 15px;
background: #fff9ef;
}
.status-bar div {
padding: 7px 5px;
text-align: center;
}
.status-bar div + div {
border-left: 2px solid #d7bfd8;
}
.status-bar small {
display: block;
color: #936779;
font-size: 9px;
font-weight: 800;
letter-spacing: .12em;
}
.status-bar strong {
display: block;
margin-top: 1px;
color: #5d2671;
font-size: 20px;
}
.stage-message {
min-height: 20px;
...
JavaScript
(function () {
"use strict";
function startMazeGame() {
const SIZE = 21;
const TOTAL_STAGES = 3;
const DIRECTIONS = {
up: [0, -1],
down: [0, 1],
left: [-1, 0],
right: [1, 0]
};
const KEY_MAP = {
ArrowUp: "up",
ArrowDown: "down",
ArrowLeft: "left",
ArrowRight: "right",
w: "up",
a: "left",
s: "down",
d: "right"
};
const app = document.getElementById("spookitonMaze");
const board = document.getElementById("mazeBoard");
const stageText = document.getElementById("stageText");
const scoreText = document.getElementById("scoreText");
const collectedText = document.getElementById("collectedText");
const stageMessage = document.getElementById("stageMessage");
const finalScore = document.getElementById("finalScore");
if (!app || !board) return;
let grid = [];
let player = { x: 1, y: 1 };
let goal = { x: SIZE - 2, y: SIZE - 2 };
let items = new Map();
let stage = 1;
let score = 0;
let collected = 0;
let changingStage = false;
let finished = false;
function shuffledDirections() {
return [[2, 0], [-2, 0], [0, 2], [0, -2]]
.sort(() => Math.random() - 0.5);
}
function generateMaze() {
grid = Array.from({ length: SIZE }, () => Array(SIZE).fill(1));
const stack = [{ x: 1, y: 1 }];
grid[1][1] = 0;
while (stack.length) {
const current = stack[stack.length - 1];
const choices = shuffledDirections()
.map(([dx, dy]) => ({
x: current.x + dx,
y: current.y + dy,
wallX: current.x + dx / 2,
wallY: current.y + dy / 2
}))
.filter((next) =>
next.x > 0 && next.y > 0 &&
next.x < SIZE - 1 && next.y < SIZE - 1 &&
grid[next.y][next.x] === 1
);
if (!choices.length)...