全篇无任何废话,本文的解释大多数都在代码段中,所以一定要看代码,边看边学边理解。
这只是初学者入门的一个小游戏,不难懂,没有什么复杂的内容
可以先学习一下比三子棋还简单的猜数字小游戏
C语言趣味小游戏——猜数字(匠心制作)
游戏的条件
1.根据坐标和电脑下棋,玩家一步棋,电脑一步棋,且不能悔棋
2.玩完一盘可以继续
可以学到的内容
1.打印一个简单的菜单
2.一些细节的用户体验
3.自定义函数 , 时间戳的运用 , #define定义
4.程序语言的一些入门逻辑
5.C语言的知识
————————————————————————————————————————
首先来一个简简单单的代码三部曲(写在test.c的文件下,后期有妙用)
// 测试三子棋游戏的逻辑
#include
int main()
{
return 0;
}
写一个返回值为void的test函数,且调用它
void test()
{
}
int main()
{
test();
return 0;
}
因为是玩游戏,所以写一个do while,并且打印菜单,和猜数字异曲同工
void menu()
{
printf("*********************\n");
printf("****** 1.play ******\n");
printf("****** 0.over ******\n");
printf("*********************\n");
}
void test()
{
do
{
menu(); // 菜单
} while ();
}
看到菜单之后就做出选择
void test()
{
int input = 0; // 初始化
do
{
menu();
printf("请选择:>");
scanf("%d", &input); // 输入
} while ();
}
定义一个整型并初始化,方便后期输入
void test()
{
int input = 0; // 初始化
do
{
menu();
printf("请选择:>");
scanf("%d", &input); // 输入
} while ();
}
加入switch语句
不懂得switch,case,default语句和do while循环可以看下这个,1分钟就懂了
https://blog.csdn.net/W20223161730/article/details/126200624?spm=1001.2014.3001.5502
void test()
{
int input = 0; // 初始化
do
{
menu();
printf("请选择:>");
scanf("%d", &input); // 输入
switch(input) // 这里使用input的原因是根据输入的值进入对应的case语句
{
case 1:
// 假设这里有三子棋游戏
printf("玩游戏!\n");
break;
case 0:
printf("选择退出,游戏结束!\n");
break;
default :
printf("选择错误,请重新输入!\n");
break;
}
} while (input); // 这里使用input的原因是根据input判断是否继续,如果输入的是1 为真(继续),否则 0为假(结束)
}
开始实现游戏的主体 (需要先去了解一下什么是二维数组)
1:游戏开始肯定会呈现一个九宫格的棋盘,三行三列,且中间什么都没有,输入坐标下棋后会被覆盖
2:被覆盖之后会被打印出来,也就是说,棋盘以及玩家和电脑的棋子会被存储起来
3:如果想要实现棋盘中间什么的没有,那么中间肯定是空格
正片开始 : 初始化一个名为board的二维数组,得是三行三列
void game()
{
// 存放下棋的数据
char board[3][3] = { 0 };
}
再初始化棋盘为全空格,后面还会有更多这样的操作,为了方便所以就直接封装成函数,分模块来写
void game()
{
// 存放下棋的数据
char board[3][3] = { 0 };
// 初始化棋盘为全空格
Initboard(board, 3, 3);
}
所以再添加一个game.c 和game.h 的两个文件
在test.c文件里面有了这个函数之后,声明就放在game.h的头文件里面
在函数声明的时候需要告诉函数它的返回类型,参数等
// 名字为game.h 的头文件
// 初始化棋盘 棋盘 行 列
void Initboard(char board[3][3], int row, int col);
然后再用define在.h中定义一下行和列,免得后期更改比较麻烦
#define ROW 3
#define COL 3
// 初始化棋盘 棋盘 行 列
void Initboard(char board[3][3], int row, int col);
把文件里的行和列都替换成define定义的ROW和COL
记得在文件中包含game.h ↓
#include"game.h"
顺便把game.h文件中写上三部曲的第一步
#include
这样做的目的是其他文件包含game.h,而game.h文件又包含了行和列还有三部曲的第一部,会节省很多时间
// game.h 文件
#pragma once
#include
#define ROW 3
#define COL 3
// 初始化棋盘 棋盘 行 列
void Initboard(char board[ROW][COL], int row, int col);
// game.c 文件
#include"game.h"
void Initboard(char board[ROW][COL], int row, int col)
{
}
// test.c 文件
#include"game.h"
然后声明完了放到game.c 的文件中实现
void Initboard(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] = ' ';
}
printf("\n");
}
}
在test.c文件中来一个打印棋盘的函数
void game()
{
// 存放下棋的数据
char board[ROW][COL] = { 0 };
// 初始化棋盘为全空格
Initboard(board, ROW, COL);
// 打印棋盘
Displayboard(board, ROW, COL);
}
还是头文件
void Displayboard(char board[ROW][COL], int row, int col);
game.c文件
void Displayboard(char board[ROW][COL], int row, int col)
{
}
棋盘打印还是需要精心设计的,这里就不过多赘述,直接上代码
void Displayboard(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");
}
}
}
效果图
后面就开始重中之重了,玩家和电脑是如何下棋的
还是继续写一个函数 用来实现玩家下棋
// test.c
Player_move(board, ROW, COL);
// game.c
void Player_move(char board[ROW][COL], int row, int col)
{
}
// game.h
void Player_move(char board[ROW][COL], int row, int col);
需要提前考虑到两点
1:玩家的非法输入
2:玩家输入并不知道数组的下标是从0开始,所以需要加1
void Player_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家下棋!\n");
printf("请输入坐标!\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
}
else
{
printf("坐标输入错误,请重新输入!\n");
}
}
玩家不可能就输入错误一次,所以加上一个循环才是合理的
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)
{
}
else
{
printf("坐标输入错误,请重新输入!\n");
}
}
}
下一步
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (board[x - 1][y - 1] == ' ') // 玩家输入的要减1,因为数组的下标是从0开始的
// 如果输入的是一个空格,那么就说明这个位置没有被占用
{
board[x - 1][y - 1] == '*'; // 那么就把空格替换成 * 来当作玩家下的棋
break;
// 一定要记得break,不然一直死循环
}
else
{
printf("该坐标被占用,请重新输入!\n");
}
}
再重新打印一下棋盘
void game()
{
// 存放下棋的数据
char board[ROW][COL] = { 0 };
// 初始化棋盘为全空格
Initboard(board, ROW, COL);
// 打印棋盘
Displayboard(board, ROW, COL);
// 玩家下棋
Player_move(board, ROW, COL);
// 重新打印
Displayboard(board, ROW, COL);
}
效果图
然后开始写一个简单的电脑下棋函数,以后感兴趣可以自行加上堵截,优先胜利的代码,这次就先不过多介绍,因为是入门先理解
// test.c
Computer_move(board, ROW, COL);
// game.c
void Computer_move(char board[ROW][COL], int row, int col)
{
}
// game.h
void Computer_move(char board[ROW][COL], int row, int col);
那么问题来了,怎么控制让电脑在规定的范围内,且不重复的下棋
需要简单的了解rand,srand,time函数
这个函数调用一次,每次就归零一次(重新开始),所以不需要频繁的调用,只需要调用一次就可以了, 所以直接放到主函数的test函数里面
srand((unsigned int)time(NULL));
在game.h的头文件包含一下
#include // srand
#include // time
电脑下棋的实现
void Computer_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋!\n");
while (1)
{
x = rand() % row; // 值为0到2
y = rand() % col; // 值为0到2
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
玩家和电脑下完棋都打印一下 ,下棋不可能只下一步,所以放到循环里面
void game() // 因为是传参,所以要带上ROW和COL
{
// 存放下棋的数据
char board[ROW][COL] = { 0 };
// 初始化棋盘为全空格
Initboard(board, ROW, COL);
// 打印棋盘
Displayboard(board, ROW, COL);
while (1)
{
// 玩家下棋
Player_move(board, ROW, COL);
// 下完棋打印一下
Displayboard(board, ROW, COL);
// 电脑下棋
Computer_move(board, ROW, COL);
// 下完棋打印一下
Displayboard(board, ROW, COL);
}
}
效果
现在还需要实现怎么判断输赢的函数
什么情况下属于游戏结束
1:玩家赢 2:电脑赢 3:平局 或者 :继续
所以封装成一个函数来用是最方便的 那么直接硬写肯定是不太行的
所以当目的达到了,返回一个特定的符号,如果是返回的情况,一定需要一个用来接收的内容
// 玩家赢---'*'
//
// 电脑赢---'#'
//
// 平局---'Q'
//
// 继续---'C'
//
而且每下完一步棋,都需要判断一下,所以这个判断输赢的函数要放在玩家和电脑下完棋之后
void game()
{
// 接收is_win的返回类型
char ret = 0;
// 存放下棋的数据
char board[ROW][COL] = { 0 };
// 初始化棋盘为全空格
Initboard(board, ROW, COL);
// 打印棋盘
Displayboard(board, ROW, COL);
while (1)
{
// 玩家下棋
Player_move(board, ROW, COL);
// 下完棋打印一下
Displayboard(board, ROW, COL);
// 再判断一下输赢
ret = is_win(board, ROW, COL);
if (ret != 'C') // 等于'C'继续,不等于'C'就是有胜利的一方或者平局
{
break;
}
// 电脑下棋
Computer_move(board, ROW, COL);
// 下完棋打印一下
Displayboard(board, ROW, COL);
// 再判断一下输赢
ret = is_win(board, ROW, COL);
if (ret != 'C') // 等于'C'继续,不等于'C'就是有胜利的一方或者平局
{
break;
}
}
// break结束跳到这里
}
再来判断输赢或者平局
if (ret == '*')
{
printf("玩家赢\n");
}
else if (ret == '#')
{
printf("电脑赢\n");
}
else
{
printf("平局\n");
}
最后一步,实现判断输赢函内容的细节
三子棋输赢的判断就是三个棋子在行,列,对角线是否三子相连
1: 用来判断行是否相等
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][2];
}
}
灵魂画手上线
如果玩家赢,返回的就是玩家的棋子 * ,如果电脑赢,返回的就是电脑的棋子 #
2: 用来判断列是否相等
int i = 0;
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
{
return board[2][i];
}
}
一定要加上不等于 ' ' 因为要把空格排除在外
3: 用来判断两条对角线是否相等
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];
}
4: 判断平局的情况
在game.c的文件中实现一个判断平局的函数,不需要在.h文件中实现,因为这个函数并不是全局的,仅仅用于判断平局
加上 static 只能在本文件下使用
static int is_full(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++)
{
if (board[i][j] == ' ')
{
return 0; // 棋盘还没被下满
}
}
}
return 1; // 棋盘被下满了
}
放到 is_win函数内部来使用
// 用来判断平局
if (is_full(board, row, col) == 1)
{
return 'Q';
}
// 游戏继续
return 'C';
最后: 整体代码不会放出来的,希望大家能一步步的看,一步步的实现,多的不说了,加油!