人工智能博弈树算法做的井字棋游戏
不会输,超碉!井字棋这个游戏真是太无聊啦!
算法大概就是,有一个给状况进行估价的函数,深搜每种状况,假设每个人都按对自己最有利的方式走(假设玩家也是不傻),最后让电脑走出最有利的一步。
实验报告:
http://wenku.baidu.com/view/b48996b78bd63186bcebbcba.html
代码:
1 //#pragma comment(linker, "/STACK:102400000,102400000") 2 #include<cstdio> 3 #include<cmath> 4 #include<iostream> 5 #include<cstring> 6 #include<algorithm> 7 #include<cmath> 8 #include<map> 9 #include<set> 10 #include<stack> 11 #include<queue> 12 #include<conio.h> 13 #include<time.h> 14 using namespace std; 15 #define mz(array) memset(array, 0, sizeof(array)) 16 #define mf1(array) memset(array, -1, sizeof(array)) 17 #define minf(array) memset(array, 0x3f, sizeof(array)) 18 #define REP(i,n) for(i=0;i<(n);i++) 19 #define FOR(i,x,n) for(i=(x);i<=(n);i++) 20 #define FORD(i,x,y) for(i=(x);i>=(y);i--) 21 #define RD(x) scanf("%d",&x) 22 #define RD2(x,y) scanf("%d%d",&x,&y) 23 #define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z) 24 #define WN(x) printf("%d\n",x); 25 #define RE freopen("D.in","r",stdin) 26 #define WE freopen("huzhi.txt","w",stdout) 27 #define mp make_pair 28 #define pb push_back 29 #define pf push_front 30 #define ppf pop_front 31 #define ppb pop_back 32 typedef long long ll; 33 typedef unsigned long long ull; 34 35 const char CHESS[3]= {'-', '@', '#'}; 36 37 struct Board { 38 int a[3][3]; 39 inline void Init() { 40 memset(a,0,sizeof(a)); 41 } 42 inline void operator =(Board b) { 43 int i,j; 44 REP(i,3)REP(j,3)a[i][j]=b.a[i][j]; 45 } 46 }; 47 48 int GetInput(string s, int maxNum) { 49 int t; 50 char c; 51 while(1) { 52 cout<<s; 53 c=getch(); 54 t=c-'0'; 55 if(t>=0 && t<maxNum)break; 56 else { 57 puts("输入错误,请重新输入!"); 58 } 59 } 60 puts(""); 61 return t; 62 } 63 64 void OutBoard(Board bod) { 65 int i,j; 66 printf(" "); 67 REP(i,3)printf("%2d",i); 68 puts("\n ======"); 69 REP(i,3) { 70 printf("%2d|",i); 71 REP(j,3) { 72 printf("%2c",CHESS[bod.a[i][j]]); 73 } 74 puts(""); 75 } 76 } 77 78 void PlayerMove(Board &bod) { 79 int x,y; 80 OutBoard(bod); 81 while(true) { 82 printf("该你了,请输入你要下%c的横纵坐标(用空格隔开的两个数):",CHESS[1]); 83 scanf("%d%d",&x,&y); 84 if(!(x>=0 && x<=2 && y>=0 && y<=2)) { 85 puts("输入错误!请重新输入!横纵坐标都要是0或者1或者2。"); 86 continue; 87 } 88 if(bod.a[x][y]!=0) { 89 puts("下错了,这个位置有棋!请选择其他位置。"); 90 continue; 91 } 92 break; 93 } 94 bod.a[x][y]=1; 95 //OutBoard(bod); 96 printf("你下到了(%d,%d)位置。\n",x,y); 97 puts(""); 98 // puts("(按回车键继续)"); 99 // getch(); 100 // system("cls"); 101 } 102 103 inline int Evaluate(const Board &bod) { 104 int i,j; 105 int cnt[3]; 106 int re=0; 107 REP(i,3) { 108 mz(cnt); 109 REP(j,3)cnt[bod.a[i][j]]++; 110 if(cnt[1]==3)return 1000; 111 if(cnt[2]==3)return -1000; 112 if(cnt[1]==2&&cnt[0]==1)re+=50; 113 else if(cnt[1]==1 && cnt[0]==2)re+=10; 114 if(cnt[2]==2&&cnt[0]==1)re-=50; 115 else if(cnt[2]==1 && cnt[0]==2)re-=10; 116 mz(cnt); 117 REP(j,3)cnt[bod.a[j][i]]++; 118 if(cnt[1]==3)return 1000; 119 if(cnt[2]==3)return -1000; 120 if(cnt[1]==2&&cnt[0]==1)re+=50; 121 else if(cnt[1]==1 && cnt[0]==2)re+=10; 122 if(cnt[2]==2&&cnt[0]==1)re-=50; 123 else if(cnt[2]==1 && cnt[0]==2)re-=10; 124 } 125 mz(cnt); 126 REP(i,3) { 127 cnt[bod.a[i][i]]++; 128 } 129 if(cnt[1]==3)return 1000; 130 if(cnt[2]==3)return -1000; 131 if(cnt[1]==2&&cnt[0]==1)re+=50; 132 else if(cnt[1]==1 && cnt[0]==2)re+=10; 133 if(cnt[2]==2&&cnt[0]==1)re-=50; 134 else if(cnt[2]==1 && cnt[0]==2)re-=10; 135 mz(cnt); 136 REP(i,3) { 137 cnt[bod.a[i][2-i]]++; 138 } 139 if(cnt[1]==3)return 1000; 140 if(cnt[2]==3)return -1000; 141 if(cnt[1]==2&&cnt[0]==1)re+=50; 142 else if(cnt[1]==1 && cnt[0]==2)re+=10; 143 if(cnt[2]==2&&cnt[0]==1)re-=50; 144 else if(cnt[2]==1 && cnt[0]==2)re-=10; 145 return re; 146 } 147 148 Board boa; 149 150 inline int dfs(const int &depth, const int &nowWho) { 151 int i,j,t,ma=-100000,mi=100000,ok=0; 152 int eva = Evaluate(boa); 153 if(depth==0 || (eva>=1000)|| (eva<=-1000)) { 154 return eva; 155 } 156 157 REP(i,3) { 158 REP(j,3) { 159 if(boa.a[i][j]!=0)continue; 160 ok=1; 161 boa.a[i][j]=nowWho+1; 162 t=dfs(depth-1 , nowWho^1); 163 boa.a[i][j]=0; 164 ma=max(t,ma); 165 mi=min(t,mi); 166 } 167 } 168 if(!ok)return eva; 169 if(nowWho==0)return ma; 170 if(nowWho==1)return mi; 171 } 172 173 void ComputerMove(Board &bod) { 174 int x,y,i,j; 175 boa=bod; 176 puts("电脑:“该我啦!看我思考一下...”"); 177 vector<pair<int , pair<int,int> > > v; 178 v.clear(); 179 REP(i,3)REP(j,3) { 180 if(boa.a[i][j]!=0)continue; 181 boa.a[i][j]=2; 182 v.pb(mp(dfs(9,0),mp(i,j))); 183 boa.a[i][j]=0; 184 } 185 sort(v.begin(),v.end()); 186 j=1; 187 while(j<v.size() && v[j].first ==v[0].first) 188 j++; 189 if(j>1)printf("电脑:“想完啦!我有%d种不同的效果差不多的棋可以走,看我随便走一个!”\n",j); 190 else printf("电脑:“想完啦!看我要走出这步惊天之棋了!”\n"); 191 if(v.size()>j && v[j].first==1000)puts("电脑:“要是我走除此之外的棋我就输了,我已经看出来了。”"); 192 if(v[0].first==-1000)puts("电脑:“现在,高下立判,你输定了!”"); 193 j=rand()%j; 194 x=v[j].second.first; 195 y=v[j].second.second; 196 bod.a[x][y]=2; 197 //OutBoard(bod); 198 printf("电脑:“经过我的精确思考,我下到了(%d,%d)位置。”\n",x,y); 199 puts(""); 200 } 201 202 int WhoWin(Board bod) { 203 int i,j,k=0; 204 int t = Evaluate(bod); 205 if(t==1000)return 1; 206 if(t==-1000)return 2; 207 REP(i,3)REP(j,3)if(bod.a[i][j]!=0)k++; 208 if(k==9)return 3; 209 return 0; 210 } 211 212 void Game() { 213 int playerFirst, nowMove, whoWin, continueGame=1; 214 Board now; 215 int t; 216 int step; 217 srand(time(NULL)); 218 while(continueGame) { 219 system("cls"); 220 puts("欢迎来到 【带鱼人工智能博弈树井字棋游戏】!"); 221 playerFirst = GetInput("请选择玩家先后手(0先手,1后手):",2); 222 if(playerFirst==0){ 223 puts("电脑:“就算你选先手,我也不会输,不信走着瞧!”"); 224 }else puts("电脑:“居然敢选后手,要是你稍有不慎,就输定了!”"); 225 now.Init(); 226 printf("(棋盘说明:%c是空位,%c是你的棋子,%c是电脑的棋子,上面是横坐标,左边是纵坐标)\n\n",CHESS[0],CHESS[1],CHESS[2]); 227 nowMove=playerFirst; 228 whoWin=0; 229 step=0; 230 while(!whoWin) { 231 printf("【第%d手】\n",++step); 232 if(nowMove==0) { 233 PlayerMove(now); 234 } else { 235 ComputerMove(now); 236 } 237 whoWin=WhoWin(now); 238 if(whoWin!=0) { 239 OutBoard(now); 240 if(whoWin==1) { 241 printf("电脑:“哇,你居然赢了,真是不敢相信!我输了!”\n"); 242 } 243 if(whoWin==2) { 244 printf("电脑:“蛤铪哈!我赢了,你太弱啦!”\n"); 245 } 246 if(whoWin==3) { 247 printf("电脑:“平局!你还是有点实力,但是我是绝对不会输的!”\n"); 248 } 249 } 250 nowMove^=1; 251 } 252 continueGame=GetInput("是否继续游戏?(0结束游戏,1重新开始):",2); 253 } 254 } 255 256 int main() { 257 Game(); 258 return 0; 259 }
2017.1.20 update:
187行由
if(v.size()>1)while(v[j].first ==v[0].first)j++;
改为:
while(j<v.size() && v[j].first ==v[0].first) j++;