Example #1
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);
        }
Example #2
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);
        }