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"); } } }
运行结果如下所示
可是这样的话,我们会发现,这样其实就被写死了,以后想要修改就很难修改了。比如我们将行和列都改为十的话。就会发现和我们所设想的棋盘大大不符合
(3)更进一步优化
所以我们要改。要优化代码,那么怎么优化呢?其实我们可以在我们上面的代码上进行修改,我们上面的代码是由两次打印,我们将两次打印都进行展开。具体到每一列上操作。第一次打印就把一个数据和一个|字符当成一组逻辑,第二次打印就把---|当成一组逻辑。当然每一组逻辑最后一次是不需要打印|的。所以根据这些分析,我们已经可以推导出代码了
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) { for (j = 0; j < col; j++) { printf("---"); if (j < col - 1) { printf("|"); } } printf("\n"); } } }
运行结果为
可见符合了我们的预期。
7.截止至目前已经实现功能,所写的代码展示
作为一个新手,刚开始写这种小游戏的时候,常常读着读着就已经晕了。这是因为新手暂时还没有一种全局的目光。所以为了方便读者理解,我们将截止至目前已经实现的代码在此处进行一下展示方便读者更好的阅读下去。
test.c模块
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void meau() { printf("********************************\n"); printf("******** 1.play *******\n"); printf("******** 0.exit *******\n"); printf("********************************\n"); } void game() { char board[ROW][COL]; init_board(board, ROW, COL); print_board(board, ROW, COL); } void test() { int input = 0; do { meau(); printf("请输入>:\n"); scanf("%d", &input); switch (input) { case 1: printf("进入三子棋游戏\n"); game(); break; case 0: printf("退出游戏\n"); break; default: printf("选择错误,请重新选择\n"); break; } } while (input); } int main() { 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; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { board[i][j] = ' '; } } } 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) { for (j = 0; j < col; j++) { printf("---"); if (j < col - 1) { printf("|"); } } printf("\n"); } } }
game.h模块
#pragma once #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);
8. 玩家下棋(player_move)
有了棋盘,自然就需要下棋了。
(1)玩家下棋函数的定义和声明
void player_move(char board[ROW][COL], int row, int col);
(2)实现玩家下棋
我们要让玩家下棋,那么我们不妨就让玩家通过输入坐标来进行下棋,如下图所示,这样的话,就可以使玩家输入一个坐标。
有了坐标,我们首先要通过这个坐标来判断是否在我们棋盘的范围内,也就是是否合法,而且要注意的是,玩家可不是程序员,他们不知道这个数组的下标是从0开始的,所以我们要在玩家输入的坐标上-1,而且我们这样应该是一个循环,坐标非法时候需要重新输入坐标,当我成功下棋以后,我们可以在里面放入一个break语句来打破循环。
坐标合法,但是如果这个位置之前有人下过棋了呢,这种情况也是不允许的,所以我们要在坐标合法里面加入一条判断,这个位置是一个空格字符,不能有棋子。
(提示:此处存在一处错误,这是我们之前所讲过的经典的错误标准的零分,细心的你发现了吗,没有发现的话,那就接着往下看吧!后文会讲解的)
(3)测试玩家下棋模块
我们试着测试一下,在test.c种加入打印棋盘,方便我们查看
运行结果为
输入成功案例
坐标非法案例
此处位置已经被下了棋案例
这部分我们可以使用两次玩家下棋来完成我们先将test.c在进行一次玩家输入和打印,这是因为我们目前这是第一次下棋,只有在第二次下棋才会出现这种情况
我们试着下棋
我们发现居然不符合我们的预期了!!!,这是怎么回事呢,其实这个错误细心的同学肯定在前文中已经发现了。
(4)经典的错误标准的零分
这个错误有太多人犯了,所以在此处故意设下了一个坑,当然也是提醒大家,细心的重要性。当然这个错误看似简单,但是在这个一个很简单的项目里可不简单,即便我在前文中提醒过此处有一个经典错误标准零分,但还是有人找不到,那如果将整个游戏写完之后在想找到这个错误是很困难的。这么一个小错误对于这个游戏而言是很致命的。而这个错误很难发现,所以这就再次提醒大家,一定要细心,细心。写一部分测一部分。否则未来在企业中,那种超大规模的代码想要调试出来是极其困难的!!!
修改后再次测试
这下就是符合我们的预期了
(5)玩家下棋的代码
void player_move(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; printf("玩家下棋\n"); while (1) { printf("请输入坐标>:\n"); 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("该位置已经有棋子了,请重新下棋"); } } else { printf("坐标非法,请重新输入\n"); } } }
9.电脑下棋(computer_move)
(1)电脑下棋函数的声明和定义
我们发现我们使用了一个while循环,这样是为了实现电脑和人交互下棋。
void computer_move(char board[ROW][COL], int row, int col);
(2)电脑下棋的具体实现
电脑下棋是如何实现的呢,我们可以制作一个简单版本的电脑下棋。也就是让电脑随机下。当然想让电脑智能下棋的话对于目前的阶段还是比较困难的。我们目前只完成电脑随机下。想要让电脑随机下你,那么就必须得要让电脑产生一个随机的坐标,这个坐标不能非法,也必须在一个没有下过棋子的位置才可以,这样一思考,其实就简单了。这可比我们的人下棋要简单一点。唯一一点需要注意的就是如何生成随机坐标。其实随机坐标的生成也不难理解,我们之前在讲解猜数字小游戏的时候已经讲解过了。这里给出链接:猜数字小游戏详解,有了随机坐标。那么电脑下棋的步骤也就很简单了。
具体实现如下
当然有些人觉得这个电脑下棋的代码有一些没有必要的东西,比如x和y不需要+1,如果这样做,还能少了一层判断。因为他是电脑内部操作的。不需要玩家来认识,这样做当然是可以的,但是我们这样写形式上与玩家下棋一致。更容易理解。
(3)电脑下棋的代码测试
还是和之前一样,写一点测一点,满足我们的预期。
(4)电脑下棋的代码展示
void computer_move(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; printf("电脑下棋\n"); while (1) { x = rand() % row + 1; y = rand() % col + 1; if (x >= 1 && x <= row && y >= 1 && y <= col) { if (board[x - 1][y - 1] == ' ') { board[x - 1][y - 1] = '#'; break; } } } }
10.判断输赢
我们已经实现了玩家和电脑的下棋,那么游戏肯定是有输赢的,我们接下来来实现游戏的输赢
(1)判断输赢的声明和定义以及判断输赢的基本逻辑
我们思考一下游戏一共有多少种状态呢?
答案是四种,分别为,电脑赢,玩家赢,平局,游戏继续
我们将电脑赢记作#
玩家赢记作*
平局记作 Q
游戏继续记作C
这样一来的话,我们就可以构建出我们的函数了,吧目前的数组和行列传给他,他生成一个返回值。然后根据这个返回值就能判断出游戏的状态,从而得出结论了
函数的定义和声明如下
char is_win(char board[ROW][COL], int row, int col);
输赢的基本逻辑实现如下