CircuitPython Zombies

If you are new to CircuitPython, be sure to check out the Welcome guide for an overview. And if you want to know even more, check out the Essentials guide.

Ensure your Circuit Playground Express has CircuitPython 3.0 or later.

Adafruit suggests using the Mu editor to edit your code and have an interactive REPL in      CircuitPython. You can learn about Mu and installation in this tutorial.

In the CircuitPython version, you can select what you want to be when the board is powered up. To be a human, simply start up the board. To be a healer, hold down button A while first running the program. To be a zombie, hold down button B while first running the program.

Copy the following to code.py on at least three Circuit Playground Expresses:

import time
import pulseio
import board
import adafruit_irremote
import digitalio
import neopixel

pixels = neopixel.NeoPixel(board.NEOPIXEL, 10)

button_a = digitalio.DigitalInOut(board.BUTTON_A)
button_a.switch_to_input(pull=digitalio.Pull.DOWN)

button_b = digitalio.DigitalInOut(board.BUTTON_B)
button_b.switch_to_input(pull=digitalio.Pull.DOWN)

red_led = digitalio.DigitalInOut(board.D13)
red_led.direction = digitalio.Direction.OUTPUT

# Create a 'pulseio' input, to listen to infrared signals on the IR receiver
pulsein = pulseio.PulseIn(board.IR_RX, maxlen=120, idle_state=True)
# Create a decoder that will take pulses and turn them into numbers
decoder = adafruit_irremote.GenericDecode()

# Create a 'pulseio' output, to send infrared signals on the IR transmitter @ 38KHz
pwm = pulseio.PWMOut(board.IR_TX, frequency=38000, duty_cycle=2 ** 15)
pulseout = pulseio.PulseOut(pwm)
# Create an encoder that will take numbers and turn them into NEC IR pulses
encoder = adafruit_irremote.GenericTransmit(header=[9500, 4500], one=[550, 550],
                                            zero=[550, 1700], trail=0)

# Set between 0 and 1 to set LED pulse speed. Smaller numbers are slower.
healer_led_pulse = 0.008
zombie_led_pulse = 0.07
pixels.brightness = 0.0

# Change to set number of seconds between each signal being sent.
HEALER_TIME = 1
ZOMBIE_TIME = 3

# The Healer and Zombie IR signal codes
HEALER_IR = [72, 69, 65, 76]
ZOMBIE_IR = [90, 79, 77, 66]

human_health_counter = 10
human = 0
healer = 1
zombie = 2

if button_a.value:  # Hold button A down before starting up to set mode Healer.
    mode = healer
    pixels.fill((255, 255, 255))
    pixels.brightness = 0.3
elif button_b.value:  # Hold button A down before starting up to set mode Zombie.
    mode = zombie
    pixels.fill((255, 0, 0))
    pixels.brightness = 0.8
else:  # Defaults to human mode!
    mode = human
    pixels.fill((0, 255, 0))
    pixels.brightness = 0.5

start = time.monotonic()
while True:
    now = time.monotonic()
    if mode is human:
        pulses = decoder.read_pulses(pulsein)
        try:
            # Attempt to convert received pulses into numbers
            received_code = decoder.decode_bits(pulses)
        except adafruit_irremote.IRNECRepeatException:
            # We got an unusual short code, probably a 'repeat' signal
            continue
        except adafruit_irremote.IRDecodeException:
            # Something got distorted or maybe its not an NEC-type remote?
            continue
        print("NEC Infrared code received: ", received_code)
        if received_code == ZOMBIE_IR:
            print("Zombie code received!")
            pixels.fill(0)
            human_health_counter -= 1
            for i in range(human_health_counter):
                pixels[i] = (0, 255, 0)
            if human_health_counter < 1:
                mode = zombie
                pixels.fill((255, 0, 0))
                print("Zombified!")
        if received_code == HEALER_IR:
            print("Healer code received!")
            if human_health_counter < 10:
                pixels.fill(0)
                human_health_counter += 1
                for i in range(human_health_counter):
                    pixels[i] = (0, 255, 0)
            else:
                pass
    elif mode is zombie:
        brightness = pixels.brightness
        brightness += zombie_led_pulse
        if not 0.0 <= brightness <= 1.0:
            zombie_led_pulse = -zombie_led_pulse
            continue
        pixels.brightness = brightness
        if now - start > ZOMBIE_TIME:
            print("Zombie code sent! \n")
            red_led.value = True
            encoder.transmit(pulseout, ZOMBIE_IR)
            red_led.value = False
            start = time.monotonic()
    elif mode is healer:
        brightness = pixels.brightness
        brightness += healer_led_pulse
        if not 0.0 <= brightness <= 0.5:
            healer_led_pulse = -healer_led_pulse
            continue
        pixels.brightness = brightness
        if now - start > HEALER_TIME:
            print("Healer code sent! \n")
            red_led.value = True
            encoder.transmit(pulseout, HEALER_IR)
            red_led.value = False
            start = time.monotonic()

Healer

Let's start with the healer. To choose the healer role, you'll hold down button A while the program starts up. This means you can hold button A before you provide power to the board and let it start up while holding the button. You can also hold the button and then reset the board while continuing to hold the button. When the role is successfully chosen, the LEDs will be white.

The healer lights up and slowly pulses the LEDs white. Every second, the healer transmits an IR signal. The little red LED lights up to let you know the signal is being sent.

The signal interval is configurable in the code - you can change HEALER_TIME = 1 to a different number of seconds. Increase the number to send the signal less often and make the game more difficult. Decrease the number to send the signal more often and make the game easier.

That's all there is to the healer. Healers are not affected by humans or zombies.

Zombie

Next, we'll look at the zombie. To choose the zombie role, you'll hold down button B while the program starts up. This works the same way as choosing the healer role. You can hold the button down and during powering on the board, or you can hold it down and reset the board while continuing to hold it. When the role is successfully chosen, the LEDs will be red.

The zombie lights up and quickly pulses the LEDs red. Every three seconds, the zombie transmits an IR signal. The little red LED lights up to let you know the signal is being sent.

The signal interval is configurable in the code - you can change ZOMBIE_TIME = 3 to a different number of seconds. As with the healer, increase the number to send the signal less often and decrease it to send it more often.

That's all there is to the zombie. Zombies are not affected by humans or healers.

Human

Finally, we'll look at the human. To choose the human role, all you have to do is start up the board. The code defaults to the human role. When the role is successfully chosen, the LEDs will be green.

The human lights up solid green. The human starts with 10 health points, as indicated by 10 green LEDs. Humans are affected by both healers and zombies. The human waits to receive signals from the healer and zombie.

When the zombie signal is received, the human will lose a health point, and one green LED will shut off. This will continue every time a zombie signal is received.

If the human cannot find a healer, they'll continue to lose health points until they have 0. When this happens, the human will turn into a zombie. Zombies can't return to being human. At that point, they'll start transmitting the zombie signal which can affect any other humans still in the game.

When a healer signal is received, the human will regain one health point, and the last green LED will turn back on. As long as the human is receiving the healer signal, they will continue to regain health points, and the green LEDs will turn back on until the human is at full health.

Time To Play!

Now it's time to load the code on as many boards as you can find, start them up into the role you'd like to play, and start running! Good luck!

This guide was first published on Jul 31, 2018. It was last updated on Jul 31, 2018. This page (CircuitPython Zombies) was last updated on Apr 23, 2020.