diff --git a/game.py b/game.py index a3d2aa8..82fe91c 100755 --- a/game.py +++ b/game.py @@ -9,43 +9,9 @@ import time from errno import EPIPE -# The current time of day -now = time.time() - # Heartbeat frequency (in seconds) pulse = 2.0 -## -## Heartbeat stuff -## - -hearts = set() -last_beat = 0 - -def add_heart(cb): - global hearts - - hearts.add(cb) - - -def del_heart(cb): - global hearts - - hearts.remove(cb) - - -def beat_heart(): - global hearts, last_beat, now - - if now - last_beat > pulse: - last_beat = now - for cb in hearts: - try: - cb() - except: - traceback.print_exc() - - ## ## Network stuff ## @@ -59,6 +25,7 @@ class Listener(asyncore.dispatcher): self.listen(4) self.player_factory = player_factory self.manager = manager + self.last_beat = 0 def handle_accept(self): conn, addr = self.accept() @@ -66,6 +33,12 @@ class Listener(asyncore.dispatcher): # We don't need to keep the player, asyncore.socket_map already # has a reference to it for as long as it's open. + def readable(self): + now = time.time() + if now > self.last_beat + pulse: + self.manager.heartbeat(now) + return True + class Flagger(asynchat.async_chat): """Connection to flagd""" @@ -115,11 +88,12 @@ class Manager: self.games = set() self.lobby = set() self.contestants = [] - add_heart(self.heartbeat) + self.last_beat = 0 - def heartbeat(self): + def heartbeat(self, now): + # Called by listener to beat heart for game in list(self.games): - game.heartbeat() + game.heartbeat(now) def enter_lobby(self, player): self.lobby.add(player) @@ -143,6 +117,7 @@ class Manager: """Start a new contest.""" self.contestants = list(self.lobby) + print('new playoff:', [c.name for c in self.contestants]) def run_contest(self): # Purge any disconnected players @@ -175,7 +150,7 @@ class Manager: player.attach_game(game) def declare_winner(self, game, winner=None): - print('winner', game, winner) + print('Winner:', winner and winner.name) self.games.remove(game) # Winner stays in the contest @@ -199,8 +174,6 @@ class Player(asynchat.async_chat): timeout = 10.0 def __init__(self, sock, manager): - global now - asynchat.async_chat.__init__(self, sock=sock) self.manager = manager self.game = None @@ -209,14 +182,12 @@ class Player(asynchat.async_chat): self.blocked = None self.name = None self.pending = None - self.last_activity = now + self.last_activity = time.time() def readable(self): - global now, timeout - ret = (not self.blocked) and asynchat.async_chat.readable(self) if ret: - if now - self.last_activity > self.timeout: + if time.time() - self.last_activity > self.timeout: # They waited too long. self.err('idle timeout') self.close() @@ -229,10 +200,8 @@ class Player(asynchat.async_chat): def unblock(self): """Unblock reads""" - global now - self.blocked = False - self.last_activity = now + self.last_activity = time.time() def attach_game(self, game): self.game = game @@ -330,7 +299,7 @@ class Game: def setup(self): pass - def heartbeat(self): + def heartbeat(self, now): pass def declare_winner(self, winner): @@ -382,8 +351,7 @@ class TurnBasedGame(Game): game_timeout = 6.0 def __init__(self, manager, players): - global now - + now = time.time() self.ended_turn = set() self.running = True self.winner = None @@ -391,10 +359,8 @@ class TurnBasedGame(Game): self.began = now Game.__init__(self, manager, players) - def heartbeat(self): - global now - - if now - self.began > self.game_timeout: + def heartbeat(self, now=None): + if now and (now - self.began > self.game_timeout): self.running = False # Idle players forfeit. They're also booted, so we don't have @@ -443,7 +409,7 @@ class TurnBasedGame(Game): def end_turn(self, player): """End player's turn.""" - global now + now = time.time() self.ended_turn.add(player) self.lastmoved[player] = now @@ -483,18 +449,13 @@ class TurnBasedGame(Game): ## Running a game ## -def loop(): - global pulse, now - - while asyncore.socket_map: - now = time.time() - beat_heart() - asyncore.poll2(timeout=pulse, map=asyncore.socket_map) - - -def run(game_factory, port, auth, minplayers, maxplayers=None): +def start(game_factory, port, auth, minplayers, maxplayers=None): flagger = Flagger(('localhost', 6668), auth) manager = Manager(game_factory, flagger, minplayers, maxplayers) listener = Listener(('', port), Player, manager) - loop() + return (flagger, manager, listener) + +def run(game_factory, port, auth, minplayers, maxplayers=None): + start(game_factory, port, auth, minplayers, maxplayers) + asyncore.loop(use_poll=True) diff --git a/histogram.py b/histogram.py index b638fe4..3d5bc9c 100755 --- a/histogram.py +++ b/histogram.py @@ -46,19 +46,22 @@ def main(s=None): write_scores(when) scoresfile.flush() - gp = os.popen('gnuplot 2> /dev/null', 'w') - gp.write('set style data lines\n') - gp.write('set xdata time\n') - gp.write('set timefmt "%s"\n') - gp.write('set format ""\n') - gp.write('set border 3\n') - gp.write('set xtics nomirror\n') - gp.write('set ytics nomirror\n') - gp.write('set nokey\n') - gp.write('set terminal png transparent size 640,200 x000000 xffffff\n') - gp.write('set output "histogram.png"\n') - gp.write('plot %s\n' % ','.join(plotparts)) - gp.close() + instructions = tempfile.NamedTemporaryFile('w') + instructions.write(''' +set style data lines +set xdata time +set timefmt "%%s" +set format "" +set border 3 +set xtics nomirror +set ytics nomirror +set nokey +set terminal png transparent size 640,200 x000000 xffffff +set output "histogram.png" +plot %(plot)s\n''' % {'plot': ','.join(plotparts)}) + instructions.flush() + + gp = os.system('gnuplot %s' % instructions.name) if __name__ == '__main__': main() diff --git a/pointscli.py b/pointscli.py index 0e3e89b..c4bd659 100755 --- a/pointscli.py +++ b/pointscli.py @@ -17,11 +17,11 @@ def submit(sock, cat, team, score): break b = sock.recv(500) try: - when, txt = points.decode_response(b) + when, cat_, txt = points.decode_response(b) except ValueError: # Ignore invalid packets continue - if when != mark: + if (when != mark) or (cat_ != cat): # Ignore wrong timestamp continue if txt == 'OK': diff --git a/roshambo.py b/roshambo.py index 81e2497..db22462 100755 --- a/roshambo.py +++ b/roshambo.py @@ -35,9 +35,13 @@ class Roshambo(game.TurnBasedGame): self.make_move(player, 'paper') -def main(): - game.run(Roshambo, 5388, b'roshambo:::984233f357ecac03b3e38b9414cd262b', 2) +def start(): + return game.start(Roshambo, 5388, b'roshambo:::984233f357ecac03b3e38b9414cd262b', 2) if __name__ == '__main__': - main() + import asyncore + + start() + asyncore.loop(use_poll=True) + diff --git a/roshambocli.py b/roshambocli.py index 28a3b4f..7ded7ac 100755 --- a/roshambocli.py +++ b/roshambocli.py @@ -27,7 +27,7 @@ class Client: if not line: return if self.debug: - print ('<-- %s' % line) + print (self, '<-- %s' % line) return json.loads(line) def command(self, *val): @@ -41,29 +41,33 @@ class Client: return ret -class RandomBot(threading.Thread): - def __init__(self, team): +class IdiotBot(threading.Thread): + def __init__(self, team, move): threading.Thread.__init__(self) self.team = team + self.move = move + + def get_move(self): + return self.move def run(self): c = Client(('localhost', 5388)) - c.debug = True + c.debug = False #print('lobby', c.command('^', 'lobby')) c.command('login', self.team, 'furble') while True: - move = random.choice(['rock', 'scissors', 'paper']) + move = self.get_move() ret = c.command(move) if ret == ['WIN']: print('%s wins' % self.team) - amt = random.uniform(0.2, 2.1) + amt = random.uniform(0.1, 1.2) if c.debug: print(c, 'sleep %f' % amt) time.sleep(amt) def main(): bots = [] - for i in ['zebra', 'aardvark', 'wembly']: - bots.append(RandomBot(i).start()) + for team, move in (('rockbot', 'rock'), ('cutbot', 'scissors'), ('paperbot', 'paper')): + bots.append(IdiotBot(team, move).start()) main() diff --git a/scoreboard.cgi b/scoreboard.cgi index 58d544e..2f42bc0 100755 --- a/scoreboard.cgi +++ b/scoreboard.cgi @@ -35,7 +35,7 @@ for cat, total in categories: for points, team in scores: color = teamcolors[team] print('
' % (float(points * 100)/total, color)) - print(' %s: %d' % (cat, team, points)) + print(' %s: %d' % (cat, team, points)) print('
') print('') print('') diff --git a/ubersrv.py b/ubersrv.py index de0bb83..78ccc4b 100755 --- a/ubersrv.py +++ b/ubersrv.py @@ -2,12 +2,15 @@ import asyncore import pointsd +import roshambo +import game import flagd import histogram def main(): pointsrv = pointsd.start() flagsrv = flagd.start() + roshambosrv = roshambo.start() s = pointsrv.store slen = 0 while True: