Location: PHPKode > projects > PHP-Minesweeper > php-minesweeper-master/lib/Minesweeper/Grid.php
<?php
namespace Minesweeper;

class Grid {

	/**
	 * @var  array   array containing the rows and columns (e.g. [8][8])
	 */
	private $grid = array();

	/**
	 * @var  boolean  Whether the game is over. Defaults FALSE
	 */
	private $game_over = FALSE;

	/**
	 * @var  boolean  Whether the game has been won by the player
	 */
	private $won_by_player = FALSE;

	/**
	 * @var  array   Positions that are already filled randomly.
	 */
	private $occupied_random_positions = array();

	/**
	 * Construct Grid with a grid size.
	 *
	 * @param  int  $rows
	 * @param  int  $columns
	 */
	public function __construct($rows=8, $columns=8)
	{
		// Negative number
		if ( ! is_numeric($rows) OR $rows < 0)
		{
			$rows = 8;
		}

		// Negative number
		if (! is_numeric($columns) OR $columns < 0)
		{
			$columns = 8;
		}

		// Prepare grid array
		for ($row=0; $row < $rows; $row++)
		for ($column=0; $column < $columns; $column++)
		{
			$this->grid[$row][$column] = NULL;
		}

		// Reset grid
		$this->reset();
	}

	/**
	 * Get raw grid
	 *
	 * @return  array  grid
	 */
	public function getGrid()
	{
		return $this->grid;
	}


	/**
	 * Reset the grid so it only consists of empty squares
	 */
	public function reset()
	{
		// Fill whole grid with empty squares
		for ($row=0; $row < $this->getRows(); $row++)
		for ($column=0; $column < $this->getColumns(); $column++)
		{
			$this->addSquare(new Square\EmptySquare, array($row, $column), FALSE);
		}

		// Fill surrounding squares of all squares
		$this->fillSurroundingSquares();
	}

	/**
	 * Get amount of columns in the grid.
	 *
	 * @return  int
	 */
	public function getColumns()
	{
		return count($this->grid[0]);
	}


	/**
	 * Get amount of rows in the grid.
	 *
	 * @return  int
	 */
	public function getRows()
	{
		return count($this->grid);
	}

	/**
	 * Add square to grid. Returns the position on success.
	 *
	 * @param   Square    $square
	 *
	 * @param   array     $position  array with key 0 for row and key 1 for
	 *                               column zero based.
	 *
	 * @param   boolean   $fix_square_surroundings
	 *        Whether to fill square surroundings afterwards. When disabled
	 *        (for improved performance), make sure running
	 *        fillSurroundingSquares()
	 *
	 *
	 * @throws  Exception\InvalidPositionException
	 *
	 * @return  int  position
	 */
	public function addSquare(Square\Square $square,
	                          array $position = NULL,
	                          $fix_square_surroundings=TRUE)
	{
		// Use given position
		if ($position)
		{
			if ( ! $this->isValidPosition($position))
			{
				throw new Exception\InvalidPositionException;
			}

		}
		// Create random position
		else
		{
			$position = $this->createRandomPosition();

			// All places already filled randomly?
			$random_full = sizeof($this->occupied_random_positions) ===
			                   $this->numberOfSquares();

			// Not everything filled randomly. Make sure the random position does
			// not fill a previous random position
			while (in_array($position, $this->occupied_random_positions) AND ! $random_full)
			{
				$position = $this->createRandomPosition();
			}

			// Add position to occupied random positions
			$this->occupied_random_positions[] = $position;
		}

		// Add square to grid
		$this->grid[$position[0]][$position[1]] = $square;

		// Fix positions
		if ($fix_square_surroundings)
		{
			$this->fillSurroundingSquares();
		}

		// Return the position
		return $position;
	}

	/**
	 * Get square from position
	 *
	 * @param  array  $position  array with key 0 for row and key 1 for column
	 *                           zero based.
	 *
	 * @return Square
	 */
	public function getSquare(array $position)
	{
		if( ! $this->isValidPosition($position))
		{
			throw new Exception\InvalidPositionException;
		}

		return $this->grid[$position[0]][$position[1]];
	}


	/**
	 * Reveal position
	 *
	 * @throws  Exception\GameOverException
	 * @throws  Exception\InvalidPositionException
	 * @throws  Exception\SquareAlreadyRevealedException
	 *
	 * @return  boolean  game over
	 */
	public function reveal(array $position)
	{
		// Game over
		if ($this->isGameOver())
		{
			throw new Exception\GameOverException;
		}

		// Not a valid position
		if ( ! $this->isValidPosition($position))
		{
			throw new Exception\InvalidPositionException;
		}

		// Get square
		$square = $this->grid[$position[0]][$position[1]];

		// Already revealed
		if ($square->isRevealed())
		{
			throw new Exception\SquareAlreadyRevealedException;
		}

		// Let the square reveal
		$this->setGameOver($square->reveal());

		// Not gameover and all revealed
		if ( ! $this->isGameOver() AND $this->allRevealed())
		{
			// Player won
			$this->won_by_player = TRUE;

			// Game over
			$this->setGameOver(TRUE);
		}

		// Return whether the game is over
		return $this->isGameOver();
	}

	/**
	 * Get position by square
	 *
	 * @param Square\Square $square
	 */
	public function getPositionBySquare(Square\Square $square)
	{
		for ($row=0; $row < $this->getRows(); $row++)
		for ($column=0; $column < $this->getColumns(); $column++)
		{
			if ($this->getSquare(array($row, $column)) === $square)
			{
				return array($row, $column);
			}
		}
	}

	/**
	 * Get the surrounding squares by position.
	 *
	 * Example grid:
	 *
	 *       0 1 2 3 4 5 6 7
	 *    0  * * * * * * * *
	 *    1  * X X X * * * *
	 *    2  * X x X * * * *
	 *    3  * X X X * * * *
	 *    4  * * * * * * * *
	 *    5  * * * * * * * *
	 *    6  * * * * * * * *
	 *    7  * * * * * * * *
	 *
	 * @param   array  $position
	 *
	 * @throws  Exception\InvalidPositionException
	 *
	 * @return  array  position
	 */
	public function getSurroundingSquaresByPosition(array $position)
	{
		// Not a valid position
		if ( ! $this->isValidPosition($position))
		{
			throw new Exception\InvalidPositionException;
		}

		// Get all surrounding squares (from top left to left)
		$squares = array(
				// Top left
				Arr::get(
					Arr::get($this->grid, ($position[0] - 1)),
					($position[1] - 1))
				,
				// Top
				Arr::get(
					Arr::get($this->grid, ($position[0] - 1)),
					$position[1])
				,
				// Top right
				Arr::get(
					Arr::get($this->grid, ($position[0] - 1)),
					($position[1] + 1))
				,
				// Right
				Arr::get($this->grid[$position[0]], ($position[1] + 1)),

				// Bottom right
				Arr::get(
					Arr::get($this->grid, ($position[0] + 1)),
					($position[1] + 1))
				,
				// Bottom
				Arr::get(
					Arr::get($this->grid, ($position[0] + 1)),
					$position[1])
				,
				// Bottom left
				Arr::get(
					Arr::get($this->grid, ($position[0] + 1)),
					($position[1] - 1))
				,
				// Left
				Arr::get($this->grid[$position[0]], ($position[1] - 1)),
		);

		// Remove NULL values
		$squares = array_values(array_filter($squares, 'is_object'));

		return $squares;
	}

	/**
	 * Returns TRUE when game over
	 *
	 * @return  boolean
	 */
	public function isGameOver()
	{
		return $this->game_over;
	}

	/**
	 * Returns TRUE when the player has won
	 *
	 * @return  boolean
	 */
	public function isWonByPlayer()
	{
		return $this->won_by_player;
	}

	/**
	 * Set whether the game is over
	 *
	 * @param  boolean  $game_over
	 */
	public function setGameOver($game_over)
	{
		$this->game_over = (bool) $game_over;
	}

	/**
	 * Check whether a position array is valid
	 *
	 * @param  array  $position
	 */
	public function isValidPosition(array $position)
	{
		// Valid row, column?
		if ( ! $x = is_numeric(Arr::get($position, 0)) OR
				! $y = is_numeric(Arr::get($position, 1)))
		{
			return FALSE;
		}

		// Position in grid?
		if ( ! array_key_exists($position[0], $this->grid) OR
				! array_key_exists($position[1], $this->grid[$position[0]]))
		{
			return FALSE;
		}

		return TRUE;
	}

	/**
	 * Returns the number of squares
	 *
	 * @param  string  $type
	 *
	 * @return  int  number of squares
	 */
	public function numberOfSquares($type=NULL)
	{
		// No type
		if ( ! $type)
		{
			return $this->getRows() * $this->getColumns();
		}

		// By type
		$number = 0;
		for ($row=0; $row < $this->getRows(); $row++)
		for ($column=0; $column < $this->getColumns(); $column++)
		{
			if ($square = $this->getSquare(array($row, $column)) instanceof $type)
			{
				$number++;
			}
		}

		return $number;
	}

	/**
	 * Test whether all non-gameover squares are revealed
	 */
	public function allRevealed()
	{
		for ($row=0; $row < $this->getRows(); $row++)
		for ($column=0; $column < $this->getColumns(); $column++)
		{
			$square = $this->getSquare(array($row, $column));
			if ( ! $square->isGameOver() AND ! $square->isRevealed())
			{
				RETURN FALSE;
			}
		}

		return TRUE;
	}

	/**
	 * Fills the surrounding squares on all squares within this grid. Use this
	 * function when using addSquare without `$fix_square_surroundings = TRUE`
	 */
	public function fillSurroundingSquares()
	{
		for ($row=0; $row < $this->getRows(); $row++)
			for ($column=0; $column < $this->getColumns(); $column++)
			{
				$position = array($row, $column);

				// Get square first
				$square = $this->getSquare($position);

				// Set surrounding squares to the square
				$square->setSurroundingSquares(
						$this->getSurroundingSquaresByPosition($position)
				);
			}
	}

	/**
	 * Create a new random position
	 *
	 * @return  array  position
	 */
	public function createRandomPosition()
	{
		return array(
				rand(0, $this->getRows() - 1),
				rand(0, $this->getColumns() - 1)
		);
	}
}
Return current item: PHP-Minesweeper