相信大家都玩过windows下的扫雷游戏,对其的规则也比较了解,
这里我们就不对规则做过多赘述了。
首先菜单和主函数的编写和上一篇三子棋中的内容一致,
这里我们直接给出代码:
void menu()
{
printf("********* Welcom to Mines *********\n");
printf("***************************************\n");
printf("* 1.play 0.exit *\n");
printf("***************************************\n");
}
int main()
{
int choice;
srand((unsigned int)time(NULL));
do
{
menu();
printf("Input your choice:");
scanf("%d", &choice);
switch (choice)
{
case 1:
game();
break;
case 0:
break;
default:
printf(" Input error!Please try again.\n");
break;
}
} while (choice);
return 0;
}
一样,我们先定义一个game()函数,而后慢慢填充其中的内容。
和三子棋不同的是,这里需要创建两个数组,其中一个是雷区数组,另一个是需要输出在屏幕上的显示数组。
为了便于区分两个数组,雷区数组我用mines表示,显示数组我用show_area来表示。
但这一步我们先跳过,先来完成初始化数组的功能函数。
这次我选择使用memset()函数来进行初始化操作。
在init函数中,我们需要将雷区数组全部赋成“0”,“0”表示没雷。
将显示数组全部初始化为“.”,“.”表示未探索区域。
由于是扫雷程序,那么在雷区中一定要有雷才行,所以我们用上一篇提到的rand()来随机生成坐标指向雷区数组的元素,
并将其赋值为“1”,“1”表示有雷。
代码如下:
void init_mines(char mine[ROWS][COLS],char show[ROWS][COLS],int rows,int cols,int row,int col)
{
int i, x, y;
memset(mine, '0', rows*cols*sizeof(char));
memset(show, '.', rows*cols*sizeof(char));
for (i = 0; i < MINES; i++)
{
while (1)
{
x = rand() % row + 1;
y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
break;
}
}
}
}
当用户完成每次扫雷操作后,一定要打印出当前的状况,这时就需要一个显示功能函数display()。
代码如下:
void display(char mine[ROWS][COLS],int row,int col)
{
int i, j;
for (i = 1; i <= row; i++)
{
printf("%4d", i);
}
printf("\n");
for (i = 1; i <= col; i++)
{
printf("%2d", i);
for (j = 1; j <= col; j++)
{
printf(" %c “, mine[i][j]);
}
printf(”\n");
}
}
为了用户体验,我们在打印出的雷区上方和左方加上了行数、列数,方便用户观察坐标。
这时我们就需要利用扫雷规则来编写完成checkwin()函数,以判断是否胜利。
这里我们首先判断其是不是雷,
如果不是雷,我们再编写一个checkmine()函数,用来遍历其周围雷的个数,并将个数填入显示数组中对应的元素中。
如果是雷,则返回一个值“”,“”表示被炸死。
最后判断是否胜利。
我们先定义一个count变量,用来记录未探索区域的个数,
如果未探索区域的个数和初始化时随机生成的雷的个数相等,则表示雷排完了,
返回一个值“w”,“w”表示胜利。
代码如下:
int checkwin(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col,int x,int y)
{
int count = 0;
if (mine[x][y] == '0')
show[x][y] = checkmine(mine, show, x, y);
else return '*';
for (x = 1; x <= row; x++)
{
for (y = 1; y <= col; y++)
{
if (show[x][y] == '.')
count++;
}
}
if (count == MINES)
return 'w';
return 0;
}
接着我们给出上面提到的,用来遍历该坐标周围雷数的checkmine()函数。
static char checkmine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int m, n;
char mine_count = '0';
for (m = x - 1; m <= x + 1 ; m++)
{
for (n = y - 1; n <= y + 1; n++)
{
if (mine[m][n] == '1')
mine_count++;
}
}
return mine_count;
}
由于该代码只在该源文件中使用,所以我在其类型前加上了static。
这时我们就完成了扫雷各个功能函数的编写,接着根据我们平时玩游戏的思路将上述函数引入game()中即可完成简单扫雷的编写。
game()函数的代码如下:
void game()
{
char mines[ROWS][COLS], show_area[ROWS][COLS], ret;
int x, y;
init_mines(mines, show_area, ROWS, COLS, ROW, COL);
display(show_area, ROW, COL);
while (1)
{
printf("Input x and y(like x y):");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
ret = checkwin(mines, show_area, ROW, COL, x, y);
if (ret == 'w')
{
display(mines, ROW, COL);
printf("Congratulations!You win!\n");
break;
}
else if (ret == '*')
{
display(mines, ROW, COL);
printf("You lose!Good luck next time!\n");
break;
}
else
{
display(show_area, ROW, COL);
printf("\n");
}
}
else printf("Input error!Please try again.\n");
}
}
和三子棋一样,我这里定义数组时也运用了宏定义。
最后放上我自己写出的代码:
代码分为三部分,
第一部分是头文件:
#ifndef __MINES_H__
#define __MINES_H__
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
#define COLS 12
#define ROWS 12
#define COL 10
#define ROW 10
#define MINES 10
void init_mines(char mine[ROWS][COLS], char show[ROWS][COLS], int rows, int cols, int row, int col);
void display(char mine[ROWS][COLS], int row, int col);
int checkwin(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y);
#endif//__MINES_H__
第二部分是源文件的函数部分:
#include"mines.h"
void init_mines(char mine[ROWS][COLS],char show[ROWS][COLS],int rows,int cols,int row,int col)
{
int i, x, y;
memset(mine, '0', rows*cols*sizeof(char));
memset(show, '.', rows*cols*sizeof(char));
for (i = 0; i < MINES; i++)
{
while (1)
{
x = rand() % row + 1;
y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
break;
}
}
}
}
void display(char mine[ROWS][COLS],int row,int col)
{
int i, j;
for (i = 1; i <= row; i++)
{
printf("%4d", i);
}
printf("\n");
for (i = 1; i <= col; i++)
{
printf("%2d", i);
for (j = 1; j <= col; j++)
{
printf(" %c ", mine[i][j]);
}
printf("\n");
}
}
static char checkmine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int m, n;
char mine_count = '0';
for (m = x - 1; m <= x + 1 ; m++)
{
for (n = y - 1; n <= y + 1; n++)
{
if (mine[m][n] == '1')
mine_count++;
}
}
return mine_count;
}
int checkwin(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col,int x,int y)
{
int count = 0;
if (mine[x][y] == '0')
show[x][y] = checkmine(mine, show, x, y);
else return '*';
for (x = 1; x <= row; x++)
{
for (y = 1; y <= col; y++)
{
if (show[x][y] == '.')
count++;
}
}
if (count == MINES)
return 'w';
return 0;
}
这部分内容,我并没有完成关键的2点。 1、为了用户体验,在第一次用户执行扫雷操作时,不会被炸死。
2、当用户输入的坐标周围没雷时,可以实现无雷区的展开。
第三部分是源文件的游戏测试部分:
#include"mines.h"
void menu()
{
printf("********* Welcom to Mines *********\n");
printf("***************************************\n");
printf("* 1.play 0.exit *\n");
printf("***************************************\n");
}
void game()
{
char mines[ROWS][COLS], show_area[ROWS][COLS], ret;
int x, y;
init_mines(mines, show_area, ROWS, COLS, ROW, COL);
display(mines, ROW, COL);
display(show_area, ROW, COL);
while (1)
{
printf("Input x and y(like x y):");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
ret = checkwin(mines, show_area, ROW, COL, x, y);
if (ret == 'w')
{
display(mines, ROW, COL);
printf("Congratulations!You win!\n");
break;
}
else if (ret == '*')
{
display(mines, ROW, COL);
printf("You lose!Good luck next time!\n");
break;
}
else
{
display(show_area, ROW, COL);
printf("\n");
}
}
else printf("Input error!Please try again.\n");
}
}
int main()
{
int choice;
srand((unsigned int)time(NULL));
do
{
menu();
printf("Input your choice:");
scanf("%d", &choice);
switch (choice)
{
case 1:
game();
break;
case 0:
break;
default:
printf(" Input error!Please try again.\n");
break;
}
} while (choice);
return 0;
}
希望各位能对我的代码提出意见和建议,并能指导我完成第二部分钟我未完成的两点。