2384 lines
101 KiB
Python
2384 lines
101 KiB
Python
import re
|
|
from pandas import DataFrame
|
|
|
|
from helpers import *
|
|
from db_calls import *
|
|
from db_calls_scouting import *
|
|
from db_calls_gameday import *
|
|
|
|
import discord
|
|
from discord.ext import commands
|
|
|
|
|
|
class Gameday(commands.Cog):
|
|
def __init__(self, bot):
|
|
self.bot = bot
|
|
|
|
async def cog_command_error(self, ctx, error):
|
|
await ctx.send(f'{error}')
|
|
|
|
@staticmethod
|
|
def lineup_check(this_game, home=True):
|
|
if home:
|
|
team_id = this_game['home_team_id']
|
|
team_name = 'home team'
|
|
else:
|
|
team_id = this_game['away_team_id']
|
|
team_name = 'away team'
|
|
|
|
full_lineup = get_lineup_members(this_game['id'], active=1, team_id=team_id)
|
|
roster_check = {
|
|
'C': 0,
|
|
'1B': 0,
|
|
'2B': 0,
|
|
'3B': 0,
|
|
'SS': 0,
|
|
'LF': 0,
|
|
'CF': 0,
|
|
'RF': 0,
|
|
'P': 0,
|
|
'DH': 0,
|
|
'batting_count': 0,
|
|
}
|
|
|
|
for x in full_lineup['members']:
|
|
if x['position'] in roster_check.keys():
|
|
roster_check[x["position"]] += 1
|
|
if 0 < x['batting_order'] < 10:
|
|
roster_check['batting_count'] += 1
|
|
|
|
errors = []
|
|
for key in roster_check:
|
|
if roster_check[key] > 1 and key != 'batting_count':
|
|
errors.append(f'{team_name} has {roster_check[key]}x {key}')
|
|
if roster_check[key] < 1 and key != 'DH':
|
|
errors.append(f'{team_name} has {roster_check[key]}x {key}')
|
|
if roster_check['batting_count'] != 9:
|
|
errors.append(f'{team_name} has {roster_check["batting_count"]} players in the batting order')
|
|
|
|
logging.info(f'{"home" if home else "away"} error count: {len(errors)}')
|
|
return {
|
|
'valid': True if len(errors) == 0 else False,
|
|
'errors': errors
|
|
}
|
|
|
|
async def game_state_ascii(self, this_game):
|
|
away_team = await get_one_team(this_game['away_team_id'])
|
|
home_team = await get_one_team(this_game['home_team_id'])
|
|
gs = get_game_state(this_game['id'])['states'][0]
|
|
occupied = '●'
|
|
unoccupied = '○'
|
|
|
|
logging.info(f'checking baserunners')
|
|
first_base = unoccupied if not gs['on_first'] else occupied
|
|
second_base = unoccupied if not gs['on_second'] else occupied
|
|
third_base = unoccupied if not gs['on_third'] else occupied
|
|
logging.info(f'checking inning and score')
|
|
if gs['final']:
|
|
outs = ''
|
|
inning = 'FINAL'
|
|
else:
|
|
outs = f'{gs["outs"]} Out{"s" if gs["outs"] != 1 else ""}'
|
|
half = '▲' if gs['top_half'] else '▼'
|
|
inning = f'{half} {gs["inning"]}'
|
|
|
|
logging.info(f'getting pitcher and batter members')
|
|
pbc_data = self.get_pbc(this_game)
|
|
batter_member = pbc_data['batter']
|
|
pitcher_member = pbc_data['pitcher']
|
|
logging.info(f'getting pitcher and batter players')
|
|
if not batter_member:
|
|
this_batter = {'name': 'BATTER NOT FOUND', 'image': LOGO}
|
|
else:
|
|
this_batter = await get_one_player(batter_member['player_id'])
|
|
if not pitcher_member:
|
|
this_pitcher = {'name': 'PITCHER NOT FOUND', 'image': LOGO}
|
|
else:
|
|
this_pitcher = await get_one_player(pitcher_member['player_id'])
|
|
# pitcher_name = this_pitcher['name'].split(' ', 1)[1]
|
|
# batter_name = this_batter['name'].split(' ', 1)[1]
|
|
|
|
logging.info(f'setting player names')
|
|
# pitcher_name = this_pitcher['name'] if len(this_pitcher['name']) <= 19 else f'{this_pitcher["name"][:16]}...'
|
|
# batter_name = this_batter['name'] if len(this_batter['name']) <= 19 else f'{this_batter["name"][:16]}...'
|
|
logging.info(f'setting player stats')
|
|
era = f'{6.9:.2f}'
|
|
avg = f'{.420:.3f}'
|
|
if avg[0] == '0':
|
|
avg = avg[1:]
|
|
|
|
logging.info(f'generating game string')
|
|
game_string = f'```\n' \
|
|
f'{away_team["abbrev"]: ^4}{gs["away_score"]: ^3} {second_base}' \
|
|
f'{inning: >12}\n' \
|
|
f'{home_team["abbrev"]: ^4}{gs["home_score"]: ^3} {third_base} {first_base}' \
|
|
f'{outs: >10}\n```'
|
|
return {'gs': game_string, 'batter': this_batter, 'pitcher': this_pitcher, 'away_team': away_team,
|
|
'home_team': home_team, 'batter_member': batter_member, 'pitcher_member': pitcher_member}
|
|
|
|
@staticmethod
|
|
def on_base_code(this_state):
|
|
if this_state['on_first'] and this_state['on_second'] and this_state['on_third']:
|
|
obc = 7
|
|
elif this_state['on_second'] and this_state['on_third']:
|
|
obc = 6
|
|
elif this_state['on_first'] and this_state['on_third']:
|
|
obc = 5
|
|
elif this_state['on_first'] and this_state['on_second']:
|
|
obc = 4
|
|
elif this_state['on_third']:
|
|
obc = 3
|
|
elif this_state['on_second']:
|
|
obc = 2
|
|
elif this_state['on_first']:
|
|
obc = 1
|
|
else:
|
|
obc = 0
|
|
|
|
return obc
|
|
|
|
@staticmethod
|
|
def increment_score(this_game, runs: int):
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
if this_state['top_half']:
|
|
new_state = patch_game_state(
|
|
this_state['id'],
|
|
away_score=this_state['away_score'] + runs
|
|
)
|
|
else:
|
|
new_state = patch_game_state(
|
|
this_state['id'],
|
|
home_score=this_state['home_score'] + runs
|
|
)
|
|
|
|
return new_state
|
|
|
|
@staticmethod
|
|
def increment_outs(this_game, outs: int):
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
if this_state['outs'] + outs < 3:
|
|
new_outs = this_state['outs'] + outs
|
|
new_state = patch_game_state(
|
|
this_state['id'],
|
|
outs=new_outs
|
|
)
|
|
elif this_state['top_half']:
|
|
new_state = patch_game_state(
|
|
this_state['id'],
|
|
outs=0,
|
|
top_half=False,
|
|
on_first_id=False,
|
|
on_second_id=False,
|
|
on_third_id=False
|
|
)
|
|
else:
|
|
new_state = patch_game_state(
|
|
this_state['id'],
|
|
outs=0,
|
|
top_half=True,
|
|
inning=this_state['inning'] + 1,
|
|
on_first_id=False,
|
|
on_second_id=False,
|
|
on_third_id=False
|
|
)
|
|
|
|
return new_state
|
|
|
|
def advance_runners(self, this_state, num_bases: int, error: int, force_only=False):
|
|
"""
|
|
Moves runners and gives them a run scored if necessary - does not update game state
|
|
|
|
:param error:
|
|
:param this_state:
|
|
:param num_bases:
|
|
:param force_only:
|
|
:return: int: runs scored on play
|
|
"""
|
|
rbi = 0
|
|
|
|
# logging.info(f'this_state:\n{this_state}\n')
|
|
if this_state['on_third']:
|
|
if not force_only or (force_only and this_state['on_second'] and this_state['on_first']):
|
|
logging.info(f'runner {this_state["on_third"]["id"]} scores')
|
|
on_third = get_one_atbat(
|
|
game_id=this_state['game']['id'], batter_id=this_state['on_third']['player_id']
|
|
)
|
|
patch_atbat(on_third['id'], run=1, error=error)
|
|
patch_game_state(this_state['id'], on_third_id=False)
|
|
rbi += 1
|
|
|
|
# logging.info('pre-on_second')
|
|
if this_state['on_second']:
|
|
if not force_only or (force_only and this_state['on_first']):
|
|
if num_bases > 1:
|
|
logging.info(f'runner {this_state["on_second"]["id"]} scores')
|
|
on_second = get_one_atbat(
|
|
game_id=this_state['game']['id'], batter_id=this_state['on_second']['player_id']
|
|
)
|
|
patch_atbat(on_second['id'], run=1, error=error)
|
|
patch_game_state(this_state['id'], on_second_id=False)
|
|
rbi += 1
|
|
else:
|
|
logging.info(f'runner {this_state["on_second"]["id"]} from second to third')
|
|
patch_game_state(this_state['id'], on_second_id=False, on_third_id=this_state['on_second']['id'])
|
|
|
|
# logging.info('pre-on_first')
|
|
if this_state['on_first']:
|
|
if num_bases > 2:
|
|
logging.info(f'runner {this_state["on_first"]["id"]} scores')
|
|
on_first = get_one_atbat(
|
|
game_id=this_state['game']['id'], batter_id=this_state['on_first']['player_id']
|
|
)
|
|
patch_atbat(on_first['id'], run=1, error=error)
|
|
patch_game_state(this_state['id'], on_first_id=False)
|
|
rbi += 1
|
|
elif num_bases == 2:
|
|
logging.info(f'runner {this_state["on_first"]["id"]} from first to third')
|
|
patch_game_state(this_state['id'], on_first_id=False, on_third_id=this_state['on_first']['id'])
|
|
else:
|
|
logging.info(f'runner {this_state["on_first"]["id"]} from first to second')
|
|
patch_game_state(this_state['id'], on_first_id=False, on_second_id=this_state['on_first']['id'])
|
|
|
|
return rbi
|
|
|
|
@staticmethod
|
|
def get_pbc(this_game):
|
|
"""
|
|
Return a dict including the pitcher, batter, and catcher
|
|
|
|
:param this_game:
|
|
:return:
|
|
"""
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
# Helpers for finding pitcher and batter
|
|
if this_state['top_half']:
|
|
bat_team_id = this_game['away_team_id']
|
|
pit_team_id = this_game['home_team_id']
|
|
batting_order = this_state['away_batter_up']
|
|
else:
|
|
bat_team_id = this_game['home_team_id']
|
|
pit_team_id = this_game['away_team_id']
|
|
batting_order = this_state['home_batter_up']
|
|
|
|
# Get pitcher and batter
|
|
logging.info('get pitcher and batter')
|
|
try:
|
|
this_batter = get_one_member(
|
|
game_id=this_game['id'], team_id=bat_team_id, batting_order=batting_order, active=1
|
|
)
|
|
except:
|
|
this_batter = None
|
|
|
|
try:
|
|
this_pitcher = get_one_member(
|
|
game_id=this_game['id'], team_id=pit_team_id, position='P', active=1
|
|
)
|
|
except:
|
|
this_pitcher = None
|
|
|
|
try:
|
|
this_catcher = get_one_member(
|
|
game_id=this_game['id'], team_id=pit_team_id, position='C', active=1
|
|
)
|
|
except:
|
|
this_catcher = None
|
|
|
|
return {'batter': this_batter, 'pitcher': this_pitcher, 'catcher': this_catcher}
|
|
|
|
@staticmethod
|
|
def get_defender(this_game, pos):
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
if this_state['top_half']:
|
|
this_member = get_one_member(
|
|
game_id=this_game['id'],
|
|
team_id=this_game['home_team_id'],
|
|
position=pos.upper(),
|
|
active=1
|
|
)
|
|
else:
|
|
this_member = get_one_member(
|
|
game_id=this_game['id'],
|
|
team_id=this_game['away_team_id'],
|
|
position=pos.upper(),
|
|
active=1
|
|
)
|
|
|
|
return this_member
|
|
|
|
@staticmethod
|
|
async def get_batter_statline(data):
|
|
batter_string = ''
|
|
bs = get_atbat_totals(data['batter_member']['id'])
|
|
if not bs['empty']:
|
|
batter_string += f' - {bs["hit"] if bs["hit"] else 0}-{bs["ab"] if bs["ab"] else 0}'
|
|
if bs["homerun"]:
|
|
batter_string += f', '
|
|
if bs["homerun"] > 1:
|
|
batter_string += f'{bs["homerun"]} '
|
|
batter_string += 'HR'
|
|
if bs["triple"]:
|
|
batter_string += f', '
|
|
if bs["triple"] > 1:
|
|
batter_string += f'{bs["triple"]} '
|
|
batter_string += '3B'
|
|
if bs["double"]:
|
|
batter_string += f', '
|
|
if bs["double"] > 1:
|
|
batter_string += f'{bs["double"]} '
|
|
batter_string += '2B'
|
|
if bs["bb"]:
|
|
batter_string += f', '
|
|
if bs["bb"] > 1:
|
|
batter_string += f'{bs["bb"]} '
|
|
batter_string += 'BB'
|
|
if bs["hbp"]:
|
|
batter_string += f', '
|
|
if bs["hbp"] > 1:
|
|
batter_string += f'{bs["hbp"]} '
|
|
batter_string += 'HBP'
|
|
if bs["sac"]:
|
|
batter_string += f', '
|
|
if bs["sac"] > 1:
|
|
batter_string += f'{bs["sac"]} '
|
|
batter_string += 'SAC'
|
|
if bs["so"]:
|
|
batter_string += f', '
|
|
if bs["so"] > 1:
|
|
batter_string += f'{bs["so"]} '
|
|
batter_string += 'K'
|
|
else:
|
|
b = await get_one_battingseason(data['batter_member']['player_id'])
|
|
batter_string = '1st PA'
|
|
if len(b) > 0:
|
|
if b['ab'] > 0:
|
|
singles = b['hit'] - b['hr'] - b['triple'] - b['double']
|
|
avg = b['hit'] / b['ab']
|
|
obp = (b['hit'] + b['bb'] + b['ibb'] + b['hbp']) / b['pa']
|
|
slg = ((b['hr'] * 4) + (b['triple'] * 3) + (b['double'] * 2) + singles) / b['ab']
|
|
ops = obp + slg
|
|
woba = ((b['bb'] * .69) + (b['hbp'] * .72) + (singles * .89) + (b['double'] * 1.27) +
|
|
(b['triple'] * 1.62) + (b['hr'] * 2.1)) / (b['pa'] - b['hbp'] - b['sac'])
|
|
ab = f'{b["ab"]:.0f}'
|
|
run = f'{b["run"]:.0f}'
|
|
hit = f'{b["hit"]:.0f}'
|
|
double = f'{b["double"]:.0f}'
|
|
top_stat = {'name': '2B', 'value': double, 'int': b['double']}
|
|
|
|
triple = f'{b["triple"]:.0f}'
|
|
if b["triple"] > top_stat['int']:
|
|
top_stat['name'] = '3B'
|
|
top_stat['value'] = triple
|
|
top_stat['int'] = b['triple']
|
|
|
|
hr = f'{b["hr"]:.0f}'
|
|
if b["hr"] > top_stat['int']:
|
|
top_stat['name'] = 'HR'
|
|
top_stat['value'] = hr
|
|
top_stat['int'] = b['hr']
|
|
|
|
rbi = f'{b["rbi"]:.0f}'
|
|
sb = f'{b["sb"]:.0f}'
|
|
if b["sb"] > top_stat['int']:
|
|
top_stat['name'] = 'SB'
|
|
top_stat['value'] = sb
|
|
top_stat['int'] = b['sb']
|
|
|
|
so = f'{b["so"]:.0f}'
|
|
|
|
batter_string = f' - {avg:.3f} / {obp:.3f} / {slg:.3f}, {top_stat["value"]} {top_stat["name"]}'
|
|
|
|
# batting_string = f'```\n' \
|
|
# f' AVG OBP SLG OPS\n' \
|
|
# f' {avg:.3f} {obp:.3f} {slg:.3f} {ops:.3f}\n``````\n' \
|
|
# f' AB R H 2B 3B HR RBI SB SO\n' \
|
|
# f'{ab: >3} {run: >2} {hit: ^3} {double: >2} {triple: >2} {hr: >2} {rbi: >3} ' \
|
|
# f'{sb: >2} {so: ^3}\n```'
|
|
|
|
logging.info(f'batter_string: {batter_string}')
|
|
return batter_string
|
|
|
|
@staticmethod
|
|
async def get_pitcher_statline(data):
|
|
pitcher_string = ''
|
|
bs = get_atbat_pitching_totals(data['pitcher_member']['id'])
|
|
if not bs['empty']:
|
|
pitcher_string += f'{bs["ip"]:.1f} IP'
|
|
if bs["run"]:
|
|
pitcher_string += f', {bs["run"]} R'
|
|
if bs["run"] != bs["erun"]:
|
|
pitcher_string += f' ({bs["erun"]} ER)'
|
|
if bs["so"]:
|
|
pitcher_string += f', {bs["so"]} K'
|
|
if bs["hit"]:
|
|
pitcher_string += f', {bs["hit"]} H'
|
|
if bs["bb"]:
|
|
pitcher_string += f', {bs["bb"]} BB'
|
|
if bs["hbp"]:
|
|
pitcher_string += f', {bs["hbp"]} HBP'
|
|
if bs["hr"]:
|
|
pitcher_string += f', {bs["hbp"]} HR'
|
|
else:
|
|
p = await get_one_pitchingseason(data['pitcher_member']['player_id'])
|
|
if len(p) > 0:
|
|
if p['ip'] > 0:
|
|
win = f'{p["win"]:.0f}'
|
|
loss = f'{p["loss"]:.0f}'
|
|
save = f'{p["sv"]:.0f}'
|
|
era = f'{(p["erun"] * 9) / p["ip"]:.2f}'
|
|
game = f'{p["game"]:.0f}'
|
|
gs = f'{p["gs"]:.0f}'
|
|
ip = f'{p["ip"]:.0f}'
|
|
if p["ip"] % 1 == 0:
|
|
ip += '.0'
|
|
elif str(p["ip"] % 1)[2] == '3':
|
|
ip += '.1'
|
|
else:
|
|
ip += '.2'
|
|
so = f'{p["so"]:.0f}'
|
|
whip = f'{(p["bb"] + p["hit"]) / p["ip"]:.2f}'
|
|
|
|
g_string = f'{gs} GS' if p['game'] == p['gs'] else f'{game} G'
|
|
pitcher_string = f' - {g_string}, ({win}-{loss})'
|
|
if p["sv"]:
|
|
pitcher_string += f', {save} SV'
|
|
pitcher_string += f', {era} ERA'
|
|
|
|
# pitching_string = f'```\n' \
|
|
# f' W L SV ERA IP SO WHIP\n' \
|
|
# f'{win: >2} {loss: >2} {save: >2} {era: >5} {ip: >5} ' \
|
|
# f'{so: >3} {whip: >4}\n```'
|
|
|
|
logging.info(f'pitcher_string: {pitcher_string}')
|
|
return pitcher_string
|
|
|
|
@staticmethod
|
|
def adv_specific_runner(this_game, from_base, num_bases):
|
|
"""
|
|
Moves a single runner and gives them a run scored if necessary - does not update game state
|
|
|
|
:param this_game:
|
|
:param from_base:
|
|
:param num_bases:
|
|
:return:
|
|
"""
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
rbi = 0
|
|
if from_base == 3 and this_state['on_third']:
|
|
logging.info(f'runner {this_state["on_third"]["id"]} scores')
|
|
this_runner = get_atbat(
|
|
game_id=this_state['game']['id'], batter_id=this_state['on_third']['player_id']
|
|
)['atbats']
|
|
on_third = this_runner[len(this_runner) - 1]
|
|
patch_atbat(on_third['id'], run=1)
|
|
patch_game_state(this_state['id'], on_third_id=False)
|
|
rbi += 1
|
|
|
|
elif from_base == 2 and this_state['on_second']:
|
|
if num_bases > 1:
|
|
logging.info(f'runner {this_state["on_second"]["id"]} scores')
|
|
this_runner = get_atbat(
|
|
game_id=this_state['game']['id'], batter_id=this_state['on_second']['player_id']
|
|
)['atbats']
|
|
on_second = this_runner[len(this_runner) - 1]
|
|
patch_atbat(on_second['id'], run=1)
|
|
patch_game_state(this_state['id'], on_second_id=False)
|
|
rbi += 1
|
|
else:
|
|
logging.info(f'runner {this_state["on_second"]["id"]} from second to third')
|
|
patch_game_state(this_state['id'], on_second_id=False, on_third_id=this_state['on_second']['id'])
|
|
|
|
elif from_base == 1 and this_state['on_first']:
|
|
if num_bases > 2:
|
|
logging.info(f'runner {this_state["on_first"]["id"]} scores')
|
|
this_runner = get_atbat(
|
|
game_id=this_state['game']['id'], batter_id=this_state['on_first']['player_id']
|
|
)['atbats']
|
|
on_first = this_runner[len(this_runner) - 1]
|
|
patch_atbat(on_first['id'], run=1)
|
|
patch_game_state(this_state['id'], on_first_id=False)
|
|
rbi += 1
|
|
elif num_bases == 2:
|
|
logging.info(f'runner {this_state["on_first"]["id"]} from first to third')
|
|
patch_game_state(this_state['id'], on_first_id=False, on_third_id=this_state['on_first']['id'])
|
|
else:
|
|
logging.info(f'runner {this_state["on_first"]["id"]} from first to second')
|
|
patch_game_state(this_state['id'], on_first_id=False, on_second_id=this_state['on_first']['id'])
|
|
|
|
return rbi
|
|
|
|
async def game_state_embed(self, this_game, full_length=True):
|
|
data = await self.game_state_ascii(this_game)
|
|
|
|
embed = discord.Embed(title=f'{data["away_team"]["sname"]} @ {data["home_team"]["sname"]}')
|
|
embed.add_field(name='Game State', value=data['gs'], inline=False)
|
|
|
|
pitcher_string = f'[{data["pitcher"]["name"]}]({get_player_url(data["pitcher"])}) ' \
|
|
f'{await self.get_pitcher_statline(data)}'
|
|
# embed.add_field(name='Pitcher', value=pitcher_string)
|
|
|
|
batter_string = f'[{data["batter"]["name"]}]({get_player_url(data["batter"])}) ' \
|
|
f'{await self.get_batter_statline(data)}'
|
|
# embed.add_field(name='Batter', value=batter_string)
|
|
embed.add_field(name='Matchup', value=f'Pitcher: {pitcher_string}\nvs\nBatter: {batter_string}', inline=False)
|
|
|
|
embed.set_thumbnail(url=f'{data["pitcher"]["image"]}')
|
|
embed.set_image(url=f'{data["batter"]["image"]}')
|
|
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
baserunner_string = ''
|
|
if this_state['on_first']:
|
|
runner = await get_one_player(this_state['on_first']['player_id'])
|
|
# embed.add_field(name='On First', value=f'[{runner["name"]}]({get_player_url(runner)})')
|
|
baserunner_string += f'On First: [{runner["name"]}]({get_player_url(runner)})\n'
|
|
if this_state['on_second']:
|
|
runner = await get_one_player(this_state['on_second']['player_id'])
|
|
# embed.add_field(name='On Second', value=f'[{runner["name"]}]({get_player_url(runner)})')
|
|
baserunner_string += f'On Second: [{runner["name"]}]({get_player_url(runner)})\n'
|
|
if this_state['on_third']:
|
|
runner = await get_one_player(this_state['on_third']['player_id'])
|
|
# embed.add_field(name='On Third', value=f'[{runner["name"]}]({get_player_url(runner)})')
|
|
baserunner_string += f'On Third: [{runner["name"]}]({get_player_url(runner)})\n'
|
|
if len(baserunner_string) > 0:
|
|
embed.add_field(name='Baserunners', value=baserunner_string, inline=False)
|
|
|
|
if not full_length:
|
|
return embed
|
|
|
|
async def lineup_string(team):
|
|
full_lineup = get_lineup_members(this_game['id'], active=1, team_id=team['id'])
|
|
sorted_lineup = sorted(full_lineup['members'], key=lambda item: item['batting_order'])
|
|
l_string = ''
|
|
for x in sorted_lineup:
|
|
player = await get_one_player(x["player_id"])
|
|
l_string += f'{x["batting_order"]}. [{player["name"]}]({get_player_url(player)}) - ' \
|
|
f'{x["position"]}\n'
|
|
if len(l_string) == 0:
|
|
l_string = 'None, yet\n\'!sl\' to set'
|
|
return l_string
|
|
|
|
embed.add_field(
|
|
name=f'{data["away_team"]["abbrev"]} Lineup',
|
|
value=await lineup_string(data["away_team"])
|
|
)
|
|
embed.add_field(
|
|
name=f'{data["home_team"]["abbrev"]} Lineup',
|
|
value=await lineup_string(data["home_team"])
|
|
)
|
|
|
|
return embed
|
|
|
|
def log_play_core(self, this_game, bases=0, outs=0, ab=1, hit=0, error=0, force_only=False):
|
|
"""
|
|
Logs an AtBat, taking the optional parameters into account; does not place batter runner
|
|
|
|
:param error:
|
|
:param this_game:
|
|
:param bases:
|
|
:param outs:
|
|
:param ab:
|
|
:param hit:
|
|
:param force_only:
|
|
:return: {'batter': LineupMember, 'ab': AtBat}
|
|
"""
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
data = self.get_pbc(this_game)
|
|
this_batter = data['batter']
|
|
this_pitcher = data['pitcher']
|
|
|
|
# Get OBC and patch GameState score if necessary
|
|
logging.info('Get OBC and patch GameState score if necessary')
|
|
obc = self.on_base_code(this_state)
|
|
|
|
rbi = 0
|
|
next_batters = {
|
|
'away': this_state['away_batter_up'] + 1 if this_state['away_batter_up'] < 9 else 1,
|
|
'home': this_state['home_batter_up'] + 1 if this_state['home_batter_up'] < 9 else 1,
|
|
}
|
|
new_half = this_state['top_half']
|
|
new_inning = this_state['inning']
|
|
if this_state['outs'] + outs < 3:
|
|
new_outs = this_state['outs'] + outs
|
|
|
|
# Advance runners
|
|
logging.info('Advance runners')
|
|
if obc > 0 and bases > 0:
|
|
rbi = self.advance_runners(this_state, bases, error=error, force_only=force_only)
|
|
if bases == 4:
|
|
rbi += 1
|
|
else:
|
|
# Ends half inning
|
|
new_outs = 0
|
|
new_half = not this_state['top_half']
|
|
if new_half:
|
|
new_inning += 1
|
|
patch_game_state(
|
|
this_state['id'],
|
|
on_first_id=False,
|
|
on_second_id=False,
|
|
on_third_id=False
|
|
)
|
|
|
|
if this_state['top_half']:
|
|
# Update score, batter up, and runner on first
|
|
logging.info(f'current batter: {this_state["away_batter_up"]} / next: {next_batters["away"]}')
|
|
patch_game_state(
|
|
this_state['id'],
|
|
away_score=this_state['away_score'] + rbi,
|
|
away_batter_up=next_batters['away'],
|
|
outs=new_outs,
|
|
top_half=new_half,
|
|
inning=new_inning
|
|
)
|
|
else:
|
|
patch_game_state(
|
|
this_state['id'],
|
|
home_score=this_state['home_score'] + rbi,
|
|
home_batter_up=next_batters['home'],
|
|
outs=new_outs,
|
|
top_half=new_half,
|
|
inning=new_inning
|
|
)
|
|
|
|
logging.info(f'post AB:\ngame_id: {this_game["id"]}\nbatter_id: {this_batter["player_id"]}\n'
|
|
f'pitcher_id: {this_pitcher["player_id"]}\nobc: {obc}\ninning: {this_state["inning"]}\n'
|
|
f'hit: {hit}\nrbi: {rbi}\nab: {ab}\nerror: {error}')
|
|
this_ab = post_atbat({
|
|
'game_id': this_game['id'],
|
|
'batter_id': this_batter['player_id'],
|
|
'pitcher_id': this_pitcher['player_id'],
|
|
'on_base_code': obc,
|
|
'inning': this_state['inning'],
|
|
'hit': hit,
|
|
'rbi': rbi if not error else 0,
|
|
'ab': ab,
|
|
'error': error
|
|
})
|
|
|
|
return {'batter': this_batter, 'ab': this_ab}
|
|
|
|
async def get_csv_batters(self, this_game, team_id):
|
|
bats = []
|
|
|
|
for x in get_lineup_members(this_game['id'], team_id=team_id)['members']:
|
|
this_player = await get_one_player(x['player_id'])
|
|
bs = get_atbat_totals(x['id'])
|
|
rs = get_running_totals(x['id'])
|
|
ch = get_chaos_totals(x['id'])
|
|
ds = get_defense_totals(x['id'])
|
|
|
|
if bs['empty'] and rs['empty'] and ch['empty'] and len(ds['defense']) == 0:
|
|
logging.info(f'No stats for {this_player["name"]} - moving on')
|
|
else:
|
|
dpos = None
|
|
if len(ds['defense']) == 1:
|
|
logging.info('one defense found')
|
|
if ds['defense'][0]['pos'] == x['position']:
|
|
logging.info('matches position')
|
|
dpos = ds['defense'][0]
|
|
else:
|
|
logging.info('doesn\'t match position')
|
|
line = ds['defense'][0]
|
|
bats.append([
|
|
this_player['name'], this_player['team']['abbrev'], x['position'], '', '', '',
|
|
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', line['xch'],
|
|
line['xch'],
|
|
line['xch'], line['xhit'], line['errors'], '', line['sba'], line['csc'], line['roba'],
|
|
line['robs'], line['raa'], line['rto']
|
|
])
|
|
else:
|
|
logging.info('many defenses found')
|
|
for line in ds['defense']:
|
|
if line['pos'] == line['pos']:
|
|
logging.info('this one matches pos')
|
|
dpos = line
|
|
else:
|
|
logging.info('this one does not matche pos')
|
|
bats.append([
|
|
this_player['name'], this_player['team']['abbrev'], x['position'], '', '', '',
|
|
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', line['xch'],
|
|
line['xch'],
|
|
line['xch'], line['xhit'], line['errors'], '', line['sba'], line['csc'], line['roba'],
|
|
line['robs'], line['raa'], line['rto']
|
|
])
|
|
if not dpos:
|
|
logging.info('no defense found; adding blanks')
|
|
dpos = {
|
|
'xch': '',
|
|
'xhit': '',
|
|
'errors': '',
|
|
'sba': '',
|
|
'csc': '',
|
|
'roba': '',
|
|
'robs': '',
|
|
'raa': '',
|
|
'rto': '',
|
|
}
|
|
logging.info('done with defense')
|
|
|
|
bats.append([
|
|
this_player['name'], this_player['team']['abbrev'], x['position'], bs['pa'], bs['ab'], bs['run'],
|
|
bs['hit'], bs['rbi'], bs['double'], bs['triple'], bs['homerun'], bs['bb'], bs['so'],
|
|
bs['hbp'], bs['sac'], bs['ibb'], bs['gidp'], rs['sb'], rs['cs'], bs['bphr'], bs['bpfo'], bs['bp1b'],
|
|
bs['bplo'], '', '', dpos['xch'], dpos['xhit'], dpos['errors'],
|
|
ch['pb'] if x['position'] == 'C' else '', dpos['sba'], dpos['csc'], '', '', dpos['raa'], dpos['rto']
|
|
])
|
|
return bats
|
|
|
|
async def get_csv_pitchers(self, this_game, team_id):
|
|
arms = []
|
|
|
|
for x in get_lineup_members(this_game['id'], team_id=team_id, position='P')['members']:
|
|
this_player = await get_one_player(x['player_id'])
|
|
bs = get_atbat_pitching_totals(x['id'])
|
|
cs = get_chaos_totals(x['id'])
|
|
|
|
if bs['empty'] and cs['empty']:
|
|
logging.info(f'No stats for {this_player["name"]} - moving on')
|
|
else:
|
|
arms.append([
|
|
this_player['name'], this_player['team']['abbrev'], bs['ip'], bs['hit'], bs['run'], bs['erun'],
|
|
bs['so'], bs['bb'], bs['hbp'], cs['wp'], cs['bk'], bs['hr']
|
|
])
|
|
return arms
|
|
|
|
async def get_game_results(self, this_game, combined=False):
|
|
away_bats = await self.get_csv_batters(this_game, this_game['away_team_id'])
|
|
away_arms = await self.get_csv_pitchers(this_game, this_game['away_team_id'])
|
|
home_bats = await self.get_csv_batters(this_game, this_game['home_team_id'])
|
|
home_arms = await self.get_csv_pitchers(this_game, this_game['home_team_id'])
|
|
|
|
if combined:
|
|
batters = [['player', 'team', 'pos', 'pa', 'ab', 'run', 'hit', 'rbi', '2b', '3b', 'hr', 'bb', 'so', 'hbp',
|
|
'sac', 'ibb', 'gidp', 'sb', 'cs', 'bphr', 'bpfo', 'bp1b', 'bplo', 'xba', 'xbt', 'xch', 'xhit', 'e',
|
|
'pb', 'sba', 'csc', 'roba', 'robs', 'raa', 'rto']]
|
|
for x in away_bats:
|
|
batters.append(x)
|
|
for x in home_bats:
|
|
batters.append(x)
|
|
|
|
pitchers = [['pitcher', 'team', 'ip', 'h', 'r', 'er', 'so', 'bb', 'hbp', 'wp', 'bk', 'hr', 'ir', 'irs',
|
|
'gs', 'win', 'loss', 'hold', 'save', 'bs']]
|
|
for x in away_arms:
|
|
pitchers.append(x)
|
|
for x in home_arms:
|
|
pitchers.append(x)
|
|
|
|
return {'batters': batters, 'pitchers': pitchers}
|
|
else:
|
|
return {'ab': away_bats, 'aa': away_arms, 'hb': home_bats, 'ha': home_arms}
|
|
|
|
async def get_game_sheet(self, home_team, away_team, results, player_name):
|
|
sheets = pygsheets.authorize(service_file='storage/major-domo-service-creds.json')
|
|
results_sheet = sheets.drive.copy_file(
|
|
'1e_o5Fg0-Q9sp09btesBpXA_4lUtbNkRngQ32_Ie9_iI',
|
|
f'{away_team["abbrev"]} @ {home_team["abbrev"]} for {player_name}',
|
|
'1G0XJvlUb2cIGjD3b2-XT89TSwAOZmzaz')
|
|
sheet_id = results_sheet['id']
|
|
|
|
results_tab = sheets.open_by_key(sheet_id).worksheet_by_title('Results')
|
|
results_tab.update_values(
|
|
crange='A3', values=results['ab']
|
|
)
|
|
results_tab.update_values(
|
|
crange='A33', values=results['hb']
|
|
)
|
|
results_tab.update_values(
|
|
crange='A22', values=results['aa']
|
|
)
|
|
results_tab.update_values(
|
|
crange='A52', values=results['ha']
|
|
)
|
|
|
|
return sheet_id
|
|
|
|
@commands.command(name='newgame', aliases=['ng'], help='Start a new game')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def new_game_command(self, ctx):
|
|
current = await get_current()
|
|
this_team = await get_team_by_owner(current['season'], ctx.author.id)
|
|
|
|
await ctx.send('**Note:** Make sure this is the channel where you will be playing commands. Once this is set, '
|
|
'I will only take gameplay commands here.')
|
|
|
|
async def get_team(which='home'):
|
|
prompt = f'Please enter the abbrev of the {which} team.'
|
|
this_q = Question(self.bot, ctx.channel, prompt, qtype='text', timeout=15)
|
|
resp = await this_q.ask([ctx.author])
|
|
|
|
if not resp:
|
|
await ctx.send('You keep thinking about it and hit me up later if you figure it out.')
|
|
return None
|
|
else:
|
|
other_team = await get_one_team(resp)
|
|
if not other_team:
|
|
await ctx.send(f'What\'s a **{resp}**? If you could go ahead and run this command again, that\'d '
|
|
f'be great.')
|
|
return None
|
|
else:
|
|
return other_team
|
|
|
|
if not this_team:
|
|
await ctx.send('Hmm...I can\'t find your team. Are you from around here?')
|
|
return
|
|
|
|
try:
|
|
this_matchup = await get_one_schedule(
|
|
season=current['season'],
|
|
team_abbrev1=this_team['abbrev'],
|
|
week=current['week']
|
|
)
|
|
except ValueError as e:
|
|
home_team = await get_team('home')
|
|
away_team = await get_team('away')
|
|
if not home_team or not away_team:
|
|
return
|
|
else:
|
|
away_team = this_matchup['awayteam']
|
|
home_team = this_matchup['hometeam']
|
|
|
|
prompt = f'Would you like to start {away_team["abbrev"]} @ {home_team["abbrev"]} now?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 15)
|
|
resp = await this_q.ask([ctx.author])
|
|
|
|
if not resp:
|
|
home_team = await get_team('home')
|
|
away_team = await get_team('away')
|
|
if not home_team or not away_team:
|
|
return
|
|
|
|
this_game = post_game({
|
|
'away_team_id': away_team['id'],
|
|
'home_team_id': home_team['id'],
|
|
'channel_id': ctx.channel.id
|
|
})
|
|
await ctx.send(random_conf_gif())
|
|
await ctx.send('Go ahead and set lineups with the !setlineup command')
|
|
|
|
@commands.command(name='endgame', aliases=['eg'], help='End game in channel')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def end_game_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs_embed = await self.game_state_embed(this_game)
|
|
|
|
prompt = 'Is this the game you would like to end?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 15, gs_embed)
|
|
resp = await this_q.ask([ctx.author])
|
|
end_proper = True
|
|
|
|
if not resp:
|
|
await ctx.send('Okay, we can leave it active for now.')
|
|
return
|
|
else:
|
|
away_team = await get_one_team(this_game['away_team_id'])
|
|
home_team = await get_one_team(this_game['home_team_id'])
|
|
|
|
try:
|
|
async with ctx.typing():
|
|
results = await self.get_game_results(this_game)
|
|
sheet_id = await self.get_game_sheet(home_team, away_team, results, ctx.author.display_name)
|
|
except pygsheets.InvalidArgumentValue as e:
|
|
prompt = 'There was an error creating the scorecard. Should I just nuke this game?'
|
|
this_q.prompt = prompt
|
|
resp = await this_q.ask([ctx.author])
|
|
|
|
if not resp:
|
|
await ctx.send('Okay, we can leave it active for now.')
|
|
return
|
|
else:
|
|
end_proper = False
|
|
|
|
if end_proper:
|
|
await ctx.send(f'Here is the results sheet for this game: https://docs.google.com/spreadsheets/d/'
|
|
f'{sheet_id}')
|
|
else:
|
|
await ctx.send(random_conf_gif())
|
|
patch_game(game_id=this_game['id'], active=False)
|
|
|
|
@commands.command(name='setlineup', aliases=['sub', 'sl'], help='!sl <formatted_subs>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def set_lineup_command(self, ctx, team_abbrev, *, lineup):
|
|
ERROR_MESSAGE = 'The format for a lineup is: <batting order # (10 for pitcher)>-<Player Name>-<position>\n' \
|
|
'Examples:\n2-Trea Turner-SS\n10-Rich Hill-P\n\n' \
|
|
'You may set multiple spots by adding a comma at the end of the position and, without ' \
|
|
'spaces, entering the next spot.\n' \
|
|
'Examples:\n1-Ronald Acuna Jr-RF,2-Trea Turner-SS,3-Robinson Cano-2B\n' \
|
|
'7-Travis Jankowski-LF,10-Anthony Bass-P,5-Erik Gonzalez-3B'
|
|
|
|
# current = await get_current()
|
|
this_team = await get_one_team(team_abbrev)
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
logging.info(f'this_game: {this_game}')
|
|
if this_team['id'] not in [this_game['away_team_id'], this_game['home_team_id']]:
|
|
away_team = await get_one_team(this_game['away_team_id'])
|
|
home_team = await get_one_team(this_game['home_team_id'])
|
|
await ctx.send(f'This channel is for {away_team["abbrev"]} @ {home_team["abbrev"]} - I can\'t set a lineup '
|
|
f'for **{team_abbrev.upper()}**.')
|
|
return
|
|
|
|
async with ctx.typing():
|
|
for member in re.split(',', lineup):
|
|
data = re.split('-', member)
|
|
this_player = await get_one_player(data[1])
|
|
|
|
# Check if player is already in game
|
|
try:
|
|
this_member = get_one_member(game_id=this_game['id'], player_id=this_player['id'], active=1)
|
|
except:
|
|
this_member = None
|
|
|
|
# If already in game, just change position
|
|
if this_member:
|
|
# Stop sub if changing batting order - unless pitcher is entering the lineup
|
|
if this_member['batting_order'] != int(data[0]) and this_member['batting_order'] != 10:
|
|
await ctx.send(f'{this_player["name"]} is already in the game batting '
|
|
f'#{this_member["batting_order"]} so they cannot move in the order.')
|
|
return
|
|
|
|
new_member = patch_lineup_member(
|
|
member_id=this_member['id'],
|
|
position=data[2].upper(),
|
|
batting_order=data[0]
|
|
)
|
|
|
|
# Else add new lineup member
|
|
else:
|
|
new_member = post_lineup_member({
|
|
'game_id': this_game['id'],
|
|
'team_id': this_team['id'],
|
|
'player_id': this_player['id'],
|
|
'position': data[2].upper(),
|
|
'batting_order': data[0]
|
|
})
|
|
|
|
members = get_lineup_members(this_game['id'], this_team['id'], batting_order=data[0], active=1)
|
|
if len(members['members']) > 1:
|
|
for x in range(len(members['members'])):
|
|
if members['members'][x]['id'] != new_member['id']:
|
|
old_member = patch_lineup_member(members['members'][x]['id'], active=False)
|
|
|
|
this_state = get_one_game_state(game_id=this_game['id'])
|
|
logging.info(f'old_member_id: {old_member["id"]} / on_first: {this_state["on_first"]} / '
|
|
f'on_second: {this_state["on_second"]} / on_third: {this_state["on_third"]}')
|
|
if old_member in [this_state['on_first'], this_state['on_second'], this_state['on_third']]:
|
|
if old_member == this_state['on_first']:
|
|
this_state = patch_game_state(
|
|
this_state['id'],
|
|
on_first_id=new_member['id']
|
|
)
|
|
elif old_member == this_state['on_second']:
|
|
this_state = patch_game_state(
|
|
this_state['id'],
|
|
on_second_id=new_member['id']
|
|
)
|
|
elif old_member == this_state['on_third']:
|
|
this_state = patch_game_state(
|
|
this_state['id'],
|
|
on_third_id=new_member['id']
|
|
)
|
|
|
|
sub_ab = post_atbat({
|
|
'game_id': this_game['id'],
|
|
'batter_id': new_member['player_id'],
|
|
'pitcher_id': self.get_pbc(this_game)['pitcher']['player_id'],
|
|
'on_base_code': 0,
|
|
'inning': this_state['inning'],
|
|
'pa': 0
|
|
})
|
|
|
|
# If player appears elsewhere in lineup, deactive old members
|
|
dupes = get_lineup_members(
|
|
this_game['id'], team_id=this_team['id'], player_id=this_player['id'], active=1
|
|
)
|
|
if len(dupes['members']) > 1:
|
|
for x in range(len(dupes['members']) - 1):
|
|
patch_lineup_member(dupes['members'][x]['id'], active=False)
|
|
|
|
home_check = self.lineup_check(this_game, True)
|
|
away_check = self.lineup_check(this_game, False)
|
|
|
|
check_string = f'Away lineup valid: {away_check["valid"]}\n'
|
|
if len(away_check['errors']) > 0:
|
|
error_string = "\n- ".join(away_check["errors"])
|
|
check_string += f'- {error_string}\n\n'
|
|
|
|
check_string += f'Home lineup valid: {home_check["valid"]}\n'
|
|
if len(home_check['errors']) > 0:
|
|
error_string = "\n- ".join(home_check["errors"])
|
|
check_string += f'- {error_string}\n\n'
|
|
|
|
if away_check['valid'] and home_check['valid']:
|
|
logging.info('Both lineups valid - sending gamestate')
|
|
content = None
|
|
embed = await self.game_state_embed(this_game, full_length=False)
|
|
else:
|
|
logging.info('Both lineups are not valid - sending check_string')
|
|
content = check_string
|
|
embed = None
|
|
|
|
await ctx.send(content=content, embed=embed)
|
|
|
|
@commands.command(name='gamestate', aliases=['gs'], help='Show game state')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def game_state_command(self, ctx):
|
|
# current = await get_current()
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game))
|
|
|
|
@commands.group(name='log', help='`!help log` for all commands')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_play(self, ctx):
|
|
if ctx.invoked_subcommand is None:
|
|
await ctx.send('No play details listed. Run `!help log` for available commands.')
|
|
|
|
@log_play.command(
|
|
name='single',
|
|
aliases=['si', 'si*', 'si**', 'siwh', '1b', '1b*', 'bp1b', 'bpsi', '1b**', '1bwh', 'single*', 'single**',
|
|
'singlewh'],
|
|
help='si, 1b, bp1b, bpsi, single, *, **, wh'
|
|
)
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_single_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
cmd = ctx.message.content.lower()
|
|
|
|
# Get baserunner advancement
|
|
if 'wh' in cmd or '**' in cmd:
|
|
bases = 2
|
|
else:
|
|
bases = 1
|
|
|
|
data = self.log_play_core(this_game, bases=bases, hit=1)
|
|
patch_game_state(
|
|
this_game['id'],
|
|
on_first_id=data['batter']['id']
|
|
)
|
|
|
|
if 'bp' in cmd:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
bp1b=1
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(
|
|
name='double',
|
|
aliases=['do', 'do**', 'do***', 'dowh', '2b', '2b**', '2b***', '2bwh', 'double**', 'double***', 'doublewh'],
|
|
help='do, 2b, double, **, ***, wh'
|
|
)
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_double_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
# Get baserunner advancement
|
|
if 'wh' in ctx.message.content.lower() or '***' in ctx.message.content:
|
|
bases = 3
|
|
else:
|
|
bases = 2
|
|
|
|
data = self.log_play_core(this_game, bases=bases, hit=1)
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
double=1,
|
|
)
|
|
patch_game_state(
|
|
this_game['id'],
|
|
on_second_id=data['batter']['id']
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='triple', aliases=['tr', '3b'], help='tr, 3b')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_triple_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
# Get baserunner advancement
|
|
bases = 3
|
|
|
|
data = self.log_play_core(this_game, bases=bases, hit=1)
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
triple=1,
|
|
)
|
|
patch_game_state(
|
|
this_game['id'],
|
|
on_third_id=data['batter']['id']
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='homerun', aliases=['hr', 'bphr'], help='hr, bphr')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_homerun_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
# Get baserunner advancement
|
|
ballpark = 1 if 'bphr' in ctx.message.content.lower() else 0
|
|
data = self.log_play_core(this_game, bases=4, hit=1)
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
run=1,
|
|
rbi=data['ab']['rbi'],
|
|
homerun=1,
|
|
bphr=ballpark
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='sacrifice', aliases=['sacf', 'sacb'], help='sacf, sacb')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_sac_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
rbi = 0
|
|
sac = 0
|
|
bases = 0
|
|
ab = 0
|
|
if gs['outs'] < 2:
|
|
cmd = ctx.message.content.lower()
|
|
sac = 1
|
|
ab = 0
|
|
if 'sacf' in cmd:
|
|
rbi = self.adv_specific_runner(this_game, 3, 1)
|
|
elif 'sacb' in cmd:
|
|
bases = 1
|
|
|
|
data = self.log_play_core(this_game, bases=bases, outs=1, ab=ab)
|
|
patch_atbat(
|
|
atbat_id=data['ab']['id'],
|
|
rbi=rbi,
|
|
sac=sac
|
|
)
|
|
if rbi:
|
|
self.increment_score(this_game, rbi)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='walk', aliases=['bb', 'hbp', 'ibb'], help='bb, hbp, ibb')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_walk_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
data = self.log_play_core(this_game, bases=1, ab=0, force_only=True)
|
|
patch_game_state(
|
|
this_game['id'],
|
|
on_first_id=data['batter']['id']
|
|
)
|
|
|
|
cmd = ctx.message.content.lower()
|
|
if 'ibb' in cmd:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
ibb=1,
|
|
)
|
|
elif 'walk' in cmd or 'bb' in cmd:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
bb=1,
|
|
)
|
|
elif 'hbp' in cmd:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
hbp=1,
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='sb_attempt', aliases=['sb', 'cs'], help='sb, cs')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_sb_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
if not gs['on_first'] and not gs['on_second'] and not gs['on_third']:
|
|
await ctx.send('I don\'t see any baserunners aboard - maybe this isn\'t a stolen base.')
|
|
return
|
|
|
|
runner_found = None
|
|
this_runner = None
|
|
count = 4
|
|
for runner in [gs['on_third'], gs['on_second'], gs['on_first']]:
|
|
count -= 1
|
|
if runner:
|
|
a_runner = await get_one_player(runner['player_id'])
|
|
prompt = f'Did {a_runner["name"]} attempt the steal?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
resp = await this_q.ask([ctx.author])
|
|
if resp:
|
|
runner_found = count
|
|
this_runner = runner
|
|
break
|
|
|
|
if not runner_found:
|
|
await ctx.send('If nobody was forced out - it must not have been a fielder\'s choice.')
|
|
return
|
|
|
|
cmd = ctx.message.content.upper()
|
|
stolen_base = 1 if 'SB' in cmd else 0
|
|
battery = self.get_pbc(this_game)
|
|
|
|
post_running({
|
|
'game_id': this_game['id'],
|
|
'runner_id': this_runner['player_id'],
|
|
'stolen_base': stolen_base,
|
|
'caught_stealing': not stolen_base
|
|
})
|
|
|
|
post_defense({
|
|
'game_id': this_game['id'],
|
|
'player_id': battery['catcher']['player_id'],
|
|
'position': 'C',
|
|
'stolen_base_attempt': 1,
|
|
'caught_stealing': not stolen_base,
|
|
})
|
|
|
|
if stolen_base:
|
|
if runner_found == 1:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_second_id=runner['id'],
|
|
on_first_id=False
|
|
)
|
|
elif runner_found == 2:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_third_id=runner['id'],
|
|
on_second_id=False
|
|
)
|
|
else:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_third_id=False
|
|
)
|
|
self.increment_score(this_game, 1)
|
|
else:
|
|
if runner_found == 1:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_first_id=False
|
|
)
|
|
elif runner_found == 2:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_second_id=False
|
|
)
|
|
else:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_third_id=False
|
|
)
|
|
self.increment_outs(this_game, 1)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
# @log_play.command(name='extra_base', aliases=['xba', 'xbt'], help='xba, xbt')
|
|
# @commands.has_any_role(SBA_PLAYERS_ROLE_NAME)
|
|
# async def log_xba_command(self, ctx):
|
|
# this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
# gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
#
|
|
# if not gs['on_first'] and not gs['on_second'] and not gs['on_third']:
|
|
# await ctx.send('I don\'t see any baserunners aboard - maybe there wasn\'t an advance.')
|
|
# return
|
|
#
|
|
# runner_found = None
|
|
# runner_player = None
|
|
# this_runner = None
|
|
# base_taken = True
|
|
# count = 4
|
|
# for runner in [gs['on_third'], gs['on_second'], gs['on_first']]:
|
|
# count -= 1
|
|
# if runner:
|
|
# runner_player = await get_one_player(runner['player_id'])
|
|
# prompt = f'Did {runner_player["name"]} attempt the advance?'
|
|
# this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
# resp = await this_q.ask([ctx.author])
|
|
# if resp:
|
|
# runner_found = count
|
|
# this_runner = runner
|
|
# break
|
|
#
|
|
# if not runner_found:
|
|
# await ctx.send('If nobody made an attempt - it must not have been an xba.')
|
|
# return
|
|
#
|
|
# prompt = 'Was there a throw from an outfielder? (Was a d20 rolled?)'
|
|
# this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
# defender_found = None
|
|
# this_defender = None
|
|
# resp = await this_q.ask([ctx.author])
|
|
# if resp:
|
|
# count = -1
|
|
# for defender in [self.get_defender(this_game, 'CF'), self.get_defender(this_game, 'RF'),
|
|
# self.get_defender(this_game, 'LF')]:
|
|
# count += 1
|
|
# if defender:
|
|
# a_defender = await get_one_player(defender['player_id'])
|
|
# prompt = f'Did {a_defender["name"]} make the throw?'
|
|
# this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
# resp = await this_q.ask([ctx.author])
|
|
# if resp:
|
|
# defender_found = ['CF', 'RF', 'LF'][count]
|
|
# this_defender = defender
|
|
# break
|
|
#
|
|
# if not defender_found:
|
|
# await ctx.send('So I guess there wasn\'t an attempt.')
|
|
# return
|
|
#
|
|
# this_q.prompt = f'Did {runner_player["name"]} reach safely?'
|
|
# resp = await this_q.ask([ctx.author])
|
|
# if resp is not None:
|
|
# base_taken = resp
|
|
#
|
|
# logging.info(f'defender: {defender}')
|
|
# post_defense({
|
|
# 'game_id': this_game['id'],
|
|
# 'player_id': this_defender['player_id'],
|
|
# 'position': this_defender['position'],
|
|
# 'runner_adv_att': 1,
|
|
# 'runner_throw_out': not base_taken
|
|
# })
|
|
#
|
|
# post_running({
|
|
# 'game_id': this_game['id'],
|
|
# 'runner_id': this_runner['player_id'],
|
|
# 'extra_base_attempt': 1,
|
|
# 'extra_base_taken': base_taken
|
|
# })
|
|
#
|
|
# if base_taken:
|
|
# if runner_found == 1:
|
|
# self.adv_specific_runner(this_game, 1, 1)
|
|
# elif runner_found == 2:
|
|
# self.adv_specific_runner(this_game, 2, 1)
|
|
# else:
|
|
# self.adv_specific_runner(this_game, 3, 1)
|
|
# self.increment_score(this_game, 1)
|
|
# await ctx.send('**Take a note to log this RBI - it\'s one of the things '
|
|
# 'I can\'t do automatically, yet.**')
|
|
# else:
|
|
# if runner_found == 1:
|
|
# patch_game_state(
|
|
# state_id=gs['id'],
|
|
# on_first_id=False
|
|
# )
|
|
# elif runner_found == 2:
|
|
# patch_game_state(
|
|
# state_id=gs['id'],
|
|
# on_second_id=False
|
|
# )
|
|
# else:
|
|
# patch_game_state(
|
|
# state_id=gs['id'],
|
|
# on_third_id=False
|
|
# )
|
|
# self.increment_outs(this_game, 1)
|
|
#
|
|
# await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
# @log_play.command(name='robs', aliases=['roba'], help='RobA, RobS')
|
|
# @commands.has_any_role(SBA_PLAYERS_ROLE_NAME)
|
|
# async def log_rob_command(self, ctx):
|
|
# this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
#
|
|
# defender_found = None
|
|
# this_defender = None
|
|
# count = 0
|
|
# for defender in [self.get_defender(this_game, 'CF'), self.get_defender(this_game, 'LF'),
|
|
# self.get_defender(this_game, 'RF')]:
|
|
# count += 1
|
|
# if defender:
|
|
# a_defender = await get_one_player(defender['player_id'])
|
|
# prompt = f'Did {a_defender["name"]} attempt the rob?'
|
|
# this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
# resp = await this_q.ask([ctx.author])
|
|
# if resp:
|
|
# defender_found = True
|
|
# this_defender = defender
|
|
# break
|
|
#
|
|
# if not defender_found:
|
|
# await ctx.send('So I guess there wasn\'t an attempt.')
|
|
# return
|
|
#
|
|
# prompt = f'Did {a_defender["name"]} successfully rob it?'
|
|
# this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
# resp = await this_q.ask([ctx.author])
|
|
# if resp is not None:
|
|
# base_taken = resp
|
|
#
|
|
# post_defense({
|
|
# 'game_id': this_game['id'],
|
|
# 'player_id': this_defender['player_id'],
|
|
# 'position': this_defender['position'],
|
|
# 'rob_attempt': 1,
|
|
# 'rob_success': resp
|
|
# })
|
|
#
|
|
# await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(
|
|
name='out',
|
|
aliases=['flyout', 'groundout', 'popout', 'lineout', 'strikeout', 'fo', 'bpfo', 'go', 'po', 'lo', 'bplo',
|
|
'so', 'k'],
|
|
help='fo, bpfo, go, po, lo, bplo, so, k'
|
|
)
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME)
|
|
async def log_out_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
cmd = ctx.message.content.lower()
|
|
if 'bpfo' in cmd:
|
|
data = self.log_play_core(this_game, outs=1, ab=1)
|
|
sac_rbi = self.adv_specific_runner(this_game, 3, 1)
|
|
patch_atbat(
|
|
atbat_id=data['ab']['id'],
|
|
ab=1 - sac_rbi,
|
|
bpfo=1,
|
|
rbi=sac_rbi,
|
|
sac=sac_rbi
|
|
)
|
|
else:
|
|
data = self.log_play_core(this_game, outs=1)
|
|
if 'so' in cmd or 'k' in cmd or 'strikeout' in cmd:
|
|
patch_atbat(
|
|
atbat_id=data['ab']['id'],
|
|
so=1
|
|
)
|
|
elif 'bplo' in cmd:
|
|
patch_atbat(
|
|
atbat_id=data['ab']['id'],
|
|
bplo=1
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='xcheck', aliases=['x'], help='x <pos>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_xcheck_command(self, ctx, position):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
if position.upper() not in ['C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF', 'P']:
|
|
await ctx.send(f'Ope, **{position.upper()}** is not a valid position.')
|
|
return
|
|
|
|
defender = self.get_defender(this_game, position)
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
prompt = f'Please enter the hit/out result (\'1B(\\*\\*)\', \'2B(\\*\\*\\*)\', \'3B\', or \'out\')'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'text', 15)
|
|
resp = await this_q.ask([ctx.author])
|
|
if not resp:
|
|
await ctx.send('You think on it and get back to me.')
|
|
return
|
|
elif resp.upper() not in ['1B', '1BWH', '1B**', '2B', '2BWH', '2B***', '3B', 'OUT']:
|
|
await ctx.send('I can only take 1B, 2B, 3B, or OUT')
|
|
return
|
|
else:
|
|
if resp.upper() == 'OUT':
|
|
hit = 0
|
|
else:
|
|
hit_bases = int(resp[0])
|
|
if len(resp) > 2:
|
|
bases = int(resp[0]) + 1
|
|
else:
|
|
bases = hit_bases
|
|
hit = 1
|
|
data = self.log_play_core(this_game, bases=bases, hit=1)
|
|
if hit_bases == 1:
|
|
patch_game_state(
|
|
state_id=this_state['id'],
|
|
on_first_id=data['batter']['id']
|
|
)
|
|
elif hit_bases == 2:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
double=1,
|
|
)
|
|
patch_game_state(
|
|
state_id=this_state['id'],
|
|
on_second_id=data['batter']['id']
|
|
)
|
|
elif hit_bases == 3:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
triple=1,
|
|
)
|
|
patch_game_state(
|
|
state_id=this_state['id'],
|
|
on_third_id=data['batter']['id']
|
|
)
|
|
|
|
prompt = f'For an error, please enter the number of bases. Otherwise, enter 0.'
|
|
this_q.prompt = prompt
|
|
this_q.qtype = 'int'
|
|
resp = await this_q.ask([ctx.author])
|
|
if resp is None:
|
|
await ctx.send('You think on it and get back to me.')
|
|
return
|
|
elif 0 > resp > 3:
|
|
await ctx.send('I can only take 0 -> 3')
|
|
return
|
|
else:
|
|
if resp > 0:
|
|
error = 1
|
|
if hit == 0:
|
|
data = self.log_play_core(this_game, bases=resp, error=1)
|
|
logging.info(f'error: {resp}')
|
|
if resp == 1:
|
|
patch_game_state(
|
|
state_id=this_state['id'],
|
|
on_first_id=data['batter']['id']
|
|
)
|
|
elif resp == 2:
|
|
patch_game_state(
|
|
state_id=this_state['id'],
|
|
on_second_id=data['batter']['id']
|
|
)
|
|
elif resp == 3:
|
|
patch_game_state(
|
|
state_id=this_state['id'],
|
|
on_third_id=data['batter']['id']
|
|
)
|
|
else:
|
|
self.advance_runners(this_state=this_state, num_bases=resp, error=1)
|
|
else:
|
|
error = 0
|
|
|
|
post_defense({
|
|
'game_id': this_game['id'],
|
|
'player_id': defender['player_id'],
|
|
'x_check': 1,
|
|
'position': defender['position'],
|
|
'hit': hit,
|
|
'error': error
|
|
})
|
|
if hit == 0 and error == 0:
|
|
await ctx.send('**The defense check has been logged - please log the specific out now. (e.g. !log '
|
|
'fo, !log gora, !log sacf)**')
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='doubleplay', aliases=['gidp', 'gba'], help='gidp, gbA')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_gidp_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
if not gs['on_first'] and not gs['on_second'] and not gs['on_third']:
|
|
await ctx.send('I don\'t see any baserunners aboard - maybe this should just be a groundout.')
|
|
return
|
|
|
|
runner_found = None
|
|
count = 0
|
|
for runner in [gs['on_first'], gs['on_second'], gs['on_third']]:
|
|
count += 1
|
|
if runner:
|
|
this_runner = await get_one_player(runner['player_id'])
|
|
prompt = f'Was {this_runner["name"]} doubled off?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
resp = await this_q.ask([ctx.author])
|
|
if resp:
|
|
runner_found = count
|
|
break
|
|
|
|
if not runner_found:
|
|
await ctx.send('If nobody was double off - it must not have been a GIDP.')
|
|
return
|
|
|
|
# If both outs of gidp are recorded, remove baserunner and record gidp
|
|
if gs['outs'] < 2:
|
|
gidp = 1
|
|
if runner_found == 1:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_first_id=False
|
|
)
|
|
elif runner_found == 2:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_second_id=False
|
|
)
|
|
else:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_third_id=False
|
|
)
|
|
else:
|
|
gidp = 0
|
|
|
|
data = self.log_play_core(this_game, outs=2)
|
|
patch_atbat(
|
|
atbat_id=data['ab']['id'],
|
|
gidp=gidp
|
|
)
|
|
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
rbi = 0
|
|
count = 1
|
|
for runner in [gs['on_second'], gs['on_third']]:
|
|
count += 1
|
|
if runner:
|
|
this_runner = await get_one_player(runner['player_id'])
|
|
prompt = f'Did {this_runner["name"]} advance?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
resp = await this_q.ask([ctx.author])
|
|
if resp:
|
|
rbi += self.adv_specific_runner(this_game, count, 1)
|
|
|
|
if rbi:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
rbi=rbi,
|
|
)
|
|
patch_game_state(
|
|
gs['id'],
|
|
home_score=gs['home_score'] + rbi
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='gora', aliases=['gbc'], help='gora, gbC')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_gora_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
data = self.log_play_core(this_game, outs=1, bases=1)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='fc', aliases=['gbb'], help='fc, gbB')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_fc_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
if not gs['on_first'] and not gs['on_second'] and not gs['on_third']:
|
|
await ctx.send('I don\'t see any baserunners aboard - maybe this should just be a groundout.')
|
|
return
|
|
|
|
runner_found = None
|
|
count = 0
|
|
for runner in [gs['on_first'], gs['on_second'], gs['on_third']]:
|
|
count += 1
|
|
if runner:
|
|
this_runner = await get_one_player(runner['player_id'])
|
|
prompt = f'Was {this_runner["name"]} the forced out runner?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
resp = await this_q.ask([ctx.author])
|
|
if resp:
|
|
runner_found = count
|
|
break
|
|
|
|
if not runner_found:
|
|
await ctx.send('If nobody was forced out - it must not have been a fielder\'s choice.')
|
|
return
|
|
|
|
# If fewer than 2 outs, remove forced out runner first
|
|
if gs['outs'] < 2:
|
|
if runner_found == 1:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_first_id=False
|
|
)
|
|
elif runner_found == 2:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_second_id=False
|
|
)
|
|
else:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_third_id=False
|
|
)
|
|
|
|
data = self.log_play_core(this_game, outs=1)
|
|
patch_game_state(
|
|
gs['id'],
|
|
on_first_id=data['batter']['id']
|
|
)
|
|
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
rbi = 0
|
|
count = 1
|
|
for runner in [gs['on_second'], gs['on_third']]:
|
|
count += 1
|
|
if runner:
|
|
this_runner = await get_one_player(runner['player_id'])
|
|
prompt = f'Did {this_runner["name"]} advance?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
resp = await this_q.ask([ctx.author])
|
|
if resp:
|
|
rbi += self.adv_specific_runner(this_game, count, 1)
|
|
|
|
if rbi:
|
|
patch_atbat(
|
|
data['ab']['id'],
|
|
rbi=rbi,
|
|
)
|
|
self.increment_score(this_game, rbi)
|
|
# patch_game_state(
|
|
# gs['id'],
|
|
# home_score=gs['home_score'] + rbi
|
|
# )
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@log_play.command(name='chaos', aliases=['wp', 'pb', 'pick', 'balk'], help='wp, pb, pick, balk')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def log_chaos_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
cmd = ctx.message.content.lower()
|
|
pick = 0
|
|
wp = 0
|
|
pb = 0
|
|
balk = 0
|
|
if 'pick' in cmd:
|
|
if not gs['on_first'] and not gs['on_second'] and not gs['on_third']:
|
|
await ctx.send('I don\'t see any baserunners aboard.')
|
|
return
|
|
|
|
runner_found = None
|
|
count = 0
|
|
for runner in [gs['on_first'], gs['on_second'], gs['on_third']]:
|
|
count += 1
|
|
if runner:
|
|
this_runner = await get_one_player(runner['player_id'])
|
|
prompt = f'Was {this_runner["name"]} picked off?'
|
|
this_q = Question(self.bot, ctx.channel, prompt, 'yesno', 10)
|
|
resp = await this_q.ask([ctx.author])
|
|
if resp:
|
|
runner_found = count
|
|
break
|
|
|
|
if not runner_found:
|
|
await ctx.send('If nobody was picked off - maybe try another play.')
|
|
return
|
|
elif runner_found == 1:
|
|
patch_game_state(
|
|
gs['id'],
|
|
on_first_id=False
|
|
)
|
|
elif runner_found == 2:
|
|
patch_game_state(
|
|
gs['id'],
|
|
on_second_id=False
|
|
)
|
|
elif runner_found == 3:
|
|
patch_game_state(
|
|
gs['id'],
|
|
on_third_id=False
|
|
)
|
|
|
|
pick = 1
|
|
self.increment_outs(this_game, 1)
|
|
else:
|
|
if 'wp' in cmd:
|
|
wp = 1
|
|
elif 'balk' in cmd:
|
|
balk = 1
|
|
else:
|
|
pb = 1
|
|
runs = self.advance_runners(gs, num_bases=1, error=True)
|
|
if runs:
|
|
self.increment_score(this_game, runs)
|
|
|
|
data = self.get_pbc(this_game)
|
|
|
|
post_chaos({
|
|
'game_id': this_game['id'],
|
|
'pitcher_id': data['pitcher']['player_id'],
|
|
'catcher_id': data['catcher']['player_id'],
|
|
'wild_pitch': wp,
|
|
'passed_ball': pb,
|
|
'pick_off': pick,
|
|
'balk': balk
|
|
})
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@commands.command(name='undo', help='Roll back last entered play', hidden=True)
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def roll_back_command(self, ctx):
|
|
await ctx.send('This command will do something soon, I promise.')
|
|
|
|
@commands.command(name='advance', aliases=['adv'], help='!adv <from_base> <num_bases>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def advance_runners_command(self, ctx, from_base: int, num_bases: int):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
if 0 > from_base > 3:
|
|
await ctx.send('from_base parameter must be 1, 2, or 3')
|
|
return
|
|
if 0 > num_bases > 3:
|
|
await ctx.send('num_bases parameter must be 1, 2, or 3')
|
|
return
|
|
|
|
self.adv_specific_runner(this_game, from_base, num_bases)
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@commands.command(name='place', help='!place <base_num> <player_to_place>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def place_runner_command(self, ctx, base: int, *, player_name):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
if 0 > base > 3:
|
|
await ctx.send('from_base parameter must be 1, 2, or 3')
|
|
return
|
|
|
|
player_cog = self.bot.get_cog('Players')
|
|
player_name = await fuzzy_player_search(ctx, ctx.channel, self.bot, player_name, player_cog.player_list.keys())
|
|
player = await get_one_player(player_name)
|
|
this_member = get_one_member(
|
|
game_id=this_game['id'],
|
|
player_id=player['id'],
|
|
active=1
|
|
)
|
|
|
|
if base == 1:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_first_id=this_member['id']
|
|
)
|
|
elif base == 2:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_second_id=this_member['id']
|
|
)
|
|
else:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_third_id=this_member['id']
|
|
)
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@commands.command(name='clear', help='!clear <base_to_clear>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def clear_runner_command(self, ctx, base: int):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
if 0 > base > 3:
|
|
await ctx.send('from_base parameter must be 1, 2, or 3')
|
|
return
|
|
|
|
if base == 1:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_first_id=False
|
|
)
|
|
elif base == 2:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_second_id=False
|
|
)
|
|
else:
|
|
patch_game_state(
|
|
state_id=gs['id'],
|
|
on_third_id=False
|
|
)
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@commands.command(name='outs', help='!outs <modifier>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def outs_command(self, ctx, modifier: int):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
if -2 <= modifier <= 3:
|
|
outs = modifier
|
|
else:
|
|
await ctx.send('I can only change the outs from -2 to +3')
|
|
return
|
|
if outs == 0:
|
|
await ctx.message.add_reaction('👍')
|
|
return
|
|
|
|
if outs > 0:
|
|
self.increment_outs(this_game, outs)
|
|
else:
|
|
if this_state['outs'] + outs < 0:
|
|
new_outs = 0
|
|
else:
|
|
new_outs = this_state['outs'] + outs
|
|
patch_game_state(
|
|
this_state['id'],
|
|
outs=0
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@commands.command(name='score', help='!score <home_or_away> <modifier>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def score_command(self, ctx, home_or_away: str, modifier: int):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
this_state = get_game_state(game_id=this_game['id'])['states'][0]
|
|
home_score = this_state['home_score']
|
|
away_score = this_state['away_score']
|
|
|
|
if home_or_away.lower() == 'home':
|
|
new_home_score = home_score + modifier
|
|
new_away_score = away_score
|
|
elif home_or_away.lower() == 'away':
|
|
new_away_score = away_score + modifier
|
|
new_home_score = home_score
|
|
else:
|
|
await ctx.send('I only take \'home\' or \'away\'. Try again.')
|
|
return
|
|
|
|
new_state = patch_game_state(
|
|
this_state['id'],
|
|
home_score=new_home_score,
|
|
away_score=new_away_score
|
|
)
|
|
|
|
await ctx.send(content=None, embed=await self.game_state_embed(this_game, full_length=False))
|
|
|
|
@commands.group(name='show', help='`!help show` for all options')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def show_data(self, ctx):
|
|
if ctx.invoked_subcommand is None:
|
|
await ctx.send('No play details listed. Run `!help show` for available commands.')
|
|
|
|
@show_data.command(name='pitcher', help='Show pitcher card')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def show_pitcher_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
logging.info(f'gs: {gs}')
|
|
|
|
if gs['top_half']:
|
|
this_member = get_one_member(
|
|
game_id=this_game['id'],
|
|
team_id=this_game['home_team_id'],
|
|
batting_order=10,
|
|
active=1
|
|
)
|
|
else:
|
|
this_member = get_one_member(
|
|
game_id=this_game['id'],
|
|
team_id=this_game['away_team_id'],
|
|
batting_order=10,
|
|
active=1
|
|
)
|
|
|
|
this_player = await get_one_player(this_member['player_id'])
|
|
await ctx.send(content=None, embed=await get_player_embed(this_player, await get_current()))
|
|
|
|
@show_data.command(name='batter', help='Show batter card')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def show_batter_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
logging.info(f'gs: {gs}')
|
|
|
|
if gs['top_half']:
|
|
this_member = get_one_member(
|
|
game_id=this_game['id'],
|
|
team_id=this_game['away_team_id'],
|
|
batting_order=gs['away_batter_up'],
|
|
active=1
|
|
)
|
|
else:
|
|
this_member = get_one_member(
|
|
game_id=this_game['id'],
|
|
team_id=this_game['home_team_id'],
|
|
batting_order=gs['home_batter_up'],
|
|
active=1
|
|
)
|
|
|
|
this_player = await get_one_player(this_member['player_id'])
|
|
await ctx.send(content=None, embed=await get_player_embed(this_player, await get_current()))
|
|
|
|
@show_data.command(
|
|
name='position',
|
|
aliases=['c', '1b', '2b', '3b', 'ss', 'lf', 'cf', 'rf'],
|
|
help='!show 1b, !show rf, etc'
|
|
)
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def show_defense_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
pos = ctx.message.content.split(' ')[1].upper()
|
|
logging.info(f'pos: {pos}')
|
|
|
|
this_player = await get_one_player(self.get_defender(this_game, pos)['player_id'])
|
|
await ctx.send(content=None, embed=await get_player_embed(this_player, await get_current()))
|
|
|
|
@show_data.command(name='runner', help='!show runner <base_num>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def show_runner_command(self, ctx, base_num: int):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
gs = get_game_state(game_id=this_game['id'])['states'][0]
|
|
|
|
if base_num == 1:
|
|
runner_id = gs['on_first']['player_id']
|
|
elif base_num == 2:
|
|
runner_id = gs['on_second']['player_id']
|
|
elif base_num == 3:
|
|
runner_id = gs['on_third']['player_id']
|
|
else:
|
|
await ctx.send('I can only check for runners on base 1, 2, or 3. Try again maybe?')
|
|
return
|
|
|
|
this_player = await get_one_player(runner_id)
|
|
await ctx.send(content=None, embed=await get_player_embed(this_player, await get_current()))
|
|
|
|
@commands.command(name='stats', aliases=['tocsv'], help='Get current stats')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def export_csv_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
|
|
async with ctx.typing():
|
|
results = await self.get_game_results(this_game, combined=True)
|
|
|
|
csv = DataFrame(results['batters']).to_csv(header=False, index=False)
|
|
file_name = f'storage/csv/{ctx.author.display_name}{random.randint(1000000000,9999999999)}.csv'
|
|
file = open(file_name, 'w')
|
|
file.write(csv)
|
|
file.close()
|
|
|
|
await ctx.send(content='Here is your batter csv:', file=discord.File(file_name))
|
|
|
|
csv = DataFrame(results['pitchers']).to_csv(header=False, index=False)
|
|
file_name = f'storage/csv/{ctx.author.display_name}{random.randint(1000000000,9999999999)}.csv'
|
|
file = open(file_name, 'w')
|
|
file.write(csv)
|
|
file.close()
|
|
|
|
await ctx.send(content='Here is your pitcher csv:', file=discord.File(file_name))
|
|
|
|
@commands.command(name='tosheets', help='Get results Sheet')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def export_sheets_command(self, ctx):
|
|
this_game = get_game(channel_id=ctx.channel.id, active=1)
|
|
away_team = await get_one_team(this_game['away_team_id'])
|
|
home_team = await get_one_team(this_game['home_team_id'])
|
|
|
|
results = await self.get_game_results(this_game)
|
|
await ctx.send('You got it - just pulled stats and posting them to a spreadsheet for ya.')
|
|
|
|
async with ctx.typing():
|
|
sheet_id = await self.get_game_sheet(home_team, away_team, results, ctx.author.display_name)
|
|
|
|
await ctx.send(f'Here is the results sheet for this game: https://docs.google.com/spreadsheets/d/'
|
|
f'{sheet_id}')
|
|
|
|
@commands.command(name='pull', hidden=True)
|
|
@commands.is_owner()
|
|
async def pull_command(self, ctx, player_type):
|
|
if player_type.lower() not in ['pitchers', 'batters']:
|
|
await ctx.send('Can only import pitcher or batter')
|
|
return
|
|
|
|
# Get data from Sheets
|
|
async with ctx.typing():
|
|
sheets = pygsheets.authorize(service_file='storage/major-domo-service-creds.json')
|
|
if player_type == 'pitchers':
|
|
data_sheet = sheets.open_by_key('1tAKj1PXTJbVyEXzB8YPcGOdydS24N12qoT6n7bSSk_w').worksheet_by_title(
|
|
'2020 Pitchers')
|
|
raw_data = data_sheet.get_values('A3', 'AP547')
|
|
else:
|
|
data_sheet = sheets.open_by_key('1tAKj1PXTJbVyEXzB8YPcGOdydS24N12qoT6n7bSSk_w').worksheet_by_title(
|
|
'2020 Batters')
|
|
raw_data = data_sheet.get_values('A3', 'AZ509')
|
|
|
|
await ctx.send('Alrighty, got the import data. Now to sift through...')
|
|
|
|
all_data = []
|
|
async with ctx.typing():
|
|
if player_type == 'pitchers':
|
|
for line in raw_data:
|
|
this_player = await get_one_player(line[0])
|
|
this_line = {
|
|
'player_id': this_player['id'],
|
|
'name': line[0],
|
|
'hand': line[2],
|
|
'innings': line[3],
|
|
'so_vl': line[4],
|
|
'so_vr': line[5],
|
|
'bb_vl': line[6],
|
|
'bb_vr': line[7],
|
|
'hit_vl': line[8],
|
|
'hit_vr': line[9],
|
|
'ob_vl': line[10],
|
|
'ob_vr': line[11],
|
|
'tb_vl': line[12],
|
|
'tb_vr': line[13],
|
|
'tb123_vl': line[16],
|
|
'tb123_vr': line[17],
|
|
'hit123_vl': line[14],
|
|
'hit123_vr': line[15],
|
|
'hr_vl': line[18],
|
|
'hr_vr': line[19],
|
|
'bphr_vl': line[22],
|
|
'bphr_vr': line[23],
|
|
'bp1b_vl': line[24],
|
|
'bp1b_vr': line[25],
|
|
'dp_vl': line[26],
|
|
'dp_vr': line[27],
|
|
'hold': line[29],
|
|
'st_rating': line[30] if line[30] != '' else None,
|
|
're_rating': line[31] if line[31] != '' else None,
|
|
'cl_rating': line[32] if line[32] != '' else None,
|
|
'range': line[33],
|
|
'error': line[34],
|
|
'balk': line[35],
|
|
'wild_pitch': line[36],
|
|
'speed': line[39],
|
|
'bunt': line[40],
|
|
'injury': line[41],
|
|
}
|
|
all_data.append(this_line)
|
|
|
|
posts = post_pitchers(all_data)
|
|
else:
|
|
for line in raw_data:
|
|
this_player = await get_one_player(line[0])
|
|
this_line = {
|
|
'player_id': this_player['id'],
|
|
'name': line[0],
|
|
'hand': line[3],
|
|
'atbats': line[2],
|
|
'so_vl': line[4],
|
|
'so_vr': line[5],
|
|
'bb_vl': line[6],
|
|
'bb_vr': line[7],
|
|
'hit_vl': line[8],
|
|
'hit_vr': line[9],
|
|
'ob_vl': line[10],
|
|
'ob_vr': line[11],
|
|
'tb_vl': line[12],
|
|
'tb_vr': line[13],
|
|
'hr_vl': line[14],
|
|
'hr_vr': line[15],
|
|
'bphr_vl': line[18],
|
|
'bphr_vr': line[19],
|
|
'bp1b_vl': line[20],
|
|
'bp1b_vr': line[21],
|
|
'dp_vl': line[24],
|
|
'dp_vr': line[25],
|
|
'steal': line[27],
|
|
'speed': line[28],
|
|
'bunt': line[29],
|
|
'hit_and_run': line[30],
|
|
'injury': line[31],
|
|
'rating_c': line[32] if line[32] != '' else None,
|
|
'rating_1b': line[33] if line[33] != '' else None,
|
|
'rating_2b': line[34] if line[34] != '' else None,
|
|
'rating_3b': line[35] if line[35] != '' else None,
|
|
'rating_ss': line[36] if line[36] != '' else None,
|
|
'rating_lf': line[37] if line[37] != '' else None,
|
|
'rating_cf': line[38] if line[38] != '' else None,
|
|
'rating_rf': line[39] if line[39] != '' else None,
|
|
'error_c': line[40] if line[40] != '' else None,
|
|
'error_1b': line[41] if line[41] != '' else None,
|
|
'error_2b': line[42] if line[42] != '' else None,
|
|
'error_3b': line[43] if line[43] != '' else None,
|
|
'error_ss': line[44] if line[44] != '' else None,
|
|
'error_lf': line[45] if line[45] != '' else None,
|
|
'error_cf': line[46] if line[46] != '' else None,
|
|
'error_rf': line[47] if line[47] != '' else None,
|
|
'arm_c': line[49] if line[49] != '' else None,
|
|
'arm_of': line[48] if line[48] != '' else None,
|
|
'overthrow': line[50] if line[50] != '' else None,
|
|
'passed_ball': line[51] if line[51] != '' else None,
|
|
}
|
|
all_data.append(this_line)
|
|
posts = post_batters(all_data)
|
|
|
|
await ctx.send(f'Posted {posts} {player_type.lower()}!')
|
|
|
|
@commands.command(name='vs', help='!vs <Pitcher Name> vs <Batter Name>')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def versus_command(self, ctx, *, args):
|
|
# Split names out
|
|
names = re.split(' vs ', args)
|
|
if len(names) == 1:
|
|
names = re.split(' vs. ', args)
|
|
if len(names) == 1:
|
|
await ctx.send('I could not find player names in that message. Make sure the format is '
|
|
'`!vs Gerrit Cole vs Salvador Perez')
|
|
return
|
|
|
|
# Perform fuzzy search to get player objects
|
|
player_cog = self.bot.get_cog('Players')
|
|
player_name = await fuzzy_player_search(ctx, ctx.channel, self.bot, names[0], player_cog.player_list.keys())
|
|
player1 = await get_one_player(player_name)
|
|
player_name = await fuzzy_player_search(ctx, ctx.channel, self.bot, names[1], player_cog.player_list.keys())
|
|
player2 = await get_one_player(player_name)
|
|
pitcher_player = None
|
|
batter_player = None
|
|
pitcher_pos = ['SP', 'RP', 'CP']
|
|
|
|
if player1['pos_1'] in pitcher_pos:
|
|
pitcher_player = player1
|
|
if player2['pos_1'] not in pitcher_pos:
|
|
batter_player = player2
|
|
else:
|
|
await ctx.send(f'I could not find batting info for {player2["name"]}.')
|
|
return
|
|
else:
|
|
pitcher_player = player2
|
|
if player1['pos_1'] not in pitcher_pos:
|
|
batter_player = player1
|
|
else:
|
|
await ctx.send(f'I could not find batting info for {player1["name"]}.')
|
|
return
|
|
|
|
# Get Pitcher and Batter objects
|
|
pitcher = get_one_pitcher(pitcher_player['id'])
|
|
batter = get_one_batter(batter_player['id'])
|
|
|
|
da_roll = random.randint(1, 1000)
|
|
embed = get_team_embed(f'{pitcher_player["name"]} vs {batter_player["name"]}', thumbnail=False)
|
|
|
|
# Calculate matchup OBP
|
|
batter_ob = batter['ob_vr'] if pitcher['hand'] == 'R' else batter['ob_vl']
|
|
if batter['hand'] == 'L':
|
|
pitcher_ob = pitcher['ob_vl']
|
|
elif batter['hand'] == 'R':
|
|
pitcher_ob = pitcher['ob_vr']
|
|
else:
|
|
pitcher_ob = pitcher['ob_vr'] if pitcher['hand'] == 'L' else pitcher['ob_vl']
|
|
obp = ((batter_ob + pitcher_ob) / 216)
|
|
|
|
# Calculate matchup AVG
|
|
batter_hit = batter['hit_vr'] if pitcher['hand'] == 'R' else batter['hit_vl']
|
|
batter_hr = batter['hr_vr'] if pitcher['hand'] == 'R' else batter['hr_vl']
|
|
batter_bb = batter['bb_vr'] if pitcher['hand'] == 'R' else batter['bb_vl']
|
|
batter_so = batter['so_vr'] if pitcher['hand'] == 'R' else batter['so_vl']
|
|
if batter['hand'] == 'L':
|
|
pitcher_hit = pitcher['hit_vl']
|
|
pitcher_hr = pitcher['hr_vl']
|
|
pitcher_bb = pitcher['bb_vl']
|
|
pitcher_so = pitcher['so_vl']
|
|
elif batter['hand'] == 'R':
|
|
pitcher_hit = pitcher['hit_vr']
|
|
pitcher_hr = pitcher['hr_vr']
|
|
pitcher_bb = pitcher['bb_vr']
|
|
pitcher_so = pitcher['so_vr']
|
|
else:
|
|
pitcher_hit = pitcher['hit_vr'] if pitcher['hand'] == 'L' else pitcher['hit_vl']
|
|
pitcher_hr = pitcher['hr_vr'] if pitcher['hand'] == 'L' else pitcher['hr_vl']
|
|
pitcher_bb = pitcher['bb_vr'] if pitcher['hand'] == 'L' else pitcher['bb_vl']
|
|
pitcher_so = pitcher['so_vr'] if pitcher['hand'] == 'L' else pitcher['so_vl']
|
|
avg = ((batter_hit + pitcher_hit) / (216 - batter_ob - pitcher_ob + batter_hit + pitcher_hit))
|
|
|
|
# Calculate matchup SLG
|
|
batter_tb = batter['tb_vr'] if pitcher['hand'] == 'R' else batter['tb_vl']
|
|
if batter['hand'] == 'L':
|
|
pitcher_tb = pitcher['tb_vl']
|
|
elif batter['hand'] == 'R':
|
|
pitcher_tb = pitcher['tb_vr']
|
|
else:
|
|
pitcher_tb = pitcher['tb_vr'] if pitcher['hand'] == 'L' else pitcher['tb_vl']
|
|
slg = ((batter_tb + pitcher_tb) / (216 - batter_ob - pitcher_ob + batter_hit + pitcher_hit))
|
|
|
|
embed.add_field(name='Average', value=f'{avg:.3f}')
|
|
embed.add_field(name='On Base', value=f'{obp:.3f}')
|
|
embed.add_field(name='Slugging', value=f'{slg:.3f}')
|
|
|
|
# Check for hit
|
|
if da_roll <= round(avg * 1000):
|
|
hit_roll = random.randint(1, round((batter_hit + pitcher_hit) * 1000))
|
|
if hit_roll <= round((batter_hr + pitcher_hr) * 1000):
|
|
result = 'Home Run!'
|
|
else:
|
|
tb123 = batter_tb + pitcher_tb - ((batter_hr + pitcher_hr) * 4)
|
|
si_chance = tb123 / (batter_hit + pitcher_hit)
|
|
if random.random() <= si_chance:
|
|
result = 'Single!'
|
|
else:
|
|
result = 'Double!'
|
|
|
|
# Check for on base
|
|
elif da_roll <= round(obp * 1000):
|
|
bb_chance = (batter_bb + pitcher_bb) / (batter_ob + pitcher_ob - batter_hit - pitcher_hit)
|
|
logging.info(f'bb_chance: {bb_chance}')
|
|
if random.random() <= bb_chance:
|
|
result = 'Walk!'
|
|
else:
|
|
result = 'HBP!'
|
|
|
|
# Check for out
|
|
else:
|
|
out_chances = 216 - round(216 * obp)
|
|
out_roll = random.randint(1, out_chances)
|
|
|
|
if out_roll <= batter_so + pitcher_so:
|
|
result = 'Strikeout!'
|
|
elif out_roll <= out_chances - ((out_chances - (batter_so + pitcher_so)) / 2):
|
|
result = 'Groundout!'
|
|
else:
|
|
result = 'Flyout!'
|
|
|
|
embed.add_field(name='Result', value=result)
|
|
|
|
logging.info(f'{pitcher_player["name"]} vs {batter_player["name"]}\n'
|
|
f'batter OB: {batter_ob} / pitcher OB: {pitcher_ob}\n'
|
|
f'batter BB: {batter_bb} / pitcher BB: {pitcher_bb}\n'
|
|
f'batter hit: {batter_hit} / pitcher hit: {pitcher_hit}\n'
|
|
f'batter hr: {batter_hr} / pitcher hr: {pitcher_hr}')
|
|
await ctx.send(content=None, embed=embed)
|
|
|
|
@commands.command(name='tutorial', help='Quick how-to guide')
|
|
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
|
|
async def tutorial_command(self, ctx):
|
|
embed = get_team_embed(f'Discord Scorebot Tutorial')
|
|
embed.add_field(
|
|
name='Start & End Game',
|
|
value=f'The `!startgame` command will initiate a game in the channel it is run. Other commands will only '
|
|
f'be accepted in this channel. The `!endgame` command will ask for confirmation and then send game '
|
|
f'stats to a google sheet (just like running `!tosheets`) before closing out the game.'
|
|
)
|
|
embed.add_field(
|
|
name='Set Lineups',
|
|
value=f'The `!setlineups` command or `!sl` is used to set the initial lineups as well as make mid-game '
|
|
f'substitutions. The format is as follows:\n```!sl <team_abbrev> <lineup_order>-<player_name>-'
|
|
f'<position>```\nIf there are multiple subs (or this is the initial lineup), enter a comma after the '
|
|
f'position and, without any spaces, begin the next player. Below are two examples:\n```!sl WV 1-'
|
|
f'Ronald Acuna Jr-RF```and```!sl WV 2-Trea Turner-SS,4-Eloy Jimenez-RF,7-Cody Bellinger-CF,10-'
|
|
f'Anthony Bass-P```\nNote on pitchers: if the pitcher is not batting, they are entered with 10 for '
|
|
f'their lineup_order; if they enter the game to bat, they are then entered in their batting order.',
|
|
inline=False
|
|
)
|
|
embed.add_field(
|
|
name='Logging Plays',
|
|
value=f'Plays are logged with the `!log <play_details>` command. You can run `!help log` to see a full '
|
|
f'list. For help with a specific play log, run `!help log x` for help logging an x-chance.',
|
|
inline=False
|
|
)
|
|
embed.add_field(
|
|
name='Game State Modifications',
|
|
value=f'The score, the number of ours, and the placement of baserunners can all be modified manually in '
|
|
f'cases of either bad automation (please let Cal know) or a mistake was made.\nThe `!advance` '
|
|
f'command manually advanced baserunners; the `!clear` command removes a baserunner; the `!outs` '
|
|
f'command can add or subtract outs (never past 0 or 3); the `!place` command manually places '
|
|
f'baserunners; the `!score` command adjusts either the home or away team\'s score.',
|
|
inline=False
|
|
)
|
|
embed.add_field(
|
|
name='Results',
|
|
value=f'The `!tosheets` command will compile the game\'s stats and upload them into a google sheets page '
|
|
f'formatted for the !submit command. The `!stats` command will display the game\'s stats in a simple '
|
|
f'csv format and post them to discord. Both of these commands can be run at any time __before__ the '
|
|
f'`!endgame` command is run.',
|
|
inline=False
|
|
)
|
|
await ctx.send(content=None, embed=embed)
|
|
|
|
|
|
async def setup(bot):
|
|
await bot.add_cog(Gameday(bot))
|
|
|