ARTICLE DETAIL

资讯详情

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

五子棋AI实战:从评估函数到Alpha-Beta剪枝的课程设计全解析

五子棋AI实战:从评估函数到Alpha-Beta剪枝的课程设计全解析 简介以五子棋为载体的人工智能课程设计报告适合人工智能、算法设计方向的学生与开发者阅读重点演示AI在棋类游戏中的具体应用。报告从五子棋规则与需求分析入手先介绍15×15棋盘与五子连珠规则再围绕人机对战、悔棋、胜负判断等功能展开随后在主要名词说明中逐个拆解棋盘数据m_data、清空棋盘Clear、初始化Init、绘制棋子Draw、左键消息OnLButtonUp、绘制棋盘OnPaint、胜负判断Win、悔棋操作Back等关键模块并重点讲解胜负判断的Win算法延伸到深度优先搜索、最小最大搜索与Alpha-Beta剪枝等经典策略帮助读者理解计算机如何评估棋局并选择最佳落子。报告还包含程序运行界面展示、不足说明和作者心得体会完整呈现从需求分析到算法实现、再到界面设计与排错优化的项目流程。资源压缩包仅1个PDF文件大小358KB目录结构清晰方便按章节查阅目前已有1064人学习对课程设计选题、算法实践或入门AI应用都有较高参考价值。1. 五子棋课程设计把人工智能从理论变成能对弈的代码每到期末人工智能课程设计的选题清单里总有一个五子棋。它看着简单——15×15的棋盘黑白两色规则一句话能讲完真正动手才知道要让AI在1秒内算出一步好棋背后是评估函数、搜索剪枝和工程实现的完整链路。《人工智能课程设计报告-五子棋》这类题目对应着一个可运行的博弈程序、一份能支撑答辩的实验分析以及一套从“会下棋”到“下得好”的迭代方法。下面把这条链路拆开讲清楚先立住评估和搜索两个理论地基再给出一份能直接跑的Python实现最后讲参数怎么调、报告里哪些数据最加分。适合正在做人工智能大作业、需要交代码和报告的学生也适合想用五子棋快速体验博弈树搜索的人工智能入门者。2. 棋型评估与搜索树五子棋AI的算法地基2.1 为什么五子棋不走深度学习路线很多人的第一反应是“要不要上卷积神经网络”。五子棋的棋盘只有225个位置规则状态变化完全确定这是典型的完全信息博弈。深度学习在像猫狗识别这样的图像分类任务上优势明显但在五子棋这种小状态空间里反而会因为数据集构造复杂、训练不稳定而拖垮课程设计周期。常见做法是走经典博弈路线评估函数加极大极小搜索Minimax加Alpha-Beta剪枝。这条路线不需要训练写好评测函数就能下棋可解释性强——每一步AI为什么选这个点可以回溯搜索树解释给答辩老师听。这份报告里的核心工作量也集中在评估和搜索这两块上。2.2 从“局面”到“棋型分”评估函数的基本思路评估函数要回答一个问题当前棋盘对AI有多大优势最简单可靠的做法是把局面拆成棋型。棋型是指连续或近似连续的相同颜色棋子序列常见的类型有活二、活三、活四、冲四等。每个棋型给一个基础分AI方总分减去对手方总分就得到局面评价。棋型分数示例含义活四100000两端都开放的四连必胜冲四50000一端被封的四连对方必须堵活三10000两端开放的连三下一步可成活四眠三1000一端被堵的连三活二500两端开放的连二眠二100一端被堵的连二分数比例比绝对值更重要。活四的分数必须远远大于多个活三之和否则AI可能为了造一堆活二而错过一步制胜的活四。这里也是报告里“评估函数设计”章节最能体现工作量的一部分不要只写“给每个棋型分”要把棋型识别、分数调整、防守系数写清楚。2.3 搜索树Minimax 与 Alpha-Beta 剪枝确定了局面打分就需要在落子前模拟未来几步。AI假设对手每次都会选择对AI最不利的走法因此每一层搜索轮流取最大值和最小值这就是人工智能基础里都会提到的Minimax。五子棋每步可选的落点大约在20个以上搜索4层就要评估几百万个局面不剪枝根本跑不动。Alpha-Beta剪枝维护两个边界alpha是目前已找到的对MAX方最好的分数beta是MIN方最好的分数。一旦某条分支配不上界就放弃继续搜索。剪枝不改变搜索结果只减少搜索量。实际实现时搜索顺序越接近“最优走法优先”剪枝效率越高。2.3.1 搜索深度与分支因子的关系4层搜索配上评估较准的棋型分已经能战胜多数不假思索的普通玩家6层搜索配合启发式排序可以在棋力上再上一个档位。这里想强调的是评估函数的权重排第一搜索深度排第二。一个能看清活三和冲四差别的评估函数比一个傻深到6层但只看棋子个数的搜索强得多。报告结论部分如果能用实验数据证明这一点比单独贴几页代码有说服力得多。3. Python实现从打分函数到可对弈的完整程序3.1 棋盘数据结构与候选点生成棋盘用二维列表表示board[x][y]0表示空1表示黑棋2表示白棋。AI内部约定当前玩家永远是1对手永远是2这样评估函数不需要区分颜色。候选点的生成逻辑是只考虑已经落子位置周围两格以内的空点。这一条简单的规则能把每步候选点从225个降到30个以内大幅缩小搜索树。def get_candidates(board): size len(board) candidates set() for x in range(size): for y in range(size): if board[x][y] ! 0: for dx in (-2, -1, 0, 1, 2): for dy in (-2, -1, 0, 1, 2): nx, ny x dx, y dy if 0 nx size and 0 ny size and board[nx][ny] 0: candidates.add((nx, ny)) return list(candidates)这段代码先找出所有有棋子的点再把这些点周围两格以内的空点加入候选集。用set是为了去重因为一个空点可能同时是两个不同棋子的邻居。候选点数量直接影响搜索速度30个候选点做4层Alpha-Beta搜索剪枝后可以在几十毫秒到几百毫秒量级内出结果如果不做距离限制225个空点全进搜索4层搜索接近千万节点普通笔记本就跑不起来了。3.2 胜负判定与评估函数胜负判定扫描四个方向从每个点出发数连续同色棋子满五个就返回该棋色。评估函数则是核心中的核心常见做法是分别统计AI和对手的棋型总分返回差值。def check_winner(board): size len(board) directions [(1, 0), (0, 1), (1, 1), (1, -1)] for x in range(size): for y in range(size): if board[x][y] 0: continue player board[x][y] for dx, dy in directions: count 1 nx, ny x dx, y dy while 0 nx size and 0 ny size and board[nx][ny] player: count 1 nx dx ny dy if count 5: return player return 0def pattern_score(count, open_left, open_right): open_ends int(open_left) int(open_right) if count 5: return 100000 if count 4: return 100000 if open_ends 2 else 50000 if open_ends 1 else 0 if count 3: return 10000 if open_ends 2 else 1000 if open_ends 1 else 0 if count 2: return 500 if open_ends 2 else 100 if open_ends 1 else 0 if count 1: return 50 if open_ends 2 else 10 if open_ends 1 else 0 return 0 def evaluate_board(board, ai_player): size len(board) ai_score 0 human_score 0 directions [(1, 0), (0, 1), (1, 1), (1, -1)] for player in (1, 2): player_total 0 for x in range(size): for y in range(size): if board[x][y] ! player: continue for dx, dy in directions: # 只从连续段的起点统计避免重复计数 bx, by x - dx, y - dy if 0 bx size and 0 by size and board[bx][by] player: continue count 1 nx, ny x dx, y dy while 0 nx size and 0 ny size and board[nx][ny] player: count 1 nx dx ny dy open_left 0 bx size and 0 by size and board[bx][by] 0 open_right 0 nx size and 0 ny size and board[nx][ny] 0 player_total pattern_score(count, open_left, open_right) if player ai_player: ai_score player_total else: human_score player_total return ai_score - human_score这里有两个需要注意的点。第一每个棋型会被同一个方向的多次扫描重复计算三个连续的棋子从第一个棋子和第二个棋子出发都会数出“三连”。解决方法是加一个起点判断如果当前点的反方向相邻位置已经有同色棋子就跳过这个方向。第二check_winner里count 5不只判定五个因为无禁手规则下长连同样算赢这样写能兼容超长连的情况。3.3 Alpha-Beta搜索主循环搜索的主循环是AI决策的核心。maximizing_player为True时当前是AI决策取最大分为False时是对手决策取最小分。每次尝试落子后要立刻撤销保证搜索分支之间互不影响。check_winner的判定必须写进搜索里因为“现在能赢”和“现在会输”的优先级高于评估函数给出的常规分数两个100000代表胜与负的硬编码值远大于任何常规局面分数。def alphabeta(board, depth, alpha, beta, maximizing_player): winner check_winner(board) if winner ! 0: return 100000 if winner 1 else -100000 if depth 0: return evaluate_board(board, AI_PLAYER) candidates get_candidates(board) if maximizing_player: best -float(inf) for x, y in candidates: board[x][y] 1 score alphabeta(board, depth - 1, alpha, beta, False) board[x][y] 0 best max(best, score) alpha max(alpha, score) if alpha beta: break return best else: best float(inf) for x, y in candidates: board[x][y] 2 score alphabeta(board, depth - 1, alpha, beta, True) board[x][y] 0 best min(best, score) beta min(beta, score) if alpha beta: break return best再套一个最外层函数选出落子坐标def ai_move(board, depth4): candidates get_candidates(board) best_score -float(inf) best_move candidates[0] for x, y in candidates: board[x][y] AI_PLAYER score alphabeta(board, depth - 1, -float(inf), float(inf), False) board[x][y] 0 if score best_score: best_score score best_move (x, y) return best_moveai_move把所有候选点作为AI的第一步尝试逐个落子后递归搜索最后返回分数最高的点。注意初始调用时alpha和beta要分别设置为正负无穷大这样剪枝边界完全由搜索过程动态收紧。3.4 命令行对弈主循环def play(): board [[0 for _ in range(15)] for _ in range(15)] while True: x, y map(int, input(你的落子(x y): ).split()) if board[x][y] ! 0: print(该位置已有棋子) continue board[x][y] 2 if check_winner(board) 2: print(你赢了) break move ai_move(board, depth4) print(AI落子:, move) board[move[0]][move[1]] 1 if check_winner(board) 1: print(AI赢了) break这里人类执白先手AI执黑后手。input接收空格分隔的两个坐标没有处理非数字输入如果要做成可展示的图形界面可以在这个循环的基础上套一层pygame或tkinter把input替换成鼠标点击事件。命令行版本的价值是核心逻辑清晰、容易调试也方便在报告附录里贴核心代码。4. 参数调优与排错让AI从“会下”到“会赢”4.1 搜索深度与响应时间的取舍深度是第一个要调的参数。深度2时AI只看眼前一两步经常被冲四反复牵制深度3开始能主动防守深度4以上棋力明显提升但每家机器能承受的深度不同。实测参考如下。搜索深度候选点约30个时单步耗时棋力表现2 0.05s只会堵不进攻3约0.1s~0.3s能成活三不会防双三4约0.3s~1.5s主动做棋能防常见简单杀法5约2s~8s配合候选点裁剪可压到1s内耗时还和候选点数量强相关。如果只取周围一格候选点降到10个左右同深度速度快3倍代价是偶尔漏掉关键落子周围两格是课程报告里最常见的平衡点。超过5秒就要考虑裁剪候选点或者加置换表而不是继续加深度。4.2 候选点裁剪用落子聚集性降低分支因子实战中常用的裁剪方式是给候选点加一个距离权重离最近棋子越近的点优先级越高。搜索前先按优先级排序只取前N个点进入递归。这个N就是第二个必调参数。def get_candidates(board, max_count24): candidates set() for x in range(15): for y in range(15): if board[x][y] ! 0: for dx in (-2, -1, 0, 1, 2): for dy in (-2, -1, 0, 1, 2): nx, ny x dx, y dy if 0 nx 15 and 0 ny 15 and board[nx][ny] 0: candidates.add((nx, ny)) def near_score(pos): x, y pos return sum(1 for dx in range(-1, 2) for dy in range(-1, 2) if 0 xdx 15 and 0 ydy 15 and board[xdx][ydy] ! 0) return sorted(candidates, keynear_score, reverseTrue)[:max_count]near_score统计候选点周围一格内已有棋子的数量棋子越密集说明越可能形成战斗越优先搜索。max_count设为16到24能在不损失太多棋力的前提下把4层搜索时间压到1秒内。注意裁剪发生在搜索之前被裁掉的点即使藏着妙手也永远不会被搜索看到这是速度换棋力的直接代价。报告里可以做一组对比实验max_count30与max_count16对弈20盘记录胜负和平均搜索时间。4.3 评估权重是棋力上限的决定因素代码里的棋型分表就是权重。权重配比可以先成比例再微调活四和冲四的分数要比活三高一个数量级因为AI必须首先保证自己不败然后才是制造威胁。防守方的评估里对方棋型的扣分要略大于自己棋型的加分否则AI偏向进攻而容易漏掉对手的冲四。常见做法是给防守分乘一个系数比如1.1到1.2。很多“AI很菜”的课程设计问题不在搜索深度而在评估函数没有给“对方活三”足够的负分。调试时可以让AI自己跟自己对弈几十盘观察它是否总在同一个类型的局面下输棋。如果总是漏防就加大对应棋型的负权重。4.4 三个必须排掉的常见坑第一个坑是胜负判定方向不全。检查棋型时只查了横和竖漏了两个对角线结果AI反复在斜线上下棋但从不去拦斜线。排查方法是写个单测把五连棋型分别放在四个方向各测一遍。第二个坑是搜索的落子没有撤销。常见表现是AI落一步后棋盘上多出好几个AI的棋子越到深层越乱。每次递归前后必须成对出现board[x][y]player和board[x][y]0。第三个坑是评估函数符号不一致。比如AI方和对手方得分方向都是正的Minimax的min层实际在取最大值。调试方法是在一个简单局面下手算期望分数然后打印evaluate_board的返回值和手算结果对比。python3 -m unittest test_five_in_row.py用单测先锁住棋盘、胜负、评估三个模块的正确性再调参数不然调参时根本分不清算得慢还是算得错。提示参数调优之前务必先跑通单测。评估函数如果方向漏了一半后面所有实验数据都没意义。5. 再快一步置换表、启发式排序与答辩数据准备Alpha-Beta剪枝虽然高效但同一个局面在搜索树里会被反复评估多次——层数不同、路径不同局面却是相同的。可以用一个字典把局面哈希映射到分数下次遇到直接查表这就是置换表。在小棋盘上效果非常明显搜索节点数通常能下降40%到60%。代码上只需在alphabeta函数开头加一次查表、返回前加一次存表transposition {} def alphabeta(board, depth, alpha, beta, maximizing_player): key (tuple(tuple(row) for row in board), depth, alpha, beta, maximizing_player) if key in transposition: return transposition[key] # ...原有搜索逻辑... transposition[key] best return bestkey里带上alpha和beta会降低命中率但结果更安全课程设计里建议带。另一个提速技巧是走子顺序搜索前先对每个候选点做粗评分按粗评分降序进入搜索这样剪枝触发更早。实现上就是把get_candidates末尾的排序函数从near_score换成更粗略的“周围棋子数乘一个小权重”效果相似但排序成本更低。答辩报告的实验部分建议准备三组数据固定深度与不同max_count的耗时对比、不同评估权重配置下AI自对弈的胜率、有置换表与无置换表的搜索节点数对比。画两张折线图加一张表就足够支撑结论重点说明“评估函数比搜索深度更影响棋力”这个反直觉结论。展示环节先让AI和老师下几步再用同样的局面分别跑剪枝前后的版本当场打印搜索节点数。这份五子棋课程设计的功夫八分在评估函数和参数取舍二分在代码组织别让报告变成所有代码的罗列。本文还有配套的精品资源点击获取
返回列表