CodeRigor

LESSON

Separate held directions from command events

Convert held left/right keys and one-frame commands into a deterministic Controls value.

A held key is a state. A key-down event is a transition. Treating both as “the keys” creates subtle bugs: pause can toggle sixty times a second, or movement can occur only when the operating system repeats a key-down message.

The desktop adapter has the Pygame-specific code. It consumes the event queue once per frame, translates window-close and key-down commands, and samples held directions. The pure model receives left/right names and pause/restart/quit events. It can therefore be tested without an operating-system keyboard.

Two input lifetimes
start / 3
Held directions describe the current frame; command events describe transitions observed in that frame. Both become one Controls value.
Two input lifetimes
Held directions describe the current frame; command events describe transitions observed in that frame. Both become one Controls value.

Define every ambiguous combination. Neither direction means zero. Right only means +1. Left only means −1. Both mean zero. Duplicates do not make the paddle faster; the input is membership, not a count. The same rule works for a list, tuple or set.

Python
for held in [[], ["left"], ["right"], ["left", "right"], ["right", "right"]]:
    axis = int("right" in held) - int("left" in held)
    print(held, "->", axis)

Pause, restart and quit are Boolean flags for this frame. Two pause events in the same frame coalesce to one flag. On the next frame the event list starts empty; a previous pause flag must not remain latched.

Pygame’s event documentation explains that its queue has a limit and must be handled each frame. Our adapter uses event.get(), then get_pressed() for held directions. The model does not call either function. This division also lets a deterministic replay supply the same semantic inputs as a human player.

This lesson converts inputs only. The later state-machine lesson decides what wins if pause, restart and quit arrive together. Avoid hiding that priority in the order of the raw event list.

Sample, normalize, advance
start / 3
One frame consumes its own events. A fresh list on the next frame prevents a command from latching.
Sample, normalize, advance
One frame consumes its own events. A fresh list on the next frame prevents a command from latching.
Python
frames = [["pause", "pause"], [], ["restart"]]
for events in frames:
    print({name: name in events for name in ("pause", "restart", "quit")})

The exercise supplies validation so your focus is the conversion. Keep the input collections unchanged. After implementing it, test both directions together, duplicate events and the frame after a pause.

Primary reference: Pygame event queue. The desktop lab includes the actual key translation and a scripted replay, so you can inspect both sides of this boundary.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

Optional long-form companion. Our catch game and model exercises are original and self-contained.