降智警告:本人为编程新手,遵守面向CSDN编程原则,代码架构具有个人特色,仅供参考
前言:课程期末项目写了个黑白棋,所以放假写个五子棋,作为今年的总结
(该五子棋仅涉及最基础的规则,轮留下,连五赢)
一.最终效果
功能:规定玩家先下,结束时展示结果。restart按钮会变色,鼠标在按钮上时颜色变深。按下restart按钮可以重新开局。
二.AI实现
1.思路
①棋局
使用二维列表表示棋盘,0表示空位,1表示玩家棋子(黑),2表示电脑棋子(白)
②局面评估
为了减少计算量,我将电脑当前的可下位置限制为,非空位置的八邻域中的空位。然后对每一个可下位置进行评估。
我们需要为每一个当前可下位置进行评估得到一个分数,然后取分数最高的位置作为本次的下子位置。
在评估时,我们需要考虑该位置对于本方的重要性,同时也要考虑该位置对敌方的重要性,所以我们需要将两个评估所得分数相加作为该位置的最终分数。
③评估方法
首先对于每一个需要评估的位置,先将电脑棋子放到该位置,然后分别取该位置的横,竖,左斜,右斜四个方向上的线上的所有位置作为四条棋线,对每个位置的四条棋线进行评估。然后反转棋盘,将电脑的棋子换为玩家棋子,玩家棋子换为电脑棋子,再对该位置进行评估。两个评估分数相加即为该位置的最终分数。
五子棋中有许多棋型,如连五,活四,冲四,活三等,为每一个棋型设置一个分数,每个位置的四条棋线中存在的所有棋型的分数相加,得到该位置的一方评估分数。
参考资料:棋型参考, 棋型评分参考
2.代码实现
这里仅给出根据当前棋局给出最佳下子位置的实现,项目完整代码在我的github
①获取当前棋局所有可下位置(限制条件下)
def get_charge_pos(board) :
#八邻域
way = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
ret = []
for i in range(15) :
for j in range(15) :
#当前位置不为空位
if board[i][j] != 0 :
#搜索八邻域
for w in way :
pos = (i + w[0], j + w[1])
if pos[0] not in range(15) or pos[1] not in range(15) :
continue
if (board[pos[0]][pos[1]] == 0) and (pos not in ret) :
ret.append(pos)
return ret
②获取当前位置棋线
(将一条棋线上的位置整理为一个列表,将列表转化为字符串并进行处理,最终只剩下包含0,1,2三个数字的字符串,返回包含四条棋线代表的字符串的列表)
def get_score(pos, board) :
#获取棋盘副本,在当前位置放入棋子
ori = copy.deepcopy(board)
ori[pos[0]][pos[1]] = 2
#横,竖
h = str(ori[pos[0]])[1:-1].replace(',', '').replace(' ', '')
s = str([ori[i][pos[1]] for i in range(15)])[1:-1].replace(',', '').replace(' ', '')
#左斜
lx = str([ori[i][i - pos[0] + pos[1]] for i in range(15) if (i - pos[0] + pos[1]) in range(15)])[1:-1].replace(',', '').replace(' ', '')
#右斜
rx = str([ori[i][pos[0] + pos[1] - i] for i in range(15) if (pos[0] + pos[1] - i) in range(15)])[1:-1].replace(',', '').replace(' ', '')
return get_line_score([h, s, lx, rx])
③棋线评分
(由于棋型过多,判断比较繁琐,截取部分代码作为示例)
注意:由于棋线上对于棋型是单向查找的,所以对于不对称的棋型要反转棋型再次查找
④主函数
实现对每个可下位置进行两次评分得到最终分数后,返回分数最高的位置
def get_pos(board) :
pos = get_charge_pos(board)
get = (-1, -1)
score = -float("inf")
for p in pos :
#反转棋盘
o_board = opp_board(board)
s = get_score(p, board) + get_score(p, o_board)
if s > score :
get = p
score = s
return get
三.总结
这个项目的界面实现就不放代码了,有兴趣的可以看一下(项目代码)
使用pygame来构造界面,调用了棋盘,黑白棋子,restart按钮共五张图片,如果修改图片的话需要更改显示的具体参数
因为界面用到好几张图,如果转化为py文件一起打包的话,最后的exe会很大,所以就放在一个文件夹里直接调用了,顺便把程序框图标的ico也放进去了
最终的效果还是不错的,AI的胜率挺高的,也用到了今年所学的东西,也是今年的最后一个项目了。