1. 游戏介绍
三子棋是很简单的双人游戏,双方在3X3的棋盘上轮流落子,当一条直线上出现三颗连续且相同的棋子时即获胜。此程序让玩家先落子,电脑在棋盘上随机落子。游戏结束时显示胜负,玩家可以选择是否继续玩游戏。
2. 思路
- 游戏菜单,选择玩/不玩
- 打印棋盘
- 玩家落子
- 打印棋盘
- 电脑落子
- 打印棋盘
- …
- 显示胜负
- 返回游戏菜单,选择玩/不玩
3. 具体步骤
1. 游戏菜单menu()
弹出游戏菜单选择玩/不玩,选择不玩,退出;选择玩,进入游戏,游戏结束后,再次弹出让玩家选择。因为程序一开始就需要弹出,并且每次游戏结束后再次弹出,可用do while来实现。
void menu()
{
printf("****************\n");
printf("*** 1.play ***\n");
printf("*** 0.play ***\n");
printf("****************\n");
}
int main()
{
int input = 0;
srand(time(NULL));
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("游戏结束\n");
break;
default:
printf("请重新选择\n");
break;
}
} while (input);
system("pause");
return 0;
}
2. 游戏函数game()
在这个函数中,需要实现游戏的运行,包括打印棋盘,玩家落子,打印棋盘,电脑落子,打印棋盘…同时每次落完子后需要判段游戏的胜负,如果分出胜负,显示;没有分出胜负继续。
2.1 打印棋盘
为了方便理解同时给出了空棋盘(左)和落子后(右)的棋盘,落子后‘X’的左右两边是空隔,而‘X’存储在一个二维数组数中,在没有落子时,这个二维数组的内容显示在每个小方格的中央,在没落子时存储的是一个空格‘ ’,因此空棋盘就如左示。 用print()函数来实现打印棋盘。void Print(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");
for (j = 0; j < col; j++)
{
if (i < row - 1)
{
printf("---");
if (j < col - 1)
printf("|");
}
}
printf("\n");
}
}
在打印棋盘之需要先创建一个board[3][3]的数组,刚开始是数组存储的内容为空格‘ ’,用Inic()函数实现。
void Inic(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] = ' ';
}
}
}
2.2 玩家落子
玩家落子其实就是选择数组中的一个数,然后把它所存储的‘ ’换成‘ X’,电脑落子也是一样只不过是换成‘ 0’。不过需要注意的是数组是从[0][0]开始的,而玩家在棋盘落子时选择的坐标从1 1开始,另外在玩家和电脑落子前需要判断,选择的坐标是否可以落子(如选择坐标外,或选的地方已经落过子了),在落子结束后判断游戏胜负。
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 (board[x-1][y-1] == ' ')
{
board[x-1][y-1] = 'X';
break;
}
else
printf("无法在此下子,请重新选择\n");
}
}
落子后需要打印棋盘,同时判断胜负。
2.3 电脑走
这里电脑的走法是随机的,只要满足条件,在棋盘上随意落子。让电脑随机生成x和y,给定范围0-2;如果可以落子,修改‘ ’为‘0’然后跳出循环,否则进行下一次循环。 注意:
X,Y 需要放在while的里面,如果放在外面,一旦随机的位置不能落子,陷入死循环!另外在刚开时因为玩家只下了一次,电脑随机的位置大概率不会与玩家相同,可以落子,玩家再次落子,电脑可能生成的随机位置仍可以落子,但是一般两次后即陷入死循环,电脑不再落子。 |
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑走:>\n");
int x = 0;
int y = 0;
while (1)
{
x = rand() % ROW;
y = rand() % COL;
if (board[x][y] == ' ')
{
board[x][y] = '0';
break;
}
}
}
上面的方法电脑采用的是随机的走法,想要让游戏更有挑战可以采用下面的写法,以下电脑不再随机落子,而是在落子前进行判断,如果玩家已经有两子在一条直线,电脑选择堵,如果没有,电脑会选择较优的位置落子,电脑将会利于不败。
2.3.1 电脑落子优化
让电脑选择更优位置落子,需要知道何为最优位置,这是对游戏玩法的理解,三字棋是一个简单的游戏,不论先落子还是后落子,只要走法正确 ,都可以利于不败。
三子棋是一个3X3的棋盘,一共有九个位置可以选择,在这九个位置中哪些位置相对较好,为什么呢?
- 最中心的位置:可以与横竖斜共四条线上的棋子组成三子棋。
- 四个角上的位置:可以与横竖斜共三条线上的棋子组成三子棋。
- 每天边的中心位置:可以与横竖共二条线上的棋子组成 三子棋。
在明白这个道理后,可以让电脑落子时进行选择
1 .判断玩家再落一子是否胜利,如果是,电脑在此落子,堵。
2 .中心位置可否落子?
3 .四个角可否落子?
4 .四条边的中心位置。
电脑在每次落子前依次进行这样的判段,即可实现不败。代码如下:
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑走:>\n");
int x = 0;
int y = 0;
while (1)
{
for (x = 0; x <= 2; x++)//当玩家有两个子连成一条线时,堵!
{
for (y = 0; y <= 2; y++)
{
if (board[x][y] == ' ')
{
board[x][y] = 'X';
if (IsWin(board, ROW, COL) == 'X')
{
board[x][y] = '0';//一旦落子,就直接跳出while循环,后面都一样
return 0;
}
else
board[x][y] = ' ';//还原
}
}
}
if (board[1][1] == ' ')//如果中间可以落子,在中间落子
{
board[1][1] = '0';
return 0;
}
else //如果四个角可以落子,在四个角上落子。
{
for (x = 0; x <= 2; x+=2)
{
for (y = 0; y <= 2; y += 2)
{
if (board[x][y] == ' ')
{
board[x][y] = '0';
return 0;
}
}
}
x = rand() % ROW;//四个边的中间随机落子,0—2
y = rand() % COL;
if (board[x][y] == ' ')
{
board[x][y] = '0';
}
}
}
}
3 判断胜负
判断胜负只会出现四种情况,用IsWin()函数来判断返回值类型为char,返回‘X’代表玩家赢,返回‘0’代表电脑赢,返回‘Q’代表平局,返回‘ ’代表继续。这里判断胜负的办法很愚蠢,即对每一条直线进行判断,横竖斜一共八条,如果相同,将直线上任意元素返回。如果没有分出胜负,接着判断是否为平局,即没有元素为‘ ’,若不是平均,继续。
char IsWin(char board[ROW][COL], int row, int col)
{
int i = 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 (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board [0][i]!= ' ')
return board[0][i];
}
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
return board[1][1];
else if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
return board[1][1];
else if (IsFull(board, ROW, COL)==1)
return 'Q';
else
return ' ';
}
int IsFull(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;
}