扫雷游戏程序的编写


在电脑还是Windows xp和Windows 7的时代,想必很多人都一定会接触到一款很经典的游戏,扫雷游戏。简单的界面,易上手的难度,不用上网即可游玩的模式,让它大受欢迎。虽然现在Windows 10系统上已不再默认安装扫雷游戏,但我们却可以通过自己编写代码的方式,来回味我们曾经游玩时的感受。但由于本人目前能力有限,仅能实现由键盘输入的方式,来实现位置的确认。

大概思路

首先,我们需要明确扫雷游戏的规则。我们通过键盘输入坐标的方式,来确定位置排雷,若此位置有雷,则游戏结束,若此位置没有雷,则游戏继续,并显示周围地雷的个数。若我们将所有没有地雷的地方确认后,埋有地雷的所有地方也都知道了,则玩家胜利。
然后,我们可以去思考如何去实现了。因为地雷的数据不能让玩家在游玩时知道,且玩家输入的数据需要存储,同时显示在面板上的数据应该是周围地雷的数量,两者并不相同,基于以上两个原因,我们需要创建两个二维数组,一个用于显示未排雷的地方和周围地雷的数量,命名为show_board,另一个用于存储地雷的位置,命名为mine_board。
但还有个很关键的地方,若我们想展示出一个66的面板,我们是该创建两个66的二维数组吗?这样虽然是最符合我们最直接的想法,但随之而来的问题也有了。若玩家输入的坐标位于面板的边框,如最上面的一行,我们在计算周围地雷数量的时候,则不需要检测上面一行的数据,但若是在最下面一行,则不需要检测下面的一行,甚至若是在左上角,则上面和左边的一行都不需要检测了,这会对我们编写检测周围地雷数的代码有很大的麻烦。因此我们需要在外面再加上一圈,则创建一个8*8的数组,在显示和存储时,都会方便不少。
接着,我们可以编写我们需要的功能函数了。我们需要的功能函数应该有:显示目录的函数Menu,埋下地雷的函数SetMines,显示面板的函数ShowBoard,计算周围地雷数量的函数CountMines,执行游戏相关命令的函数Game。当我们梳理出思路后,便可以上手编写程序了。

功能函数

显示目录的函数Menu

该函数用于向玩家提供游玩或者退出的选项。

void Menu()
{
	printf("########################\n");
	printf("# 1. Play       0.Exit #\n");
	printf("########################\n");
}

埋下地雷的函数SetMines

取出两个随机数作为坐标,将其所指向的位置赋值,则为埋下地雷。同时,定义宏定义,将地雷的个数NUM定义为20,方便后期程序维护时修改。

#define NUM 20
static void SetMines(char board[][COL], int row, int col)
{
	int count = NUM;
	while (count){
		int x = rand() % (row - 2) + 1;
		int y = rand() % (col - 2) + 1;//找到一个随机的位置
		if (board[x][y] == '0'){
			board[x][y] = '1';//埋下地雷
			count--;
		}
	}
}

显示面板的函数ShowBoard

将show_board中所有值通过循环显示出来。因为每行都要显示出表的下边框,为了方便和美观,单独写一个函数ShowLine来显示出下边框。定义两个宏定义ROW和COL来表示创建的数组的行和列。

#define ROW 8
#define COL 8

static void ShowBoard(char board[][COL], int row, int col)
{
	printf("     ");
	for (int i = 1; i <= (col - 2); i++){
		printf("%d   ", i);
	}
	printf("\n");
	ShowLine(col);
	for (int i = 1; i <= (row - 2); i++){
		printf("%-3d|", i);
		for (int j = 1; j <= (col - 2); j++){
			printf(" %c |", board[i][j]);
		}
		printf("\n");
		ShowLine(col);
	}
}
static void ShowLine(int col)
{
	for (int i = 0; i <= (col - 2); i++){
		printf("----");
	}
	printf("\n");
}

计算周围地雷数量的函数CountMines

此函数则为将所指向的坐标的座位8个位置的数据全部相加,则为周围所有地雷的数量。因为一个位置在没有埋下地雷的时候,其所显示的值为0,在埋下地雷后,显示的值被改为1,将8个位置的值相加,但我们存入的值为char型,所以还需要减去7个‘0’的值,才能真正表示出周围地雷的数量。

static char CountMines(char board[][COL], int x, int y)
{
	return board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] + \
		board[x][y + 1] + board[x + 1][y + 1] + board[x + 1][y] + \
		board[x + 1][y - 1] + board[x][y - 1] - 7 * '0';
}

执行游戏相关命令的函数Game

首先我们我们需要向玩家展示出面板,让玩家输入一个坐标,然后判断此坐标是否合法,则判断此坐标是否超出面板限制的范围,和判断是否已被占用,若出现以上两种情况,则需要显示出相关的提示语句。为了玩家查看提示语句方便,则将提示语句放在下一次显示面板钱,同时使用选择语句,来显示不同的提示语句。若坐标合法,则判断此坐标是否有雷,有雷则游戏结束,没有雷则将坐标所只想到的位置赋值。当面板上已无除地雷外的位置时,游戏胜利。

#define ROW 8
#define COL 8
#define STYLE '?'
#define NUM 20

void Game()
{
	srand((unsigned long)time(NULL));

	char show_board[ROW][COL];
	char mine_board[ROW][COL];

	memset(show_board, STYLE, sizeof(show_board));
	memset(mine_board, '0', sizeof(mine_board));//初始化show_board和mine_board

	SetMines(mine_board, ROW, COL);

	int count = (ROW - 2)*(COL - 2) - NUM;//计算除地雷外位置的总数
	int error = 0;
	while (count){
		system("cls");
		if (error == 1){
			printf("Postion Error!\n");
		}//玩家输入的坐标超出范围
		else if(error == 2){
			printf("Postion Is not *\n");
		}//玩家输入的坐标已被占用
		ShowBoard(show_board, ROW, COL);
		printf("Please Enter Your Postion<x,y># ");
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);
		if (x < 1 || x > ROW - 2 || y < 1 || y > COL - 2){
			error = 1;
			continue;
		}
		if (show_board[x][y] != STYLE){
			error = 2;
			continue;
		}
		if (mine_board[x][y] == '1'){
			printf("game over!\n");
			ShowBoard(mine_board, ROW, COL);
			break;//排雷失败,结束游戏
		}
		error = 0;
		show_board[x][y] = CountMines(mine_board, x, y);
		count--;
	}
	if (!count){
		printf("You win!\n");//排雷成功
	}
}

主调函数main

先显示目录,让玩家输入其想执行的操作,并执行相应的命令。

int main()
{
	int quit = 0;
	int select = 0;
	while (!quit){
		Menu();
		printf("Please Enter# ");
		scanf("%d", &select);
		switch (select){
		case 1:
			Game();
			break;
		case 0:
			quit = 1;
			break;
		default:
			printf("Postion Error, Try Again!\n");
			break;
		}
	}

	printf("byebye!\n");

	system("pause");
	return 0;
}

至此,所有三子棋的程序都已经编写完毕,后面为将前面所有代码合在一起的结果。

合集

合集中game.h文件中,主要为宏定义和函数的声明;game.c文件中为所有功能函数的编写;main.c文件中为主调函数的编写。

clear_mine.h文件

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>

#define ROW 8
#define COL 8
#define STYLE '?'
#define NUM 20

extern void Game();
extern void Menu();

clear_mine.c文件

#include "clear_mine.h"

void Menu()
{
	printf("########################\n");
	printf("# 1. Play       0.Exit #\n");
	printf("########################\n");
}

static void SetMines(char board[][COL], int row, int col)
{
	int count = NUM;
	while (count){
		int x = rand() % (row - 2) + 1;
		int y = rand() % (col - 2) + 1;
		if (board[x][y] == '0'){
			board[x][y] = '1';
			count--;
		}
	}
}

static void ShowLine(int col)
{
	for (int i = 0; i <= (col - 2); i++){
		printf("----");
	}
	printf("\n");
}

static void ShowBoard(char board[][COL], int row, int col)
{
	printf("     ");
	for (int i = 1; i <= (col - 2); i++){
		printf("%d   ", i);
	}
	printf("\n");
	ShowLine(col);
	for (int i = 1; i <= (row - 2); i++){
		printf("%-3d|", i);
		for (int j = 1; j <= (col - 2); j++){
			printf(" %c |", board[i][j]);
		}
		printf("\n");
		ShowLine(col);
	}
}

static char CountMines(char board[][COL], int x, int y)
{
	return board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] + \
		board[x][y + 1] + board[x + 1][y + 1] + board[x + 1][y] + \
		board[x + 1][y - 1] + board[x][y - 1] - 7 * '0';
}

void Game()
{
	srand((unsigned long)time(NULL));

	char show_board[ROW][COL];
	char mine_board[ROW][COL];

	memset(show_board, STYLE, sizeof(show_board));
	memset(mine_board, '0', sizeof(mine_board));

	SetMines(mine_board, ROW, COL);

	int count = (ROW - 2)*(COL - 2) - NUM;
	int error = 0;
	while (count){
		system("cls");
		if (error == 1){
			printf("Postion Error!\n");
		}
		else if(error == 2){
			printf("Postion Is not *\n");
		}
		ShowBoard(show_board, ROW, COL);
		printf("Please Enter Your Postion<x,y># ");
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);
		if (x < 1 || x > ROW - 2 || y < 1 || y > COL - 2){
			error = 1;
			continue;
		}
		if (show_board[x][y] != STYLE){
			error = 2;
			continue;
		}
		if (mine_board[x][y] == '1'){
			printf("game over!\n");
			ShowBoard(mine_board, ROW, COL);
			break;
		}
		error = 0;
		show_board[x][y] = CountMines(mine_board, x, y);
		count--;
	}
	if (!count){
		printf("You win!\n");
	}
}

main.c文件

#include "clear_mine.h"
int main()
{
	int quit = 0;
	int select = 0;
	while (!quit){
		Menu();
		printf("Please Enter# ");
		scanf("%d", &select);
		switch (select){
		case 1:
			Game();
			break;
		case 0:
			quit = 1;
			break;
		default:
			printf("Postion Error, Try Again!\n");
			break;
		}
	}

	printf("byebye!\n");
	system("pause");
	return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值