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); } } } }
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); } }
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(); } }
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"); } } }