井字棋算法

Posted send-off-a-friend

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了井字棋算法相关的知识,希望对你有一定的参考价值。

井字棋算法

绪言

说到井字棋,也许都想起了自己小时候的时光吧。

井字棋其实很简单,只要你去认真分析它,你就能明白什么叫做“先手不输,后手不赢”。

算法

V1

随机算法。

扫描全局找出所有空位。

随机一个空位,下子。

V2

先看看自己有没有已经构成两个一空的

  O

    

X X O 

(只是打个比方)

标红的地方都是

有的话就下子

如果没有再看看敌人是否已经构成了两子一空。

  如果敌人构成了则要将其破坏(下子)

  如果敌人也没有,就走V1

V3

首先搭载V2

在判断完没有两子一空之后,就可以开始动笔了。

大量实验证明,先手下角赢的概率最大

技术图片

(伪)

这是为什么呢?

因为这是一个套路:

如果你的棋盘是这样的

技术图片

先下角

技术图片

 对手走4或2

技术图片

那我就继续走一个邻角

这时候2P就必须走4否则2P就输了

这时候,我只需要继续走另一个邻角就好

技术图片

这时,789和159都构成了两子一空,无论对方走哪里,只要补齐另一个空就行了


要是先手走角,后手也走角呢?

那我就继续走角

技术图片

这是2P只能走4

接着我继续走剩下的一个角9

技术图片

2P又陷入了僵局


那要是先手走角,后手走中间呢?

技术图片

那先手就走对角9(走邻角7或3会平局,自己试试咯)

技术图片

此时,后手可以走角37或者边2468

如果后手走角3

技术图片

那先手继续走角即可,如图,147,789都构成了两空一子

如果后手走边4 那先手根据V2只能走6

技术图片

后手根据V2只能走3

技术图片

先手根据V2只能走7

后手根据V2只能走8

技术图片

结果就只有平局的份了……

 

所以,先手走边时,后手唯一造成平局的机会就是

先走中心,再走边

Code

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<cmath>
  6 #include<set>
  7 #include<queue>
  8 #include<vector>
  9 #include<windows.h>
 10 #include<sstream>
 11 #include<ctime>
 12 #include<conio.h>
 13 #define IL inline
 14 #define re register
 15 #define LL long long
 16 using namespace std;
 17 /*
 18 sssss sssss sssss
 19 ss7ss ss8ss ss9ss
 20 sssss sssss sssss
 21 
 22 sssss sssss sssss
 23 ss4ss ss5ss ss6ss
 24 sssss sssss sssss
 25 
 26 sssss sssss sssss
 27 ss1ss ss2ss ss3ss
 28 sssss sssss sssss
 29 s==space
 30 */
 31 
 32 int mode=0;//0 2p 1 easy 2 mid 3 hard
 33 int diff;
 34 int map[3][3];//0 空 1 1P 2 2P||AI 
 35 int stats[3];//0 ping 1 1P 2 2P  
 36 void set_stats(){
 37     stringstream ss;
 38     ss<<"title total:"<<stats[0]+stats[1]+stats[2]<<";stats:1P:P:2P="<<stats[1]<<":"<<stats[0]<<":"<<stats[2]<<"="<<stats[1]*1.0/stats[0]<<":1:"<<stats[2]*1.0/stats[0]<<":"<<stats[1]*1.0/stats[2];
 39     system(ss.str().c_str());
 40     if(stats[0]+stats[1]+stats[2]==10000) system("pause");
 41 }
 42 struct xy{
 43     int x;
 44     int y;
 45     int num(){
 46         return (2-x)*3+y+1;//7-3x+y=num() 3x-y=7-num() 3x=7-num()+y
 47     }
 48     bool operator!=(xy z){
 49         if(x==z.x&&y==z.y) return 0;
 50         return 1;
 51     }
 52     bool operator==(xy z){
 53         if(x==z.x&&y==z.y) return 1;
 54         return 0;
 55     }
 56     int value(){
 57         return map[x][y];
 58     }
 59     xy eof(){
 60         return {-1,-1};
 61     }
 62     xy up(){
 63         xy ans=*this;
 64         if(x<2&&x>=0) ans.x++;
 65         else ans={-1,-1};
 66         return ans;
 67     }
 68     xy down(){
 69         xy ans=*this;
 70         if(x>0&&x<3) ans.x--;
 71         else ans=eof();
 72         return ans;
 73     }
 74     xy left(){
 75         xy ans=*this;
 76         if(y>0&&y<3) ans.y--;
 77         else ans=eof();
 78         return ans;
 79     }
 80     xy right(){
 81         xy ans=*this;
 82         if(y<2&&y>=0) ans.y++;
 83         else ans=eof();
 84         return ans;
 85     }
 86     xy edge(int w){
 87         if(*this==eof()) return eof();
 88         xy ans=*this;
 89         if(w<3) ans=ans.up();
 90         if(w>=2&&w<=4) ans=ans.right();
 91         if(w>=4&&w<=6) ans=ans.down();
 92         if((w>=6&&w<8)||w==0) ans=ans.left(); 
 93         return ans;
 94     }
 95     int point(){
 96         if(num()==5)return 0;
 97         if(num()%2) return 2;
 98         return 1;
 99     }
100 };
101 xy turn(int num){
102     xy ans;
103     ans.y=(num-1)%3;
104     ans.x=(7-num+ans.y)/3;
105     return ans;
106 }
107 xy eof(){
108     return {-1,-1};
109 }
110 unsigned short lb,lf;
111 const int A=10,B=11,C=12,D=13,E=14,F=15;
112 void SetColor(unsigned short BackGroundColor,unsigned short ForeColor)
113 {
114     HANDLE hCon=GetStdHandle(STD_OUTPUT_HANDLE);  
115     SetConsoleTextAttribute(hCon,(ForeColor%16)|(BackGroundColor%16*16));  
116 }
117 int lx,ly;
118 void getxy()
119 {
120     HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
121     CONSOLE_SCREEN_BUFFER_INFO csbi;
122     GetConsoleScreenBufferInfo(hConsole, &csbi);
123     lx=csbi.dwCursorPosition.X,ly=csbi.dwCursorPosition.Y;
124 }
125 void gotoxy(int x, int y)
126 {
127     COORD pos;
128     pos.X = x - 1;
129     pos.Y = y - 1;
130     SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);
131 }
132 IL void backxy()
133 {
134     COORD pos;
135     pos.X = lx;
136     pos.Y = ly;
137     SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);
138 }
139 int main(); 
140 void init(){
141     for(int i=1;i<10;i++) map[turn(i).x][turn(i).y]=0;
142     system("cls");
143     cout<<"请输入模式:
【0】双人对战
【1】人机easy
【2】人机mid
【3】人机hard
【4】神仙打架
【5】学习神仙
";
144     do{
145         mode=getch()-0;
146     }while(mode<0||mode>5);
147     diff=max(min(mode,3),1);
148     cout<<"游戏中 按下r键可以重新选择
";
149     system("pause");    
150 }
151 const string cls[9]={"sssss sssss sssss
","ss7ss ss8ss ss9ss
","sssss sssss sssss

","sssss sssss sssss
","ss4ss ss5ss ss6ss
","sssss sssss sssss

","sssss sssss sssss
","ss1ss ss2ss ss3ss
","sssss sssss sssss
"};
152 void show(){
153     system("cls");
154     for(int i=0;i<9;i++) cout<<cls[i];
155     memset(map,sizeof(map),0);
156 }
157 int fp;//先手 
158 vector<int>step;
159 xy algo(int p,int m=diff){//1 随机 2 找2排2 3 特殊 
160     int dr=p==1?2:1;
161     vector<xy>line;
162     if(m>1){
163         //找出自己二连 
164         for(int n=1;n<10;n++)
165         for(int k=0;k<8;k++){
166             if(turn(n).value()==0){
167                 if(turn(n).edge(k)!=eof()&&turn(n).edge(k).value()==p&&turn(n).edge(k).edge(k)!=eof()&&turn(n).edge(k).edge(k).value()==p) return turn(n); 
168                 if(turn(n).edge(k)!=eof()&&turn(n).edge(k).value()==p&&turn(n).edge((k+4)%8)!=eof()&&turn(n).edge((k+4)%8).value()==p) return turn(n);
169             }
170         }
171         //排除敌人二连 
172         for(int n=1;n<10;n++)
173         for(int k=0;k<8;k++){
174             if(turn(n).value()==0){
175                 if(turn(n).edge(k)!=eof()&&turn(n).edge(k).value()==dr&&turn(n).edge(k).edge(k)!=eof()&&turn(n).edge(k).edge(k).value()==dr) return turn(n); 
176                 if(turn(n).edge(k)!=eof()&&turn(n).edge(k).value()==dr&&turn(n).edge((k+4)%8)!=eof()&&turn(n).edge((k+4)%8).value()==dr) return turn(n);
177             }
178         }
179         if(m==3||(m==2&&rand()%10<4)){
180             /*
181             第一步走角        无明显差异
182             第二步走角        无明显差异 
183             优先走角        优 1.3427:1:0.8555 1.5694
184             除前两步走角    优 1.3582:1:0.8667 1.5670
185             优先走中心        优 2.0000:8:1.0000 2.0000 
186             中心->角        优 0.4090:1:0.1468 2.7849
187             角->中心        优 1.4097:1:0.8786 1.6044 
188             */
189             if(step.size()+1==2)//如果是第二步 
190             {
191                 if(turn(step[0]).point()==2) return turn(5);//先手走角就走中间 
192             }
193             if(step.size()+1==3)//如果是第三步 
194             {
195                 if(turn(step[0]).point()==2)//并且第一步走的是角 
196                 {
197                     if(turn(step[1]).point()==0)//如果后手走中心
198                     {
199                         for(int k=0;k<8;k+=2) if(turn(step[0]).edge(k).edge(k)!=eof()) return turn(step[0]).edge(k).edge(k);
200                     } 
201                 }    
202             }
203             if(step.size()+1==4)//如果是第四步 
204             {
205                 if(turn(step[0]).point()==2&&turn(step[1]).point()==0&&turn(step[2]).point()==2&&step[0]+step[2]==10)//1角2中3对角则4边 
206                 {
207                     if(!turn(2).value()) line.push_back(turn(2));
208                     if(!turn(4).value()) line.push_back(turn(4));
209                     if(!turn(6).value()) line.push_back(turn(6));
210                     if(!turn(8).value()) line.push_back(turn(8));
211                     random_shuffle(line.begin(),line.end());
212                     if(!line.empty()) return line.front(); 
213                 } 
214             }
215             
216             {
217                 //
218                 if(!turn(1).value()) line.push_back(turn(1));
219                 if(!turn(3).value()) line.push_back(turn(3));
220                 if(!turn(7).value()) line.push_back(turn(7));
221                 if(!turn(9).value()) line.push_back(turn(9));
222                 random_shuffle(line.begin(),line.end());
223                 if(!line.empty()) return line.front(); 
224             }
225             {
226                 //中心
227                 if(!turn(5).value()) return turn(5); 
228             }
229             {
230                 if(!turn(2).value()) line.push_back(turn(2));
231                 if(!turn(4).value()) line.push_back(turn(4));
232                 if(!turn(6).value()) line.push_back(turn(6));
233                 if(!turn(8).value()) line.push_back(turn(8));
234                 random_shuffle(line.begin(),line.end());
235                 if(!line.empty()) return line.front(); 
236             } 
237             
238             //找到最优
239         }
240     }
241     //随机
242     for(int x=0;x<3;x++)
243     for(int y=0;y<3;y++)
244         if(map[x][y]==0) line.push_back({x,y});
245     random_shuffle(line.begin(),line.end());
246     return line.front();
247 }
248 
249 void edit(xy p,int c){
250 //    return;////////////////////////////////////////////////////////////////////////////
251     getxy();
252     if(c==1) SetColor(7,A);
253     if(c==2) SetColor(A,7);
254     if(c==3) SetColor(F,C);
255     for(int i=p.x*3;i<p.x*3+3;i++)
256     for(int j=p.y*6;j<p.y*6+5;j++)
257     {
258         gotoxy(j+1,i+1+p.x);cout<<cls[i][j];
259     }
260     backxy();
261     SetColor(0,7);
262 }
263 
264 void win(int n,int k,int p){
265     step.clear();
266     gotoxy(1,13);
267     if(p==-1){
268         cout<<"平局!";
269         stats[0]++;
270     } 
271     if(p==1){
272         edit(turn(n),3);
273         edit(turn(n).edge(k),3);
274         edit(turn(n).edge(k).edge(k),3);
275         cout<<"1P wins!";
276         stats[1]++;
277     }
278     if(p==2){
279         edit(turn(n),3);
280         edit(turn(n).edge(k),3);
281         edit(turn(n).edge(k).edge(k),3);
282         cout<<"2P wins!";
283         stats[2]++;
284     }
285     set_stats();
286     if(mode!=4) system("pause"); 
287     system("cls");
288     for(int i=1;i<10;i++) map[turn(i).x][turn(i).y]=0;
289     show();
290 }
291 
292 int check(){
293     for(int n=1;n<10;n++)
294     for(int k=0;k<8;k++)
295     if(turn(n).value())
296     if(turn(n).edge(k)!=eof()&&turn(n).edge(k).edge(k)!=eof())
297     if(turn(n).value()==turn(n).edge(k).value()&&turn(n).edge(k).value()==turn(n).edge(k).edge(k).value())
298     {
299         win(n,k,turn(n).value());
300         return turn(n).value();
301     }
302     bool flag=1;
303     for(int n=1;n<10;n++){
304         if(turn(n).value()==0){
305             flag=0;
306             break; 
307         } 
308     }
309     if(flag){
310         win(-1,-1,-1);
311         return 3;
312     }
313     return 0;
314 }
315 
316 void first(){
317     if(mode==4||mode==5){
318         gotoxy(1,13);
319         cout<<"AI:";
320         xy n;
321         if(mode!=4) Sleep((5-mode)*100);
322         if(mode==5) Sleep(1000);
323         n=algo(1);
324         step.push_back(n.num());
325         edit(n,1);
326         map[n.x][n.y]=1;
327     }
328     else{
329         int n;
330         do{
331             gotoxy(1,13);
332             cout<<"1P:";
333             n=getch();
334             if(n==r){
335                 main();
336                 exit(0);
337             }
338             if(n==a){
339                 n=algo(1,3).num()+0;
340             }
341             if(n==d){
342                 n=algo(1,2).num()+0;
343             }
344             n-=0;
345         }while(n<1||n>9||map[turn(n).x][turn(n).y]!=0);
346         step.push_back(n);
347         edit(turn(n),1);
348         map[turn(n).x][turn(n).y]=1;
349     }
350     
351 }
352 void second(){
353     if(mode!=0){
354         gotoxy(1,13);
355         cout<<"AI:";
356         xy n;
357         if(mode!=4) Sleep((5-mode)*100);
358         if(mode==5) Sleep(1000);
359         n=algo(2);////////////////////////////可调整AI2的难度 
360         step.push_back(n.num());
361         edit(n,2);
362         map[n.x][n.y]=2;
363     }
364     else{
365         int n;
366         gotoxy(1,13);
367         cout<<"2P:";
368             do{
369                 n=getch();
370                 if(n==r){
371                     main();
372                     exit(0);
373                 }
374                 if(n==a){
375                     n=algo(1,3).num()+0;
376                 }
377                 if(n==d){
378                     n=algo(1,2).num()+0;
379                 }
380                 n-=0;
381         }while(n<1||n>9||map[turn(n).x][turn(n).y]!=0);
382         step.push_back(n);
383         edit(turn(n),2);
384         map[turn(n).x][turn(n).y]=2;
385     }
386         
387     
388 }
389 
390 void about()
391 {
392     system("cls"); 
393     cout<<"井字棋 最终版 code by SOAF
没有使用搜索算法而先把所有情况列了出来
这是一个先手不会输,后手不会赢的游戏。
先手所能做的就是尝试赢,后手所能做的就是尝试不输。
新手建议尝试easy上手,大佬也别急着玩hard哦!
祝您游戏愉快!
"; 
394     system("pause");
395     system("cls");
396 }
397 
398 int main()
399 {
400     srand(clock());
401     about();
402     init();
403     show();
404     set_stats();
405     while(true){
406         fp=1;
407         while(true){
408             first();
409             if(check()==1){
410                 break;
411             }
412             second();
413             if(check()==2){
414                 break;
415             }
416         }
417         if(kbhit()){
418             if(getch()==r) main(),exit(0);
419         }
420         fp=2;
421         while(true){
422             second();
423             if(check()){
424                 break;
425             }
426             first();
427             if(check()){
428                 break;
429             }
430         }
431     }
432     return 0;
433 }

代码较长但功能齐全。不需要的请适当删减!

End

当你如此认真的分析完井字棋之后,你就会发现,这真是一个无聊的游戏啊……

 

以上是关于井字棋算法的主要内容,如果未能解决你的问题,请参考以下文章

井字棋算法

井字棋

找到所有获胜的井字棋棋盘状态的集合

C语言实现三子棋(井字棋)

(教你简单地C语言黑框框三子棋(井字棋)

JavaScript写的一个带AI的井字棋