Skip to main content

SDK for writing bots in ploupy.plouc314.ch

Project description

Ploupy Python SDK

Installation

pip install ploupy-sdk

Note
This library requires python 3.10 or higher

Getting started

Here is a minimal example:

import ploupy as pp

class MyBehaviour(pp.Behaviour):
    pass  # the bot code goes here

BOT_KEY = "..." # the key given when creating the bot

bot = pp.Bot(bot_key=BOT_KEY, behaviour_class=MyBehaviour)

if __name__ == "__main__":
    bot.run()

Behaviour

The SDK is events driven. Define the bot by inheriting from the Behaviour class and overriding callback methods.

Callbacks

All callbacks are asynchronous methods, to allow to perform orders and other IO bound tasks. They are automatically called when receiving updates from the server.

Warning
Time-consuming tasks (typically where there is a sleep call) should not be executed directly in a callback, as this could block another callback from being executed. Instead use the start_background_task to execute the task in parrallel.

# this function is very time-consuming
# calling directly in on_turret_build would
# potentially delay other callbacks by several seconds
async def time_consuming_task(behaviour: pp.Behaviour) -> None:
    
    # wait for 5 seconds to give opponents a chance
    await pp.sleep(5)

    tile = behaviour.map.get_tile((10,10))

    # build a new factory
    await behaviour.place_order(pp.BuildFactoryOrder(tile))


class MyBehaviour(pp.Behaviour):

    async def on_turret_build(self, turret: pp.Turret, player: pp.Player) -> None:
        # check that it is the bot that builded the turret
        if player is not self.player:
            return

        # acquire a new tech, this is IO bound (thus the await)
        # but not time-consuming (a requests over a websocket)
        await self.place_order(pp.AcquireTechOrder(pp.Techs.TURRET_SCOPE))

        # start a task in parrallel to avoid delaying other callbacks
        pp.start_background_task(time_consuming_task, self)

Attributes

The Behaviour class expose some useful instances of the game, such as:

  • game (Game): the game instance
  • map (Map): the game's map
  • player (Player): the bot's Player
  • config (GameConfig): The game configuration

Here is an example of a bot that build a factory as soon as he can:

import ploupy as pp

class MyBehaviour(pp.Behaviour):

    async def on_income(self, money: int) -> None:
        # select the tile to build the factory on
        # using Map instance exposed by Behaviour class
        # and the bot's Player instance
        tiles = self.map.get_buildable_tiles(self.player)
        if len(tiles) == 0:
            return
        
        # get one of the possible tiles
        tile = tiles.pop() # tiles is a set

        # check if the bot has enough money
        # using GameConfig instance exposed by Behaviour class
        if money >= self.config.factory_price:
            # send an action to the server
            # this can failed if all the necessary conditions aren't
            # met to perform the action
            try:
                await self.build_factory(tile.coord)
            except pp.ActionFailedException:
                return

Tiles & geometry

The geometry module and Map class provide utility functions / methods to work with coordinates / tiles.

Here is an example of a bot that will try to build a turret when attacked:

import ploupy as pp

class MyBehaviour(pp.Behaviour):

    async def on_probes_attack(
        self,
        probes: list[pp.Probe],
        attacked_player: pp.Player,
        attacking_player: pp.Player,
    ) -> None:
        # check that it is the bot that is attacked
        if attacked_player is not self.player:
            return

        # get the center of where the probes are attacking
        target = pp.geometry.center([probe.target for probe in probes])

        # get tiles where a turret could be built
        tiles = self.map.get_buildable_tiles(self.player)

        # if none are buildable then too bad...
        if len(tiles) == 0:
            return

        # get the tile that is as close as possible to the center of the attack
        tile = pp.geometry.closest_tile(tiles, target)

        # place an order on "build turret" action, the action will be performed
        # when the necessary conditions are met
        await self.place_order(
            pp.BuildTurretOrder(
                tile,
                with_timeout=2.0, # maximum time (sec) before aborting the order
            )
        )

Stages

As the complexity of the bot grows, it can become very handy to encapsulate different types of behaviour. This can be done by defining multiple BehaviourStage (which is essentially the same as Behaviour) and grouping them in a BehaviourDispatcher.

Here is an example that splits the game into different stages:

import ploupy as pp

class EarlyStage(pp.BehaviourStage):
    def __init__(self, dispatcher: pp.BehaviourDispatcher) -> None:
        super().__init__(dispatcher, "early")  # specify stage name

    ...  # the bot's behaviour in early game

class MidStage(pp.BehaviourStage):
    def __init__(self, dispatcher: pp.BehaviourDispatcher) -> None:
        super().__init__(dispatcher, "mid")
    
    async def on_income(self, money: int) -> None:
        
        # switch of BehaviourStage
        await self.set_current_stage("end")

class EndStage(pp.BehaviourStage):
    def __init__(self, dispatcher: pp.BehaviourDispatcher) -> None:
        super().__init__(dispatcher, "end")

    async def on_stage(self) -> None:
        ...  # this callback is called when the stage is selected as current stage

# regroup all stages into one behaviour
class BotBehaviour(pp.BehaviourDispatcher):
    def __init__(self, uid: str, game: pp.Game) -> None:
        super().__init__(uid, game)

        self.add_stage(EarlyStage(self))
        self.add_stage(MidStage(self))
        self.add_stage(EndStage(self))

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ploupy-sdk-0.2.1.tar.gz (35.7 kB view details)

Uploaded Source

Built Distribution

ploupy_sdk-0.2.1-py3-none-any.whl (42.8 kB view details)

Uploaded Python 3

File details

Details for the file ploupy-sdk-0.2.1.tar.gz.

File metadata

  • Download URL: ploupy-sdk-0.2.1.tar.gz
  • Upload date:
  • Size: 35.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.10.7

File hashes

Hashes for ploupy-sdk-0.2.1.tar.gz
Algorithm Hash digest
SHA256 3963d9ed7fdf8c8509a59c857c83a1eb0a0eafd45a31f2b3e389f9ef8e7671fb
MD5 3a47eb1c699bcb862bb2988d2ad99538
BLAKE2b-256 9bc93508a9acdbea6d9276532c222f13e4251763fe0d65a4b75bdab8ea31d509

See more details on using hashes here.

File details

Details for the file ploupy_sdk-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: ploupy_sdk-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 42.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.10.7

File hashes

Hashes for ploupy_sdk-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1db0876205dc4b8dcd171409f807796921eead645e04156daedb5eb954913607
MD5 e302e02140be966dd3c5478d73a7b476
BLAKE2b-256 0b499bca0f614e8f14cdf429b123ef8b0601d350e61db12a5e5648453604c537

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page