/// <summary>
        /// Player removes follower from tile.
        /// </summary>
        /// <param name="player">Color of player making the move.</param>
        /// <param name="coords">Coordinates of tile where follower is placed.</param>
        /// <exception cref="GameException">Exeption describing the error.</exception>
        public void RemoveFollower(PlayerColor player, Coords coords)
        {
            // Check if correct player is making the request
            if (CurrentGameState.Params.PlayerOrder[CurrentGameState.CurrentPlayerIndex] != player)
            {
                throw new NotOnMoveException();
            }

            // Check if it is correct time to place a tile
            if (CurrentGameState.CurrentPhase != GamePhase.TILE_PLACED)
            {
                throw new WrongMovePhaseException();
            }

            // Retrieve the follower placement
            var correctFollowers = CurrentGameState.PlacedFollowers.Where(f => f.Color == player && f.TileCoords == coords).ToList();

            // Check if exactly one place is found
            if (correctFollowers.Count() != 1)
            {
                throw new IncorrectFollowerCoordsException();
            }

            // Retrieve the score
            var score = GridExplorer.GetPointsForFollower(CurrentGameState.Grid, coords, correctFollowers[0].RegionId, forceOpen: false);

            // Remove the follower
            CurrentGameState.PlacedFollowers.Remove(correctFollowers[0]);
            CurrentGameState.Grid[coords].PlacedFollower = null;
            CurrentGameState.Scores[player] += score;
            FollowerRemovedEvent.Invoke(player, coords, score);

            // Also finish the move
            FinishMove();
        }
        /// <summary>
        /// Player places tile on a grid.
        /// </summary>
        /// <param name="player">Color of player making the move.</param>
        /// <param name="tile">Tile that is being placed.</param>
        /// <param name="coords">Coordinates of tile.</param>
        /// <param name="orientation">Orientation of the tile.</param>
        /// <exception cref="GameException">Exeption describing the error.</exception>
        public void PlaceTile(PlayerColor player, TileScheme tile, Coords coords, TileOrientation orientation)
        {
            // Check if the first tile is being placed
            if (player == PlayerColor.NONE && (CurrentGameState.CurrentPhase == GamePhase.BEFORE_START || _TileSetType != TileSetType.STANDARD))
            {
                // Check the tile placement
                GridExplorer.CheckTilePlacement(CurrentGameState.Grid, tile, coords, orientation, first: true);

                if (_TileSetType == TileSetType.STANDARD_PASSIVE)
                {
                    ((StandardPassiveTileSetSupplier)CurrentGameState.TileSupplier).DiscardTile(tile);
                }

                CurrentGameState.Grid.Add(coords, new TilePlacement()
                {
                    Orientation = orientation,
                    Scheme      = tile
                });
                TilePlacedEvent.Invoke(PlayerColor.NONE, tile, coords, orientation);

                return;
            }

            // Check if correct player is making the request
            if (CurrentGameState.Params.PlayerOrder[CurrentGameState.CurrentPlayerIndex] != player)
            {
                throw new NotOnMoveException();
            }

            // Check if it is correct time to place a tile
            if (CurrentGameState.CurrentPhase != GamePhase.TILE_SELECTED)
            {
                throw new WrongMovePhaseException();
            }

            // Check if tile is the same
            if ((_TileSetType == TileSetType.STANDARD) && !CurrentGameState.CurrentTile.IsSameAs(tile))
            {
                throw new WrongTileException();
            }

            // Check the tile placement
            GridExplorer.CheckTilePlacement(CurrentGameState.Grid, tile, coords, orientation, first: false);

            // Place the tile
            CurrentGameState.Grid.Add(coords, new TilePlacement()
            {
                Orientation = orientation,
                Scheme      = tile
            });
            CurrentGameState.CurrentCoords = coords;
            CurrentGameState.CurrentPhase  = GamePhase.TILE_PLACED;
            TilePlacedEvent.Invoke(player, tile, coords, orientation);

            if (_TileSetType == TileSetType.STANDARD_PASSIVE)
            {
                ((StandardPassiveTileSetSupplier)CurrentGameState.TileSupplier).DiscardTile(tile);
            }
        }
        /// <summary>
        /// Player places follower on a region.
        /// </summary>
        /// <param name="player">Color of player making the move.</param>
        /// <param name="coords">Coordinates of tile.</param>
        /// <param name="regionId">Region ID on tile.</param>
        /// <exception cref="GameException">Exeption describing the error.</exception>
        public void PlaceFollower(PlayerColor player, Coords coords, int regionId)
        {
            // Check if correct player is making the request
            if (CurrentGameState.Params.PlayerOrder[CurrentGameState.CurrentPlayerIndex] != player)
            {
                throw new NotOnMoveException();
            }

            // Check if it is correct time to place a tile
            if (CurrentGameState.CurrentPhase != GamePhase.TILE_PLACED)
            {
                throw new WrongMovePhaseException();
            }

            // Check if coordinates of tile follower corresponds to the current tile coordinates
            if (CurrentGameState.CurrentCoords != coords)
            {
                throw new IncorrectFollowerCoordsException();
            }

            // Check if player has any unoccupied follower
            if (CurrentGameState.PlacedFollowers.Where(f => f.Color == player).Count() >= CurrentGameState.Params.FollowerAmount)
            {
                throw new NoFollowerAvailableException();
            }

            // Check follower placement (whether region is occupied)
            if (GridExplorer.IsRegionOccupied(CurrentGameState.Grid, coords, regionId))
            {
                throw new AlreadyOccupiedException();
            }

            // Place the follower
            var tileInfo = CurrentGameState.Grid[coords];

            tileInfo.PlacedFollower = new FollowerPlacement()
            {
                Color      = player,
                RegionId   = regionId,
                TileCoords = coords
            };

            // Add follower placement info to the list
            CurrentGameState.PlacedFollowers.Add(tileInfo.PlacedFollower);

            // Invoke the event
            FollowerPlacedEvent.Invoke(player, coords, regionId);

            // Also finish the move
            FinishMove();
        }
        /// <summary>
        /// Finishes the game.
        /// </summary>
        private void FinishGame()
        {
            // Retrieve remaining followers
            FollowerPlacement[] remaining = new FollowerPlacement[CurrentGameState.PlacedFollowers.Count];
            CurrentGameState.PlacedFollowers.CopyTo(remaining);

            // Remove remaining followers
            foreach (var fp in remaining)
            {
                // Retrieve the score
                var score = GridExplorer.GetPointsForFollower(CurrentGameState.Grid, fp.TileCoords, fp.RegionId, forceOpen: true);

                // Remove the follower

                CurrentGameState.Scores[fp.Color] += score;

                FollowerRemovedEvent.Invoke(fp.Color, fp.TileCoords, score);
            }

            // Notify about end of the game and change state type
            CurrentGameState.CurrentPhase = GamePhase.GAME_OVER;
            GameEndedEvent.Invoke();
        }