Yaifl - a Haskell interactive fiction library

I’ve been working on this on and off for a while and rewritten everything from scratch at least 3 or 4 times - but I feel I’ve gotten far enough to be happy to show it off. :slight_smile:

It’s a Haskell eDSL that is very very heavily inspired by the Inform7 architecture, which I think is absolutely fantastic.

It is primarily a project for curiosity’s sake rather than being a serious authoring platform (e.g. it’s never going to compile to the Z-machine because it’s Haskell rather than a new language with a compiler written in Haskell)

The core is 95% done - objects, rulebooks, running activities and actions, some adaptive text with verb conjugations and whatnot, and so on - and I’m currently adding the standard library of actions and activities by going through all of the 400+ i7 examples and porting them to Yaifl. I’m up to about example…12…but as an example, here is inform7’s Starry Void:

starryVoidWorld :: Game PlainWorldModel ()
starryVoidWorld = do
  setTitle "Starry Void"
  tcr <- addRoom "The Centre Ring"
    ! done
  tsv <- addRoom "The Starry Void"
    ! done
  tsv `isInsideFrom` tcr

  tmb <- addDoor "magician's booth"
    ! #initialAppearance (DynamicText @PlainWorldModel $ Right ("description of magician's booth door", RuleLimitedEffect $
        withThing $ \t ->
        do
          p <- getPlayer
          picr <- (== unTag tcr) . getID <$> getLocation p
          let cl = isClosed t
          [sayingTell|{?if picr}A magician's booth stands in the corner, painted dark blue with glittering gold stars.¬
      {?else if cl}A crack of light indicates the way back out to the center ring.¬
      {?else}The door stands open to the outside.{?end if}|]))
    ! #front (tsv, Out)
    ! #back (tcr, In)
    ! done

  insteadOf (ActionRule #examining) [theObject tmb, whenIn tcr] $ \_ -> do
    booth <- getObject tmb
    let cl = isOpen booth
    [saying|It is dark blue and glittering with gold stars. {?if cl}The door currently stands open{?else}It has been firmly shut{?end if}.|]

  insteadOf (ActionRule #examining) [theObject tmb, whenIn tsv] $ \_ -> do
    booth <- getObject tmb
    let cl = isOpen booth
    [saying|The booth door is {?if cl}wide open{?else}shut, admitting only a thin crack of light{?end if}.|]

  tmb `isUnderstoodAs` ["door", "of", "the", "light", "crack", "thin crack"]

  before (ActionRule #going) [throughTheClosedDoor tmb] "" $ \_ -> do
    [saying|(first opening the door of the booth)|]
    Nothing <$ parseAction silentAction [] "open door"
  pass

It’s been very enjoyable and I’ve learned a lot. I’m hoping that now I’ve done the big things (looking and room descriptions, going places, printing out lists of things with varying levels of information) it’ll be faster going to get it more complete.

Anyway, hopefully it’s of interest to someone :slight_smile:

11 Likes