目录
一、建立源文件与头文件
二、呈现初始化菜单和玩家的选择面板
三、呈现初始化棋盘
四、玩家下棋
五、电脑下棋
六、判断输赢
七、从人工智障变成人工智能
八、代码总结
序言:想必大家都应该玩过三字棋吧,这是我们童年时期都喜欢玩的小游戏,因为对局简单,没事的时候就来玩几局,当我们学习了c语言了,我们怎么来用c语言来实现这个简单的小游戏呢,现在就由我来分享如何用c语言来实现三字棋的实现过程。
由于game函数中需要定义和引用的函数较多,因此需要定义一个.h为后缀的头文件来声明各个函数,还有库函数使用所需的头文件,以及需要定义的常量,game.c文件在该源文件中实现游戏过程中具体的函数,test.c在该源文件里实现三字棋的主体流程。
游戏菜单的打印以及玩家的选择界面都放在main函数中,先搭建一个菜单函数menu(),再搭建一个供玩家选择的函数test(),这里想要实现的是玩家输入为1则开始游戏,输入为0则退出游戏。再将test()与menu()都放在main的主函数中,代码实现如下:
void menu() { //游戏界面
printf("**********菜单********\n");
printf("****1->play****0->exit\n");
printf("**********************\n");
}
void test() {
int input = 0;
do{
scanf("%d", &input);
if (input == 1)
printf("游戏开始\n");
switch (input)
{
case 1:
game()
break;
case 0:
printf("退出游戏\n:");
break;
default:
printf("输入错误,请重新输入\n:");
break;
}
} while (input);
}
int main() {
menu();
test();
return 0;
}
代码说明:在test()函数当中,我运用了do.....while语句来实现游戏的进入与结束,保证游戏界面至少进入一次,用swich分支来实现如果输入0,游戏结束,输入1,输入其他数字,就会提醒输入错误,请重新输入,游戏开始,游戏内容由game()函数实现游戏逻辑。
在玩家输入1之后,游戏开始。那么,为了更直观的游戏体验,开始我想让玩家能看到一个空棋盘,并且之后能在这个空棋盘上落子。那么,这该如何实现呢?
请看下面一段初始化代码;
void init_board(char arr[ROW][COL],int row,int col) {
int i = 0;
int j = 0;
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
arr[i][j] = ' ';
}
}
说明:通过两层for循环对棋盘的内容进行初始化,初始化是打印“ ”。
下面这一段代码就是来进行打印棋盘的操作了:
void display_board(char board[ROW][COL], int row, int col) {
int i = 0;
for (i = 0; i < row; i++)
{
//数据
printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
//---|---|---
if(i
注意:这两段代码都是写在game.c文件中,在game.h文件中要记得声明,在game()函数中进行调用。
不过大家有没有发现一个问题;我写的这一个代码,它只能打印三乘三方格,如果把game.h文件中的#define ROW 3 改为#define ROW 10,#define COL 3 改为#define COL 10,结果就会发生改变:
所以还需要将代码调整一下,如果改为10,就可以打印10乘10方格。代码如下
void display_board(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
//数据
int j = 0;
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");
}
}
}
这样修改的话就可以随意改几乘几的方格了,不过今天我要写的是3乘3方格,这样棋盘界面也完成了,最终打印结果如图:
初始化阶段我们已经把二维数组board中所有元素赋成了空格,因此,想要实现玩家落子,只需要让玩家输入一个坐标(几行几列),改变这个坐标上的元素为新的元素(这个元素便可视为是一个棋子,我们定义这个棋子为X,便于随后的判断输赢),再用一次display_board()函数即可呈现出带有玩家落子的棋盘。
void playermove(char board[ROW][COL], int row, int col) {
int x = 0, y = 0;
printf("玩家下棋:\n");
ax:
while (1)
{
printf("请玩家输入落子的行和列:\n");
scanf("%d-%d", &x, &y);
//1.坐标的合法性
//2.坐标是否被占用
if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) {
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] ='X';
break;
}
else {
printf("该坐标已经被占用,请重新输入:");
goto ax;
}
}
else {
printf("该坐标非法,请重新输入:");
goto ax;
}
}
}
电脑落子和玩家落子同理,都是通过变换数组元素来实现的(这里我们定义电脑的子为字符Y,便于随后的判断输赢), 但玩家可以输入一个坐标,而电脑该如何实现呢?这里我们可以发现,如果电脑能生成一个随机坐标,并且这个坐标在棋盘范围以内,就可以很好的实现了。这里我们要用到c语言中生成随机数的知识。
我们要运用到生成随机数的代码,这是生成随机数的代码:
//注意 game.h文件中要包含生成随机数的头文件。
#include
#include
#include
int main()
{
srand((unsigned int)time(NULL));
int rad=rand();
}
如果我们想要获得区间(a,b)的随机数,可以通过rad=rand()%(b-a+1)+a来实现。
下面就是电脑下棋的代码:
void computer_move(char board[ROW][COL], int row, int col)
{
printf("电脑下棋:\n");
//随机数0~32726
//%3-->随机数0~2
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = 'Y';
break;
}
}
}
运行结果:
再判断输赢之前,我还要先写一个是否溢出的代码;
int is_full(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
if (board[i][j] ==' ')
{
return 0;
}
}
}
判断输赢也是游戏的重要的机制,下面我来写一个判断游戏输赢的代码:
char is_win(char board[ROW][COL], int row, int col)
{
int i = 0;
//判断三行不为空,是‘x’或‘y’,返回哪个值就谁赢。
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];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
{
return board[1][1];
}
//判断平局
if (is_full(board, row, col) == 1)
{
return 'Q';
}
//继续
return 'C';
}
1、三字棋的规则就是当一行或一列,或一对角字符一样,则获胜者就出现了,当电脑和玩家下完棋后,分别判断三行三列或对脚线内容是否相等,如果有出现相等的就返回其中的字符内容
2、返回代表值‘X’为玩家获胜,返回‘Y’则电脑获胜,返回‘Q’表示平局,返回‘C'表示未分胜负,也未平局。
最终在game()里面写一个打印结果
if (ret == 'X')
{
printf("玩家赢\n");
}
else if (ret == 'Y')
{
printf("电脑赢\n");
}
else if (ret == 'Q')
{
printf("平局\n");
}
display_board(board, ROW, COL);
}
最终测试结果为:
大家可能对我这个标题有疑问?
不是已经做完了吗,为什么还要再做,这其实是对电脑下棋的一个优化,之前电脑下棋是用随机数来进行的,根本不会有“人”的思维进行对抗,现在我要把电脑落子从“人工智障”升级为能够防守玩家的落子,增强游戏的可玩性,具体代码如下:
我将电脑写的代码进行了修改,也在网上借鉴了一些大佬的代码:
void computer_move(char arr[ROW][COL], int row, int col, int* n, int* m)//防守向
{
printf("电脑走\n");
int x = 0;
int y = 0;
if ((*m) < 2)//玩家下子小于2时,随机下
{
while (1)
{
srand((unsigned int)time(NULL));
x = rand() % row;
y = rand() % col;
if (arr[x][y] == ' ')
{
arr[x][y] = 'Y';
(*n)++;
return;
}
}
}
else//防守玩家下棋路线
{
int i = 0;
for (i = 0; i < row; i++)//横二
{
if (arr[i][0] == arr[i][1] && arr[i][1] == 'X' && arr[i][2] == ' ')
{
arr[i][2] = 'Y';
(*n)++;
return;
}
if (arr[i][0] == arr[i][2] && arr[i][2] == 'X' && arr[i][1] == ' ')
{
arr[i][1] = 'Y';
(*n)++;
return;
}
if (arr[i][1] == arr[i][2] && arr[i][2] == 'X' && arr[i][0] == ' ')
{
arr[i][0] = 'Y';
(*n)++;
return;
}
}
for (i = 0; i < col; i++)//竖二
{
if (arr[0][i] == arr[1][i] && arr[1][i] == 'X' && arr[2][i] == ' ')
{
arr[2][i] = 'Y';
(*n)++;
return;
}
if (arr[0][i] == arr[2][i] && arr[2][i] == 'X' && arr[1][i] == ' ')
{
arr[1][i] = 'Y';
(*n)++;
return;
}
if (arr[1][i] == arr[2][i] && arr[2][i] == 'X' && arr[0][i] == ' ')
{
arr[0][i] = 'Y';
(*n)++;
return;
}
}
if (arr[0][0] == arr[1][1] && arr[1][1] == 'X' && arr[2][2] == ' ')//斜二
{
arr[2][2] = 'Y';
(*n)++;
return;
}
else if (arr[0][0] == arr[2][2] && arr[2][2] == 'X' && arr[1][1] == ' ')
{
arr[1][1] = 'Y';
(*n)++;
return;
}
else if (arr[2][2] == arr[1][1] && arr[1][1] == 'X' && arr[0][0] == ' ')
{
arr[0][0] = 'Y';
(*n)++;
return;
}
if (arr[2][0] == arr[1][1] && arr[1][1] == 'X' && arr[0][2] == ' ')//反斜二
{
arr[0][2] = 'Y';
(*n)++;
return;
}
else if (arr[2][0] == arr[0][2] && arr[2][2] == 'X' && arr[1][1] == ' ')
{
arr[1][1] = 'Y';
(*n)++;
return;
}
else if (arr[0][2] == arr[1][1] && arr[1][1] == 'X' && arr[2][0] == ' ')
{
arr[2][0] = 'Y';
(*n)++;
return;
}
while (1)//如果都没有,随机下
{
x = rand() % row;
y = rand() % col;
if (arr[x][y] == ' ')
{
arr[x][y] = 'Y';
(*n)++;
return;
}
}
}//
}
原理:我的思路是这样进行的,当玩家下子小于2时,电脑还是跟之前一样随机落子,一旦玩家下子大于等于2时,电脑开始进行防守模式,寻找玩家有无再下一颗子的取胜序列,如有就堵住玩家,没有就继续下,那我们就开始来和电脑开始对战吧!
结果如下:(电脑是不是开始防守你的棋子了)
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu() {
printf("**********菜单********\n");
printf("****1->play****0->exit\n");
printf("**********************\n");
}
void game()
{
char ret = 0;
int n = 0;
int m= 0;
//数据的存储需要一个3*3的二维数组
char board[ROW][COL] = {" "};
init_board(board, ROW, COL);
display_board(board, ROW, COL);
while (1) {
playermove(board, ROW, COL);
display_board(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
break;
computer_move(board, ROW, COL,&n,&m);
display_board(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == 'X')
{
printf("玩家赢\n");
}
else if (ret == 'Y')
{
printf("电脑赢\n");
}
else if (ret == 'Q')
{
printf("平局\n");
}
display_board(board, ROW, COL);
}
//玩家赢 - 'X'
//电脑赢 - 'Y'
//平局了 - 'Q'
//游戏继续 - 'C'
void test() {
int input = 0;
do{
scanf("%d", &input);
if (input == 1)
printf("游戏开始\n");
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n:");
break;
default:
printf("输入错误,请重新输入\n:");
break;
}
} while (input);
}
int main() {
menu();
test();
return 0;
}
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void init_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++) {
board[i][j] = ' ';
}
}
}
//void display_board(char board[ROW][COL], int row, int col) {
// int i = 0;
// for (i = 0; i < row; i++)
// {
// //数据
// printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
// //---|---|---
// if(i= 1 && x <= row) && (y >= 1 && y <= col)) {
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] ='X';
break;
}
else {
printf("该坐标已经被占用,请重新输入:");
goto ax;
}
}
else {
printf("该坐标非法,请重新输入:");
goto ax;
}
}
}
//void computer_move(char board[ROW][COL], int row, int col)
//{
// printf("电脑下棋:\n");
// //0~32726
// //%3-->0~2
// /*while (1)
// {*/
// srand((unsigned int)time(NULL));
// int x = rand() % row;
// int y = rand() % col;
// if (board[x][y] == ' ')
// {
// board[x][y] = 'Y';
// }
// /*}*/
//}
void computer_move(char arr[ROW][COL], int row, int col, int* n, int* m)//防守向
{
printf("电脑走\n");
int x = 0;
int y = 0;
if ((*m) < 2)//玩家下子小于2时,随机下
{
while (1)
{
srand((unsigned int)time(NULL));
x = rand() % row;
y = rand() % col;
if (arr[x][y] == ' ')
{
arr[x][y] = 'Y';
(*n)++;
return;
}
}
}
else//防守玩家下棋路线
{
int i = 0;
for (i = 0; i < row; i++)//横二
{
if (arr[i][0] == arr[i][1] && arr[i][1] == 'X' && arr[i][2] == ' ')
{
arr[i][2] = 'Y';
(*n)++;
return;
}
if (arr[i][0] == arr[i][2] && arr[i][2] == 'X' && arr[i][1] == ' ')
{
arr[i][1] = 'Y';
(*n)++;
return;
}
if (arr[i][1] == arr[i][2] && arr[i][2] == 'X' && arr[i][0] == ' ')
{
arr[i][0] = 'Y';
(*n)++;
return;
}
}
for (i = 0; i < col; i++)//竖二
{
if (arr[0][i] == arr[1][i] && arr[1][i] == 'X' && arr[2][i] == ' ')
{
arr[2][i] = 'Y';
(*n)++;
return;
}
if (arr[0][i] == arr[2][i] && arr[2][i] == 'X' && arr[1][i] == ' ')
{
arr[1][i] = 'Y';
(*n)++;
return;
}
if (arr[1][i] == arr[2][i] && arr[2][i] == 'X' && arr[0][i] == ' ')
{
arr[0][i] = 'Y';
(*n)++;
return;
}
}
if (arr[0][0] == arr[1][1] && arr[1][1] == 'X' && arr[2][2] == ' ')//斜二
{
arr[2][2] = 'Y';
(*n)++;
return;
}
else if (arr[0][0] == arr[2][2] && arr[2][2] == 'X' && arr[1][1] == ' ')
{
arr[1][1] = 'Y';
(*n)++;
return;
}
else if (arr[2][2] == arr[1][1] && arr[1][1] == 'X' && arr[0][0] == ' ')
{
arr[0][0] = 'Y';
(*n)++;
return;
}
if (arr[2][0] == arr[1][1] && arr[1][1] == 'X' && arr[0][2] == ' ')//反斜二
{
arr[0][2] = 'Y';
(*n)++;
return;
}
else if (arr[2][0] == arr[0][2] && arr[2][2] == 'X' && arr[1][1] == ' ')
{
arr[1][1] = 'Y';
(*n)++;
return;
}
else if (arr[0][2] == arr[1][1] && arr[1][1] == 'X' && arr[2][0] == ' ')
{
arr[2][0] = 'Y';
(*n)++;
return;
}
while (1)//如果都没有,随机下
{
x = rand() % row;
y = rand() % col;
if (arr[x][y] == ' ')
{
arr[x][y] = 'Y';
(*n)++;
return;
}
}
}//
}
//输入棋盘的返回值,如果为空,则返回0,不为空,则返回1。
int is_full(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
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;
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];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
{
return board[1][1];
}
//判断平局
if (is_full(board, row, col) == 1)
{
return 'Q';
}
//继续
return 'C';
}
game.h
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#define ROW 3
#define COL 3
void init_board(char board[ROW][COL], int row, int col);//初始化棋盘
void display_board(char board[ROW][COL], int row, int col);//打印棋盘
void playermove(char board[ROW][COL], int row, int col);//玩家下棋
void computer_move(char board[ROW][COL], int row);///电脑下棋
static int is_full(char board[ROW][COL], int row, int col);//判断棋盘是否已满
char is_win(char board[ROW][COL], int row, int col);//判断输赢
后面的有点多,大家理解思路就行,后面的不需要仔细阅读,经过十几个小时的学习才写出这篇博客,也是为了巩固知识,加深对知识的理解。
博客就到这里就结束了,希望可以帮助大家,希望大家可以点个赞噢!