Skip to content

GGDL syntax

Kevin Damm edited this page Nov 15, 2023 · 7 revisions

GGDL is based on GDL which in turn is based on Datalog, a logic programming language.

There are two variants of GDL in the wild, the prefix-formatted one that is constructed from s-expressions, and the infix-formatted one that looks more like a subset of KIF with prolog/datalog inference operator :- for defining relations that imply. The prefix-formatted version is found in rulesheets defined for Stanford's collection of games, the infix notation is more human-readable and what is typically found in academic journals and other offshoots that derived from GDL.

While making GGDL a superset of GDL, I also noticed that the player responses and server updates are a further restricted subset of GDL. Indeed, the possible strings (after normalization) that a player could legally submit as an action are countably finite, and typically a trivially small quantity. It occurred to me, since you would not want to allow the full strength of the interpreter system, that you would either need a sufficiently constrained parser or an interpreter with enough context to know not to evaluate certain otherwise well-formed sentences, nor include them in any unification passes. Indeed, with enough knowledge of the game definition, a compiler could produce a parser that would only accept legal moves from the player, and that it would be much smaller than even a ground-statement parser since all symbolic and relational names would be known beforehand, and that there would be no need to warily evaluate its contents after a successful parse.

So, when defining the syntax, it is important to call out which parts are core GDL, which parts are extensions of that language, and which parts influence the construction of a custom move-accepting parser. And, because GDL is powerful enough to define a broad set of game rules as it is, the language used when conveying the game rules can be interpreted with only GDL syntax and a small runtime library, provided here as a typescript imlplementation.

...

Keywords

init

Provides a starting point for the game state and board state, asserting certain facts and relations to be true.

init GroundObjectIsTrue

An initial atomic value is the simplest init statement. These facts evaluate to true and persist until they are negated. An init statement can set up an object or functional relation as well:

init Cell(?i:index, ?j:index) := blank

Here, index is a type that has elsewhere been defined as between 1 and BoardSize. Internally, a Cell\2 instance of size ${BoardSize}^2$ is created. Its inferred type is the only type blank is a member of (i.e., marking), defined as a base relation it has only three values (itself and the two roles). An interpreter could define a state representation that can be compactly defined in range $3^{ {BoardSize}^2 }$ within a well-defined ordering. This ordering depends primarily on the semantic structure of the game -- it depends on the syntactic ordering only in the case of custom type definitions such as index and marking. The semantics are equivalent to the set of GDL relations [(cell 1 1 blank) (cell 1 2 blank) ... (cell 3 3 blank)].

Multiple init statements can be combined in wrapping parentheses:

init (
  Cell(?i:index, ?j:index) := blank
  BoardControl := x
)

Init statements can be combined with the ^ operator to set up parameters at the game's scope, defining its type and default values. The associated comment can be extracted for documenting this parameter in clients as well.

// Size, both width and height, of the board.
init ^ BoardSize :: int >= 3 <= 20 default 3

Clone this wiki locally