MineSweeper C#
Tags: csharp, minesweepe, showdev
Published: 2022-11-13 15:14:46

## Introduction


[Github repo](https://github.com/KDevZilla/MineSweeper)
[Download Exe](https://github.com/KDevZilla/MineSweeper/releases/download/v1.0.0/MineSweeper.zip)

![MineSweeper](https://raw.githubusercontent.com/KDevZilla/ImageUpload/main/MineSweeper/MineSweeper_01.png)



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.

```C#
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;
    }
  }

}
```

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

```C#
	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;

	    }
```