Skip to content

Haskell-written interactive fiction engine + a couple of trippy example games

License

Notifications You must be signed in to change notification settings

harryaskham/hif

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HIF: Haskell Interactive Fiction

https://circleci.com/gh/harryaskham/hif.svg?style=svg

Interactive Fiction engine in Haskell and some games (under HIF.Games) using it.

Implements an ECS in which the game is built up incrementally inside the State monad.

Example Game

The following example from HIF.Games.ExampleGame implements a simple game demonstrating most of the functionality. Click below to view a cast of the example code in action.

https://asciinema.org/a/337073.png

buildGame :: App ()
buildGame = do
  -- Create a few locations.
  forest <- mkLocation "forest"
  clearing <- mkLocation "clearing"
  unknown <- mkLocation "unknown location"

  -- Link the locations geographically.
  -- We need to specify both directions, because we allow for non-reversible paths.
  forest `isSouthOf` clearing
  clearing `isNorthOf` forest

  -- Add a simple constant description to the forest and the through-the-portal place.
  describeC
    forest
    "A thick, overgrown forest. Little sunlight penetrates the canopy overhead."
  describeC
    unknown
    "The land beyond the portal is strange beyond description. Say 'end' to conclude the game."

  -- Create and describe the player.
  player <- mkPlayer "yourself" forest
  describeC player "You are a weary adventurer, wandering this forest."

  -- Create a simple, interactable object that appears in the clearing.
  -- It can be referred to by the alias 'chain'.
  -- By making it 'Storable' it can be picked up, and by 'Wearable' can be worn.
  necklace <- mkSimpleObj "necklace" ["necklace", "chain"] (Just clearing)
  describeC necklace "A simple golden necklace. It radiates mystery. You should try it on."
  modifyEntity (set storable Storable) necklace
  modifyEntity (set wearable Wearable) necklace

  -- Add a dynamic description to the clearing.
  -- Whenever the description is called upon, the lambda will be called
  -- with an up-to-date view of the 'clearing' entity.
  describe clearing $ const do
    wearingNecklace <- player `isWearing` necklace
    if wearingNecklace
      then return "A clearing in the forest. The necklace you found has caused a portal to open."
      else return "An unremarkable clearing in the otherwise dense forest."

  -- Add a watcher that opens and closes the portal based on the status of the necklace.
  -- All watchers trigger each turn.
  addWatcher do
    wearingNecklace <- player `isWearing` necklace
    if wearingNecklace
      then unknown `isNorthOf` clearing
      else modifyEntity (set toNorth Nothing) clearing

  -- 'Win' the game by following the instructions to say 'end'
  addSayHandler (\words -> when (words == "end") setGameOver)

  -- Finally, add an achievement for eating the necklace...
  registerAchievement "Fool's Gold"
  modifyEntity (set edible Edible) necklace
  addEatHandler necklace $ const do
    logT "You struggle, but manage to swallow the necklace whole."
    addAchievement $ Achievement "Fool's Gold" "You should fix up your diet..."


-- The below will kick off the game loop after constructing the above game.
main :: IO ()
main = runWithBuilder buildGame

We can write some nice declarative tests for the above as follows:

main :: IO ()
main = hspec do
  describe "ExampleGame" do
    it "is winnable" do
      checkPreds
        EG.buildGame
        [ "go north"
        , "get necklace"
        , "wear necklace"
        , "go north"
        , "say end"
        ]
        [ gets (view gameOver) ]

    it "can't be won without wearing the necklace" do
      checkPreds
        EG.buildGame
        [ "go north"
        , "go north"
        ]
        [ lastInstructionFailed ]

Building

`stack run <bin-name>` should suffice; also `stack –nix` on Nix systems (though `stack –nix ghci` is broken on macOS, a known issue).

About

Haskell-written interactive fiction engine + a couple of trippy example games

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published