Jump to content

xbot - A Bot Comes Alive


Recommended Posts

After playing 0 A.D. far to often I decided to program my own AI/bot. 0 A.D. is an interesting client for an AI because first it is so beautiful, second it requires solving real world problems and, well, bots need to be written in JavaScript, which is quite popular. So this is kind of a forum blog of a Linux/Internet guy developing on an old Windows XP license against Mozilla's SpiderMonkey having no clue about C++ programming or compiling.


The following wouldn't be impossible without a working AI. Wraitii's excellent Aegis Bot is nearly game complete and was a challenging opponent for dozens of rounds. Aegis implements resource allocation, queueing and management, building placement, map exploration and more - all important things needed whatever logic an AI prefers.


I love the idea of AIs showing their capabilties in a complex environment fighting against each other. Hopefully developers reading this feel challenged and Hannibal gets opponents really soon, which is the purpose of this writing. Since all code is open everybody can use and optimize each other solutions and strategies. If you are interested, this is a good starting point. I'll plan to continue this series at least until the sample bot trains units and let them gather resources.


I have a few design goals in mind: With highest difficulty the AI should be unbeatable while the lowest should be just interesting enough to engage beginners. Unit groups should solve most problems more or less autonomously. And at a later point research would be required to restrict the AI to launch only attacks according to the selected civilisation. Hannibal will use kind of a state machine to implement behaviours based on frames. Frames will run in a special context/closure providing something as close to a domain specific language (DSL) as JavaScript, time and "strict mode" allows.


Everything below and above refers to 0 A.D. Alpha 15 Osiris (running on Windows) using SpiderMonkey 4 impementing Javascript 1.85. Some statements might be based on untested information found in the wiki or this forum and could be wrong or misleading. Please, let me know!

  • Like 2
Link to comment
Share on other sites

First I'll describe how to set up your system and show the basic layout of an 0 A.D. bot. It starts with downloading and installing the game, on my system that's the path d:\games\0.A.D\. Check out this guide for other systems and other paths mentioned below. A bit deeper is a huge zip file called public.zip, extract that into its folder keeping the folder structure intact. Now you'll find a folder called d:\Games\0 A.D. alpha\binaries\data\mods\public\simulation\ai Here the fun starts. Think of a good name, let's say XBot, and create a xbot sub folder as a sibling to qbot, testbot, aegis, etc, then two files are needed: data.json and _xbot.js.
{  "name":        "XBot",   "description": "XBot - example",   "constructor": "XBot",     "useShared":   true           }
Engine.IncludeModule("common-api-v3");function xbot(settings) {  BaseAI.call(this, settings);}xbot.constructor = xbot;xbot.prototype = new BaseAI();xbot.prototype.CustomInit = function(gameState, sharedScript) {};xbot.prototype.OnUpdate = function(sharedScript) {};

The files in your bot folder are loaded alphabetically, so the underscore makes sure the api is included first. I use a local.cfg file C:\Documents and Settings\Owner\Application Data\0ad\config in to ease development:

windowed = truesplashscreenenable = falsepauseonfocusloss = truexres = 1024yres =  768jsdebugger.enable = false
Edited by agentx
  • Like 4
Link to comment
Share on other sites

There is a browser based JS debugger allowing breakpoints and variable inspection, but I got it running only once or twice, so dbgView from SysInternals is my debugging tool of choice. A bot has 3 possibilities to talk to you while running, 1) chatting, 2) the in game console and 3) logging to std, in this case dbgView. The console is of little use since it runs in another context. However, the Aegis Bot uses it quite well to explain its actions once debug is set in its config. I decided to use dbgView only and keep the chat function to entertain the human opponent.
SpiderMonkey is very picky and demands properly written code. I use Sublime Text as editor and a JSLint plugin to make sure SpiderMonkey is happy before 0 A.D. fails to load the code. JSLint is even more picky, but there is nothing wrong with using a good coding style restricted to the good parts of JavaScript. In terms of style I tend to prefer Google's JavaScript Style Guide.
You probably want to start developing with a simple map. Get used to the map editor Atlas and create a map with two Civil Centers one for your bot (Player 1) and one for Aegis (Player 2). It's actually quite simple. Since maps are XML files, direct editing is possible too. Atlas saves maps in a special windows folder, mine is: C:\Daten\My Games\0ad\mods\user\maps\scenarios My first map is attached. You start it with this command line:
"D:\Games\0 A.D. alpha\binaries\system\pyrogenesis.exe"  -quickstart -autostart=xbotmap01 -autostart-ai=1:xbot -autostart-ai=2:aegis

At this point you have an Bot which is functional, but as clever as a stone.

It is a good idea to learn the hotkeys, triple clicks give you some advantages, also there is an useful developer overlay.

xbotmap01.xml

  • Like 4
Link to comment
Share on other sites

My first question was what objects the bot might use, so here is 'global' the equivalent of 'window' in a browser environment.
logObject(global);  Accessibility: function Accessibility() {"use strict";} ...  AssocArraytoArray: function AssocArraytoArray(assocArray) {"use strict";var end ...  BaseAI: function BaseAI(settings) {"use strict";if (!settings) {retu ...  Class: function Class(data) {"use strict";var ctor;if (data._init)  ...  DoesModificationApply: function DoesModificationApply(modification, classes) {"use  ...  Engine: OBJECT (ProfileStart, ProfileStop, IncludeModule, DumpHeap, ForceGC, ...)[8]  Entity: (function (sharedAI, entity) {"use strict";this._super.call( ...  EntityCollection: function EntityCollection(sharedAI, entities, filters) {"use ...  EntityTemplate: (function (template, techModifications) {"use strict";this._ ...  Filters: OBJECT (byType, byClass, byClassesAnd, byClassesOr, byMetadata, ...)[27]  GameState: (function () {"use strict";this.ai = null;this.cellSize = 4; ...  GetTechModifiedProperty: function GetTechModifiedProperty(currentTechModifications, e ...  ManhattanDistance: function ManhattanDistance(a,  {"use strict";var dx = a[0] ...  Map: function Map(sharedScript, originalMap, actualCopy) {"use st ...  Memoize: function Memoize(funcname, func) {"use strict";return functi ...  MemoizeInit: function MemoizeInit(obj) {"use strict";obj._memoizeCache =  ...  PickRandom: function PickRandom(list) {"use strict";if (list.length ===  ...  PlayerID: NUMBER (1)  Resources: function Resources(amounts, population) {"use strict";if (am ...  ShallowClone: function ShallowClone(obj) {"use strict";var ret = {};for (v ...  SharedScript: function SharedScript(settings) {"use strict";if (!settings) ...  SquareVectorDistance: function SquareVectorDistance(a,  {"use strict";var dx = a ...  TERRITORY_PLAYER_MASK: NUMBER (63)  Technology: function Technology(allTemplates, templateName) {"use strict ...  TerrainAnalysis: function TerrainAnalysis() {"use strict";this.cellSize = 4;} ...  VectorDistance: function VectorDistance(a,  {"use strict";var dx = a[0] -  ...  aStarPath: function aStarPath(gameState, onWater, disregardEntities, ta ...  copyPrototype: function copyPrototype(descendant, parent) {"use strict";var ...  deepcopy: function deepcopy() {[native code]} ...  error: function error() {[native code]} ...  g_FoundationForbiddenComponents: OBJECT (ProductionQueue, ResourceSupply, ResourceDropsite, GarrisonHolder, ...)[4]  g_ResourceForbiddenComponents: OBJECT (Cost, Decay, Health, UnitAI, UnitMotion, ...)[6]  global: OBJECT (global, Engine, print, log, warn, ...)[49]  inRange: function inRange(a, b, range) {"use strict";if (a === undefi ...  log: function log() {[native code]} ...  print: function print() {[native code]} ...  warn: function warn() {[native code]} ...

That already gives a hint on the rich API 0 A.D. provides. Engine looks interesting:

logObject(Engine):  DumpHeap: function DumpHeap() {[native code]} ...  DumpImage: function DumpImage() {[native code]} ...  ForceGC: function ForceGC() {[native code]} ...  IncludeModule: function IncludeModule() {[native code]} ...  PostCommand: function PostCommand() {[native code]} ...  ProfileStart: function ProfileStart() {[native code]} ...  ProfileStop: function ProfileStop() {[native code]} ...  RegisterSerializablePrototype: function RegisterSerializablePrototype() {[native code]} ...

It turns out the API functions and objects are either provided by the host environment/SpiderMonkey [warn, log, etc.] or by 0 A.D. itself written in C++ [PostCommand, etc] or by code written in JavaScript.

Next part will extend XBot and get in touch with settings, gameState, sharedScript and BaseAI.
To be continued.
PS. if you can't wait, explore this folder: D:\Games\0 A.D. alpha\binaries\data\mods\public\simulation\ai\common-api-v3 or inspect the other example bots.
  • Like 3
Link to comment
Share on other sites

I suggest you consider using the current development version for AI development instead of the latest alpha version.

Check this guide for information how to get it on Windows:

http://trac.wildfiregames.com/wiki/TortoiseSVN_Guide

You don't necessarily have to compile the code yourself because prebuilt executables are included in the repository.

Members with commit access can trigger an autobuild. We do that regularly to make it easier for the artists to keep up-to-date without having to compile.

Link to comment
Share on other sites

I have no coding skills including Javascript but ...

Well, that shoudn't stop from contributing. My bot will be heavily based on groups and their tasks. E.g. 5 females working on a field, they repair, rebuild if needed or garrison on attack and request new members on losses. What other kind of group behaviour could you think of? Ideas please! :)
Link to comment
Share on other sites

Well, that shoudn't stop from contributing. My bot will be heavily based on groups and their tasks. E.g. 5 females working on a field, they repair, rebuild if needed or garrison on attack and request new members on losses. What other kind of group behaviour could you think of? Ideas please! :)

That's a good question, and I think it depends on the tasks you want to assign to a group and what's done by the Mayor (that was the way you called the ruling part).

So you should perhaps give us a outlined schematic in which you explain the roles being taken.

Link to comment
Share on other sites

I still think I can implement any behaviour. More difficulty is to provide a group with useful information. E.g. reading the map and extracting a path over a map. But, I've developed a few routines while playing like the gatherer group outlines above which I think could a bot use.

Do you like to share your tactics?

Link to comment
Share on other sites

I still think I can implement any behaviour. More difficulty is to provide a group with useful information. E.g. reading the map and extracting a path over a map. But, I've developed a few routines while playing like the gatherer group outlines above which I think could a bot use.

Do you like to share your tactics?

Well, I was thinking on what part is managed by what part: Is it managed by the Mayor which resource to gather or is that decided by the group. So how is your AI biased? Emphasized on as much as possible done by the groups or is everything mostly done by the mayor.

Link to comment
Share on other sites

Well, I was thinking on what part is managed by what part: Is it managed by the Mayor which resource to gather or is that decided by the group. So how is your AI biased? Emphasized on as much as possible done by the groups or is everything mostly done by the mayor.

The mayor i've mentioned is thinning. Probably at the end there is no mayor, each group has all info it needs, why mayors? At the end there is only time, space, food, wood, stone, metal and health. What the bot manages are resources, groups can request resources to build or train and places to do their stuff. They'll get them when they are available. Probably there is always a backlog, here decisions are made and resources not wasted. I've found a JavaScript implementation of SHOP2, a HTN planner. If the bot kinda gets an idea of future, that would be great.

Some group thoughts: Cultures should celebrate victories, how could a dance company gather attention. Healers? How do they find units sending SOS, without attempting suicide. When should a unit garrison? At 50% health? What is the minimum/cheapest group in each civilisation able to level towsers? Can units form letters to ask for HELP ?

  • Like 1
Link to comment
Share on other sites

Perhaps the mayor can calc the favored gathering rates. The group then should automatically determine how to achieve these rates for example by checking available technologies, check which resources to gather (and also bushes vs farms etc) and finally check whether the storehouse is effectively placed and if not than build a better placed one. The third point should also be kept in mind whilst deciding the second.

  • Like 2
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...