diff --git a/src/swarm-engine/Swarm/Game/State/Robot.hs b/src/swarm-engine/Swarm/Game/State/Robot.hs index 376a6c8e1..596f12754 100644 --- a/src/swarm-engine/Swarm/Game/State/Robot.hs +++ b/src/swarm-engine/Swarm/Game/State/Robot.hs @@ -378,7 +378,7 @@ wakeWatchingRobots myID currentTick loc = do -- Step 4: Re-add the watching bots to be awakened ASAP: wakeableBotIds = map fst wakeTimes - -- It is crucial that only robots with a larger RID thatn the current robot + -- It is crucial that only robots with a larger RID than the current robot -- be scheduled for the *same* tick, since within a given tick we iterate over -- robots in increasing order of RID. -- See note in 'iterateRobots'. diff --git a/src/swarm-engine/Swarm/Game/Step.hs b/src/swarm-engine/Swarm/Game/Step.hs index edcb9a452..ac6d43d09 100644 --- a/src/swarm-engine/Swarm/Game/Step.hs +++ b/src/swarm-engine/Swarm/Game/Step.hs @@ -160,6 +160,29 @@ insertBackRobot rn rob = do -- | GameState with support for IO and Time effect type HasGameStepState sig m = (Has (State GameState) sig m, Has (Lift IO) sig m, Has Effect.Time sig m) +-- | Run a set of robots - this is used to run robots before/after the focused one. +-- +-- Note that during the iteration over the supplied robot IDs, it is possible +-- that a robot that may have been present in 'robotMap' at the outset +-- of the iteration to be removed before the iteration comes upon it. +-- This is why we must perform a 'robotMap' lookup at each iteration, rather +-- than looking up elements from 'robotMap' in bulk up front with something like +-- 'restrictKeys'. +-- +-- = Invariants +-- +-- * Every tick, every active robot shall have exactly one opportunity to run. +-- * The sequence in which robots are chosen to run is by increasing order of 'RID'. +runRobotIDs :: HasGameStepState sig m => IS.IntSet -> m () +runRobotIDs robotNames = do + time <- use $ temporal . ticks + flip (iterateRobots time) robotNames $ \rn -> do + mr <- uses (robotInfo . robotMap) (IM.lookup rn) + forM_ mr (stepOneRobot rn) + where + stepOneRobot :: HasGameStepState sig m => RID -> Robot -> m () + stepOneRobot rn rob = tickRobot rob >>= insertBackRobot rn + -- | -- Runs the given robots in increasing order of 'RID'. -- @@ -192,29 +215,6 @@ iterateRobots time f runnableBots = iterateRobots time f $ IS.union robotsToAdd remainingBotIDs --- | Run a set of robots - this is used to run robots before/after the focused one. --- --- Note that during the iteration over the supplied robot IDs, it is possible --- that a robot that may have been present in 'robotMap' at the outset --- of the iteration to be removed before the iteration comes upon it. --- This is why we must perform a 'robotMap' lookup at each iteration, rather --- than looking up elements from 'robotMap' in bulk up front with something like --- 'restrictKeys'. --- --- = Invariants --- --- * Every tick, every active robot shall have exactly one opportunity to run. --- * The sequence in which robots are chosen to run is by increasing order of 'RID'. -runRobotIDs :: HasGameStepState sig m => IS.IntSet -> m () -runRobotIDs robotNames = do - time <- use $ temporal . ticks - flip (iterateRobots time) robotNames $ \rn -> do - mr <- uses (robotInfo . robotMap) (IM.lookup rn) - forM_ mr (stepOneRobot rn) - where - stepOneRobot :: HasGameStepState sig m => RID -> Robot -> m () - stepOneRobot rn rob = tickRobot rob >>= insertBackRobot rn - -- | This is a helper function to do one robot step or run robots before/after. singleStep :: HasGameStepState sig m => SingleStep -> RID -> IS.IntSet -> m Bool singleStep ss focRID robotSet = do