目录
前言:在之前我们就写了简易版的三子棋,在电脑下棋的时候,电脑像一个傻子一样,随机下棋。
在玩家马上要赢得时候,也不会去堵玩家的棋。
今天我们要优化的就是:
1.电脑会去堵截玩家
2.电脑会主动进攻。
一.优化:
1.斜向下判断:
玩家快赢的时候,也就是两个格子都是玩家棋的时候,电脑会主动拦截。
前两次的都下的玩家的棋,如果这一次也是玩家下棋,那么电脑会主动赢下比赛。
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋:>\n");
while (1)
{
x = rand() % row;
x = rand() % col;
int i = 0;
int j = 0;
int a = 0;
int b = 0;
int count1 = 0;
int count2 = 0;
if (board[1][1] == ' ')
{
//如果中间格子为空格,电脑就下棋把它占下
//占下中间的空格,赢得概率比较大
board[1][1] = '#';
break;
}
//判断斜向下
for (i = 0, j = 0, count1 = 0, count2 = 0; i < ROW, j < COL; i++, j++)
{
if (board[i][j] == '#')
count1++;
else if (board[i][j] == '*')
count2++;
else
{
//如果board[i][j]=' '时,a就等于i,b就等于j
//下面的board[a][j]='#',就会在这个空格上面下棋
a = i;
b = j;
}
}
if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
{
//如果count1是2的话,那么就是之前有两个格子是#,下面电脑就会下#,从而赢下比赛
//如果count2是2的话,那么就是之前有两个格子是*,下面电脑就会下#,来拦截玩家
//这里count1+count2不能等于3。如果等于3了,那这斜向下的三个位置都已经下了棋了,
//后面也就没法下棋了
board[a][b] = '#';
goto end;//这里我们最后说
}
2.斜向上判断:
这里和上面的代码是差不多的,基本上一样,我们知道,斜向上的三个格子的数组坐标分别为
[2][0],[1][1]和[0][2],所以下面for循环里面的内容和上面有一点点差别。其他代码和上面的代码是一样的。
for (i = 0, j = COL - 1, count1 = 0, count2 = 0; i < ROW, j >= 0; i++, j--)
{
if (board[i][j] == '#')
count1++;
else if (board[i][j] == '*')
count2++;
else
{
a = i;
b = j;
}
}
if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
{
board[a][b] = '#';
goto end;
}
3.行判断:
for (i = 0; i < ROW; i++)
{
count1 = 0;
count2 = 0;
for (j = 0; j < COL; j++)
{
if (board[i][j] == '#')
count1++;
else if (board[i][j] == '*')
count2++;
else
{
a = i;
b = j;
}
}
if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
{
board[a][b] = '#';
goto end;
}
}
4.列判断:
for (j = 0; j < COL; j++)
{
count1 = 0;
count2 = 0;
for (i = 0; i < ROW; i++)
{
if (board[i][j] == '#')
count1++;
else if (board[i][j] == '*')
count2++;
else
{
a = i;
b = j;
}
}
if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
{
board[a][b] = '#';
goto end;
}
}
二.优化电脑下棋的全部代码:
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋:>\n");
while (1)
{
x = rand() % row;
x = rand() % col;
int i = 0;
int j = 0;
int a = 0;
int b = 0;
int count1 = 0;
int count2 = 0;
if (board[1][1] == ' ')
{
//如果中间格子为空格,电脑就下棋把它占下
//占下中间的空格,赢得概率比较大
board[1][1] = '#';
break;
}
//判断斜向下
for (i = 0, j = 0, count1 = 0, count2 = 0; i < ROW, j < COL; i++, j++)
{
if (board[i][j] == '#')
count1++;
else if (board[i][j] == '*')
count2++;
else
{
a = i;
b = j;
}
}
if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
{
board[a][b] = '#';
goto end;
}
for (i = 0, j = COL - 1, count1 = 0, count2 = 0; i < ROW, j >= 0; i++, j--)
{
if (board[i][j] == '#')
count1++;
else if (board[i][j] == '*')
count2++;
else
{
a = i;
b = j;
}
}
if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
{
board[a][b] = '#';
goto end;
}
//行判断
for (i = 0; i < ROW; i++)
{
count1 = 0;
count2 = 0;
for (j = 0; j < COL; j++)
{
if (board[i][j] == '#')
count1++;
else if (board[i][j] == '*')
count2++;
else
{
a = i;
b = j;
}
}
if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
{
board[a][b] = '#';
goto end;
}
}
//列判断
for (j = 0; j < COL; j++)
{
count1 = 0;
count2 = 0;
for (i = 0; i < ROW; i++)
{
if (board[i][j] == '#')
count1++;
else if (board[i][j] == '*')
count2++;
else
{
a = i;
b = j;
}
}
if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
{
board[a][b] = '#';
goto end;
}
}
if (board[x][y] == ' ')
{
board[x][y] = '#';
end:
break;
}
}
}
上述我们还有一个问题,为什么这里会用goto语句呢?这里我们就要先学习一下goto语句的作用。
C 语言中的 goto 语句允许把控制无条件转移到同一函数内的被标记的语句。
上面代码我们写的goto end,当电脑下棋了#之后,就会执行goto语句到下一个end,可知end在最后的代码处,然后break跳出整个while循环。非常完美。
那如果我们把每一个地方的goto end换成break发生什么呢?
这里我们可以看出电脑第二次下棋的时候一次性下了2格子,这是为什么呢?那为什么第一次电脑下棋只下了1次呢?
这里就要看break的作用了,在玩家下棋的时候,没有下最中间的位置,那么电脑第一次下棋就会下在最中间的位置,又因为这里有一个break。
if (board[1][1] == ' ')
{
//如果中间格子为空格,电脑就下棋把它占下
//占下中间的空格,赢得概率比较大
board[1][1] = '#';
break;
}
所以break跳出整个循环。第二次玩家又下棋,然后就是电脑第二次下棋,这里玩家已经两个连起了,所以电脑就会在行这里下棋,来堵截玩家。
继续这里又有一个break,但是break在while循环里面的for循环里面,break只跳出for循环(在上述代码行判断可以查看,if语句在for循环里面)。
所以代码还在while循环里面运行,代码继续走到最后一个位置。
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
if语句判断棋盘还有空格没有,很明显棋盘还有很多空格,所以电脑又会随机下一个格子,之后break再跳出while循环。所以这就是为什么电脑第二次下了两个棋子。
我们的目的是无论是玩家还是电脑都是一次下一个棋,所以这里我们用goto语句,跳到最后一个break语句,然后break语句跳出整个while循环,所以电脑就只能一次下一个棋了。
试玩一下:
可以看出电脑已经变聪明了,哈哈。
三.完整代码:
game.h:
#pragma once
//函数的定义
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 3
#define COL 3
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);
//打印棋盘
void Print_Board(char board[ROW][COL], int row, int col);
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);
//判断输赢
char Is_Win(char board[ROW][COL], int row, int col);
test.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("********************\n");
printf("****** 三子棋 ******\n");
printf("****** 1.play ******\n");
printf("****** 0.exit ******\n");
printf("********************\n");
}
void game()
{
char board[ROW][COL] = { 0 };
InitBoard(board, ROW, COL);
Print_Board(board, ROW, COL);
char ret = 0;
while (1)
{
PlayerMove(board, ROW, COL);
Print_Board(board, ROW, COL);
ret = Is_Win(board, ROW, COL);
if (ret != 'C')
{
break;
}
ComputerMove(board, ROW, COL);
Print_Board(board, ROW, COL);
ret = Is_Win(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if ('#' == ret)
printf("电脑赢\n");
else if ('*' == ret)
printf("恭喜你,你赢了\n");
else if ('Q' == ret)
printf("平局\n");
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请输入:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
game.c:
#define _CRT_SECURE_NO_WARNINGS 1
//函数的实现
#include"game.h"
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
void Print_Board(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++) {
printf(" %c ",board[i][j]);
if (j < col - 1)
printf("|");
}
printf("\n");
if(i<row-1)
for (j= 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入你要下棋的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col )
{
if (board[x - 1][y - 1] == ' ') {
board[x - 1][y - 1] = '*';
break;
}
else
printf("坐标被占用,请重新输入\n");
}
else
printf("输入的坐标不合法,请重新输入\n");
}
}
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋:>\n");
while (1)
{
x = rand() % row;
x = rand() % col;
int i = 0;
int j = 0;
int a = 0;
int b = 0;
int count1 = 0;
int count2 = 0;
if (board[1][1] == ' ')
{
//如果中间格子为空格,电脑就下棋把它占下
//占下中间的空格,赢得概率比较大
board[1][1] = '#';
break;
}
//判断斜向下
for (i = 0, j = 0, count1 = 0, count2 = 0; i < ROW, j < COL; i++, j++)
{
if (board[i][j] == '#')
count1++;
else if (board[i][j] == '*')
count2++;
else
{
a = i;
b = j;
}
}
if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
{
board[a][b] = '#';
goto end;
}
for (i = 0, j = COL - 1, count1 = 0, count2 = 0; i < ROW, j >= 0; i++, j--)
{
if (board[i][j] == '#')
count1++;
else if (board[i][j] == '*')
count2++;
else
{
a = i;
b = j;
}
}
if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
{
board[a][b] = '#';
goto end;
}
//行判断
for (i = 0; i < ROW; i++)
{
count1 = 0;
count2 = 0;
for (j = 0; j < COL; j++)
{
if (board[i][j] == '#')
count1++;
else if (board[i][j] == '*')
count2++;
else
{
a = i;
b = j;
}
}
if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
{
board[a][b] = '#';
goto end;
}
}
//列判断
for (j = 0; j < COL; j++)
{
count1 = 0;
count2 = 0;
for (i = 0; i < ROW; i++)
{
if (board[i][j] == '#')
count1++;
else if (board[i][j] == '*')
count2++;
else
{
a = i;
b = j;
}
}
if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
{
board[a][b] = '#';
goto end;
}
}
if (board[x][y] == ' ')
{
board[x][y] = '#';
end:
break;
}
}
}
int Is_Full(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
char Is_Win(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
return board[i][0];
}
for (j = 0; j < col; j++)
{
if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')
return board[0][j];
}
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
return board[1][1];
if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
return board[1][1];
if (Is_Full(board, row, col))
{
return 'Q';
}
return 'C';
}
参考:【C语言】 三子棋小游戏 (超详解+游戏优化+原代码)
优化代码原作者:程序羊羊的笔记本
最后感谢大家的支持。