目录
一、基本步骤:
二、具体实现
(一).本次实现采用多文件的方式,适合初学者进行拓展学习。即:
(1).函数的声明放在game.h文件
(2).函数的定义放在game.c文件
(3).main函数放在test.c文件
(二).因为是游戏,可以循环玩,所以考虑用do while()循环套用switch case语句作为main函数主体结构,如下:
(三)、因为考虑到棋盘大小可以改变,所以用宏定义定义两个常量作为棋盘的长宽
(四).就可以开始在game函数里面实现游戏了
今天学习到了一个有趣的东西,用C语言实现三子棋,(核心知识——二维数组,函数),非常适合初学者练习,使初学者对二维数组与函数有更加深刻的认识。
1.创建一个3*3的二维数组作为棋盘
2.初始化棋盘
3.玩家下棋——判断输赢
4.电脑下棋——判断输赢
5.得出结果
(1).函数的声明放在game.h文件
#include
#include #include #define ROW 3 #define COL 3 Initboard(char board[ROW][COL], int row, int col);//初始化棋盘 Display(char broad[ROW][COL], int row, int col);//打印棋盘 void Playmove(char board[ROW][COL], int row, int col);//玩家下棋 Computermove(char board[ROW][COL], int row,int col);//电脑下棋 char Iswin(char board[ROW][COL],int row,int col);//判断输赢 这里值的注意的是:因为我们会在.c文件中引我们自定义的头文件game.h,所以对于一些库函数,宏定义的使用,我们可以放在game.h中,这样就不用重复使用了
(2).函数的定义放在game.c文件
#include "game.h" 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] = ' '; } } } Display(char broad[ROW][COL], int row, int col) { int i = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < row; j++)//打印一行的内容 { printf(" %c ", broad[i][j]); if(j
"); scanf("%d %d", &x, &y); if (x - 1 >= 0 && x - 1 < row && y - 1 >= 0 && y - 1 < col)//判断坐标是否正确 { if (board[x - 1][y - 1] == ' ')//判断此坐标是否已经落子 { board[x - 1][y - 1] = '*'; break; } else { printf("此坐标以被下,请重新落子\n"); } } else { printf("坐标错误,请重新落子\n"); } } } Computermove(char board[ROW][COL], int row, int col) { int x = 0, y = 0; printf("电脑下棋:\n"); while (1) { x = rand() % row; y = rand() % col; if (x >= 0 && x < row && y >= 0 && y < col)//判断坐标是否正确 { if (board[x][y] == ' ')//判断此坐标是否已经落子 { board[x][y] = '#'; break; } } } } char Iswin(char board[ROW][COL], int row, int col) { //玩家赢—'*' 电脑赢—'#' 平局—'a' 继续—'b' //某家赢 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]; } else if (board[0][i] == board[1][i] && board[0][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]; } else if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][0] != ' ')//判断对角线 { return board[0][2]; } //没有一家赢 for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { if (board[i][j] == ' ') return 'b';//继续 } } return 'a';//平局 } (3).main函数放在test.c文件
#include"game.h" void menu() { printf("***************************\n"); printf("******* 1.play *****\n"); printf("******* 0.exit *****\n"); printf("***************************\n"); } void game() { char rew = '0'; char board[ROW][COL]; Initboard(board,ROW,COL);//初始化棋盘为空格 Display(board, ROW, COL);//打印棋盘 while (1) { //玩家下棋 Playmove(board, ROW, COL); Display(board, ROW, COL);//打印棋盘 //判断输赢 rew=Iswin(board, ROW, COL); if (rew == '*') { printf("玩家赢!\n"); break; } //电脑下棋 Computermove(board,ROW,COL); Display(board, ROW, COL);//打印棋盘 //判断输赢 rew=Iswin(board,ROW,COL); if (rew == '#') { printf("电脑赢!\n"); break; } if (rew == 'b') { printf("平局!\n"); break; } } } int main() { srand((unsigned int)time(NULL)); int input = 0; do { menu();//菜单函数 printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 2: printf("退出成功\n"); break; default: printf("输入失败,请重新输入\n"); break; } } while (input); return 0; }
这里就有必要说说多文件的好处了,一是可以使main函数内部代码简洁,二是使总体代码层次更鲜明,三是可以多人一起操作等等,有兴趣的小伙伴可以自行了解。
int input = 0; do { menu();//菜单函数 printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 2: printf("退出成功\n"); break; default: printf("输入失败,请重新输入\n"); break; } } while (input);
#define ROW 3 #define COL 3
1.创建一个3*3二维数组,并创建Initboard函数对其进行初始化,既然是棋盘,刚开始棋盘肯定不能有东西,所以想到用两个For循环将其全部初始化为空格
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] = ' '; } } }
2.现在棋盘全是空格,所以需要增加一些东西,是棋盘更好看。效果如下:
所以就需要考虑创建一个函数Display(),将修饰后的棋盘打印出来:
(1).由图可以思考,对于行,可以将 (空格%c空格 | ) 看做一组数据,打印一排,(根据前面宏定义常量ROW==COL==3),共有ROW组,且最后一组的‘ | ’不打印,所以自然想到循环,代码实现如下:
int i = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < row; j++)//打印一行的内容 { printf(" %c ", broad[i][j]); if(j
(2).一行数据打印我们就该换行printf("\n");
(3).对于列,可以将(_ _ _ |)看做一组,且最后一组的‘ _ _ _ ’不打印,共有COL组,代码实现如下:
for (j = 0; j < row; j++)//打印分隔符 { printf("---"); if (j < row - 1) printf("|"); } printf("\n");
3.处理好棋盘就可以开始下棋了(设 玩家棋子——‘*’,电脑棋子——‘#’)
值得注意的是:不论电脑还是玩家下棋,都应该考虑(1).输入的坐标有没有超出棋盘范围
(2).此坐标在之前有没有落过子
(1).玩家先下棋,创建Playermove()函数,因为可能输入非法坐标,所以采用while循环输入,并且注意,我们应该站在用户的角度思考,正常情况坐标从1开始,而数组下标从0开始,所以用户输入进的数据在代码中应该减1,在最为数组坐标
1).玩家输入坐标
2).if语句判断坐标是否超出棋盘范围,超出则重新输入,没超出则进入if
3).再用if语句判断此坐标是否在之前落过子,落过则重新输入,没落过则将此坐标的‘空格’改为‘*’
4).代码实现如下
void Playmove(char board[ROW][COL], int row, int col) { int x = 0,y = 0; printf("玩家下棋:\n"); while (1) { printf("请输入坐标,(注意用空格隔开):>"); scanf("%d %d", &x, &y); if (x - 1 >= 0 && x - 1 < row && y - 1 >= 0 && y - 1 < col)//判断坐标是否正确 { if (board[x - 1][y - 1] == ' ')//判断此坐标是否已经落子 { board[x - 1][y - 1] = '*'; break; } else { printf("此坐标以被下,请重新落子\n"); } } else { printf("坐标错误,请重新落子\n"); } } }
(2).电脑下棋,因为电脑是自动下棋,所以这里我们采用随机数让电脑落子,其余操作与玩家下棋一样,只是需要将电脑棋子改为——‘#’,对于随机数的相关知识,有兴趣的小伙伴可自行了解,代码实现如下:
Computermove(char board[ROW][COL], int row, int col) { int x = 0, y = 0; printf("电脑下棋:\n"); while (1) { x = rand() % row; y = rand() % col; if (x >= 0 && x < row && y >= 0 && y < col)//判断坐标是否正确 { if (board[x][y] == ' ')//判断此坐标是否已经落子 { board[x][y] = '#'; break; } } } }
5.根据逻辑,不论是玩家下棋后,还是电脑下棋后,我们都应该考虑一次输赢。现在先实现输赢的标志。
1).创建函数char Iswin(),并用该函数的返回值来判断输赢
玩家赢——返回“*”
电脑赢——返回“#”
平局——返回“a”
继续——返回“b”
2).某一方赢的标志即看数组的某一行、某一列、某一条对角线相等且不等于“空格”。若有一方赢,则返回相应的符号;若没有一家赢,则判断棋盘是否占满,即遍历数组,看是否还具有“空格”,然后返回相应符号。代码实现如下:
char Iswin(char board[ROW][COL], int row, int col) { //玩家赢—'*' 电脑赢—'#' 平局—'a' 继续—'b' //某家赢 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]; } else if (board[0][i] == board[1][i] && board[0][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]; } else if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][0] != ' ')//判断对角线 { return board[0][2]; } //没有一家赢 for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { if (board[i][j] == ' ') return 'b';//继续 } } return 'a';//平局 }
这里小编没有想到合适的方法判断,只有暴力穷举,所以如果更改棋盘大小,此方法就不适用了,有兴趣的小伙伴可以自行思考。
6.判断输赢函数实现后,就要将它套入下棋里面,即:
(1).玩家落一次子,则调用一次此函数,用临时变量接收返回值并判断是否为“*”,是则玩家赢,不是则电脑下子
(2).电脑落一次子,调用一次此函数,用临时变量接收返回值并判断是否为“#”,是则电脑赢,不是则玩家下子
(3).若不是以上两种,则重新判断是“a”还是“b”
a则平局,游戏结束
b则继续下棋.
(4).代码实现如下:
void game() { char rew = '0'; char board[ROW][COL]; Initboard(board,ROW,COL);//初始化棋盘为空格 Display(board, ROW, COL);//打印棋盘 while (1) { //玩家下棋 Playmove(board, ROW, COL); Display(board, ROW, COL);//打印棋盘 //判断输赢 rew=Iswin(board, ROW, COL); if (rew == '*') { printf("玩家赢!\n"); break; } //电脑下棋 Computermove(board,ROW,COL); Display(board, ROW, COL);//打印棋盘 //判断输赢 rew=Iswin(board,ROW,COL); if (rew == '#') { printf("电脑赢!\n"); break; } if (rew == 'b') { printf("平局!\n"); break; } } }
好了,游戏实现到这里就基本结束了,下面来看看效果吧!
后续小伙伴们可以自行思考,添加更多功能,或者运行图形库等相关知识使界面更精美等等等等,此次知识到这里就结束了,拜拜!!!