Lesson 3 — Conditionals
By the end of this you can: make a program choose, compare values with
== and <, chain decisions with elif, combine tests with and and or,
and make a game react to what is actually happening.
The problem conditionals solve
Here is a program with no decision in it:
health = 100
health = health - 30
print("Health:", health)
Health: 70
It works. It also does exactly the same thing every single time you run it, which is the problem. A game that cannot notice anything is not a game — it is an animation. Something has to be able to ask has this happened yet?
That question is if:
health = 30
if health < 50:
print("Warning: low health")
print("Still playing")
Warning: low health
Still playing
Change health to 70 and run it again. The warning disappears and
Still playing is all you get. The program now behaves differently depending
on what is true, and nothing about the code changed.
Two things to notice, because both catch people:
- The line ends in a colon.
- The line underneath is indented. That indent is what says "this belongs
to the
if".print("Still playing")is back out at the left, so it is not part of the decision and always runs.
Asking the question
The thing between if and the colon is a comparison, and it comes out
either True or False:
print(10 > 5)
print(10 < 5)
print(10 == 10)
print(10 != 10)
print(10 >= 10)
print(3 <= 2)
True
False
True
False
True
False
>and<— bigger than, smaller than.>=and<=— bigger or equal, smaller or equal.==— equal. Two equals signs, always.!=— not equal.
= and == are different and it matters. One equals sign stores:
health = 30 puts 30 into health. Two equals signs asks: health == 30 is a
question whose answer is True or False. Writing this:
if health = 30:
is not a typo Python can forgive — it stops with SyntaxError: invalid syntax,
because you asked it to store something in the middle of a question. It is the
most common mistake in this lesson, and the error message does not explain
itself, so learn what the message means rather than what it says.
else — the other path
if on its own does something or does nothing. else gives it somewhere to go:
score = 40
if score >= 50:
print("You win")
else:
print("Try again")
Try again
Exactly one of those two lines will print, always. That is the point of
else: there is no way to fall through both.
elif — more than two answers
Real games have more than two outcomes. elif — short for "else if" — chains
them:
score = 78
if score >= 90:
print("Rank: S")
elif score >= 75:
print("Rank: A")
elif score >= 50:
print("Rank: B")
else:
print("Rank: C")
Rank: A
Python checks them top to bottom and stops at the first one that is True.
78 is not 90 or more, so it moves on. 78 is 75 or more, so it prints
Rank: A and skips everything below — including score >= 50, which is also
true. First match wins.
That "stops at the first true one" is the whole reason elif exists. Watch
what happens with three separate if statements instead:
score = 95
if score >= 90:
print("Rank: S")
if score >= 75:
print("Rank: A")
if score >= 50:
print("Rank: B")
Rank: S
Rank: A
Rank: B
Three ranks for one score. Each if is its own separate question, so all
three get asked and all three are true. Nothing errors — you just get nonsense,
which is the worst kind of bug to find.
Order matters too. If you put score >= 50 first, every score of 50 or
more prints Rank: B and the better ranks can never be reached. With elif,
put the hardest test at the top.
and, or, not
Sometimes one question is not enough:
has_key = True
door_locked = True
if has_key and door_locked:
print("You unlock the door")
if has_key or door_locked:
print("At least one of those is true")
if not has_key:
print("You need a key")
You unlock the door
At least one of those is true
and— both have to be true.or— at least one has to be true.not— flips it.
The third one printed nothing, because has_key is True, so not has_key is
False.
True and False are real Python values, spelled with a capital letter. You
can store one in a variable like anything else, and a variable holding one is
already a question you can ask:
game_over = False
if not game_over:
print("Keep going")
Keep going
You never need to write if game_over == True. if game_over says the same
thing and reads like English.
Now put it on the screen
A health bar that tells you how worried to be:
from kaypy import *
kaypy(width=800, height=600, background=[20, 24, 36])
health = 30
if health > 60:
bar = GREEN
elif health > 25:
bar = YELLOW
else:
bar = RED
add([rect(health * 4, 30), pos(40, 40), color(bar)])
add([text("Health: " + str(health), size=24), pos(40, 90)])
Press Run. The bar is short and yellow, because 30 is more than 25 but not more than 60.
Change one number. Set
healthto 80 and run it again — long and green. Then 10 — short and red. One variable, three completely different pictures, and you did not touch the drawing code. That separation is what conditionals buy you.
GREEN, YELLOW and RED are colors the engine already knows. There are
eight of them, and they are only numbers — print(RED) gives (255, 0, 0).
Deciding every frame
The examples so far decide once, when the program starts. A game has to keep deciding, so the test goes inside something that runs every frame:
from kaypy import *
kaypy(width=800, height=600, background=[20, 24, 36])
loadSprite("bean", "images/bean.png")
player = add([sprite("bean"), pos(100, 300), area()])
@onUpdate
def wrap_around():
player.pos.x = player.pos.x + 3
if player.pos.x > width():
player.pos.x = 0
The bean walks right, and the moment it goes past the right-hand edge the if
notices and puts it back at 0. Run it and watch it loop forever.
width() is the width of the window, so this works whatever size you made it.
That is worth more than typing 800 — a number you typed by hand is wrong the
day you change the window.
Watch for
== asks, = stores. The error is SyntaxError: invalid syntax and it
points at the if line.
The colon and the indent are both required. Forgetting the colon is a
syntax error. Forgetting the indent gives you IndentationError: expected an
indented block.
Use elif, not a stack of separate ifs — unless you really do want every
true one to happen.
Put the hardest test first in an elif chain, or it can never be reached.
Do not compare to True. Write if ready: rather than if ready == True:.
Classwork 1 — The Damage Check
Build a program that reacts to a single number.
Start from this and press Run to see where you are:
from kaypy import *
kaypy(width=800, height=600, background=[20, 24, 36])
health = 45
add([text("Health: " + str(health), size=28), pos(40, 40)])
Now add, one at a time, running after each:
- A bar under the text:
rect(health * 4, 24)atpos(40, 90). - Color it with an
if/elif/elsechain — green above 60, yellow above 25, red otherwise. - A message under the bar that says
Critical!only when health is below - Use a plain
if, with noelse. - A second variable,
shield = True, and a line that printsShield holdingto the console when the shield is up and health is above 0.
Change health and rerun until you have seen all three bar colors, and made
Critical! appear and disappear.
Stretch: make Critical! appear only when health is below 20 and the
shield is down. What do you set the two variables to so that it shows?
Classwork 2 — The Locked Door
Two sprites and one decision.
from kaypy import *
kaypy(width=800, height=600, background=[20, 24, 36])
loadSprite("bean", "images/bean.png")
loadSprite("key", "images/key.png")
loadSprite("door", "images/door.png")
has_key = False
add([sprite("door"), pos(600, 260)])
add([sprite("bean"), pos(120, 280), area()])
- Add the key to the screen at
pos(340, 300)— but only whenhas_keyis False. Once it has been picked up there is nothing left to draw. - Add a label at
pos(40, 40)that saysYou have the keyorThe door is locked, depending onhas_key. - Set
has_key = Trueand run it again. The key should vanish and the label should change, and you only changed one word.
Stretch: add a moves_left number. The label should say Out of moves
when it reaches 0, whatever has_key is — so that test has to come first in
the chain. Prove it by setting has_key = True and moves_left = 0 together.
Check yourself
What does this print?
lives = 3
if lives > 5:
print("Plenty")
elif lives > 0:
print("Careful")
elif lives > 2:
print("Fine")
else:
print("Game over")
Careful
Why not "Fine"?
lives > 2 is true — 3 is more than 2 — but Python never gets there. It takes
the first branch that is true, which is lives > 0, prints Careful, and
skips the rest of the chain.
That third branch can never run at all. Any number that passes lives > 2 has
already passed lives > 0 one line earlier. A branch that is impossible to
reach is not an error and Python will not warn you, which is exactly why the
order of an elif chain is worth reading twice.