Click here to Skip to main content
15,918,808 members
Articles / Programming Languages / C#
Tip/Trick

Minesweeper Game Code Using C# OOP Console Application

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
21 May 2024CPOL2 min read 3.4K   112   1   1
This article describe how to write a code using C# to build a game

Introduction

This article describe how to create a Minesweeper using C# with taking into consideration the basics of object oriented concepts.

How the Game works

Minesweeper is single-player logic-based computer game played on rectangular board whose object is to locate a predetermined number of randomly-placed "mines" in the shortest possible time by clicking on "safe" squares while avoiding the squares with mines. If the player clicks on a mine, the game ends. Otherwise, a number between 0 and 8 is displayed that identifies the total number of mines present in the eight neighboring squares. Therefore, finding a square containing "8" indicated that all eight adjacent squares contain mines, while if a zero (displayed as a blank) is uncovered, there are no mines in the surrounding squares. A square suspected of containing a mine may be marked with flag.

The Cell

First the cell, every cell has properties:

Position: row, col.

IsMine: boolean, IsRevealed: boolean, IsFlagged = bool.

AdjacentMines: in case the cell is not flagged, or is not a mine mine, then it contain a number that describe the number of mines around.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Minesweeper
{
    class Cell
    {
        public int Row { get; }
        public int Col { get; }
        public bool IsMine { get; set; }
        public bool IsRevealed { get; set; }
        public bool IsFlagged { get; set; }
        public int AdjacentMines { get; set; }

        public Cell(int row, int col)
        {
            Row = row;
            Col = col;
            IsMine = false;
            IsRevealed = false;
            IsFlagged = false;
            AdjacentMines = 0;
        }

        public override string ToString()
        {
            if (IsFlagged)
                return "F";
            if (!IsRevealed)
                return "?";
            if (IsMine)
                return "*";
            return AdjacentMines > 0 ? AdjacentMines.ToString() : " ";
        }
    }
}

The Board 

The board should have a size, mines number, and grid of cells.

When the board is created, PlaceMines() will place the mines randomly.

When the board is created, if the cell is not mine, then give it a number and this number is nothing if there are no mines around, or a number of mines around, CalculateAdjacentMine() will find the adjacent number.

Every time a cell revealed is empty and it is not a mine, then reveal the cell that is next to it in case it is safe also, for this RevealAljacentCells() will reveal them.

You can flag and unflag a cell by using FlagCell().

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Minesweeper
{
    class Board
    {
        public int Size { get; }
        public int NumMines { get; }
        public Cell[,] Grid { get; }

        public Board(int size, int numMines)
        {
            Size = size;
            NumMines = numMines;
            Grid = new Cell[size, size];

            for (int row = 0; row < size; row++)
            {
                for (int col = 0; col < size; col++)
                {
                    Grid[row, col] = new Cell(row, col);
                }
            }

            PlaceMines();
            CalculateAdjacentMines();
        }

        private void PlaceMines()
        {
            var rand = new Random();
            int minesPlaced = 0;
            while (minesPlaced < NumMines)
            {
                int row = rand.Next(Size);
                int col = rand.Next(Size);
                var cell = Grid[row, col];
                if (!cell.IsMine)
                {
                    cell.IsMine = true;
                    minesPlaced++;
                }
            }
        }

        private void CalculateAdjacentMines()
        {
            for (int row = 0; row < Size; row++)
            {
                for (int col = 0; col < Size; col++)
                {
                    if (!Grid[row, col].IsMine)
                    {
                        Grid[row, col].AdjacentMines = CountAdjacentMines(row, col);
                    }
                }
            }
        }

        private int CountAdjacentMines(int row, int col)
        {
            int count = 0;
            for (int r = Math.Max(0, row - 1); r <= Math.Min(Size - 1, row + 1); r++)
            {
                for (int c = Math.Max(0, col - 1); c <= Math.Min(Size - 1, col + 1); c++)
                {
                    if (Grid[r, c].IsMine)
                    {
                        count++;
                    }
                }
            }
            return count;
        }

        public void RevealCell(int row, int col)
        {
            var cell = Grid[row, col];
            if (cell.IsRevealed || cell.IsFlagged)
                return;
            cell.IsRevealed = true;
            if (cell.AdjacentMines == 0 && !cell.IsMine)
            {
                RevealAdjacentCells(row, col);
            }
        }

        private void RevealAdjacentCells(int row, int col)
        {
            for (int r = Math.Max(0, row - 1); r <= Math.Min(Size - 1, row + 1); r++)
            {
                for (int c = Math.Max(0, col - 1); c <= Math.Min(Size - 1, col + 1); c++)
                {
                    if (!Grid[r, c].IsRevealed)
                    {
                        RevealCell(r, c);
                    }
                }
            }
        }

        public void FlagCell(int row, int col)
        {
            var cell = Grid[row, col];
            if (!cell.IsRevealed)
            {
                cell.IsFlagged = !cell.IsFlagged;
            }
        }

        public override string ToString()
        {
            var boardStr = "";
            for (int row = 0; row < Size; row++)
            {
                for (int col = 0; col < Size; col++)
                {
                    boardStr += Grid[row, col] + " ";
                }
                boardStr += Environment.NewLine;
            }
            return boardStr;
        }
    }
}

Game Initializer

In order to create a new game, MinesweeperGame take a game size and a number of mines and generate a random board.

On every revealCell() the function should check if there was a gameOver, or a win situation.

C#
 
namespace Minesweeper
{
    class MinesweeperGame
    {
        private Board _board;
        public bool _gameOver;
        private bool _win;

        public MinesweeperGame(int size = 10, int numMines = 10)
        {
            _board = new Board(size, numMines);
            _gameOver = false;
            _win = false;
        }

        public void RevealCell(int row, int col)
        {
            if (_gameOver)
                return;
            _board.RevealCell(row, col);
            var cell = _board.Grid[row, col];
            if (cell.IsMine)
            {
                _gameOver = true;
                _win = false;
            }
            else
            {
                CheckWinCondition();
            }
        }

        public void FlagCell(int row, int col)
        {
            if (!_gameOver)
            {
                _board.FlagCell(row, col);
            }
        }

        private void CheckWinCondition()
        {
            foreach (var cell in _board.Grid)
            {
                if (!cell.IsRevealed && !cell.IsMine)
                {
                    return;
                }
            }
            _gameOver = true;
            _win = true;
        }

        public override string ToString()
        {
            return _board.ToString();
        }

        public void PrintStatus()
        {
            if (_gameOver)
            {
                Console.WriteLine(_win ? "Congratulations! You've won!" : "Game Over! You hit a mine!");
            }
            else
            {
                Console.WriteLine("Game in progress...");
            }
        }
    }
}

Program.cs

If the game is not over, then ask for the user to input the action (reveal or flag) in the row, column they chose:

C#
using Minesweeper;
 

class Program
{
    static void Main(string[] args)
    {
        var game = new MinesweeperGame(size: 5, numMines: 5);

        while (!game._gameOver)
        {
            Console.Clear();
            Console.WriteLine(game);
            game.PrintStatus();
            Console.Write("Enter 'r' to reveal or 'f' to flag, followed by row and column: ");
            var input = Console.ReadLine().Split();
            if (input[0] == "r")
            {
                game.RevealCell(int.Parse(input[1]), int.Parse(input[2]));
            }
            else if (input[0] == "f")
            {
                game.FlagCell(int.Parse(input[1]), int.Parse(input[2]));
            }
        }
        Console.Clear();
        Console.WriteLine(game);
        game.PrintStatus();
    }
}

Conclusion

This was a basic code abstracting the main objects of the game, please let me know in the comment what you think about it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Lebanon Lebanon
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionmineswepper Pin
Member 1531459122-May-24 4:58
Member 1531459122-May-24 4:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.