Skip to content

Commit

Permalink
Merge pull request #132 from Lux-AI-Challenge/v1.1.3-release
Browse files Browse the repository at this point in the history
V1.1.3 release
  • Loading branch information
StoneT2000 authored Dec 9, 2022
2 parents bd61911 + 382d70a commit adf6c10
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 946 deletions.
10 changes: 10 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# ChangeLog

### v1.1.3

- Fix bug with lichen frontiers being computed incorrectly
- Fix bug where units could transfer more than they held into factories
- Fix bug when traces were printed it errored instead.
- Added some aggregate meta data for preparation of factions
- Fixed critical bug in starter kit where units wasted their time expending energy sending a move center action
### v1.1.2
- Fix bug with C++ agents not running with CLI tool

### v1.1.1

- Allow "negative" bids to bid to go second instead of first in factory placement. The winning bidder is whoever has the higher absolute value bid. The winning bidder now loses water and metal equal to the absolute value of their bid.
Expand Down
1 change: 0 additions & 1 deletion kits/python/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ def act(self, step: int, obs, remainingOverageTime: int = 60):
closest_factory = None
adjacent_to_factory = False
if len(factory_tiles) > 0:
actions[unit_id] = [unit.move(0, repeat=0)]
factory_distances = np.mean((factory_tiles - unit.pos) ** 2, 1)
closest_factory_tile = factory_tiles[np.argmin(factory_distances)]
closest_factory = factory_units[np.argmin(factory_distances)]
Expand Down
923 changes: 0 additions & 923 deletions kits/python/starter_python.ipynb

This file was deleted.

41 changes: 34 additions & 7 deletions luxai2022/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ def env():
class LuxAI2022(ParallelEnv):
metadata = {"render.modes": ["human", "html", "rgb_array"], "name": "luxai2022_v0"}

def __init__(self, **kwargs):
def __init__(self, collect_stats: bool = False, **kwargs):
self.collect_stats = collect_stats # note: added here instead of in configs since it would break existing bots
default_config = EnvConfig(**kwargs)

self.env_cfg = default_config
self.possible_agents = ["player_" + str(r) for r in range(2)]
self.agent_name_mapping = dict(zip(self.possible_agents, list(range(len(self.possible_agents)))))
Expand Down Expand Up @@ -101,10 +103,6 @@ def render(self, mode="human", **kwargs):
Renders the environment. In human mode, it can print to terminal, open
up a graphical window, or open up some other display that a human can see and understand.
"""
# if len(self.agents) == 2:
# string = ("Current state: Agent1: {} , Agent2: {}".format(MOVES[self.state[self.agents[0]]], MOVES[self.state[self.agents[1]]]))
# else:
# string = "Game over"

if mode == "human":
if self._init_render():
Expand Down Expand Up @@ -170,6 +168,12 @@ def reset(self, seed=None):
for agent in self.possible_agents:
self.state.units[agent] = OrderedDict()
self.state.factories[agent] = OrderedDict()
if self.collect_stats:
self.state.stats[agent] = dict(
lichen_grown=0, lichen_lost=0,
units_lost=dict(LIGHT=0, HEAVY=0),
factories_lost=0, units_built=dict(LIGHT=0, HEAVY=0)
)
obs = self.state.get_obs()
observations = {agent: obs for agent in self.agents}
return observations
Expand Down Expand Up @@ -329,7 +333,7 @@ def _handle_transfer_actions(self, actions_by_type: ActionsByType):
factory_id = f"factory_{self.state.board.factory_occupancy_map[transfer_pos.x, transfer_pos.y]}"
if factory_id in self.state.factories[unit.team.agent]:
factory = self.state.factories[unit.team.agent][factory_id]
actually_transferred = factory.add_resource(transfer_action.resource, transfer_action.transfer_amount)
actually_transferred = factory.add_resource(transfer_action.resource, transfer_amount)
elif units_there is not None:
assert len(units_there) == 1, "Fatal error here, this is a bug"
target_unit = units_there[0]
Expand Down Expand Up @@ -366,6 +370,9 @@ def _handle_self_destruct_actions(self, actions_by_type: ActionsByType):
pos_hash = self.state.board.pos_hash(unit.pos)
del self.state.board.units_map[pos_hash]
self.destroy_unit(unit)
if self.collect_stats:
self.state.stats[unit.team.agent]["units_lost"][unit.unit_type.name] += 1

def _handle_factory_build_actions(self, actions_by_type: ActionsByType, weather_cfg):
for factory, factory_build_action in actions_by_type["factory_build"]:
factory: Factory
Expand Down Expand Up @@ -399,6 +406,8 @@ def _handle_factory_build_actions(self, actions_by_type: ActionsByType, weather_
unit_type=factory_build_action.unit_type,
pos=factory.pos.pos,
)
if self.collect_stats:
self.state.stats[factory.team.agent]["units_built"][factory_build_action.unit_type.name] += 1
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)
Expand Down Expand Up @@ -489,6 +498,9 @@ def _handle_movement_actions(self, actions_by_type: ActionsByType, weather_cfg):

for u in destroyed_units:
self.destroy_unit(u)
if self.collect_stats:
self.state.stats[u.team.agent]["units_lost"][u.unit_type.name] += 1

def _handle_recharge_actions(self, actions_by_type: ActionsByType):
# for recharging actions, check if unit has enough power. If not, add action back to the queue
for unit, recharge_action in actions_by_type["recharge"]:
Expand Down Expand Up @@ -572,7 +584,7 @@ def step(self, actions):
self.state.units[agent][unit_id].action_queue = formatted_actions
except Exception as e:
# catch errors when trying to format unit or factory actions
print(e.with_traceback())
print(e.with_traceback(None))
failed_agents[agent] = True

# 2. store actions by type
Expand All @@ -598,6 +610,10 @@ def step(self, actions):
# 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)

if self.collect_stats:
lichen_before = self.state.board.lichen.copy()
lichen_strains_before = self.state.board.lichen_strains.copy()

self._handle_transfer_actions(actions_by_type)
self._handle_pickup_actions(actions_by_type)
self._handle_dig_actions(actions_by_type, weather_cfg)
Expand All @@ -611,6 +627,15 @@ def step(self, actions):
self.state.board.lichen -= 1
self.state.board.lichen = self.state.board.lichen.clip(0, self.env_cfg.MAX_LICHEN_PER_TILE)
self.state.board.lichen_strains[self.state.board.lichen == 0] = -1
if self.collect_stats:
lichen_change = self.state.board.lichen - lichen_before
for agent in self.agents:
for strain in self.state.teams[agent].factory_strains:
start_of_step_lichen_tiles: np.ndarray = lichen_change[lichen_strains_before == strain]
lichen_lost = start_of_step_lichen_tiles[start_of_step_lichen_tiles < 0].sum()
lichen_gained = start_of_step_lichen_tiles.sum() - lichen_lost
self.state.stats[agent]["lichen_grown"] += lichen_gained
self.state.stats[agent]["lichen_lost"] -= lichen_lost

# resources refining
for agent in self.agents:
Expand All @@ -623,6 +648,8 @@ def step(self, actions):
for factory in factories_to_destroy:
# destroy factories that ran out of water
self.destroy_factory(factory)
if self.collect_stats:
self.state.stats[factory.team.agent]["factories_lost"] += 1
# power gain
if is_day(self.env_cfg, self.state.real_env_steps):
for agent in self.agents:
Expand Down
29 changes: 15 additions & 14 deletions luxai2022/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,29 +36,30 @@ def compute_water_info(init: np.ndarray, MIN_LICHEN_TO_SPREAD: int, lichen: np.n
if forbidden[pos[0], pos[1]]:
continue
pos_lichen = lichen[pos[0], pos[1]]
pos_strain = lichen_strains[pos[0], pos[1]]
# check for surrounding tiles with lichen and no incompatible lichen strains, grow on those
can_grow = True
for move_delta in move_deltas[1:]:
check_pos = pos + move_delta
if (check_pos[0], check_pos[1]) in seen: continue
# check surrounding tiles on the map
if check_pos[0] < 0 or check_pos[1] < 0 or check_pos[0] >= W or check_pos[1] >= H: continue
if check_pos[0] < 0 or check_pos[1] < 0 or check_pos[0] >= H or check_pos[1] >= W: continue
adj_strain = lichen_strains[check_pos[0], check_pos[1]]
if adj_strain == -1:
if pos_lichen >= MIN_LICHEN_TO_SPREAD:
# adjacent tile is empty and isn't a resource and the current tile has enough lichen to spread
seen.add(tuple(check_pos))
frontier.append(check_pos)
elif adj_strain != strain_id:
# adjacent tile is not empty and is not a strain this factory owns.
can_grow = False
seen.add(tuple(check_pos))
else:
# adjacent tile has our own strain, we can grow here too
if adj_strain != -1 and adj_strain != strain_id:
can_grow = False

# if seen, skip
if (check_pos[0], check_pos[1]) in seen:
continue

# we add it to the frontier only in two cases:
# 1. it is an empty tile, and current pos has enough lichen to expand.
# 2. both current tile and check_pos are of our strain.
if (adj_strain == -1 and pos_lichen >= MIN_LICHEN_TO_SPREAD) \
or (adj_strain == strain_id and pos_strain == strain_id):
seen.add(tuple(check_pos))
frontier.append(check_pos)

if can_grow:
if can_grow or (lichen_strains[pos[0], pos[1]] == strain_id):
grow_lichen_positions.add((pos[0], pos[1]))
return grow_lichen_positions

Expand Down
1 change: 1 addition & 0 deletions luxai2022/state/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class State:
factories: Dict[str, Dict[str, Factory]] = field(default_factory=dict)
teams: Dict[str, Team] = field(default_factory=dict)
global_id: int = 0
stats: Dict[str, Dict[str, int]] = field(default_factory=dict)

@property
def real_env_steps(self):
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def read(fname):

setup(
name="luxai2022",
version="1.1.2",
version="1.1.3",
author="Lux AI Challenge",
description="The Lux AI Challenge Season 2",
license="MIT",
Expand Down

0 comments on commit adf6c10

Please sign in to comment.