65.9K
CodeProject is changing. Read more.
Home

Minesweeper Game Code Using C# OOP Console Application

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0 vote)

May 21, 2024

CPOL

2 min read

viewsIcon

4240

downloadIcon

131

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.

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().

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.

 
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:

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.