implemented default rink graph

This commit is contained in:
Sakimori 2022-01-11 12:29:18 -05:00
parent cc0487469a
commit 1c6a5400a5
8 changed files with 1518 additions and 8 deletions

1260
Rinks/Graphs/defaultedges.nx Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
LDLW
LDHW
LDNW
LONW
LOHW
LOLW
LDC
LDL
LDH
LDN
LON
LOH
LOL
LOC
DT
DLS
DHS
CD
CO
OHS
OLS
OT
RDC
RDL
RDH
RDN
RON
ROH
ROL
ROC
RDLW
RDHW
RDNW
RONW
ROHW
ROLW

View file

@ -0,0 +1,36 @@
01
02
03
04
05
06
10
11
12
13
14
15
16
17
20
21
22
23
24
25
26
27
30
31
32
33
34
35
36
37
41
42
43
44
45
46

View file

@ -1,6 +1,10 @@
import os, player, tweepy, twitHandler, time, skillContests, random
import os, player, tweepy, twitHandler, time, skillContests, random, itertools
from attributes import normalDis
from hocTests import AttributeTest
from hocUtils import RinkGraph
if __name__ == "__main__":
#for name in ["Vivi", "Artemis", "Laika", "Sharks", "Dragons", "Melua", "Sabriina", "Jorts (Buttered)", "Jorts (Unbuttered)"]:
@ -10,8 +14,9 @@ if __name__ == "__main__":
# print(atr)
# print("----------")
test = AttributeTest()
test.allTests()
g = RinkGraph(edgeFilename="defaultedges.nx")
g.getNodeDebug('25')
#twitter = twitHandler.TwitHandler()
#if os.path.exists(os.path.join("Data", "lastID.twt")):

View file

@ -29,6 +29,9 @@
<Compile Include="hocTests.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="hocUtils.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="skillContests.py">
<SubType>Code</SubType>
</Compile>
@ -59,6 +62,9 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Data\" />
<Folder Include="Rinks\" />
<Folder Include="Rinks\Backgrounds\" />
<Folder Include="Rinks\Graphs\" />
</ItemGroup>
<ItemGroup>
<Content Include="Data\s options.txt" />

43
game.py
View file

@ -1,8 +1,14 @@
import random, team, player
import random, team, player, os
from team import Team
from player import Player, AtkAction, DefAction
from skillContests import SkillContestParams, Situations
from attributes import normalDis
from hocUtils import RinkGraph
from enum import Enum
import networkx as nx
RINKDIRPATH = os.path.join("Rinks","Graphs")
DEFAULTRINKFILENAME = "defaultedges.nx"
class Game(object):
"""A game of hockey!"""
@ -12,6 +18,11 @@ class Game(object):
self.away = awayTeam
self.home = homeTeam
self.awayZones = RinkGraph(edgeFilename=DEFAULTRINKFILENAME)
self.homeZones = RinkGraph(edgeFilename=DEFAULTRINKFILENAME)
self.currentZone = None
self.faceoff = FaceoffDot.Center
self.lineSize = 5
if len(awayTeam.roster) != 10 or len(homeTeam.roster) != 10 or threes:
self.lineSize = 3
@ -19,10 +30,10 @@ class Game(object):
self.goalieHome = self.home.chooseGoalie()
self.goalieAway = self.away.chooseGoalie()
self.playerInPossession = None
self.positionInPossession = None
self.teamInPossession = None
self.skatersHome = [] #LW LD C RD RW
self.skatersHome = [] #LW LD C RD RW, use the SkaterPosition enum for indexing.
self.skatersAway = []
self.penaltyBoxAway = []
@ -41,6 +52,10 @@ class Game(object):
else:
return self.home
def attackingTeam(self):
"""Alias for teamInPossession, to match defendingTeam()"""
return teamInPossession
def homeAttacking(self):
return teamInPossession == self.home
@ -66,4 +81,24 @@ class Game(object):
atkRoll = normalDis(atkValue, atkValue/2, 0)
defRoll = normalDis(defValue, defValue/2, 0)
return atkRoll-defRoll > 0
return atkRoll-defRoll > 0
class FaceoffDot(Enum):
"""All orientations are given from the perspective of the defending team."""
AwayZoneLeft = -4
AwayZoneRight = -3
AwayNeutralLeft = -2
AwayNeutralRight = -1
Center = 0
HomeNeutralRight = 1
HomeNeutralLeft = 2
HomeZoneRight = 3
HomeZoneLeft = 4
class SkaterPosition(Enum):
"""Allows easy indexing to the active skaters lists for each team."""
LW = 0
LD = 1
C = 2
RD = 3
RW = 4

View file

@ -1,4 +1,5 @@
import skillContests, player, team, game, attributes
import skillContests, player, team, game, attributes, os
class AttributeTest(object):
def __init__(self):

131
hocUtils.py Normal file
View file

@ -0,0 +1,131 @@
import os, itertools
import networkx as nx
class RinkGraph(object):
"""Base class for a graph of nodes representing a hockey rink. Description of nodes found in design documents."""
G = nx.empty_graph()
def __init__(self, nodeFilename:str=None, edgeFilename:str=None):
if nodeFilename is not None:
with open(os.path.join("Rinks","Graphs",nodeFilename)) as nodeFile:
nodeListS = [node.strip() for node in nodeFile.readlines()]
self.G = nx.empty_graph(create_using=nx.DiGraph)
edges = itertools.permutations(nodeListS,2)
self.G.add_edges_from(edges)
elif edgeFilename is not None:
self.G = nx.readwrite.edgelist.read_edgelist(os.path.join("Rinks","Graphs", edgeFilename), create_using=nx.DiGraph)
def writeGraph(self, writeFilename:str):
nx.readwrite.edgelist.write_edgelist(self.G, os.path.join("Rinks","Graphs",writeFilename))
def nameToZones(self, name:str):
column = int(name[1])
row = int(name[0])
def adjacencyRule(self):
for node1, node2 in list(self.G.edges):
self.G.edges[node1, node2]['actions'] = []
if (abs(int(node1[0]) - int(node2[0])) <= 1 and abs(int(node1[1]) - int(node2[1])) <= 0) or (abs(int(node1[0]) - int(node2[0])) <= 0 and abs(int(node1[1]) - int(node2[1])) <= 1):
self.G.edges[node1, node2]['adjacent'] = 1
elif node1 == "01" and node2 == "10" or node1 == "10" and node2 == "01" or node1 == "06" and node2 == "17" or node1 == "17" and node2 == "06":
self.G.edges[node1, node2]['adjacent'] = 1
elif node1 == "41" and node2 == "30" or node1 == "30" and node2 == "41" or node1 == "46" and node2 == "37" or node1 == "37" and node2 == "46":
self.G.edges[node1, node2]['adjacent'] = 1
else:
self.G.edges[node1, node2]['adjacent'] = 0
def backSkateRule(self):
for node1, node2 in list(self.G.edges):
if node1[1] in ['1','2','3','4','6']:
if abs(int(node1[0]) - int(node2[0])) <= 1 and int(node2[1]) == int(node1[1])-1:
self.G.edges[node1, node2]['actions'].append('SkateB')
def forwardSkateRule(self):
for node1, node2 in list(self.G.edges):
access = False
if node1[1] in ['0','1','2']:
access = True
distance = 2
elif node1[1] in ['3','4']:
access = True
distance = 1
if access and node2[1] >= node1[1]:
atkDistance = int(node2[1]) - int(node1[1])
horzDistance = abs(int(node2[0]) - int(node1[0]))
if (atkDistance+horzDistance) <= distance:
self.G.edges[node1, node2]['actions'].append('SkateF')
def throughSkateRule(self):
for node1, node2 in list(self.G.edges):
if node1[1] in ['5','6','7']:
if node1[0] not in ['0','4']:
if node2 == '26':
self.G.edges[node1, node2]['actions'].append('SkateT')
else:
if node1[0] == '0' and node2 == '16' or node1[0] == '4' and node2 == '36':
self.G.edges[node1, node2]['actions'].append('SkateT')
def aroundSkateRule(self):
for node1, node2 in list(self.G.edges):
if node1[1] in ['5','6'] and node1[0] != '2' and node2 == '27':
self.appendAction(node1, node2, 'SkateA')
def stretchPassRule(self):
for node1, node2 in list(self.G.edges):
if node1[1] in ['1','2'] and node2[1] == '5':
self.appendAction(node1, node2, "PassS")
def forwardPassRule(self):
for node1, node2 in list(self.G.edges):
if node1[1] in ['1','2', '3', '5', '6']:
if int(node2[1]) >= int(node1[1]) and int(node2[1]) - int(node1[1]) <= 2:
self.appendAction(node1, node2, 'PassF')
elif node1[1] == '4' and node2[1] in ['4','5']:
self.appendAction(node1, node2, 'PassF')
elif node1[1] == '7' and node2[1] in ['5','6']:
self.appendAction(node1, node2, 'PassF')
def backwardPassRule(self):
for node1, node2 in list(self.G.edges):
if node1[1] not in ['0','5','7']: #back pass from column 7 is treated as forward pass for difficulty
if int(node1[1]) - int(node2[1]) == 1 and node1[0] == node2[0]:
self.appendAction(node1, node2, 'PassB')
def appendAction(self, node1, node2, actionString):
self.G.edges[node1, node2]['actions'].append(actionString)
def allRules(self):
self.adjacencyRule()
self.backSkateRule()
self.forwardSkateRule()
self.throughSkateRule()
self.aroundSkateRule()
self.stretchPassRule()
self.forwardPassRule()
self.backwardPassRule()
self.writeGraph("defaultedges.nx")
def getAllReachableFrom(self, nodeName):
"""Returns a dictionary where the keys are all reachable nodes, and the values are the list of actions that can reach the key node."""
if isinstance(nodeName, int):
nodeName = str(nodeName)
allConnected = dict(self.G[nodeName])
possibleReachable = {}
for otherNode in allConnected:
if allConnected[otherNode]['actions'] != []:
possibleReachable[otherNode] = allConnected[otherNode]['actions']
return possibleReachable
def getAdjacentNodes(self, nodeName):
"""Returns a list of all nodes marked as adjacent by the current map."""
if isinstance(nodeName, int):
nodeName = str(nodeName)
allConnected = dict(self.G[nodeName])
adjacents = []
for otherNodeName, nodeDic in allConnected.items():
if nodeDic['adjacent']:
adjacents.append(otherNodeName)
return adjacents