Hannibal

Intro

The Hannibal AI/Bot is a new approach to reduce the complexity of programming a bot for 0 A.D. It was started in February 2014 by agentx. This page will document the development process and parts of the source, explains major concepts and provides tips & tricks. The forum has already a few posts discussing the bot's features. Little Query Language, AI Tournaments. Ultimately Hannibal should be unbeatable if started with highest difficulty.

In particular the bot will use:

  • a state machine to handle game phases
    like village, town, city, attack, defense, reconstruction, etc.
  • a triple store to link features of the cultures,
    like who can gather fields, can a healer melee?
  • a simple query language to retrieve information from the triple store
    "food.grain GATHEREDBY WITH costs.metal = 0, costs.stone = 0, costs.wood = 0 SORT < costs.food"
  • a plugin system describing the behavior of groups of units
    (grain-picker, hunter, warrior, guerrilla, miner, etc)
  • an economy model with an order queue, a cost analyzer
    and a simple statistic module providing metrics based on resource flows.
  • a HTN PLanner to calculate attack strategies
    (probably the last implemented feature, HTN)

Development Log

April 2014

The new module system for bots landing with Alpha 16 provided many features and led to an productivity boost. As of writing (end of March 2014) the code consists of 17 files with their functionality nicely separated. The template parser, query and store modules are close to production quality, basic plugins work and the first group (grain picker) successfully requests drop sites, units and fields, flee from attacks, repairs their field, rebuilds destroyed ones. The event dispatcher needs a bit more testing as sometimes entities are not registered properly. I think, I have now a detailed overview of how and whether the API can be used or not. The lack of any debugging possibilities is time consuming and hopefully I don't run out of ideas solving how to log critical function within the first 50 ticks. The developer overlay + time machine is a gem in this regard. Next challenge is a concept of shared and exclusive structures.


State Machine

The state machines relies on clearly distinguishable states. They are connected and have entry and exit conditions. The game starts in the whiteflag state, which it tries to exit immediately. If that is not possible the game is either lost or the map is unplayable due to lack of resources.

example code

states: {
  "ai:ai":              {
    entry:              "whiteflag",
    whiteflag:          ["village"],
    village:            ["populate", "town", "victory"],
    town:               ["expand", "defense", "city", "victory"],
    city:               ["attack", "defense", "attack", "victory"],
    victory:            [],
  },
  "ai:ai:1":            {
   village:            ["populate", "technology", "town", "victory"],
  }
}

If the village state's entry conditions like enough resources are met the machine switches and processes all states or sub states mentioned in the list. Victory is checked every turn, to make sure that doesn't happen unrecognized :) "ai:ai:1" indicates a state set for a higher difficulty and basically overwrites the lower one (adding technology) if chosen by the user. This hierarchy allows easy editing and provides a fine tuned set of game difficulties. The states are implemented in states.js, defined in config.js, the function getBehaviour() applies the difficulty. Within OnUpdate() this line:

this.state = this.behaviour.process(this); 

checks the conditions, processes the state and sub-states and returns the same or a new state.


Triple Store


During start-up Hannibal parses all templates of involved civilizations and builds a mesh network consisting of nodes and edges. Nodes are e.g. classes, resources, technologies and all units and entities. Athen's culture generates around 160 nodes and 2000 edges. Nodes use a sanitized form of the templates name as unique name and are saved as a JS object with additional properties like cost, hit points and size. Maps or Weakmaps might perform better, but are untested. Edges consist of an JS array with source node, a verb and the target node, hence the name triple store. Currently there are 26 verbs for 13 edge types with two directions. During the game the store is updated with created and destroyed entities, so the query language can select these as nodes too. In game nodes have a '#' + their id as name suffix. The parser is coded in cultures.js and the store in store.js. Both are separated so the domain knowledge is in cultures.js only.

    this.verbs = [
      "member",     "contain",
      "provide",    "providedby",
      "gather",     "gatheredby",
      "build",      "buildby",
      "train",      "trainedby",
      "hold",       "holdby",
      "heal",       "healedby",
      "research",   "researchedby",
      "require",    "enable",
      "accept",     "acceptedby",
      "carry",      "carriedby",
      "ingame",     "describedby",
      "techingame", "techdescribedby"
    ];

The verbs are mostly made up and may change into more idiomatic English ones. Proposals are highly welcomed. A few verbs like 'describedby' are probably not needed. The parsing happens in CustomInit():

  // culture knowledge base as triple store
  this.culture = new H.Culture("athen");
  this.culture.loadTemplates();           // from templates to triple store
  this.culture.loadEntities();            // from game to triple store
  this.culture.loadTechnologies();        // from game to triple store

Query Language

Examples:

  • query in game entities
    "INGAME" (returns all)
    "INGAME WITH id = 44"
    "INGAME WITH metadata.group = 'none'
  • listing entities of classes
    "healer, hero CONTAIN"
  • checking required technologies
    "units.athen.support.female.citizen.house REQUIRE"
  • looking for trainers (in game)
    "units.athen.infantry.spearman.b TRAINEDBY"
    "units.athen.infantry.spearman.b TRAINEDBY INGAME"
  • or less specific: "infantry CONTAIN TRAINEDBY INGAME"

The query engine is already in its second iteration and a few optimizations are still possible. However all above examples deliver results within less than 15 milliseconds on a 3GHZ Intel CPU. Queries starting with a name clause are very fast, usually < 1 msec. A query is invoked with the triple store of the civilization :

  var TS = H.Bot.culture.store,
      nodes = new H.HCQ(TS, "food.grain PROVIDEDBY WITH civ = 'athen'").execute();

There is also a short cut to simplify iterations:

  var nodes = H.QRY(order.hcq).forEach(function(node){
    node.qualifies = true;
  });

Plugins


Domain Specific Language


Economy


HTN Planner

After some research it looks like a HTN planner is an appropriate solution for 0 A.D. Especially the SHOP algorithm stands out. It is simple and short. The Python implementation Pyhop has less than 50 lines.


Scratchpad

(just a section for thoughts and experiments)

Last modified 10 years ago Last modified on Mar 28, 2014, 9:36:37 PM

Attachments (3)

Download all attachments as: .zip

Note: See TracWiki for help on using the wiki.