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.
Contents
The Module Concept
General Use of Commands
Terminology
Saving Data
Game Start Sequence
Location Codes
Diplomacy
Accessory
Reference: Server Commands
Reference: Client Commands
Project Homepage
>>> c-evo.org
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.
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.
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 term | Meaning/player term |
Enemy | All other nations, even allied |
Model | Unit class |
Domain | Unit domain (Ground/Sea/Air) |
Ship | Transstellar colony ship |
Master | If unit A carries unit B, A is the master of B. |
Health | Unit property, = 100-Damage |
Job | Settler terrain improvement job |
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.
Starting a New Game
This is what happens step by step when the player starts a new game.
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:
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:
Kind | Code |
State report | opCivilReport + p<<16, p = concerned player |
Military report | opMilReport + p<<16, p = concerned player |
World map | opMap |
Next closer treaty | opTreaty + tr+1, tr = current treaty |
End current treaty | opTreaty + tr-1, tr = current treaty |
Colony Ship Parts | opShipParts + t<<16 + n, t = part type, n = number |
Money | opMoney + v, v = value |
Technology | opTech + ad, ad = technology |
All technologies | opAllTech |
Unit design | opModel + mix, mix = model index |
All unit designs | opAllModel |
Price of choice | opChoose |
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.
sGetChart
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
int[maxTurn];
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
sGetTechCost
Request required research points for currently developed tech.
out int *Data - required research points
sGetTileInfo
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
sGetCityTileInfo
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
sGetHypoCityTileInfo
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
sGetJobProgress
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
sGetUnits
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
sGetDefender
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
sGetBattleForecast
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
sGetUnitReport
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"
sGetMoveAdvice
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
sGetPlaneReturn
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
sGetCityReport
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
sGetCityReportNew
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
sGetCityAreaInfo
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")
sGetCity
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
sGetEnemyCityReport
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
sGetEnemyCityReportNew
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
sGetCityTileAdvice
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
sTurn
Finish the current turn.
scContact
Request diplomatic contact with enemy. (Contact must be allowed by game
rules!)
in Command - command = scContact + player <<4
scReject
Refuse diplomatic contact with enemy.
scDipStart
Accept diplomatic contact with enemy.
scDipNotice
Notice latest decision of negotiation opponent.
scDipAccept
Accept latest offer of negotiation opponent.
scDipCancelTreaty
Cancel current treaty with negotiation opponent.
scDipOffer
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
scDipBreak
Unagreed break of negotiation.
sReload
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
sSetGovernment
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)
sSetRates
Set tax, luxury and science rate.
in Subject - desired rates (0..100): (taxrate/10) + (luxrate/10)<<4
sRevolution
Change to anarchy.
sSetResearch
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
sStealTech
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
sCancelTreaty
Cancel current state treaty with the player who was requested for contact
but rejected. Only allowed in this situation.
sCreateDevModel
Create unit model to start develop this turn.
in Subject - desired unit domain (dGround..dAir)
sSetDevModelCap
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)
sMessage
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
sSetDebugMap
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
sRefreshDebugMap
During own turn, trigger refresh of debug map display.
(Not necessary in the end of the turn because refresh happens automatically then.)
cClientEx
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.
sRemoveUnit
Remove unit. If the unit is located in a city, it is being utilized for the
current city project.
in Subject - unit index
sSetUnitHome
Set unit home to the city it is located in.
in Subject - unit index
sSetSpyMission
Set mission type for subsequent covert operations.
in Command - command = sSetSpyMission + Mission <<4
sLoadUnit
Load unit onto a transport ship or plane on the same tile.
in Subject - unit index
sUnloadUnit
Unload a unit from a transport ship or plane.
in Subject - unit index
sSelectTransport
Prefer this transport when loading units. Voids any former call to this function.
in Subject - unit index
sMoveUnit
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
sAddToCity
Add unit to city.
in Subject - unit index, unit must be settler or a conscripts unit
sStartJob
Start terrain improvement.
in Command - command = sStartJob + Job <<4
in Subject - unit index, unit must be settler
sSetCityProject
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
sSetCityTiles
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.
These commands apply to your whole AI module, do not use the passed Player parameter.
cInitModule
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
cReleaseModule
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.
cBreakGame
The current game is going to end.
cGetReady, cReplay
See under Game Start Sequence.
cTurn
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.
cContinue
Continue this player's turn after a negotiation.
Possible deactivations: sTurn, scContact
scContact
Enemy asks for starting negotiation.
in int Data - player
Possible deactivations: scDipStart, scReject
scDipStart
Enemy has accepted contact, start negotiation now.
Possible deactivations: scDipOffer, scDipCancelTreaty, scDipBreak
scDipNotice
Enemy has noticed latest decision, continue with negotiation now.
Possible deactivations: scDipOffer, scDipCancelTreaty, scDipBreak
scDipAccept
Enemy has accepted latest offer, continue with negotiation now.
Possible deactivations: scDipOffer, scDipCancelTreaty, scDipBreak
scDipCancelTreaty
Enemy has canceled state treaty.
Possible deactivations: scDipNotice, scDipCancelTreaty, scDipBreak
scDipOffer
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
scDipBreak
Enemy breaks negotiation.
Possible deactivations: scDipNotice, scDipCancelTreaty
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
cShowCancelTreaty
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
cShowCancelTreatyByAlliance
Tells that an enemy has canceled all treaties with you after you canceled
peace with one of his allies.
in int Data - player
cShowEndContact
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.