This is an addendum to the previous tutorials on simple variables and equality comparisons. It’s very common for people to use booleans instead of strings in their script when strings are a better choice to reduce errors while still remembering multiple options.
As this tutorial builds off of the prior tutorials Simple Variable Types in Ren’Py and Equality Comparisons in Ren’Py, you should read those first before this tutorial.
Rock-Paper-Scissors
Consider the following situation:
During your game, a round of rock-paper-scissors is played which determines who gets to eat the last cookie. The player gets to choose which of rock, paper, or scissors they would like to play, and if they win, they earn the cookie. You’re keeping track of the results like so:
default npc_rock = False
default npc_paper = False
default npc_scissors = False
default player_wins = False
label start:
# Round 1
# You could do some logic here, like using
# renpy.random to randomly select which of
# rock/paper/scissors the non-player character
# (NPC) should use, but here it's set directly.
$ npc_paper = True
label rock_paper_scissors:
# Show which move the NPC chose
show paper_img
menu:
"Rock":
if npc_rock:
"It's a tie! Go again."
jump rock_paper_scissors
if npc_paper:
$ player_wins = False
if npc_scissors:
$ player_wins = True
"Paper":
if npc_rock:
$ player_wins = True
if npc_paper:
"It's a tie! Go again."
jump rock_paper_scissors
if npc_scissors:
$ player_wins = False
"Scissors":
if npc_rock:
$ player_wins = False
if npc_paper:
$ player_wins = True
if npc_scissors:
"It's a tie! Go again."
jump rock_paper_scissors
if player_wins:
"You won!"
jump later_on
else:
"You lost!"
jump no_cookie_for_you
There’s a lot happening here, but most of it is just repeat code for all the options. For example, let’s look at the code under the “Rock” choice in the menu:
"Rock":
if npc_rock:
"It's a tie! Go again."
jump rock_paper_scissors
if npc_paper:
$ player_wins = False
if npc_scissors
$ player_wins = True
First, the game checks if npc_rock
aka did the NPC play “Rock”? If so, and the player chose rock, then it’s a tie and the game will jump to the rock_paper_scissors label so the player can try again, no penalty.
Next, the game checks if npc_paper
(did the NPC play “Paper”?). If they did, and the player chose rock, then the player loses, so the game sets player_wins
to False
.
Finally, the game checks if npc_scissors
(the NPC played scissors). The player played rock, so they win. The game sets player_wins
to True
and continues on.
After the player has played the game, the game continues as usual, either jumping to the label later_on
if the player won, or the label no_cookie_for_you
if the player lost.
All seems pretty normal so far. Later in your script, the characters decide to play another game of rock-paper-scissors, this time for the last piece of pizza. You decide to use the same format for this new round:
label later_on:
# Set up the NPC's move
$ npc_rock = True
label rock_paper_scissors2:
show rock_img
# This menu is the same as the original
menu:
"Rock":
if npc_rock:
"It's a tie! Go again."
jump rock_paper_scissors2
if npc_paper:
$ player_wins = False
if npc_scissors:
$ player_wins = True
"Paper":
if npc_rock:
$ player_wins = True
if npc_paper:
"It's a tie! Go again."
jump rock_paper_scissors2
if npc_scissors:
$ player_wins = False
"Scissors":
if npc_rock:
$ player_wins = False
if npc_paper:
$ player_wins = True
if npc_scissors:
"It's a tie! Go again."
jump rock_paper_scissors2
if player_wins:
"You won!"
jump eat_the_pizza
else:
"You lost!"
jump no_pizza_for_you
Can you spot what the issue is here?
You forgot to reset the NPC’s original choice! So by the time the game gets to the rock_paper_scissors2
label, both npc_paper
and npc_rock
are True
!
What does that even mean? You can’t play both rock and paper at the same time. Now let’s look at what happens the second time when the player chooses Scissors:
"Scissors":
if npc_rock:
# This is True!
$ player_wins = False
if npc_paper:
# This is also True!
$ player_wins = True
if npc_scissors:
"It's a tie! Go again."
jump rock_paper_scissors2
Since both npc_rock
and npc_paper
are True
, player_wins
is first set to False
, and then set to True
! Even though the NPC was supposed to play rock and you showed rock_img
to the player, the player now inexplicably wins the round even though they played scissors when the NPC played rock (and rock beats scissors).
One way to try to fix this is to make sure you reset each of the rock/paper/scissors variables before starting a new game e.g.
label later_on:
# Set up the NPC's move
$ npc_rock = True
$ npc_paper = False
$ npc_scissors = False
But this still leaves you vulnerable to accidental configurations like more than one value being True or all three being False if you’re not careful, and it’s already a lot of extra typing even though you only have three options. This is where strings come in!
Here’s a rewrite of the rock-paper-scissors game, using strings:
default npc_move = "rock" # New!
default player_wins = False
label start:
# Round 1
# You could do some logic here, like using
# renpy.random to randomly select which of
# rock/paper/scissors the non-player character
# (NPC) should use, but here it's set directly.
$ npc_move = "paper" # New!
label rock_paper_scissors:
# Show which move the NPC chose
show paper_img
menu:
"Rock":
if npc_move == "rock":
"It's a tie! Go again."
jump rock_paper_scissors
elif npc_move == "paper":
$ player_wins = False
else:
$ player_wins = True
"Paper":
if npc_move == "rock":
$ player_wins = True
elif npc_move == "paper":
"It's a tie! Go again."
jump rock_paper_scissors
else:
$ player_wins = False
"Scissors":
if npc_move == "rock":
$ player_wins = False
elif npc_move == "paper":
$ player_wins = True
else:
"It's a tie! Go again."
jump rock_paper_scissors
if player_wins:
"You won!"
jump later_on
else:
"You lost!"
jump no_cookie_for_you
Now if you set npc_move
to "rock"
later on for the second round, it’s impossible to have conflicts with the previous game of rock-paper-scissors, since either npc_move
is equal to "rock"
or it’s equal to "paper"
or "scissors"
, and it’s impossible for it to be equal to more than one. Note that ==
compares two values directly to see if they’re equal/the same, so in the menu a line like if npc_move == "rock"
is True if npc_move
has the value "rock"
(since "rock" == "rock"
). if npc_move = "rock"
with only one =
is incorrect programming syntax and will cause an error, because only one =
is used to set the value of a variable, not to compare with other values (See Equality Comparisons in Ren’Py for more on this if you haven’t read it already).
I’ve also cleaned up all the if
statements to be if/elif/else
, which is more efficient and makes it clearer that these options are mutually exclusive. Now we’ve got a proper system going for the rock-paper-scissors game, with no chance of accidentally letting the NPC make two moves at once.
Summary
When choosing what variable type to use, make sure you consider whether the concepts are mutually exclusive or not. If you have more than two options and they are all mutually exclusive, you should probably be using strings instead of booleans. If there is a configuration of your boolean variables which shouldn’t be possible (e.g. both went_to_park
and went_to_store
are True even though the player can only ever go to one of the two locations), reconsider if you can use just one boolean (e.g. either they went_to_park
or didn’t, and if went_to_park
is False
you know they went to the store) or if a string would be more appropriate.
Next Steps
Hopefully by now you’re feeling comfortable with all the variable types introduced so far and the different ways to compare them. Next you’ll learn about numerical comparisons and combining more than one expression to make more complex conditional statements in Numerical Comparisons and Combinations in Ren’Py.