电赛E题井字棋无视觉方案:模块化思维与STM32实战指南

电赛E题井字棋无视觉方案:模块化思维与STM32实战指南
对于电赛新手来说24年E题井字棋最让人头疼的就是视觉方案——摄像头调试复杂、环境光影响大、算法门槛高。但题目其实留了后路用矩阵键盘输入坐标OLED显示棋盘完全可以实现无视觉方案。我帮几个大一团队跑通了1-6问核心经验是把问题拆成“输入-判断-显示”三个稳定模块先确保单步可靠再拼成完整逻辑。1. 无视觉方案到底在考什么不是算法是模块化思维很多人看到“井字棋”就觉得要搞AI算法其实电赛E题的重点是基础外设控制状态机逻辑。无视觉方案意味着输入用4x4矩阵键盘代替摄像头选手手动输入落子坐标如1A、2B显示OLED或LCD显示棋盘状态和回合信息核心STM32或51单片机处理棋局逻辑、胜负判断、简单AI应对这种方案的优势很明显不依赖OpenCV、不受光线干扰、调试直观。但难点在于如何让三个模块稳定协作——键盘防抖、显示刷新、逻辑判断必须互不阻塞。1.1 题目要求的6个问题本质是功能叠加从第1问到第6问实际是在测试模块的完整性1-2问基础棋盘显示手动对战测输入输出模块3-4问加入简单AI随机落子胜负判断测逻辑模块5-6问增加游戏记录、重新开始、难度选择测状态机设计所以实现时一定要按问题顺序开发每完成一问就保存一个稳定版本避免后期调试时找不到问题源头。1.2 硬件配置门槛比想象中低很多同学担心硬件不够其实核心只需主控STM32F103C8T6或51开发板输入4x4矩阵键盘10元以内显示0.96寸OLED I2C接口15元左右其他杜邦线、按键、LED指示灯用于回合提示如果学校提供的套件有LCD可以直接用但OLED更省IO口且显示棋盘更清晰。关键是在开赛前确认所有器件能正常驱动不要等到比赛时才发现屏幕不亮或键盘检测异常。2. 环境搭建先让键盘和屏幕跑起来再谈逻辑我的习惯是先隔离测试每个外设因为多数问题出在硬件驱动层面。2.1 OLED显示模块——固定棋盘框架井字棋棋盘是固定的3x3网格直接硬编码坐标最可靠// OLED显示示例使用U8g2库 void draw_board(void) { u8g2_DrawFrame(u8g2, 0, 0, 64, 64); // 外框 u8g2_DrawVLine(u8g2, 21, 0, 64); // 竖线1 u8g2_DrawVLine(u8g2, 42, 0, 64); // 竖线2 u8g2_DrawHLine(u8g2, 0, 21, 64); // 横线1 u8g2_DrawHLine(u8g2, 0, 42, 64); // 横线2 }关键细节棋盘不要动态计算坐标直接写死像素位置避免刷新时闪烁落子显示用DrawDisc圆代表O和DrawXX代表X大小控制在网格内每次落子后只刷新单个格子区域部分刷新不要全屏重绘2.2 矩阵键盘模块——防抖和编码是重点4x4键盘的典型接法行ROW0-3接输出列COL0-3接输入。常见问题是按键抖动和长按重复触发// 按键检测示例STM32 HAL库 uint8_t key_scan(void) { static uint8_t last_key 0; uint8_t key get_key(); // 原始扫描值 if(key ! last_key) { HAL_Delay(20); // 防抖延时 if(key get_key()) { // 再次确认 last_key key; return key; // 返回键值 } } return 0; }键值映射方案数字键1-3对应行号字母键A-C对应列号需在键盘上标记或者用键盘自带数字1-9对应9个格子更直观实测建议在键盘上贴标签明确每个键的坐标测试时先打印串口日志确认键值正确再绑定到棋盘坐标。2.3 主控选型STM32比51更适合状态机虽然51单片机也能做但STM32的优势明显有足够内存存储棋盘状态3x3数组硬件I2C驱动OLED更稳定定时器中断处理按键扫描不卡主循环后期加语音提示、LED动画等扩展容易初始化顺序系统时钟配置GPIO初始化键盘行列、LEDI2C初始化OLED定时器初始化按键扫描用初始化棋盘数组全03. 核心实现从单人对战到AI应对的递进步骤第1-2问是基础必须完全稳定后再加AI逻辑。3.1 第1问棋盘显示与手动输入双人对战核心是一个game_state状态机enum game_state { PLAYER1_INPUT, // 玩家1落子 PLAYER2_INPUT, // 玩家2落子 CHECK_WIN, // 检查胜负 GAME_OVER // 显示结果 };落子处理流程等待按键输入获取坐标如按下2B表示第2行第2列检查该位置是否为空对应数组元素为0更新棋盘数组玩家1标记为1玩家2标记为2OLED更新对应格子显示切换玩家状态避坑点数组索引从0开始但显示坐标从1开始需要转换行-1列-A每次落子后立即检查胜负不要等回合结束平局判断棋盘满且无胜负3.2 第2问胜负判断的8种情况要全覆盖井字棋胜负有8种可能3行3列2对角线用数组循环判断最可靠int check_win(uint8_t board[3][3]) { int i; // 检查行 for(i 0; i 3; i) { if(board[i][0] board[i][0] board[i][1] board[i][1] board[i][2]) return board[i][0]; // 返回玩家编号 } // 检查列 for(i 0; i 3; i) { if(board[0][i] board[0][i] board[1][i] board[1][i] board[2][i]) return board[0][i]; } // 检查对角线 if(board[0][0] board[0][0] board[1][1] board[1][1] board[2][2]) return board[0][0]; if(board[0][2] board[0][2] board[1][1] board[1][1] board[2][0]) return board[0][2]; // 检查平局 for(i 0; i 3; i) { for(int j 0; j 3; j) { if(board[i][j] 0) return 0; // 还有空位继续游戏 } } return 3; // 平局 }3.3 第3-4问AI实现从随机到简单策略第3问的随机AI生成1-9的随机数作为格子编号如果该格子已被占用重新生成由于棋盘只有9格最多重试9次一定能找到空位void ai_random_move(uint8_t board[3][3]) { int x, y; do { x rand() % 3; y rand() % 3; } while(board[x][y] ! 0); board[x][y] 2; // AI作为玩家2 }第4问的简单策略AI优先级判断先检查AI是否能直接获胜进攻再检查是否需要阻止玩家获胜防守否则选择中心、角落、边缘的优先级位置// 检查是否有立即获胜的机会 int find_winning_move(uint8_t board[3][3], uint8_t player) { // 模拟在每个空位落子检查是否获胜 for(int i 0; i 3; i) { for(int j 0; j 3; j) { if(board[i][j] 0) { board[i][j] player; if(check_win(board) player) { board[i][j] 0; // 恢复 return i * 3 j; // 返回格子编号 } board[i][j] 0; } } } return -1; }3.4 第5-6问游戏流程与状态管理第5问要求记录游戏结果并支持重新开始需要增加游戏记录结构体struct game_record { uint8_t result; // 1:玩家赢 2:AI赢 3:平局 uint8_t rounds; // 回合数 uint32_t timestamp; };重新开始机制设置专用重启按键如键盘上的*键重启时清零棋盘数组、重置状态机、清屏重绘第6问的难度选择通过按键切换AI策略简单纯随机落子中等进攻防守策略困难增加开局库和预测算法时间有限可不实现4. 调试从模块测试到整体联调的排查顺序电赛现场最怕整体调试建议按这个顺序验证4.1 第一阶段外设单独验证OLED测试上电显示启动画面测试棋盘绘制函数测试局部刷新单个格子更新键盘测试每个按键按下时串口输出键值测试连续按键和同时按键的处理确认防抖参数15-25ms为宜LED指示测试玩家回合LED亮不同颜色游戏结束LED闪烁模式4.2 第二阶段功能模块验证棋盘逻辑测试人工模拟落子序列检查胜负判断是否正确测试边缘情况棋盘已满、连续落子同一位置AI行为测试观察AI是否优先选择获胜位置测试AI防守行为阻止玩家三连4.3 第三阶段整体流程验证完整游戏流程开机显示欢迎界面按键选择模式双人/AI进行游戏显示当前回合游戏结束显示结果重启或返回模式选择压力测试快速连续按键是否卡死长时间运行是否内存泄漏断电重启后是否能正常初始化5. 现场注意事项省时策略和常见救场方案5.1 编码阶段的时间分配建议第1天上午完成硬件驱动OLED显示、键盘输入第1天下午实现基础棋盘逻辑和双人对战1-2问第2天上午实现AI逻辑3-4问第2天下午完善流程控制和扩展功能5-6问第3天上午调试优化撰写报告第3天下午备份代码准备演示5.2 常见问题现场解决方案键盘响应不稳定检查上拉电阻是否启用增加防抖延时到30ms避免在主循环中阻塞式等待按键OLED显示闪烁改用局部刷新代替全屏刷新降低刷新频率500ms一次检查I2C时钟速率不要超过400kHzAI决策太慢优化棋盘判断算法减少循环次数使用查表法预计算胜负条件限制AI搜索深度井字棋不需要深度搜索程序跑飞或复位检查数组越界棋盘索引确保在0-2之间增加看门狗定时器堆栈大小设置合理STM32至少1024字节5.3 演示技巧让评委看清你的设计思路准备测试用例预先设计几个关键棋局演示时快速展示AI的决策逻辑增加视觉反馈落子时LED闪烁回合切换时有提示音模块化演示先展示基础功能再逐级开启高级功能异常处理演示故意在已落子位置再次落子展示程序的健壮性6. 扩展思考从比赛项目到实际应用的转换虽然井字棋本身很简单但这个无视觉方案的核心思路可以扩展到更复杂的应用6.1 状态机设计模式游戏逻辑的状态机同样适用于智能家居控制流程自动模式、手动模式、场景模式工业控制顺序启动、运行、停止、报警用户交互流程待机、输入、处理、结果展示6.2 人机交互的可靠性设计矩阵键盘OLED的组合在很多场合比触摸屏更可靠工业现场抗干扰强车载设备物理反馈明确低成本消费电子BOM成本控制6.3 AI决策的渐进式开发从随机算法到规则策略的演进路径随机响应验证基础框架规则判断if-else逻辑优先级系统加权决策预测算法迷你Max等这种渐进式开发确保每个阶段都有可演示成果降低项目风险。无视觉方案的最大价值在于降低复杂度提高可靠性。对于电赛这类时间紧迫的比赛选择成熟稳定的技术路线往往比追求新技术更实际。先把输入、处理、输出三个环节做扎实再考虑优化和扩展这是我从多次电赛指导中总结的最实用经验。