TurboRisk Programming Reference
One of the unique features of TurboRisk is that it allows the user to program his/her own computer players: programs that define the behaviour of a player controlled by the computer.
TurboRisk programs are text files with extension ".TRP" (TurboRisk Program) that contain instructions in a subset of PASCAL. TurboRisk uses the Delphi-style Innerfuse Pascal Script 3, free for non-commercial uses, developed by Carlo Kok (thanks Carlo!).
The language is essentially PASCAL. If you know PASCAL you should not have any particular program. Study the SIMPLE.TRP example program to have a starting point. A PASCAL course is far beyond the scope of this document (and my possibilities!). A very important restriction you must know is that the interpreter does not allow procedure and functions defined within another procedure or function, and this makes some of the TurboRisk 1.2 TRPs no more compatible with the current version of TurboRisk. The document conversion.html deals with some of the issues involved in porting 1.2 TRPs to current version of TurboRisk.
Each TurboRisk program MUST CONTAIN five procedures with predefined name and parameters that are called by TurboRisk when the computer player is expected to take some decision. Each procedure receives some "var" parameters (the PASCAL convention to say a parameter is passed by reference instead of by value), which are used to return to TurboRsk its results. This is the typical program structure of a TRP program:
Program
MyProgram; // this statement is optional
procedure
MyProcedure(...); // define as many procedures and functions as you
want
begin
...
end;
function
MyFunction(...): ...;
begin
...
end;
// This and the following procedures MUST exist in the program.
// Their position in the code is not important.
procedure
Assignment(var ToTerritory: integer);
begin
// The ASSIGNMENT procedure is called during the initial assignment
of territories
// to know which is the program's choice. Return the result in the
ToTerritory
// var parameter.
...
end;
procedure
Placement(var ToTerritory: integer);
begin
// The PLACEMENT procedure is called at the beginning of each turn
to decide where
// to place new armies. It is called N times, each one to place a
single army, until
// all new armies are placed. Return the result in the ToTerritory
var parameter.
...
end;
procedure
Attack(var FromTerritory, ToTerritory: integer);
begin
// The ATTACK procedure is called at attack time. Each call to
ATTACK corresponds
// to a single throw of the dice. Return the program's choice in
the FromTerritory
// and ToTerritory var parameters. Set FromTerritory to 0 if you
decide not to attack
// and end your attack phase.
...
end;
procedure
Occupation(FromTerritory, ToTerritory: integer; var Armies: integer);
begin
// The OCCUPATION procedure is called after a successful attack.
The parameters
// FromTerritory and ToTerritory are properly set by TurboRisk when
entering the
// routine. Return the number of armies you want to move in the
Armies var parameter.
...
end;
procedure
Fortification(var FromTerritory, ToTerritory, Armies: integer);
begin
// The FORTIFICATION procedure is called after the attack phase.
Return the program's
// choice in the FromTerritory, ToTerritory and Armies var
parameters.
// Set FromTerritory to 0 if you decide not to move any army and
end your turn.
...
end;
begin
// You can place here some optional code which is executed just
once at the beginning
// of a new game.
...
end.
You can define your own variables, procedures and functions. Please remember that all your variables will be local variables, that is they will be erased between calls. Even if you give your own variables a global scope, out of any procedure, they will not preserve their value. This happens because TurboRisk uses the same space to load and execute all of the different computer player programs, turn after turn. To overcome this problem TurboRisk provides a place where to store global values: it's an internal buffer, which you can have access to through the UBufferSet and UBufferGet functions explained below. You have 50 buffers for your own use, each one being able to containe a value of type double. Be sure to use the trunc() function when assigning the value of a buffer to an integer value:
var
i: integer;
begin
i:=trunc(UBufferGet(5));
IFPS3 is not very tolerant about type mixing in expression and logical conditions. The following code aborts execution, because of the comparing of an integer value with a double value:
var
TotalArmies: integer;
begin
if PArmiesCount(T)>TotalArmies * 0.35 then
The correct code fragment is the following:
var
TotalArmies: integer;
begin
if PArmiesCount(T)>trunc(TotalArmies * 0.35) then
There are some other reported issues about IFPS3 and type mixing in logical expressions. For example the code:
var
i: integer;
d: double;
if (TArmies(T) / i) < d then
stops the interpreter any time TArmies(T)/i is not a whole number. Unfortunately
the trunc() function doesn't seem to solve this case, which needs typecasting
and a temporary variable:
var
i: integer;
d, temp: double;
temp := (double(TArmies(T))) / (double(i));
if temp < d then
Arrays are supported only in their dynamic form. Use the SetArrayLength() procedure to set their dimension. First element is always at location 0, last element at Length-1. The function GetArrayLength returns the size of the array. Example:
var
i: integer;
MyArr: array of integer;
begin
SetArrayLength(MyArr,5);
for i:=0 to GetArrayLength(MyArr)-1 do
MyArr[i] := i*i;
end;
The following PASCAL features are currently not supported by IFPS3:
TurboRisk provides a large set of procedures and functions which form a sort of API: they give your program access to information about territories, players, continents and the status of the game.
Many functions require or return information about territories, players and continents. They follow a common convention, where territories are numbered from 1 to 42, players from 1 to 10 and continents from 1 to 6.
1 Alaska
2 Northwest Territory
3 Greenland
4 Alberta
5 Ontario
6 Quebec
7 Western US
8 Eastern US
9 Central America
10 Venezuela
11 Peru
12 Brazil
13 Argentina
14 North Africa
15 Egypt
16 East Africa
17 Congo
18 South Africa
19 Madagascar
20 Iceland
21 Scandinavia
22 Ukraine
23 Northern Europe
24 Great Britain
25 Western Europe
26 Southern Europe
27 Middle East
28 Yamal-Nemets
29 Kazakhstan
30 India
31 Siam
32 China
33 Mongolia
34 Taymyr
35 Yakut
36 Buryat
37 Koryak
38 Japan
39 Indonesia
40 Western Australia
41 Eastern Australia
42 New Guinea
1 North America
2 South America
3 Europe
4 Africa
5 Asia
6 Australia
function TName(T: integer): string;
returns the name of territory T, '?' if T is out of range
function TOwner(T: integer): integer;
returns the owner of territory T, 0 if T is unassigned, -1 if T is out of range
function TArmies(T: integer): integer;
returns the number of armies on territory T, 0 if T is unassigned, -1 if T is
out of range
function TContinent(T: integer): integer;
returns the ID of the continent which territory T belongs to, -1 if T is out of
range
function TBordersCount(T: integer): integer;
returns the number of borders of territory T (max 6), -1 if T is out of range
function TBorder(T,B: integer): integer;
returns bordering territory #B of territory T, -1 if T or B are out of range
function TIsBordering(T1,T2: integer): boolean;
returns true if territory T1 is bordering on territory T2, false if not or T or
B are out of range
function TIsFront(T: integer): boolean;
returns true if territory T is owned by current player and has at least one
bordering territory occupied by opponents, false if not or T is out of range
function TIsMine(T: integer): boolean;
returns true if territory T is owned by current player, false if not or T is
out of range
function TIsEntry(T: integer): boolean;
returns true if territory T is an Entry Territory
Entry territories are territories which are part of a continent but
may be accessed from another continent
function TFrontsCount(T: integer): integer;
returns the number of territories bordering on territory T which are occupied
by opponents, -1 if T is out of range
function TFront(T,F: integer): integer;
returns bordering enemy territory #F of territory T, -1 if T or F are out of
range
function TStrongestFront(T: integer; var ET,EA: integer): boolean;
returns false if T is out of range, otherwise true.
The "var" variables will contain information on territory T:
ET: number of the strongest enemy's territory bordering on T, 0 if T has not
fronts
EA: enemy's armies on territory ET
(unassigned territories are not counted)
function TWeakestFront(T: integer; var ET,EA: integer): boolean;
returns false if T is out of range, otherwise true.
The "var" variables will contain information on territory T:
ET: number of the weakest enemy's territory bordering on T, 0 if T has not
fronts
EA: enemy's armies on territory ET
(unassigned territories are not counted)
function TPressure(T: integer): integer;
returns the total number of enemy armies bordering on territory T, -1 if T is
out of range
function PMe: integer;
returns the ID of the player in turn
function PName(P: integer): string;
returns player's P name, '?' if P is out of range
function PProgram(P: integer): string;
returns player's P program file name (lowercase) if player is computer,
'human' if human, '?' if P is out of range
function PActive(P: integer): boolean;
returns true if player P is active, false if not or P is out of range
function PHuman(P: integer): boolean;
returns true if P is a human player, false if P is computer player or P is out
of range
function PArmiesCount(P: integer): integer;
returns the number of armies totally controlled by player P on all of its
territories, -1 if P is out of range
function PNewArmies(P: integer): integer;
returns the number of armies which player P still has to place on his
territories, -1 if P is out of range
function PTerritoriesCount(P: integer): integer;
returns the number of territories controlled by player P, -1 if P is out of
range
function COwner(C: integer): integer;
returns the owner of continent C, 0 if C has more then one occupant, -1 if C is
out of range
function CBonus(C: integer): integer;
returns the number of armies (per turn) which the control of continent C
entitles to, -1 if C is out of range
function CTerritoriesCount(C: integer): integer;
returns the number of territories belonging to continent C, -1 if C is out of
range
function CTerritory(C,T: integer): integer;
returns the ID of territory #T belonging to continent C, -1 if T or C are out
of range
function CBordersCount(C: integer): integer;
returns the number of border territories on continent C (max 6), -1 if C is out
of range
Border territories are territories which are outside the continent but
may be accessed from it
function CBorder(C,B: integer): integer;
returns border territory #B of continent C, -1 if C or B are out of range
Border territories are territories which are outside the continent but
may be accessed from it
function CEntriesCount(C: integer): integer;
returns the number of entry territories on continent C (max 6), -1 if C is out
of range
Entry territories are territories which are part of a continent but
may be accessed from another continent
function CEntry(C,B: integer): integer;
returns entry territory #B of continent C, -1 if C or B are out of range
Entry territories are territories which are part of a continent but
may be accessed from another continent
function CAnalysis(C: integer; var PT,PA,ET,EA: integer): boolean;
returns false if C is out of range, otherwise true.
The "var" variables will contain information on continent C:
PT: player in turn's number of territories on continent C
PA: player in turn's armies on continent C
ET: enemy's number of territories on continent C
EA: enemy's armies on continent C
(unassigned territories are not counted)
function CLeader(C: integer; var P,T,A: integer): boolean;
returns false if C is out of range, otherwise true.
The "var" variables will contain information on continent C:
P: leader = player who controls greatest number of territories
T: leader's number of territories on continent C
A: leader's armies on continent C
if two or more players own the same number of territories, the leader is the
one which has more armies. If continent is empty (all territories unissegned) P
returns 0.
(unassigned territories are not counted)
function SConquest: boolean;
returns true if current player has conquered at least one territory on current
turn, otherwise false
function SPlayersCount: integer;
returns the number of active players
procedure UMessage(M: string);
displays a modal window with message M
procedure ULog(M: string);
writes message M on the log
procedure UBufferSet(B: integer; V: double);
set B element of Player's buffer to value V (B ranges from 1 to 50)
function UBufferGet(B: integer): double;
returns Bth element of Player's buffer, -1 if B is out of range (B ranges from
1 to 50)
function URandom(R: integer): double;
if R>0 returns an integer random number I in the range 0
<= I < R, otherwise returns a double X in the range 0 < = X < 1