五子棋(人機)-極大極小值搜尋演算法
從人落子開始到出現勝負或者和局,之間所落的子,構成了一個解。而解空間就是一個樹,解就是這解空間中的一條路徑。只不過這個解空間是電腦的選擇和人的選擇共同構成的(奇數層是電腦(因為輪到電腦落子麼),偶數層是人)。
極大極小值搜尋演算法,來搜尋(回溯)這個解空間:它假設人和電腦都是極其聰明的,他們都會選擇出最優的一步。
但是搜尋整棵樹是不現實的,16*16!指數級,所以只回溯n步,即這個AI考慮的是N步之內的最優解,它是考慮了n步之後的情況的
-------------------------------------------------------------------我是分割線-------------------------------------------------------------------------
假設玩家執黑子,電腦執白子
評估函式:評估函式將對棋盤上的所有黑子做出評分(連成線的等級越高,數量越多,估分就越高)作scorehumber;也將對棋盤上的所有白子做出評分(連成線的等級越高,數量越多,估分就越高)作scorecomputer。然後評估值為【scorecoputer-scorehumber】。它將認為,這個評估值越高,整個局面對電腦越有利;這個評估值越低,整個局面對玩家越有利。
max-min搜尋最優解,即向後回溯depth步,輪到電腦時,電腦做出最有利於自己的選擇(選擇最高的評估值),輪到玩家時,玩家做出最有利於自己的選擇(選擇最低的評估值)。(他們的選擇將被推遲,葉子節點先做出選擇,然後層層往上推出那一層的最優解
-------------------------------------------------------------------我是分割線-------------------------------------------------------------------------int MinMax(int depth) { // 函式的評估都是以白方的角度來評估的 if (SideToMove() == WHITE) { // 白方是“最大”者 return Max(depth); } else { // 黑方是“最小”者 return Min(depth); } } int Max(int depth) { int best = -INFINITY; if (depth <= 0) { return Evaluate(); } GenerateLegalMoves(); while (MovesLeft()) { MakeNextMove(); val = Min(depth - 1); UnmakeMove(); if (val > best) { best = val; } } return best; } int Min(int depth) { int best = INFINITY; // 注意這裡不同於“最大”演算法 if (depth <= 0) { return Evaluate(); } GenerateLegalMoves(); while (MovesLeft()) { MakeNextMove(); val = Max(depth - 1); UnmakeMove(); if (val < best) { // 注意這裡不同於“最大”演算法 best = val; } } return best; }
計分板
成五 | +100000 |
活四 | +10000 |
死四 | +1000 |
活三 | +1000 |
死三 | +100 |
活二 | +100 |
死二 | +10 |
活一 | +10 |
int max_noalphabeta(int depth,int i1,int i2);//輪到電腦走步時,電腦作的選擇
int min_noalphabeta(int depth,int i1,int i2);//輪到人走步時,人作的選擇
void generatepoint(vector< pair<int,int> > &v);//產生空子序列
int scoretable(int number,int empty1);//積分表
int countscore(vector<int> n,int turn); //算單個數組分
bool hasne(int x,int y);//周圍是否有子存在,無子的就加考慮
bool hasne(int x,int y)//空子只算旁邊有子的
{
int i,j;
for(i=(x-3>0?x-3:0);i<=x+3&&i<16;++i)
for(j=(y-3>0?y-3:0);j<=y+3&&j<16;++j)
if(i!=0||j!=0)
if(pos[i][j]!=0)
return true;
return false;
}
void generatepoint(vector< pair<int,int> > &v)//產生空子序列
{
for(int i=0;i<16;++i)
for(int j=0;j<16;++j)
if(pos[i][j]==0&&hasne(i,j))
{
pair<int,int> p;
p.first=i;
p.second=j;
v.push_back(p);
}
}
//按照成五100000、活四10000、活三1000、活二100、活一10、死四1000、死三100、死二10的規則
//給棋盤上的所有棋子打分,之和為電腦的單方面得分scorecomputer,然後對玩家的棋子同樣打分,之和為scorehumber
//scoreComputer-scorehumber即為當前局勢的總分數
int scoretable(int number,int empty1)//計分板
{
if(number>=5) return 100000;
else if(number==4)
{
if(empty1==2) return 10000;
else if(empty1==1) return 1000;
}
else if(number==3)
{
if(empty1==2) return 1000;
else if(empty1==1) return 100;
}
else if(number==2)
{
if(empty1==2) return 100;
else if(empty1==1) return 10;
}
else if(number==1&&empty1==2) return 10;
return 0;
}
int countscore(vector<int> n,int turn)//正斜線、反斜線、橫、豎,均轉成一維陣列來計算
{
int scoretmp=0;
int len=n.size();
int empty1=0;
int number=0;
if(n[0]==0) ++empty1;
else if(n[0]==turn) ++number;
int i=1;
while(i<len)
{
if(n[i]==turn) ++number;
else if(n[i]==0)
{
if(number==0) empty1=1;
else
{
scoretmp+=scoretable(number,empty1+1);
empty1=1;
number=0;
}
}
else
{
scoretmp+=scoretable(number,empty1);
empty1=0;
number=0;
}
++i;
}
scoretmp+=scoretable(number,empty1);
return scoretmp;
}
int evaluate_minmax_noalphabeta()//評估函式,評估局勢
{
int scorecomputer=0;
int scorehumber=0;
//橫排們
for(int i=0;i<16;++i)
{
vector<int> n;
for(int j=0;j<16;++j)
n.push_back(pos[i][j]);
scorecomputer+=countscore(n,2);
scorehumber+=countscore(n,1);
n.clear();
}
//豎排們
for(int j=0;j<16;++j)
{
vector<int> n;
for(int i=0;i<16;++i)
n.push_back(pos[i][j]);
scorecomputer+=countscore(n,2);
scorehumber+=countscore(n,1);
n.clear();
}
//上半正斜線們
for(int i=0;i<16;++i)
{
int x,y;
vector<int> n;
for(x=i,y=0;x<16&&y<16;++x,++y)
n.push_back(pos[y][x]);
scorecomputer+=countscore(n,2);
scorehumber+=countscore(n,1);
n.clear();
}
//下半正斜線們
for(int j=1;j<16;++j)
{
int x,y;
vector<int> n;
for(x=0,y=j;y<16&&x<16;++x,++y)
n.push_back(pos[y][x]);
scorecomputer+=countscore(n,2);
scorehumber+=countscore(n,1);
n.clear();
}
//上半反斜線們
for(int i=0;i<16;++i)
{
vector<int> n;
int x,y;
for(y=i,x=0;y>=0&&x<16;--y,++x)
n.push_back(pos[y][x]);
scorecomputer+=countscore(n,2);
scorehumber+=countscore(n,1);
n.clear();
}
//下半反斜線們
for(int j=1;j<16;++j)
{
vector<int> n;
int x,y;
for(y=j,x=15;y<16&&x>=0;++y,--x)
n.push_back(pos[x][y]);
scorecomputer+=countscore(n,2);
scorehumber+=countscore(n,1);
n.clear();
}
return scorecomputer-scorehumber;
}
int min_noalphabeta(int depth,int i1,int i2)//玩家落子時 //當min(人)走步時,人的最好情況
{
int res=evaluate_minmax_noalphabeta();
Chess cc;
cc.chess_isover(i1,i2,2);
if(isover!=0||depth<=0)
{
isover=0;
return res;
}
vector< pair<int,int> > v;
generatepoint(v);
int len=v.size();
int best=INT_MAX;
for(int i=0;i<len;++i)
{
pos[v[i].first][v[i].second]=1;
int tmp=max_noalphabeta(depth-1,v[i].first,v[i].second);
if(tmp<best) best=tmp;//玩家落子時選擇最有利自己的局面,將推遲,葉子節點做出選擇後,層層往上推
pos[v[i].first][v[i].second]=0;
}
return best;
}
int max_noalphabeta(int depth,int i1,int i2) //當max(電腦)走步時,max(電腦)應該考慮最好的情況
{
int res=evaluate_minmax_noalphabeta();
Chess cc;
cc.chess_isover(i1,i2,1);
if(isover!=0||depth<=0)
{
isover=0;
return res;
}
vector< pair<int,int> > v;
generatepoint(v);
int len=v.size();
int best=INT_MIN;
for(int i=0;i<len;++i)
{
pos[v[i].first][v[i].second]=2;
int tmp=min_noalphabeta(depth-1,v[i].first,v[i].second);
if(tmp>best) best=tmp;//電腦落子時,選擇最有利於自己的局面,將推遲
pos[v[i].first][v[i].second]=0;
}
return best;
}
void Chess::chess_ai_minmax_noalphabeta(int &x,int &y,int depth)//極大極小值演算法搜尋n步後的最優解
{
vector< pair<int,int> > v;
generatepoint(v);
int best=INT_MIN;
int len=v.size();
vector< pair<int,int> > v2;
for(int i=0;i<len;++i)
{
pos[v[i].first][v[i].second]=2; //選該子,將該子置白,防止後面遞迴時,再遞迴到
int tmp=min_noalphabeta(depth-1,v[i].first,v[i].second);
if(tmp==best)
v2.push_back(v[i]);
if(tmp>best)
{
best=tmp;
v2.clear();
v2.push_back(v[i]);
}
pos[v[i].first][v[i].second]=0; //假設完之後,該子需要重新置空,恢復原來的樣子
}
len=v2.size();
int i=(int)(rand()%len);
x=v2[i].first;
y=v2[i].second;
}
參考:
http://blog.csdn.net/lihongxun945/article/details/50625267
http://blog.csdn.net/kingkong1024/article/details/7639401
相關推薦
五子棋(人機)-極大極小值搜尋演算法
從人落子開始到出現勝負或者和局,之間所落的子,構成了一個解。而解空間就是一個樹,解就是這解空間中的一條路徑。只不過這個解空間是電腦的選擇和人的選擇共同構成的(奇數層是電腦(因為輪到電腦落子麼),偶數層是人)。 極大極小值搜尋演算法,來搜尋(回溯)這個解空間:它假設人和電腦都
QT五子棋專案詳解之四:AI人機對戰max-min極大極小值博弈演算法
不考慮博弈的演算法怎麼能算是AI呢?max-min極大極小值演算法就是考慮了博弈的演算法。來看一個簡單的例子在這個棋局中,電腦為白旗,白旗走哪一步更好呢,也許使用策略表會告訴你,應該衝4,但是衝4後,玩家就會連成4。這就是考慮了博弈之後,這一步棋就是敗局。這就是為什麼有max
極大極小值搜尋 + 剪枝
/* 題意: Alice和Bob玩遊戲,在一個4x4 的方格上 每個人每次選擇2x2的區域將裡面的四個值求和加到最後的分數當中(兩個人共用一個分數), 然後逆時針翻轉它們, Alice想要分數儘量大Bob想要分數儘量小 兩個人每次的選擇都是最優的,求最後的分數
201803-4棋局評估_極大極小值演算法_對抗搜尋(轉載)
問題描述 試題編號:201803-4 試題名稱:棋局評估 時間限制:1.0s 記憶體限制:256.0MB 問題描述:問題描述 Alice和Bob正在玩井字棋遊戲。 井字棋遊戲的規則很簡單
中國象棋人機對弈搜尋演算法學習-極大極小值,負極大值,alpha-beta演算法
極大極小值法 深度搜索(dfs)虛擬碼 /** 1。 p 為棋盤 2。 d 為規定的搜素最大深度,比如d層紅方,d-1層為黑方,d-2層為紅方...依此類推,可採用mod2來判斷當前是哪一方 4。評估棋盤的函式evaluation,當然需要看
POJ 1085 Triangle War(博弈,極大極小搜尋+alpha_beta剪枝)
題目:給出10個點,總共有18條邊,每次兩個人輪流加入一條邊,如果形成一個三角形,則三角形歸他所有,而且可以額外再走一步。最後三角形多的人勝 博弈問題 所謂的極大極小搜尋,其實就是搞個估價函式。然後主角肯定選個估價函式最大的,即對自己最有利的局面走。 而輪到對方的時候,
牛頓法求極大極小值
牛頓法至少有兩個應用方向,1、求方程的根,2、最優化。牛頓法涉及到方程求導,下面的討論均是在連續可微的前提下討論。 1、求解方程。 並不是所有的方程都有求根公式,或者求根公式很複雜,導致求解困難。利用牛頓法,可以迭代求解。 原理是利用泰勒公式,在x0處展開,且展開
極大極小搜索思想+(α/β)減枝 【轉自-----https://blog.csdn.net/hzk_cpp/article/details/79275772】
ima 基本 個數 博弈論 數字 這就是 pre -- 繼續 極大極小搜索,即minimax搜索算法,專門用來做博弈論的問題的暴力. 多被稱為對抗搜索算法. 這個搜索算法的基本思想就是分兩層,一層是先手,記為a,還有一層是後手,記為b. 這個搜索是認為這a與b的利益關
Mathematica 繪製二元函式極小值隨引數變化圖(二維)
問題來自群友,直接上程式碼 ListLinePlot[Labeled[{#[[2,1,2]],#[[2,2,2]]},NumberForm[#[[1]],3]]&/@Table[FindMinimum[{Cos[x]-Exp[x y],z+x^2+y^2<=8
梯度爆炸與梯度消失的原因以及解決方法,區域性極小值問題以及學習率問題(對SGD的改進)
梯度爆炸與梯度消失的原因:簡單地說,根據鏈式法則,如果每一層神經元對上一層的輸出的偏導乘上權重結果都小於1的話( ),那麼即使這個結果是0.99,在經過足夠多層傳播之後,誤差對輸入層的偏導會趨於0( )。下面是數學推導推導。假設網路輸出層中的第 個神經元輸出為,而要學習的目標
【BZOJ2669】區域性極小值(容斥原理+狀壓dp)
題意:有一個nn行mm列的整數矩陣,其中11到nmnm之間的每個整數恰好出現一次。如果一個格子比所有相鄰格子(相鄰是指有公共邊或公共頂點)都小,我們說這個格子是區域性極小值。給出所有區域性極小值的位置,你的任務是判斷有多少個可能的矩陣。(1<=n<=
MATLAB—一字棋(極大極小搜尋)
init.m %初始化棋盤狀態 function cur=init() cur=rand(3,3); %儲存當前棋盤的狀態 %計算機為先手時的初值,即均為0 for i=1:3 for j=1:3 cur(i,j)=0;
POJ 1085 Triangle War(極大極小搜尋+alpha-beta剪枝)
// // main.cpp // Richard // // Created by 邵金傑 on 16/8/29. // Copyright © 2016年 邵金傑. All rights reserved. // #include<iostream&g
動態規劃求解(添+號求最小值和問題)
案例提出:在一個n位整數a(只考慮正整數的情況)中插入r個加號,將它分成r+1個整數,找出一種加號的插入方法,使得這r+1個整數的和最小。 動態規劃設計要點:對於一般插入r個+號問題,採用列舉不適合。注意到插入r個+號是一個多階層決策問題,所以採用動態規劃 來求解是最適宜的
微信小程式(遊戲)----五子棋(總結)
思路分析 繪製棋盤: 計算橫線和豎線的起始、終結點座標,繪製棋盤網格; 棋盤交叉點座標: 計算每格寬高,迴圈儲存棋盤所有點座標,並初始化狀態為0,表示此位置沒有棋子,形成“棋盤座標
巧婦能為少米之炊(1)——Android下小內存下的生存之道
直接內存 -a 響應時間 分區 popu 身邊 執行 人的 算法 常常聽到身邊用安卓的朋友抱怨手機卡頓,內存動不動就快沒了。而Google聲稱在512M的內存下也能流暢執行Android 4.4。究竟它做了什麽? 總結一下它主要做了四件事: 1.優化內核,使用Activ
HTML5基礎小結(二)——標簽小例
加速 支持 ide oat enter controls 畫圓 side tint 隨篇博客的思維導圖。繼續: 二。看下標簽的使用,這裏看幾個小樣例(效果圖不再給出): 1。結構標簽的使用,這裏來看一個頁面的布局:<!doc
計蒜客 2017 NOIP 提高組模擬賽(四)Day1 T1 小X的質數 線性篩素數
範圍 線性篩 mat 需要 接下來 包含 能夠 數字 bottom 小 X 是一位熱愛數學的男孩子,在茫茫的數字中,他對質數更有一種獨特的情感。小 X 認為,質數是一切自然數起源的地方。 在小 X 的認知裏,質數是除了本身和 1 以外,沒有其他因數的數字。 但由於小 X
微信小程序(7)--微信小程序連續旋轉動畫
creat position ref step nsh bsp debug pan anim 微信小程序連續旋轉動畫 https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-animation.html <view ani
2669[cqoi2012]局部極小值 容斥+狀壓dp
方案 容斥 ng- scrip pad iostream set mes scan 2669: [cqoi2012]局部極小值Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 774 Solved: 411[Submit][St