大家吼呀,在开始这一张之前,我们先了解一下今天我们会用到的算法
也就是我们今天AI的主算法:minmax!
如何介绍这个算法呢,我们从《复仇者盟4》中奇博士的话讲起
“我的脑中复现了14000605种可能性,只有一种,我们赢了”
Minmax的算法就是实现这一“超时空运算能力的算法
简单的来说,就是在五子棋开始的一瞬间计算所有合法的可能,然后进行比较。
经过上述推力,我们很容易就能想出minmax算法是一种递归,首先会进行数值比较,如果沿着这一条路走下去,minmax赢了,呢么就给这条路上的每一个节点数值a加一,同时把这一节点的deep值赋值为目前深度,如果输了数值a减1,如果平了数值a不变,重复此过程。最终比较时,把每个节点数值进行比较,然后选出深度最短的路线走,若数值相同就走a最大的。
这就是minmax的基本运行框架了,接下来是如何模拟路线,也就是所谓的“超时空运算”,首先我们知道,人类都是聪明的,所以他们也会从最好情况考虑,所以这个minmax的递归函数其实是让两个自己打仗,让后让真正的自己赢,就找到要去的路径了。
理论存在,实践开始!
我们先实现一个简单的五子棋代码(双人)
#include<bits/stdc++.h>
#include<windows.h>
#include<graphics.h>
#define kb(vk_c) (GetAsyncKeyState(vk_c)&0x8000?1:0)
using namespace std;
bool who;
int map_[5][5];
string to_s(int n)
{
int m=n;
char s[500];
char ss[500];
int i=0,j=0;
if(n=0)
{
s[0]=0+'0';
s[1]='\0';
}
while(m>0)
{
s[i++]=m%10+'0';
m/=10;
}
s[i]='\0';
i=i-1;
while (i>=0)
{
ss[j++]=s[i--];
}
ss[j]='\0';
return ss;
}
int win_()
{
int count_1,count_2;
if(map_[0][0]==1&&map_[0][1]==1&&map_[0][2]==1)return 1;
if(map_[1][0]==1&&map_[1][1]==1&&map_[1][2]==1)return 1;
if(map_[2][0]==1&&map_[2][1]==1&&map_[2][2]==1)return 1;
if(map_[0][0]==1&&map_[1][0]==1&&map_[2][0]==1)return 1;
if(map_[0][1]==1&&map_[1][1]==1&&map_[2][1]==1)return 1;
if(map_[0][2]==1&&map_[1][2]==1&&map_[2][2]==1)return 1;
if(map_[0][0]==1&&map_[1][1]==1&&map_[2][2]==1)return 1;
if(map_[0][2]==1&&map_[1][1]==1&&map_[2][0]==1)return 1;
if(map_[0][0]==2&&map_[0][1]==2&&map_[0][2]==2)return 2;
if(map_[1][0]==2&&map_[1][1]==2&&map_[1][2]==2)return 2;
if(map_[2][0]==2&&map_[2][1]==2&&map_[2][2]==2)return 2;
if(map_[0][0]==2&&map_[1][0]==2&&map_[2][0]==2)return 2;
if(map_[0][1]==2&&map_[1][1]==2&&map_[2][1]==2)return 2;
if(map_[0][2]==2&&map_[1][2]==2&&map_[2][2]==2)return 2;
if(map_[0][0]==2&&map_[1][1]==2&&map_[2][2]==2)return 2;
if(map_[0][2]==2&&map_[1][1]==2&&map_[2][0]==2)return 2;
int ount;
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
if(map_[i][j]!=0)ount++;
}
}
if(ount==3*3)
{
return 0;
}
return -1;
}
int timefps;
int main()
{
setinitmode(0);
srand(time(0)*time(0)+time(0));
HWND kzt=GetForegroundWindow();
initgraph(300,300);
setbkcolor(WHITE);
setcaption("Tic_Tac_Tok");
PIMAGE bg=newimage();
getimage(bg,"back.png");
PIMAGE x=newimage();
getimage(x,"x.png");
PIMAGE o=newimage();
getimage(o,"o.png");
mouse_msg msg;
int mou_x,mou_y;
if(GetForegroundWindow()==kzt)
{
closegraph();
MessageBox(NULL,"[errow]errowcode:0x00001[errow]\n Unable to define graphics window:(","errow",MB_OK);
ShowWindow(kzt,1);
return 0;
}
ShowWindow(kzt,0);
settextjustify(CENTER_TEXT, CENTER_TEXT);
putimage_withalpha(NULL,bg,1,1);
bool whooooooooooooooooooooooooo=0;
for(;is_run();delay_fps(370))
{
msg=mouse_msg();
mousepos(&mou_x,&mou_y);
if(kb(VK_LBUTTON)&&timefps<=3&&map_[(mou_x-1)/99][(mou_y-1)/99]==0&&whooooooooooooooooooooooooo==0)
{
whooooooooooooooooooooooooo=1;
map_[(mou_x-1)/99][(mou_y-1)/99]=1;
putimage(((mou_x-1)/99)*99,((mou_y-1)/99)*99,x);
timefps=5;
putimage_withalpha(NULL,bg,1,1);
}
if(kb(VK_LBUTTON)&&timefps<=3&&map_[(mou_x-1)/99][(mou_y-1)/99]==0&&whooooooooooooooooooooooooo==1)
{
whooooooooooooooooooooooooo=0;
map_[(mou_x-1)/99][(mou_y-1)/99]=2;
putimage(((mou_x-1)/99)*99,((mou_y-1)/99)*99,o);
timefps=5;
putimage_withalpha(NULL,bg,1,1);
}
timefps--;
if(win_()!=-1)
{
break;
}
}
outtextxy(150,150,"按Q退出");
Sleep(10);
while(1)if(kb('Q'))break;
closegraph();
ShowWindow(kzt,1);
return 0;
}
然后我们来做人机代码
#include<bits/stdc++.h>
#include<windows.h>
#include<graphics.h>
#define kb(vk_c) (GetAsyncKeyState(vk_c)&0x8000?1:0)
using namespace std;
bool who;
int map_[5][5];
struct poc
{
int x,y;
}Ai;
string to_s(int n)
{
int m=n;
char s[500];
char ss[500];
int i=0,j=0;
if(n=0)
{
s[0]=0+'0';
s[1]='\0';
}
while(m>0)
{
s[i++]=m%10+'0';
m/=10;
}
s[i]='\0';
i=i-1;
while (i>=0)
{
ss[j++]=s[i--];
}
ss[j]='\0';
return ss;
}
int win_()
{
int count_1,count_2;
if(map_[0][0]==1&&map_[0][1]==1&&map_[0][2]==1)return 1;
if(map_[1][0]==1&&map_[1][1]==1&&map_[1][2]==1)return 1;
if(map_[2][0]==1&&map_[2][1]==1&&map_[2][2]==1)return 1;
if(map_[0][0]==1&&map_[1][0]==1&&map_[2][0]==1)return 1;
if(map_[0][1]==1&&map_[1][1]==1&&map_[2][1]==1)return 1;
if(map_[0][2]==1&&map_[1][2]==1&&map_[2][2]==1)return 1;
if(map_[0][0]==1&&map_[1][1]==1&&map_[2][2]==1)return 1;
if(map_[0][2]==1&&map_[1][1]==1&&map_[2][0]==1)return 1;
if(map_[0][0]==2&&map_[0][1]==2&&map_[0][2]==2)return 2;
if(map_[1][0]==2&&map_[1][1]==2&&map_[1][2]==2)return 2;
if(map_[2][0]==2&&map_[2][1]==2&&map_[2][2]==2)return 2;
if(map_[0][0]==2&&map_[1][0]==2&&map_[2][0]==2)return 2;
if(map_[0][1]==2&&map_[1][1]==2&&map_[2][1]==2)return 2;
if(map_[0][2]==2&&map_[1][2]==2&&map_[2][2]==2)return 2;
if(map_[0][0]==2&&map_[1][1]==2&&map_[2][2]==2)return 2;
if(map_[0][2]==2&&map_[1][1]==2&&map_[2][0]==2)return 2;
int ount;
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
if(map_[i][j]!=0)ount++;
}
}
if(ount==3*3)
{
return 0;
}
return -1;
}
int timefps;
int minmax(bool whoisplay)
{
int winner=win_();
if(winner==1)
{
return -1;
}
if(winner==2)
{
return 1;
}
if(winner==0)
{
return 0;
}
if(whoisplay)
{
int superscore=10;
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
if(map_[i][j]==0)
{
map_[i][j]=2;
int score=minmax(0);
map_[i][j]=0;
superscore+=min(score,superscore);
}
}
}
return superscore;
}
else
{
int superscore=10;
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
if(map_[i][j]==0)
{
map_[i][j]=1;
int score=minmax(1);
map_[i][j]=0;
superscore+=min(score,superscore);
}
}
}
return superscore;
}
}
poc AI_go()
{
int x__=4,y__=4,bestscore=-100,score;
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
if(map_[i][j]==0)
{
map_[i][j]=2;
score=minmax(1);
if(score>bestscore)
{
bestscore=score;
x__=i;
y__=j;
}
map_[i][j]=0;
}
}
}
map_[x__][y__]=2;
poc a;
a.x=x__;
a.y=y__;
return a;
}
int main()
{
setinitmode(0);
srand(time(0)*time(0)+time(0));
HWND kzt=GetForegroundWindow();
initgraph(300,300);
setbkcolor(WHITE);
setcaption("Tic_Tac_Tok");
PIMAGE bg=newimage();
getimage(bg,"back.png");
PIMAGE x=newimage();
getimage(x,"x.png");
PIMAGE o=newimage();
getimage(o,"o.png");
mouse_msg msg;
int mou_x,mou_y;
if(GetForegroundWindow()==kzt)
{
closegraph();
MessageBox(NULL,"[errow]errowcode:0x00001[errow]\n Unable to define graphics window:(","errow",MB_OK);
ShowWindow(kzt,1);
return 0;
}
ShowWindow(kzt,0);
settextjustify(CENTER_TEXT, CENTER_TEXT);
putimage_withalpha(NULL,bg,1,1);
for(;is_run();delay_fps(370))
{
msg=mouse_msg();
mousepos(&mou_x,&mou_y);
if(kb(VK_LBUTTON)&&timefps<=3&&map_[(mou_x-1)/99][(mou_y-1)/99]==0)
{
map_[(mou_x-1)/99][(mou_y-1)/99]=1;
putimage(((mou_x-1)/99)*99,((mou_y-1)/99)*99,x);
timefps=5;
putimage_withalpha(NULL,bg,1,1);
Ai=AI_go();
putimage(Ai.x*99,Ai.y*99,o);
putimage_withalpha(NULL,bg,1,1);
}
timefps--;
if(win_()!=-1)
{
break;
}
}
outtextxy(150,150,"按Q退出");
Sleep(10);
while(1)if(kb('Q'))break;
closegraph();
ShowWindow(kzt,1);
return 0;
}
Ok了
这个AI不是让自己赢的ai
其实他,是一个让你必赢自己必输(除非你bug自杀)的ai
用来给别人比完赛增加信心的。
当然大家也可以快乐的修改一下代码
让这个机器人聪明起来
为了方便大家修改,我们来看一看加上注释的ai核心代码
int minmax(bool whoisplay)//whoisplay代表当前是谁在走
{
int winner=win_();//win()是获得当前赢家
if(winner==1)
{
return 1;//如果人类赢了,路线加一分
}
if(winner==2)
{
return -1;//如果人没赢,路线减一分
}
if(winner==0)
{
return 0;//如果平局,路线分数不变
}
if(whoisplay)//如果是机器走
{
int superscore=10;//比较用最高分
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
if(map_[i][j]==0)
{
map_[i][j]=2;//赋值为2
int score=minmax(0);//递归人走
map_[i][j]=0;
superscore+=min(score,superscore);//判断最低分
}
}
}
return superscore;//返回最终选择路线
}//下同
else
{
int superscore=10;
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
if(map_[i][j]==0)
{
map_[i][j]=1;
int score=minmax(1);
map_[i][j]=0;
superscore+=min(score,superscore);
}
}
}
return superscore;
}
}
Etanya会很期待大家的作品哦
好的今天的文章就到这里啦
等等
空战领域又更新了?
传说中的点地行走,鼠标瞄准
他来了!
空战领域下载链接
本期文章就到这里了拜拜!