Package: ploupy-sdk
Ploupy Python SDKInstallationpip install ploupy-sdkNoteThis library requires python 3.10 or higherGetting startedHere is a minimal example:importploupyasppclassMyBehaviour(pp.Behaviour):pass# the bot code goes hereBOT_KEY="..."# the key given when creating the botbot=pp.Bot(bot_key=BOT_KEY,behaviour_class=MyBehaviour)if__name__=="__main__":bot.run()BehaviourThe SDK is events driven. Define the bot by inheriting from theBehaviourclass
and overriding callback methods.CallbacksAll callbacks are asynchronous methods, to allow to perform orders and other IO bound
tasks. They are automatically called when receiving updates from the server.WarningTime-consuming tasks (typically where there is asleepcall) should not be executed
directly in a callback, as this could block another callback from being executed.
Instead use thestart_background_taskto execute the task in parrallel.# this function is very time-consuming# calling directly in on_turret_build would# potentially delay other callbacks by several secondsasyncdeftime_consuming_task(behaviour:pp.Behaviour)->None:# wait for 5 seconds to give opponents a chanceawaitpp.sleep(5)tile=behaviour.map.get_tile((10,10))# build a new factoryawaitbehaviour.place_order(pp.BuildFactoryOrder(tile))classMyBehaviour(pp.Behaviour):asyncdefon_turret_build(self,turret:pp.Turret,player:pp.Player)->None:# check that it is the bot that builded the turretifplayerisnotself.player:return# acquire a new tech, this is IO bound (thus the await)# but not time-consuming (a requests over a websocket)awaitself.place_order(pp.AcquireTechOrder(pp.Techs.TURRET_SCOPE))# start a task in parrallel to avoid delaying other callbackspp.start_background_task(time_consuming_task,self)AttributesTheBehaviourclass expose some useful instances of the game,
such as:game (Game): the game instancemap (Map): the game's mapplayer (Player): the bot's Playerconfig (GameConfig): The game configurationHere is an example of a bot that build a factory as soon as he can:importploupyasppclassMyBehaviour(pp.Behaviour):asyncdefon_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 instancetiles=self.map.get_buildable_tiles(self.player)iflen(tiles)==0:return# get one of the possible tilestile=tiles.pop()# tiles is a set# check if the bot has enough money# using GameConfig instance exposed by Behaviour classifmoney>=self.config.factory_price:# send an action to the server# this can failed if all the necessary conditions aren't# met to perform the actiontry:awaitself.build_factory(tile.coord)exceptpp.ActionFailedException:returnTiles & geometryThegeometrymodule andMapclass 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:importploupyasppclassMyBehaviour(pp.Behaviour):asyncdefon_probes_attack(self,probes:list[pp.Probe],attacked_player:pp.Player,attacking_player:pp.Player,)->None:# check that it is the bot that is attackedifattacked_playerisnotself.player:return# get the center of where the probes are attackingtarget=pp.geometry.center([probe.targetforprobeinprobes])# get tiles where a turret could be builttiles=self.map.get_buildable_tiles(self.player)# if none are buildable then too bad...iflen(tiles)==0:return# get the tile that is as close as possible to the center of the attacktile=pp.geometry.closest_tile(tiles,target)# place an order on "build turret" action, the action will be performed# when the necessary conditions are metawaitself.place_order(pp.BuildTurretOrder(tile,with_timeout=2.0,# maximum time (sec) before aborting the order))StagesAs the complexity of the bot grows, it can become very handy to encapsulate different
types of behaviour. This can be done by defining multipleBehaviourStage(which is essentially
the same asBehaviour) and grouping them in aBehaviourDispatcher.Here is an example that splits the game into different stages:importploupyasppclassEarlyStage(pp.BehaviourStage):def__init__(self,dispatcher:pp.BehaviourDispatcher)->None:super().__init__(dispatcher,"early")# specify stage name...# the bot's behaviour in early gameclassMidStage(pp.BehaviourStage):def__init__(self,dispatcher:pp.BehaviourDispatcher)->None:super().__init__(dispatcher,"mid")asyncdefon_income(self,money:int)->None:# switch of BehaviourStageawaitself.set_current_stage("end")classEndStage(pp.BehaviourStage):def__init__(self,dispatcher:pp.BehaviourDispatcher)->None:super().__init__(dispatcher,"end")asyncdefon_stage(self)->None:...# this callback is called when the stage is selected as current stage# regroup all stages into one behaviourclassBotBehaviour(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))