C語言實(shí)現(xiàn)掃雷游戲詳細(xì)流程
前言
嘿!是不是寫掃雷小游戲的時(shí)候發(fā)現(xiàn)一個(gè)個(gè)輸入太慢了?是不是想要展開卻發(fā)現(xiàn)陷入了死遞歸?讓小黃教教你怎么巧妙地解決這個(gè)問題吧!
其實(shí)總結(jié)起來就是一句話“可以讓計(jì)算機(jī)多判斷,但是不能讓他多算”。只要每次判斷一個(gè)格子周圍雷數(shù)的時(shí)候賦值到另一個(gè)棋盤,后續(xù)遞歸的時(shí)候就不判斷這個(gè)地方的棋盤就解決啦!
PS:采用了多文件的編寫方式哦~?
頭文件部分
“簡單“”對游戲做了一個(gè)“小小的”提升,添加了可自行選擇棋盤的大小,以及游戲時(shí)可以選擇如何排查,存疑,確定操作,這樣才能算真正的掃雷嘛。
#pragma once #include<stdio.h> #include<time.h> #include<stdlib.h> #include<windows.h> #define hang 11 #define lie 11 #define mines 10 //是否進(jìn)入游戲 void menu(); //游戲難度選擇菜單 void menu2(); //游戲操作選擇菜單 void menu3(); //初始化棋盤 void star(char arr[100][100], int g, int h); //打印棋盤 void prin(char arr[100][100],int g,int h); //生成地雷 void mkmine(char arr[100][100], int g, int h, int f); //數(shù)雷并且賦值給mine棋盤 void countmine(char arr[100][100],int g, int h); //初步排查是否炸雷 int paic(char arr1[100][100], char arr2[100][100], int x, int y, int g, int h); //進(jìn)一步實(shí)現(xiàn)非雷區(qū)展開(展開一片全是0的格子) void paic2(char arr1[100][100], char arr2[100][100], int x, int y, int g, int h); //判斷輸贏 int pand(char arr[100][100], char arr2[100][100], int g, int h,int f);
主函數(shù)部分(源文件)
其實(shí)寫完之后才發(fā)現(xiàn)hang,lie,mines沒必要提前宏替換哈哈哈哈哈,因?yàn)闉榱藢?shí)現(xiàn)棋盤大小的設(shè)定,所以傳參略略有些多。
這一部分其實(shí)就是游戲的大體框架,也就是游戲的過程,函數(shù)的定義部分我放在了另一個(gè)源文件內(nèi)
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
int main()
{
char mine[100][100], show[100][100];
int key = 0, x, y, nandu, i, j, o, v, b;
srand((unsigned int)time(NULL));
do
{
menu();
scanf("%d", &key);
if (key == 1)
{
menu2();
scanf("%d", &nandu);
switch (nandu)
{
case 1:
i = hang, j = lie, o = mines;
break;
case 2:
i = hang + 7, j = lie + 7, o = mines + 30;
break;
case 3:
i = hang + 7, j = lie + 31, o = mines + 89;
break;
case 4:
printf("請輸入行數(shù):");
scanf("%d", &i);
i += 2;
printf("請輸入列數(shù):");
scanf("%d", &j);
j += 2;
printf("請輸入雷數(shù):");
scanf("%d", &o);
break;
}
star(mine, i, j);
star(show, i, j);
mkmine(mine, i, j, o);
countmine(mine, i, j);
while (1)
{
system("cls");
prin(show, i, j);
loop:menu3();
loop1:scanf("%d", &b);
if (b == 2)
{
printf("請輸入確定的雷區(qū)-> ");
scanf("%d%d", &x, &y);
show[x][y] = '@';
}
else if (b == 3)
{
printf("請輸入存疑的雷區(qū)-> ");
scanf("%d%d", &x, &y);
show[x][y] = '?';
}
else if (b == 1)
{
printf("請輸入要排查的雷區(qū)-> ");
scanf("%d%d", &x, &y);
v = (paic(mine, show, x, y, i, j));
if (v == 1)
{
if (pand(show, mine, i, j, o))
{
printf("You win!\n");
break;
}
else;
}
else if (v == 0)
break;
else if (v == 2)
goto loop;
}
else
{
printf("worng input,again?\n");
goto loop1;
}
}
}
else if (key != 0)
{
system("cls");
printf("wrong input,again?\n\n\n");
}
} while (key);
return 0;
}函數(shù)定義(原文件)
1.菜單打印
就是幾個(gè)不同的選擇界面啦~
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void menu()
{
printf("#################################\n");
printf("###########0.結(jié)束游戲############\n");
printf("###########1.開始游戲############\n");
printf("#################################\n");
}
void menu2()
{
printf("#################################\n");
printf("######## 請選擇游戲難度 #########\n");
printf("########## 1.簡單模式 ###########\n");
printf("########## 2.普通模式 ###########\n");
printf("########## 3.困難模式 ###########\n");
printf("########## 4.自定義模式 #########\n");
printf("#################################\n");
}
void menu3()
{
printf("#################################\n");
printf("###### 請選擇您要進(jìn)行的操作 #####\n");
printf("########## 1.排查雷區(qū) ###########\n");
printf("########## 2.雷區(qū)確定 ###########\n");
printf("########## 3.雷區(qū)存疑 ###########\n");
printf("#################################\n");
}2.初始化棋盤
void star(char arr[100][100], int g, int h)
//初始化的時(shí)候比真實(shí)棋盤大一圈,方便后面的paic函數(shù),避免出現(xiàn)越界現(xiàn)象
{
int m, n;
for (m = 0; m < g; m++)
for (n = 0; n < h; n++)
arr[m][n] = ' ';
}3.打印棋盤
void prin(char arr[100][100], int g, int h)
//打印的時(shí)候只需要打印真實(shí)棋盤即可
{
int m, n;
for (m = 0; m <= h - 2; m++)
printf("%3d", m);//第一行的坐標(biāo),方便玩家找掃雷位置
printf("\n");
for (m = 1; m <= g - 2; m++)
{
printf("%3d", m);//行號
for (n = 1; n <= h - 2; n++)
{
printf("%3c", arr[m][n]);
}
printf("\n");
//%3d ,%3c都是為了美觀
}
}4.生成地雷
void mkmine(char arr[100][100], int g, int h,int f)
//種子一定要放在外面,主函數(shù)里面,如果放在函數(shù)里面生成會(huì)特別慢(暫不清楚原因)
{
int i, k=0, x, y;
for (i = 0; k < f; i++)
{
x = rand() % (g - 2) + 1;
y = rand() % (h - 2) + 1;//生成的數(shù)范圍從 1 開始
if (arr[x][y] == ' ')
{
arr[x][y] = '*';//布置雷‘*'
k++;//計(jì)數(shù)雷的個(gè)數(shù)
}
}
}5.數(shù)雷并賦值給trueboard
這里是一個(gè)優(yōu)化過程,先計(jì)算好每一個(gè)格子周圍的雷應(yīng)該是多少,賦值給trueboar,玩家排查的時(shí)候只需要從trueboard中找對應(yīng)點(diǎn)位就好了。避免了每次排查時(shí)需要計(jì)算周圍的雷數(shù)所造成時(shí)間上的浪費(fèi)。
void countmine(char arr[100][100], int g, int h)
{
int m, n, m1, n1, k;
for ( m = 1; m <= g -2; m++)
for (n = 1; n <= h - 2; n++)//只用給真實(shí)棋盤范圍賦值
{
k = 0;
if (arr[m][n] == '*')
continue;
else//如果arr[m][n]不是雷
{
for (m1 = m - 1; m1 <= m + 1; m1++)
for (n1 = n - 1; n1 <= n + 1; n1++)
if (arr[m1][n1] == '*')
k++;//統(tǒng)計(jì)九宮格內(nèi)‘*'的個(gè)數(shù)
arr[m][n] = k+48;//arr是字符數(shù)組,所以記錄字符‘1'-‘9',k+48即將數(shù)字1-9變?yōu)樽址?'-‘9'對應(yīng)的ascll碼值
}
}
}6.排查雷區(qū)
如果掃到雷,就把雷的位置給到showboard上面,讓玩家死的明明白白哈哈哈哈啊
int paic(char arr1[100][100], char arr2[100][100], int x, int y, int g, int h)
{
int m, n;
if (arr1[x][y] == '*')
{
system("cls");//清空屏幕,為了美觀
for (m = 0; m < g; m++)
for (n = 0; n < h; n++)
if(arr1[m][n]=='*')
arr2[m][n] = arr1[m][n];//將余下的雷的位置賦給show棋盤,再進(jìn)行展示
prin(arr2, g, h);
printf("You lose!\n\n\n");
return 0;//輸了之后回到主函數(shù)就重新開始,所以返回0
}
else if (x > g - 2 || x < 1 || y > h - 2 || y < 1)//錯(cuò)誤地址(超出棋盤限制)
{
printf("wrong location,again?\n");
return 2;
}
else//排查正確
{
paic2(arr1,arr2,x,y, g, h);
return 1;
}
}7.展開
只要排查點(diǎn)對應(yīng)0時(shí)才需要判斷是否展開,是則必定九宮格內(nèi)沒有雷,展開九宮格,然后對九宮格進(jìn)行一個(gè)判斷,如果存在0并且,在showboard上九宮格內(nèi)存在沒有排查的,就進(jìn)入遞歸,這樣的話,最多遞歸次數(shù)就是棋盤的長,到達(dá)邊界后就會(huì)返回上一層paic2,不會(huì)出現(xiàn)指數(shù)增長式的遞歸導(dǎo)致棧溢出
void paic2(char arr1[100][100], char arr2[100][100], int x, int y, int g, int h)
{
int m1, n1, m, n;
arr2[x][y] = arr1[x][y];//先賦值,判斷是不是0==>有沒有展開的必要
if (arr2[x][y] == '0')
{
//展開九宮格內(nèi)
for (m1 = x - 1; m1 <= x + 1; m1++)
for (n1 = y - 1; n1 <= y + 1; n1++)
{
arr2[m1][n1] = arr1[m1][n1];
}
for (m1 = x - 1; m1 <= x + 1; m1++)
for (n1 = y - 1; n1 <= y + 1; n1++)
if (arr2[m1][n1] == '0' && m1 >= 1 && n1 >= 1 && m1 <= g - 2 && n1 <= h - 2)//判斷九宮格內(nèi)是否存在0==>是否可以進(jìn)一步展開
{
for (m = m1 - 1; m <= m1 + 1; m++)
for (n = n1 - 1; n <= n1 + 1; n++)
if (arr2[m][n] == ' ' && m >= 1 && n >= 1 && m <= g - 2 && n <= h - 2)
//進(jìn)一步篩選如果該 0 九宮格內(nèi)存在空格(未排查的地方)則展開,若不存在則說明周圍已經(jīng)排查過了,沒必要展開,避免了棧溢出
paic2(arr1, arr2, m, n, g, h);//遞歸實(shí)現(xiàn)展開
}
}
}8.判斷輸贏
如果showboard上面存在沒有被排查的地方,也就是對應(yīng)字符小于0或大于9且不為‘@’,并且trueboard對應(yīng)點(diǎn)位不是雷;或者已經(jīng)排查,但是排查錯(cuò)誤,也就是對應(yīng)字符‘@’,但trueboard對應(yīng)字符不是雷,就說明沒有排查完,均會(huì)返回0,表示需要繼續(xù)
int pand(char arr1[100][100], char arr2[100][100], int g, int h,int f)
{
int m, n, count=0,count2=0;
for (m = 1; m <= g - 2; m++)
for (n = 1; n <= h - 2; n++)
if (((arr1[m][n] == ' ' || arr1[m][n] > '?') && arr2[m][n] != '*') || (arr1[m][n] == '@' && arr2[m][n] != '*'))//非雷區(qū)必須排查
return 0;
return 1;
}到此這篇關(guān)于C語言實(shí)現(xiàn)掃雷游戲詳細(xì)流程的文章就介紹到這了,更多相關(guān)C語言掃雷內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++高性能服務(wù)器框架之協(xié)程調(diào)度模塊
這篇文章主要介紹了C++高性能服務(wù)器框架中的協(xié)程調(diào)度模塊,文中通過代碼示例介紹的非常詳細(xì),對我們的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-06-06
如何用C++求兩個(gè)數(shù)的最大公約數(shù)和最小公倍數(shù)
最大公約數(shù)是指兩個(gè)或多個(gè)整數(shù)共有約數(shù)中,最大的一個(gè)約數(shù),常用的方法是歐幾里得算法,也叫輾轉(zhuǎn)相除法,下面這篇文章主要給大家介紹了關(guān)于如何用C++求兩個(gè)數(shù)的最大公約數(shù)和最小公倍數(shù)的相關(guān)資料,需要的朋友可以參考下2023-01-01
深入了解C語言結(jié)構(gòu)化的程序設(shè)計(jì)
這篇文章主要介紹了C語言編程中程序的一些基本的編寫優(yōu)化技巧,文中涉及到了基礎(chǔ)的C程序內(nèi)存方面的知識,非常推薦!需要的朋友可以參考下2021-07-07
C++實(shí)現(xiàn)基于時(shí)序公平的讀寫鎖詳解
讀寫鎖與普通的互斥鎖的區(qū)別在于有兩種上鎖方式:讀鎖和寫鎖,不用的用戶對同一個(gè)讀寫鎖獲取讀鎖是非互斥的,其他情況則是互斥的,本文小編將給大家詳細(xì)介紹C++實(shí)現(xiàn)基于時(shí)序公平的讀寫鎖,需要的朋友可以參考下2023-10-10
QT連接SQLServer數(shù)據(jù)庫的實(shí)現(xiàn)
要使用Qt連接SQL Server數(shù)據(jù)庫,需要使用Qt提供的SQL模塊和SQL Server驅(qū)動(dòng)程序,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
淺談C語言中include""與include<>的區(qū)別
C語言中包含文件有兩種包含符號,一個(gè)是<>尖括號,另一個(gè)是""雙引號。那么這兩個(gè)有什么區(qū)別呢?本文就詳細(xì)的介紹一下,感興趣的可以了解一下2021-06-06

