AI Development

C-evo Version: 1.2.0
Last modification: 26-April-13

This text explains how to create an artificial intelligence module for C-evo. This text is not a programming tutorial. Before trying to create an AI, you should know what an algorithm is and you should know how to use a compiler. Please do not come up with questions like "I am starting with programming, and I picked your project for that. Now what am I to do with the sources?"...

The easiest way to create a C-evo AI is by using the official development kit, which is automatically installed with the game itself into the subfolder "AI Development Kit". This kit is using the language PASCAL (works with Delphi and with FreePascal). It's based on a documented base class that encapsulates low-level communication completely, making you program on a more abstract level without having to deal with too much technical detail. The kit also contains a working sample AI based on this system. It demonstrates unit movement, city management and diplomacy.

If you use this kit, you should use the documentation there instead of the one you are currently reading. This document is for those who, for any reason, do not use the official development kit. It gives assistance in building an AI DLL from the scratch or using the C++ AI template available from the files section of the project home page.


The Module Concept
General Use of Commands
Saving Data
Game Start Sequence
Location Codes

Reference: Server Commands

Reference: Client Commands

Latest Changes

Project Homepage


If you notice any problems or have any questions or suggestions, please contact me.
There's also a web forum for C-evo AI developers.

The Module Concept

A specialty of C-evo are the exchangeable competitor modules. These modules can be developed apart from the main program, even in a different programming language. Such a module takes control over one or more nations. This can for instance be done by artificial intelligence or handing over the control to the user. Even the player interface is nothing but a special competitor module and uses only the AI interface to communicate with the game core. That's why suspicions of AI having not the same information or possibilities that a human player has can never be true.

A module is contained by a Windows Dynamic Link Library ('DLL') File. A DLL contains program code (e.g. algorithms, dialog windows...) and has two important qualities: 1. It is not fixed to a special program. When the code is used by more than one program, containing it in a DLL will save hard disk space and memory. 2. The program determines at runtime which DLL files will be used. So it is possible to choose between different code modules - even those that did not exist when the program was written. Pre-condition: the DLLs must have the expected interface. That is why a major part of this text deals with the interface definition of the C-evo competitor DLLs. If you want to develop such a module, you must exactly comply with these specifications.

If you take a closer look at the interface specification, you will probably notice that some information you can think of never is a subject of communication. That is true. The interface is a logical one. It handles only numbers, no names and no picture information. These things are not part of the game core, they are internal to the player interface and do not concern AI programming.

General Use of Commands

I will use a C near syntax throughout this text. You should not experience problems if you use different compiler. I tried to make the interface as easy and clear as possible. There is only one communication function on the side of the main program (server) and one on the side of the competitor module (client):

int server (int Command, int Player, int Subject, void* Data) void client (int Command, int Player, void* Data)

The Command parameter specifies the command to be executed by the server resp. client. All possible commands are listed below. The Player parameter determines the player concerned. The use of the Subject parameter and the structure of the memory block referenced by the Data parameter depend on the command. However, the size of the Data block in DWords is always specified by the lowest 4 bits of the command, except for the information request commands ( < 0x1000). Often, the Subject or Data parameter is a unit, model or city index. Indices always start with 0.

As an AI programmer, you have to implement the client side, i.e. you must handle all possible commands of the client function. Your means to realize the desired game play are appropriate calls to the server function. Additionally, you can read information from a data block. This is your main source of information, take it for read-only! (That's why it's called 'RO' below.) Please see the protocol.pas resp. protocol.h file for the exact structure of the RO block and the data it refers to. Most members have descriptive comments there or are self explaining.

Just a remark about the unit and city lists refered to in the RO (members Un, City, EnemyUn, EnemyCity). Note that these lists have gaps, indicated by items having their Location <0. These items do not exist, commands will not work for them. Furthermore, the items in these lists might change their position within the list in between turns, when the server prepares your turn. The enemy units might even change their indices while the enemies are moving. However, indices never change while you are in active mode.

Note: When I started this project, I intended to prevent cheat attempts by AI modules with technical means. Later I realized that makes up a lot of work and I better spend my time and energy on making the game better than on fighting against potential swindlers. So this is my appeal to you: Read everything you want from the RO block and what it refers to - all this information is for your eyes. But: Do not write it! It might work, but it is not honest. Also, writing the RO block is very likely to corrupt the game, so that it's impossible to reload it.

If you call the Server function with a command minus sExecute, it will not execute the command but will only return the correct code. This is supported for all commands except info request and client exclusive commands. Note that there is one exception where the code is not the same as if you would really execute the command. This happens when a unit move is not possible due to facts you don't know. In this case, when you call the server with sMoveUnit-sExecute, it will answer as if the move was possible. Means, the return codes eZOC_EnemySpotted, eHiddenUnit and eStealthUnit are not returned before you really try to execute the move.

During the game, the AI of a nation changes between three basic modes: turn mode, negotiation mode and inactive mode. Most server calls are only allowed during turn mode. Additionally to client deactivation from negotiation mode, only the following commands are available outside turn mode: sGetChart, sGetTechCost, sGetDefender, client exclusive commands.

Turn mode and negotiation mode are active modes. Only one nation can be in active mode at a time. The server activates a client by calling one of its activation commands. The deactivation of a client must be done by itself, means the handler of an activation command must call exactly one client deactivation command before returning. This deactivation command should be the last server command called by the handler.


Code internal termMeaning/player term
EnemyAll other nations, even allied
ModelUnit class
DomainUnit domain (Ground/Sea/Air)
ShipTransstellar colony ship
MasterIf unit A carries unit B, A is the master of B.
HealthUnit property, = 100-Damage
JobSettler terrain improvement job

Saving Data

You can hold any data your AI needs within the local memory of your DLL. The problem: after the player has quit the game and continues the day after, all this data will be lost. For most of the data, this might not be a problem, because you can calculate it from other information whenever necessary. But there are also some items that need to be saved, e.g. if you wish to remember more diplomatic information about your neighbours than just treaty and credibility.

For that purpose, the game provides you with means to save data within the save file of the game. These means respect the special fact that the player can reload a game at any turn. After loading, the data of your AI should be the data of this turn, not the data from the last turn contained in the save file.

There are three ways to save data:

For the sake of a reasonable save file size, you should let the game only save information that is necessary to save. Do not let it save data that only needs to live within your turn, because the game will never be interrupted within an AI turn. Also, do not save information that you could as well calculate from other information after reloading. All changes in the Data block and in the Status fields will be logged to the save file, so please store temporary information outside the Data block and filter it from the Status values after your turn.

Game Start Sequence

Starting a New Game

This is what happens step by step when the player starts a new game.

  1. (Server) RO and all linked memory for all players is created and initialized, no models or units yet, the map is still completely undiscovered.
  2. (Client) cNewGame for the module is called by the server. The AI will store the arguments passed with the call and recognize which players it has to control. Also, it has to initialize the Data blocks of its players now. This should be a standard initialization. Do not write values to the Data block that depend on other information than that contained in the RO and the function parameters, e.g. random values, because this could make the restoration of the Data block fail.
  3. (Server) Models and units are being created, the map remains undiscovered.
  4. (Client) cGetReady is called by the server for all players. The client can call initial cClientEx commands now and receives them immediately. Data block and unit/model status changes done from now on will be restored correctly, means now you could write random values.
  5. The server starts the game, the next command sent to the client is an in-game command, usually cTurn.

Loading a Saved Game

When a saved game is loaded, it will be replayed completely from the start. This does not affect AI very much, because all commands the AI sent during the original game are stored in the file and are simply processed again now. Means, during a game is loaded, AI does not have to think again what commands to order, it is not even allowed to. AI does not receive client commands, with a few exceptions. The exact process of loading a game is the following:

  1. Same as above.
  2. Same as above, except that cLoadGame is called instead of cNewGame.
  3. Same as above.
  4. (Client) The client now receives all initial cClientEx commands it sent from the cGetReady handler when the game was started originally.
  5. (Server) The Data block and the unit/model status members are restored to the values they had in the end of cGetReady when the game was started originally.
  6. (Client) cReplay is called by the server for all players. If the client calls cClientEx commands now, they will be processed. But usually, this should not be necessary here, because the client already received them in Step 4. These commands would not be saved anyway. Do not write to the Data block or to some status members here, because this could corrupt the data.
  7. The server now replays the game. The client receives all cClientEx commands it sent during the original game until the turn in which the game is being opened. The Data block and all status members are being restored to the state they had in this turn.
  8. The server continues the game, the next command sent to the client is an in-game command like cTurn, scContact or cShowXYZ.

Location Codes

Note: lx and ly are used in the following text as the horizontal and vertical extent of the playground as you receive with the cNewGame command.

Each tile on the playground has a unique location code. The codes range from 0 to lx*ly-1 and are formed like this:

Usually, you do not have to care for how the code is calculated. You can use location codes to identify a tile, e.g. units with the same location are placed on the same tile. All server commands that concern more than one tile use relative coordinates (dx,dy), which are much easier to handle. A relative coordinate determines the relative position to a starting tile. Relative coordinates are formed like this:

Note that relative coordinates only are valid if (dx+dy)&1 == 0. To transform relative coordinates to a location code, use this procedure, which calculates a location relative to Loc:

int dLoc(int Loc, int dx, int dy) { int y0 = (Loc+lx*1234)/lx-1234; return (Loc+(dx+(y0&1)+lx*1234)/2)%lx+lx*(y0+dy); }

To transform location codes to relative coordinates, use this procedure which calculates the coordinates of Loc1 relative to Loc0:

void Distance (int Loc0, int Loc1, int& dx, int& dy) { dx = (((Loc1%lx)*2+(Loc1/lx&1))-((Loc0%lx)*2+(Loc0/lx&1))+3*lx)%(2*lx)-lx; dy = Loc1/lx-Loc0/lx; }

Using the dLoc and Distance procedures you can make do with simple relative coordinates without having to care for the playground extent, isometry or the cylindrical world - all this works automatically. The only thing you still have to consider are the poles. If dLoc returns a code not in the valid range, the relative position you have calculated is beyond a pole outside the playground. Do not try to use such an invalid code.

Sample application: To inspect all adjacent land tiles of the tile at location Loc0, use

for (dx=-2;dx<=2;dx++) for (dy=-2;dy<=2;dy++) if (abs(dx)+abs(dy)==2) //valid and adjacent coordinates { Loc1 = dLoc(Loc0,dx,dy); //location of adjacent tile if (Loc1>=0 && Loc1<lx*ly //not beyond pole && Game.RO[Player]->Map[Loc1]&fTerrain>=fGrass) //not a water tile ... }


Diplomacy is built around making and accepting offers. An offer contains prices which will be delivered when the offer gets accepted as well as prices which have to be paid in order to accept the offer. Each price is represented by a 32 bit code, depending on its kind:

State reportopCivilReport + p<<16, p = concerned player
Military reportopMilReport + p<<16, p = concerned player
World mapopMap
Next closer treatyopTreaty + tr+1, tr = current treaty
End current treatyopTreaty + tr-1, tr = current treaty
Colony Ship PartsopShipParts + t<<16 + n, t = part type, n = number
MoneyopMoney + v, v = value
TechnologyopTech + ad, ad = technology
All technologiesopAllTech
Unit designopModel + mix, mix = model index
All unit designsopAllModel
Price of choiceopChoose

Offering to deliver things you do not have is not allowed. Also, offers containing more than one treaty price are not possible. But even if an offer is allowed, it is not necessarily useful, for example demanding a price of choice. Please consider that offers the opponent does not understand are wasted, because he will never accept them.

A special kind of offers are null-offers, containing no prices. Such an offer means the player has no more offers to make for this negotiation. If null-offers of both players immediately follow one another, the negotiation ends in agreement (in contrast to one player breaking it).


Besides the DLL file, your module pack must contain two more files:

("MyAI" stands for the filename of your module - must always be the same!)

To make your AI available for the game, first compile the sources (you should get a file *.dll). Second, move the DLL together with the accessory specified above to the main C-evo folder.

Server Commands: Information Request

Get nation chart. In case of enemy charts, only values for the turns 0..TurnOfCivilReport-1 (resp. TurnOfMilReport for military charts) are returned, the rest of the array remains undefined.
Data structure: TChart


in Command - command = sGetChart + type <<4, chart types are stExplore..stMil
in Subject - player
out Data [t] - value for turn t, meaning depends on chart type:
stPop - sum of all city sizes plus citizens in addable units
stTerritory - number of tiles of nation territory
stMil - sum of combat unit cost*health/100
stScience - number of technologies in 1/100
stExplore - number of discovered tiles
stWork - number of MP spent by settlers for tile improvement

Request required research points for currently developed tech.
out int *Data - required research points

Request resource production of a tile. Considers government form, wonders and national projects, but no city improvements. Trade is reported with government limit only (means as if the city had a courthouse). Works for all known tiles.
Data structure: TTileInfo

struct { int Food, Prod, Trade, ExplCity; }

in Subject - tile location
out Data->Food, Data->Prod, Data->Trade - resources produced

Request exact resource production of a tile for the city which is currently exploiting it. All factors are taken into consideration. Works only for own cities.
Data structure: TTileInfo

struct { int Food, Prod, Trade, ExplCity; }

in Subject - tile location
out Data->Food, Data->Prod, Data->Trade - resources produced
out Data->ExplCity - index of exploiting city

Request exact resource production of a tile for an (own) city of choice. All factors are taken into consideration.
Data structure: TTileInfo

struct { int Food, Prod, Trade, ExplCity; }

in Subject - tile location
in Data->ExplCity - index of city
out Data->Food, Data->Prod, Data->Trade - resources produced

Request progress of terrain improvement job.
Data structure: TJobProgressData

struct { int Required, Done, NextTurnPlus; }[nJob]

in Subject - tile location
out Data[x].Required - total MP required to complete job x
out Data[x].Done - MP done until now for job x
out Data[x].NextTurnPlus - gain of MP for job x in the end of the turn by units currently working on it

View enemy unit stack after the tile has been spied out with a special commando or spy plane. This tile state is indicated by the fSpiedOut flag. The function writes the units of the stack to RO.EnemyUn, beginning at index RO.nEnemyUn. This information is only valid immediately after the command was called, the next server command call might lead to its corruption.
in Subject - tile location
out int *Data - number of units in stack

Request which own unit will defend a certain tile. (For enemy locations, this is not necessary because you only know the defender.)
in Subject - tile location
out int *Data - unit index

This query allows to simulate an attack of a hypothetical attacker to an existing defender. It can be used to check an own attack in advance as well as to estimate a possible enemy attack. Restrictions: the attacked tile must be observed and the attacking model must be known to the requesting nation. The server return code is equal to an sMoveUnit that would order this attack.
Data structure: TBattleForecast

struct { int pAtt, mixAtt, HealthAtt, ExpAtt, FlagsAtt, Movement, EndHealthDef, EndHealthAtt; }

in Subject - location of defender
in Data->pAtt - attacking player
in Data->mixAtt - model index of attacking unit
in Data->HealthAtt, Data->ExpAtt, Data->FlagsAtt, Data->Movement - health, experience, flags and movement of attacking unit
out Data->EndHealthDef, Data->EndHealthAtt - health of defending and attacking unit after the battle

Request additional information about an own unit.
Data structure: TUnitReport

struct { int FoodSupport, ProdSupport, ReportFlags; }

in Subject - unit index
out Data->FoodSupport - food support required
out Data->ProdSupport - material support required (free units per city are not considered)
out Data->ReportFlags - additional flags, see protocol.pas under "unit report flags"

Find the shortest way for a unit to a specified tile or to the next city. This function only considers the information the player knows of. Neither are unknown tiles or roads used on the way nor unknown blockings considered. That is why moves suggested by this function might even be invalid. The function returns only the part of the way the unit can do in the current turn. (If your nation owns the Shinkansen Express, the 25 steps returned might be less than possible within the turn. You can use subsequent calls of sGetMoveAdvice to query the rest of the way.)
Note: This function does not make compromise in favour of execution speed. If the shortest path is visible to your nation, this function calculates it perfectly, even considering the actual remainder of movement points in the end of each turn along the way. I recommend to use this function only for real movement, not for decision making whether or not to move a certain unit to a certain location, because for the latter job a non-accurate algorithm is sufficient, which is potentially much faster.
Data structure: TMoveAdviceData

struct { int ToLoc, nStep, MoreTurns, MaxHostile_MovementLeft, dx[25], dy[25]; }

in Subject - unit index
in Data->ToLoc - desired move destination, maNextCity for shortest way to next city
in Data->MoreTurns - Maximum number of additional turns to consider. E.g. if this number is 0, the function only looks for moves which can be completed in the current turn. The lower the MoreTurns parameter is, the faster the function returns if there is no way possible.
in Data->MaxHostile_MovementLeft - must be set to 100, otherwise function will not work
out Data->nStep - number of moves in this turn (= number of valid entries in dx/dy)
out Data->MoreTurns - number of additional turns required to get to the desired location
out Data->dx[i], Data->dy[i] - single moves ending at destination (more about relative coordinates under "Location Codes")
out Data->MaxHostile_MovementLeft - MPs remaining for the turn after reaching the location
return values: eOk if success, eNoWay if no way known

Check whether an aircraft could return to city/base/carrier from certain location.
Data structure: TPlaneReturnData

struct { int Loc, Fuel, Movement; }

in Subject - unit index
in Data->Loc - hypothetical location of unit
in Data->Fuel - current fuel
in Data->Movement - hypothetical movement points left for the current turn
return values: eOk if aircraft could return, eNoWay if not

Request old style (C-evo 1.0) report on city work. Must be own city.
Data structure: TCityReport

struct { int HypoTiles, HypoTax, HypoLux, Working, Happy, FoodRep, ProdRep, Trade, PollRep, Corruption, Tax, Lux, Science, Support, Eaten, ProdCost, Storage, Deployed; }

in Subject - city index
in Data->HypoTiles - tiles that should be considered as exploited (for the current adjustment, set this to -1 or to TCity.Tiles of the city)
in Data->HypoTax, Data->HypoLux - tax and luxury rate that should be assumed (for current rates, set this to -1 or to RO.TaxRate resp. RO.LuxRate)
out Data->Working - number of working citizens
out Data->Happy - number of happy citizens, can be higher than number of working citizens if more working citizens would be happy
out Data->FoodRep, ProdRep, Trade - total collected food, production and trade points after improvement effects
out Data->PollRep - pollution
out Data->Corruption, Data->Tax, Data->Lux, Data->Science - corruption, tax, luxury and science output of city
out Data->Support, Data->Eaten - production and food taken for citizens and unit support
out Data->Storage - size of food storage
out Data->Deployed - number of deployed units

Request new style (C-evo 1.1) report on city work. Must be own city.
Data structure: TCityReportNew

struct { int HypoTiles,HypoTaxRate,HypoLuxuryRate, Morale, FoodSupport,MaterialSupport, ProjectCost, Storage, Deployed, CollectedControl,CollectedFood,CollectedMaterial,CollectedTrade, Working, FoodSurplus,Production,AddPollution, Corruption,Tax,Science,Luxury, HappinessBalance; }

in Subject - city index
in Data->HypoTiles - tiles that should be considered as exploited (for the current adjustment, set this to -1 or to TCity.Tiles of the city)
in Data->HypoTaxRate, Data->HypoLuxuryRate - tax and luxury rate that should be assumed (for current rates, set this to -1 or to RO.TaxRate resp. RO.LuxRate)
out Data->Morale - morale
out Data->FoodSupport, Data->MaterialSupport - food and material taken for unit support
out Data->ProjectCost - material cost of current project
out Data->Storage - size of food storage
out Data->Deployed - number of units causing unrest (unrest=2*deployed)
out Data->CollectedControl, Data->CollectedFood, Data->CollectedMaterial, Data->CollectedTrade - raw control, food, material and trade as collected by the citizens
out Data->Working - number of exploited tiles including city tile
out Data->FoodSurplus, Data->Production, Data->AddPollution - food surplus, production gain and pollution after all effects
out Data->Corruption, Data->Tax, Data->Science, Data->Luxury - corruption, tax, science and wealth after all effects
out Data->HappinessBalance = (Morale+Wealth+Control) - (Size+Unrest), value < 0 means disorder

Request state of all tiles in the city area.
Data structure: TCityAreaInfo

struct { int Available[27]; }

in Subject - city index
out Data->Available - state for area tile (dx,dy) in Available[(dy+3)<<2+(dx+3)>>1] (faAvailable..faInvalid), (dx,dy) relative to central tile (more about relative coordinates under "Location Codes")

Get enemy city information after this city has been spied out with a special commando or spy plane. This tile state is indicated by the fSpiedOut flag.
Data structure: TGetCityData

struct { int Owner; TCity c; }

in Subject - city tile location
out Data->Owner - owner of city
out Data->c - the city data

Get old style (C-evo 1.0) enemy city report after this city has been spied out with a special commando or spy plane. This tile state is indicated by the fSpiedOut flag.
Data structure: TCityReport, see under sGetCityReport
in Subject - city tile location
out Data - the city report

Get new style (C-evo 1.1) enemy city report after this city has been spied out with a special commando or spy plane. This tile state is indicated by the fSpiedOut flag.
Data structure: TCityReportNew, see under sGetCityReportNew
in Subject - city tile location
out Data - the city report

Find the perfect combination of tiles to exploit in the city area. Food supply and unit support are ensured, if possible. Extra food, material, tax and science are maximized. The optimization also works in connection with Luxury or Michelangelo's Chapel.
Data structure: TCityTileAdviceData

struct { unsigned long ResourceWeights, Tiles; TCityReport CityReport; }

in Subject - city index
in Data->ResourceWeights - specifies the evaluation formula (what to maximize), can be one of the values below "resource weights" in protocol.h
out Data->Tiles - tiles to set in order to achieve maximum resource output (valid input for sSetCityTiles)
out Data->CityReport - the city report if these tiles were set

Server Commands: Client Deactivation

Finish the current turn.

Request diplomatic contact with enemy. (Contact must be allowed by game rules!)
in Command - command = scContact + player <<4

Refuse diplomatic contact with enemy.

Accept diplomatic contact with enemy.

Notice latest decision of negotiation opponent.

Accept latest offer of negotiation opponent.

Cancel current treaty with negotiation opponent.

Make offer.
Data structure: TOffer

struct { int nDeliver, nCost, Price[12]; }

in Data->nDeliver - number of prices delivered if offer is accepted
in Data->nCost - number of prices required to pay to accept this offer
in Data->Price[0..nDeliver-1] - codes of the prices delivered
in Data->Price[nDeliver..nDeliver+nCost-1] - codes of the prices to pay

Unagreed break of negotiation.

Go back in time. Break the game and reload it at an earlier point. This is an option for AI development only, usable for a self-learning development approach. The command only works in supervisor mode. Be sure to remove all calls before shipping your AI.
in int *Data - Turn in which to reload the game

Server Commands: General Control

Set government form. Only valid if period of anarchy has just ended this turn (phChangeGov flag set in RO.Happened).
in Subject - desired government form (gAnarchy..gDemocracy)

Set tax, luxury and science rate.
in Subject - desired rates (0..100): (taxrate/10) + (luxrate/10)<<4

Change to anarchy.

Set advance to research next. Only valid if the last research was finished in this turn (phTech flag set in RO.Happened).
in Subject - technology, adMilitary for military research

After capturing a city, this command might be possible, indicated by the phStealTech flag set in RO.Happened. (With the current rules, this can only happen when you own the Temple of Zeus wonder.) Choose a technology to steal from the former city owner.
in Subject - technology

Cancel current state treaty with the player who was requested for contact but rejected. Only allowed in this situation.

Create unit model to start develop this turn.
in Subject - desired unit domain (dGround..dAir)

Change capability of unit model to develop, must be created with sCreateDevModel this turn.
in Command - command = sSetModelCap + desired value for this capability <<4
in subject - feature to change (mcOffense..mcJet)

Display AI debug message. Note that debug messages are turned off by default. Open the debug message popup menu with a right mouse click into the AI debug message window, choose there which message levels to display.
Please keep messages shorter than 256 characters.
in Subject - message level
in char *Data - message text

Set a map of debug numbers, one for each tile. You can turn the number display on using the menu item "General|Options|Test|AI Debug Map". The values are directly read from the array passed to this function everytime the map display is updated, means you only have to call this function once. In the end, before you deallocate the debug map memory, call sSetDebugMap with Data = NULL!
in void *Data - pointer to array of integers

During own turn, trigger refresh of debug map display. (Not necessary in the end of the turn because refresh happens automatically then.)

All commands >= cClientEx are taken for client exclusive. The server does nothing but immediately call the client back with exactly the same command, player and data. The maximum command possible is 0xFFFF. The subject parameter is ignored.

Use this command to save data. When a game is loaded, you will receive all ClientEx commands you sent in the right order. (However, be aware that all data you submit that way are being stored in the saved game. Excessive use will make the save file big.) As described above, the size of the Data block in DWords should always be specified by the lowest 4 bits of the command. You have to take care for that when using a ClientEx command. This means that you cannot store more than 60 bytes (15 DWords) with one ClientEx call.

This command is mainly intended for the user interface to save string-type information like file names, city names etc. I recommend not to use it for AI programming, because it's difficult to use and there are some problems to take care for (see below). Try to make do with the other ways of storing data, see under Saving Data.

Attention: Never call the Server from within a cClientEx handler! It might work during playing, but will cause problems when a saved game is being loaded. Also, note that parts of the RO data are not valid then. So I recommend not to access the RO block from a cClientEx handler at all. Last not least, you should not access the Data block, because cClientEx commands and data restoring are processesed in a different order when loading a saved game.

Server Commands: Unit Control

Remove unit. If the unit is located in a city, it is being utilized for the current city project.
in Subject - unit index

Set unit home to the city it is located in.
in Subject - unit index

Set mission type for subsequent covert operations.
in Command - command = sSetSpyMission + Mission <<4

Load unit onto a transport ship or plane on the same tile.
in Subject - unit index

Unload a unit from a transport ship or plane.
in Subject - unit index

Prefer this transport when loading units. Voids any former call to this function.
in Subject - unit index

Move a unit resp. attack enemy unit.
in Command - command = sMoveUnit + (dx&7)<<4 +(dy&7)<<7, move to direction (dx,dy) (more about relative coordinates under "Location Codes")

Since only moves to adjacent tiles are allowed, no more than the following eight command values are valid:
sMoveUnit+0x020 - move/attack to east
sMoveUnit+0x060 - move/attack to west
sMoveUnit+0x090 - move/attack to south-east
sMoveUnit+0x0F0 - move/attack to south-west
sMoveUnit+0x100 - move/attack to south
sMoveUnit+0x300 - move/attack to north
sMoveUnit+0x390 - move/attack to north-east
sMoveUnit+0x3F0 - move/attack to north-west

in Subject - unit index

Add unit to city.
in Subject - unit index, unit must be settler or a conscripts unit

Start terrain improvement.
in Command - command = sStartJob + Job <<4
in Subject - unit index, unit must be settler

Server Commands: City Management

Set city project.
in Subject - city index
in int *Data - desired project (model index or improvement index +cpImp)

sBuyCityProject, sSellCityProject
Buy resp. sell city project.
in Subject - city index

sSellCityImprovement, sRebuildCityImprovement
Sell resp. rebuild city improvement.
in Subject - city index
in int *Data - improvement

Set tiles to exploit by a city. This function does not work partially. If not all tiles can be set as required, the function does nothing and returns an error.
in Subject - city index
in int *Data - bitarray telling the tiles (same format as TCity.Tiles). Set bit with index (dy+3)<<2+(dx+3)>>1 for tiles to exploit. (dx,dy) relative to central tile (more about relative coordinates under "Location Codes"). Note that the bit for the city tile itself (dx=dy=0) must always be set.

Client Commands: Module Related

These commands apply to your whole AI module, do not use the passed Player parameter.

Initialize the Module.
Data structure: TInitModuleData

struct { TServerCall Server; int DataVersion, DataSize; }

in Data->Server - address of the server function
out Data->DataVersion - version of Data block, see Saving Data
out Data->DataSize - required size of Data block in bytes, maximum is 4096

Release the Module.

cNewGame, cLoadGame
Start a new game resp. load a saved one. See under Game Start Sequence.
Data structure: TNewGameData

struct { int lx, ly, LandMass, MaxTurn, Difficulty[15]; PLAYER_RO* RO[15]; }

in Data->lx, Data->ly - playground horizontal and vertical size
in Data->LandMass - landmass (per cent of the complete map), 0 indicates a custom map
in Data->Difficulty - Difficulty levels on which the single players are playing. 0 for supervisor, -1 for unused player numbers.
in Data->RO - Addresses of the Read-Only data blocks of the players your module is going to control. Pointers for enemy players are set to NULL.

The current game is going to end.

Client Commands: General Control

cGetReady, cReplay
See under Game Start Sequence.

Client Commands: Activation (Turn Mode)

Start this player's turn.
Possible deactivations: sTurn, scContact

Attention: In the turn after a nation was made extinct, this client function is called a last time with the player's flag erased in RO.Alive. This allows you to do some finalization for this player. In this situation, you should call sTurn as the only server command, most others will not work. The client will never get called again for this player within this game.

Continue this player's turn after a negotiation.
Possible deactivations: sTurn, scContact

Client Commands: Activation (Negotiation Mode)

Enemy asks for starting negotiation.
in int Data - player
Possible deactivations: scDipStart, scReject

Enemy has accepted contact, start negotiation now.
Possible deactivations: scDipOffer, scDipCancelTreaty, scDipBreak

Enemy has noticed latest decision, continue with negotiation now.
Possible deactivations: scDipOffer, scDipCancelTreaty, scDipBreak

Enemy has accepted latest offer, continue with negotiation now.
Possible deactivations: scDipOffer, scDipCancelTreaty, scDipBreak

Enemy has canceled state treaty.
Possible deactivations: scDipNotice, scDipCancelTreaty, scDipBreak

Enemy makes offer.
Data structure: OFFER

struct { int nDeliver, nCost, Price[12]; }

in Data->nDeliver - number of prices delivered if offer is accepted
in Data->nCost - number of prices required to pay to accept this offer
in Data->Price[0..nDeliver-1] - codes of the prices delivered
in Data->Price[nDeliver..nDeliver+nCost-1] - codes of the prices to pay
Possible deactivations: scDipAccept (if made offer is allowed to be accepted), scDipOffer, scDipCancelTreaty, scDipBreak

Enemy breaks negotiation.
Possible deactivations: scDipNotice, scDipCancelTreaty

Client Commands: Information

These commands tell about an enemy's activity during his turn. The player being told remains inactive.

cShowMoving, cShowCapturing, cShowAttacking
Tells about enemy movement or attack.
cShowMoving: Enemy unit moves, no city capture. Moving unit is already removed from origin tile but not yet placed to destination tile. Command is always followed by cShowAfterMove.
cShowCapturing: Enemy unit captures a city (not necessarily one of yours). Moving unit is already removed from origin tile but not yet placed to destination tile, city still belongs to old owner. Command is always followed by cShowAfterMove.
cShowAttacking: Enemy unit is going to attack (not necessarily one of your units). Nothing happened yet. Command is always followed by cShowAfterAttack.
The unit specified by the data structure is the moving/attacking one.
Data structure: TShowMove

struct { int Owner, Health, mix, emix, Flags, FromLoc, dx, dy, EndHealth, EndHealthDef; }

in Data->Owner - owner of unit
in Data->Health - health of unit
in Data->mix - model index for owner
in Data->emix - model index in EnemyUn list
in Data->Flags - fMulti if more than one unit
in Data->FromLoc - move starting location
in Data->dx, Data->dy - move direction
in Data->EndHealth - health of unit after move/attack, 0 if lost
in Data->EndHealthDef - (cShowAttacking only) health of defender after combat, 0 if destroyed

cShowUnitChanged, cShowAfterMove, cShowAfterAttack, cShowCityChanged
Tells about enemy unit or city change.
cShowUnitChanged: Defender of a tile has changed due to unknown reason. Not called in case of moves and attacks that are visible for you (cShowMoving/cShowAttacking).
cShowAfterMove: Always follows cShowMoving or cShowCapturing. Move action is completely finished now, cities are captured. Passed location is the destination tile of the move.
cShowAfterAttack: Always follows sShowAttacking. Attack action is completely finished now. Called twice: first with defender location, second with attacker location.
cShowCityChanged: City was founded, destroyed or captured. Nation borders have already been recalculated.
in int Data - location

Tells that an enemy is about to cancel the current state treaty after you refused contact. This command is not sent when a treaty is canceled during a negotiation (scDipCancelTreaty).
in int Data - player

Tells that an enemy has canceled all treaties with you after you canceled peace with one of his allies.
in int Data - player

Negotiations have ended, enemy is continuing his turn.

There are some additional client information commands, but these are not relevant for AI programming.


Q1. The rules of the game are not exactly specified. I need more information than what is written in the manual.

Answer: Sometimes an AI programmer needs very exact information about calculations or about the behavior of the game in special situations. This exact information often is not contained in the in-game manual, because this manual is for players. Players usually don't need and don't want that precision overkill. If you need more information, please ask me or go to the AI forum. (Or maybe try to analyze the sources of the game...)

Q2. How can my AI...

Answer: All of these things are not part of the actual game. The user interface implements these mechanisms in order to make the game better playable by human players. If you think something similar could be helpful in your AI, you must implement it. The means described in this manual are enough for that.

Q3. The protocol.pas contains constants for some server and client commands that are not explained in this document/not contained in the protocol.h. What commands are these?

These commands are not for AI use. It's special commands for the user interface, which uses the AI interface for communication with the server, too.

Changes since 1.0.4