今天给大家分享一个关于C语言的一个小游戏,首先得有一定的代码基础,分享的这个小游戏,最主要不是用来玩,主要是用来锻炼我们组织大型代码的能力,话不多说,正文开始:
📌三子棋:
在写代码之前我们先来了解一下这个小游戏,说三子棋很多老铁可能有点陌生,但是说井字棋就豁然开朗,我们首先来看代码实现的总体效果是什么样的:
字体有点小,可以慢慢看,理清逻辑关系。
📌文件的创建:
我们为了逻辑关系清晰,所以创建多个文件:
创建一个头文件:game.h 用来包含我们的头文件和函数的声明以及其他的定义
创建一个源文件:game.c 用来实现关于游戏的函数
再创建一个源文件:test.c 用来测试游戏的基本逻辑
打印菜单和基本选择逻辑:
当我们创建好这些文件之后,我们在test.c里面实现游戏的基本逻辑:
首先,我们先要打印一个菜单,然后供玩家选择,如果玩家玩完一次还想玩还可以继续,不想玩直接退出就行,所以我们需要一个do while的循环,我们把头文件全部包含到game.h里面,然后在包含game.h就行了
头文件:game.h:
//game.h的头文件//包含头文件:
源文件:test.c:
//包含game.h的头文件voidmenu() //打印菜单的函数,不需要返回,所以返回类型是void{ printf("********************\n"); printf("**** 1. play *****\n"); printf("**** 0. exit *****\n"); printf("********************\n"); } intmain() { intinput=0; do//do while循环来实现多次操作 { menu(); printf("请选择:>"); scanf("%d", &input); } while (input); //这里的进入循环的条件是input为真(非0),如果为假(0),则不会进入循环return0; }
打印完菜单之后,就要完善这个菜单带来的效果了,选择play,则进入三子棋游戏,如果选择exit,则退出游戏吧!所以我们就需要用到switch case语句来进行选择
完善过后的do while循环:
intmain() { intinput=0; do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) //将输入进来的值进行判断 { case1: //如果为1,则玩游戏printf("三子棋游戏:\n"); break; case0: //如果为0,则退出游戏printf("退出游戏\n"); break; default: //既不是1也不是0,选择错误,重新选择吧printf("选择错误,请重新选择\n"); break; } } while (input); return0; }
我们选1要进行三子棋游戏,所以我们要分装一个game函数,来实现三子棋游戏
game函数:
在创建一个game函数之后,首先我们得创建一个二维数组,再初始化棋盘,将其初始化为空格
所以我们要在switch case语句中的case 1:中加入game函数
源文件:test.c :
switch (input) { case1: printf("三子棋游戏:\n"); game(); //游戏逻辑的函数break;
voidgame() { charboard[3][3] = { 0 }; //创建一个二维数组3*3//初始化棋盘:InitBoard(board, 3, 3); //将数组,行,列都传给这个函数实现初始化棋盘}
在这里有一个创建二维数组的好方法:
我们可以在头文件里面用#define来定义ROW为行,定义COL为列,然后赋值,因为如果我们要改变这个棋盘的大小,我们需要把我们整个程序里面的所有关于行和列的值全部修改,所以太麻烦,因此我们只需要在头文件中定义行和列,在后期需要改变的时候直接在头文件里面进行修改,非常方便,所以我们需要改动一下我们写出的代码:
头文件:game.h:
//行和列的定义
源文件:test.c:
voidgame() { charboard[ROW][COL] = { 0 }; //初始化棋盘:InitBoard(board, ROW, COL); //将数组,行,列都传给这个函数实现初始化棋盘}
修改好之后就来进入我们的初始化棋盘这个函数:
2.1初始化棋盘:
我们初始化棋盘来设定一个函数:InitBoard
源文件:test.c:
voidgame() { charboard[ROW][COL] = { 0 }; //初始化棋盘:InitBoard(board, ROW, COL); //将数组,行,列都传给这个函数实现初始化棋盘}
然后先要在头文件里面声明
头文件:game.h:
//函数的声明://初始化棋盘voidInitBoard(charboard[ROW][COL], introw, intcol); //不需要返回什么所以返回类型是void
在声明函数之后,我们在game.c里面来实现这个初始化棋盘这个函数:
初始化棋盘就是说将这个二维数组里面的元素先初始化为空格,有两种方法,我们一一来介绍:
第一种:遍历这个数组中的每一个元素,将其修改为空格
源文件:game.c:
//遍历每一个元素进行修改voidInitBoard(charboard[ROW][COL], introw, intcol) { inti=0; intj=0; for (i=0; i<row; i++) { for (j=0; j<col; j++) { board[i][j] =' '; } } }
第二种:使用memset函数来初始化
voidInitBoard(charboard[ROW][COL], introw, intcol) { memset(&board[row][col], ' ', row*col*sizeof(board[0][0])); } //使用memset时需要包含头文件#include<string.h>
两种方法都可以用。
在初始化棋盘过后,我们把初始化的棋盘打印出来,再分装一个打印棋盘的函数
2.2打印棋盘:
设定一个打印棋盘的函数:PrintfBoard
源文件:test.c:
voidgame() { charboard[ROW][COL] = { 0 }; //初始化棋盘:InitBoard(board, ROW, COL); //将数组,行,列都传给这个函数实现初始化棋盘//打印棋盘PrintfBoard(board, ROW, COL); }
设定好了之后先在头文件里面声明
头文件game.h:
//函数的声明://初始化棋盘voidInitBoard(charboard[ROW][COL], introw, intcol); //打印棋盘:voidPrintfBoard(charboard[ROW][COL], introw, intcol);
在game.c里面来实现这个打印棋盘的函数:
首先我们得知道我们要打印什么:
虽然我们初始化了棋盘,但是我们得给棋盘来分割一下,看起来比较明显,也利于玩家下棋
那这种分割线怎么来打印呢?
我们一步一步来进行分析:首先得设置总的打印行数,我们将一个“ %c | %c | %c |”(%c的前后都有空格)看作一组,将一个“---|---|---|”也看作一组,所以我们需要打印三行嘛,先写出这个代码:
voidPrintfBoard(charboard[ROW][COL], introw, intcol) { inti=0; for (i=0; i<row; i++) { printf(" %c | %c | %c |", board[i][0], board[i][1], board[i][2]); printf("\n"); printf("---|---|---|", board[i][0], board[i][1], board[i][2]); printf("\n"); } }
如果这样写,就多打印了一行“---|---|---|”和“ %c | %c | %c |”,所以我们打印的时候就设置if语句来少打印一行
voidPrintfBoard(charboard[ROW][COL], introw, intcol) { inti=0; for (i=0; i<row; i++) { printf(" %c | %c | %c |", board[i][0], board[i][1], board[i][2]); printf("\n"); if (i<row-1) { printf("---|---|---|", board[i][0], board[i][1], board[i][2]); printf("\n"); } } }
可以看到,在我们修改完之后,后面还是多了一列的“|”,再加上这种写法本身是存在缺陷,这里的列是固定的,无法灵活的修改,所以我们采用新的方法
我们可以将“ %c |”这样的一个看成一组,但是在打印最后一组时,只打印“ %c ”,不打印“|”,同时,我们也要将“---|”这样的看成一组,只需要打印两行即可。
//打印棋盘:voidPrintfBoard(charboard[ROW][COL], introw, intcol) { inti=0; for (i=0; i<row; i++) //设置一个需要打印几行 { intj=0; for (j=0; j<col; j++) //设置打印“ %c |”的行数 { printf(" %c ", board[i][j]); if (j<col-1) //因为最后一列不需要打印“|” { printf("|"); } } printf("\n"); //打印完之后换行if (i<row-1) //在打印“---|”是比打印“ %c |”少一行 { for (j=0; j<col; j++) //设置打印多少行的“---|” { printf("---"); if (j<col-1) //最后一列也不需要打印“|” { printf("|"); } } } printf("\n"); } }
可以看到,棋盘很好的打印了出来,接下来就需要轮到我们来进行下棋的环节了
2.3玩家下棋:
假如我们设置玩家先下棋把,然后再电脑下棋,那么首先来设置玩家下棋的函数
设定一个函数:PlayerMove
源文件:test.c:
voidgame() { charboard[ROW][COL] = { 0 }; //初始化棋盘:InitBoard(board, ROW, COL); //将数组,行,列都传给这个函数实现初始化棋盘//打印棋盘:PrintfBoard(board, ROW, COL); //玩家下棋:PlayerMove(board,ROW,COL); PrintfBoard(board, ROW, COL); //下完棋之后打印出棋盘}
设定好先在头文件里面声明
头文件:game.h:
//函数的声明://初始化棋盘voidInitBoard(charboard[ROW][COL], introw, intcol); //打印棋盘:voidPrintfBoard(charboard[ROW][COL], introw, intcol); //玩家下棋:voidPlayerBoard(charboard[ROW][COL], introw, intcol);
声明好了之后,我们在game.c中实现这个函数:
源文件:game.c:
//玩家下棋:voidPlayerBoard(charboard[ROW][COL], introw, intcol) { printf("玩家下棋\n"); intx=0; inty=0; while (1) //设置死循环,如果输入非法就一直输入,如果输入正确就跳出循环 { printf("请输入你所要下棋的坐标:>"); scanf("%d %d", &x, &y); if (x>=1&&x<=row&&y>=1&&y<=col) //用来判断坐标的合法性 { if (board[x-1][y-1] !=' ') //因为玩家通常不会认为数组的下标是从0开始的 { //他们通常认为下标是1,所以玩家输入的坐标-1才能得到棋盘 //中的坐标printf("该位置已经落子,请重新输入坐标:>"); } else { board[x-1][y-1] ='*'; break; //输入成功,则跳出循环 } } else//如果输入非法,重新输入 { printf("输入非法,请重新输入:>"); } } }
玩家下完棋打印出棋盘,再是电脑下完棋打印棋盘
2.4电脑下棋:
电脑下棋的话,我们就让电脑随机下棋,下完棋再打印出棋盘,所以需要再设置一个电脑下棋的函数
设定一个函数:ComputerMove
源文件:test.c:
voidgame() { charboard[ROW][COL] = { 0 }; //初始化棋盘:InitBoard(board, ROW, COL); //将数组,行,列都传给这个函数实现初始化棋盘//打印棋盘:PrintfBoard(board, ROW, COL); //玩家下棋:PlayerBoard(board, ROW, COL); PrintfBoard(board, ROW, COL); //电脑下棋:ComputerMove(board, ROW, COL); PrintfBoard(board, ROW, COL); //下完棋打印出棋盘}
头文件里面要声明这个函数:
头文件game.h:
//函数的声明://初始化棋盘voidInitBoard(charboard[ROW][COL], introw, intcol); //打印棋盘:voidPrintfBoard(charboard[ROW][COL], introw, intcol); //玩家下棋:voidPlayerBoard(charboard[ROW][COL], introw, intcol); //电脑下棋:voidComputerMove(charboard[ROW][COL], introw, intcol);
电脑下棋是随机下棋,所以我们需要用到rand函数来生成随机数,并且在主函数中需要设置
源文件:game.c:
//电脑下棋voidComputerMove(charboard[ROW][COL], introw, intcol) { intx=0; inty=0; printf("电脑下棋:>\n"); x=rand() %row; //生成随机数,要在头文件中包含#include<stdlib.h>y=rand() %col; //rang()%row的意思就是随机生成0~2之间的数while (1) { if (board[x][y] ==' ') //因为电脑下棋就是按照二维数组的小标来下的所以不用改 { board[x][y] ='#'; break; } } }
在使用rand函数时要在main函数中加上
srand((unsigned int)time(NULL));
源文件:test.c:
intmain() { intinput=0; srand((unsignedint)time(NULL)); //在使用time函数时也需要包含头文件#include<time.h>do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) //将输入进来的值进行判断 { case1: //如果为1,则玩游戏printf("三子棋游戏:\n"); game(); break; case0: //如果为0,则退出游戏printf("退出游戏\n"); break; default: //既不是1也不是0,选择错误,重新选择吧printf("选择错误,请重新选择\n"); break; } } while (input); return0; }
头文件:game.h:
//包含头文件:
2.5判断输赢:
在玩家下完棋之后需要进行判断输赢,如果赢了,则跳出,没赢就继续,在电脑下完棋之后也需要进行判断,那假如棋盘下满了,没有人胜出,则为平局。一共四种情况,所以我们要分装一个判断输赢的函数
这个函数一共要处理四种情况,玩家赢、电脑赢、平局、继续游戏,所以我们假设:如果玩家赢,返回‘*’,如果电脑赢,返回‘#’,如果平局,返回‘P’,谁都没赢游戏继续,返回‘Q’,所以我们要用一个值来接收返回的字符
设定一个函数:IsWin
源文件:test.c:
voidgame() { intret=0; charboard[ROW][COL] = { 0 }; //初始化棋盘:InitBoard(board, ROW, COL); //将数组,行,列都传给这个函数实现初始化棋盘//打印棋盘:PrintfBoard(board, ROW, COL); //玩家下棋:PlayerBoard(board, ROW, COL); PrintfBoard(board, ROW, COL); //判断输赢:ret=IsWIN(board, ROW, COL); //电脑下棋:ComputerMove(board, ROW, COL); PrintfBoard(board, ROW, COL); //判断输赢:ret=IsWIN(board, ROW, COL); }
头文件中也要声明函数
头文件:game.h:
//函数的声明://初始化棋盘voidInitBoard(charboard[ROW][COL], introw, intcol); //打印棋盘:voidPrintfBoard(charboard[ROW][COL], introw, intcol); //玩家下棋:voidPlayerBoard(charboard[ROW][COL], introw, intcol); //电脑下棋:voidComputerMove(charboard[ROW][COL], introw, intcol); //判断输赢:charIsWin(charboard[ROW][COL], introw, intcol);
在源文件game.c中来实现我们的函数
判断玩家赢还是电脑赢就是行连成三子,列连成三子,对角线连成三子,若为平局,就证明整个棋盘满了,也就是没有空格了,但要注意,排除空格,因为三个空格连成一起也不行
源文件:game.c:
charIsWin(charboard[ROW][COL], introw, intcol) { inti=0; for (i=0; i<row; i++) { if (board[i][0] ==board[i][1] &&board[i][1] ==board[i][2] &&board[i][0] !=' ') returnboard[i][0]; //判断三行是否相同 } for (i=0; i<row; i++) { if (board[0][i] ==board[1][i] &&board[1][i] ==board[2][i] &&board[1][i] !=' ') returnboard[1][i]; //判断三列是否相同 } //判断对角线if (board[0][0] ==board[1][1] &&board[1][1] ==board[2][2] &&board[0][0] !=' ') { returnboard[0][0]; } if (board[0][2] ==board[1][1] &&board[1][1] ==board[2][0] &&board[1][1] !=' ') { returnboard[1][1]; } //判断平局再分装一个函数IsFullif (IsFull(board, row, col)) //可以看到这里与上面的函数不同的是, { return'P'; //平局返回‘P ’ } return'Q'; //若上面的条件一个都没有发生,则返回‘Q’}
分装一个判断是否平局的函数:同样的也要在头文件中声明
2.5.1判断是否平局:
头文件:game.h:
//函数的声明://初始化棋盘voidInitBoard(charboard[ROW][COL], introw, intcol); //打印棋盘:voidPrintfBoard(charboard[ROW][COL], introw, intcol); //玩家下棋:voidPlayerBoard(charboard[ROW][COL], introw, intcol); //电脑下棋:voidComputerMove(charboard[ROW][COL], introw, intcol); //判断输赢:charIsWin(charboard[ROW][COL], introw, intcol); //判断是否平局intIsFull(charboard[ROW][COL], introw, intcol); //由于这里要接收返回值所以设置返回类型为int
源文件:game.c:
charIsWin(charboard[ROW][COL], introw, intcol) { inti=0; for (i=0; i<row; i++) { if (board[i][0] ==board[i][1] &&board[i][1] ==board[i][2] &&board[i][0] !=' ') returnboard[i][0]; } for (i=0; i<row; i++) { if (board[0][i] ==board[1][i] &&board[1][i] ==board[2][i] &&board[1][i] !=' ') returnboard[1][i]; } if (board[0][0] ==board[1][1] &&board[1][1] ==board[2][2] &&board[0][0] !=' ') { returnboard[0][0]; } if (board[0][2] ==board[1][1] &&board[1][1] ==board[2][0] &&board[1][1] !=' ') { returnboard[1][1]; } if (IsFull(board, row, col)) //如果返回为真,则进入 { return'P'; } return'Q'; }
IsFull函数的实现:
源文件:game.c:
intIsFull(charboard[ROW][COL], introw, intcol) { inti=0; intj=0; for (i=0; i<row; i++) //遍历一下整个数组看有没有存在空格 { for (j=0; j<col; j++) { if (board[i][j] ==' ') { return0; } } } return1; }
当我们的一整套流程写完之后,就该在game函数里面来接收这些返回值了:
源文件:test.c:
voidgame() { intret=0; charboard[ROW][COL] = { 0 }; //初始化棋盘:InitBoard(board, ROW, COL); //将数组,行,列都传给这个函数实现初始化棋盘//打印棋盘:PrintfBoard(board, ROW, COL); while (1) //由于这些下棋的过程是一个循环,只有当有人胜出或者平局才会跳出循环 { //玩家下棋:PlayerBoard(board, ROW, COL); PrintfBoard(board, ROW, COL); //判断输赢:ret=IsWIN(board, ROW, COL); //设置一个变量来接收返回值if (ret!='Q') //如果ret不是‘Q’,则证明就有人胜出或者平局,则会跳出循环 { break; } //电脑下棋:ComputerMove(board, ROW, COL); PrintfBoard(board, ROW, COL); //判断输赢:ret=IsWIN(board, ROW, COL); if (ret!='Q') { break; } } //当跳出循环后,我们就应该判断是谁赢,还是平局if (ret=='*') { printf("恭喜你,你赢了!\n"); } elseif (ret=='#') { printf("很遗憾,你输了!\n"); } elseif (ret=='p') { printf("平局\n"); } }
一整套的程序都写完了,在写程序的时候,我们要注意几点:
- 使用函数时要在头文件里面声明
- 使用的每一个库函数都要注意包含头文件
- 写完一个函数先让它运行一下,测试当前写的这个函数对不对
- 在设定函数时要注意设定的函数的返回类型
整个程序代码:
头文件:game.h:
//包含头文件://行和列的定义//函数的声明://初始化棋盘voidInitBoard(charboard[ROW][COL], introw, intcol); //打印棋盘:voidPrintfBoard(charboard[ROW][COL], introw, intcol); //玩家下棋:voidPlayerBoard(charboard[ROW][COL], introw, intcol); //电脑下棋:voidComputerMove(charboard[ROW][COL], introw, intcol); //判断输赢:charIsWin(charboard[ROW][COL], introw, intcol); //判断是否平局intIsFull(charboard[ROW][COL], introw, intcol);
源文件:game.c:
//初始化棋盘voidInitBoard(charboard[ROW][COL], introw, intcol) { inti=0; intj=0; for (i=0; i<row; i++) { for (j=0; j<col; j++) { board[i][j] =' '; } } //memset(&board[row][col], ' ', row * col * sizeof(board[0][0]));} //打印棋盘:voidPrintfBoard(charboard[ROW][COL], introw, intcol) { inti=0; for (i=0; i<row; i++) { intj=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"); } } //玩家下棋:voidPlayerBoard(charboard[ROW][COL], introw, intcol) { printf("玩家下棋\n"); intx=0; inty=0; while (1) { printf("请输入你所要下棋的坐标:>"); scanf("%d %d", &x, &y); if (x>=1&&x<=row&&y>=1&&y<=col) { if (board[x-1][y-1] !=' ') { printf("该位置已经落子,请重新输入坐标:>\n"); } else { board[x-1][y-1] ='*'; break; } } else { printf("输入非法,请重新输入:>\n"); } } } //电脑下棋voidComputerMove(charboard[ROW][COL], introw, intcol) { intx=0; inty=0; printf("电脑下棋:>\n"); x=rand() %row; y=rand() %col; while (1) { if (board[x][y] ==' ') { board[x][y] ='#'; break; } } } //判断平局:intIsFull(charboard[ROW][COL], introw, intcol) { inti=0; intj=0; for (i=0; i<row; i++) { for (j=0; j<col; j++) { if (board[i][j] ==' ') { return0; } } } return1; } //判断输赢:charIsWin(charboard[ROW][COL], introw, intcol) { inti=0; for (i=0; i<row; i++) { if (board[i][0] ==board[i][1] &&board[i][1] ==board[i][2] &&board[i][0] !=' ') returnboard[i][0]; } for (i=0; i<row; i++) { if (board[0][i] ==board[1][i] &&board[1][i] ==board[2][i] &&board[1][i] !=' ') returnboard[1][i]; } if (board[0][0] ==board[1][1] &&board[1][1] ==board[2][2] &&board[0][0] !=' ') { returnboard[0][0]; } if (board[0][2] ==board[1][1] &&board[1][1] ==board[2][0] &&board[1][1] !=' ') { returnboard[1][1]; } if (IsFull(board, row, col)) { return'P'; } return'Q'; }
源文件:test.c:
voidgame() { intret=0; charboard[ROW][COL] = { 0 }; //初始化棋盘:InitBoard(board, ROW, COL); //将数组,行,列都传给这个函数实现初始化棋盘//打印棋盘:PrintfBoard(board, ROW, COL); while (1) { //玩家下棋:PlayerBoard(board, ROW, COL); PrintfBoard(board, ROW, COL); //判断输赢:ret=IsWin(board, ROW, COL); if (ret!='Q') { break; } //电脑下棋:ComputerMove(board, ROW, COL); PrintfBoard(board, ROW, COL); //判断输赢:ret=IsWin(board, ROW, COL); if (ret!='Q') { break; } } if (ret=='*') { printf("恭喜你,你赢了!\n"); } elseif (ret=='#') { printf("很遗憾,你输了!\n"); } elseif (ret=='p') { printf("平局\n"); } } voidmenu() { printf("********************\n"); printf("**** 1. play *****\n"); printf("**** 0. exit *****\n"); printf("********************\n"); } intmain() { intinput=0; srand((unsignedint)time(NULL)); do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) //将输入进来的值进行判断 { case1: //如果为1,则玩游戏printf("三子棋游戏:\n"); game(); break; case0: //如果为0,则退出游戏printf("退出游戏\n"); break; default: //既不是1也不是0,选择错误,重新选择吧printf("选择错误,请重新选择\n"); break; } } while (input); return0; }
三子棋小游戏就写完了,如果本篇有什么疑问或者不足,大家可以私信或者在评论区指出,感谢大家阅读!