public IEnumerable <ICommand> Solve() { for (int i = 0; i < N - 1; i++) { for (int k = 0; k < i; k++) { yield return(new Wait()); } yield return(new Fission(new NearDifference(new Vec(0, 0, 1)), N - i - 2)); } pos = new Vec[N]; regions = new Region[N]; for (int i = 0; i < N; i++) { pos[i] = new Vec(0, 0, i); regions[i] = Region.ForShift(new Vec(i % Nx * BlockSizeX, 0, i / Nx * BlockSizeZ), new Vec(BlockSizeX - 1, R - 1, BlockSizeZ - 1)); } for (int i = N - 1; i >= 0; i--) { var volatiles = pos.ToHashSet(); var target = regions[i].Start; var commands = new PathFinder(state, pos[i], target, volatiles, null).TryFindPath(); if (commands == null) { throw new InvalidOperationException($"Failed to find path from {pos[i]} to {target}"); } foreach (var command in commands) { for (int k = 0; k < i; k++) { yield return(new Wait()); } yield return(command); for (int k = i + 1; k < N; k++) { yield return(new Wait()); } } pos[i] = target; } var enumerators = new IEnumerator <ICommand> [N]; while (true) { for (int bot = 0; bot < N; bot++) { if (enumerators[bot] == null) { enumerators[bot] = SolveSingle(bot).GetEnumerator(); } } var tickCommands = new ICommand[N]; for (int bot = 0; bot < N; bot++) { if (enumerators[bot].MoveNext()) { tickCommands[bot] = enumerators[bot].Current; } else { enumerators[bot].Dispose(); enumerators[bot] = null; } } if (tickCommands.All(c => c == null)) { break; } foreach (var tickCommand in tickCommands) { yield return(tickCommand ?? new Wait()); } } for (int bot = N - 1; bot >= 1; bot--) { var parentPos = pos[bot - 1]; List <ICommand> commands = null; foreach (var near in parentPos.GetNears().OrderBy(n => n.MDistTo(pos[bot]))) { if (near.IsInCuboid(R) && !state.Get(near)) { var pathFinder = new PathFinder(state, pos[bot], near, null, null); commands = pathFinder.TryFindPath(); if (commands != null) { pos[bot] = near; break; } } } if (commands == null) { throw new InvalidOperationException($"Couldn't find path for fusion from {pos[bot]} to near of {parentPos}"); } foreach (var command in commands) { for (int k = 0; k < bot; k++) { yield return(new Wait()); } yield return(command); } for (int k = 0; k < bot - 1; k++) { yield return(new Wait()); } yield return(new FusionP(new NearDifference(pos[bot] - parentPos))); yield return(new FusionS(new NearDifference(parentPos - pos[bot]))); } var cmds = new List <ICommand>(); Move(0, Vec.Zero, cmds); foreach (var command in cmds) { yield return(command); } yield return(new Halt()); }