About my ChoiceScript dialect (fewer gotos and gosub arguments)


#1

I’ve been trying to get Dan for a while now to make some changes to ChoiceScript to match the particular dialect that I use. I wrote Choice of Robots and Choice of Alexandria using these changes, and now I’m writing Choice of Magics this way. These discussions with Dan typically take place in a vacuum, where I’ve got my arguments and he’s got his, and mostly they don’t go anywhere. But it occurred to me that others don’t even really know that I write ChoiceScript with a few different features added, and maybe telling you about the changes could inspire you to hack ChoiceScript yourself, or (I wish) clamor that my changes be merged into the main branch.

The main two features of the way that I write ChoiceScript are fewer gotos and using arguments for gosub. I see both of these as really nice for transforming complex ChoiceScript into less complex ChoiceScript. I’ll tell you about each and why and how I did it; if you’re motivated and know computer science, you can replicate them yourself, and if you’re motivated and don’t know computer science, I suppose you can ask ChoiceScript to support them.

*Fewer gotos. ChoiceScript is unique as a language in requiring gotos (or returns) to get out of if/else statements. My dialect looks like this:

*if autonomy >= 50      "I have an idea," says ${robot_name}. *else      "What should we do?" asks ${robot_name}. $!{rhe} looks *if autonomy >= 50      excited. *else      worried.

In this dialect, it’s assumed that if and else will fall out to the next line with the same indentation as the “if.” I find that using less gotos like this achieves a few things. One, I’m not stopping to think of label names every few seconds. Two, genuinely meaningful jumps that go someplace entirely different in the code stand out more; a goto always means something unusual is going on, as it does in other languages.

*if robot_dead      *comment Skip ahead to after the robot leaves      *goto someLaterScene *elseif robot_happy      "Hello," ${robot_name} brightly. *else      Your robot looks upset. How do you respond?

This change is IIRC fairly easy to accomplish if you want to modify scene.js – just comment out the part of the if-handling code where it gives the error when you don’t have a goto.

Doing the same thing for choices is slightly trickier. fake_choice can do one level like this, but not nested levels; by default, ChoiceScript doesn’t keep track of where to jump to in the outer fake_choices if you pop out of an inner one. So I modified the interpreter to remember what those line numbers to jump to are, when exiting choice code. So my choices don’t end in goto either; they always are assumed to go to the next line at the same indent as the *choice. Same reason - I want a goto to really signify we’re going somewhere weird. (Not coming up with labels is also nice.)

Dan’s fairly convinced that requiring the gotos prevents bugs. I’m not so sure - Python works very much this way, and all other languages have some way of signaling that the if/else is done that doesn’t require goto, like “endif” or a close curly brace. Most languages really try hard to avoid goto.

*Arguments. ChoiceScript doesn’t have full-fledged “functions” in the sense of other computer languages - pieces of code that take some inputs (“arguments”) and perform some activities based on their input. The closest thing it has are subroutines with *gosub, which jumps and returns like a function, but can’t take any arguments.

I found this unfortunate in Choice of Robots, because I wanted to report variable changes that always matched the text. (For example, “Autonomy++” for a gain of two autonomy.) Doing these things independently seemed like it would allow for bugs where one change happened, but another was reported - printing two pluses but only gaining one autonomy, for example. Furthermore, I wasn’t totally sure that the reporting of stat changes would survive the editorial process, and I wouldn’t want to go through the whole game and change how they were all reported.

So instead, I modified gosub so that it would take “arguments.” It was hacky because I didn’t expect to merge to the main ChoiceScript branch, but essentially, if I wrote something like

*gosub bump “autonomy” 2

then a variable arg0 would be set to the string “autonomy,” the variable arg1 would be set to 2, and I’d jump to a gosub called “bump” that would set the variable mentioned in the string to the value in the variable, and also report the change by putting down a number of +'s or -'s equal to the change. If I wanted to change a bump, I would change only one number. If I wanted to change how the stat changes were reported, I could do it in one place. (Well, a few places, since either gosub_scene didn’t work at the time or I wasn’t aware of it.)

I admit, that’s about all I’ve used said functions for, but it’s quite handy for that, as all my stat changes would be a lot more cumbersome without it.

None of these changes required a whole lot of code, but they do require a bit of picking at the code every time I bother to update my ChoiceScript, which means that happens basically once per game I write. Still, I find these features indispensable.

I hope you found this interesting, and you’re now thinking not just about how ChoiceScript is, but how it could be.


Question regarding the use of if/else statements
#2

I hope Dan expands the *multiple choice feature to allow repeatable options in the sets of the choice bodies. If he’d allow that I would be using that feature even more then I do and I figure I use it more then most everyone in the community. It is very powerful but not quite perfect yet :slight_smile:

I’m not a computer wizard, so I will have to rely on the magic of all the code magicians such as yourself.


#3

Interesting. I remember checking out your code a while back after playing CoR and struggling to figure out how it worked when it seemed to break the rules I had just learnt.

I will have to go back and have a look at some point. Thanks for sharing.


#4

I read CoRobot’s code and badly wanted to use your custom code. I am glad you suggested them to Dan. I realize changing the master code of CS is a process so hopefully over time, it gets there with arrays and functions. Imagine the games we can make then.


#5

Well, I’ll add the changes if Dan ever gives the okay…


#6

It’s both ugly and annoying forcing the useless GOTOs, but I appreciate the reasoning behind it. Like you, I would much prefer an *endif or similar. That would be a lot more intuitive.


YES. If there is ONE feature that CS really needs, it’s this (and *create_array). I always work around this by having a series of global (*create) vars, arg0-3 etc.

*set arg 0 ...
*set arg1 ...
*gosub_scene thing

.... thing.txt
*comment do stuff
*set arg0 ""
*set arg1 ""
*return

To be honest, the most interesting thing about this thread is that you’re allowed to use modified code. That’s something I’ve wondered for years but never had an official answer on.


#7

I’ve suggested *endif as an alternative, too, and I think it addresses Dan’s objection to omitting the goto, that it’s possible someone didn’t intend to fall through.

I assumed the fact that I used modified ChoiceScript was an open secret since people can see the source of Choice of Robots and realize something funky is going on. Presumably CoG would frown on many changes, particularly those that make the code hard to read. I’d like to think the reason they agreed to mine is that they actually make the code easier to read. It’s possible my day job credentials helped, too (I teach CS at Northeastern).


#8

Would an *endchoice (or a more generic *end) work too (so that we could fall out of *choice blocks without using *gotos or *fake_choices)?


#9

*endchoice would certainly be possible, and from an implementation point of view, I think the only difference from Python-like fallthrough would be a line that raises an error if endchoice is missing. I’m not sure whether it would appear at the end of every #option or the end of the whole *choice, which further prompts the question of what kind of error it’s really trying to prevent. I’m frankly pretty happy without it or *endif.


#10

Well, I was thinking of it being used mainly to re-set the indent, rather than having some special deep significance. (And it would certainly only be at the end of the whole *choice.)


#11

I’m assuming Dan’s arguments revolve around keeping ChoiceScript on the easier-to-use side, so that more authors will be able to work with CS and not be intimidated by all the various things it can do?

Perhaps a happy compromise would be to allow programmers to modify the java that controls CS so that authors like @kgold can use their custom dialect.

Would the above compromise present a security risk, however?
While I am quite certain the established CoG authors are trustworthy, CoG is still a LLC, and has to cover themselves legally.

If the code governing ChoiceScript has been thoroughly tested and vetted, would alterations need to go through the same process in order to achieve “general release” quality?

Also, would modified code be allowed on the existing distribution platforms like Steam, Android, etc.?
Assuming a testing phase, would this have to happen for all instances of authors submitting modified code?
It might get a bit unwieldy.


#12

In my code, I’ve been doing variations of this:

Bob turns around,
*if (Friendly > 50)
  smiling.
*if not(Friendly > 50)
  frowning.

As you note, there are often times when *goto feels like swatting a fly with a flamethrower.

It’s also possible to use (Friendly > 50) vs (Friendly =< 50), of course, but I find the “if … if not” syntax more helpful for keeping track of what’s supposed to be mutually exclusive and what isn’t.


#13

That’s insidious and clever, but it seems to introduce the possibility of getting the checks inconsistent with each other, which could lead to your falling through with neither condition. Consider at least putting the number in a temp so that adjusting in one place changes both.


#14

I don’t know if it’s the way CS is supposed to work, but I can confirm this style of *if lists works (and the new multireplace feature @{|} helps significantly as well).

I’ll join you in camp less-gotos-please, especially in *choice bodies. For some reason *fake_choice often leads to errors, which means sticking with *choice even when most everything’s leading to the same place. I can understand potential follow-through errors, but isn’t that something that should be addressed in testing?

YES.

Also YES. This is what I’ve been wishing for without explicitly realizing it. This would be such a game-changer.

And for people worried about new authors being overwhelmed, I just want to point out that I’m a totally new CS author working on my first story, but I love working with the advanced features. One of the first things I coded was a loop involving rounding, modulo, referenced values, and a two-dimensional array (such as CS is capable of). It turned ~160 lines of code into ~60.
Obviously these features are a little obscure, but for authors who want to make use of them, they’re extremely helpful. New authors can easily stick with the basics, and I’m sure most do, so I don’t think ease-of-use should prevent the addition of more programmer-oriented features.


#15

Actually, the issue is more towards people who never heard even the word “CODING”

I believe CScript is made, at least, to allow these kind of people to not being afraid of coding their IF.


This kind of error are actually can be reproduced easily. Just nest some *fake_choices inside another *fake_choice and don’t put any *gotos in it.
You won’t be able to pass the “parent” *fake_choice, unless you make your choices by selecting the most bottom option of the list. Don’t ask me how or why :frowning_face:


But hey, don’t be mistaken. I’d like to join the side of less-gotos-please as well! :sweat_smile:


#16

But I don’t think any of the proposals affect non-coders, except to possibly make the coding easier. The basics would still be there: choices, labels, finishes, basic variable creation and setting, etc. Adding gosub arguments or extending array capabilities doesn’t change that.

The only thing that would affect new users would be changing the usage of *goto statements, which I wouldn’t foresee being difficult for people.

I think Team Fewer-Gotos-Please (excuse my prior poor English grammar) needs a flag or some matching T-shirts or something. :white_flag: :tshirt:


#17

I would like to join the cult of less gotos and choice parenting i.e. the fake choice nesting


#18

I’m pleased to note here that Dan and I made some progress in talking about this stuff over the weekend, and I’ll be merging in the changes I describe as soon as I download the newest ChoiceScript and figure out how they fit. Nothing’s absolutely finally approved, but it looks like you’ll be able to remove the goto requirement from if/else and choices by setting a predefined variable (in ChoiceScript, not the underlying JavaScript), as well as add arguments to gosub and gosub_scene. Stay tuned.


#19

Like a flag? Fantastic! That’s a promising start!


#20

Wow. That will be major. Thanks for your efforts @kgold.