ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

五子棋人机项目解压与AI调优:从Alpha-Beta剪枝到评估函数

五子棋人机项目解压与AI调优:从Alpha-Beta剪枝到评估函数 简介这份rar压缩包提供了一套基于VC的五子棋人机对战完整工程面向学习人工智能搜索算法与Windows桌面程序开发的初学者或课程设计者。包内共29个文件包括4个cpp、5个h源码文件以及8个bmp位图、3个ico图标等界面资源还附带可执行文件与项目工程配置打开即可编译运行便于对照代码理解人机博弈的实现流程。整个资源仅367KB轻量但结构完整已有134人学习下载。项目核心利用Minimax算法与Alpha-Beta剪枝评估棋盘局势并优化搜索代码中结合启发式函数判断连珠威胁同时使用MFC完成棋盘绘制与交互处理适合以此掌握博弈树搜索、界面搭建及VC项目组织方式。压缩包内另有doc说明与相关文本可辅助阅读源码和复盘设计思路是五子棋人机对战课程设计或算法实践的一份实用参考。1. wuziqi.rar_五子棋人机从压缩包到能跑起来的本机对弈“wuziqi.rar_五子棋人机”这个命名很直白就是一份五子棋人机对弈的压缩包。你从某个下载站或课程资源里拿到它满心期待打开就能和AI下棋结果往往先是解压报错再是缺DLL或ModuleNotFoundError。实际上这类资源的价值通常不只是“能玩”而是里面的人机AI核心——棋盘判断、搜索算法、评估函数——可以拆出来学习或改造。这篇笔记会从rar包怎么干净解压讲起一路到AI搜索深度怎么调、评估函数权重怎么设把新手会卡住的点全部走一遍。适合做课程设计的学生、想入门棋类AI的开发者以及准备二次开发一个五子棋人机的从业者。2. 解压和识别先弄清wuziqi.rar里面装的是源码还是可执行文件2.1 用开源解压工具打开rar避开广告和伪加密拿到任何.rar文件我习惯先用开源工具而不是商业压缩软件原因不是功能不够而是那些免费压缩软件经常塞广告弹窗和全家桶解压一个课程设计资源没必要担这个风险。Windows下我常用7-ZipLinux/macOS用p7zip或unrar-free。先看文件头确认它是真rar还是改名zipfile wuziqi.rar # 输出类似wuziqi.rar: RAR archive data, v5.0如果file命令显示是Zip archive说明资源方把zip改成了rar直接改用unzip或者7-Zip解压就行。如果显示RAR archive data就按rar流程走。解压命令如下# 7-Zip 能解压大多数rar包括v5格式 7z x wuziqi.rar # Linux下用p7zip的7z命令也可以 # 如果只是解压老版本rarunrar-free也能应付 unrar x wuziqi.rar这里有个常见坑有些rar为了防盗打包者做了“伪加密”——压缩目录里每个文件头加了密码标志但数据本身没加密。7-Zip解压时会要密码导致你卡在第一步。解决办法放在第5章单独说这里先记住看到要密码别急着找密码先怀疑伪加密。2.2 看目录结构判断语言与运行方式Makefile、CMakeLists 还是 Python解压完成后第一件事是不要急着双击exe或者run先把目录结构列出来find . -maxdepth 2 -type f | head -50常见五子棋人机资源有几种形态纯Python会出现main.py、game.py、ai.py依赖写在requirements.txt里。C/C可能出现CMakeLists.txt或Makefile以及src/、include/目录。易语言/其他更可能是一个编译好的exe加几个模块文件但源码不一定完整。Java会有src/和pom.xml或build.gradle。判断语言后运行方式就跟着变了。Python项目我喜欢先在项目根目录建虚拟环境再装依赖python -m venv venv source venv/bin/activate # Windows下是 venv\Scripts\activate pip install -r requirements.txt注意有些课程设计资源根本没有requirements.txt只有一堆.py文件。这时我一般先看看import了哪些标准库如果只有random、time、tkinter这类那直接python main.py就能跑不需要额外装包。如果有numpy就pip install numpy。C/C项目则要先生成构建系统cmake -S . -B build cmake --build build这里特别容易翻车拿到的是一个老旧的Visual Studio工程.vcxprojcmake根本认不了。这时直接看有没有.sln文件用MSVC或MinGW对应工具链编译。我见过太多人卡在“编译器不认工程文件”上其实换个思路用g *.cpp -o wuziqi能暴力编译过前提是依赖不多。2.3 如果是密码保护的压缩包先检查伪加密标志再说如果解压时提示需要密码先别急着去找密码或下暴力破解工具。很多rar文件是伪加密头信息里有一个加密位被置位压缩数据并未加密。这时候可以用一个简单的Python脚本读rar头把加密标志位改掉再重新打包或解压。这块操作属于“恢复自己的资源”场景不涉及破解他人文件。常见做法是先用7z l wuziqi.rar看压缩包列表如果能看到里面的文件名说明至少没做全加密。如果确定不是伪加密而是真加密且密码是资源方统一设置的先去找资源说明页而不是急着下密码恢复工具——网上常见的rar password cracker、rar烈火等都属于暴力撞库时间成本极高密码稍微复杂一点就跑不动。我一般只会在确知密码规则时才考虑这类工具。伪加密则可以换工具绕过某些Linux版本的unrar不检查加密标志的合法性反而能直接解压或者用7-Zip时直接把压缩包拖到“测试”模式有时能一次性列出所有文件并把数据提出来。与其折腾恢复rar不如先多试两个解压工具常常就解开了。3. 把五子棋人机跑起来最小可复现的AI核心3.1 棋盘与胜负判断用一维数组还是二维数组解压出来的项目可能用的是15×15传统棋盘也可能是19×19棋盘。不管它用嵌套列表还是numpy二维数组核心数据结构就一件事棋盘上每个点有黑、白、空三种状态。我习惯用0表示空1表示黑棋2表示白棋。这样在评估函数里用整数运算非常快BOARD_SIZE 15 board [[0] * BOARD_SIZE for _ in range(BOARD_SIZE)] def place_stone(board, row, col, player): board[row][col] player def check_win(board, row, col, player): directions [(1, 0), (0, 1), (1, 1), (1, -1)] for dr, dc in directions: count 1 r, c row dr, col dc while 0 r BOARD_SIZE and 0 c BOARD_SIZE and board[r][c] player: count 1 r dr c dc r, c row - dr, col - dc while 0 r BOARD_SIZE and 0 c BOARD_SIZE and board[r][c] player: count 1 r - dr c - dc if count 5: return True return False这里directions定义了两个方向的组合水平、垂直、斜向。每个方向只查一次因为朝正负两个方向延展。第一次接触的人容易写出四个方向各写一遍实际用(dr, dc)和(-dr, -dc)就可以合并。注意这里用二维数组[row][col]直观但如果你后面要做位运算棋局编码或做性能优化一维数组board[i*15j]会更省内存。对于课程设计级别二维数组完全够用不必上来就学位棋盘。3.2 人机AI选型为什么不建议只用随机落子而用极小化极大当你打开一个五子棋人机项目发现AI只是随机在空位落子那这个“人机”就是玩具。真正的五子棋人机AI至少要做对抗搜索。核心思路是轮到AI下它假设对手每一步都下最优自己也要下最优。最经典的方案是极小化极大搜索Minimax配合Alpha-Beta剪枝。对五子棋这种棋盘落子游戏状态空间太大纯Minimax到6层就爆炸但加上剪枝和启发式候选点能下到可用的水平。选型理由很简单Minimax保证在给定深度内AI能找到一个“不输”的落子策略假设评估函数够准。Alpha-Beta剪枝能把搜索量减到四分之一甚至更低同样是6层剪枝前可能上亿节点剪枝后可能只有几百万。蒙特卡洛树搜索MCTS更高级但实现复杂度高而且在小棋盘上反而不一定比深搜索的Minimax强。所以拿到一个五子棋人机源码如果看到minimax或alpha_beta函数那这项目就有学习价值。如果看到random.choice(empty_positions)那就是给你练习改AI用的。3.3 Alpha-Beta剪枝的Python实现核心代码与搜索深度这里我给出一份最简可跑的AI核心。它接收当前棋盘、当前玩家AI是1还是2、搜索深度返回一个落子坐标。AI_PLAYER 1 HUMAN_PLAYER 2 def evaluate(board): # 简化版评估统计所有方向的连续子数加权求和 score 0 for row in range(BOARD_SIZE): for col in range(BOARD_SIZE): if board[row][col] 0: continue player board[row][col] for dr, dc in [(1,0), (0,1), (1,1), (1,-1)]: count 1 r, c row dr, col dc while 0 r BOARD_SIZE and 0 c BOARD_SIZE and board[r][c] player: count 1 r dr c dc if count 5: score 10000 if player AI_PLAYER else -10000 elif count 4: score 1000 if player AI_PLAYER else -1000 elif count 3: score 100 if player AI_PLAYER else -100 elif count 2: score 10 if player AI_PLAYER else -10 return score def alpha_beta(board, depth, alpha, beta, maximizing): if depth 0: return evaluate(board) moves [(r, c) for r in range(BOARD_SIZE) for c in range(BOARD_SIZE) if board[r][c] 0] if maximizing: max_eval -float(inf) for r, c in moves: board[r][c] AI_PLAYER eval alpha_beta(board, depth - 1, alpha, beta, False) board[r][c] 0 max_eval max(max_eval, eval) alpha max(alpha, eval) if beta alpha: break return max_eval else: min_eval float(inf) for r, c in moves: board[r][c] HUMAN_PLAYER eval alpha_beta(board, depth - 1, alpha, beta, True) board[r][c] 0 min_eval min(min_eval, eval) beta min(beta, eval) if beta alpha: break return min_eval def ai_move(board, depth): best_score -float(inf) best_move None alpha -float(inf) beta float(inf) moves [(r, c) for r in range(BOARD_SIZE) for c in range(BOARD_SIZE) if board[r][c] 0] for r, c in moves: board[r][c] AI_PLAYER score alpha_beta(board, depth - 1, alpha, beta, False) board[r][c] 0 if score best_score: best_score score best_move (r, c) alpha max(alpha, score) return best_move这里depth是搜索深度alpha是当前Max方已知的最佳值beta是当前Min方已知的最佳值。当alpha beta时说明这条分支已经不可能影响最终决策剪掉。参数说明depth建议从2开始调试每增加1层耗时大约乘5到10倍15×15满棋盘搜索到4层已经接近百万节点必须配合候选点裁剪。这份代码直接跑会非常慢因为moves包含棋盘上所有空位——这也是五子棋人机项目最容易卡死的地方AI一步几十秒你以为死机其实是搜索没剪枝。4. 调参与者评估函数权重、搜索深度与单步耗时的取舍4.1 评估函数设计连子、活三、冲四怎么量化上一章的evaluate只是粗糙地对2/3/4/5连子给了固定分它没有区分“活三”和“眠三”。实战中一个活三两端都没被堵比一个被堵一端的眠三威胁大得多。如果权重都是100AI可能去堵眠三而放过活三最终被对手连成五子。所以真正的评估函数要分清楚棋型。常见做法是遍历棋盘对每个点扫描四个方向统计黑白双方在这条线上的连续子数和两端状态棋型权重活五1000000活四100000冲四8000活三5000眠三1000活二500眠二50这张表是我常用的初始值你可以按调参经验调整。注意评分要同时计算自己这边的进攻分和对手的防守分。最终评估值 己方所有棋型分之和 - 对手棋型分之和 × 某个系数。系数一般在1.0到1.5之间越大AI越偏向防守。我通常设1.2这样AI既会主动进攻又不会无视对手的冲三。4.2 搜索深度与节点裁剪4层还是6层每层约多少节点十五路棋盘空位满盘时有225个点。如果搜索深度为4层第一层225个分支第二层约224个第三层223个第四层222个——大约25亿条路径这不是个人电脑能扛的必须剪枝。Alpha-Beta剪枝后最优情况下节点数约等于O(b^(d/2))但五子棋的move ordering如果没做好实际剪枝效率要差很多。我的经验是搜索深度2适合快速演示单步耗时小于0.1秒棋力很弱只会看两步。搜索深度4配合候选点裁剪每层只看20个左右落子候选单步耗时约0.3秒到1秒棋力已经能赢大多数初学者。搜索深度6需要每层候选点限制到8个左右否则耗时突破十秒。棋力显著增强但评估函数不够好时反而会出现“算得越深越差”的玄学。这里的核心参数是“候选点数”。Alpha-Beta剪枝依赖于好的move ordering——先搜最好的分支。五子棋里最合理的落子总在已有棋子的附近所以没必要搜索整个棋盘。常见做法是只搜索以现有棋子为中心2格范围内的空位数量通常在30到60个之间。递归进入下一层时候选点范围可以进一步缩小。4.3 启发式落子候选点只看附近8格能减少多少搜索量下面这段代码演示怎么生成候选点并限制每层的扩展数量def get_candidate_moves(board, max_moves20): candidates set() for r in range(BOARD_SIZE): for c in range(BOARD_SIZE): if board[r][c] ! 0: for dr in range(-2, 3): for dc in range(-2, 3): nr, nc r dr, c dc if 0 nr BOARD_SIZE and 0 nc BOARD_SIZE and board[nr][nc] 0: candidates.add((nr, nc)) # 按与棋盘中心的距离或局部热度排序截取前max_moves个 candidates list(candidates) candidates.sort(keylambda pos: score_candidate(board, pos), reverseTrue) return candidates[:max_moves] def score_candidate(board, pos): # 简单统计周围8格已有棋子密度密度越高越优先 r, c pos near 0 for dr in (-1, 0, 1): for dc in (-1, 0, 1): nr, nc r dr, c dc if 0 nr BOARD_SIZE and 0 nc BOARD_SIZE and board[nr][nc] ! 0: near 1 return nearscore_candidate函数统计候选点周围8格的已有棋子密度密度越高越优先。搜索范围range(-2, 3)覆盖以每个已有棋子为中心的5×5区域角落和边线不会漏掉因为没有棋子时不会有候选点。max_moves是每层最多扩展的候选点数量我一般开局设30盘面空旷需要更多可能中局设20残局设8。把这一节加到上一章的alpha_beta里你会发现同样的depth 4耗时从几十秒降到几百毫秒。这一步是五子棋人机从“能跑”到“能用”的分水岭。5. 常见问题与避坑解压失败、中文乱码、AI假死、棋力不稳5.1 现象rar解压到一半报错“未知文件末端”解出来的目录缺文件原因大概率是伪加密或者压缩包损坏。有些下载站为了防盗链把文件头做了手脚解压软件在读到某个文件时认为数据不完整。解决先换解压工具我用7z x wuziqi.rar比UnRAR能容忍更多伪加密情况。如果仍然报错尝试用7z t wuziqi.rar测试压缩包完整性。若提示一个文件出错但其他文件正常优先解压还没出错的文件。还有一种情况是rar分卷比如wuziqi.part1.rar你只下载了其中一个必须全部分卷放同一目录再解压。确认不是分卷后再考虑用7z l列清单如果能看到文件名但提取不了基本可以断定是伪加密按2.3的方法换工具或用脚本修复加密标志。5.2 现象Python源码运行报错ModuleNotFoundError: No module named numpy或No module named pygame原因项目依赖没有装齐或者你用了系统Python环境和项目要求的包版本冲突。解决不要直接在全局环境pip install。按2.2说的建虚拟环境然后看项目里有没有requirements.txt。没有的话用import语句反推依赖。我经常干的事是pip install pygame numpy如果项目是Tkinter写的那就不需要额外依赖直接python main.py。注意有些资源是Python 2写的print不带括号在Python 3下直接语法错误。这种我一般用2to3工具转一遍或者干脆手动改几十处print。还有一种隐蔽情况项目里用到了tkinter但你的Linux服务器没装图形界面运行时直接抛No display。这时候要么装python3-tk要么改走命令行输入输出别在无头环境里硬跑GUI。5.3 现象AI落子速度极慢一步要几十秒点击后界面像假死原因搜索深度设置过大或者没有做候选点裁剪Alpha-Beta循环遍历了所有225个空位及其递归分支。解决先找AI代码里的深度常量通常叫depth或max_depth从6改到2试试。再把moves [...]那个遍历改成只取附近2格的点。如果项目里原本就有get_candidate_moves确认它有没有被调用。很多课程设计源码写好了裁剪函数但主流程里忘用了找出来接上就行。这里补充一个排查手段在AI落子函数前后打印时间戳用time.time()算耗时比肉眼判断准得多。5.4 现象AI棋力忽高忽低有时能看见冲四有时对活三熟视无睹原因评估函数只数了连子长度没有区分活三和眠三导致AI认为堵一个被挡死的眠三和堵一个活三的收益一样。解决按4.1的表格扩充评估函数。如果嫌自己实现麻烦可以先抄一个简单的对每个位置检查四个方向如果连续子两端都空分数×3只有一端空分数×1。比如一个连3眠三是30分活三就是90分这样AI会优先处理活三。改完这个棋力提升立竿见影。注意评估函数改动后要同时检查进攻和防守两条线别只加了己方进攻权重却不防守对手的活三。5.5 现象Win/mac解压出来的文件名乱码代码里中文注释变问号原因压缩包在Windows下用GBK编码文件名打包在macOS/Linux下按UTF-8解压中文就乱码了源码文件内部编码也类似。解决解压后用ls看文件名如果是乱码用7z x -mcp936 wuziqi.rar让7-Zip按GBK编码解压文件名对应中文Windows。代码文件如果不是UTF-8用iconv从GBK转成UTF-8iconv -f GBK -t UTF-8 main.py main_utf8.py但注意转完以后文件里的字符串字面量也会变化如果程序里有中文菜单要让引擎能认GBK。大多数现代编辑器打开时自动识别GBK不一定非要转。最省心的办法是在Windows下用7-Zip解压然后拷贝到其他系统。这些坑我几乎在每一个课程设计资源里都会踩一遍最气人的是AI假死和棋力不稳这种“黑匣子”问题靠肉眼很难定位所以改代码前先备份原文件改坏了还有后悔药。6. 让AI更像人自对弈验证与更强的搜索方向判断一个五子棋人机改得好不好最笨也最有效的办法是让AI自己和自己下100盘。写个简单脚本黑棋白棋都调ai_move统计胜率和平均步数。如果改动后的AI在自对弈里明显占据优势说明改动有效如果反而变弱优先检查评估函数里进攻和防守的系数是不是调反了。def self_play(game_count20): ai_wins 0 for _ in range(game_count): board [[0] * BOARD_SIZE for _ in range(BOARD_SIZE)] player AI_PLAYER step 0 while True: r, c ai_move(board, depth4) board[r][c] player if check_win(board, r, c, player): if player AI_PLAYER: ai_wins 1 break player HUMAN_PLAYER if player AI_PLAYER else AI_PLAYER step 1 if step BOARD_SIZE * BOARD_SIZE: break print(fAI胜率: {ai_wins / game_count:.2%})这个脚本的game_count不宜太大depth4时一局可能要几分钟。我一般跑10局先看胜负分布再改权重。如果你想让AI再上一个台阶可以从Minimax换到MCTS或者参考开源里棋力更强的rapfi五子棋项目它用到了更精细的棋型库和预训练评估模型代码结构比课程设计复杂得多但核心思路仍是“候选点生成 - 递归搜索 - 评估函数”。我现在拿到这类rar第一件事不是急着解压而是先用file看类型再find看结构确认是源码就直接进开发流程确认是编译好的exe就先跑一局试试棋力。这个习惯救了我很多次希望帮到你。本文还有配套的精品资源点击获取
返回列表