|
|
|
#!/usr/bin/python3
|
|
|
|
|
|
|
|
import sys
|
|
|
|
import time
|
|
|
|
import enum
|
|
|
|
import threading
|
|
|
|
import argparse
|
|
|
|
import subprocess
|
|
|
|
import socket
|
|
|
|
import struct
|
|
|
|
import blup.frame
|
|
|
|
import blup.output
|
|
|
|
|
|
|
|
class Game(object):
|
|
|
|
def __init__(self):
|
|
|
|
self.balance = None
|
|
|
|
self.pygame = False
|
|
|
|
self.output = None
|
|
|
|
pass
|
|
|
|
|
|
|
|
@property
|
|
|
|
def logo(self):
|
|
|
|
return self._logo
|
|
|
|
|
|
|
|
@property
|
|
|
|
def args(self):
|
|
|
|
args = []
|
|
|
|
args += ['--players', str(self.players)]
|
|
|
|
if self.balance is not None:
|
|
|
|
args += ['--balance', self.balance]
|
|
|
|
if self.pygame:
|
|
|
|
args += ['--pygame']
|
|
|
|
if self.output is not None:
|
|
|
|
args += ['--out', self.output]
|
|
|
|
return args
|
|
|
|
|
|
|
|
class PongGame(Game):
|
|
|
|
def __init__(self):
|
|
|
|
self.name = 'Pong'
|
|
|
|
self.executable = 'balancep0ng.py'
|
|
|
|
self.players = 0
|
|
|
|
self._logo = [
|
|
|
|
[1,1,0,0,0,1,1,0,0,1,0,0,1,0,0,1,1,0],
|
|
|
|
[1,0,1,0,1,0,0,1,0,1,1,0,1,0,1,0,0,0],
|
|
|
|
[1,1,0,0,1,0,0,1,0,1,0,1,1,0,1,0,1,1],
|
|
|
|
[1,0,0,0,1,0,0,1,0,1,0,0,1,0,1,0,0,1],
|
|
|
|
[1,0,0,0,0,1,1,0,0,1,0,0,1,0,0,1,1,0],
|
|
|
|
]
|
|
|
|
|
|
|
|
class TetrisGame(Game):
|
|
|
|
def __init__(self):
|
|
|
|
self.name = 'Tetris'
|
|
|
|
self.executable = 'ttrs.py'
|
|
|
|
self.players = 0
|
|
|
|
self._logo = [
|
|
|
|
[1,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,0,0,1,1],
|
|
|
|
[0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,1,0,1,0,0],
|
|
|
|
[0,1,0,0,1,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0],
|
|
|
|
[0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,1,0,0,0,1],
|
|
|
|
[0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,1,0,1,1,0],
|
|
|
|
]
|
|
|
|
|
|
|
|
arrow_up = [
|
|
|
|
[0,0,1,0,0],
|
|
|
|
[0,1,1,1,0],
|
|
|
|
[1,1,1,1,1],
|
|
|
|
]
|
|
|
|
arrow_down = list(reversed(arrow_up))
|
|
|
|
|
|
|
|
arrow_left = [
|
|
|
|
[0,0,1],
|
|
|
|
[0,1,1],
|
|
|
|
[1,1,1],
|
|
|
|
[0,1,1],
|
|
|
|
[0,0,1],
|
|
|
|
]
|
|
|
|
arrow_right = [list(reversed(x)) for x in arrow_left]
|
|
|
|
|
|
|
|
go = [
|
|
|
|
[0,1,0,0,0,1,0],
|
|
|
|
[1,0,0,0,1,0,1],
|
|
|
|
[1,0,1,0,1,0,1],
|
|
|
|
[0,1,0,0,0,1,0],
|
|
|
|
]
|
|
|
|
|
|
|
|
select_game = [
|
|
|
|
[0,1,1,0,1,1,0,1,0,0,1,1,0,1,1,0,1,1,1],
|
|
|
|
[1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0],
|
|
|
|
[0,1,0,0,1,1,0,1,0,0,1,1,0,1,0,0,0,1,0],
|
|
|
|
[0,0,1,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0],
|
|
|
|
[1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,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,1,0,0,1,0,0,0,1,0,1,1,0],
|
|
|
|
[0,1,0,0,0,0,1,0,1,0,1,1,0,1,1,0,1,0,0],
|
|
|
|
[0,1,0,1,1,0,1,1,1,0,1,0,1,0,1,0,1,1,0],
|
|
|
|
[0,1,0,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,0],
|
|
|
|
[0,0,1,1,0,0,1,0,1,0,1,0,0,0,1,0,1,1,0],
|
|
|
|
]
|
|
|
|
|
|
|
|
player = [
|
|
|
|
[1,1,0,0,1,0,0,0,1,0,0,1,0,1,0,1,1,0,1,1,0],
|
|
|
|
[1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1],
|
|
|
|
[1,1,0,0,1,0,0,1,1,1,0,0,1,0,0,1,1,0,1,1,0],
|
|
|
|
[1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,0,1],
|
|
|
|
[1,0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,1,0,1,0,1],
|
|
|
|
]
|
|
|
|
|
|
|
|
two = [
|
|
|
|
[0,1,1,1,0,0,0,1,1,0],
|
|
|
|
[1,0,0,0,1,0,1,0,0,1],
|
|
|
|
[0,0,0,1,0,0,0,0,1,0],
|
|
|
|
[0,0,1,0,0,0,0,1,0,0],
|
|
|
|
[0,1,0,0,0,0,0,0,0,0],
|
|
|
|
[1,1,1,1,1,0,0,1,0,0],
|
|
|
|
]
|
|
|
|
|
|
|
|
def copy_bitmap(bitmap, frame, color, xoffs=0, yoffs=0):
|
|
|
|
for x in range(len(bitmap[0])):
|
|
|
|
for y in range(len(bitmap)):
|
|
|
|
if bitmap[y][x] == 1:
|
|
|
|
frame.setPixel(xoffs + x, yoffs + y, color)
|
|
|
|
|
|
|
|
def create_init_screen(dimension):
|
|
|
|
frame = blup.frame.Frame(dimension)
|
|
|
|
|
|
|
|
xoffs = (dimension.size()[0]-len(select_game[0]))//2
|
|
|
|
yoffs = (dimension.size()[1]-len(select_game))//2
|
|
|
|
color = (0, 255, 0)
|
|
|
|
copy_bitmap(select_game, frame, color, xoffs, yoffs)
|
|
|
|
|
|
|
|
return frame
|
|
|
|
|
|
|
|
def create_2player_screen(dimension):
|
|
|
|
frame = blup.frame.Frame(dimension)
|
|
|
|
|
|
|
|
xoffs = (dimension.size()[0]-len(player[0]))//2
|
|
|
|
yoffs = 1
|
|
|
|
color = (0, 255, 0)
|
|
|
|
copy_bitmap(player, frame, color, xoffs, yoffs)
|
|
|
|
|
|
|
|
xoffs = (dimension.size()[0]-len(two[0]))//2
|
|
|
|
yoffs += 1+len(two)
|
|
|
|
color = (0, 255, 0)
|
|
|
|
copy_bitmap(two, frame, color, xoffs, yoffs)
|
|
|
|
|
|
|
|
return frame
|
|
|
|
|
|
|
|
def create_menu_frame(dimension, game):
|
|
|
|
frame = blup.frame.Frame(dimension)
|
|
|
|
|
|
|
|
xoffs = (dimension.size()[0]-len(arrow_up[0]))//2
|
|
|
|
yoffs = 0
|
|
|
|
color = (255, 0, 0)
|
|
|
|
copy_bitmap(arrow_up, frame, color, xoffs, yoffs)
|
|
|
|
|
|
|
|
xoffs = xoffs+len(arrow_up[0])+1
|
|
|
|
color = (255, 0, 0)
|
|
|
|
copy_bitmap(go, frame, color, xoffs, yoffs)
|
|
|
|
|
|
|
|
xoffs = 0
|
|
|
|
yoffs = dimension.size()[1]-len(arrow_right)
|
|
|
|
color = (0, 0, 255)
|
|
|
|
copy_bitmap(arrow_left, frame, color, xoffs, yoffs)
|
|
|
|
xoffs = dimension.size()[0]-len(arrow_right[0])
|
|
|
|
color = (255, 255, 0)
|
|
|
|
copy_bitmap(arrow_right, frame, color, xoffs, yoffs)
|
|
|
|
|
|
|
|
xoffs = (dimension.size()[0]-len(game.logo[0]))//2
|
|
|
|
yoffs = (dimension.size()[1]-len(game.logo))//2
|
|
|
|
color = (0, 255, 255)
|
|
|
|
copy_bitmap(game.logo, frame, color, xoffs, yoffs)
|
|
|
|
|
|
|
|
return frame
|
|
|
|
|
|
|
|
InputEvent = enum.Enum('InputEvent', ['UP', 'DOWN', 'LEFT',
|
|
|
|
'RIGHT', 'QUIT', 'STEP_ON'])
|
|
|
|
|
|
|
|
class AbstractInput():
|
|
|
|
def __init__(self):
|
|
|
|
self.player_present = False
|
|
|
|
pass
|
|
|
|
|
|
|
|
def get_event(self):
|
|
|
|
return None
|
|
|
|
|
|
|
|
class TestInput(AbstractInput):
|
|
|
|
def __init__(self):
|
|
|
|
self.__evt = None
|
|
|
|
|
|
|
|
import pygame
|
|
|
|
self.__pygame = pygame
|
|
|
|
self.screen = pygame.display.set_mode((100, 100))
|
|
|
|
pygame.display.update()
|
|
|
|
|
|
|
|
self.controls = {
|
|
|
|
pygame.K_a: InputEvent.LEFT,
|
|
|
|
pygame.K_d: InputEvent.RIGHT,
|
|
|
|
pygame.K_w: InputEvent.UP,
|
|
|
|
pygame.K_s: InputEvent.STEP_ON,
|
|
|
|
pygame.K_ESCAPE: InputEvent.QUIT,
|
|
|
|
}
|
|
|
|
|
|
|
|
def get_event(self):
|
|
|
|
pygame = self.__pygame
|
|
|
|
for event in pygame.event.get():
|
|
|
|
if event.type == pygame.KEYDOWN:
|
|
|
|
if self.controls.get(event.key, None) == InputEvent.STEP_ON:
|
|
|
|
self.player_present = True
|
|
|
|
return self.controls.get(event.key, None)
|
|
|
|
return None
|
|
|
|
|
|
|
|
class BalanceInput(AbstractInput, threading.Thread):
|
|
|
|
def __init__(self, addr, player_id):
|
|
|
|
self.addr = addr
|
|
|
|
self.player_id = player_id
|
|
|
|
self.evt = None
|
|
|
|
self.lastevt = None
|
|
|
|
self.player_present = False
|
|
|
|
self.running = False
|
|
|
|
threading.Thread.__init__(self, daemon=True)
|
|
|
|
self.start()
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
try:
|
|
|
|
sock.connect(self.addr)
|
|
|
|
except ConnectionRefusedError:
|
|
|
|
print('could not connect to balance server', file=sys.stderr)
|
|
|
|
self.evt = InputEvent.QUIT
|
|
|
|
return
|
|
|
|
self.running = True
|
|
|
|
evt = None
|
|
|
|
oldevt = None
|
|
|
|
lastchange = 0
|
|
|
|
while self.running:
|
|
|
|
sock.send(b'a')
|
|
|
|
data = sock.recv(4)
|
|
|
|
p0x, p0y, p1x, p1y = struct.unpack('bbbb', data)
|
|
|
|
if self.player_id == 0:
|
|
|
|
xbal, ybal = p0x, p0y
|
|
|
|
elif self.player_id == 1:
|
|
|
|
xbal, ybal = p1x, p1y
|
|
|
|
|
|
|
|
THRESHOLD = 40
|
|
|
|
MIN_TIMES = {
|
|
|
|
InputEvent.LEFT: 0.05,
|
|
|
|
InputEvent.RIGHT: 0.05,
|
|
|
|
InputEvent.UP: 0.05,
|
|
|
|
InputEvent.DOWN: 0.05,
|
|
|
|
}
|
|
|
|
if self.player_present:
|
|
|
|
if xbal == -128 or ybal == -128:
|
|
|
|
self.evt = InputEvent.QUIT
|
|
|
|
self.player_present = False
|
|
|
|
continue
|
|
|
|
|
|
|
|
if abs(xbal) < THRESHOLD and abs(ybal) < THRESHOLD:
|
|
|
|
evt = None
|
|
|
|
else:
|
|
|
|
if abs(xbal) < abs(ybal):
|
|
|
|
if ybal > 0:
|
|
|
|
evt = InputEvent.UP
|
|
|
|
else:
|
|
|
|
evt = InputEvent.DOWN
|
|
|
|
else:
|
|
|
|
if xbal > 0:
|
|
|
|
evt = InputEvent.RIGHT
|
|
|
|
else:
|
|
|
|
evt = InputEvent.LEFT
|
|
|
|
if evt != oldevt:
|
|
|
|
lastchange = time.time()
|
|
|
|
oldevt = evt
|
|
|
|
|
|
|
|
if time.time() - lastchange < MIN_TIMES.get(evt, 0):
|
|
|
|
print('player_id=%d debounce %s' % (self.player_id, evt))
|
|
|
|
continue
|
|
|
|
|
|
|
|
if self.lastevt != evt:
|
|
|
|
self.evt = evt
|
|
|
|
self.lastevt = evt
|
|
|
|
print('player_id=%d event=%s' % (self.player_id, self.evt))
|
|
|
|
time.sleep(0.005)
|
|
|
|
else:
|
|
|
|
if xbal != -128 and ybal != -128:
|
|
|
|
self.evt = InputEvent.STEP_ON
|
|
|
|
self.player_present = True
|
|
|
|
continue
|
|
|
|
time.sleep(0.05)
|
|
|
|
|
|
|
|
def get_event(self):
|
|
|
|
evt = self.evt
|
|
|
|
if self.running:
|
|
|
|
self.evt = None
|
|
|
|
return evt
|
|
|
|
|
|
|
|
def start_game(game, players):
|
|
|
|
print('Starting', game.name, 'for', players, 'player(s)')
|
|
|
|
game.players = players
|
|
|
|
arg = ['./'+game.executable] + game.args
|
|
|
|
subprocess.call(arg)
|
|
|
|
sys.exit()
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
parser = argparse.ArgumentParser(description='Blinkenbunt Game Selection Menu')
|
|
|
|
parser.add_argument('--pygame', dest='pygame', action='store_true',
|
|
|
|
help='use pygame as input')
|
|
|
|
parser.add_argument('--balance', dest='balance', type=str,
|
|
|
|
metavar='HOST:PORT', help='use balance input')
|
|
|
|
parser.add_argument('--out', dest='out', type=str, metavar='OUTPUT',
|
|
|
|
default='e3blp', help='blup output specification')
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
if args.balance is None and not args.pygame:
|
|
|
|
print('please specify an input method', file=sys.stderr)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
w = 22
|
|
|
|
h = 16
|
|
|
|
|
|
|
|
out = blup.output.getOutput(args.out)
|
|
|
|
dim = blup.frame.FrameDimension(w, h, 256, 3)
|
|
|
|
|
|
|
|
if args.pygame:
|
|
|
|
inp1 = TestInput()
|
|
|
|
inp2 = TestInput()
|
|
|
|
elif args.balance is not None:
|
|
|
|
host, port = args.balance.split(':')
|
|
|
|
inp1 = BalanceInput((host, int(port)), 0)
|
|
|
|
inp2 = BalanceInput((host, int(port)), 1)
|
|
|
|
|
|
|
|
while True:
|
|
|
|
evt = inp1.get_event()
|
|
|
|
if evt == InputEvent.STEP_ON:
|
|
|
|
break
|
|
|
|
elif evt == InputEvent.QUIT:
|
|
|
|
sys.exit()
|
|
|
|
time.sleep(0.05)
|
|
|
|
|
|
|
|
init_frame = create_init_screen(dim)
|
|
|
|
out.sendFrame(init_frame)
|
|
|
|
time.sleep(1)
|
|
|
|
games = Game.__subclasses__()
|
|
|
|
game_idx = 0
|
|
|
|
old_idx = -1
|
|
|
|
lastrefresh = 0
|
|
|
|
while True:
|
|
|
|
if old_idx != game_idx:
|
|
|
|
gamecls = games[game_idx]
|
|
|
|
game = gamecls()
|
|
|
|
old_idx = game_idx
|
|
|
|
lastrefresh = 0
|
|
|
|
|
|
|
|
if time.time() >= lastrefresh+1:
|
|
|
|
out.sendFrame(create_menu_frame(dim, game))
|
|
|
|
lastrefresh = time.time()
|
|
|
|
|
|
|
|
evt = inp1.get_event()
|
|
|
|
if evt == InputEvent.LEFT:
|
|
|
|
game_idx -= 1
|
|
|
|
if game_idx < 0:
|
|
|
|
game_idx = len(games)-1
|
|
|
|
if evt == InputEvent.RIGHT:
|
|
|
|
game_idx += 1
|
|
|
|
if game_idx >= len(games):
|
|
|
|
game_idx = 0
|
|
|
|
if evt == InputEvent.UP:
|
|
|
|
game.output = args.out
|
|
|
|
game.balance = args.balance
|
|
|
|
game.pygame = args.pygame
|
|
|
|
break
|
|
|
|
if evt == InputEvent.QUIT:
|
|
|
|
sys.exit()
|
|
|
|
|
|
|
|
p2_frame = create_2player_screen(dim)
|
|
|
|
out.sendFrame(p2_frame)
|
|
|
|
wait_time = 2
|
|
|
|
start = time.time()
|
|
|
|
while time.time() < start+wait_time:
|
|
|
|
evt = inp2.get_event()
|
|
|
|
if inp2.player_present:
|
|
|
|
inp1.running = False
|
|
|
|
inp2.running = False
|
|
|
|
start_game(game, 2)
|
|
|
|
elif evt == InputEvent.QUIT:
|
|
|
|
sys.exit()
|
|
|
|
time.sleep(0.1)
|
|
|
|
|
|
|
|
inp1.running = False
|
|
|
|
inp2.running = False
|
|
|
|
start_game(game, 1)
|
|
|
|
|