|
|
|
@ -1,5 +1,7 @@
@@ -1,5 +1,7 @@
|
|
|
|
|
#!/usr/bin/env python3 |
|
|
|
|
|
|
|
|
|
import argparse |
|
|
|
|
import sys |
|
|
|
|
import socket |
|
|
|
|
import struct |
|
|
|
|
import time |
|
|
|
@ -9,6 +11,7 @@ import collections
@@ -9,6 +11,7 @@ import collections
|
|
|
|
|
import blup.frame |
|
|
|
|
import blup.output |
|
|
|
|
import random |
|
|
|
|
import threading |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InvalidMoveError(Exception): |
|
|
|
@ -183,146 +186,110 @@ class Playground():
@@ -183,146 +186,110 @@ class Playground():
|
|
|
|
|
continue |
|
|
|
|
frame.setPixel(xpos + int(b.pos.x), ypos + int(b.pos.y), b.color) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PlayerEvent = enum.Enum('PlayerEvent', ['ROTATE', 'DROP', 'MOVE_LEFT', |
|
|
|
|
'MOVE_RIGHT', 'QUIT']) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TtrsPlayer(): |
|
|
|
|
def __init__(self, playground): |
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
def get_move(self, minopos): |
|
|
|
|
return 0 |
|
|
|
|
|
|
|
|
|
def get_drop(self): |
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
def get_rotate(self): |
|
|
|
|
return False |
|
|
|
|
def get_event(self): |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
def get_quit(self): |
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
class TestTtrsPlayer(TtrsPlayer): |
|
|
|
|
def __init__(self, playground): |
|
|
|
|
self.playground = playground |
|
|
|
|
self.__move = 0 |
|
|
|
|
self.__drop = False |
|
|
|
|
self.__rotate = False |
|
|
|
|
self.__quit = False |
|
|
|
|
self.__evt = None |
|
|
|
|
|
|
|
|
|
import pygame |
|
|
|
|
self.__pygame = pygame |
|
|
|
|
self.screen = pygame.display.set_mode((100, 100)) |
|
|
|
|
pygame.display.update() |
|
|
|
|
|
|
|
|
|
def __process_events(self): |
|
|
|
|
self.controls = { |
|
|
|
|
pygame.K_a: PlayerEvent.MOVE_LEFT, |
|
|
|
|
pygame.K_d: PlayerEvent.MOVE_RIGHT, |
|
|
|
|
pygame.K_w: PlayerEvent.ROTATE, |
|
|
|
|
pygame.K_s: PlayerEvent.DROP, |
|
|
|
|
pygame.K_ESCAPE: PlayerEvent.QUIT, |
|
|
|
|
} |
|
|
|
|
#self.controls = { |
|
|
|
|
# pygame.K_LEFT: PlayerEvent.MOVE_LEFT, |
|
|
|
|
# pygame.K_RIGHT: PlayerEvent.MOVE_RIGHT, |
|
|
|
|
# pygame.K_UP: PlayerEvent.ROTATE, |
|
|
|
|
# pygame.K_DOWN: PlayerEvent.DROP, |
|
|
|
|
# pygame.K_ESCAPE: PlayerEvent.QUIT, |
|
|
|
|
#} |
|
|
|
|
|
|
|
|
|
def get_event(self): |
|
|
|
|
pygame = self.__pygame |
|
|
|
|
for event in pygame.event.get(): |
|
|
|
|
if event.type == pygame.KEYDOWN: |
|
|
|
|
if event.key == pygame.K_a: |
|
|
|
|
self.__move = -1 |
|
|
|
|
elif event.key == pygame.K_d: |
|
|
|
|
self.__move = 1 |
|
|
|
|
elif event.key == pygame.K_w: |
|
|
|
|
self.__rotate = True |
|
|
|
|
elif event.key == pygame.K_s: |
|
|
|
|
self.__drop = True |
|
|
|
|
elif event.key == pygame.K_ESCAPE: |
|
|
|
|
self.__quit = True |
|
|
|
|
elif event.type == pygame.KEYUP: |
|
|
|
|
if event.key == pygame.K_a or event.key == pygame.K_d: |
|
|
|
|
self.__move = 0 |
|
|
|
|
elif event.key == pygame.K_w: |
|
|
|
|
self.__rotate = False |
|
|
|
|
elif event.key == pygame.K_s: |
|
|
|
|
self.__drop = False |
|
|
|
|
|
|
|
|
|
def get_move(self, minopos): |
|
|
|
|
self.__process_events() |
|
|
|
|
return self.__move |
|
|
|
|
|
|
|
|
|
def get_drop(self): |
|
|
|
|
self.__process_events() |
|
|
|
|
return self.__drop |
|
|
|
|
|
|
|
|
|
def get_rotate(self): |
|
|
|
|
self.__process_events() |
|
|
|
|
return self.__rotate |
|
|
|
|
|
|
|
|
|
def get_quit(self): |
|
|
|
|
self.__process_events() |
|
|
|
|
return self.__quit |
|
|
|
|
|
|
|
|
|
def reset_rotate(self): |
|
|
|
|
pass |
|
|
|
|
return self.controls.get(event.key, None) |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
class BalanceTtrsPlayer(TtrsPlayer): |
|
|
|
|
|
|
|
|
|
class BalanceTtrsPlayer(TtrsPlayer, threading.Thread): |
|
|
|
|
def __init__(self, playground, addr, player_id): |
|
|
|
|
self.playground = playground |
|
|
|
|
self.player_id = player_id |
|
|
|
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
|
|
|
self.sock.connect(addr) |
|
|
|
|
threading.Thread.__init__(self, daemon=True) |
|
|
|
|
self.start() |
|
|
|
|
|
|
|
|
|
def __update_balance(self): |
|
|
|
|
self.sock.send(b'a') |
|
|
|
|
data = self.sock.recv(4) |
|
|
|
|
p1x, p1y, p2x, p2y = struct.unpack('bbbb', data) |
|
|
|
|
def run(self): |
|
|
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
|
|
|
sock.connect(addr) |
|
|
|
|
self.running = True |
|
|
|
|
while self.running: |
|
|
|
|
sock.send(b'a') |
|
|
|
|
data = sock.recv(4) |
|
|
|
|
p0x, p0y, p2x, p2y = struct.unpack('bbbb', data) |
|
|
|
|
if self.player_id == 0: |
|
|
|
|
self.xbal = p1x |
|
|
|
|
self.ybal = p1y |
|
|
|
|
self.__calc_event(p0x, p0y) |
|
|
|
|
elif self.player_id == 1: |
|
|
|
|
self.xbal = p2x |
|
|
|
|
self.ybal = p2y |
|
|
|
|
|
|
|
|
|
def reset_rotate(self): |
|
|
|
|
self.__rotate_reset = True |
|
|
|
|
self.__rotate = False |
|
|
|
|
|
|
|
|
|
def get_move(self, minopos): |
|
|
|
|
self.__update_balance() |
|
|
|
|
|
|
|
|
|
#if p1x > 40: |
|
|
|
|
# self.__move = 1 |
|
|
|
|
#elif p1x < -40: |
|
|
|
|
# self.__move = -1 |
|
|
|
|
#elif p1x > -20 and p1x < 20: |
|
|
|
|
# self.__move = 0 |
|
|
|
|
self.__calc_event(p1x, p1y) |
|
|
|
|
time.sleep(0.01) |
|
|
|
|
|
|
|
|
|
def __calc_event(self, xbal, ybal): |
|
|
|
|
if self.ybal > 50: |
|
|
|
|
return PlayerEvent.ROTATE |
|
|
|
|
|
|
|
|
|
MAX_Y_AMPLITUDE = 65 |
|
|
|
|
bal = self.xbal |
|
|
|
|
normbal = (bal + MAX_Y_AMPLITUDE) / (2 * MAX_Y_AMPLITUDE) |
|
|
|
|
px = round(normbal * (self.playground.width - 1)) |
|
|
|
|
print('player %d balance=%d pos=%d' % (self.player_id, bal, px)) |
|
|
|
|
if minopos.x > px: |
|
|
|
|
return -1 |
|
|
|
|
return PlayerEvent.MOVE_LEFT |
|
|
|
|
elif minopos.x < px: |
|
|
|
|
return 1 |
|
|
|
|
else: |
|
|
|
|
return 0 |
|
|
|
|
|
|
|
|
|
def get_drop(self): |
|
|
|
|
self.__update_balance() |
|
|
|
|
return (self.ybal < -50) |
|
|
|
|
|
|
|
|
|
def get_rotate(self): |
|
|
|
|
self.__update_balance() |
|
|
|
|
if self.ybal > 50 and not self.__rotate_reset: |
|
|
|
|
return True |
|
|
|
|
elif self.ybal < 35: |
|
|
|
|
self.__rotate_reset = False |
|
|
|
|
return False |
|
|
|
|
return PlayerEvent.MOVE_RIGHT |
|
|
|
|
|
|
|
|
|
def get_quit(self): |
|
|
|
|
self.__update_balance() |
|
|
|
|
return (self.xbal == -128 or self.ybal == -128) |
|
|
|
|
if self.ybal < -50: |
|
|
|
|
return PlayerEvent.DROP |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TtrsGame(): |
|
|
|
|
def __init__(self, playground, player): |
|
|
|
|
class TtrsGame(threading.Thread): |
|
|
|
|
def __init__(self, playground, player, rnd=None): |
|
|
|
|
self.playground = playground |
|
|
|
|
self.player = player |
|
|
|
|
if rnd is None: |
|
|
|
|
self.rnd = random.Random() |
|
|
|
|
else: |
|
|
|
|
self.rnd = rnd |
|
|
|
|
self.running = False |
|
|
|
|
self.tick_callbacks = [] |
|
|
|
|
threading.Thread.__init__(self, daemon=True) |
|
|
|
|
|
|
|
|
|
def add_tick_callback(self, cb): |
|
|
|
|
self.tick_callbacks.append(cb) |
|
|
|
|
|
|
|
|
|
def __call_callbacks(self): |
|
|
|
|
for cb in self.tick_callbacks: |
|
|
|
|
cb(self) |
|
|
|
|
|
|
|
|
|
def run(self): |
|
|
|
|
self.running = True |
|
|
|
|
spawnpos = Point(self.playground.width // 2, -1) |
|
|
|
@ -333,18 +300,19 @@ class TtrsGame():
@@ -333,18 +300,19 @@ class TtrsGame():
|
|
|
|
|
ticks = 0 |
|
|
|
|
lastfall = 0 |
|
|
|
|
lastmove = 0 |
|
|
|
|
dropping = False |
|
|
|
|
while self.running: |
|
|
|
|
for cb in self.tick_callbacks: |
|
|
|
|
cb() |
|
|
|
|
self.__call_callbacks() |
|
|
|
|
time.sleep(TICK_TIME) |
|
|
|
|
ticks += 1 |
|
|
|
|
|
|
|
|
|
if self.player.get_quit(): |
|
|
|
|
evt = self.player.get_event() |
|
|
|
|
if evt == PlayerEvent.QUIT: |
|
|
|
|
self.running = False |
|
|
|
|
break |
|
|
|
|
|
|
|
|
|
if mino is None: |
|
|
|
|
newminocls = random.choice(Tetrimino.__subclasses__()) |
|
|
|
|
newminocls = self.rnd.choice(Tetrimino.__subclasses__()) |
|
|
|
|
mino = newminocls(self.playground, spawnpos) |
|
|
|
|
self.playground.minos = {mino} |
|
|
|
|
|
|
|
|
@ -366,13 +334,11 @@ class TtrsGame():
@@ -366,13 +334,11 @@ class TtrsGame():
|
|
|
|
|
for i in range(2): |
|
|
|
|
for b in to_delete: |
|
|
|
|
self.playground.blocks.add(b) |
|
|
|
|
for cb in self.tick_callbacks: |
|
|
|
|
cb() |
|
|
|
|
self.__call_callbacks() |
|
|
|
|
time.sleep(0.2) |
|
|
|
|
for b in to_delete: |
|
|
|
|
self.playground.blocks.remove(b) |
|
|
|
|
for cb in self.tick_callbacks: |
|
|
|
|
cb() |
|
|
|
|
self.__call_callbacks() |
|
|
|
|
time.sleep(0.2) |
|
|
|
|
|
|
|
|
|
to_add = set() |
|
|
|
@ -384,7 +350,7 @@ class TtrsGame():
@@ -384,7 +350,7 @@ class TtrsGame():
|
|
|
|
|
to_add.add(newb) |
|
|
|
|
self.playground.blocks.update(to_add) |
|
|
|
|
|
|
|
|
|
if ticks - lastfall >= FALL_INTERVAL or self.player.get_drop(): |
|
|
|
|
if ticks - lastfall >= FALL_INTERVAL or evt == PlayerEvent.DROP: |
|
|
|
|
lastfall = ticks |
|
|
|
|
try: |
|
|
|
|
mino.move(Point(0, 1)) |
|
|
|
@ -394,60 +360,96 @@ class TtrsGame():
@@ -394,60 +360,96 @@ class TtrsGame():
|
|
|
|
|
self.playground.minos = set() |
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
move = self.player.get_move(mino.pos) |
|
|
|
|
if ticks - lastmove >= MOVE_INTERVAL and move != 0: |
|
|
|
|
lastmove = ticks |
|
|
|
|
if ticks - lastmove >= MOVE_INTERVAL: |
|
|
|
|
try: |
|
|
|
|
mino.move(Point(move, 0)) |
|
|
|
|
if evt == PlayerEvent.MOVE_LEFT: |
|
|
|
|
mino.move(Point(-1, 0)) |
|
|
|
|
lastmove = ticks |
|
|
|
|
elif evt == PlayerEvent.MOVE_RIGHT: |
|
|
|
|
mino.move(Point(1, 0)) |
|
|
|
|
lastmove = ticks |
|
|
|
|
except InvalidMoveError: |
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
if self.player.get_rotate(): |
|
|
|
|
if evt == PlayerEvent.ROTATE: |
|
|
|
|
try: |
|
|
|
|
mino.rotate() |
|
|
|
|
self.player.reset_rotate() |
|
|
|
|
except InvalidMoveError: |
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
|
w = 22 |
|
|
|
|
h = 16 |
|
|
|
|
|
|
|
|
|
dim = blup.frame.FrameDimension(w, h, 256, 3) |
|
|
|
|
frame = blup.frame.Frame(dim) |
|
|
|
|
out = blup.output.getOutput('e3blp:blinkenbunt:2342') |
|
|
|
|
|
|
|
|
|
pg = Playground(10, 16) |
|
|
|
|
def repaint(pg): |
|
|
|
|
pg.paint(frame, 0, 0) |
|
|
|
|
out.sendFrame(frame) |
|
|
|
|
time.sleep(0.5) |
|
|
|
|
|
|
|
|
|
def repaint(): |
|
|
|
|
pg.paint(frame, 0, 0) |
|
|
|
|
out.sendFrame(frame) |
|
|
|
|
|
|
|
|
|
#player = TestTtrsPlayer(pg) |
|
|
|
|
player = BalanceTtrsPlayer(pg, ('blinkenbunt', 4711), 0) |
|
|
|
|
game = TtrsGame(pg, player) |
|
|
|
|
game.add_tick_callback(repaint) |
|
|
|
|
game.run() |
|
|
|
|
|
|
|
|
|
#t = TetriL(pg, Point(2, 2)) |
|
|
|
|
#pg.minos.add(t) |
|
|
|
|
#pg.paint(frame, 0, 0) |
|
|
|
|
#out.sendFrame(frame) |
|
|
|
|
#time.sleep(0.5) |
|
|
|
|
|
|
|
|
|
#for i in range(10): |
|
|
|
|
# t.rotate(ccw=(i>=5)) |
|
|
|
|
# pg.paint(frame, 0, 0) |
|
|
|
|
# out.sendFrame(frame) |
|
|
|
|
# time.sleep(0.1) |
|
|
|
|
|
|
|
|
|
class GamePlaygroundPainter(): |
|
|
|
|
def __init__(self, output, dimension): |
|
|
|
|
self.output = output |
|
|
|
|
self.games = {} |
|
|
|
|
self.frame = blup.frame.Frame(dimension) |
|
|
|
|
bgcolor = (100, 100, 100) |
|
|
|
|
for x in range(dimension.width): |
|
|
|
|
for y in range(dimension.height): |
|
|
|
|
self.frame.setPixel(x, y, bgcolor) |
|
|
|
|
|
|
|
|
|
def add_game(self, game, xpos, ypos): |
|
|
|
|
game.add_tick_callback(self.repaint) |
|
|
|
|
self.games[game] = (xpos, ypos) |
|
|
|
|
|
|
|
|
|
def repaint(self, game): |
|
|
|
|
xpos, ypos = self.games[game] |
|
|
|
|
game.playground.paint(self.frame, xpos, ypos) |
|
|
|
|
self.output.sendFrame(self.frame) |
|
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
|
parser = argparse.ArgumentParser(description='Blinkenbunt Tetris!') |
|
|
|
|
parser.add_argument('--players', dest='players', type=int, default=1, |
|
|
|
|
help='number of players') |
|
|
|
|
parser.add_argument('--pygame', dest='pygame', action='store_true', |
|
|
|
|
help='use pygame as input') |
|
|
|
|
parser.add_argument('--balance', dest='balance', type=str, nargs=1, |
|
|
|
|
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) |
|
|
|
|
|
|
|
|
|
if args.pygame and args.players == 2: |
|
|
|
|
print('pygame input does only support one player', file=sys.stderr) |
|
|
|
|
sys.exit(1) |
|
|
|
|
|
|
|
|
|
w = 22 |
|
|
|
|
h = 16 |
|
|
|
|
|
|
|
|
|
out = blup.output.getOutput(args.out) |
|
|
|
|
dim = blup.frame.FrameDimension(w, h, 256, 3) |
|
|
|
|
painter = GamePlaygroundPainter(out, dim) |
|
|
|
|
|
|
|
|
|
seed = random.random() |
|
|
|
|
games = [] |
|
|
|
|
def start_game(player_id, xpos): |
|
|
|
|
pg = Playground(10, 16) |
|
|
|
|
if args.pygame: |
|
|
|
|
player = TestTtrsPlayer(pg) |
|
|
|
|
elif args.balance is not None: |
|
|
|
|
host, port = args.balance.split(':') |
|
|
|
|
player = BalanceTtrsPlayer(pg, (host, int(port)), player_id) |
|
|
|
|
rnd = random.Random(seed) |
|
|
|
|
game = TtrsGame(pg, player, rnd) |
|
|
|
|
painter.add_game(game, xpos, 0) |
|
|
|
|
game.start() |
|
|
|
|
games.append(game) |
|
|
|
|
|
|
|
|
|
if args.players == 1: |
|
|
|
|
start_game(0, 6) |
|
|
|
|
elif args.players == 2: |
|
|
|
|
start_game(0, 0) |
|
|
|
|
start_game(1, 12) |
|
|
|
|
|
|
|
|
|
for game in games: |
|
|
|
|
game.join() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|