一、三子棋游戏介绍
三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子走成一条线就视为胜利,而对方就算输了,但是三子棋在很多时候会出现和棋的局面。
下面作者将带着大家一起用C语言实现一个三子棋的小游戏。
二、代码分装
源文件:
test.c: 整个游戏相关的测试
game.c: 实现游戏的相关函数
头文件:
game.h: 相关函数的声明,整个代码要引用的头文件以及宏定义
三、代码实现步骤
1.制作菜单menu函数以及游戏运行逻辑流程
- 首先在 test.c 中定义一个menu函数打印菜单,提示玩家1开始游戏,0退出游戏。
- scanf接收玩家输入并用switch判断,1即开始游戏,0则退出游戏,非1非0则提示选择错误,重新选择。而为了能够让用户重新选择以及结束一局游戏后能够再来一盘,需要使用do while语句将它们包含起来。
test.c
void menu() { printf("*********************\n"); printf("***** 1. game *****\n"); printf("***** 0. exit *****\n"); printf("*********************\n"); } void game() { printf("三子棋\n"); } int main() { int input = 0; do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏!\n"); break; default: printf("选择错误,重新选择!\n"); break; } } while (input); return 0; }
运行代码检测游戏运行逻辑流程
2.初始化棋盘InitBoard函数
- 首先三子棋是九个格子,即需要创建一个3x3的数组。为了方便未来修改代码数值,头文件 game.h 中定义ROW(行),COL(竖),然后在 test.c 中引用头文件 game.h即可。
- 在game函数中创建棋盘board数组,开始的时候,数组的内容应该都是空格,为了方便后续使用,创建一个初始化棋盘函数InitBoard。
- 在 game.h 中定义InitBoard函数,并在 game.c 中实现函数。
game.h
#include <stdio.h> #define ROW 3 #define COL 3 //初始化棋盘 void InitBoard(char board[ROW][COL],int row,int col);
game.c
//初始化棋盘 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] = ' ';//空格 } } }
3.打印棋盘DisplayBoard函数
接下来创建一个打印棋盘函数DisplayBoard来查看一下初始化是否完成。同样的:在 game.h 中定义DisplayBoard函数,并在 game.c 中实现函数。
首先看棋盘效果图:
思路如下:先打印数据行,再打印分割行
1、数据行打印形式:空格数据空格竖杠
当到达最后一列时不打印竖杠
2、分割行打印形式:---|
当到达最后一列时不打印竖杠,并且当到达最后一行时,不打印分割行
game.h
//打印棋盘 void DisplayBoard(char board[ROW][COL], int row, int col);
game.c
//打印棋盘 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"); } }
4.玩家下棋PlayerMove函数
在 game.h 中定义PlayerMove函数,并在 game.c 中实现函数。
game.h
//玩家下棋 void PlayerMove(char board[ROW][COL], int row, int col);
game.c
需要考虑坐标被占用的情况以及输入非法坐标的情况。
使用while循环嵌套输入部分与判断部分,这样当输入错误或者输入的坐标被占用时,可以重新输入。当输入正确时且没被占用,则在该坐标里将 空格 改为 * 。
//玩家下棋 void PlayerMove(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; printf("玩家下棋\n"); while (1) { printf("请输入要下棋的坐标:>"); scanf("%d %d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (board[x - 1][y - 1] == ' ') //玩家的输入对应数组的下标需要-1 { board[x - 1][y - 1] = '*'; break; } else { printf("该坐标被占用,请输入其他坐标\n"); } } else { printf("坐标非法,请重新输入\n"); } } }
需要注意的是,因为玩家下棋的坐标范围是1-3的,而数组中的范围是0-2,因此需要将玩家输入的数-1得到对应的数组下标,才能执行正确。
5.电脑下棋ComputerMove函数
这里电脑的下棋策列是随机下棋,因此首先需要生成随机数,这就需要用到srand函数、rand函数和time函数,关于srand函数、rand函数和time函数之间的知识点,我在往期的博客中有进行讲解,感兴趣的可以前往:【C语言】猜数字小游戏——深度刨析rand函数生成随机数_Hacynn的博客-CSDN博客查看,在这里就不过多赘述。
使用srand和rand需要包含 stdlib.h 头文件,使用time需要包含 time.h 头文件。并且在main函数中调用srand生成随机种子,这样rand函数便可以生成随机数。
game.h
//电脑下棋 void ComputerMove(char board[ROW][COL], int row, int col);
game.c
//电脑下棋 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] = '#'; break; } } }
当完成这一步时,三子棋游戏就大概完成了,玩家和电脑可以相互下棋,但是缺少了获胜的条件,因此还需要设置一个获胜条件来停止游戏。
6. 判断输赢IsWin函数
这里我们先要考虑判断IsWin函数需要返回几种结果:
1、玩家获胜===》函数返回*
2、电脑获胜===》函数返回#
3、平局 ===》函数返回Q
4、游戏继续===》函数返回C
game函数
void game() { int ret = 0; char board[ROW][COL]; InitBoard(board, ROW, COL); DisplayBoard(board, ROW, COL); while (1) { //玩家 PlayerMove(board, ROW, COL);//玩家下棋 DisplayBoard(board, ROW, COL);//打印棋盘结果 ret = IsWin(board, ROW, COL);//判断输赢 if (ret != 'C') { break; } //电脑 ComputerMove(board, ROW, COL);//电脑下棋 DisplayBoard(board, ROW, COL);//打印棋盘结果 ret = IsWin(board, ROW, COL);//判断输赢 if (ret != 'C') { break; } } if (ret == '*') printf("玩家赢!\n"); else if (ret == '#') printf("电脑赢!\n"); else printf("平局!\n"); }
判断IsWin函数返回的值并返回提示或者继续游戏。
game.h
//判断输赢 char IsWin(char board[ROW][COL], int row, int col);
game.c
//判断是否为满 static 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; } //判断输赢 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]; } if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ') //撇相同 { return board[1][1]; } //判断平局 if (IsFull(board, row, col)) { return 'Q';//平局 } return 'C';//继续 }
其中需要注意的是,IsFull函数仅是用来判断数组是否已满,未满返回0满返回1。而且IsFull只辅助于IsWin函数,不被其他函数调用,因此可以在前面加static修饰。
四、三子棋完整代码
test.c
#define _CRT_SECURE_NO_WARNINGS 1 #include "game.h" void menu() { printf("*********************\n"); printf("***** 1. game *****\n"); printf("***** 0. exit *****\n"); printf("*********************\n"); } void game() { int ret = 0; char board[ROW][COL]; InitBoard(board, ROW, COL); DisplayBoard(board, ROW, COL); while (1) { //玩家 PlayerMove(board, ROW, COL);//玩家下棋 DisplayBoard(board, ROW, COL);//打印棋盘结果 ret = IsWin(board, ROW, COL);//判断输赢 if (ret != 'C') { break; } //电脑 ComputerMove(board, ROW, COL);//电脑下棋 DisplayBoard(board, ROW, COL);//打印棋盘结果 ret = IsWin(board, ROW, COL);//判断输赢 if (ret != 'C') { break; } } if (ret == '*') printf("玩家赢!\n"); else if (ret == '#') printf("电脑赢!\n"); else printf("平局!\n"); } int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏!\n"); break; default: printf("选择错误,重新选择!\n"); break; } } while (input); return 0; }
game.h
#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 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);
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 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 PlayerMove(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; printf("玩家下棋\n"); 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) { printf("电脑下棋\n"); int x = 0; int y = 0; while (1) { x = rand() % row; y = rand() % col; if (board[x][y] == ' ') { board[x][y] = '#'; break; } } } //判断是否为满 static 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; } //判断输赢 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]; } if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ') //撇相同 { return board[1][1]; } //判断平局 if (IsFull(board, row, col)) { return 'Q';//平局 } return 'C';//继续 }
此次的三子棋小游戏的内容就到此为止了,相信大家也能够做出属于自己的三子棋小游戏,这个游戏最重要的是代码思维而不是代码本身,理解代码思维能够得到更大的提升。
如果觉得作者写的不错,求给作者一个大大的点赞支持一下,你们的支持是我更新的最大动力!