Skip to content

Commit 5236720

Browse files
Merge branch 'main' into public-release
2 parents 067f6ef + e74bdcf commit 5236720

File tree

8 files changed

+105
-16
lines changed

8 files changed

+105
-16
lines changed

battlecode25/engine/container/runner.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,13 @@ def instrument_call(self):
167167
self.bytecode -= 1
168168
if self.bytecode <= 0:
169169
self.error_method(f'Ran out of bytecode. Remaining bytecode: {self.bytecode}')
170-
self.thread.wait()
170+
self.pause()
171171

172172
def multinstrument_call(self, n):
173173
self.bytecode -= n
174174
if self.bytecode <= 0:
175175
self.error_method(f'Ran out of bytecode. Remaining bytecode: {self.bytecode}')
176-
self.thread.wait()
176+
self.pause()
177177

178178
def import_call(self, name, globals=None, locals=None, fromlist=(), level=0, caller='robot'):
179179
if not isinstance(name, str) or not (isinstance(fromlist, tuple) or fromlist is None):
@@ -259,6 +259,9 @@ def run(self):
259259
self.thread.finished_event.wait()
260260
self.thread.finished_event.clear()
261261

262+
def pause(self):
263+
self.thread.wait()
264+
262265
def kill(self):
263266
self.thread.kill()
264267
self.thread.join()

battlecode25/engine/game/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ class GameConstants:
2828

2929
TOWER_BYTECODE_LIMIT = 20000
3030

31+
# The maximum execution time that can be spent on a team in one match. If the total time spent executing a team's bots
32+
# exceeds this limit, the team will immediately lose the game. Execution time is measured in ns.
33+
MAX_TEAM_EXECUTION_TIME = 1200000000000
34+
3135
# The maximum length of indicator strings that a player can associate with a robot.
3236
INDICATOR_STRING_MAX_LENGTH = 64
3337

battlecode25/engine/game/game.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import math
2+
import time
13
import random
24
from enum import Enum
35
from .robot import Robot
@@ -19,8 +21,6 @@
1921
from typing import Generator
2022
from .message import Message
2123

22-
import math
23-
2424
class Game:
2525

2626
def __init__(self, code, initial_map: InitialMap, game_fb: GameFB, game_args):
@@ -66,12 +66,20 @@ def __init__(self, code, initial_map: InitialMap, game_fb: GameFB, game_args):
6666
self.update_defense_towers(robot.team, new_type)
6767

6868
def run_round(self):
69+
def run_turn(robot: Robot):
70+
start_time = time.time_ns()
71+
robot.turn()
72+
run_time = time.time_ns() - start_time
73+
self.team_info.add_execution_time(robot.team, run_time)
74+
if self.team_info.get_execution_time(robot.team) >= GameConstants.MAX_TEAM_EXECUTION_TIME:
75+
self.resign(robot.team)
76+
6977
if self.running:
7078
self.round += 1
7179
self.game_fb.start_round(self.round)
7280
self.update_resource_patterns()
7381
self.each_robot(lambda robot: robot.process_beginning_of_round())
74-
self.each_robot_update(lambda robot: robot.turn())
82+
self.each_robot_update(run_turn)
7583
self.serialize_team_info()
7684
self.team_info.process_end_of_round()
7785
self.game_fb.end_round()
@@ -250,6 +258,9 @@ def set_winner_arbitrary(self):
250258
self.set_winner(Team.A if random.random() < 0.5 else Team.B, DominationFactor.WON_BY_DUBIOUS_REASONS)
251259
return True
252260

261+
def resign(self, team: Team):
262+
self.set_winner(team.opponent(), DominationFactor.RESIGNATION)
263+
253264
def run_tiebreakers(self):
254265
if self.set_winner_if_more_area(): return
255266
if self.set_winner_if_more_allied_towers(): return
@@ -584,12 +595,17 @@ def create_methods(self, rc: RobotController):
584595
'upgrade_tower': rc.upgrade_tower,
585596
'resign': rc.resign,
586597
'disintegrate': rc.disintegrate,
598+
'yield_turn': (rc.yield_turn, 0),
599+
'get_bytecode_num': (rc.get_bytecode_num, 0),
600+
'get_bytecodes_left': (rc.get_bytecodes_left, 0),
601+
'get_time_elapsed': (rc.get_time_elapsed, 0),
602+
'get_time_left': (rc.get_time_left, 0),
587603
'set_indicator_string': rc.set_indicator_string,
588604
'set_indicator_dot': rc.set_indicator_dot,
589605
'set_indicator_line': rc.set_indicator_line,
590-
'set_timeline_marker': rc.set_timeline_marker,
606+
'set_timeline_marker': rc.set_timeline_marker
591607
}
592-
608+
593609
def create_patterns(self):
594610
resource_pattern = [
595611
[True, True, False, True, True],

battlecode25/engine/game/play.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def run_game(args: RunGameArgs):
9393
b_wins += 1
9494
game_fb.make_match_footer(game.winner, game.domination_factor, game.round)
9595
except Exception as e:
96-
print("[server:error] An internal engine error has occurred. Please report this to the devs. This match has been terminated.")
96+
print(f"[server:error] An internal engine error has occurred. Please report this to the devs. This match has been terminated : {e}")
9797
game.set_winner_arbitrary()
9898
game.stop()
9999
# Internal engine occurred, we have to throw away this replay

battlecode25/engine/game/robot.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ def __init__(self, game: Game, id, team: Team, type: UnitType, loc: MapLocation)
2525
self.paint = round(self.type.paint_capacity * GameConstants.INITIAL_ROBOT_PAINT_PERCENTAGE / 100)
2626
else:
2727
self.paint = GameConstants.INITIAL_TOWER_PAINT_AMOUNT
28-
self.bytecodes_used = 0
2928
self.rounds_alive = 0
3029
self.action_cooldown = type.action_cooldown
3130
self.movement_cooldown = GameConstants.COOLDOWN_LIMIT
@@ -52,12 +51,12 @@ def calc_paint_cooldown_multiplier(self):
5251
if paint_percent < 0.5:
5352
return 2 - 2 * paint_percent
5453
return 1
55-
54+
5655
def add_action_cooldown(self, cooldown=-1):
5756
if cooldown == -1:
5857
cooldown = self.type.action_cooldown
5958
self.action_cooldown += round(cooldown * self.calc_paint_cooldown_multiplier())
60-
59+
6160
def add_movement_cooldown(self):
6261
self.movement_cooldown += round(GameConstants.MOVEMENT_COOLDOWN * self.calc_paint_cooldown_multiplier())
6362

@@ -68,7 +67,7 @@ def upgrade_tower(self):
6867

6968
def log(self, msg):
7069
self.logs.append(msg)
71-
70+
7271
def error(self, msg):
7372
self.logs.append(msg)
7473

@@ -80,6 +79,15 @@ def animate(self, code, methods, debug=False):
8079
def kill(self):
8180
self.runner.kill()
8281

82+
def get_bytecode_limit(self):
83+
return self.runner.bytecode_limit
84+
85+
def get_bytecodes_left(self):
86+
return self.runner.bytecode
87+
88+
def get_bytecodes_used(self):
89+
return max(self.runner.bytecode_limit - self.runner.bytecode, 0)
90+
8391
def turn(self):
8492
self.process_beginning_of_turn()
8593
self.logs.clear()
@@ -104,7 +112,7 @@ def process_beginning_of_turn(self):
104112
self.action_cooldown = max(0, self.action_cooldown - GameConstants.COOLDOWNS_PER_TURN)
105113
self.movement_cooldown = max(0, self.movement_cooldown - GameConstants.COOLDOWNS_PER_TURN)
106114
self.game.game_fb.start_turn(self.id)
107-
115+
108116
def process_end_of_turn(self):
109117
loc_idx = self.game.loc_to_index(self.loc)
110118
paint_status = self.game.paint[loc_idx]
@@ -135,7 +143,7 @@ def process_end_of_turn(self):
135143
if self.type.is_robot_type() and self.paint == 0:
136144
self.add_health(-GameConstants.NO_PAINT_DAMAGE)
137145

138-
self.game.game_fb.end_turn(self.id, self.health, self.paint, self.movement_cooldown, self.action_cooldown, self.bytecodes_used, self.loc)
146+
self.game.game_fb.end_turn(self.id, self.health, self.paint, self.movement_cooldown, self.action_cooldown, self.get_bytecodes_used(), self.loc)
139147
self.rounds_alive += 1
140148

141149
def get_robot_info(self) -> RobotInfo:

battlecode25/engine/game/robot_controller.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -792,11 +792,28 @@ def set_timeline_marker(self, label: str, red: int, green: int, blue: int) -> No
792792
self.game.game_fb.add_timeline_marker(self.robot.team, label, red, green, blue)
793793

794794
def resign(self) -> None:
795-
self.game.set_winner(self.robot.team.opponent(), DominationFactor.RESIGNATION)
795+
self.game.resign(self.robot.team)
796796

797797
def disintegrate(self) -> None:
798798
self.game.destroy_robot(self.robot.id)
799-
799+
800+
# CLOCK METHODS
801+
802+
def yield_turn(self) -> None:
803+
self.robot.runner.pause()
804+
805+
def get_bytecode_num(self) -> int:
806+
return self.robot.get_bytecodes_used()
807+
808+
def get_bytecodes_left(self) -> int:
809+
return self.robot.get_bytecodes_left()
810+
811+
def get_time_elapsed(self) -> int:
812+
return self.game.team_info.get_execution_time(self.robot.team)
813+
814+
def get_time_left(self) -> int:
815+
return max(GameConstants.MAX_TEAM_EXECUTION_TIME - self.get_time_elapsed(), 0)
816+
800817
class RobotError(Exception):
801818
"""Raised for illegal robot inputs"""
802819
pass

battlecode25/engine/game/team_info.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ def __init__(self, game_world):
1010
self.old_coin_counts = [0] * 2
1111
self.unit_counts = [0] * 2
1212
self.defense_damage_increase = [0] * 2
13+
self.execution_time = [0] * 2
1314

1415
# ***** GETTER METHODS *****
1516

@@ -33,6 +34,9 @@ def get_unit_count(self, team):
3334

3435
def get_defense_damage_increase(self, team):
3536
return self.defense_damage_increase[team.value]
37+
38+
def get_execution_time(self, team):
39+
return self.execution_time[team.value]
3640

3741
# ***** UPDATE METHODS *****
3842

@@ -56,3 +60,6 @@ def add_unit_count(self, team, amount):
5660

5761
def add_defense_damage_increase(self, team, amount):
5862
self.defense_damage_increase[team.value] += amount
63+
64+
def add_execution_time(self, team, amount):
65+
self.execution_time[team.value] += amount

battlecode25/stubs.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,3 +476,37 @@ def disintegrate() -> None:
476476
Destroys this robot.
477477
"""
478478
pass
479+
480+
# CLOCK METHODS
481+
482+
def yield_turn() -> None:
483+
"""
484+
Ends the processing of this robot during the current round.
485+
"""
486+
pass
487+
488+
def get_bytecode_num() -> int:
489+
"""
490+
Returns the number of bytecodes the current robot has executed since the
491+
beginning of the current round.
492+
"""
493+
pass
494+
495+
def get_bytecodes_left() -> int:
496+
"""
497+
Returns the number of bytecodes this robot has left in this round.
498+
"""
499+
pass
500+
501+
def get_time_elapsed() -> int:
502+
"""
503+
Returns the total amount of time, in nanoseconds, that this team's robots have collectively spent executing
504+
since the beginning of the match.
505+
"""
506+
pass
507+
508+
def get_time_left() -> int:
509+
"""
510+
Returns the total amount of execution time, in nanoseconds, left this team has before they timeout
511+
"""
512+
pass

0 commit comments

Comments
 (0)