🏠 Home page > 🐍 Pygame Zero tutorials
A tutorial for Python and Pygame Zero 1.2
Watch as a sequence of numbers flash.
Repeat the sequence using the number keys.
If you successfully repeat the sequence, a new number is added and the sequence flashes again
The sequence list is created. For now it contains a test sequence of numbers between 1 and 4.
sequence = [4, 3, 1, 2, 2, 3] # Temporary def draw(): screen.fill((0, 0, 0)) screen.draw.text(', '.join(map(str, sequence)), (0, 0))
The current sequence position starts at 1.
If the number in the sequence at the current position is pressed, then 1 is added to the current position.
This will error once the current position is beyond the length of the sequence list.
sequence = [4, 3, 1, 2, 2, 3] # Temporary current = 0 def on_key_down(key): global current if key in (keys.K_1, keys.K_2, keys.K_3, keys.K_4): if key == keys.K_1: number = 1 elif key == keys.K_2: number = 2 elif key == keys.K_3: number = 3 elif key == keys.K_4: number = 4 if number == sequence[current]: current += 1 def draw(): screen.fill((0, 0, 0)) screen.draw.text(', '.join(map(str, sequence)), (0, 0)) screen.draw.text(str(current + 1) + '/' + str(len(sequence)), (0, 20)) screen.draw.text('sequence[current]: ' + str(sequence[current]), (0, 40))
When the current position goes beyond the sequence length, it is reset to 0.
def on_key_down(key): global current if key in (keys.K_1, keys.K_2, keys.K_3, keys.K_4): # etc. if number == sequence[current]: current += 1 if current == len(sequence): current = 0
When the current position is reset, a random number between 1 and 4 is added to the sequence.
The random module is imported so that random.randint can be used.
def on_key_down(key): # etc. if current == len(sequence): current = 0 sequence.append(random.randint(1, 4))
The sequence is now created with a single random number.
Because the code for adding a random number to the sequence is reused, it is made into a function.
sequence = [] def add_to_sequence(): sequence.append(random.randint(1, 4)) add_to_sequence() # etc. def on_key_down(key): # etc. if current == len(sequence): current = 0 add_to_sequence()
A function is made which sets the initial state of the game.
This function is called before the game begins and when an incorrect number key is pressed.
def add_to_sequence(): sequence.append(random.randint(1, 4)) def reset(): global sequence global current sequence = [] add_to_sequence() current = 0 reset() def on_key_down(key): global current if key in (keys.K_1, keys.K_2, keys.K_3, keys.K_4): # etc. if number == sequence[current]: current += 1 if current == len(sequence): current = 0 add_to_sequence() else: reset()
The first square is drawn with a dark red square and a white number.
def draw(): screen.fill((0, 0, 0)) square_size = 50 screen.draw.filled_rect( Rect(0, 0, square_size, square_size), color=(50, 0, 0) ) screen.draw.text('1', (19, 18)) screen.draw.text(str(current + 1) + '/' + str(len(sequence)), (20, 60)) screen.draw.text('sequence[current]: ' + str(sequence[current]), (20, 100)) screen.draw.text(', '.join(map(str, sequence)), (20, 140))
The rest of the squares are drawn similarly.
def draw(): screen.fill((0, 0, 0)) square_size = 50 screen.draw.filled_rect( Rect(0, 0, square_size, square_size), color=(50, 0, 0) ) screen.draw.text('1', (19, 18)) screen.draw.filled_rect( Rect(square_size, 0, square_size, square_size), color=(0, 50, 0) ) screen.draw.text('2', (square_size + 21, 18)) screen.draw.filled_rect( Rect(square_size * 2, 0, square_size, square_size), color=(0, 0, 50) ) screen.draw.text('3', (square_size * 2 + 21, 18)) screen.draw.filled_rect( Rect(square_size * 3, 0, square_size, square_size), color=(50, 50, 0) ) screen.draw.text('4', (square_size * 3 + 21, 18)) # etc.
The code for drawing each square is similar, so it is made into a function.
def draw(): screen.fill((0, 0, 0)) def draw_square(number, color): square_size = 50 screen.draw.filled_rect( Rect(square_size * (number - 1), 0, square_size, square_size), color=color ) screen.draw.text(str(number), (square_size * (number - 1) + 21, 18)) draw_square(1, (50, 0, 0)) draw_square(2, (0, 50, 0)) draw_square(3, (0, 0, 50)) draw_square(4, (50, 50, 0)) # etc.
Numbers will flash every second.
A timer variable starts at 0 and increases by dt each frame.
When the timer is at or above 1 it is reset to 0.
For now, 'tick' is printed every time the numbers will flash.
def reset(): # etc. global timer # etc. timer = 0 def update(dt): global timer timer += dt if timer >= 1: timer = 0 # Temporary print('tick')
The current sequence position is reused to flash each square in the sequence.
The timer is used to advance the current sequence position.
For now, the square corresponding to the number at the current sequence position is drawn using its color, while the other squares are drawn in black.
The test sequence from before is used again.
This will error once current goes beyond the length of sequence.
def reset(): # etc. sequence = [4, 3, 1, 2, 2, 3] # Temporary def update(dt): global timer global current timer += dt if timer >= 1: timer = 0 current += 1 def draw(): screen.fill((0, 0, 0)) def draw_square(number, color): if number == sequence[current]: square_color = color else: square_color = (0, 0, 0) square_size = 50 screen.draw.filled_rect( Rect(square_size * (number - 1), 0, square_size, square_size), color=square_color ) screen.draw.text(str(number), (square_size * (number - 1) + 21, 18)) # etc.
The squares are given a highlighted color for flashing.
def draw(): screen.fill((0, 0, 0)) def draw_square(number, color, color_flashing): if number == sequence[current]: square_color = color_flashing else: square_color = color square_size = 50 screen.draw.filled_rect( Rect(square_size * (number - 1), 0, square_size, square_size), color=square_color ) screen.draw.text(str(number), (square_size * (number - 1) + 21, 18)) draw_square(1, (50, 0, 0), (255, 0, 0)) draw_square(2, (0, 50, 0), (0, 255, 0)) draw_square(3, (0, 0, 50), (0, 0, 255)) draw_square(4, (50, 50, 0), (255, 255, 0)) # etc.
A variable is created which indicates whether the squares are flashing ('watch') or whether the player is inputing numbers ('repeat').
The state starts as 'watch' and changes to 'repeat' after the flashing sequence has ended.
The keyboard input code is only run if the state is 'repeat'.
Once the sequence has been successfully entered, the state changes back to 'watch'.
def reset(): global state # etc. state = 'watch' # 'watch', 'repeat' def update(dt): global timer global current global state if state == 'watch': timer += dt if timer >= 1: timer = 0 current += 1 if current == len(sequence): state = 'repeat' current = 0 def on_key_down(key): global current global state if state == 'repeat': if key in (keys.K_1, keys.K_2, keys.K_3, keys.K_4): if key == keys.K_1: number = 1 elif key == keys.K_2: number = 2 elif key == keys.K_3: number = 3 elif key == keys.K_4: number = 4 if number == sequence[current]: current += 1 if current == len(sequence): current = 0 add_to_sequence() state = 'watch' else: reset() def draw(): screen.fill((0, 0, 0)) def draw_square(number, color, color_flashing): if state == 'watch' and number == sequence[current]: square_color = color_flashing else: square_color = color # etc. screen.draw.text('state: ' + state, (20, 180))
A boolean variable is used to indicate whether to set the highlighted color or not.
It starts off as False, gets toggled to True when the timer ticks, and gets toggled back to False when the timer ticks again.
The timer limit is changed to tick twice as fast.
def reset(): # etc. global flashing # etc. flashing = False def update(dt): global timer global current global state global flashing if state == 'watch': timer += dt if timer >= 0.5: timer = 0 flashing = not flashing if not flashing: current += 1 if current == len(sequence): state = 'repeat' current = 0 def draw(): screen.fill((0, 0, 0)) def draw_square(number, color, color_flashing): if state == 'watch' and flashing and number == sequence[current]: square_color = color_flashing else: square_color = color # etc. screen.draw.text('flashing: ' + str(flashing), (20, 220))
If the wrong key is pressed, instead of resetting the game immediately, the state is set to 'gameover'. When a key is pressed in the 'gameover' state, the game is then reset.
def on_key_down(key): global current global state if state == 'repeat': if key in (keys.K_1, keys.K_2, keys.K_3, keys.K_4): # etc. if number == sequence[current]: # etc. else: state = 'gameover' elif state == 'gameover': reset()
The current sequence position and the length of the sequence is only displayed if the game is in the 'repeat' state, and a game over message is shown if the game is in the 'gameover' state.
def draw(): # etc. if state == 'repeat': screen.draw.text(str(current + 1) + '/' + str(len(sequence)), (20, 60)) elif state == 'gameover': screen.draw.text('Game over!', (20, 60)) # Removed: screen.draw.text('sequence[current]: ' + str(sequence[current]), (20, 100)) # Removed: screen.draw.text(', '.join(map(str, sequence)), (20, 140)) # Removed: screen.draw.text('state: ' + state, (20, 180)) # Removed: screen.draw.text('flashing: ' + str(flashing), (20, 220))