Exemplo n.º 1
0
 internal void PlaceShip(BoardIndex ix, int shipLength, Direction direction)
 {
     for (var i = 0; i < shipLength; i++)
     {
         this[ix] = SquareContent.Ship;
         ix.TryNext(direction, out ix);
     }
 }
Exemplo n.º 2
0
        /// <inheritdoc/>
        public bool TryPlaceShip(BoardIndex ix, int shipLength, Direction direction)
        {
            if (!CanPlaceShip(ix, shipLength, direction, (c, r) => this[new BoardIndex(c, r)] == SquareContent.Water))
            {
                return(false);
            }

            PlaceShip(ix, shipLength, direction);
            return(true);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Finds out if a ship can be places at given coordinates.
        /// </summary>
        /// <param name="ix">Coordinates where the ship should be placed</param>
        /// <param name="shipLength">Length of the ship (max. 10)</param>
        /// <param name="direction">Direction of the ship</param>
        /// <param name="isWater">Callback to find out if a given suqare is water before placing the ship</param>
        /// <returns>
        /// <c>true</c> if the ship can be placed here, otherwise <c>false</c>.
        /// </returns>
        /// <remarks>
        /// Each ship occupies a number of consecutive squares on the battleship board, arranged either horizontally
        /// or vertically. The ships cannot overlap (i.e., only one ship can occupy any given square on the board).
        /// There has to be at least one square with water between each ship (i.e., ships must not be place
        /// directly adjacent to each other).
        /// </remarks>
        /// <exception cref="ArgumentOutOfRangeException">Thrown in case of invalid argument</exception>
        internal static bool CanPlaceShip(BoardIndex ix, int shipLength, Direction direction, IsWater isWater)
        {
            #region Check input parameter
            // This is a public method, so we have to check that parameters
            // contain valid values.

            // Check if ship isn't too long
            if (shipLength > 10)
            {
                throw new ArgumentOutOfRangeException(nameof(shipLength), "Maximum length of ship is 10");
            }

            if (!Enum.IsDefined(typeof(Direction), direction))
            {
                throw new ArgumentOutOfRangeException(nameof(direction), "Unknown direction");
            }
 /// <summary>
 /// Initializes a new <see cref="BoardIndexRange"/> instance.
 /// </summary>
 /// <param name="from">Starting point</param>
 /// <param name="to">End point</param>
 /// <remarks>
 /// If <paramref name="from"/> is greater than <paramref name="to"/>, from and to are switched
 /// so that <see cref="Length"/> is always positive.
 /// </remarks>
 public BoardIndexRange(BoardIndex from, BoardIndex to)
 {
     if (from.Column == to.Column)
     {
         this.from = to.Row > from.Row ? from : to;
         this.to   = to.Row > from.Row ? to : from;
     }
     else if (from.Row == to.Row)
     {
         this.from = to.Column > from.Column ? from : to;
         this.to   = to.Column > from.Column ? to : from;
     }
     else
     {
         throw new ArgumentException("Row or column or from and to have to match (e.g. width = 1)");
     }
 }
 /// <inheritdoc/>
 public SquareContent this[BoardIndex ix]
 {
     get => boardContent[ix];
Exemplo n.º 6
0
        /// <summary>
        /// Find out whether there is a ship on a given location and return its boundaries
        /// </summary>
        /// <param name="board">Board on which to find the ship</param>
        /// <param name="ix">Location to check</param>
        /// <param name="shipRange">Boundaries of the found ship</param>
        /// <returns>
        /// The method returns <see cref="ShipFindingResult.NoShip"/> if there is no ship on the specified cell.
        /// It returns <see cref="ShipFindingResult.PartialShip"/> if there is a ship on the specified cell, but it
        /// cannot be said for sure how long it is (because of <see cref="SquareContent.Unknown"/> cells in front or
        /// behind the ship). If a ship was found and the size of the ship could be determined, the function returns
        /// <see cref="ShipFindingResult.CompleteShip"/>.
        /// </returns>
        public static ShipFindingResult TryFindShip(this IReadOnlyBoard board, BoardIndex ix, out BoardIndexRange shipRange)
        {
            // Note pattern matching

            if (board[ix] is not SquareContent.HitShip and not SquareContent.Ship)
            {
                // No ship at specified index
                shipRange = new BoardIndexRange();
                return(ShipFindingResult.NoShip);
            }

            // Note local method returning tuple

            (BoardIndex ix, bool complete) FindShipEdge(BoardIndex current, Direction direction, bool prev)
            {
                bool canMoveFurther;

                do
                {
                    BoardIndex next;
                    if (prev)
                    {
                        canMoveFurther = current.TryPrevious(direction, out next);
                    }
                    else
                    {
                        canMoveFurther = current.TryNext(direction, out next);
                    }

                    if (canMoveFurther)
                    {
                        current = next;
                    }
                }while (canMoveFurther && board[current] is not SquareContent.Water and not SquareContent.Unknown);

                var complete = board[current] is not SquareContent.Unknown;

                if (board[current] is not SquareContent.Water and not SquareContent.Unknown)
                {
                    return(current, complete);
                }

                if (!prev)
                {
                    return(direction == Direction.Horizontal ? current.PreviousColumn() : current.PreviousRow(), complete);
                }

                return(direction == Direction.Horizontal ? current.NextColumn() : current.NextRow(), complete);
            }

            // Note local method receiving tuple

            bool TryDirection(Direction direction, out (BoardIndexRange range, ShipFindingResult complete) result)
            {
                var(beginningIx, beginningComplete) = FindShipEdge(ix, direction, true);
                var(endIx, endComplete)             = FindShipEdge(ix, direction, false);
                result = (new BoardIndexRange(beginningIx, endIx),
                          beginningComplete&& endComplete ? ShipFindingResult.CompleteShip : ShipFindingResult.PartialShip);
                return(result.range.Length > 1);
            }

            // Go left and find first water
            if (TryDirection(Direction.Horizontal, out var resultHorizontal))
            {
                shipRange = resultHorizontal.range;
                return(resultHorizontal.complete);
            }

            // Note discard operator to indicate that result is not relevant

            _         = TryDirection(Direction.Vertical, out var resultVertical);
            shipRange = resultVertical.range;
            return(resultVertical.complete);
        }
        // Discuss: C# deconstructors. Read more at https://docs.microsoft.com/en-us/dotnet/csharp/deconstruct

        /// <summary>
        /// Deconstructs the instance
        /// </summary>
        /// <param name="from">Starting point</param>
        /// <param name="to">End point</param>
        public readonly void Deconstruct(out BoardIndex from, out BoardIndex to) => (from, to) = (this.from, this.to);
Exemplo n.º 8
0
        /// <summary>
        /// Find out whether there is a ship on a given location and return its boundaries
        /// </summary>
        /// <param name="board">Board on which to find the ship</param>
        /// <param name="ix">Location to check</param>
        /// <param name="shipRange">Boundaries of the found ship</param>
        /// <returns>
        /// The method returns <see cref="ShipFindingResult.NoShip"/> if there is no ship on the specified cell.
        /// It returns <see cref="ShipFindingResult.PartialShip"/> if there is a ship on the specified cell, but it
        /// cannot be said for sure how long it is (because of <see cref="SquareContent.Unknown"/> cells in front or
        /// behind the ship). If a ship was found and the size of the ship could be determined, the function returns
        /// <see cref="ShipFindingResult.CompleteShip"/>.
        /// </returns>
        public static ShipFindingResult TryFindShip(this IReadOnlyBoard board, BoardIndex ix, out BoardIndexRange shipRange)
        {
            // Note pattern matching

            if (board[ix] is not SquareContent.HitShip and not SquareContent.Ship and not SquareContent.SunkenShip)
            {
                // No ship at specified index
                shipRange = new BoardIndexRange();
                return(ShipFindingResult.NoShip);
            }

            // Note local method returning tuple

            (BoardIndex ix, bool complete) FindShipEdge(BoardIndex current, Direction direction, bool prev)
            {
                bool canMoveFurther;

                do
                {
                    BoardIndex next;
                    if (prev)
                    {
                        canMoveFurther = current.TryPrevious(direction, out next);
                    }
                    else
                    {
                        canMoveFurther = current.TryNext(direction, out next);
                    }

                    if (canMoveFurther)
                    {
                        current = next;
                    }
                }while (canMoveFurther && board[current] is not SquareContent.Water and not SquareContent.Unknown);

                var complete = board[current] is not SquareContent.Unknown;

                if (board[current] is not SquareContent.Water and not SquareContent.Unknown)
                {
                    return(current, complete);
                }

                if (!prev)
                {
                    return(direction == Direction.Horizontal ? current.PreviousColumn() : current.PreviousRow(), complete);
                }

                return(direction == Direction.Horizontal ? current.NextColumn() : current.NextRow(), complete);
            }

            // Note local method receiving tuple

            bool TryDirection(Direction direction, out (BoardIndexRange range, ShipFindingResult complete) result)
            {
                var(beginningIx, beginningComplete) = FindShipEdge(ix, direction, true);
                var(endIx, endComplete)             = FindShipEdge(ix, direction, false);
                result = (new BoardIndexRange(beginningIx, endIx),
                          beginningComplete&& endComplete ? ShipFindingResult.CompleteShip : ShipFindingResult.PartialShip);
                return(result.range.Length > 1);
            }

            // Check if the ship is placed horizontally
            var isHorizontal = TryDirection(Direction.Horizontal, out var resultHorizontal);

            if (isHorizontal)
            {
                shipRange = resultHorizontal.range;
                return(resultHorizontal.complete);
            }

            // Check if the ship is placed vertically
            var isVertical = TryDirection(Direction.Vertical, out var resultVertical);

            shipRange = resultVertical.range;

            // When only a single square of a ship is known, no orientation can be determined and therefore the ship is only partial.
            if (!isHorizontal && !isVertical)
            {
                return(ShipFindingResult.PartialShip);
            }

            return(resultVertical.complete);
        }