[Home]PuertoRicoOnline/WritingBots

ec2-3-16-15-149.us-east-2.compute.amazonaws.com | ToothyWiki | PuertoRicoOnline | RecentChanges | Login | Webcomic

The bots for PuertoRicoOnline are written in Object Pascal Script.
See PuertoRicoOnline/RandomBot for an example script that takes random actions.
Please send finished bots to me (at) <myrealfirstname><myrealsurname> dot com and I'll stick them on the live server.
Rachael wonders how many ToothyWikizens know qqzm's real name. And what subset of them can spell it.
True. They are "McIntosh?" and "Stephen" and not necessarily in that order.

General Format of a Bot Script


Each script must have at the bare minimum:
function TakeTurn(const Game: GameType; const BotPlayer: Integer): String;
begin
end;

begin
  {}
end.

The begin {} end. is mandatory for the script to compile, but is never actually called.

The bot must be saved as <BotName?>.rops and placed in the bots folder on the server.

When it is the bot's turn to go, the server calls the TakeTurn? function passing a game object which describes the current game state (see the game type declaration below) and an integer specifying which player number the bot is (indexed from 0). The TakeTurn? function must return a string specifying what action the bot should take.
If the bot is being requested to choose a role, the string is:
role.html?Game=<Game Number>&Player=<Player Number>&Role=<Chosen role ID>

Otherwise, the string selected from the following corresponding to the current role:
builder.html?Game=<Game Number>&Player=<Player Number>&Building=<Building ID to build>
captain.html?Game=<Game Number>&Player=<Player Number>&GoodsType=<Good to ship>&Ship=<Ship number, 0 indexed>
rotting.html?Game=<Game Number>&Player=<Player Number>&<GoodsType to keep><Index>=on 
// e.g. &11=on will keep your first barrel of Grain.
craftsman.html?Game=<Game Number>&Player=<Player Number>&ExtraBarrel=<Goods type of extra barrel>
mayor.html?Game=<Game Number>&Player=<Player Number>&px=on&bxy=on 
// e.g.: p0=on will man the first plantation. b11=on&b12=on will put 2 colonists on building 1
settler.html?Game=<Game Number>&Player=<Player Number>&Plantation=<Chosen plantation ID>
// A plantation ID of -2 indicates that the bot wishes to use its hacienda.
trader.html?Game=<Game Number>&Player=<Player Number>&GoodsType=<Goods type to trade>


Goods types are 1 for Grain, 2 for Indigo...5 for Coffee. Building IDs and role IDs are listed in the constants below.
Are there no Prospectors? --Rachael
The Prospectors are role IDs 6 and 7. (7 is only used in 5 player games). --qqzm
Plantation IDs are the same as goods but with the addition of 6 for a Quarry.

Debugging


The bot compiler outputs all debugging information to a log file located at shared\logs\<Bot Name> Game <Game Number> Player <Player Number>.log.
You can browse to this file at
http://host:port/logs/<Bot Name> Game <Game Number> Player <Player Number>.log

If you wish to write any additional debugging output to this file, call:
procedure WriteLn(GameNumber, BotPlayer: Integer; const s: string);

[Here], you can download a copy of the server for testing your bots.
Version 0.20 of the server is now available and is a highly recommended upgrade for those of you currently debugging bots. It is (hopefully) the last major change before the final version. There are 2 small changes to the format of the saved games, so you will either have to remove all your existing saved games, or update them manually.
Since downloading 0.20, attempts to access my local server request "httP://localhost:8080/index.xml" (8080 is correct, I changed the config file). However, all I'm getting is a blank page in FireFox. InternetExplorer gives the game-selection page. It's possible FireFox has XML turned off - anyone know where to turn it on, if so? --CH
It seems firefox is slightly fussier than ie and insists that the content type of the xml file is "text/xml" whereas ie accepts "text/html" and then detects that it is in fact an xml file. I've fixed this and will release a new version soon once I've fixed a couple of other weird things that firefox does with xsl :) --qqzm
Version 0.21 now available and is once again Firefox-friendly. --qqzm.

Server Types and Constants Accessible from the Scripts


const
  // Roles.
  BUILDER          = 0;
  CAPTAIN          = 1;
  CRAFTSMAN        = 2;
  MAYOR            = 3;
  SETTLER          = 4;
  TRADER           = 5;
  ROTTING          = -2;
  // Buildings.
  SMALLINDIGOPLANT = 1;
  SMALLSUGARMILL   = 2;
  SMALLMARKET      = 3;
  HACIENDA         = 4;
  CONSTRUCTIONHUT  = 5;
  SMALLWAREHOUSE   = 6;
  INDIGOPLANT      = 7;
  SUGARMILL        = 8;
  HOSPICE          = 9;
  OFFICE           = 10;
  LARGEMARKET      = 11;
  LARGEWAREHOUSE   = 12;
  TOBACCOSTORAGE   = 13;
  COFFEEROASTER    = 14;
  FACTORY          = 15;
  UNIVERSITY       = 16;
  HARBOUR          = 17;
  WHARF            = 18;
  GUILDHALL        = 19;
  RESIDENCE        = 20;
  FORTRESS         = 21;
  CUSTOMSHOUSE     = 22;
  CITYHALL         = 23;

type
  PlantationType
    ID: Integer: Type of plantation 1=Grain, 2=Indigo etc.
    Name: String: Human-readable name of the plantation "Grain", "Indigo" etc.
    TradeValue: Integer: How much doubloons a barrel is worth when traded.
    Manned: Boolean: Whether the planatation has a colonist on it.
    NumberPlantations: Integer: If the plantation is in the bank, this specifies how many of this type are in the bank.
    NumberBarrels: Integer: If the plantation is in the bank, this specifies how many barrels of this type are in the bank.



  BuildingType
    ID: Integer: Type of building = one of the building constants above.
    Name: String: Human-readable name of the building.
    Cost: Integer: How many doubloons it costs to build.
    VPs: Integer: How many VPs the building is worth at game end.
    ColonistSpaces: Integer: How many colonist spaces the building has.
    Description: String: Description of the building.
    Number: Integer: If the building is in the bank, this specifies the total number of buildings of this type there are in the bank.
    Colonists: Integer: How many colonists currently occupy the building.



  TraderType
    Goods: TraderArray: array[0..3] of Integer: the barrels currently in the trading house.
    function CanTrade(GoodsType: Integer): Boolean: Returns true if the specified good can be traded in the trading house.
    procedure Insert(GoodsType: Integer): Inserts the specified good into the trading house.
    function IsFull: Boolean: Returns true if the trading house is full.



  Role
    Name: String: Human-readable name of the role.
    Available: Boolean: True if the role has not yet been selected this turn.
    Doubloons: Integer: How many bonus doubloons the role has on it.



  GameType
    GameNumber: Integer: Specifies the number of the game.
    GameName: String: Specifies the game name.
    TradingHouse: TraderType: The game's trading house.
    Players: TStringList: List of players in the game. Each element is a (string, object) pair where the string is the player name, and the object is a player object. There are always 5 elements in the list, with the spare ones having Assigned set to false in 3 and 4 player games.
    GivenCaptainExtra: Boolean: Whether the Captain has received his/her bonus VP this turn.
    GameInProgress: Boolean: Whether this game is currently in progress.
    GameHasEnded: Boolean: Whether this game has finished.
    GameEndCriterionMet: Boolean: Set to true once any of the end game conditions has been met.

    NumberofPlayers: Integer: Number of players in the game.
    BankVPs: Integer: Number of VPs in the bank.
    BankColonists: Integer: Number of colonists in the bank.
    ColonistsOnShip: Integer: Number of colonists on the colonist ship.

    BuildingsInBank: TStringList: List of buildings in the bank.
    PlantationsInBank: TStringList: List of plantations in the bank.
    Roles: TStringList: List of roles.
    GoodsShips: TStringList: List of Goods ships.

    CurrentGovernor: Integer: The player (indexed from 0) that is currently acting as governor.
    CurrentRoleChooser: Integer: The player who chose (is choosing) the current role.
    CurrentRole: Integer: The current role (-1 if no role has yet been chosen this turn).
    CurrentPlayer: Integer: The player whose turn it is.
    GameLog: TStringList: The game log.
    GameChat: TStringList: The in-game chat.

    function CanAnybodyShip: Boolean: Returns true if any of the current players are able to ship this turn.



  GoodsShip
    GoodsType: Integer: The type of good in the ship.
    Quantity: Integer: The number of barrels in the ship.
    Capacity: Integer: The capacity of the ship.
    function CanShip(GoodsTypeToShip: Integer): Boolean: Returns true if the specified goods type can be shipped in this ship.
    function IsFull: Boolean: Returns true if the ship is full.




  Player                  
    Name: String: The name of the player.
    PlayerNumber: Integer: The player's number in the player list.
    IsAssigned: Boolean: True if the player is in the game (false if this is player 4 in a 4 person game).
    Doubloons: Integer: The number of douboons the player has.
    LastRole: Integer: The last role the player chose.
    Plantations: array[0..11] of PlantationType: The plantations the player has.
    Buildings: array[0..11] of BuildingType: The buildings the player has.
    Barrels: array[1..5] of Integer: The number of barrels of each good the player has.
    LastBarrels: array[1..5] of Integer: The number of barrels of each good the player produced in the last craftsman phase.
    SpareColonists: Integer: The number of colonists the player has in San Juan.
    TotalColonists: Integer: The total number of colonists the player has.
    UsedWharf: Boolean: True if the player has used a wharf this turn.
    LastWharfGood: Integer: Identifies the last good the player shipped in his/her wharf.
    UsedHacienda: Boolean: True if the player has used a hacienda this turn.
    LastHaciendaPlantation: Integer: Identifies the last plantation the player received from his/her hacienda.
    ProducedLastCraftsman: Integer: True if the player produced any barrels in the last craftsman phase.

    function GetMannedPlantations(GoodsType: Integer): Integer: Returns the number of manned plantations of the specified type that the player has.
    function GetNumberOfPlantations(GoodsType: Integer): Integer: Returns the total number of plantations of the specified type (both manned and unmanned).
    function GetProductionCapacity(GoodsType: Integer): Integer: Returns the number of circles present on a player's production buildings for GoodsType, both manned and unmanned.
    function IsManned(BuildingID: Integer): Boolean: Returns true if the player has a building of the specified type, and it is manned.
    function CanBuild(Building, IsBuilder: Integer): Boolean: Returns true if the specified building can be built by the player.
    function CanTradeGood(TradingHouse: TraderType; GoodsType: Integer): Boolean: Returns true if the player can trade the specified good.
    function CanTrade(TradingHouse: TraderType): Boolean: Returns true if the player is able to trade any good this turn.
    function TownSpaces: Integer: Specifies the number of empty colonist spaces in the player's town (i.e. on buildings).

    function CanShipGood(GoodsShips: TStringList; GoodsTypeToShip: Integer): Boolean: Returns true if the player can ship the specified good.
    function CanUseShip(GoodsShips: TStringList; ShipNumber: Integer): Boolean: Returns true if the player can ship on the specified ship.
    function CanShip(GoodsShips: TStringList): Integer: Returns true if the player can ship at all this turn.

    function CanKeepBarrels: Boolean: Returns true if the player can store all of the barrels he/she currently holds.



Due to a problem with the script engine I'm using, you can't access any arrays from the bot scripts. To get around this, I've had to write a couple of helper functions to retrieve values from arrays:

  function GetBarrel(GameNumber, PlayerNumber, Index: Integer): Integer;
  // To access the array of barrels that a player currently holds. 

  function GetLastBarrel(GameNumber, PlayerNumber, Index: Integer): Integer;
  // Holds the barrels that the player produced last craftsman phase. 
  function GetNextPlantation(GameNumber, PlayerNumber, Index: Integer): Integer;
  // Holds the plantations that will be available next settler phase.
  function GetPlantation(GameNumber, PlayerNumber, Index: Integer): PlantationType;
  // Holds the plantations owned by a player.
  function GetBuilding(GameNumber, PlayerNumber, Index: Integer): BuildingType;
  // Holds the buildings owned by a player.
  function GetTrader(GameNumber, Index: Integer): Integer;
  // Holds the goods in the trading house.


You can also use many standard Delphi classes and methods, the most useful of which are:
  function Random(i: Integer): Integer;
  // Returns a random integer x in the range 0 <= x < i.
  function Format(const FormatStr: string; const Args: array of const): string;
  // Formats a string.


Playing A Game With Only Bots


In order to play a game with only bots:
1) Start a new game, add n-1 bots to it and then click start.
2) Close the server.
3) Open the Games\<Game Number>.xml file.
4) In the first Player node, change name from your name to the name of the bot you wish player 1 to be.
5) Change IsBot? to True.
6) Save the file and restart the server, it'll now play the bots off against one another.


Requests


Could we have a NumberOfPlantations?(GoodsType?: Integer) (or other sensible name) function, to return the number of plantations, manned and unmanned, of the specified type? While I'm at it, ProductionCapacity?(GoodsType?: Integer) would be useful, returning the number of circles present on a player's production buildings for GoodsType? (so, always 0 or (3 / 2) for (Tobacco / Coffee); and taking both small and large Indigo / Sugar plants into account)? I could write them myself, I guess, but I'm not highly familiar with the language, and I figured they'd be of fairly generic use. --CH
Certainly, I'll add them to my todo list, none of them should take very long to implement. --qqzm
Done 2005/05/12.
AlexChurchill turns his attention to this, and discovers a HasBuilding?(BuildingID?) function on players, to say whether they have the specified building or not, regardless of whether it's manned, would be rather useful. Principally for assessing other players' potential actions and goods they're likely to produce.
Something along the lines of:
function HasBuilding(Game: GameType; Player: Integer; BuildingID: Integer): Boolean;
var
  i: Integer;
begin
  Result:= False;
  for i:= 0 to 11 do
    if GetBuilding(Game.GameNumber, Player, i).ID = BuildingID then
      begin
        Result:= True;
        Break;
      end;
end;

should probably work, I haven't tested it. --qqzm

ec2-3-16-15-149.us-east-2.compute.amazonaws.com | ToothyWiki | PuertoRicoOnline | RecentChanges | Login | Webcomic
This page is read-only | View other revisions | Recently used referrers
Last edited September 23, 2008 4:15 pm (viewing revision 33, which is the newest) (diff)
Search: