All of the core pieces of functionality of the game are now implemented, so it is time to start looking at the higher level logic that actually implements the game itself.

First, I know I'll be playing sequences of lights and tones. A good way to represent the growing sequence is to use a list, so I can create a `play_sequence()` function that takes a list of region numbers and reproduces the sequence by invoking the `light_region()` function for each region:

```def play_sequence(sequence):
duration = 1 - len(sequence) * 0.05
if duration < 0.1:
duration = 0.1
for region in sequence:
light_region(region, duration)
```

The key part of this function is in the last two lines, where a for-loop runs through the regions given in the `sequence` list one by one. But to make the game a bit more interesting I decided to make the duration of each light shorter as the sequence gets longer. I start from a duration of one second, and substract 0.05 of a second for each item that is in the sequence. The longer the sequence, the shorter the duration, so it gets harder to play. Since a very long sequence could make the `duration` variable zero or even negative, I make sure that it never goes below 0.1.

The game is going to start by playing a sequence, which initially will have a length of one. Then it will expect the player to repeat the sequence by touching the pads. The next function I'm going to add is going to be for this purpose, and I'm going to call it `read_sequence()`. This function will also take the `sequence` list, but instead of playing the color regions, it will read them from the player:

```def read_sequence(sequence):
for region in sequence:
# the player made a mistake!
return False
light_region(region, 0.25)
return True```

You can see that here each time the player touches a region I light up that region with a short duration of 0.25 of a second. This is important, as it gives the player feedback that the input was recognized. If the user touches the wrong region, or if the read function times out and returns `None`, then the conditional will cause the function to return `False`, which will indicate that the player has lost the game. If the entire sequence is entered correctly, then the return value is `True`, and this will trigger the game logic to add one more element to the sequence and repeat the cycle.

In the event of the player making a mistake, the game needs to indicate that the game has been lost. This can be achieved with a low frequency tone. The function `play_error()` does this:

```def play_error():
cpx.start_tone(160)
time.sleep(1)
cpx.stop_tone()```

And now, finally all the parts are in place to implement the main game logic!

To select a new color when adding to the sequence, the `random.randint()` function from CircuitPython can be used. Here is how this function works in a REPL session:

```>>> import random
>>> random.randint(0, 3)
3
>>> random.randint(0, 3)
1
>>> random.randint(0, 3)
2
>>>```

Each time `random.randint()` is called, a random number that is between the two arguments is generated. Each time the player reproduces the sequence correctly, a new element will be generated randomly and appended at the end of the sequence.

Here is the function `play_game()`, which implements the complete game logic:

```import random

def play_game():
sequence = []
while True:
sequence.append(random.randint(0, 3))
play_sequence(sequence)
# game over
play_error()
break
time.sleep(1)```

Isn't it amazing that the game is now so simple to write? This function starts by creating an empty `sequence` list. This is where the color regions are going to be added as the player makes progress through the game. The rest of the game is implemented inside a `while True` loop, which will run until the player makes a mistake.

The loop starts by adding a random color to the sequence. The sequence is then played by calling the `play_sequence()` function. Next the player needs to repeat the sequence, which is something that I already implemented in the `read_sequence()` function. If this function returns `False` it means that the player lost the game, so the error tone is played by calling `play_error()` and then the `break` statement causes the while-loop to exit, which in turn makes the `play_game()` function exit as well. If the player enters the entire sequence correctly, then I have a short delay of one second to give the player a short break, and then the loop will start again from the top, adding another element to the sequence and repeating the cycle.

The last snippet of code that I need is to call `play_game()` in the global scope, so that the board automatically runs the game when it is powered. Instead of just calling the function, I'm going to put it inside a `while True` loop, so that each time the player loses the game a new game is started:

```while True:
play_game()```

And with this, the game is now complete!

Here is the complete code for the game. Download the code and save onto your CIRCUITPY drive as code.py.

```# SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries
#

import time
import random

REGION_LEDS = (
(5, 6, 7),  # yellow region
(2, 3, 4),  # blue region
(7, 8, 9),  # red region
(0, 1, 2),  # green region
)

REGION_COLOR = (
(255, 255, 0),  # yellow region
(0, 0, 255),    # blue region
(255, 0, 0),    # red region
(0, 255, 0),    # green region
)

REGION_TONE = (
252,  # yellow region
209,  # blue region
310,  # red region
415,  # green region
)

'A1': 0,  # yellow region
'A2': 2,  # red region
'A3': 2,  # red region
'A4': 3,  # green region
'A5': 3,  # green region
'A6': 1,  # blue region
'A7': 1,  # blue region
}

def light_region(region, duration=1):
# turn the LEDs for the selected region on
for led in REGION_LEDS[region]:
cpx.pixels[led] = REGION_COLOR[region]

# play a tone for the selected region
cpx.start_tone(REGION_TONE[region])

# wait the requested amount of time
time.sleep(duration)

# stop the tone
cpx.stop_tone()

# turn the LEDs for the selected region off
for led in REGION_LEDS[region]:
cpx.pixels[led] = (0, 0, 0)

val = 0
start_time = time.time()
while time.time() - start_time < timeout:
if cpx.touch_A1:
time.sleep(.3)
break
elif cpx.touch_A2:
time.sleep(.3)
break
elif cpx.touch_A3:
time.sleep(.3)
break
elif cpx.touch_A4:
time.sleep(.3)
break
elif cpx.touch_A5:
time.sleep(.3)
break
elif cpx.touch_A6:
time.sleep(.3)
break
elif cpx.touch_A7:
time.sleep(.3)
break
return val

def play_sequence(sequence):
duration = 1 - len(sequence) * 0.05
if duration < 0.1:
duration = 0.1
for region in sequence:
light_region(region, duration)

for region in sequence:
# the player made a mistake!
return False
light_region(region, 0.25)
return True

def play_error():
cpx.start_tone(160)
time.sleep(1)
cpx.stop_tone()

def play_game():
sequence = []
while True:
sequence.append(random.randint(0, 3))
play_sequence(sequence)
# game over
play_error()
break
time.sleep(1)

while True:
play_game()
```

I hope you found this guide useful, not only to learn how to write this specific game, but also to give you some ideas on how you can use similar techniques to implement other games!

This guide was first published on Jul 24, 2019. It was last updated on Jul 24, 2019.