TicTacToe(井字棋)的演算法——不比人的智商低的AI
何為TicTacToe?
TicTacToe也俗稱井字棋或三連棋(兩人輪流在一有九格方盤上劃加字或圓圈, 誰先把三個同一記號排成橫線、直線、斜線, 即是勝者),男女老少皆宜的入門級棋類遊戲。
關於遊戲的實現方式,比較簡單,在這就不多說了。直接談一談如何寫出在TicTacToe遊戲中不輸給人類棋手的電腦。
想要寫出遊戲AI,第一步是自己要會玩遊戲,有自己的策略。第二部才是告訴我們的AI如何實現這樣的策略。我們在玩的時候發現能儘可能佔據有利的位置會幫助我們快速贏下比賽,玩遊戲還能發現的一點就是一定要防住對面即將連成一條線的一路,還有很多,很多。這些特徵可以說明,遊戲中不同情況下,盤面的每一個位置似乎有著不同的重要性,我們完全可以利用這一點來指導我們的AI去馳騁沙場。(我一開始打算仔細判斷每一種情況,比如雙二單二什麼的,結果不但程式碼量大而且效果和這樣的思路沒什麼區別)
也就是說,給與AI一個非常重要的變數(一個3*3的陣列),表示每一個位置的權值,也就是該位置的重要性的體現。比賽中第一重要的當然是自己連成一條線,這可以賦予最高的權值。而且,這個權值應該大到什麼地步呢?當然應該大到無論其他什麼情況發生都以此為第一要素的地步。所以我們可以在AI檢查出自己能成一條線的位置時,給這個位置賦值10000,代表這個位置至高無上的地位。
第二重要的則是防止對手成一條線,此舉的重要性,可謂一人之下萬人之上,就在有這種情況的時候賦予這個位置1000的值吧。
其他需要賦值的三種情況是:1.某條線僅僅有己方棋子一枚;2.某條線僅僅有敵方棋子一枚;3.某條線上空空如也。如果是我們的AI先手,我認為第一條的權重大於第二條大於第三條。而AI後手則2>3>1。
因為玩過Tictactoe的朋友一定知道,這是一個先手贏不了,後手輸不了的遊戲。但是作為後手一旦失誤就會輸棋,先手若能抓住後手的失誤則能贏棋。數年前中國五子棋第一人那威曾經說過“先手要攻,後手要守”。雖然是說五子棋,不過Tictactoe實際上就是三字棋,與五子棋的區別僅僅是少了很多很多的變化而已。所以我們需要給我們的AI貫徹這樣的思想:先手要積極進攻,後手要小心放手。好了,AI的思路基本上就是這樣了,下面貼上核心部分的程式碼(java)
注:遊戲的地圖儲存在一個叫net的陣列裡,每一個位置值為0表示沒有棋,1表示為X棋,2表示為O棋
int[][] net=new int[4][4];//0 means null;1 means X;2 means O;
int level[][]=new int[4][4];//越大優先順序越高
IOHelper h=new IOHelper();
int o2=10000;//一個己方活二權值
int x2=1000;//一個對方活二權值
int x=10;//一個對方活一權值
int o=6;//一個己方活一權值
int nothing=4;//一個空行權值
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
if(net[i][j]!=0)level[i][j]=0;
else{
//橫豎兩條,每人都有
//自己活二,,220
if(((net[0][j]+net[1][j]+net[2][j])==4)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==-1)
level[i][j]=level[i][j]+o2;
if(((net[i][0]+net[i][1]+net[i][2])==4)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==-1)
level[i][j]=level[i][j]+o2;
//對方活二,110
if(((net[0][j]+net[1][j]+net[2][j])==2)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==0)
level[i][j]=level[i][j]+x2;
if(((net[i][0]+net[i][1]+net[i][2])==2)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==0)
level[i][j]=level[i][j]+x2;
//單個X,100
if(((net[0][j]+net[1][j]+net[2][j])==1)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==0)
level[i][j]=level[i][j]+x;
if(((net[i][0]+net[i][1]+net[i][2])==1)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==0)
level[i][j]=level[i][j]+x;
//單個O,200
if(((net[0][j]+net[1][j]+net[2][j])==2)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==1)
level[i][j]=level[i][j]+o;
if(((net[i][0]+net[i][1]+net[i][2])==2)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==1)
level[i][j]=level[i][j]+o;
//空行,000
if(((net[0][j]+net[1][j]+net[2][j])==0)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==-1)
level[i][j]=level[i][j]+nothing;
if(((net[i][0]+net[i][1]+net[i][2])==0)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==-1)
level[i][j]=level[i][j]+nothing;
//分情況
//主對角線
if((i==0&&j==0)||(i==2&&j==2)||(i==1&&j==1)){
//己方活二
if(((net[0][0]+net[1][1]+net[2][2])==4)&&(net[0][0]*net[1][1]*net[2][2])==0&&
((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==-1)
level[i][j]=level[i][j]+o2;
//對方活二
if(((net[0][0]+net[1][1]+net[2][2])==2)&&(net[0][0]*net[1][1]*net[2][2])==0&&
((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==0)
level[i][j]=level[i][j]+x2;
//單個X
if(((net[0][0]+net[1][1]+net[2][2])==1)&&(net[0][0]*net[1][1]*net[2][2])==0&&
((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==0)
level[i][j]=level[i][j]+x;
//單個O
if(((net[0][0]+net[1][1]+net[2][2])==2)&&(net[0][0]*net[1][1]*net[2][2])==0&&
((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==1)
level[i][j]=level[i][j]+o;
//空行,000
if(((net[0][0]+net[1][1]+net[2][2])==0)&&(net[0][0]*net[1][1]*net[2][2])==0&&
((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==-1)
level[i][j]=level[i][j]+nothing;
}
//副對角線
if((i==0&&j==2)||(i==2&&j==0)||(i==1&&j==1)){
//己方活二
if(((net[0][2]+net[1][1]+net[2][0])==4)&&(net[0][2]*net[1][1]*net[2][0])==0&&
((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==-1)
level[i][j]=level[i][j]+o2;
//對方活二
if(((net[0][2]+net[1][1]+net[2][0])==2)&&(net[0][2]*net[1][1]*net[2][0])==0&&
((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==0)
level[i][j]=level[i][j]+x2;
//單個X
if(((net[0][2]+net[1][1]+net[2][0])==1)&&(net[0][2]*net[1][1]*net[2][0])==0&&
((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==0)
level[i][j]=level[i][j]+x;
//單個O
if(((net[0][2]+net[1][1]+net[2][0])==2)&&(net[0][2]*net[1][1]*net[2][0])==0&&
((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==1)
level[i][j]=level[i][j]+o;
//空行,000
if(((net[0][2]+net[1][1]+net[2][0])==0)&&(net[0][2]*net[1][1]*net[2][0])==0&&
((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==-1)
level[i][j]=level[i][j]+nothing;
}//the end of for if
}//the end of if
}//the end of for j
}//the end of for j
//尋找最大權值的位置
int maxi = 0,maxj = 0,temp = 0;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
if(level[i][j]>temp)
{
temp=level[i][j];
maxi=i;
maxj=j;
}
System.out.println("net[i][j]="+net[i][j]);
System.out.println("level[i][j]="+level[i][j]);
}
}
覺得作者不容易的話請頂一個(*^__^*) 謝謝
相關文章
- 採用α-β演算法實現井字棋遊戲演算法遊戲
- Python程式碼 | 井字棋Python
- 基於落點打分的井字棋智慧下棋演算法(C語言實現)演算法C語言
- [CareerCup] 17.2 Tic Tac Toe 井字棋遊戲遊戲
- Python:用海龜實現井字棋Python
- 用洛書幻方對抗人類玩家的井字棋程式
- 用C語言編寫小遊戲——“井字棋”C語言遊戲
- Minimax 和 Alpha-beta 剪枝演算法簡介,及以此實現的井字棋遊戲(Tic-tac-toe)演算法遊戲
- 《棋隱》的註冊演算法 (19千字)演算法
- 低智商者更傾向強烈的音樂?
- “井蓋吃人”的屢見不鮮,智慧井蓋來了!
- 強化學習實戰 | 自定義Gym環境之井字棋強化學習
- 二營長,快掏個CSS出來給我畫個井字棋遊戲CSS遊戲
- 強化學習實戰 | 表格型Q-Learning玩井字棋(二)強化學習
- 強化學習實戰 | 表格型Q-Learning玩井字棋(一)強化學習
- 五子棋AI演算法(一)AI演算法
- 用 Python 做個簡單的井字遊戲Python遊戲
- 如何公平分配?AI比人更懂AI
- 關於五子棋電腦AI演算法的一些思考AI演算法
- 強化學習實戰 | 表格型Q-Learning玩井字棋(四)遊戲時間強化學習遊戲
- 有道圍棋 AI:智慧匹配兒童棋力的良師益友AI
- 圖的遍歷演算法-馬遍歷棋盤演算法
- 揭祕井井有條的流水線(ZooKeeper 原理篇)
- Java學習筆記(七):五子棋AI演算法Java筆記AI演算法
- 封殺這個公式,AI智商將為零公式AI
- 【AI 演算法評測】BERT 對 NLP 效果的改善,不負眾望!AI演算法
- 用 go 寫的五子棋預測演算法Go演算法
- IT職場:如何利用5S保持工作場所的井井有條?
- Gartner L2:品牌數字智商TOP 10
- Waymo:研究稱Waymo Driver自動駕駛汽車事故率比人類司機低85%自動駕駛
- 字尾表示式的求值的演算法演算法
- 國盾量子:高市值和低營收的不對等營收
- AI智商排名:Claude-3首次突破100AI
- 【重磅】AlphaZero煉成最強通用棋類AI,DeepMind強化學習演算法8小時完爆人類棋類遊戲AI強化學習演算法遊戲
- 拼圖遊戲和它的AI演算法遊戲AI演算法
- 阿里巴巴有一群全年無休從不領工資的高智商員工阿里
- 8月女性向遊戲井噴!評分有高有低原因何在?遊戲
- 騰訊圍棋AI技術PhoenixGo正式開源AIGo