Stage 2: Movement¶
In this lesson we will learn
- what event-driven programming is and the role of a main loop
- how methods use arguments and return values to control behaviour
- how to create a method that returns a new state
- how to build a main loop that reads user input and runs event handlers
- how to test branching code and handle invalid commands
Terminology
- state – the situation a program is in at a particular moment, such as which room the player is in.
- main loop – the loop that keeps a program running, waiting for the user's input and reacting to it each time around.
- state machine – a way of thinking about a program as always being in one state, with rules that decide the next state when an event happens.
- event-driven programming – a style of programming where the program waits for events, like the user typing a command, and runs the code that matches each one.
- event – something that happens that a program can react to, such as the user typing a command, clicking a button or a sensor sending data.
- return value – the value a method sends back to the code that called it, such as the
Roomobject returned bymove. - infinite loop – a loop that never stops on its own, which we can end by pressing Ctrl+C in the Shell or clicking Stop in Thonny.
- flag variable – a variable that stores
TrueorFalseto control part of a program, such asrunningkeeping the main loop going. - event handler – the code that responds to a particular event, such as moving the player when they type a direction.
- branch – one of the possible paths through code that uses
if,elifandelse, each of which needs to be tested. - testing table – a table that lists each test with its expected result and actual result, so we can spot any differences.
Introduction¶
In Stage 1 we made three rooms, connected them, and got the program to describe each one. That was a good start, but it's not much of a game yet. In Stage 2 we'll write code that lets the player move between rooms, which means changing the game's state (for example, which room the player is in), and we'll build the main loop.
State machines
A state machine is a way of thinking about how a program changes as things happen. At any moment, the program is in one state (like being in a certain room in our game). When an event happens, such as the player typing a command, the program follows a rule that decides what the next state should be.
For example, typing "east" might move us from the Armoury to the Lab. Each state has certain things we can do, and each action can move us to a new state. It's like following a map where every choice leads to a different place, and the program always knows exactly where it is and what it should do next.
The main loop is a key part of event-driven programming. Our main.py file will set up the game and create all the objects it needs. Then it will enter the main loop, where the program waits for the player to type something and then reacts to that input.
Event-driven programming
Event-driven programming is when a program doesn't just run from top to bottom, but waits for things to happen and reacts to them. These things are called events, like the user typing a command, clicking a button, or a sensor sending data.
The program sits in a loop, listening for these events. When one occurs, it runs the code that matches that event. This makes programs more flexible because they only do something when there's a reason to, just like we don't answer someone until they speak to us first.
Pseudocode¶
- Create the move method
- Initialise the starting room
- Create the main loop, which:
- describes the current room
- accepts user input
- responds to user input
Class diagram¶
We've updated the Room class diagram for Stage 2.

Notice the new method move(direction):room. It:
- accepts one argument (
direction) - returns a
Roomobject
Create the move method¶
Open room.py and add the highlighted code below.
We need to build the main loop before we can use this method, but let's investigate it now.
Code explanation
- line 22 → defines the
movemethod, which takes one argument: the direction the player wants to go. - line 24 → checks whether the direction is one of the keys in this room's
linked_roomsdictionary. - line 25 → returns the room linked in that direction, so the player moves there.
- lines 26–27 → otherwise tells the player they can't go that way…
- line 28 → …and returns
self, so the player stays in the same room.
Initialise the starting room¶
Now go to main.py and make the highlighted changes below.
Code explanation
- lines 21–26 → wrap the room descriptions in
'''so Python treats them as a string and ignores them. We could delete them, but keeping them means we can bring them back later for debugging. - line 29 → creates the
current_roomvariable, which tracks which room the player is in, and starts the player in thecavern.
Create the main loop¶
Still in main.py, add the highlighted code below to create the main loop.
Escaping an infinite loop
If our program gets stuck in an infinite loop, we can stop it by pressing Ctrl+C in the Shell, or by clicking the Stop button in Thonny.
PRIMM
- Predict what you think will happen. Be specific.
- Run the program.
- Time to investigate the code. What does each line do?
Code explanation
- line 29 → creates the
runningvariable, which keeps the main loop going until the player quits. This is a flag variable: it starts asTrue, and gets changed toFalsewhen the player wants to exit. - line 33 → starts the main loop, which repeats as long as
runningisTrue. - line 34 → runs the
describemethod of whichever room is stored incurrent_room. At the start, this is thecavern. - line 36 → shows
>, waits for the player to type something, turns it into lower case with.lower(), and stores it incommand.
Notice that no matter what we type, the same thing happens. We've built the main loop, which waits for events (the player's input), but we haven't written any code to react to those events yet.
In a state machine, the game should change state when something happens, like moving to a new room. Right now there are no rules telling the program how to change state, so the loop just repeats.
Responding to commands¶
In event-driven programming, the player entering a command is an event. The code that responds to an event is called an event handler.
In main.py, let's create an event handler for the directions ("north", "south", "east" or "west"). Add the highlighted code.
PRIMM
- Predict what you think will happen. Be specific.
- Run the program.
- Time to investigate the code. What does each line do?
Code explanation
- line 38 → checks whether the player's command is in the list of directions the game accepts.
- line 39 → calls the
movemethod with the player's direction, and stores the room it returns incurrent_room, so the game's state now matches the new room.
Testing¶
Testing branching code
When we test branching code, we need to test every possible branch, methodically. To do this:
- create a table that lists every possible branch
- for each branch, write the expected result
- record the actual result
- identify any differences
Now that we can move between rooms, let's test that our code works. Draw up a table to test each option. Below is an example.
| Current room | Command | Expected result | Actual result |
|---|---|---|---|
| cavern | north |
"You can't go that way" | "You can't go that way" |
| cavern | south |
moved to armoury | moved to armoury |
| cavern | east |
"You can't go that way" | "You can't go that way" |
| cavern | west |
"You can't go that way" | "You can't go that way" |
| armoury | north |
moved to cavern | moved to cavern |
| armoury | south |
"You can't go that way" | "You can't go that way" |
| armoury | east |
moved to lab | moved to lab |
| armoury | west |
"You can't go that way" | "You can't go that way" |
| lab | north |
"You can't go that way" | "You can't go that way" |
| lab | south |
"You can't go that way" | "You can't go that way" |
| lab | east |
"You can't go that way" | "You can't go that way" |
| lab | west |
moved to armoury | moved to armoury |
Notice that we tested each of the four directions in each of the three rooms.
Exiting¶
The player can now move around the dungeon, but they can't exit the game. Let's make an event handler for quitting.
PRIMM
- Predict what you think will happen. Be specific.
- Run the program, and make sure you test
quit. - Time to investigate the code. What does each line do?
Code explanation
- line 40 → if the command isn't a direction, checks whether it's
quit… - line 41 → …and changes our flag variable to
False, so thewhileloop stops when it next checksrunning.
Capture incorrect commands¶
The game now understands the movement commands and quit, but what if the player types something completely different? The loop just shows the same room again, which isn't very helpful. We should tell the player when their command doesn't make sense.
Change main.py to include the highlighted code below.
PRIMM
- Predict what you think will happen. Be specific.
- Run the program, and test some incorrect commands.
- Time to investigate the code. What does each line do?
Code explanation
- line 42 → catches any command that isn't recognised…
- line 43 → …and tells the player their command doesn't make sense.
Testing¶
Now let's test those two new features. Draw up a table to test each option. Below is an example.
| Command | Expected result | Actual result |
|---|---|---|
south |
moved to armoury | moved to armoury |
dog |
"I don't understand." | "I don't understand." |
quit |
program exits | program exits |
Exercises¶
There isn't much to make in this stage, but we do need to test the room we added in Stage 1.
Exercise 1¶
Can you expand the movement testing table so it also tests moving to and from the room you added in Stage 1? Run each test and record the actual results.