From e8a77d6017cc2fc8a23060a204e435e8188ac6e3 Mon Sep 17 00:00:00 2001 From: Karl Ostmo Date: Sun, 25 Jun 2023 14:05:05 -0700 Subject: [PATCH] goal dialog suppression with --autoplay (#1344) Closes #1340. Compare: scripts/play.sh --scenario data/scenarios/Challenges/blender.yaml --run data/scenarios/Challenges/_blender/solution.sw vs. scripts/play.sh --scenario data/scenarios/Challenges/blender.yaml --autoplay --- src/Swarm/Game/State.hs | 17 ++++++++++++++--- src/Swarm/TUI/Controller.hs | 12 ++++++++++-- src/Swarm/TUI/Launch/Model.hs | 9 +-------- src/Swarm/TUI/Launch/Prep.hs | 2 +- src/Swarm/TUI/Model/StateUpdate.hs | 29 +++++++++++++++++++---------- src/Swarm/TUI/Model/UI.hs | 6 ++++++ 6 files changed, 51 insertions(+), 24 deletions(-) diff --git a/src/Swarm/Game/State.hs b/src/Swarm/Game/State.hs index 2640a2ae0..ea7078d41 100644 --- a/src/Swarm/Game/State.hs +++ b/src/Swarm/Game/State.hs @@ -81,6 +81,10 @@ module Swarm.Game.State ( notificationsCount, notificationsContent, + -- ** Launch parameters + LaunchParams, + ValidatedLaunchParams, + -- ** GameState initialization GameStateConfig (..), initGameState, @@ -164,6 +168,7 @@ import Swarm.Game.Recipe ( ) import Swarm.Game.Robot import Swarm.Game.Scenario.Objective +import Swarm.Game.Scenario.Status import Swarm.Game.ScenarioInfo import Swarm.Game.Terrain (TerrainType (..)) import Swarm.Game.World (Coords (..), WorldFun (..), locToCoords, worldFunFromArray) @@ -941,6 +946,13 @@ deleteRobot rn = do -- Initialization ------------------------------------------------------------ +type LaunchParams a = ParameterizableLaunchParams CodeToRun a + +-- | In this stage in the UI pipeline, both fields +-- have already been validated, and "Nothing" means +-- that the field is simply absent. +type ValidatedLaunchParams = LaunchParams Identity + -- | Record to pass information needed to create an initial -- 'GameState' record when starting a scenario. data GameStateConfig = GameStateConfig @@ -1001,11 +1013,10 @@ initGameState gsc = -- | Create an initial game state corresponding to the given scenario. scenarioToGameState :: Scenario -> - Maybe Seed -> - Maybe CodeToRun -> + ValidatedLaunchParams -> GameStateConfig -> IO GameState -scenarioToGameState scenario userSeed toRun gsc = do +scenarioToGameState scenario (LaunchParams (Identity userSeed) (Identity toRun)) gsc = do -- Decide on a seed. In order of preference, we will use: -- 1. seed value provided by the user -- 2. seed value specified in the scenario description diff --git a/src/Swarm/TUI/Controller.hs b/src/Swarm/TUI/Controller.hs index fb1d7cad9..98c906f42 100644 --- a/src/Swarm/TUI/Controller.hs +++ b/src/Swarm/TUI/Controller.hs @@ -859,7 +859,13 @@ updateUI = do goalOrWinUpdated <- doGoalUpdates - let redraw = g ^. needsRedraw || inventoryUpdated || replUpdated || logUpdated || infoPanelUpdated || goalOrWinUpdated + let redraw = + g ^. needsRedraw + || inventoryUpdated + || replUpdated + || logUpdated + || infoPanelUpdated + || goalOrWinUpdated pure redraw -- | Either pops up the updated Goals modal @@ -942,7 +948,9 @@ doGoalUpdates = do -- automatically popped up. gameState . announcementQueue .= mempty - openModal GoalModal + isAutoplaying <- use $ uiState . uiIsAutoplay + unless isAutoplaying $ + openModal GoalModal return goalWasUpdated diff --git a/src/Swarm/TUI/Launch/Model.hs b/src/Swarm/TUI/Launch/Model.hs index 5938b9e0a..7c4b286cc 100644 --- a/src/Swarm/TUI/Launch/Model.hs +++ b/src/Swarm/TUI/Launch/Model.hs @@ -15,20 +15,13 @@ import Control.Lens (makeLenses) import Data.Functor.Identity (Identity (Identity)) import Data.Text (Text) import Swarm.Game.Scenario.Status (ParameterizableLaunchParams (LaunchParams), ScenarioInfoPair, SerializableLaunchParams) -import Swarm.Game.State (CodeToRun, getRunCodePath, parseCodeFile) +import Swarm.Game.State (CodeToRun, LaunchParams, ValidatedLaunchParams, getRunCodePath, parseCodeFile) import Swarm.TUI.Model.Name -type LaunchParams a = ParameterizableLaunchParams CodeToRun a - -- | Use this to store error messages -- on individual fields type EditingLaunchParams = LaunchParams (Either Text) --- | In this stage in the UI pipeline, both fields --- have already been validated, and "Nothing" means --- that the field is simply absent. -type ValidatedLaunchParams = LaunchParams Identity - toSerializableParams :: ValidatedLaunchParams -> SerializableLaunchParams toSerializableParams (LaunchParams seedValue (Identity codeToRun)) = LaunchParams seedValue $ pure $ getRunCodePath =<< codeToRun diff --git a/src/Swarm/TUI/Launch/Prep.hs b/src/Swarm/TUI/Launch/Prep.hs index 2a41bb3a8..9c194300f 100644 --- a/src/Swarm/TUI/Launch/Prep.hs +++ b/src/Swarm/TUI/Launch/Prep.hs @@ -18,7 +18,7 @@ import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Functor.Identity (runIdentity) import Data.Text qualified as T import Swarm.Game.Scenario.Status (ParameterizableLaunchParams (..), ScenarioInfoPair, getLaunchParams, scenarioStatus) -import Swarm.Game.State (Seed, getRunCodePath) +import Swarm.Game.State (Seed, ValidatedLaunchParams, getRunCodePath) import Swarm.TUI.Launch.Model import Swarm.TUI.Model.Name import Swarm.Util (listEnums) diff --git a/src/Swarm/TUI/Model/StateUpdate.hs b/src/Swarm/TUI/Model/StateUpdate.hs index 1d6762354..228a5372e 100644 --- a/src/Swarm/TUI/Model/StateUpdate.hs +++ b/src/Swarm/TUI/Model/StateUpdate.hs @@ -48,7 +48,7 @@ import Swarm.TUI.Attr (swarmAttrMap) import Swarm.TUI.Editor.Model qualified as EM import Swarm.TUI.Editor.Util qualified as EU import Swarm.TUI.Inventory.Sorting -import Swarm.TUI.Launch.Model (ValidatedLaunchParams, toSerializableParams) +import Swarm.TUI.Launch.Model (toSerializableParams) import Swarm.TUI.Model import Swarm.TUI.Model.Goal (emptyGoalDisplay) import Swarm.TUI.Model.Repl @@ -110,7 +110,7 @@ startGameWithSeed :: ScenarioInfoPair -> ValidatedLaunchParams -> m () -startGameWithSeed siPair@(_scene, si) lp@(LaunchParams (Identity userSeed) (Identity toRun)) = do +startGameWithSeed siPair@(_scene, si) lp = do t <- liftIO getZonedTime ss <- use $ runtimeState . scenarios p <- liftIO $ normalizeScenarioPath ss (si ^. scenarioPath) @@ -124,7 +124,7 @@ startGameWithSeed siPair@(_scene, si) lp@(LaunchParams (Identity userSeed) (Iden (toSerializableParams lp) (Metric Attempted $ ProgressStats t emptyAttemptMetric) (prevBest t) - scenarioToAppState siPair userSeed toRun + scenarioToAppState siPair lp -- Beware: currentScenarioPath must be set so that progress/achievements can be saved. -- It has just been cleared in scenarioToAppState. gameState . currentScenarioPath .= Just p @@ -137,15 +137,18 @@ startGameWithSeed siPair@(_scene, si) lp@(LaunchParams (Identity userSeed) (Iden scenarioToAppState :: (MonadIO m, MonadState AppState m) => ScenarioInfoPair -> - Maybe Seed -> - Maybe CodeToRun -> + ValidatedLaunchParams -> m () -scenarioToAppState siPair@(scene, _) userSeed toRun = do +scenarioToAppState siPair@(scene, _) lp = do rs <- use runtimeState - gs <- liftIO $ scenarioToGameState scene userSeed toRun (mkGameStateConfig rs) + gs <- liftIO $ scenarioToGameState scene lp $ mkGameStateConfig rs gameState .= gs - void $ withLensIO uiState $ scenarioToUIState siPair gs + void $ withLensIO uiState $ scenarioToUIState isAutoplaying siPair gs where + isAutoplaying = case runIdentity (initialCode lp) of + Just (CodeToRun ScenarioSuggested _) -> True + _ -> False + withLensIO :: (MonadIO m, MonadState AppState m) => Lens' AppState x -> (x -> IO x) -> m x withLensIO l a = do x <- use l @@ -174,13 +177,19 @@ attainAchievement' t p a = do liftIO $ saveAchievementsInfo $ M.elems newAchievements -- | Modify the UI state appropriately when starting a new scenario. -scenarioToUIState :: ScenarioInfoPair -> GameState -> UIState -> IO UIState -scenarioToUIState siPair@(scenario, _) gs u = do +scenarioToUIState :: + Bool -> + ScenarioInfoPair -> + GameState -> + UIState -> + IO UIState +scenarioToUIState isAutoplaying siPair@(scenario, _) gs u = do curTime <- getTime Monotonic return $ u & uiPlaying .~ True & uiGoal .~ emptyGoalDisplay + & uiIsAutoplay .~ isAutoplaying & uiFocusRing .~ initFocusRing & uiInventory .~ Nothing & uiInventorySort .~ defaultSortOptions diff --git a/src/Swarm/TUI/Model/UI.hs b/src/Swarm/TUI/Model/UI.hs index 8bff1ebf3..ff046196b 100644 --- a/src/Swarm/TUI/Model/UI.hs +++ b/src/Swarm/TUI/Model/UI.hs @@ -26,6 +26,7 @@ module Swarm.TUI.Model.UI ( uiError, uiModal, uiGoal, + uiIsAutoplay, uiAchievements, lgTicksPerSecond, lastFrameTime, @@ -111,6 +112,7 @@ data UIState = UIState , _uiError :: Maybe Text , _uiModal :: Maybe Modal , _uiGoal :: GoalDisplay + , _uiIsAutoplay :: Bool , _uiAchievements :: Map CategorizedAchievement Attainment , _uiShowFPS :: Bool , _uiShowREPL :: Bool @@ -197,6 +199,9 @@ uiModal :: Lens' UIState (Maybe Modal) -- has been displayed to the user initially. uiGoal :: Lens' UIState GoalDisplay +-- | When running with --autoplay, suppress the goal dialogs +uiIsAutoplay :: Lens' UIState Bool + -- | Map of achievements that were attained uiAchievements :: Lens' UIState (Map CategorizedAchievement Attainment) @@ -326,6 +331,7 @@ initUIState speedFactor showMainMenu cheatMode = do , _uiError = Nothing , _uiModal = Nothing , _uiGoal = emptyGoalDisplay + , _uiIsAutoplay = False , _uiAchievements = M.fromList $ map (view achievement &&& id) achievements , _uiShowFPS = False , _uiShowREPL = True