1. 项目概述:HTML版五子棋的实现价值
五子棋作为一款经典的双人对弈游戏,其HTML版本实现完美展现了前端技术的三大核心能力:DOM操作、事件响应和状态管理。这个项目特别适合前端初学者作为第一个综合练习——你既能看到每行代码如何直接影响界面交互,又能体验完整的功能开发流程。
我曾用这个项目带过二十多位新人入门,发现它涵盖了初学者最需要掌握的六个关键技能点:棋盘绘制、落子逻辑、胜负判定、交互反馈、状态重置和响应式布局。下面我就从实际开发角度,带你完整走一遍构建过程。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心架构设计
2.1 技术选型决策
采用纯HTML+CSS+JS方案而非Canvas或SVG,主要基于三点考虑:
- DOM操作更直观:通过div构建的棋盘能让初学者清晰看到元素结构
- 调试更方便:可以直接在浏览器检查器中观察元素状态变化
- 扩展性更强:后续添加悔棋、AI对战等功能时更容易维护
项目文件结构建议如下:
code复制/gobang
├── index.html # 主页面结构
├── style.css # 棋盘样式
└── script.js # 游戏逻辑
2.2 棋盘数据建模
使用15x15的二维数组存储棋盘状态是最佳实践:
javascript复制let board = Array(15).fill().map(() => Array(15).fill(0));
// 0-空位 1-黑子 2-白子
这种建模方式相比一维数组有两个优势:
- 直观对应棋盘坐标(board[x][y])
- 方便实现胜负判断算法(后续会详细说明)
3. 关键实现细节
3.1 棋盘动态生成
使用CSS Grid布局创建弹性棋盘:
css复制/* style.css */
.board {
display: grid;
grid-template-columns: repeat(15, 1fr);
aspect-ratio: 1/1;
background: #DCB35C; /* 木质底色 */
}
.cell {
border: 1px solid #000;
position: relative;
}
.cell::after { /* 创建交叉点 */
content: '';
position: absolute;
width: 6px; height: 6px;
background: #000;
border-radius: 50%;
transform: translate(-50%, -50%);
}
JavaScript动态生成棋盘:
javascript复制// script.js
const boardEl = document.querySelector('.board');
for (let i = 0; i < 225; i++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.dataset.index = i; // 存储位置索引
boardEl.appendChild(cell);
}
3.2 落子交互实现
事件委托优化性能:
javascript复制boardEl.addEventListener('click', (e) => {
const cell = e.target.closest('.cell');
if (!cell || cell.querySelector('.piece')) return;
const index = parseInt(cell.dataset.index);
const x = Math.floor(index / 15);
const y = index % 15;
placePiece(x, y, currentPlayer);
});
落子动画效果:
css复制.piece {
position: absolute;
width: 80%; height: 80%;
border-radius: 50%;
top: 10%; left: 10%;
animation: drop 0.3s ease-out;
}
@keyframes drop {
from { transform: scale(0); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
4. 胜负判定算法
4.1 方向向量检测法
定义四个检测方向:
javascript复制const directions = [
[1, 0], // 水平
[0, 1], // 垂直
[1, 1], // 主对角线
[1, -1] // 副对角线
];
核心检测函数:
javascript复制function checkWin(x, y, player) {
for (const [dx, dy] of directions) {
let count = 1;
// 正向检测
count += countInDirection(x, y, dx, dy, player);
// 反向检测
count += countInDirection(x, y, -dx, -dy, player);
if (count >= 5) return true;
}
return false;
}
function countInDirection(x, y, dx, dy, player) {
let count = 0, i = 1;
while (true) {
const nx = x + i*dx, ny = y + i*dy;
if (nx < 0 || nx >= 15 || ny < 0 || ny >= 15) break;
if (board[nx][ny] !== player) break;
count++;
i++;
}
return count;
}
4.2 优化技巧
-
边界剪枝:检测时先计算最大可能范围
javascript复制const minX = Math.max(0, x - 4); const maxX = Math.min(14, x + 4); -
增量更新:只检测新落子周围8个方向
-
位运算加速:对专业比赛级实现可用位棋盘
5. 进阶功能实现
5.1 悔棋功能
使用栈记录历史步骤:
javascript复制const history = [];
function saveStep(x, y, player) {
history.push({x, y, player});
}
function undo() {
if (history.length === 0) return;
const last = history.pop();
board[last.x][last.y] = 0;
// 更新DOM...
}
5.2 人机对战
简易AI实现思路:
javascript复制function aiMove() {
// 1. 检查是否有四连可赢
// 2. 阻止玩家四连
// 3. 寻找最佳落子点
const emptyCells = [];
for (let i = 0; i < 15; i++) {
for (let j = 0; j < 15; j++) {
if (board[i][j] === 0) {
const score = evaluatePosition(i, j);
emptyCells.push({i, j, score});
}
}
}
emptyCells.sort((a, b) => b.score - a.score);
return emptyCells[0];
}
6. 性能优化实践
6.1 事件节流
防止快速连续点击:
javascript复制let canClick = true;
boardEl.addEventListener('click', () => {
if (!canClick) return;
canClick = false;
setTimeout(() => canClick = true, 300);
// 处理点击...
});
6.2 离屏渲染
预生成棋子DOM:
javascript复制const piecePool = [];
for (let i = 0; i < 225; i++) {
const piece = document.createElement('div');
piece.classList.add('piece');
piecePool.push(piece);
}
function getPiece() {
return piecePool.find(p => !p.parentElement);
}
7. 常见问题排查
7.1 棋子错位问题
现象:棋子位置偏离交叉点
解决方案:
- 检查cell元素的position是否为relative
- 确认piece的定位参数:
css复制.piece { position: absolute; top: 10%; left: 10%; }
7.2 胜负判断失效
调试步骤:
- 在checkWin函数内打印board状态
- 验证落子时board数组是否正确更新
- 检查方向向量是否正确定义
7.3 移动端适配
添加触摸事件支持:
javascript复制boardEl.addEventListener('touchstart', (e) => {
e.preventDefault();
const touch = e.touches[0];
const cell = document.elementFromPoint(touch.clientX, touch.clientY);
// 后续逻辑与click相同...
});
8. 项目扩展方向
- 联机对战:使用WebSocket实现
- 观战模式:同步棋盘状态
- 棋谱记录:导出PGN格式
- 难度分级:实现不同AI级别
这个项目最让我有成就感的是看到新手通过它理解DOM操作的本质——每个棋子背后都是数据状态与视图的绑定。建议你在实现基础功能后,尝试给棋子添加拖动效果,这能加深对事件传播机制的理解。
