private BoardCoordinate? FindNextFreeSpace(CellStatus[,] workingSet, BoardCoordinate cellToStartSearch, ShipDirection direction, int shipLength)
        {
            // the free space must be a cluster of cells with 'Free' status, such that
            // they can be contiguous and none of the cells of the new ship lands on
            // a 'not available for placement' or 'ship part' field.
            for (var row = cellToStartSearch.Row; row < _gameConfiguration.BoardSize; row++)
            {
                for (var column = cellToStartSearch.Column; column < _gameConfiguration.BoardSize; column++)
                {
                    if (workingSet[row, column] == CellStatus.Free)
                    {
                        if (direction == ShipDirection.Horizontal)
                        {
                            // first - check if there is enough room to place the ship in the current row/column
                            if (column + shipLength > _gameConfiguration.BoardSize - 1)
                                continue;

                            // next, check if the entire span of the next few cells is available
                            var spanAvailable = true;
                            for (var c = column; c < column + shipLength; c++)
                            {
                                if (workingSet[row, c] != CellStatus.Free)
                                {
                                    spanAvailable = false;
                                    break;
                                }
                            }
                            if (!spanAvailable)
                                continue;
                        }
                        else
                        {
                            if (row + shipLength > _gameConfiguration.BoardSize - 1)
                                continue;

                            var spanAvailable = true;
                            for (var r = row; r < row + shipLength; r++)
                            {
                                if (workingSet[r, column] != CellStatus.Free)
                                {
                                    spanAvailable = false;
                                    break;
                                }
                            }
                            if (!spanAvailable)
                                continue;
                        }

                        return new BoardCoordinate(row, column);
                    }
                }
            }

            return null;
        }
        private void PlaceShipOnMap(CellStatus[,] workingSet, int lengthOfNextShipToPosition, ShipDirection directionOfPositioning, BoardCoordinate position)
        {
            // first mark everything on and around cells where the ship will be as unavailable
            var startingCoordinateX = Math.Max(position.Column - 1, 0);
            var startingCoordinateY = Math.Max(position.Row - 1, 0);
            var endCoordinateX = directionOfPositioning == ShipDirection.Horizontal ?
                Math.Min(position.Column + lengthOfNextShipToPosition, _gameConfiguration.BoardSize - 1) :
                Math.Min(position.Column + 1, _gameConfiguration.BoardSize - 1);
            var endCoordinateY = directionOfPositioning == ShipDirection.Vertical ?
                Math.Min(position.Row + lengthOfNextShipToPosition, _gameConfiguration.BoardSize - 1) :
                Math.Min(position.Row + 1, _gameConfiguration.BoardSize - 1);

            for (var row = startingCoordinateY; row <= endCoordinateY; row++)
                for (var column = startingCoordinateX; column <= endCoordinateX; column++)
                    workingSet[row, column] = CellStatus.NotAvailableForPlacement;

            //Debug.WriteLine(RepresentWorkingSet(workingSet));

            // next mark where the ship actually is
            for (int i = 0; i < lengthOfNextShipToPosition; i++)
            {
                var row = directionOfPositioning == ShipDirection.Horizontal ? position.Row : Math.Min(position.Row + i, _gameConfiguration.BoardSize - 1);
                var column = directionOfPositioning == ShipDirection.Vertical ? position.Column : Math.Min(position.Column + i, _gameConfiguration.BoardSize - 1);

                workingSet[row, column] = CellStatus.ShipPart;
            }

            // finally, add the positioned ship to the collection of positioned ships
            var shipsPositionedSoFar = _shipPositioningParametersPerShipLevel.Count;
            var currentlyPositionedShipPositioningParameters = new ShipPositioningParameters
            {
                ShipCoordinate = position,
                ShipDirection = directionOfPositioning
            };
            _shipPositioningParametersPerShipLevel.Add(shipsPositionedSoFar, currentlyPositionedShipPositioningParameters);

            //Debug.WriteLine(RepresentWorkingSet(workingSet));
        }
 public void Add(BoardCoordinate coordinate)
 {
     _coordinates.Add(coordinate);
 }
            public string ValidateShape()
            {
                // Skip ships with single mast
                if (_coordinates.Count == 1)
                    return null;

                foreach (BoardCoordinate coordinate in _coordinates)
                {
                    var verticalNeighbours = new BoardCoordinate[2]
                    {
                        // Top
                        new BoardCoordinate(coordinate.Row - 1, coordinate.Column),
                        // Bottom
                        new BoardCoordinate(coordinate.Row + 1, coordinate.Column),
                    };

                    var horizontalNeighbours = new BoardCoordinate[2]
                    {
                        // Left side
                        new BoardCoordinate(coordinate.Row, coordinate.Column - 1),
                        // Right side
                        new BoardCoordinate(coordinate.Row, coordinate.Column + 1),
                    };

                    bool vertical = _coordinates.Any(c => verticalNeighbours.Contains(c));
                    bool horizontal = _coordinates.Any(c => horizontalNeighbours.Contains(c));

                    if (!vertical && !horizontal)
                        return "Found neighbour in the corner";

                    if (vertical && horizontal)
                        return "Found both vertical and horizontal neighbour";

                    // If there is no information wheather the group is horizontal or vertical, just check
                    // one of the neighbours first
                    if (_direction.HasValue == false)
                        _direction = vertical ? ShipDirection.Vertical : ShipDirection.Horizontal;

                    if (_direction == ShipDirection.Vertical && horizontal)
                        return "This coordinate has horizontal neighbours, but the rest of the group is vertical";
                    if (_direction == ShipDirection.Horizontal && vertical)
                        return "This coordinate has vertical neighbours, but the rest of the group is horizontal";
                }

                return null;
            }
        private void AddCoordinateAndNeighboursToGroup(
            BoardCoordinate startCoordinate, 
            Dictionary<BoardCoordinate, int> groups,
            int currentGroup)
        {
            groups[startCoordinate] = currentGroup;

            HashSet<BoardCoordinate> possibleNeighbours = new HashSet<BoardCoordinate>();
            for (int i = 0; i < 3; i++)
            {
                // Left side
                possibleNeighbours.Add(new BoardCoordinate(startCoordinate.Row - 1 + i, startCoordinate.Column - 1));
                // Right side
                possibleNeighbours.Add(new BoardCoordinate(startCoordinate.Row - 1 + i, startCoordinate.Column + 1));
                // Top side
                possibleNeighbours.Add(new BoardCoordinate(startCoordinate.Row - 1, startCoordinate.Column - 1 + i));
                // Bottom side
                possibleNeighbours.Add(new BoardCoordinate(startCoordinate.Row + 1, startCoordinate.Column - 1 + i));
            }

            foreach (BoardCoordinate coordinate in possibleNeighbours)
            {
                if (BoardRepresentation.Contains(coordinate) && !groups.ContainsKey(coordinate))
                {
                    AddCoordinateAndNeighboursToGroup(coordinate, groups, currentGroup);
                }
            }
        }