/// <summary> /// constructor. this builds a fresh map using the same dimensions as given by the parameters and populates it with the /// same number of mines given. if the number of mines can't be less than 1, and there has to be at least one non-mine cell. /// </summary> /// <param name="width">how wide each row in the map should be</param> /// <param name="height">how tall each column in the map should be</param> /// <param name="mines">how many mines should be in this map. this constructor ensures there is at least /// one mine and one non-mine cell in the map.</param> public MinesweeperMap(int width, int height, int mines) { numCells = width * height; rowLength = width; flaggedCells = 0; revealedCells = 0; cells = new MinesweeperCell[numCells]; // populate the new map with cells for (int idx = 0; idx < numCells; idx++) { cells[idx] = new MinesweeperCell(); } // if the desired number of mines is too great or too little, the real number of mines needs to be capped. // there must be at least one mine and one non-mine cell. if (mines < 1) { numMines = 1; } else if (mines > numCells - 1) { numMines = numCells - 1; } else { numMines = mines; } }
/// <summary> /// reveals a given cell if it hasn't been revealed already. if it has been flagged or already revealed, nothing happens. /// throws GameOverException if the revealed cell is a mine or if the game has been won, with a message of either "won" or "lost". /// </summary> /// <param name="x">the x coordinate of the targeted cell.</param> /// <param name="y">the y coordinate of the targeted cell.</param> public void RevealCell(int x, int y) { MinesweeperCell cell = cells[x + (y * rowLength)]; // flagged cells cannot be revealed, need to check if the cell is flagged first and do nothing else if it is. // revealed cells don't need to be revealed again either, so we skip those too. if (!cell.isFlagged && !cell.isVisible) { cell.isVisible = true; revealedCells++; // if the cell is a mine, the player has lost. if (cell.hasMine) { throw new GameOverException("lost"); } // if the cell was the last non-mine cell, the player has won. if (revealedCells == numCells - numMines) { throw new GameOverException("won"); } // if this cell has no neighboring mines, we can safely reveal all neighboring cells for the player automatically. else if (cell.neighboringMines == 0) { RevealNeighbors(x, y); } } }
/// <summary> /// private helper method. this makes the targeted cell a mine and increments the mine counts of all its neighbors. /// does nothing if the cell already has a mine. /// </summary> /// <param name="x">the x coordinate of the targeted cell.</param> /// <param name="y">the y coordinate of the targeted cell.</param> private void SetMine(int x, int y) { MinesweeperCell target = cells[x + (y * rowLength)]; // only proceed if this cell doesn't have a mine already to avoid incorrectly incrementing neighbors if (!target.hasMine) { target.hasMine = true; foreach ((int x, int y)position in GetCellNeighborPositions(x, y)) { GetCell(position.x, position.y).neighboringMines++; } } }
/// <summary> /// flags a given cell if it isn't flagged, or unflags a cell if it is flagged. /// </summary> /// <param name="x">the x coordinate of the targeted cell.</param> /// <param name="y">the y coordinate of the targeted cell.</param> public void MarkCell(int x, int y) { MinesweeperCell target = cells[x + (y * rowLength)]; if (target.isFlagged) { target.isFlagged = false; flaggedCells--; } else { target.isFlagged = true; flaggedCells++; } }