Room setup and position layout not idempotent when cpu exhausted
If a call to Room.setup() or Room.updatePosition() runs out of CPU, it will try to run again the next tick. Many of the operations here are not idempotent, leading to conflicts between things placed on the previous attempt and things placed on the current tick.
Partial solution 1) Keep track of setup/update progress on a room, and start over where we left off.
Partial solution 2) Track more info about some entries in room.memory.position such as the "target" of a link
Partial solution 3) Clear some room.memory.position arrays prior to pushing to them.
Note that while implementing #1 you will run into problems with memory [de]serialization. Many steps of the current process expect the children of room.memory.position to contain RoomPosition objects, which is true the tick they are created but not the next tick. Later steps in the process will need to re-instanciate the objects rather than relying on them still being classed.
I think this is related to #452
I think to make the different layout steps idempotent is a good step in the right direction.
I don't understand part 2, where this helps to make the process more robust.
In between I had a recursive solution to build the rooms, to be able to try multiple positions. E.g. if in the process it can't build all extensions it would go back to the pathStart and try another one. It was really tricky to handle all the variables in their respective state.
I think it would also be good to handle the CPU better. E.g. checking if we expect that the next operation is able to finish.
Part 2 is necessary because a step might fail in the middle. For example, if the step for setting the storage position ever fails, we can always wipe out positions.storage and insert the new one. But if any step that creates a link fails, we can't wipe out positions.link because a different step (that isn't going to be re-run) might have already created some links.
If we kept track of what each link's purpose is (terminal, source, etc) then we would know which ones to remove after a failed step.
K, got your point.
I'm not sure if the idempotent condition is enough requirement. Your example is a bit tricky, because only redoing the storage step will rarely work, the following steps are dependent on the storage position and have to be redone, too.
Having some 'savepoints' from where we can continue. Maybe even by intention, when claiming a room we do not necessarily need to calculate the lab, terminal and observer position, this can be done later, too
Ideally, the storage step would work on its own even if the other steps have already been done, and would produce the same result each time it runs. There will be a storage-shaped hole in all the other steps' results, which is exactly where the storage step should put the storage if it runs idempotently.
Savepoints to continue from is a lesser but still useful step, and that would mostly be my #1 with a little of my #3.
I ran into a bug in my code recently where there was a sometimes-infinite loop in a helper function (maybe inPositions?) that caused arbitrary parts of the init/setup/update code to time out. I ended up adding a terrible savepoint system which I threw away and made this issue to prompt myself to eventually recreate.