##
## Mega Simon Says in Micropython ##
## by Hari Wiguna, 2024 ###
##

from machine import Pin, PWM
import time
import random

ios = [23,22,21,19,18,5,17,16,4,0,2,15,8,7,6,11,10,9,13,12,14,27,26,25] # These are GPIO pins. Each connected to an LED AND a push button
freqs = [261,293,329,349,369,392,415,440,466,493,523,554,587,622,659,698,739,783,830,880,932,987,1046,1108,1174,1244,1318] # must have at least same # of elements as ios
sequence = []

def InitPinsAsInputs():
    for p in ios:
        Pin(p, Pin.IN, Pin.PULL_UP)

def TestLeds():
    for i,p in enumerate(ios):
        led = Pin(p, Pin.OUT)
        led.value(0) # bring LED cathode to ground to turn LED on.
        buzz(i,.4)
        Pin(p, Pin.IN, Pin.PULL_UP) # turn io into input to turn off the led

def buzz(freqIndex, durationSecs):
    buzzer = PWM(Pin(32))
    buzzer.freq(freqs[freqIndex])
    timeToStop = time.ticks_ms() + durationSecs*1000
    buzzer.duty(512)
    time.sleep(durationSecs)
    buzzer.duty(0)


def GameOver(score):
    for i in reversed(range(len(freqs))):
        buzz(i,.08)
    print("Game over! Your Score:", score)


def readButtons(waitfor):
    pressed = -1

    # Prepare to read buttons
    buttons = []
    for p in ios:
        buttons.append( Pin(p, Pin.IN, Pin.PULL_UP) )

    timeout = time.ticks_ms() + waitfor
    while time.ticks_ms() < timeout and pressed == -1:
        for i,b in enumerate(buttons):
            if b.value() == 0:
                pressed = i
                buzz(i, 0.5)
                break

        time.sleep(0.1)
    
    return pressed

#== Setup ==
InitPinsAsInputs()
#TestLeds()

print("Ready?")
time.sleep(1)

# *** MAIN ***
isGameOver = False
score = 0
print(isGameOver)
while not isGameOver:
    #== Add random to sequence ==
    dice_roll = random.randint(0, len(ios)-1)
    sequence.append(dice_roll)

    #== Play sequence ==
    print("Simon Says: ")
    for s in sequence:
        p = ios[s]
        print(s, end=", ")
        led = Pin(p, Pin.OUT)
        led.value(0) # bring LED cathode to ground to turn LED on.
        buzz(s, 0.5)
        Pin(p, Pin.IN, Pin.PULL_UP) # turn io into input to turn off the led
        time.sleep(.5)
    print()

    #== Take in user's input ==
    print("You say: ")
    seqIndex = 0
    while not isGameOver and seqIndex<len(sequence):
        #== Wait for user input for 2 seconds==
        pressed = readButtons(2000)
        print(pressed, end=", ")

        #== if timeout -> game over ==
        if pressed == -1:
            isGameOver = True
        else:
            #== if received button press, compare it to sequence, if wrong -> game over ==
            if pressed == sequence[seqIndex]:
                seqIndex = seqIndex + 1
                score = seqIndex
            else:
                isGameOver = True
        # loop back to check next in sequence.
        # if no more, then make the sequence longer
    print()

    if isGameOver:
        GameOver(score)
    else:
        time.sleep(2) # Pause before starting new round