Пример #1
0
class PunishCoach(metaclass=Singleton):
    """
    """
    def __init__(self, launcher: Launcher):
        self.__launcher = launcher
        self.__current_punish_window = None
        self.__frames_since_new_window = 0

        subscriber = Subscriber()
        self.__launcher.publisher.register(Launcher.Event.UPDATED, subscriber,
                                           self.update_punish_window)
        self.publisher = Publisher(PunishWindowEvent)

    def update_punish_window(self, success):
        if success:
            if self.__launcher.game_state.state_log[-1].is_player_player_one:
                cyclopedia = self.__launcher.cyclopedia_p2
            else:
                cyclopedia = self.__launcher.cyclopedia_p1

            last_punish_window = self.__current_punish_window
            self.__current_punish_window = next(
                (punish_window
                 for punish_window in reversed(cyclopedia.punish_windows)
                 if punish_window.result != PunishResult.NOT_YET_CLOSED), None)

            if self.__current_punish_window:
                if self.__current_punish_window != last_punish_window:
                    self.__frames_since_new_window = 0
                    self.publisher.dispatch(PunishWindowEvent.CHANGED,
                                            self.__current_punish_window)
                else:
                    self.__frames_since_new_window += 1
                    self.publisher.dispatch(PunishWindowEvent.SAME,
                                            self.__frames_since_new_window)
Пример #2
0
    def __init__(self, launcher: Launcher):
        self.__launcher = launcher
        self.__current_punish_window = None
        self.__frames_since_new_window = 0

        subscriber = Subscriber()
        self.__launcher.publisher.register(Launcher.Event.UPDATED, subscriber,
                                           self.update_punish_window)
        self.publisher = Publisher(PunishWindowEvent)
Пример #3
0
    def __init__(self):
        self.game_io_manager = ProcessIOManager()
        self.duplicate_frame_obtained = 0
        self.state_log = []
        self.graphic_settings = None
        self.mirrored_state_log = []
        self.is_mirrored = False
        self.futurestate_log = None

        self.graphic_settings_publisher = Publisher(GraphicSettingsChangeEvent)
Пример #4
0
 def __init__(self, view, extended_print=False):
     self.view = view
     self.extended_print = extended_print
     self.initialized = False
     self.publisher = Publisher(Launcher.Event)
     self.game_state = TekkenGameState()
     self.cyclopedia_p1 = TekkenEncyclopedia(
         True, print_extended_frame_data=self.extended_print
     )
     self.cyclopedia_p2 = TekkenEncyclopedia(
         False, print_extended_frame_data=self.extended_print
     )
Пример #5
0
    def __init__(self, view, extended_print=False):
        self.view = view
        self.extended_print = extended_print
        self.initialized = False
        self.publisher = Publisher(Launcher.Event)
        self.game_state = None
        self.cyclopedia_p2 = None
        self.cyclopedia_p1 = None
        self.queue = queue.Queue()
        self.__run = True

        self.delay = Launcher.INITIAL_SHORT_DELAY
        self.last_executed_time = None
Пример #6
0
    def __init__(self):
        self.game_io_manager = ProcessIOManager()
        self.duplicate_frame_obtained = 0
        self.state_log = []
        self.graphic_settings = None
        self.pad_controllers = defaultdict(lambda: None)
        self.mirrored_state_log = []
        self.is_mirrored = False
        self.futurestate_log = None

        logging_handler = logging.StreamHandler(sys.stdout)
        logging_handler.setFormatter(Formatter())
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.DEBUG)
        self.logger.addHandler(logging_handler)

        self.graphic_settings_publisher = Publisher(GraphicSettingsChangeEvent)
Пример #7
0
    def __init__(self, view, extended_print=False):
        self.view = view
        self.extended_print = extended_print

        stdout_handler = logging.StreamHandler(sys.stdout)
        stdout_handler.setFormatter(Formatter())
        self.logger = logging.getLogger(__name__)
        self.logger.addHandler(stdout_handler)

        self.initialized = False
        self.publisher = Publisher(Launcher.Event)
        self.game_state = TekkenGameState()
        self.cyclopedia_p1 = TekkenEncyclopedia(
            True, print_extended_frame_data=self.extended_print
        )
        self.cyclopedia_p2 = TekkenEncyclopedia(
            False, print_extended_frame_data=self.extended_print
        )
Пример #8
0
class Launcher:
    """
    """
    class Event(enum.IntEnum):
        INITIALIZED = enum.auto()
        UPDATED = enum.auto()
        CLOSED = enum.auto()

    INITIAL_SHORT_DELAY = 2
    INITIAL_LONG_DELAY = 8

    def __init__(self, view, extended_print=False):
        self.view = view
        self.extended_print = extended_print
        self.initialized = False
        self.publisher = Publisher(Launcher.Event)
        self.game_state = TekkenGameState()
        self.cyclopedia_p1 = TekkenEncyclopedia(
            True, print_extended_frame_data=self.extended_print
        )
        self.cyclopedia_p2 = TekkenEncyclopedia(
            False, print_extended_frame_data=self.extended_print
        )

    def start(self):
        self.__update_launcher()

    def __update_launcher(self):
        start = os_time.now(resolution=os_time.Resolution.MILLI)
        sucessful = self.game_state.update()
        if sucessful:
            self.cyclopedia_p1.update(self.game_state)
            self.cyclopedia_p2.update(self.game_state)
        end = os_time.now(resolution=os_time.Resolution.MILLI)
        elapsed_time = (end - start)
        if self.game_state.is_pid_valid():
            if not self.initialized:
                self.initialized = True
                self.publisher.dispatch(Launcher.Event.INITIALIZED)
            else:
                self.publisher.dispatch(Launcher.Event.UPDATED, sucessful)
            self.view.after(
                max(
                    Launcher.INITIAL_SHORT_DELAY,
                    round(Launcher.INITIAL_LONG_DELAY - elapsed_time)
                ),
                self.__update_launcher
            )
        else:
            if self.initialized:
                self.initialized = False
                self.publisher.dispatch(Launcher.Event.CLOSED)
            self.view.after(1000, self.__update_launcher)
Пример #9
0
class Launcher:
    """
    """
    class Event(enum.IntEnum):
        INITIALIZED = enum.auto()
        UPDATED = enum.auto()
        CLOSED = enum.auto()

    INITIAL_SHORT_DELAY = 2
    INITIAL_LONG_DELAY = 8

    def __init__(self, view, extended_print=False):
        self.view = view
        self.extended_print = extended_print
        self.initialized = False
        self.publisher = Publisher(Launcher.Event)
        self.game_state = None
        self.cyclopedia_p2 = None
        self.cyclopedia_p1 = None
        self.queue = queue.Queue()
        self.__run = True

        self.delay = Launcher.INITIAL_SHORT_DELAY
        self.last_executed_time = None

    def start(self):
        self.__start()
        self.__update_launcher()

    def stop(self):
        self.__run = False

    def __start(self):
        try:
            message = self.queue.get_nowait()
            if isinstance(message, dict):
                self.game_state = message['game_state']
                self.cyclopedia_p1 = message['p1']
                self.cyclopedia_p2 = message['p2']
                if not self.initialized:
                    self.initialized = True
                    self.publisher.dispatch(Launcher.Event.INITIALIZED)
                self.publisher.dispatch(
                    Launcher.Event.UPDATED, message['sucessful']
                )
            else:
                self.initialized = False
                self.publisher.dispatch(Launcher.Event.CLOSED)

            self.__update_delay()
        except queue.Empty:
            pass
        self.view.after(self.delay, self.__start)

    def __update_delay(self):
        if self.last_executed_time is None:
            self.last_executed_time = time.time()
        else:
            now = time.time()
            elapsed_time = 1000 * (now - self.last_executed_time)
            avg_delay = (self.delay * 0.8 + elapsed_time * 0.2) / 2
            if avg_delay > Launcher.INITIAL_SHORT_DELAY:
                self.delay = int(avg_delay)
            self.last_executed_time = now

    def __update_launcher(self):
        def update(launcher_queue):
            if not self.game_state:
                game_state = TekkenGameState()
            if not self.cyclopedia_p1:
                cyclopedia_p1 = TekkenEncyclopedia(
                    True, print_extended_frame_data=self.extended_print
                )
            if not self.cyclopedia_p2:
                cyclopedia_p2 = TekkenEncyclopedia(
                    False, print_extended_frame_data=self.extended_print
                )
            while self.__run:
                start = time.time()
                sucessful = game_state.update()
                if sucessful:
                    cyclopedia_p1.update(game_state)
                    cyclopedia_p2.update(game_state)
                launcher_queue.put(
                    {
                        'sucessful': sucessful,
                        'game_state': game_state,
                        'p1': cyclopedia_p1,
                        'p2': cyclopedia_p2
                    }
                )
                end = time.time()
                elapsed_time = end - start
                if game_state.get_reader().is_pid_valid():
                    time.sleep(
                        max(
                            Launcher.INITIAL_SHORT_DELAY / 1000,
                            Launcher.INITIAL_LONG_DELAY / 1000 - elapsed_time
                        )
                    )
                else:
                    if self.initialized:
                        launcher_queue.put(False)
                    time.sleep(1)

        threading.Thread(target=update, args=(self.queue,)).start()
Пример #10
0
class TekkenGameState:
    """
    """
    def __init__(self):
        self.game_io_manager = ProcessIOManager()
        self.duplicate_frame_obtained = 0
        self.state_log = []
        self.graphic_settings = None
        self.mirrored_state_log = []
        self.is_mirrored = False
        self.futurestate_log = None

        self.graphic_settings_publisher = Publisher(GraphicSettingsChangeEvent)

    def get_reader(self):
        return self.game_io_manager.process_reader

    def get_writer(self):
        return self.game_io_manager.process_writer

    def is_pid_valid(self):
        return self.game_io_manager.is_pid_valid()

    def update(self, buffer=0):
        """
        """
        game_state = self.game_io_manager.update(buffer)

        game_graphic_settings = game_state[0]
        game_battle_state = game_state[1]

        if game_battle_state is not None:
            # we don't run perfectly in sync, if we get back the same frame,
            # throw it away
            if (not self.state_log or game_battle_state.frame_count !=
                    self.state_log[-1].frame_count):
                self.duplicate_frame_obtained = 0

                frames_lost = 0
                if self.state_log:
                    frames_lost = (game_battle_state.frame_count -
                                   self.state_log[-1].frame_count - 1)

                for i in range(min(7 - buffer, frames_lost)):
                    dropped_game_state = self.game_io_manager.read_update(
                        min(7, frames_lost + buffer) - i)
                    dropped_game_graphic_settings = dropped_game_state[0]
                    dropped_game_battle_state = dropped_game_state[1]

                    if dropped_game_graphic_settings is not None:
                        self.compare_graphic_settings(
                            dropped_game_graphic_settings)

                    if dropped_game_battle_state is not None:
                        self.append_game_data(dropped_game_battle_state)

                self.append_game_data(game_battle_state)
                return True
            if game_battle_state.frame_count == self.state_log[-1].frame_count:
                self.duplicate_frame_obtained += 1
        if game_graphic_settings is not None:
            self.compare_graphic_settings(game_graphic_settings)
        return False

    def append_game_data(self, game_data):
        if not self.is_mirrored:
            self.state_log.append(game_data)
            self.mirrored_state_log.append(game_data.from_mirrored())
        else:
            self.state_log.append(game_data.from_mirrored())
            self.mirrored_state_log.append(game_data)

        if len(self.state_log) > 300:
            self.state_log.pop(0)
            self.mirrored_state_log.pop(0)

    def compare_graphic_settings(self, graphic_settings):
        graphic_settings_changed = False
        if not graphic_settings.equal_screen_mode(self.graphic_settings):
            graphic_settings_changed = True
            self.graphic_settings_publisher.dispatch(
                GraphicSettingsChangeEvent.SCREEN_MODE,
                graphic_settings.screen_mode)
        if not graphic_settings.equal_resolution(self.graphic_settings):
            graphic_settings_changed = True
            self.graphic_settings_publisher.dispatch(
                GraphicSettingsChangeEvent.RESOLUTION,
                graphic_settings.resolution)
        if not graphic_settings.equal_position(self.graphic_settings):
            graphic_settings_changed = True
            self.graphic_settings_publisher.dispatch(
                GraphicSettingsChangeEvent.POSITION, graphic_settings.position)
        if graphic_settings_changed:
            self.graphic_settings = graphic_settings

    def flip_mirror(self):
        temp_log = self.mirrored_state_log
        self.mirrored_state_log = self.state_log
        self.state_log = temp_log
        self.is_mirrored = not self.is_mirrored

    def back_to_the_future(self, frames):
        if self.futurestate_log is not None:
            raise AssertionError('Already called BackToTheFuture, '
                                 'need to return to the present first, Marty')
        self.futurestate_log = self.state_log[0 - frames:]
        self.state_log = self.state_log[:0 - frames]

    def return_to_present(self):
        if self.futurestate_log is None:
            raise AssertionError(
                "We're already in the present, Marty, what are you doing?")
        self.state_log += self.futurestate_log
        self.futurestate_log = None

    def is_game_happening(self):
        return (not self.game_io_manager.process_reader.
                is_state_reacquisition_required())

    def is_bot_on_left(self):
        is_player_one_on_left = (
            self.game_io_manager.process_reader.original_facing ==
            self.state_log[-1].facing_bool)
        if not self.is_mirrored:
            return is_player_one_on_left
        return not is_player_one_on_left

    def get_bot_health(self):
        return max(0, 170 - self.state_log[-1].bot.damage_taken)

    def get_dist(self):
        return self.state_log[-1].get_distance()

    def did_opp_combo_counter_just_start_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return (self.state_log[0 - frames_ago].opp.combo_counter == 1 and
                    self.state_log[0 - frames_ago - 1].opp.combo_counter == 0)
        return False

    def did_opp_combo_counter_just_end_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return (self.state_log[0 - frames_ago].opp.combo_counter == 0 and
                    self.state_log[0 - frames_ago - 1].opp.combo_counter > 0)
        return False

    def get_opp_combo_damage_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return self.state_log[0 - frames_ago].opp.combo_damage
        return 0

    def get_opp_combo_hits_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return self.state_log[0 - frames_ago].opp.combo_counter
        return 0

    def get_opp_juggle_damage_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return self.state_log[0 - frames_ago].opp.juggle_damage
        return 0

    def did_bot_start_getting_punished_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return self.state_log[0 - frames_ago].bot.is_punish()
        else:
            return False

    def did_opp_start_getting_punished_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return self.state_log[0 - frames_ago].opp.is_punish()
        else:
            return False

    def bot_frames_until_recovery_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return (self.state_log[0 - frames_ago].bot.recovery -
                    self.state_log[0 - frames_ago].bot.move_timer)
        else:
            return 99

    def opp_frames_until_recovery_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return (self.state_log[0 - frames_ago].opp.recovery -
                    self.state_log[0 - frames_ago].opp.move_timer)
        else:
            return 99

    def is_bot_blocking(self):
        return self.state_log[-1].bot.is_blocking()

    def is_bot_getting_counter_hit(self):
        return self.state_log[-1].bot.is_getting_counter_hit()

    def is_bot_getting_hit_on_ground(self):
        return self.state_log[-1].bot.is_getting_ground_hit()

    def is_opp_blocking(self):
        return self.state_log[-1].opp.is_blocking()

    def is_opp_getting_hit(self):
        return self.state_log[-1].opp.is_getting_hit()

    def is_bot_getting_hit(self):
        return self.state_log[-1].bot.is_getting_hit()
        # return self.get_frames_since_bot_took_damage() < 15

    def is_opp_hitting(self):
        return self.state_log[-1].opp.is_hitting()

    def is_bot_started_getting_hit(self):
        if len(self.state_log) > 2:
            return (self.is_bot_getting_hit()
                    and not self.state_log[-2].bot.is_getting_hit())
        return False

    def is_bot_started_being_thrown(self):
        if len(self.state_log) > 2:
            return (self.is_bot_being_thrown()
                    and not self.state_log[-2].opp.is_in_throwing())
        return False

    def is_bot_coming_out_of_block(self):
        if len(self.state_log) >= 2:
            previous_state = self.state_log[-2].bot.is_blocking()
            current_state = self.state_log[-1].bot.is_blocking()
            return previous_state and not current_state
        return False

    def get_recovery_of_move_id(self, move_id):
        largest_time = -1
        for state in reversed(self.state_log):
            if state.bot.move_id == move_id:
                largest_time = max(largest_time, state.bot.move_timer)
        return largest_time

    def get_last_move_id(self):
        for state in reversed(self.state_log):
            if state.bot.startup > 0:
                return state.bot.move_id
        return -1

    def get_bot_just_move_id(self):
        return self.state_log[-2].bot.move_id

    def did_bot_recently_do_move(self):
        if len(self.state_log) > 5:
            return (self.state_log[-1].bot.move_timer <
                    self.state_log[-5].bot.move_timer)
        return False

    def did_bot_recently_do_damage(self):
        if len(self.state_log) > 10:
            if (self.state_log[-1].opp.damage_taken >
                    self.state_log[-20].opp.damage_taken):
                return True
        return False

    def is_bot_crouching(self):
        return self.state_log[-1].bot.is_technical_crouch()

    def is_opp_attack_mid(self):
        return self.state_log[-1].opp.is_attack_mid()

    def is_opp_attack_unblockable(self):
        return self.state_log[-1].opp.is_attack_unblockable()

    def is_opp_attack_antiair(self):
        return self.state_log[-1].opp.is_attack_antiair()

    def is_opp_attack_throw(self):
        return self.state_log[-1].opp.is_attack_throw()

    def is_opp_attack_low(self):
        return self.state_log[-1].opp.is_attack_low()

    def is_opp_attacking(self):
        return self.state_log[-1].opp.is_attack_starting()

    # only finds landing canceled moves?
    def get_opp_move_interrupted_frames(self):
        if len(self.state_log) > 3:
            if self.state_log[-1].opp.move_timer == 1:
                interrupted_frames = self.state_log[-2].opp.move_timer - (
                    self.state_log[-3].opp.move_timer + 1)
                if interrupted_frames > 0:
                    # landing animation causes move_timer to go *up* to the end
                    # of the move
                    return interrupted_frames
        return 0

    def get_frames_until_out_of_block(self):
        # print(self.state_log[-1].bot.block_flags)
        if not self.is_bot_blocking():
            return 0
        recovery = self.state_log[-1].bot.recovery
        block_frames = self.get_frames_bot_has_been_blocking_attack()
        return (recovery) - block_frames

    def get_frame_progress_of_opp_attack(self):
        most_recent_state_with_attack = None
        frames_since_last_attack = 0
        for state in reversed(self.state_log):
            if most_recent_state_with_attack is None:
                if state['p2_attack_startup'] > 0:
                    most_recent_state_with_attack = state
            elif (state['p2_move_id']
                  == most_recent_state_with_attack.opp.move_id
                  and state.opp.move_timer <
                  most_recent_state_with_attack.opp.move_timer):
                frames_since_last_attack += 1
            else:
                break
        return frames_since_last_attack

    def get_frames_bot_has_been_blocking_attack(self):
        if not self.state_log[-1].bot.is_blocking():
            return 0

        opponent_move_id = self.state_log[-1].opp.move_id
        opponent_move_timer = self.state_log[-1].opp.move_timer

        frames_spent_blocking = 0
        for state in reversed(self.state_log):
            if (state.bot.is_blocking()
                    and state.opp.move_timer <= opponent_move_timer
                    and state.opp.move_id == opponent_move_id
                    and state.opp.move_timer > state.opp.startup):
                frames_spent_blocking += 1
                opponent_move_timer = state.opp.move_timer
            else:
                break
        return frames_spent_blocking

    def is_opp_whiffing_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return self.state_log[0 - frames_ago].opp.is_attack_whiffing()
        else:
            return False

    def is_opp_whiffing(self):
        return self.state_log[-1].opp.is_attack_whiffing()

    def is_bot_whiffing(self):
        return self.state_log[-1].bot.is_attack_whiffing()

    def is_bot_while_standing(self):
        return self.state_log[-1].bot.is_while_standing()

    def get_bot_frames_until_recovery_ends(self):
        return (self.state_log[-1].bot.recovery -
                self.state_log[-1].bot.move_timer)

    def is_bot_move_changed(self):
        if len(self.state_log) > 2:
            return (self.state_log[-1].bot.move_id !=
                    self.state_log[-2].bot.move_id)
        return False

    def is_bot_whiffing_alt(self):
        current_bot = self.state_log[-1].bot
        if current_bot.startup == 0:  # we might still be in recovery
            for _, state in enumerate(reversed(self.state_log)):
                if state.bot.startup > 0:
                    pass
        else:
            return current_bot.is_attack_whiffing()

    def get_opponent_move_id_with_character_marker(self):
        character_marker = self.state_log[-1].opp.char_id
        return self.state_log[-1].opp.move_id + character_marker * 10000000

    def get_opp_startup(self):
        return self.state_log[-1].opp.startup

    def get_bot_startup_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return self.state_log[0 - frames_ago].bot.startup
        return False

    def get_opp_active_frames(self):
        return self.state_log[-1].opp.get_active_frames()

    def get_last_active_frame_hit_was_on(self, frames):
        return_next_state = False
        for state in reversed(self.state_log[-(frames + 2):]):
            if return_next_state:
                return (state.opp.move_timer - state.opp.startup) + 1
            if state.bot.move_timer == 1:
                return_next_state = True
        return 0

    def get_opp_active_frames_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return self.state_log[0 - frames_ago].opp.get_active_frames()
        return 0

    def get_opp_recovery(self):
        return self.state_log[-1].opp.recovery

    def get_opp_frames_till_next_move(self):
        return self.get_opp_recovery() - self.get_opp_move_timer()

    def get_bot_frames_till_next_move(self):
        return self.get_bot_recovery() - self.get_bot_move_timer()

    def get_bot_recovery(self):
        return self.state_log[-1].bot.recovery

    def get_opp_move_id(self):
        return self.state_log[-1].opp.move_id

    def get_opp_attack_type(self):
        return self.state_log[-1].opp.attack_type

    def get_opp_attack_type_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return self.state_log[0 - frames_ago].opp.attack_type
        return False

    def get_bot_move_id(self):
        return self.state_log[-1].bot.move_id

    def get_bot_startup(self):
        return self.state_log[-1].bot.startup

    def get_bot_move_timer(self):
        return self.state_log[-1].bot.move_timer

    def get_opp_move_timer(self):
        return self.state_log[-1].opp.move_timer

    def is_bot_attack_starting(self):
        return (self.get_bot_startup() - self.get_bot_move_timer()) > 0

    def get_opp_time_until_impact(self):
        return (self.get_opp_startup() - self.state_log[-1].opp.move_timer +
                self.state_log[-1].opp.get_active_frames())

    def get_bot_time_until_impact(self):
        return (self.get_bot_startup() - self.state_log[-1].bot.move_timer +
                self.state_log[-1].bot.get_active_frames())

    def is_bot_on_ground(self):
        return self.state_log[-1].bot.is_on_ground()

    def is_bot_being_knocked_down(self):
        return self.state_log[-1].bot.is_being_knocked_down()

    def is_bot_being_wall_splatted(self):
        return self.state_log[-1].bot.is_getting_wall_splatted()

    def get_opp_damage(self):
        return self.state_log[-1].opp.attack_damage

    def get_most_recent_opp_damage(self):
        if self.state_log[-1].opp.attack_damage > 0:
            return self.state_log[-1].opp.attack_damage

        current_health = self.state_log[-1].bot.damage_taken

        for state in reversed(self.state_log):
            if state.bot.damage_taken < current_health:
                return current_health - state.bot.damage_taken
        return 0

    def get_opp_latest_non_zero_startup_and_damage(self):
        for state in reversed(self.state_log):
            damage = state.opp.attack_damage
            startup = state.opp.startup
            if damage > 0 or startup > 0:
                return (startup, damage)
        return (0, 0)

    def is_bot_just_grounded(self):
        if len(self.state_log) > 2:
            return (self.state_log[-1].bot.is_on_ground()
                    and not self.state_log[-2].bot.is_on_ground()
                    and not self.state_log[-2].bot.is_being_juggled()
                    and not self.state_log[-2].bot.is_being_knocked_down())
        return False

    def is_bot_being_juggled(self):
        return self.state_log[-1].bot.is_being_juggled()

    def is_bot_started_being_juggled(self):
        if len(self.state_log) > 2:
            return (self.state_log[-1].bot.is_being_juggled()
                    and not self.state_log[-2].bot.is_being_juggled())
        return False

    def is_bot_being_thrown(self):
        return self.state_log[-1].opp.is_in_throwing()

    def is_opp_wall_splat(self):
        return self.state_log[-1].opp.is_wall_splat()

    def did_bot_just_take_damage(self, frames_ago=1):
        if len(self.state_log) > frames_ago:
            return max(
                0, self.state_log[0 - frames_ago].bot.damage_taken -
                self.state_log[0 - frames_ago - 1].bot.damage_taken)
        return 0

    def did_opp_just_take_damage(self, frames_ago=1):
        if len(self.state_log) > frames_ago:
            return max(
                0, self.state_log[0 - frames_ago].opp.damage_taken -
                self.state_log[0 - frames_ago - 1].opp.damage_taken)
        return 0

    def did_opp_take_damage_during_startup(self):
        current_damage_taken = self.state_log[-1].opp.damage_taken
        current_move_timer = self.state_log[-1].opp.move_timer
        for state in reversed(self.state_log):
            if state.opp.damage_taken < current_damage_taken:
                return True
            if current_move_timer < state.opp.move_timer:
                return False
            else:
                current_move_timer = state.opp.move_timer
        return False

    def did_bot_timer_interrupt_x_moves_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return (self.state_log[0 - frames_ago].bot.move_timer <
                    self.state_log[0 - frames_ago - 1].bot.move_timer)
        return False

    def did_bot_start_getting_hit_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return (
                self.state_log[0 - frames_ago].bot.is_getting_hit() and
                not self.state_log[0 - frames_ago - 1].bot.is_getting_hit())
        return False

    def did_opp_start_getting_hit_x_frames_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return (
                self.state_log[0 - frames_ago].opp.is_getting_hit() and
                not self.state_log[0 - frames_ago - 1].opp.is_getting_hit())
        return False

    def did_bot_id_change_x_moves_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return (self.state_log[0 - frames_ago].bot.move_id !=
                    self.state_log[0 - frames_ago - 1].bot.move_id)
        return False

    def did_opp_id_change_x_moves_ago(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return (self.state_log[0 - frames_ago].opp.move_id !=
                    self.state_log[0 - frames_ago - 1].opp.move_id)
        return False

    def get_bot_elapsed_frames_of_rage_move(self, rage_move_startup):
        frozenFrames = 0
        last_move_timer = -1
        for state in reversed(self.state_log[-rage_move_startup:]):
            if state.bot.move_timer == last_move_timer:
                frozenFrames += 1
            last_move_timer = state.bot.move_timer
        return rage_move_startup - frozenFrames

    def is_opp_in_rage(self):
        return self.state_log[-1].opp.is_in_rage()

    def did_opponent_use_rage_recently(self, recentlyFrames):
        if not self.is_opp_in_rage():
            for state in reversed(self.state_log[-recentlyFrames:]):
                if state.opp.is_in_rage():
                    return True
        return False

    def get_frames_since_bot_took_damage(self):
        damage_taken = self.state_log[-1].bot.damage_taken
        for i, state in enumerate(reversed(self.state_log)):
            if state.bot.damage_taken < damage_taken:
                return i
        return 1000

    def get_last_opp_snapshot_with_different_move_id(self):
        moveId = self.state_log[-1].opp.move_id
        for state in reversed(self.state_log):
            if state.opp.move_id != moveId:
                return state
        return self.state_log[-1]

    def get_last_opp_with_different_move_id(self):
        return self.get_last_opp_snapshot_with_different_move_id().opp

    def get_opp_last_move_input(self):
        oppMoveId = self.state_log[-1].opp.move_id
        input = []
        for state in reversed(self.state_log):
            if (state.opp.move_id != oppMoveId
                    and state.opp.get_input_state()[1] != InputAttack.NULL):
                input.append(state.opp.get_input_state())
                return input

        return [(InputDirectionCodes.N, InputAttack.NULL, False)]

    def get_current_opp_move_string(self):
        if self.state_log[-1].opp.movelist_parser != None:
            move_id = self.state_log[-1].opp.move_id
            previous_move_id = -1

            input_array = []

            i = len(self.state_log)

            while True:
                next_move, last_move_was_empty_cancel = (
                    self.get_opp_move_string(move_id, previous_move_id))
                next_move = str(next_move)

                if last_move_was_empty_cancel:
                    input_array[-1] = ''

                input_array.append(next_move)

                if (self.state_log[-1].opp.movelist_parser.
                        can_be_done_from_neutral(move_id)):
                    break

                while True:
                    i -= 1
                    if i < 0:
                        break
                    if self.state_log[i].opp.move_id != move_id:
                        previous_move_id = move_id
                        move_id = self.state_log[i].opp.move_id
                        break
                if i < 0:
                    break

            clean_input_array = reversed(
                [a for a in input_array if len(a) > 0])
            return ','.join(clean_input_array)
        else:
            return 'N/A'

    def get_opp_move_string(self, move_id, previous_move_id):
        return self.state_log[-1].opp.movelist_parser.input_for_move(
            move_id, previous_move_id)

    def has_opp_returned_to_neutral_from_move_id(self, move_id):
        for state in reversed(self.state_log):
            if state.opp.move_id == move_id:
                return False
            if state.opp.movelist_parser.can_be_done_from_neutral(
                    state.opp.move_id):
                return True
        return True

    def get_frame_data_of_current_opp_move(self):
        if self.state_log[-1].opp.startup > 0:
            opp = self.state_log[-1].opp
        else:
            game_state = self.get_last_opp_snapshot_with_different_move_id()
            if game_state != None:
                opp = game_state.opp
            else:
                opp = self.state_log[-1].opp
        return self.get_frame_data(self.state_log[-1].bot, opp)

    def get_frame_data_of_current_bot_move(self):
        return self.get_frame_data(self.state_log[-1].opp,
                                   self.state_log[-1].bot)

    def get_frame_data(self, defendingPlayer, attackingPlayer):
        return ((defendingPlayer.recovery + attackingPlayer.startup) -
                attackingPlayer.recovery)

    def get_bot_char_id(self):
        char_id = self.state_log[-1].bot.char_id
        print("Character: " + str(char_id))
        return char_id

    def is_fulfill_jump_fallback_conditions(self):
        if len(self.state_log) > 10:
            if (self.state_log[-7].bot.is_airborne()
                    and self.state_log[-7].opp.is_airborne()):
                if (not self.state_log[-8].bot.is_airborne()
                        or not self.state_log[-8].opp.is_airborne()):
                    for state in self.state_log[-10:]:
                        if not (state.bot.is_holding_up()
                                or state.opp.is_holding_up()):
                            return False
                    return True
        return False

    def is_opp_able_to_act(self):
        return self.state_log[-1].opp.is_cancelable

    def get_bot_input_state(self):
        return self.state_log[-1].bot.get_input_state()

    def get_opp_input_state(self):
        return self.state_log[-1].opp.get_input_state()

    def get_bot_name(self):
        return self.state_log[-1].bot.character_name

    def get_opp_name(self):
        return self.state_log[-1].opp.character_name

    def get_bot_throw_tech(self, activeFrames):
        for state in reversed(self.state_log[-activeFrames:]):
            tech = state.bot.throw_tech
            if tech != ThrowTechs.NONE:
                return tech
        return ThrowTechs.NONE

    def get_opp_tracking_type(self, startup):
        if len(self.state_log) > startup:
            complex_states = [ComplexMoveStates.UNKN]
            for state in reversed(self.state_log[-startup:]):
                if -1 < state.opp.get_traicking_type().value < 8:
                    complex_states.append(state.opp.get_traicking_type())
            return Counter(complex_states).most_common(1)[0][0]
        else:
            return ComplexMoveStates.F_MINUS

    def get_opp_technical_states(self, startup):

        #opp_id = self.state_log[-1].opp.move_id
        tc_frames = []
        tj_frames = []
        cancel_frames = []
        buffer_frames = []
        pc_frames = []
        homing_frames1 = []
        homing_frames2 = []
        parryable_frames1 = []
        parryable_frames2 = []
        startup_frames = []
        frozen_frames = []

        previous_state = None
        skipped_frames_counter = 0
        frozen_frames_counter = 0
        for i, state in enumerate(reversed(self.state_log[-startup:])):
            if previous_state != None:
                is_skipped = (state.opp.move_timer !=
                              previous_state.opp.move_timer - 1)
                if is_skipped:
                    skipped_frames_counter += 1
                is_frozen = (
                    state.bot.move_timer == previous_state.bot.move_timer)
                if is_frozen:
                    frozen_frames_counter += 1
            else:
                is_skipped = False
                is_frozen = False
            if skipped_frames_counter + i <= startup:
                tc_frames.append(state.opp.is_technical_crouch())
                tj_frames.append(state.opp.is_technical_jump())
                cancel_frames.append(state.opp.is_cancelable)
                buffer_frames.append(state.opp.is_bufferable)
                pc_frames.append(state.opp.is_power_crush)
                homing_frames1.append(state.opp.is_homing1())
                homing_frames2.append(state.opp.is_homing2())
                parryable_frames1.append(state.opp.is_parry1)
                parryable_frames2.append(state.opp.is_parry2)
                startup_frames.append(is_skipped)
                frozen_frames.append(is_frozen)

            previous_state = state

        parryable1 = MoveDataReport('PY1', parryable_frames1)
        parryable2 = MoveDataReport('PY2', parryable_frames2)
        unparryable = MoveDataReport(
            'NO PARRY?',
            [not parryable1.is_present() and not parryable2.is_present()])

        return [
            MoveDataReport('TC', tc_frames),
            MoveDataReport('TJ', tj_frames),
            MoveDataReport('BUF', buffer_frames),
            MoveDataReport('xx', cancel_frames),
            MoveDataReport('PC', pc_frames),
            MoveDataReport('HOM1', homing_frames1),
            MoveDataReport('HOM2', homing_frames2),
            MoveDataReport('SKIP', startup_frames),
            MoveDataReport('FROZ', frozen_frames),
            # parryable1,
            # parryable2,
            # unparryable
        ]

    def is_fight_over(self):
        return self.duplicate_frame_obtained > 5

    def was_timer_reset(self):
        if len(self.state_log) > 2:
            return (self.state_log[-1].timer_frames_remaining >
                    self.state_log[-2].timer_frames_remaining)
        return False

    def did_timer_start_ticking(self, buffer):
        return self.state_log[-1].timer_frames_remaining == 3600 - 1 - buffer

    def was_fight_reset(self):
        false_reset_buffer = 0
        if len(self.state_log) > 2:
            return (
                self.state_log[-1].frame_count < self.state_log[-2].frame_count
                and self.state_log[-2].frame_count > false_reset_buffer)
        return False

    def get_timer(self, frames_ago):
        if len(self.state_log) > frames_ago:
            return self.state_log[-frames_ago].timer_frames_remaining
        return False

    def get_round_number(self):
        return self.state_log[-1].opp.wins + self.state_log[-1].bot.wins + 1

    def get_opp_round_summary(self, frames_ago):
        if len(self.state_log) > frames_ago:
            opp = self.state_log[-frames_ago].opp
            bot = self.state_log[-frames_ago].bot
            return (opp.wins, bot.damage_taken)
        return (0, 0)

    def get_range_of_move(self):
        move_timer = self.state_log[-1].opp.move_timer
        opp_id = self.state_log[-1].opp.move_id
        for state in reversed(self.state_log):
            starting_skeleton = state.opp.skeleton
            bot_skeleton = state.bot.skeleton
            old_dist = state.get_distance()
            if move_timer < state.opp.move_timer:
                break
            if opp_id != state.opp.move_id:
                break
            move_timer = state.opp.move_timer
        ending_skeleton = self.state_log[-1].opp.skeleton

        avg_ss_x = sum(starting_skeleton[0]) / len(starting_skeleton[0])
        avg_ss_z = sum(starting_skeleton[2]) / len(starting_skeleton[2])
        avg_bs_x = sum(bot_skeleton[0]) / len(bot_skeleton[0])
        avg_bs_z = sum(bot_skeleton[2]) / len(bot_skeleton[2])

        vector_towards_bot = (avg_bs_x - avg_ss_x, avg_bs_z - avg_ss_z)

        toward_bot_magnitude = math.sqrt(
            pow(vector_towards_bot[0], 2) + pow(vector_towards_bot[1], 2))
        unit_vector_towards_bot = (vector_towards_bot[0] /
                                   toward_bot_magnitude,
                                   vector_towards_bot[1] /
                                   toward_bot_magnitude)

        movements = [(ai_x - bi_x, ai_z - bi_z) for ai_x, bi_x, ai_z, bi_z in
                     zip(ending_skeleton[0], starting_skeleton[0],
                         ending_skeleton[2], starting_skeleton[2])]
        dotproducts = []
        for movement in movements:
            dotproducts.append(movement[0] * unit_vector_towards_bot[0] +
                               movement[1] * unit_vector_towards_bot[1])

        max_product = max(dotproducts)
        max_index = dotproducts.index(max_product)
        return max_index, max_product

    def is_bot_using_opp_movelist(self):
        return self.state_log[-1].bot.use_opponents_movelist

    def get_current_bot_move_name(self):
        move_id = self.state_log[-1].bot.move_id
        return self.get_opp_move_name(
            move_id,
            self.state_log[-1].bot.use_opponents_movelist,
            is_for_bot=True)

    def get_current_opp_move_name(self):
        move_id = self.state_log[-1].opp.move_id
        return self.get_opp_move_name(
            move_id,
            self.state_log[-1].opp.use_opponents_movelist,
            is_for_bot=False)

    def get_opp_move_name(self,
                          move_id,
                          use_opponents_movelist,
                          is_for_bot=False):
        if move_id > 30000:
            return 'Universal_{}'.format(move_id)

        try:
            if (not self.is_mirrored and not is_for_bot
                    or self.is_mirrored and is_for_bot):
                if not use_opponents_movelist:
                    movelist = (
                        self.game_io_manager.process_reader.p2_movelist_names)
                else:
                    movelist = (
                        self.game_io_manager.process_reader.p1_movelist_names)
            else:
                if not use_opponents_movelist:
                    movelist = (
                        self.game_io_manager.process_reader.p1_movelist_names)
                else:
                    movelist = (
                        self.game_io_manager.process_reader.p2_movelist_names)

            return movelist[(move_id * 2) + 4].decode('utf-8')
        except:
            return "ERROR"

    def is_tekken_foreground_wnd(self):
        return self.game_io_manager.process_reader.is_tekken_foreground_wnd()

    def is_in_battle(self):
        return self.game_io_manager.process_reader.is_in_battle