From 51445bda14c7ee8813785a0fec6985523a3427c9 Mon Sep 17 00:00:00 2001 From: stone_tao Date: Tue, 8 Nov 2022 19:49:37 -0800 Subject: [PATCH 1/5] Update constants.ts --- visualizer/src/store/autoplay/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visualizer/src/store/autoplay/constants.ts b/visualizer/src/store/autoplay/constants.ts index aefdcff7..3bf4c990 100644 --- a/visualizer/src/store/autoplay/constants.ts +++ b/visualizer/src/store/autoplay/constants.ts @@ -4,6 +4,6 @@ export const SPEEDS = [0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512] as const export type Speed = (typeof SPEEDS)[number] -export const initialSpeed: Speed = 128 +export const initialSpeed: Speed = 16 export const speedToIndex: Map = new Map(SPEEDS.map((x, i) => [x, i])) \ No newline at end of file From 8d77ac814776d5f63ddbbfdcf8c33a9bfd4aa9e0 Mon Sep 17 00:00:00 2001 From: stone_tao Date: Tue, 8 Nov 2022 19:51:29 -0800 Subject: [PATCH 2/5] Fix #86 --- luxai2022/actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luxai2022/actions.py b/luxai2022/actions.py index ebe22768..fe0a85b4 100644 --- a/luxai2022/actions.py +++ b/luxai2022/actions.py @@ -319,7 +319,7 @@ def invalidate_action(msg): ) continue if valid_action: - actions_by_type_validated["self_destruct"].append((unit, move_action)) + actions_by_type_validated["self_destruct"].append((unit, self_destruct_action)) for factory, build_action in actions_by_type["factory_build"]: valid_action = True From f367d2ce8a7b4dff5f217681bb77f9ea4bc9e280 Mon Sep 17 00:00:00 2001 From: stone_tao Date: Tue, 8 Nov 2022 20:02:35 -0800 Subject: [PATCH 3/5] fix #85 and store precomputed power_cost value in action objects --- luxai2022/actions.py | 16 ++++++++++++++-- luxai2022/env.py | 10 ++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/luxai2022/actions.py b/luxai2022/actions.py index fe0a85b4..f83004f0 100644 --- a/luxai2022/actions.py +++ b/luxai2022/actions.py @@ -21,6 +21,7 @@ class Action: def __init__(self, act_type: str) -> None: self.act_type = act_type self.repeat = False + self.power_cost = 0 def state_dict(): raise NotImplementedError("") @@ -30,6 +31,7 @@ class FactoryBuildAction(Action): def __init__(self, unit_type: luxai_unit.UnitType) -> None: super().__init__("factory_build") self.unit_type = unit_type + self.power_cost = 0 def state_dict(self): if self.unit_type == luxai_unit.UnitType.LIGHT: @@ -41,7 +43,7 @@ class FactoryWaterAction(Action): def __init__(self) -> None: super().__init__("factory_water") self.water_cost = None - + self.power_cost = 0 def state_dict(): return 2 @@ -53,6 +55,7 @@ def __init__(self, move_dir: int, dist: int = 1, repeat=False) -> None: self.move_dir = move_dir self.dist = dist self.repeat = repeat + self.power_cost = 0 def state_dict(self): return np.array([0, self.move_dir, self.dist, 0, self.repeat]) @@ -66,6 +69,7 @@ def __init__(self, transfer_dir: int, resource: int, transfer_amount: int, repea self.resource = resource self.transfer_amount = transfer_amount self.repeat = repeat + self.power_cost = 0 def state_dict(self): return np.array([1, self.transfer_dir, self.resource, self.transfer_amount, self.repeat]) @@ -78,6 +82,7 @@ def __init__(self, resource: int, pickup_amount: int, repeat=False) -> None: self.resource = resource self.pickup_amount = pickup_amount self.repeat = repeat + self.power_cost = 0 def state_dict(self): return np.array([2, 0, self.resource, self.pickup_amount, self.repeat]) @@ -87,6 +92,7 @@ class DigAction(Action): def __init__(self, repeat=False) -> None: super().__init__("dig") self.repeat = repeat + self.power_cost = 0 def state_dict(self): return np.array([3, 0, 0, 0, self.repeat]) @@ -96,6 +102,7 @@ class SelfDestructAction(Action): def __init__(self, repeat=False) -> None: super().__init__("self_destruct") self.repeat = repeat + self.power_cost = 0 def state_dict(self): return np.array([4, 0, 0, 0, self.repeat]) @@ -106,6 +113,7 @@ def __init__(self, power: int, repeat=False) -> None: super().__init__("recharge") self.power = power self.repeat = repeat + self.power_cost = 0 def state_dict(self): return np.array([5, 0, 0, self.power, self.repeat]) @@ -307,6 +315,7 @@ def invalidate_action(msg): ) continue if valid_action: + move_action.power_cost = power_required actions_by_type_validated["move"].append((unit, move_action)) for unit, self_destruct_action in actions_by_type["self_destruct"]: @@ -319,6 +328,7 @@ def invalidate_action(msg): ) continue if valid_action: + self_destruct_action.power_cost = power_required actions_by_type_validated["self_destruct"].append((unit, self_destruct_action)) for factory, build_action in actions_by_type["factory_build"]: @@ -330,10 +340,12 @@ def invalidate_action(msg): if factory.cargo.metal < unit_cfg.METAL_COST: invalidate_action(f"Invalid factory build action for factory {factory} - Insufficient metal, factory has {factory.cargo.metal}, but requires {unit_cfg.METAL_COST} to build {build_action.unit_type}") continue - if factory.power < math.ceil(unit_cfg.POWER_COST * weather_cfg["power_loss_factor"]): + power_required = math.ceil(unit_cfg.POWER_COST * weather_cfg["power_loss_factor"]) + if factory.power < power_required: invalidate_action(f"Invalid factory build action for factory {factory} - Insufficient power, factory has {factory.power}, but requires ceil({unit_cfg.POWER_COST} x {weather_cfg['power_loss_factor']}) to build {build_action.unit_type.name}. Power cost factor is {weather_cfg['power_loss_factor']}") continue if valid_action: + build_action.power_cost = power_required actions_by_type_validated["factory_build"].append((factory, build_action)) pass diff --git a/luxai2022/env.py b/luxai2022/env.py index b5cd197b..762bb9dd 100644 --- a/luxai2022/env.py +++ b/luxai2022/env.py @@ -348,10 +348,10 @@ def _handle_factory_build_actions(self, actions_by_type: ActionsByType, weather_ ) if factory_build_action.unit_type == UnitType.HEAVY: factory.sub_resource(3, self.env_cfg.ROBOTS["HEAVY"].METAL_COST) - factory.sub_resource(4, math.ceil(self.env_cfg.ROBOTS["HEAVY"].POWER_COST * weather_cfg["power_loss_factor"])) + factory.sub_resource(4, factory_build_action.power_cost) else: factory.sub_resource(3, self.env_cfg.ROBOTS["LIGHT"].METAL_COST) - factory.sub_resource(4, math.ceil(self.env_cfg.ROBOTS["LIGHT"].POWER_COST * weather_cfg["power_loss_factor"])) + factory.sub_resource(4, factory_build_action.power_cost) def _handle_movement_actions(self, actions_by_type: ActionsByType, weather_cfg): new_units_map: Dict[str, List[Unit]] = defaultdict(list) heavy_entered_pos: Dict[str, List[Unit]] = defaultdict(list) @@ -364,9 +364,7 @@ def _handle_movement_actions(self, actions_by_type: ActionsByType, weather_cfg): continue old_pos_hash = self.state.board.pos_hash(unit.pos) target_pos = unit.pos + move_action.dist * move_deltas[move_action.move_dir] - rubble = self.state.board.rubble[target_pos.y, target_pos.x] - power_required = unit.unit_cfg.MOVE_COST + unit.unit_cfg.RUBBLE_MOVEMENT_COST * rubble - power_required = math.ceil(power_required * weather_cfg["power_loss_factor"]) + power_required = move_action.power_cost unit.pos = target_pos new_pos_hash = self.state.board.pos_hash(unit.pos) @@ -549,7 +547,7 @@ def step(self, actions): # update information for lichen growing and cache it factory.cache_water_info(self.state.board, self.env_cfg) - # 3. validate all actions against current state, throw away impossible actions TODO + # 3. validate all actions against current state, throw away impossible actions actions_by_type = validate_actions(self.env_cfg, self.state, actions_by_type, verbose=self.env_cfg.verbose, weather_cfg=weather_cfg) self._handle_transfer_actions(actions_by_type) From c80f83c34e2c6e16d390dfa52c78d13f108f31d4 Mon Sep 17 00:00:00 2001 From: stone_tao Date: Tue, 8 Nov 2022 22:20:28 -0800 Subject: [PATCH 4/5] Update env.py --- luxai2022/env.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/luxai2022/env.py b/luxai2022/env.py index 762bb9dd..bb6ebd9c 100644 --- a/luxai2022/env.py +++ b/luxai2022/env.py @@ -522,9 +522,9 @@ def step(self, actions): continue unit.power -= update_power_req self.state.units[agent][unit_id].action_queue = formatted_actions - except ValueError as e: + except Exception as e: # catch errors when trying to format unit or factory actions - print(e) + print(e.with_traceback()) failed_agents[agent] = True # 2. store actions by type From c67676cbe92f3042d7bb8feb9d4abdb26746a5ca Mon Sep 17 00:00:00 2001 From: stone_tao Date: Tue, 8 Nov 2022 22:24:50 -0800 Subject: [PATCH 5/5] work --- ChangeLog.md | 6 ++++++ luxai2022/env.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index fb2a6d24..28c7a9a6 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,11 @@ # ChangeLog +### v1.0.6 +- Fix bug where game ends at turns < 1000 (kaggle-environments bug) +- Fixed bug with self-destruct actions not being validated or added +- Log unit ids that collided. E.g. `14: 1 Units: (unit_11) collided at 33,44 with [1] unit_9 UnitType.HEAVY at (33, 44) surviving` +- Fixed bug where power costs were recomputed after state change in a single step, causing potentially negative energy in edge cases + ### v1.0.5 Environment: diff --git a/luxai2022/env.py b/luxai2022/env.py index bb6ebd9c..b6df28f8 100644 --- a/luxai2022/env.py +++ b/luxai2022/env.py @@ -403,7 +403,7 @@ def _handle_movement_actions(self, actions_by_type: ActionsByType, weather_cfg): for u in units: if u.unit_id != surviving_unit.unit_id: destroyed_units.add(u) - self._log(f"{len(destroyed_units)} Units collided at {pos_hash} with {surviving_unit} surviving") + self._log(f"{len(destroyed_units)} Units: ({', '.join([u.unit_id for u in destroyed_units])}) collided at {pos_hash} with {surviving_unit} surviving") new_units_map_after_collision[pos_hash].append(surviving_unit) else: # check for stationary heavy unit there