利用C语言数组、函数等一些基础知识实现与电脑对战的三子棋游戏,电脑下棋为随机下棋。此游戏需有一定的数组、函数知识支撑。
三子棋也就是我们熟知的井字棋,在3*3表格中,两人之间轮流下棋,行、列、对角线谁先下满三个子,谁获胜。
要想实现整个游戏,必然需要一套可靠的逻辑支撑:
我们使用三个文件实现三子棋游戏,test.c(游戏的整体框架),game.c(游戏涉及的自定义函数),game.h(头文件,用于声明各种函数等)
我们先写一个主函数,并创建test函数将游戏的整体框架放入其中
int main()
{
test();
return 0;
}
接着在test()函数中调用menu函数打印菜单,选择1表示进行游戏,选择0表示退出游戏
void menu()
{
printf("****************************\n");
printf("****** 1. play ******\n");
printf("****** 0. exit ******\n");
printf("****************************\n");
}
我们利用do-while循环实现三子棋游戏的整体简单逻辑,其中利用了switch语句对玩家的选择(input)进行了判断,选择为0就会退出游戏打破do-while循环,选择为1就会进入game函数
void test()
{
int input = 0;
srand((unsigned int)time(NULL));//设置随机数所需要的srand函数,后文会讲到
do
{
menu();
printf("请选择>:");//选择进行游戏还是退出游戏
scanf("%d", &input);//玩家进行选择
switch (input)//判断玩家选择
{
case 1://调用game()函数进行游戏环节
game();
break;
case 0:
printf("退出游戏\n");
break;
default://玩家输入非零数,重新选择
printf("输入错误,请重新输入\n");
break;
}
} while (input);
}
以上就是游戏开始前的整体准备,接下来我们实现game函数
玩家选择1后就会开始调用game函数,所以我们需要自定义一个game函数,然后我们分开来讲解game函数的实现逻辑
我们先创建一个二维数组,用来装棋盘元素
char board[ROW][COL] = {0};
这里ROW,COL分别代表行数和列数,是由我们通过define定义的常量,这里就需要我们的头文件了(一般在代码工程中,函数声明、自定义常量、库函数的调用等一般都写在头文件中,需要使用时直接调用头文件即可,调用方式与调用库函数略有不同,格式为:#include "xxxx.h",在这里调用的话就是,#include "game.h")
#define ROW 3//棋盘的行数 #define COL 3//棋盘的列数
在这里,我们附上三子棋游戏中的整个头文件(浏览即可,不必看懂,会按游戏步骤进行讲解)
#pragma once//头文件自带语句 #define ROW 3//棋盘的行数 #define COL 3//棋盘的列数 #include
//调用rand()、srand()、time()函数需要使用的库函数 #include #include //声明初始化棋盘函数 void InitBoard(char board[ROW][COL], int row, int col); //声明打印棋盘函数 void DisplayBoard(char board[ROW][COL], int row, int col); //声明玩家移动函数 void player_move(char board[ROW][COL], int row, int col); //声明电脑移动函数 void computer_move(char board[ROW][COL], int row, int col); //声明判断胜负函数 char is_win(char board[ROW][COL], int row, int col);
创建二维数组后,我们再自定义一个初始化棋盘的函数,实现将棋盘内元素全部成为 ' '
我们利用for循环嵌套,即可实现初始化棋盘的逻辑
//初始化棋盘函数的实现逻辑
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 DisplayBoard(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 player_move(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");
}
}
}
电脑下棋与玩家下棋,有些相似,但并不需要人为赋值,这时就需要用到rand、srand、time函数来生成随机数 ,这些需调用新的库函数,并将函数的调用放入头文件game.h中去
//调用rand()、srand()、time()函数需要使用的库函数
#include
#include
实现电脑下棋,需注意一下几点:
注意,在计算机中所有的随机数,都是伪随机数,计算机中只有伪随机,没有真正的随机一说。
我们附上电脑下棋这块的代码(逻辑与玩家下棋相近)
//电脑移动函数的实现逻辑
void computer_move(char board[ROW][COL], int row, int col)
{
printf("电脑下棋>:\n");
while (1)
{
//rand函数生成随机数
int x = rand() % ROW;
int y = rand() % COL;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
实际上,玩家或者电脑每行动一步,都得进行胜负的判断,其本质的逻辑是一个循环,玩家移动 ---> 判断胜负 ---> 电脑移动 ---> 玩家移动 ....这样的一个循环,只有当其中一方达到胜利条件,或平局时才会打破这个循环,这时就需要用到判断胜负这一函数
我们在这里约定用字符代表胜负情况,自定义is_win函数去接受一个字符的返回值
在函数实现过程中需要注意两点:
1. 判断是否平局,需要自定义is_full函数,实现如果数组中有元素为 ' ' ,证明棋盘还没下满,返回0。如果全不为 ' ',证明棋盘已下满,返回1。如果棋盘满了未分出胜负,则代表平局。
2. 判定胜负条件时,需要判定所有的行、列,以及两条对角线。
//判断棋盘是否下满的函数实现逻辑
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;
//利用for循环判断三行中是否有一行完全相同
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
{
return board[i][1];
}
}
//三列的判断
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
{
return board[1][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 (1 == is_full(board, row, col))
{
return 'Q';
}
//继续游戏
return 'C';
}
将上文提到的所有块合起来,就能实现我们的game函数
void game()
{
char board[ROW][COL] = { 0 };//创建二维数组来为创建棋盘做准备
InitBoard(board, ROW, COL);//初始化数组元素,将数组内元素全初始为 ' '
DisplayBoard(board, ROW, COL);//打印棋盘,供玩家选择下棋位置
char ret = 0;
//创建字符变量接受判断胜负条件的字符
//使用'C'代表游戏未结束,继续游戏
//使用'*'代表游戏结束,玩家获胜
//使用'#'代表游戏结束,电脑获胜
//使用'Q'代表游戏结束,平局
while (1)//利用while循环重复玩家电脑下棋动作
{
player_move(board, ROW, COL);//玩家下棋
DisplayBoard(board, ROW, COL);//打印下棋后的棋盘
ret = is_win(board, ROW, COL);//判断胜负
if (ret != 'C')//如果ret不为'C'时,跳出循环,进行判断胜负的最后环节
{
break;
}
computer_move(board, ROW, COL);//电脑下棋
DisplayBoard(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
//判断胜负的最后环节
if (ret == '*')
{
printf("玩家获胜,你赢了!\n");
}
if (ret == '#')
{
printf("电脑获胜,你输了!\n");
}
if (ret == 'Q')
{
printf("无人获胜,平局\n");
}
}
game.h头文件
#pragma once//头文件自带语句
#define ROW 3//棋盘的行数
#define COL 3//棋盘的列数
#include
//调用rand()、srand()、time()函数需要使用的库函数
#include
#include
//声明初始化棋盘函数
void InitBoard(char board[ROW][COL], int row, int col);
//声明打印棋盘函数
void DisplayBoard(char board[ROW][COL], int row, int col);
//声明玩家移动函数
void player_move(char board[ROW][COL], int row, int col);
//声明电脑移动函数
void computer_move(char board[ROW][COL], int row, int col);
//声明判断胜负函数
char is_win(char board[ROW][COL], int row, int col);
game.c源文件
#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 DisplayBoard(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 player_move(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 computer_move(char board[ROW][COL], int row, int col)
{
printf("电脑下棋>:\n");
while (1)
{
//rand函数生成随机数
int x = rand() % ROW;
int y = rand() % COL;
if (board[x][y] == ' ')
{
board[x][y] = '#';
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;
//利用for循环判断三行中是否有一行完全相同
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
{
return board[i][1];
}
}
//三列的判断
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
{
return board[1][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 (1 == is_full(board, row, col))
{
return 'Q';
}
//继续游戏
return 'C';
}
test.c源文件
#include "game.h"
void menu()
{
printf("****************************\n");
printf("****** 1. play ******\n");
printf("****** 0. exit ******\n");
printf("****************************\n");
}
void game()
{
char board[ROW][COL] = { 0 };//创建二维数组来为创建棋盘做准备
InitBoard(board, ROW, COL);//初始化数组元素,将数组内元素全初始为 ' '
DisplayBoard(board, ROW, COL);//打印棋盘,供玩家选择下棋位置
char ret = 0;
//创建字符变量接受判断胜负条件的字符
//使用'C'代表游戏未结束,继续游戏
//使用'*'代表游戏结束,玩家获胜
//使用'#'代表游戏结束,电脑获胜
//使用'Q'代表游戏结束,平局
while (1)//利用while循环重复玩家电脑下棋动作
{
player_move(board, ROW, COL);//玩家下棋
DisplayBoard(board, ROW, COL);//打印下棋后的棋盘
ret = is_win(board, ROW, COL);//判断胜负
if (ret != 'C')//如果ret不为'C'时,跳出循环,进行判断胜负的最后环节
{
break;
}
computer_move(board, ROW, COL);//电脑下棋
DisplayBoard(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
//判断胜负的最后环节
if (ret == '*')
{
printf("玩家获胜,你赢了!\n");
}
if (ret == '#')
{
printf("电脑获胜,你输了!\n");
}
if (ret == 'Q')
{
printf("无人获胜,平局\n");
}
}
void test()
{
int input = 0;
srand((unsigned int)time(NULL));//设置随机数所需要的函数,后文会讲到
do
{
menu();
printf("请选择>:");//选择进行游戏还是退出游戏
scanf("%d", &input);//玩家进行选择
switch (input)//判断玩家选择
{
case 1:
game();//调用game()函数进行游戏环节
break;
case 0:
printf("退出游戏\n");
break;
default://非零选项,重新选择
printf("输入错误,请重新输入\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
代码中还有很多可以修改的地方,这只是一个简单的三子棋游戏的实现,希望大家见谅