赞
踩
目录
以在线版的扫雷游戏为参考,分析它的功能:扫雷游戏网页版 - Minesweeper
- (1)如果不是雷,则显示周围(8个位置)的雷个数。
-
- (2)如果是雷,游戏失败。
-
- (3)如果把雷之外的所有非雷位置找出,游戏成功。
- test.c:游戏的主逻辑,可用于测试。
-
- game.c:实现游戏的各种函数。
-
- game.c:实现游戏的各种函数的声明、数据的声明。
存储棋盘形状的数据,使用二维数组再好不过了,易理解易操作。需要存储的数据:① 哪些位置是雷(用0和1表示)。② 打印在控制台的扫雷画面(未排雷的位置显示 ' * ' ,已排雷且非雷的位置显示周围雷的个数)。
因为有字符' * ',所以存周围雷个数的数组的元素用char类型;虽然存雷位置的数组都是数字0和1,但是统一起见,元素的类型都定义为char,这样写代码的时候不用区分char还是int,不宜弄错。
虽然说这两种数据可以放到一起,比如非雷位置放9,雷位置放10;排雷时非雷位置(9)可改成周围雷个数(≤8);打印时,等于9或10的位置打印' * ',小于等于8的位置打印周围雷个数。但是这样在打印时会多一些判断,读代码的人理解起来也没有分成两个数组容易。
数据结构如下:
- char mine[ROWS][COLS] = {0};
- char show[ROWS][COLS] = {0};
排雷时,如果找到非雷,则显示周围雷的个数。当这个非雷是二维数组的边缘部分时,统计周围雷的个数会发生越界:
为了解决这个问题,需要把数组的大小增大一圈(两个数组统一增大,免得写代码判断的时候范围不统一,记混了):
game.c
- #define _CRT_SECURE_NO_WARNINGS 1;
- #include "game.h"
-
- void print_menu(){
- printf("******************************\n");
- printf("********* 1.play ********\n");
- printf("********* 2.exit ********\n");
- printf("******************************\n");
- printf("请选择:");
- }
-
- void init_mine(char mine[][EASY_COLS]) {
- for (int i = 0; i < EASY_ROWS; i++)
- for (int j = 0; j < EASY_COLS; j++)
- mine[i][j] = '0';
- }
-
- void init_show(char show[][EASY_COLS]) {
- for (int i = 0; i < EASY_ROWS; i++)
- for (int j = 0; j < EASY_COLS; j++)
- show[i][j] = '*';
- }
-
- void set_mine(char mine[][EASY_COLS]) {
- int r = 0;
- int c = 0;
-
- for (int i = 0; i < EASY_COUNT; i++) {
- r = rand() % EASY_ROW + 1; // 雷要在棋盘里边,不能在增加的一圈里
- c = rand() % EASY_COL + 1;
- if ('0' == mine[r][c]) // 这个位置不是雷,放雷
- mine[r][c] = '1';
- else
- i--; // 这个位置已经是雷了,这轮不算
- }
- }
-
- void print_mine(char mine[][EASY_COLS]) {
- for (int i = 0; i <= EASY_ROW; i++)
- printf("%d ", i);
- printf("\n");
- for (int i = 1; i <= EASY_ROW; i++) {
- printf("%d ", i);
- for (int j = 1; j <= EASY_COL; j++)
- printf("%c ", mine[i][j]);
- printf("\n");
- }
- }
-
- void print_show(char show[][EASY_COLS]) {
- for (int i = 0; i <= EASY_ROW; i++)
- printf("%d ", i);
- printf("\n");
- for (int i = 1; i <= EASY_ROW; i++) {
- printf("%d ", i);
- for (int j = 1; j <= EASY_COL; j++)
- printf("%c ", show[i][j]);
- printf("\n");
- }
- printf("输入坐标:");
- }
-
- void find_mine(char mine[][EASY_COLS], char show[][EASY_COLS]) {
- int x = 0; // 输入的坐标
- int y = 0;
- int count = 0; // 已找到的的非雷
-
- while (count != EASY_ROW * EASY_COL - EASY_COUNT) { // 没有找到所有非雷,游戏就继续
- print_show(show); // 打印棋盘
- scanf("%d %d", &x, &y); // 输入坐标
- system("cls");
- if (mine[x][y] == '1') {
- printf("炸弹!游戏失败!\n");
- break;
- }
- else if (mine[x][y] == '0') {
- char c = count_mine(mine, x, y); // 计算周围雷的个数
- show[x][y] = c;
- count++; //找到一个非雷
- }
- }
- if (count == EASY_ROW * EASY_COL - EASY_COUNT)
- printf("排雷成功!\n");
- }
-
- char count_mine(char mine[][EASY_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' + '0';
- }
game.h
- #pragma once
- #include<stdio.h>
- #include<stdlib.h>
- #include<time.h>
- #include<Windows.h>
-
- #define EASY_ROW 9
- #define EASY_COL 9
-
- #define EASY_COUNT 10
-
- #define EASY_ROWS EASY_ROW+2
- #define EASY_COLS EASY_COL+2
-
- void print_menu();
- void init_mine(char mine[][EASY_COLS]);
- void init_show(char show[][EASY_COLS]);
- void set_mine(char mine[][EASY_COLS]);
- void print_mine(char mine[][EASY_COLS]);
- void print_show(char show[][EASY_COLS]);
- void find_mine(char mine[][EASY_COLS], char show[][EASY_COLS]);
- char count_mine(char mine[][EASY_COLS], int x, int y);
test.c
- #define _CRT_SECURE_NO_WARNINGS 1;
- #include "game.h"
-
- void game() {
- char mine[EASY_ROWS][EASY_COLS]; // 存储生成的雷
- char show[EASY_ROWS][EASY_COLS]; // 存储排雷后雷的个数
-
- init_mine(mine); // 初始化,全放'0'
- init_show(show); // 初始化棋盘,全为'*'
- set_mine(mine); // 随机放雷
- find_mine(mine, show);// 扫雷
- }
-
- int main() {
- int choose = 0;
-
- srand((unsigned int)time(NULL)); // 设置随机种子
- do{
- print_menu(); // 打印主菜单
- scanf("%d", &choose); // 输入选择
- system("cls"); // 清屏
- switch (choose) {
- case 1: // 开始游戏
- game();
- break;
- case 2: // 退出
- break;
- default:
- printf("输入错误,请重新输入。\n");
- }
- } while (choose != 2);
-
- return 0;
- }
代码还有很多需要优化的地方,比如:
代码如下:
game.h:
- #pragma once
- #include<stdio.h>
- #include<stdlib.h>
- #include<time.h>
- #include<Windows.h>
-
- #define EASY_ROW 9
- #define EASY_COL 9
-
- #define EASY_COUNT 10
-
- #define EASY_ROWS EASY_ROW+2
- #define EASY_COLS EASY_COL+2
-
- // 打印字体颜色
- #define NONE "\033[m"
- #define BLUE "\033[0;32;34m"
- #define RED "\033[0;32;31m"
-
- void print_menu();
- void init_mine(char mine[][EASY_COLS]);
- void init_show(char show[][EASY_COLS]);
- void set_mine(char mine[][EASY_COLS]);
- void print_mine(char mine[][EASY_COLS]);
- void print_show(char show[][EASY_COLS]);
- void find_mine(char mine[][EASY_COLS], char show[][EASY_COLS], char flag[][EASY_COLS]);
- char count_mine(char mine[][EASY_COLS], int x, int y);
- void find_other_mine(char mine[][EASY_COLS], char show[][EASY_COLS], int x, int y, int* cnt);
- void mark_mine(char flag[][EASY_COLS]);
game.c:
- #define _CRT_SECURE_NO_WARNINGS 1;
- #include "game.h"
-
- void print_menu(){ // 打印主菜单
- printf("******************************\n");
- printf("********* 1.play ********\n");
- printf("********* 2.exit ********\n");
- printf("******************************\n");
- printf("请选择:");
- }
-
- void init_mine(char mine[][EASY_COLS]) { // 初始化雷信息
- for (int i = 0; i < EASY_ROWS; i++)
- for (int j = 0; j < EASY_COLS; j++)
- mine[i][j] = '0';
- }
-
- void init_show(char show[][EASY_COLS]) { //初始化棋盘
- for (int i = 0; i < EASY_ROWS; i++)
- for (int j = 0; j < EASY_COLS; j++)
- show[i][j] = '*';
- }
-
- void set_mine(char mine[][EASY_COLS]) { // 随即放置雷
- int r = 0;
- int c = 0;
-
- for (int i = 0; i < EASY_COUNT; i++) {
- r = rand() % EASY_ROW + 1; // 雷要在棋盘里边,不能在增加的一圈里
- c = rand() % EASY_COL + 1;
- if ('0' == mine[r][c]) // 这个位置不是雷,放雷
- mine[r][c] = '1';
- else
- i--; // 这个位置已经是雷了,这轮不算
- }
- }
-
- void print_mine(char mine[][EASY_COLS]) { // 打印雷信息
- for (int i = 0; i <= EASY_ROW; i++)
- printf(BLUE"%d "NONE, i);
- printf("\n");
- for (int i = 1; i <= EASY_ROW; i++) {
- printf(BLUE"%d "NONE, i);
- for (int j = 1; j <= EASY_COL; j++)
- printf("%c ", mine[i][j]);
- printf("\n");
- }
- }
-
- void print_show(char show[][EASY_COLS], char flag[][EASY_COLS]) { // 打印棋盘
- for (int i = 0; i <= EASY_ROW; i++)
- printf(BLUE"%d "NONE, i);
- printf("\n");
- for (int i = 1; i <= EASY_ROW; i++) {
- printf(BLUE"%d "NONE, i);
- for (int j = 1; j <= EASY_COL; j++) {
- if('1' == flag[i][j])
- printf(RED"%c "NONE, show[i][j]);
- else
- printf("%c ", show[i][j]);
- }
- printf("\n");
- }
- printf("输入坐标(输入0 0标记雷):");
- }
-
- void find_mine(char mine[][EASY_COLS], char show[][EASY_COLS], char flag[][EASY_COLS]) { // 排雷
- int x; // 输入的坐标
- int y;
- int count = 0; // 已找到的的非雷
-
- while (count != EASY_ROW * EASY_COL - EASY_COUNT) { // 没有找到所有非雷,游戏就继续
- x = 0;
- y = 0;
- while (x == 0 && y == 0) {
- print_show(show, flag); // 打印棋盘
- scanf("%d %d", &x, &y); // 输入坐标
- if (0 == x && 0 == y) // 标记雷
- mark_mine(flag);
- system("cls");
- }
- if (mine[x][y] == '1') { // 碰到炸弹了
- printf("炸弹!游戏失败!\n");
- break;
- }
- else if (mine[x][y] == '0' && show[x][y] == '*') { // (x,y)不是炸弹,并且没有被排查过
- find_other_mine(mine, show, x, y, &count);
- }
- }
- if (count == EASY_ROW * EASY_COL - EASY_COUNT) // 所有非雷点找齐了
- printf("排雷成功!\n");
- }
-
- char count_mine(char mine[][EASY_COLS], int x, int y) { // 计算非雷点(x,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' + '0';
- }
-
- void find_other_mine(char mine[][EASY_COLS], char show[][EASY_COLS], int x, int y, int* cnt) { // 递归,排查的位置不是雷,周围也没有雷,可以展开
- char c = count_mine(mine, x, y); // 计算周围雷的个数
- if(c == '0')
- show[x][y] = ' ';
- else
- show[x][y] = c;
- (*cnt)++; //找到一个非雷
- if (show[x][y] != ' ') { // 周围有雷,就不需要扩展了
- return;
- }
- for (int i = -1; i <= 1; i++) {
- for (int j = -1; j <= 1; j++) {
- int xt = x + i;
- int yt = y + j;
- if (show[xt][yt] != '*' || mine[xt][yt] == '1') // 已经排查过的,就不需要重复扩展了;本身是炸弹的,也不要扩展
- continue;
- if(xt >= 1 && yt >= 1 && xt <= EASY_ROW && yt <= EASY_COL) // 拓展的坐标要在棋盘范围内
- find_other_mine(mine, show, xt, yt, cnt);
- }
- }
- }
-
- void mark_mine(char flag[][EASY_COLS]) {
- int x;
- int y;
- printf("输入雷的坐标:");
- scanf("%d %d", &x, &y); // 输入坐标
- flag[x][y] = '1';
- }
test.c:
- #define _CRT_SECURE_NO_WARNINGS 1;
- #include "game.h"
-
- void game() {
- char mine[EASY_ROWS][EASY_COLS]; // 存储生成的雷
- char show[EASY_ROWS][EASY_COLS]; // 存储排雷后雷的个数
- char flag[EASY_ROWS][EASY_COLS]; // 屏幕上,标记的雷
-
- init_mine(mine); // 初始化,全放'0'
- init_show(show); // 初始化棋盘,全为'*'
- init_mine(flag); // 初始化全'0','0' 表示未标记,‘1’表示已标记
- set_mine(mine); // 随机放雷
- find_mine(mine, show, flag);// 扫雷
- }
-
- int main() {
- int choose = 0;
-
- srand((unsigned int)time(NULL)); // 设置随机种子
- do{
- print_menu(); // 打印主菜单
- scanf("%d", &choose); // 输入选择
- system("cls"); // 清屏
- switch (choose) {
- case 1: // 开始游戏
- game();
- break;
- case 2: // 退出
- break;
- default:
- printf("输入错误,请重新输入。\n");
- }
- } while (choose != 2);
-
- return 0;
- }
效果展示:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。