From 97c042c1f35813c2af547152d13d35711b95dc6d Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Fri, 17 Nov 2023 22:17:20 -0600 Subject: [PATCH] Groundball Automation Update --- db_calls_gameplay.py | 45 ++++++++- gameplay_helpers.py | 217 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 256 insertions(+), 6 deletions(-) diff --git a/db_calls_gameplay.py b/db_calls_gameplay.py index a0a8179..c22f59f 100644 --- a/db_calls_gameplay.py +++ b/db_calls_gameplay.py @@ -158,6 +158,7 @@ class StratManagerAi(pydantic.BaseModel): bullpen_matchup: int = 5 behind_aggression: int = 5 ahead_aggression: int = 5 + decide_throw: int = 5 """ Rating Rule of Thumb: @@ -335,6 +336,48 @@ class StratManagerAi(pydantic.BaseModel): else: return 'throw for the lead runner if their safe range is 14-' + def gb_decide_advance(self, starting_outs: int, run_lead: int): + """Returns a string to be posted with the advancement message""" + + advance_base = f'attempt to advance if their safe range is' + if self.running >= 8: + advance_range = 10 + elif self.running >= 5: + advance_range = 13 + else: + advance_range = 16 + + if starting_outs == 1: + advance_range += 3 + + if run_lead >= 4: + advance_range -= 3 + elif run_lead < 0: + advance_range += 3 + + return f'{advance_base} {min(advance_range, 20)}+' + + def gb_decide_throw(self, starting_outs: int, run_lead: int): + """Returns a string to be posted with the advancement message""" + + throw_base = f'throw for the lead runner if their safe range is' + if self.decide_throw >= 8: + throw_range = 13 + elif self.decide_throw >= 5: + throw_range = 10 + else: + throw_range = 8 + + if starting_outs == 1: + throw_range -= 3 + + if run_lead >= 4: + throw_range -= 3 + elif run_lead < 0: + throw_range += 3 + + return f'{throw_base} {max(throw_range, 0)}-' + def convert_strat_manager(manager: ManagerAi) -> StratManagerAi: manager_dict = model_to_dict(manager) @@ -351,7 +394,7 @@ def get_manager(game) -> Optional[StratManagerAi]: manager_ai_id = ((datetime.datetime.now().day * team_id) % 3) + 1 if manager_ai_id > 3 or manager_ai_id < 1: manager_ai_id = 1 - logging.info(f'manager id: {manager_ai_id} for game {game}') + logging.debug(f'manager id: {manager_ai_id} for game {game}') try: this_manager = ManagerAi.get_by_id(manager_ai_id) diff --git a/gameplay_helpers.py b/gameplay_helpers.py index 327a8c0..40fda20 100644 --- a/gameplay_helpers.py +++ b/gameplay_helpers.py @@ -5,11 +5,12 @@ import random import discord -import db_calls_gameplay from db_calls_gameplay import StratGame, StratPlay, StratLineup, StratManagerAi, patch_play, advance_runners, \ - complete_play, get_team_lineups, get_or_create_bullpen, get_player, get_sheets, make_sub, get_one_lineup -from db_calls import db_get, db_post, Player -from helpers import Pagination, get_team_embed, image_embed + complete_play, get_team_lineups, get_or_create_bullpen, get_player, get_sheets, make_sub, get_one_lineup, \ + advance_one_runner, get_one_lineup, ai_batting, get_manager +from db_calls import db_get, db_post, Player, get_pd_player +from helpers import Pagination, get_team_embed, image_embed, Confirm +from typing import Literal, Optional def single_onestar(this_play: StratPlay, comp_play: bool = True): @@ -166,7 +167,7 @@ def get_pitcher(this_game: StratGame, this_play: StratPlay): p_team_id = this_game.away_team_id if this_play.inning_half == 'top': p_team_id = this_game.home_team_id - return db_calls_gameplay.get_one_lineup(this_game.id, team_id=p_team_id, position='P', active=True) + return get_one_lineup(this_game.id, team_id=p_team_id, position='P', active=True) async def legal_check(card_ids: list, difficulty_name: str): @@ -252,3 +253,209 @@ async def show_outfield_cards(interaction: discord.Interaction, this_play: Strat await msg.edit(content=None, embed=embeds[page_num], view=view) return [lf_player, cf_player, rf_player][page_num] + + +def runner_on_first(this_play: StratPlay): + if this_play.on_base_code in [1, 4, 5, 7]: + return True + return False + + +def runner_on_second(this_play: StratPlay): + if this_play.on_base_code in [2, 4, 6, 7]: + return True + return False + + +def runner_on_third(this_play: StratPlay): + if this_play.on_base_code in [3, 5, 6, 7]: + return True + return False + + +def gb_result_1(this_play: StratPlay) -> Optional[int]: + logging.info(f'GB 1') + advance_runners(this_play.id, num_bases=0) + patch_play(this_play.id, pa=1, ab=1, outs=1) + return None + + +def gb_result_2(this_play: StratPlay) -> Optional[int]: + logging.info(f'GB 2') + num_outs = 2 if this_play.starting_outs <= 1 else 1 + + patch_play(this_play.id, pa=1, ab=1, outs=num_outs, on_first_final=False) + if runner_on_second(this_play): + advance_one_runner(this_play.id, from_base=2, num_bases=1) + if runner_on_third(this_play): + advance_one_runner(this_play.id, from_base=3, num_bases=1) + + return None + + +def gb_result_3(this_play: StratPlay) -> Optional[int]: + logging.info(f'GB 3') + advance_runners(this_play.id, num_bases=1) + patch_play(this_play.id, pa=1, ab=1, outs=1) + return None + + +def gb_result_4(this_play: StratPlay) -> Optional[int]: + logging.info(f'GB 4') + patch_play(this_play.id, pa=1, ab=1, outs=1, on_first_final=False) + if runner_on_second(this_play): + advance_one_runner(this_play.id, from_base=2, num_bases=1) + if runner_on_third(this_play): + advance_one_runner(this_play.id, from_base=3, num_bases=1) + + return 1 + + +def gb_result_5(this_play: StratPlay, to_mif: bool) -> Optional[int]: + logging.info(f'GB 5') + patch_play(this_play.id, pa=1, ab=1, outs=1) + if to_mif: + gb_result_3(this_play) + else: + gb_result_1(this_play) + + return None + + +def gb_result_6(this_play: StratPlay, to_right_side: bool) -> Optional[int]: + logging.info(f'GB 6') + patch_play(this_play.id, pa=1, ab=1, outs=1) + if to_right_side: + gb_result_3(this_play) + else: + gb_result_1(this_play) + + return None + + +def gb_result_7(this_play: StratPlay) -> Optional[int]: + logging.info(f'GB 7') + patch_play(this_play.id, pa=1, ab=1, outs=1) + advance_runners(this_play.id, num_bases=1, only_forced=True) + return None + + +def gb_result_8(this_play: StratPlay) -> Optional[int]: + logging.info(f'GB 8') + return gb_result_7(this_play) + + +def gb_result_9(this_play: StratPlay) -> Optional[int]: + logging.info(f'GB 9') + return gb_result_7(this_play) + + +def gb_result_10(this_play: StratPlay) -> Optional[int]: + logging.info(f'GB 10') + patch_play(this_play.id, pa=1, ab=1, outs=2, on_third_final=False) + advance_one_runner(this_play.id, from_base=2, num_bases=1) + advance_one_runner(this_play.id, from_base=1, num_bases=1) + + return None + + +def gb_result_11(this_play: StratPlay) -> Optional[int]: + logging.info(f'GB 11') + patch_play(this_play.id, pa=1, ab=1, outs=1, on_first_final=2, on_second_final=3, on_third_final=False) + return 1 + + +async def gb_decide(this_play: StratPlay, interaction: discord.Interaction, runner: dict, from_base: int): + logging.info(f'GB Decide') + ai_is_batting = ai_batting(this_play.game, this_play) + ai_manager = get_manager(this_play.game) + bases = ['first', 'second', 'third'] + + ai_hint = '' + if this_play.inning_half == 'Top': + run_diff = this_play.away_score - this_play.home_score + else: + run_diff = this_play.home_score - this_play.away_score + + if this_play.game.ai_team and ai_is_batting: + ai_hint = f'*The runner will ' \ + f'{ai_manager.gb_decide_advance(this_play.starting_outs, run_lead=run_diff)}*' + + view = Confirm(responders=[interaction.user], timeout=60, label_type='yes') + question = await interaction.channel.send( + f'Was {runner["p_name"]} sent from {bases[from_base - 1]} on the play?\n\n{ai_hint}', view=view + ) + await view.wait() + + if view.value: + await question.delete() + + ai_hint = '' + if not ai_is_batting: + ai_hint = f'*The defense will ' \ + f'{ai_manager.gb_decide_throw(this_play.starting_outs, run_lead=run_diff)}*' + + view = Confirm(responders=[interaction.user], timeout=60, label_type='yes') + question = await interaction.channel.send( + f'Is the defense throwing for the lead runner?\n\n{ai_hint}', view=view + ) + await view.wait() + + if view.value: + await question.delete() + view = Confirm(responders=[interaction.user], timeout=60, label_type='yes') + question = await interaction.channel.send( + content=f'Was {runner["p_name"]} thrown out?', view=view + ) + await view.wait() + + if view.value: + await question.delete() + if this_play.on_base_code == 2: + patch_play(this_play.id, on_second_final=False, pa=1, ab=1, outs=1) + elif this_play.on_base_code == 3: + patch_play(this_play.id, on_third_final=False, pa=1, ab=1, outs=1) + elif this_play.on_base_code == 6: + patch_play(this_play.id, on_third_final=False, on_second_final=3, pa=1, ab=1, outs=1) + + else: + await question.delete() + patch_play(this_play.id, pa=1, ab=1) + advance_runners(this_play.id, num_bases=1) + + return 1 + + else: + await question.delete() + advance_runners(this_play.id, num_bases=1) + patch_play(this_play.id, pa=1, ab=1, outs=1) + return None + + else: + await question.delete() + advance_runners(this_play.id, num_bases=0) + patch_play(this_play.id, pa=1, ab=1, outs=1) + return None + + +async def gb_result_12(this_play: StratPlay, defender: Literal['1b-2b', '3b', 'ss-p-c'], + interaction: discord.Interaction) -> Optional[int]: + logging.info(f'GB 12') + if defender == '1b-2b': + return gb_result_3(this_play) + elif defender == '3b': + return gb_result_1(this_play) + else: + return await gb_decide( + this_play, interaction, await get_player(this_play.game, this_play.on_second), from_base=2 + ) + + +def gb_result_13(this_play: StratPlay, defender: Literal['c-3b', 'else']) -> Optional[int]: + logging.info(f'GB 13') + if defender == 'c-3b': + patch_play(this_play.id, on_second_final=False, on_first_final=False, pa=1, ab=1, outs=2) + return 1 + else: + return gb_result_2(this_play) +