-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMyBot.py
390 lines (355 loc) · 16.2 KB
/
MyBot.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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# Let's start by importing the Halite Starter Kit so we can interface with the Halite engine
import hlt
# Then let's import the logging module so we can print out information
import logging
# GAME START
# Here we define the bot's name as Settler and initialize the game, including communication with the Halite engine.
game = hlt.Game("CarefulAttackerV3")
# Then we print our start message to the logs
logging.info("Starting my Settler bot!")
import time
from quad_tree_map import QuadTree
from cocos.rect import Rect
def get_planets_as_rects(all_planets):
'''
returns (x1, y1, x2, y2)
'''
rects = []
for planet in all_planets:
rects.append(
Rect(planet.x - planet.radius,
planet.y - planet.radius,
planet.radius * 2,
planet.radius * 2
))
return rects
all_planets = game.map.all_planets()
blockers = get_planets_as_rects(all_planets)
quad_tree = QuadTree(0, 0, game.map.width, game.map.height, blockers, 0.5)
ship_trips = {}
class ProtectMission(object):
def __init__(self, planet_to_protect, ship_id):
self.planet_to_protect = planet_to_protect
self.ship_id = ship_id
ship_trips[self.ship_id] = ship.get_ship_trip(quad_tree, self.planet_to_protect)
def get_command(self, game_map, ship, enemy_planet=None, missions=None):
return ship.navigate2(
ship_trips,
quad_tree,
ship.closest_point_to(self.planet_to_protect),
game_map,
speed=int(hlt.constants.MAX_SPEED),
ignore_ships=True)
class ColonizeMission(object):
def __init__(self, planet_to_attack, ship_id):
self.planet_to_attack = planet_to_attack
self.ship_id = ship_id
ship_trips[self.ship_id] = ship.get_ship_trip(quad_tree, self.planet_to_attack)
def get_command(self, game_map, ship, enemy_planet=None, missions=None):
docked_ships = self.planet_to_attack.all_docked_ships()
logging.info('CAN DOCK {}'.format(ship.can_dock(self.planet_to_attack)))
if docked_ships:
docked_ship = docked_ships[0]
else:
docked_ship = self.planet_to_attack
if not docked_ships and ship.can_dock(self.planet_to_attack):
return ship.dock(self.planet_to_attack)
return ship.navigate2(
ship_trips,
quad_tree,
ship.closest_point_to(self.planet_to_attack),
game_map,
speed=int(hlt.constants.MAX_SPEED),
ignore_ships=False)
'''
class DockMission(object):
def __init__(self, planet_to_attack, ship_id):
self.planet_to_attack = planet_to_attack
self.ship_id = ship_id
def get_command(self, game_map, ship, enemy_planets=None, missions=None):
if self.planet_to_attack.owner and self.planet_to_attack.owner.id != game_map.my_id:
logging.info('Docking mission aborted')
return attack_mission_command(enemy_planets, ship, missions)
if self.planet_to_attack.is_full():
logging.info('Docking mission aborted (planet full)')
return attack_mission_command(enemy_planets, ship, missions)
elif ship.can_dock(self.planet_to_attack):
logging.info('Docking mission successful')
return ship.dock(self.planet_to_attack)
else:
logging.info('Docking navigating to planet')
return ship.navigate(
ship.closest_point_to(self.planet_to_attack),
game_map,
speed=int(hlt.constants.MAX_SPEED),
ignore_ships=False)
'''
class ShipAttackMission(object):
def __init__(self, planet_to_attack, ship_id):
logging.info('Attack mission ({}) -> {}'.format(ship_id, planet_to_attack))
self.planet_to_attack = planet_to_attack
self.player_to_attack = planet_to_attack.owner.id
self.ship_id = ship_id
ship_trips[self.ship_id] = ship.get_ship_trip(quad_tree, self.planet_to_attack)
def get_command(self, game_map, ship, enemy_ships, my_planets, missions=None):
self.planet_to_attack = game_map.get_player(self.planet_to_attack.owner.id).get_ship(self.planet_to_attack.id)
if not self.planet_to_attack:
self.planet_to_attack = get_closest_enemy_ship(enemy_ships, ship)
self.trip = None
return ship.navigate2(
ship_trips,
quad_tree,
ship.closest_point_to(self.planet_to_attack),
game_map,
speed=int(hlt.constants.MAX_SPEED),
ignore_ships=False)
def get_closest_enemy_ship(enemy_ships, ship):
if not enemy_ships:
return None
closest_enemy_ship = min(
enemy_ships,
key=lambda x: ship.calculate_distance_between(x))
return closest_enemy_ship
def get_furthest_planet(planets, ship):
if not planets:
return None
furthest_planet = min(
planets,
key=lambda x: ship.calculate_distance_between(x))
return furthest_planet
class AttackMission(object):
def __init__(self, planet_to_attack, ship_id):
logging.info('Attack mission ({}) -> {}'.format(ship_id, planet_to_attack))
self.planet_to_attack = planet_to_attack
self.ship_id = ship_id
ship_trips[self.ship_id] = ship.get_ship_trip(quad_tree, self.planet_to_attack)
def get_command(self, game_map, ship, enemy_planets, missions=None):
try:
self.planet_to_attack = game_map.get_planet(self.planet_to_attack.id)
except:
self.planet_to_attack = get_closest_enemy_planet(enemy_planets, ship)
if not self.planet_to_attack:
return None
if not self.planet_to_attack:
self.planet_to_attack = get_closest_enemy_planet(enemy_planets, ship)
ship_trips[self.ship_id] = ship.get_ship_trip(quad_tree, self.planet_to_attack)
# if i am owner
if self.planet_to_attack.owner and self.planet_to_attack.owner.id == game_map.my_id:
# if its full
if self.planet_to_attack.is_full():
logging.info('{} Attacker finding different target'.format(ship.id))
closest_enemy_planet = get_closest_enemy_planet(enemy_planets, ship)
if closest_enemy_planet:
self.planet_to_attack = closest_enemy_planet
ship_trips[self.ship_id] = ship.get_ship_trip(quad_tree, self.planet_to_attack)
return ship.navigate2(
ship_trips,
quad_tree,
ship.closest_point_to(self.planet_to_attack),
game_map,
speed=int(hlt.constants.MAX_SPEED),
ignore_ships=False)
else:
logging.info("NO closest enemy planet")
# if its not full
elif ship.can_dock(self.planet_to_attack):
logging.info('{} Attacker docking to our planet'.format(ship.id))
return ship.dock(self.planet_to_attack)
else:
self.planet_to_attack = get_closest_enemy_planet(enemy_planets, ship)
ship_trips[self.ship_id] = ship.get_ship_trip(quad_tree, self.planet_to_attack)
if self.planet_to_attack:
return ship.navigate2(
ship_trips,
quad_tree,
ship.closest_point_to(self.planet_to_attack),
game_map,
speed=int(hlt.constants.MAX_SPEED),
ignore_ships=False)
# if its unoccupied
elif not self.planet_to_attack.owner:
if ship.can_dock(self.planet_to_attack):
logging.info('{} Attacker docking to non-taken planet'.format(ship.id))
return ship.dock(self.planet_to_attack)
else:
logging.info('{} Attacker navigating to dock position'.format(ship.id))
return ship.navigate2(
ship_trips,
quad_tree,
ship.closest_point_to(self.planet_to_attack),
game_map,
speed=int(hlt.constants.MAX_SPEED),
ignore_ships=False)
else:
docked_ships = [ship for ship in self.planet_to_attack.all_docked_ships() if ship.owner.id != game_map.my_id]
# if it has docked ships
if docked_ships:
docked_ship = docked_ships[0]
logging.info('{} Attacker navigating to docked ship'.format(ship.id))
logging.info('{} - {} - {}'.format(docked_ship, ship, ship.closest_point_to(docked_ship)))
return ship.navigate2(
ship_trips,
quad_tree,
ship.closest_point_to(docked_ship),
game_map,
speed=int(hlt.constants.MAX_SPEED),
ignore_ships=False)
else:
if ship.can_dock(self.planet_to_attack):
logging.info('Attacker docking to destroyed planet')
return ship.dock(self.planet_to_attack)
else:
self.planet_to_attack = get_closest_enemy_planet(enemy_planets, ship)
ship_trips[self.ship_id] = ship.get_ship_trip(quad_tree, self.planet_to_attack)
logging.info('Attacker navigating to planet with ships')
return ship.navigate2(
ship_trips,
quad_tree,
ship.closest_point_to(self.planet_to_attack),
game_map,
speed=int(hlt.constants.MAX_SPEED),
ignore_ships=False)
def get_closest_unfilled_planet(planets, element):
unfilled_planets = [p for p in planets if not p.is_full()]
if unfilled_planets:
closest_unfilled = min(
unfilled_planets,
key=lambda x: element.calculate_distance_between(x))
else:
closest_unfilled = None
return closest_unfilled
def get_closest_enemy_planet(enemy_planets, element):
if not enemy_planets:
return None
closest_enemy_planet = min(
enemy_planets,
key=lambda x: element.calculate_distance_between(x))
return closest_enemy_planet
def attack_mission_command(enemy_planets, ship, missions):
closest_enemy_planet = get_closest_enemy_planet(enemy_planets, ship)
if closest_enemy_planet:
missions[ship.id] = AttackMission(
closest_enemy_planet, ship.id)
ship_command = missions[ship.id].get_command(
game_map, ship, enemy_planets, missions)
return ship_command
else:
logging.info("NO closest enemy planet; cant give mission")
return None
def ship_attack_mission_command(enemy_ships, ship, missions, my_planets):
closest_enemy_ship = get_closest_enemy_ship(enemy_ships, ship)
furthest_planet = get_furthest_planet(my_planets, ship)
if (closest_enemy_ship and furthest_planet and
ship.calculate_distance_between(furthest_planet) + 4 > ship.calculate_distance_between(closest_enemy_ship)):
missions[ship.id] = ShipAttackMission(
closest_enemy_ship, ship.id)
ship_command = missions[ship.id].get_command(
game_map, ship, enemy_ships, missions)
return ship_command
else:
logging.info("NO closest enemy ship; cant give mission")
return None
import pickle
game_map = game.map
with open('game_map.pkl', 'wb') as output:
pickle.dump(game.map, output)
turn = 0
missions = {}
while True:
turn += 1
logging.info(ship_trips)
# TURN START
# Update the map for the new turn and get the latest version
game_map = game.update_map()
start = time.time()
my_id = game_map.my_id
command_queue = []
all_me = game_map.get_me()
total_ships = all_me.all_ships()
ship_ids = [ship.id for ship in total_ships]
ids_to_remove = []
for ship_id in ship_trips.keys():
if ship_id not in ship_ids:
ids_to_remove.append(ship_id)
for i in ids_to_remove:
del ship_trips[i]
planets_to_be_attacked = []
planets = game_map.all_planets()
my_planets = [p for p in planets if p.owner and p.owner.id == my_id]
enemy_planets = [p for p in planets if not p.owner or p.owner.id != my_id]
non_taken_planets = [p for p in planets if not p.owner]
all_enemy_ships = [ship for ship in game_map._all_ships() if ship.owner.id != my_id]
if turn <= 2:
taken_enemy_planets = [p for p in planets if p.owner and p.owner.id != my_id]
closest_enemy_planets = planets
ships_to_expand = total_ships[:3]
for i, ship in enumerate(ships_to_expand):
closest_enemy_planets = sorted(
closest_enemy_planets,
key=lambda x: ship.calculate_distance_between(x))
closest_enemy_planet = closest_enemy_planets[0]
# closest_enemy_planets.remove(closest_enemy_planet)
missions[ship.id] = ColonizeMission(
closest_enemy_planet, ship.id)
ship_command = missions[ship.id].get_command(
game_map, ship)
if ship_command:
command_queue.append(ship_command)
# if len(total_ships) > 2:
# if taken_enemy_planets:
# logging.info(enemy_planets)
# missions[total_ships[2].id] = attack_mission_command(enemy_planets, ship, missions)
# else:
# closest_enemy_ship = min(
# all_enemy_ships,
# key=lambda x: ship.calculate_distance_between(x)
# )
# logging.info(closest_enemy_ship)
# missions[total_ships[2].id] = ShipAttackMission(
# closest_enemy_ship, ship.id)
# ship_command = missions[ship.id].get_command(
# game_map, ship, all_enemy_ships, missions)
game.send_command_queue(command_queue)
else:
for ship in total_ships:
ship_command = None
end_time = time.time() - start
if end_time >= 1.25:
game.send_command_queue(command_queue)
break
# If the ship is docked
if ship.docking_status == ship.DockingStatus.DOCKED or ship.docking_status == ship.DockingStatus.DOCKING:
# Skip this ship
continue
if ship.id in missions.keys():
if enemy_planets:
if isinstance(missions[ship.id], ShipAttackMission):
ship_command = missions[ship.id].get_command(
game_map, ship, all_enemy_ships, my_planets, missions)
else: # AttackMission ; ColonizeMission
ship_command = missions[ship.id].get_command(
game_map, ship, enemy_planets, missions)
elif ship.id % 2 == 0 or turn < 50:
closest_unfilled = get_closest_unfilled_planet(my_planets, ship)
if closest_unfilled:
missions[ship.id] = AttackMission(
closest_unfilled, ship.id)
ship_command = missions[ship.id].get_command(
game_map, ship, enemy_planets, missions)
elif non_taken_planets:
ship_command = attack_mission_command(non_taken_planets, ship, missions)
else:
ship_command = attack_mission_command(enemy_planets, ship, missions)
elif ship.id % 3 == 0:
ship_command = ship_attack_mission_command(all_enemy_ships, ship, missions, my_planets)
elif ship.id not in missions.keys():
ship_command = attack_mission_command(enemy_planets, ship, missions)
logging.info("{} {}".format(ship_command, ship))
if ship_command:
command_queue.append(ship_command)
# Send our set of commands to the Halite engine for this turn
else:
logging.info("NO ship command")
game.send_command_queue(command_queue)
# TURN END
# GAME END