✨博客主页:心辛向荣
✨系列专栏:【从0到1,C语言学习】
✨一句短话:你若盛开,蝴蝶自来!
✨博客说明:尽己所能,把每一篇博客写好,帮助自己熟悉所学知识,也希望自己的这些内容可以帮助到一些在学习路上的伙伴,文章中如果发现错误及不足之处,还望在评论区留言,我们一起交流进步!
在这里总结用C语言实现三子棋和扫雷俩个小游戏,实现这俩个游戏的思路其实是差不多的,都是对二维数组进行操作 ,把功能通过分支和循环等实现;只要能想到,就可以通过顺序,选择,循环这三种结构结构实现!
一.三子棋
——1.实现思路
——2.game.h
——3.main.c
——4.game.c
——5.实现效果,待改进,不足之处
二.扫雷
——1.实现思路
——2.game.h
——3.main.c
——4.game.c
——5.实现效果,待改进,不足之处
(main.c)是测主函数测试部分
(game.c)是游戏功能具体实现部分
(game.h)是函数声明部分
然后我们这里实现打印上面这样一个三子棋盘,有人想为啥不在 上下左右加上 —— 呢,其实是可以实现的,不过实现打印图中的棋盘可以锻炼编程的逻辑,下面分析一下:
这里可以通过for循环打印棋盘的行和列,棋盘的每一行可以拆分为数据部分和分割线部分 ,通过if语句限制条件使分割部分比数据部分少打印一次;而每一行数据部分的拆成每一列循环打印,每一列的格式是空格 %c 空格 | ,同样的做法把每一行最后一个 | 过滤掉,打印分割线(- - -| )也是一样的。
棋盘打印好后,实现下棋部分,每次玩家/电脑下过棋后,都要进行判断输赢的过程。
#pragma once
#include
#include
#include
//可自由更改棋盘大小
#define ROW 3
#define COL 3
//初始化棋盘
void initboard(char board[ROW][COL],int row,int col);
//显示棋盘
void displayboard(char board[ROW][COL], int row, int col);
//玩家下棋
void palyermove(char board[ROW][COL], int row, int col);
//电脑下棋
void computermove(char board[ROW][COL], int row, int col);
//判断输赢
//玩家赢返回'*'
//电脑赢返回'#'
//平局返回'q'
//继续返回'c'
char iswin(char board[ROW][COL], int row, int col);
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("*********************\n");
printf("***** 1.开始游戏*****\n");
printf("***** 0.退出游戏*****\n");
printf("*********************\n");
}
void game()
{
char ret = '0';
char board[ROW][COL] = { 0 };
//初始化棋盘
initboard(board,ROW,COL);
//显示棋盘
displayboard(board, ROW, COL);
while (1)
{
//玩家下棋
palyermove(board, ROW, COL);
//判断输赢
ret = iswin(board, ROW, COL);
if (ret != 'c')//当返回'*'/'#'时跳出循环判断谁赢了
{
break;
}
displayboard(board, ROW, COL);
//电脑下棋
computermove(board, ROW, COL);
ret = iswin(board, ROW, COL);
if (ret != 'c')
{
break;
}
displayboard(board, ROW, COL);
}
if (ret == '*')
{
printf("玩家胜出\n");
}
if (ret == '#')
{
printf("电脑胜出\n");
}
if (ret == 'q')
{
printf("你和电脑旗鼓相当\n");
}
}
int main()
{
srand((unsigned)time(NULL));
int input = 0;
do
{
//打印一个游戏菜单
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//满了返回1
//不满返回0
int isfull(char board[ROW][COL], int row, int col)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
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] = ' ';
}
}
}
void displayboard(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]);不推荐,把棋盘规格写死了
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)//过滤掉最后一行分割线
{ //打印分割线
//printf("---|---|---\n");
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)//过滤掉最后一个竖杠
printf("|");
}
printf("\n");
}
}
}
void palyermove(char board[ROW][COL], int row, int col)
{
while(1)
{
int x = 0;
int y = 0;
printf("玩家下棋\n");
printf("请输入坐标:>");
scanf("%d%d", &x, &y);
if (x > 0 && x <= 3 && y > 0 && y <= 3)
{
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("坐标被占用,请重新选择\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
void computermove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋\n");
while (1)
{
int x = rand() % 3;
int y = rand() % 3;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
char iswin(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];
}
}
//列相同
int j = 0;
for (j = 0; j < col; j++)
{
if (board[0][j] == board[1][j]&&board[1][j] == board[2][j] && board[1][j] != ' ')
{
return board[1][j];
}
}
//对角线相同
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];
}
//棋盘满了,在上面几个判断完后
//到这里说明平局了
if (isfull(board, row, col))
{
return 'q';
}
//上面都不满足游戏继续
return 'c';
}
1.电脑下棋是生成随机数随机落子,也就是它不会根据玩家的棋子位置进行堵棋,电脑赢不了玩家。。。
2.判断输赢部分写死了,只适合三子棋盘上进行三子棋的判断输赢,后面有时间我再研究改进更新!
(main.c)是测主函数测试部分
(game.c)是游戏功能具体实现部分
(game.h)是函数声明部分
这是一个扫雷的棋盘,同样的这样的棋盘实质上是一个数组,比如我们扫雷的棋盘是99的的大小,如果我们要排雷的位置位置不是雷的话,这个位置就显示周围一圈雷的个数,而如果将数组大小设置为9行9列的话,而排雷位置在边界,在计算周围雷的个数时就会出现数组越界的情况,所以为了方便这里我们将数组扩大一圈数组大小设置为11行11列,而布雷和排雷只在中间99的范围内进行!
我们定义俩个不同的数组,一个是布雷的数组,一个是玩家排雷的数组,排雷过程中会计算布雷数组中的信息反馈到排雷数组中显示,屏幕上只显示排雷数组中的内容。
定义好俩个数组后,对俩个数组中的内容进行初始化,未布雷的棋盘中初始化为字符’0’,而未排雷的棋盘中初始为字符’#'。
然后用rand函数随机布置雷(字符’1’),雷的个数不必写死,利用宏定义的标识符常量可以做到灵活设置雷的个数。
最后一部分实现排雷过程,排雷位置是雷则被炸死,游戏失败,不是雷则通过计算布置雷数组中位置周围雷的个数显示到排雷中;
这里介绍如何计算周维雷的个数:布雷的数组中’0’不是雷,'1’是雷,这里的0和1是字符,1 + ‘0’ = ‘1’,0 + ‘0’= ‘0’;所以我们将排雷坐标周围的八个字符加起来然后减去8 * ‘0’得到的就是这个坐标周围雷的个数,这个结果再加上字符’0’,得到字符数字放到排雷的数组中。
还可以实现俩个功能,对位置进行标记 ‘-’ ,把没有雷的地方展开一片,这里展开一片功能的实现要用到递归,要满足3个条件:
否则就会死递归!
判断输赢我这里这样实现:棋盘大小-雷的个数-不是 ‘*’ 和 ‘-’ 等于0,就表明扫雷成功了!
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
//雷盘范围,可灵活更改
#define ROW 9
#define COL 9
//防止计算周围雷的个数时数组越界
#define ROWS ROW+2
#define COLS COL+2
//可更改雷的个数
#define EASY_COUNT 10
void initboard(char board[ROWS][COLS], int rows, int cols,char set);
void displayboard(char board[ROWS][COLS], int row, int col);
void setMine(char board[ROWS][COLS], int row, int col);
void findMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("|------1.play------|\n");
printf("|------0.exit------|\n");
}
void game()
{
//布置雷的棋盘
char mine[ROWS][COLS] = { 0 };
//玩家进行排雷的棋盘
char show[ROWS][COLS] = { 0 };
//初始化雷盘'0'
initboard(mine,ROWS,COLS,'0');
//初始化排雷棋盘'*'
initboard(show, ROWS, COLS, '*');
//布置雷
setMine(mine, ROW, COL);
//显示雷盘,只显示排雷的棋盘
/*displayboard(mine,ROW,COL);*/
displayboard(show, ROW, COL);
//实现排雷过程
findMine(mine,show, ROW, COL);
}
int main()
{
srand((unsigned)time(NULL));
int input = 0;
//首先显示一个游戏菜单
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
//实现扫雷功能
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void initboard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void displayboard(char board[ROWS][COLS], int row, int col)
{
printf("------扫雷游戏------\n");
int i = 0;
int j = 0;
for (j = 0; j <= col; j++)
{
printf("%d ", j);
}
printf("\n");
for (i = 1;i <= row;i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("------扫雷游戏------\n");
}
void setMine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1]
+ mine[x][y - 1] + mine[x][y + 1]
+ mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';
}
void sign(char show[ROWS][COLS])
{
int i = 0;
int x = 0, y = 0;
do
{
printf("标记坐标?(1:是/0:否/2:对已标记的坐标取消)>");
scanf("%d", &i);
switch (i)
{
case 0:
break;
case 1:
printf("输入标记位置坐标:>");
scanf("%d%d", &x, &y);
if (show[x][y] != '*'||show[x][y] == '-')
{
printf("这个位置再不能进行标记了\n");
}
else
{
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
show[x][y] = '-';
displayboard(show, ROW, COL);
}
else
{
printf("坐标非法,请重新输入!\n");
}
}
break;
case 2:
printf("输入要取消标记的坐标:>");
scanf("%d%d", &x, &y);
if (show[x][y] != '-')
{
printf("这个坐标未进行标记,重新输入\n");
}
else
{
show[x][y] = '*';
displayboard(show, ROW, COL);
}
break;
default:
printf("选择错误,请重新输入\n");
break;
}
} while (i);
}
void unfold1(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
int i = 0;
int j = 0;
int flag = 1;
if (show[x][y] == '*' || show[x][y] == '-' )
{
flag = 0;//没有被排查过的
}
int count = get_mine_count(mine, x, y);
if (count == 0)
{
show[x][y] = ' ';
}
else
{
show[x][y] = count + '0';
}
if (flag == 0 && count == 0)
{
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)//对其周围的8个坐标递归
{
unfold1(mine, show, x + i, y + j);
}
}
}
}
}
int is_win(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int ret = 0;
for (i = 1; i <= ROW; i++)
{
for (j = 1; j <= COL; j++)
{
if (show[i][j] != '*' && show[i][j] != '-')
ret++;
}
}
return ret;
}
void findMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (row * col - EASY_COUNT - is_win(show, row, col))
{
printf("输入要排雷的坐标:>");
scanf("%d%d", &x, &y);
if (show[x][y] != '*'&& show[x][y] != '-')
{
printf("这个位置已经排过了,不能重复进行\n");
}
else
{
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
//是雷
if (mine[x][y] == '1')
{
printf("游戏失败,你被炸死了!\n");
displayboard(mine, ROW, COL);
break;
}
else
{
int count = get_mine_count(mine, x, y);
if (count == 0)
{
unfold1(mine, show, x, y);//展开一片的功能
displayboard(show, ROW, COL);
sign(show);//标记功能
}
else
{
show[x][y] = count + '0';
displayboard(show, ROW, COL);
sign(show);//标记功能
}
}
}
else
{
printf("坐标非法,请重新输入!\n");
}
}
if (row * col - EASY_COUNT - is_win(show, row, col) == 0)
{
printf("恭喜你,扫雷成功!\n");
displayboard(mine, ROW, COL);
}
}
标记功能每一次排雷后都要进行选择,感觉步骤太过繁琐,有待改进,以后有时间研究更新!
各位小伙伴,看到这里就是缘分嘛,希望我的这些内容可以给你带来那么一丝丝帮助,可以的话三连支持一下呗(关注✌️点赞✌️评论✌️)!!!
感谢每一位走到这里的小伙伴,我们可以一起学习交流,一起进步!!!加油!!!