Minesweeper Game in C Language

Minesweeper Game in C Language

Function description of the Minesweeper game:

• Implement the classic Minesweeper game using the console

• The game can continue or exit through the menu

• The Minesweeper board is a 9*9 grid

• By default, 10 mines are randomly placed

• Mines can be checked:

◦ If the position is not a mine, it shows how many mines are around

◦ If the position is a mine, the game ends with an explosion

◦ Successfully find all non-mine cells except for the 10 mines, the game ends

test.c // File containing the game's test logic
game.c // File containing the implementation of game functions

game.h // File containing the data types and function declarations needed for the game

Logic starts:

1. Menu

  1. Input 1 to enter the game, input 0 to exit the game, input other numbers to show input error and re-enter
test.c#include "game.h"
int main(){
	menu();
	{
	regain:
		printf("Please enter your choice:");
		int input1;
		scanf("%d", &input1);
		switch (input1)
		{
			case 1:
			{
				printf("Entering the game\n");
				game();
				break;
			}
			case 0:
			{
				printf("Exiting the game\n");
				break;
			}
			default:
			{
				printf("Input error, please re-enter:");
				goto regain;
			}
		}
	}
	return 0;
}
game.c#include "game.h"
void menu(){
	printf("****************\n");
	printf("**** 1.Play ****\n");
	printf("**** 0.Quit ****\n");
	printf("****************\n");
}
game.h#pragma once
#include <stdio.h>
#include "game.h"
// Menu
void menu();

2. Generate a 9X9 game interface

  1. Use a two-dimensional array to implement
  2. Use two boards, one for display and one for setting mines, write a function to initialize the board
  3. Initialize the displayed board<span>char show</span> to all <span>'*'</span>, and initialize the mine board<span>char mine</span> to all<span>0</span>
  4. For convenience in later testing, write the function to print the board first

Added the following code to the test.c file

test.c#include "game.h"
void game(){
	// Two-dimensional array for placing mines
	char mine[ROWS][COLS] = { 0 };
	// Two-dimensional array for the game interface
	char show[ROWS][COLS] = { 0 };
	// Initialize the game interface array to '*'
	set_keyboard(show, ROWS, COLS, '*');
	// Initialize the mine array to '0'
	set_keyboard(mine, ROWS, COLS, '0');
	// Print function
	printf_keyboard(show, ROW, COL);
	printf_keyboard(mine, ROW, COL);
}

Added the following code to the

game.c#include "game.h"
// Initialize the board
void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set){
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}
// Display the board
void printf_keyboard(char board[ROWS][COLS], int row, int col){
	printf("-------Minesweeper--------\n");
	for (int r = 0; r <= row; r++)
	{
		printf("%d ", r);
	}
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

3. Randomly place mines

Use srand((unsigned int) time(NULL)) and rand()

Set mines to 1, mines can only be placed where char mine[x][y] == ‘0’

Added the following to the game.h

#include <time.h>

#include “stdlib.h”

#define MINE 10

void set_mine(char board[ROWS][COLS], int row, int col, int mine);

game.h#pragma once
#include <stdio.h>
#include "game.h"
#include <time.h>
#include "stdlib.h"
#define ROW 9
#define COL 9
#define ROWS ROW+3
#define COLS COL+2
#define MINE 10
// Menu
void menu();
// Initialize the board
void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set);
// Display the board
void printf_keyboard(char board[ROWS][COLS], int row, int col);
// Randomly place mines
void set_mine(char board[ROWS][COLS], int row, int col, int mine);
game.c // Add the following code
// Randomly place mines
void set_mine(char board[ROWS][COLS], int row, int col, int mine){
	srand((unsigned int) time(NULL));
	while (mine)
	{
		int x = (rand() % row) + 1;
		int y = (rand() % col) + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			mine--;
		}
	}
}

Added the following code to the test.c file

test.c
// Randomly place mines
set_mine(mine, ROW, COL, MINE);

4. Mine clearance

  1. Pay attention to the input coordinates, both horizontal and vertical coordinates can only be<span>0~9</span>, other numbers will<span>report an error and re-enter</span>
  2. The cleared coordinates should display the number of surrounding mines, if it is<span>0</span>, expand the surrounding board (using recursion)
  3. If the cleared coordinates are a mine, display<span>Game Over</span>
  4. If the input coordinates have already been checked, display<span>This coordinate has already been cleared</span>
  5. Determine if the game is won,<span>The number of cleared coordinates equals the number of cells minus the mines</span>

Changed the layout of the test.c file to the following

test.cvoid game(){
	// Two-dimensional array for placing mines
	char mine[ROWS][COLS] = { 0 };
	// Two-dimensional array for the game interface
	char show[ROWS][COLS] = { 0 };
	// Initialize the game interface array to '*'
	set_keyboard(show, ROWS, COLS, '*');
	// Initialize the mine array to '0'
	set_keyboard(mine, ROWS, COLS, '0');
	// Randomly place mines
	set_mine(mine, ROW, COL, MINE);
	// Print function
	printf_keyboard(show, ROW, COL);
	// printf_keyboard(mine, ROW, COL);
	// Mine clearance
	move_mine(show, mine, ROW, COL);
}

Added the following code to the game.c file

game.c// Calculate the number of surrounding mines
int Count_mine(char mine[ROWS][COLS], int x, int y){
	return mine[x][y] - '0';
}
// Expand the board----recursion
void Open_keyboard(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y){
	// Write recursion first write the end condition
	// Out of bounds, return
	if ((x > (ROWS - 2)) || (x < 1) || (y > (COLS - 2)) || (y < 1))
	{
		return;
	}
	// If the coordinate has been cleared, return
	if (show[x][y] != '*')
	{
		return;
	}
	// Calculate the number of mines
	int count = 0;
	for (int i = -1; i <= 1; i++)
	{
		for (int j = -1; j <= 1; j++)
		{
			count += Count_mine(mine, x + i, y + j);
		}
	}
	show[x][y] = count + '0';
	// According to the game rules, if the coordinate shows the number of mines is not zero, return
	if (show[x][y] != '0')
	{
		return;
	}
	// Expand mines
	for (int i = -1; i <= 1; i++)
	{
		for (int j = -1; j <= 1; j++)
		{
			Open_keyboard(show, mine, x + i, y + j);
		}
	}
}
// Mine clearance
void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col){
	int x, y;
	while (1)
	{
		printf("Please enter the coordinates to check:");
	regain2:
		scanf("%d %d", &x, &y);
		if ((x >= 1 && x <= 9) && (y >= 1 && y <= 9))
		{
			if (show[x][y] == '*')
			{
				if (mine[x][y] == '0')
				{
					Open_keyboard(show,mine,x,y);
					printf_keyboard(show, ROW, COL);
				}
				else
				{
					printf("Unfortunately, you hit a mine, game over\nThe positions of the mines are:");
					printf_keyboard(mine, ROW, COL);
					break;
				}
			}
			else
			{
				printf("This coordinate has already been checked, please enter another coordinate:");
				goto regain2;
			}
		}
		else
		{
			printf("Input error, please re-enter:");
			goto regain2;
		}
		// Determine win
		int Remove_mine_count = 0;
		for (int i = 1; i <= row; i++)
		{
			for (int j = 1; j <= col; j++)
			{
				if (show[i][j] != '*')
				{
					Remove_mine_count++;
				}
			}
		}
		if (Remove_mine_count == ((ROW * COL) - MINE))
		{
			printf("Congratulations, you have cleared all the mines, you win the game\n");
			printf_keyboard(mine, ROW, COL);
			break;
		}
	}
}

The code in the game.h file remains unchanged

game.h#pragma once
#include <stdio.h>
#include "game.h"
#include <time.h>
#include "stdlib.h"
#define ROW 9
#define COL 9
#define ROWS ROW+3
#define COLS COL+2
#define MINE 10
// Menu
void menu();
// Initialize the board
void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set);
// Display the board
void printf_keyboard(char board[ROWS][COLS], int row, int col);
// Randomly place mines
void set_mine(char board[ROWS][COLS], int row, int col, int mine);
// Mine clearance
void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col);

5. Complete code for the Minesweeper game

test.c

#include "game.h"  // Include the header file required for the Minesweeper game (function declarations, macro definitions, etc.)
// Core logic function of the game, responsible for initializing game data, placing mines, and handling the mine clearance process
void game(){
	// Define a two-dimensional array mine to store the position information of mines ('1' means there is a mine, '0' means no mine)
	// ROWS and COLS are macro definitions, usually larger than the actual game area by 2 (to handle boundary checks and avoid out-of-bounds)
	char mine[ROWS][COLS] = { 0 };
	// Define a two-dimensional array show to display the interface to the player (initially '*', shows surrounding mine count or mines after clearance)
	char show[ROWS][COLS] = { 0 };
	// Initialize the show array, all elements set to '*' (indicating unexplored cells)
	// set_keyboard is a custom function for batch initializing a two-dimensional array
	set_keyboard(show, ROWS, COLS, '*');
	// Initialize the mine array, all elements set to '0' (assume all cells have no mines, later randomly place mines)
	set_keyboard(mine, ROWS, COLS, '0');
	// Randomly place mines in the mine array, MINE is a macro definition (total number of mines)
	// ROW and COL are macro definitions representing the number of rows and columns in the actual game area (smaller than ROWS and COLS by 2)
	set_mine(mine, ROW, COL, MINE);
	// Print the player interface (show array), showing the current unexplored cells (all '*')
	printf_keyboard(show, ROW, COL);
	// Debugging: print the positions of the mines (mine array), will not be shown to the player in the actual game
	// printf_keyboard(mine, ROW, COL);
	// Enter the mine clearance logic, player inputs coordinates, handle the clearance results (show surrounding mine count, hit mine ends, etc.)
	move_mine(show, mine, ROW, COL);
}
// Main function, program entry, responsible for displaying the menu and handling user choices
int main(){
	menu();  // Call the menu function to print the game menu (e.g., "1. Start Game 0. Exit Game")
	{
		// Local scope, isolate internal variables
	regain1:  // Jump label for re-entering input when input error occurs
		printf("Please enter your choice:");  // Prompt user to input option (1 or 0)
		int input1;  // Store user input option
		scanf("%d", &input1);  // Read user input
		// Execute corresponding operation based on user input option
		switch (input1)
		{
			case 1:  // User selects "Start Game"
			{
				printf("Entering the game\n");  // Prompt entering the game
				game();  // Call the game function to start the Minesweeper game logic
				break;  // Exit switch branch
			}
			case 0:  // User selects "Exit Game"
			{
				printf("Exiting the game\n");  // Prompt exiting the game
				break;  // Exit switch branch
			}
			default:  // User input invalid option (not 1 or 0)
			{
				printf("Input error, please re-enter:");  // Prompt input error
				goto regain1;  // Jump to regain1 label, waiting for user input again
			}
		}
	}
	return 0;  // Program ends normally
}

game.c

#include "game.h"  // Include the header file required for the Minesweeper game (macro definitions, function declarations, etc.)
// Print the game menu
void menu(){
	printf("****************\n");
	printf("**** 1.Play ****\n");  // 1 means start game
	printf("**** 0.Quit ****\n");  // 0 means exit game
	printf("****************\n");
}
// Initialize the board (two-dimensional array)
// board: the two-dimensional array to be initialized
// rows, cols: the number of rows and columns of the array
// set: the character to fill for initialization (e.g., '*' or '0')
void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set){
	// Traverse each element of the array, set to the specified character set
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}
// Display the board (print to console)
// board: the two-dimensional array to be displayed (player interface or minefield)
// row, col: the number of rows and columns in the actual game area (excluding boundaries)
void printf_keyboard(char board[ROWS][COLS], int row, int col){
	printf("-------Minesweeper--------\n");
	// Print column numbers (0 to col), convenient for players to locate coordinates
	for (int r = 0; r <= row; r++)
	{
		printf("%d ", r);
	}
	printf("\n");
	// Print each row content (including row number and corresponding cell character)
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);  // Print row number (1 to row)
		// Print each cell of the current row (from the 1st column to the col)
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");  // New line after each row ends
	}
}
// Randomly place mines in the minefield array
// board: the two-dimensional array storing minefield information ('1' means there is a mine, '0' means no mine)
// row, col: the number of rows and columns in the actual game area
// mine: the total number of mines to be placed
void set_mine(char board[ROWS][COLS], int row, int col, int mine){
	srand((unsigned int)time(NULL));  // Initialize random seed to ensure different mine positions each time
	// Loop to place mines until the number of mines is 0
	while (mine)
	{
		// Generate random row coordinate x from 1 to row
		int x = (rand() % row) + 1;
		// Generate random column coordinate y from 1 to col
		int y = (rand() % col) + 1;
		// If the current position has no mine (is '0'), place a mine (set to '1')
		if (board[x][y] == '0')  // Note: the original code had an extra semicolon here, which may be a typo, should actually be removed
		{
			board[x][y] = '1';
			mine--;  // Decrease the number of mines by 1
		}
	}
}
// Calculate the total number of mines in the 8 surrounding directions
// mine: minefield array
// x, y: the coordinates to check
int Count_mine(char mine[ROWS][COLS], int x, int y){
	// Calculate the total number of mines in the 8 adjacent cells around the coordinate (x,y)
	// Principle: Convert the character values of the surrounding 8 cells ('0' means no mine, '1' means there is a mine) to numbers and sum them up
	return (    mine[x - 1][y] +          // Upper cell
			mine[x - 1][y - 1] +      // Upper left cell
			mine[x][y - 1] +          // Left cell
			mine[x + 1][y - 1] +      // Lower left cell
			mine[x + 1][y] +          // Lower cell
			mine[x + 1][y + 1] +      // Lower right cell
			mine[x][y + 1] +          // Right cell
			mine[x - 1][y + 1] -      // Upper right cell
			8 * '0'                   // Subtract the ASCII value of 8 '0's (convert character to number: '0'→0, '1'→1)
	);
}
// Recursively expand the area without mines (when a cell has no surrounding mines, automatically expand all surrounding non-mine cells)
// show: player interface array
// mine: minefield array
// x, y: current coordinates to expand
void Open_keyboard(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y){
	// Recursion end condition 1: Out of bounds (exceeding the actual game area)
	if ((x > (ROWS - 2)) || (x < 1) || (y > (COLS - 2)) || (y < 1))
	{
		return;
	}
	// Recursion end condition 2: This coordinate has been explored (not '*')
	if (show[x][y] != '*')
	{
		return;
	}
	// Calculate the total number of mines around the current coordinate
	int count = 0;
	for (int i = -1; i <= 1; i++)  // Row direction: -1 (up), 0 (current), 1 (down)
	{
		for (int j = -1; j <= 1; j++)  // Column direction: -1 (left), 0 (current), 1 (right)
		{
			count += Count_mine(mine, x + i, y + j);  // Accumulate the number of mines in each surrounding cell
		}
	}
	// Display the number of surrounding mines in the current cell on the player interface (character form, e.g., '0' means no mine)
	show[x][y] = count + '0';
	// Recursion end condition 3: If there are surrounding mines (count≠0), stop expanding
	if (show[x][y] != '0')
	{
		return;
	}
	// If there are no surrounding mines (count=0), recursively expand the surrounding 8 directions
	for (int i = -1; i <= 1; i++)
	{
		for (int j = -1; j <= 1; j++)
		{
			Open_keyboard(show, mine, x + i, y + j);
		}
	}
}
// Core logic of mine clearance
// show: player interface array
// mine: minefield array
// row, col: the number of rows and columns in the actual game area
void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col){
	int x, y;  // Store the coordinates input by the player
	while (1)  // Loop to handle mine clearance operations until the game ends
	{
		printf("Please enter the coordinates to check:");
	regain2:  // Jump label for re-entering input when input error occurs
		scanf("%d %d", &x, &y);  // Read player input coordinates (x for row, y for column)
		// Check if the coordinates are within valid range (1 to row, 1 to col)
		if ((x >= 1 && x <= 9) && (y >= 1 && y <= 9))  // Assume row=col=9, can change to x<=row && y<=col
		{
			// Check if the coordinate has not been cleared (still '*')
			if (show[x][y] == '*')
			{
				// If there is no mine at this position (mine[x][y] is '0')
				if (mine[x][y] == '0')
				{
					Open_keyboard(show, mine, x, y);  // Expand surrounding non-mine area
					printf_keyboard(show, ROW, COL);  // Refresh and display player interface
				}
				// If there is a mine at this position (mine[x][y] is '1')
				else
				{
					printf("Unfortunately, you hit a mine, game over\nThe positions of the mines are:");
					printf_keyboard(mine, ROW, COL);  // Display all mine positions
					break;  // Exit loop, game ends
				}
			}
			// This coordinate has already been checked
			else
			{
				printf("This coordinate has already been checked, please enter another coordinate:");
				goto regain2;  // Jump to regain2, re-enter coordinates
			}
		}
		// Invalid coordinate input (out of range)
		else
		{
			printf("Input error, please re-enter:");
			goto regain2;  // Jump to regain2, re-enter coordinates
		}
		// Determine if the player has won (all non-mine cells have been cleared)
		int Remove_mine_count = 0;  // Record the number of cleared non-mine cells
		for (int i = 1; i <= row; i++)
		{
			for (int j = 1; j <= col; j++)
			{
				if (show[i][j] != '*')  // Non '*' means cleared
				{
					Remove_mine_count++;
				}
			}
		}
		// Winning condition: number of cleared cells = total cells - total mines
		if (Remove_mine_count == ((ROW * COL) - MINE))
		{
			printf("Congratulations, you have cleared all the mines, you win the game\n");
			printf_keyboard(mine, ROW, COL);  // Display all mine positions
			break;  // Exit loop, game ends
		}
	}
}

game.h

#pragma once  // Prevent the header file from being included multiple times (compile only once)
// Include the required standard library header files
#include <stdio.h>    // Provides input and output functions (e.g., printf, scanf)
#include "game.h"     // Include other game-related declarations (note: there may be circular inclusion here, should actually be avoided)
#include <time.h>     // Provides time-related functions (e.g., time, used to initialize random seed)
#include "stdlib.h"   // Provides standard library functions (e.g., rand, srand, used to generate random numbers)
// Macro definitions: core parameters of the game
#define ROW 9        // Number of rows in the actual game area (9 rows)
#define COL 9        // Number of columns in the actual game area (9 columns)
#define ROWS ROW+3   // Total number of rows in the minefield array (larger than the actual number of rows by 3, to handle boundaries, avoid out-of-bounds access)
#define COLS COL+2   // Total number of columns in the minefield array (larger than the actual number of columns by 2, to handle boundaries)
#define MINE 10      // Total number of mines in the game (10 mines)
// Function declarations: declare all functions used in the game (for other files to call)
// Print the game menu (e.g., start/exit options)
void menu();
// Initialize the board array
// Parameters: board- the two-dimensional array to be initialized, rows- number of rows in the array, cols- number of columns in the array, set- character to fill for initialization
void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set);
// Print the displayed board (player interface or minefield)
// Parameters: board- the two-dimensional array to be displayed, row- number of rows in the actual game area, col- number of columns in the actual game area
void printf_keyboard(char board[ROWS][COLS], int row, int col);
// Randomly place mines in the minefield array
// Parameters: board- minefield array, row- number of rows in the actual game area, col- number of columns in the actual game area, mine- number of mines to be placed
void set_mine(char board[ROWS][COLS], int row, int col, int mine);
// Handle the player's mine clearance operation (core game logic)
// Parameters: show- player interface array, mine- minefield array, row- number of rows in the actual game area, col- number of columns in the actual game area
void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col);

Leave a Comment