# ===== CodeLab: game-js-ttt =====
# 以下代码片段按文章出现顺序拼接, 共 6 段
# ----- 片段 1 (html) -----
# ----- 片段 2 (css) -----
#board {
display: grid;
grid-template-columns: repeat(3, 100px);
gap: 4px;
}
.cell {
width: 100px;
height: 100px;
background: #f8f9fa;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 2.5rem;
cursor: pointer;
user-select: none;
}
.cell:hover { background: #e9ecef; }
# ----- 片段 3 (javascript) -----
const board = new Array(9).fill(null);
let current = 'X';
let gameOver = false;
const statusEl = document.getElementById('status');
const cells = document.querySelectorAll('.cell');
function render() {
cells.forEach((cell, i) => {
cell.textContent = board[i];
});
}
# ----- 片段 4 (javascript) -----
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]
];
function checkWin(player) {
return lines.some(line =>
line.every(i => board[i] === player)
);
}
function handleClick(e) {
if (gameOver) return;
const i = Number(e.target.dataset.index);
if (board[i]) return;
board[i] = current;
render();
if (checkWin(current)) {
statusEl.textContent = current + ' 赢了!🎉';
gameOver = true;
} else if (board.every(c => c)) {
statusEl.textContent = '平局!';
gameOver = true;
} else {
current = current === 'X' ? 'O' : 'X';
statusEl.textContent = '轮到 ' + current;
}
}
cells.forEach(cell => cell.addEventListener('click', handleClick));
# ----- 片段 5 (javascript) -----
function aiMove() {
for (let i = 0; i < 9; i++) {
if (!board[i]) {
board[i] = 'O';
if (checkWin('O')) return i;
board[i] = null;
}
}
for (let i = 0; i < 9; i++) {
if (!board[i]) {
board[i] = 'X';
if (checkWin('X')) return i;
board[i] = null;
}
}
const empty = [];
board.forEach((c, i) => { if (!c) empty.push(i); });
return empty[Math.floor(Math.random() * empty.length)];
}
# ----- 片段 6 (javascript) -----
function applyMove(i) {
board[i] = current;
render();
if (checkWin(current)) {
statusEl.textContent = current + ' 赢了!🎉';
gameOver = true;
} else if (board.every(c => c)) {
statusEl.textContent = '平局!';
gameOver = true;
} else {
current = current === 'X' ? 'O' : 'X';
statusEl.textContent = '轮到 ' + current;
}
}
function handleClick(e) {
if (gameOver) return;
const i = Number(e.target.dataset.index);
if (board[i]) return;
applyMove(i);
if (!gameOver && current === 'O') {
setTimeout(() => {
applyMove(aiMove());
}, 300);
}
}