
从零实现井字棋:状态机的优雅设计
在游戏开发中,即使是简单的井字棋,其内部逻辑也蕴含着精妙的设计思想。本文将深入解析一个完整的井字棋实现,重点关注其核心架构——基于状态机的游戏流程控制。
游戏概述与状态机设计理念
井字棋(Tic-Tac-Toe)是两人对战的回合制游戏,在3×3棋盘上进行。玩家轮流在空格放置自己的标记(通常为“X”和“O”),率先将三个标记连成一线(横、竖或斜)者获胜。
状态机(Finite State Machine)是管理游戏流程的经典模式,特别适合回合制游戏。它将游戏划分为离散的状态,每个状态下只处理特定逻辑,状态间通过明确条件转换,使代码结构清晰、易于维护。
核心代码解析
状态机主逻辑
我们的实现包含五个核心状态:
arrChessboardclickArray[0..2, 0..2] of Bool
arrChessboardArray[0..2, 0..2] of Int
StateInt
bStatrBool
bResetBool
WinnerInt
GameOverBool
iInt
jInt
StepInt
ActStepInt
chessInt
MessageWString[80]
/////////主逻辑///////////
CASE #State OF
0: // 初始/待机状态
IF #bStart THEN
#State := 1; // 收到开始信号,进入状态1
END_IF;
1: // 游戏初始化
// 清空棋盘
FOR #i := 0 TO 2 DO
FOR #j := 0 TO 2 DO
#arrChessboard[#i, #j] := 0;
END_FOR;
END_FOR;
// 重置计数器和玩家标记
#Step := 0;
#ActStep := 0;
#chess := 1;
#State := 2; // 进入游戏进行状态
2: // 游戏进行中
#Message := WString#'游戏开始';
// 检测棋盘点击
FOR #i := 0 TO 2 DO
FOR #j := 0 TO 2 DO
// 如果格子为空且被点击
IF #arrChessboard[#i, #j] = 0 AND #arrChessboardclick[#i, #j] THEN
#Step := #Step + 1; // 步数递增
// 根据步数奇偶决定当前玩家(1或2)
IF #Step MOD 2 = 0 THEN
#arrChessboard[#i, #j] := 1;
ELSE
#arrChessboard[#i, #j] := 2;
END_IF;
END_IF;
END_FOR;
END_FOR;
// 调用胜负判断函数
"check_winner"(arrChessboard := #arrChessboard,
Winner => #Winner,
GameOver => #GameOver);
IF #GameOver THEN
#State := 10; // 游戏结束,进入状态10
END_IF;
10: // 游戏结束,展示结果
IF #Winner = 1 THEN
#Message := WString#'游戏结束!胜利者是玩家1';
ELSIF #Winner = 2 THEN
#Message := WString#'游戏结束!胜利者是玩家2';
ELSE
#Message := WString#'游戏结束!平局';
END_IF;
#State := 0; // 返回初始状态,等待新游戏
END_CASE;
胜负判断算法详解
胜负判断函数check_winner是游戏逻辑的核心:
// 棋盘坐标系统
// [0,0] [0,1] [0,2]
// [1,0] [1,1] [1,2]
// [2,0] [2,1] [2,2]
// 1. 检查行:水平方向三连
FOR #i := 0 TO 2 DO
#firstSymbol := #arrChessboard[#i, 0];
IF #firstSymbol <> 0 THEN
#allSame := TRUE;
FOR #j := 1 TO 2 DO
IF #arrChessboard[#i, #j] <> #firstSymbol THEN
#allSame := FALSE;
EXIT;
END_IF;
END_FOR;
IF #allSame THEN
#Winner := #firstSymbol;
#GameOver := TRUE;
RETURN;
END_IF;
END_IF;
END_FOR;
// 2. 检查列:垂直方向三连
FOR #j := 0 TO 2 DO
#firstSymbol := #arrChessboard[0, #j];
IF #firstSymbol <> 0 THEN
#allSame := TRUE;
FOR #i := 1 TO 2 DO
IF #arrChessboard[#i, #j] <> #firstSymbol THEN
#allSame := FALSE;
EXIT;
END_IF;
END_FOR;
IF #allSame THEN
#Winner := #firstSymbol;
#GameOver := TRUE;
RETURN;
END_IF;
END_IF;
END_FOR;
// 3. 检查主对角线(左上到右下)
#firstSymbol := #arrChessboard[0, 0];
IF #firstSymbol <> 0 THEN
IF (#arrChessboard[1, 1] = #firstSymbol) AND (#arrChessboard[2, 2] = #firstSymbol) THEN
#Winner := #firstSymbol;
#GameOver := TRUE;
RETURN;
END_IF;
END_IF;
// 4. 检查副对角线(右上到左下)
#firstSymbol := #arrChessboard[0, 2];
IF #firstSymbol <> 0 THEN
IF (#arrChessboard[1, 1] = #firstSymbol) AND (#arrChessboard[2, 0] = #firstSymbol) THEN
#Winner := #firstSymbol;
#GameOver := TRUE;
RETURN;
END_IF;
END_IF;
// 5. 检查平局:棋盘已满且无胜者
#emptyCells := 0;
FOR #i := 0 TO 2 DO
FOR #j := 0 TO 2 DO
IF #arrChessboard[#i, #j] = 0 THEN
#emptyCells := #emptyCells + 1;
END_IF;
END_FOR;
END_FOR;
IF #emptyCells = 0 THEN
#GameOver := TRUE;
#Winner := 0; // 0表示平局
END_IF;
HMI画面控件设置



状态机在游戏开发中的通用性
这个井字棋实现展示了状态机在游戏开发中的普适价值:
清晰的状态流转:初始化 → 进行中 → 结束 → 重置的循环清晰可控
事件驱动响应:每个状态只响应特定事件(如点击、胜利条件达成)
易于扩展:添加新状态(如"动画播放中"、"暂停")不影响现有逻辑
调试友好:通过追踪状态变量即可了解游戏整体进展
总结
通过这个井字棋案例,我们看到状态机如何将复杂游戏逻辑分解为简单、可管理的状态单元。这种设计模式不仅适用于井字棋,也可扩展至更复杂的棋盘游戏(五子棋、围棋)、RPG游戏的状态管理、UI界面流程控制等场景。
状态机的精髓在于:明确定义有限状态,规范状态间转换条件,每个状态专注处理特定逻辑。掌握这一思想,你就能设计出结构清晰、维护性强的游戏系统。


![[C++]算法双指针 复写0-171主机测评](https://www.171host.com/wp-content/uploads/2026/09/20260910013601-6aa2098179e1b-220x150.png)