初步了解数组、指针后,实现了一个win32控制台下(黑框框)简易版扫雷程序.
程序中 行数列数雷数可自行定义。
程序用*代表位置未知,空格代表此位置周围无雷,数字代表周围雷个数。
分三个代码共同编译实现,SaoLei.h包含程序所需头文件,宏定义及所用函数声明。SaoLei.c为游戏逻辑代码。Function.c 为自定义函数实现代码。
程序用两个char数组分别表示两块地图,一副地图呈现给玩家,一幅地图用于实现布雷及雷数更新等。两个数组在程序中会时时交互。
程序拥有坐标合法性检查函数,保证输入不合法坐标及重复坐标时程序可以正常运行。
游戏简易思路: 创建两个char数组分别代表两个地图, 一个地图为游戏地图呈现给玩家,一个地图用来布雷(用rand()函数初始化雷坐标)。考虑到需要检测边界处周围雷个数,因此布雷数组比游戏数组多两行两列。 限于win32 平台下 ,因此用输入坐标形式进行交互(输入坐标与数组坐标不同,在程序中有注释)。 玩家每输入一个坐标, 不同函数中两个数组进行传参交互,判断输赢,坐标是否合法,显示坐标周围雷个数, 更新数组, 重新打印地图等。
程序实现后并未进行优化,所以可能存在下许问题:代码或许会有些冗杂,函数或许未很好的封装导致重复,或本为一个函数模块却分多个函数实现等。尽请见谅。
程序中有详尽注释。
源码如下:
SaoLei.h:
/*
此自定义头文件包含SaoLei.c与Function.c所需头文件及函数原型声明
*/
#ifndef __SaoLei_h__
#define __SaoLei_h__
//程序用到标准输入输出函数
#include <stdio.h>
/*
程序随机产生雷坐标
*/
#include <stdlib.h>
#include <time.h>
#define ROWS 11 //定义数组行数(注意! 实际显示给用户的地图行数为ROWS - 2)
#define COLS 11 //定义数组列数(注意! 实际显示给用户的地图列数为COLS - 2)
#define NUM 10 //定义雷个数
//打印游戏选择菜单
void menu(void);
//初始化两块地图
void init_map(char gamemap[][COLS - 2], char map[][COLS], int rows); //此处传来的参数rows为map的行数,gamemap的行数应为rows - 2
//打印最新游戏地图
void print_gamemap(char gamemap[][COLS - 2], int rows); //此处传来的参数rows为gamemap的行数ROWS - 2
//进行游戏
void playgame(char gamemap[][COLS - 2], char map[][COLS], int rows); //此处传来的参数rows为map的行数,gamemap的行数应为rows - 2
//检测玩家输入坐标是否合法
int check(char gamemap[][COLS - 2], int rows, int x, int y); //此处传来的参数rows为gamemap的行数ROWS - 2 x y 为玩家输入坐标
//判断雷是否已排完
int count(char gamemap[][COLS - 2], int rows, int num); //此处传来的参数rows为gamemap的行数ROWS - 2 num为已定义的雷个数
//判断输入坐标身边雷情况
int pailei(char map[][COLS], int x, int y); //返回-1则此坐标为雷,返回0-8代表此坐标身边雷个数
//参数x y 为玩家输入坐标
//更新gamemap
void gxgamemap(char gamemap[][COLS - 2], int x, int y, int ret); //x, y为输入坐标, ret为输入坐标身边雷个数
//打印map地图(供测试用)
void print_map(char map[][COLS], int rows); //此处传来的参数rows为map的行数ROWS
#endif
SaoLei.c:
/*
扫雷游戏逻辑
*/
#include "SaoLei.h"
int main(void)
{
int input; //用来接受用户输入
char gamemap[ROWS - 2][COLS - 2]; //gamemap用来向用户展示的地图
char map[ROWS][COLS]; //map 用来显示雷位置地图,不向用户展示
menu(); //打印游戏选择菜单
scanf("%d", &input); <span style="white-space:pre"> </span>//接收用户输入 1为进行游戏 0为退出游戏
while( input ) //对用户输入进行判断
{
init_map(gamemap, map, ROWS); /*初始化两块地图
此处传过去的参数ROWS为map的行数,gamemap的行数应为ROWS - 2*/
print_gamemap(gamemap, ROWS - 2); //打印最新游戏地图
//print_map(map, ROWS); //打印map(用于测试)
playgame(gamemap, map, ROWS); //进行游戏
menu(); //再次打印游戏菜单
scanf("%d", &input); <span style="white-space:pre"> </span>//再次接收用户输入 1为进行游戏 0为退出游戏 <span style="white-space:pre"> </span>
}//游戏结束
printf("\nBye!\n");
return 0; //程序结束
}
/*
SaoLei.c中函数实现
*/
#include "SaoLei.h"
//打印游戏菜单
void menu(void)
{
printf("********************\n");
printf("****** 1.play ******\n");
printf("****** 0.exit ******\n");
printf("********************\n");
}
//初始化两块地图
void init_map(char gamemap[][COLS - 2], char map[][COLS], int rows) //此处传来的参数rows为map的行数,gamemap的行数应为rows - 2
{
int r;
int c;
int x; //保存随机产生雷行标
int y; //保存随机产生雷列标
int count = 0; //计算雷个数 已定义NUM为雷个数
srand((unsigned)time(NULL));
for( r = 0; r < rows - 2; r++ ) <span style="white-space:pre"> </span>//初始化gamemap地图,每个位置设为'*'表示此位置未知
for( c = 0; c < COLS - 2; c++ )
gamemap[r][c] = '*';
for( r = 0; r < rows; r++ ) //初始化没有雷的map棋盘,每个位置设为'0'表示无雷
for( c = 0; c < COLS; c++ )
map[r][c] = '0';
for( count = 0; count < NUM; ) <span style="white-space:pre"> </span> //对map地图进行布雷,位置为'1'表示有雷
{
/*
map中首行首列末行末列不放雷,所以x y坐标有限制
*/
x = rand() % 9 + 1; //随机产生雷行标 1 - 9
y = rand() % 9 + 1; //随机产生雷列标 1 - 9
if( map[x][y] != '1' )
{
/*
如果此坐标未被设置雷, 则将count++, 并将此位置坐标设为雷('1')
*/
map[x][y] = '1';
count++;
}
}//退出for循环
}//init_map函数结束
//打印最新游戏地图
void print_gamemap(char gamemap[][COLS - 2], int rows) //此处传来的参数rows为gamemap的行数,ROWS - 2
{
int i;
int r;
int c;
/*
打印行标号与列标号
*/
putchar(' ');
for( i = 0; i < COLS - 2; i++ )
printf("%d ", i + 1);
putchar('\n');
/*
打印gamemap
*/
for( r = 0; r < rows; r++ )
{
printf("%d", r + 1);
for( c = 0; c < COLS - 2; c++ )
printf("%c ", gamemap[r][c]);
printf("\n");
}
}
//打印map地图(供测试用)
void print_map(char map[][COLS], int rows) //此处传来的参数rows为map的行数ROWS
{
int i;
int r;
int c;
putchar('\n');
printf(" ");
for( i = 0; i < ROWS; i++ )
printf("%d ", i + 1);
putchar('\n');
for( r = 0; r < rows; r++ )
{
printf("%d", r + 1);
for( c = 0; c < COLS; c++ )
printf("%c ", map[r][c]);
putchar('\n');
}
}
//检测玩家输入坐标是否合法
int check(char gamemap[][COLS - 2], int rows, int x, int y) //此处传来的参数rows为gamemap的行数ROWS - 2. x y 为玩家输入行列坐标
{
if( ((x >= 1) && (x <= 9)) && ((y >= 1) && (y <= 9)) ) //x y范围是否满足
if( gamemap[x - 1][y - 1] == '*' ) //此坐标是否已被使用过
return 0;
return 1; //返回0输入坐标合法, 返回1输入坐标不合法!
}
//判断雷是否已排完
int count(char gamemap[][COLS - 2], int rows, int num) //此处传来的参数rows为gamemap的行数ROWS - 2 num为已定义的雷个数
{
int r;
int c;
int n = 0; //目前雷数目
for( r = 0; r < rows; r++ )
for( c = 0; c < COLS - 2; c++ )
if( gamemap[r][c] == '*')
n++;
if( n == num )
return 0;
return 1; //返回0 则雷已排完 返回 1 则雷未排完
}
/*
判断输入坐标身边雷情况
返回-1则此坐标为雷,返回0-8代表此坐标身边雷个数
参数 x y 为玩家输入坐标
'1'代表雷
*/
int pailei(char map[][COLS], int x, int y)
{
int ret = 0; //保存雷个数
if( map[x][y] == '1' ) //此坐标为雷,返回-1
return -1;
/*
计算雷个数,并返回值
*/
if( map[x][y + 1] == '1')
ret++;
if( map[x + 1][y + 1] == '1')
ret++;
if( map[x - 1][y + 1] == '1')
ret++;
if( map[x][y - 1] == '1')
ret++;
if( map[x + 1][y - 1] == '1')
ret++;
if( map[x - 1][y - 1] == '1')
ret++;
if( map[x + 1][y] == '1')
ret++;
if( map[x - 1][y] == '1')
ret++;
return ret;
}
//更新gamemap
void gxgamemap(char gamemap[][COLS - 2], int x, int y, int ret) //ret为输入坐标身边雷个数, x y为输入坐标
{
while( 1 )
{
if( ret == 0 )
{
gamemap[x - 1][y - 1] = ' ';
break;
}
gamemap[x - 1][y - 1] = '0' + ret;
break;
}
}
//进行游戏
void playgame(char gamemap[][COLS - 2], char map[][COLS], int rows) //此处传来的参数rows为map的行数,gamemap的行数应为rows - 2
{
int x; //
int y; //存放用户输入坐标
int ret; //存放返回数值,用来说明用户输入的坐标处情况
while( count(gamemap, ROWS - 2, NUM) ) //返回0则雷已排完,游戏胜利,返回1则雷未排完.
{
printf("请输入坐标:");
scanf("%d %d", &x, &y);
while( check(gamemap, ROWS - 2, x, y) ) //检测玩家输入坐标是否合法
{
printf("坐标不合法!\n请重新输入坐标:");
scanf("%d %d", &x, &y);
}
ret = pailei(map, x, y); //返回-1则此坐标为雷,返回0-8代表此坐标身边雷个数
if( ret == -1 ) //游戏失败
{
printf("呵呵!\n");
break;
}
gxgamemap(gamemap, x, y, ret); //根据返回值更新gamemap, x y 为输入的坐标
print_gamemap(gamemap, ROWS - 2); //打印最新游戏地图
//print_map(map, ROWS); //打印map地图(用于测试)
}//退出最外层while循环
if( (count(gamemap, ROWS - 2, NUM)) == 0 ) //判断是胜利后跳出while循环还是失败后跳出,若是胜利,函数返回值为0
printf("你赢了!\n");
}//退出playgame函数