glmcdona / LuxPythonEnvGym

Matching python environment code for Lux AI 2021 Kaggle competition, and a gym interface for RL models.
MIT License
73 stars 38 forks source link

AttributeError: 'NoneType' object has no attribute 'adjacent_city_tiles' #63

Closed nosound2 closed 2 years ago

nosound2 commented 2 years ago

Hello, I encountered the following error while training, cell.city_tile is None.

C:\StudioProjects\Lux\LuxPythonEnvGym\examples\train.py in train(args, player, opponent, load_id)
    130                                              name_prefix=f'rl_model_{run_id}')
    131     model.learn(total_timesteps=args.step_count,
--> 132                 callback=checkpoint_callback)  # 20M steps
    133     if not os.path.exists(f'models/rl_model_{run_id}_{args.step_count}_steps.zip'):
    134         model.save(path=f'models/rl_model_{run_id}_{args.step_count}_steps.zip')

C:\StudioProjects\Lux\venv\lib\site-packages\stable_baselines3\ppo\ppo.py in learn(self, total_timesteps, callback, log_interval, eval_env, eval_freq, n_eval_episodes, tb_log_name, eval_log_path, reset_num_timesteps)
    308             tb_log_name=tb_log_name,
    309             eval_log_path=eval_log_path,
--> 310             reset_num_timesteps=reset_num_timesteps,
    311         )

C:\StudioProjects\Lux\venv\lib\site-packages\stable_baselines3\common\on_policy_algorithm.py in learn(self, total_timesteps, callback, log_interval, eval_env, eval_freq, n_eval_episodes, tb_log_name, eval_log_path, reset_num_timesteps)
    235         while self.num_timesteps < total_timesteps:
    236 
--> 237             continue_training = self.collect_rollouts(self.env, callback, self.rollout_buffer, n_rollout_steps=self.n_steps)
    238 
    239             if continue_training is False:

C:\StudioProjects\Lux\venv\lib\site-packages\stable_baselines3\common\on_policy_algorithm.py in collect_rollouts(self, env, callback, rollout_buffer, n_rollout_steps)
    176                 clipped_actions = np.clip(actions, self.action_space.low, self.action_space.high)
    177 
--> 178             new_obs, rewards, dones, infos = env.step(clipped_actions)
    179 
    180             self.num_timesteps += env.num_envs

C:\StudioProjects\Lux\venv\lib\site-packages\stable_baselines3\common\vec_env\base_vec_env.py in step(self, actions)
    160         """
    161         self.step_async(actions)
--> 162         return self.step_wait()
    163 
    164     def get_images(self) -> Sequence[np.ndarray]:

C:\StudioProjects\Lux\venv\lib\site-packages\stable_baselines3\common\vec_env\dummy_vec_env.py in step_wait(self)
     42         for env_idx in range(self.num_envs):
     43             obs, self.buf_rews[env_idx], self.buf_dones[env_idx], self.buf_infos[env_idx] = self.envs[env_idx].step(
---> 44                 self.actions[env_idx]
     45             )
     46             if self.buf_dones[env_idx]:

C:\StudioProjects\Lux\venv\lib\site-packages\stable_baselines3\common\monitor.py in step(self, action)
     88         if self.needs_reset:
     89             raise RuntimeError("Tried to step environment that needs reset")
---> 90         observation, reward, done, info = self.env.step(action)
     91         self.rewards.append(reward)
     92         if done:

c:\studioprojects\lux\luxpythonenvgym\luxai2021\env\lux_env.py in step(self, action_code)
     58         is_game_error = False
     59         try:
---> 60             (unit, city_tile, team, is_new_turn) = next(self.match_generator)
     61 
     62             obs = self.learning_agent.get_observation(self.game, unit, city_tile, team, is_new_turn)

c:\studioprojects\lux\luxpythonenvgym\luxai2021\game\match_controller.py in run_to_next_observation(self)
    119                 if agent.get_agent_type() == Constants.AGENT_TYPE.AGENT:
    120                     # Call the agent for the set of actions
--> 121                     actions = agent.process_turn(self.game, agent.team)
    122                     self.take_actions(actions)
    123                 elif agent.get_agent_type() == Constants.AGENT_TYPE.LEARNING:

C:\StudioProjects\Lux\LuxPythonEnvGym\examples\agent_policy.py in process_turn(self, game, team)
    527         for unit in units:
    528             if unit.can_act():
--> 529                 obs = self.get_observation(game, unit, None, unit.team, new_turn)
    530                 action_code, _states = self.model.predict(obs, deterministic=True)
    531                 if action_code is not None:

C:\StudioProjects\Lux\LuxPythonEnvGym\examples\agent_policy.py in get_observation(self, game, unit, city_tile, team, is_new_turn)
    363                                     c = game.cities[game.map.get_cell_by_pos(closest_position).city_tile.city_id]
    364                                     obs[observation_index + 6] = min(
--> 365                                         c.fuel / (c.get_light_upkeep() * 200.0),
    366                                         1.0
    367                                     )

c:\studioprojects\lux\luxpythonenvgym\luxai2021\game\city.py in get_light_upkeep(self)
     37         :return:
     38         """
---> 39         return len(self.city_cells) * self.configs["parameters"]["LIGHT_UPKEEP"]["CITY"] - self.get_adjacency_bonuses()
     40 
     41     def get_adjacency_bonuses(self):

c:\studioprojects\lux\luxpythonenvgym\luxai2021\game\city.py in get_adjacency_bonuses(self)
     46         bonus = 0
     47         for cell in self.city_cells:
---> 48             bonus += cell.city_tile.adjacent_city_tiles * self.configs["parameters"]["CITY_ADJACENCY_BONUS"]
     49 
     50         return bonus

AttributeError: 'NoneType' object has no attribute 'adjacent_city_tiles'
glmcdona commented 2 years ago

Looking into this. I've recently repro'd this same error in my own training here are there too.

royerk commented 2 years ago

Same, I'm getting them mostly when I train versus a model (as opposed to the default Agent()). I will look into it too, I'll be happy to test a fix if anyone has one too.

royerk commented 2 years ago

Random thoughts. This doesn't happen too often, which seems to indicate that it isn't some generic error about a cell being None. Could it be something about the map's edge (city tile is on the edge with one or two missing neighbors)? I'll keep looking and maybe instrument some logs about tile coordinate to check this.

Edit: after surrounding the part with try/except AttributeError, I got the error AttributeError: 'NoneType' object has no attribute 'adjacent_city_tiles' thing: 7 11 (it reads as cell.pos.x=7, cell.pos.y=11) so it's not as straightforward as a "city on the edge" problem unfortunately.

royerk commented 2 years ago

Now it's weird, since I only had this issue when training versus another PPO I was assuming the issue was coming from the opponent and not the bot undergoing training.

By surrounding with try/except:

obs[observation_index + 6] = min(
                                        c.fuel / (c.get_light_upkeep() * 200.0),
                                        1.0
                                    )

and printing the mode train or inference of the bot in the except. It turned out that it's from the bot in training. This try/except did not cause a crash.

As this was running a crash was caused by:

Traceback (most recent call last):
  File "examples/train.py", line 240, in <module>
    train(local_args)
  File "examples/train.py", line 190, in train
    callback=checkpoint_callback)  # 20M steps
  File "/opt/conda/lib/python3.7/site-packages/stable_baselines3/ppo/ppo.py", line 310, in learn
    reset_num_timesteps=reset_num_timesteps,
  File "/opt/conda/lib/python3.7/site-packages/stable_baselines3/common/on_policy_algorithm.py", line 237, in learn
    continue_training = self.collect_rollouts(self.env, callback, self.rollout_buffer, n_rollout_steps=self.n_steps)
  File "/opt/conda/lib/python3.7/site-packages/stable_baselines3/common/on_policy_algorithm.py", line 178, in collect_rollouts
    new_obs, rewards, dones, infos = env.step(clipped_actions)
  File "/opt/conda/lib/python3.7/site-packages/stable_baselines3/common/vec_env/base_vec_env.py", line 162, in step
    return self.step_wait()
  File "/opt/conda/lib/python3.7/site-packages/stable_baselines3/common/vec_env/dummy_vec_env.py", line 44, in step_wait
    self.actions[env_idx]
  File "/opt/conda/lib/python3.7/site-packages/stable_baselines3/common/monitor.py", line 90, in step
    observation, reward, done, info = self.env.step(action)
  File "/opt/conda/lib/python3.7/site-packages/luxai2021-0.1.0-py3.7.egg/luxai2021/env/lux_env.py", line 60, in step
    (unit, city_tile, team, is_new_turn) = next(self.match_generator)
  File "/opt/conda/lib/python3.7/site-packages/luxai2021-0.1.0-py3.7.egg/luxai2021/game/match_controller.py", line 142, in run_to_next_observation
    if city_tile.can_act():
AttributeError: 'NoneType' object has no attribute 'can_act'

Different location but same cause as OP posed, a cell.city_tile is None.

royerk commented 2 years ago

@nosound2 Have you tried to trigger the error after the fixes you introduced in your PR? The shared cargo for the first 2 workers could have caused it. One worker collecting resource filling both workers cargo followed by a city tile creation = crash: invalid city tiles and such.

I will try today and report here. Edit: same bug :(

Willyam42 commented 2 years ago

I've met the issue too. seems it's actually coming from deleting a city when two city tile have been built on the same cell. I wanted to propose a PR but don't have the rights. whoever is interested can look at the file in attachment.

add some checks on the SpawnCityAction in game.validate_command() as advised by author, using the original src/Game/index.ts as model add a class MatchWarn as suggested by original implementation fix an access to game.game.state -> game.state should fix issues: #63 and #102 not formally tested with unit tests. run successfully with python 3.9

game.txt

royerk commented 2 years ago

@Willyam42 good point :+1: You can make a PR though and be listed as a contributor! Fork the repo -> clone locally -> make branch with changes -> push -> open PR against the original repo (should be the default option).

Willyam42 commented 2 years ago

Thanks for the tip @royerk I've sent the PR. I hope it will be reviewed properly as it hasn't been unit tested and I'm not too familiar with python...

glmcdona commented 2 years ago

Thanks @Willyam42 for the fix! Merged it. Closing this issue finally :)