Implemented basic action matrix, set weights to initial values

This commit is contained in:
Sakimori 2022-01-09 16:11:34 -05:00
parent de46f85dfe
commit cc0487469a
7 changed files with 161 additions and 75 deletions

View file

@ -1,5 +1,6 @@
import os, player, tweepy, twitHandler, time, skillContests, random import os, player, tweepy, twitHandler, time, skillContests, random
from attributes import normalDis from attributes import normalDis
from hocTests import AttributeTest
if __name__ == "__main__": if __name__ == "__main__":
#for name in ["Vivi", "Artemis", "Laika", "Sharks", "Dragons", "Melua", "Sabriina", "Jorts (Buttered)", "Jorts (Unbuttered)"]: #for name in ["Vivi", "Artemis", "Laika", "Sharks", "Dragons", "Melua", "Sabriina", "Jorts (Buttered)", "Jorts (Unbuttered)"]:
@ -9,43 +10,8 @@ if __name__ == "__main__":
# print(atr) # print(atr)
# print("----------") # print("----------")
atkPlayer = player.Player("Vivi") test = AttributeTest()
defPlayer = player.Player("Artemis") test.allTests()
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() #twitter = twitHandler.TwitHandler()
#if os.path.exists(os.path.join("Data", "lastID.twt")): #if os.path.exists(os.path.join("Data", "lastID.twt")):

View file

@ -26,6 +26,9 @@
<Compile Include="game.py"> <Compile Include="game.py">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="hocTests.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="skillContests.py"> <Compile Include="skillContests.py">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>

View file

@ -27,7 +27,7 @@ class Attribute:
return f"{valueString} - {self.name}" return f"{valueString} - {self.name}"
def __str__(self): def __str__(self):
return f"{self.name}: {int(self.value)}" return f"{self.name} - {int(self.value)}"
#Comparison helpers #Comparison helpers
def __eq__(self, other): #can compare attributes by name, or find attribute by value def __eq__(self, other): #can compare attributes by name, or find attribute by value
@ -95,7 +95,7 @@ def normalDis(mean:float, stdDev:float, min:float=-math.inf, max:float=math.inf)
masterList = [ masterList = [
["Speed", 60, 25, 0, 100], #sprint speed (Forward) ["Speed", 60, 25, 0, 100], #sprint speed (Forward)
["Agility", 50, 35, 0, 100], #changing directions/goaltending slide (Goalie) ["Agility", 50, 35, 0, 100], #changing directions/goaltending slide (Goalie)
["Reflexes", 50, 35, 0, 100], #deflections/goaltending blocks/intercepting passes (Goalie) ["Reflexes", 50, 35, 20, 100], #deflections/goaltending blocks/intercepting passes (Goalie)
["Strength", 40, 60, 0, 100], #power of slapshot/body checking (Defense) ["Strength", 40, 60, 0, 100], #power of slapshot/body checking (Defense)
["Dexterity", 50, 25, 10, 100], #power of wrist shot/dekes (Forward) ["Dexterity", 50, 25, 10, 100], #power of wrist shot/dekes (Forward)
["Shot Accuracy", 40, 25, 10, 100], #accuracy on shots (Secondary/All Skaters) ["Shot Accuracy", 40, 25, 10, 100], #accuracy on shots (Secondary/All Skaters)
@ -119,4 +119,16 @@ def attributesFromName(name:str):
atrs = [versionNumber] #in case of stat changes, record which number to use atrs = [versionNumber] #in case of stat changes, record which number to use
for template in masterList: for template in masterList:
atrs.append(Attribute(template[0], seededNormalDis(generator, *template[1:]))) atrs.append(Attribute(template[0], seededNormalDis(generator, *template[1:])))
return atrs return atrs
def singleAttribute(shortname:str):
"""Generates a tuple of (name,value) of an attribute with given shortname"""
for atr in masterList:
if atr[0].lower().startswith(shortname.lower()):
return (atr[0], normalDis(*atr[1:]))
def attributeMinMax(shortname:str):
"""Retrieves a single attribute's minimum and maximum values. Returns (min, max)."""
for atr in masterList:
if atr[0].lower().startswith(shortname.lower()):
return (atr[3], atr[4])

46
game.py
View file

@ -2,36 +2,38 @@ import random, team, player
from team import Team from team import Team
from player import Player, AtkAction, DefAction from player import Player, AtkAction, DefAction
from skillContests import SkillContestParams, Situations from skillContests import SkillContestParams, Situations
from attributes import normalDis
class Game(object): class Game(object):
"""A game of hockey!""" """A game of hockey!"""
def __init__(self, awayTeam:Team, homeTeam:Team, threes:bool=False): def __init__(self, awayTeam:Team, homeTeam:Team, threes:bool=False):
self.away = awayTeam if awayTeam is not None and homeTeam is not None:
self.home = homeTeam self.away = awayTeam
self.home = homeTeam
self.lineSize = 5 self.lineSize = 5
if len(awayTeam.roster) != 10 or len(homeTeam.roster) != 10 or threes: if len(awayTeam.roster) != 10 or len(homeTeam.roster) != 10 or threes:
self.lineSize = 3 self.lineSize = 3
self.goalieHome = self.home.chooseGoalie() self.goalieHome = self.home.chooseGoalie()
self.goalieAway = self.away.chooseGoalie() self.goalieAway = self.away.chooseGoalie()
self.playerInPossession = None self.playerInPossession = None
self.teamInPossession = None self.teamInPossession = None
self.skatersHome = [] #LW LD C RD RW self.skatersHome = [] #LW LD C RD RW
self.skatersAway = [] self.skatersAway = []
self.penaltyBoxAway = [] self.penaltyBoxAway = []
self.penaltyBoxHome = [] self.penaltyBoxHome = []
self.pulledGoalieAway = False self.pulledGoalieAway = False
self.pulledGoalieHome = False self.pulledGoalieHome = False
self.period = 1 self.period = 1
self.clock = 60*20 #clock will be given in seconds self.clock = 60*20 #clock will be given in seconds
self.eventLog = [] self.eventLog = []
def defendingTeam(self): def defendingTeam(self):
if teamInPossession == self.home: if teamInPossession == self.home:
@ -58,6 +60,10 @@ class Game(object):
atkValue = 0 atkValue = 0
defValue = 0 defValue = 0
for attr, weight in params.atkStats: for attr, weight in params.atkStats:
atkValue += atkPlayer.getAttribute(attr) * weight/100 atkValue += atkPlayer.getAttribute(attr).value * weight/100
for attr, weight in params.defStats: for attr, weight in params.defStats:
defValue += defPlayer.getAttribute(attr) * weight/100 defValue += defPlayer.getAttribute(attr).value * weight/100
atkRoll = normalDis(atkValue, atkValue/2, 0)
defRoll = normalDis(defValue, defValue/2, 0)
return atkRoll-defRoll > 0

88
hocTests.py Normal file
View file

@ -0,0 +1,88 @@
import skillContests, player, team, game, attributes
class AttributeTest(object):
def __init__(self):
self.atkAction = player.AtkAction.ShotW
self.defAction = player.DefAction.BlockSlot
self.atkPlayer = player.Player(None)
self.defPlayer = player.Player(None)
self.fakeGame = game.Game(None, None)
self.params = skillContests.SkillContestParams(self.atkAction, self.defAction)
def lowStats(self):
"""Tests attacker and defender with minimum stats."""
for i in [0,1]:
setPlayer = [self.atkPlayer, self.defPlayer][i]
statSet = [self.params.atkStats, self.params.defStats][i]
for shortname, weight in statSet:
longname, value = attributes.singleAttribute(shortname)
setPlayer.setAttribute(longname, attributes.attributeMinMax(longname)[0]+5)
self.getAvg("mutual minimum stats")
def lowAtkHighDef(self):
for shortname, weight in self.params.atkStats:
longname, value = attributes.singleAttribute(shortname)
self.atkPlayer.setAttribute(longname, attributes.attributeMinMax(longname)[0]+5)
for shortname, weight in self.params.defStats:
longname, value = attributes.singleAttribute(shortname)
self.defPlayer.setAttribute(longname, attributes.attributeMinMax(longname)[1]-5)
self.getAvg("bad attack, good defence")
def highAtkLowDef(self):
for shortname, weight in self.params.atkStats:
longname, value = attributes.singleAttribute(shortname)
self.atkPlayer.setAttribute(longname, attributes.attributeMinMax(longname)[1]-5)
for shortname, weight in self.params.defStats:
longname, value = attributes.singleAttribute(shortname)
self.defPlayer.setAttribute(longname, attributes.attributeMinMax(longname)[0]+5)
self.getAvg("good attack, bad defence")
def highStats(self):
for i in [0,1]:
setPlayer = [self.atkPlayer, self.defPlayer][i]
statSet = [self.params.atkStats, self.params.defStats][i]
for shortname, weight in statSet:
longname, value = attributes.singleAttribute(shortname)
setPlayer.setAttribute(longname, attributes.attributeMinMax(longname)[1]-5)
self.getAvg("mutual maximum stats")
def randomStats(self):
success = 0
total = 0
for i in range(0, 5000):
for shortname, weight in self.params.atkStats:
self.atkPlayer.setAttribute(*attributes.singleAttribute(shortname))
for shortname, weight in self.params.defStats:
self.defPlayer.setAttribute(*attributes.singleAttribute(shortname))
total += 1
success += self.fakeGame.skillContest(self.atkPlayer, self.defPlayer, self.params)
print(f"Testing random stats...")
print(f"Success rate: {str(round(success/total*100,2))}%")
print("-------")
def allTests(self):
self.lowStats()
self.lowAtkHighDef()
self.highAtkLowDef()
self.highStats()
self.randomStats()
def getAvg(self, testName:str):
success = 0
total = 0
for i in range(0, 5000):
total += 1
success += self.fakeGame.skillContest(self.atkPlayer, self.defPlayer, self.params)
print(f"Testing {testName}...")
print("Attacker stat values:")
for attr, w in self.params.atkStats:
print(self.atkPlayer.getAttribute(attr))
print("Defender stat values:")
for attr, w in self.params.defStats:
print(self.defPlayer.getAttribute(attr))
print(f"Success rate: {str(round(success/total*100,2))}%")
print("-------")

View file

@ -8,10 +8,13 @@ class Player(object):
"""A hockey player with attributes and various functions.""" """A hockey player with attributes and various functions."""
def __init__(self, name:str): def __init__(self, name:str):
if len(name) > 30: if name is None:
self.attributes = []
elif len(name) > 30:
raise CreationError("Player name too long.") raise CreationError("Player name too long.")
self.name = name else:
self.attributes = self.loadAttributes() self.name = name
self.attributes = self.loadAttributes()
def loadAttributes(self): def loadAttributes(self):
"""Generates attributes based on name, or loads from database if present.""" """Generates attributes based on name, or loads from database if present."""
@ -29,11 +32,19 @@ class Player(object):
return send return send
def getAttribute(self, shortname:str): def getAttribute(self, shortname:str):
"""Returns an Attribute object with given shortname."""
for attr in self.attributes: for attr in self.attributes:
if attr.name.lower().startswith(shortname.lower()): if attr.name.lower().startswith(shortname.lower()):
return attr.value return attr
return None return None
def setAttribute(self, shortname:str, value:float):
for attr in self.attributes:
if attr.name.lower().startswith(shortname.lower()):
attr.value = value
return True
self.attributes.append(attributes.Attribute(attributes.singleAttribute(shortname)[0],value))
def __eq__(self, value): def __eq__(self, value):
if isinstance(value, Player): if isinstance(value, Player):
return self.name == value.name return self.name == value.name

View file

@ -14,7 +14,7 @@ class SkillContestParams(object):
defStats = [] defStats = []
override = None override = None
def __init__(self, atkAction:AtkAction, defAction:DefAction, situation:Situations): def __init__(self, atkAction:AtkAction, defAction:DefAction, situation:Situations=Situations.EvenStrength):
"""Determines which skills to test, and how strongly.""" """Determines which skills to test, and how strongly."""
if situation == Situations.EvenStrength: if situation == Situations.EvenStrength:
result = evenTable[atkAction.value][defAction.value] result = evenTable[atkAction.value][defAction.value]
@ -30,14 +30,14 @@ class SkillContestParams(object):
#Bool overrides, or [List of (stat,weight) , List of (stat,weight)] #Bool overrides, or [List of (stat,weight) , List of (stat,weight)]
evenTable = [ evenTable = [
#Steal #Poke #block lane #Body check #Force off puck #Pin to wall #Body block #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',20)]], [[('Dex',110),('Sti',35)],[('Sti',30)]], True, [[('Agi',11)],[('Spe',8)]], [[('Agi',20)],[('Str',8)]], [[('Agi',20)],[('Str',10)]], 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 [[[('Sti',100)],[('Sti',40)]], [[('Dex',70),('Sti',30)],[('Sti',100)]], True, [[('Str',80)],[('Str',100)]], [[('Spe',60),('Str',40)],[('Str',80)]], [[('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 [[[('Dex',75),('Sti',25)],[('Sti',50)]], [[('Dex',50),('Sti',30)],[('Sti',100)]], [[('Ref',20),('Sti',60)],[('Sti',100)]],[[('Dex',70)],[('Str',100)]], [[('Spe',60),('Str',40)],[('Str',120)]],True, [[('Sti',1)],[('Ref',3)]]], #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 [[[('Sti',100)],[('Sti',40)]], [[('Dex',70),('Sti',30)],[('Sti',100)]], True, [[('Agi',100)],[('Str',100)]],[[('Sti',100)],[('Str',100)]], [[('Sti',100)],[('Str',110)]], 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 [True, [[('Pas',70)],[('Sti',30)]], [[('Pas',80)],[('Ref',110)]], True, [[('Pas',100)],[('Str',80)]], [[('Pas',100)],[('Str',90)]], 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)],[('Ref',40),('Sti',30)]],[[('Pas',70)],[('Sti',30)]], [[('Pas',80)],[('Ref',110)]], True, [[('Pas',100)],[('Str',80)]], [[('Pas',100)],[('Str',100)]], [[('Pas',1)],[('Ref',3)]]], #Forward pass
[[[('Pas',100)],[('Int',20)]], True, True, True, True, [[('Sti',100)],[('Str',100)]], True], #Backward pass [[[('Pas',100)],[('Int',30)]], True, True, True, True, [[('Sti',100)],[('Str',100)]], True], #Backward pass
[True, True, [[('Wis',100)],[('Ref',100)]], True, True, True, True], #Dump/ice [True, True, [[('Wis',100)],[('Ref',20)]], True, True, True, True], #Dump/ice
[False, False, True, False, True, False, [[('Sho',100)],[('Ref',70),('Int',30)]]], #Slapshot [False, False, True, False, True, False, [[('Sho',120)],[('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 [[[('Dex',120)],[('Sti',40)]], [[('Dex',100)],[('Sti',40)]], [[('Sho',50)],[('Ref',25)]], [[('Dex',80)],[('Str',60)]], [[('Dex',100)],[('Str',90)]], [[('Dex',100)],[('Str',100)]], [[('Sho',1)],[('Ref',2),('Int',1)]]] #Wrist shot
] ]