Skip to content

Instantly share code, notes, and snippets.

@shawna-p
Last active February 16, 2022 04:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shawna-p/1f7c4b202acb01edd9d6203eb97a30fc to your computer and use it in GitHub Desktop.
Save shawna-p/1f7c4b202acb01edd9d6203eb97a30fc to your computer and use it in GitHub Desktop.
Conditionals and Comparisons in Ren'Py, Part 2

Conditionals and Comparisons in Ren'Py, Part 2

Welcome back to the second part of the tutorial on conditional statements and comparisons in Ren'Py. Now that you know how to write conditional statements using if/elif/else, we'll look into the different things you can compare so that you can write complex branching paths in your game.

As with the last tutorial, it's recommended that you first look over the Complete Guide to Ren'Py Variables. This guide also assumes you have a basic idea of how to write choice menus. If you need a quick refresher, you can check out the section on Menus, Labels, and Jumps in the Ren'Py quickstart. And, if you haven't already, definitely check out the first part of this tutorial, Conditionals and Comparisons in Ren'Py, Part 1!

A quiz at the end will test your knowledge of comparisons, with additional questions combining the information in this tutorial and the first part on conditionals to check your understanding of these concepts.

Conditionals and Comparisons in Ren'Py

Part 1 - Conditional Statements

Part 2 - Equality and Truthy/Falsey (You are here!)

Part 3 - Numerical Comparison & Combining Comparisons

Part 4 - Combining Everything (Coming soon!)

Comparisons

What are Comparisons?

A comparison is a way of comparing two or more values in a game. Typically at least one of the values you're comparing is a variable, so you can use the result of this comparison to do things like change dialogue or branch off in an entirely new direction.

Informally, in your program you might want to do things like:

  1. If the player has at least 8 affection points with Xia, she'll ask them on a date
  2. If the player has 10 or more bad end points, they get the bad end. If they have less than 10 bad end points but more than 0, they get the normal end. Otherwise, they get the good end.
  3. If the player bought Ashwin a digital camera and they're on a date in the park, show a line of dialogue where they take a picture of the fountain.
  4. If the player is carrying a life jacket or they made it to the top of the ship in time to get in a life boat, they survive.
  5. If the player gave Zoran the book Pride & Prejudice and has at least 5 trust points with Zoran, Zoran will share a story from his childhood.
  6. If the player has enough money, they can buy a flower.

You can see the if/else-style structure to these sorts of statements, but the comparisons have more things to check than just one value which is True/False (for example, needing to check more than one value in 3, 4, and 5, or comparing numbers in 1, 2, and 6).

Checking if two values are the same

Sometimes, you might want to check something like "if the player bought Ashwin a digital camera". If you recall from the last tutorial, we had a section of code that looked like:

default gift = None
label start():
    "At the mall, you decided you wanted to buy Ashwin something."
    menu:
        "Buy them a bath bomb.":
            $ gift = "bath bomb"
        "Buy them a self-help book.":
            $ gift = "self-help book"
        "Buy them a digital camera.":
            $ gift = "digital camera"
    "Later that day, you were able to give them the [gift] you bought earlier."
    "They liked it a lot."

In this case, gift is equal to one of "bath bomb", "self-help book", or "digital camera". This isn't a boolean, so if we want to know specifically if the player bought Ashwin the digital camera, we can't do if gift: because that just checks if gift has some not-false value (this is explained further in the section on Truthy/Falsey values).

So, informally, we want to know: is the value of gift equal to "digital camera"?

In Ren'Py (and in most programming languages), the way to check this is with two equal signs like ==:

if gift == "digital camera":
    "Ashwin took out the camera you gave them earlier to take a picture of the fountain."

It's very important to note that it's two == and not just one = for comparison. A single = is only for setting a variable, like with $ gift = "digital camera" where you are giving the variable gift a value of "digital camera". Two == let you compare variables, like checking if gift currently has the value "digital camera". Comparison does not change the current value of the variable.

As you might recall from the guide to variables, you generally should NOT do this kind of exact comparison for floats aka numbers with decimal points. So while the following code won't cause an error, it may not perform as you expect:

$ money = 10.00
if money == 10.00: # Bad; floats aren't exact
    jump go_to_market

Instead, you'll use numerical comparisons for floats, described below.

However, it's fine to use == to compare between values like integers and strings, such as:

default num_cookies = 1
label start:
    menu:
        "Give Zoran a cookie.":
            $ num_cookies -= 1
        "Don't give Zoran a cookie.":
            "You declined to give Zoran a cookie."
    if num_cookies == 1:
        "You decided to eat a cookie yourself."
    else:
        "You wished you had something to eat."

or as seen above, if gift == "digital camera":.

Note that comparing strings is case-sensitive, that is, "digital camera" is NOT the same as "Digital Camera" which is NOT the same as "DIGITAL CAMERA". Spacing also matters - if you have a stray space at the end of a string, for example, it won't match a string without that space e.g. "Xia " is NOT the same as "Xia" without the extra space. There are string utility functions to help with this, such as strip() and lower(), so you can make more accurate comparisons. We'll talk about those in a later tutorial on strings. For now, just keep in mind that capital letters in your strings are important, as two strings with different capitalizations are not the same.

You typically do not want to use == when comparing with booleans like True or False e.g. if ate_apple == True. For more on that, see the section on "Truthy" versus "Falsey".

Places where you should use ==:

  • When comparing against an integer e.g. if num_game_wins == 2
  • When comparing against a string e.g. if xia_job == "book store"

Places where you shouldn't use ==:

  • When comparing against a float e.g. if grade == 0.9 (Bad! Don't use exact comparisons with floats, since decimal numbers may have slight rounding errors)
  • When comparing against a boolean value e.g. if went_home == True or if owns_cat == False (poor style and may not be what you want; see Truthy vs Falsey)
  • If you're trying to set a variable (setting uses one = e.g. $ xia_job = "doctor" or default ash_gift = "bath bomb")

Checking if two values are different

Besides ==, you might also want to know if two values are not equal. There are two primary ways of checking for this, depending on what kind of value you're comparing against.

The first way is with !=, which stands for "not equal". In many programming languages, ! means "not", so in this case, ! (not) = (equal) -> !=

As with ==, this is a good comparison to use for strings and integers, but not for floats or booleans. So, you could do something like:

default mood = "neutral"
label start():
    "Now, at the end of a long day, you were feeling..."
    menu:
        "Happy":
            $ mood = "happy"
        "Exhausted":
            $ mood = "tired"
        "Sad":
            $ mood = "sad"
        "Just okay":
            $ mood = "neutral"
    if mood != "happy":
        "Ash" "Hey, are you feeling all right?"
        "Ash" "I know it was a pretty long day."

If the player isn't feeling good at the end of the day, Ashwin should check up on them. But there are multiple different "negative" feelings they might have - they might be sad, or maybe just tired or even neutral. Instead of having a check for each possible feeling that leads to this dialogue, we can just check if the player isn't happy with if mood != "happy".

You can use this comparison for integers as well, such as:

default mistakes = 0
default xia_job = "doctor"
label start():
    "Xia" "So what's my favourite colour, then?"
    menu:
        "Yellow.":
            pass
        "Blue.":
            $ mistakes += 1
    "Xia" "And my job?"
    menu:
        "Doctor.":
            if xia_job != "doctor":
                $ mistakes += 1
        "Librarian":
            if xia_job != "librarian":
                $ mistakes += 1
    if mistakes != 0:
        "Xia" "Looks like you don't know me as well as you think you do!"
    else:
        "Xia" "Hmm, probably made the questions too easy..."

We have a few instances of comparisons here. In the choice menu about Xia's job, we check to see what her job actually is before adding points to the mistakes variable. If the player says "Doctor" when xia_job is not "doctor", then they answered wrong so the number of mistakes increases, and the same goes for Xia being a librarian.

Then, at the end, if the player made a mistake (so mistakes is not equal to 0), Xia tells them they don't know her as well as they think. Otherwise, she wonders if her questions were too easy.

Another way to write this last conditional statement is:

if mistakes == 0:
    "Xia" "Hmm, probably made the questions too easy..."
else:
    "Xia" "Looks like you don't know me as well as you think you do!"

As with ==, the use cases for != are very similar:

Places where you should use !=:

  • When comparing against an integer e.g. if remaining_cookies != 1
  • When comparing against a string e.g. if ash_gift != "digital camera"

Places where you shouldn't use !=:

  • When comparing against a float e.g. if grade != 1.0 (Bad! Don't use exact comparisons with floats)
  • When comparing against a boolean value e.g. if went_home != True or if owns_cat != False (poor style and may not be what you want; see Truthy vs Falsey)

Not

You might remember that when we've been using booleans (True/False), to check if they're True we just do

if thai_restaurant_open:

What if you want to check if it's False instead (as in, the restaurant is NOT open)? Luckily, there's a very special key word called not which can be used like:

if not thai_restaurant_open:

In more plain language, you can read it like "if the Thai restaurant is not open" or "if the expression "the Thai restaurant is open" is not true". The expression if not thai_restaurant_open will evaluate to True if thai_restaurant_open is False - it's kind of like a double-negative. If something is not False, then it must be True.

So, you could do something like:

default thai_restaurant_open = False
default pizza_place_open = False
default dinner = "grilled cheese"
label start:
    if thai_restaurant_open:
        $ dinner = "Thai food"
    elif pizza_place_open:
        $ dinner = "pizza"
    else:
        $ dinner = "grilled cheese"
    "You ate some [dinner] for dinner."
    if not thai_restaurant_open:
        "It was too bad the Thai restaurant was closed, though."
        "You had definitely been hungry for Thai food."
    "Now that you were full, it was time for bed."

The player will see the extra lines of dialogue under if not thai_restaurant_open only if the Thai restaurant was not open (so, $ thai_restaurant_open = False somewhere in the script earlier).

Truthy versus Falsey

So this whole time I've been saying that it's better to just write if pizza_place_open and if not pizza_place_open when checking boolean values. But a boolean isn't inexact like a float is, so why is it better not to use ==, as in, if thai_restaurant_open == True?

The most important reason is that if went_to_park and if went_to_park == True are not the same thing! Explaining why involves two concepts called "Truthy" and "Falsey".

Hopefully you're feeling more comfortable now with the idea of True and False, and how to check for them with conditional statements like if not pizza_place_open and if player_ate_apple. You've also seen several variable types now, like integers, strings, and floats, besides just booleans.

Every variable type has a particular value which is considered "False" if you use it in a conditional statement as if it were a boolean. This is called a "Falsey" value, since it may not be exactly equal to the specific value False, but it still isn't "True". Anything that isn't "Falsey" is "Truthy" instead, and will evaluate to True in a conditional statement.

  • For booleans, False is Falsey, and True is Truthy (as you would expect)
  • For integers, the number 0 is Falsey. Negative numbers like -1 are Truthy, not Falsey. 0 is the only number which is Falsey, and everything else is Truthy.
  • For floats, same thing: 0.0 (or 0 with arbitrary decimal points e.g. 0.000000000) is Falsey. Any other number, no matter how large, small, or negative, is Truthy.
  • For strings, the empty string (a string with nothing in it, not even a space i.e. "" or '') is Falsey. A string with anything in it, one character or many, is Truthy, even if it's something like "False" or "0" because it's a string which has a word in it so it isn't empty.
  • The special value None is Falsey.

And for some of the more complex variable types, an empty list (a list with no items i.e. [] or list()) is Falsey, as is an empty dictionary ({} or dict()). Dictionaries or lists with any item in them, regardless of what it is, are Truthy e.g. [ False ] is Truthy, because it is a list which contains one item (so it isn't empty). The fact that the item itself is False is irrelevant.

So what does it even mean if something is "Falsey" or "Truthy"? Well, it explains how those values will be evaluated in a conditional expression like if not x or if x.

So, let's look at the following example:

default gift = ""
label start:
    if gift:
        "You gave Zoran a [gift]."
    else:
        "You hadn't bought anything, so you just waved at Zoran."

Here, gift starts with a default value of "", which is the empty string because it doesn't have anything in it (inside the quotes). We can then check it as if it was a boolean, with if gift:. This checks if gift is a "Truthy" value, and in this case because gift is a string, it checks if the string is empty or not.

The behaviour of the conditional would also be the same if we had done:

default gift = False
label start:
    if gift:
        "You gave Zoran a [gift]."
    else:
        "You hadn't bought anything, so you just waved at Zoran."

or even

default gift = None
label start:
    if gift:
        "You gave Zoran a [gift]."
    else:
        "You hadn't bought anything, so you just waved at Zoran."

In all three cases, the main idea is that we want to check that gift has some not-false value; it doesn't matter specifically what word it is, just that it's not an empty string or False or None or any other Falsey value.

Note, however, that the following is NOT the same:

# Incorrect 1
default gift = ""
label start:
    if gift == True:
        "You gave Zoran a [gift]."
    else:
        "You hadn't bought anything, so you just waved at Zoran."

if gift == True is specifically checking if gift is exactly equal to the programming value True. Similarly, if gift != False is specifically checking if gift is not equal to the exact programming value False. if gift == True won't be true for any string value of gift, not "cake" or "" or "True" (as a string), only True. We can see this if we substitute the actual value of gift into the statement - "cake" == True is False, "True" == True is False, "" == True is False, True == True is True.

If there was a menu earlier to decide on a gift, we can follow the flow of the conditional statement:

default gift = ""
label start:
    "You decided to buy Zoran..."
    menu:
        "A bouquet of daffodils.":
            $ gift = "bouquet of daffodils"
        "A hand-made apron.":
            $ gift = "hand-made apron"
        "Nothing.":
            $ gift = ""
    "Zoran spied you across the marketplace and jogged over."
    if gift:
        "You gave Zoran a [gift]."
    else:
        "You hadn't bought anything, so you just waved at Zoran."

Here, if the player buys Zoran a bouquet of daffodils or a hand-made apron, they will see the line where they give Zoran a gift. It doesn't matter specifically what the gift was in order to show the line - a player who bought a bouquet of daffodils will see "You gave Zoran a bouquet of daffodils" and a player who bought a hand-made apron will see "You gave Zoran a hand-made apron". Later, because we have the gift's name stored inside gift, we can change the dialogue or story based on what specific gift he was given. Meanwhile, a player who didn't buy a gift will see the line "You hadn't bought anything, so you just waved at Zoran.", because gift is the empty string "", which is Falsey, so they see the line under the else clause.

It's also very important to remember that == True and == False aren't the same as if x and if not x when it comes to checking things like persistent variables, which begin with the default value None. None is a "falsey" value, but it is not the exact value False. Consider the following:

if not persistent.second_playthrough:
    "She smiled at you pleasantly."
else:
    "It was weird; Xia hadn't acted this way before."
    jump new_ending
"This is the end of the game."
$ persistent.second_playthrough = True
return

Note that persistent variables can be used without setting them up with default as they begin with the value None (rather than not existing). If you'd like a persistent variable to begin with a value other than None, however, you will need to use default.

In the above code, persistent.second_playthrough begins with a value of None. if not persistent.second_playthrough, then, evaluates to True, because None is a Falsey value and if not <some falsey value> makes the whole statement True. It's the same kind of logic as if not False - if something is not False, it must be True. If something is not falsey, it must be truthy.

So the first time the player encounters this conditional statement they will see the line "She smiled at you pleasantly." and then "This is the end of the game". On a second playthrough, because persistent.second_playthrough was set to True (and persistent variables are preserved across different playthroughs), they will instead see the line "It was weird; Xia hadn't acted this way before." and jump to the label new_ending.

However, if the expression had instead been

# Incorrect 2
if persistent.second_playthrough == False:
    "She smiled at you pleasantly."
else:
    "It was weird; Xia hadn't acted this way before."
    jump new_ending
"This is the end of the game."
$ persistent.second_playthrough = True
return

Then the player would actually go right to the line "It was weird; Xia hadn't acted this way before.", because persistent.second_playthrough is equal to None, not False, so if persistent.second_playthrough == False is False and we go to the else clause instead.

In summary, each variable type can be evaluated as if it were a boolean via if x or if not x-style conditional statements. If the variable would evaluate as if it was True, it is "Truthy". If it would evaluate as if it was False, it is "Falsey". Comparisons like if x == True are only correct/True if x is the exact boolean value True, but if x covers a wider range of situations and can be used to both shorten your code and make it more readable. It is considered good coding practice to use if x and if not x instead of == True or == False, even when working with booleans, and can help prevent unintentional errors.

Summary

Before the quiz, let's go over the new comparison types we learned and how they're used in-game:

Comparing if two items are the same

if x == y:
  • Note the two == (one = is only for setting a variable; two for comparing)
  • This is True only if x and y have the exact same value
  • Good for comparing integers and strings
  • Not good for comparing floats and booleans

Comparing if two items are different

if x != y:
  • ! means "not" as in "not equal" !=
  • This is True for every case unless x and y are the exact same value
  • Good for comparing integers and strings
  • Not good for comparing floats and booleans

not

if not x:
  • If x is a "truthy" value, if not x is False
    • e.g. if x is one of "hello", -45, or True, then not x is False
  • If x is a "falsey" value, if not x is True
    • e.g. if x is one of "", 0, or False, then not x is True
  • Can be used for all variable types, but should especially be used for booleans

Quiz

Now that we've looked at equality comparisons (== and !=), not, and gone over the concept of Truthy/Falsey, here are a few quiz questions to test what you've learned.

Question 1

What line(s) of dialogue will be shown to the player when the following code is executed? (If there is more than one answer, your answer may be "Lines 6 and 7", for example).

default name = ""
label start():
    if not name:
        $ name = "Fen"
        # Line 1
        "So your name is [name], huh?"
    elif name != "Xia":
        # Line 2
        "At least your name isn't Xia."
    elif name == "Fen":
        # Line 3
        "Oh how interesting, I thought you looked like a Fen."
    else:
        # Line 4
        "So your name is [name], then?"
    return
  1. Line 1
  2. Line 2
  3. Line 3
  4. Line 4
  5. There's an error in the code.
Click for answer

The correct answer is just 1. Line 1. This is because there is only one conditional statement!

So, to start, Ren'Py will look at if not name. This is True, because name is the empty string "", which is a falsey value, and if you recall, if not <some falsey value> is similar to if not False and if something isn't False, it's True, so if not name is True and will execute the block of code underneath the expression (the code that's indented to the right under the colon).

And then that's it! As mentioned in the previous part of this tutorial, if one clause in a conditional statement is True, then none of the rest of them get executed. So, Ren'Py won't even look at elif name != "Xia" or elif name == "Fen" or else; it'll actually just end the game after showing the line "So your name is Fen, huh?".

Note: The reason name is "Fen" for that line is because of the line $ name = "Fen" just before the dialogue is shown.

If the code had been written instead like:

# Incorrect 3
default name = ""
label start():
    if not name:
        $ name = "Fen"
        # Line 1
        "So your name is [name], huh?"
    if name != "Xia":
        # Line 2
        "At least your name isn't Xia."
    if name == "Fen":
        # Line 3
        "Oh how interesting, I thought you looked like a Fen."
    else:
        # Line 4
        "So your name is [name], then?"
    return

with three separate conditional statements instead of one, then Line 1 would execute (as mentioned), and so would Line 2 (because name != "Xia" - at that point in the script, name would be set to "Fen"). Line 3 would also execute, because name was set to "Fen" earlier. Line 4 would be skipped because if name == "Fen" is True, so we'd skip the else clause.

Note, however, that this is unlikely to be what you want the game to do - if you want the game merely to comment on the value of name, then all these conditions should be part of the same conditional statement as in the question so as to prevent more than one comment from being made on the player's name.

Question 2

Which line(s) will be shown to the player? (If there is more than one answer, your answer may be "Lines 6 and 7", for example).

default num_wins = 3
label start():
    if num_wins:
        # Line 1
        "You won [num_wins] games!"

    if num_wins = 0:
        # Line 2
        "Maybe you'd do better next time."
    elif num_wins = 1:
        # Line 3
        "One win seemed decent."
    else:
        # Line 4
        "You were happy to leave it at [num_wins] wins."
  1. Line 1
  2. Line 2
  3. Line 3
  4. Line 4
  5. There's an error in the code.
Click for answer

The correct answer is 5. There's an error in the code. Can you spot it?

We did set up default num_wins = 3 so there's no NameError complaining about not seeing num_wins before, but there is a mistake in the conditional statement that starts with if num_wins = 0 - it should be if num_wins == 0, with two = signs. This is also true of elif num_wins = 1; it needs to be elif num_wins == 1.

Remember, = is used to set variables to a particular value, like we do in lines like default num_wins = 3 (just one = to set num_wins to have a value of 3), or $ name = "Fen" (just one = to set name to have the value "Fen" during the game).

== is used specifically for comparison, when comparing if two values are equal or not. So you must use == if you're dealing with any kind of conditional statement.

# Corrected 1
default num_wins = 3
label start():
    if num_wins:
        # Line 1
        "You won [num_wins] games!"

    if num_wins == 0:
        # Line 2
        "Maybe you'd do better next time."
    elif num_wins == 1:
        # Line 3
        "One win seemed decent."
    else:
        # Line 4
        "You were happy to leave it at [num_wins] wins."

If the erroneous = were corrected to == and the code ran properly, then Line 1 and Line 4 would execute. 3, the value of num_wins, is a truthy value (it's a number that's not 0), so if num_wins: is True. if num_wins == 0 is False (it's 3, not 0), elif num_wins == 1 is False (it's 3, not 1), so we see the else clause and Line 4.

Question 3

Which line(s) will be shown to the player? (If there is more than one answer, your answer may be "Lines 6 and 7", for example)

default mood = "Happy"
label start():
    if mood != "sad":
        # Line 1
        "The weather seemed nice outside; a little crisp, but it might warm up later."
    else:
        # Line 2
        "The weather outside was gloomy, like it might rain."

    if mood == "happy":
        # Line 3
        "It put a little spring in your step as you walked to the park."
    else:
        # Line 4
        "You strolled down the street at a leisurely pace."
    return
  1. Line 1
  2. Line 2
  3. Line 3
  4. Line 4
  5. There's an error in the code.
Click for answer

The correct answer is Line 1 and Line 4.

First, we have a check for if mood != "sad". mood is set to "Happy" before the start label, and "Happy" != "sad" is True ("Happy" is not equal to "sad"), so we will see Line 1. This precludes us from seeing Line 2, since these two lines are part of the same conditional statement and only one clause can be True (the else in this case is just skipped over).

Next, we have a check if mood == "happy". Again, mood is equal to "Happy", so we're looking if "Happy" == "happy". This is not True, because one has a capital letter and the other does not! So if mood == "happy" is False and we skip over the block of code under it and move on to the else clause.

The else clause executes whenever all the clauses before it are False, as it is in this case, so the player will see Line 4.

More likely in this particular case, you wanted to have mood be equal to "happy" (without the capital H) so you could properly check if mood == "happy", so the first line should have been default mood = "happy" instead. Be aware of capitalization when comparing strings!

Conclusion

In the next part of this tutorial, we'll go over more kinds of comparisons, like numerical comparisons, and also ways you can combine more than one expression together to check for complex combinations of variables.

Conditionals and Comparisons in Ren'Py, Part 3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment