python-五子棋-AI

降智警告:本人为编程新手,遵守面向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的胜率挺高的,也用到了今年所学的东西,也是今年的最后一个项目了。