Check for AI fatigue
This commit is contained in:
parent
92a56017a9
commit
4a76d59481
@ -13,7 +13,7 @@ import sqlalchemy
|
||||
from sqlmodel import func, or_
|
||||
|
||||
from api_calls import db_get
|
||||
from command_logic.logic_gameplay import bunts, chaos, complete_game, doubles, flyballs, frame_checks, get_full_roster_from_sheets, checks_log_interaction, complete_play, get_scorebug_embed, groundballs, hit_by_pitch, homeruns, is_game_over, lineouts, manual_end_game, new_game_checks, new_game_conflicts, popouts, read_lineup, show_defense_cards, singles, starting_pitcher_dropdown_view, steals, strikeouts, sub_batter_dropdown_view, triples, undo_play, update_game_settings, walks, xchecks, activate_last_play
|
||||
from command_logic.logic_gameplay import bunts, chaos, complete_game, doubles, flyballs, frame_checks, get_full_roster_from_sheets, checks_log_interaction, complete_play, get_scorebug_embed, groundballs, hit_by_pitch, homeruns, is_game_over, lineouts, manual_end_game, new_game_checks, new_game_conflicts, popouts, read_lineup, select_ai_reliever, show_defense_cards, singles, starting_pitcher_dropdown_view, steals, strikeouts, sub_batter_dropdown_view, substitute_player, triples, undo_play, update_game_settings, walks, xchecks, activate_last_play
|
||||
from dice import ab_roll
|
||||
from exceptions import *
|
||||
import gauntlets
|
||||
@ -69,9 +69,9 @@ class Gameplay(commands.Cog):
|
||||
this_play = activate_last_play(session, this_game)
|
||||
except Exception as e:
|
||||
this_play = this_game.initialize_play(session)
|
||||
|
||||
if this_play is None:
|
||||
log_exception(PlayNotFoundException, f'Attempting to display gamestate, but cannot find current play')
|
||||
finally:
|
||||
if this_play is None:
|
||||
log_exception(PlayNotFoundException, f'Attempting to display gamestate, but cannot find current play')
|
||||
|
||||
if is_game_over(this_play):
|
||||
logger.info(f'Game {this_play.game.id} seems to be over')
|
||||
@ -98,6 +98,14 @@ class Gameplay(commands.Cog):
|
||||
await self.post_play(session, interaction, this_play)
|
||||
await interaction.channel.send(content=f'I let Cal know his bot is stupid')
|
||||
|
||||
if this_play.pitcher.is_fatigued and not this_play.ai_is_batting:
|
||||
if this_play.managerai.replace_pitcher(session, this_play.game):
|
||||
logger.info(f'Running a pitcher sub')
|
||||
await interaction.edit_original_response(content='The AI is making a pitching change...')
|
||||
new_pitcher_card = await select_ai_reliever(session, this_play.pitcher.team, this_play)
|
||||
new_pitcher_lineup = substitute_player(session, this_play, this_play.pitcher, new_pitcher_card, 'P')
|
||||
logger.info(f'Sub complete')
|
||||
|
||||
scorebug_buttons, this_ab_roll = None, None
|
||||
scorebug_embed = await get_scorebug_embed(session, this_play.game, full_length=full_length, classic=CLASSIC_EMBED)
|
||||
|
||||
|
||||
@ -16,8 +16,8 @@ from exceptions import *
|
||||
from helpers import COLORS, DEFENSE_LITERAL, SBA_COLOR, get_channel, team_role
|
||||
from in_game.ai_manager import get_starting_lineup
|
||||
from in_game.game_helpers import PUBLIC_FIELDS_CATEGORY_NAME, legal_check
|
||||
from in_game.gameplay_models import BattingCard, Game, Lineup, PositionRating, RosterLink, Team, Play
|
||||
from in_game.gameplay_queries import get_active_games_by_team, get_available_batters, get_batter_card, get_batting_statline, get_pitching_statline, 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 in_game.gameplay_models import BattingCard, Card, Game, Lineup, PositionRating, RosterLink, Team, Play
|
||||
from in_game.gameplay_queries import get_active_games_by_team, get_available_batters, get_batter_card, get_batting_statline, get_game_cardset_links, get_or_create_ai_card, get_pitcher_runs_by_innings, get_pitching_statline, get_plays_by_pitcher, 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 in_game.managerai_responses import DefenseResponse
|
||||
from utilities.buttons import ButtonOptions, Confirm, ask_confirm
|
||||
from utilities.dropdown import DropdownView, SelectBatterSub, SelectStartingPitcher, SelectViewDefense
|
||||
@ -475,7 +475,6 @@ def complete_play(session:Session, this_play: Play):
|
||||
on_first, on_second, on_third = None, None, None
|
||||
|
||||
logger.info(f'Running bulk checks')
|
||||
is_go_ahead = False
|
||||
if nso >= 3:
|
||||
switch_sides = True
|
||||
obc = 0
|
||||
@ -557,12 +556,46 @@ def complete_play(session:Session, this_play: Play):
|
||||
if outs is None:
|
||||
outs = 0
|
||||
pow_outs = new_pitcher.card.pitcherscouting.pitchingcard.starter_rating * 3
|
||||
if outs >= pow_outs and not new_pitcher.is_fatigued:
|
||||
new_pitcher.is_fatigued = True
|
||||
if not new_pitcher.is_fatigued:
|
||||
if outs >= pow_outs:
|
||||
logger.info(f'Pitcher is beyond POW - adding fatigue')
|
||||
new_pitcher.is_fatigued = True
|
||||
elif new_pitcher.replacing_id is None:
|
||||
total_runs = session.exec(select(func.count(Play.id)).where(
|
||||
Play.game == this_play.game, Play.pitcher == new_pitcher, Play.run == 1
|
||||
)).one()
|
||||
logger.info(f'Runs allowed by {new_pitcher.player.name_with_desc}: {total_runs}')
|
||||
if total_runs >= 5:
|
||||
if total_runs >= 7:
|
||||
logger.info(f'Starter has allowed 7+ runs - adding fatigue')
|
||||
new_pitcher.is_fatigued = True
|
||||
else:
|
||||
last_two = [x for x in range(this_play.inning_num, this_play.inning_num - 2, -1) if x > 0]
|
||||
runs_last_two = get_pitcher_runs_by_innings(session, this_play.game, new_pitcher, last_two)
|
||||
logger.info(f'Runs allowed last two innings: {runs_last_two}')
|
||||
|
||||
if runs_last_two >= 6:
|
||||
logger.info(f'Starter has allowed at least six in the last two - adding fatigue')
|
||||
new_pitcher.is_fatigued = True
|
||||
|
||||
else:
|
||||
runs_this_inning = get_pitcher_runs_by_innings(session, this_play.game, new_pitcher, [this_play.inning_num])
|
||||
logger.info(f'Runs allowed this inning: {runs_this_inning}')
|
||||
if runs_this_inning >= 5:
|
||||
logger.info(f'Starter has allowed at least five this inning - adding fatigue')
|
||||
new_pitcher.is_fatigued = True
|
||||
session.add(new_pitcher)
|
||||
|
||||
|
||||
if outs >= (pow_outs - 3):
|
||||
in_pow = True
|
||||
if not new_pitcher.is_fatigued:
|
||||
logger.info(f'checking for runners in POW')
|
||||
runners_in_pow = session.exec(select(func.count(Play.id)).where(
|
||||
Play.game == this_play.game, Play.pitcher == new_pitcher, Play.in_pow == True, Play.batter_final != None
|
||||
)).one()
|
||||
logger.info(f'runners in pow: {runners_in_pow}')
|
||||
if runners_in_pow >= 3:
|
||||
new_pitcher.is_fatigued = True
|
||||
else:
|
||||
in_pow = False
|
||||
|
||||
@ -1385,6 +1418,7 @@ async def frame_checks(session: Session, interaction: discord.Interaction, this_
|
||||
session.refresh(this_play)
|
||||
return this_play
|
||||
|
||||
|
||||
async def check_uncapped_advance(session: Session, interaction: discord.Interaction, this_play: Play, lead_runner: Lineup, lead_base: int, trail_runner: Lineup, trail_base: int):
|
||||
this_game = this_play.game
|
||||
outfielder = await show_outfield_cards(session, interaction, this_play)
|
||||
@ -1857,7 +1891,7 @@ async def check_uncapped_advance(session: Session, interaction: discord.Interact
|
||||
|
||||
is_defense_throwing = await ask_confirm(
|
||||
interaction=interaction,
|
||||
question=f'{lead_runner.player.name} is advancing {TO_BASE[lead_base]} with a safe range of **1->{lead_safe_range if lead_base == 3 else {lead_safe_range - 1}}**! Is the defense throwing?',
|
||||
question=f'{lead_runner.player.name} is advancing {TO_BASE[lead_base]} with a safe range of **1->{lead_safe_range if lead_base == 3 else lead_safe_range - 1}**! Is the defense throwing?',
|
||||
label_type='yes'
|
||||
)
|
||||
|
||||
@ -2409,7 +2443,7 @@ async def xchecks(session: Session, interaction: discord.Interaction, this_play:
|
||||
logger.info(f'X-Check in Game #{this_play.game_id} at {this_play.check_pos} for {this_play.defender.card.player.name_with_desc} of the {this_play.pitcher.team.sname} / hit_result: {hit_result} / error_result: {error_result} / is_correct: {is_correct}')
|
||||
|
||||
if not is_correct:
|
||||
# Full questionnaire
|
||||
# TODO: Full questionnaire
|
||||
pass
|
||||
|
||||
if hit_result == 'SPD' and not is_rare_play:
|
||||
@ -3620,3 +3654,77 @@ async def new_game_conflicts(session: Session, interaction: discord.Interaction)
|
||||
content=f'Why don\'t you head down to one of the Public Fields that way other humans can help if anything pops up?'
|
||||
)
|
||||
log_exception(GameException, f'{interaction.user} attempted to start a new game in {interaction.channel.name} so they were redirected to {PUBLIC_FIELDS_CATEGORY_NAME}')
|
||||
|
||||
|
||||
async def select_ai_reliever(session: Session, ai_team: Team, this_play: Play) -> Card:
|
||||
logger.info(f'Selecting an AI reliever')
|
||||
ai_score = this_play.away_score if this_play.game.away_team_id == ai_team.id else this_play.home_score
|
||||
human_score = this_play.home_score if this_play.game.away_team_id == ai_team.id else this_play.away_score
|
||||
|
||||
logger.info(f'scores - ai: {ai_score} / human: {human_score}')
|
||||
if abs(ai_score - human_score) >= 7:
|
||||
need = 'length'
|
||||
elif this_play.inning_num >= 9 and abs(ai_score - human_score) <= 3:
|
||||
need = 'closer'
|
||||
elif this_play.inning_num in [7, 8] and abs(ai_score - human_score) <= 3:
|
||||
need = 'setup'
|
||||
elif abs(ai_score - human_score) <= 3:
|
||||
need = 'middle'
|
||||
else:
|
||||
need = 'length'
|
||||
logger.info(f'need: {need}')
|
||||
|
||||
used_pitchers = get_game_lineups(session, this_play.game, ai_team, is_active=False)
|
||||
used_player_ids = [f'{this_play.pitcher.player_id}']
|
||||
|
||||
for x in used_pitchers:
|
||||
used_player_ids.append(f'{x.player_id}')
|
||||
|
||||
logger.info(f'used ids: {used_player_ids}')
|
||||
id_string = ','.join(used_player_ids)
|
||||
|
||||
all_links = get_game_cardset_links(session, this_play.game)
|
||||
if len(all_links) > 0:
|
||||
cardset_string = ''
|
||||
for x in all_links:
|
||||
cardset_string += f'&cardset_id={x.cardset_id}'
|
||||
else:
|
||||
cardset_string = ''
|
||||
logger.info(f'cardset_string: {cardset_string}')
|
||||
|
||||
rp_json = await db_get(f'teams/{ai_team.id}/rp/{this_play.game.game_type.split("-run")[0]}?need={need}&used_pitcher_ids={id_string}{cardset_string}')
|
||||
|
||||
rp_player = await get_player_or_none(session, player_id=get_player_id_from_dict(rp_json))
|
||||
if rp_player is None:
|
||||
log_exception(PlayerNotFoundException, f'Reliever not found for the {ai_team.lname}')
|
||||
logger.info(f'rp_player: {rp_player}')
|
||||
|
||||
rp_card = await get_or_create_ai_card(session, rp_player, ai_team)
|
||||
logger.info(f'rp_card: {rp_card}')
|
||||
|
||||
return rp_card
|
||||
|
||||
|
||||
def substitute_player(session, this_play: Play, old_player: Lineup, new_player: Card, position: str) -> Lineup:
|
||||
logger.info(f'Substituting {new_player.player.name_with_desc} in for {old_player.card.player.name_with_desc} at {position}')
|
||||
new_lineup = Lineup(
|
||||
team=old_player.team,
|
||||
player=new_player.player,
|
||||
card=new_player,
|
||||
position=position,
|
||||
batting_order=old_player.batting_order,
|
||||
game=this_play.game,
|
||||
after_play=max(this_play.play_num - 1, 0),
|
||||
replacing_id=old_player.id
|
||||
)
|
||||
logger.info(f'new_lineup: {new_lineup}')
|
||||
|
||||
session.add(new_lineup)
|
||||
|
||||
logger.info(f'De-activating last player')
|
||||
old_player.active = False
|
||||
|
||||
session.add(old_player)
|
||||
session.commit()
|
||||
session.refresh(new_lineup)
|
||||
return new_lineup
|
||||
|
||||
@ -658,6 +658,15 @@ class ManagerAi(ManagerAiBase, table=True):
|
||||
logger.info(f'gb_decide_throw response: {this_resp}')
|
||||
return this_resp
|
||||
|
||||
def replace_pitcher(self, session: Session, this_game: Game):
|
||||
this_play = this_game.current_play_or_none(session)
|
||||
if this_play is None:
|
||||
raise GameException(f'No game found while checking replace_pitcher')
|
||||
|
||||
# TODO: Add replace pitcher algorithms
|
||||
return False
|
||||
|
||||
|
||||
|
||||
class CardsetBase(SQLModel):
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
|
||||
@ -7,7 +7,7 @@ import pydantic
|
||||
from sqlalchemy import func
|
||||
from api_calls import db_get, db_post
|
||||
from sqlmodel import col
|
||||
from in_game.gameplay_models import CACHE_LIMIT, BatterScouting, BatterScoutingBase, BattingCard, BattingCardBase, BattingRatings, BattingRatingsBase, Card, CardBase, Cardset, CardsetBase, Lineup, PitcherScouting, PitchingCard, PitchingCardBase, PitchingRatings, PitchingRatingsBase, Player, PlayerBase, PositionRating, PositionRatingBase, RosterLink, Session, Team, TeamBase, select, or_, Game, Play
|
||||
from in_game.gameplay_models import CACHE_LIMIT, BatterScouting, BatterScoutingBase, BattingCard, BattingCardBase, BattingRatings, BattingRatingsBase, Card, CardBase, Cardset, CardsetBase, GameCardsetLink, Lineup, PitcherScouting, PitchingCard, PitchingCardBase, PitchingRatings, PitchingRatingsBase, Player, PlayerBase, PositionRating, PositionRatingBase, RosterLink, Session, Team, TeamBase, select, or_, Game, Play
|
||||
from exceptions import DatabaseError, PositionNotFoundException, log_exception, PlayNotFoundException
|
||||
|
||||
|
||||
@ -1033,4 +1033,32 @@ def get_pitching_statline(session: Session, this_lineup: Lineup) -> str:
|
||||
pit_string += f', {number_string}K'
|
||||
|
||||
return pit_string
|
||||
|
||||
|
||||
def get_game_cardset_links(session: Session, this_game: Game) -> list[GameCardsetLink]:
|
||||
logger.info(f'Getting game cardset links for game: {this_game}')
|
||||
cardset_links = session.exec(select(GameCardsetLink).where(GameCardsetLink.game == this_game)).all()
|
||||
logger.info(f'links: {cardset_links}')
|
||||
return cardset_links
|
||||
|
||||
|
||||
def get_plays_by_pitcher(session: Session, this_game: Game, this_lineup: Lineup, reversed: bool = False) -> list[Play]:
|
||||
logger.info(f'Getting all pitching plays for {this_lineup.card.player.name_with_desc}')
|
||||
statement = select(Play).where(Play.game == this_game, Play.pitcher == this_lineup)
|
||||
if reversed:
|
||||
statement = statement.order_by(Play.id.desc())
|
||||
all_plays = session.exec(statement).all()
|
||||
|
||||
logger.info(f'all_plays: {all_plays}')
|
||||
return all_plays
|
||||
|
||||
|
||||
def get_pitcher_runs_by_innings(session: Session, this_game: Game, this_pitcher: Lineup, innings: list[int]) -> int:
|
||||
logger.info(f'Checking runs for {this_pitcher.player.name_with_desc} in innings: {innings}')
|
||||
runs = session.exec(select(func.count(Play.id)).where(
|
||||
Play.game == this_game, Play.pitcher == this_pitcher, Play.run == 1, Play.inning_num.in_(innings)
|
||||
)).one()
|
||||
logger.info(f'runs: {runs}')
|
||||
|
||||
return runs
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user