From a234d8e8022c9b2cda7f14bc7df9c2d3905bbb3a Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Sat, 19 Oct 2019 00:58:41 -0400 Subject: [PATCH 01/33] removed (redundant) gsGoalsAgainst --- src/Mtlstats/Types.hs | 23 ++++++++--------------- test/ActionsSpec.hs | 3 --- test/TypesSpec.hs | 14 ++++++-------- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/Mtlstats/Types.hs b/src/Mtlstats/Types.hs index 14bbfde..fb0eedc 100644 --- a/src/Mtlstats/Types.hs +++ b/src/Mtlstats/Types.hs @@ -94,7 +94,6 @@ module Mtlstats.Types ( gsGames, gsMinsPlayed, gsGoalsAllowed, - gsGoalsAgainst, gsWins, gsLosses, gsTies, @@ -396,8 +395,6 @@ data GoalieStats = GoalieStats -- ^ The number of minutes played , _gsGoalsAllowed :: Int -- ^ The number of goals allowed - , _gsGoalsAgainst :: Int - -- ^ The number of goals against , _gsWins :: Int -- ^ The number of wins , _gsLosses :: Int @@ -411,28 +408,25 @@ instance FromJSON GoalieStats where <$> v .: "games" <*> v .: "mins_played" <*> v .: "goals_allowed" - <*> v .: "goals_against" <*> v .: "wins" <*> v .: "losses" <*> v .: "ties" instance ToJSON GoalieStats where - toJSON (GoalieStats g m al ag w l t) = object + toJSON (GoalieStats g m a w l t) = object [ "games" .= g , "mins_played" .= m - , "goals_allowed" .= al - , "goals_against" .= ag + , "goals_allowed" .= a , "wins" .= w , "losses" .= l , "ties" .= t ] - toEncoding (GoalieStats g m al ag w l t) = pairs $ - "games" .= g <> - "mins_played" .= m <> - "goals_allowed" .= al <> - "goals_against" .= ag <> - "wins" .= w <> - "losses" .= l <> + toEncoding (GoalieStats g m a w l t) = pairs $ + "games" .= g <> + "mins_played" .= m <> + "goals_allowed" .= a <> + "wins" .= w <> + "losses" .= l <> "ties" .= t -- | Game statistics @@ -603,7 +597,6 @@ newGoalieStats = GoalieStats { _gsGames = 0 , _gsMinsPlayed = 0 , _gsGoalsAllowed = 0 - , _gsGoalsAgainst = 0 , _gsWins = 0 , _gsLosses = 0 , _gsTies = 0 diff --git a/test/ActionsSpec.hs b/test/ActionsSpec.hs index b45da09..bb09dea 100644 --- a/test/ActionsSpec.hs +++ b/test/ActionsSpec.hs @@ -117,14 +117,12 @@ resetYtdSpec = describe "resetYtd" $ ytd ^. gsGames `shouldBe` 0 ytd ^. gsMinsPlayed `shouldBe` 0 ytd ^. gsGoalsAllowed `shouldBe` 0 - ytd ^. gsGoalsAgainst `shouldBe` 0 ytd ^. gsWins `shouldBe` 0 ytd ^. gsLosses `shouldBe` 0 ytd ^. gsTies `shouldBe` 0 lt ^. gsGames `shouldNotBe` 0 lt ^. gsMinsPlayed `shouldNotBe` 0 lt ^. gsGoalsAllowed `shouldNotBe` 0 - lt ^. gsGoalsAgainst `shouldNotBe` 0 lt ^. gsWins `shouldNotBe` 0 lt ^. gsLosses `shouldNotBe` 0 lt ^. gsTies `shouldNotBe` 0) $ @@ -647,7 +645,6 @@ makeGoalieStats = GoalieStats <*> makeNum <*> makeNum <*> makeNum - <*> makeNum makeNum :: IO Int makeNum = randomRIO (1, 10) diff --git a/test/TypesSpec.hs b/test/TypesSpec.hs index b8bd0bb..1a9a00b 100644 --- a/test/TypesSpec.hs +++ b/test/TypesSpec.hs @@ -241,20 +241,18 @@ goalieStats n = newGoalieStats & gsGames .~ n & gsMinsPlayed .~ n + 1 & gsGoalsAllowed .~ n + 2 - & gsGoalsAgainst .~ n + 3 - & gsWins .~ n + 4 - & gsLosses .~ n + 5 - & gsTies .~ n + 6 + & gsWins .~ n + 3 + & gsLosses .~ n + 4 + & gsTies .~ n + 5 goalieStatsJSON :: Int -> Value goalieStatsJSON n = Object $ HM.fromList [ ( "games", toJSON n ) , ( "mins_played", toJSON $ n + 1 ) , ( "goals_allowed", toJSON $ n + 2 ) - , ( "goals_against", toJSON $ n + 3 ) - , ( "wins", toJSON $ n + 4 ) - , ( "losses", toJSON $ n + 5 ) - , ( "ties", toJSON $ n + 6 ) + , ( "wins", toJSON $ n + 3 ) + , ( "losses", toJSON $ n + 4 ) + , ( "ties", toJSON $ n + 5 ) ] gameStats :: Int -> GameStats From 1782c0bc48d607b9ea17b4b3812c7981cdfb5863 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Mon, 21 Oct 2019 23:53:41 -0400 Subject: [PATCH 02/33] implemented CreateGoalieState type --- src/Mtlstats/Types.hs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/Mtlstats/Types.hs b/src/Mtlstats/Types.hs index fb0eedc..a9bacc9 100644 --- a/src/Mtlstats/Types.hs +++ b/src/Mtlstats/Types.hs @@ -30,6 +30,7 @@ module Mtlstats.Types ( GameState (..), GameType (..), CreatePlayerState (..), + CreateGoalieState (..), Database (..), Player (..), PlayerStats (..), @@ -69,6 +70,11 @@ module Mtlstats.Types ( cpsPosition, cpsSuccessCallback, cpsFailureCallback, + -- ** CreateGoalieState Lenses + cgsNumber, + cgsName, + cgsSuccessCallback, + cgsFailureCallback, -- ** Database Lenses dbPlayers, dbGoalies, @@ -107,6 +113,7 @@ module Mtlstats.Types ( newProgState, newGameState, newCreatePlayerState, + newCreateGoalieState, newDatabase, newPlayer, newPlayerStats, @@ -254,6 +261,18 @@ data CreatePlayerState = CreatePlayerState -- ^ The function to call on failure } +-- | Goalie creation status +data CreateGoalieState = CreateGoalieState + { _cgsNumber :: Maybe Int + -- ^ The goalie's number + , _cgsName :: String + -- ^ The goalie's name + , _cgsSuccessCallback :: Action () + -- ^ The function to call on success + , _cgsFailureCallback :: Action () + -- ^ The function to call on failure + } + -- | Represents the database data Database = Database { _dbPlayers :: [Player] @@ -481,6 +500,7 @@ data Prompt = Prompt makeLenses ''ProgState makeLenses ''GameState makeLenses ''CreatePlayerState +makeLenses ''CreateGoalieState makeLenses ''Database makeLenses ''Player makeLenses ''PlayerStats @@ -542,6 +562,15 @@ newCreatePlayerState = CreatePlayerState , _cpsFailureCallback = return () } +-- | Constructor for a 'CreateGoalieState' +newCreateGoalieState :: CreateGoalieState +newCreateGoalieState = CreateGoalieState + { _cgsNumber = Nothing + , _cgsName = "" + , _cgsSuccessCallback = return () + , _cgsFailureCallback = return () + } + -- | Constructor for a 'Database' newDatabase :: Database newDatabase = Database From 2a94e99371f54d271d23fb29f958a1864c6f9575 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 22 Oct 2019 00:21:51 -0400 Subject: [PATCH 03/33] allow ProgMode to handle goalie creation --- src/Mtlstats/Control.hs | 13 +++++++++++++ src/Mtlstats/Types.hs | 2 ++ 2 files changed, 15 insertions(+) diff --git a/src/Mtlstats/Control.hs b/src/Mtlstats/Control.hs index 3f7fba2..a42f4a6 100644 --- a/src/Mtlstats/Control.hs +++ b/src/Mtlstats/Control.hs @@ -64,6 +64,10 @@ dispatch s = case s^.progMode of | null $ cps^.cpsName -> getPlayerNameC | null $ cps^.cpsPosition -> getPlayerPosC | otherwise -> confirmCreatePlayerC + CreateGoalie cgs + | null $ cgs^.cgsNumber -> getGoalieNumC + | null $ cgs^.cgsName -> getGoalieNameC + | otherwise -> confirmCreateGoalieC mainMenuC :: Controller mainMenuC = Controller @@ -336,6 +340,15 @@ confirmCreatePlayerC = Controller return True } +getGoalieNumC :: Controller +getGoalieNumC = undefined + +getGoalieNameC :: Controller +getGoalieNameC = undefined + +confirmCreateGoalieC :: Controller +confirmCreateGoalieC = undefined + gameGoal :: ProgState -> (Int, Int) gameGoal s = ( s^.database.dbGames diff --git a/src/Mtlstats/Types.hs b/src/Mtlstats/Types.hs index a9bacc9..28b1ed6 100644 --- a/src/Mtlstats/Types.hs +++ b/src/Mtlstats/Types.hs @@ -196,12 +196,14 @@ data ProgMode | NewSeason | NewGame GameState | CreatePlayer CreatePlayerState + | CreateGoalie CreateGoalieState instance Show ProgMode where show MainMenu = "MainMenu" show NewSeason = "NewSeason" show (NewGame _) = "NewGame" show (CreatePlayer _) = "CreatePlayer" + show (CreateGoalie _) = "CreateGoalie" -- | The game state data GameState = GameState From 2f0989fb35630f2ca7db29236b36f75dabf0e921 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 24 Oct 2019 00:33:01 -0400 Subject: [PATCH 04/33] created Comparable typeclass --- test/TypesSpec.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/TypesSpec.hs b/test/TypesSpec.hs index 1a9a00b..819aa0f 100644 --- a/test/TypesSpec.hs +++ b/test/TypesSpec.hs @@ -21,7 +21,7 @@ along with this program. If not, see . {-# LANGUAGE OverloadedStrings, RankNTypes #-} -module TypesSpec (spec) where +module TypesSpec (Comparable (..), spec) where import Data.Aeson (FromJSON, ToJSON, decode, encode, toJSON) import Data.Aeson.Types (Value (Object)) @@ -35,6 +35,9 @@ import Mtlstats.Types import qualified Types.MenuSpec as Menu +class Comparable a where + compareTest :: a -> a -> Spec + spec :: Spec spec = describe "Mtlstats.Types" $ do playerSpec From 24c1673fc90b4b0d63344f2946def4013d599ef2 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 24 Oct 2019 00:37:52 -0400 Subject: [PATCH 05/33] made GameState, CreatePlayerState and CreateGoalieState instances of Comparable --- test/TypesSpec.hs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/TypesSpec.hs b/test/TypesSpec.hs index 819aa0f..8b09dfe 100644 --- a/test/TypesSpec.hs +++ b/test/TypesSpec.hs @@ -642,3 +642,34 @@ bob = newPlayer 3 "Bob" "defense" steve :: Player steve = newPlayer 5 "Steve" "forward" + +instance Comparable GameState where + compareTest actual expected = + it ("should be " ++ show expected) $ + actual `shouldBe` expected + +instance Comparable CreatePlayerState where + compareTest actual expected = do + + describe "cpsNumber" $ + it ("should be " ++ show (expected^.cpsNumber)) $ + actual^.cpsNumber `shouldBe` expected^.cpsNumber + + describe "cpsName" $ + it ("should be " ++ expected^.cpsName) $ + actual^.cpsName `shouldBe` expected^.cpsName + + describe "cpsPosition" $ + it ("should be " ++ expected^.cpsPosition) $ + actual^.cpsPosition `shouldBe` expected^.cpsPosition + +instance Comparable CreateGoalieState where + compareTest actual expected = do + + describe "cgsNuber" $ + it("should be " ++ show (expected^.cgsNumber)) $ + actual^.cgsNumber `shouldBe` expected^.cgsNumber + + describe "cgsName" $ + it ("should be " ++ expected^.cgsName) $ + actual^.cgsName `shouldBe` expected^.cgsName From 4519ba4732bef242794dac4f58fcb1e26837681c Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 24 Oct 2019 01:10:42 -0400 Subject: [PATCH 06/33] made lensSpec more generic --- test/TypesSpec.hs | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/test/TypesSpec.hs b/test/TypesSpec.hs index 8b09dfe..807a0e1 100644 --- a/test/TypesSpec.hs +++ b/test/TypesSpec.hs @@ -82,13 +82,16 @@ databaseSpec = describe "Database" $ jsonSpec db dbJSON gameStateLSpec :: Spec gameStateLSpec = describe "gameStateL" $ lensSpec gameStateL -- getters - [ ( MainMenu, newGameState ) - , ( NewGame $ gs HomeGame, gs HomeGame ) + [ ( "missing state", MainMenu, newGameState ) + , ( "home game", NewGame $ gs HomeGame, gs HomeGame ) + , ( "away game", NewGame $ gs AwayGame, gs AwayGame ) ] -- setters - [ ( MainMenu, gs HomeGame ) - , ( NewGame $ gs HomeGame, gs AwayGame ) - , ( NewGame $ gs HomeGame, newGameState ) + [ ( "set home", MainMenu, gs HomeGame ) + , ( "home to away", NewGame $ gs HomeGame, gs AwayGame ) + , ( "away to home", NewGame $ gs AwayGame, gs HomeGame ) + , ( "clear home", NewGame $ gs HomeGame, newGameState ) + , ( "clear away", NewGame $ gs AwayGame, newGameState ) ] where gs t = newGameState & gameType ?~ t @@ -180,24 +183,23 @@ jsonSpec x j = do decode (encode x) `shouldBe` Just x lensSpec - :: (Eq a, Show s, Show a) + :: Comparable a => Lens' s a - -> [(s, a)] - -> [(s, a)] + -> [(String, s, a)] + -> [(String, s, a)] -> Spec -lensSpec l gs ss = do +lensSpec lens getters setters = do context "getters" $ mapM_ - (\(s, x) -> context (show s) $ - it ("should be " ++ show x) $ - s ^. l `shouldBe` x) - gs + (\(label, s, x) -> context label $ + compareTest (s^.lens) x) + getters context "setters" $ mapM_ - (\(s, x) -> context (show s) $ - it ("should set to " ++ show x) $ - (s & l .~ x) ^. l `shouldBe` x) - ss + (\(label, s, x) -> context label $ let + s' = s & lens .~ x + in compareTest (s'^.lens) x) + setters player :: Player player = newPlayer 1 "Joe" "centre" From 09c63da8bfedfc25f0f414c5f3e430fd57be81c5 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 24 Oct 2019 01:40:53 -0400 Subject: [PATCH 07/33] refactored createPlayerStateLSpec to use lensSpec --- test/TypesSpec.hs | 45 +++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/test/TypesSpec.hs b/test/TypesSpec.hs index 807a0e1..f9fe798 100644 --- a/test/TypesSpec.hs +++ b/test/TypesSpec.hs @@ -96,32 +96,25 @@ gameStateLSpec = describe "gameStateL" $ lensSpec gameStateL where gs t = newGameState & gameType ?~ t createPlayerStateLSpec :: Spec -createPlayerStateLSpec = describe "createPlayerStateL" $ do - context "getters" $ do - context "state missing" $ let - pm = MainMenu - cps = pm^.createPlayerStateL - in it "should not have a number" $ - cps^.cpsNumber `shouldBe` Nothing - - context "existing state" $ let - pm = CreatePlayer $ newCreatePlayerState & cpsNumber ?~ 1 - cps = pm^.createPlayerStateL - in it "should have a number of 1" $ - cps^.cpsNumber `shouldBe` Just 1 - - context "setters" $ do - context "state missing" $ let - pm = MainMenu - pm' = pm & createPlayerStateL.cpsNumber ?~ 1 - in it "should set the player number to 1" $ - pm'^.createPlayerStateL.cpsNumber `shouldBe` Just 1 - - context "existing state" $ let - pm = CreatePlayer $ newCreatePlayerState & cpsNumber ?~ 1 - pm' = pm & createPlayerStateL.cpsNumber ?~ 2 - in it "should set the player number to 2" $ - pm'^.createPlayerStateL.cpsNumber `shouldBe` Just 2 +createPlayerStateLSpec = describe "createPlayerStateL" $ lensSpec createPlayerStateL + -- getters + [ ( "missing state", MainMenu, newCreatePlayerState ) + , ( "with state", CreatePlayer cps1, cps1 ) + ] + -- setters + [ ( "missing state", MainMenu, cps1 ) + , ( "change state", CreatePlayer cps1, cps2 ) + , ( "clear state", CreatePlayer cps1, newCreatePlayerState ) + ] + where + cps1 = newCreatePlayerState + & cpsNumber ?~ 1 + & cpsName .~ "Joe" + & cpsPosition .~ "centre" + cps2 = newCreatePlayerState + & cpsNumber ?~ 2 + & cpsName .~ "Bob" + & cpsPosition .~ "defense" teamScoreSpec :: Spec teamScoreSpec = describe "teamScore" $ do From ceb8132a13b56a59bf3ca87d1daadfd9ea2f1b9d Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 24 Oct 2019 09:47:48 -0400 Subject: [PATCH 08/33] broke long line --- test/TypesSpec.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/TypesSpec.hs b/test/TypesSpec.hs index f9fe798..f07069f 100644 --- a/test/TypesSpec.hs +++ b/test/TypesSpec.hs @@ -96,7 +96,8 @@ gameStateLSpec = describe "gameStateL" $ lensSpec gameStateL where gs t = newGameState & gameType ?~ t createPlayerStateLSpec :: Spec -createPlayerStateLSpec = describe "createPlayerStateL" $ lensSpec createPlayerStateL +createPlayerStateLSpec = describe "createPlayerStateL" $ + lensSpec createPlayerStateL -- getters [ ( "missing state", MainMenu, newCreatePlayerState ) , ( "with state", CreatePlayer cps1, cps1 ) From e94bf59c8163481153206104f5da0b0a11fe25ea Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 24 Oct 2019 09:48:47 -0400 Subject: [PATCH 09/33] implemented createGoalieStateL --- src/Mtlstats/Types.hs | 8 ++++++++ test/TypesSpec.hs | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/Mtlstats/Types.hs b/src/Mtlstats/Types.hs index 28b1ed6..9b90e19 100644 --- a/src/Mtlstats/Types.hs +++ b/src/Mtlstats/Types.hs @@ -47,6 +47,7 @@ module Mtlstats.Types ( -- ** ProgMode Lenses gameStateL, createPlayerStateL, + createGoalieStateL, -- ** GameState Lenses gameYear, gameMonth, @@ -524,6 +525,13 @@ createPlayerStateL = lens _ -> newCreatePlayerState) (\_ cps -> CreatePlayer cps) +createGoalieStateL :: Lens' ProgMode CreateGoalieState +createGoalieStateL = lens + (\case + CreateGoalie cgs -> cgs + _ -> newCreateGoalieState) + (\_ cgs -> CreateGoalie cgs) + -- | Constructor for a 'ProgState' newProgState :: ProgState newProgState = ProgState diff --git a/test/TypesSpec.hs b/test/TypesSpec.hs index f07069f..cbaf831 100644 --- a/test/TypesSpec.hs +++ b/test/TypesSpec.hs @@ -46,6 +46,7 @@ spec = describe "Mtlstats.Types" $ do databaseSpec gameStateLSpec createPlayerStateLSpec + createGoalieStateLSpec teamScoreSpec otherScoreSpec homeTeamSpec @@ -117,6 +118,26 @@ createPlayerStateLSpec = describe "createPlayerStateL" $ & cpsName .~ "Bob" & cpsPosition .~ "defense" +createGoalieStateLSpec :: Spec +createGoalieStateLSpec = describe "createGoalieStateL" $ + lensSpec createGoalieStateL + -- getters + [ ( "missing state", MainMenu, newCreateGoalieState ) + , ( "with state", CreateGoalie cgs1, cgs1 ) + ] + -- setters + [ ( "set state", MainMenu, cgs1 ) + , ( "change state", CreateGoalie cgs1, cgs2 ) + , ( "clear state", CreateGoalie cgs1, newCreateGoalieState ) + ] + where + cgs1 = newCreateGoalieState + & cgsNumber ?~ 1 + & cgsName .~ "Joe" + cgs2 = newCreateGoalieState + & cgsNumber ?~ 2 + & cgsName .~ "Bob" + teamScoreSpec :: Spec teamScoreSpec = describe "teamScore" $ do let From 67bb12920c675761a97a527be675bbe6785607d3 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Fri, 25 Oct 2019 00:37:14 -0400 Subject: [PATCH 10/33] added goalie creation to main menu --- src/Mtlstats/Actions.hs | 19 ++++++++++++++----- src/Mtlstats/Menu.hs | 4 +++- test/ActionsSpec.hs | 7 +++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Mtlstats/Actions.hs b/src/Mtlstats/Actions.hs index 7a9b686..fcd240e 100644 --- a/src/Mtlstats/Actions.hs +++ b/src/Mtlstats/Actions.hs @@ -31,6 +31,7 @@ module Mtlstats.Actions , updateGameStats , validateGameDate , createPlayer + , createGoalie , addPlayer , recordGoalAssists , awardGoal @@ -139,13 +140,21 @@ validateGameDate s = fromMaybe s $ do -- | Starts player creation mode createPlayer :: ProgState -> ProgState createPlayer = let - cb = modify $ progMode .~ MainMenu - cps - = newCreatePlayerState - & cpsSuccessCallback .~ cb - & cpsFailureCallback .~ cb + callback = modify $ progMode .~ MainMenu + cps = newCreatePlayerState + & cpsSuccessCallback .~ callback + & cpsFailureCallback .~ callback in progMode .~ CreatePlayer cps +-- | Starts goalie creation mode +createGoalie :: ProgState -> ProgState +createGoalie = let + callback = modify $ progMode .~ MainMenu + cgs = newCreateGoalieState + & cgsSuccessCallback .~ callback + & cgsFailureCallback .~ callback + in progMode .~ CreateGoalie cgs + -- | Adds the entered player to the roster addPlayer :: ProgState -> ProgState addPlayer s = fromMaybe s $ do diff --git a/src/Mtlstats/Menu.hs b/src/Mtlstats/Menu.hs index d3a6f62..d1521f7 100644 --- a/src/Mtlstats/Menu.hs +++ b/src/Mtlstats/Menu.hs @@ -71,7 +71,9 @@ mainMenu = Menu "*** MAIN MENU ***" True modify startNewGame >> return True , MenuItem '3' "Create Player" $ modify createPlayer >> return True - , MenuItem '4' "Exit" $ do + , MenuItem '4' "Create Goalie" $ + modify createGoalie >> return True + , MenuItem '5' "Exit" $ do db <- gets $ view database liftIO $ do dir <- getAppUserDataDirectory appName diff --git a/test/ActionsSpec.hs b/test/ActionsSpec.hs index bb09dea..343e442 100644 --- a/test/ActionsSpec.hs +++ b/test/ActionsSpec.hs @@ -54,6 +54,7 @@ spec = describe "Mtlstats.Actions" $ do updateGameStatsSpec validateGameDateSpec createPlayerSpec + createGoalieSpec addPlayerSpec recordGoalAssistsSpec awardGoalSpec @@ -353,6 +354,12 @@ createPlayerSpec = describe "createPlayer" $ s = createPlayer newProgState in show (s^.progMode) `shouldBe` "CreatePlayer" +createGoalieSpec :: Spec +createGoalieSpec = describe "createGoalie" $ + it "should change the mode appropriately" $ let + s = createGoalie newProgState + in show (s^.progMode) `shouldBe` "CreateGoalie" + addPlayerSpec :: Spec addPlayerSpec = describe "addPlayer" $ do let From a9b5ada114efe4d9c99fa17ae2a825478b377e45 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 24 Oct 2019 15:53:24 -0400 Subject: [PATCH 11/33] implemented getGoalieNumC --- src/Mtlstats/Control.hs | 7 ++++++- src/Mtlstats/Prompt.hs | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Mtlstats/Control.hs b/src/Mtlstats/Control.hs index a42f4a6..11653ec 100644 --- a/src/Mtlstats/Control.hs +++ b/src/Mtlstats/Control.hs @@ -341,7 +341,12 @@ confirmCreatePlayerC = Controller } getGoalieNumC :: Controller -getGoalieNumC = undefined +getGoalieNumC = Controller + { drawController = drawPrompt goalieNumPrompt + , handleController = \e -> do + promptHandler goalieNumPrompt e + return True + } getGoalieNameC :: Controller getGoalieNameC = undefined diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 16570ae..f34ddc5 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -40,7 +40,8 @@ module Mtlstats.Prompt ( recordGoalPrompt, recordAssistPrompt, pMinPlayerPrompt, - assignPMinsPrompt + assignPMinsPrompt, + goalieNumPrompt ) where import Control.Monad (when) @@ -245,5 +246,8 @@ assignPMinsPrompt :: Prompt assignPMinsPrompt = numPrompt "Penalty minutes: " $ modify . assignPMins +goalieNumPrompt :: Prompt +goalieNumPrompt = undefined + drawSimplePrompt :: String -> ProgState -> C.Update () drawSimplePrompt pStr s = C.drawString $ pStr ++ s^.inputBuffer From ec914a38b15b13bf39b6afff7282696e2b88523d Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Fri, 25 Oct 2019 00:11:26 -0400 Subject: [PATCH 12/33] implemented goalieNumPrompt --- src/Mtlstats/Prompt.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index f34ddc5..fd5e490 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -247,7 +247,8 @@ assignPMinsPrompt = numPrompt "Penalty minutes: " $ modify . assignPMins goalieNumPrompt :: Prompt -goalieNumPrompt = undefined +goalieNumPrompt = numPrompt "Goalie number: " $ + modify . (progMode.createGoalieStateL.cgsNumber ?~) drawSimplePrompt :: String -> ProgState -> C.Update () drawSimplePrompt pStr s = C.drawString $ pStr ++ s^.inputBuffer From 0812ae3ddde1565186a267e45c77e852442e37a7 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Fri, 25 Oct 2019 00:45:39 -0400 Subject: [PATCH 13/33] implemented goalie name prompt --- src/Mtlstats/Control.hs | 7 ++++++- src/Mtlstats/Prompt.hs | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Mtlstats/Control.hs b/src/Mtlstats/Control.hs index 11653ec..d3b6f54 100644 --- a/src/Mtlstats/Control.hs +++ b/src/Mtlstats/Control.hs @@ -349,7 +349,12 @@ getGoalieNumC = Controller } getGoalieNameC :: Controller -getGoalieNameC = undefined +getGoalieNameC = Controller + { drawController = drawPrompt goalieNamePrompt + , handleController = \e -> do + promptHandler goalieNamePrompt e + return True + } confirmCreateGoalieC :: Controller confirmCreateGoalieC = undefined diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index fd5e490..4d3ae45 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -41,7 +41,8 @@ module Mtlstats.Prompt ( recordAssistPrompt, pMinPlayerPrompt, assignPMinsPrompt, - goalieNumPrompt + goalieNumPrompt, + goalieNamePrompt ) where import Control.Monad (when) @@ -250,5 +251,9 @@ goalieNumPrompt :: Prompt goalieNumPrompt = numPrompt "Goalie number: " $ modify . (progMode.createGoalieStateL.cgsNumber ?~) +goalieNamePrompt :: Prompt +goalieNamePrompt = strPrompt "Goalie name: " $ + modify . (progMode.createGoalieStateL.cgsName .~) + drawSimplePrompt :: String -> ProgState -> C.Update () drawSimplePrompt pStr s = C.drawString $ pStr ++ s^.inputBuffer From ed31ce5b1d98e9c825a3401b2474e44f4edddb60 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Fri, 25 Oct 2019 00:48:28 -0400 Subject: [PATCH 14/33] added missing documentation comments --- src/Mtlstats/Prompt.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 4d3ae45..5375193 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -236,6 +236,7 @@ recordAssistPrompt game goal assist = selectPlayerPrompt when (nAssists >= maxAssists) $ modify $ progMode.gameStateL.confirmGoalDataFlag .~ True +-- | Prompts for the player to assign penalty minutes to pMinPlayerPrompt :: Prompt pMinPlayerPrompt = selectPlayerPrompt "Assign penalty minutes to: " $ @@ -243,14 +244,17 @@ pMinPlayerPrompt = selectPlayerPrompt Nothing -> modify $ progMode.gameStateL.pMinsRecorded .~ True Just n -> modify $ progMode.gameStateL.selectedPlayer ?~ n +-- | Prompts for the number of penalty mintues to assign to the player assignPMinsPrompt :: Prompt assignPMinsPrompt = numPrompt "Penalty minutes: " $ modify . assignPMins +-- | Prompts tor the goalie's number goalieNumPrompt :: Prompt goalieNumPrompt = numPrompt "Goalie number: " $ modify . (progMode.createGoalieStateL.cgsNumber ?~) +-- | Prompts for the goalie's name goalieNamePrompt :: Prompt goalieNamePrompt = strPrompt "Goalie name: " $ modify . (progMode.createGoalieStateL.cgsName .~) From 2d2ee61aae7816c2a3c20be4074d114004d2907f Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Fri, 25 Oct 2019 01:07:04 -0400 Subject: [PATCH 15/33] implemented confirmCreateGoalieC --- src/Mtlstats/Actions.hs | 5 +++++ src/Mtlstats/Control.hs | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Mtlstats/Actions.hs b/src/Mtlstats/Actions.hs index fcd240e..acd03d1 100644 --- a/src/Mtlstats/Actions.hs +++ b/src/Mtlstats/Actions.hs @@ -33,6 +33,7 @@ module Mtlstats.Actions , createPlayer , createGoalie , addPlayer + , addGoalie , recordGoalAssists , awardGoal , awardAssist @@ -167,6 +168,10 @@ addPlayer s = fromMaybe s $ do Just $ s & database.dbPlayers %~ (++[player]) +-- | Adds the entered goalie to the roster +addGoalie :: ProgState -> ProgState +addGoalie = undefined + -- | Awards the goal and assists to the players recordGoalAssists :: ProgState -> ProgState recordGoalAssists ps = fromMaybe ps $ do diff --git a/src/Mtlstats/Control.hs b/src/Mtlstats/Control.hs index d3b6f54..f5690a3 100644 --- a/src/Mtlstats/Control.hs +++ b/src/Mtlstats/Control.hs @@ -357,7 +357,26 @@ getGoalieNameC = Controller } confirmCreateGoalieC :: Controller -confirmCreateGoalieC = undefined +confirmCreateGoalieC = Controller + { drawController = \s -> do + let cgs = s^.progMode.createGoalieStateL + C.drawString $ unlines + [ "Goalie number: " ++ show (fromJust $ cgs^.cgsNumber) + , " Goalie name: " ++ cgs^.cgsName + , "" + , "Create goalie: are you sure? (Y/N)" + ] + return C.CursorInvisible + , handleController = \e -> do + case ynHandler e of + Just True -> do + modify addGoalie + join $ gets (^.progMode.createGoalieStateL.cgsSuccessCallback) + Just False -> + join $ gets (^.progMode.createGoalieStateL.cgsFailureCallback) + Nothing -> return () + return True + } gameGoal :: ProgState -> (Int, Int) gameGoal s = From 667cf344756a9db34a0b52cf71a694b3ff81fc17 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Fri, 25 Oct 2019 01:44:56 -0400 Subject: [PATCH 16/33] implemented resetCreatePlayerState and resetCreateGoalieState --- src/Mtlstats/Actions.hs | 15 +++++++++++++++ test/ActionsSpec.hs | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/Mtlstats/Actions.hs b/src/Mtlstats/Actions.hs index acd03d1..4de1cd6 100644 --- a/src/Mtlstats/Actions.hs +++ b/src/Mtlstats/Actions.hs @@ -34,6 +34,8 @@ module Mtlstats.Actions , createGoalie , addPlayer , addGoalie + , resetCreatePlayerState + , resetCreateGoalieState , recordGoalAssists , awardGoal , awardAssist @@ -172,6 +174,19 @@ addPlayer s = fromMaybe s $ do addGoalie :: ProgState -> ProgState addGoalie = undefined +-- | Resets the 'CreatePlayerState' value +resetCreatePlayerState :: ProgState -> ProgState +resetCreatePlayerState = progMode.createPlayerStateL + %~ (cpsNumber .~ Nothing) + . (cpsName .~ "") + . (cpsPosition .~ "") + +-- | Resets the 'CreateGoalieState' value +resetCreateGoalieState :: ProgState -> ProgState +resetCreateGoalieState = progMode.createGoalieStateL + %~ (cgsNumber .~ Nothing) + . (cgsName .~ "") + -- | Awards the goal and assists to the players recordGoalAssists :: ProgState -> ProgState recordGoalAssists ps = fromMaybe ps $ do diff --git a/test/ActionsSpec.hs b/test/ActionsSpec.hs index 343e442..4b38d79 100644 --- a/test/ActionsSpec.hs +++ b/test/ActionsSpec.hs @@ -43,6 +43,8 @@ import Mtlstats.Actions import Mtlstats.Types import Mtlstats.Util +import qualified TypesSpec as TS + spec :: Spec spec = describe "Mtlstats.Actions" $ do startNewSeasonSpec @@ -56,6 +58,8 @@ spec = describe "Mtlstats.Actions" $ do createPlayerSpec createGoalieSpec addPlayerSpec + resetCreatePlayerStateSpec + resetCreateGoalieStateSpec recordGoalAssistsSpec awardGoalSpec awardAssistSpec @@ -384,6 +388,25 @@ addPlayerSpec = describe "addPlayer" $ do s' = addPlayer $ s MainMenu in s'^.database.dbPlayers `shouldBe` [p1] +resetCreatePlayerStateSpec :: Spec +resetCreatePlayerStateSpec = describe "resetCreatePlayerState" $ let + cps = newCreatePlayerState + & cpsNumber ?~ 1 + & cpsName .~ "Joe" + & cpsPosition .~ "centre" + ps = resetCreatePlayerState $ + newProgState & progMode.createPlayerStateL .~ cps + in TS.compareTest (ps^.progMode.createPlayerStateL) newCreatePlayerState + +resetCreateGoalieStateSpec :: Spec +resetCreateGoalieStateSpec = describe "resetCreateGoalieState" $ let + cgs = newCreateGoalieState + & cgsNumber ?~ 1 + & cgsName .~ "Joe" + ps = resetCreateGoalieState $ + newProgState & progMode.createGoalieStateL .~ cgs + in TS.compareTest (ps^.progMode.createGoalieStateL) newCreateGoalieState + recordGoalAssistsSpec :: Spec recordGoalAssistsSpec = describe "recordGoalAssists" $ do let From 66a2a70bbe2b9e5744b33253de5dbe56e5690ecd Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Sat, 26 Oct 2019 02:05:55 -0400 Subject: [PATCH 17/33] implemented addGoalie --- src/Mtlstats/Actions.hs | 9 ++++++++- test/ActionsSpec.hs | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/Mtlstats/Actions.hs b/src/Mtlstats/Actions.hs index 4de1cd6..ea00bae 100644 --- a/src/Mtlstats/Actions.hs +++ b/src/Mtlstats/Actions.hs @@ -172,7 +172,14 @@ addPlayer s = fromMaybe s $ do -- | Adds the entered goalie to the roster addGoalie :: ProgState -> ProgState -addGoalie = undefined +addGoalie s = fromMaybe s $ do + let cgs = s^.progMode.createGoalieStateL + num <- cgs^.cgsNumber + let + name = cgs^.cgsName + goalie = newGoalie num name + Just $ s & database.dbGoalies + %~ (++[goalie]) -- | Resets the 'CreatePlayerState' value resetCreatePlayerState :: ProgState -> ProgState diff --git a/test/ActionsSpec.hs b/test/ActionsSpec.hs index 4b38d79..47226e3 100644 --- a/test/ActionsSpec.hs +++ b/test/ActionsSpec.hs @@ -58,6 +58,7 @@ spec = describe "Mtlstats.Actions" $ do createPlayerSpec createGoalieSpec addPlayerSpec + addGoalieSpec resetCreatePlayerStateSpec resetCreateGoalieStateSpec recordGoalAssistsSpec @@ -388,6 +389,29 @@ addPlayerSpec = describe "addPlayer" $ do s' = addPlayer $ s MainMenu in s'^.database.dbPlayers `shouldBe` [p1] +addGoalieSpec :: Spec +addGoalieSpec = describe "addGoalie" $ do + let + g1 = newGoalie 2 "Joe" + g2 = newGoalie 3 "Bob" + db = newDatabase + & dbGoalies .~ [g1] + s pm = newProgState + & database .~ db + & progMode .~ pm + + context "data available" $ + it "should create the goalie" $ let + s' = addGoalie $ s $ CreateGoalie $ newCreateGoalieState + & cgsNumber ?~ 3 + & cgsName .~ "Bob" + in s'^.database.dbGoalies `shouldBe` [g1, g2] + + context "data unavailable" $ + it "should not create the goalie" $ let + s' = addGoalie $ s MainMenu + in s'^.database.dbGoalies `shouldBe` [g1] + resetCreatePlayerStateSpec :: Spec resetCreatePlayerStateSpec = describe "resetCreatePlayerState" $ let cps = newCreatePlayerState From c65bcbbca410b3a0d78c759a8699ee32510625b6 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Sat, 26 Oct 2019 02:33:18 -0400 Subject: [PATCH 18/33] added goalie-related fields to GameState --- src/Mtlstats/Types.hs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/Mtlstats/Types.hs b/src/Mtlstats/Types.hs index 9b90e19..eb8e849 100644 --- a/src/Mtlstats/Types.hs +++ b/src/Mtlstats/Types.hs @@ -65,6 +65,11 @@ module Mtlstats.Types ( confirmGoalDataFlag, selectedPlayer, pMinsRecorded, + gameGoalieStats, + gameSelectedGoalie, + goalieMinsPlayed, + goalsAllowed, + goaliesRecorded, -- ** CreatePlayerState Lenses cpsNumber, cpsName, @@ -242,6 +247,18 @@ data GameState = GameState -- ^ Index number of the selected 'Player' , _pMinsRecorded :: Bool -- ^ Set when the penalty mintes have been recorded + , _gameGoalieStats :: M.Map Int GoalieStats + -- ^ The goalie stats accumulated over the game + , _gameSelectedGoalie :: Maybe Int + -- ^ Index number of the selected 'Goalie' + , _goalieMinsPlayed :: Maybe Int + -- ^ The number of minutes the currently selected goalie played in + -- the game + , _goalsAllowed :: Maybe Int + -- ^ The number of goals the currently selected goalie allowed in + -- the game + , _goaliesRecorded :: Bool + -- ^ Set when the user confirms that all goalie info has been entered } deriving (Eq, Show) -- | The type of game @@ -560,6 +577,11 @@ newGameState = GameState , _confirmGoalDataFlag = False , _selectedPlayer = Nothing , _pMinsRecorded = False + , _gameGoalieStats = M.empty + , _gameSelectedGoalie = Nothing + , _goalieMinsPlayed = Nothing + , _goalsAllowed = Nothing + , _goaliesRecorded = False } -- | Constructor for a 'CreatePlayerState' From 28a29e2f641f2510e05830609cf05a7f8476cfb3 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 29 Oct 2019 02:00:14 -0400 Subject: [PATCH 19/33] control flow branch for goalie input --- src/Mtlstats/Control.hs | 2 ++ src/Mtlstats/Control/GoalieInput.hs | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/Mtlstats/Control/GoalieInput.hs diff --git a/src/Mtlstats/Control.hs b/src/Mtlstats/Control.hs index f5690a3..688f788 100644 --- a/src/Mtlstats/Control.hs +++ b/src/Mtlstats/Control.hs @@ -31,6 +31,7 @@ import Lens.Micro.Extras (view) import qualified UI.NCurses as C import Mtlstats.Actions +import Mtlstats.Control.GoalieInput import Mtlstats.Format import Mtlstats.Handlers import Mtlstats.Menu @@ -58,6 +59,7 @@ dispatch s = case s^.progMode of | fromJust (unaccountedPoints gs) -> goalInput gs | isJust $ gs^.selectedPlayer -> getPMinsC | not $ gs^.pMinsRecorded -> pMinPlayerC + | not $ gs^.goaliesRecorded -> goalieInput gs | otherwise -> reportC CreatePlayer cps | null $ cps^.cpsNumber -> getPlayerNumC diff --git a/src/Mtlstats/Control/GoalieInput.hs b/src/Mtlstats/Control/GoalieInput.hs new file mode 100644 index 0000000..4dad19e --- /dev/null +++ b/src/Mtlstats/Control/GoalieInput.hs @@ -0,0 +1,28 @@ +{- | + +mtlstats +Copyright (C) 2019 Rhéal Lamothe + + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +-} + +module Mtlstats.Control.GoalieInput (goalieInput) where + +import Mtlstats.Types + +-- | The dispatcher for handling goalie input +goalieInput :: GameState -> Controller +goalieInput = undefined From 8ef1c6917abe8b6ebba5171795cff5f13370b808 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 29 Oct 2019 02:40:17 -0400 Subject: [PATCH 20/33] implemented goalieInput dispatcher --- src/Mtlstats/Control/GoalieInput.hs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Mtlstats/Control/GoalieInput.hs b/src/Mtlstats/Control/GoalieInput.hs index 4dad19e..195f806 100644 --- a/src/Mtlstats/Control/GoalieInput.hs +++ b/src/Mtlstats/Control/GoalieInput.hs @@ -21,8 +21,22 @@ along with this program. If not, see . module Mtlstats.Control.GoalieInput (goalieInput) where +import Lens.Micro ((^.)) + import Mtlstats.Types -- | The dispatcher for handling goalie input goalieInput :: GameState -> Controller -goalieInput = undefined +goalieInput gs + | null $ gs^.gameSelectedGoalie = selectGoalieC + | null $ gs^.goalieMinsPlayed = minsPlayedC + | otherwise = goalsAllowedC + +selectGoalieC :: Controller +selectGoalieC = undefined + +minsPlayedC :: Controller +minsPlayedC = undefined + +goalsAllowedC :: Controller +goalsAllowedC = undefined From 6c634cd3667355232cacfbc3a2ebfdeef0a893c4 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Tue, 29 Oct 2019 02:51:20 -0400 Subject: [PATCH 21/33] implemented Mtlstats.Control.GoalieInput.selectGoalieC --- src/Mtlstats/Control/GoalieInput.hs | 14 ++++++++++---- src/Mtlstats/Prompt.hs | 7 ++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Mtlstats/Control/GoalieInput.hs b/src/Mtlstats/Control/GoalieInput.hs index 195f806..12b6d69 100644 --- a/src/Mtlstats/Control/GoalieInput.hs +++ b/src/Mtlstats/Control/GoalieInput.hs @@ -23,17 +23,23 @@ module Mtlstats.Control.GoalieInput (goalieInput) where import Lens.Micro ((^.)) +import Mtlstats.Prompt import Mtlstats.Types -- | The dispatcher for handling goalie input goalieInput :: GameState -> Controller goalieInput gs - | null $ gs^.gameSelectedGoalie = selectGoalieC - | null $ gs^.goalieMinsPlayed = minsPlayedC - | otherwise = goalsAllowedC + | null $ gs^.gameSelectedGoalie = selectGoalieC + | null $ gs^.goalieMinsPlayed = minsPlayedC + | otherwise = goalsAllowedC selectGoalieC :: Controller -selectGoalieC = undefined +selectGoalieC = Controller + { drawController = drawPrompt selectGameGoaliePrompt + , handleController = \e -> do + promptHandler selectGameGoaliePrompt e + return True + } minsPlayedC :: Controller minsPlayedC = undefined diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 5375193..c16d799 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -42,7 +42,8 @@ module Mtlstats.Prompt ( pMinPlayerPrompt, assignPMinsPrompt, goalieNumPrompt, - goalieNamePrompt + goalieNamePrompt, + selectGameGoaliePrompt ) where import Control.Monad (when) @@ -259,5 +260,9 @@ goalieNamePrompt :: Prompt goalieNamePrompt = strPrompt "Goalie name: " $ modify . (progMode.createGoalieStateL.cgsName .~) +-- | Prompts for a goalie who played in the game +selectGameGoaliePrompt :: Prompt +selectGameGoaliePrompt = undefined + drawSimplePrompt :: String -> ProgState -> C.Update () drawSimplePrompt pStr s = C.drawString $ pStr ++ s^.inputBuffer From faa214bf6da814b477c2ae1193cf0ff95e0c359b Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Wed, 30 Oct 2019 00:29:33 -0400 Subject: [PATCH 22/33] implemented selectGameGoaliePrompt --- src/Mtlstats/Prompt.hs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index c16d799..9b84651 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -37,6 +37,7 @@ module Mtlstats.Prompt ( playerNamePrompt, playerPosPrompt, selectPlayerPrompt, + selectGoaliePrompt, recordGoalPrompt, recordAssistPrompt, pMinPlayerPrompt, @@ -204,6 +205,16 @@ selectPlayerPrompt pStr callback = Prompt _ -> return () } +-- | Selects a goalie (creating one if necessary) +selectGoaliePrompt + :: String + -- ^ The prompt string + -> (Maybe Int -> Action ()) + -- ^ The callback to run (takes the index number of the goalie as + -- input) + -> Prompt +selectGoaliePrompt = undefined + -- | Prompts for the player who scored the goal recordGoalPrompt :: Int @@ -262,7 +273,10 @@ goalieNamePrompt = strPrompt "Goalie name: " $ -- | Prompts for a goalie who played in the game selectGameGoaliePrompt :: Prompt -selectGameGoaliePrompt = undefined +selectGameGoaliePrompt = selectGoaliePrompt "Select goalie: " $ + \case + Nothing -> modify $ progMode.gameStateL.goaliesRecorded .~ True + Just n -> modify $ progMode.gameStateL.gameSelectedGoalie ?~ n drawSimplePrompt :: String -> ProgState -> C.Update () drawSimplePrompt pStr s = C.drawString $ pStr ++ s^.inputBuffer From 1e78ca6f402ad088901afb7d3bbaf412eb66ced8 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Wed, 30 Oct 2019 16:57:08 -0400 Subject: [PATCH 23/33] implemented selectPrompt --- src/Mtlstats/Prompt.hs | 39 +++++++++++++++++++++++++++++++++++++++ src/Mtlstats/Types.hs | 19 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 9b84651..38b6a1b 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -27,6 +27,7 @@ module Mtlstats.Prompt ( promptHandler, strPrompt, numPrompt, + selectPrompt, -- * Individual prompts gameYearPrompt, gameDayPrompt, @@ -48,6 +49,7 @@ module Mtlstats.Prompt ( ) where import Control.Monad (when) +import Control.Monad.Extra (whenJust) import Control.Monad.Trans.State (gets, modify) import Data.Char (isDigit, toUpper) import Data.Foldable (forM_) @@ -112,6 +114,43 @@ numPrompt pStr act = Prompt , promptSpecialKey = const $ return () } +-- | Builds a selection prompt +selectPrompt :: SelectParams a -> Prompt +selectPrompt params = Prompt + { promptDrawer = \s -> do + let sStr = s^.inputBuffer + C.drawString $ spPrompt params ++ sStr + (row, col) <- C.cursorPosition + C.drawString $ "\n\n" ++ spSearchHeader params ++ "\n" + let results = zip [1..maxFunKeys] $ spSearch params sStr (s^.database) + C.drawString $ unlines $ map + (\(n, (_, x)) -> let + desc = spElemDesc params x + in "F" ++ show n ++ ") " ++ desc) + results + C.moveCursor row col + , promptCharCheck = const True + , promptAction = \sStr -> if null sStr + then spCallback params Nothing + else do + db <- gets (^.database) + case spSearchExact params sStr db of + Nothing -> spNotFound params sStr + Just n -> spCallback params $ Just n + , promptSpecialKey = \case + C.KeyFunction rawK -> do + sStr <- gets (^.inputBuffer) + db <- gets (^.database) + let + n = pred $ fromInteger rawK + results = spSearch params sStr db + when (n < maxFunKeys) $ + whenJust (nth n results) $ \(n, _) -> do + modify $ inputBuffer .~ "" + spCallback params $ Just n + _ -> return () + } + -- | Prompts for the game year gameYearPrompt :: Prompt gameYearPrompt = numPrompt "Game year: " $ diff --git a/src/Mtlstats/Types.hs b/src/Mtlstats/Types.hs index eb8e849..2c5b4a9 100644 --- a/src/Mtlstats/Types.hs +++ b/src/Mtlstats/Types.hs @@ -38,6 +38,7 @@ module Mtlstats.Types ( GoalieStats (..), GameStats (..), Prompt (..), + SelectParams (..), -- * Lenses -- ** ProgState Lenses database, @@ -517,6 +518,24 @@ data Prompt = Prompt -- ^ Action to perform when a special key is pressed } +-- | Parameters for a search prompt +data SelectParams a = SelectParams + { spPrompt :: String + -- ^ The search prompt + , spSearchHeader :: String + -- ^ The header to display at the top of the search list + , spSearch :: String -> Database -> [(Int, a)] + -- ^ The search function + , spSearchExact :: String -> Database -> Maybe Int + -- ^ Search function looking for an exact match + , spElemDesc :: a -> String + -- ^ Provides a string description of an element + , spCallback :: Maybe Int -> Action () + -- ^ The function when the selection is made + , spNotFound :: String -> Action () + -- ^ The function to call when the selection doesn't exist + } + makeLenses ''ProgState makeLenses ''GameState makeLenses ''CreatePlayerState From d215f27f4fcf4f4e1e32df8c74d7f8ed1b3314a8 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Wed, 30 Oct 2019 17:29:47 -0400 Subject: [PATCH 24/33] make selectPlayerPrompt call selectPrompt --- src/Mtlstats/Prompt.hs | 61 +++++++++++++----------------------------- 1 file changed, 18 insertions(+), 43 deletions(-) diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 38b6a1b..bc7e87f 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -199,49 +199,24 @@ selectPlayerPrompt -- ^ The callback to run (takes the index number of the payer as -- input) -> Prompt -selectPlayerPrompt pStr callback = Prompt - { promptDrawer = \s -> do - let sStr = s^.inputBuffer - C.drawString pStr - C.drawString sStr - (row, col) <- C.cursorPosition - C.drawString "\n\nPlayer select:\n" - let sel = zip [1..maxFunKeys] $ playerSearch sStr $ s^.database.dbPlayers - mapM_ - (\(n, (_, p)) -> C.drawString $ - "F" ++ show n ++ ") " ++ p^.pName ++ " (" ++ show (p^.pNumber) ++ ")\n") - sel - C.moveCursor row col - , promptCharCheck = const True - , promptAction = \sStr -> if null sStr - then callback Nothing - else do - players <- gets $ view $ database.dbPlayers - case playerSearchExact sStr players of - Just (n, _) -> callback $ Just n - Nothing -> do - mode <- gets $ view progMode - let - cps = newCreatePlayerState - & cpsName .~ sStr - & cpsSuccessCallback .~ do - modify $ progMode .~ mode - pIndex <- pred . length <$> gets (view $ database.dbPlayers) - callback $ Just pIndex - & cpsFailureCallback .~ do - modify $ progMode .~ mode - modify $ progMode .~ CreatePlayer cps - , promptSpecialKey = \case - C.KeyFunction n -> do - sStr <- gets $ view inputBuffer - players <- gets $ view $ database.dbPlayers - modify $ inputBuffer .~ "" - let - fKey = pred $ fromIntegral n - options = playerSearch sStr players - sel = fst <$> nth fKey options - callback sel - _ -> return () +selectPlayerPrompt pStr callback = selectPrompt SelectParams + { spPrompt = pStr + , spSearchHeader = "Player select:" + , spSearch = \sStr db -> playerSearch sStr (db^.dbPlayers) + , spSearchExact = \sStr db -> fst <$> playerSearchExact sStr (db^.dbPlayers) + , spElemDesc = playerSummary + , spCallback = callback + , spNotFound = \sStr -> do + mode <- gets (^.progMode) + let + cps = newCreatePlayerState + & cpsName .~ sStr + & cpsSuccessCallback .~ do + modify $ progMode .~ mode + index <- pred . length <$> gets (^.database.dbPlayers) + callback $ Just index + & cpsFailureCallback .~ modify (progMode .~ mode) + modify $ progMode .~ CreatePlayer cps } -- | Selects a goalie (creating one if necessary) From 2926e28e3431936bcb63e06fbccafb04c56185cd Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Wed, 30 Oct 2019 21:57:31 -0400 Subject: [PATCH 25/33] implemented selectGoaliePrompt --- src/Mtlstats/Prompt.hs | 22 ++++++++++++++++++++-- src/Mtlstats/Types.hs | 30 +++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index bc7e87f..0b02a25 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -227,7 +227,25 @@ selectGoaliePrompt -- ^ The callback to run (takes the index number of the goalie as -- input) -> Prompt -selectGoaliePrompt = undefined +selectGoaliePrompt pStr callback = selectPrompt SelectParams + { spPrompt = pStr + , spSearchHeader = "Goalie select:" + , spSearch = \sStr db -> goalieSearch sStr (db^.dbGoalies) + , spSearchExact = \sStr db -> fst <$> goalieSearchExact sStr (db^.dbGoalies) + , spElemDesc = goalieSummary + , spCallback = callback + , spNotFound = \sStr -> do + mode <- gets (^.progMode) + let + cgs = newCreateGoalieState + & cgsName .~ sStr + & cgsSuccessCallback .~ do + modify $ progMode .~ mode + index <- pred . length <$> gets (^.database.dbGoalies) + callback $ Just index + & cgsFailureCallback .~ modify (progMode .~ mode) + modify $ progMode .~ CreateGoalie cgs + } -- | Prompts for the player who scored the goal recordGoalPrompt @@ -287,7 +305,7 @@ goalieNamePrompt = strPrompt "Goalie name: " $ -- | Prompts for a goalie who played in the game selectGameGoaliePrompt :: Prompt -selectGameGoaliePrompt = selectGoaliePrompt "Select goalie: " $ +selectGameGoaliePrompt = selectGoaliePrompt "Which goalie played this game: " $ \case Nothing -> modify $ progMode.gameStateL.goaliesRecorded .~ True Just n -> modify $ progMode.gameStateL.gameSelectedGoalie ?~ n diff --git a/src/Mtlstats/Types.hs b/src/Mtlstats/Types.hs index 2c5b4a9..a74618a 100644 --- a/src/Mtlstats/Types.hs +++ b/src/Mtlstats/Types.hs @@ -149,7 +149,11 @@ module Mtlstats.Types ( playerIsActive, -- ** PlayerStats Helpers psPoints, - addPlayerStats + addPlayerStats, + -- ** Goalie Helpers + goalieSearch, + goalieSearchExact, + goalieSummary ) where import Control.Monad.Trans.State (StateT) @@ -830,3 +834,27 @@ addPlayerStats s1 s2 = newPlayerStats & psGoals .~ s1^.psGoals + s2^.psGoals & psAssists .~ s1^.psAssists + s2^.psAssists & psPMin .~ s1^.psPMin + s2^.psPMin + +-- | Searches a list of goalies +goalieSearch + :: String + -- ^ The search string + -> [Goalie] + -- ^ The list to search + -> [(Int, Goalie)] + -- ^ The search results with their corresponding index numbers +goalieSearch = undefined + +-- | Searches a list of goalies for an exact match +goalieSearchExact + :: String + -- ^ The search string + -> [Goalie] + -- ^ The list to search + -> Maybe (Int, Goalie) + -- ^ The result with its index number +goalieSearchExact = undefined + +-- | Provides a description string for a 'Goalie' +goalieSummary :: Goalie -> String +goalieSummary = undefined From 7e19ee072f7aba9a1890c1a67dab53fd6c17a491 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Wed, 30 Oct 2019 23:01:59 -0400 Subject: [PATCH 26/33] implemented goalieSearch --- src/Mtlstats/Types.hs | 3 ++- test/TypesSpec.hs | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Mtlstats/Types.hs b/src/Mtlstats/Types.hs index a74618a..c171676 100644 --- a/src/Mtlstats/Types.hs +++ b/src/Mtlstats/Types.hs @@ -843,7 +843,8 @@ goalieSearch -- ^ The list to search -> [(Int, Goalie)] -- ^ The search results with their corresponding index numbers -goalieSearch = undefined +goalieSearch sStr = filter (\(_, goalie) -> sStr `isInfixOf` (goalie^.gName)) . + zip [0..] -- | Searches a list of goalies for an exact match goalieSearchExact diff --git a/test/TypesSpec.hs b/test/TypesSpec.hs index cbaf831..3312160 100644 --- a/test/TypesSpec.hs +++ b/test/TypesSpec.hs @@ -65,6 +65,7 @@ spec = describe "Mtlstats.Types" $ do playerIsActiveSpec psPointsSpec addPlayerStatsSpec + goalieSearchSpec Menu.spec playerSpec :: Spec @@ -651,6 +652,28 @@ addPlayerStatsSpec = describe "addPlayerStats" $ do it "should be 9" $ s3^.psPMin `shouldBe` 9 +goalieSearchSpec :: Spec +goalieSearchSpec = describe "goalieSearch" $ do + let + goalies = + [ newGoalie 2 "Joe" + , newGoalie 3 "Bob" + , newGoalie 5 "Steve" + ] + result n = (n, goalies!!n) + + context "partial match" $ + it "should return Joe and Steve" $ + goalieSearch "e" goalies `shouldBe` [result 0, result 2] + + context "no match" $ + it "should return an empty list" $ + goalieSearch "x" goalies `shouldBe` [] + + context "exact match" $ + it "should return Steve" $ + goalieSearch "Bob" goalies `shouldBe` [result 1] + joe :: Player joe = newPlayer 2 "Joe" "center" From 12c8d0bdd6d2b47e51a3431f9cf8ff7438012b23 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Wed, 30 Oct 2019 23:18:15 -0400 Subject: [PATCH 27/33] implemented goalieSearchExact --- src/Mtlstats/Types.hs | 7 ++++++- test/TypesSpec.hs | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/Mtlstats/Types.hs b/src/Mtlstats/Types.hs index c171676..a875aaf 100644 --- a/src/Mtlstats/Types.hs +++ b/src/Mtlstats/Types.hs @@ -854,7 +854,12 @@ goalieSearchExact -- ^ The list to search -> Maybe (Int, Goalie) -- ^ The result with its index number -goalieSearchExact = undefined +goalieSearchExact sStr goalies = let + results = filter (\(_, goalie) -> sStr == goalie^.gName) $ + zip [0..] goalies + in case results of + [] -> Nothing + result:_ -> Just result -- | Provides a description string for a 'Goalie' goalieSummary :: Goalie -> String diff --git a/test/TypesSpec.hs b/test/TypesSpec.hs index 3312160..113f196 100644 --- a/test/TypesSpec.hs +++ b/test/TypesSpec.hs @@ -66,6 +66,7 @@ spec = describe "Mtlstats.Types" $ do psPointsSpec addPlayerStatsSpec goalieSearchSpec + goalieSearchExactSpec Menu.spec playerSpec :: Spec @@ -674,6 +675,30 @@ goalieSearchSpec = describe "goalieSearch" $ do it "should return Steve" $ goalieSearch "Bob" goalies `shouldBe` [result 1] +goalieSearchExactSpec :: Spec +goalieSearchExactSpec = describe "goalieSearchExact" $ do + let + goalies = + [ newGoalie 2 "Joe" + , newGoalie 3 "Bob" + , newGoalie 5 "Steve" + ] + result n = (n, goalies!!n) + + mapM_ + (\(name, num) -> context name $ + it ("should return " ++ name) $ + goalieSearchExact name goalies `shouldBe` Just (result num)) + -- name, num + [ ( "Joe", 0 ) + , ( "Bob", 1 ) + , ( "Steve", 2 ) + ] + + context "Greg" $ + it "should return Nothing" $ + goalieSearchExact "Greg" goalies `shouldBe` Nothing + joe :: Player joe = newPlayer 2 "Joe" "center" From 1c692a21f08d3d52c4bcf653cce306667aa15b4c Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Wed, 30 Oct 2019 23:27:38 -0400 Subject: [PATCH 28/33] implemented goalieSummary --- src/Mtlstats/Types.hs | 2 +- test/TypesSpec.hs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Mtlstats/Types.hs b/src/Mtlstats/Types.hs index a875aaf..994753b 100644 --- a/src/Mtlstats/Types.hs +++ b/src/Mtlstats/Types.hs @@ -863,4 +863,4 @@ goalieSearchExact sStr goalies = let -- | Provides a description string for a 'Goalie' goalieSummary :: Goalie -> String -goalieSummary = undefined +goalieSummary g = g^.gName ++ " (" ++ show (g^.gNumber) ++ ")" diff --git a/test/TypesSpec.hs b/test/TypesSpec.hs index 113f196..27ea3f6 100644 --- a/test/TypesSpec.hs +++ b/test/TypesSpec.hs @@ -67,6 +67,7 @@ spec = describe "Mtlstats.Types" $ do addPlayerStatsSpec goalieSearchSpec goalieSearchExactSpec + goalieSummarySpec Menu.spec playerSpec :: Spec @@ -699,6 +700,11 @@ goalieSearchExactSpec = describe "goalieSearchExact" $ do it "should return Nothing" $ goalieSearchExact "Greg" goalies `shouldBe` Nothing +goalieSummarySpec :: Spec +goalieSummarySpec = describe "goalieSummary" $ + it "should provide a summary string" $ + goalieSummary (newGoalie 2 "Joe") `shouldBe` "Joe (2)" + joe :: Player joe = newPlayer 2 "Joe" "center" From a6395ada9c47c8da3e2cf0fb45892c56a37ebf7b Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Wed, 30 Oct 2019 23:41:55 -0400 Subject: [PATCH 29/33] implemented minsPlayedC --- src/Mtlstats/Control/GoalieInput.hs | 22 +++++++++++++++++++++- src/Mtlstats/Prompt.hs | 7 ++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/Mtlstats/Control/GoalieInput.hs b/src/Mtlstats/Control/GoalieInput.hs index 12b6d69..84f3bb1 100644 --- a/src/Mtlstats/Control/GoalieInput.hs +++ b/src/Mtlstats/Control/GoalieInput.hs @@ -21,10 +21,14 @@ along with this program. If not, see . module Mtlstats.Control.GoalieInput (goalieInput) where +import Data.Maybe (fromMaybe) import Lens.Micro ((^.)) +import qualified UI.NCurses as C +import Mtlstats.Format import Mtlstats.Prompt import Mtlstats.Types +import Mtlstats.Util -- | The dispatcher for handling goalie input goalieInput :: GameState -> Controller @@ -42,7 +46,23 @@ selectGoalieC = Controller } minsPlayedC :: Controller -minsPlayedC = undefined +minsPlayedC = Controller + { drawController = \s -> do + C.drawString $ header s + drawPrompt goalieMinsPlayedPrompt s + , handleController = \e -> do + promptHandler goalieMinsPlayedPrompt e + return True + } goalsAllowedC :: Controller goalsAllowedC = undefined + +header :: ProgState -> String +header s = unlines + [ "*** GAME " ++ padNum 2 (s^.database.dbGames) ++ " ***" + , fromMaybe "" $ do + n <- s^.progMode.gameStateL.gameSelectedGoalie + g <- nth n $ s^.database.dbGoalies + Just $ goalieSummary g + ] diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 0b02a25..17a36dd 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -45,7 +45,8 @@ module Mtlstats.Prompt ( assignPMinsPrompt, goalieNumPrompt, goalieNamePrompt, - selectGameGoaliePrompt + selectGameGoaliePrompt, + goalieMinsPlayedPrompt ) where import Control.Monad (when) @@ -310,5 +311,9 @@ selectGameGoaliePrompt = selectGoaliePrompt "Which goalie played this game: " $ Nothing -> modify $ progMode.gameStateL.goaliesRecorded .~ True Just n -> modify $ progMode.gameStateL.gameSelectedGoalie ?~ n +-- | Prompts for the number of minutes the goalie has played +goalieMinsPlayedPrompt :: Prompt +goalieMinsPlayedPrompt = undefined + drawSimplePrompt :: String -> ProgState -> C.Update () drawSimplePrompt pStr s = C.drawString $ pStr ++ s^.inputBuffer From 4fa707bc0f3a250be396563adb5dca4472513ad8 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Wed, 30 Oct 2019 23:58:51 -0400 Subject: [PATCH 30/33] implemented goalieMinsPlayedPrompt --- src/Mtlstats/Prompt.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 17a36dd..4246de0 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -313,7 +313,8 @@ selectGameGoaliePrompt = selectGoaliePrompt "Which goalie played this game: " $ -- | Prompts for the number of minutes the goalie has played goalieMinsPlayedPrompt :: Prompt -goalieMinsPlayedPrompt = undefined +goalieMinsPlayedPrompt = numPrompt "Minutes played: " $ + modify . (progMode.gameStateL.goalieMinsPlayed ?~) drawSimplePrompt :: String -> ProgState -> C.Update () drawSimplePrompt pStr s = C.drawString $ pStr ++ s^.inputBuffer From cb0b4f9d0bf95ae15307449c6bd4e6bc344f0c72 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 31 Oct 2019 00:24:54 -0400 Subject: [PATCH 31/33] implemented goalsAllowedC --- src/Mtlstats/Control/GoalieInput.hs | 9 ++++++++- src/Mtlstats/Prompt.hs | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Mtlstats/Control/GoalieInput.hs b/src/Mtlstats/Control/GoalieInput.hs index 84f3bb1..3b5fed3 100644 --- a/src/Mtlstats/Control/GoalieInput.hs +++ b/src/Mtlstats/Control/GoalieInput.hs @@ -56,7 +56,14 @@ minsPlayedC = Controller } goalsAllowedC :: Controller -goalsAllowedC = undefined +goalsAllowedC = Controller + { drawController = \s -> do + C.drawString $ header s + drawPrompt goalsAllowedPrompt s + , handleController = \e -> do + promptHandler goalsAllowedPrompt e + return True + } header :: ProgState -> String header s = unlines diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 4246de0..50c5302 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -46,7 +46,8 @@ module Mtlstats.Prompt ( goalieNumPrompt, goalieNamePrompt, selectGameGoaliePrompt, - goalieMinsPlayedPrompt + goalieMinsPlayedPrompt, + goalsAllowedPrompt ) where import Control.Monad (when) @@ -316,5 +317,9 @@ goalieMinsPlayedPrompt :: Prompt goalieMinsPlayedPrompt = numPrompt "Minutes played: " $ modify . (progMode.gameStateL.goalieMinsPlayed ?~) +-- | Prompts for the number of goals the goalie allowed +goalsAllowedPrompt :: Prompt +goalsAllowedPrompt = undefined + drawSimplePrompt :: String -> ProgState -> C.Update () drawSimplePrompt pStr s = C.drawString $ pStr ++ s^.inputBuffer From ff541c2385c4c93be62cfbc8e5414f0ff5cf0210 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 31 Oct 2019 01:21:21 -0400 Subject: [PATCH 32/33] implemented goalsAllowedPrompt --- src/Mtlstats/Actions.hs | 5 +++++ src/Mtlstats/Config.hs | 4 ++++ src/Mtlstats/Prompt.hs | 8 +++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Mtlstats/Actions.hs b/src/Mtlstats/Actions.hs index ea00bae..7d8eae1 100644 --- a/src/Mtlstats/Actions.hs +++ b/src/Mtlstats/Actions.hs @@ -41,6 +41,7 @@ module Mtlstats.Actions , awardAssist , resetGoalData , assignPMins + , recordGoalieStats , backHome , scrollUp , scrollDown @@ -269,6 +270,10 @@ assignPMins mins s = fromMaybe s $ do ) . (selectedPlayer .~ Nothing) +-- | Records the goalie's game stats +recordGoalieStats :: ProgState -> ProgState +recordGoalieStats = undefined + -- | Resets the program state back to the main menu backHome :: ProgState -> ProgState backHome diff --git a/src/Mtlstats/Config.hs b/src/Mtlstats/Config.hs index 9a5137f..eb6637a 100644 --- a/src/Mtlstats/Config.hs +++ b/src/Mtlstats/Config.hs @@ -40,3 +40,7 @@ dbFname = "database.json" -- | The maximum number of assists maxAssists :: Int maxAssists = 2 + +-- | The length of a typical game (in minutes) +gameLength :: Int +gameLength = 60 diff --git a/src/Mtlstats/Prompt.hs b/src/Mtlstats/Prompt.hs index 50c5302..24ca51e 100644 --- a/src/Mtlstats/Prompt.hs +++ b/src/Mtlstats/Prompt.hs @@ -55,6 +55,7 @@ import Control.Monad.Extra (whenJust) import Control.Monad.Trans.State (gets, modify) import Data.Char (isDigit, toUpper) import Data.Foldable (forM_) +import Data.Maybe (fromMaybe) import Lens.Micro ((^.), (&), (.~), (?~), (%~)) import Lens.Micro.Extras (view) import Text.Read (readMaybe) @@ -319,7 +320,12 @@ goalieMinsPlayedPrompt = numPrompt "Minutes played: " $ -- | Prompts for the number of goals the goalie allowed goalsAllowedPrompt :: Prompt -goalsAllowedPrompt = undefined +goalsAllowedPrompt = numPrompt "Goals allowed: " $ \n -> do + modify (progMode.gameStateL.goalsAllowed ?~ n) + mins <- fromMaybe 0 <$> gets (^.progMode.gameStateL.goalieMinsPlayed) + when (mins >= gameLength) $ + modify $ progMode.gameStateL.goaliesRecorded .~ True + modify recordGoalieStats drawSimplePrompt :: String -> ProgState -> C.Update () drawSimplePrompt pStr s = C.drawString $ pStr ++ s^.inputBuffer From eb96ce6152bc5042e942915c7a6745fd9f0a08f8 Mon Sep 17 00:00:00 2001 From: Jonathan Lamothe Date: Thu, 31 Oct 2019 03:21:11 -0400 Subject: [PATCH 33/33] implemented recordGoalieStats --- src/Mtlstats/Actions.hs | 23 ++++++- test/ActionsSpec.hs | 132 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 1 deletion(-) diff --git a/src/Mtlstats/Actions.hs b/src/Mtlstats/Actions.hs index 7d8eae1..e75caa0 100644 --- a/src/Mtlstats/Actions.hs +++ b/src/Mtlstats/Actions.hs @@ -272,7 +272,28 @@ assignPMins mins s = fromMaybe s $ do -- | Records the goalie's game stats recordGoalieStats :: ProgState -> ProgState -recordGoalieStats = undefined +recordGoalieStats s = fromMaybe s $ do + let gs = s^.progMode.gameStateL + gid <- gs^.gameSelectedGoalie + goalie <- nth gid $ s^.database.dbGoalies + mins <- gs^.goalieMinsPlayed + goals <- gs^.goalsAllowed + + let + bumpStats gs = gs + & gsMinsPlayed +~ mins + & gsGoalsAllowed +~ goals + + Just $ s + & progMode.gameStateL + %~ (gameGoalieStats %~ updateMap gid newGoalieStats bumpStats) + . (gameSelectedGoalie .~ Nothing) + . (goalieMinsPlayed .~ Nothing) + . (goalsAllowed .~ Nothing) + & database.dbGoalies + %~ modifyNth gid (\goalie -> goalie + & gYtd %~ bumpStats + & gLifetime %~ bumpStats) -- | Resets the program state back to the main menu backHome :: ProgState -> ProgState diff --git a/test/ActionsSpec.hs b/test/ActionsSpec.hs index 47226e3..11adbf1 100644 --- a/test/ActionsSpec.hs +++ b/test/ActionsSpec.hs @@ -66,6 +66,7 @@ spec = describe "Mtlstats.Actions" $ do awardAssistSpec resetGoalDataSpec assignPMinsSpec + recordGoalieStatsSpec backHomeSpec scrollUpSpec scrollDownSpec @@ -670,6 +671,137 @@ assignPMinsSpec = describe "assignPMins" $ let , ( Nothing, 4, 3, 2, 6, 5, 0 ) ] +recordGoalieStatsSpec :: Spec +recordGoalieStatsSpec = describe "recordGoalieStats" $ let + goalieStats mins goals = newGoalieStats + & gsMinsPlayed .~ mins + & gsGoalsAllowed .~ goals + + joe = newGoalie 2 "Joe" + & gYtd .~ goalieStats 10 11 + & gLifetime .~ goalieStats 12 13 + + bob = newGoalie 3 "Bob" + & gYtd .~ goalieStats 14 15 + & gLifetime .~ goalieStats 16 17 + + gameState n mins goals = newGameState + & gameGoalieStats .~ M.fromList [(1, goalieStats 1 2)] + & gameSelectedGoalie .~ n + & goalieMinsPlayed .~ mins + & goalsAllowed .~ goals + + progState n mins goals = newProgState + & database.dbGoalies .~ [joe, bob] + & progMode.gameStateL .~ gameState n mins goals + + in mapM_ + (\(name, gid, mins, goals, joeData, bobData, reset) -> let + s = recordGoalieStats $ progState gid mins goals + in context name $ do + + mapM_ + (\(name, gid, (gMins, gGoals, ytdMins, ytdGoals, ltMins, ltGoals)) -> + context name $ do + let + gs = s^.progMode.gameStateL.gameGoalieStats + game = M.findWithDefault newGoalieStats gid gs + goalie = fromJust $ nth gid $ s^.database.dbGoalies + ytd = goalie^.gYtd + lt = goalie^.gLifetime + + context "game minutes played" $ + it ("should be " ++ show gMins) $ + game^.gsMinsPlayed `shouldBe` gMins + + context "game goals allowed" $ + it ("should be " ++ show gGoals) $ + game^.gsGoalsAllowed `shouldBe` gGoals + + context "year-to-date minutes played" $ + it ("should be " ++ show ytdMins) $ + ytd^.gsMinsPlayed `shouldBe` ytdMins + + context "year-to-date goals allowed" $ + it ("should be " ++ show ytdGoals) $ + ytd^.gsGoalsAllowed `shouldBe` ytdGoals + + context "lifetime minutes played" $ + it ("should be " ++ show ltMins) $ + lt^.gsMinsPlayed `shouldBe` ltMins + + context "lifetime goals allowed" $ + it ("should be " ++ show ltGoals) $ + lt^.gsGoalsAllowed `shouldBe` ltGoals) + [ ( "Joe", 0, joeData ) + , ( "Bob", 1, bobData ) + ] + + context "selected goalie" $ let + expected = if reset then Nothing else gid + in it ("should be " ++ show expected) $ + (s^.progMode.gameStateL.gameSelectedGoalie) `shouldBe` expected + + context "minutes played" $ let + expected = if reset then Nothing else mins + in it ("should be " ++ show expected) $ + (s^.progMode.gameStateL.goalieMinsPlayed) `shouldBe` expected + + context "goals allowed" $ let + expected = if reset then Nothing else goals + in it ("should be " ++ show expected) $ + (s^.progMode.gameStateL.goalsAllowed) `shouldBe` expected) + + [ ( "Joe" + , Just 0 + , Just 1 + , Just 2 + , ( 1, 2, 11, 13, 13, 15 ) + , ( 1, 2, 14, 15, 16, 17 ) + , True + ) + , ( "Bob" + , Just 1 + , Just 1 + , Just 2 + , (0, 0, 10, 11, 12, 13 ) + , (2, 4, 15, 17, 17, 19 ) + , True + ) + , ( "goalie out of bounds" + , Just 2 + , Just 1 + , Just 2 + , (0, 0, 10, 11, 12, 13 ) + , (1, 2, 14, 15, 16, 17 ) + , False + ) + , ( "missing goalie" + , Nothing + , Just 1 + , Just 2 + , (0, 0, 10, 11, 12, 13 ) + , (1, 2, 14, 15, 16, 17 ) + , False + ) + , ( "missing minutes" + , Just 0 + , Nothing + , Just 1 + , (0, 0, 10, 11, 12, 13 ) + , (1, 2, 14, 15, 16, 17 ) + , False + ) + , ( "missing goals" + , Just 0 + , Just 1 + , Nothing + , (0, 0, 10, 11, 12, 13 ) + , (1, 2, 14, 15, 16, 17 ) + , False + ) + ] + makePlayer :: IO Player makePlayer = Player <$> makeNum