diff --git a/pelita/game.py b/pelita/game.py index add90fe52..f3d8c6e61 100644 --- a/pelita/game.py +++ b/pelita/game.py @@ -43,10 +43,12 @@ DEAD_ENDS = 0.25 class TkViewer: - def __init__(self, *, address, controller, geometry=None, delay=None, stop_after=None, fullscreen=False): - self.proc = self._run_external_viewer(address, controller, geometry=geometry, delay=delay, stop_after=stop_after, fullscreen=fullscreen) + def __init__(self, *, address, controller, geometry=None, delay=None, + stop_after=None, stop_after_kill=False, fullscreen=False): + self.proc = self._run_external_viewer(address, controller, geometry=geometry, delay=delay, + stop_after=stop_after, stop_after_kill=stop_after_kill, fullscreen=fullscreen) - def _run_external_viewer(self, subscribe_sock, controller, geometry, delay, stop_after, fullscreen): + def _run_external_viewer(self, subscribe_sock, controller, geometry, delay, stop_after, stop_after_kill, fullscreen): # Something on OS X prevents Tk from running in a forked process. # Therefore we cannot use multiprocessing here. subprocess works, though. viewer_args = [ str(subscribe_sock) ] @@ -60,6 +62,8 @@ def _run_external_viewer(self, subscribe_sock, controller, geometry, delay, stop viewer_args += ["--delay", str(delay)] if stop_after is not None: viewer_args += ["--stop-after", str(stop_after)] + if stop_after_kill: + viewer_args += ["--stop-after-kill"] tkviewer = 'pelita.scripts.pelita_tkviewer' external_call = [sys.executable, @@ -251,12 +255,14 @@ def setup_viewers(viewers=None, options=None, print_result=True): if viewer_state['controller']: proc = TkViewer(address=zmq_publisher.socket_addr, controller=viewer_state['controller'].socket_addr, stop_after=options.get('stop_at'), + stop_after_kill=options.get('stop_after_kill'), geometry=options.get('geometry'), delay=options.get('delay'), fullscreen=options.get('fullscreen')) else: proc = TkViewer(address=zmq_publisher.socket_addr, controller=None, stop_after=options.get('stop_at'), + stop_after_kill=options.get('stop_after_kill'), geometry=options.get('geometry'), delay=options.get('delay'), fullscreen=options.get('fullscreen')) diff --git a/pelita/scripts/pelita_main.py b/pelita/scripts/pelita_main.py index 190dd0b73..a12b8345e 100755 --- a/pelita/scripts/pelita_main.py +++ b/pelita/scripts/pelita_main.py @@ -245,6 +245,8 @@ def long_help(s): parser.set_defaults(timeout_length=3) game_settings.add_argument('--stop-at', dest='stop_at', type=int, metavar="N", help='Stop before playing round N.') +game_settings.add_argument('--stop-after-kill', dest='stop_after_kill', action='store_true', + help='Stop when a bot has been killed.') viewer_settings = parser.add_argument_group('Viewer settings') viewer_settings.add_argument('--geometry', type=geometry_string, metavar='NxM', @@ -363,12 +365,14 @@ def main(): geometry = args.geometry delay = int(1000./args.fps) stop_at = args.stop_at + stop_after_kill = args.stop_after_kill viewer_options = { "fullscreen" : args.fullscreen, "geometry": geometry, "delay": delay, - "stop_at": stop_at + "stop_at": stop_at, + "stop_after_kill": stop_after_kill } if args.reply_to: diff --git a/pelita/scripts/pelita_tkviewer.py b/pelita/scripts/pelita_tkviewer.py index 1725281bc..c6965a585 100755 --- a/pelita/scripts/pelita_tkviewer.py +++ b/pelita/scripts/pelita_tkviewer.py @@ -37,6 +37,8 @@ def geometry_string(s): help='delay') parser.add_argument('--stop-after', type=int, metavar="N", help='Stop after N rounds.') +parser.add_argument('--stop-after-kill', action='store_true', + help='Stop after a bot has been killed.') parser._optionals = parser.add_argument_group('Options') parser.add_argument('--version', help='show the version number and exit', action='store_const', const=True) @@ -59,7 +61,8 @@ def main(): 'geometry': args.geometry, 'fullscreen' : args.fullscreen, 'delay': args.delay, - 'stop_after': args.stop_after + 'stop_after': args.stop_after, + 'stop_after_kill': args.stop_after_kill } v = TkViewer(**{k: v for k, v in list(tkargs.items()) if v is not None}) v.run() diff --git a/pelita/ui/tk_canvas.py b/pelita/ui/tk_canvas.py index 64ffe540b..752365d0e 100644 --- a/pelita/ui/tk_canvas.py +++ b/pelita/ui/tk_canvas.py @@ -157,7 +157,7 @@ class UI: class TkApplication: def __init__(self, window, controller_address=None, - geometry=None, delay=1, stop_after=None, fullscreen=False): + geometry=None, delay=1, stop_after=None, stop_after_kill=False, fullscreen=False): self.window = window self.window.configure(background="white") @@ -344,6 +344,9 @@ def __init__(self, window, controller_address=None, self._stop_after_delay = delay if self._stop_after is not None: self._delay = self._min_delay + self._stop_after_kill = stop_after_kill + # This will be set once we get data + self._last_bot_was_killed = [] self._check_speed_button_state() @@ -1139,6 +1142,19 @@ def observe(self, game_state): game_state['bots'] = _ensure_list_tuples(game_state['bots']) game_state['shape'] = tuple(game_state['shape']) game_state['food_age'] = {tuple(pos): food_age for pos, food_age in game_state['food_age']} + + # check if a bot has been killed in the last round + # gs.bot_was_killed does not reset the True state for a killed bot + # until a whole round is over, so we have to cache previous values + bot_was_killed = False + for last, now in zip(self._last_bot_was_killed, game_state['bot_was_killed']): + if now and not last: + bot_was_killed = True + self._last_bot_was_killed = game_state['bot_was_killed'] + + if self._stop_after_kill and bot_was_killed: + self.running = False + self.update(game_state) if self._stop_after is not None: if self._stop_after == 0: diff --git a/pelita/ui/tk_viewer.py b/pelita/ui/tk_viewer.py index 47eeab773..ddbbb4ab8 100644 --- a/pelita/ui/tk_viewer.py +++ b/pelita/ui/tk_viewer.py @@ -71,13 +71,14 @@ class TkViewer: app : The TkApplication class """ - def __init__(self, address, controller_address=None, geometry=None, delay=1, stop_after=None, fullscreen=False): + def __init__(self, address, controller_address=None, geometry=None, delay=1, stop_after=None, stop_after_kill=False, fullscreen=False): self.address = address self.controller_address = controller_address self.delay = delay self.geometry = geometry if geometry else (900, 580) self.fullscreen = fullscreen self.stop_after = stop_after + self.stop_after_kill = stop_after_kill self.context = zmq.Context() self.socket = self.context.socket(zmq.SUB) @@ -113,7 +114,9 @@ def run(self): controller_address=self.controller_address, geometry=self.geometry, delay=self.delay, - stop_after=self.stop_after, fullscreen=self.fullscreen) + stop_after=self.stop_after, + stop_after_kill=self.stop_after_kill, + fullscreen=self.fullscreen) # schedule next read self.root.after_idle(self.read_queue) try: