Version alpha:
更新时间:23/12/2
//现存问题:
//实际埋雷数不等于要求埋雷数,因为生成的随机数可能重复.
//要加个坐标系.
//不显示(或错误显示)周边雷数.
//埋雷未考虑增广区域.
#include<stdio.h>
#include<string.h>
#include<time.h>
#include<stdlib.h>
#define SIZE 10
int matrix[SIZE + 2][SIZE + 2]; //记录雷位置的棋盘
//**注意此棋盘的(1,1)对应展示棋盘的(0,0)**
char display[SIZE][SIZE]; //展示的棋盘.
void initialize(void); //初始化棋盘.
void mine(int n); //埋雷.
void print_display(void); //打印展示棋盘.
int operate(int x, int y); //操作录入与判断.
int minecount(int x, int y); //计算操作点周边雷数.
void change(int x, int y); //变化展示棋盘,显示周边雷数(若无雷)(不含打印).
void boom(void); //显示爆炸(若有雷)(含打印).
void print_time(int gap); //换算整理用时并打印.
int main(void) {
long long int time_gap = time(NULL);
initialize();
int x, y;
int n;
int judge = 0;
printf("How many mines?\n");
scanf("%d", &n);
mine(n);
for(;;) {
printf("\n");
print_display();
printf("\nPlease input:\n");
scanf("%d%d", &x, &y);
if(operate(x, y) == 1) break;
else {
minecount(x, y);
change(x, y);
if(n == 0) {
judge = 1;
break;
}
}
}
time_gap = time(NULL) - time_gap;
if(judge == 0) {
boom();
printf("Lose!\n");
print_time((int)time_gap);
} else {
print_display();
printf("Win!\n");
print_time((int)time_gap);
}
return 0;
}
//初始化.
void initialize(void) {
for(int i = 0; i < SIZE; i++) {
for(int j = 0; j < SIZE; j++) {
display[j][i] = '.';
}
}
return;
}
//埋雷.
void mine(int n) {
srand((unsigned int)time(NULL));
for(int i = 0; i < n; i++) {
int retx = rand() % SIZE;
int rety = rand() % SIZE;
matrix[retx + 1][rety + 1] = 1;
}
return;
}
//打印展示棋盘.
void print_display(void) {
for(int i = 0; i < SIZE; i++) {
for(int j = 0; j < SIZE; j++) {
printf("%c ", display[j][i]);
}
printf("\n");
}
return;
}
//操作录入与判断.
int operate(int x, int y) {
if(matrix[x + 1][y + 1] == 1) {
return 1;
} else {
change(x, y);
return 0;
}
}
//计算操作点周边雷数.
int minecount(int x, int y) {
x++; y++;
int count = 0;
for(int i = -1; i <= 1; i++) {
for(int j = -1; j <= 1; j++) {
if(matrix[x + i][y + i] == 1) count++;
}
}
return count;
}
//变化显示棋盘,显示周边雷数(若无雷).
void change(int x, int y) {
display[x][y] = minecount(x, y) + 48;
return;
}
//显示爆炸(若有雷).
void boom(void) {
for(int i = 0; i < SIZE; i++) {
for(int j = 0; j < SIZE; j++) {
if(matrix[i + 1][j + 1] == 1)
display[j][i] = '*';
}
}
print_display();
return;
}
//换算整理用时并打印.
void print_time(int time_gap) {
int h = 0, m = 0, s = time_gap;
for(;;) {
if(time_gap > 59) {
time_gap-=59;
m++;
} else break;
}
for(;;) {
if(m > 59) {
m-=59;
h++;
} else break;
}
if(m == 0) printf("Time: %ds", s);
else if(h == 0) printf("Time: %dm %ds", m, s);
else printf("Time: %dh %dm %ds", h, m, s);
return;
}
晚上十一点半突然有了这个想法。大概写了一个半小时,只实现了基本功能,甚至还有很多bug。基本没法正常游玩(正常运行都谢天谢地了)。
Version beta:
更新时间:23/12/3
//更新日志:
//v_alpha:实现基本功能.
//v_beta:
//增加了坐标系,增加了标雷功能,增加开始界面,修复了埋雷卡死bug,
//修复了显示周边雷数与实际周边雷数不匹配的问题,
//修复了要求埋雷数与实际埋雷数不相等的问题,增加了扩散效果,
//默认棋盘大小改至16*16,新增推荐埋雷数40的提示.
//增加了末尾的getchar()保证其运行结束后暂停.
//修改了游戏结束后显示的文本内容.
//修改了计时器显示错误的bug.
//需要改进:
//增加通过标记所有的雷来取胜的方式(目前是点完所有无雷的格点取胜).
//直接用类似-3 -8的输入方式来标记(这样做或许可以很方便地让第一次输入也可以做标记).
//更好的扩散效果.
//防呆机制.
//*****无法取胜!*****
#include<stdio.h>
#include<string.h>
#include<time.h>
#include<stdlib.h>
#define SIZE 16
char version[100] = {"Beta"};
int matrix[SIZE + 2][SIZE + 2]; //记录雷位置的棋盘
//**注意此棋盘的(1,1)对应展示棋盘的(0,0)**
char display[SIZE][SIZE]; //展示的棋盘.
void initialize(void); //初始化棋盘.
void mine(int n, int x, int y); //埋雷,初始位置minecount应为0.
void print_display(void); //打印展示棋盘.
int operate(int x, int y); //操作录入与判断.
void bfs_spread(int x, int y); //基于广度优先搜索,实现扩散效果.
int minecount(int x, int y); //计算操作点周边雷数.
void change(int x, int y); //变化展示棋盘,显示周边雷数(若无雷)(不含打印).
void mark(void); //进入标雷模式.
void boom(void); //显示爆炸(若有雷)(含打印).
void print_time(int gap); //换算整理用时并打印.
int main(void) {
//开始界面.
printf
("Ciallo! XD\n"
"Welcome to my MineClearance! \n"
"Version: %s. \n"
"Author: Sylphii. \n\n"
"It needs some explaination. \n"
"When \"input\", you needs input "
"the coordinate you want to click. \n"
"And please use a space to separate \"x\" and \"y\". \n\n"
" -- > x\n|\nv eg: input \"1 1\" to infer (1,1).\ny\n\n"
"If you want to mark a mine, "
"please input \"-1 -1\" and press Enter. \n"
"Then the operation mod will be changed to \"Mark\". \n"
"Everytime you input, the mod will "
"change to \"Click\" automatically. \n"
"\n***********Warning***********\n"
"Because of my foolishness, please don't input \"-1 -1\" at the beginning. \n"
"***********Warning***********\n\n"
"Now, press Enter to start...\n", version);
getchar();
//初始化.
long long int time_gap = time(NULL);
initialize();
int x, y;
int n;
int judge = 0;
//输入埋雷数并进行第一次操作.
printf("How many mines?\n(Recommend: 40)\n");
scanf("%d", &n);
print_display();
printf("\nPlease input:\n");
scanf("%d%d", &x, &y);
mine(n, x ,y);
operate(x ,y);
//后续操作.
for(;;) {
printf("\n");
print_display();
printf("\nPlease input:\n");
scanf("%d%d", &x, &y);
if(x == -1 && y == -1)
mark();
else {
if(operate(x, y) == 1)
break;
else {
change(x, y);
if(n == 0) {
judge = 1;
break;
}
}
}
}
//游戏结束.
time_gap = time(NULL) - time_gap;
if(judge == 0) {
boom();
printf("\nDamn it!\n");
print_time((int)time_gap);
} else {
print_display();
printf("\nGodness!\n");
print_time((int)time_gap);
}
printf("\nPress Enter to continue...");
getchar();
getchar();
return 0;
}
//初始化.
void initialize(void) {
for(int i = 0; i < SIZE; i++) {
for(int j = 0; j < SIZE; j++) {
display[j][i] = '.';
}
}
return;
}
//埋雷,初始位置minecount应为0.
void mine(int n, int x, int y) {
//初次埋雷.
srand((unsigned int)time(NULL));
for(int i = 0; i < n; i++) {
int retx = rand() % SIZE;
int rety = rand() % SIZE;
if(matrix[retx + 1][rety + 1] == 1)
n++;
else
matrix[retx + 1][rety + 1] = 1;
}
//检查初始位置minecount是否为0.
//是则继续.
if(minecount(x, y) == 0)
return;
//否则删雷重埋.
else {
for(;;) {
//删雷,并记录个数以便重埋.
int count = 0;
for(int i = -1; i <= 1; i++) {
for(int j = -1; j <= 1; j++) {
if(matrix[x + 1 + i][y + 1 + j] == 1) {
matrix[x + 1 + i][y + 1 + j] = 0;
count++;
}
}
}
//重埋count个雷.
for(int i = 0; i < count; i++) {
int retx = rand() % SIZE;
int rety = rand() % SIZE;
if(matrix[retx + 1][rety + 1] == 1)
count++;
else
matrix[retx + 1][rety + 1] = 1;
}
//再次初始位置判断,合格则返回.
//不合格则循环该过程.
if(minecount(x, y) == 0)
return;
}
}
}
//打印展示棋盘.
void print_display(void) {
printf(" ");
for(int i = 0; i < SIZE; i++) {
printf("%2d ", i);
}
printf("\n");
for(int i = 0; i < SIZE; i++) {
printf("%2d ", i);
for(int j = 0; j < SIZE; j++) {
printf("%c ", display[j][i]);
}
printf("\n");
}
return;
}
//操作录入与判断.
int operate(int x, int y) {
if(matrix[x + 1][y + 1] == 1) {
return 1;
} else {
if(minecount(x, y) == 0)
bfs_spread(x, y);
else
change(x, y);
}
return 0;
}
//基于广度优先搜索,实现扩散效果.
//左,上,右,下.
int wx[4] = {-1, 0, 1, 0};
int wy[4] = {0, -1, 0, 1};
//已经搜索过的点.
int pass[SIZE][SIZE];
void bfs_spread(int x, int y) {
if(pass[x][y] != 1) {
pass[x][y] = 1;
change(x, y);
for(int i = 0; i < 4; i++) {
int tx = x + wx[i];
int ty = y + wy[i];
if(tx >= 0 && tx <= SIZE - 1 && ty >= 0 && ty <=SIZE - 1) {
if(minecount(tx, ty) == 0)
bfs_spread(tx, ty);
else
change(tx, ty);
}
}
return;
}
else
return;
}
//计算操作点周边雷数.
int minecount(int x, int y) {
x++; y++;
int count = 0;
for(int i = -1; i <= 1; i++) {
for(int j = -1; j <= 1; j++) {
if(matrix[x + i][y + j] == 1)
count++;
}
}
return count;
}
//变化显示棋盘,显示周边雷数(若无雷).
void change(int x, int y) {
display[x][y] = minecount(x, y) + 48;
return;
}
//进入标雷模式.
void mark(void) {
int x, y;
printf("\nPlease mark:\n");
scanf("%d %d", &x, &y);
display[x][y] = '#';
print_display();
return;
}
//显示爆炸(若有雷).
void boom(void) {
for(int i = 0; i < SIZE; i++) {
for(int j = 0; j < SIZE; j++) {
if(matrix[i + 1][j + 1] == 1)
display[i][j] = '*';
}
}
print_display();
return;
}
//换算整理用时并打印.
void print_time(int time_gap) {
int h = 0, m = 0, s = time_gap;
for(;;) {
if(s > 59) {
s-=60;
m++;
} else
break;
}
for(;;) {
if(m > 59) {
m-=60;
h++;
} else
break;
}
if(m == 0)
printf("Time: %ds", s);
else {
if(h == 0)
printf("Time: %dm %ds", m, s);
else
printf("Time: %dh %dm %ds", h, m, s);
}
return;
}
修修补补加了一些新功能,增加了扩散效果,改了一些bug。
写的时候一直出现埋雷卡死和扩散卡死的情况。
而且扩散效果虽说基本实现了,但是会出现一些很呆的情况,比如角上的"0"格子对角的格子一定无雷,但是扩散效果却没有扩散到。究其原因是该扩散效果是从中心向四个方向进发的,只能扩散到周围的四个格子,而不是八个格子,可能需要大改。
标记功能每次都要输入"-1 -1"很呆,经朋友提醒或许可以直接在坐标前加负号来表示作标记,但这样做的问题是(0,0)没法作标记。可能需要用"-17 -17"来表示在(0,0)作标记。
玩到后期会出现一次操作打印两次的问题,待查明。
还要增加防呆机制,防止非法输入卡崩程序。