Module blaseball_mike.models.game

Expand source code
from .base import Base
from .modification import Modification
from .player import Player
from .statsheet import GameStatsheet
from .team import Team
from .stadium import Stadium
from .weather import Weather
from .. import database, chronicler


class Game(Base):
    """
    Represents one blaseball game
    """
    @classmethod
    def _get_fields(cls):
        p = cls.load_by_id("1cbd9d82-89e6-46b2-9082-815f59e1a130")
        return [cls._from_api_conversion(x) for x in p.fields]

    @classmethod
    def load_by_id(cls, id_):
        """
        Load by ID
        """
        return cls(database.get_game_by_id(id_))

    @classmethod
    def load_by_day(cls, season, day):
        """
        Load by in-game season and day. Season and Day are 1-indexed
        """
        return {
            id_: cls(game) for id_, game in database.get_games(season, day).items()
        }

    @classmethod
    def load_tournament_by_day(cls, tournament, day):
        """
        Loads all games played in a tournament on a given in-game day. Day is 1-indexed
        Tournament refers to things such as the Coffee Cup which exist outside the normal blaseball timeline.
        """
        return {
            id_: cls(game) for id_, game in database.get_tournament(tournament, day).items()
        }

    @classmethod
    def load_by_season(cls, season, team_id=None, day=None):
        """
        Return dictionary of games for a given season keyed by game ID.
        Can optionally be filtered by in-game day or team ID. Season and Day are 1-indexed
        """
        return {
            game["gameId"]: cls(game["data"]) for game in chronicler.get_games(team_ids=team_id, season=season, day=day)
        }

    @classmethod
    def load_by_tournament(cls, tournament, team_id=None, day=None):
        """
        Return dictionary of games for a given tournament keyed by game ID.
        Can optionally be filtered by in-game day or team ID. Day is 1-indexed
        """
        return {
            game["gameId"]: cls(game["data"]) for game in
            chronicler.get_games(team_ids=team_id, tournament=tournament, day=day)
        }

    @property
    def winning_team(self):
        return self.home_team if self.home_score > self.away_score else self.away_team

    @property
    def winning_team_name(self):
        return self.home_team_name if self.home_score > self.away_score else self.away_team_name

    @property
    def winning_team_nickname(self):
        return self.home_team_nickname if self.home_score > self.away_score else self.away_team_nickname

    @property
    def losing_team(self):
        return self.home_team if self.home_score < self.away_score else self.away_team

    @property
    def losing_team_name(self):
        return self.home_team_name if self.home_score < self.away_score else self.away_team_name

    @property
    def losing_team_nickname(self):
        return self.home_team_nickname if self.home_score < self.away_score else self.away_team_nickname

    @property
    def winning_score(self):
        return self.home_score if self.home_score > self.away_score else self.away_score

    @property
    def losing_score(self):
        return self.home_score if self.home_score < self.away_score else self.away_score

    @Base.lazy_load("_base_runner_ids", cache_name="_base_runners", default_value=list())
    def base_runners(self):
        players = Player.load(*self._base_runner_ids)
        return [players.get(id_) for id_ in self._base_runner_ids]

    @Base.lazy_load("_weather", use_default=False)
    def weather(self):
        return Weather.load_one(self._weather)

    @Base.lazy_load("_home_team_id", cache_name="_home_team")
    def home_team(self):
        return Team.load(self._home_team_id)

    @Base.lazy_load("_away_team_id", cache_name="_away_team")
    def away_team(self):
        return Team.load(self._away_team_id)

    @Base.lazy_load("_home_pitcher_id", cache_name="_home_pitcher")
    def home_pitcher(self):
        return Player.load_one(self._home_pitcher_id)

    @Base.lazy_load("_away_pitcher_id", cache_name="_away_pitcher")
    def away_pitcher(self):
        return Player.load_one(self._away_pitcher_id)

    @Base.lazy_load("_home_batter_id", cache_name="_home_batter")
    def home_batter(self):
        return Player.load_one(self._home_batter_id)

    @Base.lazy_load("_away_batter_id", cache_name="_away_batter")
    def away_batter(self):
        return Player.load_one(self._away_batter_id)

    @property
    def at_bat_team(self):
        if self.top_of_inning:
            return self.away_team
        else:
            return self.home_team

    @property
    def at_bat_team_name(self):
        if self.top_of_inning:
            return self.away_team_name
        else:
            return self.home_team_name

    @property
    def at_bat_team_nickname(self):
        if self.top_of_inning:
            return self.away_team_nickname
        else:
            return self.home_team_nickname

    @property
    def pitching_team(self):
        if self.top_of_inning:
            return self.home_team
        else:
            return self.away_team

    @property
    def pitching_team_name(self):
        if self.top_of_inning:
            return self.home_team_name
        else:
            return self.away_team_name

    @property
    def pitching_team_nickname(self):
        if self.top_of_inning:
            return self.home_team_nickname
        else:
            return self.away_team_nickname

    @property
    def current_pitcher(self):
        if self.top_of_inning:
            return self.home_pitcher
        else:
            return self.away_pitcher

    @property
    def current_pitcher_name(self):
        if self.top_of_inning:
            return self.home_pitcher_name
        else:
            return self.away_pitcher_name

    @property
    def current_batter(self):
        if self.top_of_inning:
            return self.away_batter
        else:
            return self.home_batter

    @property
    def current_batter_name(self):
        if self.top_of_inning:
            return self.away_batter_name
        else:
            return self.home_batter_name

    @Base.lazy_load("_season", use_default=False)
    def season(self):
        return self._season + 1

    @Base.lazy_load("_day", use_default=False)
    def day(self):
        return self._day + 1

    @Base.lazy_load("_inning", use_default=False)
    def inning(self):
        return self._inning + 1

    @Base.lazy_load("_statsheet_id", cache_name="_statsheet")
    def statsheet(self):
        return GameStatsheet.load(self._statsheet_id)[self._statsheet_id]

    @Base.lazy_load("_stadium_id", cache_name="_stadium")
    def stadium_id(self):
        return Stadium.load_one(self._stadium_id)

    @property
    def stadium(self):
        # stadium is an alias for stadium_id
        return self.stadium_id

    @Base.lazy_load("_base_runner_mod_ids", cache_name="_base_runner_mods", default_value=list())
    def base_runner_mods(self):
        return Modification.load(*self._base_runner_mod_ids)

    @Base.lazy_load("_home_pitcher_mod_id", cache_name="_home_pitcher_mod", use_default=False)
    def home_pitcher_mod(self):
        return Modification.load_one(getattr(self, "_home_pitcher_mod_id", None))

    @Base.lazy_load("_home_batter_mod_id", cache_name="_home_batter_mod", use_default=False)
    def home_batter_mod(self):
        return Modification.load_one(getattr(self, "_home_batter_mod_id", None))

    @Base.lazy_load("_away_pitcher_mod_id", cache_name="_away_pitcher_mod", use_default=False)
    def away_pitcher_mod(self):
        return Modification.load_one(getattr(self, "_away_pitcher_mod_id", None))

    @Base.lazy_load("_away_batter_mod_id", cache_name="_away_batter_mod", use_default=False)
    def away_batter_mod(self):
        return Modification.load_one(getattr(self, "_away_batter_mod_id", None))

    @staticmethod
    def _payout_calc_discipline(odds, amount):
        if odds == 0.5:
            return round(2 * amount)
        elif odds < 0.5:
            return round(amount * (2 + 0.000555 * (100 * (0.5 - odds)) ** 2.4135))
        else:
            return round(amount * (2 - 0.000335 * (100 * (odds - 0.5)) ** 2.045))

    @staticmethod
    def _payout_calc_s12(odds, amount):
        if odds == 0.5:
            return round(2 * amount)
        elif odds < 0.5:
            return round(amount * (2 + 0.0015 * (100 * (0.5 - odds)) ** 2.2))
        else:
            return round(amount * (0.571 + 1.429 / (1 + (3 * (odds - 0.5)) ** 0.77)))

    @staticmethod
    def _payout_calc(odds, amount):
        if odds == 0.5:
            return round(2 * amount)
        elif odds < 0.5:
            return round(amount * (2 + 0.0015 * (100 * (0.5 - odds)) ** 2.2))
        else:
            return round(amount * (3.206 / (1 + ((0.443 * (odds - 0.5)) ** 0.95)) - 1.206))

    def home_payout(self, bet, season=None):
        """
        Calculate the payout if the home team wins
        """
        if season is None:
            season = self.season

        if season < 12:
            return self._payout_calc_discipline(self.home_odds, bet)
        elif season == 12:
            return self._payout_calc_s12(self.home_odds, bet)
        else:
            return self._payout_calc(self.home_odds, bet)

    def away_payout(self, bet, season=None):
        """
        Calculate the payout if the away team wins
        """
        if season is None:
            season = self.season

        if season < 12:
            return self._payout_calc_discipline(self.away_odds, bet)
        elif season == 12:
            return self._payout_calc_s12(self.away_odds, bet)
        else:
            return self._payout_calc(self.away_odds, bet)

Classes

class Game (data, strict=False)

Represents one blaseball game

Expand source code
class Game(Base):
    """
    Represents one blaseball game
    """
    @classmethod
    def _get_fields(cls):
        p = cls.load_by_id("1cbd9d82-89e6-46b2-9082-815f59e1a130")
        return [cls._from_api_conversion(x) for x in p.fields]

    @classmethod
    def load_by_id(cls, id_):
        """
        Load by ID
        """
        return cls(database.get_game_by_id(id_))

    @classmethod
    def load_by_day(cls, season, day):
        """
        Load by in-game season and day. Season and Day are 1-indexed
        """
        return {
            id_: cls(game) for id_, game in database.get_games(season, day).items()
        }

    @classmethod
    def load_tournament_by_day(cls, tournament, day):
        """
        Loads all games played in a tournament on a given in-game day. Day is 1-indexed
        Tournament refers to things such as the Coffee Cup which exist outside the normal blaseball timeline.
        """
        return {
            id_: cls(game) for id_, game in database.get_tournament(tournament, day).items()
        }

    @classmethod
    def load_by_season(cls, season, team_id=None, day=None):
        """
        Return dictionary of games for a given season keyed by game ID.
        Can optionally be filtered by in-game day or team ID. Season and Day are 1-indexed
        """
        return {
            game["gameId"]: cls(game["data"]) for game in chronicler.get_games(team_ids=team_id, season=season, day=day)
        }

    @classmethod
    def load_by_tournament(cls, tournament, team_id=None, day=None):
        """
        Return dictionary of games for a given tournament keyed by game ID.
        Can optionally be filtered by in-game day or team ID. Day is 1-indexed
        """
        return {
            game["gameId"]: cls(game["data"]) for game in
            chronicler.get_games(team_ids=team_id, tournament=tournament, day=day)
        }

    @property
    def winning_team(self):
        return self.home_team if self.home_score > self.away_score else self.away_team

    @property
    def winning_team_name(self):
        return self.home_team_name if self.home_score > self.away_score else self.away_team_name

    @property
    def winning_team_nickname(self):
        return self.home_team_nickname if self.home_score > self.away_score else self.away_team_nickname

    @property
    def losing_team(self):
        return self.home_team if self.home_score < self.away_score else self.away_team

    @property
    def losing_team_name(self):
        return self.home_team_name if self.home_score < self.away_score else self.away_team_name

    @property
    def losing_team_nickname(self):
        return self.home_team_nickname if self.home_score < self.away_score else self.away_team_nickname

    @property
    def winning_score(self):
        return self.home_score if self.home_score > self.away_score else self.away_score

    @property
    def losing_score(self):
        return self.home_score if self.home_score < self.away_score else self.away_score

    @Base.lazy_load("_base_runner_ids", cache_name="_base_runners", default_value=list())
    def base_runners(self):
        players = Player.load(*self._base_runner_ids)
        return [players.get(id_) for id_ in self._base_runner_ids]

    @Base.lazy_load("_weather", use_default=False)
    def weather(self):
        return Weather.load_one(self._weather)

    @Base.lazy_load("_home_team_id", cache_name="_home_team")
    def home_team(self):
        return Team.load(self._home_team_id)

    @Base.lazy_load("_away_team_id", cache_name="_away_team")
    def away_team(self):
        return Team.load(self._away_team_id)

    @Base.lazy_load("_home_pitcher_id", cache_name="_home_pitcher")
    def home_pitcher(self):
        return Player.load_one(self._home_pitcher_id)

    @Base.lazy_load("_away_pitcher_id", cache_name="_away_pitcher")
    def away_pitcher(self):
        return Player.load_one(self._away_pitcher_id)

    @Base.lazy_load("_home_batter_id", cache_name="_home_batter")
    def home_batter(self):
        return Player.load_one(self._home_batter_id)

    @Base.lazy_load("_away_batter_id", cache_name="_away_batter")
    def away_batter(self):
        return Player.load_one(self._away_batter_id)

    @property
    def at_bat_team(self):
        if self.top_of_inning:
            return self.away_team
        else:
            return self.home_team

    @property
    def at_bat_team_name(self):
        if self.top_of_inning:
            return self.away_team_name
        else:
            return self.home_team_name

    @property
    def at_bat_team_nickname(self):
        if self.top_of_inning:
            return self.away_team_nickname
        else:
            return self.home_team_nickname

    @property
    def pitching_team(self):
        if self.top_of_inning:
            return self.home_team
        else:
            return self.away_team

    @property
    def pitching_team_name(self):
        if self.top_of_inning:
            return self.home_team_name
        else:
            return self.away_team_name

    @property
    def pitching_team_nickname(self):
        if self.top_of_inning:
            return self.home_team_nickname
        else:
            return self.away_team_nickname

    @property
    def current_pitcher(self):
        if self.top_of_inning:
            return self.home_pitcher
        else:
            return self.away_pitcher

    @property
    def current_pitcher_name(self):
        if self.top_of_inning:
            return self.home_pitcher_name
        else:
            return self.away_pitcher_name

    @property
    def current_batter(self):
        if self.top_of_inning:
            return self.away_batter
        else:
            return self.home_batter

    @property
    def current_batter_name(self):
        if self.top_of_inning:
            return self.away_batter_name
        else:
            return self.home_batter_name

    @Base.lazy_load("_season", use_default=False)
    def season(self):
        return self._season + 1

    @Base.lazy_load("_day", use_default=False)
    def day(self):
        return self._day + 1

    @Base.lazy_load("_inning", use_default=False)
    def inning(self):
        return self._inning + 1

    @Base.lazy_load("_statsheet_id", cache_name="_statsheet")
    def statsheet(self):
        return GameStatsheet.load(self._statsheet_id)[self._statsheet_id]

    @Base.lazy_load("_stadium_id", cache_name="_stadium")
    def stadium_id(self):
        return Stadium.load_one(self._stadium_id)

    @property
    def stadium(self):
        # stadium is an alias for stadium_id
        return self.stadium_id

    @Base.lazy_load("_base_runner_mod_ids", cache_name="_base_runner_mods", default_value=list())
    def base_runner_mods(self):
        return Modification.load(*self._base_runner_mod_ids)

    @Base.lazy_load("_home_pitcher_mod_id", cache_name="_home_pitcher_mod", use_default=False)
    def home_pitcher_mod(self):
        return Modification.load_one(getattr(self, "_home_pitcher_mod_id", None))

    @Base.lazy_load("_home_batter_mod_id", cache_name="_home_batter_mod", use_default=False)
    def home_batter_mod(self):
        return Modification.load_one(getattr(self, "_home_batter_mod_id", None))

    @Base.lazy_load("_away_pitcher_mod_id", cache_name="_away_pitcher_mod", use_default=False)
    def away_pitcher_mod(self):
        return Modification.load_one(getattr(self, "_away_pitcher_mod_id", None))

    @Base.lazy_load("_away_batter_mod_id", cache_name="_away_batter_mod", use_default=False)
    def away_batter_mod(self):
        return Modification.load_one(getattr(self, "_away_batter_mod_id", None))

    @staticmethod
    def _payout_calc_discipline(odds, amount):
        if odds == 0.5:
            return round(2 * amount)
        elif odds < 0.5:
            return round(amount * (2 + 0.000555 * (100 * (0.5 - odds)) ** 2.4135))
        else:
            return round(amount * (2 - 0.000335 * (100 * (odds - 0.5)) ** 2.045))

    @staticmethod
    def _payout_calc_s12(odds, amount):
        if odds == 0.5:
            return round(2 * amount)
        elif odds < 0.5:
            return round(amount * (2 + 0.0015 * (100 * (0.5 - odds)) ** 2.2))
        else:
            return round(amount * (0.571 + 1.429 / (1 + (3 * (odds - 0.5)) ** 0.77)))

    @staticmethod
    def _payout_calc(odds, amount):
        if odds == 0.5:
            return round(2 * amount)
        elif odds < 0.5:
            return round(amount * (2 + 0.0015 * (100 * (0.5 - odds)) ** 2.2))
        else:
            return round(amount * (3.206 / (1 + ((0.443 * (odds - 0.5)) ** 0.95)) - 1.206))

    def home_payout(self, bet, season=None):
        """
        Calculate the payout if the home team wins
        """
        if season is None:
            season = self.season

        if season < 12:
            return self._payout_calc_discipline(self.home_odds, bet)
        elif season == 12:
            return self._payout_calc_s12(self.home_odds, bet)
        else:
            return self._payout_calc(self.home_odds, bet)

    def away_payout(self, bet, season=None):
        """
        Calculate the payout if the away team wins
        """
        if season is None:
            season = self.season

        if season < 12:
            return self._payout_calc_discipline(self.away_odds, bet)
        elif season == 12:
            return self._payout_calc_s12(self.away_odds, bet)
        else:
            return self._payout_calc(self.away_odds, bet)

Ancestors

Subclasses

Static methods

def load_by_day(season, day)

Load by in-game season and day. Season and Day are 1-indexed

Expand source code
@classmethod
def load_by_day(cls, season, day):
    """
    Load by in-game season and day. Season and Day are 1-indexed
    """
    return {
        id_: cls(game) for id_, game in database.get_games(season, day).items()
    }
def load_by_id(id_)

Load by ID

Expand source code
@classmethod
def load_by_id(cls, id_):
    """
    Load by ID
    """
    return cls(database.get_game_by_id(id_))
def load_by_season(season, team_id=None, day=None)

Return dictionary of games for a given season keyed by game ID. Can optionally be filtered by in-game day or team ID. Season and Day are 1-indexed

Expand source code
@classmethod
def load_by_season(cls, season, team_id=None, day=None):
    """
    Return dictionary of games for a given season keyed by game ID.
    Can optionally be filtered by in-game day or team ID. Season and Day are 1-indexed
    """
    return {
        game["gameId"]: cls(game["data"]) for game in chronicler.get_games(team_ids=team_id, season=season, day=day)
    }
def load_by_tournament(tournament, team_id=None, day=None)

Return dictionary of games for a given tournament keyed by game ID. Can optionally be filtered by in-game day or team ID. Day is 1-indexed

Expand source code
@classmethod
def load_by_tournament(cls, tournament, team_id=None, day=None):
    """
    Return dictionary of games for a given tournament keyed by game ID.
    Can optionally be filtered by in-game day or team ID. Day is 1-indexed
    """
    return {
        game["gameId"]: cls(game["data"]) for game in
        chronicler.get_games(team_ids=team_id, tournament=tournament, day=day)
    }
def load_tournament_by_day(tournament, day)

Loads all games played in a tournament on a given in-game day. Day is 1-indexed Tournament refers to things such as the Coffee Cup which exist outside the normal blaseball timeline.

Expand source code
@classmethod
def load_tournament_by_day(cls, tournament, day):
    """
    Loads all games played in a tournament on a given in-game day. Day is 1-indexed
    Tournament refers to things such as the Coffee Cup which exist outside the normal blaseball timeline.
    """
    return {
        id_: cls(game) for id_, game in database.get_tournament(tournament, day).items()
    }

Instance variables

var at_bat_balls
var at_bat_strikes
var at_bat_team
Expand source code
@property
def at_bat_team(self):
    if self.top_of_inning:
        return self.away_team
    else:
        return self.home_team
var at_bat_team_name
Expand source code
@property
def at_bat_team_name(self):
    if self.top_of_inning:
        return self.away_team_name
    else:
        return self.home_team_name
var at_bat_team_nickname
Expand source code
@property
def at_bat_team_nickname(self):
    if self.top_of_inning:
        return self.away_team_nickname
    else:
        return self.home_team_nickname
var away_balls
var away_bases
var away_batter
var away_batter_mod
var away_batter_name
var away_odds
var away_outs
var away_pitcher
var away_pitcher_mod
var away_pitcher_name
var away_score
var away_strikes
var away_team
var away_team_batter_count
var away_team_color
var away_team_emoji
var away_team_name
var away_team_nickname
var away_team_secondary_color
var base_runner_mods
var base_runner_names
var base_runners
var baserunner_count
var bases_occupied
var bottom_inning_score
var current_batter
Expand source code
@property
def current_batter(self):
    if self.top_of_inning:
        return self.away_batter
    else:
        return self.home_batter
var current_batter_name
Expand source code
@property
def current_batter_name(self):
    if self.top_of_inning:
        return self.away_batter_name
    else:
        return self.home_batter_name
var current_pitcher
Expand source code
@property
def current_pitcher(self):
    if self.top_of_inning:
        return self.home_pitcher
    else:
        return self.away_pitcher
var current_pitcher_name
Expand source code
@property
def current_pitcher_name(self):
    if self.top_of_inning:
        return self.home_pitcher_name
    else:
        return self.away_pitcher_name
var day
var end_phase
var finalized
var game_complete
var game_start
var game_start_phase
var half_inning_outs
var half_inning_score
var home_balls
var home_bases
var home_batter
var home_batter_mod
var home_batter_name
var home_odds
var home_outs
var home_pitcher
var home_pitcher_mod
var home_pitcher_name
var home_score
var home_strikes
var home_team
var home_team_batter_count
var home_team_color
var home_team_emoji
var home_team_name
var home_team_nickname
var home_team_secondary_color
var id
var inning
var is_postseason
var is_title_match
var last_update
var losing_score
Expand source code
@property
def losing_score(self):
    return self.home_score if self.home_score < self.away_score else self.away_score
var losing_team
Expand source code
@property
def losing_team(self):
    return self.home_team if self.home_score < self.away_score else self.away_team
var losing_team_name
Expand source code
@property
def losing_team_name(self):
    return self.home_team_name if self.home_score < self.away_score else self.away_team_name
var losing_team_nickname
Expand source code
@property
def losing_team_nickname(self):
    return self.home_team_nickname if self.home_score < self.away_score else self.away_team_nickname
var new_half_inning_phase
var new_inning_phase
var outcomes
var phase
var pitching_team
Expand source code
@property
def pitching_team(self):
    if self.top_of_inning:
        return self.home_team
    else:
        return self.away_team
var pitching_team_name
Expand source code
@property
def pitching_team_name(self):
    if self.top_of_inning:
        return self.home_team_name
    else:
        return self.away_team_name
var pitching_team_nickname
Expand source code
@property
def pitching_team_nickname(self):
    if self.top_of_inning:
        return self.home_team_nickname
    else:
        return self.away_team_nickname
var play_count
var queued_events
var repeat_count
var rules
var score_ledger
var score_update
var season
var secret_baserunner
var series_index
var series_length
var shame
var stadium
Expand source code
@property
def stadium(self):
    # stadium is an alias for stadium_id
    return self.stadium_id
var stadium_id
var state
var statsheet
var terminology
var top_inning_score
var top_of_inning
var tournament
var weather
var winning_score
Expand source code
@property
def winning_score(self):
    return self.home_score if self.home_score > self.away_score else self.away_score
var winning_team
Expand source code
@property
def winning_team(self):
    return self.home_team if self.home_score > self.away_score else self.away_team
var winning_team_name
Expand source code
@property
def winning_team_name(self):
    return self.home_team_name if self.home_score > self.away_score else self.away_team_name
var winning_team_nickname
Expand source code
@property
def winning_team_nickname(self):
    return self.home_team_nickname if self.home_score > self.away_score else self.away_team_nickname

Methods

def away_payout(self, bet, season=None)

Calculate the payout if the away team wins

Expand source code
def away_payout(self, bet, season=None):
    """
    Calculate the payout if the away team wins
    """
    if season is None:
        season = self.season

    if season < 12:
        return self._payout_calc_discipline(self.away_odds, bet)
    elif season == 12:
        return self._payout_calc_s12(self.away_odds, bet)
    else:
        return self._payout_calc(self.away_odds, bet)
def home_payout(self, bet, season=None)

Calculate the payout if the home team wins

Expand source code
def home_payout(self, bet, season=None):
    """
    Calculate the payout if the home team wins
    """
    if season is None:
        season = self.season

    if season < 12:
        return self._payout_calc_discipline(self.home_odds, bet)
    elif season == 12:
        return self._payout_calc_s12(self.home_odds, bet)
    else:
        return self._payout_calc(self.home_odds, bet)