You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

349 lines
10 KiB

#!/usr/bin/python3
import sys
import time
import enum
import threading
import argparse
import subprocess
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 += ['--output', 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],
]
player2 = [
[0,1,1,1,0,0,1,1,1,0,0,0,1,1,0],
[1,0,0,0,1,0,1,0,0,1,0,1,0,0,1],
[0,0,0,1,0,0,1,0,0,1,0,0,0,1,0],
[0,0,1,0,0,0,1,1,1,0,0,0,1,0,0],
[0,1,0,0,0,0,1,0,0,0,0,0,0,0,0],
[1,1,1,1,1,0,1,0,0,0,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(player2[0]))//2
yoffs = (dimension.size()[1]-len(player2))//2
color = (0, 255, 0)
copy_bitmap(player2, 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):
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:
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.oldevt = None
self.player_present = 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
print('player_id=%d xbal=%d ybal=%d' % (self.player_id, xbal, ybal))
THRESHOLD = 40
MIN_TIMES = {
InputEvent.LEFT: 0.1,
InputEvent.RIGHT: 0.1,
InputEvent.UP: 0.1,
InputEvent.DOWN: 0.1,
}
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
self.evt = evt
print('player_id=%d event=%s' % (self.player_id, self.evt))
else:
if xbal != -128 and ybal != -128:
self.evt = InputEvent.STEP_ON
self.player_present = True
continue
def get_event(self):
evt = self.evt
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 inp1.get_event() != InputEvent.STEP_ON:
time.sleep(0.05)
init_frame = create_init_screen(dim)
out.sendFrame(init_frame)
time.sleep(2)
games = Game.__subclasses__()
game_idx = 0
while True:
gamecls = games[game_idx]
game = gamecls()
out.sendFrame(create_menu_frame(dim, game))
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 = 3
start = time.time()
while time.time() < start+wait_time:
if inp2.get_event() == InputEvent.STEP_ON:
start_game(game, 2)
time.sleep(0.1)
start_game(game, 1)