简介
井字棋(Tic-Tac-Toe)是最简单的棋类游戏之一,在 3x3 棋盘上两人轮流落子,先连成一线者胜。游戏厅版本实现了人机对战模式,电脑 AI 采用攻守兼备的策略,支持比分统计和获胜连线高亮动画。
棋盘表示
使用一维数组存储 3x3 棋盘状态:
1var board = ['', '', '', '', '', '', '', '', ''];
2// 索引映射:
3// 0 1 2
4// 3 4 5
5// 6 7 8
玩家使用 “x”,电脑使用 “o”:
1var PLAYER = 'x';
2var COMPUTER = 'o';
CSS Grid 渲染
1.ttt-board {
2 display: inline-grid;
3 grid-template-columns: repeat(3, var(--ttt-cell, 100px));
4 grid-template-rows: repeat(3, var(--ttt-cell, 100px));
5 gap: 4px;
6 background: #e2e8f0;
7 border-radius: 10px;
8 padding: 4px;
9}
10.ttt-cell {
11 width: var(--ttt-cell, 100px);
12 height: var(--ttt-cell, 100px);
13 background: #fff;
14 border-radius: 8px;
15 font-size: calc(var(--ttt-cell) * 0.5);
16 cursor: pointer;
17 user-select: none;
18}
19.ttt-cell.x { color: #6366f1; } /* 玩家 - 靛色 */
20.ttt-cell.o { color: #f97316; } /* 电脑 - 橙色 */
游戏逻辑
落子处理
玩家点击空格 → 检查胜负/平局 → 电脑自动落子:
1function placePiece(idx) {
2 if (gameOver || board[idx] !== '') return;
3
4 board[idx] = PLAYER;
5
6 // 检查玩家是否获胜
7 var win = checkWin(PLAYER);
8 if (win) {
9 gameOver = true;
10 scoreX++;
11 highlightWin(win);
12 showStatus('你赢了!');
13 return;
14 }
15
16 // 检查平局
17 if (board.every(function(c) { return c !== ''; })) {
18 gameOver = true;
19 showStatus('平局!');
20 return;
21 }
22
23 render();
24
25 // 电脑回合(延迟 400ms,模拟思考)
26 setTimeout(computerMove, 400);
27}
胜负检测
8 条可能的连线(3 横、3 竖、2 对角线):
1var WIN_LINES = [
2 [0, 1, 2], [3, 4, 5], [6, 7, 8], // 横
3 [0, 3, 6], [1, 4, 7], [2, 5, 8], // 竖
4 [0, 4, 8], [2, 4, 6] // 对角线
5];
6
7function checkWin(player) {
8 for (var i = 0; i < WIN_LINES.length; i++) {
9 var [a, b, c] = WIN_LINES[i];
10 if (board[a] === player && board[b] === player && board[c] === player) {
11 return WIN_LINES[i]; // 返回获胜连线
12 }
13 }
14 return null;
15}
获胜连线高亮动画
1.ttt-cell.win {
2 animation: ttt-win-pulse 0.5s ease-in-out infinite alternate;
3 background: #fef3c7 !important;
4}
5@keyframes ttt-win-pulse {
6 0% { transform: scale(1); }
7 100% { transform: scale(1.06); }
8}
1function highlightWin(winLine) {
2 var cells = boardEl.children;
3 winLine.forEach(function(idx) {
4 cells[idx].classList.add('win');
5 });
6}
电脑 AI 策略
采用两层优先级的攻守策略:
1. 进攻优先 — 寻找自己的必胜位置
1function findBestMove(player) {
2 // 检查所有空格,找到能让自己获胜的位置
3 for (var i = 0; i < 9; i++) {
4 if (board[i] === '') {
5 board[i] = player; // 尝试落子
6 if (checkWin(player)) { // 是否获胜?
7 board[i] = ''; // 恢复
8 return i; // 返回必胜位置
9 }
10 board[i] = ''; // 恢复
11 }
12 }
13 return -1;
14}
2. 防守优先 — 阻止玩家获胜
同一函数,传入 PLAYER 即可找到需要防守的位置:
1function computerMove() {
2 // 策略 1:检查自己能否获胜
3 var move = findBestMove(COMPUTER);
4 if (move !== -1) { board[move] = COMPUTER; /* 获胜!*/ return; }
5
6 // 策略 2:阻止玩家获胜
7 move = findBestMove(PLAYER);
8 if (move !== -1) { board[move] = COMPUTER; /* 防守成功 */ return; }
9
10 // 策略 3:按优先级选择空位
11 var preferred = [4, 0, 2, 6, 8, 1, 3, 5, 7]; // 中心 > 四角 > 四边
12 for (var i = 0; i < preferred.length; i++) {
13 if (board[preferred[i]] === '') {
14 board[preferred[i]] = COMPUTER;
15 return;
16 }
17 }
18}
策略优先级表
| 优先级 | 策略 | 说明 |
|---|---|---|
| 1 | 进攻获胜 | 检查自己能否一步获胜 |
| 2 | 防守阻止 | 阻止玩家下一步获胜 |
| 3 | 中心位置 | 占据棋盘中心(位置 4) |
| 4 | 角落位置 | 占据四角(位置 0, 2, 6, 8) |
| 5 | 边缘位置 | 占据四边(位置 1, 3, 5, 7) |
比分统计
实时显示玩家 vs 电脑的胜负比分:
1var scoreX = 0; // 玩家胜场
2var scoreO = 0; // 电脑胜场
3
4function updateScore() {
5 document.getElementById('attt-score-x').textContent = scoreX;
6 document.getElementById('attt-score-o').textContent = scoreO;
7}
状态提示
根据不同阶段显示不同状态:
1function showStatus(msg) {
2 var statusEl = document.getElementById('attt-status');
3 statusEl.innerHTML = '<span>' +
4 (gameOver ? '🎉' : '❌') +
5 '</span><span>' + msg + '</span>';
6}
状态流转:
- 初始:
❌ 你的回合 - 玩家落子后:
⭕ 电脑思考中... - 电脑落子后:
❌ 你的回合 - 游戏结束:
🎉 你赢了!/💀 电脑赢了!/🤝 平局!
响应式格子尺寸
1function render() {
2 var maxW = Math.min(window.innerWidth - 300, 340);
3 if (window.innerWidth <= 900) maxW = window.innerWidth - 50;
4 var cellSize = Math.floor((maxW - 8) / 3);
5 cellSize = Math.min(cellSize, 100);
6 boardEl.style.setProperty('--ttt-cell', cellSize + 'px');
7}
总结
井字棋虽然规则简单,但 AI 策略设计体现了博弈论的基本思想——攻守兼备。两阶段检测(先看自己能否赢,再看是否需要挡)配合位置优先级(中心 > 角落 > 边缘),构建了一个有一定挑战性的电脑对手。获胜连线脉冲动画增强了游戏反馈的仪式感。
留言评论
期待你的想法评论加载中