## Game play

To play the Simon game, power up the PyRuler by plugging in a USB cable connected to a computer or a USB cell phone battery.

You will see the 4 leds above each touch pad light up in sequence then turn off.

Next, you will see one of the 4 leds turn on and off.

Then the DotStar RGB LED on the Trinket-sized circuit board will turn green prompting you to touch the pad corresponding to the led that was just on.

If you correctly input the given pattern, the DotStar led will cycle through a random pattern and the next pattern will be shown.

Each future pattern has the same initial sequence from before with one more added to the sequence each time.

When the DotStar is blue, the program is showing you the sequence.

If you get a sequence wrong, the DotStar will turn red then the game will start over with the leds cycling through the intro pattern.

## How the code works

Here's what's going on behind the scenes to deliver you the Simon game you know and love.

## Initialization

This segment towards the beginning of the program initializes the 4 touch pads on the PyRuler as well as the corresponding 4 leds.

```touches = [DigitalInOut(board.CAP0)]
for p in (board.CAP1, board.CAP2, board.CAP3):
touches.append(touchio.TouchIn(p))

leds = []
for p in (board.LED4, board.LED5, board.LED6, board.LED7):
led = DigitalInOut(p)
led.direction = Direction.OUTPUT
leds.append(led)

cap_touches = [False, False, False, False]```

## Functions

These functions simplify and optimize how the program runs.

## DotStar code

This part shows how the rainbow pattern for the dot star works.

```def wheel(pos):
# Input a value 0 to 255 to get a color value.
# The colours are a transition r - g - b - back to r.
if pos < 0 or pos > 255:
return (0, 0, 0)
if pos < 85:
return (255 - pos * 3, pos * 3, 0)
if pos < 170:
pos -= 85
return (0, 255 - pos * 3, pos * 3)
pos -= 170
return (pos * 3, 0, 255 - pos * 3)

def rainbow_cycle(wait):
for j in range(255):
for i in range(len(pixels)):
rc_index = (i * 256 // len(pixels)) + j
pixels[i] = wheel(rc_index & 255)
time.sleep(wait)```

## Detecting capacitive touch and setting a timer

Up next we read input from the PyRuler capacitive touch pads with the `read_caps()` function. Then with the `timeout_touch()` function we set a 3 second timer after each pattern is displayed as well as in-between each touch.

```def read_caps():
t0_count = 0
t0 = touches[0]
t0.direction = Direction.OUTPUT
t0.value = True
t0.direction = Direction.INPUT
# funky idea but we can 'diy' the one non-hardware captouch device by hand
# by reading the drooping voltage on a tri-state pin.
t0_count = t0.value + t0.value + t0.value + t0.value + t0.value + \
t0.value + t0.value + t0.value + t0.value + t0.value + \
t0.value + t0.value + t0.value + t0.value + t0.value
cap_touches[0] = t0_count > 2
cap_touches[1] = touches[1].raw_value > 3000
cap_touches[2] = touches[2].raw_value > 3000
cap_touches[3] = touches[3].raw_value > 3000
return cap_touches

def timeout_touch(timeout=3):
start_time = time.monotonic() # start 3 second timer waiting for user input
while time.monotonic() - start_time < timeout:
for i,c in enumerate(caps):
if c:
return i```

## Playing and reading each sequence with leds

• `light_cap()` turns on the led associated with each touch pad if the pad was touched.
• `play_sequence()` plays each led for the given sequence and slowly speeds up the playback of each sequence as they get longer.
• `read_sequence()` First turns the DotStar green (indicating to user to enter the sequence) then reads the touch pads and determines if they are the right sequence. If the wrong pad was touched, the function returns `False` which will cause a game over (more on this later).
```def light_cap(cap, duration=0.5):
# turn the LED for the selected cap on
leds[cap].value = True
time.sleep(duration)
leds[cap].value = False
time.sleep(duration)

def play_sequence(seq):
duration = max(0.1, 1 - len(sequence) * 0.05)
for cap in seq:
light_cap(cap, duration)

pixels.fill(green)
for cap in seq:
if timeout_touch() != cap:
# the player made a mistake!
return False
light_cap(cap, 0.5)
return True```

## The main loop

• First trigger the starting sequence of leds demonstrating the game is beginning.
• Next in a nested loop, turn the DotStar blue demonstrating the sequence is being shown.
• Then add a random number between 0 and 3 to the sequence and play the sequence.
• If the user enters the wrong sequence or the time runs out, turn the DotStar red indicating game over, and exit the loop starting the game over at the top of the main loop.
• Otherwise, trigger the rainbow animation on the DotStar (indicating a correct sequence) and move to next sequence.
```while True:
# led light sequence at beginning of each game
pixels.fill(blue)
time.sleep(1)
for led in leds:
led.value = True
time.sleep(0.25)
for led in leds:
led.value = False
sequence = []
while True:
pixels.fill(blue) # blue for showing user sequence
time.sleep(1)
sequence.append(random.randint(0, 3)) # add new light to sequence each time
play_sequence(sequence) # show the sequence
if not read_sequence(sequence): # if user inputs wrong sequence, gameover
# game over, make dot star red
pixels.fill(red)
time.sleep(3)
print("gameover")
break
else:
print("Next sequence unlocked!")
rainbow_cycle(0) # Dot star animation after each correct sequence
pixels.fill(0)
time.sleep(1)```

That's it, now you're a CircuitPython wiz!

Simon Says time to make your own game with the PyRuler!

This guide was first published on Aug 15, 2019. It was last updated on Jun 16, 2024.

This page (How Simon Works) was last updated on Mar 08, 2024.