基於剪枝對抗搜尋的井子棋報告 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,也就是和電腦下,這樣,你需要兩個函式,乙個是給步驟,你自...