2024.12.14 全流程实现了一个真正意义上的简单小游戏。此时学习编程4个月。
使用语言:c语言
实现平台:Visual Studio 2022
特别说明:由于是使用VS实现,采用了scanf_s函数,其余IDE需要将全部都scanf_s函数转换为scanf即可正常运行。
起始UI
游玩界面
1. 主函数(main): - 初始化游戏,设置随机种子。 - 创建一个11x11的数组来代表游戏棋盘,并初始化。
2. 开始界面(begin_s): - 提供用户选择的菜单,包括开始游戏、退出、教程和开发者模式。 - 如果用户选择开始游戏,会让用户输入地雷的数量,并初始化棋盘,生成地雷和提示。
3. 游玩界面(play): - 显示当前游戏棋盘的状态。 - 接收用户的输入,包括操作类型(挖掘、标记地雷或标疑问符)和坐标。 - 根据用户的操作,调用`operate`函数来执行相应的游戏逻辑。
4. 失败和胜利界面: - `lose`函数显示失败时的动画和棋盘状态。 - `failed_interface`函数显示失败时的结算界面。 - `victory_interface`函数显示胜利时的结算界面。
5. 后端逻辑: - `MineGeneration`函数在棋盘上随机生成地雷。 - `Prompt`函数生成地雷周围的提示数字。 - `pure`函数将空白地块标记为隐藏状态。 - `printf_play`函数用于打印当前棋盘的状态。 - `operate`函数处理用户的游玩操作,包括挖掘地块、标记地雷和标疑问符。 - `move`函数用于在控制台中移动光标位置。 - `Victory_judgement`函数用于判断用户是否赢得了游戏。 - `pure_look`函数用于递归地揭示空白地块。
6. 测试函数(printf_arr): - 用于打印数组的状态,方便调试。
别问我为什么这张画风不一样,因为换软件了awa
ps:由于是使用VS实现,采用了scanf_s函数,其余IDE需要将全部都scanf_s函数转换为scanf即可正常运行。
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
#include<Windows.h>
int gamemode = 0;
void MineGeneration(int* arr, int Set_MineNum);
void printf_arr(int* arr);
void Prompt(int* arr);
int begin_s(int* arr, int* num_operation);
int play(int* arr);
void printf_play(int* arr);
void pure(int* arr);
int operate(int play_operate, int play_x, int play_y, int* arr);
void move(int position);
void lose(int* arr);
void failed_interface(int num_operations, int left_mines, int Set_MineNum);
int Victory_judgement(int* arr, int* left_mine);
void victory_interface(int num_operations, int Set_MineNum);
void pure_look(int* arr, int pure_x, int pure_y);
int main(void){
srand((unsigned int)time(NULL)); //随机种子
int arr[11][11];
memset(arr, 0, 11 * 11 * sizeof(int)); //数组初始化
//操作次数
int num_operations = 0;
int* num_operation = &num_operations;
//剩余雷数
int left_mines = 0;
int* left_mine = &left_mines;
int game_state = 0; //游戏状态
int Set_MineNum = begin_s(arr, num_operation); //----开始界面
if (Set_MineNum == -2) { return 0; }
do {
if (num_operations >= 300)
{
printf("超过300次操作啦,自动结束游戏哦");
}
if (Victory_judgement(arr, left_mine) == 1)
{
game_state = 1;
break;
}
num_operations++;
} while (!(game_state = play(arr)));
switch (game_state)
{
case -1:
failed_interface(num_operations, left_mines, Set_MineNum);
break;
case 1:
system("cls");
printf_play(arr);
victory_interface(num_operations, Set_MineNum);
break;
default:
printf("游戏状态错误");
printf("game_state = %d", game_state);
break;
}
return 0;
}
1.前端
///1.1 初始界面
//1.1.1初始界面生成函数
int begin_s(int* arr,int* num_operation) {
//循环保护
static int Loop_detector = 0;
begin_again:
if(Loop_detector >= 50){
system("cls");
printf("程序循环了50次,触发保护");
return;
}
Loop_detector++;
int begin_num = 0;
printf("\n################################\n");
printf("· \n");
printf("· >.欢迎来到扫雷游戏 \n");
printf("· 1.开始游戏 \n");
printf("· 2.退出游戏 \n");
printf("· 3.教程 \n");
printf("· 4.开/关 开发者模式 \n");
printf("· >.请在键盘输入对应的数字序号进行操作 \n");
printf("· \n");
printf("################################\n");
scanf_s("%d", &begin_num);
if (!(begin_num == 1 || begin_num == 2 || begin_num == 3 || begin_num == 4)) {
system("cls");
printf("出错了:好像输入了无法理解的操作呢……请输入表中给出的操作哦");
goto begin_again;
}
switch (begin_num)
{
case 1:
int Set_MineNum = 0;
printf("请输入生成的地雷数(1-81):");
scanf_s("%d",&Set_MineNum);
//安全性判断
if (Set_MineNum > 81 || Set_MineNum < 1)
{
system("cls");
printf("出错了:好像输入了无法理解的操作呢……请输入表中给出的操作哦");
Set_MineNum = 0;
goto begin_again;
}
memset(arr, 0, 11 * 11 * sizeof(int)); //数组初始化
//printf_arr(arr);
//printf("\n——————————————————\n");//----初始化正确性检查点;
/*Set_MineNum = 11;*/
MineGeneration(arr, Set_MineNum);
//printf_arr(arr);
//printf("\n——————————————————\n"); //----地雷生成正确性检查点
Prompt(arr);
//printf_arr(arr);
//printf("\n——————————————————\n"); //----地雷提示生成正确性检查点
pure(arr);
//printf_arr(arr);
//printf("\n——————————————————\n"); //----空白地块生成检查点
*num_operation = 0; //初始化操作次数
return Set_MineNum;
case 2: return -2;
case 3:
system("cls");
printf("欢迎游玩扫雷游戏!\n");
Sleep(2000);
printf("你接下来会看到一个9*9的棋盘\n");
Sleep(2000);
memset(arr, 0, 11 * 11 * sizeof(int)); //数组初始化
int test = 0, test1 = 0, test2 = 0, test3 = 0;
*(arr + 11*1+3) = 1;
Prompt(arr);
pure(arr);
printf_play(arr);
printf("对,就是上面这样");
printf("(输入1继续)\n");
while (test != 1) {
scanf_s("%d", &test);
}
printf("每一个白色方块下面都有可能藏着一颗地雷\n");
Sleep(3000);
printf("你的任务就是找出所有的地雷\n");
Sleep(2000);
printf("不过别担心,我们会有提示\n");
Sleep(2000);
printf("输入 0 1 1试试,数字之间用空格隔开:\n");
while (test1 != 0 || test2 != 1 || test3 != 1) {
scanf_s("%d%d%d", &test1, &test2, &test3);
}
system("cls");
operate(0, 1, 1, arr);
printf_play(arr);
printf("太棒了,你成功的掀开了一个区域,幸运的是里面没有地雷。\n");Sleep(3000);
printf("让我们解析一下你输入的内容。\n");Sleep(3000);
printf("第一个数字是操作符,操作符号有3种,“0’的作用是掀开这个区域\n");Sleep(4000);
printf("除此之外还有‘1’,用于标记地雷所在地\n");Sleep(3000);
printf("最后一个操作符号是‘2’,用于辅助你判断哪个方块之下有地雷");Sleep(1000);
printf("(输入1继续)\n");
test = 0;
while (test != 1) {
scanf_s("%d", &test);
}
printf("后面两位数字的作用你可能已经猜到了,没错,是坐标!\n");Sleep(3000);
printf("你刚刚输入了‘1’‘1’,意味着你对着点(1,1)进行了“掀开”操作\n");Sleep(4000);
printf("不过为什么你只针对(1,1)进行操作,却同时掀开那么多的点?\n");Sleep(4000);
printf("那是因为:没有地雷且没有提示的连续方块,被看作一个区域\n");Sleep(4000);
printf("这样就可以减少你手指耐久度的损耗了,不是吗(笑)");Sleep(3000);
printf("(输入1继续)\n");
test = 0;
while (test != 1) {
scanf_s("%d", &test);
}
system("cls");
printf_play(arr);
printf("好,接下来我们继续完成这盘对局\n");Sleep(3000);
printf("输入0 2 2试试\n");Sleep(3000);
while (test1 != 0 || test2 != 2 || test3 != 2) {
scanf_s("%d%d%d", &test1, &test2, &test3);
}
system("cls");
operate(0, 2, 2, arr);
printf_play(arr);
printf("如你所见,这里有一个数字‘1’\n");Sleep(4000);
printf("它代表:以它为中心,周围3*3的区域里,一共有1个地雷\n");Sleep(4000);
printf("没错,这就是“提示”\n");Sleep(4000);
printf("这一类数字包含了1-8,他们的意义同理\n");Sleep(4000);
printf("玩到这里,你应该知道地雷在哪里了吧!\n");Sleep(4000);
printf("没错,在(3,1)!\n");Sleep(4000);
printf("现在,使用操作符号‘1’,对(3,1)进行标记吧!\n");Sleep(2000);
while (test1 != 1 || test2 != 3 || test3 != 1) {
scanf_s("%d%d%d", &test1, &test2, &test3);
}
system("cls");
operate(1, 3, 1, arr);
printf_play(arr);
printf("太棒了,你找到了所有地雷,通过了这个特别简单的教学关卡\n");Sleep(5000);
printf("选择开始游戏,你可以自定义难度,挑战更高难度的关卡\n");Sleep(5000);
printf("输入1开始你的扫雷之旅吧!\n");
test = 0;
while (test != 1) {
scanf_s("%d", &test);
}
system("cls");
goto begin_again;
case 4:
if (gamemode == 0) {
gamemode = 1;
system("cls");
printf("已开启开发者模式\n");
goto begin_again;
}
else {
gamemode = 0;
system("cls");
printf("已关闭开发者模式\n");
goto begin_again;
}
}
}
///1.2 游玩界面
//1.2.1 游玩面板
int play(int* arr){
int play_state = 0;//游玩状态控制
//测试点
if (gamemode == 1) {
printf("测试台:\n");
printf_arr(arr);
printf("\n——————————————————\n");
}
else {
system("cls");
}
printf_play(arr);
printf("\n--------");
printf("操作符:0.挖掘地块\t1.地雷标记/取消标记\t2.标疑问符/去疑问符");
printf("--------\n\n");
int play_operate = 0, play_x = 0, play_y = 0;
scanf_s("%d%d%d", &play_operate, &play_x, &play_y);
play_state = operate(play_operate, play_x, play_y, arr);
if (play_state == -1) {
lose(arr);
return -1;
}
return 0;
}
///1.3 结算界面
//1.3.1 失败动画
void lose(int* arr) {
//移动光标
printf("\n");
move(26);
//列提示生成
printf(" 行\t1\t2\t3\t4\t5\t6\t7\t8\t9\t \n列\n\n");
for (int onei = 1; onei < 10; onei++)
{
//9*9棋盘生成
printf("%d\t", onei); //输出提示列坐标
for (int onej = 1; onej < 10; onej++)
{
switch (*(arr + onei * 11 + onej) / 100) {
case 1:
//旗帜生成
if (*(arr + onei * 11 + onej) % 100 == 1) {
printf("(*)\t");
}
else { printf("X\t"); }
continue;
case 2:
//问号生成
printf("?\t");
continue;
case 0:
break;
default:
system("cls");
printf("严重错误:数字错误!!!!");
break;
}
switch (*(arr + onei * 11 + onej) % 100)
{
//提示符(隐藏)生成
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
//空白块(隐藏)生成
case 17:
printf("■\t");
break;
//地雷爆炸!!
case 1:
printf("*");
Sleep(100);
printf("\b■");
Sleep(200);
printf("\b*\t");
Sleep(100);
break;
//数字提示(显示)生成
case 21:
case 22:
case 23:
case 24:
case 25:
case 26:
case 27:
case 28:
printf("%d\t", *(arr + onei * 11 + onej) - 20);
break;
//空白块(揭开)生成
case 2:
printf("\t");
break;
//错误提示生成
default:
system("cls");
printf("严重错误:数字错误!!!!");
break;
}
}
printf("\n\n"); //换行
}
return;
}
//1.3.2 失败结算界面
void failed_interface(int num_operations, int left_mines, int Set_MineNum) {
printf("很遗憾,你输了。");
printf("\t您此局一共操作了%d次, 一共%d个雷, 剩余%d个雷没找出,再接再厉!", num_operations, Set_MineNum , left_mines);
printf("\n ");
printf("\n ");
printf("\n ");
printf("\n ");
}
//1.3.3 胜利结算
void victory_interface(int num_operations, int Set_MineNum) {
printf("恭喜你,你赢了!");
printf("\t您此局一共操作了%d次, 找出了全部一共%d个雷!", num_operations, Set_MineNum);
printf("\n ");
printf("\n ");
printf("\n ");
printf("\n ");
}
2.后端
///2.1数组模块
//2.1.1地雷生成函数
void MineGeneration(int* arr, int Set_MineNum) {
int MineNum = 0, Arr_i = 1, Arr_j = 0, ins_mine = 0;
while (MineNum < Set_MineNum) {
ins_mine = (rand() % 10 + 1);
if (ins_mine <= 8)
{
Arr_j = (rand() % 9) + 1;
//重复性检查1
if (*(arr+Arr_i*11+Arr_j)) {
Arr_j = (Arr_j + 1) % 9 + 1;
for (int ii = 1; ii <= 9; ii++)
{
if (*(arr + Arr_i * 11 + Arr_j)) {
Arr_j = (Arr_j + 1) % 9 + 1;
}
else {
*(arr + Arr_i * 11 + Arr_j) = 1;
MineNum++;
break;
}
}
Arr_i = (Arr_i + 1) % 9 + 1;
}
else {
*(arr + Arr_i * 11 + Arr_j) = 1;
MineNum++;
Arr_i = (Arr_i + 1) % 9 + 1;
}
}
}
return;
}
//2.1.2地雷提示字生成
void Prompt(int* arr) {
//定义局部变量
static int mine_num = 0;
//遍历1-9行
for (int onei = 1; onei < 10; onei++)
{
//遍历1-9列
for (int onej = 1; onej < 10; onej++)
{
//判定是否含雷
if (!*(arr + onei * 11 + onej)) {
//初始化雷数
mine_num = 0;
///进入雷数收集
//遍历列
for (int twoi = -1; twoi < 2; twoi++)
{
//遍历行
for (int twoj = -1; twoj < 2; twoj++)
{
//判定是否有雷
if (*(arr+11*(onei+twoi)+(onej+twoj)) == 1)
{
//雷数+1
mine_num++;
}
}
}
//赋值阶段
if (mine_num != 0)
{
*(arr + onei * 11 + onej) = 5 + mine_num;
}
}
}
}
}
//2.1.3 空白地块生成
void pure(int* arr) {
for (int onei = 1; onei < 10; onei++)
{
for (int onej = 1; onej < 10; onej++)
{
if ((*(arr + onei * 11 + onej)) == 0)
{
*(arr + onei * 11 + onej) = 17;
}
}
}
}
///2.2用户游玩模块
//2.2.1 游玩输出
void printf_play(int* arr) {
//列提示生成
printf(" 行\t1\t2\t3\t4\t5\t6\t7\t8\t9\t \n列\n\n");
for (int onei = 1; onei < 10; onei++)
{
//9*9棋盘生成
printf("%d\t", onei); //输出提示列坐标
for (int onej = 1; onej < 10; onej++)
{
switch (*(arr + onei * 11 + onej) / 100) {
case 1:
//旗帜生成
printf("X\t");
continue;
case 2:
//问号生成
printf("?\t");
continue;
case 0:
break;
default:
system("cls");
printf("严重错误:数字错误!!!!");
break;
}
switch (*(arr + onei * 11 + onej) % 100)
{
//提示符(隐藏)生成
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
//空白块(隐藏)生成
case 17:
//地雷隐藏块生成
case 1:
printf("■\t");
break;
//数字提示(显示)生成
case 21:
case 22:
case 23:
case 24:
case 25:
case 26:
case 27:
case 28:
printf("%d\t", *(arr + onei * 11 + onej) - 20);
break;
//空白块(揭开)生成
case 2:
printf("\t");
break;
//错误提示生成
default:
system("cls");
printf("严重错误:数字错误!!!!");
break;
}
}
printf("\n\n"); //换行
}
}
//2.2.2 游玩操作
int operate(int play_operate, int play_x, int play_y, int* arr) {
//操作符:0.挖掘地块 1.插旗/拔旗 2.标疑问符/去疑问符
switch (play_operate) {
case 0:
switch (*(arr + play_y * 11 + play_x) % 100) {
//含地雷方块掀开
case 3:
case 4:
case 1:
//接结束函数:end();
return -1;
//地雷提示数字掀开
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
*(arr + play_y * 11 + play_x) += 15;
break;
//空白块
case 17:
pure_look(arr, play_x, play_y);
}
break;
case 1:
//插旗
if (*(arr + play_y * 11 + play_x) >= 100 && *(arr + play_y * 11 + play_x) < 200) {
*(arr + play_y * 11 + play_x) -= 100;
}
//拔旗
else {
*(arr + play_y * 11 + play_x) += 100;
}
break;
case 2:
//标问号
if (*(arr + play_y * 11 + play_x) >= 200 && *(arr + play_y * 11 + play_x) < 300) {
*(arr + play_y * 11 + play_x) -= 200;
}
//去问号
else {
*(arr + play_y * 11 + play_x) += 200;
}
break;
default:
system("cls");
printf("出错了:操作符错误!");
printf("play_operate = %d", play_operate);
break;
}
return 0;
}
//2.2.3 基于windowsAPI的光标移动
void move(int position) {
// 获取当前光标位置
CONSOLE_SCREEN_BUFFER_INFO csbi;
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hConsole, &csbi);
// 计算上一行的位置
COORD newPos = { csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y - position };
// 移动光标到上一行
SetConsoleCursorPosition(hConsole, newPos);
}
//2.2.4 胜利判定
int Victory_judgement(int* arr, int* left_mine) {
*left_mine = 0;
for (int onei = 1; onei < 10 ; onei++)
{
for (int onej = 1; onej < 10; onej++)
{
if (((*(arr + onei * 11 + onej)) % 100 == 1) && !((*(arr + onei * 11 + onej)) / 100 == 1)) {
(*left_mine)++;
}
}
}
if(*left_mine == 0){return 1;}
return;
}
//2.2.5 空白块显示函数
void pure_look(int* arr,int pure_x,int pure_y){
for (int onei = -1; onei <= 1; onei++)
{
for (int onej = -1; onej <= 1; onej++)
{
if ((*((arr + pure_y * 11 + pure_x) + onej * 11 + onei) % 100) == 17) {
*((arr + pure_y * 11 + pure_x) + onej * 11 + onei) = 2;
pure_look(arr, pure_x + onei, pure_y + onej);
}
}
}
}
3.测试
void printf_arr(int* arr) {
for (int i = 0; i < 11; i++)
{
for (int j = 0; j < 11; j++)
{
if (*(arr + i * 11 + j) <= 13 && *(arr + i * 11 + j) >= 6) {
printf("N:%d\t", *(arr + i * 11 + j) - 5);
continue;
}
if (*(arr + i * 11 + j) <= 28 && *(arr + i * 11 + j) >= 21) {
printf("N:%d\t", *(arr + i * 11 + j) - 20);
continue;
}
else{ printf("%d\t", *(arr + i * 11 + j));continue;
}
}
printf("\n");
}
}