A few tidbits about ifs and gotos and errors


#1

Because these are amongst the most common troubles people run into when starting to code, a few things:

  • Sometimes, when quicktest tells you a goto is needed at line X, it might mean the line above it. Often happens when a goto is missing in a choicebody. Don’t worry when QT tells you there’s no goto at line X yet there is one in the choice on that line. The goto might be missing in the last line of the previous choice.
  • the if-elseif-else structure is not always needed. If you want things to end at the same bit of text, you can do like this:
Text A
*if (x = 1)
   flavortext 1
*if ((x = 2) or (x = 3))
   flavortext 2
*if (x = 4)
   flavortext 3
text B

this will result in the same as either

Text A
*if (x = 1)
   flavortext 1
   *goto b
*elseif ((x = 2) or (x = 3))
   flavortext 2
   *goto b
*else
   flavortext 3
   *goto b
*label b
text B

or

Text A
*if (x = 1)
   flavortext 1
   *goto b
*elseif (x = 4)
   flavortext 3
   *goto b
*else
   flavortext 2
   *goto b
*label b
text B
  • The if-elseif-else structure comes in handy when all conditions go to different labels. When only one or two conditions go elsewhere, you can split things like this:
*if (x =1)
   *goto x
*else
   *goto y

or

*if ((x =1) or (x =3))
   *goto x
*else
   *goto y
  • I wish I could tell you how exactly the hierarchy of parenthesis goes, but I’m not too certain myself (someone add what they can here), but I think it’s like this (correct me if I’m wrong)
((x =1) and (x =2))
or
((x =1) or (x =2))

checks if all/either condition listed applies

(x =1) and ((y =1) or (z =2))

checks if x is 1 and if either y is 1 or z is 2.

(x =1) or ((y =2) and (z =1))

applies a condition if either x is 1 or if y is 2 and z is 1

I hope I got this right.

Anyway, I think these are the biggest bits that people trip over. Don’t worry about it, you’ll get there :slight_smile: Happy coding


#2

This Text ist really a good idea. Short and very helpful.

I want to add one thing.
An If loop ends, when one If condition is Met. So structure them, the way you need.

example from the CSide tutorial, but altered:


*if rep1 > 25
	*set elves "considerable loathing"
	*goto next01
*elseif rep1 > 35
	*set elves "distrust and wariness"
	*goto next01  
*elseif   rep1 > 75
	*set elves "great affection"
	*goto next01
*else
	*set elves "outright hostility"
	*goto next01

If the code is wrapped this way around , the first “loop” would check if teh reputation is above 25, if it is, the variable of the reputation would be set to considerable loathing. But after setting this the loop ends and it jumps to the *goto. So everything would be the same if you have a reputation of 30 or 65, because they are both above 25. The other if conditions will only be checked, if the previous comes out as false.
So group your if´s from the least possible to the common ones, so everything will run smoothly


#3

Example entered, hope it is clear


#4

I believe as it is the first condition will always be met.

I’ll add to it later


#5

The big difference between if-elseif and multiple ifs in a row is that in the first case it checks conditions until it meets one and does only that thing, while in the second it checks all the conditions and does everything it meets the conditions for. Unless there’s a goto, which will skip every later condition. Also, an else links only to the closest if, so doing the several ifs in a row structure and the default case else together generally won’t do what you want; a case that meets an earlier if will do both that and the else.

Paren rules are basically the ones from standard math; the stuff in the parens happens before the stuff outside, and if you have parens inside parens innermost happen first. I recommend using parens lots because it means not having to remember how (x = 1) and (y=3) or (x=2) gets evaluated.

Below is an extended digression on the if-elseif vs. if question. TLDR: if-elseif is preferred for reasons relevant to big programs with many coders that are frequently modified. It is taught as standard because those are the most difficult so it’s good to be in the habit.

Now, the default computer scientist advice would be to just always use if-elseif, but odds are if you’re coming to this thread for advice your current ambitions only extend to writing a Choicescript game, so instead of just telling you to do that I’ll explain why it’s the default and why you don’t necessarily need to.

The big thing underlying most “always/never” programming advice is that most programs get rewritten. So in the example case, you start out with four equality checks on one variable, but maybe later you’ll add a second variable and do math on it and your conditions stop being mutually exclusive, but you still only want to do one of the things. Writing that as separate ifs is a nightmare. So it’s best to write big programs with if-elseif to begin with. But odds are you’re not going to want to do a huge logic rewrite very often.

The other reason is that in the if-elseif example, when x = 1 it does one equality calculation, whereas in the if only example it does four, which takes… okay that’s really complicated but let’s go with four times as long. This is very important in many programs but totally irrelevant to Choicescript. It takes no more than a couple thousand clock cycles(the check itself is one, there’s setup that mostly won’t have to be repeated) and you have billions per second. It adds up over lots of checks, repeated checks, or very complicated conditions, so it’s drilled in as a habit.


#6

Sidenote: the whole notion of “‘real programmers’ ‘certified coders’ ‘computer scientists’ do it like THIS”-talk is one of the reasons I made this.
That notion is grossly discouraging, especially when one was proud of what they had achieved without and experience and otherwise just briefly stumbled.
Some might think that it in some way ‘encourages’ but as powerful as spite is, it doesn’t.


#7

Yes, but beginners might one day become advanced users. So it’s not good to teach bad habits, but I also don’t want to just give a blanket admonition to do things “the right way” given that it’s “the right way” for reasons that won’t necessarily become relevant. I’ll rearrange and mark up things a bit to clarify what people won’t necessarily need to know.


#8

I would say that what’s important is the result and that one understands what one’s doing.

Sure, there’s always room for improvement in terms of efficient coding, but to say ‘you must use this only that is the right way’ when a different, just as long/short way works too is a bit… ehh

This is what I meant: if several ways work in choicescript, none significantly longer in code than the others, I think users should know bout all and pick the one they can work with the best.

But I can understand where you are coming from. Shall we try to put example cases into easy to look up examples?

EDIT: Gonna take the example from above:

*if rep1 >25 
   *set elves "considerable loathing" 
   *goto next01 
*elseif rep1 >35 
   *set elves "distrust and wariness" 
   *goto next01 
*elseif rep1 >75 
   *set elves "great affection" 
   *goto next01 
*else 
   *set elves "outright hostility" 
   *goto next01

If one isn’t certain if they got all options covered by the above, this should work too:

*if (rep1 <=24)
   *set elves "outright hostility" 
   *goto next01
*if ((rep1 >=25) and (rep1 <=34)) 
   *set elves "considerable loathing" 
   *goto next01 
*if ((rep1 >=35) and (rep1 <=74)) 
   *set elves "distrust and wariness" 
   *goto next01 
*if (rep1 >75) 
   *set elves "great affection" 
   *goto next01 

I thiiiink this would also work as an if-elseif structure, but I’m not entirely certain


#9

The if-elseif is misordered. You want:

*if rep1 >75 
   *set elves "great affection" 
   *goto next01 
*elseif rep1 >35 
   *set elves "distrust and wariness" 
   *goto next01 
*elseif rep1 >25 
   *set elves "considerable loathing" 
   *goto next01 
*else 
   *set elves "outright hostility" 
   *goto next01

The original order sets “considerable loathing” for 25-100. This way gets the same result as the if chain but without needing to explicitly check that 27 is not >= 35. The more complex the conditionals get, the more it saves explicit checks like that.


#10

Can you explain the second in a bit more detail? With an example or two? I understand the first scenario (having just made one myself a few days ago with a lot of help!), but I’m still puzzled on where or when I should use *else and the *elseif commands.


#11

Just copying here.

Mhnn… so, rule of hand ‘start with highest’?

Also, this is for numbers. I think it gets different once we’re dealing with words.

I’ll try to give an example with text. Imagine you are playing a cat burglar after a night of break ins:

*label success (<-- means you got stuff)
You lean back and look at the spoils of the night:
*line_break
The 
*if neck (<-- meaning the variable for a necklace etc has been set to true)
   necklace
   *if (painting true) or (ring true)
      @{(painting true) and (ring true) , the| and the} 
   *if (painting false)
      @{ring and the| .}
*if painting
   painting@{ring and the| .}
*if ring
   ring.
*line_break
It's 
*if (spoils =3)
been a successful night.
*if (spoils =2)
a little less than you had hoped for.
*if (spoils =1)
not at all what you had hoped for.

If you managed to steal all three items this should result in

You lean back and look at the spoils of the night:
The necklace, the painting and the ring.
It's been a successful night.

If you managed to ‘just’ get the painting and the ring it should read like this:

You lean back and look at the spoils of the night:
The painting and the ring.
It's a little less than you had hoped for.

or if you got ‘only’ the necklace and the ring

You lean back and look at the spoils of the night:
The necklace and the ring.
It's a little less than you had hoped for.

multireplace here replaces (heh) further *if bits, checking if any further variable is true and sets the text/punctation accordingly, while the ‘spoils’ variable keeps track of how much you got that night.

Hope that’s clear?

EDIT2: Here it would also be possible to work entirely with multireplace, by having a loot variable (whether set via create or a temporary one for the scene) keep track of the outcome:

*if (((neck) and (painting)) and (ring)) 
   *set loot 1
*if (((neck) and (painting)) and (ring false)) 
   *set loot 2
*if (((neck) and (painting false)) and (ring)) 
   *set loot 3
*if (((neck false) and (painting)) and (ring)) 
   *set loot 4
*if (((neck) and (painting false)) and (ring false)) 
   *set loot 5
*if (((neck false) and (painting)) and (ring false)) 
   *set loot 6
*if (((neck false) and (painting false)) and (ring)) 
   *set loot 7
You lean back and look at the spoils of the night:
*line_break
The @{loot necklace, the painting and the ring| necklace and the painting| necklace and the ring| painting and the ring| necklace| painting| ring}.
*line_break
It's @{spoils not at all what you had hoped for| a little less than you had hoped for| been a successful night}

multireplace will display the text whose position matches with the number of the outcome.


#12

Rule of thumb is that if two conditions can be true at once, start with the one you’d rather use. In this case that’s the highest.

Basically the *elseif effectively adds “all the previous conditions are false”. It actually doesn’t execute the conditional at all, which matters if for some reason it changes data or if it can crash sometimes. You generally shouldn’t create a condition where those matter on purpose.


#13

Standing remark on all the various alternate ways of doing things and programmer advice on which you should “always” use: these rules are all from big projects. Start out using successive ifs, and a decade and millions of lines of code later you may regret it. In choicescript you may regret it if you’re going very complicated and want the text to depend on the relationship of several different variables.

It’s not what you should “always” do because it’s necessarily the easiest way to write any piece of code; it’s taught as what you should “always” do because it’s easier to always do it than to completely rewrite the code structure if the problem becomes harder than expected. If you want to take up programming for a living it’s worth building the habit; if you just want to write games do whatever is easiest. Mind, what’s easiest migrates towards the “always” thing as you get more logic per line of text displayed; the “always” method tends to make hard things easier at the expense of making easy things harder.

There can also be performance advantages, but a choicescript game is not going to be affected in a way anyone will even notice unless you’re doing something you really shouldn’t anyways such that you run through the statement tens of thousands of times for one screen.


#14

I still say one should code in a way that’s easiest for them, and possibly overhaul the code once one got the grip on other methods.

I can only speak for myself, but my latest success was getting a gosub to work.
Yet I know some users on here would prolly sneer and be baffled why I’m so giddy about having managed ‘that easy thing’.


#15

It’s slightly clear.

What are these sections of code supposed to represent? The narrative text? What would that code look like if you used *elseif and the *else commands?


#16

I think without multireplace it’d look like this:

*label success

You lean back and look at the spoils of the night:
*line_break 
The 
*if necknecklace 
   *if ((painting) or (ring)) 
      *if ((painting) and (ring))
         , the
      *if ((painting false) or (ring false))
          and the 
   *if (painting false) 
      *if ring
         and the
      *if not (ring)
         .
etc

#17

Don’t even try; you would need an elseif for each and every different combination and write the desired paragraph for each. elseif is for when you want to do exactly one distinct thing from the entire list. If you had three chances to loot from separate sets of items, you could use three sets of if-elseif to set the text relevant to each chance.

If you did it, you’d basically need to have each condition also check that all following conditions are false.


#18

If *elseif is meant to make all previous conditions false, what does *else effectively do?


#19

*else is, pragmatically, picking up the leftovers.

Imagine a variable can be ‘great’ ‘good’ ‘ok’ ‘bad’ ‘ouch’ or ‘bigouch’

*if (a = "great")
   thing A
   *goto A
*elseif (a = "good")
   thing B
   *goto B
*elseif (a = "ok")
   thing C
   *goto B
*elseif (a = "bad")
   thing D
   *goto C
*else (<-- aka if it's either ouch or bigouch)
   thing E
   *goto D

And if I understood it correctly, with numbers it’s like (to use the example from above again)

*if x >75 (<-- means x is at least 76)
   *set thing "a"
   *goto x
*elseif x >35 (<-- x is not 76 but at least 36 or higher)
   *set thing "b"
   *goto x
*elseif x >25 (<-- x is not 36 but at least 25 or higher)
   *set thing "c"
   *goto x
*else (<-- x is 25 or below)
   *set thing "d"
   *goto x

and then continues to the same label.


#20

*else is basically for when every prior condition is false. If your conditions check every possible case, you can make a last elseif into an else and get identical behaviour. Alternately you can keep all the checks and add something like this at the end:

*else
   *goto error

Then you find out if you’re wrong about covering every case very easily. That’s more marginal, though; unless you’re checking user input directly in the conditional you want to make that error page unreachable so it’s more a testing aid than something you want in the final product.