예제 #1
0
        public IEnumerable <Command> Solve1(State state)
        {
            var map = state.Map;

            var distsFromCenter                 = map.DistsFromCenter;
            var distsFromCenterTimer            = 0;
            var distsFromCenterTimerResetPeriod = Math.Max(10, 1 + (map.CellsToVisit.Count() / this.recalcDistsFromCenterCount));

            var bfs = new BfsState(state.Map);

            // for debugging purposes
            // var history = new List<(Command, State)>();
            Command Next(Command cmd)
            {
                var newState    = state.Next(cmd) ?? throw new InvalidOperationException("Generated invalid move!");
                var wrappedDiff = newState.WrappedCellsCount - state.WrappedCellsCount;

                state = newState;

                distsFromCenterTimer += wrappedDiff;
                if (distsFromCenterTimer >= distsFromCenterTimerResetPeriod)
                {
                    distsFromCenter      = new DistsFromCenter(state);
                    distsFromCenterTimer = 0;
                }

                // history.Add((cmd, state));
                return(cmd);
            }

            while (state.WrappedCellsCount != map.CellsToVisit.Count)
            {
                Bfs(distsFromCenter);

                if (state.HaveManipulatorExtensions())
                {
                    yield return(Next(this.manipStrategy.Grow(state)));

                    continue;
                }

                foreach (var cmd in bfs.Path)
                {
                    // During walking we've found extension thingy - let's take it then!
                    if (state.HaveManipulatorExtensions())
                    {
                        yield return(Next(this.manipStrategy.Grow(state)));

                        break;
                    }

                    yield return(Next(cmd));
                }
            }

            void Bfs(DistsFromCenter distsFromCenter)
            {
                var bot = state.GetBot(0);

                ++bfs.Generation;
                bfs.Queue.Clear();
                bfs.Queue.Enqueue((bot.X, bot.Y, bot.Dir));
                bfs.Nodes[bot.X, bot.Y, bot.Dir] = new BfsState.Node(bfs.Generation, -1, 0);

                int?maxDepth = null;

                (int, int, int, bool isExtensionBooster, int numVis, int distFromCenter)? bestDest = null;

                while (bfs.Queue.Count > 0)
                {
                    var(x, y, dir) = bfs.Queue.Dequeue();
                    var depth = bfs.Nodes[x, y, dir].Depth;

                    if (bfs.Nodes[x, y, dir].Generation != bfs.Generation)
                    {
                        throw new Exception("oops");
                    }

                    if (maxDepth != null && depth > maxDepth.Value)
                    {
                        if (bestDest == null)
                        {
                            throw new InvalidOperationException();
                        }

                        var(destX, destY, destDir, destIsExtensionBooster, destNumVis, bestDistFromCenter) = bestDest.Value;
                        FindBackwardPath(destX, destY, destDir);
                        return;
                    }

                    // path found, but search a bit more for deeper fruits
                    var isExtensionBooster = map[x, y] == Map.Cell.ManipulatorExtension && !state.IsPickedUp(x, y);
                    var(numVis, distFromCenter) = state.MaxUnwrappedVisibleDistFromCenter(x, y, dir, distsFromCenter);
                    if (numVis > 0 || isExtensionBooster)
                    {
                        if (maxDepth == null)
                        {
                            maxDepth = depth + this.bfsExtraDepth;
                        }

                        if (bestDest == null ||
                            Quality(isExtensionBooster, numVis, distFromCenter) > Quality(bestDest.Value.isExtensionBooster, bestDest.Value.numVis, bestDest.Value.distFromCenter))
                        {
                            bestDest = (x, y, dir, isExtensionBooster, numVis, distFromCenter);
                        }
                    }

                    for (var i = 0; i < Move.All.Length; ++i)
                    {
                        var move = Move.All[i];
                        var nx   = x + move.Dx;
                        var ny   = y + move.Dy;
                        if (map.IsFree(nx, ny) && bfs.Nodes[nx, ny, dir].Generation != bfs.Generation)
                        {
                            bfs.Nodes[nx, ny, dir] = new BfsState.Node(bfs.Generation, i, depth + 1);
                            bfs.Queue.Enqueue((nx, ny, dir));
                        }
                    }

                    for (var i = 0; i < Turn.All.Length; ++i)
                    {
                        var ddir = Turn.All[i].Ddir;
                        var ndir = (dir + ddir) & 3;
                        if (bfs.Nodes[x, y, ndir].Generation != bfs.Generation)
                        {
                            bfs.Nodes[x, y, ndir] = new BfsState.Node(bfs.Generation, -ddir, depth + 1);
                            bfs.Queue.Enqueue((x, y, ndir));
                        }
                    }
                }

                var(finalX, finalY, finalDir, p1, p2, p3) =
                    bestDest ?? throw new InvalidOperationException("Couldn't find any path with BFS!");
                FindBackwardPath(finalX, finalY, finalDir);

                int Quality(bool isBooster, int numVis, int distFromCenter1) =>
                (isBooster ? 10000 : 0) + (this.numVisCoeff * numVis) + (10 * distFromCenter1);
            }

            void FindBackwardPath(int x, int y, int dir)
            {
                bfs.FindBackwardPath(x, y, dir, state.GetBot(0));

                // if there's more than one command, then filter out all turns
                if (this.removeTurns && bfs.Path.Count > 1)
                {
                    var writeIdx = 0;
                    for (var i = 0; i < bfs.Path.Count; ++i)
                    {
                        if (!(bfs.Path[i] is Turn))
                        {
                            bfs.Path[writeIdx++] = bfs.Path[i];
                        }
                    }

                    // degenerate case: 180 degree turn
                    if (writeIdx != 0)
                    {
                        bfs.Path.RemoveRange(writeIdx, bfs.Path.Count - writeIdx);
                    }
                }
            }
        }
예제 #2
0
        public IEnumerable <Command> Solve1(State initState)
        {
            // To be used only in the root of the function
            var masterState = initState;
            var map         = masterState.Map;

            // Used only by RunBfs() and FindSmallZones()
            var bfs             = new BfsState(map);
            var distsFromCenter = map.DistsFromCenter;

            var globalZoneToVisit = map.CellsToVisit.ToHashSet();

            foreach (var visited in masterState.WrappedCells.Enumerate())
            {
                globalZoneToVisit.Remove(visited.Key);
            }

            foreach (var cmd in RunInZone(globalZoneToVisit))
            {
                yield return(cmd);
            }

            IEnumerable <Command> RunInZone(HashSet <(int, int)> zone)
            {
                while (true)
                {
                    var firstPath = new List <Command>();

                    var noTurn = MeasureTurnProfit(masterState, zone, null, this.lookAheadSize, firstPath);

                    if (firstPath.Count == 0)
                    {
                        yield break;
                    }

                    if (this.lookAheadSize == 0 || firstPath.Count > 10)
                    {
                        var upTo = firstPath.Count - (this.lookAheadSize == 0 ? 0 : 10);
                        for (var i = 0; i < upTo; ++i)
                        {
                            masterState = masterState.Next(firstPath[i]) ?? throw new Exception("Impossible");
                            yield return(firstPath[i]);
                        }

                        continue;
                    }

                    var profits = new[]
                    {
                        noTurn,
                        MeasureTurnProfit(masterState, zone, Turn.Left, this.lookAheadSize - 1),
                        MeasureTurnProfit(masterState, zone, Turn.Right, this.lookAheadSize - 1),
                    };

                    var best = noTurn;
                    foreach (var p in profits)
                    {
                        if (p.profit > best.profit && p.profit > noTurn.profit + 1)
                        {
                            best = p;
                        }
                    }

                    if (best.command != null)
                    {
                        masterState = masterState.Next(best.command) ?? throw new Exception("Impossible");
                        yield return(best.command);
                    }
                    else
                    {
                        throw new Exception("Impossible");
                    }
                }
            }

            int CalcProfit(List <(int, int)> touchedCells)
            {
                var bot       = masterState.GetBot(0);
                var edgeCells = 0;

                foreach (var cell in touchedCells)
                {
                    var(x, y) = cell;
                    if (!IsFree(x + 1, y) || !IsFree(x - 1, y) || !IsFree(x, y + 1) || !IsFree(x, y - 1))
                    {
                        edgeCells += 1;
                    }
                }

                return(touchedCells.Count + (10 * edgeCells));

                bool IsFree(int x, int y) =>
                map.IsFree(x, y);     // && !masterState.IsWrapped(x, y);
            }

            (Command?command, int profit) MeasureTurnProfit(
                State state,
                HashSet <(int, int)> zone,
                Command?firstCommand,
                int lookAhead,
                List <Command>?firstPath = null)
            {
                var touchedCells = new List <(int, int)>();

                if (firstCommand != null)
                {
                    var nextState = state.Next(touchedCells, firstCommand);
                    if (nextState == null)
                    {
                        return(null, 0);
                    }

                    state = nextState;
                }

                var afterFirstSize = touchedCells.Count;

                var firstBfsCommand = RepeatBfs(state, zone, 1 + lookAhead, touchedCells, firstPath);
                var bfsProfit       = CalcProfit(touchedCells);

                touchedCells.RemoveRange(afterFirstSize, touchedCells.Count - afterFirstSize);

                if (lookAhead == 0)
                {
                    firstCommand ??= firstBfsCommand;
                    return(firstCommand, bfsProfit);
                }

                var possibleMoves = DirToMoves[state.GetBot(0).Dir];
                var bestMove      = (command : (Command?)firstBfsCommand, profit : bfsProfit);

                foreach (var move in possibleMoves)
                {
                    if (move != firstBfsCommand)
                    {
                        TryMove(state, zone, move, lookAhead, touchedCells, null);
                        var profit = CalcProfit(touchedCells);
                        touchedCells.RemoveRange(afterFirstSize, touchedCells.Count - afterFirstSize);
                        if (profit > bestMove.profit)
                        {
                            bestMove = (move, profit);
                        }
                    }
                }

                firstCommand ??= bestMove.command;

                return(firstCommand, bestMove.profit);
            }

            void TryMove(
                State state,
                HashSet <(int, int)> zone,
                Command firstCommand,
                int lookAhead,
                List <(int, int)> touchedCells,
                List <Command>?firstPath = null)
            {
                var nextState = state.Next(touchedCells, firstCommand);

                if (nextState == null)
                {
                    return;
                }

                firstPath?.Add(firstCommand);
                RepeatBfs(nextState, zone, lookAhead, touchedCells, firstPath);
            }

            Command?RepeatBfs(State state, HashSet <(int, int)> zone, int times, List <(int, int)> touchedCells, List <Command>?firstPath)
            {
                Command?firstCmd = null;

                while (times > 0 && RunBfs(state, zone) && bfs.Path.Count > 0)
                {
                    for (var i = 0; i < bfs.Path.Count && times > 0; ++i, --times)
                    {
                        if (firstCmd == null)
                        {
                            firstCmd = bfs.Path[0];
                            firstPath?.AddRange(bfs.Path);
                        }

                        state = state.Next(touchedCells, bfs.Path[i]) ?? throw new Exception("Impossible");
                    }
                }

                return(firstCmd);
            }

            bool RunBfs(State state, HashSet <(int, int)> zone)
            {
                var bot = state.GetBot(0);

                var possibleMoves = DirToMoves[bot.Dir];

                ++bfs.Generation;
                bfs.Nodes[bot.X, bot.Y, bot.Dir] = new BfsState.Node(bfs.Generation, -1, 0);
                bfs.Queue.Clear();
                bfs.Queue.Enqueue((bot.X, bot.Y, bot.Dir));

                while (bfs.Queue.Count > 0)
                {
                    var(x, y, dirUnused) = bfs.Queue.Dequeue();

                    if (state.UnwrappedCellsVisibleInZone(x, y, bot.Dir, zone))
                    {
                        bfs.FindBackwardPath(x, y, bot.Dir, bot, possibleMoves);
                        return(true);
                    }

                    for (var moveIdx = 0; moveIdx < 4; ++moveIdx)
                    {
                        var move = possibleMoves[moveIdx];
                        var nx   = x + move.Dx;
                        var ny   = y + move.Dy;
                        if (map.IsFree(nx, ny) && bfs.Nodes[nx, ny, bot.Dir].Generation != bfs.Generation)
                        {
                            bfs.Nodes[nx, ny, bot.Dir] = new BfsState.Node(bfs.Generation, moveIdx, 0);
                            bfs.Queue.Enqueue((nx, ny, bot.Dir));
                        }
                    }
                }

                return(false);
            }
        }
예제 #3
0
        public IEnumerable <Command> Solve1(State state)
        {
            var map = state.Map;
            var bfs = new BfsState(map);

            // No point in running if there's another strategy that will collect everything
            if (CollectBoostersFactory.PrevCount(this.boostersCount) >= map.NumManipulatorExtensions)
            {
                throw new SkipStrategyException();
            }

            var remainingBoostersCount = Math.Min(state.ManipulatoExtensionsOnTheFloorCount, this.boostersCount);

            Command Next(Command cmd)
            {
                state = state.Next(cmd) ?? throw new InvalidOperationException("Generated invalid move!");

                if (cmd is UseManipulatorExtension)
                {
                    --remainingBoostersCount;
                }

                return(cmd);
            }

            while (remainingBoostersCount > 0)
            {
                foreach (var cmd in FindManipulatorExtension())
                {
                    yield return(Next(cmd));
                }

                while (state.HaveManipulatorExtensions())
                {
                    yield return(Next(this.manipStrategy.Grow(state)));
                }
            }

            IEnumerable <Command> FindManipulatorExtension()
            {
                var bot = state.GetBot(0);

                ++bfs.Generation;
                bfs.Queue.Clear();
                bfs.Queue.Enqueue((bot.X, bot.Y, bot.Dir));
                bfs.Nodes[bot.X, bot.Y, bot.Dir] = new BfsState.Node(bfs.Generation, -1, 0);
                while (bfs.Queue.Count > 0)
                {
                    var(x, y, dir) = bfs.Queue.Dequeue();
                    if (bfs.Nodes[x, y, dir].Generation != bfs.Generation)
                    {
                        throw new Exception("oops");
                    }

                    if (map[x, y] == Map.Cell.ManipulatorExtension && !state.IsPickedUp(x, y))
                    {
                        bfs.FindBackwardPath(x, y, dir, bot);
                        return(bfs.Path);
                    }

                    for (var i = 0; i < Move.All.Length; ++i)
                    {
                        var move = Move.All[i];
                        var nx   = x + move.Dx;
                        var ny   = y + move.Dy;
                        if (map.IsFree(nx, ny) && bfs.Nodes[nx, ny, dir].Generation != bfs.Generation)
                        {
                            bfs.Nodes[nx, ny, dir] = new BfsState.Node(bfs.Generation, i, 0);
                            bfs.Queue.Enqueue((nx, ny, dir));
                        }
                    }
                }

                throw new InvalidOperationException();
            }
        }
예제 #4
0
        public IEnumerable <Command[]> SolveWithClones(State state)
        {
            var addedBots = new List <(int time, State.Bot bot)>();
            var time      = 0;

            var cloningCommands = MakeClones().ToList();
            var cloningTime     = cloningCommands.Count;

            var stateAfterCloning = state;

            var firstBotSolutionSize = this.baseStrategy
                                       .Solve(stateAfterCloning.ReplaceBots(resetBoosters: false, stateAfterCloning.GetBot(0)))
                                       .Count();

            var totalBonusTime = addedBots.Sum(tup => cloningTime - tup.time);
            var stepsPerBot    = Math.Min(
                firstBotSolutionSize / (1 + addedBots.Count),
                Math.Max(0, firstBotSolutionSize - totalBonusTime) / (1 + addedBots.Count));

            while (true)
            {
                var commandsPerBot = new Command[addedBots.Count + 1][];

                var botState = stateAfterCloning.ReplaceBots(resetBoosters: false, state.GetBot(0));
                var bot      = botState.GetBot(0);
                var botIdx   = 0;

                while (true)
                {
                    var bonusTime = botIdx == 0
                        ? 0
                        : cloningTime - addedBots[botIdx - 1].time;
                    var left        = stepsPerBot + bonusTime;
                    var commandsBuf = new List <Command>();
                    foreach (var cmd in this.baseStrategy.Solve(botState))
                    {
                        if (left-- <= 0)
                        {
                            break;
                        }

                        commandsBuf.Add(cmd.Single());

                        var nextState = botState.Next(cmd);
                        if (nextState == null)
                        {
                            throw new Exception("shouldn't happen");
                        }

                        botState = nextState;
                    }

                    commandsPerBot[botIdx] = commandsBuf.ToArray();

                    if (++botIdx >= stateAfterCloning.BotsCount)
                    {
                        break;
                    }

                    botState = botState.ReplaceBots(resetBoosters: true, addedBots[botIdx - 1].bot);
                }

                if (botState.Map.CellsToVisit.Count == botState.WrappedCellsCount)
                {
                    commandsPerBot[0] = cloningCommands.Concat(commandsPerBot[0]).ToArray();
                    foreach (var cmds in CommandsSerializer.Transponse(commandsPerBot))
                    {
                        yield return(cmds);
                    }

                    break;
                }

                stepsPerBot = (int)(1 + (stepsPerBot * 1.1));
            }

            IEnumerable <Command> MakeClones()
            {
                var map = state.Map;
                var bfs = new BfsState(map);

                while (addedBots.Count < map.NumCloneBoosts)
                {
                    var bot = state.GetBot(0);

                    foreach (var cmd in FindBooster())
                    {
                        ++time;
                        state = state.Next(cmd) ?? throw new Exception("Impossible");
                        yield return(cmd);
                    }

                    while (map[bot.X, bot.Y] == Map.Cell.SpawnPoint && state.CloneBoosterCount > 0)
                    {
                        ++time;
                        addedBots.Add((time, bot));
                        state = state.Next(Clone.Instance) ?? throw new Exception("Impossible");
                        yield return(Clone.Instance);
                    }
                }

                IEnumerable <Command> FindBooster()
                {
                    var bot = state.GetBot(0);

                    ++bfs.Generation;
                    bfs.Queue.Clear();
                    bfs.Queue.Enqueue((bot.X, bot.Y, bot.Dir));
                    bfs.Nodes[bot.X, bot.Y, 0] = new BfsState.Node(bfs.Generation, -1, 0);
                    while (bfs.Queue.Count > 0)
                    {
                        var(x, y, dir) = bfs.Queue.Dequeue();

                        if ((map[x, y] == Map.Cell.Clone && !state.IsPickedUp(x, y) && (x, y) != (bot.X, bot.Y)) ||
                            (map[x, y] == Map.Cell.SpawnPoint &&
                             (state.CloneBoosterCount > 0 || (map[bot.X, bot.Y] == Map.Cell.Clone && !state.IsPickedUp(bot.X, bot.Y)))))
                        {
                            bfs.FindBackwardPath(x, y, 0, bot);
                            return(bfs.Path);
                        }

                        for (var i = 0; i < Move.All.Length; ++i)
                        {
                            var move = Move.All[i];
                            var nx   = x + move.Dx;
                            var ny   = y + move.Dy;
                            if (map.IsFree(nx, ny) && bfs.Nodes[nx, ny, dir].Generation != bfs.Generation)
                            {
                                bfs.Nodes[nx, ny, dir] = new BfsState.Node(bfs.Generation, i, 0);
                                bfs.Queue.Enqueue((nx, ny, dir));
                            }
                        }
                    }

                    throw new Exception("oops");
                }
            }
        }