Exemplo n.º 1
0
def main():
    pew.init()
    screen = pew.Pix()

    while True:
        screen.box(0, 0, 0)
        board = Board(screen)
        board.draw()

        while not pew.keys():
            pew.tick(0.25)

        while True:
            pew.tick(0.25)

            keys = pew.keys()
            if keys & pew.K_UP:
                board.move_paddle(board.left_paddle, Direction.UP)
            if keys & pew.K_DOWN:
                board.move_paddle(board.left_paddle, Direction.DOWN)
            if keys & pew.K_O:
                board.move_paddle(board.right_paddle, Direction.UP)
            if keys & pew.K_X:
                board.move_paddle(board.right_paddle, Direction.DOWN)

            try:
                board.refresh()
            except BallOut:
                break
Exemplo n.º 2
0
 def __init__(self):
     pew.init()
     self.screen = pew.Pix()
     self.cursorx, self.cursory = 4, 4
     self.counter = 0
     self.pressing = False
     self.main_loop()
Exemplo n.º 3
0
def start():
    pew.init()

    try:
        game()
        end_game(winner=True)
    except:
        end_game(winner=False)
def main():

    # -- animations ----

    def blink(row):
        while len(animations) > 1:
            yield
        while True:
            yield
            for x, y in row:
                screen.pixel(x, y + 2, 0)
            yield

    def drop(color, x, y):
        for i in range(1, y):
            screen.pixel(x, y, 0)
            screen.pixel(x, i, color)
            yield

    # -- initialization ----

    pew.init()
    screen = pew.Pix()
    board = pew.Pix(7, 6)
    cursor = 3
    opcursor = 3
    turn = 1
    prevk = 0b111111
    won = False
    animations = []

    with open('four-name', 'rb') as f:
        myname = f.read()
    lobbyprefix = b'fourinarow/lobby/'
    lobbytopic = lobbyprefix + myname
    joinprefix = b'fourinarow/join/'
    jointopic = joinprefix + myname
    client = mqtt.MQTTClient('', 'mqtt.kolleegium.ch')
    client.set_last_will(lobbytopic, b'', True)
    client.connect()
    try:

        # -- lobby initialization ----

        client.publish(lobbytopic, b'1', True)
        joined = None
        lobby = set()
        lobbylist = ['>exit']
        menu = menugen(screen, lobbylist)

        def onMessageLobby(topic, message):
            nonlocal joined, mycolor
            if topic.startswith(lobbyprefix):
                username = topic[len(lobbyprefix):]
                if message:
                    lobby.add(username)
                    screen.box(1, 0, 7, 8, 1)
                else:
                    lobby.discard(username)
                    screen.box(2, 0, 7, 8, 1)
                lobbylist[:-1] = [
                    str(n, 'ascii') for n in lobby if n != myname
                ]
            elif topic == jointopic:
                joined = message
                mycolor = 2

        client.set_callback(onMessageLobby)
        client.subscribe(lobbyprefix + b'+')
        client.subscribe(jointopic)

        # -- lobby loop ----

        for selected in menu:
            client.check_msg()
            if joined:
                break
            pew.show(screen)
            pew.tick(1 / 24)
        else:
            if selected < len(lobbylist) - 1:
                joined = bytes(lobbylist[selected], 'ascii')
                client.publish(joinprefix + joined, myname)
                mycolor = 1
        client.publish(lobbytopic, b'', True)
        screen.box(0)
        pew.show(screen)

        if not joined:
            return

        # -- game initialization ----

        mygameprefix = b'fourinarow/game/' + myname + b'/'
        mycursortopic = mygameprefix + b'cursor'
        mydroptopic = mygameprefix + b'drop'
        opgameprefix = b'fourinarow/game/' + joined + b'/'
        opcursortopic = opgameprefix + b'cursor'
        opdroptopic = opgameprefix + b'drop'

        def move(cursor):
            nonlocal won, turn
            y = 0
            while y < 6 and board.pixel(cursor, y) == 0:
                y += 1
            if y != 0:
                board.pixel(cursor, y - 1, turn)
                animations.append(drop(turn, cursor, y + 1))
                won = check(board)
                if won:
                    animations.append(blink(won))
                turn = 3 - turn

        def onMessageGame(topic, message):
            nonlocal opcursor
            if topic == opcursortopic and len(message) == 1:
                opcursor = message[0]
            elif topic == opdroptopic and len(
                    message) == 1 and turn == 3 - mycolor:
                move(message[0])

        client.set_callback(onMessageGame)
        client.subscribe(opgameprefix + b'#')
        client.publish(mycursortopic, bytes((cursor, )), True)

        # -- game loop ----

        while True:

            # -- input handling ----

            k = pew.keys()
            if not won:
                if k & pew.K_LEFT:
                    if cursor > 0:
                        cursor -= 1
                        client.publish(mycursortopic, bytes((cursor, )), True)
                if k & pew.K_RIGHT:
                    if cursor < 6:
                        cursor += 1
                        client.publish(mycursortopic, bytes((cursor, )), True)
                if k & ~prevk & (pew.K_DOWN | pew.K_O
                                 | pew.K_X) and turn == mycolor:
                    move(cursor)
                    client.publish(mydroptopic, bytes((cursor, )), False)
            else:
                if prevk == 0 and k != 0 and len(animations) == 1:
                    return
            prevk = k

            client.check_msg()

            # -- drawing ----

            screen.box(0, 0, 0, 8, 2)
            if not won:
                screen.pixel(cursor, 0, mycolor)
                screen.pixel(opcursor, 0,
                             3 if cursor == opcursor else 3 - mycolor)
                if turn == mycolor:
                    screen.pixel(7, 1, turn)
            screen.blit(board, 0, 2)
            for i in range(len(animations) - 1, -1, -1):
                try:
                    next(animations[i])
                except StopIteration:
                    del animations[i]
            pew.show(screen)
            pew.tick(0.15)

    finally:
        client.publish(lobbytopic, b'', True)
        client.disconnect()
Exemplo n.º 5
0
import struct
import usb_hid
import pew

pew.init()
screen = pew.Pix()
for gamepad in usb_hid.devices:
    if gamepad.usage_page == 0x01 and gamepad.usage == 0x05:
        break
else:
    raise RuntimeError("Gamepad HID device not found")
report = bytearray(6)

while True:
    buttons = pew.keys()
    report_buttons = 0

    if buttons & pew.K_O:
        screen.pixel(6, 3, 3)
        report_buttons |= 0x01
    else:
        screen.pixel(6, 3, 1)

    if buttons & pew.K_X:
        screen.pixel(6, 5, 3)
        report_buttons |= 0x02
    else:
        screen.pixel(6, 5, 1)

    if buttons & pew.K_UP:
        y = -127
Exemplo n.º 6
0
 def __init__(self):
     pew.init()
     self.screen = pew.Pix()
     self.main_loop()
Exemplo n.º 7
0
def main_loop(ins):
    """
    main loop of the program. Takes an instruction
    set and calls it iteratively to process the
    pushed buttons and update the display.
    Button 'O' exits the loop
    """
    # initialize PewPew console
    pew.init()

    # Load start screens
    for start_screen in start_screens:
        pew.show(pew.Pix.from_iter(start_screen))
        pew.tick(0.2)
    pew.show(pew.Pix.from_iter(blank_screen))
    pew.tick(0.5)

    # initialization stage
    pew.show(ins.get_current_screen())

    # flags used throughout the loop
    bool_loop = True
    old_keys = 0

    while bool_loop:
        keys = pew.keys()

        if keys != 0 and keys != old_keys:
            old_keys = keys

            # dispatch the pushed buttons
            if keys & pew.K_X:
                value = pew.K_X
            elif keys & pew.K_DOWN:
                value = pew.K_DOWN
            elif keys & pew.K_LEFT:
                value = pew.K_LEFT
            elif keys & pew.K_RIGHT:
                value = pew.K_RIGHT
            elif keys & pew.K_UP:
                value = pew.K_UP
            elif keys & pew.K_O:
                value = pew.K_O
                bool_loop = False
            else:
                value = 0

            ins.key_pressed(value)

        elif keys == 0:
            # this is necessary to be able to push
            # a button twice in a row
            old_keys = keys

        # update the screen and wait for 20ms
        pew.show(ins.get_current_screen())
        pew.tick(0.02)

    # the program has been terminated.
    # display the final sequence
    for final_screen in final_screens:
        pew.show(pew.Pix.from_iter(final_screen))
        pew.tick(0.2)
    pew.show(pew.Pix.from_iter(blank_screen))
    pew.tick(0.2)
import pew
import random
import pygame
import numpy as np

dis = pew.init()
screen = pew.Pix()

game_speed = 4
snake = [(2, 4)]
dx, dy = 1, 0
apple_x, apple_y = 6, 4
screen.pixel(apple_x, apple_y, 2)

#circuit graphics
pygame.font.init()
#add backgorund
ima = pygame.image.load('background.jpeg')
dis.blit(ima, (0, 320))
#gate backkgorund
ima2 = pygame.image.load('gateback.jpeg')
dis.blit(ima2, (0, 320))
#add gate, change from H to Z to Meas by key stroke
font1 = pygame.font.Font(None, 100)
text = font1.render('H', True, (0, 255, 0))
dis.blit(text, (10, 330))
gates = np.zeros(3, dtype=str)
gates[0] = 'H'
gates[1] = 'Z'
gates[2] = 'M'
g = 0
Exemplo n.º 9
0
# Simple moving dot from random start position
import pew
from random import randint

pew.init()  # intialise device
screen = pew.Pix()  # create empty screen image

x = randint(0, 7)  # random intial x position
y = randint(0, 7)  # random intial y position

dx = 1  # initial x step
dy = 1  # initial y step

while True:
    screen.pixel(x, y, 0)  #make previuos pixel not lit
    if x < 0 or x > 7:  #if next x position is off screen
        dx = -dx  #reverse its direction
    if y < 0 or y > 7:  #if next y position is off screen
        dy = -dy  #reverse its direction
    x += dx  #update x position
    y += dy  #update y position
    screen.pixel(x, y, 2)  #plot next pixel
    pew.show(screen)  #update screen
    pew.tick(1 / 12)  #pause approx 1/2 second
Exemplo n.º 10
0
def run():

    cos = [
        4, 4, 3, 3, 2, 1, 0, -1, -2, -3, -3, -4, -4, -4, -3, -3, -2, -1, 0, 1,
        2, 3, 3, 4
    ]
    sin = cos[18:] + cos[:18]

    with open('m3dlevel.bmp', 'rb') as f:
        f.seek(54)
        textures = f.read(64)
        textures = bytes(textures[4 * i] for i in range(16))
        level = f.read()

    def bresenham(ax, ay, bx, by):
        x = ax
        y = ay
        dx = bx - ax
        if dx < 0:
            dx = -dx
            ix = -1
        else:
            ix = 1
        dy = by - ay
        if dy < 0:
            dy = -dy
            iy = -1
        else:
            iy = 1
        dx <<= 1
        dy <<= 1
        sx = dx
        sy = dy
        if dx >= dy:
            sx >>= 1
            while x != bx:
                if sx < sy:
                    y += iy
                    sx += dx
                x += ix
                sy += dy
                yield x, y
        else:
            sy >>= 1
            while y != by:
                if sy < sx:
                    x += ix
                    sy += dy
                y += iy
                sx += dx
                yield x, y

    def lookup(x, y):
        b = level[(y << (_LOG_LEVEL_W - 1 - 2)) & (((1 << _LOG_LEVEL_H) - 1) <<
                                                   (_LOG_LEVEL_W - 1)) |
                  ((x >> 3) & ((1 << (_LOG_LEVEL_W - 1)) - 1))]
        return (b & 0xF) if (x & 4) else (b >> 4)

    pew.init()
    screen = pew.Pix()

    x = (1 << (_LOG_LEVEL_W + 1))
    y = (1 << (_LOG_LEVEL_H + 1))
    nx = None
    b = 6
    sb = sin[b]
    cb = cos[b]

    while pew.keys():
        pew.tick(0.1)
    while True:
        keys = pew.keys()
        if keys & pew.K_X:
            break
        if keys & pew.K_RIGHT:
            if keys & pew.K_O:
                nx = x + sb
                ny = y - cb
            else:
                b = (b + 23) % 24
                sb = sin[b]
                cb = cos[b]
                #print('xy:', x, y, 'b:', b)
        if keys & pew.K_LEFT:
            if keys & pew.K_O:
                nx = x - sb
                ny = y + cb
            else:
                b = (b + 1) % 24
                sb = sin[b]
                cb = cos[b]
                #print('xy:', x, y, 'b:', b)
        if keys & pew.K_UP:
            nx = x + cb
            ny = y + sb
        if keys & pew.K_DOWN:
            nx = x - cb
            ny = y - sb
        if nx is not None and lookup(nx, ny) == 0:
            x = nx
            y = ny
            nx = None
            #print('xy:', x, y, 'b:', b)

        rx = x + 16 * cb - 14 * sb
        ry = y + 16 * sb + 14 * cb
        for c in range(8):
            p = 0
            for bx, by in bresenham(x, y, rx, ry):
                p = lookup(bx, by)
                if p != 0:
                    bxy = max(abs(bx - x), abs(by - y))
                    rxy = max(abs(rx - x), abs(ry - y))
                    for r in range(8):
                        t = (7 * rxy - 8 * bxy * (5 - 2 * r)) // (4 * rxy) - 1
                        screen.pixel(
                            c, r, 0 if t < 0 else 73 - 8 * r if t >= 4 else
                            ((textures[p] >>
                              (t << 1)) & 3) + 4 * (bxy * 15 // rxy))
                    #if keys != 0:
                    #	print(rx, ',', ry, ':', bx, ',', by, '=', p, 'z', 64*bxy/rxy)
                    break
            else:
                for r in range(8):
                    screen.pixel(c, r, 0 if r < 3 else 73 - 8 * r)

            rx += 4 * sb
            ry -= 4 * cb

        pew.show(screen)
        pew.tick(0.06)
Exemplo n.º 11
0
                      (3, 0, 0, 0, 0, 0, 2, 3), (0, 3, 3, 3, 3, 3, 3, 0)),
                     ((0, 3, 3, 3, 3, 3, 3, 0), (3, 0, 0, 2, 1, 0, 0, 3),
                      (3, 0, 1, 2, 0, 1, 0, 3), (3, 1, 0, 0, 2, 0, 1, 3),
                      (3, 3, 0, 0, 2, 0, 3, 3), (3, 0, 3, 3, 3, 3, 0, 3),
                      (3, 0, 0, 0, 0, 2, 0, 3), (0, 3, 3, 3, 3, 3, 3, 0)),
                     ((0, 3, 3, 3, 3, 3, 3, 0), (3, 0, 0, 1, 2, 0, 0, 3),
                      (3, 0, 1, 0, 2, 1, 0, 3), (3, 1, 0, 2, 0, 0, 1, 3),
                      (3, 3, 0, 2, 0, 0, 3, 3), (3, 0, 3, 3, 3, 3, 0, 3),
                      (3, 0, 2, 0, 0, 0, 0, 3), (0, 3, 3, 3, 3, 3, 3, 0)),
                     ((0, 3, 3, 3, 3, 3, 3, 0), (3, 0, 0, 1, 1, 0, 2, 3),
                      (3, 0, 1, 0, 0, 2, 0, 3), (3, 1, 0, 0, 2, 0, 1, 3),
                      (3, 3, 0, 2, 0, 0, 3, 3), (3, 0, 3, 3, 3, 3, 0, 3),
                      (3, 2, 0, 0, 0, 0, 0, 3), (0, 3, 3, 3, 3, 3, 3, 0)))
    i = 0  # frame counter
    value = 0  # holds the selected level (0 = none)
    pew.init()  # initialize the PewPew console

    while not value:
        # check for pressed keys
        keys = pew.keys()
        if keys & pew.K_UP:
            value = 1
        elif keys & pew.K_RIGHT:
            value = 2
        elif keys & pew.K_DOWN:
            value = 3
        elif keys & pew.K_LEFT:
            value = 4

        # display the next frame of the animation
        animation = (0, 0, 1, 1, 2, 2, 3, 3, 2, 2, 1, 1)
Exemplo n.º 12
0
# 6 Sided die - graphic display = press X for next number

from random import randint  # import random integer function

import pew  # Import pewpew library

pew.init()  # Initlise library

screen = pew.Pix()  # Create blsnk screen

# Define pattern for 1 dot
score1 = pew.Pix.from_iter((
    (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, 0, 0, 1, 1, 0, 0, 0),
    (0, 0, 0, 1, 1, 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, 0, 0),
))

# Define pattern for 2 dots
score2 = pew.Pix.from_iter((
    (0, 0, 0, 0, 0, 0, 0, 0),
    (0, 1, 1, 0, 0, 0, 0, 0),
    (0, 1, 1, 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, 0, 1, 1, 0),
    (0, 0, 0, 0, 0, 1, 1, 0),
Exemplo n.º 13
0
import pew  # setting up tools for the pewpew
import random
from microqiskit import QuantumCircuit, simulate  # setting up tools for quantum

pew.init()  # initialize the game engine...
screen = pew.Pix()  # ...and the screen

qc = QuantumCircuit(
    2, 2)  # create an empty circuit with two qubits and two output bits
qc.h(0)
qc.cx(0, 1)

# create circuits with the required measurements, so we can add them in easily
meas = QuantumCircuit(2, 2)
meas.measure(0, 0)
meas.measure(1, 1)

# loop over the squares centered on (1,2), (6,2) (1,4) and (6,4) and make all dim
for (X, Y) in [(1, 4), (6, 4)]:
    for dX in [+1, 0, -1]:
        for dY in [+1, 0, -1]:
            screen.pixel(X + dX, Y + dY, 2)
pew.show(screen)

for (X, Y) in [(1, 4), (6, 4)]:
    screen.pixel(X, Y, 0)  # turn off the center pixels of the squares

old_keys = 0
while True:  # loop which checks for user input and responds

    # look for and act upon key presses
def main():

    # -- animations ----

    # these functions need access to `screen`, which is a local variable of the main() function, so they must be defined locally as well (or, alternatively, they could take it passed as an argument)

    # Generator function that takes a winning row in the format returned by
    # check() and makes it blink by alternatingly doing nothing (leaving the
    # pixels in their original color) and overwriting them with black.
    def blink(row):
        # as long as a drop animation is still running, do nothing
        while len(animations) > 1:
            yield
        # infinite loop, the blinking does not end by itself
        while True:
            # odd iterations: do nothing -> colored pixels
            yield
            # even iterations: black pixels
            for x, y in row:
                # x, y are in board coordinates, add 2 to convert to screen coordinates
                screen.pixel(x, y + 2, 0)
            yield

    # Generator function that takes the color and final position of a piece and
    # animates it dropping from the top down to that position.
    # Drawn over a board where the piece is already in the final position, so
    # - the final pixel must be erased
    # - the animation ends one before the final position
    def drop(color, x, y):
        # start at 1 (because the cursor was already at 0) and run up to and excluding y
        for i in range(1, y):
            # erase final position with black
            screen.pixel(x, y, 0)
            # draw at current position
            screen.pixel(x, i, color)
            yield

    # -- initialization ----

    pew.init()
    # the framebuffer
    screen = pew.Pix()
    # the board
    board = pew.Pix(7, 6)
    # x coordinate of my cursor
    cursor = 3
    # x coordinate of opponent's cursor
    opcursor = 3
    # color value of whose turn it is (1=green, 2=red)
    turn = 1
    # keys pressed in the previous game loop iteration (pew.keys() value),
    # initialized to "all keys pressed" in binary so that there cannot be a
    # rising edge in the first iteration
    prevk = 0b111111
    # whether the game has ended, expressed by the return value of check():
    # either False or the winning row, which counts as "true" becaues it is a
    # non-empty sequence
    won = False
    # a list of generators that implement any currently running animations
    animations = []

    # read the player name from the configuration file
    # open for reading ('r'), in binary mode ('b') because we want the name as bytes, not as a string
    with open('four-name', 'rb') as f:
        myname = f.read()
    # various common parts of MQTT topics as bytes
    lobbyprefix = b'fourinarow/lobby/'
    lobbytopic = lobbyprefix + myname
    joinprefix = b'fourinarow/join/'
    jointopic = joinprefix + myname
    # set up the MQTT client object
    client = mqtt.MQTTClient('', 'mqtt.kolleegium.ch')
    # last will is the "leaving the lobby" message
    client.set_last_will(lobbytopic, b'', True)
    client.connect()
    # Whatever happens from now on, whether we exit by an error or by a
    # deliberate return, we want to close the connection in the end. Use a
    # try-finally statement for that.
    try:

        # -- lobby initialization ----

        # I am present
        client.publish(lobbytopic, b'1', True)
        # the name of the player who joined us or whom we joined, currently nobody
        joined = None
        # all the player names in the lobby, as a set so we can easily add and remove them by value without getting duplicates
        lobby = set()
        # the lobby as a list for the menu, which can't directly take a set, and with the additional "exit" entry at the end
        lobbylist = ['>exit']
        # create the menu generator, it keeps a reference to the list and will automatically pick up changes to it
        menu = menugen(screen, lobbylist)

        # callback for handling incoming MQTT messages while we're in the lobby
        def onMessageLobby(topic, message):
            # declare variables from the outer context that we want to assign to, otherwise assignment would create them as local variables of this function
            # access to other outer variables such as lobby or screen is read-only and needs no declaration
            nonlocal joined, mycolor
            # messages about players arriving and leaving
            if topic.startswith(lobbyprefix):
                username = topic[len(lobbyprefix):]
                # message is b'', which counts as false, or non-empty (expected b'1'), which counts as true
                if message:
                    lobby.add(username)
                    # flash a green bar at the bottom to indicate arrival
                    # this works because onMessageLobby is called from client.check_msg(), which occurs after drawing the menu (in `for selected in menu`) but before `pew.show(screen)`
                    screen.box(1, 0, 7, 8, 1)
                else:
                    # use discard(), not remove() to avoid an exception if the name is not there
                    # (it should, but we have no control over what messages others send us)
                    lobby.discard(username)
                    # red bar at the bottom to indicate departure
                    screen.box(2, 0, 7, 8, 1)
                # update the list form of the lobby by
                # - transforming the elements of the set form using a list comprehension (they are bytes but the menu wants strings)
                # - inserting them using a slice index that replaces everything but the ">exit" item at the end
                # it's important that we modify this list in place, not create a totally new list, because this list is the one the menu generator has a reference to
                lobbylist[:-1] = [
                    str(n, 'ascii') for n in lobby if n != myname
                ]
            # messages about someone joining us
            elif topic == jointopic:
                # message content is the name of the other player
                joined = message
                # the joined player (us) gets red
                mycolor = 2

        client.set_callback(onMessageLobby)
        # subscribe to all topics 1 level deep in the lobby (= user names)
        client.subscribe(lobbyprefix + b'+')
        client.subscribe(jointopic)

        # -- lobby loop ----

        # repeatedly poke the menu generator, which draws the menu and handles buttons, until the user selects an entry - conveniently done by a for loop
        # assigns the returned index to `selected` every time - we don't need it during the loop, we only need the value from the last iteration afterwards
        for selected in menu:
            # while in the menu, we also repeatedly need to check for incoming MQTT messages - this calls onMessageLobby if there is any, which may set joined
            client.check_msg()
            if joined:
                # leave the for loop
                break
            pew.show(screen)
            # this is the frame rate expected by the menu for an appropriate animation speed
            pew.tick(1 / 24)
        # we can leave the loop above in two ways:
        # 1. when the menu generator ends, which is when the local user has selected an emtry from the menu
        # 2. by the `break` statement when someone else has sent us a join message
        # the `else` block of a `for` statement is executed in case 1 but not in case 2
        else:
            # if selected == len(lobbylist) - 1, the user selected ">exit", otherwise another player
            if selected < len(lobbylist) - 1:
                # selected someone to join, look up who by their index, convert from string to bytes, and send them a join message
                joined = bytes(lobbylist[selected], 'ascii')
                client.publish(joinprefix + joined, myname)
                # the joining player gets green
                mycolor = 1
        # in any case (whether we joined someone, were joined, or are exiting), we now leave the lobby
        client.publish(lobbytopic, b'', True)
        # clear the menu from the screen
        screen.box(0)
        pew.show(screen)

        # if the user chose ">exit", we're done, return from the main() function
        if not joined:
            # (the `finally` block at the end will still be executed because we're jumping out from inside the `try` block)
            return

        # -- game initialization ----

        # more MQTT topics
        mygameprefix = b'fourinarow/game/' + myname + b'/'
        mycursortopic = mygameprefix + b'cursor'
        mydroptopic = mygameprefix + b'drop'
        opgameprefix = b'fourinarow/game/' + joined + b'/'
        opcursortopic = opgameprefix + b'cursor'
        opdroptopic = opgameprefix + b'drop'

        # Execute a move by dropping a piece of whose turn it is at the given column.
        def move(cursor):
            nonlocal won, turn
            # determine the topmost occupied (or beyond-the-bottom) place in the column by iterating from the top
            y = 0
            while y < 6 and board.pixel(cursor, y) == 0:
                y += 1
            # now either y == 6 (all were free) or place y was occupied, in both cases y-1 is the desired free place
            # unless the whole column was full (y == 0)
            if y != 0:
                # place the piece in the final position
                board.pixel(cursor, y - 1, turn)
                # start the drop animation - doesn't draw anything yet, just sets up the generator that will draw when poked
                animations.append(drop(turn, cursor, y + 1))
                # check for winning rows
                won = check(board)
                # won is either False or a non-empty sequence that counts as true
                if won:
                    # start the blink animation
                    animations.append(blink(won))
                # reverse the turn: 1 -> 2, 2 -> 1
                turn = 3 - turn

        # callback for handling incoming MQTT messages while we're in the game
        def onMessageGame(topic, message):
            nonlocal opcursor
            # input validation: check length, otherwise if someone sends us an empty message we crash with an IndexError on the following line
            if topic == opcursortopic and len(message) == 1:
                opcursor = message[0]
            # input validation: the opponent is only allowed to make a move when it is their turn
            elif topic == opdroptopic and len(
                    message) == 1 and turn == 3 - mycolor:
                # opponent's move
                move(message[0])

        client.set_callback(onMessageGame)
        # subscribe to all of the opponent's game topics (cursor and drop)
        client.subscribe(opgameprefix + b'#')
        # initial update so the opponent knows where my cursor is from the start
        # convert number to one-element bytes by packing it into an intermediate tuple (needs trailing comma to distinguish from grouping parentheses)
        client.publish(mycursortopic, bytes((cursor, )), True)

        # -- game loop ----

        while True:

            # -- input handling ----

            k = pew.keys()
            # key handling is different depending on whether the game is running or over
            if not won:
                # check for bits in k using the bitwise AND operator - the result is zero or nonzero, which count as false or true
                if k & pew.K_LEFT:
                    # move cursor left if possible and publish the new position
                    if cursor > 0:
                        cursor -= 1
                        client.publish(mycursortopic, bytes((cursor, )), True)
                if k & pew.K_RIGHT:
                    # move cursor right if possible and publish the new position
                    if cursor < 6:
                        cursor += 1
                        client.publish(mycursortopic, bytes((cursor, )), True)
                # drop only if the respective key was not pressed in the last iteration, otherwise we would repeatedly drop while the key is held down (edge detection)
                if k & ~prevk & (pew.K_DOWN | pew.K_O
                                 | pew.K_X) and turn == mycolor:
                    # my move
                    move(cursor)
                    client.publish(mydroptopic, bytes((cursor, )), False)
            else:
                # when the game is over, exit on a key press - several conditions to check:
                # - the first time we're getting here, the key that dropped the final piece may still be pressed - do nothing until all keys have been up in the previous iteration
                # - do nothing until the drop animation has completed and only the blink animation (which is endless) remains
                if prevk == 0 and k != 0 and len(animations) == 1:
                    return
            # save the pressed keys for the next iteration to detect edges
            prevk = k

            # check for incoming messages, which may execute an opponent's move via onMessageGame
            client.check_msg()

            # -- drawing ----

            # clear previous cursor, dropping piece, and turn indicator
            screen.box(0, 0, 0, 8, 2)
            if not won:
                # draw two cursors - if they overlap, in orange, otherwise in their respective color
                screen.pixel(cursor, 0, mycolor)
                screen.pixel(opcursor, 0,
                             3 if cursor == opcursor else 3 - mycolor)
                # turn indicator
                if turn == mycolor:
                    screen.pixel(7, 1, turn)
            # draw the board (in unanimated state)
            screen.blit(board, 0, 2)
            # poke all active animations to draw one iteration each
            # we'll be removing items from the list while iterating over it, so we need to do it backwards to avoid skipping items
            # start at the last position (len-1), continue to 0 inclusively which is -1 exclusively, in steps of -1
            for i in range(len(animations) - 1, -1, -1):
                try:
                    # next() pokes, it raises StopIteration when the generator is exhausted (animation is over)
                    next(animations[i])
                except StopIteration:
                    # remove completed animations
                    del animations[i]
            # done drawing into the framebuffer, send it to the display
            pew.show(screen)
            # wait until it's time for the next frame
            # 0.15 seconds is an appropriate frame time for our animations and key repeating - increase it if you still find it hard to move the cursor by exactly one pixel
            # drawing at a faster rate would require more complex key repeat handling
            pew.tick(0.15)

        # end of game loop

    finally:
        # however we exited from the try block, by an error or by a deliberate return, leave the lobby and close the connection
        client.publish(lobbytopic, b'', True)
        client.disconnect()