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的勝率挺高的,也用到了今年所學的東西,也是今年的最後一個項目了。