三子棋和扫雷这种c语言的简易小游戏可以帮助我们接触到分模块写代码的思想并养成良好习惯
首先打印游戏的菜单
#include"game.h"
void menu() {
printf("***************************\n");
printf("***********1.play************\n");
printf("***********2.exit**********\n");
printf("***************************\n");
}
int main() {
do {
menu();
printf("请选择:>\n");
int input=0;
srand((unsigned int)time(NULL));//生成随机数
scanf_s("%d", &input);
switch (input) {
case 1:
printf("开始扫雷游戏\n");
game();
break;
case 2:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
}
} while (1);
}

很多c语言游戏的菜单都是这样
另建一个头文件game.h专门引用和宏定义
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10
然后是游戏的函数
为了实现扫雷我们需要
初始化棋盘,和三子棋不同,扫雷需要两个一模一样的棋盘,一个用来埋藏雷,一个用来显示雷的个数
把棋盘展示出来
随机埋藏雷
寻找雷
void game() {
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };//两个相同行列的数组
Initqipan(mine, ROWS, COLS,'0');//埋藏雷
Initqipan(show, ROWS, COLS,'*');//显示雷的个数
Displayqipan(show, ROW, COL//展示棋盘
Setmine(mine,ROW,COL);//埋藏雷
Findmine(mine,show, ROW, COL);//找雷
}
设置两个相同行列的数组是为了后续只需同一个函数就可以完成初始化和展示的功能
void Initqipan(char arr[ROWS][COLS], int rows, int cols,char set) {
int i = 0;
for (i=0; i < rows; i++) {
int j = 0;
for (; j < cols; j++) {
arr[i][j] = set;
}
}
}//初始化棋盘
void Displayqipan(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("*********扫雷*********\n");
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|", arr[i][j]);
}
printf("\n");
}
}//展示棋盘
接着需要用随机数埋藏雷
void Setmine(char mine[ROWS][COLS], int row, int col) {
int count = EASY_COUNT;
while (count) {
int x = rand() % row+ 1;//将生成随机数的范围限定在1~9
int y = rand() % col + 1;
if (mine[x][y] == '0'){
mine[x][y] = '1';
count--;
}
}
}
下一步是扫雷
我们需要通过数字显示周围九个格子的雷的个数
设置11*11而不是9*9的二维数组就是为了能用一个函数实现边缘和中心格子的数字显示而不会溢出
计算以一个格子为中心的九个格子内的雷的数量
int Getminecount(char mine[ROWS][COLS], int x, int y) {
int count=mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0'; return count;
}
扫雷
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) {
int x = 0;
int y = 0;
int win = 0;
char ch = 0;
while (win<row*col-EASY_COUNT) {
printf("请输入要排查的坐标:>\n");
scanf_s("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) {
if (mine[x][y] == '1') {
printf("很遗憾,你被炸死了\n");
Displayqipan(mine, ROW, COL);
break;
}
else
{
Explode_spread(mine, show, row, col, x, y);//炸金花式展开
system("cls");
Displayqipan(show, row, col);
printf("需要标注地雷就输入:Y,不需要标注地雷则输入:N\n");
while ((ch = getchar()) != '\n'){//清空一下缓冲区
scanf("%c", &ch);
switch (ch){
case 'Y':
sign_mine(show, row, col);//标记函数
break;
default:
break;
}
}
}
else {
printf("排查的坐标非法,请重新输入\n");
}
}
Show_all_mine(mine, show, row, col);//把所有mine中地雷全部显示到show上
system("cls");//打印棋盘
Displayqipan(show, row, col);
if (win >= row * col - EASY_COUNT)
{
printf("恭喜你排查出所有的地雷!!!\n");
}
else
{
printf("扫雷失败,你被炸死了!!!\n");
}
}
炸金花式展开是指当排查某个周围没有雷的坐标时,该坐标会向周围一圈展开,然后展开的这些坐标会继续再向外围展开,继而把所有周围没有地雷的坐标统统排查出来
应该用递归的思想实现这个函数
而会被炸金花式展开的坐标应该符合以下三个条件
该坐标不是雷
该坐标周围没有雷
该坐标没有被排查过(如果不加入第三条条件会陷入死递归)
void Explode_spread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
int count = Getminecount(mine, x, y);
if (count == 0)
{
show[x][y] = ' ';
int i = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
if (show[i][j] == '*')
{
Explode_spread(mine, show, row, col, i, j);//递归
}
}
}
}
else
{
show[x][y] = count + '0';//若四周存在地雷则应该在这个位置上标注上地雷的个数
}
}
}
标记
void Signmine(char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入要标记的坐标:");
scanf_s("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == '*')
{
show[x][y] = '!';
break;
}
else
{
printf("该位置不能被标记,请重新输入:\n");
}
}
else
{
printf("输入坐标非法,请重新输入:\n");
}
}
system("cls");
Displayqipan(show, row, col);
}
这样就结束啦