赞
踩
三子棋的总局面数约为 549 , 946 549,946 549,946,因此我们先以它为基础开始编写。
注意:节点上方的 [ a / b ] [a/b] [a/b]表示某个决策下一步还有 b b b种可能,还剩下 a a a种未显示
这里,我们先来讨论某个局面下一步的决策。
源码见文末
T
e
s
t
1
Test 1
Test1 源码
关注左上红下
(
1
,
3
)
(1,3)
(1,3)的节点,可以发现下一步蓝色可以获胜,那么红方必然不能下
(
1
,
3
)
(1,3)
(1,3)。
同理,红下
(
3
,
2
)
&
(
1
,
2
)
(3,2)\&(1,2)
(3,2)&(1,2)同样不行,只能下
(
3
,
1
)
(3,1)
(3,1)。
继续看蓝方,可以发现蓝方也只能下 ( 1 , 3 ) (1,3) (1,3)
于是,我们就得到了三子棋的AI的博弈树,现在来分析具体方法。
我们将平局定为
5
5
5分,红胜为
10
10
10,红负为
0
0
0分,这样,我们就可以得到每个局面对红方的好处。
设前一步的节点为
u
u
u,下一步的节点为
v
v
v
对于每一个蓝点(到蓝色下棋),下一步是红方控制的,当然会选择对自己优的,所以取
V
u
=
max
{
V
v
}
V_u=\max\{V_v\}
Vu=max{Vv},比如下图中粉框节点,下一步红方可以取胜,那当然要选取胜了
对于每一个红点(到红色下棋),下一步是对方控制的,当然会选择对红方劣的,所以取
V
u
=
min
{
V
v
}
V_u=\min\{V_v\}
Vu=min{Vv},比如如果有一个节点,下一步蓝方可以取胜,那当然要选取胜(红败)了
这样,我们就得到了所有局面的胜率。
源码见文末
T
e
s
t
2
−
1
Test\ 2-1
Test 2−1 源码,AI下3子棋见
T
e
s
t
2
−
2
Test\ 2-2
Test 2−2 源码
当然,经过对所有局面的考察,可以发现没有必胜的可能,但是还是有可能在前几步利用对手失误获得必胜(读者可以自行查看程序),如上图。
五子棋的总局面数约为 1 0 50 10^{50} 1050,因此不可能构造完整的决策树。
还是以三子棋为例,把
[
X
]
[
X
]
[
]
[X][X][\ \ \ ]
[X][X][ ]记为
50
50
50分,
[
]
[
X
]
[
]
[\ \ \ ][X][\ \ \ ]
[ ][X][ ]和
[
]
[
]
[
X
]
[\ \ \ ][\ \ \ ][X]
[ ][ ][X]记为
10
10
10分,
[
X
]
[
X
]
[
X
]
[X][X][X]
[X][X][X]记为
100
100
100分,就可以在决策树层数较少的情况估算价值。
有了估值,AI就智慧多了,如果我们把比较有可能的点(这里是选前2个)单独挑出来,其余的点不进行展开,可以发现它已经把较优的点选好了(如上图),就会大大减少节点数。
以下是一次对局:(AI:蓝)
评价:目光较短浅,但是速度非常快(约0.3s)
源码见
T
e
s
t
3
Test \ 3
Test 3
AI不行,就加深度,时间太长,就加剪枝
发现
V
u
=
m
a
x
{
m
i
n
{
a
,
b
}
,
c
}
V_u=max\{min\{a,b\},c\}
Vu=max{min{a,b},c}时,如果
a
&
b
a\&b
a&b有一个
<
c
<c
<c,那就不用算了,
V
u
V_u
Vu就是
c
c
c,
同理
V
u
=
m
i
n
{
m
a
x
{
a
,
b
}
,
c
}
V_u=min\{max\{a,b\},c\}
Vu=min{max{a,b},c},如果
a
&
b
a\&b
a&b有一个
>
c
>c
>c,
V
u
V_u
Vu就是
c
c
c
即:
m
i
n
(
7
,
m
a
x
(
10
,
?
)
)
=
7
min(7,max(10,?))=7
min(7,max(10,?))=7
(实测大约剪掉了
3
/
4
3/4
3/4的节点,效率不太高,所以笔者并没有用)
T e s t 1 Test \ 1 Test 1
#include<iostream> #include<vector> #include<graphics.h> using namespace std; struct BOARD{int a[3][3];int number,win;}; struct SHOW{int x,y;int SON;}node[4020889]; int NODE=1;bool show[4028089]; BOARD tree[4028809]; vector<int>son[4002889]; int mx,my,Dl,LINE[400288][5],LN,Dr,LOCK,Rc=1; int check(BOARD now) { for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(now.a[i][j]) for(int fx=-1;fx<=1;fx++) for(int fy=-1;fy<=1;fy++) if(fx!=0||fy!=0) { int bo=now.a[i][j]; for(int f=1;f<=2;f++) if(i+fx*f>=3||j+fy*f>=3||i+fx*f<0||j+fy*f<0||now.a[i][j]!=now.a[i+fx*f][j+fy*f])bo=0; if(bo)return bo; } return 0; } int getstep(BOARD now) { int step=0; for(int i=0;i<3;i++) for(int j=0;j<3;j++)step+=now.a[i][j]; if(step==0)step=1;else step=-1; return step; } void dfs(BOARD now) { tree[now.number]=now; if(now.win) { return; } int step=getstep(now); for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(!now.a[i][j]) { NODE++;BOARD next=now;next.number=NODE; son[now.number].push_back(NODE); next.a[i][j]=step; next.win=check(next); dfs(next); } } BOARD empty_board() { BOARD empty; for(int i=0;i<3;i++)for(int j=0;j<3;j++)empty.a[i][j]=0;empty.number=1;empty.win=0; return empty; } void print_board(int x,int y,BOARD z) { setcolor(getstep(z)==1?RED:BLUE); setfillcolor(WHITE); fillrect(x,y,x+100,y+100); x+=10;y+=10; setcolor(BLACK); int t=80/3; for(int i=x;i<=x+80;i+=t)line(i,y,i,y+80); for(int j=y;j<=y+80;j+=t)line(x,j,x+80,j); for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(z.a[i][j]==1) { setcolor(BLUE);setlinewidth(2); circle(x+i*t+t/2,y+j*t+t/2,t/2-1);setlinewidth(1); } else if(z.a[i][j]==-1) { setcolor(RED); setlinewidth(3); line(x+t*i+2,y+t*j+2,x+t*i+t-2,y+t*j+t-2); line(x+t*i+2,y+t*j+t-2,x+t*i+t-2,y+t*j+2); setlinewidth(1); } } void change_board(int x,int y,BOARD &z) { x+=10;y+=10; int t=80/3; for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(mx>x+t*i&&mx<x+t*i+t&&my>y+t*j&&my<y+t*j+t) { if(Dr)z.a[i][j]=getstep(z); } } void print_line() { setcolor(BLACK); for(int i=1;i<=LN;i++)line(node[LINE[i][0]].x+50,node[LINE[i][0]].y+50,node[LINE[i][1]].x+50,node[LINE[i][1]].y+50); } void mousedata() { if(Dr==0)LOCK=0; Dl=0;Rc=0; while(mousemsg()) { mouse_msg m=getmouse(); if(m.is_move())mx=m.x,my=m.y; if(m.is_down()&&m.is_mid())Dl=1; if(m.is_down()&&m.is_left())Dr=1; if(m.is_up()&&m.is_left())Dr=0; if(m.is_down()&&m.is_right())Rc=1; } } int main() { initgraph(1000,600);setbkcolor(WHITE);setcolor(BLACK); BOARD empty=empty_board(); while(1) { dfs(empty); setcaption("显示模式(点击右键编辑初始棋盘,中键展开,左键移动)"); cout<<"TOTAL:"<<NODE<<endl; int t=0; node[1].y=250;node[1].x=5;show[1]=1;Rc=0;tree[1]=empty; while(Rc==0) { cleardevice(); print_line(); for(int i=1;i<=NODE;i++) if(show[i]) { print_board(node[i].x,node[i].y,tree[i]); outtextxy(node[i].x,node[i].y-20,("["+to_string(son[i].size())+"/"+to_string(node[i].SON+son[i].size())+"]").c_str()); if(tree[i].win==-1)outtextxy(node[i].x,node[i].y-20,"WIN:RED"); if(tree[i].win==1)outtextxy(node[i].x,node[i].y-20,"WIN:BLUE"); if(Dl&&!son[i].empty()&&mx>node[i].x&&mx<node[i].x+100&&my>node[i].y&&my<node[i].y+100) { show[son[i].back()]=1;node[son[i].back()].x=node[i].x+120; node[son[i].back()].y=node[i].y;LINE[++LN][0]=i;LINE[LN][1]=son[i].back(); son[i].pop_back();node[i].SON++; } if(Dr&&mx>node[i].x&&mx<node[i].x+100&&my>node[i].y&&my<node[i].y+100) { if(LOCK==i||LOCK==0) { node[i].x=mx-50;node[i].y=my-50; LOCK=i; } } } mousedata(); delay_ms(100); } setcaption("编辑模式(点击右键开始计算,左键编辑棋盘)"); Rc=0; cleardevice(); while(Rc==0) { print_board(5,250,empty); change_board(5,250,empty); mousedata(); if(Dl)empty=empty_board(); delay_ms(100); } for(int i=1;i<=NODE;i++) { show[i]=0;tree[i].number=0;tree[i].win=0; son[i].clear();node[i].SON=0; } NODE=1;LN=0; } }
T e s t 2 − 1 Test \ 2-1 Test 2−1
#include<iostream> #include<vector> #include<graphics.h> using namespace std; struct BOARD{int a[3][3];int number,win,score;}; struct SHOW{int x,y;int SON;}node[4020889]; int NODE=1;bool show[4028089]; BOARD tree[4028809]; vector<int>son[4002889]; int mx,my,Dl,LINE[400288][5],LN,Dr,LOCK,Rc=1; int check(BOARD now) { for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(now.a[i][j]) for(int fx=-1;fx<=1;fx++) for(int fy=-1;fy<=1;fy++) if(fx!=0||fy!=0) { int bo=now.a[i][j]; for(int f=1;f<=2;f++) if(i+fx*f>=3||j+fy*f>=3||i+fx*f<0||j+fy*f<0||now.a[i][j]!=now.a[i+fx*f][j+fy*f])bo=0; if(bo)return bo; } return 0; } int getstep(BOARD now) { int step=0; for(int i=0;i<3;i++) for(int j=0;j<3;j++)step+=now.a[i][j]; if(step==0)step=1;else step=-1; return step; } int dfs(BOARD now) { tree[now.number]=now; if(now.win) { tree[now.number].score=now.win==-1?10:0; return now.win==-1?10:0; } int step=getstep(now),ping=1,Min=1e9,Max=-1e9; for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(!now.a[i][j]) {ping=0; NODE++;BOARD next=now;next.number=NODE; son[now.number].push_back(NODE); next.a[i][j]=step; next.win=check(next); if(step==1)Min=min(Min,dfs(next)); else Max=max(Max,dfs(next)); } if(ping==1) { tree[now.number].score=5; return 5; } tree[now.number].score=(step==1?Min:Max); return tree[now.number].score; } BOARD empty_board() { BOARD empty; for(int i=0;i<3;i++)for(int j=0;j<3;j++)empty.a[i][j]=0;empty.number=1;empty.win=0; return empty; } void print_board(int x,int y,BOARD z) { setcolor(getstep(z)==1?RED:BLUE); setfillcolor(WHITE); fillrect(x,y,x+100,y+100); x+=10;y+=10; setcolor(BLACK); int t=80/3; for(int i=x;i<=x+80;i+=t)line(i,y,i,y+80); for(int j=y;j<=y+80;j+=t)line(x,j,x+80,j); for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(z.a[i][j]==1) { setcolor(BLUE);setlinewidth(2); circle(x+i*t+t/2,y+j*t+t/2,t/2-1);setlinewidth(1); } else if(z.a[i][j]==-1) { setcolor(RED); setlinewidth(3); line(x+t*i+2,y+t*j+2,x+t*i+t-2,y+t*j+t-2); line(x+t*i+2,y+t*j+t-2,x+t*i+t-2,y+t*j+2); setlinewidth(1); } } void change_board(int x,int y,BOARD &z) { x+=10;y+=10; int t=80/3; for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(mx>x+t*i&&mx<x+t*i+t&&my>y+t*j&&my<y+t*j+t) { if(Dr)z.a[i][j]=getstep(z); } } void print_line() { setcolor(BLACK); for(int i=1;i<=LN;i++)line(node[LINE[i][0]].x+50,node[LINE[i][0]].y+50,node[LINE[i][1]].x+50,node[LINE[i][1]].y+50); } void mousedata() { if(Dr==0)LOCK=0; Dl=0;Rc=0; while(mousemsg()) { mouse_msg m=getmouse(); if(m.is_move())mx=m.x,my=m.y; if(m.is_down()&&m.is_mid())Dl=1; if(m.is_down()&&m.is_left())Dr=1; if(m.is_up()&&m.is_left())Dr=0; if(m.is_down()&&m.is_right())Rc=1; } } int main() { initgraph(1000,600);setbkcolor(WHITE);setcolor(BLACK); BOARD empty=empty_board(); while(1) { dfs(empty); setcaption("显示模式(点击右键编辑初始棋盘,中键展开,左键移动)"); cout<<"TOTAL:"<<NODE<<endl; int t=0; node[1].y=250;node[1].x=5;show[1]=1;Rc=0; while(Rc==0) { cleardevice(); print_line(); for(int i=1;i<=NODE;i++) if(show[i]) { print_board(node[i].x,node[i].y,tree[i]); outtextxy(node[i].x,node[i].y-20,("["+to_string(son[i].size())+"]"+"Score:"+to_string(tree[i].score)).c_str()); if(Dl&&!son[i].empty()&&mx>node[i].x&&mx<node[i].x+100&&my>node[i].y&&my<node[i].y+100) { show[son[i].back()]=1;node[son[i].back()].x=node[i].x+120; node[son[i].back()].y=node[i].y;LINE[++LN][0]=i;LINE[LN][1]=son[i].back(); son[i].pop_back();node[i].SON++; } if(Dr&&mx>node[i].x&&mx<node[i].x+100&&my>node[i].y&&my<node[i].y+100) { if(LOCK==i||LOCK==0) { node[i].x=mx-50;node[i].y=my-50; LOCK=i; } } } mousedata(); delay_ms(100); } setcaption("编辑模式(点击右键开始计算,左键编辑棋盘)"); Rc=0; cleardevice(); while(Rc==0) { print_board(5,250,empty); change_board(5,250,empty); mousedata(); if(Dl)empty=empty_board(); delay_ms(100); } for(int i=1;i<=NODE;i++) { show[i]=0;tree[i].number=0;tree[i].win=0; son[i].clear();node[i].SON=0; } NODE=1;LN=0; } }
T e s t 2 − 2 Test \ 2-2 Test 2−2
#include<iostream> #include<vector> #include<graphics.h> using namespace std; struct BOARD{int a[3][3];int number,win,score;}; struct SHOW{int x,y;int SON;}node[4020889]; int NODE=1;bool show[4028089]; BOARD tree[4028809]; vector<int>son[4002889]; int mx,my,Dl,LINE[400288][5],LN,Dr,LOCK,Rc=1,PC=0; int check(BOARD now) { for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(now.a[i][j]) for(int fx=-1;fx<=1;fx++) for(int fy=-1;fy<=1;fy++) if(fx!=0||fy!=0) { int bo=now.a[i][j]; for(int f=1;f<=2;f++) if(i+fx*f>=3||j+fy*f>=3||i+fx*f<0||j+fy*f<0||now.a[i][j]!=now.a[i+fx*f][j+fy*f])bo=0; if(bo)return bo; } return 0; } int getstep(BOARD now) { int step=0; for(int i=0;i<3;i++) for(int j=0;j<3;j++)step+=now.a[i][j]; if(step==0)step=1;else step=-1; return step; } int dfs(BOARD now) { tree[now.number]=now; if(now.win) { tree[now.number].score=now.win==-1?10:0; return now.win==-1?10:0; } int step=getstep(now),ping=1,Min=1e9,Max=-1e9; for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(!now.a[i][j]) {ping=0; NODE++;BOARD next=now;next.number=NODE; son[now.number].push_back(NODE); next.a[i][j]=step; next.win=check(next); if(step==1)Min=min(Min,dfs(next)); else Max=max(Max,dfs(next)); } if(ping==1) { tree[now.number].score=5; return 5; } tree[now.number].score=(step==1?Min:Max); return tree[now.number].score; } BOARD empty_board() { BOARD empty; for(int i=0;i<3;i++)for(int j=0;j<3;j++)empty.a[i][j]=0;empty.number=1;empty.win=0; return empty; } void print_board(int x,int y,BOARD z) { setcolor(getstep(z)==1?RED:BLUE); setfillcolor(WHITE); fillrect(x,y,x+100,y+100); x+=10;y+=10; setcolor(BLACK); int t=80/3; for(int i=x;i<=x+80;i+=t)line(i,y,i,y+80); for(int j=y;j<=y+80;j+=t)line(x,j,x+80,j); for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(z.a[i][j]==1) { setcolor(BLUE);setlinewidth(2); circle(x+i*t+t/2,y+j*t+t/2,t/2-1);setlinewidth(1); } else if(z.a[i][j]==-1) { setcolor(RED); setlinewidth(3); line(x+t*i+2,y+t*j+2,x+t*i+t-2,y+t*j+t-2); line(x+t*i+2,y+t*j+t-2,x+t*i+t-2,y+t*j+2); setlinewidth(1); } } void change_board(int x,int y,BOARD &z) { x+=10;y+=10; int t=80/3; for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(mx>x+t*i&&mx<x+t*i+t&&my>y+t*j&&my<y+t*j+t) { if(Dr)z.a[i][j]=1;PC=1; } } void print_line() { setcolor(BLACK); for(int i=1;i<=LN;i++)line(node[LINE[i][0]].x+50,node[LINE[i][0]].y+50,node[LINE[i][1]].x+50,node[LINE[i][1]].y+50); } void mousedata() { if(Dr==0)LOCK=0; Dl=0;Rc=0; while(mousemsg()) { mouse_msg m=getmouse(); if(m.is_move())mx=m.x,my=m.y; if(m.is_down()&&m.is_mid())Dl=1; if(m.is_down()&&m.is_left())Dr=1; if(m.is_up()&&m.is_left())Dr=0; if(m.is_down()&&m.is_right())Rc=1; } } int main() {srand(time(0)); initgraph(1000,600);setbkcolor(WHITE);setcolor(BLACK); BOARD empty=empty_board(); while(1) { setcaption("下棋模式(左键下棋,右键确认)");Rc=0; while(Rc==0) { cleardevice(); print_board(5,250,empty); change_board(5,250,empty); mousedata(); delay_ms(50); }print_board(5,250,empty); for(int i=1;i<=NODE;i++) { show[i]=0;tree[i].number=0;tree[i].win=0; son[i].clear();node[i].SON=0; } NODE=1;LN=0;empty.number=1; dfs(empty); setcaption("显示模式(点击右键AI决策,中键展开,左键移动)"); cout<<"TOTAL:"<<NODE<<endl; int t=0; node[1].y=250;node[1].x=5;show[1]=1;Rc=0; int Min=1e9,Max=-1e9; vector<BOARD>CAN;CAN.clear(); if(son[1].size()==0)exit(0); //for(int i=1;i<=10;i++)cout<<son[i].size(); for(auto i:son[1]) {//cout<<i<<endl; Max=max(Max,tree[i].score); } Min=Max; for(auto i:son[1])if(tree[i].score==Min)CAN.push_back(tree[i]); empty=CAN[rand()%CAN.size()];cout<<"[COMPLETE]\n"; while(Rc==0) { cleardevice(); print_line(); for(int i=1;i<=NODE;i++) if(show[i]) { print_board(node[i].x,node[i].y,tree[i]); outtextxy(node[i].x,node[i].y-20,("["+to_string(son[i].size())+"]"+"Score:"+to_string(tree[i].score)).c_str()); if(Dl&&!son[i].empty()&&mx>node[i].x&&mx<node[i].x+100&&my>node[i].y&&my<node[i].y+100) { show[son[i].back()]=1;node[son[i].back()].x=node[i].x+120; node[son[i].back()].y=node[i].y;LINE[++LN][0]=i;LINE[LN][1]=son[i].back(); son[i].pop_back();node[i].SON++; } if(Dr&&mx>node[i].x&&mx<node[i].x+100&&my>node[i].y&&my<node[i].y+100) { if(LOCK==i||LOCK==0) { node[i].x=mx-50;node[i].y=my-50; LOCK=i; } } } mousedata(); delay_ms(100); } } }
T e s t 3 Test \ 3 Test 3
#include<bits/stdc++.h> #include<graphics.h> using namespace std; struct BOARD{int a[10][10];int number,win,score,stepx,stepy;}; struct SHOW{int x,y;int SON;}node[402088]; int NODE=1;bool show[402808]; BOARD tree[402880]; vector<int>son[400288]; int mx,my,Dl,LINE[400288][5],LN,Dr,LOCK,Rc=1; int cmp(int a,int b) { return tree[a].score<tree[b].score; } int check(BOARD now,int x,int y) { for(int i=x;i<=x;i++) for(int j=y;j<=y;j++) if(now.a[i][j]) for(int fx=-1;fx<=1;fx++) for(int fy=-1;fy<=1;fy++) if(fx!=0||fy!=0) { int bo=now.a[i][j]; for(int f=1;f<=4;f++) if(i+fx*f>=10||j+fy*f>=10||i+fx*f<0||j+fy*f<0||now.a[i][j]!=now.a[i+fx*f][j+fy*f])bo=0; if(bo)return bo; } return 0; } int evaluateDirection(const BOARD board, int row, int col, int player) { int directions[4][2] = {{1, 0}, {0, 1}, {1, 1}, {1, -1}}; int score = 0; for (int i = 0; i < 4; i++) { int count = 0,dc=1; for (int j = 1; j <= 5; j++) { int newRow = row + j * directions[i][0]; int newCol = col + j * directions[i][1]; if (newRow < 0 || newRow >= 10 || newCol < 0 || newCol >= 10 || board.a[newRow][newCol] != player) { if(board.a[newRow][newCol] != 0)dc=2; break; } count++; } if(board.a[row -1 * directions[i][0]][col - 1 * directions[i][1]]==-player)dc*=2; if (count == 1) { score += 1/dc; } else if (count == 2) { score += 10/dc; } else if (count == 3) { score += 100/dc; } else if (count == 4) { score += 900/dc; } else if (count == 5) { score += 1000/dc; } } return score; } int evaluateBoard(const BOARD board) { int score = 0; for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { if (board.a[i][j] == 1) score += evaluateDirection(board, i, j, 1); else if (board.a[i][j] == -1) score -= evaluateDirection(board, i, j, -1); } } return score; } int getstep(BOARD now) { int step=0; for(int i=0;i<10;i++) for(int j=0;j<10;j++)step+=now.a[i][j]; if(step==0)step=1;else step=-1; return step; } int dfs(BOARD now,int k,int Nk) {//if(rand()%10000==0)cout<<now.number; son[now.number].clear(); int nd=NODE; tree[now.number]=now; if(now.win) { tree[now.number].score=now.win==-1?1000:0; return now.win==-1?1000:0; } int step=getstep(now),Min=1e9,Max=-1e9; if(k>min(Nk,4)) { tree[now.number].score=-evaluateBoard(now); return tree[now.number].score; } map<int,int>ndx,ndy; for(int i=0;i<10;i++) for(int j=0;j<10;j++) if(!now.a[i][j]) { NODE++;BOARD next=now;next.number=NODE; son[now.number].push_back(NODE); next.a[i][j]=step;ndx[NODE]=i;ndy[NODE]=j; next.win=check(next,i,j);next.stepx=i;next.stepy=j; if(step==1)Min=min(Min,dfs(next,k+1,Nk)); else Max=max(Max,dfs(next,k+1,Nk)); } sort(son[now.number].begin(),son[now.number].end(),cmp); if(getstep(tree[now.number])==1)reverse(son[now.number].begin(),son[now.number].end()); if(k>2) { Min=1e9,Max=-1e9;NODE=nd; vector<int>SON; for(int i=0;i<5;i++) { NODE++;BOARD next=now;next.number=NODE; SON.push_back(NODE); next.stepx=ndx[son[now.number][i]];next.stepy=ndy[son[now.number][i]]; next.a[next.stepx][next.stepy]=step;next.win=check(next,next.stepx,next.stepy); if(step==1)Min=min(Min,dfs(next,k+1,k+3)); else Max=max(Max,dfs(next,k+1,k+3)); } son[now.number]=SON; } tree[now.number].score=(step==1?Min:Max); return tree[now.number].score; } BOARD empty_board() { BOARD empty; for(int i=0;i<10;i++)for(int j=0;j<10;j++)empty.a[i][j]=0;empty.number=1;empty.win=0; return empty; } void print_board_all(int x,int y,BOARD z) { setcolor(getstep(z)==1?RED:BLUE); setfillcolor(WHITE); fillrect(x,y,x+300,y+300); x+=10;y+=10; setcolor(BLACK); int t=270/10; for(int i=x;i<=x+280;i+=t)line(i,y,i,y+280); for(int j=y;j<=y+280;j+=t)line(x,j,x+280,j); for(int i=0;i<10;i++) for(int j=0;j<10;j++) if(z.a[i][j]==1) { setcolor(BLUE);setlinewidth(2); circle(x+i*t+t/2,y+j*t+t/2,t/2-1);setlinewidth(1); } else if(z.a[i][j]==-1) { setcolor(RED); setlinewidth(3); line(x+t*i+2,y+t*j+2,x+t*i+t-2,y+t*j+t-2); line(x+t*i+2,y+t*j+t-2,x+t*i+t-2,y+t*j+2); setlinewidth(1); } } void print_board_step(int x,int y,BOARD z) { setcolor(getstep(z)==1?RED:BLUE); setfillcolor(WHITE); fillrect(x,y,x+100,y+40); x+=20;y+=10; setcolor(BLACK); if(z.stepx==0&&z.stepy==0);else outtextxy(x,y,("("+to_string(z.stepx+1)+","+to_string(z.stepy+1)+")").c_str()); } void change_board(int x,int y,BOARD &z) { x+=10;y+=10; int t=270/10; for(int i=0;i<10;i++) for(int j=0;j<10;j++) if(mx>x+t*i&&mx<x+t*i+t&&my>y+t*j&&my<y+t*j+t) { if(Dr)z.a[i][j]=getstep(z); } } void print_line() { setcolor(BLACK); for(int i=1;i<=LN;i++)line(node[LINE[i][0]].x+50,node[LINE[i][0]].y+20,node[LINE[i][1]].x+50,node[LINE[i][1]].y+20); } void mousedata() { if(Dr==0)LOCK=0; Dl=0;Rc=0; while(mousemsg()) { mouse_msg m=getmouse(); if(m.is_move())mx=m.x,my=m.y; if(m.is_down()&&m.is_mid())Dl=1; if(m.is_down()&&m.is_left())Dr=1; if(m.is_up()&&m.is_left())Dr=0; if(m.is_down()&&m.is_right())Rc=1; } } int main() { initgraph(1000,600);setbkcolor(WHITE);setcolor(BLACK); BOARD empty=empty_board(); while(1) { setcaption("游玩模式(点击右键AI开始计算,左键下棋)"); Rc=0; cleardevice(); while(Rc==0) { print_board_all(5,250,empty); change_board(5,250,empty); mousedata(); if(Dl)empty=empty_board(); delay_ms(100); } for(int i=1;i<=NODE;i++) { show[i]=0;tree[i].number=0;tree[i].win=0; son[i].clear();node[i].SON=0; } NODE=1;LN=0; dfs(empty,1,2); setcaption("显示模式(点击右键编辑初始棋盘,中键展开,左键移动)"); cout<<"TOTAL:"<<NODE<<endl; int t=0; node[1].y=250;node[1].x=5;show[1]=1;Rc=0;int lc=1; empty=tree[son[1].back()];empty.number=1;empty.score=0; while(Rc==0) { cleardevice(); print_line();print_board_all(0,0,tree[lc==0?1:lc]); for(int i=NODE;i>=1;i--) if(show[i]) { print_board_step(node[i].x,node[i].y,tree[i]); outtextxy(node[i].x,node[i].y-20,("["+to_string(son[i].size())+"]"+"Score:"+to_string(tree[i].score)).c_str()); if(Dl&&!son[i].empty()&&mx>node[i].x&&mx<node[i].x+100&&my>node[i].y&&my<node[i].y+100) { show[son[i].back()]=1;node[son[i].back()].x=node[i].x+120; node[son[i].back()].y=node[i].y;LINE[++LN][0]=i;LINE[LN][1]=son[i].back(); son[i].pop_back();node[i].SON++; } if(Dr&&mx>node[i].x&&mx<node[i].x+100&&my>node[i].y&&my<node[i].y+40) { if(LOCK==i||LOCK==0) { node[i].x=mx-50;node[i].y=my-20; LOCK=i;lc=i; } } } mousedata(); delay_ms(100); } } }
五子棋最终 A I ( 我无法评价 ) 五子棋最终AI(我无法评价) 五子棋最终AI(我无法评价)
#include<bits/stdc++.h> #include<graphics.h> using namespace std; struct BOARD{int a[10][10];int number,win,score,stepx,stepy;}; struct SHOW{int x,y;int SON;}node[402088]; int NODE=1;bool show[402808]; BOARD tree[402880]; vector<int>son[400288]; int mx,my,Dl,LINE[400288][5],LN,Dr,LOCK,Rc=1; int cmp(int a,int b) { return tree[a].score<tree[b].score; } int check(BOARD now,int x,int y) { for(int i=x;i<=x;i++) for(int j=y;j<=y;j++) if(now.a[i][j]) for(int fx=-1;fx<=1;fx++) for(int fy=-1;fy<=1;fy++) if(fx!=0||fy!=0) { int bo=now.a[i][j]; for(int f=1;f<=4;f++) if(i+fx*f>=10||j+fy*f>=10||i+fx*f<0||j+fy*f<0||now.a[i][j]!=now.a[i+fx*f][j+fy*f])bo=0; if(bo)return bo; } return 0; } int evaluateDirection(const BOARD board, int row, int col, int player) { int directions[4][2] = {{1, 0}, {0, 1}, {1, 1}, {1, -1}}; int score = 0; for (int i = 0; i < 4; i++) { int count = 0,dc=1; for (int j = 1; j <= 5; j++) { int newRow = row + j * directions[i][0]; int newCol = col + j * directions[i][1]; if (newRow < 0 || newRow >= 10 || newCol < 0 || newCol >= 10 || board.a[newRow][newCol] != player) { if(board.a[newRow][newCol] != 0)dc=2; break; } count++; } if(board.a[row -1 * directions[i][0]][col - 1 * directions[i][1]]==-player)dc*=2; if (count == 1) { score += 1/dc; } else if (count == 2) { score += 10/dc; } else if (count == 3) { score += 100/dc; } else if (count == 4) { score += 900/dc; } else if (count == 5) { score += 1000/dc; } } return score; } int evaluateBoard(const BOARD board) { int score = 0; for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { if (board.a[i][j] == 1) score += evaluateDirection(board, i, j, 1); else if (board.a[i][j] == -1) score -= evaluateDirection(board, i, j, -1); } } return score; } int getstep(BOARD now) { int step=0; for(int i=0;i<10;i++) for(int j=0;j<10;j++)step+=now.a[i][j]; if(step==0)step=1;else step=-1; return step; } int dfs(BOARD now,int k,int Nk,int alpha,int beta) {//if(rand()%10000==0)cout<<now.number; son[now.number].clear(); int nd=NODE; tree[now.number]=now; if(now.win) { tree[now.number].score=now.win==-1?1000:0; return now.win==-1?1000:0; } int step=getstep(now),Min=1e9,Max=-1e9; if(k>min(Nk,6)) { tree[now.number].score=-evaluateBoard(now); return tree[now.number].score; } map<int,int>ndx,ndy; for(int i=0;i<10;i++) for(int j=0;j<10;j++) if(!now.a[i][j]) { NODE++;BOARD next=now;next.number=NODE; son[now.number].push_back(NODE); next.a[i][j]=step;ndx[NODE]=i;ndy[NODE]=j; next.win=check(next,i,j);next.stepx=i;next.stepy=j; int t=step==1?dfs(next,k+1,Nk,alpha,Min):dfs(next,k+1,Nk,Max,beta); //if(t>beta||t<alpha)return t; if(step==1)Min=min(Min,t); else Max=max(Max,t); } sort(son[now.number].begin(),son[now.number].end(),cmp); if(getstep(tree[now.number])==1)reverse(son[now.number].begin(),son[now.number].end()); if(k>2) { Min=1e9,Max=-1e9;NODE=nd; vector<int>SON; for(int i=0;i<5;i++) { NODE++;BOARD next=now;next.number=NODE; SON.push_back(NODE); next.stepx=ndx[son[now.number][i]];next.stepy=ndy[son[now.number][i]]; next.a[next.stepx][next.stepy]=step;next.win=check(next,next.stepx,next.stepy); int t=step==1?dfs(next,k+1,Nk,alpha,Min):dfs(next,k+1,Nk,Max,beta); if(step==1)Min=min(Min,t); else Max=max(Max,t); } son[now.number]=SON; } tree[now.number].score=(step==1?Min:Max); return tree[now.number].score; } BOARD empty_board() { BOARD empty; for(int i=0;i<10;i++)for(int j=0;j<10;j++)empty.a[i][j]=0;empty.number=1;empty.win=0; return empty; } void print_board_all(int x,int y,BOARD z) { setcolor(getstep(z)==1?RED:BLUE); setfillcolor(WHITE); fillrect(x,y,x+300,y+300); x+=10;y+=10; setcolor(BLACK); int t=270/10; for(int i=x;i<=x+280;i+=t)line(i,y,i,y+280); for(int j=y;j<=y+280;j+=t)line(x,j,x+280,j); for(int i=0;i<10;i++) for(int j=0;j<10;j++) if(z.a[i][j]==1) { setcolor(BLUE);setlinewidth(2); circle(x+i*t+t/2,y+j*t+t/2,t/2-1);setlinewidth(1); } else if(z.a[i][j]==-1) { setcolor(RED); setlinewidth(3); line(x+t*i+2,y+t*j+2,x+t*i+t-2,y+t*j+t-2); line(x+t*i+2,y+t*j+t-2,x+t*i+t-2,y+t*j+2); setlinewidth(1); } } void print_board_step(int x,int y,BOARD z) { setcolor(getstep(z)==1?RED:BLUE); setfillcolor(WHITE); fillrect(x,y,x+100,y+40); x+=20;y+=10; setcolor(BLACK); if(z.stepx==0&&z.stepy==0);else outtextxy(x,y,("("+to_string(z.stepx+1)+","+to_string(z.stepy+1)+")").c_str()); } void change_board(int x,int y,BOARD &z) { x+=10;y+=10; int t=270/10; for(int i=0;i<10;i++) for(int j=0;j<10;j++) if(mx>x+t*i&&mx<x+t*i+t&&my>y+t*j&&my<y+t*j+t) { if(Dr)z.a[i][j]=getstep(z); } } void print_line() { setcolor(BLACK); for(int i=1;i<=LN;i++)line(node[LINE[i][0]].x+50,node[LINE[i][0]].y+20,node[LINE[i][1]].x+50,node[LINE[i][1]].y+20); } void mousedata() { if(Dr==0)LOCK=0; Dl=0;Rc=0; while(mousemsg()) { mouse_msg m=getmouse(); if(m.is_move())mx=m.x,my=m.y; if(m.is_down()&&m.is_mid())Dl=1; if(m.is_down()&&m.is_left())Dr=1; if(m.is_up()&&m.is_left())Dr=0; if(m.is_down()&&m.is_right())Rc=1; } } int main() { initgraph(1000,600);setbkcolor(WHITE);setcolor(BLACK); BOARD empty=empty_board(); while(1) { setcaption("游玩模式(点击右键AI开始计算,左键下棋)"); Rc=0; cleardevice(); while(Rc==0) { print_board_all(0,0,empty); change_board(0,0,empty); mousedata(); if(Dl)empty=empty_board(); delay_ms(100); } for(int i=1;i<=NODE;i++) { show[i]=0;tree[i].number=0;tree[i].win=0; son[i].clear();node[i].SON=0; } NODE=1;LN=0; dfs(empty,1,3,-10000,10000); setcaption("显示模式(点击右键编辑初始棋盘,中键展开,左键移动)"); cout<<"TOTAL:"<<NODE<<endl; int t=0; node[1].y=250;node[1].x=5;show[1]=1;Rc=0;int lc=1; empty=tree[son[1].back()];empty.number=1;empty.score=0; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。