Log stealing done

Log xchecks started
This commit is contained in:
Cal Corum 2024-11-25 13:17:51 -06:00
parent 073bd04b4b
commit d6d3d7beb0
9 changed files with 2442 additions and 23 deletions

View File

@ -11,7 +11,7 @@ import pygsheets
from sqlmodel import or_
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 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
@ -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.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_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.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...')
for batter in batter_lineups:
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)
@ -564,6 +564,58 @@ class Gameplay(commands.Cog):
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')
async def log_undo_play_command(self, interaction: discord.Interaction):
with Session(engine) as session:

View File

@ -14,7 +14,7 @@ from discord.ext import commands, tasks
from peewee import IntegrityError
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, \
get_pos_abbrev, SBA_COLOR, get_roster_lineups, give_packs, send_to_channel, \
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'])
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)
all_embeds = [x for x in embeds if x is not None]

View File

@ -10,11 +10,12 @@ from sqlalchemy import delete
from typing import Literal
from api_calls import db_delete, db_get, db_post
from dice import sa_fielding_roll
from exceptions import *
from helpers import DEFENSE_LITERAL, SBA_COLOR, get_channel
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_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.dropdown import DropdownView, SelectStartingPitcher, SelectViewDefense
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')}'
if len(baserunner_string) > 0:
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
# 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:
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)
@ -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)
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(
interaction=interaction,
@ -1534,6 +1535,142 @@ async def chaos(session: Session, interaction: discord.Interaction, this_play: P
session.refresh(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:
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)
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

1944
dice.py

File diff suppressed because it is too large Load Diff

View File

@ -73,3 +73,7 @@ class NoHumanTeamsException(GameException):
class GoogleSheetsException(GameException):
pass
class InvalidResultException(GameException):
pass

View File

@ -10,7 +10,7 @@ from sqlmodel import Session, SQLModel, UniqueConstraint, create_engine, select,
from sqlalchemy import func, desc
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')
@ -545,7 +545,7 @@ class ManagerAi(ManagerAiBase, table=True):
if this_play is None:
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)
adjusted_running = self.running + aggression_mod
@ -569,7 +569,7 @@ class ManagerAi(ManagerAiBase, table=True):
if this_play is None:
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
current_outs = this_play.starting_outs + this_play.outs
@ -608,7 +608,7 @@ class ManagerAi(ManagerAiBase, table=True):
if this_play is None:
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
if ai_rd > 4:
@ -656,7 +656,7 @@ class ManagerAi(ManagerAiBase, table=True):
if this_play is None:
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
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:
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):
id: int | None = Field(default=None, primary_key=True)
@ -1093,7 +1119,8 @@ class PlayBase(SQLModel):
def uppercase_strings(cls, value: str) -> str:
return value.upper()
def ai_run_diff(self):
@property
def ai_run_diff(self) -> int:
if self.game.ai_team == 'away':
return self.away_score - self.home_score
else:

View File

@ -33,6 +33,10 @@ class DecisionModel(pydantic.BaseModel):
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]:
logger.info(f'Getting games in channel {channel_id}')
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_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 None
@ -307,7 +311,7 @@ async def shared_get_scouting(session: Session, this_card: Card, which: Literal[
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}')
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()
@ -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}')
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}')

View File

@ -1,7 +1,7 @@
import pytest
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 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
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

View File

@ -9,7 +9,7 @@ from sqlmodel import Session
from exceptions import CardNotFoundException, log_exception
from in_game.game_helpers import legal_check
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')
@ -116,7 +116,7 @@ class SelectStartingPitcher(discord.ui.Select):
)
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)
if not legal_data['legal']: