Module livemelee.utils

Group of functions returning helpful stats, mainly info from gamestates.

Two kinds of gamestate functions: conditions returning bools, and pretty printers returning strings.

Also included is a controller state wrapper: loggable_controller().

These would be nice additions to the libmelee package, but we make do with this patchy submodule.

>>> stat_output = utils.func(real_gamestate_obj)
>>> print( utils.loggable_controller(controller.current) )
Expand source code
'''Group of functions returning helpful stats, mainly info from gamestates.

Two kinds of gamestate functions:
    conditions returning bools, and
    pretty printers returning strings.

Also included is a controller state wrapper: `loggable_controller()`.

These would be nice additions to the libmelee package,
but we make do with this patchy submodule.

>>> stat_output = utils.func(real_gamestate_obj)

>>> print( utils.loggable_controller(controller.current) )'''

from melee import ControllerState, Button, Menu, Action

### gamestate utils

### conditions
# todo: replace hard coded ports

def in_game(g):
    return g.menu_state in (Menu.IN_GAME, Menu.SUDDEN_DEATH)

def not_taunting(gamestate):
    return not gamestate.player[2].action in (Action.TAUNT_LEFT,
                                              Action.TAUNT_RIGHT)
def grounded(gamestate):
    return gamestate.player[2].on_ground


### pretty printing


def gamestate(g):
    return 'Gamestate: {}'.format(g)

def frame_num(g):
    return 'Frame num: {}'.format(g.frame)

def menu(g):
    return 'Menu: {}'.format(g.menu_state)

def distance(g):
    return 'Distance: {:4f}'.format(g.distance)

def percents(g):
    return 'Percents: {}%  {}%'.format(g.player[1].percent,
                                     g.player[2].percent)

# def percents(g, my_port, other_port):
#     return 'Percents: {}%  {}%'.format(g.player[my_port].percent,
#                                        g.player[other_port].percent)

def actions(g):
    return 'Action states: {}  {}'.format(g.player[1].action,
                                        g.player[2].action)
def stocks(g):
    return 'Stocks: {}  {}'.format(g.player[1].stock,
                                 g.player[2].stock)

def get_controller(g, port):
    return 'Controller: ' + str(loggable_controller(g.player[port].controller_state))

### controller wrapper

class _ComparableState(ControllerState):
    '''Extending with comparisons and smaller debug output.'''

    def __init__(self, existing):
        # constructor copying a melee.ControllerState
        self.button = {**existing.button}   # deepcopy for dict
        self.main_stick = existing.main_stick
        self.c_stick = existing.c_stick
        self.l_shoulder = existing.l_shoulder
        self.r_shoulder = existing.r_shoulder

    def active(self):
        # returns set of buttons(+sticks) not in default position
        # digitals
        active = {btn for btn, pressed in self.button.items() if pressed}
        # analogs

        active.add((Button.BUTTON_MAIN, *self.main_stick))
        active.add((Button.BUTTON_L, self.l_shoulder))
        # if not self.main_stick == (.5, .5): # perhaps rare float comparison error? not important now
        #     active.add(Button.BUTTON_MAIN)
        # if not self.c_stick == (.5, .5):
        #     active.add(Button.BUTTON_C)
        # if not self.l_shoulder == 0:
        #     active.add(Button.BUTTON_L)
        # if not self.r_shoulder == 0:
        #     active.add(Button.BUTTON_R)
        return active

    def __str__(self):
        btns = self.active()
        return ' '.join(str(b) for b in btns) if btns else 'neutral'
        # return ' '.join('{}:{}'.format(btn,pressed) for btn,pressed in self.button.items())

    def __eq__(self, other):
        # any buttons in different state?
        # should also compare stick positions (and then L/R amount) but oh well
        if isinstance(other, _ComparableState):
            return len(self - other) == 0
        return False

    def __sub__(self, other):
        return self.active() - other.active()

def loggable_controller(controller_state):
    '''Returns comparable controller state with smaller debug output.

    >>> print( loggable_controller(controller.current) )
    >>> print(( loggable_controller(controller.current) ==
    >>>         buttons(controller.prev) ))'''
    return _ComparableState(controller_state)

Functions

def actions(g)
Expand source code
def actions(g):
    return 'Action states: {}  {}'.format(g.player[1].action,
                                        g.player[2].action)
def distance(g)
Expand source code
def distance(g):
    return 'Distance: {:4f}'.format(g.distance)
def frame_num(g)
Expand source code
def frame_num(g):
    return 'Frame num: {}'.format(g.frame)
def gamestate(g)
Expand source code
def gamestate(g):
    return 'Gamestate: {}'.format(g)
def get_controller(g, port)
Expand source code
def get_controller(g, port):
    return 'Controller: ' + str(loggable_controller(g.player[port].controller_state))
def grounded(gamestate)
Expand source code
def grounded(gamestate):
    return gamestate.player[2].on_ground
def in_game(g)
Expand source code
def in_game(g):
    return g.menu_state in (Menu.IN_GAME, Menu.SUDDEN_DEATH)
def loggable_controller(controller_state)

Returns comparable controller state with smaller debug output.

>>> print( loggable_controller(controller.current) )
>>> print(( loggable_controller(controller.current) ==
>>>         buttons(controller.prev) ))
Expand source code
def loggable_controller(controller_state):
    '''Returns comparable controller state with smaller debug output.

    >>> print( loggable_controller(controller.current) )
    >>> print(( loggable_controller(controller.current) ==
    >>>         buttons(controller.prev) ))'''
    return _ComparableState(controller_state)
def menu(g)
Expand source code
def menu(g):
    return 'Menu: {}'.format(g.menu_state)
def not_taunting(gamestate)
Expand source code
def not_taunting(gamestate):
    return not gamestate.player[2].action in (Action.TAUNT_LEFT,
                                              Action.TAUNT_RIGHT)
def percents(g)
Expand source code
def percents(g):
    return 'Percents: {}%  {}%'.format(g.player[1].percent,
                                     g.player[2].percent)
def stocks(g)
Expand source code
def stocks(g):
    return 'Stocks: {}  {}'.format(g.player[1].stock,
                                 g.player[2].stock)