前言
游戏的模块主要包括:棋盘初始化、地雷布置、棋盘显示、胜利条件、扩展空白区域、扫雷、游戏菜单、游戏调用。 本文使用9*9的棋盘大小,为了方便计算选定的坐标周围的地雷数量,将棋盘实际大小设定成11*11,初始地雷数量设定为5。
# define ROW 9
# define COL 9
# define ROWS ROW+ 2
# define COLS COL+ 2
# define EAST_COUNT 5
棋盘初始化
设置两个棋盘mine和show,mine用于设置地雷坐标,show用于展示给用户。 使用set接收棋盘位置字符,’ 0 '表示无地雷,用 ’ 1 ‘表示有地雷,用’ * '表示未知区域。
void init_board ( char arr[ ROWS] [ COLS] , int rows, int cols, char set) {
for ( int i = 0 ; i < rows; i++ ) {
for ( int j = 0 ; j < cols; j++ ) {
arr[ i] [ j] = set;
}
}
}
设置地雷
对mine棋盘进行设置地雷,使用字符 ’ 1 ’ 表示地雷,方便后续计算该坐标周围地雷数量。
void set_mine ( char mine[ ROWS] [ COLS] , int row, int col) {
int count = EAST_COUNT;
int x = 0 ;
int y = 0 ;
while ( count) {
x = rand ( ) % row + 1 ;
y = rand ( ) % col + 1 ;
if ( mine[ x] [ y] == '0' ) {
mine[ x] [ y] = '1' ;
count-- ;
}
}
}
棋盘显示
void show_board ( char arr[ ROWS] [ COLS] , int row, int col) {
printf ( "--------扫雷--------\n" ) ;
for ( int i = 0 ; i <= col; i++ ) {
printf ( "%d " , i) ;
}
printf ( "\n" ) ;
for ( int i = 1 ; i <= row; i++ ) {
printf ( "%d " , i) ;
for ( int j = 1 ; j <= col; j++ ) {
printf ( "%c " , arr[ i] [ j] ) ;
}
printf ( "\n" ) ;
}
printf ( "--------------------\n" ) ;
}
胜利条件
以未知区域数量作于判断胜利的条件,当剩余’ * '的数量等于设置的地雷数量(EAST_COUNT)时,游戏胜利。
int win_con ( char show[ ROWS] [ COLS] , int row, int col) {
int i, j, count = 0 ;
for ( i = 1 ; i <= row; i++ )
{
for ( j = 1 ; j <= col; j++ )
{
if ( show[ i] [ j] == '*' )
count++ ;
}
}
return count;
}
扩展空白安全区域
首先需要使用get_mine_count()计算该坐标周围的地雷数量,然后使用递归方法 safe_area(),对该坐标周围8个坐标进行递归计算地雷数量,遇到周围有地雷的坐标则停止,并在该坐标填充地雷数量。 *
int get_mine_count ( char mine[ ROWS] [ COLS] , int x, int y) {
return mine[ x - 1 ] [ y - 1 ] + mine[ x - 1 ] [ y] + mine[ x - 1 ] [ y + 1 ] + mine[ x] [ y - 1 ] + mine[ x] [ y + 1 ] + mine[ x + 1 ] [ y - 1 ] + mine[ x + 1 ] [ y] + mine[ x + 1 ] [ y + 1 ] - 8 * '0' ;
}
void safe_area ( char mine[ ROWS] [ COLS] , char show[ ROWS] [ COLS] , int row, int col)
{
int count;
count = get_mine_count ( mine, row, col) ;
if ( 0 == count)
{
show[ row] [ col] = ' ' ;
if ( row - 1 > 0 && row + 1 <= ROW && col > 0 && col <= COL && show[ row - 1 ] [ col] == '*' )
safe_area ( mine, show, row - 1 , col) ;
if ( row + 1 > 0 && row + 1 <= ROW && col > 0 && col <= COL && show[ row + 1 ] [ col] == '*' )
safe_area ( mine, show, row + 1 , col) ;
if ( row > 0 && row <= ROW && col - 1 > 0 && col - 1 <= COL && show[ row] [ col - 1 ] == '*' )
safe_area ( mine, show, row, col - 1 ) ;
if ( row > 0 && row <= ROW && col + 1 > 0 && col + 1 <= COL && show[ row] [ col + 1 ] == '*' )
safe_area ( mine, show, row, col + 1 ) ;
if ( row - 1 > 0 && row - 1 <= ROW && col - 1 > 0 && col - 1 <= COL && show[ row - 1 ] [ col - 1 ] == '*' )
safe_area ( mine, show, row - 1 , col - 1 ) ;
if ( row + 1 > 0 && row + 1 <= ROW && col + 1 > 0 && col + 1 <= COL && show[ row + 1 ] [ col + 1 ] == '*' )
safe_area ( mine, show, row + 1 , col + 1 ) ;
if ( row - 1 > 0 && row - 1 <= ROW && col + 1 > 0 && col + 1 <= COL && show[ row - 1 ] [ col + 1 ] == '*' )
safe_area ( mine, show, row - 1 , col + 1 ) ;
if ( row + 1 > 0 && row + 1 <= ROW && col - 1 > 0 && col - 1 <= COL && show[ row + 1 ] [ col - 1 ] == '*' )
safe_area ( mine, show, row + 1 , col - 1 ) ;
}
else
{
show[ row] [ col] = get_mine_count ( mine, row, col) + '0' ;
}
}
扫雷
循环进行扫雷,直到踩雷或者排雷成功跳出循环。 每次扫雷都判断是否符合胜利条件,符合则跳出循环,游戏胜利。
void find_mine ( char mine[ ROWS] [ COLS] , char show[ ROWS] [ COLS] , int row, int col) {
int x = 0 ;
int y = 0 ;
while ( 1 ) {
printf ( "请输入要排查的坐标:>\n" ) ;
scanf ( "%d %d" , & x, & y) ;
if ( x >= 1 && x <= row && y >= 1 && y <= col) {
if ( mine[ x] [ y] == '1' ) {
printf ( "该地点是雷,游戏结束!\n" ) ;
show_board ( mine, ROW, COL) ;
break ;
}
else {
safe_area ( mine, show, x, y) ;
int total = win_con ( show, ROW, COL) ;
if ( EAST_COUNT == total) {
printf ( "恭喜你,排雷成功\n" ) ;
show_board ( show, ROW, COL) ;
break ;
}
show_board ( show, ROW, COL) ;
}
}
else {
printf ( "坐标非法\n" ) ;
}
}
}
游戏菜单和游戏调用
void menu ( ) {
printf ( "************************\n" ) ;
printf ( "************************\n" ) ;
printf ( "******** 1.play ********\n" ) ;
printf ( "******** 0.exit ********\n" ) ;
printf ( "************************\n" ) ;
printf ( "************************\n" ) ;
}
void game ( ) {
char mine[ ROWS] [ COLS] = { 0 } ;
char show[ ROWS] [ COLS] = { 0 } ;
init_board ( mine, ROWS, COLS, '0' ) ;
init_board ( show, ROWS, COLS, '*' ) ;
set_mine ( mine, ROW, COL) ;
show_board ( show, ROW, COL) ;
find_mine ( mine, show, ROW, COL) ;
}
int main ( ) {
int input = 0 ;
srand ( ( unsigned int ) time ( NULL ) ) ;
do {
menu ( ) ;
printf ( "请选择:>" ) ;
scanf ( "%d" , & input) ;
switch ( input)
{
case 1 :
printf ( "扫雷\n" ) ;
game ( ) ;
break ;
case 0 :
printf ( "退出游戏" ) ;
break ;
default :
printf ( "选择错误" ) ;
break ;
}
} while ( input) ;
return 0 ;
}
总结
总棋盘(11*11)比显示棋盘(9*9)多一圈,统一显示棋盘中坐标周围地雷的计算,使用便利。 两个棋盘的设置,mine棋盘以字符0和1进行存储,方便程序对某坐标周围地雷数量的计算;show棋盘以字符*显示未知区域,便于程序判断胜利条件。 使用递归的方法对周围无地雷的坐标进行扩展,提升游戏的可玩性。
# include <stdio.h>
# include <stdlib.h>
# include <time.h>
# define ROW 9
# define COL 9
# define ROWS ROW+ 2
# define COLS COL+ 2
# define EAST_COUNT 5
void init_board ( char arr[ ROWS] [ COLS] , int rows, int cols, char set) ;
void show_board ( char arr[ ROWS] [ COLS] , int row, int col) ;
void set_mine ( char mine[ ROWS] [ COLS] , int row, int col) ;
int win_con ( char mine[ ROWS] [ COLS] , int row, int col) ;
void safe_area ( char mine[ ROWS] [ COLS] , char show[ ROWS] [ COLS] , int row, int col) ;
void find_mine ( char mine[ ROWS] [ COLS] , char show[ ROWS] [ COLS] , int row, int col) ;
void init_board ( char arr[ ROWS] [ COLS] , int rows, int cols, char set) {
for ( int i = 0 ; i < rows; i++ ) {
for ( int j = 0 ; j < cols; j++ ) {
arr[ i] [ j] = set;
}
}
}
void show_board ( char arr[ ROWS] [ COLS] , int row, int col) {
printf ( "--------扫雷--------\n" ) ;
for ( int i = 0 ; i <= col; i++ ) {
printf ( "%d " , i) ;
}
printf ( "\n" ) ;
for ( int i = 1 ; i <= row; i++ ) {
printf ( "%d " , i) ;
for ( int j = 1 ; j <= col; j++ ) {
printf ( "%c " , arr[ i] [ j] ) ;
}
printf ( "\n" ) ;
}
printf ( "--------------------\n" ) ;
}
void set_mine ( char mine[ ROWS] [ COLS] , int row, int col) {
int count = EAST_COUNT;
int x = 0 ;
int y = 0 ;
while ( count) {
x = rand ( ) % row + 1 ;
y = rand ( ) % col + 1 ;
if ( mine[ x] [ y] == '0' ) {
mine[ x] [ y] = '1' ;
count-- ;
}
}
}
int win_con ( char show[ ROWS] [ COLS] , int row, int col) {
int i, j, count = 0 ;
for ( i = 1 ; i <= row; i++ )
{
for ( j = 1 ; j <= col; j++ )
{
if ( show[ i] [ j] == '*' )
count++ ;
}
}
return count;
}
void safe_area ( char mine[ ROWS] [ COLS] , char show[ ROWS] [ COLS] , int row, int col)
{
int count;
count = get_mine_count ( mine, row, col) ;
if ( 0 == count)
{
show[ row] [ col] = ' ' ;
if ( row - 1 > 0 && row + 1 <= ROW && col > 0 && col <= COL && show[ row - 1 ] [ col] == '*' )
safe_area ( mine, show, row - 1 , col) ;
if ( row + 1 > 0 && row + 1 <= ROW && col > 0 && col <= COL && show[ row + 1 ] [ col] == '*' )
safe_area ( mine, show, row + 1 , col) ;
if ( row > 0 && row <= ROW && col - 1 > 0 && col - 1 <= COL && show[ row] [ col - 1 ] == '*' )
safe_area ( mine, show, row, col - 1 ) ;
if ( row > 0 && row <= ROW && col + 1 > 0 && col + 1 <= COL && show[ row] [ col + 1 ] == '*' )
safe_area ( mine, show, row, col + 1 ) ;
if ( row - 1 > 0 && row - 1 <= ROW && col - 1 > 0 && col - 1 <= COL && show[ row - 1 ] [ col - 1 ] == '*' )
safe_area ( mine, show, row - 1 , col - 1 ) ;
if ( row + 1 > 0 && row + 1 <= ROW && col + 1 > 0 && col + 1 <= COL && show[ row + 1 ] [ col + 1 ] == '*' )
safe_area ( mine, show, row + 1 , col + 1 ) ;
if ( row - 1 > 0 && row - 1 <= ROW && col + 1 > 0 && col + 1 <= COL && show[ row - 1 ] [ col + 1 ] == '*' )
safe_area ( mine, show, row - 1 , col + 1 ) ;
if ( row + 1 > 0 && row + 1 <= ROW && col - 1 > 0 && col - 1 <= COL && show[ row + 1 ] [ col - 1 ] == '*' )
safe_area ( mine, show, row + 1 , col - 1 ) ;
}
else
{
show[ row] [ col] = get_mine_count ( mine, row, col) + '0' ;
}
}
int get_mine_count ( char mine[ ROWS] [ COLS] , int x, int y) {
return mine[ x - 1 ] [ y - 1 ] + mine[ x - 1 ] [ y] + mine[ x - 1 ] [ y + 1 ] + mine[ x] [ y - 1 ] + mine[ x] [ y + 1 ] + mine[ x + 1 ] [ y - 1 ] + mine[ x + 1 ] [ y] + mine[ x + 1 ] [ y + 1 ] - 8 * '0' ;
}
void find_mine ( char mine[ ROWS] [ COLS] , char show[ ROWS] [ COLS] , int row, int col) {
int x = 0 ;
int y = 0 ;
while ( 1 ) {
printf ( "请输入要排查的坐标:>\n" ) ;
scanf ( "%d %d" , & x, & y) ;
if ( x >= 1 && x <= row && y >= 1 && y <= col) {
if ( mine[ x] [ y] == '1' ) {
printf ( "该地点是雷,游戏结束!\n" ) ;
show_board ( mine, ROW, COL) ;
break ;
}
else {
safe_area ( mine, show, x, y) ;
int total = win_con ( show, ROW, COL) ;
if ( EAST_COUNT == total) {
printf ( "恭喜你,排雷成功\n" ) ;
show_board ( show, ROW, COL) ;
break ;
}
show_board ( show, ROW, COL) ;
}
}
else {
printf ( "坐标非法\n" ) ;
}
}
}
void menu ( ) {
printf ( "************************\n" ) ;
printf ( "************************\n" ) ;
printf ( "******** 1.play ********\n" ) ;
printf ( "******** 0.exit ********\n" ) ;
printf ( "************************\n" ) ;
printf ( "************************\n" ) ;
}
void game ( ) {
char mine[ ROWS] [ COLS] = { 0 } ;
char show[ ROWS] [ COLS] = { 0 } ;
init_board ( mine, ROWS, COLS, '0' ) ;
init_board ( show, ROWS, COLS, '*' ) ;
set_mine ( mine, ROW, COL) ;
show_board ( show, ROW, COL) ;
find_mine ( mine, show, ROW, COL) ;
}
int main ( ) {
int input = 0 ;
srand ( ( unsigned int ) time ( NULL ) ) ;
do {
menu ( ) ;
printf ( "请选择:>" ) ;
scanf ( "%d" , & input) ;
switch ( input)
{
case 1 :
printf ( "扫雷\n" ) ;
game ( ) ;
break ;
case 0 :
printf ( "退出游戏" ) ;
break ;
default :
printf ( "选择错误" ) ;
break ;
}
} while ( input) ;
return 0 ;
}