C语言-黑白棋(人机对战)

徘徊边缘 提交于 2020-02-26 13:04:25
  1 #include <stdio.h>
  2 #include <conio.h>
  3 /*
  4 分析:要实现黑白棋的关键在于由计算机下棋时,智能判断最佳下棋位置。具体思路如下:
  5 1.定义一个二维数组chessboard[8][8],用于保存黑白双方所下棋子的位置。如果数组元素为0,表示该单元格为落子;如果是-1,表示该单元格是黑子;如果是1,则表示该单元格是白子。
  6 2.当一方下棋时,先检查是否有位置可下,如果没有就让对手下,如果有就让玩家或计算机下。
  7 3. 若玩家下,需要等待玩家输入下子坐标。
  8 4.若计算机下,程序需对棋盘的空位置进行判断,找出最佳的下子位置。
  9 5.重复步骤2~4,直到棋盘已满或双方都不能下子时结束。 
 10 */
 11  
 12 void Output(char chessboard[][8]); //显示棋盘中的下子情况
 13 int Check(char chessboard[][8],int moves[][8],char player);//检查一方是否有位置下子
 14 void PlayStep(char chessboard[][8],int row,int col,char player);//在指定位置下棋
 15 void AutoPlayStep(char chessboard[][8],int moves[][8],char player);//计算机思考下子
 16 int GEtMaxScore(char chessboard[][8],char player);//获取分数
 17 int BestPlay(char chessboard[][8],int moves[][8],char player);//最优下子位置 
 18 
 19 
 20 int main(){
 21     char chessboard[8][8];//保存棋盘中各单元格下子的状态
 22     int isDown[8][8] = {0};//保存棋盘中的各个位置是否可以下子,可以下的位置为1,其余为0 
 23     int row,col,x,y;
 24     int iCount = 0;//以下子的数量
 25     int player = 0;//下棋方
 26     int SkipPlay = 0;//重置下子的次数为0,若为2,则表示双方都不能下子
 27     int Score[2];//保存计算机和玩家的得分
 28     
 29     char select,ch;
 30     
 31     printf("黑白棋\n\n");
 32     printf("玩家执黑子先行,计算机执白,按Enter开始\n");
 33     
 34     
 35     scanf("%c",&select);
 36     
 37     do{
 38         
 39         if(player==0){
 40             player = 1;
 41         }else{
 42             player = 0;
 43         }
 44         
 45         for(row=0;row<8;row++){
 46             for(col=0;col<8;col++){
 47                 chessboard[row][col]=0;
 48             }
 49         }
 50         
 51         iCount = 4;//游戏开始的四颗棋子 
 52         chessboard[3][3] = chessboard[4][4] = 1;
 53         chessboard[3][4] = chessboard[4][3] = -1;
 54         
 55         printf("\n棋盘初始状态:\n");
 56         Output(chessboard);
 57         
 58         //双方下棋 
 59         do{
 60             if(player==1){//玩家下棋(黑) 
 61                 player = 0;
 62                 if(Check(chessboard,isDown,2)){
 63                     while(1){
 64                         fflush(stdin);
 65                         printf("输入下子的位置(行,列):");
 66                         scanf("%d%c",&x,&ch);
 67                         x--;//因为数组是从0开始存的 
 68                         if(ch>='a'){
 69                             y = ch - 'a' + 1;
 70                         } else{
 71                             y = ch - 'A' + 1;
 72                         }
 73                         y--;
 74                         //判断是否越界、是否已有棋子 
 75                         if(x>=0&&x<8&&y>=0&&y<8&&isDown[x][y]){
 76                             PlayStep(chessboard,x,y,2);
 77                             iCount++;
 78                             break; 
 79                         }else{
 80                             printf("坐标输入有误,请重新输入。\n\n");
 81                         }
 82                     }
 83                     
 84                     printf("\n你下子之后的状态:\n");
 85                     Output(chessboard);
 86                     printf("按任意键计算机下子\n");
 87                     getch();
 88                 }else if(++SkipPlay < 2){//无效下子的次数小于2 
 89                     fflush(stdin);
 90                     printf("你没位置可下,按Enter让计算机下子");
 91                     scanf("%c",&select); 
 92                 } else{
 93                     printf("双方都没位置下子,游戏结束!\n");
 94                 }
 95             }else{//计算机下棋(白) 
 96             
 97                 player = 1;
 98                 if(Check(chessboard,isDown,1)) {
 99                     SkipPlay = 0;//清除无效下子次数
100                     AutoPlayStep(chessboard,isDown,1); //下子
101                     iCount++;
102                     printf("\n计算机下子后的状态:\n");
103                     Output(chessboard); 
104                 }else if(++SkipPlay < 2){//无效下子的次数小于2 
105                     fflush(stdin);
106                     printf("我没位置可下,你走\n");
107                     scanf("%c",&select); 
108                 } else{
109                     printf("双方都没位置下子,游戏结束!\n");
110                 }
111             
112             
113             
114             } 
115             
116             
117         }while(iCount<64&&SkipPlay<2);
118         
119         Output(chessboard);
120         Score[0] = Score[1] = 0;//清空几份变量 
121          
122         for(row=0;row<8;row++){
123             for(col=0;col<8;col++){
124                 if(chessboard[row][col]==-1){
125                     Score[0]++;//统计黑子数量 
126                 }
127                 if(chessboard[row][col]==1){
128                     Score[1]++;//统计白子数量 
129                 }
130             }
131         }
132         printf("最终成绩:\n");
133         printf("玩家:%d\n计算机:%d\n",Score[0],Score[1]);
134         fflush(stdin);
135         printf("继续下一局(y/n)?\n");
136         scanf("%c",&select); 
137         
138     }while(select=='y'||select=='Y');
139     printf("游戏结束!");
140     getch();
141     return 0; 
142          
143 }
144 
145 
146 //显示棋盘中的下子情况
147 void Output(char chessboard[][8]){
148     int row, col;
149   printf("\n  ");
150   for (col = 0; col < 8; col++)        //输出列标号
151         printf("  %c", 'A' + col);
152   printf("\n");
153   printf("  ┌");    //输出顶部横线
154   for (col = 0; col < 7; col++)        //输出一行
155         printf("—┬");
156   printf("—┐\n");
157   for (row = 0; row < 8; row++)
158   {
159         printf("%2d|", row + 1);       //输出行号
160         for (col = 0; col < 8; col++)  //输出棋盘各单元格中棋子的状态
161         {
162         if (chessboard[row][col] == 1)  //白棋
163             printf("○|");
164         else if (chessboard[row][col] == -1)    //黑棋
165             printf("●|");
166         else   //未下子处
167             printf("  |");
168         }
169         printf("\n");
170         if (row < 8-1)
171         {
172         printf("  ├"); //输出交叉线
173         for (col = 0; col < 8-1; col++)   //输出一行
174             printf("—┼");
175         printf("—┤\n");
176         }
177   }
178   printf("  └");
179   for (col = 0; col < 8-1; col++) //最后一行的横线
180         printf("—┴");
181   printf("—┘\n");
182 }
183 
184 //检查一方是否有位置下子
185 int Check(char chessboard[][8], int isDown[][8], char player)
186 {
187   int rowdelta, coldelta, row, col, x, y = 0;
188   int iStep = 0;//可下的位置数量 
189   char opponent = (player == 1) ? -1 : 1;   //对方棋子
190   char myplayer = -1 *opponent;//我方棋子
191   for (row = 0; row < 8; row++) //将isDown数组全部清0
192         for (col = 0; col < 8; col++)
193         isDown[row][col] = 0;
194   for (row = 0; row < 8; row++) //循环判断棋盘中的哪些单元格可以下子
195   {
196         for (col = 0; col < 8; col++)
197         {
198             if (chessboard[row][col] != 0)  //若棋盘上的对应位置不为空(表示已经有子)
199                 continue;   //继续处理下一个单元格
200             for (rowdelta = -1; rowdelta <= 1; rowdelta++)  //循环检查上下行
201             {
202                 for (coldelta = -1; coldelta <= 1; coldelta++)   //循环检查左右列
203                 {
204                   if (row + rowdelta < 0 || row + rowdelta >= 8 || col + coldelta < 0 || col + coldelta >= 8|| (rowdelta == 0 && coldelta == 0))//若坐标超过棋盘或为当前单元格
205                                           continue;  //继续循环
206                   if (chessboard[row + rowdelta][col + coldelta] == opponent)//若(row,col)四周有对手下的子
207                   {
208                           x = row + rowdelta;    //以对手的下子位置为坐标
209                           y = col + coldelta;
210                           while(1)//以对手的下子为起始点,向四周查找自己方的棋子,以攻击对方棋子
211                           {
212                             x += rowdelta; //对手下子的四周坐标
213                             y += coldelta;
214                             if (x < 0 || x >= 8 || y < 0 || y >= 8) //超过棋盘
215                                 break;  //退出循环
216                               if (chessboard[x][y] == 0)          //若对应位置为空
217                                 break;
218                               if (chessboard[x][y] == myplayer) //若对应位置下的子是当前棋手的
219                             {
220                                 //设置移动数组中对应位置为1 (该位置可下子,形成向对手进攻的棋形)
221                               isDown[row][col] = 1;
222                                  iStep++;                //加可下子的位置数量
223                                 break;
224                             }
225                          }
226                   }
227                 }
228             }
229         }
230   }
231   return iStep; //返回可下的位置数量(若返回值为0,表示没地方可下)
232 }    
233 
234 
235 //在指定位置下子
236 void PlayStep(char chessboard[][8], int row, int col, char player)
237 {
238   int rowdelta = 0;
239   int coldelta = 0;
240   int x = 0;
241   int y = 0;
242   char opponent = (player == 1) ? -1 : 1;   //对方棋子
243   char myplayer = -1 * opponent;//我方棋子
244   chessboard[row][col] = myplayer;  //保存所下的棋子
245   for (rowdelta = -1; rowdelta <= 1; rowdelta++)//检查所下子四周的棋子
246   {
247         for (coldelta = -1; coldelta <= 1; coldelta++)
248         {
249             if (row + rowdelta < 0 || row + rowdelta >= 8 || col + coldelta < 0 || col + coldelta >= 8 || (rowdelta == 0 && coldelta == 0))
250                         //若坐标超过棋盘界限
251                         continue;   //继续下一位置
252                     //若该位置是对手的棋子
253                     if (chessboard[row + rowdelta][col + coldelta] == opponent)
254                     {
255                         x = row + rowdelta; //以对手的棋子作为坐标
256                         y = col + coldelta;
257                         while(1) //在对手棋子的四周寻找我方棋子
258                         {
259                           x += rowdelta;
260                           y += coldelta;
261                           if (x < 0 || x >= 8 || y < 0 || y >= 8)   //若坐标超过棋盘
262                                   break; //退出循环
263                           if (chessboard[x][y] == 0)//若对应位置为空
264                                   break; //退出循环
265                           if (chessboard[x][y] == myplayer) //若对应位置是我方棋子
266                           {
267                               //循环处理
268                               while (chessboard[x -= rowdelta][y -= coldelta] == opponent)
269                               chessboard[x][y] = myplayer;   //将中间的棋子都变成我方棋子
270                               break; //退出循环
271                           }
272                         }
273                     }
274         }
275   }
276 }
277 
278 //计算机自动下子
279 void AutoPlayStep(char chessboard[][8], int isDown[][8], char player)
280 {
281   int row, col, row1, col1, i, j;
282   int Score = 0, MinScore = 100;//对方可下子得到的分数和最小分数
283   char chessboard1[8][8];   //临时数组,保存棋盘的下子位置
284   int isDown1[8][8];    //临时数组,保存可下子的位置
285   char opponent = (player == 1) ? -1 : 1;   //对手下的棋子
286   for (row = 0; row < 8; row++) //循环检查棋盘的每个单元格
287   {
288         for (col = 0; col < 8; col++)
289         {
290         if (isDown[row][col] == 0)  //若不可下子
291             continue;           //继续下一个位置
292         for (i = 0; i < 8; i++)//将棋盘原来的棋子复制到临时数组中
293             for (j = 0; j < 8; j++)
294         chessboard1[i][j] = chessboard[i][j];
295         PlayStep(chessboard1, row, col, player);
296                                 //试着在临时棋盘中的一个位置下子
297         Check(chessboard1, isDown1, opponent);  //检查对手是否有地方可下子
298         Score = BestPlay(chessboard1, isDown1, opponent);
299                                 //获得临时棋盘中对方下子的得分情况
300         if (Score < MinScore)   //保存对方得分最低的下法
301         {
302             MinScore = Score;
303             row1 = row;
304             col1 = col;
305         }
306         }
307   }
308   PlayStep(chessboard, row1, col1, player); //计算机按最优下法下子
309 }
310 
311 //获取分数
312 int GetMaxScore(char chessboard[][8], char player)
313 {
314   int Score, row, col;
315   char opponent = (player == 1) ? -1 : 1;       //对方棋子
316   char myplayer=-1*opponent;
317   for (row = 0; row < 8; row++)                 //循环
318   {
319         for (col = 0; col < 8; col++)
320         {
321         //如果棋盘对应位置是对手下的棋子,从总分中减1分
322         Score -= chessboard[row][col] == opponent;
323         //如果棋盘对应位置是我方的棋子,在总分中加1分
324         Score += chessboard[row][col] == myplayer;
325         }
326   }
327   return Score;                                 //返回分数
328 }
329 
330 //最优下子位置 
331 int BestPlay(char chessboard[][8], int isDown[][8], char player)
332 {
333   int row, col, i, j;
334   char chessboard1[8][8] = { 0 };   //定义一个临时数组
335   int MaxScore = 0;                 //保存最高分
336   int Score = 0;
337   char opponent = (player == 1) ? -1 : 1;//对手下的棋子
338   for (row = 0; row < 8; row++)     //循环检查每个单元格
339   {
340         for (col = 0; col < 8; col++)
341         {
342         if (!isDown[row][col])      //如果该位置不可下子
343             continue;               //继续检查
344         for (i = 0; i < 8; i++)     //复制棋盘各单元格下子的状态到临时数组
345             for (j = 0; j < 8; j++)
346               chessboard1[i][j] = chessboard[i][j];
347         PlayStep(chessboard1, row, col, player);    //在临时数组中的指定行列下子
348         Score = GetMaxScore(chessboard1, player);   //获取下子后可得到的分数
349         if (MaxScore < Score)       //若原方案得到的分数小于本次下子的分数
350             MaxScore = Score;       //保存最高分
351         }
352   }
353   return MaxScore;                  //返回得到的最高分
354 }

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!