Пример #1
0
        public IEnumerable <RoverAction> Simulate(IRoverStatusAccessor rover)
        {
            foreach (var action in GatherPower(rover))
            {
                yield return(action);
            }

            while (true)
            {
                var         adjacent = rover.Adjacent;
                TerrainType occupied = adjacent[Direction.None];

                if (rover.MovesLeft <= 5)
                {
                    foreach (var action in DoLowMoves(rover, adjacent))
                    {
                        yield return(action);
                    }
                    yield break;
                }

                if (occupied.IsSampleable())
                {
                    yield return(RoverAction.CollectSample);

                    UpdateMap(rover);
                    if (rover.SamplesCollected >= Parameters.SamplesPerProcess && rover.Power > Parameters.ProcessCost + Parameters.MoveSmoothCost)
                    {
                        yield return(RoverAction.ProcessSamples);
                    }
                }

                Direction nextMove = Direction.None;
                if (_path != null && _path.Count > 1)
                {
                    nextMove = _path.Pop();
                }
                else if (FindAdjacentSampleable(adjacent).HasValue)
                {
                    nextMove = FindAdjacentSampleable(adjacent).Value;
                }

                if (nextMove == Direction.None || adjacent[nextMove] == TerrainType.Impassable)
                {
                    Direction?reset = ResetDestination(rover.Position);
                    if (reset == null)
                    {
                        yield break; // We're stuck.
                    }
                    nextMove = reset.Value;
                }

                yield return(new RoverAction(nextMove));

                UpdateMap(rover);
            }
        }
Пример #2
0
        /// <summary>
        /// Searches for at least two contiguous smooth tiles to later gather power from.
        /// </summary>
        private IEnumerable <RoverAction> Explore(IRoverStatusAccessor rover)
        {
            Position center = Parameters.BottomRight / 2;

            while (true)
            {
                Position position = rover.Position;
                // First we check to see if we've found a position that we can gather power from.
                for (Int32 i = 0; i < Direction.DirectionCount + 1; i++)
                {
                    Direction      direction = Direction.FromInt32(i);
                    CoordinatePair neighbor  = position + direction;
                    if (_map[neighbor] == TerrainType.Smooth && _map.CountNeighborsOfType(neighbor, TerrainType.Smooth) > 0)
                    {
                        // If we have, break out of the exploration phase.
                        Int32 requiredPower = Parameters.GetMovementPowerCost(rover.Adjacent[direction]) + 1;
                        foreach (var action in EnsureSufficientPower(rover, requiredPower))
                        {
                            yield return(action);
                        }
                        yield return(new RoverAction(direction));

                        UpdateMap(rover);
                        yield break;
                    }
                }

                Direction exploreDirection = FindNextExploreDirection(center, position);

                if (exploreDirection != Direction.None)
                {
                    Int32 requiredPower = Parameters.GetMovementPowerCost(rover.Adjacent[exploreDirection]) + 1;
                    foreach (var action in EnsureSufficientPower(rover, requiredPower))
                    {
                        yield return(action);
                    }
                    yield return(new RoverAction(exploreDirection));

                    UpdateMap(rover);
                    continue;
                }

                // None of our immediate neighbors have any unknown neighbors, so just find a path towards the nearest unknown tile.
                var path = _pathfinder.FindNearestUnknown(position);
                while (path.Count > 1) // Don't move on to the unknown tile, just next to it.
                {
                    Int32 requiredPower = Parameters.GetMovementPowerCost(rover.Adjacent[path.Peek()]) + 1;
                    foreach (var action in EnsureSufficientPower(rover, requiredPower))
                    {
                        yield return(action);
                    }
                    yield return(new RoverAction(path.Pop()));

                    UpdateMap(rover);
                }
            }
        }
Пример #3
0
        private IEnumerable <RoverAction> EnsureSufficientPower(IRoverStatusAccessor rover, Int32 target)
        {
            if (rover.Power >= target)
            {
                return(System.Linq.Enumerable.Empty <RoverAction>());
            }

            Int32 powerDeficit = Math.Max(target - rover.Power - rover.CollectablePower, 0);

            return(System.Linq.Enumerable.Repeat(RoverAction.CollectPower, powerDeficit + 1));
        }
Пример #4
0
 private IEnumerable <RoverAction> GatherPower(IRoverStatusAccessor rover)
 {
     UpdateMap(rover);
     foreach (var action in Explore(rover))
     {
         yield return(action);
     }
     foreach (var action in Exploit(rover))
     {
         yield return(action);
     }
 }
Пример #5
0
        private Int32 CalculateExcessPowerThershold(IRoverStatusAccessor rover)
        {
            const Double smoothRoughRatio       = 1;
            Double       meanUnsampledMoveCost  = (smoothRoughRatio * Parameters.MoveSmoothCost + Parameters.MoveRoughCost) / (smoothRoughRatio + 1.0);
            Double       processCostPerSample   = (Double)Parameters.ProcessCost / Parameters.SamplesPerProcess;
            Double       meanSampleSequenceCost = meanUnsampledMoveCost + Parameters.SampleCost + processCostPerSample;

            Int32 maxSampleSequenceCount = (rover.MovesLeft - 1) / 3;

            Int32 targetPower = (Int32)(maxSampleSequenceCount * meanSampleSequenceCost) + Parameters.TransmitCost;

            return(targetPower);
        }
Пример #6
0
        private AdjacentTerrain GetAdjacent(IRoverStatusAccessor rover)
        {
            var adjacent = rover.Adjacent;
            var position = rover.Position;

            return(new AdjacentTerrain
                   (
                       IsDeadEnd(position, Direction.Up) ? TerrainType.Impassable : adjacent.Up,
                       IsDeadEnd(position, Direction.Right) ? TerrainType.Impassable : adjacent.Right,
                       IsDeadEnd(position, Direction.Down) ? TerrainType.Impassable : adjacent.Down,
                       IsDeadEnd(position, Direction.Left) ? TerrainType.Impassable : adjacent.Left,
                       IsDeadEnd(position, Direction.None) ? TerrainType.Impassable : adjacent.Occupied
                   ));
        }
Пример #7
0
        private Int32 CalculateExcessPowerThershold(IRoverStatusAccessor rover)
        {
            const Int32 smoothRoughRatio      = 10; // For the default generator, the ratio is 2:1
            Double      meanUnsampledMoveCost = (smoothRoughRatio * Parameters.MoveSmoothCost + Parameters.MoveRoughCost) / (smoothRoughRatio + 1.0);
            Double      processCostPerSample  = (Double)Parameters.ProcessCost / Parameters.SamplesPerProcess;
            Double      meanSampleCost        = meanUnsampledMoveCost + Parameters.SampleCost + processCostPerSample;

            Double sampleToLogisticMoveRatio = 3.9; // For 2 every move/sample/process sequences, we estimate 1 moves will be purely logistics (move, power).
            Double meanCost = (meanSampleCost * sampleToLogisticMoveRatio + Parameters.MoveSmoothCost) / (sampleToLogisticMoveRatio + 1);

            Int32 targetPower = (Int32)((rover.MovesLeft - 1) * meanCost) + Parameters.TransmitCost;

            return(targetPower);
        }
Пример #8
0
        public void Simulate(IRoverStatusAccessor rover)
        {
            var scratchRover = new ScratchRover(rover);

            Ai.Simulate(scratchRover);
        }
Пример #9
0
        public IEnumerable <RoverAction> Simulate(IRoverStatusAccessor rover)
        {
            while (true)
            {
                var         adjacent = GetAdjacent(rover);
                TerrainType occupied = adjacent[Direction.None];
                (Direction? adjacentSmoothDir, Direction? adjacentRoughDir, Int32 smoothCount) = FindAdjacentUnsampled(adjacent);

                if (rover.MovesLeft <= 5)
                {
                    foreach (var action in DoLowMoves(rover, adjacent))
                    {
                        yield return(action);
                    }
                    yield break;
                }

                if (rover.Power < LowPowerThreshold || (!adjacentSmoothDir.HasValue && occupied == TerrainType.Smooth))
                {
                    if (rover.Power < CalculateExcessPowerThershold(rover) / 2 && rover.NoBacktrack > 3)
                    {
                        yield return(RoverAction.CollectPower);
                    }
                    if (rover.Power < LowPowerThreshold)
                    {
                        yield return(RoverAction.Transmit);
                    }
                }
                else if (_gatheringPower && HasExcessPower(rover, rover.CollectablePower))
                {
                    yield return(RoverAction.CollectPower);

                    _gatheringPower = false;
                }

                // While we're gather power, we don't collect samples and instead abuse the Backtracking mechanic gather a large amount of power.
                if (occupied.IsSampleable() && (!_gatheringPower || (adjacentSmoothDir == null && occupied == TerrainType.Smooth) || _gatherPowerDir != Direction.None))
                {
                    yield return(RoverAction.CollectSample);

                    if (rover.SamplesCollected >= Parameters.SamplesPerProcess && rover.Power > Parameters.ProcessCost + Parameters.MoveSmoothCost)
                    {
                        yield return(RoverAction.ProcessSamples);
                    }
                }

                Boolean hasExcessPower = HasExcessPower(rover);
                if (hasExcessPower)
                {
                    _gatheringPower = false;
                }

                (Boolean isDeadEnd, Direction deadEndEscape) = CheckDeadEnd(adjacent);
                if (isDeadEnd && rover.Adjacent.Occupied != TerrainType.Smooth)
                {
                    AddDeadEnd(rover.Position);
                }

                if (_gatherPowerDir != Direction.None)
                {
                    _destination = _gatherPowerDir;
                }
                else if (adjacentSmoothDir.HasValue)
                {
                    _destination = adjacentSmoothDir.Value; // Prioritize smooth squares
                }
                else if (hasExcessPower && !_gatheringPower && adjacentRoughDir.HasValue)
                {
                    _destination = adjacentRoughDir.Value; // Visit rough squares if the rover has enough power
                }
                else if (isDeadEnd)
                {
                    _destination = deadEndEscape;
                }

                Direction nextMove = _destination;
                if (nextMove == Direction.None || adjacent[nextMove] == TerrainType.Impassable)
                {
                    if (_avoidanceDestination != Direction.None)
                    {
                        if (adjacent[_avoidanceDestination] == TerrainType.Impassable)
                        {
                            _destination          = ResetDestination(adjacent);
                            nextMove              = _destination;
                            _avoidanceDestination = Direction.None;
                        }
                        else
                        {
                            nextMove = _avoidanceDestination;
                        }
                    }
                    else
                    {
                        nextMove = AvoidObstacle(adjacent); // Obstacle avoidance
                        _avoidanceDestination = nextMove;
                    }
                }
                else
                {
                    _avoidanceDestination = Direction.None;
                }

                if (_gatheringPower && rover.Adjacent.Occupied == TerrainType.Smooth && smoothCount > 1)
                {
                    _gatherPowerDir = nextMove.Opposite();
                }
                else
                {
                    _gatherPowerDir = Direction.None;
                }

                yield return(new RoverAction(nextMove));

                _roundRobin++;
            }
        }
Пример #10
0
        private IEnumerable <RoverAction> DoLowMoves(IRoverStatusAccessor rover, AdjacentTerrain adjacent)
        {
            if (rover.MovesLeft > 5)
            {
                yield break;
            }

            Boolean smoothOccupied = adjacent[Direction.None] == TerrainType.Smooth;
            Boolean roughOccupied  = adjacent[Direction.None] == TerrainType.Rough;

            (Direction? smoothDir, Direction? roughDir, _) = FindAdjacentUnsampled(adjacent);
            if (rover.MovesLeft == 5 && rover.Power > Parameters.SampleCost + Parameters.MoveRoughCost + Parameters.SampleCost + Parameters.ProcessCost)
            {
                if (smoothOccupied || roughOccupied)
                {
                    yield return(RoverAction.CollectSample);
                }
                Direction?moveDir = smoothDir ?? roughDir;
                if (moveDir.HasValue)
                {
                    yield return(new RoverAction(moveDir.Value));

                    yield return(RoverAction.CollectSample);
                }

                yield return(RoverAction.ProcessSamples);

                yield return(RoverAction.Transmit);

                yield break;
            }

            if (rover.MovesLeft >= 4)
            {
                if (rover.Power < Parameters.ProcessCost + Parameters.SampleCost + Parameters.ProcessCost)
                {
                    yield return(RoverAction.CollectPower);
                }
                yield return(RoverAction.CollectPower);

                if (rover.Power > Parameters.ProcessCost)
                {
                    if (rover.Power > Parameters.ProcessCost + Parameters.SampleCost && smoothOccupied)
                    {
                        yield return(RoverAction.CollectSample);
                    }
                    yield return(RoverAction.ProcessSamples);
                }
            }
            else if (rover.MovesLeft == 3)
            {
                if (rover.Power > Parameters.ProcessCost + Parameters.SampleCost && smoothOccupied)
                {
                    yield return(RoverAction.CollectSample);
                }
                else
                {
                    yield return(RoverAction.CollectPower);
                }
                if (rover.Power > Parameters.ProcessCost)
                {
                    yield return(RoverAction.ProcessSamples);
                }
            }
            if (rover.MovesLeft == 2)
            {
                if (rover.Power == 0)
                {
                    yield return(RoverAction.CollectPower);
                }
                else if (rover.Power > Parameters.ProcessCost && rover.SamplesCollected > 0)
                {
                    yield return(RoverAction.ProcessSamples);
                }
            }
            if (rover.SamplesProcessed > 0)
            {
                yield return(RoverAction.Transmit);
            }
            yield break;
        }
Пример #11
0
        private Boolean HasExcessPower(IRoverStatusAccessor rover, Int32 potentialPower)
        {
            Int32 targetPower = CalculateExcessPowerThershold(rover);

            return(rover.Power + potentialPower >= targetPower);
        }
Пример #12
0
 private void UpdateMap(IRoverStatusAccessor rover) => _map.UpdateTerrain(rover.Position, rover.Adjacent);
Пример #13
0
        private Int32 CalculateNoBacktrackTarget(IRoverStatusAccessor rover)
        {
            Int32 power = CalculateExcessPowerThershold(rover);

            return((Int32)Math.Ceiling(Math.Pow(power, 1.0 / 3.0)));
        }
Пример #14
0
        /// <summary>
        /// Gathers power by staying on smooth squares and revealing as much as possible until the threshold is reached.
        /// </summary>
        private IEnumerable <RoverAction> Exploit(IRoverStatusAccessor rover)
        {
            Stack <Direction> pathToUnknown = _pathfinder.FindNearestUnknownThroughSmooth(rover.Position);
            Boolean           isPathDirty   = false;

            while (!HasExcessPower(rover, rover.CollectablePower))
            {
                Int32 noBacktrackTarget = CalculateNoBacktrackTarget(rover);
                Int32 movesToTarget     = noBacktrackTarget - rover.NoBacktrack;
                Int32 powerNeeded       = movesToTarget * Parameters.MoveSmoothCost + 1;
                Int32 excessPower       = rover.Power - powerNeeded;

                Direction?preferredDir = _map.FindNeighborWithMultipleSmoothNeighbors(rover.Position);
                if (rover.Adjacent.Occupied == TerrainType.Smooth && preferredDir.HasValue && excessPower > 0 && !rover.IsHopperFull)
                {
                    yield return(RoverAction.CollectSample);

                    yield return(new RoverAction(preferredDir.Value));

                    UpdateMap(rover);
                    isPathDirty = true;
                    continue;
                }

                if (rover.Power < Parameters.MoveSmoothCost + 1)
                {
                    Int32 cost = Parameters.GetMovementPowerCost(TerrainType.Smooth);
                    Int32 requiredPower;
                    if (cost <= 3)
                    {
                        requiredPower = cost + 1;
                    }
                    else
                    {
                        Int32 movesNeeded = (Int32)(Math.Ceiling(Math.Sqrt(4 * cost - 3) - 3) / 2);
                        requiredPower = movesNeeded * cost + 1;
                    }

                    foreach (var action in EnsureSufficientPower(rover, requiredPower))
                    {
                        yield return(action);
                    }
                    continue;
                }

                if (pathToUnknown != null)
                {
                    // We reveal unknown tiles as we go, but without leaving the smooth area.
                    if (pathToUnknown.Count <= 1 || isPathDirty)
                    {
                        pathToUnknown = _pathfinder.FindNearestUnknownThroughSmooth(rover.Position);
                        isPathDirty   = false;
                        continue;
                    }

                    yield return(new RoverAction(pathToUnknown.Pop()));
                }
                else
                {
                    Direction?smoothDir = FindAdjacentSmooth(rover.Adjacent);
                    yield return(new RoverAction(smoothDir.Value));
                }
                UpdateMap(rover);
            }

            yield return(RoverAction.CollectPower);
        }
Пример #15
0
        IEnumerable <RoverAction> IAi.Simulate(IRoverStatusAccessor rover)
        {
            var scratchRover = new ScratchRover(rover);

            return(Ai.Simulate(scratchRover));
        }
Пример #16
0
 public ScratchRover(IRoverStatusAccessor rover)
 {
     Rover = rover ?? throw new ArgumentNullException(nameof(rover));
 }
Пример #17
0
 private Boolean HasExcessPower(IRoverStatusAccessor rover) => HasExcessPower(rover, 0);