finished state machine - adding event log

This commit is contained in:
Sakimori 2024-08-14 20:53:52 -04:00
parent b1106f23d5
commit 775d114569

217
game.py
View file

@ -15,40 +15,72 @@ class Game(object):
def __init__(self, awayTeam:Team, homeTeam:Team, threes:bool=False):
if awayTeam is not None and homeTeam is not None:
#initial setup
self.away = awayTeam
self.home = homeTeam
self.awayScore = 0
self.homeScore = 0
self.awayZones = RinkGraph(edgeFilename=DEFAULTRINKFILENAME)
self.homeZones = RinkGraph(edgeFilename=DEFAULTRINKFILENAME)
self.currentZone:str = None
self.currentZone = 23 #home defensive center
self.faceoffSpot = FaceoffDot.Center
self.playStopped = True
self.gameOver = False
#determine skater/goalie linups
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.positionInPossession = None #use SkaterPosition enum
self.teamInPossession = None
self.loosePuck = False
self.skatersHome = self.home.roster[:5] #LW LD C RD RW, EA; use the SkaterPosition enum for indexing. Threes uses left winger, left defenseman, center.
self.skatersAway = self.away.roster[:5]
self.positionInPossession = SkaterPosition.C #use SkaterPosition enum
self.teamInPossession = self.home
self.loosePuck = True
self.penaltyBoxAway = []
self.penaltyBoxHome = []
self.pulledGoalieAway = False
self.pulledGoalieHome = False
self.period = 1
self.clock = 60*20 #Time remaining in period, given in seconds
self.startClock = 60*20 #Set clock to zhis value after each period
self.clock = self.startClock*1 #Time remaining in period, given in seconds
self.powerPlayEndTimes = []
self.eventLog = []
self.eventLogVerbose = []
#event affecting next event tracking
self.noDefender = False #breakaways
self.ineligibleDefenders = [] #prevent defender continuing to cause issues after being neutralized
self.space = False #give space-making passes and skates an advantage
self.loosePuckDefAdv = False #if attacker gets removed from play, defending team more likely to retrieve loose puck
def attackingSkater(self):
return self.skatersInPossession()[self.positionInPossession.value]
def defendingSkater(self):
"""Randomly selects a defensive skater to act as defender based on node"""
left = self.currentZone >= 30 #defensive LW or LD
if self.positionInPossession not in [SkaterPosition.LD, SkaterPosition.RD]:
#FW in possession
if left:
counts = [2,5,3,4,1]
else:
counts = [1,4,3,5,2]
else:
#D in possession
if left:
counts = [5,3,4,1,2]
else:
counts = [2,1,4,3,5]
return self.skatersDefending()[random.sample(self.allPositions(), 1, counts=counts)[0].value]
def defendingTeam(self):
if self.teamInPossession == self.home:
@ -65,9 +97,15 @@ class Game(object):
def skatersInPossession(self):
return self.skatersHome if self.homeAttacking() else self.skatersAway
def skatersDefending(self):
return self.skatersHome if not self.homeAttacking() else self.skatersAway
def defendingGoalie(self):
return self.goalieAway if self.homeAttacking() else self.goalieHome
def activeGraph(self):
return self.homeZones if self.homeAttacking() else self.awayZones
def clockToMinutesSeconds(self):
"""Returns a string MM:SS elapsed in period."""
@ -77,9 +115,16 @@ class Game(object):
if len(seconds) == 1:
seconds = f"0{seconds}"
return f"{minutes}:{seconds}"
def allPositions(self):
"""Get a list of all SkaterPosition enums."""
return [
SkaterPosition.LW, SkaterPosition.LD, SkaterPosition.C, SkaterPosition.RD, SkaterPosition.RW
]
def currentSituation(self):
"""Returns a Situations enum based on current skater counts."""
skatersH = self.lineSize + self.pulledGoalieHome - len(self.penaltyBoxHome)
skatersA = self.lineSize + self.pulledGoalieAway - len(self.penaltyBoxAway)
if self.teamInPossession == self.home:
@ -118,17 +163,164 @@ class Game(object):
def event(self):
"""Meat and potatoes. Everything that happens is a direct result of this being called."""
if self.playStopped: #need a faceoff
if self.clock < 0: #period/game over
if self.period >= 3 and self.homeScore != self.awayScore: #game over
self.gameOver = True
self.addEventLog(f"Final score: {self.away.shortname} {self.awayScore} - {self.homeScore} {self.home.shortname}")
else: #increment period, reset values
self.period += 1
self.clock = self.startClock*1
self.playStopped = True
self.faceoffSpot = FaceoffDot.Center
self.addEventLog(f"Period {self.period} begins!")
elif self.playStopped: #need a faceoff
self.teamInPossession = self.home if self.faceoffContest(self.skatersAway[SkaterPosition.C.value], self.skatersHome[SkaterPosition.C.value]) else self.away
self.positionInPossession = SkaterPosition(random.sample([0, 1, 1, 3, 3, 4],1)[0]) #wingers are less likely to recieve the faceoff than defensemen
self.playStopped = False
winningPlayer = self.skatersInPossession()[SkaterPosition.C.value]
receivingPlayer = self.skatersInPossession()[self.positionInPossession.value]
eventString = f"{self.clockToMinutesSeconds()} - {self.teamInPossession.shortname} {str(winningPlayer)} wins faceoff to {str(receivingPlayer)}"
self.eventLog.append(eventString)
self.eventLogVerbose.append(eventString)
self.addEventLog(eventString)
self.clock -= random.randint(2,5)
self.currentZone = self.zoneAfterFaceoff()
elif self.loosePuck: #who gets it
#first pick skaters chasing puck
defenders = self.currentZone % 10 <= 3
if defenders: #offensive team needs defensemen
validPosO = [SkaterPosition.LD, SkaterPosition.RD]
validPosD = [SkaterPosition.RW, SkaterPosition.LW]
else:
validPosO = [SkaterPosition.RW, SkaterPosition.LW]
validPosD = [SkaterPosition.LD, SkaterPosition.RD]
validPosO.append(SkaterPosition.C)
validPosD.append(SkaterPosition.C) #center can always chase :>
atkPos = random.sample(validPosO,1)[0]
defPos = random.sample(validPosD,1)[0]
atkChaser = self.skatersInPossession()[atkPos.value]
defChaser = self.skatersDefending()[defPos.value]
puckChaseSkills = [('Spe',50), ('Int',25), ('Agi',25)]
params = SkillContestParams(puckChaseSkills, puckChaseSkills)
result = self.skillContest(atkChaser, defChaser, params)
if result: #offensive skater gets it
self.positionInPossession = atkPos
else:
self.positionInPossession = defPos
self.changePossession()
self.addEventLog(f"{self.attackingSkater()} chases za puck down for {self.attackingTeam().shortname}")
self.clock -= random.randint(3,8)
else: #run za state machine
validActions = self.activeGraph().getAllReachableFrom(self.currentZone)
attacker = self.attackingSkater()
defender = self.defendingSkater()
while defender in self.ineligibleDefenders:
defender = self.defendingSkater() #reroll until eligible defender found
self.ineligibleDefenders = [] #clear ineligible defs
atkAction, nodeTarget = attacker.chooseAtkAction(validActions, defender)
defAction = defender.chooseDefAction()
scParams = SkillContestParams().actionCheck(atkAction, defAction, self.currentSituation())
result = self.skillContest(attacker, defender, scParams)
if result: #attacker succeeded
if atkAction in [AtkAction.ShotS, AtkAction.ShotW]: #shot
self.goalieCheck(atkAction, attacker) #shot goes zhrough
else:
self.currentZone = int(nodeTarget)
if atkAction in [AtkAction.PassB, AtkAction.PassF, AtkAction.PassS]: #pass
self.space = atkAction == AtkAction.PassB #backpasses create space for next action
#successful pass, determine new possession
allPos = self.allPositions()
allPos.remove(self.positionInPossession) #cant pass to yourself
if nodeTarget % 10 >= 6: #D wouldnt be behind net, ever
allPos.remove(SkaterPosition.RD)
allPos.remove(SkaterPosition.LD)
#emphasize pass side attacker
if nodeTarget < 30: #attacking left
if self.positionInPossession != SkaterPosition.LW:
allPos.append(SkaterPosition.LW)
else:
allPos.append(SkaterPosition.C)
else:
if self.positionInPossession != SkaterPosition.RW:
allPos.append(SkaterPosition.RW)
else:
allPos.append(SkaterPosition.C)
self.positionInPossession = random.sample(allPos, 1)[0]
self.ineligibleDefenders.append(defender)
self.clock -= random.randint(1,3) #passes are quick
elif atkAction in [AtkAction.SkateA, AtkAction.SkateF, AtkAction.SkateT, AtkAction.SkateB]:
if atkAction == AtkAction.SkateB:
self.space = True
else:
self.space = False
self.ineligibleDefenders.append(defender) #got around 'em
self.clock -= random.randint(3,6) #skating is slow
else: #dumped puck
raise NotImplementedError
else: #defender won
if defAction in [DefAction.Force, DefAction.Steal, DefAction.Body]: #actions zat grant defender puck at start of action
self.changePossession()
self.positionInPossession = SkaterPosition(self.skatersDefending().index(defender))
self.clock -= random.randint(4,6)
elif defAction in [DefAction.Pin, DefAction.Poke]: #actions zat cause loose puck at start of action
self.loosePuck = True
self.loosePuckDefAdv = defAction == DefAction.Poke
self.currentZone = int(random.sample(self.activeGraph().getAdjacentNodes(), 1)[0])
self.clock -= random.randint(2,4)
elif defAction == DefAction.BlockSlot: #grants defender puck at end of action
self.currentZone = nodeTarget
self.changePossession()
self.positionInPossession = SkaterPosition(self.skatersDefending().index(defender))
self.clock -= random.randint(1,3)
elif defAction == DefAction.BlockLn: #pass fuckery
self.passCheck(nodeTarget, defender, atkAction)
self.clock -= random.randint(3,6)
def passCheck(self, target, blockingDefender, passType):
if passType == AtkAction.PassS or random.random()*100 < normalDis(blockingDefender.getAttribute("Ref"),30,20,80): #stretch pass always intercepted, chance for interception based on defender's reflexes
self.currentZone = target
self.changePossession()
self.positionInPossession = SkaterPosition(self.skatersDefending().index(blockingDefender))
else: #loose puck!
if random.random() > 0.5:
self.currentZone = target
self.loosePuck = True
self.loosePuckDefAdv = True
def goalieCheck(self, shotType, shooter):
atkMult = self.activeGraph().shotDanger(self.currentZone)/100 #worse shots furzher out
if shotType == AtkAction.ShotS: #slapshot
shooterSkills = [('Sho', 80*atkMult), ('Str', 30*atkMult)]
goalieSkills = [('Ref', 90)]
else: #wristshot
shooterSkills = [('Sho', 50*atkMult), ('Dex', 30*atkMult)]
goalieSkills = [('Ref', 80),('Agi', 20)]
params = SkillContestParams(shooterSkills, goalieSkills)
result = self.skillContest(shooter,self.defendingGoalie(),params)
if result: #GOAL
if self.homeAttacking():
self.homeScore += 1
else:
self.awayScore += 1
self.playStopped = True
self.faceoffSpot = FaceoffDot.Center
elif random.randint(0,100) < normalDis(self.defendingGoalie().getAttribute('Dex'),75,0,100): #caught puck
self.saveMadeStop(shooter, shotType)
else: #blocked shot
self.loosePuck = True
self.loosePuckDefAdv = True
self.currentZone = random.sample(self.activeGraph().getAdjacentNodes(27),1)[0]
self.clock -= random.randint(2,6)
def changePossession(self):
self.teamInPossession = self.away if self.homeAttacking() else self.home
#gotta flip node
self.currentZone = 47 - self.currentZone
def saveMadeStop(self, shootingPlayer, shotType):
"""Stops play due to a save made by a goalie, and sets the faceoff dot to be used."""
@ -203,6 +395,11 @@ class Game(object):
def powerPlaySetup(self):
raise NotImplementedError()
def addEventLog(self, eventString, verbose:bool=False):
if not verbose:
self.eventLog.append(eventString)
self.eventLogVerbose.append(eventString)
def eventLogLength(self):
count = 0