├── .gitignore ├── README.md ├── exe └── Hyzzy │ └── Main.hs ├── games ├── castle │ ├── Commands.hs │ ├── Objects.hs │ ├── PublicObjects.hs │ ├── Rooms │ │ ├── Castle.hs │ │ └── Garden.hs │ ├── Start.hs │ ├── package.yaml │ └── tests │ │ └── example └── maze │ ├── Commands.hs │ ├── Objects.hs │ ├── PublicObjects.hs │ ├── Rooms │ ├── A1.hs │ ├── A2.hs │ ├── A3.hs │ ├── A4.hs │ ├── A5.hs │ ├── A6.hs │ ├── A7.hs │ ├── B1.hs │ ├── B2.hs │ ├── B3.hs │ ├── B4.hs │ ├── B5.hs │ ├── B6.hs │ ├── B7.hs │ ├── C1.hs │ ├── C2.hs │ ├── C3.hs │ ├── C4.hs │ ├── C5.hs │ ├── C6.hs │ ├── C7.hs │ ├── D1.hs │ ├── D2.hs │ ├── D3.hs │ ├── D4.hs │ ├── D5.hs │ ├── D6.hs │ ├── D7.hs │ ├── E1.hs │ ├── E2.hs │ ├── E3.hs │ ├── E4.hs │ ├── E5.hs │ ├── E6.hs │ ├── E7.hs │ ├── F1.hs │ ├── F2.hs │ ├── F3.hs │ ├── F4.hs │ ├── F5.hs │ ├── F6.hs │ ├── F7.hs │ ├── G1.hs │ ├── G2.hs │ ├── G3.hs │ ├── G4.hs │ ├── G5.hs │ ├── G6.hs │ └── G7.hs │ ├── Start.hs │ └── package.yaml ├── lib └── Hyzzy │ ├── BridgeTypes.hs │ ├── Command.hs │ ├── Object.hs │ └── Room.hs ├── package.yaml ├── regenPublicObjects.sh ├── runTests.sh └── stack.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work/ 2 | *.cabal 3 | stack.yaml.lock 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | hyzzy 2 | === 3 | 4 | A framework for defining text adventures via Haskell files. Play by combining functions, not by guessing phrases. 5 | 6 | Two small example games are included, `games/castle` and `games/maze`. An example game plays like this: 7 | 8 | $ stack run hyzzy games/castle 9 | A toy text adventure where commands have Haskell types. 10 | Type ":help" to view the meta-commands. 11 | > look 12 | You stand in front of the castle's heavy door. 13 | > open door 14 | It's locked. 15 | > unlock 16 | unlock :: Key -> Door -> Command 17 | > :inventory 18 | key :: Key 19 | > unlock key door 20 | That random key you happened to carry in your pockets happens to fit the lock. What are the odds? 21 | > open door 22 | The door opens with a drawn-out yawn. 23 | > enter door 24 | You're in! 25 | 26 | If you want to make your own games, here's how the above game is defined: 27 | 28 | ```haskell 29 | intro :: Command 30 | intro = Command $ do 31 | display "A toy text adventure where commands have Haskell types." 32 | display "Type \":help\" to view the meta-commands." 33 | 34 | addToInventory "key" Key () 35 | 36 | look :: Command 37 | look = "You stand in front of the castle's heavy door." 38 | 39 | 40 | newtype Door = Door (Object DoorFields) 41 | data DoorFields = DoorFields 42 | { doorLocked :: Bool 43 | , doorOpened :: Bool 44 | } 45 | deriving Generic 46 | 47 | door :: Instance Door 48 | door = Instance Door $ DoorFields 49 | { doorLocked = True 50 | , doorOpened = False 51 | } 52 | 53 | enter :: Door -> Command 54 | enter door = Command $ do 55 | DoorFields {..} <- getFields door 56 | if doorOpened 57 | then do 58 | display "You're in!" 59 | goToRoom "Castle" 60 | else do 61 | display "The door is closed." 62 | 63 | open :: Door -> Command 64 | open door = Command $ do 65 | DoorFields {..} <- getFields door 66 | if | doorLocked -> display "It's locked." 67 | | doorOpened -> display "It's already opened." 68 | | otherwise -> do 69 | setField Door door #doorOpened True 70 | display "The door opens with a drawn-out yawn." 71 | 72 | 73 | newtype Key = Key (Object ()) 74 | 75 | unlock :: Key -> Door -> Command 76 | unlock key door = Command $ do 77 | consume Key key 78 | setField Door door #doorLocked False 79 | display "That random key you happened to carry in your pockets happens to fit the lock. What are the odds?" 80 | ``` 81 | -------------------------------------------------------------------------------- /exe/Hyzzy/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveGeneric, GADTs, GeneralizedNewtypeDeriving, LambdaCase, OverloadedLabels, RankNTypes, RecordWildCards, TypeApplications, ViewPatterns #-} 2 | {-# OPTIONS -Wno-name-shadowing #-} 3 | module Hyzzy.Main where 4 | 5 | import Control.Exception (AsyncException(UserInterrupt)) 6 | import Control.Lens 7 | import Control.Monad 8 | import Control.Monad.Catch 9 | import Control.Monad.Free 10 | import Control.Monad.IO.Class 11 | import Control.Monad.Morph (hoist) 12 | import Control.Monad.Trans.Class 13 | import Control.Monad.Trans.State 14 | import Control.Monad.Trans.Writer 15 | import Data.Char 16 | import Data.Dynamic 17 | import Data.Foldable 18 | import Data.Function 19 | import Data.Functor.Coyoneda 20 | import Data.Generics.Labels () 21 | import Data.IORef 22 | import Data.List 23 | import Data.Map (Map, (!)) 24 | import Data.Maybe 25 | import Data.Proxy 26 | import Data.Unique 27 | import GHC.Generics (Generic) 28 | import Language.Haskell.Interpreter (OptionVal((:=)), Interpreter, InterpreterError, InterpreterT, ModuleName) 29 | import System.Console.Haskeline 30 | import System.Directory 31 | import System.FilePath 32 | import System.Environment 33 | import System.Exit 34 | import Text.Printf 35 | import Type.Reflection (SomeTypeRep, TyCon) 36 | import qualified Data.Map as Map 37 | import qualified Language.Haskell.Interpreter as I 38 | import qualified Type.Reflection as Typeable 39 | 40 | import Hyzzy.Command 41 | import Hyzzy.Object 42 | import Hyzzy.Room 43 | 44 | 45 | type GamePath = FilePath 46 | type TermName = String 47 | type TypeName = String 48 | 49 | 50 | type Code = String 51 | 52 | newtype Ctx = Ctx 53 | { eval :: forall r. Typeable r 54 | => Code -> M r 55 | } 56 | 57 | emptyCtx 58 | :: Ctx 59 | emptyCtx = Ctx $ \code -> do 60 | liftI $ I.interpret code I.infer 61 | 62 | extendCtx 63 | :: Typeable a 64 | => TermName 65 | -> a 66 | -> Ctx -> Ctx 67 | extendCtx termName a ctx = Ctx $ \code -> do 68 | a2r <- eval ctx $ printf "\\(%s :: %s) -> %s" 69 | termName 70 | (show $ Typeable.typeOf a) 71 | code 72 | pure $ a2r a 73 | 74 | extendCtxWithDynamic 75 | :: TermName 76 | -> Dynamic 77 | -> Ctx -> Ctx 78 | extendCtxWithDynamic termName (Dynamic typeRep a) 79 | = Typeable.withTypeable typeRep $ extendCtx termName a 80 | 81 | 82 | data Inventory = Inventory 83 | { inventoryNames :: Map TermName Unique 84 | , inventoryItems :: Map Unique Dynamic 85 | } 86 | deriving Generic 87 | 88 | initialInventory 89 | :: Inventory 90 | initialInventory 91 | = Inventory mempty mempty 92 | 93 | inventoryToList 94 | :: Inventory 95 | -> [(TermName, Dynamic)] 96 | inventoryToList (Inventory {..}) 97 | = [ (name, inventoryItems ! unique) 98 | | (name, unique) <- Map.toList inventoryNames 99 | ] 100 | 101 | extendCtxWithInventory 102 | :: Inventory -> Ctx -> Ctx 103 | extendCtxWithInventory inventory ctx 104 | = foldr (uncurry extendCtxWithDynamic) ctx 105 | . inventoryToList 106 | $ inventory 107 | 108 | newObject 109 | :: Ctor object fields 110 | -> fields 111 | -> IO (Unique, object) 112 | newObject ctor fields = do 113 | unique <- newUnique 114 | object <- Object unique <$> newIORef fields 115 | pure (unique, ctor object) 116 | 117 | 118 | data World = World 119 | { playerInventory :: Inventory 120 | , playerLocation :: RoomName 121 | , worldRooms :: Map RoomName Room 122 | } 123 | deriving Generic 124 | 125 | currentRoom 126 | :: Lens' World Room 127 | currentRoom = lens getter setter 128 | where 129 | getter 130 | :: World -> Room 131 | getter world 132 | = (world ^. #worldRooms) ! (world ^. #playerLocation) 133 | 134 | setter 135 | :: World -> Room -> World 136 | setter world room 137 | = over #worldRooms 138 | (Map.insert (world ^. #playerLocation) room) 139 | world 140 | 141 | currentCtx 142 | :: M Ctx 143 | currentCtx = do 144 | inventory <- liftW $ use #playerInventory 145 | room <- liftW $ use currentRoom 146 | pure $ extendCtxWithInventory inventory 147 | $ extendCtxWithRoom room 148 | $ emptyCtx 149 | 150 | 151 | newtype M a = M 152 | { unM :: StateT World (InterpreterT IO) a 153 | } 154 | deriving ( Functor, Applicative, Monad 155 | , MonadIO 156 | , MonadThrow, MonadCatch, MonadMask 157 | ) 158 | 159 | liftW 160 | :: State World a 161 | -> M a 162 | liftW 163 | = M . hoist (pure . runIdentity) 164 | 165 | liftI 166 | :: Interpreter a 167 | -> M a 168 | liftI 169 | = M . lift 170 | 171 | runM 172 | :: World -> M a -> Interpreter a 173 | runM world 174 | = flip evalStateT world 175 | . unM 176 | 177 | 178 | haskelineSettings 179 | :: Settings M 180 | haskelineSettings 181 | = setComplete completionFunc defaultSettings 182 | 183 | isWordChar 184 | :: Char -> Bool 185 | isWordChar c = isAlphaNum c || c == ':' 186 | 187 | completionFunc 188 | :: (String, String) 189 | -> M (String, [Completion]) 190 | completionFunc (reversedLhs, _) = do 191 | let reversedWordPrefix = takeWhile isWordChar reversedLhs 192 | let wordPrefix = reverse reversedWordPrefix 193 | names <- execWriterT $ do 194 | tell $ toListOf (each . #metaCommandName) metaCommands 195 | tell =<< lift availableCommandNames 196 | (tell =<<) $ lift $ liftW $ Map.keys <$> use (currentRoom . #roomCommands) 197 | (tell =<<) $ lift $ liftW $ Map.keys <$> use (currentRoom . #roomObjectNames) 198 | (tell =<<) $ lift $ liftW $ Map.keys <$> use (#playerInventory . #inventoryNames) 199 | completions <- execWriterT $ do 200 | for_ names $ \name -> do 201 | when (wordPrefix `isPrefixOf` name) $ do 202 | let completion = Completion 203 | { replacement = drop (length wordPrefix) name 204 | , display = name 205 | , isFinished = True 206 | } 207 | tell [completion] 208 | pure (reversedLhs, completions) 209 | 210 | 211 | availableCommandNames 212 | :: M [TermName] 213 | availableCommandNames = liftI $ do 214 | loadModuleDefs "Commands" 215 | 216 | runCommand 217 | :: Command -> M () 218 | runCommand 219 | = foldFree (lowerM . hoistCoyoneda runCommandF) 220 | . unCommand 221 | 222 | runCommandF 223 | :: CommandF a -> M a 224 | runCommandF = \case 225 | Display s -> do 226 | liftIO $ putStrLn s 227 | 228 | AddToInventory name ctor fields -> do 229 | (unique, object) <- liftIO $ newObject ctor fields 230 | 231 | let possibleNames = name 232 | : [name ++ show (n :: Int) | n <- [2..]] 233 | usedNames <- liftW $ use (#playerInventory . #inventoryNames . to Map.keys) 234 | let remainingNames = possibleNames \\ usedNames 235 | let name = head remainingNames 236 | 237 | liftW $ modifying (#playerInventory . #inventoryNames) 238 | $ Map.insert name unique 239 | liftW $ modifying (#playerInventory . #inventoryItems) 240 | $ Map.insert unique (toDyn object) 241 | 242 | AddToRoom name ctor fields -> do 243 | (unique, object) <- liftIO $ newObject ctor fields 244 | 245 | let possibleNames = name 246 | : [name ++ show (n :: Int) | n <- [2..]] 247 | usedNames <- liftW $ use (currentRoom . #roomObjectNames . to Map.keys) 248 | let remainingNames = possibleNames \\ usedNames 249 | let name = head remainingNames 250 | 251 | liftW $ modifying (currentRoom . #roomObjectNames) 252 | $ Map.insert name unique 253 | liftW $ modifying (currentRoom . #roomObjectInstances) 254 | $ Map.insert unique (toDyn object) 255 | 256 | GetFields (Object {..}) -> do 257 | liftIO $ readIORef objectFields 258 | 259 | SetField (Object {..}) field value -> do 260 | liftIO $ modifyIORef objectFields (field .~ value) 261 | 262 | Consume (Object {..}) -> do 263 | liftW $ modifying (#playerInventory . #inventoryNames) 264 | $ Map.filter (/= objectId) 265 | liftW $ modifying (#playerInventory . #inventoryItems) 266 | $ Map.delete objectId 267 | 268 | liftW $ modifying (currentRoom . #roomObjectNames) 269 | $ Map.filter (/= objectId) 270 | liftW $ modifying (currentRoom . #roomObjectInstances) 271 | $ Map.delete objectId 272 | 273 | GoToRoom roomName -> do 274 | liftW $ #playerLocation .= roomName 275 | 276 | 277 | data MetaCommand = MetaCommand 278 | { metaCommandName :: String 279 | , metaCommandHelp :: String 280 | , metaCommandAction :: M () 281 | } 282 | deriving Generic 283 | 284 | metaCommands 285 | :: [MetaCommand] 286 | metaCommands 287 | = [ MetaCommand ":help" "List the meta-commands." $ do 288 | let column1Width = fromMaybe 0 289 | $ maximumOf (each . #metaCommandName . to length) metaCommands 290 | for_ metaCommands $ \(MetaCommand {..}) -> do 291 | liftIO $ putStrLn $ take (column1Width + 2) (metaCommandName ++ repeat ' ') 292 | ++ metaCommandHelp 293 | , MetaCommand ":browse" "List the commands which are available in every room." $ do 294 | commandNames <- availableCommandNames 295 | for_ commandNames $ \commandName -> do 296 | typeName <- liftI $ typeNameOf commandName 297 | liftIO $ putStrLn $ commandName ++ " :: " ++ typeName 298 | , MetaCommand ":look" "List the room-specific commands and objects." $ do 299 | room <- liftW $ use currentRoom 300 | let dynamicList = roomToCommandList room ++ roomToObjectList room 301 | for_ dynamicList $ \(commandName, dynamic) -> do 302 | let typeName = show $ dynTypeRep dynamic 303 | liftIO $ putStrLn $ commandName ++ " :: " ++ typeName 304 | , MetaCommand ":inventory" "List the objects you have picked up so far." $ do 305 | inventory <- liftW $ use #playerInventory 306 | for_ (inventoryToList inventory) $ \(objectName, object) -> do 307 | let typeName = show . dynTypeRep $ object 308 | liftIO $ putStrLn $ objectName ++ " :: " ++ typeName 309 | , MetaCommand ":quit" "Abandon the quest (Ctrl-D works too)." $ do 310 | liftIO exitSuccess 311 | ] 312 | 313 | lookupMetaCommand 314 | :: String -> Maybe MetaCommand 315 | lookupMetaCommand name 316 | = elemIndexOf (each .> selfIndex <. #metaCommandName) name metaCommands 317 | 318 | runMetaCommand 319 | :: MetaCommand -> M () 320 | runMetaCommand 321 | = metaCommandAction 322 | 323 | 324 | typeNameOf 325 | :: TermName 326 | -> Interpreter TypeName 327 | typeNameOf 328 | = I.typeOf 329 | 330 | someTypeRepOf 331 | :: TermName 332 | -> Interpreter SomeTypeRep 333 | someTypeRepOf termName = do 334 | dynTypeRep <$> loadDynamic termName 335 | 336 | tyConOf 337 | :: TermName 338 | -> Interpreter TyCon 339 | tyConOf termName = do 340 | Typeable.someTypeRepTyCon <$> someTypeRepOf termName 341 | 342 | loadDynamic 343 | :: TermName 344 | -> Interpreter Dynamic 345 | loadDynamic termName = do 346 | I.interpret ("toDyn " ++ termName) I.infer 347 | 348 | instanceTyCon 349 | :: TyCon 350 | instanceTyCon 351 | = Typeable.someTypeRepTyCon 352 | $ Typeable.someTypeRep 353 | $ Proxy @(Instance ()) 354 | 355 | loadSomeInstance 356 | :: TermName 357 | -> Interpreter (Maybe SomeInstance) 358 | loadSomeInstance termName = do 359 | tyCon <- tyConOf termName 360 | if tyCon == instanceTyCon 361 | then Just <$> do 362 | I.interpret ("SomeInstance " ++ termName) I.infer 363 | else do 364 | pure Nothing 365 | 366 | loadModuleDefs 367 | :: ModuleName 368 | -> Interpreter [TermName] 369 | loadModuleDefs moduleName = do 370 | moduleElems <- I.getModuleExports moduleName 371 | execWriterT $ do 372 | for_ moduleElems $ \case 373 | I.Fun termName -> do 374 | tell [termName] 375 | _ -> do 376 | pure () 377 | 378 | loadModule 379 | :: ModuleName -> Interpreter (Map TermName Dynamic) 380 | loadModule moduleName = do 381 | I.setImports ["Data.Dynamic", moduleName] 382 | termNames <- loadModuleDefs moduleName 383 | execWriterT $ do 384 | for_ termNames $ \termName -> do 385 | dynamic <- lift $ loadDynamic termName 386 | tell $ Map.singleton termName dynamic 387 | 388 | 389 | data Room = Room 390 | { roomCommands :: Map String Dynamic 391 | , roomObjectNames :: Map String Unique 392 | , roomObjectInstances :: Map Unique Dynamic 393 | } 394 | deriving Generic 395 | 396 | instance Semigroup Room where 397 | Room x1 x2 x3 <> Room y1 y2 y3 398 | = Room (x1 <> y1) 399 | (x2 <> y2) 400 | (x3 <> y3) 401 | 402 | instance Monoid Room where 403 | mempty = emptyRoom 404 | 405 | emptyRoom 406 | :: Room 407 | emptyRoom 408 | = Room mempty 409 | mempty 410 | mempty 411 | 412 | roomToCommandList 413 | :: Room -> [(TermName, Dynamic)] 414 | roomToCommandList 415 | = Map.toList . roomCommands 416 | 417 | roomToObjectList 418 | :: Room -> [(TermName, Dynamic)] 419 | roomToObjectList (Room {..}) 420 | = [ (name, roomObjectInstances ! unique) 421 | | (name, unique) <- Map.toList roomObjectNames 422 | ] 423 | 424 | roomModule 425 | :: RoomName -> String 426 | roomModule roomName 427 | = "Rooms." ++ unRoomName roomName 428 | 429 | loadRoom 430 | :: RoomName -> Interpreter Room 431 | loadRoom roomName = do 432 | let moduleName = roomModule roomName 433 | I.setImports ["Data.Dynamic", "Hyzzy.Object", moduleName] 434 | termNames <- loadModuleDefs moduleName 435 | execWriterT $ do 436 | for_ termNames $ \termName -> do 437 | maybeSomeInstance <- lift $ loadSomeInstance termName 438 | case maybeSomeInstance of 439 | Just (SomeInstance (Instance ctor fields)) -> do 440 | (unique, object) <- liftIO $ newObject ctor fields 441 | tell $ emptyRoom 442 | { roomObjectNames = Map.singleton termName unique 443 | , roomObjectInstances = Map.singleton unique (toDyn object) 444 | } 445 | Nothing -> do 446 | dynamic <- lift $ loadDynamic termName 447 | tell $ emptyRoom 448 | { roomCommands = Map.singleton termName dynamic 449 | } 450 | 451 | extendCtxWithRoom 452 | :: Room -> Ctx -> Ctx 453 | extendCtxWithRoom room ctx 454 | = foldr (uncurry extendCtxWithDynamic) ctx 455 | $ roomToCommandList room 456 | ++ roomToObjectList room 457 | 458 | 459 | initialize 460 | :: GamePath -> Interpreter World 461 | initialize gamePath = do 462 | roomBasePaths <- liftIO $ listDirectory (gamePath "Rooms") 463 | let roomNames = RoomName 464 | <$> dropExtension 465 | <$> roomBasePaths 466 | let roomPaths = (gamePath ) 467 | <$> ("Rooms" ) 468 | <$> roomBasePaths 469 | I.loadModules $ [ gamePath "Commands.hs" 470 | , gamePath "Objects.hs" 471 | , gamePath "PublicObjects.hs" 472 | , gamePath "Start.hs" 473 | ] 474 | ++ roomPaths 475 | 476 | rooms <- execWriterT $ do 477 | for_ roomNames $ \roomName -> do 478 | room <- lift $ loadRoom roomName 479 | tell $ Map.singleton roomName room 480 | 481 | I.setImports ["Hyzzy.BridgeTypes", "Start"] 482 | startingRoom <- I.interpret "startingRoom" I.infer 483 | 484 | pure $ World 485 | { playerInventory = initialInventory 486 | , playerLocation = startingRoom 487 | , worldRooms = rooms 488 | } 489 | 490 | processInput 491 | :: String -> M () 492 | processInput "" = do 493 | pure () 494 | processInput (lookupMetaCommand -> Just metaCommand) = do 495 | runMetaCommand metaCommand 496 | processInput input = do 497 | ctx <- currentCtx 498 | r <- try $ eval ctx input 499 | case r of 500 | Left (I.UnknownError e) -> do 501 | liftIO $ putStrLn e 502 | Left (I.WontCompile es) -> do 503 | -- does it at least type-check? 504 | r <- liftI $ try $ typeNameOf input 505 | case (r :: Either InterpreterError String) of 506 | Left _ -> do 507 | -- show interpret's error, not typeNameOf's 508 | for_ es $ \e -> do 509 | liftIO $ putStrLn $ I.errMsg e 510 | Right typeName -> do 511 | -- e.g. "open :: Door -> Command" 512 | liftIO $ putStrLn $ input ++ " :: " ++ typeName 513 | Left (I.NotAllowed e) -> do 514 | liftIO $ putStrLn e 515 | Left (I.GhcException e) -> do 516 | liftIO $ putStrLn e 517 | Right command -> do 518 | runCommand command 519 | 520 | play 521 | :: GamePath -> IO () 522 | play gamePath = do 523 | r <- I.runInterpreter $ do 524 | world <- initialize gamePath 525 | runM world $ do 526 | liftI $ I.setImports ["Hyzzy.BridgeTypes", "Start"] 527 | intro <- liftI $ I.interpret "intro" I.infer 528 | runCommand intro 529 | 530 | runInputT haskelineSettings $ fix $ \loop -> do 531 | r <- try $ getInputLine "> " 532 | case r of 533 | Left UserInterrupt -> do 534 | -- clear the line on Ctrl-C 535 | loop 536 | Left e -> do 537 | throwM e 538 | Right Nothing -> do 539 | -- eof 540 | pure () 541 | Right (Just input) -> do 542 | roomName <- lift . liftW $ use #playerLocation 543 | 544 | -- so we can use "(x :: Type) -> ...", so we can 545 | -- write "open _" to get valid type-hole suggestions 546 | lift . liftI $ I.set 547 | [ I.languageExtensions := [I.ScopedTypeVariables] 548 | ] 549 | 550 | lift . liftI $ I.setImports 551 | $ ["Hyzzy.BridgeTypes", "Commands", "PublicObjects"] 552 | ++ [roomModule roomName] 553 | lift $ processInput 554 | $ dropWhile (== ' ') 555 | $ dropWhileEnd (== ' ') 556 | $ input 557 | loop 558 | case r of 559 | Left e -> do 560 | print e 561 | exitFailure 562 | Right () -> do 563 | pure () 564 | 565 | 566 | usage 567 | :: IO () 568 | usage = do 569 | putStrLn "usage:" 570 | putStrLn " stack run hyzzy games/" 571 | putStrLn "" 572 | putStrLn "Start the adventure described in the given folder." 573 | putStrLn "That folder must contain at least the following files:" 574 | putStrLn "" 575 | putStrLn " games//Commands.hs" 576 | putStrLn " games//Objects.hs" 577 | putStrLn " games//PublicObjects.hs" 578 | putStrLn " games//Start.hs" 579 | 580 | main 581 | :: IO () 582 | main = do 583 | getArgs >>= \case 584 | [] -> do 585 | usage 586 | ["-h"] -> do 587 | usage 588 | ["--help"] -> do 589 | usage 590 | [gamePath] -> do 591 | play gamePath 592 | _ -> do 593 | usage 594 | exitFailure 595 | -------------------------------------------------------------------------------- /games/castle/Commands.hs: -------------------------------------------------------------------------------- 1 | -- Commands which are available in every room. 2 | {-# LANGUAGE MultiWayIf, OverloadedLabels, OverloadedStrings, RecordWildCards #-} 3 | module Commands where 4 | 5 | import Data.Generics.Labels () 6 | 7 | import Hyzzy.Command 8 | 9 | import Objects 10 | 11 | 12 | open :: Door -> Command 13 | open door = Command $ do 14 | DoorFields {..} <- getFields door 15 | if | doorLocked -> display "It's locked." 16 | | doorOpened -> display "It's already opened." 17 | | otherwise -> do 18 | setField Door door #doorOpened True 19 | display "The door opens with a drawn-out yawn." 20 | 21 | unlock :: Key -> Door -> Command 22 | unlock key door = Command $ do 23 | consume Key key 24 | setField Door door #doorLocked False 25 | display "That random key you happened to carry in your pockets happens to fit the lock. What are the odds?" 26 | -------------------------------------------------------------------------------- /games/castle/Objects.hs: -------------------------------------------------------------------------------- 1 | -- The objects with which the player can interact. 2 | -- Remember to run "./regenPublicObjects.sh games/castle" after modifying this file! 3 | {-# LANGUAGE DeriveGeneric #-} 4 | module Objects where 5 | 6 | import GHC.Generics 7 | 8 | import Hyzzy.Object 9 | 10 | 11 | newtype Key = Key (Object ()) 12 | 13 | newtype Door = Door (Object DoorFields) 14 | data DoorFields = DoorFields 15 | { doorLocked :: Bool 16 | , doorOpened :: Bool 17 | } 18 | deriving Generic 19 | 20 | newtype Treasure = Treasure (Object ()) 21 | -------------------------------------------------------------------------------- /games/castle/PublicObjects.hs: -------------------------------------------------------------------------------- 1 | -- GENERATED BY "./regenPublicObjects.sh games/castle", DO NOT MODIFY BY HAND 2 | 3 | -- A variant of Objects which only export the type constructors, not the data constructors, 4 | -- so the player cannot cheat. 5 | module PublicObjects (Key, Door, Treasure) where 6 | 7 | import Objects 8 | -------------------------------------------------------------------------------- /games/castle/Rooms/Castle.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | {-# OPTIONS -Wno-name-shadowing #-} 3 | module Rooms.Castle where 4 | 5 | import Hyzzy.Command 6 | import Hyzzy.Object 7 | 8 | import Objects 9 | 10 | 11 | look :: Command 12 | look = "The castle's opulence is even more exhuberant than the rumors claimed!" 13 | 14 | door :: Instance Door 15 | door = Instance Door $ DoorFields 16 | { doorLocked = False 17 | , doorOpened = True 18 | } 19 | 20 | treasure :: Instance Treasure 21 | treasure = Instance Treasure () 22 | 23 | take :: Treasure -> Command 24 | take treasure = Command $ do 25 | treasureFields <- getFields treasure 26 | consume Treasure treasure 27 | addToInventory "treasure" Treasure treasureFields 28 | display "Once you sell this at the town, you'll finally be able to buy that adamantium armor you've always wanted." 29 | 30 | exit :: Door -> Command 31 | exit _ = Command $ do 32 | goToRoom "Garden" 33 | -------------------------------------------------------------------------------- /games/castle/Rooms/Garden.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings, RecordWildCards #-} 2 | {-# OPTIONS -Wno-name-shadowing #-} 3 | module Rooms.Garden where 4 | 5 | import Hyzzy.Command 6 | import Hyzzy.Object 7 | 8 | import Objects 9 | 10 | 11 | look :: Command 12 | look = "You stand in front of the castle's heavy door." 13 | 14 | door :: Instance Door 15 | door = Instance Door $ DoorFields 16 | { doorLocked = True 17 | , doorOpened = False 18 | } 19 | 20 | enter :: Door -> Command 21 | enter door = Command $ do 22 | DoorFields {..} <- getFields door 23 | if doorOpened 24 | then do 25 | display "You're in!" 26 | goToRoom "Castle" 27 | else do 28 | display "The door is closed." 29 | 30 | leave :: Treasure -> Command 31 | leave _ = "You leave the castle with a smile on your face, THE END. Type \":quit\" to leave the game as well." 32 | -------------------------------------------------------------------------------- /games/castle/Start.hs: -------------------------------------------------------------------------------- 1 | -- The state of the game at the beginning of the adventure. 2 | {-# LANGUAGE OverloadedStrings #-} 3 | module Start where 4 | 5 | import Hyzzy.Command 6 | import Hyzzy.Room 7 | 8 | import Objects 9 | 10 | 11 | intro 12 | :: Command 13 | intro = Command $ do 14 | display "A toy text adventure where commands have Haskell types." 15 | display "Type \":help\" to view the meta-commands." 16 | 17 | addToInventory "key" Key () 18 | 19 | startingRoom 20 | :: RoomName 21 | startingRoom 22 | = "Garden" 23 | -------------------------------------------------------------------------------- /games/castle/package.yaml: -------------------------------------------------------------------------------- 1 | name: castle 2 | version: 0.1 3 | synopsis: Toy text adventure demonstrating hyzzy 4 | license: PublicDomain 5 | author: Samuel Gélineau 6 | maintainer: gelisam+github@gmail.com 7 | category: Game 8 | ghc-options: -W -Wall 9 | 10 | dependencies: 11 | - base 12 | - generic-lens 13 | - hyzzy 14 | - lens 15 | 16 | library: 17 | source-dirs: . 18 | -------------------------------------------------------------------------------- /games/castle/tests/example: -------------------------------------------------------------------------------- 1 | A toy text adventure where commands have Haskell types. 2 | Type ":help" to view the meta-commands. 3 | > :help 4 | :help List the meta-commands. 5 | :browse List the commands which are available in every room. 6 | :look List the room-specific commands and objects. 7 | :inventory List the objects you have picked up so far. 8 | :quit Abandon the quest (Ctrl-D works too). 9 | > :browse 10 | open :: Door -> Command 11 | unlock :: Key -> Door -> Command 12 | > :look 13 | enter :: Door -> Command 14 | leave :: Treasure -> Command 15 | look :: Command 16 | door :: Door 17 | > look 18 | You stand in front of the castle's heavy door. 19 | > enter door 20 | The door is closed. 21 | > open door 22 | It's locked. 23 | > unlock 24 | unlock :: Key -> Door -> Command 25 | > :inventory 26 | key :: Key 27 | > unlock key door 28 | That random key you happened to carry in your pockets happens to fit the lock. What are the odds? 29 | > :inventory 30 | > enter door 31 | The door is closed. 32 | > open door 33 | The door opens with a drawn-out yawn. 34 | > enter door 35 | You're in! 36 | > :look 37 | exit :: Door -> Command 38 | look :: Command 39 | take :: Treasure -> Command 40 | door :: Door 41 | treasure :: Treasure 42 | > look 43 | The castle's opulence is even more exhuberant than the rumors claimed! 44 | > take treasure 45 | Once you sell this at the town, you'll finally be able to buy that adamantium armor you've always wanted. 46 | > :look 47 | exit :: Door -> Command 48 | look :: Command 49 | take :: Treasure -> Command 50 | door :: Door 51 | > :inventory 52 | treasure :: Treasure 53 | > exit door 54 | > leave treasure 55 | You leave the castle with a smile on your face, THE END. Type ":quit" to leave the game as well. 56 | > :quit 57 | -------------------------------------------------------------------------------- /games/maze/Commands.hs: -------------------------------------------------------------------------------- 1 | -- Commands which are available in every room. 2 | {-# LANGUAGE MultiWayIf, OverloadedLabels, OverloadedStrings, RecordWildCards #-} 3 | module Commands where 4 | 5 | import Data.Generics.Labels () 6 | import Data.Foldable 7 | 8 | import Hyzzy.Command 9 | 10 | import Objects 11 | 12 | 13 | examine :: Paper -> Command 14 | examine paper = Command $ do 15 | PaperFields {..} <- getFields paper 16 | if null paperLines 17 | then do 18 | display "It's a blank piece of paper." 19 | else do 20 | display "The note says:" 21 | for_ paperLines $ \paperLine -> do 22 | display (show paperLine) 23 | 24 | write :: Pen -> Paper -> String -> Command 25 | write _ paper paperLine = Command $ do 26 | PaperFields {..} <- getFields paper 27 | setField Paper paper #paperLines (paperLines ++ [paperLine]) 28 | 29 | pickUp :: Paper -> Command 30 | pickUp paper = Command $ do 31 | paperFields <- getFields paper 32 | consume Paper paper 33 | addToInventory "paper" Paper paperFields 34 | 35 | litter :: Paper -> Command 36 | litter paper = Command $ do 37 | paperFields <- getFields paper 38 | consume Paper paper 39 | addToRoom "trash" Paper paperFields 40 | -------------------------------------------------------------------------------- /games/maze/Objects.hs: -------------------------------------------------------------------------------- 1 | -- The objects with which the player can interact. 2 | -- Remember to run "./regenPublicObjects.sh games/castle" after modifying this file! 3 | {-# LANGUAGE DeriveGeneric #-} 4 | module Objects where 5 | 6 | import GHC.Generics 7 | 8 | import Hyzzy.Object 9 | 10 | 11 | newtype PaperDispenser = PaperDispenser (Object ()) 12 | 13 | newtype Paper = Paper (Object PaperFields) 14 | data PaperFields = PaperFields 15 | { paperLines :: [String] 16 | } 17 | deriving Generic 18 | 19 | newtype Pen = Pen (Object ()) 20 | -------------------------------------------------------------------------------- /games/maze/PublicObjects.hs: -------------------------------------------------------------------------------- 1 | -- NOT generated by ./regenPublicObjects.sh, so we can also export String. 2 | 3 | -- A variant of Objects which only export the type constructors, not the data constructors, 4 | -- so the player cannot cheat. 5 | module PublicObjects (PaperDispenser, Paper, Pen, String) where 6 | 7 | import Objects 8 | -------------------------------------------------------------------------------- /games/maze/Rooms/A1.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.A1 where 3 | 4 | import Hyzzy.Command 5 | import Hyzzy.Object 6 | 7 | import Objects 8 | 9 | 10 | look :: Command 11 | look = "More foliage walls. This place is a maze..." 12 | 13 | note :: Instance Paper 14 | note = Instance Paper $ PaperFields 15 | { paperLines = ["EAST"] 16 | } 17 | 18 | 19 | 20 | east :: Command 21 | east = Command $ goToRoom "A2" 22 | 23 | south :: Command 24 | south = Command $ goToRoom "B1" 25 | 26 | 27 | 28 | 29 | 30 | -- 1 2 3 4 5 6 7 31 | -- +-+-+ +-+-+-+-+ 32 | -- A|* | | 33 | -- + +-+-+-+-+-+ + 34 | -- B| | | | 35 | -- + + +-+-+ + +-+ 36 | -- C| | | | | 37 | -- + + +-+ +-+ + + 38 | -- D| | | | | 39 | -- + + + + + + + + 40 | -- E| | | | | | 41 | -- + + +-+ + +-+ + 42 | -- F| | | | 43 | -- + +-+-+ +-+-+ + 44 | -- G| | 45 | -- +-+-+-+-+-+-+-+ 46 | -------------------------------------------------------------------------------- /games/maze/Rooms/A2.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | {-# OPTIONS -Wno-name-shadowing #-} 3 | module Rooms.A2 where 4 | 5 | import Hyzzy.Command 6 | import Hyzzy.Object 7 | 8 | import Objects 9 | 10 | 11 | look :: Command 12 | look = "More foliage walls. This place is a maze..." 13 | 14 | note :: Instance Paper 15 | note = Instance Paper $ PaperFields 16 | { paperLines = ["Thanks for obeying the signs! Enjoy this free pen."] 17 | } 18 | 19 | take :: Pen -> Command 20 | take pen = Command $ do 21 | penFields <- getFields pen 22 | consume Pen pen 23 | addToInventory "pen" Pen penFields 24 | 25 | pen :: Instance Pen 26 | pen = Instance Pen () 27 | 28 | 29 | 30 | 31 | 32 | 33 | west :: Command 34 | west = Command $ goToRoom "A1" 35 | 36 | 37 | -- 1 2 3 4 5 6 7 38 | -- +-+-+ +-+-+-+-+ 39 | -- A| *| | 40 | -- + +-+-+-+-+-+ + 41 | -- B| | | | 42 | -- + + +-+-+ + +-+ 43 | -- C| | | | | 44 | -- + + +-+ +-+ + + 45 | -- D| | | | | 46 | -- + + + + + + + + 47 | -- E| | | | | | 48 | -- + + +-+ + +-+ + 49 | -- F| | | | 50 | -- + +-+-+ +-+-+ + 51 | -- G| | 52 | -- +-+-+-+-+-+-+-+ 53 | -------------------------------------------------------------------------------- /games/maze/Rooms/A3.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.A3 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage wa... wait! Is this the exit??" 9 | 10 | north :: Command 11 | north = Command $ do 12 | display "You finally leave the maze! You are so happy to finally see a wall which is not" 13 | display "made of foliage. This wall is a real wall, made of large, heavy rocks, the kind of" 14 | display "wall which makes you wonder why you didn't simply walk through the foliage. Such a" 15 | display "nice, heavy wall. Nice door, too." 16 | display "" 17 | display "You stand in front of the castle's heavy door." 18 | display "" 19 | display "THE END" 20 | 21 | east :: Command 22 | east = Command $ goToRoom "A4" 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -- 1 2 3 4 5 6 7 32 | -- +-+-+ +-+-+-+-+ 33 | -- A| |* | 34 | -- + +-+-+-+-+-+ + 35 | -- B| | | | 36 | -- + + +-+-+ + +-+ 37 | -- C| | | | | 38 | -- + + +-+ +-+ + + 39 | -- D| | | | | 40 | -- + + + + + + + + 41 | -- E| | | | | | 42 | -- + + +-+ + +-+ + 43 | -- F| | | | 44 | -- + +-+-+ +-+-+ + 45 | -- G| | 46 | -- +-+-+-+-+-+-+-+ 47 | -------------------------------------------------------------------------------- /games/maze/Rooms/A4.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.A4 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | east :: Command 14 | east = Command $ goToRoom "A5" 15 | 16 | 17 | 18 | 19 | west :: Command 20 | west = Command $ goToRoom "A3" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | * | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/A5.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.A5 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | east :: Command 14 | east = Command $ goToRoom "A6" 15 | 16 | 17 | 18 | 19 | west :: Command 20 | west = Command $ goToRoom "A4" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | * | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/A6.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.A6 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | east :: Command 14 | east = Command $ goToRoom "A7" 15 | 16 | 17 | 18 | 19 | west :: Command 20 | west = Command $ goToRoom "A5" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | * | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/A7.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.A7 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | south :: Command 17 | south = Command $ goToRoom "B7" 18 | 19 | west :: Command 20 | west = Command $ goToRoom "A6" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | *| 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/B1.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.B1 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "A1" 12 | 13 | 14 | 15 | 16 | south :: Command 17 | south = Command $ goToRoom "C1" 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B|*| | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/B2.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.B2 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | east :: Command 14 | east = Command $ goToRoom "B3" 15 | 16 | south :: Command 17 | south = Command $ goToRoom "C2" 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| |* | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/B3.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.B3 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | east :: Command 14 | east = Command $ goToRoom "B4" 15 | 16 | 17 | 18 | 19 | west :: Command 20 | west = Command $ goToRoom "B2" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | * | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/B4.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.B4 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | east :: Command 14 | east = Command $ goToRoom "B5" 15 | 16 | 17 | 18 | 19 | west :: Command 20 | west = Command $ goToRoom "B3" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | * | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/B5.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.B5 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | south :: Command 17 | south = Command $ goToRoom "C5" 18 | 19 | west :: Command 20 | west = Command $ goToRoom "B4" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | *| | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/B6.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.B6 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | east :: Command 14 | east = Command $ goToRoom "B7" 15 | 16 | south :: Command 17 | south = Command $ goToRoom "C6" 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | |* | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/B7.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.B7 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "A7" 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | west :: Command 20 | west = Command $ goToRoom "B6" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | *| 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/C1.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.C1 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "B1" 12 | 13 | 14 | 15 | 16 | south :: Command 17 | south = Command $ goToRoom "D1" 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C|*| | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/C2.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.C2 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "B2" 12 | 13 | 14 | 15 | 16 | south :: Command 17 | south = Command $ goToRoom "D2" 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| |*| | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/C3.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.C3 where 3 | 4 | import Hyzzy.Command 5 | import Hyzzy.Object 6 | 7 | import Objects 8 | 9 | 10 | look :: Command 11 | look = "More foliage walls. This place is a maze..." 12 | 13 | map :: Instance Paper 14 | map = Instance Paper $ PaperFields 15 | { paperLines = 16 | [ " 1 2 3 4 5 6 7 " 17 | , " +-+-+ +-+-+-+-+" 18 | , "A| | |" 19 | , " + +-+-+-+-+-+ +" 20 | , "B| | | |" 21 | , " + + +-+-+ + +-+" 22 | , "C| | | | |" 23 | , " + + +-+ +-+ + +" 24 | , "D| | | | |" 25 | , " + + + + + + + +" 26 | , "E| | | | | |" 27 | , " + + +-+ + +-+ +" 28 | , "F| | | |" 29 | , " + +-+-+ +-+-+ +" 30 | , "G| |" 31 | , " +-+-+-+-+-+-+-+" 32 | ] 33 | } 34 | 35 | 36 | 37 | east :: Command 38 | east = Command $ goToRoom "C4" 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -- 1 2 3 4 5 6 7 48 | -- +-+-+ +-+-+-+-+ 49 | -- A| | | 50 | -- + +-+-+-+-+-+ + 51 | -- B| | | | 52 | -- + + +-+-+ + +-+ 53 | -- C| | |* | | 54 | -- + + +-+ +-+ + + 55 | -- D| | | | | 56 | -- + + + + + + + + 57 | -- E| | | | | | 58 | -- + + +-+ + +-+ + 59 | -- F| | | | 60 | -- + +-+-+ +-+-+ + 61 | -- G| | 62 | -- +-+-+-+-+-+-+-+ 63 | -------------------------------------------------------------------------------- /games/maze/Rooms/C4.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.C4 where 3 | 4 | import Hyzzy.Command 5 | import Hyzzy.Object 6 | 7 | import Objects 8 | 9 | 10 | look :: Command 11 | look = Command $ do 12 | display "More foliage walls. This place is a maze..." 13 | display "" 14 | display "On the south is the paper-dispensing machine." 15 | 16 | note :: Instance Paper 17 | note = Instance Paper $ PaperFields 18 | { paperLines = ["EAST"] 19 | } 20 | 21 | 22 | 23 | 24 | 25 | 26 | east :: Command 27 | east = Command $ goToRoom "C5" 28 | 29 | south :: Command 30 | south = Command $ goToRoom "D4" 31 | 32 | west :: Command 33 | west = Command $ goToRoom "C3" 34 | 35 | 36 | -- 1 2 3 4 5 6 7 37 | -- +-+-+ +-+-+-+-+ 38 | -- A| | | 39 | -- + +-+-+-+-+-+ + 40 | -- B| | | | 41 | -- + + +-+-+ + +-+ 42 | -- C| | | * | | 43 | -- + + +-+ +-+ + + 44 | -- D| | | | | 45 | -- + + + + + + + + 46 | -- E| | | | | | 47 | -- + + +-+ + +-+ + 48 | -- F| | | | 49 | -- + +-+-+ +-+-+ + 50 | -- G| | 51 | -- +-+-+-+-+-+-+-+ 52 | -------------------------------------------------------------------------------- /games/maze/Rooms/C5.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.C5 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "B5" 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | west :: Command 20 | west = Command $ goToRoom "C4" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | *| | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/C6.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.C6 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "B6" 12 | 13 | east :: Command 14 | east = Command $ goToRoom "C7" 15 | 16 | south :: Command 17 | south = Command $ goToRoom "D6" 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | |* | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/C7.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.C7 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | south :: Command 17 | south = Command $ goToRoom "D7" 18 | 19 | west :: Command 20 | west = Command $ goToRoom "C6" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | *| 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/D1.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.D1 where 3 | 4 | import Hyzzy.Command 5 | import Hyzzy.Object 6 | 7 | import Objects 8 | 9 | 10 | look :: Command 11 | look = "More foliage walls. This place is a maze..." 12 | 13 | note :: Instance Paper 14 | note = Instance Paper $ PaperFields 15 | { paperLines = ["NORTH"] 16 | } 17 | 18 | north :: Command 19 | north = Command $ goToRoom "C1" 20 | 21 | east :: Command 22 | east = Command $ goToRoom "D2" 23 | 24 | south :: Command 25 | south = Command $ goToRoom "E1" 26 | 27 | 28 | 29 | 30 | -- 1 2 3 4 5 6 7 31 | -- +-+-+ +-+-+-+-+ 32 | -- A| | | 33 | -- + +-+-+-+-+-+ + 34 | -- B| | | | 35 | -- + + +-+-+ + +-+ 36 | -- C| | | | | 37 | -- + + +-+ +-+ + + 38 | -- D|* | | | | 39 | -- + + + + + + + + 40 | -- E| | | | | | 41 | -- + + +-+ + +-+ + 42 | -- F| | | | 43 | -- + +-+-+ +-+-+ + 44 | -- G| | 45 | -- +-+-+-+-+-+-+-+ 46 | -------------------------------------------------------------------------------- /games/maze/Rooms/D2.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.D2 where 3 | 4 | import Hyzzy.Command 5 | import Hyzzy.Object 6 | 7 | import Objects 8 | 9 | 10 | look :: Command 11 | look = "More foliage walls. This place is a maze..." 12 | 13 | note :: Instance Paper 14 | note = Instance Paper $ PaperFields 15 | { paperLines = ["WEST"] 16 | } 17 | 18 | north :: Command 19 | north = Command $ goToRoom "C2" 20 | 21 | 22 | 23 | 24 | south :: Command 25 | south = Command $ goToRoom "E2" 26 | 27 | west :: Command 28 | west = Command $ goToRoom "D1" 29 | 30 | 31 | -- 1 2 3 4 5 6 7 32 | -- +-+-+ +-+-+-+-+ 33 | -- A| | | 34 | -- + +-+-+-+-+-+ + 35 | -- B| | | | 36 | -- + + +-+-+ + +-+ 37 | -- C| | | | | 38 | -- + + +-+ +-+ + + 39 | -- D| *| | | | 40 | -- + + + + + + + + 41 | -- E| | | | | | 42 | -- + + +-+ + +-+ + 43 | -- F| | | | 44 | -- + +-+-+ +-+-+ + 45 | -- G| | 46 | -- +-+-+-+-+-+-+-+ 47 | -------------------------------------------------------------------------------- /games/maze/Rooms/D3.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.D3 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = Command $ do 9 | display "More foliage walls. This place is a maze..." 10 | display "" 11 | display "On the east is the paper-dispensing machine." 12 | 13 | 14 | 15 | 16 | east :: Command 17 | east = Command $ goToRoom "D4" 18 | 19 | south :: Command 20 | south = Command $ goToRoom "E3" 21 | 22 | 23 | 24 | 25 | 26 | -- 1 2 3 4 5 6 7 27 | -- +-+-+ +-+-+-+-+ 28 | -- A| | | 29 | -- + +-+-+-+-+-+ + 30 | -- B| | | | 31 | -- + + +-+-+ + +-+ 32 | -- C| | | | | 33 | -- + + +-+ +-+ + + 34 | -- D| |* | | | 35 | -- + + + + + + + + 36 | -- E| | | | | | 37 | -- + + +-+ + +-+ + 38 | -- F| | | | 39 | -- + +-+-+ +-+-+ + 40 | -- G| | 41 | -- +-+-+-+-+-+-+-+ 42 | -------------------------------------------------------------------------------- /games/maze/Rooms/D4.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.D4 where 3 | 4 | import Hyzzy.Command 5 | import Hyzzy.Object 6 | 7 | import Objects 8 | 9 | 10 | look :: Command 11 | look = Command $ do 12 | display "What have you gotten yourself into this time? You are at the center of a foliage" 13 | display "maze, with zero recollection of how you got here." 14 | display "" 15 | display "You stand next to a strange machine." 16 | 17 | 18 | paperDispenser :: Instance PaperDispenser 19 | paperDispenser = Instance PaperDispenser () 20 | 21 | activate :: PaperDispenser -> Command 22 | activate _ = Command $ do 23 | display "Bzzt! A paper comes out. You take it." 24 | addToInventory "paper" Paper $ PaperFields 25 | { paperLines = [] 26 | } 27 | 28 | north :: Command 29 | north = Command $ goToRoom "C4" 30 | 31 | east :: Command 32 | east = Command $ goToRoom "D5" 33 | 34 | south :: Command 35 | south = Command $ goToRoom "E4" 36 | 37 | west :: Command 38 | west = Command $ goToRoom "D3" 39 | 40 | 41 | -- 1 2 3 4 5 6 7 42 | -- +-+-+ +-+-+-+-+ 43 | -- A| | | 44 | -- + +-+-+-+-+-+ + 45 | -- B| | | | 46 | -- + + +-+-+ + +-+ 47 | -- C| | | | | 48 | -- + + +-+ +-+ + + 49 | -- D| | * | | | 50 | -- + + + + + + + + 51 | -- E| | | | | | 52 | -- + + +-+ + +-+ + 53 | -- F| | | | 54 | -- + +-+-+ +-+-+ + 55 | -- G| | 56 | -- +-+-+-+-+-+-+-+ 57 | -------------------------------------------------------------------------------- /games/maze/Rooms/D5.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.D5 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = Command $ do 9 | display "More foliage walls. This place is a maze..." 10 | display "" 11 | display "On the west is the paper-dispensing machine." 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | south :: Command 20 | south = Command $ goToRoom "E5" 21 | 22 | west :: Command 23 | west = Command $ goToRoom "D4" 24 | 25 | 26 | -- 1 2 3 4 5 6 7 27 | -- +-+-+ +-+-+-+-+ 28 | -- A| | | 29 | -- + +-+-+-+-+-+ + 30 | -- B| | | | 31 | -- + + +-+-+ + +-+ 32 | -- C| | | | | 33 | -- + + +-+ +-+ + + 34 | -- D| | *| | | 35 | -- + + + + + + + + 36 | -- E| | | | | | 37 | -- + + +-+ + +-+ + 38 | -- F| | | | 39 | -- + +-+-+ +-+-+ + 40 | -- G| | 41 | -- +-+-+-+-+-+-+-+ 42 | -------------------------------------------------------------------------------- /games/maze/Rooms/D6.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.D6 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "C6" 12 | 13 | 14 | 15 | 16 | south :: Command 17 | south = Command $ goToRoom "E6" 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | |*| | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/D7.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.D7 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "C7" 12 | 13 | 14 | 15 | 16 | south :: Command 17 | south = Command $ goToRoom "E7" 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | |*| 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/E1.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.E1 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "D1" 12 | 13 | 14 | 15 | 16 | south :: Command 17 | south = Command $ goToRoom "F1" 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E|*| | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/E2.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.E2 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "D2" 12 | 13 | 14 | 15 | 16 | south :: Command 17 | south = Command $ goToRoom "F2" 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| |*| | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/E3.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.E3 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "D3" 12 | 13 | east :: Command 14 | east = Command $ goToRoom "E4" 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | |* | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/E4.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.E4 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = Command $ do 9 | display "More foliage walls. This place is a maze..." 10 | display "" 11 | display "On the north is the paper-dispensing machine." 12 | 13 | north :: Command 14 | north = Command $ goToRoom "D4" 15 | 16 | 17 | 18 | 19 | south :: Command 20 | south = Command $ goToRoom "F4" 21 | 22 | west :: Command 23 | west = Command $ goToRoom "E3" 24 | 25 | 26 | -- 1 2 3 4 5 6 7 27 | -- +-+-+ +-+-+-+-+ 28 | -- A| | | 29 | -- + +-+-+-+-+-+ + 30 | -- B| | | | 31 | -- + + +-+-+ + +-+ 32 | -- C| | | | | 33 | -- + + +-+ +-+ + + 34 | -- D| | | | | 35 | -- + + + + + + + + 36 | -- E| | | *| | | 37 | -- + + +-+ + +-+ + 38 | -- F| | | | 39 | -- + +-+-+ +-+-+ + 40 | -- G| | 41 | -- +-+-+-+-+-+-+-+ 42 | -------------------------------------------------------------------------------- /games/maze/Rooms/E5.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.E5 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "D5" 12 | 13 | 14 | 15 | 16 | south :: Command 17 | south = Command $ goToRoom "F5" 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | |*| | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/E6.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.E6 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "D6" 12 | 13 | east :: Command 14 | east = Command $ goToRoom "E7" 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | |* | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/E7.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.E7 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "D7" 12 | 13 | 14 | 15 | 16 | south :: Command 17 | south = Command $ goToRoom "F7" 18 | 19 | west :: Command 20 | west = Command $ goToRoom "E6" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | *| 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/F1.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.F1 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "E1" 12 | 13 | 14 | 15 | 16 | south :: Command 17 | south = Command $ goToRoom "G1" 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F|*| | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/F2.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.F2 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "E2" 12 | 13 | east :: Command 14 | east = Command $ goToRoom "F3" 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| |* | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/F3.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.F3 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | east :: Command 14 | east = Command $ goToRoom "F4" 15 | 16 | 17 | 18 | 19 | west :: Command 20 | west = Command $ goToRoom "F2" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | * | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/F4.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.F4 where 3 | 4 | import Hyzzy.Command 5 | import Hyzzy.Object 6 | 7 | import Objects 8 | 9 | 10 | look :: Command 11 | look = Command $ do 12 | display "More foliage walls. This place is a maze..." 13 | display "" 14 | display "The paper-dispensing machine is few steps to the north." 15 | 16 | note :: Instance Paper 17 | note = Instance Paper $ PaperFields 18 | { paperLines = ["WEST"] 19 | } 20 | 21 | north :: Command 22 | north = Command $ goToRoom "E4" 23 | 24 | 25 | 26 | 27 | south :: Command 28 | south = Command $ goToRoom "G4" 29 | 30 | west :: Command 31 | west = Command $ goToRoom "F3" 32 | 33 | 34 | -- 1 2 3 4 5 6 7 35 | -- +-+-+ +-+-+-+-+ 36 | -- A| | | 37 | -- + +-+-+-+-+-+ + 38 | -- B| | | | 39 | -- + + +-+-+ + +-+ 40 | -- C| | | | | 41 | -- + + +-+ +-+ + + 42 | -- D| | | | | 43 | -- + + + + + + + + 44 | -- E| | | | | | 45 | -- + + +-+ + +-+ + 46 | -- F| | *| | 47 | -- + +-+-+ +-+-+ + 48 | -- G| | 49 | -- +-+-+-+-+-+-+-+ 50 | -------------------------------------------------------------------------------- /games/maze/Rooms/F5.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.F5 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "E5" 12 | 13 | east :: Command 14 | east = Command $ goToRoom "F6" 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | |* | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/F6.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.F6 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | east :: Command 14 | east = Command $ goToRoom "F7" 15 | 16 | 17 | 18 | 19 | west :: Command 20 | west = Command $ goToRoom "F5" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | * | 36 | -- + +-+-+ +-+-+ + 37 | -- G| | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/F7.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.F7 where 3 | 4 | import Hyzzy.Command 5 | import Hyzzy.Object 6 | 7 | import Objects 8 | 9 | 10 | look :: Command 11 | look = "More foliage walls. This place is a maze..." 12 | 13 | note :: Instance Paper 14 | note = Instance Paper $ PaperFields 15 | { paperLines = ["SOUTH"] 16 | } 17 | 18 | north :: Command 19 | north = Command $ goToRoom "E7" 20 | 21 | 22 | 23 | 24 | south :: Command 25 | south = Command $ goToRoom "G7" 26 | 27 | west :: Command 28 | west = Command $ goToRoom "F6" 29 | 30 | 31 | -- 1 2 3 4 5 6 7 32 | -- +-+-+ +-+-+-+-+ 33 | -- A| | | 34 | -- + +-+-+-+-+-+ + 35 | -- B| | | | 36 | -- + + +-+-+ + +-+ 37 | -- C| | | | | 38 | -- + + +-+ +-+ + + 39 | -- D| | | | | 40 | -- + + + + + + + + 41 | -- E| | | | | | 42 | -- + + +-+ + +-+ + 43 | -- F| | | *| 44 | -- + +-+-+ +-+-+ + 45 | -- G| | 46 | -- +-+-+-+-+-+-+-+ 47 | -------------------------------------------------------------------------------- /games/maze/Rooms/G1.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.G1 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | north :: Command 11 | north = Command $ goToRoom "F1" 12 | 13 | east :: Command 14 | east = Command $ goToRoom "G2" 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G|* | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/G2.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.G2 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | east :: Command 14 | east = Command $ goToRoom "G3" 15 | 16 | 17 | 18 | 19 | west :: Command 20 | west = Command $ goToRoom "G1" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| * | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/G3.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.G3 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | east :: Command 14 | east = Command $ goToRoom "G4" 15 | 16 | 17 | 18 | 19 | west :: Command 20 | west = Command $ goToRoom "G2" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| * | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/G4.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.G4 where 3 | 4 | import Hyzzy.Command 5 | import Hyzzy.Object 6 | 7 | import Objects 8 | 9 | 10 | look :: Command 11 | look = Command $ do 12 | display "More foliage walls. This place is a maze..." 13 | display "" 14 | display "You can see the paper-dispensing machine further to the north." 15 | 16 | note :: Instance Paper 17 | note = Instance Paper $ PaperFields 18 | { paperLines = ["NORTH"] 19 | } 20 | 21 | north :: Command 22 | north = Command $ goToRoom "F4" 23 | 24 | east :: Command 25 | east = Command $ goToRoom "G5" 26 | 27 | 28 | 29 | 30 | west :: Command 31 | west = Command $ goToRoom "G3" 32 | 33 | 34 | -- 1 2 3 4 5 6 7 35 | -- +-+-+ +-+-+-+-+ 36 | -- A| | | 37 | -- + +-+-+-+-+-+ + 38 | -- B| | | | 39 | -- + + +-+-+ + +-+ 40 | -- C| | | | | 41 | -- + + +-+ +-+ + + 42 | -- D| | | | | 43 | -- + + + + + + + + 44 | -- E| | | | | | 45 | -- + + +-+ + +-+ + 46 | -- F| | | | 47 | -- + +-+-+ +-+-+ + 48 | -- G| * | 49 | -- +-+-+-+-+-+-+-+ 50 | -------------------------------------------------------------------------------- /games/maze/Rooms/G5.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.G5 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | east :: Command 14 | east = Command $ goToRoom "G6" 15 | 16 | 17 | 18 | 19 | west :: Command 20 | west = Command $ goToRoom "G4" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| * | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/G6.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.G6 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | look :: Command 8 | look = "More foliage walls. This place is a maze..." 9 | 10 | 11 | 12 | 13 | east :: Command 14 | east = Command $ goToRoom "G7" 15 | 16 | 17 | 18 | 19 | west :: Command 20 | west = Command $ goToRoom "G5" 21 | 22 | 23 | -- 1 2 3 4 5 6 7 24 | -- +-+-+ +-+-+-+-+ 25 | -- A| | | 26 | -- + +-+-+-+-+-+ + 27 | -- B| | | | 28 | -- + + +-+-+ + +-+ 29 | -- C| | | | | 30 | -- + + +-+ +-+ + + 31 | -- D| | | | | 32 | -- + + + + + + + + 33 | -- E| | | | | | 34 | -- + + +-+ + +-+ + 35 | -- F| | | | 36 | -- + +-+-+ +-+-+ + 37 | -- G| * | 38 | -- +-+-+-+-+-+-+-+ 39 | -------------------------------------------------------------------------------- /games/maze/Rooms/G7.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Rooms.G7 where 3 | 4 | import Hyzzy.Command 5 | 6 | 7 | 8 | look :: Command 9 | look = "More foliage walls. This place is a maze..." 10 | 11 | north :: Command 12 | north = Command $ goToRoom "F7" 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | west :: Command 21 | west = Command $ goToRoom "G6" 22 | 23 | 24 | -- 1 2 3 4 5 6 7 25 | -- +-+-+ +-+-+-+-+ 26 | -- A| | | 27 | -- + +-+-+-+-+-+ + 28 | -- B| | | | 29 | -- + + +-+-+ + +-+ 30 | -- C| | | | | 31 | -- + + +-+ +-+ + + 32 | -- D| | | | | 33 | -- + + + + + + + + 34 | -- E| | | | | | 35 | -- + + +-+ + +-+ + 36 | -- F| | | | 37 | -- + +-+-+ +-+-+ + 38 | -- G| *| 39 | -- +-+-+-+-+-+-+-+ 40 | -------------------------------------------------------------------------------- /games/maze/Start.hs: -------------------------------------------------------------------------------- 1 | -- The state of the game at the beginning of the adventure. 2 | {-# LANGUAGE OverloadedStrings #-} 3 | module Start where 4 | 5 | import Hyzzy.Command 6 | import Hyzzy.Room 7 | 8 | 9 | intro 10 | :: Command 11 | intro = Command $ do 12 | display "Another toy text adventure where commands have Haskell types." 13 | display "Type \":help\" to view the meta-commands." 14 | 15 | startingRoom 16 | :: RoomName 17 | startingRoom 18 | = "D4" 19 | -------------------------------------------------------------------------------- /games/maze/package.yaml: -------------------------------------------------------------------------------- 1 | name: maze 2 | version: 0.1 3 | synopsis: Another toy text adventure demonstrating hyzzy 4 | license: PublicDomain 5 | author: Samuel Gélineau 6 | maintainer: gelisam+github@gmail.com 7 | category: Game 8 | ghc-options: -W -Wall 9 | 10 | dependencies: 11 | - base 12 | - generic-lens 13 | - hyzzy 14 | - lens 15 | 16 | library: 17 | source-dirs: . 18 | -------------------------------------------------------------------------------- /lib/Hyzzy/BridgeTypes.hs: -------------------------------------------------------------------------------- 1 | -- The types of all the values transfered between the game and hyzzy. 2 | module Hyzzy.BridgeTypes 3 | ( -- needed for the game-specific commands. 4 | Command, CommandF, Coyoneda, Free 5 | 6 | -- needed for the game-specific objects. 7 | , Object 8 | 9 | -- needed for the game-specific rooms. 10 | , RoomName 11 | ) where 12 | 13 | import Control.Monad.Free 14 | import Data.Functor.Coyoneda 15 | 16 | import Hyzzy.Command 17 | import Hyzzy.Object 18 | import Hyzzy.Room 19 | -------------------------------------------------------------------------------- /lib/Hyzzy/Command.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveFunctor, FlexibleContexts, FlexibleInstances, GADTs, RankNTypes, ScopedTypeVariables, TypeApplications, TypeSynonymInstances #-} 2 | module Hyzzy.Command where 3 | 4 | import Control.Lens 5 | import Control.Monad.Free 6 | import Data.Coerce 7 | import Data.Functor.Coyoneda 8 | import Data.String 9 | import Data.Typeable 10 | 11 | import Hyzzy.Object 12 | import Hyzzy.Room 13 | 14 | 15 | 16 | data CommandF r where 17 | Display :: String -> CommandF () 18 | AddToInventory :: Typeable object 19 | => String 20 | -> Ctor object fields 21 | -> fields 22 | -> CommandF () 23 | AddToRoom :: Typeable object 24 | => String 25 | -> Ctor object fields 26 | -> fields 27 | -> CommandF () 28 | GetFields :: Object fields 29 | -> CommandF fields 30 | SetField :: Object fields 31 | -> Lens' fields field 32 | -> field 33 | -> CommandF () 34 | Consume :: Object fields 35 | -> CommandF () 36 | GoToRoom :: RoomName 37 | -> CommandF () 38 | 39 | newtype Command = Command 40 | { unCommand :: Free (Coyoneda CommandF) () } 41 | 42 | display 43 | :: String -> Free (Coyoneda CommandF) () 44 | display s 45 | = liftF $ liftCoyoneda 46 | $ Display s 47 | 48 | addToInventory 49 | :: Typeable object 50 | => String 51 | -> Ctor object fields 52 | -> fields 53 | -> Free (Coyoneda CommandF) () 54 | addToInventory name ctor fields 55 | = liftF $ liftCoyoneda 56 | $ AddToInventory name ctor fields 57 | 58 | addToRoom 59 | :: Typeable object 60 | => String 61 | -> Ctor object fields 62 | -> fields 63 | -> Free (Coyoneda CommandF) () 64 | addToRoom name ctor fields 65 | = liftF $ liftCoyoneda 66 | $ AddToRoom name ctor fields 67 | 68 | getFields 69 | :: Coercible object (Object fields) 70 | => object 71 | -> Free (Coyoneda CommandF) fields 72 | getFields object 73 | = liftF $ liftCoyoneda 74 | $ GetFields (coerce object) 75 | 76 | setField 77 | :: Coercible object (Object fields) 78 | => Ctor object fields 79 | -> object 80 | -> Lens' fields field 81 | -> field 82 | -> Free (Coyoneda CommandF) () 83 | setField _ object field value 84 | = liftF $ liftCoyoneda 85 | $ SetField (coerce object) field value 86 | 87 | consume 88 | :: forall object fields 89 | . Coercible object (Object fields) 90 | => Ctor object fields 91 | -> object 92 | -> Free (Coyoneda CommandF) () 93 | consume _ object 94 | = liftF $ liftCoyoneda 95 | $ Consume (coerce @object @(Object fields) object) 96 | 97 | goToRoom 98 | :: RoomName -> Free (Coyoneda CommandF) () 99 | goToRoom roomName 100 | = liftF $ liftCoyoneda 101 | $ GoToRoom roomName 102 | 103 | instance IsString Command where 104 | fromString = Command . display 105 | -------------------------------------------------------------------------------- /lib/Hyzzy/Object.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE GADTs #-} 2 | module Hyzzy.Object where 3 | 4 | import Data.IORef 5 | import Data.Typeable 6 | import Data.Unique 7 | 8 | 9 | data Object fields = Object 10 | { objectId :: Unique 11 | , objectFields :: IORef fields 12 | } 13 | 14 | -- e.g. 'Foo' for 'newtype Foo = Foo (Object FooFields)' 15 | type Ctor object fields 16 | = Object fields -> object 17 | 18 | data Instance object where 19 | Instance :: Ctor object fields 20 | -> fields 21 | -> Instance object 22 | 23 | data SomeInstance where 24 | SomeInstance :: Typeable object 25 | => Instance object 26 | -> SomeInstance 27 | -------------------------------------------------------------------------------- /lib/Hyzzy/Room.hs: -------------------------------------------------------------------------------- 1 | module Hyzzy.Room where 2 | 3 | import Data.String 4 | 5 | 6 | newtype RoomName = RoomName 7 | { unRoomName :: String } 8 | deriving (Eq, Ord, Show) 9 | 10 | instance IsString RoomName where 11 | fromString = RoomName 12 | -------------------------------------------------------------------------------- /package.yaml: -------------------------------------------------------------------------------- 1 | name: hyzzy 2 | version: 0.1 3 | synopsis: Text adventures where actions have types 4 | description: A framework for defining text adventures via Haskell files. Play by combining functions, not by guessing phrases. 5 | homepage: https://github.com/gelisam/hyzzy#readme 6 | github: gelisam/hyzzy 7 | license: PublicDomain 8 | author: Samuel Gélineau 9 | maintainer: gelisam+github@gmail.com 10 | category: Game 11 | extra-source-files: 12 | - README.md 13 | ghc-options: -W -Wall 14 | 15 | dependencies: 16 | - base 17 | - free 18 | - generic-lens 19 | - kan-extensions 20 | - lens 21 | 22 | library: 23 | source-dirs: lib 24 | 25 | executables: 26 | hyzzy: 27 | source-dirs: exe 28 | main: Hyzzy.Main 29 | dependencies: 30 | - containers 31 | - directory 32 | - exceptions 33 | - filepath 34 | - haskeline 35 | - hint 36 | - hyzzy 37 | - mmorph 38 | - transformers 39 | -------------------------------------------------------------------------------- /regenPublicObjects.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function usage { 4 | echo "usage:" 5 | echo " $0 [games/]" 6 | echo 7 | echo "Generate \"games//PublicObjects.hs\"" 8 | echo "based on \"games//Objects.hs\"." 9 | } 10 | 11 | function generatePublicObjects { 12 | echo "-- GENERATED BY \"$0 $ARGS\", DO NOT MODIFY BY HAND" 13 | echo 14 | echo "-- A variant of Objects which only export the type constructors, not the data constructors," 15 | echo "-- so the player cannot cheat." 16 | echo -n "module PublicObjects (" 17 | sed 's/$/, /g' | tr '\n' ' ' | sed 's/, $/) where/g' | tr -d ' ' 18 | echo 19 | echo "import Objects" 20 | } 21 | 22 | ARGS="$@" 23 | if [ "$1" = "--help" -o "$1" = "-h" ]; then 24 | usage 25 | elif [ -d "$1" -a -f "$1/Objects.hs" ]; then 26 | # e.g. "games/castle" 27 | GAME="$1" 28 | 29 | cat "$GAME/Objects.hs" | \ 30 | grep '^newtype' | \ 31 | cut -d' ' -f2 | \ 32 | generatePublicObjects > "$GAME/PublicObjects.hs" 33 | else 34 | usage 35 | exit 1 36 | fi 37 | -------------------------------------------------------------------------------- /runTests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | function usage { 5 | echo "usage:" 6 | echo " $0 [games/ games//tests/]" 7 | echo 8 | echo "If no test file is given, runs all the tests." 9 | } 10 | 11 | function runTest { 12 | local GAME="$1" 13 | local TESTFILE="$2" 14 | local EXPECT_SCRIPT="$(cat "$TESTFILE" | \ 15 | sed 's/\([\\."]\)/\\\1/g' | \ 16 | sed 's/^\([^>].*\)$/expect { "\1\\r" {} timeout { exit 1 } } /g' | \ 17 | sed 's/^> \(.*\)$/expect { "> " { set timeout 1 } timeout { exit 1 } } send "\1\\r" /g' | tr ' ' '\n')" 18 | 19 | #echo "$EXPECT_SCRIPT" 20 | #echo 21 | 22 | #./regenPublicObjects.sh "$GAME" 23 | stack build hyzzy 24 | expect -c 'set timeout 5' \ 25 | -c "spawn stack run hyzzy $GAME" \ 26 | -f <(echo "$EXPECT_SCRIPT") 27 | } 28 | 29 | if [ "$1" = "--help" -o "$1" = "-h" ]; then 30 | usage 31 | elif [ -d "$1" -a -f "$2" ]; then 32 | runTest "$1" "$2" 33 | echo "** TEST PASSED **" 34 | elif [ -z "$1" ]; then 35 | for GAME in games/*; do 36 | for TESTFILE in $GAME/tests/*; do 37 | runTest "$GAME" "$TESTFILE" 38 | done 39 | done 40 | echo "** ALL TESTS PASS **" 41 | else 42 | ( 43 | echo "unrecognized arguments: " $@ 44 | usage 45 | ) 1>&2 46 | exit 1 47 | fi 48 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-15.11 2 | packages: 3 | - . 4 | - games/castle 5 | - games/maze 6 | extra-deps: 7 | - haskeline-0.8.0.0 8 | --------------------------------------------------------------------------------