forked from BurnySc2/python-sc2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplayer.py
182 lines (151 loc) · 6.76 KB
/
player.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
from pathlib import Path
from typing import Union, List
from .bot_ai import BotAI
from .data import AIBuild, Difficulty, PlayerType, Race
class AbstractPlayer:
def __init__(self, p_type, race=None, name=None, difficulty=None, ai_build=None, fullscreen=False):
assert isinstance(p_type, PlayerType), f"p_type is of type {type(p_type)}"
assert name is None or isinstance(name, str), f"name is of type {type(name)}"
self.name = name
self.type = p_type
self.fullscreen = fullscreen
if race is not None:
self.race = race
if p_type == PlayerType.Computer:
assert isinstance(difficulty, Difficulty), f"difficulty is of type {type(difficulty)}"
# Workaround, proto information does not carry ai_build info
# We cant set that in the Player classmethod
assert ai_build is None or isinstance(ai_build, AIBuild), f"ai_build is of type {type(ai_build)}"
self.difficulty = difficulty
self.ai_build = ai_build
elif p_type == PlayerType.Observer:
assert race is None
assert difficulty is None
assert ai_build is None
else:
assert isinstance(race, Race), f"race is of type {type(race)}"
assert difficulty is None
assert ai_build is None
@property
def needs_sc2(self):
return not isinstance(self, Computer)
class Human(AbstractPlayer):
def __init__(self, race, name=None, fullscreen=False):
super().__init__(PlayerType.Participant, race, name=name, fullscreen=fullscreen)
def __str__(self):
if self.name is not None:
return f"Human({self.race._name_}, name={self.name !r})"
else:
return f"Human({self.race._name_})"
class Bot(AbstractPlayer):
def __init__(self, race, ai, name=None, fullscreen=False):
"""
AI can be None if this player object is just used to inform the
server about player types.
"""
assert isinstance(ai, BotAI) or ai is None, f"ai is of type {type(ai)}, inherit BotAI from bot_ai.py"
super().__init__(PlayerType.Participant, race, name=name, fullscreen=fullscreen)
self.ai = ai
def __str__(self):
if self.name is not None:
return f"Bot {self.ai.__class__.__name__}({self.race._name_}), name={self.name !r})"
else:
return f"Bot {self.ai.__class__.__name__}({self.race._name_})"
class Computer(AbstractPlayer):
def __init__(self, race, difficulty=Difficulty.Easy, ai_build=AIBuild.RandomBuild):
super().__init__(PlayerType.Computer, race, difficulty=difficulty, ai_build=ai_build)
def __str__(self):
return f"Computer {self.difficulty._name_}({self.race._name_}, {self.ai_build.name})"
class Observer(AbstractPlayer):
def __init__(self):
super().__init__(PlayerType.Observer)
def __str__(self):
return f"Observer"
class Player(AbstractPlayer):
@classmethod
def from_proto(cls, proto):
if PlayerType(proto.type) == PlayerType.Observer:
return cls(proto.player_id, PlayerType(proto.type), None, None, None)
return cls(
proto.player_id,
PlayerType(proto.type),
Race(proto.race_requested),
Difficulty(proto.difficulty) if proto.HasField("difficulty") else None,
Race(proto.race_actual) if proto.HasField("race_actual") else None,
proto.player_name if proto.HasField("player_name") else None,
)
def __init__(self, player_id, p_type, requested_race, difficulty=None, actual_race=None, name=None, ai_build=None):
super().__init__(p_type, requested_race, difficulty=difficulty, name=name, ai_build=ai_build)
self.id: int = player_id
self.actual_race: Race = actual_race
class BotProcess(AbstractPlayer):
"""
Class for handling bots launched externally, including non-python bots.
Default parameters comply with sc2ai and aiarena ladders.
:param path: the executable file's path
:param launch_list: list of strings that launches the bot e.g. ["python", "run.py"] or ["run.exe"]
:param race: bot's race
:param name: bot's name
:param sc2port_arg: the accepted argument name for the port of the sc2 instance to listen to
:param hostaddress_arg: the accepted argument name for the address of the sc2 instance to listen to
:param match_arg: the accepted argument name for the starting port to generate a portconfig from
:param realtime_arg: the accepted argument name for specifying realtime
:param other_args: anything else that is needed
e.g. to call a bot capable of running on the bot ladders:
BotProcess(os.getcwd(), "python run.py", Race.Terran, "INnoVation")
"""
def __init__(
self,
path: Union[str, Path],
launch_list: List[str],
race: Race,
name=None,
sc2port_arg="--GamePort",
hostaddress_arg="--LadderServer",
match_arg="--StartPort",
realtime_arg="--RealTime",
other_args: str = None,
stdout: str = None,
):
self.race = race
self.type = PlayerType.Participant
self.name = name
assert Path(path).exists()
self.path = path
self.launch_list = launch_list
self.sc2port_arg = sc2port_arg
self.match_arg = match_arg
self.hostaddress_arg = hostaddress_arg
self.realtime_arg = realtime_arg
self.other_args = other_args
self.stdout = stdout
def __repr__(self):
if self.name is not None:
return f"Bot {self.name}({self.race.name} from {self.launch_list})"
else:
return f"Bot({self.race.name} from {self.launch_list})"
def cmd_line(
self, sc2port: Union[int, str], matchport: Union[int, str], hostaddress: str, realtime: bool = False
) -> List[str]:
"""
:param sc2port: the port that the launched sc2 instance listens to
:param matchport: some starting port that both bots use to generate identical portconfigs.
Note: This will not be sent if playing vs computer
:param hostaddress: the address the sc2 instances used
:param realtime: 1 or 0, indicating whether the match is played in realtime or not
:return: string that will be used to start the bot's process
"""
cmd_line = [
*self.launch_list,
self.sc2port_arg,
str(sc2port),
self.hostaddress_arg,
hostaddress,
]
if matchport is not None:
cmd_line.extend([self.match_arg, str(matchport)])
if self.other_args is not None:
cmd_line.append(self.other_args)
if realtime:
cmd_line.extend([self.realtime_arg])
return cmd_line