hsahovic / poke-env

A python interface for training Reinforcement Learning bots to battle on pokemon showdown
https://poke-env.readthedocs.io/
MIT License
297 stars 103 forks source link

'NoneType' object has no attribute 'message' Error #260

Open yellowface7 opened 2 years ago

yellowface7 commented 2 years ago

I am new to both Python and poke-env.

I thought I was doing decent until I got the following log:

PS G:\Projects\BotRepo> & C:/Users/REDACTED/AppData/Local/Microsoft/WindowsApps/python3.8.exe g:/Projects/BotRepo/TestBot.py
Health: 233
Health: 233
2022-02-09 19:06:44,181 - yellowfac2 - ERROR - Unhandled exception raised while handling message:
>battle-gen8randombattle-1507748815
|player|p2|yellowfac2|170|
|teamsize|p1|6
|teamsize|p2|6
|gen|8
|tier|[Gen 8] Random Battle
|rule|Species Clause: Limit one of each Pokémon
|rule|HP Percentage Mod: HP is shown in percentages
|rule|Sleep Clause Mod: Limit one foe put to sleep
|
|t:|1644451607
|start
|switch|p1a: Sigilyph|Sigilyph, L83, M|100/100
|switch|p2a: Archeops|Archeops, L82, M|233/233
|turn|1
Traceback (most recent call last):
  File "C:\Users\REDACTED\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\poke_env\player\player_network_interface.py", line 131, in _handle_message
    await self._handle_battle_message(split_messages)
  File "C:\Users\REDACTED\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\poke_env\player\player.py", line 299, in _handle_battle_message
    await self._handle_battle_request(battle)
  File "C:\Users\REDACTED\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\poke_env\player\player.py", line 321, in _handle_battle_request
    message = self.choose_move(battle).message
AttributeError: 'NoneType' object has no attribute 'message'
Task exception was never retrieved
future: <Task finished name='Task-24' coro=<PlayerNetwork._handle_message() done, defined at C:\Users\REDACTED\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\poke_env\player\player_network_interface.py:117> exception=AttributeError("'NoneType' object has no attribute 'message'")>
Traceback (most recent call last):
  File "C:\Users\REDACTED\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\poke_env\player\player_network_interface.py", line 177, in _handle_message
    raise exception
  File "C:\Users\REDACTED\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\poke_env\player\player_network_interface.py", line 131, in _handle_message
    await self._handle_battle_message(split_messages)
  File "C:\Users\REDACTED\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\poke_env\player\player.py", line 299, in _handle_battle_message
    await self._handle_battle_request(battle)
  File "C:\Users\REDACTED\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\poke_env\player\player.py", line 321, in _handle_battle_request
    message = self.choose_move(battle).message
AttributeError: 'NoneType' object has no attribute 'message'
PS G:\Projects\BotRepo>

Here is the code that leads up to the error:

    def choose_move(self, battle):
        # If the bot can attack, it will.
        if battle.available_moves:
            #Is our health low?
            print("Health: " + str(battle.active_pokemon.current_hp))
            if battle.active_pokemon.current_hp != None:
                print("Health: " + str(battle.active_pokemon.current_hp))
                if battle.active_pokemon.current_hp < battle.active_pokemon.current_hp / 2:
                    print("-Healing-")
                    # Find a healing move.
                    HealingMove = max(battle.available_moves, key=lambda move: move.heal)
                    return self.create_order(HealingMove)

I created a reddit post in the Pokemon Showdown Coding subreddit but got no response. I decided to post here since I saw that someone else had this error at one point.

hsahovic commented 2 years ago

Hey @yellowface7,

For you bot to function, choose_move should always return a BattleOrder. Here, your code is testing if your active pokemon can use a move, and if its health is low, it will use the move that will restore as max HP as possible.

However, if your hp is not low, or if your pokemon can not use a move (for instance, if it fainted and needs to be switched out), choose_move will just stop, and return None. This is what is happening here - one way to fix it would be to do something like:

    def choose_move(self, battle):
        # If the bot can attack, it will.
        if battle.available_moves:
            #Is our health low?
            print("Health: " + str(battle.active_pokemon.current_hp))
            if battle.active_pokemon.current_hp != None:
                print("Health: " + str(battle.active_pokemon.current_hp))
                if battle.active_pokemon.current_hp < battle.active_pokemon.current_hp / 2:
                    print("-Healing-")
                    # Find a healing move.
                    HealingMove = max(battle.available_moves, key=lambda move: move.heal)
                    return self.create_order(HealingMove)
        print("Choosing a random move!")
        return self.choose_random_move(battle)

Let me know if this helps!

yellowface7 commented 2 years ago

Hey @yellowface7,

For you bot to function, choose_move should always return a BattleOrder. Here, your code is testing if your active pokemon can use a move, and if its health is low, it will use the move that will restore as max HP as possible.

However, if your hp is not low, or if your pokemon can not use a move (for instance, if it fainted and needs to be switched out), choose_move will just stop, and return None. This is what is happening here - one way to fix it would be to do something like:

    def choose_move(self, battle):
        # If the bot can attack, it will.
        if battle.available_moves:
            #Is our health low?
            print("Health: " + str(battle.active_pokemon.current_hp))
            if battle.active_pokemon.current_hp != None:
                print("Health: " + str(battle.active_pokemon.current_hp))
                if battle.active_pokemon.current_hp < battle.active_pokemon.current_hp / 2:
                    print("-Healing-")
                    # Find a healing move.
                    HealingMove = max(battle.available_moves, key=lambda move: move.heal)
                    return self.create_order(HealingMove)
        print("Choosing a random move!")
        return self.choose_random_move(battle)

Let me know if this helps!

After the last part of the code I shared, I had an else statement. I have a better understanding of choose_move thanks to the explanation. I decided to take a better look at the getting started section in the docs since I jumped right in previously.