Implementation of minesweeping game (C language detailed version)

catalogue

preface

Realization idea

Design of game framework

Game interface design

Implementation of game operation

Function function

Initialization function

Print chessboard

Lay thunder

Determine whether the game is over

Calculate the number of nine palaces of gnere

Extended demining

Mark mine

Check thunder

source file

game.h

game.c

test.c

preface

The original version of minesweeping can be traced back to 1973, a game called "square".

Soon, "square" was rewritten into the game "Rlogic". In "Rlogic", the player's task is to act as US Marine Corps Team members, find a safe route without mines for the command center. If all the roads are blocked by mines, you will lose. Two years later, Tom Anderson wrote the game "mine" on the basis of "Rlogic", which laid the prototype of modern mine clearance game.

In 1981, Robert Dole and Carter Johnson of Microsoft worked in windows 3 1 the game was loaded on the system, and the mine sweeping game was officially promoted all over the world.

This game is played by randomly arranging a certain number of mines in a 9 * 9 (primary), 16 * 16 (intermediate), 16 * 30 (Advanced) or custom size square matrix (10 for primary, 40 for intermediate and 99 for advanced). Players open the boxes one by one to find out all mines as the final goal of the game. If the player opens a box with mines, the game is over.

Realization idea

When we realize the minesweeping game, we should first master its rules of the game.

Taking the following figure as an example, we choose the position of the red circle. If it is not a thunder, the number of all thunder in the nine palace grid centered on it will be displayed in this position. If he is ray, the game is over. If this position shows n, it indicates that there are n mines in the eight positions around him.

 

The following figure is an example

 

The small red flag must be thunder, but the three positions on the right of number 2 are not thunder, but the one in the lower right corner can't judge who is thunder in the two positions above. We can check the position (4, 3) first, and then make further reasoning.

 

After mastering the rules of the game, let's implement this program.

 

Design of game framework

After the program runs, we need to have a main menu. After the game is over, we can return to the main menu and choose to start the game or exit the game.

Game interface design

At the beginning of the game, we should have a minefield interface (here I use the minefield of 9 * 9 lines. If you want to change it, you can modify the values of ROW and COL in the header file), and then there is a prompt to select the operation. In order to make it convenient for players to find the corresponding position, I have made an output of ROW and column numbers here. After each operation, the player uses the system("cls") screen clearing function.

Before outputting the minefield interface, two two-dimensional arrays are used to store the mine position information mine[ROWS][COLS] and the character show[ROWS][COLS] of the output interface respectively. (here, ROWS = ROW + 2 and COLS = COL + 2 are used to prevent boundary array elements from crossing the boundary when calculating the number of mines)

In the end interface, the minelaying map needs to be displayed to the player for success or failure of minesweeping, and the corresponding prompt is given. Here I use the Sleep(3000) function to make the end interface stay for three seconds. (the parameter of Sleep() function is in milliseconds in VC environment, but in Linux environment, it is in seconds, which should be noted here.)

 

Implementation of game operation

Lay thunder

Before conducting mine screening, mines should be randomly arranged in the minefield. (the number of Mines cannot exceed the space in the minefield) the timestamp is used here to randomly generate mines. Obtain the timestamp through time(), and then through srand() rand() The combined use of produces false random number Sequence and arrange the mine.

Check thunder

First of all, it should be clear that only the locations that have not been checked and marked can be checked.

If the location checked is thunder, the game is over.

If the detected location is not a mine, the number of surrounding mines will be displayed at this location.

However, in the mine sweeping game, there will actually be a function of expanding one area. When the number of mines in a circle around the position to be checked is zero, it will continue to check eight positions in a circle around and display the number of mines around those positions.

Mark mine

After judging that a location is a mine, we should mark it to prevent stepping on it next time.

So we design a function to mark thunder.

       1. We can only mark locations that have not been checked

       2. The marked position cannot be checked

       3. The marked position can be unmarked

Judgment of game state

There are only two situations that can end the game when we conduct mine clearance:

       1. If the location of the player's investigation is thunder, the player is killed and the game is over

       2. After the player checks all non mine areas, the player wins and the game ends, that is, the total number of unchecked positions and marked positions is equal to the total number of Mines arranged.

Function function

Initialization function

//One more parameter set is convenient for arranging thunder and printing chessboard
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0, j = 0;
	for (i = 0; i < rows;i++)
	{
		for (j = 0;j < cols;j++)
		{
			arr[i][j] = set;
		}
	}
}

Print chessboard

void ShowBoard(char show[ROWS][COLS], int row, int col)
{
	printf("------------------mine clearance------------------\n");
	int i = 0, j = 0;
	printf("   |");
    //Print line number
	for (i = 1;i <= row;i++)
	{
		printf(" %d ", i);
		if (i<row)
		{
			printf("|");
		}
	}
	printf("\n");
	for (i = 1;i <= row;i++)
	{
		for (j = 0;j <= col;j++)
		{
			printf("---");
			if (j < col)
			{
				printf("|");
			}
		}
        //Print column number
		printf("\n %d |", i);
        //Minefield
		for (j = 1;j <= col;j++)
		{
			printf(" %c ", show[i][j]);
			if (j < col)
			{
				printf("|");
			}
		}
		printf("\n");
	}
	printf("------------------mine clearance------------------\n");
}

Lay thunder

void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = MINES;
	int x = 0, y = 0;
	//The count of each thunder is reduced by 1 until the thunder cloth is set
	while (count)
	{
		x = rand() % row + 1;
		y = rand() % row + 1;
        //It can only be arranged if there is no thunder at this position
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

Determine whether the game is over

//When the player wins, the game returns 0 when the game is over, and 1 when the game is not over
int IsWin(char show[ROWS][COLS], int row, int col)
{
	int i = 0, j = 0;
	int count = 0;
	for (i = 1;i <= row;i++)
	{
		for (j = 1;j <= col;j++)
		{
			//Only when the position is * or #, it will be recorded as the remaining operable area,
			//When the remaining operable area is equal to the number of mines, the game wins
			if (show[i][j] == '*' || show[i][j] == '#')
			{
				count++;
			}
		}
	}
	//If the unopened area and marked area are equal to the number of mines, it returns 0, otherwise it returns 1
	if (count == MINES)
		return 0;
	else
		return 1;
}

Calculate the number of nine palaces of gnere

int GetMines(char mine[ROWS][COLS], int x, int y)
{
    //If it is thunder, the position is character 1, and the number of thunder can be obtained by subtracting eight characters 0
	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';

}

Extended demining

void ExpandMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	//If there is no thunder around the location, search and expand the surrounding 8 blocks
	if (GetMines(mine, x, y) == 0)
	{
		//All expanded positions are set to 0
		show[x][y] = '0';
		int i = 0;
		int j = 0;
		for (i = x - 1; i <= x + 1; i++)
		{
			for (j = y - 1; j <= y + 1; j++)
			{
				//Only when the position is not opened can it be retrieved, and the retrieval position should be within the chessboard
				if (show[i][j] == '*' && i > 0 && i <= ROW && j > 0 && j <= COL)
				{
					ExpandMine(mine, show, i, j);
				}
			}
		}
	}
	else
	{
		//If it is not necessary to expand, the number of nearby mines will be displayed
		show[x][y] = GetMines(mine, x, y) + '0';
	}
}

Mark mine

void SignMine(char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0;
	printf("Please enter coordinates;>");
	scanf("%d %d", &x, &y);
	//Mark mine
	if (show[x][y] == '*')
	{
		show[x][y] = '#';
	}
	//Unmark
	else if (show[x][y] == '#')
	{
		show[x][y] = '*';
	}
	//If this position is opened, it cannot be marked
	else
		printf("Marking failed");
}

Check thunder

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0;
	while (IsWin(show, row, col))
	{
		int input = 0;
		//1. Mine detection
		//2. Marking mine
		//For each operation, you can choose whether to check mines or mark mines
		do
		{
			printf("Please select action 1.Check thunder 2.Mark mine:>");
			scanf("%d", &input);
			switch (input)
			{
				//Check thunder
			case 1:
				printf("Please enter coordinates;>");
				scanf("%d %d", &x, &y);
				if (x > 0 && x <= row && y > 0 && y <= col && show[x][y] != '#')
				{
					if (mine[x][y] == '1')
					{
						system("cls");
						printf("I'm sorry you were killed\n");
						ShowBoard(mine, ROW, COL);
						Sleep(3000);
						//Get blown up and quit
						return ;
					}
					else
					{
						ExpandMine(mine, show, x, y);
						system("cls");
						ShowBoard(show, ROW, COL);
					}
				}
				else
				{
					//If this position is marked, it cannot be checked
					if (show[x][y] == '#')
					{
						printf("This position is marked as mine, please re-enter\n");
					}
					//If the inspection position is out of bounds, an error will be reported
					else
						printf("Illegal coordinates, please re-enter\n");
				}
				break;
				//Mark mine
			case 2:
				SignMine(show, row, col);
				system("cls");
				ShowBoard(show, ROW, COL);
				break;
			default:
				break;
			}
		} while (IsWin(show, row, col));//The cycle ends when the player wins or steps on thunder
	}
	//If the player sweeps the mine successfully, the minelaying map will be displayed
	if (!IsWin(show, row, col))
	{
		system("cls");
		printf("Congratulations on your successful mine clearance!:>\n");
		ShowBoard(mine, ROW, COL);
		Sleep(3000);
	}
}

source file

game.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
//The number of mines shall be less than or equal to ROW*COL
#define MINES 80

//1. Marking
//2. Expand one

//Initialize chessboard
void InitBoard(char arr[ROWS][COLS], int row, int col, char set);

//Print chessboard
void ShowBoard(char show[ROWS][COLS], int row, int col);

//Lay thunder
void SetMine(char show[ROWS][COLS], int row, int col);

//Check thunder
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

//Mark mine
void SignMine(char show[ROWS][COLS], int row, int col);

game.c

#include"game.h"

//Initialize chessboard
//One more parameter set is convenient for arranging thunder and printing chessboard
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0, j = 0;
	for (i = 0; i < rows;i++)
	{
		for (j = 0;j < cols;j++)
		{
			arr[i][j] = set;
		}
	}
}

//Print chessboard
void ShowBoard(char show[ROWS][COLS], int row, int col)
{
	printf("------------------mine clearance------------------\n");
	int i = 0, j = 0;
	printf("   |");
	for (i = 1;i <= row;i++)
	{
		printf(" %d ", i);
		if (i<row)
		{
			printf("|");
		}
	}
	printf("\n");
	for (i = 1;i <= row;i++)
	{
		for (j = 0;j <= col;j++)
		{
			printf("---");
			if (j < col)
			{
				printf("|");
			}
		}
		printf("\n %d |", i);
		for (j = 1;j <= col;j++)
		{
			printf(" %c ", show[i][j]);
			if (j < col)
			{
				printf("|");
			}
		}
		printf("\n");
	}
	printf("------------------mine clearance------------------\n");
}

//Lay thunder
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = MINES;
	int x = 0, y = 0;
	//The count of each thunder is reduced by 1 until the thunder cloth is set
	while (count)
	{
		x = rand() % row + 1;
		y = rand() % row + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

//Determine whether the game is over
int IsWin(char show[ROWS][COLS], int row, int col)
{
	int i = 0, j = 0;
	int count = 0;
	for (i = 1;i <= row;i++)
	{
		for (j = 1;j <= col;j++)
		{
			//Only when the position is * or #, it will be recorded as the remaining operable area,
			//When the remaining operable area is equal to the number of mines, the game wins
			if (show[i][j] == '*' || show[i][j] == '#')
			{
				count++;
			}
		}
	}
	//If the unopened area and marked area are equal to the number of mines, it returns 0, otherwise it returns 1
	if (count == MINES)
		return 0;
	else
		return 1;
}


//Calculate the number of surrounding mines
int GetMines(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';

}

//Extended demining
void ExpandMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	//If there is no thunder around the location, search and expand the surrounding 8 blocks
	if (GetMines(mine, x, y) == 0)
	{
		//All expanded positions are set to 0
		show[x][y] = '0';
		int i = 0;
		int j = 0;
		for (i = x - 1; i <= x + 1; i++)
		{
			for (j = y - 1; j <= y + 1; j++)
			{
				//Only when the position is not opened can it be retrieved, and the retrieval position should be within the chessboard
				if (show[i][j] == '*' && i > 0 && i <= ROW && j > 0 && j <= COL)
				{
					ExpandMine(mine, show, i, j);
				}
			}
		}
	}
	else
	{
		//If it is not necessary to expand, the number of nearby mines will be displayed
		show[x][y] = GetMines(mine, x, y) + '0';
	}
}

//Check thunder
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0;
	while (IsWin(show, row, col))
	{
		int input = 0;
		//1. Mine detection
		//2. Marking mine
		//For each operation, you can choose whether to check mines or mark mines
		do
		{
			printf("Please select action 1.Check thunder 2.Mark mine:>");
			scanf("%d", &input);
			switch (input)
			{
				//Check thunder
			case 1:
				printf("Please enter coordinates;>");
				scanf("%d %d", &x, &y);
				if (x > 0 && x <= row && y > 0 && y <= col && show[x][y] != '#')
				{
					if (mine[x][y] == '1')
					{
						system("cls");
						printf("I'm sorry you were killed\n");
						ShowBoard(mine, ROW, COL);
						Sleep(3000);
						//Get blown up and quit
						return ;
					}
					else
					{
						ExpandMine(mine, show, x, y);
						system("cls");
						ShowBoard(show, ROW, COL);
					}
				}
				else
				{
					//If this position is marked, it cannot be checked
					if (show[x][y] == '#')
					{
						printf("This position is marked as mine, please re-enter\n");
					}
					//If the inspection position is out of bounds, an error will be reported
					else
						printf("Illegal coordinates, please re-enter\n");
				}
				break;
				//Mark mine
			case 2:
				SignMine(show, row, col);
				system("cls");
				ShowBoard(show, ROW, COL);
				break;
			default:
				break;
			}
		} while (IsWin(show, row, col));//The cycle ends when the player wins or steps on thunder
	}
	//If the player sweeps the mine successfully, the minelaying map will be displayed
	if (!IsWin(show, row, col))
	{
		system("cls");
		printf("Congratulations on your successful mine clearance!:>\n");
		ShowBoard(mine, ROW, COL);
		Sleep(3000);
	}
}

//Mark mine
void SignMine(char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0;
	printf("Please enter coordinates;>");
	scanf("%d %d", &x, &y);
	//Mark mine
	if (show[x][y] == '*')
	{
		show[x][y] = '#';
	}
	//Unmark
	else if (show[x][y] == '#')
	{
		show[x][y] = '*';
	}
	//If this position is opened, it cannot be marked
	else
		printf("Marking failed");
}

test.c

#include"game.h"
void game()
{
	char Mine[ROWS][COLS] = { 0 };
	char Show[ROWS][COLS] = { 0 };
	//Initialize the minefield and game interface first
	InitBoard(Mine, ROWS, COLS, '0');
	InitBoard(Show, ROWS, COLS, '*');
	//Lay thunder
	SetMine(Mine, ROW, COL);
	//Display hidden minefields
	ShowBoard(Show, ROW, COL);
    //If cheating is required, the mined area after arrangement can be displayed directly
	/*ShowBoard(Mine, ROW, COL);*/
	//Game operation function
	FindMine(Mine, Show, ROW, COL);
	system("cls");
}
//Main menu
void menu()
{
	printf("+---------------------------+\n");
	printf("|          1. play          |\n");
	printf("|          0. exit          |\n");
	printf("+---------------------------+\n");
}
//Test function
void test()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("Please select:>\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			system("cls");
			game();
			break;
		case 0:
			printf("Exit the game\n");
			break;
		default:
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

This is the end of the article. Thank you for reading.

 

Keywords: C Game Development

Added by altumdesign on Fri, 28 Jan 2022 07:02:48 +0200