began work on tuning the stat weights for various actions

This commit is contained in:
Sakimori 2022-01-08 19:42:11 -05:00
parent e647a33499
commit de46f85dfe
7 changed files with 222 additions and 20 deletions

View file

@ -1,4 +1,5 @@
import os, player, tweepy, twitHandler, time
import os, player, tweepy, twitHandler, time, skillContests, random
from attributes import normalDis
if __name__ == "__main__":
#for name in ["Vivi", "Artemis", "Laika", "Sharks", "Dragons", "Melua", "Sabriina", "Jorts (Buttered)", "Jorts (Unbuttered)"]:
@ -8,16 +9,57 @@ if __name__ == "__main__":
# print(atr)
# print("----------")
twitter = twitHandler.TwitHandler()
if os.path.exists(os.path.join("Data", "lastID.twt")):
with open(os.path.join("Data", "lastID.twt")) as idFile:
lastID = idFile.readline().strip()
else:
lastID = 0
atkPlayer = player.Player("Vivi")
defPlayer = player.Player("Artemis")
for plyr in [atkPlayer, defPlayer]:
print(f"{plyr.name}:")
for atr in plyr.attributes:
print(atr)
print("----------")
def skillContest(atkPlayer:player.Player, defPlayer:player.Player, params:skillContests.SkillContestParams):
"""Contests the two players with the given stats and stat weights. Returns True on offensive success."""
if params.override is not None:
print(params.override)
else:
atkValue = 0
defValue = 0
for attr, weight in params.atkStats:
atkValue += 95 * weight/100
for attr, weight in params.defStats:
defValue += 35 * weight/100
print(f"Attack: {atkValue}")
print(f"Defense:{defValue}")
success = 0
total = 5000
for i in range(0,5000):
atkRoll = normalDis(atkValue, atkValue/2, 0)
defRoll = normalDis(defValue, defValue/2, 0)
success += (atkRoll-defRoll) > 0
print(f"Success {round(success/total*100,3)}% of the time.")
defAction = player.DefAction.Poke
for atkAction in [player.AtkAction.SkateF]:
params = skillContests.SkillContestParams(atkAction, defAction, skillContests.Situations.EvenStrength)
skillContest(atkPlayer, defPlayer, params)
#twitter = twitHandler.TwitHandler()
#if os.path.exists(os.path.join("Data", "lastID.twt")):
# with open(os.path.join("Data", "lastID.twt")) as idFile:
# lastID = idFile.readline().strip()
#else:
# lastID = 0
#while True:
# twitter.scanForMention(lastID)
# time.sleep(30)
# with open(os.path.join("Data", "lastID.twt")) as idFile:
# lastID = idFile.readline().strip()
while True:
twitter.scanForMention(lastID)
time.sleep(30)
with open(os.path.join("Data", "lastID.twt")) as idFile:
lastID = idFile.readline().strip()
#twitter.sendTextTweet(player.Player("Amogus").twitterString())

View file

@ -23,6 +23,15 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="attributes.py" />
<Compile Include="game.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="skillContests.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="team.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="twitHandler.py">
<SubType>Code</SubType>
</Compile>

View file

@ -76,13 +76,20 @@ class Attribute:
def normalDis(generator:random.Random, mean:float, stdDev:float, min:float=-math.inf, max:float=math.inf) -> float:
"""Generates random number from normal distribution with given mean and standard deviation. Optionally takes min and max values."""
def seededNormalDis(generator:random.Random, mean:float, stdDev:float, min:float=-math.inf, max:float=math.inf) -> float:
"""Generates random number from seeded normal distribution with given mean and standard deviation. Optionally takes min and max values."""
num = generator.gauss(mean, stdDev)
while min > num or num > max:
num = generator.gauss(mean, stdDev)
return num
def normalDis(mean:float, stdDev:float, min:float=-math.inf, max:float=math.inf) -> float:
"""Generates random number from normal distribution with given mean and standard deviation. Optionally takes min and max values."""
num = random.gauss(mean, stdDev)
while min > num or num > max:
num = random.gauss(mean, stdDev)
return num
#Attributes to generate
#["sample name", mean, stdDev, min(optional), max(optional)]
masterList = [
@ -92,9 +99,9 @@ masterList = [
["Strength", 40, 60, 0, 100], #power of slapshot/body checking (Defense)
["Dexterity", 50, 25, 10, 100], #power of wrist shot/dekes (Forward)
["Shot Accuracy", 40, 25, 10, 100], #accuracy on shots (Secondary/All Skaters)
["Pass Accuracy", 60, 35, 0, 100], #does what you think it does (Defense)
["Wisdom", 40, 50, 0, 100], #takes better shots/makes safer passes (Secondary/All)
["Stickhandling", 50, 35, 0, 100], #dekes/checking (Secondary/All)
["Pass Accuracy", 60, 20, 20, 100], #does what you think it does (Defense)
["Wisdom", 60, 50, 0, 100], #takes better shots/makes safer passes (Secondary/All)
["Stickhandling", 75, 20, 30, 100], #dekes/checking (Secondary/All) 35-95
["Discipline", 75, 60, 0, 100], #Higher means less penalties
["Intelligence", 50, 50, 0, 100], #Skill at positioning (as skater or goalie) (Secondary/All)
["Constitution", 60, 20, 0, 100] #Stamina/injury resilience
@ -111,5 +118,5 @@ def attributesFromName(name:str):
generator.seed(name)
atrs = [versionNumber] #in case of stat changes, record which number to use
for template in masterList:
atrs.append(Attribute(template[0], normalDis(generator, *template[1:])))
atrs.append(Attribute(template[0], seededNormalDis(generator, *template[1:])))
return atrs

63
game.py Normal file
View file

@ -0,0 +1,63 @@
import random, team, player
from team import Team
from player import Player, AtkAction, DefAction
from skillContests import SkillContestParams, Situations
class Game(object):
"""A game of hockey!"""
def __init__(self, awayTeam:Team, homeTeam:Team, threes:bool=False):
self.away = awayTeam
self.home = homeTeam
self.lineSize = 5
if len(awayTeam.roster) != 10 or len(homeTeam.roster) != 10 or threes:
self.lineSize = 3
self.goalieHome = self.home.chooseGoalie()
self.goalieAway = self.away.chooseGoalie()
self.playerInPossession = None
self.teamInPossession = None
self.skatersHome = [] #LW LD C RD RW
self.skatersAway = []
self.penaltyBoxAway = []
self.penaltyBoxHome = []
self.pulledGoalieAway = False
self.pulledGoalieHome = False
self.period = 1
self.clock = 60*20 #clock will be given in seconds
self.eventLog = []
def defendingTeam(self):
if teamInPossession == self.home:
return self.away
else:
return self.home
def homeAttacking(self):
return teamInPossession == self.home
def currentSituation(self):
skatersH = self.lineSize + self.pulledGoalieHome - len(self.penaltyBoxHome)
skatersA = self.lineSize + self.pulledGoalieAway - len(self.penaltyBoxAway)
if self.teamInPossession == self.home:
return Situations(skatersH - skatersA)
else:
return Situations(skatersA - skatersH)
def skillContest(self, atkPlayer:Player, defPlayer:Player, params:SkillContestParams):
"""Contests the two players with the given stats and stat weights. Returns True on offensive success."""
if params.override is not None:
return params.override
else:
atkValue = 0
defValue = 0
for attr, weight in params.atkStats:
atkValue += atkPlayer.getAttribute(attr) * weight/100
for attr, weight in params.defStats:
defValue += defPlayer.getAttribute(attr) * weight/100

View file

@ -1,4 +1,5 @@
import attributes
from enum import Enum
class CreationError(Exception):
pass
@ -27,6 +28,12 @@ class Player(object):
send += "\n"
return send
def getAttribute(self, shortname:str):
for attr in self.attributes:
if attr.name.lower().startswith(shortname.lower()):
return attr.value
return None
def __eq__(self, value):
if isinstance(value, Player):
return self.name == value.name
@ -42,3 +49,24 @@ class Skater(Player):
class Goalie(Player):
"""A hockey player that *is* a goalie."""
class AtkAction(Enum):
SkateB = 0
SkateF = 1
SkateT = 2
SkateA = 3
PassS = 4
PassF = 5
PassB = 6
Dump = 7
ShotS = 8
ShotW = 9
class DefAction(Enum):
Steal = 0
Poke = 1
BlockLn = 2
Body = 3
Force = 4
Pin = 5
BlockSlot = 6

43
skillContests.py Normal file
View file

@ -0,0 +1,43 @@
from player import AtkAction, DefAction
from enum import Enum
class Situations(Enum):
EvenStrength = 0
PP1 = 1 #4v3, 5v4, or 6v5
PP2 = 2 #5v3 or 6v4
SH1 = -1 #3v4, 4v5, 5v6
SH2 = -2 #3v5 or 4v6
class SkillContestParams(object):
"""Basic structure for contests of skill."""
atkStats = []
defStats = []
override = None
def __init__(self, atkAction:AtkAction, defAction:DefAction, situation:Situations):
"""Determines which skills to test, and how strongly."""
if situation == Situations.EvenStrength:
result = evenTable[atkAction.value][defAction.value]
if isinstance(result, bool):
self.override = result
return
self.atkStats = result[0]
self.defStats = result[1]
#Bool overrides, or [List of (stat,weight) , List of (stat,weight)]
evenTable = [
#Steal #Poke #block lane #Body check #Force off puck #Pin to wall #Body block
[[[('Sti',100)],[('Sti',20)]], [[('Dex',75),('Sti',35)],[('Sti',40)]], True, [[('Agi',110)],[('Spe',100)]],[[('Agi',110)],[('Str',100)]], [[('Agi',110)],[('Str',100)]], True], #Skate back
[[[('Sti',100)],[('Sti',40)]], [[('Dex',50),('Sti',30)],[('Sti',100)]], True, [[('Str',100)],[('Str',100)]],[[('Spe',60),('Str',40)],[('Str',100)]],[[('Spe',60),('Str',40)],[('Str',100)]],True], #Skate forward
[[[('Dex',75),('Sti',25)],[('Sti',50)]], [[('Dex',70),('Sti',30)],[('Sti',100)]], [[('Ref',50),('Sti',50)],[('Sti',100)]],[[('Dex',100)],[('Str',100)]],True, True, [[('Sti',100)],[('Ref',100)]]], #Skate through
[[[('Sti',100)],[('Sti',30)]], [[('Dex',70),('Sti',30)],[('Sti',100)]], True, [[('Agi',100)],[('Str',100)]],[[('Sti',100)],[('Str',100)]], [[('Sti',100)],[('Str',100)]], True], #Skate around
[True, [[('Pas',100)],[('Sti',100)]], [[('Pas',100)],[('Ref',100)]], True, [[('Str',80),('Pas',20)],[('Str',100)]],[[('Str',80),('Pas',20)],[('Str',100)]],True], #Stretch pass
[[[('Pas',100)],[('Ref',20),('Sti',40)]],[[('Pas',100)],[('Sti',100)]], [[('Pas',100)],[('Ref',100)]], True, [[('Str',100)],[('Str',100)]], [[('Str',100)],[('Str',100)]], [[('Pas',100)],[('Ref',100)]]], #Forward pass
[[[('Pas',100)],[('Int',20)]], True, True, True, True, [[('Sti',100)],[('Str',100)]], True], #Backward pass
[True, True, [[('Wis',100)],[('Ref',100)]], True, True, True, True], #Dump/ice
[False, False, True, False, True, False, [[('Sho',100)],[('Ref',70),('Int',30)]]], #Slapshot
[[[('Dex',100)],[('Sti',40)]], [[('Dex',100)],[('Sti',100)]], [[('Sho',100)],[('Ref',100)]], [[('Dex',100)],[('Str',100)]],[[('Dex',100)],[('Str',100)]], [[('Dex',100)],[('Str',100)]], True] #Wrist shot
]

10
team.py Normal file
View file

@ -0,0 +1,10 @@
import player
from random import sample
class Team(object):
"""A team of either 6 or 10 skaters and 1-3 goalies."""
roster = [] #ordered, first line then second line; (#, name)
goalies = [] # (#, name)
def chooseGoalie(self):
return sample(goalies,1)