1. Open Day

1.1. guess_the_number_AB

from microbit import *
import random


def get_secret(min_num=1, max_num=9):
    return random.randint(min_num, max_num)


def select_number(start_num, min_num=1, max_num=9):
    counter = start_num
    display.show(counter, delay=200)

    # Wait until the logo is pressed to confirm
    while pin_logo.is_touched() is False:

        # A decreases, stops at 1
        if button_a.is_pressed():
            if counter > min_num:
                counter -= 1
            display.show(counter, delay=200)
            sleep(200)

        # B increases, stops at 9
        if button_b.is_pressed():
            if counter < max_num:
                counter += 1
            display.show(counter, delay=200)
            sleep(200)

        sleep(100)

    return counter



def check_guess(secret_num, guess):
    if guess == secret_num:
        display.show(Image.YES)
        sleep(400)
        return True
    elif guess > secret_num:
        display.show(Image.ARROW_S)
        sleep(400)
        display.show(guess)
        return False
    else:
        display.show(Image.ARROW_N)
        sleep(400)
        display.show(guess)
        return False


secret_num = get_secret()
guess = 5
game_guesses = 0
guessed = False
display.scroll("1-9?")


while True:
    guess = select_number(guess, min_num=1, max_num=9)
    game_guesses += 1
    guessed = check_guess(secret_num, guess)

    if guessed:
        display.scroll(str(game_guesses) + " GUESSES", delay=80)
        # new game
        secret_num = get_secret()
        guess = 5
        game_guesses = 0
        guessed = False

1.2. random_pet

# pet store


from microbit import *
import random

# Built-in animals
animal_icons = {
    "Cow": Image.COW,
    "Duck": Image.DUCK,
    "Giraffe": Image.GIRAFFE,
    "Rabbit": Image.RABBIT,
    "Snake": Image.SNAKE,
}

animals = list(animal_icons.keys())

pet_names = {
    "Cow": ["Daisy", "MooMoo",],
    "Duck": ["Quacker", "Daffy", "Waddles",],
    "Giraffe": ["Stretch", "Lofty"],
    "Rabbit": ["Hoppy", "Bunny", "Bugsy"],
    "Snake": ["Slither", "Fang", "Slinky"],
}

current_animal = None

while True:
    if button_a.was_pressed():
        current_animal = random.choice(animals)
        display.show(animal_icons[current_animal])

    if button_b.was_pressed():
        if current_animal:
            name = random.choice(pet_names[current_animal])
            display.scroll(name + " the " + current_animal)
        else:
            display.scroll("Pick animal first")

    sleep(100)

1.3. maze

from microbit import *

# FINAL MAZES (converted to 0/1)
MAZES = [
    # 7x7
    [
        "1111111",
        "0000001",
        "1011101",
        "1000101",
        "1110101",
        "1000000",
        "1111111"
    ],
    # 9x9
    [
        "111111111",
        "000000001",
        "101111101",
        "100001001",
        "111101101",
        "100001001",
        "101101101",
        "100000000",
        "111111111"
    ],
    # 11x11
    [
        "11111111111",
        "00000000001",
        "10111111001",
        "10100001001",
        "10101101001",
        "10101001001",
        "10101001101",
        "10100000001",
        "10111111001",
        "10000000000",
        "11111111111"
    ]
]

# Start openings (in wall)
START_OPENINGS = [(0,1), (0,1), (0,1)]
# Player starts inside the maze
STARTS = [(1,1), (1,1), (1,1)]
# Exit openings (in wall)
EXIT_OPENINGS = [(6,5), (8,7), (9,9)]
# Exit tiles inside the maze
EXITS = [(5,5), (7,7), (8,9)]


def draw_window(maze, px, py):
    img = Image(5,5)
    for dy in range(5):
        for dx in range(5):
            mx = px + dx - 2
            my = py + dy - 2
            if 0 <= mx < len(maze[0]) and 0 <= my < len(maze):
                if maze[my][mx] == "1":
                    img.set_pixel(dx, dy, 5)
    img.set_pixel(2,2,9)
    return img


def play_maze(maze, start, start_opening, exit_tile):
    x, y = start
    sx, sy = start_opening
    ex, ey = exit_tile

    # Close the start opening behind the player
    maze[sy] = maze[sy][:sx] + "1" + maze[sy][sx+1:]

    speed = 120  # starting speed (ms)

    while True:
        display.show(draw_window(maze, x, y))

        dx = dy = 0

        # SPEED CONTROL
        if button_a.was_pressed():
            speed = min(300, speed + 20)  # slow down
        if button_b.was_pressed():
            speed = max(40, speed - 20)   # speed up

        # MOVEMENT (tilt)
        if accelerometer.get_x() < -200:
            dx = -1
        elif accelerometer.get_x() > 200:
            dx = 1
        elif accelerometer.get_y() < -200:
            dy = -1
        elif accelerometer.get_y() > 200:
            dy = 1

        # APPLY MOVEMENT
        nx = x + dx
        ny = y + dy
        if maze[ny][nx] == "0":
            x, y = nx, ny

        # EXIT CHECK
        if (x, y) == (ex, ey):
            return

        sleep(speed)


def main():
    while True:
        display.show(Image.HAPPY)
        if button_a.was_pressed():
            for i in range(len(MAZES)):
                maze = MAZES[i].copy()
                play_maze(maze, STARTS[i], START_OPENINGS[i], EXITS[i])
                display.show(Image.YES)
                sleep(1000)
            display.scroll("WIN!")
        sleep(100)

main()

1.4. space_invaders

from microbit import *
import random


MIN_COORD, MAX_COORD = 0, 4  # Range of valid coordinates for the display.
MAX_MISSILES = 5  # Number of missiles player can have on screen at once.
DIFFICULTY_INCREASE = 0.25  # Increase in difficulty between waves.

ALIEN_START_POSITIONS = [
    # Each row is a unique start pattern, defined as tuples of x,y coordinates.
    [(1, 0), (2, 0), (3, 0), (1, 1), (2, 1), (3, 1)],
    [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (2, 1)],
    [(1, 0), (2, 0), (3, 0), (0, 1), (2, 1), (4, 1)],
    [(1, 0), (2, 0), (3, 0), (1, 1), (3, 1), (2, 2)],
]


def wait_for_button():
    """ Wait for either button to be pressed. """
    while not (button_a.was_pressed() or button_b.was_pressed()):
        sleep(1)


def move(sprite, x, y):
    """ Move the given sprite by the given x & y amounts. """
    return sprite[0] + x, sprite[1] + y


def in_bounds(pos):
    """ Return True if the position is within the valid screen coordinates. """
    if pos[0] < MIN_COORD or pos[0] > MAX_COORD:
        return False
    if pos[1] < MIN_COORD or pos[1] > MAX_COORD:
        return False
    return True


class Game:
    """ Game class holds the current game state. """

    def game_reset(self):
        # Initial values
        self.x = 2  # Player x coordinate start (middle).
        self.xf = 2.0  # x coordinate float, allows us to use tilt for move speed.

        self.missiles = []  # Active missiles on screen.
        self.aliens = []  # Active aliens on screen.
        self.alien_velocity_x = 1  # Horizontal speed of aliens.

        self.bombs = 3  # Number of bombs the player has.
        self.active_bomb = 0  # Countdown timer for the current active bomb.

        self.score = 0  # Player score.

        self.tick = 0  # Game loop tick.
        self.level = 0  # Current game level.
        self.difficulty = 20  # Is in reverse, decrement to increase.

    def handle_input(self):
        self.tick += 1
        acc_x = accelerometer.get_x()

        # Use the accelerometer / 512 so the player can move x at speed by tilting more.
        if acc_x < 0:
            self.xf += acc_x / 512
        if acc_x > 0:
            self.xf += acc_x / 512

        # Constrain to the screen dimensions.
        if self.xf > MAX_COORD:
            self.xf = MAX_COORD

        if self.xf < MIN_COORD:
            self.xf = MIN_COORD

        self.x = int(self.xf)

        if button_a.was_pressed():
            # Add missile, at players current x position.
            self.missiles.append((self.x, 4))

        if button_b.was_pressed() and self.bombs:
            # Fire bomb. Flash + remove half the aliens.
            # randint(0,1) will be 50% 1, 50% 0 ..if 0 (False) alien will be skipped.
            self.aliens = [alien for alien in self.aliens if random.randint(0, 1)]
            self.active_bomb = 3  # Reduces 1 per tick. Screen at 3 * bright.
            self.bombs -= 1

    def add_aliens(self):
        # We need to copy, or we'll me modifying the original lists.
        alien_position = self.level % len(ALIEN_START_POSITIONS)
        self.aliens = ALIEN_START_POSITIONS[alien_position].copy()
        self.tick = 0

    def advance_aliens(self):
        """ If aliens have reached the screen edge, advance them all downwards. """
        for alien in self.aliens:
            if (self.alien_velocity_x == -1 and alien[0] == MIN_COORD) or (
                self.alien_velocity_x == +1 and alien[0] == MAX_COORD
            ):
                # If any aliens are at the far edge, increment y, and reverse.
                self.alien_velocity_x = -self.alien_velocity_x
                self.aliens = [move(alien, 0, 1) for alien in self.aliens]
                # This can happen if detached alien slips past bottom.
                self.aliens = [alien for alien in self.aliens if in_bounds(alien)]
                return True  # No other move this time.

    def aliens_can_move(self):
        if self.tick > self.difficulty:
            self.tick = 0
            return True

    def move_aliens(self):
        # Move aliens horizontally.
        self.aliens = [move(alien, self.alien_velocity_x, 0) for alien in self.aliens]

    def move_missiles(self):
        # Advance positions of missiles (upwards)
        self.missiles = [move(missile, 0, -1) for missile in self.missiles]
        self.missiles = [missile for missile in self.missiles if in_bounds(missile)]

    def check_collisions(self):
        for missile in self.missiles[:]:  # Iterate a copy.
            if missile in self.aliens:
                # Since we store by coordinates, we can remove using the missile coords.
                self.aliens.remove(missile)
                self.missiles.remove(missile)
                self.score += 1

        if not self.aliens:
            # Wave complete? Increase difficulty (decrement) and add new aliens.
            self.difficulty -= DIFFICULTY_INCREASE
            self.level += 1
            self.bombs += 1
            self.add_aliens()

    def draw(self):
        display.clear()

        if self.active_bomb:
            # Bomb is drawn as an overlay of gradually decaying light.
            for dx in range(MAX_COORD + 1):
                for dy in range(MAX_COORD + 1):
                    display.set_pixel(dx, dy, self.active_bomb * 3)

            # Decrement so next draw is fainter.
            self.active_bomb -= 1

        # Draw all the aliens.
        for pos in self.aliens:
            display.set_pixel(pos[0], pos[1], 9)

        # Draw all the current player missiles.
        for pos in self.missiles:
            display.set_pixel(pos[0], pos[1], 5)

        # Draw the players spaceship.
        display.set_pixel(self.x, 4, 9)

    def game_over(self):
        return (self.x, 4) in self.aliens


game = Game()  # Create our game object.


while True:

    display.show(Image.TARGET)
    wait_for_button()

    game.game_reset()  # Reset the game state.
    game.add_aliens()

    # Main loop
    while not game.game_over():
        game.handle_input()
        if game.aliens_can_move():
            if not game.advance_aliens():
                game.move_aliens()
        game.move_missiles()
        game.draw()
        game.check_collisions()

        sleep(100)

    display.show(Image.ANGRY)
    sleep(1000)
    display.scroll(game.score)

1.5. vertical_scroller

from microbit import *
import random


def draw(track, t, x, y):
    img = Image(track)
    img.set_pixel(x, y, t * 4 + 1)
    return img


def play_game():
    # lines of track
    track_bits = ["50005:", "53005:", "50305:", "50035:"]
    # starting track
    track = "50005:50005:50005:50005:50005:"
    # co-ordinates of player character
    x = 2
    y = 4
    # time variable for blinking
    tick = 0
    score = 0
    # track pace
    sleep_delay = 150
    last = 0
    alive = True
    while alive == True:
        tt = draw(track, tick, x, y)
        if button_a.was_pressed() and x != 1:
            x = x - 1
        elif button_b.was_pressed() and x != 3:
            x = x + 1
        # update ticks
        tick += 1
        # show track
        display.show(tt)
        # every third tick
        if tick == 3:
            # update score
            score += 1
            # check for collision
            if tt.get_pixel(x, y - 1) != 0:
                alive = False
            # reset ticks
            tick = 0
            # delete bottom row of track
            track = track[:-6]
            # update track
            if last == 0:
                last = random.randint(0, len(track_bits) - 1)
            else:
                last = 0
            track = track_bits[last] + track
            # redraw screen
            tt = draw(track, tick, x, y)
            display.show(tt)
        sleep(sleep_delay)
    display.scroll(score, delay=80)


def main():
    while True:
        if button_a.was_pressed():
            play_game()
            sleep(3000)
        sleep(50)


main()

1.6. radio_hotcold

from microbit import *
import radio

radio.config(group=7)
radio.config(power=2)
radio.on()

radio_on = False  # Start with radio OFF

while True:

    # A turns radio ON
    if button_a.was_pressed():
        radio_on = True
        display.show(Image.HEART)
        sleep(300)
        display.show(Image.HEART_SMALL)
        sleep(300)

    # B turns radio OFF
    if button_b.was_pressed():
        radio_on = False
        display.clear()

    # Only send if radio is ON
    if radio_on:
        radio.send("7")
        # Pulse heart to show "ON" status
        display.show(Image.HEART)
        sleep(300)
        display.show(Image.HEART_SMALL)
        sleep(300)
    else:
        sleep(1000)
from microbit import *
import radio

# Radio setup
radio.config(group=7)
radio.on()

# Smoothing + timeout
smooth_rssi = -95
last_packet_time = running_time()


def display_level(level):
    display.clear()

    # Clamp level between 0 and 5
    if level < 0:
        level = 0
    if level > 5:
        level = 5

    # Level 0 = no bars
    if level == 0:
        return

    # Light rows from bottom upward
    for i in range(level):
        row = 4 - i
        for x in range(5):
            display.set_pixel(x, row, 9)


while True:
    packet = radio.receive_full()
    now = running_time()

    if packet:
        msg, rssi, timestamp = packet
        last_packet_time = now

        # Smooth RSSI
        smooth_rssi = (smooth_rssi * 0.7) + (rssi * 0.3)

        # Map RSSI so:
        # -75 = 0 bars (≈1 meter)
        # -55 = 5 bars (≈2 cm)
        level = scale(int(smooth_rssi), from_=(-75, -55), to=(0, 5))

        # Hard cutoff for weak signals
        if smooth_rssi < -75:
            level = 0

        display_level(level)

    else:
        # No packets for 0.5 seconds → drop to zero
        if now - last_packet_time > 500:
            display_level(0)

    sleep(100)

1.7. radio_draw

from microbit import *
import radio

# Choose own group in pairs 0-255
radio.config(group=6)
# Turn on the radio
radio.on()

x = 2
y = 2
tick = -1

grid = [
    [0,0,0,0,0],
    [0,0,0,0,0],
    [0,0,0,0,0],
    [0,0,0,0,0],
    [0,0,0,0,0]
    ]


def Toggle(tx, ty):
    # bitwise or swaps 0 for 1 and 1 for 0
    grid[tx][ty] ^= 1


def Draw(t):
    img = Image('00000:'*5)
    for cy in range(0,5):
        for cx in range(0,5):
            img.set_pixel(cx, cy, grid[cx][cy]*5)
    img.set_pixel(x, y, (t % 2)*9)
    return img


def ImageString():
    s = ""
    for cy in range(0,5):
        for cx in range(0,5):
            s += str(grid[cx][cy]*9)
        s += ":"
    return s


while True:
    tick += 1
    if tick == 2:
        tick = 0
    # check for movement
    dx = accelerometer.get_x()
    dy = accelerometer.get_y()
    if dx > 300:
        x += 1
        sleep(200)
    if dx < -300:
        x -= 1
        sleep(200)
    if dy > 300:
        y += 1
        sleep(200)
    if dy < -300:
        y -= 1
        sleep(200)
    # keep on grid
    x = max(0, min(x, 4))
    y = max(0, min(y, 4))
    # check for button press
    if button_a.was_pressed():
        Toggle(x, y)
        sleep(200)
    # update screen
    i = Draw(tick)
    display.show(i)
    if button_b.was_pressed():
        radio.send(ImageString())
    sleep(50)
from microbit import *
import radio


# Choose own group in pairs 0-255
radio.config(group=6)
# Turn on the radio
radio.on()

while True:
    incoming_message = radio.receive()
    if incoming_message:
        # print(incoming_message)
        i = Image(incoming_message)
        display.show(i)