Compare commits

...

3 Commits

  1. 19
      v3/game.py
  2. 8
      v3/gators.py
  3. 16
      v3/inninggenerator.py
  4. 23
      v3/logsetup.py
  5. 10
      v3/players.py
  6. 2
      v3/requirements.txt
  7. 121
      v3/roll.py
  8. 20
      v3/simulator.py

19
v3/game.py

@ -3,7 +3,7 @@ import logging @@ -3,7 +3,7 @@ import logging
from players import Team
from gators import Congregation
from generators import InningGenerator
from inninggenerator import InningGenerator
from states import GameState, TeamState
@ -18,14 +18,12 @@ class Game(object): @@ -18,14 +18,12 @@ class Game(object):
"""
def __init__(self, config, redteam, blueteam, congregation):
logger.warning("...")
logger.warning("...")
logger.warning("\n\n")
logger.warning("---------------------- STARTING GAME -------------------------")
logger.warning("WARNING level: on")
logger.info("INFO level: on")
logger.debug("DEBUG level: on")
logger.warning("...")
logger.warning("...")
logger.warning("\n\n")
self.config = config
@ -50,10 +48,17 @@ class Game(object): @@ -50,10 +48,17 @@ class Game(object):
logger.warning('====================================')
logger.warning('============ TOP INNING ============')
logger.warning('====================================')
top_inning = InningGenerator.generate_half(self.config, self.state, True)
t1_w, t1_r = InningGenerator.generate_half(self.config, self.state, True)
logger.warning('====================================')
logger.warning('============ BOT INNING ============')
logger.warning('====================================')
bottom_inning = InningGenerator.generate_half(self.config, self.state, False)
t2_w, t2_r = InningGenerator.generate_half(self.config, self.state, False)
logger.warning('====================================')
logger.warning('=========== GAME SUMMARY ===========')
logger.warning('====================================')
state = self.state
logger.info(f"{state.team1.name}: {state.state1.wickets} / {state.state1.runs} - {len(t1_r)}.{len(t1_r[-1])}")
logger.info(f"{state.team2.name}: {state.state2.wickets} / {state.state2.runs} - {len(t2_r)}.{len(t2_r[-2])}")

8
v3/gators.py

@ -21,7 +21,7 @@ class Gator(object): @@ -21,7 +21,7 @@ class Gator(object):
def __init__(self):
self.id = str(uuid.uuid4())
self.attr = {}
self.attr['agg'] = random.randint(1,5)
self.attr['rea'] = random.randint(1,5)
self.attr['rxn'] = random.randint(1,5)
self.attr['con'] = random.randint(1,5)
self.attr['agg'] = 3 # random.randint(1,5)
self.attr['rea'] = 3 # random.randint(1,5)
self.attr['rxn'] = 3 # random.randint(1,5)
self.attr['con'] = 3 # random.randint(1,5)

16
v3/generators.py → v3/inninggenerator.py

@ -38,10 +38,6 @@ class InningGenerator(object): @@ -38,10 +38,6 @@ class InningGenerator(object):
final_runs = []
final_wickets = []
# Keep running track of runs/wickets
current_runs = batting_state.runs
current_wickets = batting_state.wickets
team_name = batting_team.name
opi = config['OVERS_PER_INNING']
@ -57,7 +53,7 @@ class InningGenerator(object): @@ -57,7 +53,7 @@ class InningGenerator(object):
# Increment runs/wickets and update pokers
if outcome > 0:
# Outcome is runs for the player
current_runs += outcome
batting_state.runs += outcome
this_over.append(outcome)
this_wickets.append(0)
if outcome%2 == 1:
@ -69,8 +65,8 @@ class InningGenerator(object): @@ -69,8 +65,8 @@ class InningGenerator(object):
elif outcome < 0:
# Outcome is wickets
current_wickets += 1
this_over.append(-1)
batting_state.wickets += 1
this_over.append(0)
this_wickets.append(1)
# If we have reached maximum number of wickets, this will be None, check happens below
batting_state.pokers[0] = batting_team.get_next_player()
@ -82,11 +78,11 @@ class InningGenerator(object): @@ -82,11 +78,11 @@ class InningGenerator(object):
# Determine if the side should end
if cls.check_runs_end_over(config, batting_state, bowling_state, top):
logger.warning(f"{team_name}: Over {current_over+1}: Game ends due to {team_name} getting required runs!")
logger.warning(f"{team_name}: Over {iover+1}: Game ends due to {team_name} getting required runs!")
batting_state.done = True
if cls.check_wickets_end_over(config, batting_state, bowling_state, top):
logger.warning(f"{team_name}: Over {current_over+1}: Inning ends due to {team_name} getting maximum wickets!")
logger.warning(f"{team_name}: Over {iover+1}: Inning ends due to {team_name} getting maximum wickets!")
batting_state.done = True
if batting_state.done:
@ -97,7 +93,7 @@ class InningGenerator(object): @@ -97,7 +93,7 @@ class InningGenerator(object):
logger.info("---------------")
logger.info(f"{team_name}: This Over ({iover+1}): {sum(this_wickets)} / {' '.join([str(z) if z>=0 else 'W' for z in this_over])}")
logger.info(f"{team_name}: Cumulative: {current_wickets} / {current_runs} - {iover+1}")
logger.info(f"{team_name}: Cumulative: {batting_state.wickets} / {batting_state.runs} - {iover+1}")
logger.info(f"--------------")
if batting_state.done:

23
v3/logsetup.py

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
import logging
# Set basic universal log options
# (level should be WARNING INFO DEBUG)
loglevel = logging.DEBUG
logging.basicConfig(format='', level=logging.WARNING)
root_logger = logging.getLogger()
root_logger.handlers = []
# Create gp logger and add handlers
### logger = logging.Logger('gp')
logger = logging.getLogger('gp')
logger.setLevel(loglevel)
fh = logging.FileHandler('output.log')
fh.setLevel(loglevel)
logger.addHandler(fh)
sh = logging.StreamHandler()
sh.setLevel(loglevel)
logger.addHandler(sh)

10
v3/players.py

@ -24,7 +24,7 @@ class Team(object): @@ -24,7 +24,7 @@ class Team(object):
"""
Check if there is a next player to return
"""
return self.index >= len(self.roster)
return self.index < len(self.roster)
def get_next_player(self):
"""
@ -40,7 +40,7 @@ class Player(object): @@ -40,7 +40,7 @@ class Player(object):
def __init__(self):
self.id = str(uuid.uuid4())
self.attr = {}
self.attr['agg'] = random.randint(1,5)
self.attr['rea'] = random.randint(1,5)
self.attr['rxn'] = random.randint(1,5)
self.attr['con'] = random.randint(1,5)
self.attr['agg'] = 3 # random.randint(1,5)
self.attr['rea'] = 3 # random.randint(1,5)
self.attr['rxn'] = 3 # random.randint(1,5)
self.attr['con'] = 3 # random.randint(1,5)

2
v3/requirements.txt

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
numpy
scipy

121
v3/roll.py

@ -1,14 +1,127 @@ @@ -1,14 +1,127 @@
import numpy as np
import random
import logging
from scipy.stats import beta
logger = logging.getLogger('gp')
class Roll(object):
"""
Provides class methods to perform various rolls using players and gators.
Uses beta functions under the hood to generate random outcomes.
"""
@classmethod
def random_sample_beta_inv_cdf(cls, E, a):
"""
Sample a beta inverse CDF with expectation E and alpha param a.
The values of E and alpha will fix the value of beta.
"""
y_in = random.random()
b = a * (1.0/E - 1.0)
# Use the percent point function - inverse CDF
x_out = beta.ppf(y_in, a, b)
x = y_in # This is the input random number, uniform between 0-1
y = x_out # This is the output weighted random number
return (x, y)
@classmethod
def attr_roll(cls, attr_lab, player, gator):
player_attr = player.attr[attr_lab]
gator_attr = gator.attr[attr_lab]
"""
Use player/gator attributes to construct a beta function, and sample it randomly.
Return the player and gator rolls (bounded between 0-1 inclusive).
"""
nstars = 5
# Map star ratings to new spaces
# Expected outcome
player_attr_min = 0.25
player_attr_max = 0.75
player_attr_space = np.linspace(player_attr_min, player_attr_max, nstars)
# Consistency (alpha param of beta distribution)
player_con_space = np.logspace(-0.5, 0.5, nstars)
# Expected outcome
gator_attr_min = 0.25
gator_attr_max = 0.75
gator_attr_space = np.linspace(gator_attr_min, gator_attr_min, nstars)
# Consistency
gator_con_space = np.logspace(-0.5, 0.5, nstars)
# Get attribute
pa = player.attr[attr_lab]
ga = gator.attr[attr_lab]
# Get consistency
pc = player.attr['con']
gc = gator.attr['con']
# Transform to get beta function parameters (expectation E, alpha)
p_E = player_attr_space[pa-1]
g_E = gator_attr_space[ga-1]
p_alpha = player_con_space[pc-1]
g_alpha = gator_con_space[gc-1]
_, p_outcome = cls.random_sample_beta_inv_cdf(p_E, p_alpha)
_, g_outcome = cls.random_sample_beta_inv_cdf(g_E, g_alpha)
return (p_outcome, g_outcome)
@classmethod
def roll(cls, player, gator):
outcomes = [-3, -2, -1, 0, 1, 4, 6]
return random.choice(outcomes)
"""
Given a player and a gator, run the various rolls and find an outcome.
"""
# First roll requires gator or player wins both aggressiveness and reach rolls
agg = cls.attr_roll('agg', player, gator)
rea = cls.attr_roll('rea', player, gator)
rxn = cls.attr_roll('rxn', player, gator)
agg_diff = agg[0] - agg[1]
rea_diff = rea[0] - rea[1]
rxn_diff = rxn[0] - rxn[1]
if agg_diff > 0 and rea_diff > 0:
# Player won
if rxn_diff < 0:
# Player won but gator got reversal
logger.debug(f"Player flinched!")
return 0
else:
# Outcome: runs
diff = rxn[0] - rxn[1]
if diff > 0.8:
logger.debug(f"Gator got slapped!")
return 6
elif diff > 0.5:
logger.debug(f"Gator got booped in the snoot!")
return 4
else:
logger.debug(f"Gator got poked!")
return 1
elif agg_diff < 0 and rea_diff < 0:
# Gator won
if rxn_diff > 0:
# Gator won but player got reversal
logger.debug(f"Gator flinched!")
return 0
else:
# Outcome: wickets
if abs(rxn_diff) > 0.8:
logger.debug(f"Player was eaten by gator!")
return -3
elif abs(rxn_diff) > 0.5:
logger.debug(f"Player's arm got chomped by the gator!")
return -2
elif abs(rxn_diff) > 0.2:
logger.debug(f"Player's finger got chomped by the gator!")
return -1
else:
logger.debug(f"Player escaped!")
return 0
else:
logger.debug(f"Nothing happened!")
return 0

20
v3/simulator.py

@ -1,38 +1,26 @@ @@ -1,38 +1,26 @@
import logging
import random
import logsetup
from config import Config
from players import Team
from gators import Congregation
from game import Game
# 1 = critical info
# 2 = all info
# 3 = debug
LOG_LEVEL = 3
loglevels = [logging.WARNING, logging.INFO, logging.DEBUG]
logging.basicConfig(format='', level=logging.DEBUG)
logger = logging.Logger('gp')
logger.addHandler(logging.StreamHandler())
logger.addHandler(logging.FileHandler('output.log'))
logger = logging.getLogger('gp')
class GameSimulator(object):
def simulate(self):
logger.warning("...")
logger.warning("...")
logger.warning("\n\n")
logger.warning("---------------------- STARTING SIMULATOR -------------------------")
logger.warning("logger warning level: on")
logger.info("logger info level: on")
logger.debug("logger debug level: on")
logger.warning("...")
logger.warning("...")
logger.warning("\n\n")
config_dict = dict(
PLAYERS_PER_SIDE = 11,

Loading…
Cancel
Save