Skip to content

Commit 5f52c30

Browse files
committed
Add PlayerRole class and formation file readers to handle player roles
1 parent 750286a commit 5f52c30

File tree

4 files changed

+189
-123
lines changed

4 files changed

+189
-123
lines changed

src/strategy/formation_file.py

Lines changed: 7 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,9 @@
11
from scipy.spatial import Delaunay
22
from pyrusgeom.geom_2d import *
3-
from enum import Enum
43
from pyrusgeom.soccer_math import min_max
54
import logging
6-
from abc import ABC, abstractmethod
7-
import json
8-
9-
10-
class FormationType(Enum):
11-
Static = 's'
12-
DelaunayTriangulation2 = 'D'
13-
14-
class FormationIndexData:
15-
def __init__(self, ball, players):
16-
self._ball: list[float] = ball
17-
self._players: list[list[float]] = players
18-
19-
def ball(self) -> list[float]:
20-
return self._ball
21-
22-
def players(self) -> list[list[float]]:
23-
return self._players
24-
25-
class PlayerRole:
26-
def __init__(self, name, type, side, pair):
27-
self._name = name
28-
self._type = type
29-
self._side = side
30-
self._pair = pair
31-
32-
class IFormationFileReader(ABC):
33-
@abstractmethod
34-
def read_file(self, path) -> list[FormationIndexData]:
35-
pass
36-
37-
class OldStaticFormationFileReader(IFormationFileReader):
38-
def read_file(self, lines):
39-
players = {}
40-
roles = {}
41-
for i in range(len(lines)):
42-
if i == 0 or lines[i].startswith('#'):
43-
continue
44-
player = lines[i].split()
45-
players[int(player[0])] = ([float(player[2]), float(player[3])])
46-
roles[int(player[0])] = PlayerRole(player[1], None, None, None)
47-
48-
return [FormationIndexData(None, players)], roles
49-
50-
class OldDelaunayFormationFileReader(IFormationFileReader):
51-
def read_file(self, lines):
52-
roles = {}
53-
begin_roles = False
54-
for i in range(len(lines)):
55-
if lines[i].startswith('Begin Roles'):
56-
begin_roles = True
57-
continue
58-
if lines[i].startswith('End Roles'):
59-
break
60-
if begin_roles:
61-
player = lines[i].split()
62-
roles[int(player[0])] = PlayerRole(player[1], None, None, int(player[2]))
63-
indexes = []
64-
for i in range(len(lines)):
65-
if lines[i].find('Ball') >= 0:
66-
indexes.append(self.read_index(i, lines))
67-
i += 11
68-
return indexes, roles
69-
70-
def read_index(self, i, lines):
71-
ball = lines[i].split(' ')
72-
ball_x = float(ball[1])
73-
ball_y = float(ball[2])
74-
ball = [ball_x, ball_y]
75-
players = {}
76-
for j in range(1, 12):
77-
player = lines[i + j].split(' ')
78-
player_x = float(player[1])
79-
player_y = float(player[2])
80-
players[j] = ([player_x, player_y])
81-
return FormationIndexData(ball, players)
82-
83-
class JsonFormationFileReader(IFormationFileReader):
84-
def read_file(self, lines) -> list[FormationIndexData]:
85-
text = ''.join(lines)
86-
data = json.loads(text)
87-
roles = {}
88-
for role in data['role']:
89-
roles[role['number']] = PlayerRole(role['name'], role['type'], role['side'], role['pair'])
90-
indexes = []
91-
for index in data['data']:
92-
ball = [index['ball']['x'], index['ball']['y']]
93-
players = {}
94-
for i in range(1, 12):
95-
players[i] = [index[str(i)]['x'], index[str(i)]['y']]
96-
indexes.append(FormationIndexData(ball, players))
97-
return indexes, roles
98-
99-
@staticmethod
100-
def is_json(lines):
101-
return lines[0].find('{') >= 0
102-
103-
@staticmethod
104-
def get_method(lines):
105-
text = ''.join(lines)
106-
data = json.loads(text)
107-
if data['method'] == 'Static':
108-
return FormationType.Static
109-
return FormationType.DelaunayTriangulation2
110-
111-
112-
class FormationFileReaderFactory:
113-
def get_reader(self, lines) -> list[IFormationFileReader, FormationType]:
114-
if JsonFormationFileReader.is_json(lines):
115-
return JsonFormationFileReader(), JsonFormationFileReader.get_method(lines)
116-
if lines[0].find('Static') >= 0:
117-
return OldStaticFormationFileReader(), FormationType.Static
118-
return OldDelaunayFormationFileReader(), FormationType.DelaunayTriangulation2
119-
120-
def read_file(self, path) -> list[FormationIndexData]:
121-
file = open(path, 'r')
122-
lines = file.readlines()
123-
reader, formation_type = self.get_reader(lines)
124-
indexes, roles = reader.read_file(lines)
125-
return indexes, roles, formation_type
5+
from src.strategy.formation_file_reader import FormationFileReaderFactory, FormationType
6+
from src.strategy.player_role import PlayerRole
1267

1278
class FormationFile:
1289
def __init__(self, path, logger: logging.Logger):
@@ -133,11 +14,12 @@ def __init__(self, path, logger: logging.Logger):
13314
self._formation_type = FormationType.Static
13415
self._target_players = {}
13516
self._path = path
17+
self._roles: dict[int, PlayerRole] = {}
13618
self.read_file(path)
13719
self.calculate()
13820

13921
def read_file(self, path):
140-
indexes, roles, self._formation_type = FormationFileReaderFactory().read_file(path)
22+
indexes, self._roles, self._formation_type = FormationFileReaderFactory().read_file(path)
14123

14224
if self._formation_type == FormationType.Static:
14325
data = indexes[0]
@@ -202,6 +84,9 @@ def get_pos(self, unum):
20284

20385
def get_poses(self):
20486
return self._target_players
87+
88+
def get_role(self, unum) -> PlayerRole:
89+
return self._roles[unum]
20590

20691
def __repr__(self):
20792
return self._path
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
from pyrusgeom.geom_2d import *
2+
from enum import Enum
3+
from abc import ABC, abstractmethod
4+
import json
5+
from src.strategy.player_role import PlayerRole
6+
7+
8+
class FormationType(Enum):
9+
Static = 's'
10+
DelaunayTriangulation2 = 'D'
11+
12+
class FormationIndexData:
13+
def __init__(self, ball, players):
14+
self._ball: list[float] = ball
15+
self._players: list[list[float]] = players
16+
17+
def ball(self) -> list[float]:
18+
return self._ball
19+
20+
def players(self) -> list[list[float]]:
21+
return self._players
22+
23+
class IFormationFileReader(ABC):
24+
@abstractmethod
25+
def read_file(self, path) -> list[list[FormationIndexData], dict[int, PlayerRole]]:
26+
pass
27+
28+
class OldStaticFormationFileReader(IFormationFileReader):
29+
def read_file(self, lines: list[str]) -> list[list[FormationIndexData], dict[int, PlayerRole]]:
30+
players = {}
31+
roles = {}
32+
for i in range(len(lines)):
33+
if i == 0 or lines[i].startswith('#'):
34+
continue
35+
player = lines[i].split()
36+
players[int(player[0])] = ([float(player[2]), float(player[3])])
37+
roles[int(player[0])] = PlayerRole(player[1], None, None, None)
38+
39+
return [FormationIndexData(None, players)], roles
40+
41+
class OldDelaunayFormationFileReader(IFormationFileReader):
42+
def read_file(self, lines: list[str]) -> list[list[FormationIndexData], dict[int, PlayerRole]]:
43+
roles = {}
44+
begin_roles = False
45+
for i in range(len(lines)):
46+
if lines[i].startswith('Begin Roles'):
47+
begin_roles = True
48+
continue
49+
if lines[i].startswith('End Roles'):
50+
break
51+
if begin_roles:
52+
player = lines[i].split()
53+
roles[int(player[0])] = PlayerRole(player[1], None, None, int(player[2]))
54+
indexes = []
55+
for i in range(len(lines)):
56+
if lines[i].find('Ball') >= 0:
57+
indexes.append(self.read_index(i, lines))
58+
i += 11
59+
return indexes, roles
60+
61+
def read_index(self, i: int, lines: list[str]) -> FormationIndexData:
62+
ball = lines[i].split(' ')
63+
ball_x = float(ball[1])
64+
ball_y = float(ball[2])
65+
ball = [ball_x, ball_y]
66+
players = {}
67+
for j in range(1, 12):
68+
player = lines[i + j].split(' ')
69+
player_x = float(player[1])
70+
player_y = float(player[2])
71+
players[j] = ([player_x, player_y])
72+
return FormationIndexData(ball, players)
73+
74+
class JsonFormationFileReader(IFormationFileReader):
75+
def read_file(self, lines: list[str]) -> list[list[FormationIndexData], dict[int, PlayerRole]]:
76+
text = ''.join(lines)
77+
data = json.loads(text)
78+
roles = {}
79+
for role in data['role']:
80+
roles[role['number']] = PlayerRole(role['name'], role['type'], role['side'], role['pair'])
81+
indexes = []
82+
for index in data['data']:
83+
ball = [index['ball']['x'], index['ball']['y']]
84+
players = {}
85+
for i in range(1, 12):
86+
players[i] = [index[str(i)]['x'], index[str(i)]['y']]
87+
indexes.append(FormationIndexData(ball, players))
88+
return indexes, roles
89+
90+
@staticmethod
91+
def is_json(lines: list[str]) -> bool:
92+
return lines[0].find('{') >= 0
93+
94+
@staticmethod
95+
def get_method(lines: list[str]) -> FormationType:
96+
text = ''.join(lines)
97+
data = json.loads(text)
98+
if data['method'] == 'Static':
99+
return FormationType.Static
100+
return FormationType.DelaunayTriangulation2
101+
102+
103+
class FormationFileReaderFactory:
104+
def get_reader(self, lines) -> list[IFormationFileReader, FormationType]:
105+
if JsonFormationFileReader.is_json(lines):
106+
return JsonFormationFileReader(), JsonFormationFileReader.get_method(lines)
107+
if lines[0].find('Static') >= 0:
108+
return OldStaticFormationFileReader(), FormationType.Static
109+
return OldDelaunayFormationFileReader(), FormationType.DelaunayTriangulation2
110+
111+
def read_file(self, path) -> list[list[FormationIndexData], dict[int, PlayerRole], FormationType]:
112+
file = open(path, 'r')
113+
lines = file.readlines()
114+
reader, formation_type = self.get_reader(lines)
115+
indexes, roles = reader.read_file(lines)
116+
return indexes, roles, formation_type

src/strategy/formation_strategy.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from src.interfaces.IPositionStrategy import IPositionStrategy
22
from src.strategy.formation_file import *
33
from src.interfaces.IAgent import IAgent
4+
from src.strategy.player_role import PlayerRole, RoleName, RoleType, RoleSide
45
from enum import Enum
56
from pyrusgeom.soccer_math import *
67
from service_pb2 import *
78
import logging
89

910

11+
1012
class Situation(Enum):
1113
OurSetPlay_Situation = 0,
1214
OppSetPlay_Situation = 1,
@@ -112,9 +114,24 @@ def update(self, agent: IAgent):
112114

113115
logger.debug(f'{self._poses=}')
114116

115-
def get_position(self, uniform_number):
117+
def get_position(self, uniform_number) -> Vector2D:
116118
return self._poses[uniform_number]
117119

120+
def get_role_name(self, uniform_number) -> int:
121+
return self.current_formation_file.get_role(uniform_number).name
122+
123+
def get_role_type(self, uniform_number) -> int:
124+
return self.current_formation_file.get_role(uniform_number).type
125+
126+
def get_role_side(self, uniform_number) -> int:
127+
return self.current_formation_file.get_role(uniform_number).side
128+
129+
def get_role_pair(self, uniform_number) -> int:
130+
return self.current_formation_file.get_role(uniform_number).pair
131+
132+
def get_role(self, uniform_number) -> PlayerRole:
133+
return self.current_formation_file.get_role(uniform_number)
134+
118135
def get_offside_line(self):
119136
home_poses_x = [pos.x() for pos in self._poses.values()]
120137
home_poses_x.sort()

src/strategy/player_role.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from enum import Enum
2+
3+
4+
class RoleName(Enum):
5+
Goalie = "Goalie"
6+
CenterBack = "CenterBack"
7+
SideBack = "SideBack"
8+
DefensiveHalf = "DefensiveHalf"
9+
OffensiveHalf = "OffensiveHalf"
10+
SideForward = "SideForward"
11+
CenterForward = "CenterForward"
12+
Unknown = "Unknown"
13+
14+
class RoleType(Enum):
15+
G = "G"
16+
DF = "DF"
17+
MF = "MF"
18+
FW = "FW"
19+
Unknown = "Unknown"
20+
21+
class RoleSide(Enum):
22+
L = "L"
23+
R = "R"
24+
C = "C"
25+
Unknown = "Unknown"
26+
27+
class PlayerRole:
28+
def __init__(self, name: str, type: str, side: str, pair: int):
29+
self._name: RoleName = RoleName(name) if name in RoleName.__members__ else RoleName.Unknown
30+
self._type: RoleType = RoleType(type) if type in RoleType.__members__ else RoleType.Unknown
31+
self._side: RoleSide = RoleSide(side) if side in RoleSide.__members__ else RoleSide.Unknown
32+
self._pair: int = pair
33+
34+
@property
35+
def name(self) -> RoleName:
36+
return self._name
37+
38+
@property
39+
def type(self) -> RoleType:
40+
return self._type
41+
42+
@property
43+
def side(self) -> RoleSide:
44+
return self._side
45+
46+
@property
47+
def pair(self) -> int:
48+
return self._pair

0 commit comments

Comments
 (0)