def refresh_screen(scr, cline, cp, offset, blink=False): global curcol curcol = 1 if curcol == 3 else 3 if not scr: scr = pew.Pix() if blink: scr.pixel(0, cp, curcol) else: dt = (cline + " ")[offset:offset + 8] for i in range(8): scr.pixel(0, i, curcol if cp == i else 1) try: b = MORSE.get(dt[i], 0) except IndexError: b = MORSE.get(" ") for j, v in enumerate(to_beep(b)): if v > 0: v += 1 scr.pixel(STARTPOS + j, i, v) if offset > 0: scr.pixel(7, 0, curcol) else: scr.pixel(7, 0, 0) if len(cline) - 8 > offset: scr.pixel(7, 7, curcol) else: scr.pixel(7, 7, 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
def __init__(self): pew.init() self.screen = pew.Pix() self.cursorx, self.cursory = 4, 4 self.counter = 0 self.pressing = False self.main_loop()
def run(self): screen = pew.Pix() apple = Apple(self._snake) apple.paint(screen) while True: self.paint(screen) pew.show(screen) pew.tick(1 / self._game_speed) self._keyboard.update(pew.keys()) x = (self._snake[-1][0] + self._x_delta) % 8 y = (self._snake[-1][1] + self._y_delta) % 8 if (x, y) in self._snake: return False if len(self._snake) >= self._win_length: return True self._snake.append((x, y)) if x == apple.x_location and y == apple.y_location: apple.rub_out(screen) apple = Apple(self._snake) apple.paint(screen) self._game_speed += 0.2 else: self.rub_out(screen, 0)
def board_to_pix(board): """ Returns board converted to pew.Pix. """ pix = pew.Pix() for x, y in board: pix.pixel(x, y, 3) return pix
def print(string): _screen = pew.Pix() _text = pew.Pix.from_text(string, 3, 0) for dx in range(-8, _text.width): _screen.blit(_text, -dx, 1) pew.show(_screen) pew.tick(1 / 8)
def __init__(self): self.screen = pew.Pix() # Game stats self.player = (__PLAYER_STARTING_X, __PLAYER_STARTING_Y) self.old_player = (__PLAYER_STARTING_X, __PLAYER_STARTING_Y) self.reset_game_logic() self.game_speed = __STARTING_SPEED self.speed_factor = __DEFAULT_SPEED_FACTOR # Visuals self.title = pew.Pix.from_text("AcidRain") self.game_over = pew.Pix.from_text("Game Over!")
def update(values, previous): output = pew.Pix() for (x_off, y_off), (shift, rot) in zip(OFFSETS, values): for y in range(4): for x in range(4): a, b = x, y for i in range(rot): a, b = 3 - b, a b = (b + shift) % 4 output.pixel(a + x_off, b + y_off, previous.pixel(x + x_off, y + y_off)) return output
def hint(lt): if lt: b = MORSE.get(lt, 0) if b: scr = pew.Pix() scr.blit(pew.Pix.from_text(lt, color=3), 2, 0) for j, v in enumerate(to_beep(b)): if v > 1: v = 3 scr.pixel(STARTPOS + j, 7, v) pew.show(scr) pew.tick(1)
def make_image(state): blocks = [] for num in state: blocks.append(make_block(num)) image = pew.Pix() for i in range(2): for j in range(4): tmp = blocks[2 * i][j] + blocks[2 * i + 1][j] for x in range(8): image.pixel(x, 4 * i + j, tmp[x]) return image
def play(): sc = pew.Pix() keys = pew.keys() player_x = 1 p = Player(x=player_x) o = Obstacle() score = 0 player_collided = False # Game loop while not keys & pew.K_X and not player_collided: # Parse events p.parse_keys(keys) if o.get_pos() == player_x: if p.collides(o.get_collisions()): player_collided = True elif o.get_pos() == player_x - 1: score += 1 p.fall() score_array = get_score_array(score)[::-1] for i in range(len(score_array)): sc.pixel(7 - i, 0, 3 * score_array[i]) p.blit(sc) o.blit(sc) pew.show(sc) pew.tick(1 / 2) # Clear last player position p.blit(sc, clear=True) o.blit(sc, clear=True) o.move() p.clear_jump() keys = pew.keys() if p.collides(o.get_collisions()): # Display score print(f"Game ended with a score of {score}!!") else: print("Quit game.")
def show_text(t): if not t.strip(): return scr = pew.Pix() rpt = False text = pew.Pix.from_text(t, color=3) while True: for dx in range(-8, text.width): scr.blit(text, -dx, 1) pew.show(scr) keys = pew.keys() if keys: if keys & pew.K_X: return if keys & pew.K_O: pew.tick(1 / 24) rpt = True else: pew.tick(1 / 6) if not rpt: return
def loop(): screen = pew.Pix() # Run until user input while not pew.keys(): current_time = time.localtime() h = current_time[3] % 12 m = current_time[4] // 5 s = current_time[5] // 5 # Paint pixels enable_hand(screen, h, 1) enable_hand(screen, m, 2) enable_hand(screen, s, 3) # Update display pew.show(screen) # Wait until next iteration pew.tick(1 / 2) # Clear pixels disable_hand(screen, h) disable_hand(screen, m) disable_hand(screen, s)
def main(): screen = pew.Pix() r1 = -1 r2 = -1 stop = False stopped = 0 blend = bytearray(16) t1r = t1g = t2r = t2g = 0 while stopped != 3: stop = stop or pew.keys() if r1 < 10: if stop: stopped |= 1 cx1 = 500 r1 = 10 dr1 = 20 else: cx1 = random.getrandbits(7) cy1 = random.getrandbits(7) dx1 = random.getrandbits(4) - 8 dy1 = random.getrandbits(4) - 11 r1 = 10 dr1 = random.getrandbits(4) + 5 t1r = random.getrandbits(8) % 23 t1g = 7 if t1r >= 16: t1g = 22 - t1r t1r = 15 for a1 in range(4): for a2 in range(4): blend[(a1 << 2) | a2] = 0x80 | (( (3 * a1 * t1r + (3 - a1) * a2 * t2r + 4) // 9) << 3) | ( (3 * a1 * t1g + (3 - a1) * a2 * t2g + 4) // 9) if r2 < 10: if stop: stopped |= 2 cx2 = 500 r2 = 10 dr2 = 20 else: cx2 = random.getrandbits(7) cy2 = random.getrandbits(7) dx2 = random.getrandbits(4) - 8 dy2 = random.getrandbits(4) - 11 r2 = 10 dr2 = random.getrandbits(4) + 5 t2r = random.getrandbits(8) % 23 t2g = 7 if t2r >= 16: t2g = 22 - t2r t2r = 15 for a1 in range(4): for a2 in range(4): blend[(a1 << 2) | a2] = 0x80 | (( (3 * a1 * t1r + (3 - a1) * a2 * t2r + 4) // 9) << 3) | ( (3 * a1 * t1g + (3 - a1) * a2 * t2g + 4) // 9) r = r1 >> 1 l1 = r - 5 l1 *= l1 m1 = r * r u1 = r + 5 u1 *= u1 r = r2 >> 1 l2 = r - 5 l2 *= l2 m2 = r * r u2 = r + 5 u2 *= u2 for y in range(8): for x in range(8): rx = (x << 4) - cx1 ry = (y << 4) - cy1 rr1 = rx * rx + ry * ry rx = (x << 4) - cx2 ry = (y << 4) - cy2 rr2 = rx * rx + ry * ry screen.pixel( x, y, blend[((3 if rr1 < l1 else 2 if rr1 < m1 else 1 if rr1 < u1 else 0) << 2) | (3 if rr2 < l2 else 2 if rr2 < m2 else 1 if rr2 < u2 else 0)]) r1 += dr1 dr1 -= 1 cx1 += dx1 cy1 += dy1 r2 += dr2 dr2 -= 1 cx2 += dx2 cy2 += dy2 pew.show(screen) pew.tick(0.05)
# 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),
# Othello, copyright 2019 Christian Walther import pew try: import random except ImportError: import urandom as random import os if 'PewPew 10.' in os.uname().machine: screen2 = pew.Pix() def show(p): for i in range(len(p.buffer)): c = p.buffer[i] screen2.buffer[i] = c ^ (c >> 1) pew.show(screen2) else: show = pew.show def lookup(board, x, y): return 0 if x < 0 or y < 0 or x >= 8 or y >= 8 else board[y * 8 + x] def move(board, x, y, color): if lookup(board, x, y) != 0: return None turned = False newboard = bytearray(board) newboard[8 * y + x] = color
#CC written by Joost Markerink (March 2020) http://joostmarkerink.nl import pew import time pew.init() screen = pew.Pix() drawing = pew.Pix() cursorx = 3 cursory = 4 prevkeys = pew.keys() paint = 1 editing = True willEdit = False xbuttontime = 0 eventTime = 0 isClear = True splash = [ 0b11111111, 0b10000001, 0b10111101, 0b10100101, 0b10111101, 0b10100001, 0b10000001, 0b11111111 ] for y in range(8): for x in range(8): screen.pixel(x, y, (splash[y] >> (7 - x)) & 1) pew.show(screen) pew.tick(2.4) def fill(sx, sy, fill_value): orig_value = drawing.pixel(sx, sy) stack = set(((sx, sy), )) if fill_value == orig_value:
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)
# 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
def __init__(self): self.screens = pew.Pix(), pew.Pix() self.x = 1 self.y = 1
def new_scene(): return pew.Pix()
def __init__(self): pew.init() self.screen = pew.Pix() self.main_loop()
def print_dot(self): self.screen = pew.Pix() self.screen.box(x=0, y=0, color=self.color_animation(), width=self.size, height=self.size)
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
for x in range(brick.width): if (brick.pixel(x, y) and board.pixel(brick_x + x + 1, brick_y + y + 3)): return True return False def debounce(): for i in range(100): pew.tick(1 / 100) if not pew.keys(): return pew.init() screen = pew.Pix(width=8, height=8) screen.box(color=2, x=6, y=0, width=2, height=8) next_brick = BRICKS[qRand(3)] board = pew.Pix(width=8, height=12) board.box(color=1) board.box(color=0, x=1, y=0, width=6, height=11) while True: brick = next_brick next_brick = BRICKS[qRand(3)] # 0-7 screen.box(color=0, x=6, y=0, width=2, height=5) screen.blit(next_brick, dx=6, dy=0) brick_x = 2 brick_y = -3 while True: if is_colliding(board, brick, brick_x, brick_y):
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 ---- 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()
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()