From f3bce9912095270350536ed0f0dcf0b716a12daf Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Thu, 22 Dec 2022 22:05:25 +0100 Subject: [PATCH] #10 #18 Fix parsing --- README.md | 2 + levels/level1.txt | 2 +- levels/level2.txt | 10 +-- levels/level3.txt | 2 +- levels/level4.txt | 2 +- levels/level_more_levels.txt | 10 +-- lib/RPGEngine.hs | 55 +++++++++------ lib/RPGEngine/Data.hs | 3 +- lib/RPGEngine/Data/Default.hs | 27 ++++++- lib/RPGEngine/Input/Core.hs | 2 +- lib/RPGEngine/Input/Playing.hs | 11 ++- lib/RPGEngine/Parse.hs | 6 +- lib/RPGEngine/Parse/StructureToGame.hs | 25 +++---- lib/RPGEngine/Parse/TextToStructure.hs | 19 ++--- rpg-engine.cabal | 8 ++- test/Parser/GameSpec.hs | 91 +++++++++++++++++------- test/Parser/StructureSpec.hs | 98 ++++++++++++++++++++++++-- test/Spec.hs | 19 ++++- 18 files changed, 289 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index 832883e..455e7af 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,8 @@ These are the keybinds *in* the game. All other keybinds in the menus should be | Move down | `Arrow Down` | `s` | | Move right | `Arrow Right` | `d` | | Show inventory | `i` | | +| Restart level | `r` | | +| Quit game | `Esc` | | ### Example playthrough diff --git a/levels/level1.txt b/levels/level1.txt index 42ba56a..02bc322 100644 --- a/levels/level1.txt +++ b/levels/level1.txt @@ -15,4 +15,4 @@ levels: [ entities: [] } -] +] \ No newline at end of file diff --git a/levels/level2.txt b/levels/level2.txt index ca3a220..641cc56 100644 --- a/levels/level2.txt +++ b/levels/level2.txt @@ -19,8 +19,8 @@ levels: [ items: [ { id: "key", - x: 0, - y: 1, + x: 1, + y: 2, name: "Sleutel", description: "Deze sleutel kan een deur openen", useTimes: 1, @@ -35,8 +35,8 @@ levels: [ entities: [ { id: "door", - x: 0, - y: 3, + x: 1, + y: 4, name: "Deur", description: "Deze deur kan geopend worden met een sleutel", direction: up, @@ -48,4 +48,4 @@ levels: [ } ] } -] +] \ No newline at end of file diff --git a/levels/level3.txt b/levels/level3.txt index f943fe7..349c63c 100644 --- a/levels/level3.txt +++ b/levels/level3.txt @@ -76,4 +76,4 @@ levels: [ } ] } -] +] \ No newline at end of file diff --git a/levels/level4.txt b/levels/level4.txt index f7ec761..ba5b8e5 100644 --- a/levels/level4.txt +++ b/levels/level4.txt @@ -131,4 +131,4 @@ levels: [ } ] } -] +] \ No newline at end of file diff --git a/levels/level_more_levels.txt b/levels/level_more_levels.txt index 879dc6b..3dd557e 100644 --- a/levels/level_more_levels.txt +++ b/levels/level_more_levels.txt @@ -1,9 +1,6 @@ -# Dit gehele bestand is een Block - -# Dit is een entry: key + value -player: { # Value is hier een block +player: { hp: 50, - inventory: [ # BlockList + inventory: [ { id: "dagger", x: 0, @@ -18,7 +15,6 @@ player: { # Value is hier een block ] } -# Dit is een entry levels: [ { layout: { @@ -135,4 +131,4 @@ levels: [ } ] } -] +] \ No newline at end of file diff --git a/lib/RPGEngine.hs b/lib/RPGEngine.hs index db19c09..e7cbf72 100644 --- a/lib/RPGEngine.hs +++ b/lib/RPGEngine.hs @@ -9,7 +9,7 @@ import RPGEngine.Config ( bgColor, winDimensions, winOffsets ) import RPGEngine.Render ( initWindow, render ) import RPGEngine.Input ( handleAllInput ) import RPGEngine.Input.Playing ( checkPlaying, spawnPlayer ) -import RPGEngine.Data (Game (..), State (..), Layout, Level (..), Physical (..)) +import RPGEngine.Data (Game (..), State (..), Layout, Level (..), Physical (..), Entity(..), Direction(..), Player(..)) import RPGEngine.Data.Default (defaultLevel, defaultPlayer) import Graphics.Gloss ( play ) @@ -27,22 +27,15 @@ playRPGEngine title fps = do -- TODO revert this -- Initialize the game initGame :: Game --- initGame = Game { --- state = Menu{ base = StateBase{ --- renderer = renderMenu, --- inputHandler = handleInputMenu --- }} --- } -initGame = Game{ - state = initState -} - where initState = Playing{ - levels = [defaultLevel, otherLevel], - count = 0, - level = defaultLevel, - player = spawnPlayer defaultLevel defaultPlayer, - restart = initState - } +initGame = Game { state = Menu } +-- initGame = Game{ state = initState } +-- where initState = Playing{ +-- levels = [defaultLevel, otherLevel], +-- count = 0, +-- level = defaultLevel, +-- player = spawnPlayer defaultLevel defaultPlayer, +-- restart = initState +-- } -- TODO remove this otherLayout :: Layout @@ -50,6 +43,8 @@ otherLayout = [ [Blocked, Blocked, Blocked], [Blocked, Entrance, Blocked], [Blocked, Walkable, Blocked], + [Blocked, Walkable, Blocked], + [Blocked, Walkable, Blocked], [Blocked, Exit, Blocked], [Blocked, Blocked, Blocked] ] @@ -69,12 +64,30 @@ otherLevel = Level { (1, 2, Walkable), (2, 2, Blocked), (0, 3, Blocked), - (1, 3, Exit), + (1, 3, Walkable), (2, 3, Blocked), (0, 4, Blocked), - (1, 4, Blocked), - (2, 4, Blocked) + (1, 4, Walkable), + (2, 4, Blocked), + (0, 5, Blocked), + (1, 5, Exit), + (2, 5, Blocked), + (0, 6, Blocked), + (1, 6, Blocked), + (2, 6, Blocked) ], items = [], - entities = [] + entities = [ + Entity{ + entityId = "door", + entityX = 1, + entityY = 3, + entityName = "Epic door", + entityDescription = "epic description", + entityActions = [], + entityValue = Nothing, + entityHp = Nothing, + direction = North + } + ] } \ No newline at end of file diff --git a/lib/RPGEngine/Data.hs b/lib/RPGEngine/Data.hs index 01efa75..534c36b 100644 --- a/lib/RPGEngine/Data.hs +++ b/lib/RPGEngine/Data.hs @@ -12,7 +12,7 @@ import RPGEngine.Render.Core ( Renderer ) -- A game is the base data container. data Game = Game { state :: State -} +} deriving (Eq, Show) ------------------------------- State -------------------------------- @@ -33,6 +33,7 @@ data State = Menu | Win -- Lost a level | Lose { restart :: State } + deriving (Eq, Show) ------------------------------- Level -------------------------------- diff --git a/lib/RPGEngine/Data/Default.hs b/lib/RPGEngine/Data/Default.hs index f877f7f..c2e2814 100644 --- a/lib/RPGEngine/Data/Default.hs +++ b/lib/RPGEngine/Data/Default.hs @@ -64,9 +64,30 @@ defaultLevel = Level { defaultPlayer :: Player defaultPlayer = Player { - playerHp = Prelude.Nothing, -- Compares to infinity - inventory = [], - position = (0, 0) + -- playerHp = Prelude.Nothing, -- Compares to infinity + playerHp = Just 50, + inventory = [ Item{ + itemId = "key", + itemX = 0, + itemY = 0, + itemName = "Epic key", + itemDescription = "MyKey", + itemActions = [], + itemValue = Nothing, + useTimes = Nothing + }, Item{ + itemId = "dagger", + itemX = 0, + itemY = 0, + itemName = "My dagger", + itemDescription = "dagger", + itemActions = [], + itemValue = Nothing, + useTimes = Nothing + }], + position = (0, 0), + showInventory = False, + showHp = True } defaultSelector :: ListSelector diff --git a/lib/RPGEngine/Input/Core.hs b/lib/RPGEngine/Input/Core.hs index 07ea182..467e149 100644 --- a/lib/RPGEngine/Input/Core.hs +++ b/lib/RPGEngine/Input/Core.hs @@ -20,7 +20,7 @@ type InputHandler a = Event -> (a -> a) data ListSelector = ListSelector { selection :: Int, selected :: Bool -} +} deriving (Eq, Show) ------------------------------ Exported ------------------------------ diff --git a/lib/RPGEngine/Input/Playing.hs b/lib/RPGEngine/Input/Playing.hs index 3068d24..9703560 100644 --- a/lib/RPGEngine/Input/Playing.hs +++ b/lib/RPGEngine/Input/Playing.hs @@ -2,6 +2,7 @@ module RPGEngine.Input.Playing ( handleInputPlaying , checkPlaying , spawnPlayer +, putCoords ) where import RPGEngine.Input.Core (InputHandler, handle, handleKey, composeInputHandlers) @@ -32,6 +33,8 @@ handleInputPlaying = composeInputHandlers [ handleKey (Char 's') Down $ movePlayer South, handleKey (Char 'a') Down $ movePlayer West, + handleKey (Char 'r') Down restartGame, + handleKey (Char 'i') Down $ toggleInventoryShown True, handleKey (Char 'i') Up $ toggleInventoryShown False ] @@ -58,13 +61,15 @@ pauseGame g@Game{ state = playing@Playing{} } = pausedGame where pausedGame = g{ state = Paused playing } pauseGame g = g +restartGame :: Game -> Game +restartGame g@Game{ state = playing@Playing{ restart = restarted } } = g{ state = restarted } + -- Go to next level if there is a next level, otherwise, initialize win state. goToNextLevel :: State -> State goToNextLevel s@Playing{ levels = levels, level = current, count = count, player = player } = nextState - where -- Either the next level or winState - nextState | (count + 1) < length levels = nextLevelState + where nextState | (count + 1) < length levels = nextLevelState | otherwise = Win - nextLevelState = s{ level = nextLevel, count = count + 1, player = movedPlayer } + nextLevelState = s{ level = nextLevel, count = count + 1, player = movedPlayer, restart = nextLevelState } nextLevel = levels !! (count + 1) movedPlayer = spawnPlayer nextLevel player goToNextLevel s = s diff --git a/lib/RPGEngine/Parse.hs b/lib/RPGEngine/Parse.hs index c12e8da..c63afd3 100644 --- a/lib/RPGEngine/Parse.hs +++ b/lib/RPGEngine/Parse.hs @@ -6,13 +6,11 @@ import RPGEngine.Data ( Game ) import RPGEngine.Parse.StructureToGame ( structureToGame ) import GHC.IO (unsafePerformIO) import Text.Parsec.String (parseFromFile) -import RPGEngine.Parse.TextToStructure (structure) +import RPGEngine.Parse.TextToStructure ( gameFile ) ------------------------------ Exported ------------------------------ parse :: FilePath -> Game parse filename = structureToGame struct where (Right struct) = unsafePerformIO io - io = parseFromFile structure filename - -tempParse = parseFromFile \ No newline at end of file + io = parseFromFile gameFile filename \ No newline at end of file diff --git a/lib/RPGEngine/Parse/StructureToGame.hs b/lib/RPGEngine/Parse/StructureToGame.hs index 7e81274..09e9e83 100644 --- a/lib/RPGEngine/Parse/StructureToGame.hs +++ b/lib/RPGEngine/Parse/StructureToGame.hs @@ -10,26 +10,25 @@ import RPGEngine.Data entityActions, entityValue, entityHp, direction), Item(itemId, itemX, itemY, itemName, itemDescription, itemValue, itemActions, useTimes), - Level(layout, items, entities), + Level(layout, items, entities, index), Game (..), State (..) ) import RPGEngine.Parse.TextToStructure ( Value(Infinite, Action, Layout, String, Direction, Integer), Key(Tag, ConditionList), Structure(..) ) import RPGEngine.Data.Default (defaultPlayer, defaultLevel, defaultItem, defaultEntity) +import RPGEngine.Input.Playing (putCoords, spawnPlayer) ------------------------------ Exported ------------------------------ -structureToGame :: Structure -> Game --- structureToGame [Entry(Tag "player") playerBlock, Entry(Tag "levels") levelsBlock] = game -structureToGame (Entry (Tag "player") playerBlock) = game - where game = Game{ state = newState } - newState = Playing{ levels = newLevels, level = currentLevel, player = newPlayer, restart = newState } - -- newLevels = structureToLevels levelsBlock - -- currentLevel = head newLevels - newLevels = [defaultLevel] - currentLevel = defaultLevel - newPlayer = structureToPlayer playerBlock +structureToGame :: [Structure] -> Game +structureToGame [Entry (Tag "player") playerBlock, Entry (Tag "levels") levelsBlock] = game + where game = Game newState + newState = Playing newLevels 0 currentLevel newPlayer newState + newLevels = structureToLevels levelsBlock + currentLevel = head newLevels + newPlayer = spawnPlayer currentLevel $ structureToPlayer playerBlock +structureToGame _ = Game Menu ------------------------------- Player ------------------------------- @@ -60,7 +59,9 @@ structureToLevels (Block struct) = structureToLevel <$> struct structureToLevels _ = [defaultLevel] structureToLevel :: Structure -> Level -structureToLevel (Block entries) = structureToLevel' entries defaultLevel +structureToLevel (Block entries) = indexIsSet + where indexIsSet = level{ index = putCoords level } + level = structureToLevel' entries defaultLevel structureToLevel _ = defaultLevel structureToLevel' :: [Structure] -> Level -> Level diff --git a/lib/RPGEngine/Parse/TextToStructure.hs b/lib/RPGEngine/Parse/TextToStructure.hs index dc76003..fa2486e 100644 --- a/lib/RPGEngine/Parse/TextToStructure.hs +++ b/lib/RPGEngine/Parse/TextToStructure.hs @@ -18,9 +18,13 @@ import Text.Parsec notFollowedBy, sepBy, many, - try ) + try, spaces, endOfLine ) import qualified Text.Parsec as P ( string ) import Text.Parsec.String ( Parser ) +import Text.Parsec.Combinator (lookAhead) + +gameFile :: Parser [Structure] +gameFile = try $ do many1 $ ignoreWS structure -------------------------- StructureElement -------------------------- @@ -111,7 +115,7 @@ data Value = String String ---------------------------------------------------------------------- value :: Parser Value -value = choice [layout, string, integer, infinite, action, direction] +value = choice [layout, string, integer, infinite, direction, action] string :: Parser Value string = try $ String <$> between (char '\"') (char '\"') reading @@ -149,7 +153,7 @@ direction = try $ do ignoreWS $ P.string "left", ignoreWS $ P.string "right" ] - notFollowedBy alphaNum + -- lookAhead $ char ',' return $ Direction $ make value where make "up" = North make "right" = East @@ -160,15 +164,12 @@ direction = try $ do layout :: Parser Value layout = try $ do open <- ignoreWS $ oneOf openingBrackets - ignoreWS $ char '|' - list <- ignoreWS $ ignoreWS strip `sepBy` ignoreWS (char '|') let closing = getMatchingClosingBracket open - ignoreWS $ char closing - return $ Layout list + value <- many1 strip <* ignoreWS (char closing) + return $ Layout value strip :: Parser Strip -strip = try $ do - physical `sepBy` char ' ' +strip = try $ do ignoreWS (char '|') *> ignoreWS (physical `sepBy` char ' ') physical :: Parser Physical physical = try $ do diff --git a/rpg-engine.cabal b/rpg-engine.cabal index 76a2eee..3323cdb 100644 --- a/rpg-engine.cabal +++ b/rpg-engine.cabal @@ -55,7 +55,11 @@ test-suite rpg-engine-test main-is: Spec.hs hs-source-dirs: test default-language: Haskell2010 - build-depends: base >=4.7 && <5, hspec <= 2.10.6, hspec-discover, rpg-engine + build-depends: + base >=4.7 && <5, + rpg-engine, + hspec <= 2.10.6, hspec-discover, + parsec >= 3.1.15.1 other-modules: Parser.GameSpec - Parser.StructureSpec + Parser.StructureSpec \ No newline at end of file diff --git a/test/Parser/GameSpec.hs b/test/Parser/GameSpec.hs index 1f167a3..b2b7371 100644 --- a/test/Parser/GameSpec.hs +++ b/test/Parser/GameSpec.hs @@ -6,24 +6,50 @@ import RPGEngine.Data import RPGEngine.Parse.Core import RPGEngine.Parse.TextToStructure import RPGEngine.Parse.StructureToGame +import RPGEngine.Parse.TextToStructure (gameFile) spec :: Spec spec = do describe "Game" $ do - it "TODO: Simple game" $ do - pending - it "TODO: More complex game" $ do - pending - it "TODO: Game with multiple levels" $ do - pending + -- TODO There is a weird bug that caused this to go in an infinite loop. Fix later. + xit "Simple game" $ do + let input = "player: {\n hp: 50,\n inventory: []\n}\n\nlevels: [\n {\n layout: {\n | * * * * * *\n | * s . . e *\n | * * * * * *\n },\n \n items: [],\n\n entities: []\n\n\n }\n]" + correct = Game { + state = Playing { + levels = [], + count = 0, + level = Level { + RPGEngine.Data.layout = [], + index = [], + items = [], + entities = [] + }, + player = Player { + playerHp = Just 50, + inventory = [], + position = (0, 0), + showHp = True, + showInventory = False + }, + restart = Menu + } + } + (Right struct) = parseWith gameFile input + structureToGame struct `shouldBe` correct + it "More complex game" $ do + pendingWith "fix parsing first" + it "Game with multiple levels" $ do + pendingWith "fix parsing first" describe "Player" $ do it "cannot die" $ do let input = "player: { hp: infinite, inventory: [] }" correct = Player { - playerHp = Prelude.Nothing, - inventory = [], - position = (0, 0) + playerHp = Prelude.Nothing, + inventory = [], + position = (0, 0), + showHp = True, + showInventory = False } Right (Entry (Tag "player") struct) = parseWith structure input structureToPlayer struct `shouldBe` correct @@ -31,9 +57,11 @@ spec = do it "without inventory" $ do let input = "player: { hp: 50, inventory: [] }" correct = Player { - playerHp = Just 50, - inventory = [], - position = (0, 0) + playerHp = Just 50, + inventory = [], + position = (0, 0), + showHp = True, + showInventory = False } Right (Entry (Tag "player") struct) = parseWith structure input structureToPlayer struct `shouldBe` correct @@ -54,14 +82,12 @@ spec = do useTimes = Prelude.Nothing } ], - position = (0, 0) + position = (0, 0), + showHp = True, + showInventory = False } Right (Entry (Tag "player") struct) = parseWith structure input structureToPlayer struct `shouldBe` correct - - describe "Layout" $ do - it "simple" $ do - pending describe "Items" $ do it "simple" $ do @@ -117,23 +143,40 @@ spec = do structureToActions struct `shouldBe` correct describe "Entities" $ do - it "TODO: Simple entity" $ do - pending + it "Simple entity" $ do + pendingWith "fix parsing first" describe "Level" $ do it "Simple layout" $ do - let input = "{ layout: { | * * * * * * \n| * s . . e *\n| * * * * * * }, items: [], entities: [] }" + let input = "{ layout: { | * * * * * *\n| * s . . e *\n| * * * * * *\n }, items: [], entities: [] }" correct = Level { RPGEngine.Data.layout = [ [Blocked, Blocked, Blocked, Blocked, Blocked, Blocked], [Blocked, Entrance, Walkable, Walkable, Exit, Blocked], [Blocked, Blocked, Blocked, Blocked, Blocked, Blocked] ], + index = [ + (0, 0, Blocked), + (1, 0, Blocked), + (2, 0, Blocked), + (3, 0, Blocked), + (4, 0, Blocked), + (5, 0, Blocked), + (0, 1, Blocked), + (1, 1, Entrance), + (2, 1, Walkable), + (3, 1, Walkable), + (4, 1, Exit), + (5, 1, Blocked), + (0, 2, Blocked), + (1, 2, Blocked), + (2, 2, Blocked), + (3, 2, Blocked), + (4, 2, Blocked), + (5, 2, Blocked) + ], items = [], entities = [] } Right struct = parseWith structure input - structureToLevel struct `shouldBe` correct - - it "TODO: Complex layout" $ do - pending \ No newline at end of file + structureToLevel struct `shouldBe` correct \ No newline at end of file diff --git a/test/Parser/StructureSpec.hs b/test/Parser/StructureSpec.hs index e9296b8..b084a02 100644 --- a/test/Parser/StructureSpec.hs +++ b/test/Parser/StructureSpec.hs @@ -5,6 +5,8 @@ import Test.Hspec import RPGEngine.Data import RPGEngine.Parse.Core import RPGEngine.Parse.TextToStructure +import Text.Parsec.String (parseFromFile) +import GHC.IO (unsafePerformIO) spec :: Spec spec = do @@ -68,7 +70,7 @@ spec = do ]], "") parseWithRest structure input `shouldBe` correct - let input = "entities: [ { id: \"door\", x: 4, y: 1, name:\"Secret door\", description: \"This secret door can only be opened with a key\", direction: left , actions: { [inventoryContains(key)] useItem(key), [] leave() } } ]" + let input = "entities: [ { id: \"door\", x: 4, y: 1, name:\"Secret door\", description: \"This secret door can only be opened with a key\", direction: left, actions: { [inventoryContains(key)] useItem(key), [] leave() } } ]" correct = Right (Entry (Tag "entities") $ Block [ Block [ Entry (Tag "id") $ Regular $ String "door", Entry (Tag "x") $ Regular $ Integer 4, @@ -83,6 +85,17 @@ spec = do ]], "") parseWithRest structure input `shouldBe` correct + it "combines actions and direction" $ do + let input = "entities: [ { direction: left, actions: { [inventoryContains(key)] useItem(key), [] leave() } } ]" + correct = Right (Entry (Tag "entities") $ Block [ Block [ + Entry (Tag "direction") $ Regular $ Direction West, + Entry (Tag "actions") $ Block [ + Entry (ConditionList [InventoryContains "key"]) $ Regular $ Action $ UseItem "key", + Entry (ConditionList []) $ Regular $ Action Leave + ] + ]], "") + parseWithRest structure input `shouldBe` correct + it "can parse entries" $ do let input = "id: \"dagger\"" correct = Right $ Entry (Tag "id") $ Regular $ String "dagger" @@ -252,22 +265,21 @@ spec = do parseWith RPGEngine.Parse.TextToStructure.direction input `shouldBe` correct it "can parse layouts" $ do - let input = "| * * * * * * * *\n| * s . . . . e *\n| * * * * * * * *" + let input = "{ | * * * * * * * *\n | * s . . . . e *\n | * * * * * * * *\n }" correct = Right $ Layout [ [Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked], [Blocked, Entrance, Walkable, Walkable, Walkable, Walkable, Exit, Blocked], [Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked] ] - parseWith RPGEngine.Parse.TextToStructure.layout input `shouldBe` correct + parseWith value input `shouldBe` correct - let input = "{ |* * * * * * * *|* s . . . . e *|* * * * * * * * }" - -- correct = Right $ Entry (Tag "layout") $ Regular $ Layout [ - correct = Right $ Layout [ + let input = "layout: { | * * * * * * * *\n | * s . . . . e *\n | * * * * * * * *\n }" + correct = Right $ Entry (Tag "layout") $ Regular $ Layout [ [Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked], [Blocked, Entrance, Walkable, Walkable, Walkable, Walkable, Exit, Blocked], [Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked, Blocked] ] - parseWith RPGEngine.Parse.TextToStructure.value input `shouldBe` correct + parseWith structure input `shouldBe` correct describe "Brackets" $ do it "matches closing <" $ do @@ -289,3 +301,75 @@ spec = do let input = '[' correct = ']' getMatchingClosingBracket input `shouldBe` correct + + describe "Full game file" $ do + it "single level" $ do + let input = "player: {\n hp: 50,\n inventory: []\n}\n\nlevels: [\n {\n layout: {\n | * * * * * *\n | * s . . e *\n | * * * * * *\n },\n \n items: [],\n\n entities: []\n\n\n }\n]" + correct = Right [ + Entry (Tag "player") $ Block [ + Entry (Tag "hp") $ Regular $ Integer 50, + Entry (Tag "inventory") $ Block [] + ], + Entry (Tag "levels") $ Block [ Block [ + Entry (Tag "layout") $ Regular $ Layout [ + [Blocked, Blocked, Blocked, Blocked, Blocked, Blocked], + [Blocked, Entrance, Walkable, Walkable, Exit, Blocked], + [Blocked, Blocked, Blocked, Blocked, Blocked, Blocked] + ], + Entry (Tag "items") $ Block [], + Entry (Tag "entities") $ Block [] + ]] + ] + parseWith gameFile input `shouldBe` correct + + it "two levels" $ do + let input = "player: {\n hp: 50,\n inventory: []\n}\n\nlevels: [\n {\n layout: {\n | * * * * * *\n | * s . . e *\n | * * * * * *\n },\n \n items: [],\n\n entities: []\n },\n {\n layout: {\n | * * *\n | * e *\n | * . *\n | * . *\n | * . *\n | * . *\n | * s *\n | * * *\n },\n\n items: [],\n\n entities: []\n }\n]" + correct = Right [ + Entry (Tag "player") $ Block [ + Entry (Tag "hp") $ Regular $ Integer 50, + Entry (Tag "inventory") $ Block [] + ], + Entry (Tag "levels") $ Block [ + Block [ + Entry (Tag "layout") $ Regular $ Layout [ + [Blocked, Blocked, Blocked, Blocked, Blocked, Blocked], + [Blocked, Entrance, Walkable, Walkable, Exit, Blocked], + [Blocked, Blocked, Blocked, Blocked, Blocked, Blocked] + ], + Entry (Tag "items") $ Block [], + Entry (Tag "entities") $ Block [] + ], Block [ + Entry (Tag "layout") $ Regular $ Layout [ + [Blocked,Blocked,Blocked], + [Blocked,Exit,Blocked], + [Blocked,Walkable,Blocked], + [Blocked,Walkable,Blocked], + [Blocked,Walkable,Blocked], + [Blocked,Walkable,Blocked], + [Blocked,Entrance,Blocked], + [Blocked,Blocked,Blocked] + ], + Entry (Tag "items") $ Block [], + Entry (Tag "entities") $ Block [] + ] + ] + ] + parseWith gameFile input `shouldBe` correct + + it "from file" $ do + let correct = Right [ + Entry (Tag "player") $ Block [ + Entry (Tag "hp") $ Regular $ Integer 50, + Entry (Tag "inventory") $ Block [] + ], + Entry (Tag "levels") $ Block [ Block [ + Entry (Tag "layout") $ Regular $ Layout [ + [Blocked, Blocked, Blocked, Blocked, Blocked, Blocked], + [Blocked, Entrance, Walkable, Walkable, Exit, Blocked], + [Blocked, Blocked, Blocked, Blocked, Blocked, Blocked] + ], + Entry (Tag "items") $ Block [], + Entry (Tag "entities") $ Block [] + ]] + ] + unsafePerformIO (parseFromFile gameFile "levels/level1.txt") `shouldBe` correct \ No newline at end of file diff --git a/test/Spec.hs b/test/Spec.hs index 52ef578..bf4362e 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -1 +1,18 @@ -{-# OPTIONS_GHC -F -pgmF hspec-discover #-} \ No newline at end of file +{-# OPTIONS_GHC -F -pgmF hspec-discover #-} + +-------------------------- How to use Hspec -------------------------- + +-- If a test has not yet been written: +-- Use `pending` or `pendingWith`. +-- it "Description" $ do +-- pendingWith "Reason" + +-- Temporarily disable running a test: +-- Replace `it` with `xit` +-- xit "Description" $ do ... + +-- Temporarily only run a specific test: +-- Put `focus` in front. +-- it "Description" $ do ... +-- becomes +-- focus $ it "Description" $ do ... \ No newline at end of file