I originally learned ChoiceScript several years ago, long prior to the introduction of Implicit Control Flow in 2017, then vanished from the community and stopped writing in CS for several years. During that time, I forgot basically everything about the language–basic syntax,
*choice statements, temporary variables, you name it.
Then, just a few months ago, I decided to get back into ChoiceScript and began re-learning the language from scratch. I was a complete blank slate. Then I started running into some frustrations with mandatory
*gotos and, while searching for a workaround, stumbled across Implicit Control Flow. Thinking I’d give it a shot, I flipped ICF on and learned the rest of ChoiceScript while using it.
Additionally, in between these two periods of my life, I acquired a computer science degree. I now code regularly and am very familiar with the traditional structure of imperative languages, but those things weren’t true when I learned ChoiceScript the first time. At that point, my only experience with “programming” had been some minimal exposure to the block-based structures used in GameMaker. I’d never coded in an actual language before. I had, however, done a lot of writing, and wrote fiction far more frequently than I do now.
These circumstances gave me the unique experience of learning ChoiceScript twice–once without ICF as a writer and non-programmer, and once with ICF, as an experienced programmer. I wanted to talk a little about that experience and see if anyone else has been in a similar position. I’ve noticed some people come into the CoG community self-identifying as either “writers first, programmers second” or “programmers/game designers first, writers second,” so I hope what I write here can speak to both of those groups and potentially bridge some gaps. I also hope to point out some ways I think ChoiceScript could be made more accessible to programmers, without making it any less accessible to non-programmers.
- Learning ChoiceScript with ICF
Turning on ICF was a game-changer for my productivity. My writing speed saw a huge improvement, as I was able to create much more complicated branching paths in a fraction of the time.
I experienced this speed-up for 3 reasons.
First, because 95% of my
*gotos took me to
*labels placed immediately after the
*choice block within which the
*goto is enclosed. Moving from explicit to implicit control flow felt like I had an extra person next to me, typing out each of those
*labels on my behalf.
Second, because coming up with new label names is a creative task that, while not very hard, does eat up a tiny amount of time whenever you have to do it. ICF cuts down the number of times your brain has to invent a new label.
Third, because keeping track of label names becomes ever more of a cognitive burden as a story gets longer. Before switching to ICF, I found myself storing a vague list of the current scene’s labels in my head, so that I could avoid repeating the same label twice. Not only did I want to avoid duplicate labels, but I also wanted to make new labels sufficiently distinguishable from the old ones. Ideally, I thought, my labels should be both descriptive of the part of the story they’re located in, and yet memorably different from labels located in adjacent/descriptively similar parts of the story. That way I won’t confuse them. This got harder and harder to do as my scenes got larger.
Coming to ChoiceScript as an experienced programmer, I originally didn’t even realize that
*gotos were necessary to close out
*choice blocks. I simply assumed that ChoiceScript operated like a typical programming language, where instructions are read one after the other in the order they’re written and where if/else statements don’t interrupt this process. And, since
*choice blocks have a vaguely similar syntax to
*else statements (i.e. an asterisk, followed by a command name, followed by an indent), I assumed that they were no different.
My assumptions that ChoiceScript behaved this way ran so deep that despite reading the entire tutorial on the CoG website before I started coding/writing, I was still surprised when I got my first error messages. The part of the tutorial that would have corrected me somehow went in one ear and out the other. As weird as it sounds, I think this might be a pretty common experience for programmers who are new to ChoiceScript.
The big downside I experienced from switching to ICF was losing some control over whitespace. Consider the following lines of ChoiceScript:
You walk down the street, trying to look as inconspicuous as possible. *if (ninja) As a secret ninja, you can't afford to draw attention to yourself. *goto unacceptableText *elseif (wizard) It's illegal to be a wizard, of course, and you don't want to be imprisoned. *goto unacceptableText *else The thought of being attacked by a criminal wizard or a stealthy ninja terrifies you, and you know they only target the most conspicuous of pedestrians. Do you really want to be their next victim? *goto unacceptableText *label unacceptableText No—that would be unacceptable.
If you’re either a wizard or a ninja, we want to print “No—that would be unacceptable” on the same line as the text which precedes it. If we are neither a wizard or a ninja, we instead want to insert whitespace between “Do you really want to be their next victim?” and “No—that would be unacceptable.” The above code accomplishes this.
But with ICF, the same task is no longer so simple.
*create implicit_control_flow true You walk down the street, trying to look as inconspicuous as possible. *if (ninja) As a secret ninja, you can't afford to draw attention to yourself. *elseif (wizard) It's illegal to be a wizard, of course, and you don't want to be imprisoned. *else The thought of being attacked by a criminal wizard or a stealthy ninja terrifies you, and you know they only target the most conspicuous of pedestrians. Do you really want to be their next victim? No—that would be unacceptable.
ChoiceScript interprets the blank line above “No—that would be unacceptable” as lying outside the
*else block, creating whitespace between “No—that would be unacceptable” and the text which precedes it. This occurs regardless of whether the player is a ninja or a wizard. And since the
*line_break command produces a different kind of whitespace than a traditional paragraph break, ChoiceScript has no easy way to insert whitespace in these locations. Indenting the blank line doesn’t move it inside of the else statement either–though I don’t think it should, since text editors don’t always remember the number of indents on a blank line.
This same problem occurs with the last option in a
*choice block as well.
If ChoiceScript ended blocks with a closing curly brace (as in C or Java) or a keyword like “end” (as in Julia), this wouldn’t be a issue for ICF. It also isn’t a issue when using explicit control flow, since
*goto statements serve as a sort of “closing bracket” for all indented blocks.
The workaround I came up with was repurposing the
*comment command as a closing bracket, either on every conditional:
*create implicit_control_flow true You walk down the street, trying to look as inconspicuous as possible. *if (ninja) As a secret ninja, you can't afford to draw attention to yourself. *comment endcond *elseif (wizard) It's illegal to be a wizard, of course, and you don't want to be imprisoned. *comment endcond *else The thought of being attacked by a criminal wizard or a stealthy ninja terrifies you, and you know they only target the most conspicuous of pedestrians. Do you really want to be their next victim? *comment endcond No—that would be unacceptable.
… or solely at the end of an entire
*create implicit_control_flow true You walk down the street, trying to look as inconspicuous as possible. *if (ninja) As a secret ninja, you can't afford to draw attention to yourself. *elseif (wizard) It's illegal to be a wizard, of course, and you don't want to be imprisoned. *else The thought of being attacked by a criminal wizard or a stealthy ninja terrifies you, and you know they only target the most conspicuous of pedestrians. Do you really want to be their next victim? *comment endcond No—that would be unacceptable.
Similarly, I use “*
comment endchoice” whenever I need to close out individual options within a
Adhering to this stylistic convention is still less labor-intensive than using explicit control flow, but I’d prefer something like this to be built into ChoiceScript rather than having to use a workaround. The alternative is that I sacrifice a little bit of (in my opinion, important) control over the paragraph breaks in my story.
(One possible solution: ChoiceScript could have (optional) no-op commands (perhaps called
*endchoice) which you could place at the end of choices and conditionals that would provide the same functionality as my
*comment endcond/endchoice workaround. The syntax of the Julia language, which uses an
end keyword instead of a closing bracket, does something similar. And since both Julia and ChoiceScript were built with intentionally non-threatening syntax, I figure ChoiceScript could be able to benefit from some of their design decisions.)
- Learning ChoiceScript without ICF
When I first learned ChoiceScript, I set out to write a short story about a man with Herculean strength who had to escape from a prison. Unfortunately, I never finished it. But I do still have the original files tucked away on my computer.
Looking back over my pre-ICF code from that era, I’m struck by how different my ChoiceScript style was. I used far fewer indents, far fewer conditionals, and far fewer branching paths. Back then, I conceptualized code not as a bunch of instructions executed in a sequence, but as pieces of fabric that I could stitch together with string.
Each branching path (created by an
*choice block) was a different piece of fabric. I could embroider those pieces of fabric by writing more of the story inside of them, and I could stitch them to additional pieces of fabric by creating new branching paths. I never wanted to juggle too many pieces of fabric at once, however, and if two of my branches got too large, I would use a
*goto to send them both to a common location–stitching them together into a single piece of fabric.
The ultimate goal, of course, was to create a giant quilt.
My mental model of programming wasn’t the only place past-me and present-me differed. Past-me also experienced none of present-me’s frustrations with mandatory
*goto statements. I didn’t even think of it as a restriction on what I could do with the language. Given how past-me conceptualized coding, this makes perfect sense.
Additionally, I didn’t feel frustrated with having to remember large numbers of labels. I created new labels the same way I created new temporary variables: whenever I needed them. And mentally keeping track of existing labels never struck me as a uniquely arduous task. If past-me had been handed the option to use ICF, I’m not sure I would have taken it–not at first, anyway.
In short, I was demonstrably fine. While that might have changed if I had used ChoiceScript for a longer amount of time before I took my hiatus from the language, I was perfectly content during the period I interacted with it. Additionally, my learning curve then was no longer or shorter than my learning curve today. I reached ChoiceScript fluency in about the same time, with or without ICF.
There’s a big takeaway I got from this experience: ChoiceScript contains certain features that were, counter-intuitively, easier for me to understand as a non-programmer than a programmer.
As I explained earlier, I read CoG’s entire ChoiceScript tutorial thoroughly, top to bottom, before I even wrote a single line of ChoiceScript as a programmer. The tutorial explains mandatory
*gotos very clearly. On the Introduction to ChoiceScript page, it states:
Note that every indented (nested) block must conclude with either a
*finishcommand (which ends the scene) or a
*gotoline which jumps to another line in the scene.
Yet despite this information being presented right to my face, it went in one ear and out the other. When I learned ChoiceScript as a non-programmer, I grasped the concept of mandatory
*goto statements no less easily than any other aspect of the language. But when I learned ChoiceScript as a programmer, my brain went kaput.
I think this speaks to some interesting ways that ChoiceScript is inaccessible to programmers. When my
*choice blocks first started generating errors, it took me a little bit of time to diagnose what I’d done wrong. It took a fair bit longer to find out that ICF existed, and to enable it. This wasn’t the biggest stumbling block in the world on my way to re-learning ChoiceScript, but it was a stumbling block that I never encountered at all when I learned ChoiceScript as a non-programmer. I also think it’s a stumbling block that, if the programmer version of me had been introduced to ChoiceScript a little differently, I might have avoided.
At present, Implicit Control Flow isn’t mentioned in any of the any of the tutorial pages for ChoiceScript located on the main CoG website. Information about ICF can be found on the wiki, but wikis are meant to serve as reference material, and serve a very distinct purpose from tutorials.
I think an easy way to make ChoiceScript more accessible to programmers, and to help them avoid the stumbling blocks I ran into, is to make an additional page under the Make Your Own Games tab, specifically aimed at programmers and explaining how ChoiceScript differs from the languages they’re used to. That means illustrating how ICF works, how it can be activated, and why a newcomer might be interested in using it. Since using ICF significantly changes same pretty basic parts of ChoiceScript’s functionality, I think it’s really important that people who’d prefer to ICF to explicit control flow are introduced to the option as early as possible.
A lot of effort has been put into making ChoiceScript accessible to non-programmers, and for good reason. But in my opinion, the twin goals of making ChoiceScript accessible to non-programmers and programmers aren’t mutually exclusive. In fact, I think they have substantial overlap. I think what I’ve proposed here would help introduce people who might prefer ICF–regardless of whether they are programmers–to the option a little faster, and enable programmers with similar experiences to my own to avoid a hiccup or two while learning ChoiceScript.
tl;dr Implicit Control Flow should be added to the ChoiceScript tutorial on the CoG website, under a new page aimed at explaining to programmers how ChoiceScript differs from languages they’re used to. I think this is really important and could make ChoiceScript significantly more accessible. I also describe some problems with ICF and whitespace.
I’m interested in hearing whether anyone’s had a similar experience with ICF, programmer or otherwise. Did you learn ChoiceScript before or after Implicit Control Flow was released in 2017? How did you first hear about ICF, and–if you currently use it–do you wish you’d been introduced to it sooner in the ChoiceScript learning curve?