The Game Programming Language (GPL) v. 2.0
Tyson Henry
Revised 2/5/08 (revision history at end of document)


Overview

The Game Programming Language (GPL) is an object-based language for specifying simple computer games such as those found arcade games of the late 1970's and early 1980's.   It was created as a semester long project for an undergraduate compiler course.  My goal was to create a project that demonstrated the wide applicability of compiler construction tools.  The language is simple to learn and surprisingly expressive.  A typical computer science undergraduate can learn it in about an hour and be creating significant programs within several hours.

To keep the implementation simple enough to be completed in a single semester several simplifying design decisions were made.  For example, all variables are global and there are no functions.



Running the gpl interpreter

The gpl interpreter is a stand alone program.  It does not have to be installed like many windows programs.  The only way to run it is from the command line.

In linux: OpenGL and GLUT must be installed.  OpenGL comes with most flavors of linux.  The GLUT library is distributed with many flavors of linux but may not be installed automatically.  You run the gpl interpreter from the command line.

In OSX: OpenGL and GLUT come with OSX.  You run the gpl interpreter from the command line.

In windows:  the cygwin Unix emulator (or some other Unix emulator), OpenGL, and GLUT must be installed.  You run the gpl interpreter from the cygwin command prompt.

From the command line you run the gpl interpreter as follows

% gpl filename.gpl

Where filename.gpl is a text file that contains a gpl program.  If you don't specify a filename or the specified file cannot be opened, an error message is written to standard error and the program exits.

The command line argument -seed allows users to specify a seed for the random number generator.  If a seed is not given, the clock is used.  Specifying a seed makes it easier to debug programs that use random numbers.  When you specify a seed, every time you run the program with that seed the same random numbers will be generated.  The -seed # must be the first command line argument:

% gpl -seed 42 filename.gpl


The Parts of a gpl Program

gpl programs have two distinct parts that must be in the following order:

declarations

All declarations must be before the code blocks.  The types of declarations are:

variables
types are:  int, double, string, rectangle, triangle, circle, textbox, and pixmap

animation block forward declarations
animation blocks must be declared before they can be referenced
forward block declarations provide that mechanism

Variables and block forward declarations can be in any order as long as they are before all code blocks.

code blocks

initialization block (each gpl program can have zero or many)
An initialization block is a series of statements that is executed right before program control is passed to the main event loop (i.e. after input has been parsed and after the window has been created).

animation blocks (each gpl program can have zero or many)
Animation blocks are a series of statements.  They are like functions that are automatically executed periodically.
The statements in an animation block can change global variables and the properties of rectangles, triangles, circles, textboxes, and pixmaps.  For example, by changing the x,y of an object, it will move around the window.
The frequency that animation blocks are executed is determined by the reserved variable animation_speed (see next section).

event handler blocks (each gpl program can have zero or many)
Event handlers are like callback functions.
When a user event occurs (such as a left arrow being pressed) the statements in the associated event handler are executed.


Variable Declarations

All variables must be declared in the variable declaration section.  Variables cannot be declared in code blocks.  In other words, there are no "local" variables.  All variables are global.  This make the implementation of the symbol table simple.

Basic Variable Types

Type
Example w/o initialization
Example with initialization
int
int a;
int a = 42;
double
double x;
double x = 1.42;
string
string s;
string s = "hello world";

Note: strings are delimited with double quotes, but the double quotes are not part of the string.  Strings can contain any printable character (e.g. no line feed) except double quotes.

Reserved Variables

If any of the following variables are declared, their values will be used to set up the window and the animation speed.  Note: If these variables are declared with a different type then listed below, an error will be issued.  The value of these variables is only used at game start up time.  Changes to these variables during run time will not affect the window.

If a reserved variable is not declared, the default value is used to initialize the window.

Type
Name
Description
Default Value
int
window_x
X Placement of the game window on the desktop (top left is x=0)
200
int
window_y
Y Placement of the game window on the desktop (top left is y = 0)
200
int
window_width
Width of the game window
500
int
window_height
Height of the game window
500
string
window_title
Title of the game window (appears in window's title bar)
gpl window
double
window_red
The red component of the window's background color (0.0 is no red, 1.0 is full red)* 1.0
double
window_green
The green component of the window's background color. 1.0
double
window_blue
The blue component of the window's background color. 1.0
int
animation_speed
Speed of the animation (1 is slowest, 100 is fastest).  Values are restricted to the range 1-100.  Speed is not linear (e.g. 100 is many times faster than 50).  Speed is very machine dependent.
88
*See Colors section below for more about colors.

Game Objects:
game objects are the graphical components of games

Type
Example w/o parameters
Example with parameters
circle
circle c();
circle c(x = 10, y = 20, radius = 50, animation = ball_animation);
rectangle
rectangle r();
rectangle r(x = 10, y = 10, h = 10, w = 10);
triangle
triangle t();
triangle t(x = 100, y = 100, size = 50, animation = ship_animation);
textbox
textbox title();
textbox title(x = 10, y = 10, text = "hello world", size = 0.1);
pixmap
pixmap photo();
pixmap photo(x = 10, y = 10, filename = "mountains.bmp");


Game Object Attributes:  All game objects (circle, rectangle, triangle, textbox, pixmap) have the following attributes

Name
Description
Type
Default
x
x location of object in window (in pixels)
integer
0
y
y location of object in window (in pixels)
integer
0
w
width of the object (in pixels)  integer
10
h
height of the object (in pixels)
integer
10
animation_block
name of the animation block associated with this game object
code block
none
visible if visible == 1 object is drawn on screen, otherwise it is not drawn (use 0 to indicate not-visible) integer 1
red
the red component of the object's color (0.0 is no red, 1.0 is full red)*
double
0.5
green
the green component of the object's color (0.0 is no green, 1.0 is full green)
double
0.5
blue
the blue component of the object's color (0.0 is no blue, 1.0 is full blue)
double
0.5
proximity
the distance used by near operator to determine if object is "near" to this one
int
4
drawing_order controls the oder the object is drawn, objects with high drawing_order values are drawn on top of objects with a lower drawing_order value int 0
user_int** an integer variable that can be used by the game program (very helpful when using arrays of objects) int 0
user_double** a double variable that can be used by the game program double 0.0
user_string** a string variable that can be used by the game program string ""
*See Colors section below for more about colors.
** There are 5 user variables for each type, e.g. user_int, user_int2, user_int3, user_int4, user_int5.


Circles have the additional integer attribute radius:

Name
Description
Type
Default
radius
the radius in pixels of the circle (overrides the h & w)
integer
10


Triangles have the additional attributes of size, skew, and rotation:

Name
Description
Type
Default
size
size of the length of the base of the triangle
integer
10
skew
the ratio: height/width.  If equal 1.0, triangle is equilateral, if > 1.0, triangle is taller, if <1.0 shorter
double
1.0
rotation
degrees rotated counter-clockwise around middle of triangle
double
0.0


Rectangles have one additional attribute for rotation:

Name
Description
Type
Default
rotation
degrees rotated counter-clockwise around middle of rectangle
double
0.0


Textboxes have three additional attributes: text, size, and space:


Name
Description
Type
Default
text
text to be displayed by the text box
string
empty string
size
size of the text (1.0 is about 100 pixels high, 0.1 is about 10 pixels high)
double
0.1
space
number of pixels between letters when size == 1.0
int
10


Pixmaps have one additional attribute for the filename of the pixmap (in .bmp format)

Name
Description
Type
Default
filename
filename of the file containing the pixmap (in .bmp format)
string
""

Pixmaps can only handle bitmaps with 24 bit color and 1 plane.

Attributes are named during game object initialization (e.g. text = "hello") and can be specified in any order.  Attributes can also be changed in any code block (initialization block, animation blocks, event handler blocks).

Variables and attributes can be initialized using variables and expressions.  For example:

int x_position = 40;
int y_position = x_position * 4 + 12;

circle c1(x = x_position);
circle c2(x = x_position + 40);

textbox(text = 42);
textbox(text = "hello world");
textbox(text = 4 + 12 * x_position);

Attributes can be accessed using the C/C++ "." notation for members of a class.  They can appear on both sides of assignment operators, and in expressions.

c2.x = 42;

i = c2.x

if (c2.x == 42)
{

}


Drawing Order:

By default, game objects are drawn in the order they are created.  For example, consider the following code:

rectangle r1(x = 100, y = 100, w = 100, h = 100);
rectangle r2(x = 100, y = 100, w = 200, h = 200);

Since r1 was declared before r2, every time the objects are drawn, r1 will be drawn before r2.  This means that r2 will be on top of r1.

It is possible to change the drawing order of objects.  All game objects have a drawing_order field.  This is an integer that controls where the game object is placed in the drawing vector.  Game objects with high drawing_order values will be drawn on top of objects with lower drawing_order values.  


Limitations:


The collision detection (the near and touches) assumes that objects are not rotated.  The bounding box of the original rectangle or triangle is used for collision detection.  If the objects are rotated, collisions won't be detected accurately.

At some point I will fix this, but don't hold your breath.


Arrays:

Arrays of all types can be created:

int num = 10;
int numbers[42];
circle dots[num];
rectangle blocks[num * 2];

The size of the array can be any legal expression that evaluates to an integer.

You cannot initialized arrays during initialization (this makes the interpreter much more complicated).

Use the initialization block to initialize arrays.


Animation Block Forward Declarations

Animation blocks (see below) must be declared before they can be referenced in a game object declaration.  For example:

    circle earth(x = 100, y = 100, radius = 400, animation_block = earth_animation);

earth_animation is the name of the animation block associated with the circle object earth.

Since all animation blocks must come after all declarations, a forward statement is needed to tell the interpreter that a given animation block is defined below.  The format is as follows:

forward <block_name>(<parameter_type> <parameter_name>);

forward animation earth_animation(circle cur_rectangle);

All animation blocks that are used during variable initialization must have a forward declaration.

Each animation block has a single game object parameter (e.g. circle, triangle, etc.) that acts like a pass-by-reference function argument in C/C++.  The type of this parameter must match the type of the object using the animation block (see next section).

It is an error to have an animation block forward without providing the animation block.




Initialization Blocks

A gpl program can have zero or more initialization blocks:

initialization
{
stmt;
stmt;
... 
}

The statements in an initialization block are executed right before program control is passed to the main event loop (i.e. after input has been parsed and after the window has been created).

If there are more than one initialization block, then they are executed in the order they appear in the .gpl file.

Initialization blocks are most often used to initialize arrays of variables.


Animation Blocks

Animation blocks are named blocks of statements.  They are executed at regular intervals for all objects for which they are associated.

For example, given the following declarations:

circle c1(animation = my_animation);
circle c2(animation = my_animation);
circle c3(animation = my_animation);


The animation block my_animation will be executed at regular intervals for c1, c2, and c3 (when an animation block is executed for a game object, that object is passed by reference to the animation block (see below)).

Animation blocks have the form:

animation my_animation(circle cur_circle)
{
statement  // statements are explained below
statement
...
}

where my_animation is the name of the block (it can be any legal identifier).

Animation blocks all have a single typed parameter which is specified in the block's forward statement and in the animation block header.  The parameter can be any game object type (rectangle, circle, triangle, etc).  The name of the parameter can be any legal identifier.  The type of the parameter must match the type of the object using this animation block.

For example:

forward animation my_animation(circle cur_circle);

In the above example, the parameter is of type circle, and the parameter name is cur_circle.

When an animation block is specified for a game object:

    circle earth(animation = earth_animation);

The type of the object (circle in this example) must match the type of the animation block's parameter specified in the forward statement.

The parameter behaves like a local variable inside of the animation block.

For all game objects that have an animation block, the animation block will be executed at regular intervals if the object's visible field is true.  The frequency of execution depends on the reserved variable animation_speed.


Event Handler Blocks (also called Event Handlers)

Event handlers are very similar to event callback functions in VB, C++, X-windows, etc.

The basic idea is that you specify an event (something the user does, like press a key) and associate a block of statements with it.  Event handlers have the following form:

on leftarrow
{
statement  // statements are explained below
statement
...
}

The on is a keyword.  All event handlers start with on.  The leftarrow is also a keyword that describes a particular event, in this case, the pressing of the left arrow key.

When the user causes an event (pressing the left arrow key in the above example) the statements in the event block associated with that event are executed.

The following events are supported by gpl Event Handlers:

space
user presses the space bar
leftarrow
user presses the left arrow key
rightarrow
user presses the right arrow key
uparrow
user presses the up arrow key
downarrow
user presses the down arrow key
f1
user pressed the f1 key
akey
user presses the 'A' or 'a' key
skey, dkey, fkey, hkey,jkey,kkey, lkey,wkey
work just like akey

There can be any number of event handlers for each event.  They are executed in the order they are declared.


Statements

gpl supports five statements:
  1. if statement
  2. for statement
  3. print statement
  4. exit statement
  5. assignment statement
A statement is a single statement. 
A statement_block is a series of statements enclosed in { }.
A statement_or_statement_block is either a single statement or a statement_block.

if statements have the following two forms:

if (expression) statement_or_statement_block;

if (expression) statement_or_statement_block else statement_or_statement_block;

the expression must be of type int

for statements have the following form.  These are not as general as C/C++ for statements:

for (assignment_statement; expression; assignment_statement) statement_block;

the expression must be of type int

print statements have the form:

print (expression);

Expression is evaluated to a string (which means it can be an int, double, or string)

Example:  print("value = " + 42 + " x = " + x);

exit statements have the form:

exit (expression);

expression is evaluated to calculate the value passed to the system function exit(exit_status)
expression must be an int expression

assignment statement have the form:

variable assignment_operator expression;

where assignment_operator can be:  =   +=   -=



Expressions

Expression types:

gpl expressions are strongly typed (int,double,string).  However there are some implicit casts built into gpl.

If an arithmetic expression (e.g. "i + x") contains both integers and doubles, the type of the expression will be double: integers are automatically cast to doubles (this is called an implicit cast). 

Integers and doubles are also automatically cast to strings.  If either an integer or a double is in an expression with a string, it is converted to a string that represents its value.  Consider the following code:

int i = 21;
string s1 = "I am ";
string s2 = s1 + i;

The value of s2 will be the string "I am 21" 

However, strings are not cast to integers or doubles, and doubles are not cast to integers.

There is a special form of expressions of type animation_block.  It can only consist of a variable--it can't contain any operators.  It is used when initializing or changing the member variable animation_block:

rectangle my_rectangle(animation_block = move);  
my_rectangle.animation_block = bounce;

move and bounce are expressions of type animation_block.  They each contain a single variable.


Arithmetic operators are similar to C/C++ expressions.  They can contain "(" and ")" just like C/C++ expressions.


Operator
Description
Legal input types
Result type
*
multiplication
int, double
int or double
/
division
int, double
int or double
+
addition, string concatenation
int, double, string
int, double, or string
-
minus
int, double
int or double
%
modulus
int
int
-
unary minus
int, double
int or double

Logical operators

Operator
Description
Legal Input Types
Result Type
<
less than
int, double, string
int
>
greater than
int, double, string int
<=
less than or equal
int, double, string int
>=
greater than or equal
int, double, string int
==
equal
int, double, string int
!=
not equal
int, double, string int
!
not (unary operator)
int, double int
&& logical and int, double int
|| logical or int, double int


Math Operators
:  There are several unary math operators.  While these are implemented as unary operators, they look like function calls.  The format is:

x = sin(y);
x = 2 * sin(y) + 3 * cos(z);

i = random(10);


Operator
Description
Legal Input Types
Result Type
cos
cosine
int, double
double
sin
sine
int, double double
tan
tangent
int, double double
acos
inverse cosine (arccosine)
int, double double
asin
inverse sine (arcsine)
int, double double
atan
inverse tangent (arctangent)
int, double double
sqrt
square root
int, double double
abs
absolute value
int, double int,double
floor
floor of given value
int, double
int
random
random integer between 0 - (N-1)
(where N is the given number)
int, double (double is rounded down)
int

The trig functions (cos, sin, tan) assume the input is specified in degrees.

Geometric expression allow you to compare the proximity of two game objects (rectangle, triangle, circle, etc).  The operators are near and touches.   They have the following form:

if (c1 touches c2)
{
    ...
}

If the game object c1 is touching or overlapping game object c2, this expression returns true.  It returns false otherwise.  Specifically, if the bounding box of c1 overlaps the bounding box of c2, c1 is "touching" c2.

if (c1 near c2)
{
    ...
}

If the game object c1 is near c2, this expression returns true.  It returns false otherwise.  Specifically, the bounding box of c1 is increased in all directions by c1.proximity and the bounding box of c2 is increased in all directions by c2.proximity.  If the expanded boxes overlap then c1 is "near" c2.



Identifiers

Identifiers are the same as in C/C++.  Must start with a-z, A-Z, _, but may also contain 0-9 after the first character.


Comments

Comments can appear anywhere in the program.  They are "//" to end of line.  For example:

// this is a comment
int i; // this is another comment


Constants

There are four types of constants:

Type
Example
int
42
double
3.145
logical
true, false   (note: true and false are reserved words), they evaluated to the integers 1 (true) & 0 (false)**
string
"hello world"

** Just like C/C++, 0 is false and all other value is true


Quitting the Program

The 'q' key exits a running gpl program (the window running the game must be the currently selected window).


Colors

The values range for the red, green, and blue attributes are from 0.0 (none) to 1.0 (full).  For example, (1.0, 0.0, 0.0) means that the color has full red, no green, no blue.  This is the color red.

You can create colors by experimentation (guessing values for the RGB) or you can look them up somewhere.  If you search the web for "RGB values" you will find countless charts giving the RGB values of colors.

An alternative way to specify RGB values is to use 0-255 instead of 0.0 - 1.0.  You can convert one of those numbers by dividing each element by 255:

    Lemon:  red = 255, green = 231, blue = 109

In gpl the values would be:

    Lemon: red = 255/255.0,  green = 231/255.0, blue = 109/255.0

Make sure you use the ".0" or the expressions will be evaluated as integer expressions and will always have the value 0 or 1.

Examples:

    circle earth(x = 100, y = 100, radius = 100, red = 0.0, blue = 1.0, green = .1);
    circle sun(x = 1, y = 1, radius = 300 , red = 255/255.0, blue = 231/255.0, green = 109/255.0);


Revision History

2/8/06: added && and || to the table of logical operators, added documentation for the new user variables fields (e.g. user_int2, user_int3, ...)
1/18/07: added exit statement, changed print to take an expression, changed initialization block to indicate there can now be multiple blocks and that initialization blocks are now executed after window is created, added drawing_order member variable and description of how it works, added some description of implicit cast
4/4/07: added clarification for true and false constants
2/5/08: added section on initialization blocks, added bits/plane restrictions for pixmaps, added mention that trig functions expect the input to be degrees
2/7/08: added wkey


Copyright (c) Tyson R. Henry 2006, 2007, 2008.
This document may be freely copied and distributed for not-for-profit educational purpose only.