diff --git a/pelita/ui/tk_canvas.py b/pelita/ui/tk_canvas.py index ba885533d..130dcadaf 100644 --- a/pelita/ui/tk_canvas.py +++ b/pelita/ui/tk_canvas.py @@ -290,6 +290,7 @@ def __init__(self, window, controller_address=None, self.init_bot_sprites([None] * 4) self._game_state = {} + self.history = {} self.ui_game_canvas = tkinter.Canvas(self.window) self.ui_game_canvas.configure(background="white", bd=0, highlightthickness=0, relief='flat') @@ -351,19 +352,22 @@ def __init__(self, window, controller_address=None, **LABEL_STYLE) self.ui_status_selected.pack(side=tkinter.RIGHT) + # previous tkinter.Button(self.ui_status_00, - text="PLAY/PAUSE", - command=self.toggle_running, + text="previous", + command=self.button_show_previous, **BUTTON_STYLE).pack(side=tkinter.LEFT, expand=True, **BUTTON_PADDING) + # play/pause tkinter.Button(self.ui_status_00, - text="STEP", - command=self.request_step, + text="play/pause", + command=self.toggle_running, **BUTTON_STYLE).pack(side=tkinter.LEFT, expand=True, **BUTTON_PADDING) + # next tkinter.Button(self.ui_status_00, - text="ROUND", - command=self.request_round, + text="next", + command=self.button_show_next, **BUTTON_STYLE).pack(side=tkinter.LEFT, expand=True, **BUTTON_PADDING) tkinter.Button(self.ui_status_01, @@ -1153,6 +1157,85 @@ def request_step(self): _logger.debug('---> play_step') self.controller_socket.send_json({"__action__": "play_step"}) + def get_current_pointer(self): + GS = self._game_state + + # the game state might be empty; + # happens before any message has been received + if not GS: + return None + + round = GS["round"] + turn = GS["turn"] + + # the round is None on INIT game phase; + # return this game state to be saved under key `None` + if round is None: + return None + + # convert 1-indexed round to 0-indexed, convert to total turns + # played and add the turns in the current round + return (round - 1) * 4 + turn + + def show_previous(self): + """ + Show the previous game step. + """ + current = self.get_current_pointer() + + if current in (None, 0): + # we are either in the first or second game state recorded; + # so the previous step is always the first game state + new = None + else: + new = current - 1 + + # set the currently displayed game state + self._game_state = self.history[new] + + # update ui + self.update() + + def get_next_pointer(self): + """ + Get the pointer to the next game step. + """ + current = self.get_current_pointer() + + if current is None: + new = 0 + else: + new = current + 1 + + return new + + def show_next(self): + """ + Show the next step either from history or message queue. + """ + pointer = self.get_next_pointer() + + if pointer not in self.history: + # this gamestate is not existing yet; + # we need to get it from the message queue + self.request_step() + else: + # set the currently displayed game state + self._game_state = self.history[pointer] + + # update ui + self.update() + + def button_show_previous(self): + # put game in pause automatically when pushing the button + self.running = False + self.show_previous() + + def button_show_next(self): + # put game in pause automatically when pushing the button + self.running = False + self.show_next() + def request_round(self): if not self.controller_socket: return diff --git a/pelita/ui/tk_viewer.py b/pelita/ui/tk_viewer.py index 440243d98..88c0e4203 100644 --- a/pelita/ui/tk_viewer.py +++ b/pelita/ui/tk_viewer.py @@ -140,19 +140,26 @@ def read_queue(self): if self._delay > 100: self._delay = 100 try: - # read all events. - # if queue is empty, try again in a few ms - # we don’t want to block here and lock - # Tk animations - message = self.socket.recv_unicode(flags=zmq.NOBLOCK) - message = json.loads(message) - - _logger.debug(message["__action__"]) - # we currently don’t care about the action - game_state = message["__data__"] - if game_state: - self.app.observe(game_state, self.standalone_mode) - + next_history_pointer = self.app.get_next_pointer() + if self.app.running and next_history_pointer in self.app.history: + # we are running in history, so just show the next game state + # in history until we run out of states + self.app.show_next() + else: + # read all events. + # if queue is empty, try again in a few ms + # we don’t want to block here and lock + # Tk animations + message = self.socket.recv_unicode(flags=zmq.NOBLOCK) + message = json.loads(message) + + _logger.debug(message["__action__"]) + # we currently don’t care about the action + game_state = message["__data__"] + + if game_state: + self.app.observe(game_state, self.standalone_mode) + self.app.history[self.app.get_current_pointer()] = game_state self._delay = 2 self._after(2, self.read_queue) except zmq.Again: