How Simon Works

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.

Download: file
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.

Download: file
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.

Download: file
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:
        caps = read_caps()
        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).
Download: file
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)

def read_sequence(seq):
    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.
Download: file
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 Aug 15, 2019. This page (How Simon Works) was last updated on Feb 08, 2020.