基於剪枝的對抗性搜尋的井字棋

2021-06-26 22:52:08 字數 2427 閱讀 9031

基於剪枝對抗搜尋的井子棋報告 1.

問題 井字棋,英文名叫

tic-tac-toe

,是一種在

3*3格仔上進行的連珠遊戲,和五子棋比較類似,由於棋盤一般不畫邊框,格線排成井字故得名。只要一方的三個棋子連城一條線,就算勝出。

玩過這個遊戲的人大都會發現,如果兩個玩家都作出最好的選擇,這個遊戲是一定會平局的。所以,井字棋最常使用是作為兒童遊戲。雖然這個遊戲看上去很簡單,但是它的整個過程卻複雜得多。從理論上講,

「井字棋

」一共可能有

19683

種現象和

362880

種過程。(如果不把追求獲勝的判定算進去的話)當獲勝導致遊戲結束時,就只剩下

255168

種可能過程。假設其中

x都是先手:那麼其中

131184

次將為x

獲勝,46080

次為平局,

77904次為o

獲勝。而當無視o和

x的序列並消除所有對稱的情況,就只剩下

138種可能的結果了,其中

91次是由

x獲勝,

44次是由

o獲勝,只有

3個獨特的情況下才產生平局。

所以對於下棋著兩方都是人的時候比較簡單,但如果要讓電腦實現一方就需要乙個比較優良的演算法。

2.原理

圖 1我們用min代表先手方。演算法的原理是把下棋的所有可能建成一棵多叉樹,就像圖1所示。我們遍歷這棵多叉樹,首先計算葉子節點的啟發值,再回溯計算各個節點的啟發值。

對於葉子節點啟發值的計算我們採用這種方法,用棋盤空的地方全部填成x所獲勝的數目減去棋盤空的地方全部填成o所獲勝的數目。對於其他非葉子節點的啟發值用下面的函式來求。

最後根據啟發值,max選啟發值最大的落子方式,min選擇啟發值最小的落子方式。其中用到了兩種剪枝的技巧。

首先是對稱剪枝,如下圖:

圖2其實x走這幾個位置都是一樣的,因為它是對稱的,我們用1,2,3,4,5,6,7,8 ,9分別代表落子的可能。

表1用a代表上下對稱,用b代表左右對稱,用c代表\對稱,用d代表/對稱。如果是a的情況可能走棋的位置有並且是在這些位置沒有棋子的前提下。同理得到下表:

a1,2,3,4,5,6

b1,2,4,5,7,8

c1,2,3,5,6,9

d1,2,3,4,5,7

ab1,2,4,5

cd1,2,3,5

abcd

1,2,5

表2然後在樹的深度遍歷的過程中也用到了αβ剪枝原理如下:

3.演算法流程

注:電腦作為後手。

1>    玩家a下一子;

2>    電腦計算a是否獲勝,獲勝則結束不獲勝繼續;

3>    電腦判斷是否需要防守(就是a有沒有已經兩子連成一線),若需要防守就在防守位置落子然後判斷電腦是否獲勝,獲勝結束否則跳轉1>,如果不需防守就繼續;

4>    根據已走的資訊建立一棵搜尋樹(要用到對稱剪枝),然後深度遍歷這棵樹並計算各個節點的啟發值(要用到αβ剪枝)。選擇啟發值最大的位置落子,

判斷電腦是否獲勝,若獲勝則結束,否則跳轉1>;

4.演算法實現說明

在程式的編寫過程中發現,難點是在樹的建立上,要考慮到乙個節點的子節點是不確定個數的,還有節點只需要確定的深度,以及之前所說的對稱剪枝。

首先定義乙個長度為9的全域性陣列來記錄雙方走棋的過程及當前的狀態,我們參考表1,比如1,5,6,7.就是指先手下1的位置,電腦下5的位置,然後先手下6的位置,電腦下7的位置,目前下了四步棋。

對於建圖的時候,把每個節點定義成如下的資料結構。

typedef struct node

int step[10];    //儲存下棋過程的資訊

struct  node *next[10];   //指向節點的指標

int qifazhi;             //此節點的啟發值

int stepzhi;             //此節點最後選擇下的位置

} node,*nodet;

節點指標選了10個,其中next[0]是沒有意義的,然後next[1]——next[9]對應1——9的位置。當然建立搜尋樹的時候不會用到所有的next[i],用不到的就賦null;

建圖用的是遞迴方法,偽**如下;

creat(nodet &a,int *step,int p)

if   p<=0   //p的作用是為了控制建立搜尋樹的深度

退出p=p-1;

a=newnode;

給a->step[i]賦值

for(i=0;i<10;i++)

a->next[i]=null;

for(i=1;i<=9;i++)

參考表1與陣列step;(用對成剪枝)

if i這一點可以落子

5.結果展示

因為這個問題不複雜,並且計算機計算能力強大,最多下成平局,先手不會獲勝。

C 實現的基於 剪枝演算法的井字棋遊戲

井字棋 遊戲 又叫 三子棋 是一款十分經典的益智小遊戲,操作簡單,娛樂性強。兩個玩家,乙個打圈 o 乙個打叉 x 輪流在3乘3的格上打自己的符號,最先以橫 直 斜連成一線則為勝。如果雙方都下得正確無誤,將得和局。這種遊戲實際上是由第一位玩家所控制,第一位玩家是攻,第二位玩家是守。這種遊戲的變化簡單,...

井字棋決策的Cpp描述

本文在vs17環境下對井字棋的決策演算法做出了部分解釋 博弈雙方的棋權,運用了布林型變數 通過對布林型變數的次序互換實現雙方函式的交替執行 char ww,x,o int qz bool h static int number 0 先手的初始化 cout 開始 ww if ww x else if ...

三子棋,又稱井字棋的實現

編寫乙個三子棋你需要知道的是需要哪些實現步驟 首先,你需要有乙個棋盤,這樣你就需要編寫乙個初始化棋盤的函式,並能夠將棋盤列印出來 棋盤麼,就像這種樣子 這個採用二維陣列就可以來實現了 接著,你可以選擇實現pvp或者pve,這裡我實現的是pve,也就是和電腦下,這樣,你需要兩個函式,乙個是給步驟,你自...