四、实现三子棋(Tic-Tac-Toe)
0x00 游戏介绍
三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。但是,有很多时候会出现和棋的情况。
0x01 实现思路
📚 分模块:当代码量较大,功能较多时,我们可以拆分代码,分模块来实现各个功能;
① test.c 测试游戏的逻辑;
② game.c 游戏相关函数的实现;
② game.h 关于游戏相关的函数声明、符号声明以及头文件的包含;
0x02 游戏界面
💬 test.c
该代码实现游戏界面部分
#define _CRT_SECURE_NO_WARNINGS #include "game.h" void load_game_menu() { printf("\n"); printf("*********************************\n"); printf("********** 1. 开始游戏 ***********\n"); printf("********** 0. 退出游戏 ***********\n"); printf("*********************************\n"); } int main(int argc, char const* argv[]) { int input = 0; do { /* 载入游戏菜单 */ load_game_menu(); printf("请选择: "); scanf("%d", &input); switch (input) { case 1: printf("\ntest:游戏开始\n"); // game(); break; case 0: printf("\n退出游戏\n"); break; default: printf("\n输入错误,请重新输入!\n"); break; } } while (input); return( 0 ); }
🚩 该部分运行结果如下(完成一部分功能就运行一下看看,及时发现BUG,越早发现越容易找到BUG)
0x03 创建棋盘&初始化棋盘
写game() 函数,创建棋盘,然后将初始化棋盘函数
💬 test.c
void game() { /* 创建棋盘 */ char board[ROW][COL]; /* 初始化棋盘 - 初始化空格 */ init_board(board, ROW, COL); }
💬 game.h
#include <stdio.h> /* 宏定义 */ #define ROW 3 #define COL 3 /* 函数声明 */ void init_board(char board[ROW][COL], int row, int col); void print_board(char board[ROW][COL], int row, int col);
💬 game.c
#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] = ' '; // 初始化为空格 } } }
0x04 打印棋盘
print_board()函数,本质上是打印数组的内容
利用循环画一个棋盘
💬 test.c
void game() { //存储数据 - 二维数组 char board[ROW][COL]; //初始化棋盘 - 初始化空格 init_board(board, ROW, COL); //打印一下棋盘 - 本质是打印数组的内容 print_board(board, ROW, COL); }
💬 game.h
#include <stdio.h> /* 宏定义 */ #define ROW 3 #define COL 3 /* 函数声明 */ void init_board(char board[ROW][COL], int row, int col); void print_board(char board[ROW][COL], int row, int col);
💬 game.c
void print_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 < row - 1) printf("---|---|---\n"); } }
🚩 运行结果:
❓如果修改了棋盘大小,怎么办?
💡 代码优化
void print_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) { int j = 0; for (j = 0; j < col; j++) { printf("---"); if (j < col - 1) printf("|"); } printf("\n"); } } }
🚩 我们将 ROW 和 COL 修改为5试试看能否生成一个5x5的棋盘;
0x05 设计玩家回合
💬 test.c game ( )
void game() { //存储数据 - 二维数组 char board[ROW][COL]; //初始化棋盘 - 初始化空格 init_board(board, ROW, COL); //打印一下棋盘 - 本质是打印数组的内容 print_board(board, ROW, COL); //玩家下棋 player_round(board, ROW, COL); } 💬 game.h player_round ( ) void player_round(char board[ROW][COL], int row, int col); 💬 game.c player_round ( ) void player_round(char board[ROW][COL], int row, int col) { /* 创建坐标 */ int x = 0; int y = 0; /* 要求玩家落子 */ printf("\n[玩家回合]\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"); } }
0x06 设计电脑回合
💬 test.c main ( ) 中存放随机数种子,game ( )
int main(int argc, char const* argv[]) { srand((unsigned int)time(NULL)); // 置随机数种子 ... } void game() { /* 创建棋盘 */ char board[ROW][COL]; /* 初始化棋盘 - 初始化空格 */ init_board(board, ROW, COL); /* 打印棋盘 */ print_board(board, ROW, COL); /* 玩家下棋 */ player_round(board, ROW, COL); /* 电脑下棋 */ computer_round(board, ROW, COL); }
💬 game.h 引入随机数必要的头文件,computer_round ( )
#include <time.h> #include <stdlib.h> void computer_round(char board[ROW][COL], int row, int col);
💬 game.c computer_round ( )
void computer_round(char board[ROW][COL], int row, int col) { printf("[电脑回合]"); while (1) { /* 随机坐标 */ int x = rand() % row; int y = rand() % col; /* 判断坐标坐标是否被占用 */ if (board[x][y] == ' ') { board[x][y] = '#'; break; } } }
0x07 游戏状态设计(整合玩家回合和电脑回合)
💬 test.c game ( ) 玩家和电脑走完走打印一下棋盘,更新最新的棋盘数据
void game() { //存储数据 - 二维数组 char board[ROW][COL]; //初始化棋盘 - 初始化空格 init_board(board, ROW, COL); //打印一下棋盘 - 本质是打印数组的内容 print_board(board, ROW, COL); while (1) { //玩家下棋 player_round(board, ROW, COL); print_board(board, ROW, COL); //电脑下棋 computer_round(board, ROW, COL); print_board(board, ROW, COL); }
0x08 判断输赢&宣布胜利条件
💬 test.c game ( )
void game() { //存储数据 - 二维数组 char board[ROW][COL]; //初始化棋盘 - 初始化空格 init_board(board, ROW, COL); //打印一下棋盘 - 本质是打印数组的内容 print_board(board, ROW, COL); char ret = 0;//接受游戏状态 while (1) { //玩家下棋 player_round(board, ROW, COL); print_board(board, ROW, COL); //判断玩家是否赢得游戏 ret = is_win(board, ROW, COL); if (ret != 'C') break; //电脑下棋 computer_round(board, ROW, COL); print_board(board, ROW, COL); //判断电脑是否赢得游戏 ret = is_win(board, ROW, COL); if (ret != 'C') break; } if (ret == '*') { printf("玩家赢了\n"); } else if (ret == '#') { printf("电脑赢了\n"); } else { printf("平局\n"); } print_board(board, ROW, COL); }
💬 game.h is_win ( )
char is_win(char board[ROW][COL], int row, int col);
💬 game.c is_win ( )
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][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]; } /* 判断平局 */ //如果棋盘满了返回1, 不满返回0 int ret = is_full(board, row, col); if (ret == 1) { return 'Q'; } /* 继续 */ return 'C'; }
💬 game.c is_full
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; // 棋盘满了 }
0x09 代码运行
🚩 玩家获胜
🚩 电脑获胜
🚩 平局








