井字棋算法


井字棋算法

緒言

說到井字棋,也許都想起了自己小時候的時光吧。

井字棋其實很簡單,只要你去認真分析它,你就能明白什么叫做“先手不輸,后手不贏”。

算法

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<<"請輸入模式:\n【0】雙人對戰\n【1】人機easy\n【2】人機mid\n【3】人機hard\n【4】神仙打架\n【5】學習神仙\n";
144     do{
145         mode=getch()-'0';
146     }while(mode<0||mode>5);
147     diff=max(min(mode,3),1);
148     cout<<"游戲中 按下r鍵可以重新選擇\n";
149     system("pause");    
150 }
151 const string cls[9]={"sssss sssss sssss\n","ss7ss ss8ss ss9ss\n","sssss sssss sssss\n\n","sssss sssss sssss\n","ss4ss ss5ss ss6ss\n","sssss sssss sssss\n\n","sssss sssss sssss\n","ss1ss ss2ss ss3ss\n","sssss sssss sssss\n"};
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\n沒有使用搜索算法而先把所有情況列了出來\n這是一個先手不會輸,后手不會贏的游戲。\n先手所能做的就是嘗試贏,后手所能做的就是嘗試不輸。\n新手建議嘗試easy上手,大佬也別急着玩hard哦!\n祝您游戲愉快!\n"; 
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

當你如此認真的分析完井字棋之后,你就會發現,這真是一個無聊的游戲啊……

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM