Scorekeepers are the objects that manage scoring, winning, and losing. A game design need not define any scorekeepers, and none are created by default. A scorekeeper may either maintain a numeric score that is used at the end of the game to decide rankings, or simply declare a side to have won or lost. Perhaps it would be better to refer to scorekeepers as victory condition watchers.
Form: scorekeeper
name properties...
This form creates or modifies a scorekeeper with the given name,
with the given properties.
NOTE: Specifying name is not supported (as of 2005/02/06).
ScorekeeperProperty: title
str
This property is a string that identifies the scorekeeper to the
players. Defaults to ""
.
ScorekeeperProperty: when
(type [ exp ])
This property is when the scorekeeper will be checked or updated.
Defaults to after-turn
.
ScorekeeperWhenType: before-turn
exp
This indicates that the scorekeeper will run at the start of each turn
matching exp, or after every turn if exp is not given.
NOTE: This property is currently ignored (as of 2005/01/23).
ScorekeeperWhenType: after-turn
exp
This indicates that the scorekeeper will run at the end of each turn for which exp is true, or after every turn if exp is not given. If exp is a number rather than a full expression, then a test of exp >= current turn number will be made. See the examples subsection for a full expression.
ScorekeeperWhenType: after-event
exp
This indicates that the scorekeeper will run after every event
matching exp, or after every event if exp is not given.
NOTE: Specifying exp is not supported (as of 2005/01/23).
ScorekeeperWhenType: after-action
exp
This indicates that the scorekeeper will run at the end of each action
matching exp, or after every action if exp is not given.
NOTE: Specifying exp is not supported (as of 2005/01/23).
ScorekeeperProperty: applies-to
side-and-sideclass-list
This property is the set of sides or side classes to which the
scorekeeper applies. Scorekeepers apply only to sides that are in the
game. Defaults to nil
, which means all sides.
ScorekeeperProperty: known-to
side-and-sideclass-list
This property is the list of sides that know about this scorekeeper, and
can see the value of the score for each side that it applies to.
Defaults to nil
, which means all sides.
NOTE: This property is currently ignored (as of 2005/01/23).
ScorekeeperProperty: trigger
form
This property is an expression that is true when it is time to start
checking the scorekeeper's main test. Once a scorekeeper is triggered,
it remains active. Defaults to false
.
ScorekeeperProperty: triggered
t/f
This property is true if the scorekeeper is currently triggered.
Defaults to true
.
ScorekeeperProperty: do
forms...
This property is a list of forms to execute in order each time the
scorekeeper runs. Defaults to ()
.
ScorekeeperProperty: keep-score
t/f
If this property is false
, then no numeric score is kept.
Defaults to true
.
ScorekeeperProperty: initial-score
value
This property is the value of the score upon game startup.
The forms in the body (the do
property) of the scorekeeper may be
any of the forms listed here.
ScorekeeperForm: last-side-wins
If supplied as the only symbol in the body, then the scorekeeper implements the usual "last side left in the game wins" behavior.
ScorekeeperForm: last-alliance-wins
If supplied as the only symbol in the body, then the scorekeeper implements the "last alliance left in the game wins" behavior. For the purposes of this scorekeeper, an alliance means that the sides in the alliance all trust each other.
ScorekeeperForm: if
test action [ else-action ]
If the test evaluates to a non-nil
result, then the
action will be done. Else, the else-action will be done, if
there is one.
ScorekeeperForm: cond
(test actions...) ...
ScorekeeperForm: win
This scorekeeper action causes the side to win immediately.
ScorekeeperForm: lose
This scorekeeper action causes the side to lose immediately.
ScorekeeperForm: end
This scorekeeper action ends the game immediately, with a draw for all remaining sides.
ScorekeeperForm: add-score
exp
This adds the result of evaluating exp to the score of the given side. The value may be a negative number. This form cannot be used with a non-numeric scorekeeper.
ScorekeeperForm: set-score
exp
This sets the result of evaluating exp as the score of the given side. The value may be a negative number. This form cannot be used with a non-numeric scorekeeper.
Scorekeepers can use all of the general GDL functions such as
add
, remove-list
, >=
, etc.... They can also
use special functions, which are mentioned below. Scorekeeper functions
can be used in any place where a scorekeeper test expression is allowed.
This means that they can be used in when
conditions, in if
test clauses, or the form of set-score
, for example.
ScorekeeperFunction: turn
This GDL keyword gives the current turn number.
ScorekeeperFunction: score
This GDL keyword returns the current score in the current scorekeeper for the current side. A warning will be produced if you attempt to use this keyword with a non-numeric scorekeeper.
ScorekeeperFunction: sum-uprop
types property
The result is the sum of the property values for all units of the given
type(s). The property must be an integer value, such as point-value
or hp-max
. The point-value
property is treated specially
in that an unit's actual point assignment is used if it exists; if the
point assignment does not exist, then the summation behavior is the
default, namely the value for the unit type is used.
One of the most simple and ubiquitous scorekeepers is:
(scorekeeper (do last-side-wins))
This specifies that the last side standing will win. It should be noted that if the independent side is not being controlled by an AI, then it is not considered in this case. If the independent side is being controlled by an AI, then it is considered.
If you do not wish a side (such as an AI-controlled independent side) to be
considered for a given scorekeeper, then you can use the applies-to
property to restruct which sides the scorekeeper applies to.
(scorekeeper (applies-to (not "independent")) (do last-side-wins) )
The above says to apply the scorekeeper to every side except those belonging
to the "independent"
side class. By default, only the independent
side belongs to that side class.
Alternatively, one can use side ID numbers.
(scorekeeper (applies-to (not 0)) (do last-side-wins) )
This again excludes the independent side, but, this time, by its exact ID number rather than its side class (to which other sides could belong).
We can also talk in terms of inclusion rather than exclusion.
(scorekeeper (applies-to ("chaotic" 0)) (do last-side-wins) )
This causes the scorekeeper to be applied to all sides belonging to the
"chaotic"
side class as well as the independent side.
And, we can include a side class, but exclude specific members of it.
(scorekeeper (applies-to (and "chaotic" (not (2 5 7)))) (do last-side-wins) )
Note that the and
is important. All boolean operators must be in
the higher levels of the expression. Once a list of side classes and/or
side ID numbers is encountered, then boolean evaluation is not performed
at the level of the list or below it. The following example is INCORRECT:
(scorekeeper (applies-to ("chaotic" (not (2 5 7)))) (do last-side-wins) )
As stated previously, the above example is INCORRECT. It is incorrect
because the not
appears inside a list of side classes and side ID
numbers. Do not attempt to use this example.
If this seems confusing, you may wish to consider a list of side classes
and side ID numbers as being shorthand for or
boolean expressions.
Thus:
(scorekeeper (applies-to (1 2 3)) (do last-side-wins) )
is equivalent to:
(scorekeeper (applies-to (or 1 (or 2 3))) (do last-side-wins) )
Now that we have pretty much beaten the applies-to
horse to death,
let us move on to when
conditions. By default, when
is
assigned nil
, and this is equivalent to after-turn
. So:
(scorekeeper (do last-side-wins))
is equivalent to:
(scorekeeper (when (after-turn)) (do last-side-wins) )
is equivalent to:
(scorekeeper (when (after-turn nil)) (do last-side-wins) )
With the turn-granularity scorekeepers, one can use a shorthand notation to specify that they should only run on or after a certain turn number.
(scorekeeper (when (after-turn 5)) (do last-side-wins) )
In the above example, the scorekeeper will only be checked at the end of turn 5 and at the ends of all turns thereafter.
There is more verbose way of writing this, and this involves a full expression:
(scorekeeper (when (after-turn (>= turn 5))) (do last-side-wins) )
The (>= turn 5)
is a test expression. turn
is a keyword
that gives back the current turn number.
Note that you can put together test expressions using the other available operators.
(scorekeeper (when (after-turn (<= turn 30))) (do last-side-wins) )
The above example only runs the scorekeeper at the end of the turn for the first 30 turns, and then the scorekeeper ceases to be relevant.
(scorekeeper (title "Every Other Turn") (when (after-turn (/= (/ turn 2) (/ (+ turn 1) 2)))) (applies-to (not "independent")) (do last-side-wins) )
Since we do not have a modulo arithmetic operator as of this writing
(2005/02/06), if we want to get a scorekeeper to run at some interval, we
must use a trick like the above. All above test does is check to see if
our current turn number divided by some interval has the same quotient as
the next turn number divided by the same interval. If yes, then the
scorekeeper should not run. If no, then the scorekeeper should run.
Simply replacing 2
with 3
in the above when condition, you
can make your scorekeeper run every third turn, and so and so forth.
Scorekeeper bodies can be simple, as already seen, or they can be quite sophisticated. Side scores can be manipulated and victory or loss can be forced based on some condition.
(scorekeeper (when (after-turn 20)) (do (if (>= (sum-uprop victory-point point-value) 15) win)) )
In the above example, the scorekeeper checks to see if the sum of the
point-value
property of victory-point
units is greater than
or equal to 15. If so, then it declares the current side the winner
(and, by extension, other sides as losers).
Suppose we wanted to modify the Default game. We could state that any side having more than a certain number of points worth of nukes and capital ships wins.
(scorekeeper (do (if (>= (sum-uprop (cv bb nuke) point-value) 50) win)) )
We could also do something rather bizarre such as:
(scorekeeper (do (if (>= (sum-uprop u* cp) 15000) end)) )
The above example ends the game in a draw when any one side builds too much.
Scorekeepers can set or add to the score.
(scorekeeper (do (set-score (* turn 10))) )
The above scorekeeper sets the current side's score to be the turn number
times 10
. Equivalently, one can write:
(scorekeeper (do (add-score 10)) )
In this example, the scorekeeper adds 10 to the current side's score every turn.
Scorekeeper bodies can be even more complex.
(scorekeeper (do ( (add-score 10) (if (> (* score turn) 1000) end) (if (= (sum-uprop capital-city point-value) 0) lose) )) )
So, putting everything together thus far, we might get something like:
; Last non-indep side standing wins. (scorekeeper 1 (title "Attrition Victory") (keep-score false) (applies-to (not "independent")) (do last-side-wins) ) ; First side to acquire 30 victory points wins. (scorekeeper 2 (title "Greedy Victory") (applies-to (not "independent")) (do ( (set-score (sum-prop victory-point point-value)) (if (>= score 30) win) )) ) ; First side to produce an overwhelming arsenal wins. (scorekeeper 3 (title "Inevitable Victory") (applies-to (not "independent")) (when (after-turn 50)) (do ( (set-score (/ (sum-uprop godlike-u* cp) 20)) (if (>= score 40) win) (if (>= (sum-prop (append horde-u* nuke) cp) 30000) win) )) ) ; Any side that loses nearly all of its commanding units loses. (scorekeeper 4 (title "Loss by Collapsed Command") (keep-score false) (do (if (< (sum-uprop leader-u* point-value) 50) lose)) )
GlobalVariable: scorefile-name
str
This variable supplies the name of the file to be used for recording
scores of people playing the game. The default value is ""
,
which disables the recording of scores.