一只来自AI的安慰剂-五子棋ai的设计与实现

大家吼呀,在开始这一张之前,我们先了解一下今天我们会用到的算法
也就是我们今天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会很期待大家的作品哦
好的今天的文章就到这里啦
等等
空战领域又更新了?
传说中的点地行走,鼠标瞄准
他来了!
空战领域下载链接
本期文章就到这里了拜拜!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值