Please check out my choice-based interactive fiction site

Hey guys, I’ve created my own choice-based interactive fiction site: diorama2-ussherpress.rhcloud.com

(I actually tried to post earlier but it’s not showing up. If it does show up, mods, feel free to delete this post.)

I was aware of existing systems for writing choice-based IF online, but I intentionally avoided reading about how they worked. I wanted t o see if I could design it with my own ideas in mind first.

My original goal was to write a site that made it easy to write quick, short narratives that you could share easily with others. However, after a while, I added more features to the site and soon found the syntax became a bit unwieldy. I wrote the first version of the site last summer. You can visit it at www.diorama.club.

Fast forward to a couple of months ago. I decided to rewrite the site and improve the syntax slightly. That’s what you see at diorama2-ussherpress.rhcloud.com. It’s still a work in progress. There are tutorials on the site to show how the syntax works. I created some examples as well, but admittedly I’m not a writer, so they aren’t the greatest. :slight_smile:

Thanks for reading!

first i tried this. Thought that i could offer alternative texts depending on “yog”, but i can’t get it to work.

{{ #Kitchen You are standing in the kitchen. { Look in the sink Piles of dishes. } { Look in the fridge ((!yog))There's a yogurt ((yog))It's empty ((!yog){ Eat Yogurt >>yog } { Leave it.} }

then i tried this, which works (sort of) but it feels the choices should be the text rather than the prompts, also it always says “nothing here” after eating the yogurt. looking for some kind of “else” here.

{{ #Kitchen You are standing in the kitchen. { Look in the sink Piles of dishes. } { Look in the fridge ok ((!yog)){There's a yogurt Now what {Eat it >>yog Yuk!} {Leave it} } ((yog))nothing here. }

one other point is that it’s too easy to close two bracket levels and get a “}}” rather than a “} }”. Since this is different syntax, it breaks.

Nevertheless, i quite like this choice system. It’s quite simple at first, until you get into lots of brackets. I guess that’s when you break into blocks.

nice.

Hi, thanks for trying it out!

The example you gave is from v1 of the site (diorama2-ussherpress.rhcloud.com) uses a different syntax for what you’re trying to do.

I’m not sure what the specific issue is with the example you gave, but I do remember when I was writing that syntax and implementing the parser I encountered some major design issues in how I parsed the text, hence why I re-did the syntax. :slight_smile: It’s actually possible to write problematic statements using conditionals in v1. v2 fixes these problems.

aha! yes looks like i was using the wrong page and the old syntax.

without further ado, i try some yogurts…

this works, but with problems. I’d like to be able to redirect after text inside conditionals. foo ?: bar => moo, but moo is redirected regardless. instead i create a catch all and redirect.

[code][kitchen]
You’re in the kitchen. there’s a fridge here.
{ look in the fridge

!yog ?: There’s a yogurt.
// falls through to bogus line, can’t redirect here
yog ?: It’s empty
!yog ?: { Eat yogurt

 Yuk! You hate yogurts.
 / yog = true
 => kitchen

}
!yog ?: { Leave it => kitchen }
}
{ Go home => main }

// would like to be able to catch all and loop rather than having
// each case redirect manually.
I don’t want this line to be here
=> kitchen

[main]
Let’s to go the kitchen
=> kitchen[/code]

Also testing yog all the time is messy. so i try to factor a block, thus:

[code]// this version we try to eliminate testing yog all the time
// by making a fridge block…
[kitchen]
You’re in the kitchen. there’s a fridge here.
{ look in the fridge

// these don’t work. always ends up in fridge. also would like to be
// able to redirect without text.
!yog ?: ok (bogus line) => fridge
yog ?: It’s emtpy => kitchen
}

{ Go home => main }

// another bogus line to catch thread
That was fun.
=> kitchen

[fridge]
There’s a yogurt.
{ Eat yogurt

 Yuk! You hate yogurts.
 / yog = true

}
{ Leave it => kitchen }

That was fun
=> kitchen

[main]
Let’s to go the kitchen
=> kitchen[/code]

This time, i’d like to redirect inside conditionals, and to be able to redirect without text eg foo ?: => bar

one last thing. can you make non-reachable blocks a warning not an error.

This doesn’t run. but non-reachables are inevitable whilst building and testing.

[code][xanado]
you’re in xanado

[main]
You’re in main[/code]

Otherwise, this is much better than version1.

Hi. Thanks again for the feedback. This is really is good stuff.

I agree with your first two points about having redirects without needing the text before it. That’s one design decision I made because I was afraid of having accidental infinite redirect loops like

[kitchen]
=> living room

[living room]
=> kitchen

I have a solution that I can employ to make sure that if the above happens, I can detect it and stop the script, but the user’s game wouldn’t be salvageable. Forcing some text in there ensures the user at least gets some feedback from the system that stuff is happening. Anyway, I think having the redirects are more important than avoiding the error, so I’ll fix this.

I also agree with your point about making unused sections just warnings. It definitely is useful as you’re writing a story to be able to create temporary sections that you aren’t using yet but will in the future.

I’ll try to make those two fixes this week when I have some time.

This is most interesting.

The “=>” redirect is really a “goto” and, in order to allow arbitrary logic to be programmed, you should definitely allow redirects anywhere, including inside conditionals and without necessarily having text emitted beforehand.

But, as you note, then there can be infinite loops without text being emitted. Any such loops will appear as the game “freezing” up. To be sure, there would still have been infinite loops, but with text. Still not an ideal situation.

So, can this be detected?

Simply marking each “node” as visited and clear mark when text emitted, then detect visiting nodes already marked, would work for simple cases, but is not sufficient in general.

Consider a deliberate counting loop (without text) that performs a calculation (adding up a score say) but only prints the result at the end. It would be stopped as an “infinite” loop by the above, oversimplified detector.

How would a detector know whether a set of conditions were to eventually end?

Turns out this problem is impossible being due to it being the halting problem.

A Classic - you want to write a simple choice engine. next thing you know a theoretical impossibility jumps up and kicks you in the balls! :slight_smile:

So…

I don’t think you should cripple your system because of this problem.

Instead, my suggestion is that you have a watchdog timer. This timer is reset every time you emit output. If the timer reaches a predefined threshold (could be a special variable or hardcoded), it declares an infinite loop and jumps to a predefined safe place (eg main).

I’m fascinated to know what other choice systems do in this situation? Are they too weak to have loops or do they ignore the problem altogether.

Anyone know?

You can’t solve the halting problem but you can cover a lot of real-world cases. A check for an infinite loop in unconditional gotos is still worthwhile.

Beyond that, all you can do is trace through-paths while keeping track of the state variables. Use a hard limit on execution time and let the user crank it up if necessary.

Some systems do a large number of random play-throughs – that is, simulating a random player choice at each stage. This gives you a statistical map of what’s reachable, which is valuable even though it’s not logically complete.

Yes indeed, there’s could be some simple tests for common cases.

For example, revisit a node without text output or any variable changing => loop.

or, a bit more complicated:

Revisit a node without text output and without any variable tested inside that state changing => loop

OR

are you suggesting static “compile time” path analysis.

Construct a memory graph of node connectivity. Any unconditional loops => fail.

Any conditional loops must depend on variables that are also changed by the loop (somewhere). Does not prove ok, but if dependencies are not changed, then must fail.

other ideas?

Yeah, my plan was just to keep a list of all nodes visited as I’m redirecting at runtime and if a node is ever re-visited, to error out. Like I said, it would lead to an unsalvageable state… although I could possibly back out of it to an earlier state and let the user know the node they chose had a runtime error.

The way I was going to implement the states was to keep a stack of states, one for each node that you visited, so it would possible to back out–or even ‘rewind’ to an earlier choice if you decide you wanted to change your mind or have the game return to a ‘save point’.

FYI: I updated the site to allow arbitrary redirects without text. I detect infinite loops at runtime by simply keeping track of which nodes I’ve visited that themselves have no text and just a redirect. If I visit a node twice, there’s probably an infinite loop in there. I give the user an error and back them out to before they made the choice down the path leading to the error. This way they can continue on with their game should there be another option that doesn’t have an error.

I’ve also made unused sections just warnings instead of errors.

As for the first problem of statements like this:

foo == bar ?: do something and immediately redirect => some_section
foo != bar ?: do something else => some_other_section

There’s a limitation in my parser/compiler where I don’t treat redirects on the same line as text as “belonging” to the text preceding it with the conditional. I still have to fix this because I think it would be a good feature to support.

If anyone is curious, I added some more features over the weekend: diorama2-ussherpress.rhcloud.com … e_advanced The docs I put up are sparse, but I’ll add to them this week.

You can now group content into blocks and apply a single conditional statement to them all. This is to get away from having the exact same conditional on several lines. You can also use a “subroutine” redirect which will continue to where the subroutine was called once it completes. The regular redirect just ends the script outright. Finally, I added pre and post sections that are always evaluated with each node. This way, you can have the same bit of text appear on each node shown. A typical scenario for this would be to have some global text or branch that is conditional on, say, an item being held by the user. Once the user has this item, a branch would always appear (think “Use magical item here”).

I’m going to add a variable to report back the current section name as that will help with making the pre and post sections more useful. (Imagine having a branch that only shows up in certain rooms.)

These features are so new that I still have to play with them to see what’s possible :slight_smile: , but I can say that now you can write some more complex things. Coming up with the syntax that is easy to use is definitely challenging. (Of course, I also made it extra challenging by not allowing myself to see how other systems do it.) I’ll probably write up a blog post eventually to cover how I evolved my syntax.

I’ve created yet another site like this one and the one before it. :slight_smile: Please take a look:

thiswayorthat-ussherpress.rhcloud.com/

This latest version is very different in that it is more a collaborative site where you can add to other people’s stories. You can still create branches, but now they’re more explicit and aren’t encoded in the text. You just add them, almost like a message forum.

I made this one as an experiment to see what kinds of things people can come up with if they tried making interactive fiction in an exquisite corpse fashion.