TurboRisk Conversion Reference


Overview

Since its birth and up to version 1.2, the scripting facility of TurboRisk was based on the free Delphi Interpreter by S.Kurinny & S.Kostinsky (Delphin). From version 2.0 on, TurboRisk uses the Delphi-style RemObjects Pascal Script (ROPS) runtime interpreter .

This documents explains some of the differences between the two script languages and their implementation in TurboRisk, and deals with some of the issues to face when converting a TRP from the older version to the current one.


General program structure

The typical program structure of a TRP expected by the current version of TurboRisk is described in the document programs.html. There are few differences between this structure and the previous one.

  1. Until TurboRisk 1.2, the five procedure Assignment, Placement, Attack, Occupation and Fortification were at any effect independent programs. For this reason you were not allowed to write common functions or procedures to share among all of the them. Anything that was before the declaration of those procedures or after their last end; statement was treated as a comment. This is no more true: the source file is at any effect a unique and complete Pascal program.

  2. One of the effects of point 1 is that free text between the procedures is no more allowed, you have to make sure it respects the standard Pascal conventions for comments.

  3. Another effect of point 1 is that now you can define your own functions or procedures, and that you can call them from any other procedure. Actually you have to do so, because ROPS doesn't implement the definition of procedures and functions inside another procedure or function. Some of the old TRPs use this nested structure: the inner procs and funcs have to be moved outside.

  4. The source may begin with an optional Program MyProgram; statement. It is allowed for clarity, but has no effect.

  5. TurboRisk 1.2 used three implicit global variables to send parameters to the routines and receive their results: FromTerritory, ToTerritories and Armies. These variables don't exist anymore. Values are passed and received by TurboRisk using parameters:

    procedure Assignment(var ToTerritory: integer);
    procedure Placement(var ToTerritory: integer);
    procedure Attack(var FromTerritory, ToTerritory: integer);
    procedure Occupation(FromTerritory, ToTerritory: integer; var Armies: integer);
    procedure Fortification(var FromTerritory, ToTerritory, Armies: integer);

    For those of you not very familiar with Pascal, the word var before the declaration of a parameter means that this is passed "by reference" instead of "by value". That is, if the procedure modifies its value, the calling program receives the modified value.

  6. The program MUST contain a main flow, delimited by the begin and end. words. As in any good old Pascal Program, this main flow has to be placed at the very end of the source, so that end. is the last statement in the source. This structure is required by ROPS, but TurboRisk doesn't need it, so you can leave the begin and end. block empty. However, if you place any instruction there, that code will be executed once at the beginning of a new game. This can be useful to prepare some configuration and store it in the buffers.

  7. ROPS performs much more strict controls about structure than Delphin does. For this reason, some of the old TRPs, which have non matching begin-end pairs, don't compile anymore.

Types, Variables, Expressions

As in TurboRisk 1.2, all the variables you define will be LOCAL variables, no matter their scope. This happens because TurboRisk uses the same space to load and execute all of the different computer player programs, turn after turn. You still have to store GLOBAL values using the TurboRisk internal buffers, which you can have access to through the UBufferSet and UBufferGet functions.

The possibility of defining global scope variables is still a new useful feature, which makes possible for a procedure to share values with other called procedures.

The real type is no more supported. Use double instead.

ROPS uses standard PASCAL conventions about the syntax and priorities of operators, including the logical one. The following statement was legal in TurboRisk 1.2:

  if TIsMine(x) and TArmies(x)=TArmies(x) then begin

but it's no more legal in current TurboRisk. In PASCAL the and operator has a higher priority than the "equal", "greater than", "less then" and the other comparation operators, so the proper syntax for that statement shall be:

  if TIsMine(x) and (TArmies(x)=TArmies(x)) then begin

ROPS is not very tolerant about type mixing in expression and logical conditions. The following code aborts execution, because it compares 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

You need to be careful when using this method though. Consider the following example:

var
  TotalArmies: integer;
begin
  if PArmiesCount(T)<trunc(TotalArmies * 0.35) then

Notice that the comparative operator has changed. If PArmiesCount(T) is 17 and TotalArmies * 0.35 is 17.5, then 17.5 will be truncated to 17 and the statement will be false even though it should be true.
You can use type casting in this case to get the correct result:

var
  TotalArmies: integer;
begin
  if double(PArmiesCount(T))<TotalArmies * 0.35 then

There are some other reported issues about ROPS and type mixing in logical expressions. For example the code:

var
  i: integer;
  d: double;
begin
  ...
  if (TArmies(T) / i) < d then ...

stops the interpreter any time TArmies(T)/i is not a whole number. The trunc() function won't work in this case, so it is necessary to use type casting instead:

var
  i: integer;
  d: double;
begin
  ...
  if (double(TArmies(T))) / (double(i)) < d then ...


TurboRisk API

Some of the interface functions have a new syntax:

procedure UMessage(M: string);
Previously it accepted an unlimited number of parameters, of any kind:
UMessage('Error: ',MyIntVar+4,... );
Now you have to pass a single string value. Thus the previous example becomes:
UMessage('Error: '+IntToStr(MyIntVar+4)+... );

procedure ULog(M: string);
Same as UMessage.

procedure UBufferSet(B: integer; V: double);
function UBufferGet(B: integer): double;
Previously the value stored in the buffer was of type variant. Now it is a double.

function URandom(R: integer): double;
This function as well returns a double;


TRComp

TRComp is a TurboRisk program Compiler. It allows simple editing of your source file and provide an interface to the ROPS engine. Take advantage of it to compile your TRP and detects any problem it might have in the structure or in the syntax.