GithubHelp home page GithubHelp logo

burnysc2 / python-sc2 Goto Github PK

View Code? Open in Web Editor NEW
477.0 12.0 156.0 16.22 MB

A StarCraft II bot api client library for Python 3

License: MIT License

Python 98.87% Makefile 0.07% Batchfile 0.08% Dockerfile 0.55% Shell 0.43%
hacktoberfest sc2 starcraft2 bot ai

python-sc2's Introduction

Actions Status codecov

A StarCraft II API Client for Python 3

An easy-to-use library for writing AI Bots for StarCraft II in Python 3. The ultimate goal is simplicity and ease of use, while still preserving all functionality. A really simple worker rush bot should be no more than twenty lines of code, not two hundred. However, this library intends to provide both high and low level abstractions.

This library (currently) covers only the raw scripted interface. At this time I don't intend to add support for graphics-based interfaces.

The documentation can be found here. For bot authors, looking directly at the files in the sc2 folder can also be of benefit: bot_ai.py, unit.py, units.py, client.py, game_info.py and game_state.py. Most functions in those files have docstrings, example usages and type hinting.

I am planning to change this fork more radically than the main repository, for bot performance benefits and to add functions to help new bot authors. This may break older bots in the future, however I try to add deprecation warnings to give a heads up notification. This means that the video tutorial made by sentdex is outdated and does no longer directly work with this fork.

For a list of ongoing changes and differences to the main repository of Dentosal, check here.

Installation

By installing this library you agree to be bound by the terms of the AI and Machine Learning License.

For this fork, you'll need Python 3.9 or newer.

Install the pypi package:

pip install --upgrade burnysc2

or directly from develop branch:

pip install poetry
pip install --upgrade --force-reinstall https://github.com/BurnySc2/python-sc2/archive/develop.zip

Both commands will use the sc2 library folder, so you will not be able to have Dentosal's and this fork installed at the same time, unless you use virtual environments or poetry.

StarCraft II

You'll need a StarCraft II executable. If you are running Windows or macOS, just install SC2 from blizzard app.

Linux installation

You can install StarCraft II on Linux with Wine, Lutris or even the Linux binary, but the latter is headless so you cannot actually see the game. Starcraft II can be directly installed from Battlenet once it is downloaded with Lutris. By default, it will be installed here:

/home/burny/Games/battlenet/drive_c/Program Files (x86)/StarCraft II/

Next, set the following environment variables (either globally or within your development environment, e.g. Pycharm: Run -> Edit Configurations -> Environment Variables):

SC2PF=WineLinux
WINE=/usr/bin/wine
# Or a wine binary from lutris:
# WINE=/home/burny/.local/share/lutris/runners/wine/lutris-4.20-x86_64/bin/wine64
# Default Lutris StarCraftII Installation path:
SC2PATH='/home/burny/Games/battlenet/drive_c/Program Files (x86)/StarCraft II/'

WSL

When running WSL in Windows, python-sc2 detects WSL by default and starts Windows Starcraft 2 instead of Linux Starcraft 2. If you wish to instead have the game played in Linux, you can disable this behavior by setting SC2_WSL_DETECT environment variable to "0". You can do this inside python with the following code:

import os
os.environ["SC2_WSL_DETECT"] = "0"

WSL version 1 should not require any configuration. You may be asked to allow Python through your firewall.

When running WSL version 2 you need to supply the following environment variables so that your bot can connect:

SC2CLIENTHOST=<your windows IP>
SC2SERVERHOST=0.0.0.0

If you are adding these to your .bashrc, you may need to export your environment variables by adding:

export SC2CLIENTHOST
export SC2SERVERHOST

You can find your Windows IP using ipconfig /all from PowerShell.exe or CMD.exe.

Maps

You will need maps to run the library.

Official maps

Official Blizzard map downloads are available from Blizzard/s2client-proto.
Extract these maps into their respective subdirectories in the SC2 maps directory.
e.g. install-dir/Maps/Ladder2017Season1/

Bot ladder maps

Maps that are run on the SC2 AI Ladder and SC2 AI Arena can be downloaded from the sc2ai wiki and the aiarena wiki.
Extract these maps into the root of the SC2 maps directory (otherwise ladder replays won't work).
e.g. install-dir/Maps/AcropolisLE.SC2Map

Running

After installing the library, a StarCraft II executable, and some maps, you're ready to get started. Simply run a bot file to fire up an instance of StarCraft II with the bot running. For example:

python examples/protoss/cannon_rush.py

Example

As promised, worker rush in less than twenty lines:

from sc2 import maps
from sc2.player import Bot, Computer
from sc2.main import run_game
from sc2.data import Race, Difficulty
from sc2.bot_ai import BotAI

class WorkerRushBot(BotAI):
    async def on_step(self, iteration: int):
        if iteration == 0:
            for worker in self.workers:
                worker.attack(self.enemy_start_locations[0])

run_game(maps.get("Abyssal Reef LE"), [
    Bot(Race.Zerg, WorkerRushBot()),
    Computer(Race.Protoss, Difficulty.Medium)
], realtime=True)

This is probably the simplest bot that has any realistic chances of winning the game. I have ran it against the medium AI a few times, and once in a while, it wins.

You can find more examples in the examples/ folder.

API Configuration Options

The API supports a number of options for configuring how it operates.

unit_command_uses_self_do

Set this to 'True' if your bot is issueing commands using self.do(Unit(Ability, Target)) instead of Unit(Ability, Target).

class MyBot(BotAI):
    def __init__(self):
        self.unit_command_uses_self_do = True

raw_affects_selection

Setting this to true improves bot performance by a little bit.

class MyBot(BotAI):
    def __init__(self):
        self.raw_affects_selection = True

distance_calculation_method

The distance calculation method:

  • 0 for raw python
  • 1 for scipy pdist
  • 2 for scipy cdist
class MyBot(BotAI):
    def __init__(self):
        self.distance_calculation_method: int = 2

game_step

On game start or in any frame actually, you can set the game step. This controls how often your bot's step method is called.
Do not set this in the __init__ function as the client will not have been initialized yet!

class MyBot(BotAI):
    def __init__(self):
        pass  # don't set it here!

    async def on_start(self):
        self.client.game_step: int = 2

Community - Help and support

You have questions but don't want to create an issue? Join the Starcraft 2 AI Discord server or aiarena.net Discord server. Questions about this repository can be asked in text channel #python. There are discussions and questions about SC2 bot programming and this repository every day.

Bug reports, feature requests and ideas

If you have any issues, ideas or feedback, please create a new issue. Pull requests are also welcome!

Contributing & style guidelines

Git commit messages use imperative-style messages, start with capital letter and do not have trailing commas.

To run pre-commit hooks (which run autoformatting and autosort imports) you can run

poetry run pre-commit install
poetry run pre-commit run --all-files --hook-stage push

python-sc2's People

Contributors

aiseeq avatar alexander-kazakov avatar alvarofpp avatar archiatrus avatar arthurferier avatar burnysc2 avatar danielvschoor avatar dentosal avatar drakonnan1st avatar drinfy avatar eladyaniv01 avatar frigerius avatar gusgus01 avatar h3nnn4n avatar islamelnabarawy avatar jaros3 avatar kaszanas avatar l0kr avatar lladdy avatar mac2000 avatar matuiss2 avatar merfolk avatar mjschuetze102 avatar raspersc2 avatar reypader avatar sharrap avatar soupcatcher avatar sturmianseq avatar tweakimp avatar ultramachine avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

python-sc2's Issues

bot_ai.py typo

Incorrect:

bot_ai.py

if self.enemy_structures:
            visible_enemy_structures = self.enemy_structures.tags
            for enemy_structure_tag in self._enemy_units_previous_map.keys():
                if enemy_structure_tag not in visible_enemy_structures:
                    await self.on_enemy_unit_left_vision(enemy_structure_tag)

Correct:
if self.enemy_structures:
visible_enemy_structures = self.enemy_structures.tags
for enemy_structure_tag in self.enemy structures _previous_map.keys():
if enemy_structure_tag not in visible_enemy_structures:
await self.on_enemy_unit_left_vision(enemy_structure_tag)

tested it - works

Error with numpy package on windows

Reported by Merk in discord:

RuntimeError: The current Numpy installation ('C:\Users\****\AppData\Local\Programs\Python\Python38\lib\site-packages\numpy\init.py') fails to pass a sanity check due to a bug in the windows runtime. See this issue for more information: https://tinyurl.com/y3dm3h86

Solution: Limit numpy to version 1.19.3 or older

Add support to watch replays

pysc2 and the c++ libraries allowed to watch replays regardless of game version, as far as I know.
If this could be easily maintaned, maybe this should be added to the library in a replay.py file.

self.larva_count not working properly

I tested using this print(f"{self.larva_count = }, {len(self.larva) = }") on version 4.11.2

Both should return the same number, but self.larva_count always returns 0

image

Protectiles trigger on_unit_destroyed

I was trying to create statistics over units lost between player 1 and player 2.

I was capturing the events from on_unit_destroyed, thinking that would give me the information I needed. But it turns out that this method is also called upon projectiles hitting their target and disappearing. The reason is that game_state.dead_units fires when the game object for the projectiles are destroyed.

Curiously, on_unit_created is not called when firing.

This is the case for projectiles from at least Stalkers, Adepts and Canons.

Would it make sense to filter the events in the game_state.dead_units stream?

Fix workers entering and leaving geysir

The issues mentioned here
Dentosal/python-sc2#168
Dentosal/python-sc2#169
could be fixed by:

  • Checking if the worker in the previous frame was near a geyser, had the order to mine from that geyser, and its HP were max (which means it entered the geyser, or got oneshotted by a sieged tank or by a nuke)
  • Checking if the worker didn't exist in the previous frame, and now carries vespene (which means it just left the refinery)

    python-sc2/sc2/unit.py

    Lines 477 to 480 in 9caa515

    @property_immutable_cache
    def is_carrying_vespene(self) -> bool:
    """ Checks if a worker is carrying vespene gas. """
    return not IS_CARRYING_VESPENE.isdisjoint(self.buffs)

Include documentation generated with Sphinx

First working version in gh-pages branch with url: https://burnysc2.github.io/python-sc2

TODO:

  • Write some documentation, introducing the library and its code structure
  • Add example usages for each slightly more complicated function
  • Update README.md in root folder with link to it
  • Add travis script to autodeploy and update the documentation if there are pushes on develop or master branch ( https://sphinxtechnicalwriting.readthedocs.io/en/latest/cd/config-travis.html )
  • Add params to each function similar to
    def furthest(self, ps: Union[Units, List[Point2], Set[Point2]]) -> Union[Unit, Pointlike]:
        """ This function assumes the 2d distance is meant

        :param ps: Units object, or iterable of Unit or Point2 """
        assert ps, f"ps is empty"
        return max(ps, key=lambda p: self.distance_to(p))

Add instruction on how to install this repo using pip

We need a command in the readme on how to install this repo similar to a command from here Dentosal/python-sc2#266 (comment) so people can start using it in SC2 client version 4.9.0

Should be able to install this fork with command
pip install pipenv burnysc2 --upgrade
If you are on linux or mac, you might have to use pip3 or pip3.7 instead of pip

Changes that bot authors have to consider when changing to this fork (work in progress):

General:

  • Numpy and scipy are now part of the requirements
  • Scipy is used to quickly calculate the distances between all units every frame (distances.py)
  • self.on_start() function is now async, self.on_start_async() has been removed
  • self.on_end() function is now async
  • Some effects, that are marked as Unit in the API, were moved to effects

Units management:

  • self.units (previously units and structures) was split into self.units and self.structures
  • self.known_enemy_units (enemy units and structures) and self.known_enemy_structures (only enemy structures) are now changed and renamed into self.enemy_units (only enemy units) and self.enemy_structures (only enemy structures)
  • self.geysers is renamed to self.gas_buildings (refineries, extractors, assimilators)
  • self.state.mineral_field and self.state.vespene_geyser are now in the BotAI class (property useable through self.mineral_field and self.vespene_geyser). self.resources contains both.
  • self.state.units is now self.all_units which contains all units (self, enemy, neutral)
  • Creating a new Units object now requires the bot object, because cached distances are stored in the bot object and each Unit object needs to be able to access it. So new Units objects have to be created with Units([], self)

Actions management:

  • self.do(action) is no longer async (no await needed). This function is recommended over self.actions.append(action) again. self.do(action, subtract_cost=False, subtract_supply=False, can_afford_check=False) has now optional parameters. When a build command is given, subtract_cost=True is recommended. When a unit train command is given, subtract_cost=True, subtract_supply=True is recommended.
  • self.do_action(actions_list) has become a private function self._do_actions(actions_list) which automatically executes self.actions at the end of the step and clears the list (executed by main.py)
  • self.already_pending() now checks all unit orders by default, not just worker orders

Effects:

  • Reaper grenade, parasitic bomb (after the unit died) and forcefield are now emulated effects in self.state.effects instead of units/structures

Debug draw:

  • await self._client._send_debug() is now automatically executed and emptied after the user issues draw shape requests through self._client.debug_box_out() etc. functions. See Ramp Wall Bot for more examples on debugging commands.

New bot_ai functions:

def step_time() -> Tuple[float, float, float, float]
def calculate_supply_cost(unit_type: UnitTypeId) -> float
def calculate_cost(item_id: Union[UnitTypeId, UpgradeId, AbilityId]) -> Cost
experimental: def train(unit_type: UnitTypeId, amount: int = 1, closest_to: Point2 = None, train_only_idle_buildings: bool = True) -> int
def structure_type_build_progress(structure_type: Union[UnitTypeId, int]) -> float
def tech_requirement_progress(structure_type: UnitTypeId) -> float
experimental: def research(upgrade_type: UpgradeId) -> bool
def in_map_bounds(pos: Union[Point2, tuple]) -> bool

New function for Unit.py:
Unit.is_facing(other_unit: Unit)

Added helpful dictionaries in sc2/dicts folder

Started with a documentation and tutorial

Added more tests in /test folder

Bug fixes:

  • Only one train command per structure was issued per frame (e.g. you couldn't make 200 Zerglings in a single frame if you had 100 larvae)

Write more tests

Test the following scenarios:

  • Have burrowed or invisible enemy units next to terran structure that want to build an addon - will there be an action_error?
  • Have a tank from high ground or far distance (with vision helper) that should be displayed as "snapshot" - will it be a "snapshot" or will the visibility "visible"?
  • Test if a bunker loaded with units is marked as .is_active, or only if it is shooting.

Client.debug_sphere_out accepts Point2 object, yet it leads to an error

You theoretically can draw a sphere on the map, and you have to provide either Unit, Point2, or Point3 object.
But when you do the method itself returns without errors but it leads to "AttributeError: 'Point2' object has no attribute 'as_Point'" Exception.
In my case I used Point2 object.

Example code:

    async def on_step(self, iteration):
        self.client.debug_sphere_out(
            p=self.townhalls[0],
            r=1
        )

Update Example bots with new syntax and added comments

  • Protoss example bots
  • Terran example bots
  • Zerg example bots
  • Create building grid function so that cyclone rush and one base BC example bots work again because they need space for addons next to factory and starport

mass_reaper.py error

I apologize in advance as I am not well versed in python or github.

Line 89 of the mass_reaper.py example I am getting an error right as the game starts. Not sure what is going wrong.

barracks_tech_requirement: float = self.tech_requirement_progress(UnitTypeId.BARRACKS)

Thanks for your help!

generate_id_constants_from_stableid.py update from json

2021-03-20 23:49:53.804 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.launched (was None)
2021-03-20 23:49:53.817 | INFO     | sc2.controller:create_game:36 - Creating new game
2021-03-20 23:49:53.821 | INFO     | sc2.controller:create_game:37 - Map:     AbyssalReefLE2
2021-03-20 23:49:53.825 | INFO     | sc2.controller:create_game:38 - Players: Bot WorkerRushBot(Terran), Bot WorkerRushBot1(Zerg)
2021-03-20 23:49:53.832 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.init_game (was Status.launched)
2021-03-20 23:49:53.835 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.launched (was None)
2021-03-20 23:50:18.990 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.in_game (was None)
2021-03-20 23:50:18.994 | INFO     | sc2.main:_play_game:328 - Player 2 - Bot WorkerRushBot1(Zerg)
2021-03-20 23:50:19.382 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.in_game (was None)
2021-03-20 23:50:19.384 | INFO     | sc2.main:_play_game:328 - Player 1 - Bot WorkerRushBot(Terran)
2021-03-20 23:50:26.532 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.ended (was Status.in_game)
2021-03-20 23:50:26.557 | INFO     | sc2.main:_play_game:335 - Result for player 2 - Bot WorkerRushBot1(Zerg): Defeat
2021-03-20 23:50:26.562 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.ended (was Status.in_game)
2021-03-20 23:50:26.592 | INFO     | sc2.main:_play_game:335 - Result for player 1 - Bot WorkerRushBot(Terran): Victory
2021-03-20 23:50:27.858 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.launched (was Status.ended)
2021-03-20 23:50:27.863 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.quit (was Status.launched)
2021-03-20 23:50:27.867 | INFO     | sc2.sc2process:_close_connection:231 - Closing connection at 15669...
2021-03-20 23:50:27.871 | INFO     | sc2.sc2process:kill_all:37 - kill_switch: Process cleanup for 2 processes
2021-03-20 23:50:27.872 | INFO     | sc2.sc2process:_clean:240 - Cleaning up...
2021-03-20 23:50:28.383 | INFO     | sc2.sc2process:_clean:273 - Cleanup complete
2021-03-20 23:50:28.386 | INFO     | sc2.sc2process:_clean:240 - Cleaning up...
2021-03-20 23:50:29.413 | INFO     | sc2.sc2process:_clean:273 - Cleanup complete
2021-03-20 23:50:29.417 | INFO     | sc2.protocol:__request:47 - Cannot receive: Game has already ended.
2021-03-20 23:50:29.420 | INFO     | sc2.sc2process:_close_connection:231 - Closing connection at None...
2021-03-20 23:50:29.424 | INFO     | sc2.sc2process:kill_all:37 - kill_switch: Process cleanup for 2 processes
2021-03-20 23:50:29.427 | INFO     | sc2.sc2process:_clean:240 - Cleaning up...
2021-03-20 23:50:29.431 | INFO     | sc2.sc2process:_clean:273 - Cleanup complete
2021-03-20 23:50:29.435 | INFO     | sc2.sc2process:_clean:240 - Cleaning up...
2021-03-20 23:50:29.439 | INFO     | sc2.sc2process:_clean:273 - Cleanup complete
Traceback (most recent call last):
  File "c:/Users/wwwfe/Documents/GitHub/python-sc2/map_shamo (1).py", line 43, in <module>
    run_game(maps.get("AbyssalReefLE2"), [
  File "c:\Users\wwwfe\Documents\GitHub\python-sc2\sc2\main.py", line 596, in run_game
    result = asyncio.get_event_loop().run_until_complete(
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0\lib\asyncio\base_events.py", line 616, in run_until_complete
    return future.result()
  File "c:\Users\wwwfe\Documents\GitHub\python-sc2\sc2\main.py", line 485, in _host_game
    await client.quit()
  File "c:\Users\wwwfe\Documents\GitHub\python-sc2\sc2\protocol.py", line 87, in quit
    await self._execute(quit=sc_pb.RequestQuit())
  File "c:\Users\wwwfe\Documents\GitHub\python-sc2\sc2\protocol.py", line 68, in _execute
    response = await self.__request(sc_pb.Request(**kwargs))
  File "c:\Users\wwwfe\Documents\GitHub\python-sc2\sc2\protocol.py", line 36, in __request
    await self._ws.send_bytes(request.SerializeToString())
  File "C:\Users\wwwfe\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\aiohttp\client_ws.py", line 155, in send_bytes
    await self._writer.send(data, binary=True, compress=compress)
  File "C:\Users\wwwfe\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\aiohttp\http_websocket.py", line 685, in send
    await self._send_frame(message, WSMsgType.BINARY, compress)
  File "C:\Users\wwwfe\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\aiohttp\http_websocket.py", line 598, in _send_frame
    raise ConnectionResetError("Cannot write to closing transport")
ConnectionResetError: Cannot write to closing transport2021-03-20 23:49:53.804 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.launched (was None)
2021-03-20 23:49:53.817 | INFO     | sc2.controller:create_game:36 - Creating new game
2021-03-20 23:49:53.821 | INFO     | sc2.controller:create_game:37 - Map:     AbyssalReefLE2
2021-03-20 23:49:53.825 | INFO     | sc2.controller:create_game:38 - Players: Bot WorkerRushBot(Terran), Bot WorkerRushBot1(Zerg)
2021-03-20 23:49:53.832 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.init_game (was Status.launched)
2021-03-20 23:49:53.835 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.launched (was None)
2021-03-20 23:50:18.990 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.in_game (was None)
2021-03-20 23:50:18.994 | INFO     | sc2.main:_play_game:328 - Player 2 - Bot WorkerRushBot1(Zerg)
2021-03-20 23:50:19.382 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.in_game (was None)
2021-03-20 23:50:19.384 | INFO     | sc2.main:_play_game:328 - Player 1 - Bot WorkerRushBot(Terran)
2021-03-20 23:50:26.532 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.ended (was Status.in_game)
2021-03-20 23:50:26.557 | INFO     | sc2.main:_play_game:335 - Result for player 2 - Bot WorkerRushBot1(Zerg): Defeat
2021-03-20 23:50:26.562 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.ended (was Status.in_game)
2021-03-20 23:50:26.592 | INFO     | sc2.main:_play_game:335 - Result for player 1 - Bot WorkerRushBot(Terran): Victory
2021-03-20 23:50:27.858 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.launched (was Status.ended)
2021-03-20 23:50:27.863 | INFO     | sc2.protocol:_execute:72 - Client status changed to Status.quit (was Status.launched)
2021-03-20 23:50:27.867 | INFO     | sc2.sc2process:_close_connection:231 - Closing connection at 15669...
2021-03-20 23:50:27.871 | INFO     | sc2.sc2process:kill_all:37 - kill_switch: Process cleanup for 2 processes
2021-03-20 23:50:27.872 | INFO     | sc2.sc2process:_clean:240 - Cleaning up...
2021-03-20 23:50:28.383 | INFO     | sc2.sc2process:_clean:273 - Cleanup complete
2021-03-20 23:50:28.386 | INFO     | sc2.sc2process:_clean:240 - Cleaning up...
2021-03-20 23:50:29.413 | INFO     | sc2.sc2process:_clean:273 - Cleanup complete
2021-03-20 23:50:29.417 | INFO     | sc2.protocol:__request:47 - Cannot receive: Game has already ended.
2021-03-20 23:50:29.420 | INFO     | sc2.sc2process:_close_connection:231 - Closing connection at None...
2021-03-20 23:50:29.424 | INFO     | sc2.sc2process:kill_all:37 - kill_switch: Process cleanup for 2 processes
2021-03-20 23:50:29.427 | INFO     | sc2.sc2process:_clean:240 - Cleaning up...
2021-03-20 23:50:29.431 | INFO     | sc2.sc2process:_clean:273 - Cleanup complete
2021-03-20 23:50:29.435 | INFO     | sc2.sc2process:_clean:240 - Cleaning up...
2021-03-20 23:50:29.439 | INFO     | sc2.sc2process:_clean:273 - Cleanup complete
Traceback (most recent call last):
  File "c:/Users/wwwfe/Documents/GitHub/python-sc2/map_shamo (1).py", line 43, in <module>
    run_game(maps.get("AbyssalReefLE2"), [
  File "c:\Users\wwwfe\Documents\GitHub\python-sc2\sc2\main.py", line 596, in run_game
    result = asyncio.get_event_loop().run_until_complete(
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.1776.0_x64__qbz5n2kfra8p0\lib\asyncio\base_events.py", line 616, in run_until_complete
    return future.result()
  File "c:\Users\wwwfe\Documents\GitHub\python-sc2\sc2\main.py", line 485, in _host_game
    await client.quit()
  File "c:\Users\wwwfe\Documents\GitHub\python-sc2\sc2\protocol.py", line 87, in quit
    await self._execute(quit=sc_pb.RequestQuit())
  File "c:\Users\wwwfe\Documents\GitHub\python-sc2\sc2\protocol.py", line 68, in _execute
    response = await self.__request(sc_pb.Request(**kwargs))
  File "c:\Users\wwwfe\Documents\GitHub\python-sc2\sc2\protocol.py", line 36, in __request
    await self._ws.send_bytes(request.SerializeToString())
  File "C:\Users\wwwfe\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\aiohttp\client_ws.py", line 155, in send_bytes
    await self._writer.send(data, binary=True, compress=compress)
  File "C:\Users\wwwfe\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\aiohttp\http_websocket.py", line 685, in send
    await self._send_frame(message, WSMsgType.BINARY, compress)
  File "C:\Users\wwwfe\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\aiohttp\http_websocket.py", line 598, in _send_frame
    raise ConnectionResetError("Cannot write to closing transport")
ConnectionResetError: Cannot write to closing transport


I use generate_id_constants_from_stableid.py update from stableid.json to ability_id.py
after it, I run the custom map.
when the unit SCV builds the custom building, it throws off.

_host_game_iter launches game twice

_host_game_iter is the iterator that supposed to reuse the game client which allows to just reload a bot script without closing the instance.
But most of the times it relaunches the game right after it finished.
While intended behavior is launch the game once, and reload only if user is requested it.

Replay Error and Solution

Seeing the WorkersRush Replay did not work for me. -> I got the error that there is a TypeError. "has type WindowsPath, but expected one of: bytes, unicode".

I solved this problem by adding "run_replay(my_observer_ai, replay_path=str(replay_path))" in the code.

Unfortunately, seeing replays does not yet work if they come from another source. I have downloaded the official replays from Deepmind. But with these the entry "DATA VERSION" seems to be missing in the replay files.

Do you know a solution ? Is the error known ? Or do replays generally only work if you generated them yourself ?

Greetings Dean

Client.chat_send() team_only argument should be default to False

There is a possibility to send a chat message into the in-game chat. But you also should specify should you send it team_only as well.
I think it is not necessary since any existing bot is almost certainly in the nearby future will not play team games.
Therefore this argument should be default to "False".

Realtime=True may swallow API information

This code skips steps:

python-sc2/sc2/main.py

Lines 197 to 206 in 511c34f

# On realtime=True, might get an error here: sc2.protocol.ProtocolError: ['Not in a game']
try:
requested_step = gs.game_loop + client.game_step
state = await client.observation(requested_step)
# If the bot took too long in the previous observation, request another observation one frame after
if state.observation.observation.game_loop > requested_step:
# TODO Remove these 2 comments
# t = state.observation.observation.game_loop
state = await client.observation(state.observation.observation.game_loop + 1)
# print(f"Requested step: {requested_step}, received: {t}, new: {state.observation.observation.game_loop}")

Swallowed information, that need to be cached, are data from the game state:

Change some units (Reaper grenade) to effects

  • Reaper grenade should be an effect and not a structure
  • Parasitc bomb (after the unit has died, the effect still lives on as a unit) should be an effect
  • Force field should be an effect not a unit

This can probably be achieved here

for unit in self.state.observation_raw.units:

by removing them from units and adding them to self.state.effects

  • Fix ravager bile effect
    While we are at it, the ravager bile is removed from the API too early before the bile has landed on the destination. Units will no longer try to dodge this and will probably take damage. This might need to be stored for a couple frames, or should this be handled bot specifically?
  • Tactical Nuke probably has the same problem

An issue in the documentation

Heya;
I'm sure if I should report this, as I'm not very experienced with both the Python nor GitHub; that being said:

This snippet of code that is under "Creating a bot"

import sc2
from sc2.bot_ai import BotAI
class MyBot(BotAI):
    async def on_step(self, iteration: int):
        print(f"This is my bot in iteration {iteration}!"

sc2.run_game(
    sc2.maps.get(map), [Bot(Race.Zerg, MyBot()), Computer(Race.Zerg, Difficulty.Hard)], realtime=False
)

Does not seem to work. First issue I think I see is that there's a bracket missing in the print statement, but even then, it won't open the game - it just crashes after CMD window appears for while.

ability_id is not up to date for 5.0.6

For now, in SC2 AI Arena the maps are in version 5.0.6. But the ability-id is currently 5.0.3. It would be nice to have 5.0.6 ability ids because Protoss macro builds heavily relies on shield battery and AbilityId.BATTERYOVERCHARGE_BATTERYOVERCHARGE is no longer working.

Thanks for all the way along!

Unable to open replay and further related errors

Hello!

I encountered "AssertionError: 7 - Unable to open replay." (controller.py line 80) while trying to execute the code I copied and modified a bit from examples/watch_replay.py. Basically I just left out the path operations from line 29-45 and gave run_replay an absolute path to the replay.

I am running this code under Ubuntu 16.04 and found in controller.py, line 66f., in start_replay that the replay name is extracted and given to sc_pb.RequestStartReaply(...) which results in the above error. After i changed replay_path back to the absolute path this error was fixed.

But before a replay could be started another error occurred: AssertionError: 32384 259072, pixel_map.py, line 24, in _init_. in_bits seems to be set incorrectly. For now I hand coded it to False, but im going to investigate further why this happens.

Now the replays run a while (in one case 9440 iterations, which i recon is the whole game) until this error occurs:
File "/home/psg/.conda/envs/sc2ai/lib/python3.7/site-packages/sc2/main.py", line 304, in _play_replay
await ai.on_end(client._game_result[player_id])
KeyError: 0
I downloaded the replays using the official replay-api from s2client-proto for StarCraft Version 4.7.1. So far I haven't looked into this one.

I wanted to let you know these errors occurs. Maybe it's because of my setup or another error I made, but to me it doesn't seem that way. I will look further into the causes of the wrongly set in_bits flag and the key error in the near future. For my purposes at the moment it's enough that I can run the replays.

Cheers,
psg

Option to suppress sc2 client start up messages

Suppress linux sc2 client start up message:

Launching next game.
Next launch phase started: 2
Next launch phase started: 3
Next launch phase started: 4
Next launch phase started: 5
Next launch phase started: 6
Next launch phase started: 7
Next launch phase started: 8

return subprocess.Popen(
args,
cwd=(str(Paths.CWD) if Paths.CWD else None),
# , env=run_config.env
)

Change it to something like

        return subprocess.Popen(
            args,
            cwd=(str(Paths.CWD) if Paths.CWD else None),
            stdout=subprocess.DEVNULL, 
            stderr=subprocess.DEVNULL
            # , env=run_config.env
        )

but be able to set it (enabled/disable) in the bot init function or by using argparse?

_host_game_iter is pointless unless all the modules are reloaded

In the given example of fast reload we're reloading the code of example bot, and it works.
Until you decide to split the code in multiple modules, and python's reload function of importlib module works in a way that it only reloads the module it is pointed at, ignoring all the imports, parent classes this module depends on.
So it is perhaps a python issue, but until we find the working solution to reload all the modules within the bot, the example is useless.

race_gas doesn't account for rich gas buildings

race_gas dictionary in data.py

race_gas: Dict[Race, UnitTypeId] = {
    Race.Protoss: UnitTypeId.ASSIMILATOR,
    Race.Terran: UnitTypeId.REFINERY,
    Race.Zerg: UnitTypeId.EXTRACTOR,
}

should be something like this, or add a new "race_all_gas" to keep compatibility.

race_gas: Dict[Race, List[UnitTypeId]] = {
    Race.Protoss: [UnitTypeId.ASSIMILATOR, UnitTypeId.ASSIMILATORRICH],
    Race.Terran: [UnitTypeId.REFINERY, UnitTypeId.REFINERYRICH],
    Race.Zerg: [UnitTypeId.EXTRACTOR, UnitTypeId.EXTRACTORRICH],
    Race.Random: [UnitTypeId.EXTRACTOR, UnitTypeId.EXTRACTORRICH,
            UnitTypeId.ASSIMILATOR, UnitTypeId.ASSIMILATORRICH,
            UnitTypeId.EXTRACTOR, UnitTypeId.EXTRACTORRICH]
}

and then the _prepare_units method in sc2/bot_ai.py needs to account for both rich and non-rich gas buildings

elif unit_id == race_gas[self.race]:
	self.gas_buildings.append(unit_obj)

->

elif unit_id in race_all_gas[self.race]:
	self.gas_buildings.append(unit_obj)

call to _find_expansion_locations can fail on maps with no "good" place for townhall

This function generates group of locations: 'possible_points'.
Then this group is eliminated based on distance to closest resources.
In case none is left, later min() function for distance fails as it has no arguments to compare.
Fix below enables playing on wider variety of maps. (Like many maps for 3+ players where resource placement is not perfect.)

Fix (located just above '# Choose best fitting point'):

$ diff ./python-sc2-develop/sc2/bot_ai.py ./python-sc2-develop_fixes/sc2/bot_ai.py
365a366,369
>             #Recast generator object 'possible_points' to list as no further changes are done. Skip processing of this loop if no 'possible_points' left.
>             possible_points = list(possible_points)
>             if len(possible_points) == 0:
>                 continue

Missing tech tree and list of unit abilities

  • Race tech tree dict (which unit/structure has which requirement
tech_tree = {
  THOR: [ARMORY, FACTORYTECHLAB],
}
  • Unit Abilities dict (which unit has which abilities)
unit_abilities = {
  FACTORY: [build techlab, build reactor, set rally point, lift],
  FACTORYFLYING: [build techlab at position, build reactor at position, land, move, stop, hold position],
}

rare bug with Point2.__bool__

I have a use case where I need to convert a 2d NumPy array of coordinates to a list of Point2

calling list(map(Point2, my_array)) Works
however, when running a bool check on any item in the list (Point2 type)

Example:
let's say I computed a path, as a numpy 2d array of points
then i return it as list(map(Point2, my_path_array))
picking a point from that list, let's call it position
and running a bool check on position causes this unexpected behavior

    if position:
TypeError: __bool__ should return bool, returned numpy.bool_

which is caused by mapping numpy numeric types as python numeric types

so this __bool__ override returns numpy bool instead of python bool
https://github.com/BurnySc2/python-sc2/blob/develop/sc2/position.py#L287

i managed to work around it by calling
my_path_list = path.tolist()

but I can see how this might become an issue for other users who want to compute coordinates using numpy arrays
so I thought I should let you know

Unit.mineral_contents and Unit.vespene_contents don't update

The title is self explanatory.
These attributes don't update on game progressing.
Most of mineral_patch and vespene_geyser units has these attributes set to 0, since they're not scouted, yet not the ones that are visible from the beginning, nor the ones that been scouted update these attributes.
Or should I use something else to see the current amount of resources left in the patch/geyser?

How to adjust frames per step

Currently, it takes about 1 second to perform a step so I cannot execute enough actions per second. As a result, I cannot make the marines hit and run.

Add 'gotchas' to documentation

Python-SC2 flaws:

  • How to 'renew' Unit objects
  • How to keep track of a Unit object over multiple frames
  • Snapshots (once seen, not currently visible) have different tag than visible Unit
  • Units that once went out of vision, morphed, and came back into vision, still have the same tags but different type_id (UnitTypeId)
  • Some UnitTypeIds are confusing: Viking, VikingFighter, VikingAssault, LurkerDenMP, LurkerMP, LurkerBurrowedMP
  • Distance calculation pdist and cdist: index X is out of bounds for axis 0 with size Y -> you are using Unit objects from an older game loop

SC2 API flaws:

  • Can't unload single specific units from transports (bunker, medivac, prism, nydus, dropperlord)
  • Cast range for creep tumors is 0 (while it is actually 10 if casted by non-queen)
  • Workers are listed in self.state.dead_units when entering gas buildings and get called on unit finished when they leave the gas building
  • Weapons (or weapon cooldown) is missing for some units, see #7
  • I put bot as player 1 and human as player 2, why doesn't it work? -> Have to put human as player 1
  • Which unit is my cyclone locked on to? Or the other perspective: which cyclone is locked on to my unit? - Almost impossible to figure out

Good to know:

See also here: https://aiarena.net/wiki/bot-development/#wiki-toc-api-disparities

Add example script to re-use a SC2 instance

When developing bots, it can be annoying to wait for SC2 to launch before the bot connects to it.

Following idea could speed up development:

  1. Script to launch a SC2 instance (ready state)
  2. Script to launch the bot and let the bot connect to the SC2 instance, and play the game
  3. Script to terminate the bot and disconnects it from the SC2 instance, then puts SC2 back into the ready state

This way you have to launch script 1) once, then can use scripts 2) and 3) over and over without having to wait for SC2 to launch.

The fastreload.py script seems to achieve this somewhat: https://github.com/BurnySc2/python-sc2/blob/develop/examples/fastreload.py

Also:

async def a_run_multiple_games_nokill(matches: List[GameMatch]) -> List[Dict[AbstractPlayer, Result]]:

Add possibility to start bot game with older SC2 client version

Add guide or example on how to start a game with an older SC2 client version.
This way people will not have to downgrade their SC2 and load another 20-30 gb of StarCraft 2.

It can work, but this needs to be added to the library, maybe even as a function parameter in sc2.run_game().

TODO: Need to write script that automatically downloads the latest https://github.com/Blizzard/s2client-proto/blob/master/buildinfo/versions.json version and puts them in sc2/versions.json path.

Is it possible to enter custom x,y coordinates for a location?

I am trying to enter specific x,y coordinates for the build() function but when I try to enter the coordinates as list or tuple i get an assertion error. When trying to find the type of a position of an in-game object I see that the type is <class 'sc2.position.Point2'>. I don't see any clear way to enter in custom x,y coordintes, as the it wants me to target a unit, point, or none... Is there a way to do this? Thanks!

Missing conversions between AbilityId and other Ids

For the implementation result, see: https://github.com/BurnySc2/python-sc2/tree/develop/sc2/dicts

  • AbilityId <=> UnitTypeId conversion dict (unit creation ability)
unit_creation_abilities = {
  # Adept train can have multiple abilities
  ADEPT: [gatewaytrain adept, warpgate warp adept],
}
  • AbilityId <=> UpgradeId conversion dict (upgrade research ability)
upgrade_research_abilities = {
  # Combatshield research
  SHIELDWALL: RESEARCH_COMBATSHIELD,
}
  • AbilityId <=> BuffId conversion dict (which ability causes which buff)
buff_creation_abilities = {
  # Marine and marauder stim
  EFFECT_STIM_MARINE: STIMPACK,
  EFFECT_STIM_MARAUDER: STIMPACKMARAUDER,
}
  • AbilityId <=> EffectId conversion dict (which ability causes which effect to happen)
effect_creation_abilities = {
  # Ravager
  EFFECT_CORROSIVEBILE: RAVAGERCORROSIVEBILECP,
}

Suppress spammy messages

After the game is over, sometimes the bot spams

INFO:sc2.protocol:Client status changed to Status.launched (was Status.in_replay)
INFO:sc2.sc2process:kill_switch: Process cleanup
INFO:sc2.sc2process:Cleaning up...
INFO:sc2.sc2process:Cleanup complete
INFO:sc2.sc2process:Cleaning up...
INFO:sc2.sc2process:Cleanup complete
INFO:sc2.sc2process:Cleaning up...
INFO:sc2.sc2process:Cleanup complete
INFO:sc2.sc2process:Cleaning up...

sc2.protocol.ProtocolError: ['Not in a game']

Minimal bot example provided by SoupCatcher

import sc2
from sc2 import BotAI, Race
from sc2.player import Bot

class NoBot(BotAI):
  async def on_step(self, iteration):
    return

class ResignBot(BotAI):
  async def on_step(self, iteration):
    if iteration == 10:
      await self._client.leave()

def main():
    sc2.run_game(
        sc2.maps.get("EverDream506"),
        [Bot(Race.Random, ResignBot()), Bot(Race.Random, NoBot())],
        realtime=False,
        save_replay_as="test.SC2Replay"
    )

if __name__ == "__main__":
    main()

with trace:

Traceback (most recent call last):
  File "resign.py", line 24, in <module>
    main()
  File "resign.py", line 16, in main
    sc2.run_game(
  File ".../sc2/main.py", line 596, in run_game
    result = asyncio.get_event_loop().run_until_complete(
  File "/usr/local/***/[email protected]/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File ".../sc2/main.py", line 483, in _host_game
    await client.save_replay(save_replay_as)
  File ".../sc2/client.py", line 117, in save_replay
    result = await self._execute(save_replay=sc_pb.RequestSaveReplay())
  File ".../sc2/protocol.py", line 77, in _execute
    raise ProtocolError(f"{response.error}")
sc2.protocol.ProtocolError: ['Not in a game']

The game ended correctly, yet there is still an error trace, which does not belong there.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.