Merge pull request #262 from Sakimori/draft-pool-expansion

Draft pool expansion
This commit is contained in:
Sakimori 2021-06-30 17:01:52 -04:00 committed by GitHub
commit bdc6841826
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 154 additions and 29 deletions

1
.gitignore vendored
View file

@ -358,3 +358,4 @@ env
/data/leagues /data/leagues
/simmadome/build /simmadome/build
/simmadome/.eslintcache /simmadome/.eslintcache
/matteo_env/Lib/site-packages

View file

@ -50,6 +50,8 @@ class player(object):
"strikeouts_taken" : 0 "strikeouts_taken" : 0
} }
self.stat_name = self.name self.stat_name = self.name
if self.name == "Tim Locastro":
self.randomize_stars()
def star_string(self, key): def star_string(self, key):
str_out = "" str_out = ""
@ -68,6 +70,15 @@ class player(object):
def __str__(self): def __str__(self):
return self.name return self.name
def randomize_stars(self):
for key in ["batting_stars", "pitching_stars", "baserunning_stars", "defense_stars"]:
#random star value between 0 and 6.5
stars = random.randint(0,6)
half_star = random.random() < 0.5 #half star addition
if half_star:
stars = half_star + 0.5
self.stlats[key] = stars
class team(object): class team(object):
def __init__(self): def __init__(self):
@ -208,6 +219,7 @@ class game(object):
def __init__(self, team1, team2, length=None): def __init__(self, team1, team2, length=None):
self.over = False self.over = False
self.random_weather_flag = False
self.teams = {"away" : team1, "home" : team2} self.teams = {"away" : team1, "home" : team2}
self.inning = 1 self.inning = 1
self.outs = 0 self.outs = 0
@ -682,6 +694,9 @@ class game(object):
self.top_of_inning = not self.top_of_inning self.top_of_inning = not self.top_of_inning
if self.random_weather_flag and self.top_of_inning:
setattr(self, "weather", random.choice(list(weather.safe_weathers().values()))(self))
self.weather.on_flip_inning(self) self.weather.on_flip_inning(self)
self.choose_next_batter() self.choose_next_batter()
@ -690,6 +705,7 @@ class game(object):
self.inning += 1 self.inning += 1
if self.inning > self.max_innings and self.teams["home"].score != self.teams["away"].score: #game over if self.inning > self.max_innings and self.teams["home"].score != self.teams["away"].score: #game over
self.over = True self.over = True
try: #if something goes wrong with OBL don't erase game
if self.max_innings >= 9 or self.weather.name in ["Leaf Eddies", "Torrential Downpour"]: if self.max_innings >= 9 or self.weather.name in ["Leaf Eddies", "Torrential Downpour"]:
if self.teams["home"].score == 16: if self.teams["home"].score == 16:
this_xvi_team = self.teams["home"] this_xvi_team = self.teams["home"]
@ -698,6 +714,9 @@ class game(object):
else: else:
this_xvi_team = None this_xvi_team = None
db.save_obl_results(self.teams["home"] if self.teams["home"].score > self.teams["away"].score else self.teams["away"], self.teams["home"] if self.teams["home"].score < self.teams["away"].score else self.teams["away"], xvi_team=this_xvi_team) db.save_obl_results(self.teams["home"] if self.teams["home"].score > self.teams["away"].score else self.teams["away"], self.teams["home"] if self.teams["home"].score < self.teams["away"].score else self.teams["away"], xvi_team=this_xvi_team)
except:
pass
def end_of_game_report(self): def end_of_game_report(self):
@ -798,6 +817,9 @@ def get_team(name):
team_json.rotation.append(team_json.pitcher) team_json.rotation.append(team_json.pitcher)
team_json.pitcher = None team_json.pitcher = None
update_team(team_json) update_team(team_json)
for player in team_json.rotation + team_json.lineup:
if player.name == "Tim Locastro":
player.randomize_stars()
return team_json return team_json
return None return None
except AttributeError: except AttributeError:
@ -818,6 +840,9 @@ def get_team_and_owner(name):
team_json.rotation.append(team_json.pitcher) team_json.rotation.append(team_json.pitcher)
team_json.pitcher = None team_json.pitcher = None
update_team(team_json) update_team(team_json)
for player in team_json.rotation + team_json.lineup:
if player.name == "Tim Locastro":
player.randomize_stars()
return (team_json, owner_id) return (team_json, owner_id)
return None return None
except AttributeError: except AttributeError:
@ -864,6 +889,9 @@ def search_team(search_term):
team_json.rotation.append(team_json.pitcher) team_json.rotation.append(team_json.pitcher)
team_json.pitcher = None team_json.pitcher = None
update_team(team_json) update_team(team_json)
for player in team_json.rotation + team_json.lineup:
if player.name == "Tim Locastro":
player.randomize_stars()
except AttributeError: except AttributeError:
team_json.rotation = [] team_json.rotation = []
team_json.rotation.append(team_json.pitcher) team_json.rotation.append(team_json.pitcher)

View file

@ -45,7 +45,7 @@ class game_strings_base(object):
twoparts = [] twoparts = []
diff_formats = {fielderschoice[0]: ("batter", "base_string"), diff_formats = {fielderschoice[0]: ("runner", "base_string"),
steal_success[0]: ("runner", "base_string"), steal_success[0]: ("runner", "base_string"),
steal_caught[0]: ("runner", "base_string", "defender")} steal_caught[0]: ("runner", "base_string", "defender")}
no_formats = strikeoutlooking + strikeoutswinging + doubleplay + walk + single + double + triple + homerun + grandslam no_formats = strikeoutlooking + strikeoutswinging + doubleplay + walk + single + double + triple + homerun + grandslam

View file

@ -190,15 +190,19 @@ def update_loop():
else: else:
if this_game.top_of_inning: if this_game.top_of_inning:
state["update_text"] = f"Top of {this_game.inning}. {this_game.teams['away'].name} batting!" state["update_text"] = f"Top of {this_game.inning}. {this_game.teams['away'].name} batting!"
this_game.weather.modify_top_of_inning_message(this_game, state)
if this_game.random_weather_flag:
this_game.weather.weather_report(this_game, state)
else: else:
if this_game.inning >= this_game.max_innings: if this_game.inning >= this_game.max_innings:
if this_game.teams["home"].score > this_game.teams["away"].score: if this_game.teams["home"].score > this_game.teams["away"].score:
this_game.victory_lap = True this_game.victory_lap = True
state["update_text"] = f"Bottom of {this_game.inning}. {this_game.teams['home'].name} batting!" state["update_text"] = f"Bottom of {this_game.inning}. {this_game.teams['home'].name} batting!"
this_game.weather.modify_top_of_inning_message(this_game, state) this_game.weather.modify_top_of_inning_message(this_game, state)
elif state["update_pause"] != 1 and this_game.play_has_begun: elif state["update_pause"] != 1 and this_game.play_has_begun:
if "twopart" in this_game.last_update[0].keys(): if "twopart" in this_game.last_update[0].keys():
@ -256,4 +260,4 @@ def update_loop():
socket_thread = threading.Thread(target=socketio.emit, args=("states_update", game_states)) socket_thread = threading.Thread(target=socketio.emit, args=("states_update", game_states))
socket_thread.start() socket_thread.start()
#socketio.emit("states_update", game_states) #socketio.emit("states_update", game_states)
time.sleep(8) time.sleep(3)

24
real_players.py Normal file
View file

@ -0,0 +1,24 @@
from lxml import html
import requests, json
import onomancer as ono
def random_real_player():
name = []
while name == []:
page = requests.get("http://www.baseball-reference.com/rand.fcgi")
tree = html.fromstring(page.content)
name = tree.xpath("//h1[@itemprop='name']/span/text()")
if len(name) > 0 and len(name[0]) == 4:
name = [] #gets rid of years
name = name[0]
return name
def get_real_players(num):
names = []
for i in range(0, num):
names.append(random_real_player())
players = {}
for name in names:
players[name] = json.loads(ono.get_stats(name))
return players

View file

@ -46,6 +46,9 @@
<Compile Include="games.py"> <Compile Include="games.py">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="real_players.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="roman.py" /> <Compile Include="roman.py" />
<Compile Include="the_draft.py" /> <Compile Include="the_draft.py" />
<Compile Include="the_prestige.py" /> <Compile Include="the_prestige.py" />

View file

@ -2,7 +2,7 @@ from collections import namedtuple
import games import games
import json import json
import uuid import uuid
import real_players
import onomancer import onomancer
Participant = namedtuple('Participant', ['handle', 'team']) Participant = namedtuple('Participant', ['handle', 'team'])
@ -16,19 +16,22 @@ class Draft:
""" """
@classmethod @classmethod
def make_draft(cls, teamsize, draftsize, minsize, pitchers): def make_draft(cls, teamsize, draftsize, minsize, pitchers, ono_ratio):
draft = cls(teamsize, draftsize, minsize, pitchers) draft = cls(teamsize, draftsize, minsize, pitchers, ono_ratio)
return draft return draft
def __init__(self, teamsize, draftsize, minsize, pitchers): def __init__(self, teamsize, draftsize, minsize, pitchers, ono_ratio):
self.DRAFT_SIZE = draftsize self.DRAFT_SIZE = int(draftsize * ono_ratio)
self.REAL_SIZE = draftsize - self.DRAFT_SIZE
self.REFRESH_DRAFT_SIZE = minsize # fewer players remaining than this and the list refreshes self.REFRESH_DRAFT_SIZE = minsize # fewer players remaining than this and the list refreshes
self.DRAFT_ROUNDS = teamsize self.DRAFT_ROUNDS = teamsize
self.pitchers = pitchers self.pitchers = pitchers
self._id = str(uuid.uuid4())[:6] self._id = str(uuid.uuid4())[:6]
self._participants = [] self._participants = []
self._active_participant = BOOKMARK # draft mutex self._active_participant = BOOKMARK # draft mutex
self._players = onomancer.get_names(limit=self.DRAFT_SIZE) nameslist = onomancer.get_names(limit=self.DRAFT_SIZE)
nameslist.update(real_players.get_real_players(self.REAL_SIZE))
self._players = nameslist
self._round = 0 self._round = 0
@property @property
@ -68,7 +71,9 @@ class Draft:
self.advance_draft() self.advance_draft()
def refresh_players(self): def refresh_players(self):
self._players = onomancer.get_names(limit=self.DRAFT_SIZE) nameslist = onomancer.get_names(limit=self.DRAFT_SIZE)
nameslist.update(real_players.get_real_players(self.REAL_SIZE))
self._players = nameslist
def advance_draft(self): def advance_draft(self):
""" """

View file

@ -1,4 +1,4 @@
import discord, json, math, os, roman, games, asyncio, random, main_controller, threading, time, urllib, leagues, datetime, gametext import discord, json, math, os, roman, games, asyncio, random, main_controller, threading, time, urllib, leagues, datetime, gametext, real_players
import database as db import database as db
import onomancer as ono import onomancer as ono
from league_storage import league_exists, season_save, season_restart from league_storage import league_exists, season_save, season_restart
@ -646,6 +646,8 @@ class StartDraftCommand(Command):
draftsize = 20 draftsize = 20
minsize = 4 minsize = 4
pitchers = 3 pitchers = 3
ono_ratio = 0.5
timeout = 120
for flag in flags: for flag in flags:
try: try:
@ -657,10 +659,16 @@ class StartDraftCommand(Command):
minsize = int(flag[1]) minsize = int(flag[1])
elif flag[0] == "p": elif flag[0] == "p":
pitchers = int(flag[1]) pitchers = int(flag[1])
elif flag[0] == "c":
ono_ratio = float(flag[1])
if ono_ratio > 1 or ono_ratio < 0:
raise CommandError("The Chaos value needs to be between 0 and 1, chief. Probability has rules.")
elif flag[0] == "w": #wait
timeout = int(flag[1])
else: else:
raise CommandError(f"We don't recognize that {flag[0]} flag.") raise CommandError(f"We don't recognize that {flag[0]} flag.")
except ValueError: except ValueError:
raise CommandError(f"Your {flag[0]} flag isn't a valid integer, boss.") raise CommandError(f"Your {flag[0]} flag isn't a valid nummber, boss.")
if teamsize-pitchers > 20 or pitchers > 8: if teamsize-pitchers > 20 or pitchers > 8:
raise CommandError("You can't fit that many players on a team, chief. Slow your roll.") raise CommandError("You can't fit that many players on a team, chief. Slow your roll.")
@ -669,7 +677,13 @@ class StartDraftCommand(Command):
if draftsize > 40: if draftsize > 40:
raise CommandError("40 players is the max. We're not too confident about pushing for more.") raise CommandError("40 players is the max. We're not too confident about pushing for more.")
draft = Draft.make_draft(teamsize, draftsize, minsize, pitchers) await msg.channel.send("Got it, boss. Give me a sec to find all the paperwork.")
try:
draft = Draft.make_draft(teamsize, draftsize, minsize, pitchers, ono_ratio)
except ConnectionError:
await msg.channel.send("Baseball Reference isn't playing nice. Could you try again for us in a minute or so?")
mentions = {f'<@!{m.id}>' for m in msg.mentions} mentions = {f'<@!{m.id}>' for m in msg.mentions}
content = msg.content.split('\n')[1:] # drop command out of message content = msg.content.split('\n')[1:] # drop command out of message
if not content or len(content) % 3: if not content or len(content) % 3:
@ -683,7 +697,7 @@ class StartDraftCommand(Command):
handle = mention handle = mention
break break
else: else:
await msg.channel.send(f"I don't recognize {handle_token}") await msg.channel.send(f"I don't recognize {handle_token}.")
return return
team_name = content[i + 1].strip() team_name = content[i + 1].strip()
if games.get_team(team_name): if games.get_team(team_name):
@ -727,7 +741,7 @@ class StartDraftCommand(Command):
embed=build_draft_embed(draft.get_draftees(), footer=footer), embed=build_draft_embed(draft.get_draftees(), footer=footer),
) )
try: try:
draft_message = await self.wait_draft(msg.channel, draft) draft_message = await self.wait_draft(msg.channel, draft, timeout)
draft.draft_player(f'<@!{draft_message.author.id}>', draft_message.content.split(' ', 1)[1]) draft.draft_player(f'<@!{draft_message.author.id}>', draft_message.content.split(' ', 1)[1])
except SlowDraftError: except SlowDraftError:
player = random.choice(draft.get_draftees()) player = random.choice(draft.get_draftees())
@ -777,7 +791,7 @@ class StartDraftCommand(Command):
return False return False
return False return False
async def wait_draft(self, channel, draft): async def wait_draft(self, channel, draft, timeout):
def check(m): def check(m):
if m.channel != channel: if m.channel != channel:
@ -790,16 +804,16 @@ class StartDraftCommand(Command):
return False return False
try: try:
draft_message = await client.wait_for('message', timeout=120.0, check=check) draft_message = await client.wait_for('message', timeout=timeout, check=check)
except asyncio.TimeoutError: except asyncio.TimeoutError:
raise SlowDraftError('Too slow') raise SlowDraftError('Too slow, boss.')
return draft_message return draft_message
class StartLeagueCommand(Command): class StartLeagueCommand(Command):
name = "startleague" name = "startleague"
template = "m;startleague [league name]\n[games per hour]" template = "m;startleague [league name]\n[games per hour]"
description = """Optional flags for the first line: `--queue X` or `-q X` to play X number of series before stopping; `--noautopostseason` will pause the league before starting postseason. description = """Optional flags for the first line: `--queue X` or `-q X` to play X number of series before stopping; `--autopostseason` will automatically start postseason at the end of the season.
Starts games from a league with a given name, provided that league has been saved on the website and has been claimed using claimleague. The games per hour sets how often the games will start (e.g. GPH 2 will start games at X:00 and X:30). By default it will play the entire season followed by the postseason and then stop but this can be customized using the flags. Starts games from a league with a given name, provided that league has been saved on the website and has been claimed using claimleague. The games per hour sets how often the games will start (e.g. GPH 2 will start games at X:00 and X:30). By default it will play the entire season followed by the postseason and then stop but this can be customized using the flags.
Not every team will play every series, due to how the scheduling algorithm is coded but it will all even out by the end.""" Not every team will play every series, due to how the scheduling algorithm is coded but it will all even out by the end."""
@ -2095,6 +2109,16 @@ def build_team_embed(team):
def build_star_embed(player_json): def build_star_embed(player_json):
starkeys = {"batting_stars" : "Batting", "pitching_stars" : "Pitching", "baserunning_stars" : "Baserunning", "defense_stars" : "Defense"} starkeys = {"batting_stars" : "Batting", "pitching_stars" : "Pitching", "baserunning_stars" : "Baserunning", "defense_stars" : "Defense"}
embed = discord.Embed(color=discord.Color.purple(), title=player_json["name"]) embed = discord.Embed(color=discord.Color.purple(), title=player_json["name"])
if player_json["name"] == "Tim Locastro": #the tim easter egg
for key in ["batting_stars", "pitching_stars", "baserunning_stars", "defense_stars"]:
#random star value between 0 and 6.5
stars = random.randint(0,6)
half_star = random.random() < 0.5 #half star addition
if half_star:
stars = half_star + 0.5
player_json[key] = stars
for key in starkeys.keys(): for key in starkeys.keys():
embedstring = "" embedstring = ""
starstring = str(player_json[key]) starstring = str(player_json[key])

View file

@ -57,6 +57,11 @@ class Weather:
def modify_game_end_message(self, game, state): def modify_game_end_message(self, game, state):
pass pass
def weather_report(self, game, state):
game.weather = random.choice(list(safe_weathers().values()))(game)
state["update_emoji"] = "🚌"
state["update_text"] += f" Weather report: {game.weather.name} {game.weather.emoji}"
class Supernova(Weather): class Supernova(Weather):
name = "Supernova" name = "Supernova"
@ -545,6 +550,18 @@ class LeafEddies(Weather):
if game.inning == 1: if game.inning == 1:
state["weather_text"] = self.name state["weather_text"] = self.name
class Smog(Weather):
name = "Smog"
emoji = "🚌"
duration_range = [1,1]
def __init__(self, game):
game.random_weather_flag = True
setattr(game, "weather", random.choice(list(safe_weathers().values()))(game))
pass
def all_weathers(): def all_weathers():
weathers_dic = { weathers_dic = {
"Supernova" : Supernova, "Supernova" : Supernova,
@ -562,7 +579,26 @@ def all_weathers():
"Tornado" : Tornado, "Tornado" : Tornado,
"Torrential Downpour" : Downpour, "Torrential Downpour" : Downpour,
"Summer Mist" : SummerMist, "Summer Mist" : SummerMist,
"Leaf Eddies" : LeafEddies "Leaf Eddies" : LeafEddies,
"Smog" : Smog
}
return weathers_dic
def safe_weathers():
"""weathers safe to swap in mid-game"""
weathers_dic = {
"Supernova" : Supernova,
"Midnight": Midnight,
"Slight Tailwind": SlightTailwind,
"Twilight" : Twilight,
"Thinned Veil" : ThinnedVeil,
"Drizzle" : Drizzle,
"Breezy": Breezy,
"Starlight" : Starlight,
"Meteor Shower" : MeteorShower,
"Hurricane" : Hurricane,
"Tornado" : Tornado,
"Summer Mist" : SummerMist
} }
return weathers_dic return weathers_dic