提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
数组是⼀组相同类型元素的集合,数组分为⼀维数组和多维数组,多维数组⼀般⽐较多⻅的是⼆维数组。
数学中我们其实就⻅过函数的概念,⽐如:⼀次函数 y=kx+b ,k和b都是常数,给⼀个任意的x,就 得到⼀个y值。 其实在C语⾔也引⼊函数(function)的概念,有些翻译为:⼦程序,⼦程序这种翻译更加准确⼀些。 C语⾔中的函数就是⼀个完成某项特定的任务的⼀⼩段代码。这段代码是有特殊的写法和调⽤⽅法的。 C语⾔的程序其实是由⽆数个⼩的函数组合⽽成的,也可以说:⼀个⼤的计算任务可以分解成若⼲个较 ⼩的函数(对应较⼩的任务)完成。同时⼀个函数如果能完成某项特定任务的话,这个函数也是可以 复⽤的,提升了开发软件的效率。
在学习这些基础知识后,我们就可以进行简单的应用,如扫雷游戏的开发。
一、思路整理
1.模块化
学习了函数的知识后,我们可以认识到将一个程序划分为一个个小程序即函数,模块化设计有加快游戏设计进程、合理安排工作计划、减少debug工作量等作用。
2.划分模块
对扫雷而言,我们可以简单的将其分为初始化棋盘,布雷,用户输入,检测输入,判断输赢等部分。
二、代码实现
1.模块化
要将程序模块化,不可避免的要创建新的头文件,以及多个.c文件。
如图,.h文件用于储存函数声明以及为了便于操作与后续修改所定义的类对象宏与类函数宏。
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#define easy_mine 10//雷个数
#define rows 9//行
#define cols 9//列
#define ROWS rows+2
#define COLS cols+2
void menu();
void game();
void InitBoard_data(int, int, char ar[ROWS][COLS], char);
void InitBoard_data(int, int, char ar[ROWS][COLS], char);
void DisplayBoard(char[ROWS][COLS]);
char Compute(char ar1[ROWS][COLS], char ar2[ROWS][COLS], int, int);
int FindMine(char ar1[ROWS][COLS],char ar2[ROWS][COLS]);
int IsWin(char ar[ROWS][COLS], int, int, int);
而另一个.c文件中则存储着函数原型。
#include"扫雷.h"
void menu()//菜单
{
printf("*******************\n");
printf("*** 0. play ***\n");
printf("*** 1. exit ***\n");
printf("*******************\n");
}
void InitBoard_data(int a,int b,char ar[ROWS][COLS], char ch)//初始化棋盘————二维数组————储存数据————雷位置等
{
int i, j;
for (i = 0;i < a;i++)
{
for (j = 0;j < b;j++)
{
ar[i][j] = ch;
}
}
}
void InitBoard_show(int a, int b, char ar[ROWS][COLS], char ch)//初始化棋盘————二维数组————用于展示
{
int i, j;
for (i = 0;i < a;i++)
{
for (j = 0;j < b;j++)
{
ar[i][j] = ch;
}
}
}
void DisplayBoard(char ar[ROWS][COLS])
{
printf("************扫雷************\n");
for (int i = 0;i <= cols;i++)
printf("%d ", i);
printf("\n");
for (int i = 1;i <= rows;i++)
{
printf("%d ", i);
for (int j = 1;j <= cols;j++)
{
//if (ar[i][j] >= '\0' && ar[i][j] <= '0' && ar[i][j] != '*')
// ar[i][j] = '0';
printf("%c ", ar[i][j]);
}
printf("\n");
}
printf("************扫雷************\n");
}
void SetMine(char ar[ROWS][COLS],int count,int a,int b)//随机设置雷的坐标
{
int x, y;
while(count)
{
x = rand() % a + 1, y = rand() % b + 1;
if (ar[x][y] == '0')
{
ar[x][y] = '1';
count--;
}
}
}
char Compute(char ar1[ROWS][COLS],char ar2[ROWS][COLS], int x, int y)//计算坐标点周围雷的个数
{
char ch = ar1[x - 1][y - 1] + ar1[x][y - 1] + ar1[x - 1][y] + ar1[x + 1][y - 1] + ar1[x - 1][y + 1] + ar1[x + 1][y + 1] + ar1[x][y + 1] + ar1[x + 1][y] - 8 * '0' + '0';
if (ar2[x][y] == '*')
return ch;
else
return '*';
}
void All_Compute(char ar1[ROWS][COLS], char ar2[ROWS][COLS], int x, int y)//如果坐标位置附近无雷,则检索相邻位置是否有雷
{
if (x - 1 >= 1 && x - 1 <= 9 && y - 1 >= 1 && y - 1 <= 9 && Compute(ar1, ar2, x - 1, y - 1) != '*')
if((ar2[x - 1][y - 1] = Compute(ar1, ar2, x - 1, y - 1)) == '0')
All_Compute(ar1, ar2, x - 1, y - 1);
if (x - 1 >= 1 && x - 1 <= 9 && y >= 1 && y <= 9 && Compute(ar1, ar2, x - 1, y) != '*')
if((ar2[x - 1][y] = Compute(ar1, ar2, x - 1, y)) == '0')
All_Compute(ar1, ar2, x - 1, y);
if (x - 1 >= 1 && x - 1 <= 9 && y + 1 >= 1 && y + 1 <= 9 && Compute(ar1, ar2, x - 1, y + 1) != '*')
if((ar2[x - 1][y + 1] = Compute(ar1, ar2, x - 1, y + 1)) == '0')
All_Compute(ar1, ar2, x - 1, y + 1);
if (x >= 1 && x <= 9 && y - 1 >= 1 && y - 1 <= 9 && Compute(ar1, ar2, x, y - 1) != '*')
if((ar2[x][y - 1] = Compute(ar1, ar2, x, y - 1)) == '0')
All_Compute(ar1, ar2, x, y - 1);
if (x >= 1 && x <= 9 && y + 1 >= 1 && y + 1 <= 9 && Compute(ar1, ar2, x, y + 1) != '*')
if((ar2[x][y + 1] = Compute(ar1, ar2, x, y + 1)) == '0')
All_Compute(ar1, ar2, x, y + 1);
if (x + 1 >= 1 && x + 1 <= 9 && y - 1 >= 1 && y - 1 <= 9 && Compute(ar1, ar2, x + 1, y - 1) != '*')
if((ar2[x + 1][y - 1] = Compute(ar1, ar2, x + 1, y - 1)) == '0')
All_Compute(ar1, ar2, x + 1, y - 1);
if (x + 1 >= 1 && x + 1 <= 9 && y >= 1 && y <= 9 && Compute(ar1, ar2, x + 1, y) != '*')
if((ar2[x + 1][y] = Compute(ar1, ar2, x + 1, y)) == '0')
All_Compute(ar1, ar2, x + 1, y);
if (x + 1 >= 1 && x + 1 <= 9 && y + 1 >= 1 && y + 1 <= 9 && Compute(ar1, ar2, x + 1, y + 1) != '*')
if((ar2[x + 1][y + 1] = Compute(ar1, ar2, x + 1, y + 1)) == '0')
All_Compute(ar1, ar2, x + 1, y + 1);
}
int FindMine(char ar1[ROWS][COLS], char ar2[ROWS][COLS])//寻找雷的坐标
{
int x, y;
printf("请输入你要查找的坐标:");
scanf("%d%d", &x, &y);
if (ar1[x][y] == '1')
{
printf("你失败了");
return 0;
}
else if (Compute(ar1, ar2, x, y) == '*')
{
printf("该坐标已被查明\n");
return 1;
}
else if ((ar2[x][y] = Compute(ar1, ar2, x, y)) != '*')
{
printf("这里不是雷\n");
if (ar2[x][y] == '0')
All_Compute(ar1, ar2, x, y);
return 1;
}
else if (x < 1 || x>9 || y < 1 || y>9)
{
printf("该坐标非法\n");
return 1;
}
}
int IsWin(char ar[ROWS][COLS],int a,int b,int c)
{
int count=0;
for (int i = 1;i <= b;i++)
{
for (int j = 1;j <= c;j++)
{
if (ar[i][j] != '*')
count++;
}
}
if (count == b * c - a)
{
printf("你赢了");
return 0;
}
else
return 1;
}
void game()//游戏主体
{
char data[ROWS][COLS];
char show[ROWS][COLS];
InitBoard_data(ROWS, COLS, data, '0');
InitBoard_show(ROWS, COLS, show, '*');
DisplayBoard(show);
SetMine(data, easy_mine, rows, cols);
while (FindMine(data,show))
{
DisplayBoard(show);
if (!IsWin(show, easy_mine, rows, cols))
break;
}
}
程序的主体
#include"扫雷.h"
int main()
{
int choose;
srand((unsigned int)time(NULL));
menu();
printf("请选择:");
do
{
scanf("%d", &choose);
switch (choose)
{
case 0:
game();
break;
case 1:
printf("感谢游玩");
return 0;
default:
printf("输入错误,请重新输入:");
break;
}
} while (choose);
return 0;
}
2.代码解析
1).初始化
void InitBoard_data(int a,int b,char ar[ROWS][COLS], char ch)//初始化棋盘————二维数组————储存数据————雷位置等
{
int i, j;
for (i = 0;i < a;i++)
{
for (j = 0;j < b;j++)
{
ar[i][j] = ch;
}
}
}
void InitBoard_show(int a, int b, char ar[ROWS][COLS], char ch)//初始化棋盘————二维数组————用于展示
{
int i, j;
for (i = 0;i < a;i++)
{
for (j = 0;j < b;j++)
{
ar[i][j] = ch;
}
}
}
初始化分为两个部分,第一个用于存储用于后续判断的数据,第二个用于展示。第一个初始化为字符0,第二个初始化为*。
2).打印棋盘
void DisplayBoard(char ar[ROWS][COLS])
{
printf("************扫雷************\n");
for (int i = 0;i <= cols;i++)
printf("%d ", i);
printf("\n");
for (int i = 1;i <= rows;i++)
{
printf("%d ", i);
for (int j = 1;j <= cols;j++)
{
printf("%c ", ar[i][j]);
}
printf("\n");
}
printf("************扫雷************\n");
}
3).布雷
void SetMine(char ar[ROWS][COLS],int count,int a,int b)//随机设置雷的坐标
{
int x, y;
while(count)
{
x = rand() % a + 1, y = rand() % b + 1;
if (ar[x][y] == '0')
{
ar[x][y] = '1';
count--;
}
}
}
通过rand与时间戳(srand((unsigned int)time(NULL)))的组合实现随机布雷
4).输入
int FindMine(char ar1[ROWS][COLS], char ar2[ROWS][COLS])//寻找雷的坐标
{
int x, y;
printf("请输入你要查找的坐标:");
scanf("%d%d", &x, &y);
if (ar1[x][y] == '1')
{
printf("你失败了");
return 0;
}
else if (Compute(ar1, ar2, x, y) == '*')
{
printf("该坐标已被查明\n");
return 1;
}
else if ((ar2[x][y] = Compute(ar1, ar2, x, y)) != '*')
{
printf("这里不是雷\n");
if (ar2[x][y] == '0')
All_Compute(ar1, ar2, x, y);
return 1;
}
else if (x < 1 || x>9 || y < 1 || y>9)
{
printf("该坐标非法\n");
return 1;
}
}
5).检验输入
char Compute(char ar1[ROWS][COLS],char ar2[ROWS][COLS], int x, int y)//计算坐标点周围雷的个数
{
char ch = ar1[x - 1][y - 1] + ar1[x][y - 1] + ar1[x - 1][y] + ar1[x + 1][y - 1] + ar1[x - 1][y + 1] + ar1[x + 1][y + 1] + ar1[x][y + 1] + ar1[x + 1][y] - 8 * '0' + '0';
if (ar2[x][y] == '*')
return ch;
else
return '*';
}
void All_Compute(char ar1[ROWS][COLS], char ar2[ROWS][COLS], int x, int y)//如果坐标位置附近无雷,则检索相邻位置是否有雷
{
if (x - 1 >= 1 && x - 1 <= 9 && y - 1 >= 1 && y - 1 <= 9 && Compute(ar1, ar2, x - 1, y - 1) != '*')
if((ar2[x - 1][y - 1] = Compute(ar1, ar2, x - 1, y - 1)) == '0')
All_Compute(ar1, ar2, x - 1, y - 1);
if (x - 1 >= 1 && x - 1 <= 9 && y >= 1 && y <= 9 && Compute(ar1, ar2, x - 1, y) != '*')
if((ar2[x - 1][y] = Compute(ar1, ar2, x - 1, y)) == '0')
All_Compute(ar1, ar2, x - 1, y);
if (x - 1 >= 1 && x - 1 <= 9 && y + 1 >= 1 && y + 1 <= 9 && Compute(ar1, ar2, x - 1, y + 1) != '*')
if((ar2[x - 1][y + 1] = Compute(ar1, ar2, x - 1, y + 1)) == '0')
All_Compute(ar1, ar2, x - 1, y + 1);
if (x >= 1 && x <= 9 && y - 1 >= 1 && y - 1 <= 9 && Compute(ar1, ar2, x, y - 1) != '*')
if((ar2[x][y - 1] = Compute(ar1, ar2, x, y - 1)) == '0')
All_Compute(ar1, ar2, x, y - 1);
if (x >= 1 && x <= 9 && y + 1 >= 1 && y + 1 <= 9 && Compute(ar1, ar2, x, y + 1) != '*')
if((ar2[x][y + 1] = Compute(ar1, ar2, x, y + 1)) == '0')
All_Compute(ar1, ar2, x, y + 1);
if (x + 1 >= 1 && x + 1 <= 9 && y - 1 >= 1 && y - 1 <= 9 && Compute(ar1, ar2, x + 1, y - 1) != '*')
if((ar2[x + 1][y - 1] = Compute(ar1, ar2, x + 1, y - 1)) == '0')
All_Compute(ar1, ar2, x + 1, y - 1);
if (x + 1 >= 1 && x + 1 <= 9 && y >= 1 && y <= 9 && Compute(ar1, ar2, x + 1, y) != '*')
if((ar2[x + 1][y] = Compute(ar1, ar2, x + 1, y)) == '0')
All_Compute(ar1, ar2, x + 1, y);
if (x + 1 >= 1 && x + 1 <= 9 && y + 1 >= 1 && y + 1 <= 9 && Compute(ar1, ar2, x + 1, y + 1) != '*')
if((ar2[x + 1][y + 1] = Compute(ar1, ar2, x + 1, y + 1)) == '0')
All_Compute(ar1, ar2, x + 1, y + 1);
}
前者通过asc码值与字符的关系实现检验输入坐标是否为雷,后者通过递归实现当输入坐标周围无雷时,检验周围的坐标,八个分支分别代表八个方向(想不到更好的方法QWQ)。
6).判断输赢
int IsWin(char ar[ROWS][COLS],int a,int b,int c)
{
int count=0;
for (int i = 1;i <= b;i++)
{
for (int j = 1;j <= c;j++)
{
if (ar[i][j] != '*')
count++;
}
}
if (count == b * c - a)
{
printf("你赢了");
return 0;
}
else
return 1;
}
通过未检验坐标与雷数量的关系来判断。
7).游戏主体
void game()//游戏主体
{
char data[ROWS][COLS];
char show[ROWS][COLS];
InitBoard_data(ROWS, COLS, data, '0');
InitBoard_show(ROWS, COLS, show, '*');
DisplayBoard(show);
SetMine(data, easy_mine, rows, cols);
while (FindMine(data,show))
{
DisplayBoard(show);
if (!IsWin(show, easy_mine, rows, cols))
break;
}
}
总结
上述扫雷只是简单的实现,进阶还可以更改用户输入方法,如用方向键进行选择坐标,还可以更改难度(即自定义棋盘大小和雷的个数),以及标记功能,也能游戏界面进行优化。
大家看完之后可以自己尝试完善代码,欢迎大家分享!