C语言程序设计——扫雷游戏

简介: C语言程序设计——扫雷游戏

扫雷游戏是以前Windows自带的一款经典益智游戏,游戏玩法简单,不懂的朋友可以去百度然后尝试一下就知道了,现在我们就用C语言来实现一下这款游戏。


首先我们理清整体的思路,可以采用两个二维数组来作为游戏棋盘,一个用来埋雷(只有玩家通过游戏后显示一次),一个作为游戏棋盘,这里可以将埋雷棋盘初始化为全字符0,用字符1表示地雷,将游戏棋盘初始化为全'*'。(注:在确定数组大小时,我们可以在所需棋盘大小的基础上再在周围增加一圈棋盘,即行列都加2,这样便于后续统计扫雷位置周围地雷个数)。


除此之外,我们好需要通过数字、下划线以及一些其他符号来打印一个可视化的棋盘和一个选择菜单。


棋盘、菜单示例


效果比较粗糙,大家可以自己进行调整改进,使棋盘更加美观。


image.png


代码实现:


void Menu()//选择菜单
{
  printf("#######################\n");
  printf("#  1.Play    2.Exit  ##\n");
  printf("#######################\n");
  printf("请输入你的选择: ");
}
void ShowBoard(char board[][COL], int row, int col)//打印棋盘
{
  printf("  ");
  for (int i = 1; i <= row - 2; i++){
  printf("%4d", i);
  }
  printf("\n   ");
  for (int i = 1; i <= row - 2; i++){
  printf(" ___");
  }
  for (int j = 1; j <= col - 2; j++){
  printf("\n");
  printf("%2d", j);
  printf(" |");
  for (int i = 1; i <= row - 2; i++){
    printf("_%c_|", board[j][i]);
  }
  }
  printf("\n");
  return;
}


随机埋雷


首先确定地雷个数,可以采用宏定义NUM,便于我们改变个数而不改变后续的逻辑,然后就可以利用rand函数来产生随机坐标进行埋雷了。代码实现:


void SetMines(char board[][COL], int row, int col){//随机埋雷
  int i = 0;
  while (i < NUM) {
  int _x = rand() % (row-2) + 1;
  int _y = rand() % (col-2) + 1;
  if (board[_x][_y] == BOOM) {
    continue;
  }
  board[_x][_y] = BOOM;
  i++;
  }
}


统计周围地雷个数


int 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][y + 1] + board[x + 1][y - 1] + \
  board[x + 1][y] + board[x + 1][y + 1] - 8 * '0';//('1' + '0' + '0'...'0')- ('0'+'0')
}


判断游戏是否胜利


通过统计游戏棋盘上非字符’*‘的字符个数,若等于棋盘上格子总数减去地雷个数则游戏胜利返回1,反之游戏继续。代码实现:


int WinGame(char board[][COL], int row, int col) {//游戏胜利条件:胜利1 游戏继续0
  int total = (row - 2) * (col - 2) - NUM;
  int flag = 0;
  for (int i = 1; i < row; i++) {
  for (int j = 1; j < col; j++) {
    if ((board[i][j] != '*')) {
    flag++;
    }
  }
  }
  return (flag ==total ) ? 1 : 0;
}


展开安全区域


当我们扫了一个位置后,如果当前位置周围的地雷个数为0,则需要进行向四周展开直至附件有雷的格子,并在当前格子显示附件地雷的个数,增加游戏的可完性,根据需求特点我们可以利用函数递归来实现,那么递归的出口就有两个,一个就是向四周展开都找到了附近有雷的格子(边界除外),另一个就是展开了所有没雷的格子达到了游戏胜利的条件。代码实现:


int SafeBoard(char board[][COL], char mine_board[][COL], int row, int col, int x, int y) {//可展开的安全区域
  int tmp;
  tmp = CountMines(mine_board, x, y);
  if (x >= 1 && x <= row - 2 && y >= 1 && y <= col - 2) {
  if (tmp == 0) {
    board[x][y] = ' ';
    if (mine_board[x - 1][y - 1] != '1' && board[x - 1][y - 1] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x - 1, y - 1);
    }
    if (mine_board[x - 1][y + 1] != '1' && board[x - 1][y + 1] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x - 1, y + 1);
    }
    if (mine_board[x - 1][y] != '1' && board[x - 1][y] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x - 1, y);
    }
    if (mine_board[x][y + 1] != '1' && board[x][y + 1] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x, y + 1);
    }
    if (mine_board[x - 1][y - 1] != '1' && board[x][y - 1] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x, y - 1);
    }
    if (mine_board[x + 1][y] != '1' && board[x + 1][y] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x + 1, y);
    }
    if (mine_board[x + 1][y + 1] != '1' && board[x + 1][y + 1] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x + 1, y + 1);
    }
    if (mine_board[x + 1][y - 1] != '1' && board[x + 1][y - 1] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x + 1, y - 1);
    }
  }
  else {
    board[x][y] = tmp + '0';
    if (WinGame(board,ROW,COL)) {
    return 0;
    }
  }
  }
  return 1;
}


游戏主体


最后就是游戏的整个控制函数了,只需判断玩家输入的坐标是否合法进行对应的操作和提示即可。


代码实现:


int main()
{
  srand((unsigned long)time(NULL));
  int quit = 0;
  while (!quit) {
  Menu();
  int select = 0;
  scanf("%d", &select);
  switch (select) {
  case 1:
    Game();
    break;
  case 2:
    quit = 1;
    break;
  default:
    printf("输入有误, 重新选择!\n");
    break;
  }
  }
  printf("再见!\n");
  return 0;
}
void Game(){
  char show_board[ROW][COL];//游戏棋盘
  char mine_board[ROW][COL];//炸弹棋盘
  memset(show_board, '*', sizeof(show_board));//棋盘全‘*’初始化
  memset(mine_board, '0', sizeof(mine_board));
  SetMines(mine_board, ROW, COL);//随机埋雷
  while (1) {
  int x = 0;
  int y = 0;
  ShowBoard(show_board, ROW, COL);//打印棋盘
  printf("请输入坐标<x, y> ");
  scanf("%d %d", &x, &y);
  system("cls");
  if (!(x >= 1 && x <= ROW - 2 && y >= 1 && y <= COL - 2)) {
    printf("扫雷位置不合法,请重新输入!\n");
    continue;
  }
  if (show_board[x][y] != '*') {
    printf("扫雷的位置已经被排除,请重新输入!\n");
    continue;
  }
  if (mine_board[x][y] == '1') {
    printf("对不起,你被炸死了!\n");
    break;
  }
  int tmp = SafeBoard(show_board, mine_board, ROW, COL, x, y);
  if (tmp == 0) {
    ShowBoard(mine_board, ROW, COL);
    printf("恭喜你,游戏通过!\n");
    return ;
  }
  else {
    continue;
  }
  }
}


完整代码


MineSweeper.h


#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#define ROW 12
#define COL 12
#define NUM 10
#define BOOM '1'
void Game();


game.c


int main()
{
  srand((unsigned long)time(NULL));
  int quit = 0;
  while (!quit) {
  Menu();
  int select = 0;
  scanf("%d", &select);
  switch (select) {
  case 1:
    Game();
    break;
  case 2:
    quit = 1;
    break;
  default:
    printf("输入有误, 重新选择!\n");
    break;
  }
  }
  printf("再见!\n");
  return 0;
}


MineSweeper.c


#include "MineSweeper.h"
void SetMines(char board[][COL], int row, int col){//随机埋雷
  int i = 0;
  while (i < NUM) {
  int _x = rand() % (row-2) + 1;
  int _y = rand() % (col-2) + 1;
  if (board[_x][_y] == BOOM) {
    continue;
  }
  board[_x][_y] = BOOM;
  i++;
  }
}
void ShowBoard(char board[][COL], int row, int col)//打印棋盘
{
  printf("  ");
  for (int i = 1; i <= row - 2; i++){
  printf("%4d", i);
  }
  printf("\n   ");
  for (int i = 1; i <= row - 2; i++){
  printf(" ___");
  }
  for (int j = 1; j <= col - 2; j++){
  printf("\n");
  printf("%2d", j);
  printf(" |");
  for (int i = 1; i <= row - 2; i++){
    printf("_%c_|", board[j][i]);
  }
  }
  printf("\n");
  return;
}
int 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][y + 1] + board[x + 1][y - 1] + \
  board[x + 1][y] + board[x + 1][y + 1] - 8 * '0';//('1' + '0' + '0'...'0')- ('0'+'0')
}
int WinGame(char board[][COL], int row, int col) {//游戏胜利条件:胜利1 游戏继续0
  int total = (row - 2) * (col - 2) - NUM;
  int flag = 0;
  for (int i = 1; i < row; i++) {
  for (int j = 1; j < col; j++) {
    if ((board[i][j] != '*')) {
    flag++;
    }
  }
  }
  return (flag ==total ) ? 1 : 0;
}
int SafeBoard(char board[][COL], char mine_board[][COL], int row, int col, int x, int y) {//可展开的安全区域
  int tmp;
  tmp = CountMines(mine_board, x, y);
  if (x >= 1 && x <= row - 2 && y >= 1 && y <= col - 2) {
  if (tmp == 0) {
    board[x][y] = ' ';
    if (mine_board[x - 1][y - 1] != '1' && board[x - 1][y - 1] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x - 1, y - 1);
    }
    if (mine_board[x - 1][y + 1] != '1' && board[x - 1][y + 1] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x - 1, y + 1);
    }
    if (mine_board[x - 1][y] != '1' && board[x - 1][y] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x - 1, y);
    }
    if (mine_board[x][y + 1] != '1' && board[x][y + 1] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x, y + 1);
    }
    if (mine_board[x - 1][y - 1] != '1' && board[x][y - 1] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x, y - 1);
    }
    if (mine_board[x + 1][y] != '1' && board[x + 1][y] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x + 1, y);
    }
    if (mine_board[x + 1][y + 1] != '1' && board[x + 1][y + 1] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x + 1, y + 1);
    }
    if (mine_board[x + 1][y - 1] != '1' && board[x + 1][y - 1] == '*') {
    SafeBoard(board, mine_board, ROW, COL, x + 1, y - 1);
    }
  }
  else {
    board[x][y] = tmp + '0';
    if (WinGame(board,ROW,COL)) {
    return 0;
    }
  }
  }
  return 1;
}
void Game(){
  char show_board[ROW][COL];//游戏棋盘
  char mine_board[ROW][COL];//炸弹棋盘
  memset(show_board, '*', sizeof(show_board));//棋盘全‘*’初始化
  memset(mine_board, '0', sizeof(mine_board));
  SetMines(mine_board, ROW, COL);//随机埋雷
  while (1) {
  int x = 0;
  int y = 0;
  ShowBoard(show_board, ROW, COL);//打印棋盘
  printf("请输入坐标<x, y> ");
  scanf("%d %d", &x, &y);
  system("cls");
  if (!(x >= 1 && x <= ROW - 2 && y >= 1 && y <= COL - 2)) {
    printf("扫雷位置不合法,请重新输入!\n");
    continue;
  }
  if (show_board[x][y] != '*') {
    printf("扫雷的位置已经被排除,请重新输入!\n");
    continue;
  }
  if (mine_board[x][y] == '1') {
    printf("对不起,你被炸死了!\n");
    break;
  }
  int tmp = SafeBoard(show_board, mine_board, ROW, COL, x, y);
  if (tmp == 0) {
    ShowBoard(mine_board, ROW, COL);
    printf("恭喜你,游戏通过!\n");
    return ;
  }
  else {
    continue;
  }
  }
}


相关文章
|
1天前
|
存储 算法 C语言
【C 言专栏】用 C 语言开发游戏的实践
【5月更文挑战第5天】本文探讨了使用C语言开发游戏的实践,包括选择适合的游戏类型(如贪吃蛇、俄罗斯方块),设计游戏框架、图形界面和逻辑,以及音效添加。文章还强调了性能优化、测试调试、跨平台挑战及未来发展趋势。对于热衷于C语言的开发者,这是一次挑战与乐趣并存的探索之旅。
【C 言专栏】用 C 语言开发游戏的实践
|
8天前
|
C语言
PTA 浙大版《C语言程序设计(第3版)》题目集 习题8-4 报数 (20分)
PTA 浙大版《C语言程序设计(第3版)》题目集 习题8-4 报数 (20分)
|
8天前
|
C语言
C语言 浙大版《C语言程序设计(第3版)》题目集 练习8-8 移动字母 (10分)
C语言 浙大版《C语言程序设计(第3版)》题目集 练习8-8 移动字母 (10分)
|
8天前
|
C语言
浙大版《C语言程序设计(第3版)》题目集 练习8-2 计算两数的和与差 (10分)
浙大版《C语言程序设计(第3版)》题目集 练习8-2 计算两数的和与差 (10分)
|
8天前
|
C语言
pta浙大版《C语言程序设计(第3版)》 习题6-4 使用函数输出指定范围内的Fibonacci数 (20分)
pta浙大版《C语言程序设计(第3版)》 习题6-4 使用函数输出指定范围内的Fibonacci数 (20分)
|
8天前
|
C语言
PTA 浙大版《C语言程序设计(第3版)》题目集 习题8-6 删除字符 (20分)
PTA 浙大版《C语言程序设计(第3版)》题目集 习题8-6 删除字符 (20分)
|
8天前
|
C语言
pta 浙大版《C语言程序设计(第3版)》题目集 习题6-6 使用函数输出一个整数的逆序数 (20分)
pta 浙大版《C语言程序设计(第3版)》题目集 习题6-6 使用函数输出一个整数的逆序数 (20分)
|
8天前
|
C语言
(浙大版《C语言程序设计(第3版)》 习题6-5 使用函数验证哥德巴赫猜想 (20分)
(浙大版《C语言程序设计(第3版)》 习题6-5 使用函数验证哥德巴赫猜想 (20分)
|
14天前
|
C语言
以c语言为基础实现的简易扫雷游戏(游戏代码附在文章最后,如有需要请自取)
以c语言为基础实现的简易扫雷游戏(游戏代码附在文章最后,如有需要请自取)
36 1
|
25天前
|
C语言
爱上C语言:扫雷小游戏,展开一片功能实现
爱上C语言:扫雷小游戏,展开一片功能实现
爱上C语言:扫雷小游戏,展开一片功能实现