引入
谈到三子棋,大家应该都不陌生,学生时代我们大多人都爱拿作文本有事没事就跟同桌下两把,只要任意一方三点连成一线,就可以胜利。今天我作为一个计算机方面的博主,将会用C语言实现这个简单的小游戏(人机对战,电脑非智能)。由于三子棋是3*3的棋盘,所以我们主要要用到二维数组的有关知识点,该项目也是主要围绕二维数组来展开的。
基本框架
要实现这个简单的游戏,也需要实现不同的函数与结构,也要将不同部分放在不同源文件里,我们今天将会用(test.c-测试游戏的代码部分)和(game.c-游戏有关函数的实现),还有(game.h-游戏有关函数声明),下面我们来看一下这个游戏的框架。
首先我们先来看一下主函数的基本框架:
int main() { int input = 0;//利用input来记录玩家的选择 srand((unsigned int)time(NULL));//随机数时间戳设定,后面会用到 do//设置循环使玩家在推出游戏前可多次选择再次游玩 { menu();//打印一个基本的菜单,这里选择1.play(玩)或 0.exit(退出游戏) printf("请选择:>\n"); scanf("%d", &input); switch (input)//根据input进入不同选择 { case 1: game();//选1直接进入游戏 break; case 0: printf("退出游戏\n");//后面input也为0,在while(input)为假,结束程序 break; default: printf("选择错误,请重新选择:>\n"); break; } } while (input); return 0; }
下面我们来看一下game.h这个头文件,上面的思维导图也有讲,这里包含的是函数声明等内容,
game.c和test.c都会使用到,非常重要。
//所用库函数的头文件 #include<stdio.h> #include<stdlib.h> #include<time.h> #define ROW 3//ROW即行数,定义为3 #define COL 3//COL即列数 //所需要函数的函数声明 void menu(); void Initboard(char board[ROW][COL], int row, int col); void Displayboard(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 Iswin(char board[ROW][COL], int row, int col);
然后我们来看一下这个游戏运行的大致逻辑,由test.c中的game()函数展示。
void game() { char board[ROW][COL] = { 0 };//定义数组board,作为三子棋的主要框架 Initboard(board,ROW,COL);//初始化二维数组(棋盘) Displayboard(board, ROW, COL);//打印棋盘 char ret = 0;//这里定义字符类型ret,与后面判断输赢平有关 while (1)//若未分胜负或者棋盘满了,则一直下,满足条件时跳出 { /*首先玩家下棋,然后展示一下棋盘, 然后利用Iswin函数判断输赢 (Iswin会返回一个字符,用ret接受,在后面进行判断)*/ Playermove(board, ROW, COL);//玩家下棋函数 Displayboard(board, ROW, COL); ret = Iswin(board, ROW, COL); /*电脑下棋,方法同上*/ Computermove(board, ROW, COL);//电脑下棋函数 Displayboard(board, ROW, COL); ret = Iswin(board, ROW, COL); //---为了更好地理解,这里先大致讲一下Iswin函数的大致逻辑。 //判断输赢,在玩家后者电脑每一次落子之后进行判断 //玩家赢 则返回'*' //电脑赢 则返回'#' //继续 则返回'C' //平局 则返回'Q' if (ret != 'C') break;//如果未返回'C',跳出对战,对最终结果进行判断 } //最终结果的判断 if (ret == '*') { printf("玩家赢\n"); } else if (ret == '#') { printf("电脑赢\n"); } else if (ret == 'Q') { printf("平局\n"); } }
根据前面的内容,相信你一定了解了三子棋项目的大致逻辑,下面,我将对game()中所使用的所有函数,进行逐一讲解。
具体函数讲解
Initboard(初始化棋盘)函数:分析:棋盘是用来落子的,在程序中就是用来存放数据的,我们能自然而然地想到用二维数组来存放数据.
//参数说明:二维数组board,行数row,列数col。 void Initboard(char board[ROW][COL], int row, int col) { //利用两个for循环对二维数组进行遍历,二维数组存放的数据都初始化为空格 int i = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { board[i][j] = ' '; } } } //这样就完成了棋盘的初始化
Display(打印棋盘)函数:分析:这个函数是这个项目的一个难点,磨刀不误砍柴工,首先让我们来看一下这个棋盘长什么样:
可以看出,该棋盘是由“ %c | %c | %c ”与“---|---|---”交替连接而成的。我们可以将“ %c | %c | %c ”与“---|---|---”作为一个分割行的部分,当到达最后一行时,再少打印一个“---|---|---”,然后,我们再将“ %c |”和“---|”作为每一个分割列的部分,当到达最后一列时,两个部分都不打印“|”。
下面我们来看一下代码实现:
void Displayboard(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"); for (j = 0; j < COL; j++) { if (i < ROW - 1) { printf("---"); if (j < ROW - 1) printf("|"); } if (j == COL - 1) printf("\n"); } } }
Playermove(玩家落子)函数:分析:这里主要记录玩家的落子,需要等待玩家输入要下的坐标。
这里主要要注意两点:1.玩家不是程序员,他输入的坐标肯定不是数组形式的(数组首元素从1开始),因此要注意输入坐标与数组元素的关系。
2.注意坐标的合法性:应该需要有判断,判断坐标是否合法(输入坐标超出数组的实际范围,输入的坐标那里有其他内容),若不合法,则重新输入,所以外层应该还需要嵌套循环。
void Playermove(char board[ROW][COL], int row, int col) { printf("玩家下棋\n"); printf("请输入下的坐标:>"); int i = 0, j = 0; while (1) { scanf("%d%d", &i, &j); //输入坐标-1即为数组中对应坐标 if ((board[i - 1][j - 1] != ' ')||(i - 1>ROW||j - 1>COL)) { printf("输入坐标非法,请重新输入!"); } else { board[i - 1][j - 1] = '*';//坐标合法,对应坐标落子'*' break; } } }
Computermove(电脑落子)函数:分析:这里主要记录电脑的落子,需要电脑随机确定下的坐标。
注意:这里的电脑落子坐标需要随机,即需要rand()函数(在主函数中已经定义随机数时间戳,确保了完全随机),而且随机数可以指定范围,因此在落子合法性上不需要判断越界问题。
下面再来看一下代码实现。
void Computermove(char board[ROW][COL], int row, int col) { printf("电脑下棋\n"); int i = 0; int j = 0; while (1) { i = rand() % ROW;//范围为(0-2) j = rand() % COL;//范围为(0-2) if (board[i][j] == ' ') { board[i][j] = '#';//输入坐标合法,落子为'#' break; } } }
Iswin(判断输赢平)函数:分析:这里就运用到了三子棋的规则(三行其一或三列其一或主次对角线连成一线即可)。定义类型为char,上文也说过,该函数返回的字符有决策功能。
下面来看一下代码实现:
//Isfull是一个判断棋盘是否满了的辅助函数 int Isfull(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;//未满返回0 } } } return 1;//已满返回一 } 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[1][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[0][0] != ' ') return board[0][0]; //判断此对角线是否连成一线 if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ') return board[0][2]; //利用Isfull判断棋盘是否满了,满了则平局 if (Isfull(board, row, col) == 1) { return 'Q'; } return 'C'; }
以上就是所有函数,下面我们来运行一下试试:
这里作者由于
太菜
输了。
好了,三子棋就分享到这,拜拜。