module Parser.StructureSpec where 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 describe "StructureElement" $ do it "can parse blocks" $ do let input = "{}" correct = Right $ Block [] parseWith structure input `shouldBe` correct let input = "{{}}" correct = Right $ Block [Block []] parseWith structure input `shouldBe` correct let input = "{{}, {}}" correct = Right $ Block [Block [], Block []] parseWith structure input `shouldBe` correct let input = "{ id: 1 }" correct = Right (Block [ Entry (Tag "id") $ Regular $ Integer 1 ], "") parseWithRest structure input `shouldBe` correct let input = "{ id: \"key\", x: 3, y: 1}" correct = Right $ Block [ Entry (Tag "id") $ Regular $ String "key", Entry (Tag "x") $ Regular $ Integer 3, Entry (Tag "y") $ Regular $ Integer 1 ] parseWith structure input `shouldBe` correct let input = "actions: { [not(inventoryFull())] retrieveItem(key), [] leave()}" correct = Right (Entry (Tag "actions") $ Block [ Entry (ConditionList [Not InventoryFull]) $ Regular $ Action $ RetrieveItem "key", Entry (ConditionList []) $ Regular $ Action Leave ], "") parseWithRest structure input `shouldBe` correct let input = "entities: [ { id: \"door\", x: 4, name:\"Secret door\", description: \"This secret door can only be opened with a key\", direction: left, y: 1}]" correct = Right (Entry (Tag "entities") $ Block [ Block [ Entry (Tag "id") $ Regular $ String "door", Entry (Tag "x") $ Regular $ Integer 4, Entry (Tag "name") $ Regular $ String "Secret door", Entry (Tag "description") $ Regular $ String "This secret door can only be opened with a key", Entry (Tag "direction") $ Regular $ Direction West, Entry (Tag "y") $ Regular $ Integer 1 ]], "") 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\", 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, Entry (Tag "y") $ Regular $ Integer 1, Entry (Tag "name") $ Regular $ String "Secret door", Entry (Tag "description") $ Regular $ String "This secret door can only be opened with a key", Entry (Tag "actions") $ Block [ Entry (ConditionList [InventoryContains "key"]) $ Regular $ Action $ UseItem "key", Entry (ConditionList []) $ Regular $ Action Leave ] ]], "") 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() } } ]" correct = Right (Entry (Tag "entities") $ Block [ Block [ Entry (Tag "id") $ Regular $ String "door", Entry (Tag "x") $ Regular $ Integer 4, Entry (Tag "y") $ Regular $ Integer 1, Entry (Tag "name") $ Regular $ String "Secret door", Entry (Tag "description") $ Regular $ String "This secret door can only be opened with a key", 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 "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" parseWith entry input `shouldBe` correct let input = "x: 0" correct = Right $ Entry (Tag "x") $ Regular $ Integer 0 parseWith entry input `shouldBe` correct let input = "useTimes: infinite" correct = Right $ Entry (Tag "useTimes") $ Regular Infinite parseWith entry input `shouldBe` correct let input = "direction: up" correct = Right $ Entry (Tag "direction") $ Regular $ Direction North parseWith entry input `shouldBe` correct let input = "actions: { [not(inventoryFull())] retrieveItem(key), [] leave()}" correct = Right (Entry (Tag "actions") $ Block [ Entry (ConditionList [Not InventoryFull]) $ Regular $ Action $ RetrieveItem "key", Entry (ConditionList []) $ Regular $ Action Leave ], "") parseWithRest structure input `shouldBe` correct it "can parse regulars" $ do let input = "this is a string" correct = Right $ Regular $ String input parseWith regular ("\"" ++ input ++ "\"") `shouldBe` correct let correct = Right $ Regular $ Integer 1 parseWith regular "1" `shouldBe` correct let correct = Right $ Regular Infinite parseWith regular "infinite" `shouldBe` correct let wrong = Right $ Regular Infinite parseWith regular "infinitee" `shouldNotBe` wrong let input = "leave()" correct = Right $ Regular $ Action Leave parseWith regular input `shouldBe` correct let input = "retrieveItem(firstId)" correct = Right $ Regular $ Action $ RetrieveItem "firstId" parseWith regular input `shouldBe` correct let input = "useItem(secondId)" correct = Right $ Regular $ Action $ UseItem "secondId" parseWith regular input `shouldBe` correct let input = "decreaseHp(entityId, objectId)" correct = Right $ Regular $ Action $ DecreaseHp "entityId" "objectId" parseWith regular input `shouldBe` correct let input = "decreaseHp(entityId,objectId)" correct = Right $ Regular $ Action $ DecreaseHp "entityId" "objectId" parseWith regular input `shouldBe` correct let input = "increasePlayerHp(objectId)" correct = Right $ Regular $ Action $ IncreasePlayerHp "objectId" parseWith regular input `shouldBe` correct let input = "up" correct = Right $ Regular $ Direction North parseWith regular input `shouldBe` correct let input = "right" correct = Right $ Regular $ Direction East parseWith regular input `shouldBe` correct let input = "down" correct = Right $ Regular $ Direction South parseWith regular input `shouldBe` correct let input = "left" correct = Right $ Regular $ Direction West parseWith regular input `shouldBe` correct describe "Key" $ do it "can parse tags" $ do let input = "simpletag" correct = Right $ Tag "simpletag" parseWith tag input `shouldBe` correct it "can parse conditionlists" $ do let input = "[not(inventoryFull())]" correct = Right (ConditionList [Not InventoryFull], "") parseWithRest conditionList input `shouldBe` correct let input = "[inventoryFull(), inventoryContains(itemId)]" correct = Right (ConditionList [ InventoryFull, InventoryContains "itemId" ], "") parseWithRest conditionList input `shouldBe` correct let input = "[]" correct = Right $ ConditionList [] parseWith conditionList input `shouldBe` correct it "can parse conditions" $ do let input = "inventoryFull()" correct = Right (Condition InventoryFull, "") parseWithRest condition input `shouldBe` correct let input = "inventoryContains(itemId)" correct = Right (Condition $ InventoryContains "itemId", "") parseWithRest condition input `shouldBe` correct let input = "not(inventoryFull())" correct = Right (Condition $ Not InventoryFull, "") parseWithRest condition input `shouldBe` correct let input = "not(inventoryContains(itemId))" correct = Right (Condition $ Not $ InventoryContains "itemId", "") parseWithRest condition input `shouldBe` correct describe "Value" $ do it "can parse strings" $ do let input = "dit is een string" correct = Right $ String input parseWith string ("\"" ++ input ++ "\"") `shouldBe` correct it "can parse integers" $ do let correct = Right $ Integer 1 parseWith integer "1" `shouldBe` correct it "can parse infinite" $ do let correct = Right Infinite parseWith infinite "infinite" `shouldBe` correct let wrong = Right Infinite parseWith infinite "infinitee" `shouldNotBe` wrong it "can parse actions" $ do let input = "leave()" correct = Right $ Action Leave parseWith action input `shouldBe` correct let input = "retrieveItem(firstId)" correct = Right $ Action $ RetrieveItem "firstId" parseWith action input `shouldBe` correct let input = "useItem(secondId)" correct = Right $ Action $ UseItem "secondId" parseWith action input `shouldBe` correct let input = "decreaseHp(entityId,objectId)" correct = Right $ Action $ DecreaseHp "entityId" "objectId" parseWith action input `shouldBe` correct let input = "increasePlayerHp(objectId)" correct = Right $ Action $ IncreasePlayerHp "objectId" parseWith action input `shouldBe` correct it "can parse directions" $ do let input = "up" correct = Right $ Direction North parseWith RPGEngine.Parse.TextToStructure.direction input `shouldBe` correct let input = "right" correct = Right $ Direction East parseWith RPGEngine.Parse.TextToStructure.direction input `shouldBe` correct let input = "down" correct = Right $ Direction South parseWith RPGEngine.Parse.TextToStructure.direction input `shouldBe` correct let input = "left" correct = Right $ Direction West parseWith RPGEngine.Parse.TextToStructure.direction input `shouldBe` correct it "can parse layouts" $ do 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 value input `shouldBe` correct 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 structure input `shouldBe` correct describe "Brackets" $ do it "matches closing <" $ do let input = '<' correct = '>' getMatchingClosingBracket input `shouldBe` correct it "matches closing (" $ do let input = '(' correct = ')' getMatchingClosingBracket input `shouldBe` correct it "matches closing {" $ do let input = '{' correct = '}' getMatchingClosingBracket input `shouldBe` correct it "matches closing [" $ 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