想用JavaScript做一个网页版的五子棋游戏,却卡在棋盘绘制和胜负判定上了?别担心,这篇教程就来帮你解决这两个核心问题。我们会一步步讲解如何用 canvas 绘制棋盘,以及如何高效地判断胜负,并提供完整的代码示例。
1. 棋盘绘制:Canvas 的妙用
首先,我们需要一个 HTML 文件,引入 JavaScript 代码,并创建一个 canvas 元素作为棋盘:
<!DOCTYPE html>
<html>
<head>
<title>五子棋</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="chessboard" width="450" height="450"></canvas>
<script src="script.js"></script>
</body>
</html>
接下来,在 script.js
文件中,我们获取 canvas 元素,并获取其 2D 渲染上下文:
const canvas = document.getElementById('chessboard');
const ctx = canvas.getContext('2d');
const gridSize = 30; // 每个格子的尺寸
const boardSize = 15; // 棋盘大小
// 绘制棋盘
function drawChessboard() {
for (let i = 0; i < boardSize; i++) {
// 绘制横线
ctx.beginPath();
ctx.moveTo(gridSize / 2, gridSize / 2 + i * gridSize);
ctx.lineTo(gridSize / 2 + (boardSize - 1) * gridSize, gridSize / 2 + i * gridSize);
ctx.stroke();
// 绘制竖线
ctx.beginPath();
ctx.moveTo(gridSize / 2 + i * gridSize, gridSize / 2);
ctx.lineTo(gridSize / 2 + i * gridSize, gridSize / 2 + (boardSize - 1) * gridSize);
ctx.stroke();
}
}
drawChessboard();
这段代码的核心在于 drawChessboard
函数,它使用 canvas
的 beginPath
、moveTo
和 lineTo
方法绘制横纵交错的直线,构成棋盘的网格。
关键点:
gridSize
定义了每个格子的像素尺寸,可以根据需要调整。boardSize
定义了棋盘的大小(例如,15x15)。ctx.stroke()
用于实际绘制线条。
2. 落子与棋子绘制
现在,我们需要监听 canvas 的点击事件,获取点击位置,并绘制棋子。首先,定义一个二维数组来存储棋盘状态:
let chessData = [];
for (let i = 0; i < boardSize; i++) {
chessData[i] = [];
for (let j = 0; j < boardSize; j++) {
chessData[i][j] = 0; // 0 表示该位置为空
}
}
let isBlack = true; // 轮到黑子下
然后,监听点击事件:
canvas.addEventListener('click', function(event) {
const x = event.offsetX;
const y = event.offsetY;
const i = Math.floor(x / gridSize);
const j = Math.floor(y / gridSize);
if (chessData[i][j] === 0) {
chessData[i][j] = isBlack ? 1 : 2; // 1 表示黑子,2 表示白子
drawChess(i, j, isBlack);
isBlack = !isBlack;
// TODO: 胜负判定
}
});
这段代码首先获取点击位置的坐标 x
和 y
,然后计算出对应的棋盘格子的索引 i
和 j
。如果该位置为空 (chessData[i][j] === 0
),则在该位置落子,并调用 drawChess
函数绘制棋子,最后切换到另一方。
接下来,实现 drawChess
函数:
function drawChess(i, j, isBlack) {
const x = gridSize / 2 + i * gridSize;
const y = gridSize / 2 + j * gridSize;
ctx.beginPath();
ctx.arc(x, y, gridSize / 2 - 2, 0, 2 * Math.PI); // 绘制圆形棋子
ctx.fillStyle = isBlack ? 'black' : 'white';
ctx.fill();
}
drawChess
函数使用 canvas
的 arc
方法绘制圆形棋子,并根据 isBlack
的值设置填充颜色。
关键点:
event.offsetX
和event.offsetY
获取相对于 canvas 元素的点击坐标。Math.floor(x / gridSize)
和Math.floor(y / gridSize)
计算出棋盘格子的索引。ctx.arc
用于绘制圆形。ctx.fillStyle
用于设置填充颜色。ctx.fill
用于实际填充颜色。
3. 胜负判定:核心算法
胜负判定是五子棋游戏的核心。我们需要检查当前落子位置的横向、纵向、左斜向和右斜向四个方向,看是否有连续五个相同颜色的棋子。
function checkWin(i, j, color) {
// 横向
let count = 1;
for (let k = i - 1; k >= 0 && chessData[k][j] === color; k--) {
count++;
}
for (let k = i + 1; k < boardSize && chessData[k][j] === color; k++) {
count++;
}
if (count >= 5) return true;
// 纵向
count = 1;
for (let k = j - 1; k >= 0 && chessData[i][k] === color; k--) {
count++;
}
for (let k = j + 1; k < boardSize && chessData[i][k] === color; k++) {
count++;
}
if (count >= 5) return true;
// 左斜向
count = 1;
for (let k = i - 1, l = j - 1; k >= 0 && l >= 0 && chessData[k][l] === color; k--, l--) {
count++;
}
for (let k = i + 1, l = j + 1; k < boardSize && l < boardSize && chessData[k][l] === color; k++, l++) {
count++;
}
if (count >= 5) return true;
// 右斜向
count = 1;
for (let k = i - 1, l = j + 1; k >= 0 && l < boardSize && chessData[k][l] === color; k--, l++) {
count++;
}
for (let k = i + 1, l = j - 1; k < boardSize && l >= 0 && chessData[k][l] === color; k++, l--) {
count++;
}
if (count >= 5) return true;
return false;
}
checkWin
函数接收当前落子的坐标 i
和 j
,以及棋子的颜色 color
,然后分别在四个方向上统计连续相同颜色棋子的数量。如果任何一个方向的数量达到 5,则返回 true
,表示该颜色获胜。
关键点:
- 循环遍历四个方向,统计连续相同颜色棋子的数量。
- 注意边界条件的判断,防止数组越界。
现在,将 checkWin
函数加入到 canvas 的点击事件处理函数中:
canvas.addEventListener('click', function(event) {
const x = event.offsetX;
const y = event.offsetY;
const i = Math.floor(x / gridSize);
const j = Math.floor(y / gridSize);
if (chessData[i][j] === 0) {
const color = isBlack ? 1 : 2;
chessData[i][j] = color;
drawChess(i, j, isBlack);
if (checkWin(i, j, color)) {
alert(isBlack ? '黑棋胜!' : '白棋胜!');
}
isBlack = !isBlack;
}
});
4. 完整代码
以下是 script.js
文件的完整代码:
const canvas = document.getElementById('chessboard');
const ctx = canvas.getContext('2d');
const gridSize = 30;
const boardSize = 15;
let chessData = [];
for (let i = 0; i < boardSize; i++) {
chessData[i] = [];
for (let j = 0; j < boardSize; j++) {
chessData[i][j] = 0;
}
}
let isBlack = true;
function drawChessboard() {
for (let i = 0; i < boardSize; i++) {
ctx.beginPath();
ctx.moveTo(gridSize / 2, gridSize / 2 + i * gridSize);
ctx.lineTo(gridSize / 2 + (boardSize - 1) * gridSize, gridSize / 2 + i * gridSize);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(gridSize / 2 + i * gridSize, gridSize / 2);
ctx.lineTo(gridSize / 2 + i * gridSize, gridSize / 2 + (boardSize - 1) * gridSize);
ctx.stroke();
}
}
function drawChess(i, j, isBlack) {
const x = gridSize / 2 + i * gridSize;
const y = gridSize / 2 + j * gridSize;
ctx.beginPath();
ctx.arc(x, y, gridSize / 2 - 2, 0, 2 * Math.PI);
ctx.fillStyle = isBlack ? 'black' : 'white';
ctx.fill();
}
function checkWin(i, j, color) {
// 横向
let count = 1;
for (let k = i - 1; k >= 0 && chessData[k][j] === color; k--) {
count++;
}
for (let k = i + 1; k < boardSize && chessData[k][j] === color; k++) {
count++;
}
if (count >= 5) return true;
// 纵向
count = 1;
for (let k = j - 1; k >= 0 && chessData[i][k] === color; k--) {
count++;
}
for (let k = j + 1; k < boardSize && chessData[i][k] === color; k++) {
count++;
}
if (count >= 5) return true;
// 左斜向
count = 1;
for (let k = i - 1, l = j - 1; k >= 0 && l >= 0 && chessData[k][l] === color; k--, l--) {
count++;
}
for (let k = i + 1, l = j + 1; k < boardSize && l < boardSize && chessData[k][l] === color; k++, l++) {
count++;
}
if (count >= 5) return true;
// 右斜向
count = 1;
for (let k = i - 1, l = j + 1; k >= 0 && l < boardSize && chessData[k][l] === color; k--, l++) {
count++;
}
for (let k = i + 1, l = j - 1; k < boardSize && l >= 0 && chessData[k][l] === color; k++, l--) {
count++;
}
if (count >= 5) return true;
return false;
}
canvas.addEventListener('click', function(event) {
const x = event.offsetX;
const y = event.offsetY;
const i = Math.floor(x / gridSize);
const j = Math.floor(y / gridSize);
if (chessData[i][j] === 0) {
const color = isBlack ? 1 : 2;
chessData[i][j] = color;
drawChess(i, j, isBlack);
if (checkWin(i, j, color)) {
alert(isBlack ? '黑棋胜!' : '白棋胜!');
}
isBlack = !isBlack;
}
});
drawChessboard();
5. 总结与扩展
通过这篇教程,你应该已经掌握了如何使用 JavaScript 和 canvas 绘制五子棋棋盘,以及如何判断胜负。你可以根据自己的需求,对代码进行扩展,例如:
- 添加悔棋功能。
- 实现人机对战,使用 AI 算法来决定电脑的落子位置。
- 美化棋盘和棋子的样式。
- 使用 WebSocket 实现多人在线对战。
希望这篇教程对你有所帮助! 祝你游戏开发愉快! 别忘了点个赞哦!