from IPython.core.display import HTML
def set_width(width):
    display(HTML(f"""<style>  
            .container {{ width:{width}% !important; 
                            min-width:800px !important; margin: 0 auto}} 
            .jp-Cell {{ width:{width}% !important; 
                            min-width:800px !important; margin: 0 auto}} </style>"""))
# Set container width to X% of the fullscreen 
set_width(50)

6C: Whack-a-mole#

Learning goal: Use the Alpaca’s digital signals to make a fun game: whack the mole. After exploring how the Alpaca’s switches work, you will build this game in steps with incrasing difficulty, to end up with a inpredictable game with anti-cheating measures.

Structure of an experiment:

  • Anticipe + Stimulate (5+15+0): per person. This is homework and should be finished before you start your 4 hours practicum session

  • Implement + Investigate (5+65): with your partner(group of 2)

  • Compare + Conclude: with a group of 4(per table)

BACKGROUND#

⏳ Estimated time: 5 min

Whack the mole is a game, in which moles are popping up and you have to hit them with a hammer, before they go underground again. Because your Alpaca doesn’t have moles, nor hammers, you are going to replace them with leds (moles) and buttons (hammer). No destruction here.

You already know how to use leds, but need a background on how exacly buttons work in Alpaca. When looking at the electonic scheme of the Alpaca, you should notice that (the output of) SW1&2 are on 5V when the switch is not pushed. Once the switch is pushed a connection through ground is established, and SW1&2 become 0V. As you can see on the scheme below, the 3rd button is more complicated. You will build a bit more extended circuit to be able to use switch 3 in your experiment.

ANTICIPATE: elements you will need to program in#

⏳ Estimated time: 10 min

Your game will need to utilize leds to represent moles and buttons to represent a hammer. You should also record the reaction time.

You have three LEDs and three push buttons available as well as a buzzer.

In order to be a more efficient programmer, you need to break the problem into smaller steps.

  • Which steps will you take, in order to step-by-step test your software and hardware?

  • How can you make your game less predictable?

  • How to discourage cheating on the game (like pressing the button, without looking at the LED or holding all the buttons down)?

### TO DO ="your step by step approach"

SIMULATE#

⏳ Estimated time: 0 min

no simulate => the preparation does not cost that much time, but the implementation cost much more. It would be wise to already start coding parts below.

Feel free to watch the precap video from 2022, in which you’ll get an overview of all steps plus see the whack the mole demonstrated:

IMPLEMENT & INVESTIGATE 1: See the button in action#

⏳ Estimated time: 5 min

First you will see how the button works on its own

  • Connect LED2C to GND

  • Connect SW2 to LED2A (middle switch to middle LED)

After making these connections, push and release the button a couple of times.

  • When pushed, is the light on or off?

  • Use this info in the next parts

ℹ️ Hint If you have connected the PicoPI with USB to your laptop, the LED light should be on. You should be able to switch it off by pressing the switch(button).

IMPLEMENT & INVESTIGATE 2: Build the game#

⏳ Estimated time: 10+15+15+15+10 min

Combine your plan from Anticipate and steps below to create a working game of whack-a-mole

Step 1: PicoPI + Button Controlling One LED#

⏳ Estimated time: 10 min

In this part you will turn one LED on via PicoPI, and you switch it off via pushing the corresponding switch.

  • Disconnect SW2 from LED2A

  • Connect SW2 to Din2

  • Connect LED2A to Dout2

  • Run the code below twice:

    • explore the relation between LED on/off in response to you pressing and releasing button 2

    • explore the effect of time.sleep in and out the if statement. (by commenting either line 13 or 14)

remember to connect your alpaca(both cables) to your laptop

Detailed fritzing
#%use micropython
%serialconnect to --port="COM3" 
#ADD COM PORT HERE, e.g. --port="COM4"
import machine # type: ignore
import time

# INITIALIZING THE PICO - the pins we will use to perform our measurement with
from machine import Pin # type: ignore

led = Pin(16, Pin.OUT)
button = Pin(20, Pin.IN,  None) # options:None,Pin.PULL_UP, Pin.PULL_DOWN

for ii in range(50):
    if button.value(): #think about & test whether button value is 0 or 1 when pressed
        led.toggle()
        #time.sleep(0.2) #option 1: sleep in the if statement
    time.sleep(0.2) # option 2: sleep outside the if statement
    #print(ii, button.value()) #uncomment if you want to see what is happening
print('done')
led.value(0)
# Can explain what is happening?
# Can you comment on:
# 1 whether the button value is high or low when pressed?
# 2 the difference in behavior between sleep ...
#              in and out the if statement (option1 or 2)

### TO DO="your explanation what is happening"

Step 2: PicoPI + Button-Based LED Extinguishing#

⏳ Estimated time: 15 min

In the earlier code the response of the program was different to what we want for Whack A Mole: it toggled the LED rather than permanently switching it off. Let’s adapt the earlier code to get the behavior we want.

  • Switch on one LED

  • Turn the LED off, only when the switch is pressed.

import machine # type: ignore
import time

# INITIALIZING THE PICO - the pins we will use to perform our measurement with
from machine import Pin # type: ignore

led = Pin(16, Pin.OUT)
button = Pin(20, Pin.IN,  None) # options:None,Pin.PULL_UP, Pin.PULL_DOWN
sleep_time=0.01

# switch the LED on (with either led.toggle() or led.value(0) or (1)
led."__FILL_IN__" #replace "__FILL_IN__" with the appropriate code

# use a for loop to wait max 5 seconds
for ii in range(...): # something with 5 (seconds) and sleep_time
    if ...: #something with button.value(), should you check for 0V or 5V? Or just try and adapt later if needed.
        break #end the for loop if the button is pressed
    time.sleep(sleep_time)

# don't forget to switch the LED off
led."__FILL_IN__" #replace "__FILL_IN__" with the appropriate code

ℹ️ Hint A lot of hints are already given in the comments above. Think critically about:

  • Is it smarter to use led.toggle or led.value? How do you make sure that the LED is switched off at the end of your code?

  • Do you want to break at button.value high or low?

After you verfied you can switch of a single LED with the above code, copy your above code and:

  1. add some randomness using the random module (wait a random time before switching on the led)

  2. record your reaction time

Add the following functionalities:

  • randomly wait between 1-5 seconds

  • measure the reaction time

  • display the reaction time

ℹ️ Hint

time() works in seconds, random.random() generates a random number between 0 and 1. If you want to start at 1 second and have maximum value of 5 second, how do you need to scale random.random() using math operations ?

time.ticks_ms is in miliseconds, so when you print the reaction time for that you might want to convert it to seconds. It takes a timepoint - it does NOT start counting

import time
import random

from machine import Pin # type: ignore

led = Pin(16, Pin.OUT)
button = Pin(20, Pin.IN, None)
sleep_time=0.01 # to be used in the loop

# switch LED on after random 1-5 seconds
time.sleep(...) #use random.random()

start_time=time.ticks_ms() 

#enter your solution from above, starting with switching on the led

# modify it to include time aquisition
end_time= ... # again something with time.ticks_ms() 
reaction_time= ... #something with start and end time
print('Your reaction time was ',reaction_time)

Step 3: PicoPI Button Control of Multiple LEDs#

⏳ Estimated time: 15 min

Change the previous code to light up 1 out of 3 LEDS. Which LED is turned on should be decided at random.

Implement the connection scheme mentioned below. Also already add the connections for later use of the buzzer.

  • Connect SW1 to Din1 (on the Cria), connect LED3A to Dout1 and LED3C to GND

  • Place a 10k resistor in the breadboard. Connect one side to +5V, and the other side to SW3B and Din3 (left switch to left LED, connect it on the breadboard) and connect LED1A to Dout3

  • Connect Dout0 to BUZZER

ℹ️ Hints for connections on the board

The following connections should be present on your board:

  • Dout1 - LED3A

  • Dout2 - LED2A

  • Dout3 - LED1A

  • LED1C - GND

  • LED2C - GND

  • LED 3C - GND

  • SW3A - GND

  • 5V - one end of resistor

  • Din3 - other end of resistor

  • SW3B- other end of resistor

  • Din1 -SW1

  • Din2- SW2

  • Dout0-BUZZER

Detailed fritzing

ℹ️ Hint

use random.randint(?,?) to choose which led you will switch on

In order to keep your code as short as possible, you can implement the random LED without altering your previous code. All you need is:

  • Work with a general LED in the switching code (as before, the variable used was led),

  • Before the switching code, define which random switch to this general LED variable (for example led=ledB).

  • For testing each switch, you can run your code multiple times, OR temporarily overwrite which LED and switch you use.

    • First choose the led randomly, then wait the random time. It will make later step easier.

    • Check if you connected the switches and leds correctly.

📝 Todo:

  • Test your code: Does every switch and LED behave the same? If not, check your connections and code.

import machine # type: ignore
import time
import random

from machine import Pin # type: ignore

ledA = Pin(15, Pin.OUT)
ledB = Pin(16, Pin.OUT)
ledC = Pin(17, Pin.OUT)
#ledC.value(0)
buttonA = Pin(19, Pin.IN, None)
buttonB = Pin(20, Pin.IN, None)
buttonC = Pin(21, Pin.IN, None)

#1. write code which randomly assigns led&button (from your previous code), to options A or B or C just above. 
#     depending on the option, you could say led=ledA, and then continue with the already written code
#2. copy the code for a single LED+button from the previous exercise

### TO DO="your code"

Step 4: PicoPI Button Control of Multiple LEDs - repeatedly#

⏳ Estimated time: 15 min

Now put your previous code in a loop, such that you would have to press a random LED 10 times. Also store the reaction_time value in an array reaction_time

Finish coding the game for three LEDs and 10 random blinks.

import machine # type: ignore
import time
import random

from machine import Pin

ledA = Pin(15, Pin.OUT)
ledB = Pin(16, Pin.OUT)
ledC = Pin(17, Pin.OUT)
#ledC.value(0)
buttonA = Pin(19, Pin.IN, None)
buttonB = Pin(20, Pin.IN, None)
buttonC = Pin(21, Pin.IN, None)

reaction_time=[]
# list & array debacle - as you can see, reaction_time is a list now, if you prefer working on an array, change the line of code above


# here paste the code you created above and modify it so you save the reaction time in the array/list and it plays 10 times
### TO DO="your code"

print('Your reaction times are ', reaction_time) 
print('Your average reaction time is ',sum(reaction_time)/len(reaction_time))

ℹ️ Hint
for adding new entrys to the list use list.append(??) and replace ‘list’ with the name od your list

Step 5: The End of the Game and Warning for Fraud#

⏳ Estimated time: 10 min

Signal the end of the game by making all 3 leds blink 3 times in unison.

Make sure that you cannot cheat the game by continuously pressing the button even before the LED lights up. You can resolve this be checking if the button is not pressed before you start the random wait time. If it is pressed (too early), let the buzzer sound for one second, print a warning on screen and set the measured reaction time to 5 seconds as punishment!

Test and enjoy your whack the mole game. Feel free to test whether you are faster with your left or right hand.

Remember that the buzzer is pretty loud and annoying, so don’t overdo it!

##buzzer code for you
from machine import Pin # type: ignore
import time

buzzer= Pin(14, Pin.OUT)

buzzer.value(1)
time.sleep(1)
buzzer.value(0)
    
import machine # type: ignore
import time
import random

from machine import Pin # type: ignore

buzzer= Pin(14, Pin.OUT)
ledA = Pin(15, Pin.OUT)
ledB = Pin(16, Pin.OUT)
ledC = Pin(17, Pin.OUT)
#ledC.value(0)
buttonA = Pin(19, Pin.IN, None)
buttonB = Pin(20, Pin.IN, None)
buttonC = Pin(21, Pin.IN, None)

reaction_time=[]

### TO DO="your code"


print('Your average reaction time is ',sum(reaction_time)/len(reaction_time))     

COMPARE & CONCLUDE#

⏳ Estimated time: 5 min

  • Wait till all (4) group members finish their observation

  • Compare your results with your other group members.

  • If your results agree, and are in line with all predictions, then talk to a TA and get checked off

  • Otherwise, so if your results do not agree, or your results are not in line with your predictions, then first discuss amongst your group before getting a TA.

to be checked off by a TA:

  1. let the TA enjoy your game (or you playing the game), reflect how all the features are implemented

  2. exit card:

    1. Write a brief abstract on what you learned (conclusion, useful graph),

    2. Which troubleshooting skills do you want to remember for next sessions,

    3. Which code do you copy for use in next sessions,

  3. How do you think this notebook could be improved?

#6C Whack a mole
### TO DO="1. reflect: how did you manage to get implement all required features?" 

### TO DO="2a. abstract"

### TO DO="2b. troubleshooting"

### TO DO="2c. code"

### TO DO="3. what changes would you suggest?"
%rebootdevice
%disconnect