Implemented basic action matrix, set weights to initial values
This commit is contained in:
parent
de46f85dfe
commit
cc0487469a
40
SimHoc.py
40
SimHoc.py
|
@ -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")):
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -120,3 +120,15 @@ def attributesFromName(name:str):
|
||||||
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
46
game.py
|
@ -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
88
hocTests.py
Normal 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("-------")
|
19
player.py
19
player.py
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
]
|
]
|
Loading…
Reference in a new issue