Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions project/classic-games/20260705-0445-traffic-lights.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Mini Traffic Lights</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="min-h-screen bg-neutral-950 text-neutral-100">
<main class="mx-auto flex min-h-screen max-w-3xl flex-col gap-4 px-4 py-5">
<header class="flex flex-wrap items-center justify-between gap-3">
<div>
<h1 class="text-2xl font-semibold tracking-normal">Mini Traffic Lights</h1>
<p class="text-sm text-neutral-400">点击格子升级颜色:空到绿,绿到黄,黄到红;任意同色三连即获胜。</p>
</div>
<button id="resetBtn" class="rounded bg-lime-400 px-4 py-2 text-sm font-semibold text-neutral-950 hover:bg-lime-300">重开</button>
</header>

<section class="grid grid-cols-3 gap-3">
<div class="rounded border border-neutral-700 bg-neutral-900 p-3">
<div class="text-xs uppercase text-neutral-500">回合</div>
<div id="turnLabel" class="text-2xl font-semibold">P1</div>
</div>
<div class="rounded border border-neutral-700 bg-neutral-900 p-3">
<div class="text-xs uppercase text-neutral-500">步数</div>
<div id="moveCount" class="text-2xl font-semibold">0</div>
</div>
<div class="rounded border border-neutral-700 bg-neutral-900 p-3">
<div class="text-xs uppercase text-neutral-500">状态</div>
<div id="stateLabel" class="text-2xl font-semibold">进行</div>
</div>
</section>

<section class="rounded border border-neutral-700 bg-neutral-900 p-4">
<div class="mb-4 flex items-center justify-between gap-3">
<h2 class="text-lg font-semibold">棋盘</h2>
<span id="message" class="text-sm text-lime-300">P1 行动。</span>
</div>
<div id="board" class="mx-auto grid max-w-md grid-cols-3 gap-3"></div>
</section>
</main>

<script>
const boardEl = document.getElementById("board");
const turnLabelEl = document.getElementById("turnLabel");
const moveCountEl = document.getElementById("moveCount");
const stateLabelEl = document.getElementById("stateLabel");
const messageEl = document.getElementById("message");
const resetBtn = document.getElementById("resetBtn");

const states = ["", "G", "Y", "R"];
const labels = { "": "", G: "绿", Y: "黄", R: "红" };
const colors = {
"": "bg-neutral-800 border-neutral-700",
G: "bg-emerald-400 border-emerald-200 text-neutral-950",
Y: "bg-yellow-300 border-yellow-100 text-neutral-950",
R: "bg-red-500 border-red-200 text-white"
};
const lines = [
[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6]
];

let board = [];
let player = 1;
let moves = 0;
let winner = null;
let winningLine = [];

function resetGame() {
board = Array(9).fill("");
player = 1;
moves = 0;
winner = null;
winningLine = [];
setMessage("P1 行动。");
render();
}

function setMessage(text) {
messageEl.textContent = text;
}

function upgradeCell(index) {
if (winner) return;
const current = board[index];
if (current === "R") {
setMessage("红灯不能再升级。");
return;
}
board[index] = states[states.indexOf(current) + 1];
moves += 1;
const colorWin = checkWinner();
if (colorWin) {
winner = player;
setMessage(`P${player} 用${labels[colorWin]}灯连成三格。`);
} else if (board.every((cell) => cell === "R")) {
winner = "draw";
setMessage("所有格子都是红灯,平局。");
} else {
player = player === 1 ? 2 : 1;
setMessage(`P${player} 行动。`);
}
render();
}

function checkWinner() {
for (const line of lines) {
const [a, b, c] = line;
if (board[a] && board[a] === board[b] && board[b] === board[c]) {
winningLine = line;
return board[a];
}
}
winningLine = [];
return "";
}

function cellText(value) {
return labels[value] || "+";
}

function renderCell(value, index) {
const button = document.createElement("button");
button.type = "button";
button.className = `aspect-square rounded border text-3xl font-semibold shadow-sm transition hover:scale-[1.02] ${colors[value]}`;
if (winningLine.includes(index)) button.classList.add("ring-4", "ring-white");
button.textContent = cellText(value);
button.disabled = Boolean(winner);
button.addEventListener("click", () => upgradeCell(index));
return button;
}

function render() {
boardEl.innerHTML = "";
board.forEach((cell, index) => {
boardEl.appendChild(renderCell(cell, index));
});
turnLabelEl.textContent = winner ? "-" : `P${player}`;
moveCountEl.textContent = moves;
stateLabelEl.textContent = winner ? "结束" : "进行";
}

resetBtn.addEventListener("click", resetGame);
resetGame();
</script>
</body>
</html>