Log stealing done
Log xchecks started
This commit is contained in:
parent
073bd04b4b
commit
d6d3d7beb0
@ -11,7 +11,7 @@ import pygsheets
|
|||||||
from sqlmodel import or_
|
from sqlmodel import or_
|
||||||
|
|
||||||
from api_calls import db_get
|
from api_calls import db_get
|
||||||
from command_logic.logic_gameplay import advance_runners, bunts, chaos, complete_game, doubles, flyballs, get_full_roster_from_sheets, get_lineups_from_sheets, checks_log_interaction, complete_play, get_scorebug_embed, hit_by_pitch, homeruns, is_game_over, manual_end_game, popouts, read_lineup, show_defense_cards, singles, starting_pitcher_dropdown_view, strikeouts, triples, undo_play, update_game_settings, walks
|
from command_logic.logic_gameplay import advance_runners, bunts, chaos, complete_game, doubles, flyballs, get_full_roster_from_sheets, get_lineups_from_sheets, checks_log_interaction, complete_play, get_scorebug_embed, hit_by_pitch, homeruns, is_game_over, manual_end_game, popouts, read_lineup, show_defense_cards, singles, starting_pitcher_dropdown_view, steals, strikeouts, triples, undo_play, update_game_settings, walks, xchecks
|
||||||
from dice import ab_roll
|
from dice import ab_roll
|
||||||
from exceptions import GameNotFoundException, GoogleSheetsException, TeamNotFoundException, PlayNotFoundException, GameException, log_exception
|
from exceptions import GameNotFoundException, GoogleSheetsException, TeamNotFoundException, PlayNotFoundException, GameException, log_exception
|
||||||
from helpers import DEFENSE_LITERAL, PD_PLAYERS_ROLE_NAME, get_channel, team_role, user_has_role, random_gif, random_from_list
|
from helpers import DEFENSE_LITERAL, PD_PLAYERS_ROLE_NAME, get_channel, team_role, user_has_role, random_gif, random_from_list
|
||||||
@ -20,7 +20,7 @@ from helpers import DEFENSE_LITERAL, PD_PLAYERS_ROLE_NAME, get_channel, team_rol
|
|||||||
from in_game.ai_manager import get_starting_pitcher, get_starting_lineup
|
from in_game.ai_manager import get_starting_pitcher, get_starting_lineup
|
||||||
from in_game.game_helpers import PUBLIC_FIELDS_CATEGORY_NAME, legal_check
|
from in_game.game_helpers import PUBLIC_FIELDS_CATEGORY_NAME, legal_check
|
||||||
from in_game.gameplay_models import Lineup, Play, Session, engine, player_description, select, Game
|
from in_game.gameplay_models import Lineup, Play, Session, engine, player_description, select, Game
|
||||||
from in_game.gameplay_queries import get_and_cache_position, get_available_pitchers, get_channel_game_or_none, get_active_games_by_team, get_game_lineups, get_team_or_none, get_card_or_none
|
from in_game.gameplay_queries import get_position, get_available_pitchers, get_channel_game_or_none, get_active_games_by_team, get_game_lineups, get_team_or_none, get_card_or_none
|
||||||
|
|
||||||
from utilities.buttons import Confirm, ScorebugButtons, ask_confirm
|
from utilities.buttons import Confirm, ScorebugButtons, ask_confirm
|
||||||
from utilities.dropdown import DropdownView, SelectStartingPitcher
|
from utilities.dropdown import DropdownView, SelectStartingPitcher
|
||||||
@ -274,7 +274,7 @@ class Gameplay(commands.Cog):
|
|||||||
await final_message.edit(content=f'The {ai_team.sname} lineup is in, pulling in scouting data...')
|
await final_message.edit(content=f'The {ai_team.sname} lineup is in, pulling in scouting data...')
|
||||||
for batter in batter_lineups:
|
for batter in batter_lineups:
|
||||||
if batter.position != 'DH':
|
if batter.position != 'DH':
|
||||||
await get_and_cache_position(session, batter.card, batter.position)
|
await get_position(session, batter.card, batter.position)
|
||||||
|
|
||||||
# session.refresh(this_game)
|
# session.refresh(this_game)
|
||||||
|
|
||||||
@ -564,6 +564,58 @@ class Gameplay(commands.Cog):
|
|||||||
|
|
||||||
await self.complete_and_post_play(session, interaction, this_play)
|
await self.complete_and_post_play(session, interaction, this_play)
|
||||||
|
|
||||||
|
@group_log.command(name='stealing', description='Running: stolen-base, caught-stealing')
|
||||||
|
@app_commands.describe(to_base='Base the runner is advancing to; 2 for 2nd, 3 for 3rd, 4 for Home')
|
||||||
|
async def log_stealing(self, interaction: discord.Interaction, running_type: Literal['stolen-base', 'caught-stealing', 'steal-plus-overthrow'], to_base: Literal[2, 3, 4]):
|
||||||
|
with Session(engine) as session:
|
||||||
|
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log stealing')
|
||||||
|
|
||||||
|
if (to_base == 2 and this_play.on_first is None) or (to_base == 3 and this_play.on_second is None) or (to_base == 4 and this_play.on_third is None):
|
||||||
|
logger.info(f'Illegal steal attempt')
|
||||||
|
await interaction.edit_original_response(
|
||||||
|
content=f'I don\'t see a runner there.'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (to_base == 3 and this_play.on_third is not None) or (to_base == 2 and this_play.on_second is not None):
|
||||||
|
logger.info(f'Stealing runner is blocked')
|
||||||
|
if to_base == 3:
|
||||||
|
content = f'{this_play.on_second.player.name} is blocked by {this_play.on_third.player.name}'
|
||||||
|
else:
|
||||||
|
content = f'{this_play.on_first.player.name} is blocked by {this_play.on_second.player.name}'
|
||||||
|
|
||||||
|
await interaction.edit_original_response(
|
||||||
|
content=content
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info(f'log stealing - this_play: {this_play}')
|
||||||
|
this_play = await steals(session, interaction, this_play, running_type, to_base)
|
||||||
|
|
||||||
|
await self.complete_and_post_play(session, interaction, this_play)
|
||||||
|
|
||||||
|
@group_log.command(name='xcheck', description='Defender makes an x-check')
|
||||||
|
@app_commands.choices(position=[
|
||||||
|
Choice(name='Pitcher', value='P'),
|
||||||
|
Choice(name='Catcher', value='C'),
|
||||||
|
Choice(name='First Base', value='1B'),
|
||||||
|
Choice(name='Second Base', value='2B'),
|
||||||
|
Choice(name='Third Base', value='3B'),
|
||||||
|
Choice(name='Shortstop', value='SS'),
|
||||||
|
Choice(name='Left Field', value='LF'),
|
||||||
|
Choice(name='Center Field', value='CF'),
|
||||||
|
Choice(name='Right Field', value='RF'),
|
||||||
|
])
|
||||||
|
async def log_xcheck_command(self, interaction: discord.Interaction, position: Choice[str]):
|
||||||
|
with Session(engine) as session:
|
||||||
|
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log xcheck')
|
||||||
|
|
||||||
|
logger.info(f'log xcheck - this_play: {this_play}')
|
||||||
|
this_play = await xchecks(session, interaction, this_play, position.value)
|
||||||
|
|
||||||
|
await self.complete_and_post_play(session, interaction, this_play)
|
||||||
|
|
||||||
|
|
||||||
@group_log.command(name='undo-play', description='Roll back most recent play from the log')
|
@group_log.command(name='undo-play', description='Roll back most recent play from the log')
|
||||||
async def log_undo_play_command(self, interaction: discord.Interaction):
|
async def log_undo_play_command(self, interaction: discord.Interaction):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
|
|||||||
@ -14,7 +14,7 @@ from discord.ext import commands, tasks
|
|||||||
from peewee import IntegrityError
|
from peewee import IntegrityError
|
||||||
from typing import Literal, Optional
|
from typing import Literal, Optional
|
||||||
|
|
||||||
from dice import sa_fielding_roll
|
from dice import sa_fielding_roll_legacy
|
||||||
from helpers import SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME, random_conf_gif, SBA_SEASON, PD_SEASON, IMAGES, \
|
from helpers import SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME, random_conf_gif, SBA_SEASON, PD_SEASON, IMAGES, \
|
||||||
get_pos_abbrev, SBA_COLOR, get_roster_lineups, give_packs, send_to_channel, \
|
get_pos_abbrev, SBA_COLOR, get_roster_lineups, give_packs, send_to_channel, \
|
||||||
get_channel, team_role, get_cal_user, ButtonOptions, get_ratings_guide, \
|
get_channel, team_role, get_cal_user, ButtonOptions, get_ratings_guide, \
|
||||||
@ -4142,7 +4142,7 @@ class Gameplay(commands.Cog):
|
|||||||
defender_embed.set_image(url=defender['image'])
|
defender_embed.set_image(url=defender['image'])
|
||||||
embeds = [defender_embed]
|
embeds = [defender_embed]
|
||||||
|
|
||||||
dice_embeds = sa_fielding_roll(position.value, def_team)
|
dice_embeds = sa_fielding_roll_legacy(position.value, def_team)
|
||||||
embeds.extend(dice_embeds)
|
embeds.extend(dice_embeds)
|
||||||
all_embeds = [x for x in embeds if x is not None]
|
all_embeds = [x for x in embeds if x is not None]
|
||||||
|
|
||||||
|
|||||||
@ -10,11 +10,12 @@ from sqlalchemy import delete
|
|||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
from api_calls import db_delete, db_get, db_post
|
from api_calls import db_delete, db_get, db_post
|
||||||
|
from dice import sa_fielding_roll
|
||||||
from exceptions import *
|
from exceptions import *
|
||||||
from helpers import DEFENSE_LITERAL, SBA_COLOR, get_channel
|
from helpers import DEFENSE_LITERAL, SBA_COLOR, get_channel
|
||||||
from in_game.game_helpers import legal_check
|
from in_game.game_helpers import legal_check
|
||||||
from in_game.gameplay_models import BattingCard, Game, Lineup, PositionRating, RosterLink, Team, Play
|
from in_game.gameplay_models import BattingCard, Game, Lineup, PositionRating, RosterLink, Team, Play
|
||||||
from in_game.gameplay_queries import get_and_cache_position, get_available_pitchers, get_card_or_none, get_channel_game_or_none, get_db_ready_decisions, get_db_ready_plays, get_game_lineups, get_last_team_play, get_one_lineup, get_player_id_from_dict, get_player_name_from_dict, get_player_or_none, get_sorted_lineups, get_team_or_none, get_players_last_pa, post_game_rewards
|
from in_game.gameplay_queries import get_position, get_available_pitchers, get_card_or_none, get_channel_game_or_none, get_db_ready_decisions, get_db_ready_plays, get_game_lineups, get_last_team_play, get_one_lineup, get_player_id_from_dict, get_player_name_from_dict, get_player_or_none, get_sorted_lineups, get_team_or_none, get_players_last_pa, post_game_rewards
|
||||||
from utilities.buttons import ButtonOptions, Confirm, ask_confirm
|
from utilities.buttons import ButtonOptions, Confirm, ask_confirm
|
||||||
from utilities.dropdown import DropdownView, SelectStartingPitcher, SelectViewDefense
|
from utilities.dropdown import DropdownView, SelectStartingPitcher, SelectViewDefense
|
||||||
from utilities.embeds import image_embed
|
from utilities.embeds import image_embed
|
||||||
@ -170,7 +171,7 @@ async def get_scorebug_embed(session: Session, this_game: Game, full_length: boo
|
|||||||
bat_string = f'{curr_play.batter.batting_order}. {battingcard.hand.upper()} | {curr_play.batter.player.name_card_link('batting')}'
|
bat_string = f'{curr_play.batter.batting_order}. {battingcard.hand.upper()} | {curr_play.batter.player.name_card_link('batting')}'
|
||||||
if len(baserunner_string) > 0:
|
if len(baserunner_string) > 0:
|
||||||
pitchingcard = curr_play.pitcher.card.pitcherscouting.pitchingcard
|
pitchingcard = curr_play.pitcher.card.pitcherscouting.pitchingcard
|
||||||
pit_string += f'\nHold: {pitchingcard.hold}, WP: {pitchingcard.wild_pitch}, Bk: {pitchingcard.balk}'
|
pit_string += f'\nHold: {"+" if pitchingcard.hold > 0 else ""}{pitchingcard.hold}, WP: {pitchingcard.wild_pitch}, Bk: {pitchingcard.balk}'
|
||||||
|
|
||||||
# battingcard = curr_play.batter.card.batterscouting.battingcard
|
# battingcard = curr_play.batter.card.batterscouting.battingcard
|
||||||
# bat_string += f'\nBunt: {battingcard.bunting}, HnR: {battingcard.hit_and_run}'
|
# bat_string += f'\nBunt: {battingcard.bunting}, HnR: {battingcard.hit_and_run}'
|
||||||
@ -280,7 +281,7 @@ async def read_lineup(session: Session, interaction: discord.Interaction, this_g
|
|||||||
|
|
||||||
for batter in human_lineups:
|
for batter in human_lineups:
|
||||||
if batter.position != 'DH':
|
if batter.position != 'DH':
|
||||||
await get_and_cache_position(session, batter.card, batter.position)
|
await get_position(session, batter.card, batter.position)
|
||||||
|
|
||||||
return this_game.initialize_play(session)
|
return this_game.initialize_play(session)
|
||||||
|
|
||||||
@ -1460,7 +1461,7 @@ async def bunts(session: Session, interaction: discord.Interaction, this_play: P
|
|||||||
await question.edit(content='You keep thinking on it and try again.', view=None)
|
await question.edit(content='You keep thinking on it and try again.', view=None)
|
||||||
log_exception(NoPlayerResponseException, f'{interaction.user.name} did not know who was fielding the bunt.')
|
log_exception(NoPlayerResponseException, f'{interaction.user.name} did not know who was fielding the bunt.')
|
||||||
|
|
||||||
def_pos = await get_and_cache_position(session, defender.card, defender.position)
|
def_pos = await get_position(session, defender.card, defender.position)
|
||||||
|
|
||||||
lead_runner_out = await ask_confirm(
|
lead_runner_out = await ask_confirm(
|
||||||
interaction=interaction,
|
interaction=interaction,
|
||||||
@ -1534,6 +1535,142 @@ async def chaos(session: Session, interaction: discord.Interaction, this_play: P
|
|||||||
session.refresh(this_play)
|
session.refresh(this_play)
|
||||||
return this_play
|
return this_play
|
||||||
|
|
||||||
|
async def steals(session: Session, interaction: discord.Interaction, this_play: Play, steal_type: Literal['stolen-base', 'caught-stealing', 'steal-plus-overthrow'], to_base: Literal[2, 3, 4]) -> Play:
|
||||||
|
advance_runners(session, this_play, 0)
|
||||||
|
|
||||||
|
if steal_type in ['stolen-base', 'steal-plus-overthrow']:
|
||||||
|
this_play.sb = 1
|
||||||
|
this_play.error = 1 if steal_type == 'steal-plus-overthrow' else 0
|
||||||
|
|
||||||
|
if to_base == 4 and this_play.on_third:
|
||||||
|
this_play.runner = this_play.on_third
|
||||||
|
this_play.on_third_final = 4
|
||||||
|
log_run_scored(session, this_play.on_third, this_play)
|
||||||
|
|
||||||
|
if this_play.on_second:
|
||||||
|
this_play.on_second_final = 3
|
||||||
|
if steal_type == 'steal-plus-overthrow':
|
||||||
|
this_play.on_second_final = 4
|
||||||
|
log_run_scored(session, this_play.on_second, this_play, is_earned=False)
|
||||||
|
if this_play.on_first:
|
||||||
|
this_play.on_first_final = 2 if steal_type == 'stolen-base' else 3
|
||||||
|
|
||||||
|
elif to_base == 3 and this_play.on_second:
|
||||||
|
this_play.runner = this_play.on_second
|
||||||
|
this_play.on_second_final = 3
|
||||||
|
|
||||||
|
if this_play.on_first:
|
||||||
|
this_play.on_first_final = 2
|
||||||
|
|
||||||
|
if steal_type == 'steal-plus-overthrow':
|
||||||
|
this_play.on_second_final = 4
|
||||||
|
log_run_scored(session, this_play.on_second, this_play, is_earned=False)
|
||||||
|
if this_play.on_first:
|
||||||
|
this_play.on_first_final = 3
|
||||||
|
|
||||||
|
else:
|
||||||
|
this_play.runner = this_play.on_first
|
||||||
|
this_play.on_first_final = 2 if steal_type == 'stolen-base' else 3
|
||||||
|
|
||||||
|
elif steal_type == 'caught-stealing':
|
||||||
|
this_play.outs = 1
|
||||||
|
|
||||||
|
if to_base == 4 and this_play.on_third:
|
||||||
|
this_play.runner = this_play.on_third
|
||||||
|
this_play.on_third_final = None
|
||||||
|
|
||||||
|
if this_play.on_second:
|
||||||
|
this_play.on_second_final = 3
|
||||||
|
if this_play.on_first:
|
||||||
|
this_play.on_first_final = 2
|
||||||
|
|
||||||
|
elif to_base == 3 and this_play.on_second:
|
||||||
|
this_play.runner = this_play.on_second
|
||||||
|
this_play.on_second_final = None
|
||||||
|
|
||||||
|
if this_play.on_first:
|
||||||
|
this_play.on_first_final = 2
|
||||||
|
|
||||||
|
else:
|
||||||
|
this_play.runner = this_play.on_first
|
||||||
|
this_play.on_first_final = None
|
||||||
|
|
||||||
|
|
||||||
|
session.add(this_play)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
session.refresh(this_play)
|
||||||
|
return this_play
|
||||||
|
|
||||||
|
async def xchecks(session: Session, interaction: discord.Interaction, this_play: Play, position: str) -> Play:
|
||||||
|
defense_team = this_play.pitcher.team
|
||||||
|
this_defender = get_one_lineup(
|
||||||
|
session,
|
||||||
|
this_play.game,
|
||||||
|
this_team=defense_team,
|
||||||
|
position=position
|
||||||
|
)
|
||||||
|
|
||||||
|
defender_embed = defense_team.embed
|
||||||
|
defender_embed.title = f'{defense_team.sname} {position} Check'
|
||||||
|
defender_embed.description = f'{this_defender.player.name}'
|
||||||
|
defender_embed.set_image(url=this_defender.player.image)
|
||||||
|
embeds = [defender_embed]
|
||||||
|
|
||||||
|
this_rating = await get_position(session, this_defender.card, position)
|
||||||
|
this_roll = sa_fielding_roll(defense_team, this_play, position, this_rating)
|
||||||
|
|
||||||
|
question = f'Looks like this is a {this_roll.hit_result}'
|
||||||
|
if this_roll.is_chaos:
|
||||||
|
question += ' **rare play**.'
|
||||||
|
elif this_roll.error_result is not None:
|
||||||
|
question += f' plus {this_roll.error_result}-base error.'
|
||||||
|
question += f'Is that correct?'
|
||||||
|
|
||||||
|
await interaction.edit_original_response(
|
||||||
|
content=None,
|
||||||
|
embeds=this_roll.embeds
|
||||||
|
)
|
||||||
|
is_correct = await ask_confirm(
|
||||||
|
interaction,
|
||||||
|
question,
|
||||||
|
label_type='yes',
|
||||||
|
timeout=30,
|
||||||
|
)
|
||||||
|
|
||||||
|
hit_result = this_roll.hit_result
|
||||||
|
error_result = this_roll.error_result
|
||||||
|
is_rare_play = this_roll.is_chaos
|
||||||
|
|
||||||
|
if not is_correct:
|
||||||
|
# Full questionnaire
|
||||||
|
pass
|
||||||
|
|
||||||
|
if hit_result == 'SPD':
|
||||||
|
is_out = ask_confirm(
|
||||||
|
interaction,
|
||||||
|
f'Is {this_play.batter.player.name} thrown out at first?',
|
||||||
|
custom_confirm_label='Out at first',
|
||||||
|
custom_cancel_label='Safe at first'
|
||||||
|
)
|
||||||
|
if is_out:
|
||||||
|
hit_result = 'G3'
|
||||||
|
else:
|
||||||
|
this_play = await singles(session, interaction, this_play, '*')
|
||||||
|
|
||||||
|
if hit_result not in ['SI1', 'SI2', 'DO2', 'DO3', 'TR'] and error_result is None:
|
||||||
|
|
||||||
|
if this_play.on_base_code == 0:
|
||||||
|
this_play.ab, this_play.outs = 1, 1
|
||||||
|
|
||||||
|
# TODO: Continue working through results
|
||||||
|
# elif this_play
|
||||||
|
|
||||||
|
session.add(this_play)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
session.refresh(this_play)
|
||||||
|
return this_play
|
||||||
|
|
||||||
def activate_last_play(session: Session, this_game: Game) -> Play:
|
def activate_last_play(session: Session, this_game: Game) -> Play:
|
||||||
p_query = session.exec(select(Play).where(Play.game == this_game).order_by(Play.id.desc()).limit(1)).all()
|
p_query = session.exec(select(Play).where(Play.game == this_game).order_by(Play.id.desc()).limit(1)).all()
|
||||||
@ -2060,4 +2197,246 @@ async def manual_end_game(session: Session, interaction: discord.Interaction, th
|
|||||||
await complete_game(session, interaction, current_play)
|
await complete_game(session, interaction, current_play)
|
||||||
|
|
||||||
|
|
||||||
|
async def gb_result(session: Session, interaction: discord.Interaction, this_play: Play, groundball_result: int, to_mif: bool = None, to_right_side: bool = None):
|
||||||
|
"""
|
||||||
|
Commits this_play
|
||||||
|
"""
|
||||||
|
if groundball_result == 1:
|
||||||
|
this_play = gb_result_1(session, this_play)
|
||||||
|
elif groundball_result == 2:
|
||||||
|
this_play = gb_result_2(session, this_play)
|
||||||
|
elif groundball_result == 3:
|
||||||
|
this_play = gb_result_3(session, this_play)
|
||||||
|
elif groundball_result == 4:
|
||||||
|
this_play = gb_result_4(session, this_play)
|
||||||
|
elif groundball_result == 5:
|
||||||
|
this_play = gb_result_5(session, this_play, to_mif)
|
||||||
|
elif groundball_result == 6:
|
||||||
|
this_play = gb_result_6(session, this_play, to_right_side)
|
||||||
|
elif groundball_result == 7:
|
||||||
|
this_play = gb_result_7(session, this_play)
|
||||||
|
elif groundball_result == 8:
|
||||||
|
this_play = gb_result_8(session, this_play)
|
||||||
|
elif groundball_result == 9:
|
||||||
|
this_play = gb_result_9(session, this_play)
|
||||||
|
elif groundball_result == 10:
|
||||||
|
this_play = gb_result_10(session, this_play)
|
||||||
|
elif groundball_result == 11:
|
||||||
|
this_play = gb_result_11(session, this_play)
|
||||||
|
elif groundball_result == 12:
|
||||||
|
this_play = gb_result_12(session, this_play, interaction)
|
||||||
|
elif groundball_result == 13:
|
||||||
|
this_play = gb_result_13(session, this_play)
|
||||||
|
|
||||||
|
session.add(this_play)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(this_play)
|
||||||
|
|
||||||
|
return this_play
|
||||||
|
|
||||||
|
|
||||||
|
def gb_result_1(session: Session, this_play: Play):
|
||||||
|
logger.info(f'GB 1')
|
||||||
|
this_play = advance_runners(session, this_play, 0)
|
||||||
|
this_play.ab, this_play.ab, this_play.outs = 1, 1, 1
|
||||||
|
|
||||||
|
return this_play
|
||||||
|
|
||||||
|
|
||||||
|
def gb_result_2(session: Session, this_play: Play):
|
||||||
|
logger.info(f'GB 2')
|
||||||
|
num_outs = 2 if this_play.starting_outs <= 1 else 1
|
||||||
|
|
||||||
|
this_play.ab, this_play.outs = 1, 1
|
||||||
|
this_play.on_first_final = None
|
||||||
|
|
||||||
|
if num_outs + this_play.starting_outs < 3:
|
||||||
|
if this_play.on_second:
|
||||||
|
this_play.on_second_final = 3
|
||||||
|
if this_play.on_third:
|
||||||
|
this_play.on_third_final = 4
|
||||||
|
log_run_scored(session, runner=this_play.on_third, this_play=this_play)
|
||||||
|
|
||||||
|
return this_play
|
||||||
|
|
||||||
|
|
||||||
|
def gb_result_3(session: Session, this_play: Play):
|
||||||
|
logger.info(f'GB 3')
|
||||||
|
if this_play.starting_outs < 2:
|
||||||
|
this_play = advance_runners(session, this_play, 1)
|
||||||
|
|
||||||
|
this_play.ab, this_play.outs = 1, 1
|
||||||
|
return this_play
|
||||||
|
|
||||||
|
|
||||||
|
def gb_result_4(session: Session, this_play: Play):
|
||||||
|
logger.info(f'GB 4')
|
||||||
|
if this_play.starting_outs < 2:
|
||||||
|
this_play = advance_runners(session, this_play, 1)
|
||||||
|
|
||||||
|
this_play.ab, this_play.outs = 1, 1
|
||||||
|
this_play.on_first_final = None
|
||||||
|
|
||||||
|
return this_play
|
||||||
|
|
||||||
|
|
||||||
|
def gb_result_5(session: Session, this_play: Play, to_mif: bool):
|
||||||
|
logger.info(f'GB 5')
|
||||||
|
this_play.ab, this_play.outs = 1, 1
|
||||||
|
|
||||||
|
if to_mif:
|
||||||
|
this_play = gb_result_3(session, this_play)
|
||||||
|
else:
|
||||||
|
this_play = gb_result_1(session, this_play)
|
||||||
|
|
||||||
|
return this_play
|
||||||
|
|
||||||
|
|
||||||
|
def gb_result_6(session: Session, this_play: Play, to_right_side: bool):
|
||||||
|
logger.info(f'GB 6')
|
||||||
|
this_play.ab, this_play.outs = 1, 1
|
||||||
|
|
||||||
|
if to_right_side:
|
||||||
|
this_play = gb_result_3(session, this_play)
|
||||||
|
else:
|
||||||
|
this_play = gb_result_1(session, this_play)
|
||||||
|
|
||||||
|
return this_play
|
||||||
|
|
||||||
|
|
||||||
|
def gb_result_7(session: Session, this_play: Play):
|
||||||
|
logger.info(f'GB 7')
|
||||||
|
this_play.ab, this_play.outs = 1, 1
|
||||||
|
this_play = advance_runners(session, this_play, num_bases=1, only_forced=True)
|
||||||
|
return this_play
|
||||||
|
|
||||||
|
|
||||||
|
def gb_result_8(session: Session, this_play: Play):
|
||||||
|
logger.info(f'GB 8')
|
||||||
|
return gb_result_7(session, this_play)
|
||||||
|
|
||||||
|
|
||||||
|
def gb_result_9(session: Session, this_play: Play):
|
||||||
|
logger.info(f'GB 9')
|
||||||
|
this_play.ab, this_play.outs = 1, 1
|
||||||
|
this_play.on_third_final = 3
|
||||||
|
this_play.on_first_final = 2
|
||||||
|
return this_play
|
||||||
|
|
||||||
|
|
||||||
|
def gb_result_10(session: Session, this_play: Play):
|
||||||
|
logger.info(f'GB 10')
|
||||||
|
num_outs = 2 if this_play.starting_outs <= 1 else 1
|
||||||
|
this_play.ab, this_play.outs = 1, num_outs
|
||||||
|
this_play.on_second_final = 3
|
||||||
|
this_play.on_first_final = 2
|
||||||
|
return this_play
|
||||||
|
|
||||||
|
|
||||||
|
def gb_result_11(session: Session, this_play: Play):
|
||||||
|
logger.info(f'GB 11')
|
||||||
|
this_play.ab, this_play.outs = 1, 1
|
||||||
|
this_play.on_first_final = 2
|
||||||
|
this_play.on_second_final = 3
|
||||||
|
this_play.batter_final = 1
|
||||||
|
return this_play
|
||||||
|
|
||||||
|
|
||||||
|
async def gb_decide(session: Session, this_play: Play, interaction: discord.Interaction):
|
||||||
|
logger.info(f'GB Decide')
|
||||||
|
runner = this_play.on_third if this_play.on_third is not None else this_play.on_second
|
||||||
|
pos_rating = await get_position(session, this_play.defender.card, this_play.check_pos)
|
||||||
|
safe_range = runner.card.batterscouting.battingcard.running - 4 + pos_rating.range
|
||||||
|
|
||||||
|
if this_play.game.ai_team is not None and this_play.ai_is_batting:
|
||||||
|
run_resp = this_play.managerai.gb_decide_run(session, this_play.game)
|
||||||
|
|
||||||
|
if this_play.on_second is None:
|
||||||
|
log_exception(InvalidResultException, 'Cannot run GB Decide without a runner on second.')
|
||||||
|
|
||||||
|
if safe_range >= run_resp.min_safe:
|
||||||
|
is_lead_running = True
|
||||||
|
else:
|
||||||
|
is_lead_running = False
|
||||||
|
|
||||||
|
else:
|
||||||
|
is_lead_running = await ask_confirm(
|
||||||
|
interaction,
|
||||||
|
f'Is {runner.card.player.name} attempting to advance to third?',
|
||||||
|
label_type='yes',
|
||||||
|
delete_question=False
|
||||||
|
)
|
||||||
|
|
||||||
|
if not is_lead_running:
|
||||||
|
this_play = advance_runners(session, this_play, 0)
|
||||||
|
this_play.outs = 1
|
||||||
|
|
||||||
|
else:
|
||||||
|
if this_play.game.ai_team is not None and not this_play.ai_is_batting:
|
||||||
|
throw_resp = this_play.managerai.gb_decide_throw(
|
||||||
|
session,
|
||||||
|
this_play.game,
|
||||||
|
runner_speed=runner.card.batterscouting.battingcard.running,
|
||||||
|
defender_range=pos_rating.range
|
||||||
|
)
|
||||||
|
throw_for_lead = throw_resp.at_lead_runner
|
||||||
|
await interaction.channel.send(
|
||||||
|
content=f'**{this_play.defender.player.name}** is {"not" if not throw_for_lead else ""} throwing for {runner.player.name}{"!" if throw_for_lead else "."}'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
throw_for_lead = await ask_confirm(
|
||||||
|
interaction,
|
||||||
|
f'Is {this_play.defender.player.name} throwing for {runner.player.name}?',
|
||||||
|
label_type='yes'
|
||||||
|
)
|
||||||
|
|
||||||
|
if not throw_for_lead:
|
||||||
|
if this_play.starting_outs < 2:
|
||||||
|
this_play = advance_runners(session, this_play, 1)
|
||||||
|
this_play.outs = 1
|
||||||
|
|
||||||
|
else:
|
||||||
|
is_lead_out = await ask_confirm(
|
||||||
|
interaction,
|
||||||
|
f'Was {runner.card.player.name} thrown out?',
|
||||||
|
custom_confirm_label='Thrown Out',
|
||||||
|
custom_cancel_label='Safe'
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_lead_out:
|
||||||
|
this_play.outs = 1
|
||||||
|
this_play.batter_final = 1
|
||||||
|
|
||||||
|
if this_play.on_third:
|
||||||
|
this_play.on_first_final = 2 if this_play.on_first is not None else 0
|
||||||
|
this_play.on_second_final = 3 if this_play.on_second is not None else 0
|
||||||
|
elif this_play.on_second:
|
||||||
|
this_play.on_first_final = 2 if this_play.on_first is not None else 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
this_play = advance_runners(session, this_play, num_bases=1)
|
||||||
|
this_play.batter_final = 1
|
||||||
|
|
||||||
|
return this_play
|
||||||
|
|
||||||
|
|
||||||
|
async def gb_result_12(session: Session, this_play: Play, interaction: discord.Interaction):
|
||||||
|
logger.info(f'GB 12')
|
||||||
|
if this_play.check_pos in ['1B', '2B']:
|
||||||
|
return gb_result_3(session, this_play)
|
||||||
|
elif this_play.check_pos == '3B':
|
||||||
|
return gb_result_1(session, this_play)
|
||||||
|
else:
|
||||||
|
return await gb_decide(session, this_play, interaction)
|
||||||
|
|
||||||
|
|
||||||
|
def gb_result_13(session: Session, this_play: Play):
|
||||||
|
logger.info(f'GB 13')
|
||||||
|
if this_play.check_pos in ['C', '3B']:
|
||||||
|
num_outs = 2 if this_play.starting_outs <= 1 else 1
|
||||||
|
this_play.ab, this_play.outs = 1, num_outs
|
||||||
|
this_play.batter_final = 1
|
||||||
|
else:
|
||||||
|
this_play = gb_result_2(session, this_play)
|
||||||
|
|
||||||
|
return this_play
|
||||||
|
|||||||
@ -73,3 +73,7 @@ class NoHumanTeamsException(GameException):
|
|||||||
|
|
||||||
class GoogleSheetsException(GameException):
|
class GoogleSheetsException(GameException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidResultException(GameException):
|
||||||
|
pass
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from sqlmodel import Session, SQLModel, UniqueConstraint, create_engine, select,
|
|||||||
from sqlalchemy import func, desc
|
from sqlalchemy import func, desc
|
||||||
|
|
||||||
from exceptions import *
|
from exceptions import *
|
||||||
from in_game.managerai_responses import DefenseResponse, JumpResponse, TagResponse, ThrowResponse, UncappedRunResponse
|
from in_game.managerai_responses import DefenseResponse, JumpResponse, RunResponse, TagResponse, ThrowResponse, UncappedRunResponse
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('discord_app')
|
logger = logging.getLogger('discord_app')
|
||||||
@ -545,7 +545,7 @@ class ManagerAi(ManagerAiBase, table=True):
|
|||||||
if this_play is None:
|
if this_play is None:
|
||||||
raise GameException(f'No game found while checking tag_from_second')
|
raise GameException(f'No game found while checking tag_from_second')
|
||||||
|
|
||||||
ai_rd = this_play.ai_run_diff()
|
ai_rd = this_play.ai_run_diff
|
||||||
aggression_mod = abs(self.ahead_aggression - 5 if ai_rd > 0 else self.behind_aggression - 5)
|
aggression_mod = abs(self.ahead_aggression - 5 if ai_rd > 0 else self.behind_aggression - 5)
|
||||||
adjusted_running = self.running + aggression_mod
|
adjusted_running = self.running + aggression_mod
|
||||||
|
|
||||||
@ -569,7 +569,7 @@ class ManagerAi(ManagerAiBase, table=True):
|
|||||||
if this_play is None:
|
if this_play is None:
|
||||||
raise GameException(f'No game found while checking throw_at_uncapped')
|
raise GameException(f'No game found while checking throw_at_uncapped')
|
||||||
|
|
||||||
ai_rd = this_play.ai_run_diff()
|
ai_rd = this_play.ai_run_diff
|
||||||
aggression = self.ahead_aggression if ai_rd > 0 else self.behind_aggression
|
aggression = self.ahead_aggression if ai_rd > 0 else self.behind_aggression
|
||||||
current_outs = this_play.starting_outs + this_play.outs
|
current_outs = this_play.starting_outs + this_play.outs
|
||||||
|
|
||||||
@ -608,7 +608,7 @@ class ManagerAi(ManagerAiBase, table=True):
|
|||||||
if this_play is None:
|
if this_play is None:
|
||||||
raise GameException(f'No game found while checking uncapped_advance_lead')
|
raise GameException(f'No game found while checking uncapped_advance_lead')
|
||||||
|
|
||||||
ai_rd = this_play.ai_run_diff()
|
ai_rd = this_play.ai_run_diff
|
||||||
aggression = self.ahead_aggression - 5 if ai_rd > 0 else self.behind_aggression - 5
|
aggression = self.ahead_aggression - 5 if ai_rd > 0 else self.behind_aggression - 5
|
||||||
|
|
||||||
if ai_rd > 4:
|
if ai_rd > 4:
|
||||||
@ -656,7 +656,7 @@ class ManagerAi(ManagerAiBase, table=True):
|
|||||||
if this_play is None:
|
if this_play is None:
|
||||||
raise GameException(f'No game found while checking uncapped_advance_lead')
|
raise GameException(f'No game found while checking uncapped_advance_lead')
|
||||||
|
|
||||||
ai_rd = this_play.ai_run_diff()
|
ai_rd = this_play.ai_run_diff
|
||||||
aggression = self.ahead_aggression - 5 if ai_rd > 0 else self.behind_aggression - 5
|
aggression = self.ahead_aggression - 5 if ai_rd > 0 else self.behind_aggression - 5
|
||||||
|
|
||||||
if self.starting_outs == 2 and self.on_base_code > 0:
|
if self.starting_outs == 2 and self.on_base_code > 0:
|
||||||
@ -690,7 +690,33 @@ class ManagerAi(ManagerAiBase, table=True):
|
|||||||
|
|
||||||
if len(ai_note) == 0 and self.on_base_code > 0:
|
if len(ai_note) == 0 and self.on_base_code > 0:
|
||||||
ai_note += f'- play straight up\n'
|
ai_note += f'- play straight up\n'
|
||||||
|
|
||||||
|
def gb_decide_run(self, session: Session, this_game: Game) -> RunResponse:
|
||||||
|
this_resp = RunResponse()
|
||||||
|
this_play = this_game.current_play_or_none(session)
|
||||||
|
if this_play is None:
|
||||||
|
raise GameException(f'No game found while checking gb_decide_run')
|
||||||
|
|
||||||
|
ai_rd = this_play.ai_run_diff
|
||||||
|
aggression = self.ahead_aggression - 5 if ai_rd > 0 else self.behind_aggression - 5
|
||||||
|
|
||||||
|
this_resp.min_safe = 15 - aggression # TODO: write this algorithm
|
||||||
|
return this_resp
|
||||||
|
|
||||||
|
def gb_decide_throw(self, session: Session, this_game: Game, runner_speed: int, defender_range: int) -> ThrowResponse:
|
||||||
|
this_resp = ThrowResponse(at_lead_runner=True)
|
||||||
|
this_play = this_game.current_play_or_none(session)
|
||||||
|
if this_play is None:
|
||||||
|
raise GameException(f'No game found while checking gb_decide_throw')
|
||||||
|
|
||||||
|
ai_rd = this_play.ai_run_diff
|
||||||
|
aggression = self.ahead_aggression - 5 if ai_rd > 0 else self.behind_aggression - 5
|
||||||
|
|
||||||
|
if (runner_speed - 4 + defender_range) <= (10 + aggression):
|
||||||
|
this_resp.at_lead_runner = True
|
||||||
|
|
||||||
|
return this_resp
|
||||||
|
|
||||||
|
|
||||||
class CardsetBase(SQLModel):
|
class CardsetBase(SQLModel):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
@ -1093,7 +1119,8 @@ class PlayBase(SQLModel):
|
|||||||
def uppercase_strings(cls, value: str) -> str:
|
def uppercase_strings(cls, value: str) -> str:
|
||||||
return value.upper()
|
return value.upper()
|
||||||
|
|
||||||
def ai_run_diff(self):
|
@property
|
||||||
|
def ai_run_diff(self) -> int:
|
||||||
if self.game.ai_team == 'away':
|
if self.game.ai_team == 'away':
|
||||||
return self.away_score - self.home_score
|
return self.away_score - self.home_score
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -33,6 +33,10 @@ class DecisionModel(pydantic.BaseModel):
|
|||||||
game_finished: int = 0
|
game_finished: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
def get_batting_team(session: Session, this_play: Play) -> Team:
|
||||||
|
return this_play.game.away_team if this_play.inning_half == 'top' else this_play.game.home_team
|
||||||
|
|
||||||
|
|
||||||
def get_games_by_channel(session: Session, channel_id: int) -> list[Game]:
|
def get_games_by_channel(session: Session, channel_id: int) -> list[Game]:
|
||||||
logger.info(f'Getting games in channel {channel_id}')
|
logger.info(f'Getting games in channel {channel_id}')
|
||||||
return session.exec(select(Game).where(Game.channel_id == channel_id, Game.active)).all()
|
return session.exec(select(Game).where(Game.channel_id == channel_id, Game.active)).all()
|
||||||
@ -239,7 +243,7 @@ async def get_pitcher_scouting_or_none(session: Session, card: Card, skip_cache:
|
|||||||
ratings_vr=s_query['ratings'][0] if s_query['ratings'][0]['vs_hand'] == 'R' else s_query['ratings'][1],
|
ratings_vr=s_query['ratings'][0] if s_query['ratings'][0]['vs_hand'] == 'R' else s_query['ratings'][1],
|
||||||
ratings_vl=s_query['ratings'][0] if s_query['ratings'][0]['vs_hand'] == 'L' else s_query['ratings'][1]
|
ratings_vl=s_query['ratings'][0] if s_query['ratings'][0]['vs_hand'] == 'L' else s_query['ratings'][1]
|
||||||
)
|
)
|
||||||
pos_rating = await get_and_cache_position(session, card, 'P')
|
pos_rating = await get_position(session, card, 'P')
|
||||||
return scouting
|
return scouting
|
||||||
|
|
||||||
return None
|
return None
|
||||||
@ -307,7 +311,7 @@ async def shared_get_scouting(session: Session, this_card: Card, which: Literal[
|
|||||||
return this_scouting
|
return this_scouting
|
||||||
|
|
||||||
|
|
||||||
async def get_and_cache_position(session: Session, this_card: Card, position: Literal['P', 'C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF'], skip_cache: bool = False):
|
async def get_position(session: Session, this_card: Card, position: Literal['P', 'C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF'], skip_cache: bool = False) -> PositionRating:
|
||||||
logger.info(f'Pulling position rating for {this_card.player.name_with_desc} at {position}')
|
logger.info(f'Pulling position rating for {this_card.player.name_with_desc} at {position}')
|
||||||
if not skip_cache:
|
if not skip_cache:
|
||||||
this_pos = session.exec(select(PositionRating).where(PositionRating.player_id == this_card.player.id, PositionRating.position == position, PositionRating.variant == this_card.variant)).all()
|
this_pos = session.exec(select(PositionRating).where(PositionRating.player_id == this_card.player.id, PositionRating.position == position, PositionRating.variant == this_card.variant)).all()
|
||||||
@ -347,7 +351,6 @@ async def get_and_cache_position(session: Session, this_card: Card, position: Li
|
|||||||
|
|
||||||
log_exception(PositionNotFoundException, f'{position} ratings not found for {this_card.player.name_with_desc}')
|
log_exception(PositionNotFoundException, f'{position} ratings not found for {this_card.player.name_with_desc}')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def get_or_create_ai_card(session: Session, player: Player, team: Team, skip_cache: bool = False, dev_mode: bool = False) -> Card:
|
async def get_or_create_ai_card(session: Session, player: Player, team: Team, skip_cache: bool = False, dev_mode: bool = False) -> Card:
|
||||||
logger.info(f'Getting or creating card for {player.name_with_desc} on the {team.sname}')
|
logger.info(f'Getting or creating card for {player.name_with_desc} on the {team.sname}')
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from sqlmodel import Session, select, func
|
from sqlmodel import Session, select, func
|
||||||
|
|
||||||
from command_logic.logic_gameplay import advance_runners, doubles, get_obc, get_re24, get_wpa, complete_play, log_run_scored, strikeouts
|
from command_logic.logic_gameplay import advance_runners, doubles, get_obc, get_re24, get_wpa, complete_play, log_run_scored, strikeouts, steals
|
||||||
from in_game.gameplay_models import Lineup, Play
|
from in_game.gameplay_models import Lineup, Play
|
||||||
from tests.factory import session_fixture, Game
|
from tests.factory import session_fixture, Game
|
||||||
|
|
||||||
@ -194,3 +194,17 @@ async def test_doubles(session: Session):
|
|||||||
assert play_2_ghost_2.on_first_final == 3
|
assert play_2_ghost_2.on_first_final == 3
|
||||||
|
|
||||||
|
|
||||||
|
async def test_stealing(session: Session):
|
||||||
|
game_1 = session.get(Game, 1)
|
||||||
|
play_1 = session.get(Play, 1)
|
||||||
|
play_1.hit, play_1.batter_final = 1, 1
|
||||||
|
play_2 = complete_play(session, play_1)
|
||||||
|
|
||||||
|
assert play_2.play_num == 2
|
||||||
|
|
||||||
|
play_2 = await steals(session, None, play_2, 'stolen-base', to_base=2)
|
||||||
|
|
||||||
|
assert play_2.on_first_final == 2
|
||||||
|
assert play_2.sb == 1
|
||||||
|
assert play_2.runner == play_2.on_first
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ from sqlmodel import Session
|
|||||||
from exceptions import CardNotFoundException, log_exception
|
from exceptions import CardNotFoundException, log_exception
|
||||||
from in_game.game_helpers import legal_check
|
from in_game.game_helpers import legal_check
|
||||||
from in_game.gameplay_models import Game, Lineup, Play, Team
|
from in_game.gameplay_models import Game, Lineup, Play, Team
|
||||||
from in_game.gameplay_queries import get_and_cache_position, get_card_or_none
|
from in_game.gameplay_queries import get_position, get_card_or_none
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('discord_app')
|
logger = logging.getLogger('discord_app')
|
||||||
@ -116,7 +116,7 @@ class SelectStartingPitcher(discord.ui.Select):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
await get_and_cache_position(self.session, human_sp_card, 'P')
|
await get_position(self.session, human_sp_card, 'P')
|
||||||
|
|
||||||
legal_data = await legal_check([self.values[0]], difficulty_name=self.league_name)
|
legal_data = await legal_check([self.values[0]], difficulty_name=self.league_name)
|
||||||
if not legal_data['legal']:
|
if not legal_data['legal']:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user