项目地址

一个基于 WebSocket + canvas + react 的五子棋联机游戏

为什么想做

一个 AI 五分钟就能写出的程序,我断断续续写了4,5天。作为 AI 时代的原住民,我之前的理念一直是“AI 能完成好的,绝不手写。”虽然是这么想,但是心里会一直有种不安感。AI 写的黑箱代码不仅难维护、有技术隐患,而且经常没有结构意识、逻辑混乱。最主要的是,我实在太依赖 AI 了——貌似一万年没有手写一个项目了。于是,在朋友的启发下,我萌生出做小游戏的想法。我认为写这样一个简单的项目可以熟悉语法,熟悉 react ,增强古法的能力。

而且,用自己写的代码和朋友玩联机游戏,真的超级有成就感的说!

技术栈

WebSocket

WebSocket 建立浏览器之间的连接,监听和广播消息。WebSocket 通常发送字符串,发送时需要用JSON.stringify(...)把对象转为字符串,收到后再用JSON.parse(...)转为对象。

主要消息类型:

board_state

连接时/刷新页面后/重置游戏后发送。

1
2
3
4
5
6
7
socket.send(JSON.stringify({
type: "board_state",
board,
currentPlayer,
lastMove
}));//后端发送
//或者重置时后端发送:client.send(JSON.stringify(resetMessage));
1
2
3
4
5
6
7
if (message.type == "board_state") {
setBoard(message.board);
setCurrentPlayer(message.currentPlayer);
setLastMove(message.lastMove);
setWinner(null);
return;
}//前端监听
move

下棋时发送。

前端先通过点击事件发送给后端

1
2
3
4
5
6
7
//handleClick
socket.send(JSON.stringify({
type: "move",
row,
col,
player: currentPlayer,
}));

后端监听

1
2
3
4
5
6
7
socket.on("message", (data) => {
const message = JSON.parse(data.toString());

if (message.type !== "move") return;

...
});

后端广播给所有前端

1
client.send(JSON.stringify(moveMessage));

前端监听

1
2
3
4
5
if (message.type === "move") {
setBoard(...);
setLastMove(...);
setCurrentPlayer(...);
}

useEffect 与 canvas

canvas 作为一个画布,画面更新由[board, placePosition, lastMove]这些依赖项决定。只要其中一个状态改变,就重新执行绘图。

五子判定算法

每落一子,数一次该子各方向连续同色棋子个数

1
2
3
4
5
while (currentRow >= 0 && currentRow <= BOARD_SIZE && currentCol >= 0 && currentCol <= BOARD_SIZE && board[currentRow][currentCol] === color) {
count++;
currentRow += rowStep;
currentCol += colStep;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function checkWinner(board, row, col, color) {
const directions = [
[0, 1],
[1, 0],
[1, 1],
[1, -1],
];

for (const [rowStep, colStep] of directions) {
let count = 1;
count += countStones(board, row, col, rowStep, colStep, color);
count += countStones(board, row, col, -rowStep, -colStep, color);
if (count >= 5) {
return true;
}
}
return false;
}