Skip to content

Instantly share code, notes, and snippets.

@rbricheno
Last active November 27, 2023 13:37
Show Gist options
  • Save rbricheno/5d52b2a3749cc59d15d6112f8291435d to your computer and use it in GitHub Desktop.
Save rbricheno/5d52b2a3749cc59d15d6112f8291435d to your computer and use it in GitHub Desktop.
Simon game for the pocketmoneytronics Christmas trees
# Simon game for the pocketmoneytronics Christmas trees
# By Rob!
# Public Domain and / or CC0 at your choice, share and enjoy!
import time
from time import sleep
from machine import Pin
from random import randint
game_states = {
'WAIT_START': 1,
'RUNNING_SEQUENCE': 2,
'CHECKING_RESPONSE': 3,
'FAILED': 4
}
trees = {
'RED': 1,
'AMBER': 2,
'GREEN': 3
}
start_btn = machine.Pin(22, machine.Pin.IN, machine.Pin.PULL_UP)
green_btn = machine.Pin(21, machine.Pin.IN, machine.Pin.PULL_UP)
amber_btn = machine.Pin(20, machine.Pin.IN, machine.Pin.PULL_UP)
red_btn = machine.Pin(19, machine.Pin.IN, machine.Pin.PULL_UP)
# This is a rudimentary sort of "debouncing" of the start button.
# It isn't required for the UI buttons because you have to press a
# different one for each next tree :-)
start_last = time.ticks_ms()
# The most recent button that was pressed
previous_button = None
# The sequence in which the trees will be displayed
sequence = []
# The buttons that the user pressed in response (so far)
user_input = []
# Simple state machine to handle game modes
state = game_states['WAIT_START']
# Some utility functions to handle common forrestry managment tasks :-)
def trees_off():
for led in range(15):
Pin(led, Pin.OUT).low()
def tree_name_on(tree_name):
# There are five LEDs in each tree, and they are numbered from one to fifteen.
# The five LEDs of each tree are next to each other, in order, so the first five are red, the next five amber
# and the final five are green. Just like traffic lights!
offset = 5 * trees[tree_name] - 5
for led in range(5):
Pin(led + offset, Pin.OUT).high()
def tree_number_on(tree):
# A more fancy way of turning a tree on, where it slowly lights around the edge before displaying the full thing
offset = 5 * tree - 5
trees_off()
for led in range(5):
Pin(led + offset, Pin.OUT).high()
sleep(0.05)
trees_off()
for led in range(5):
Pin(led + offset, Pin.OUT).high()
def tree_number_on_speed(tree, speed):
# Like the fancy thing above, but can be done more or less quickly for when we speed up the game
offset = 5 * tree - 5
trees_off()
for led in range(5):
Pin(led + offset, Pin.OUT).high()
sleep(0.05 * speed)
trees_off()
for led in range(5):
Pin(led + offset, Pin.OUT).high()
def red_on():
tree_name_on("RED")
def amber_on():
tree_name_on("AMBER")
def green_on():
tree_name_on("GREEN")
def trees_on():
red_on()
amber_on()
green_on()
def twinkle():
# Twinkle effect, to entice and bewitch the user.
for led in range(15):
Pin(led, Pin.OUT).low()
for i in range(50):
led = randint(0,14)
Pin(led, Pin.OUT).toggle()
sleep(0.01)
def any_other_tree(most_recent_tree):
# Make sure that we add a different tree to the end of the sequence each time
choose_one = randint(1,2)
if most_recent_tree == 1:
return choose_one + 1
elif most_recent_tree == 3:
return choose_one
else:
if choose_one == 1:
return 1
else:
return 3
def button_handler(pin):
global start_last, start_btn, green_btn, amber_btn, red_btn, state, game_states, trees, sequence, user_input, previous_button
if pin is start_btn:
if time.ticks_diff(time.ticks_ms(), start_last) > 200:
if state != game_states['WAIT_START']:
# Reset a running game
state = game_states['WAIT_START']
else:
# Start a new game
sequence = []
user_input = []
state = game_states['RUNNING_SEQUENCE']
start_last = time.ticks_ms()
elif pin is green_btn:
if state == game_states['CHECKING_RESPONSE'] and previous_button != "GREEN":
trees_off()
green_on()
user_input.append(trees['GREEN'])
previous_button = "GREEN"
elif pin is amber_btn:
if state == game_states['CHECKING_RESPONSE'] and previous_button != "AMBER":
trees_off()
amber_on()
user_input.append(trees['AMBER'])
previous_button = "AMBER"
elif pin is red_btn:
if state == game_states['CHECKING_RESPONSE'] and previous_button != "RED":
trees_off()
red_on()
user_input.append(trees['RED'])
previous_button = "RED"
start_btn.irq(trigger = machine.Pin.IRQ_RISING, handler = button_handler)
green_btn.irq(trigger = machine.Pin.IRQ_RISING, handler = button_handler)
amber_btn.irq(trigger = machine.Pin.IRQ_RISING, handler = button_handler)
red_btn.irq(trigger = machine.Pin.IRQ_RISING, handler = button_handler)
while True:
if state==game_states['WAIT_START']:
# Play a sort of demo mode when we aren't doing anything.
if randint(1,60) == 42:
twinkle()
led = randint(0,14)
Pin(led, Pin.OUT).toggle()
sleep(0.3)
if state==game_states['FAILED']:
# This is where I would sound a buzzer, IF I HAD ONE! :P
for i in range(3):
trees_on()
sleep(0.5)
trees_off()
sleep(0.5)
sleep(1)
state = game_states['WAIT_START']
if state==game_states['RUNNING_SEQUENCE']:
# Speed up as we go along
speed = 1 - 0.1 * len(sequence)
if speed < 0.2:
speed = 0.2
# Start the sequence with any random tree
if sequence == []:
next_tree = randint(1,3)
else:
# Always show a different tree next...
most_recent_tree = sequence[len(sequence) - 1]
next_tree = any_other_tree(most_recent_tree)
sequence.append(next_tree)
for tree in sequence:
trees_off()
sleep(speed/2)
tree_number_on_speed(tree, speed)
sleep(speed)
trees_off()
user_input = []
previous_button = None
state = game_states['CHECKING_RESPONSE']
if state==game_states['CHECKING_RESPONSE']:
counter = 0
if user_input:
# Check every inputted tree for correctness
for sequence_number in range(len(user_input)):
if user_input[sequence_number] != sequence[sequence_number]:
state = game_states['FAILED']
counter = counter + 1
if state == game_states['CHECKING_RESPONSE'] and counter == len(sequence):
# All of the inputs have been given, and none of them were wrong.
# A winner is you!
twinkle()
# But the princess is in another castle...
user_input = []
state = game_states['RUNNING_SEQUENCE']
sleep(0.2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment