DEV Community

Krirk Srithaweewath
Krirk Srithaweewath

Posted on • Updated on

MineSweeper C#

Introduction

Github repo
Download Exe

MineSweeper

I will show you how to develop a MineSweeper game using C#.
The goal of this project is to create a MineSweeper as close to the original using C# WindowsForm.

Rule of MineSweeper.

  1. Click a cell to open it.
  2. If the cell is a mine cell the game is over.
  3. The first cell you click is guaranteed that it will not be a mine cell.
  4. You are given the information from the open cell, the number you see is the number of mine cells around the open cell. To win the game, you need to open all the cells that are not mine cells.

The structure of the program

UI
I use a panel called pnlGame to handle most of the UI in this game.
It contains labels to display cell types.

Board class
int[,] Matrix keeps the cell information.
Boolean[,] IsOpenCell to keep the open/closed state of the cell

ConstCell
Cell has 3 values, Bomb, Blank, HasValue

Game class
Contains the Board object and the status of the game.

The logic behind the game class when the game is created is

  1. Initial Board.
  2. Generate mines on the board randomly.
  3. Loop through all of the board.
  4. if the cell is mine, list all of the neighbor cells (8 adjacent cells next to mine cell)
  5. Loop through all the neighbor cell
  6. if (the neighbor is not mine and does not have the value yet and Random value > 3) set neighbor to be HasValue cell
  7. Loop through all of the cells in the board
  8. if the cell has HasValue type, count the number of mine around the cell (8 adjacent cells next co HasValue cell)

These are the codes.

private void SetBomb(Board pBoard, int NumberofBomb, Position positionDoesnotAllow) {
  int i;
  int j;

  List <Position> listPositionBomb = GetUniqueRandomPosition(pBoard.NoofRow, pBoard.NoofCol, NumberofBomb, positionDoesnotAllow);
  int CellValue = 0;
  for (i = 0; i < NumberofBomb; i++) {
    pBoard.Matrix[listPositionBomb[i].Row, listPositionBomb[i].Col] = ConstCell.Bomb;
  }
}
private void SetHasValueCell(Board pBoard) {
  int i;
  int j;

  for (i = 0; i < pBoard.NoofRow; i++) {
    for (j = 0; j < pBoard.NoofCol; j++) {
      if (pBoard.Matrix[i, j] != ConstCell.Bomb) {
        continue;
      }
      List <Position> listPositionhasValue = GetNeighbourCell(i, j, pBoard.NoofRow, pBoard.NoofCol);
      foreach(Position posi in listPositionhasValue) {
        int NieghbourCellValue = pBoard.Matrix[posi.Row, posi.Col];
        if (NieghbourCellValue.In(ConstCell.Bomb, ConstCell.HasValue)) {
          continue;
        }
        int RandomValue = Random(1, 9);
        if (RandomValue > 3) {
          pBoard.Matrix[posi.Row, posi.Col] = ConstCell.HasValue;
        }
      }

    }
  }
}
public void GenerateMine(Board pBoard) {
  int i;

  int j;

  InitialBoard(pBoard);

  SetBomb(pBoard, this.NumberofMines, this.PostionThatMineMustNotExist);
  if (PostionThatMineMustNotExist.Row == -1) {
    this.board.Matrix[0, 0] = ConstCell.Bomb;
  }

  SetHasValueCell(pBoard);
  for (i = 0; i < pBoard.NoofRow; i++) {
    for (j = 0; j < pBoard.NoofCol; j++) {
      if (pBoard.Matrix[i, j].In(ConstCell.Bomb, ConstCell.Blank)) {
        continue;
      }

      List <Position> listNeighbour = GetNeighbourCell(i, j, pBoard.NoofRow, pBoard.NoofCol);
      int NumberBomb = 0;
      foreach(Position p in listNeighbour) {
        if (pBoard.Matrix[p.Row, p.Col] == ConstCell.Blank) {
          continue;
        }

        if (pBoard.Matrix[p.Row, p.Col] == ConstCell.Bomb) {
          NumberBomb++;
        }
      }
      pBoard.Matrix[i, j] = NumberBomb;
    }
  }

}
Enter fullscreen mode Exit fullscreen mode

The logic behind the cell click event is

  1. If the Cell is not opened yet, just open it.
  2. If it is a blank cell, open the neighbor blank cell.
  3. after the cell is opened the game object will calculate the result if the game state is finished if the first click got a mine cell (first-time bad luck) just restart the game without losing. (Our game tries to prevent a first-time bad luck). else show the result
    private void UserClick(Game pGame, int Row, int Column) {
      if (MineSweep.GameState == GameStateEnum.End) {
        return;
      }

      bool IsThisCellAlreayOpen = pGame.board.IsOpenCell[Row, Column];
      if (IsThisCellAlreayOpen) {
        return;
      }

      pGame.OpenCell(Row, Column);
      if (this.timer1.Enabled == false) {
        StartTimer();
      }
      int CellValue = pGame.board.Matrix[Row, Column];
      if (pGame.board.Matrix[Row, Column] == ConstCell.Blank) {
        pGame.OpenNeighborBlankCell(Row, Column);
      }

      if (pGame.GameState == GameStateEnum.Running) {
        ShowFaceWonder();
        return;
      }

      if (pGame.GameState == GameStateEnum.End) {
        if (!pGame.HasSuccessfulClickthefirstCellWithoutDie) {
          Position ByPassPositionFortheNewGame = new Position(Row, Column);
          NewGame(ByPassPositionFortheNewGame);
          UserClick(MineSweep, Row, Column);
          return;
        }

        StopTimer();
        if (pGame.GameResult == GameResultEnum.Lost) {
          ShowFaceLost();
        } else {
          ShowFaceWon();
          if (MineSweep.GameDifficultLevel == 4) {
            return;
          }

          String message = @ "You broke a record for " + arrDifficultLevel[MineSweep.GameDifficultLevel];
          if (MineSweep.Seconds < score.GetSecond(MineSweep.GameDifficultLevel)) {
            FormEnterNewTimeRecord f = new FormEnterNewTimeRecord();
            f.Message = message;
            f.PreviousRecordName = score.GetName(MineSweep.GameDifficultLevel);
            f.ShowDialog();
            if (f.DialogResult != DialogResult.OK) {
              return;
            }
            score.SetSecond(MineSweep.GameDifficultLevel, MineSweep.Seconds);
            score.SetName(MineSweep.GameDifficultLevel, f.NewName);
            SaveScore();
          }
        }
      }

    }

    public void OpenNeighborBlankCell(int Row, int Column) {
      List < Position > listNeighborRecursive = new List < Position > ();
      HashSet < int > hshNeighborRecursive = new HashSet < int > ();
      GetNeighbourCellRecursive(listNeighborRecursive,
        hshNeighborRecursive,
        Row,
        Column,
        board.NoofRow,
        board.NoofCol,
        ConstCell.Blank);

      listNeighborRecursive.ForEach(x => OpenCell(x.Row, x.Col));
      int i;
      foreach(Position po in listNeighborRecursive) {
        List < Position > listNeighbourNotBlank = GetNeighbourCell(po.Row, po.Col, board.NoofRow, board.NoofCol);
        foreach(Position poNotBlank in listNeighbourNotBlank) {
          if (!board.Matrix[poNotBlank.Row, poNotBlank.Col].IsBetween(1, 8)) {
            continue;
          }
          OpenCell(poNotBlank);
        }
      }
    }

    private List < Position > GetNeighbourCellRecursive(List < Position > lisNeighborAll, HashSet < int > HshNeighborAll, int Row, int Column, int NoofRow, int NoofCol, int CellValueCriteria) {
        List < Position > listNeighbor = GetNeighbourCell(Row, Column, board.NoofRow, board.NoofCol);
        foreach(Position pos in listNeighbor) {

          if (board.Matrix[pos.Row, pos.Col] != CellValueCriteria) {
            continue;
          }
          int HashValue = pos.Row * NoofRow + pos.Col;
          if (HshNeighborAll.Contains(HashValue)) {
            continue;
          }
          HshNeighborAll.Add(HashValue);
          lisNeighborAll.Add(pos.Clone());
          List < Position > listNeighborChild = GetNeighbourCellRecursive(lisNeighborAll,

          }
          return listNeighbor;

        }
Enter fullscreen mode Exit fullscreen mode

Top comments (0)