Programmer's Quick Start Guide

This guide is designed to provide a crash course in RPS programming for programmers familiar with other programming languages. This guide is not well suited to those new to programming, or those hoping for thorough documentation of the language and it's interpreter.

Here are some basic details about the RPS language:

Now for some examples:

Example #1 - a classic "Hello, World!" program:

proc init() {
     say "Hello, World!";
}

The init function is basically equivalent to the main function of a Java or C program. init is called before any other function in a program. Every RPS program must have an init function.

Functions are defined with the following form:

return-typefunction-name ([argument-list]) {
     [statements]
}

When a function, such as init, doesn't return a value, the reserved word proc (an abbreviation for procedure), is used instead of the return type. proc in RPS is equivalent to void in other common languages.

Inside the init function is a single say statement. say statements are composed of the reserved word say followed by a quoted string and a semi-colon. A say statement provides the functionality that System.out.println provides in Java or that printf provides in C.

Example #2 - playing with scissors:

proc init() {}

choice choose() {
     return scissors;
}

First, notice that although the init function doesn't do anything, it's still required. Second, notice that the choose function returns a choice. The choose function is required in any program that will play rock, paper, scissors against an opponent. It's called whenever it's time for the program to "choose" what to play: rock, paper or scissors. This simple program will always play scissors.

Example #3 - playing with scissors (most of the time):

proc init() {}

choice choose() {
    if (random number <= 75%) {
          return scissors;
     } else {
          return paper;
     }
}

If/else statements such as the one in the example are probably familiar to you. The only difference in RPS is that the braces are always required, even when the if only contains a single statement. The other control structures RPS implements that you may be familiar with are for and while loops. These have the syntax you'd expect if you've worked with a language like C or Java.

This example introduces a new primitive data type: the number. Unlike other languages that have integers, long integers, single precision floating points, double precision floating points, and so on, RPS uses just the number data type for all numeric data. Here are a few examples of number literals: 1, 3.14, -100, 50%, 125%. As you might expect, 0.5 and 50% represent the same number.

Random values can be generated via the random reserved word. All you have to do is use an expression with the following form:

random primitive-type

For instance, the random number expression in the if statement's condition in the example will give a random number ranging between 0% and 100% inclusive. Since a random number between 0% and 100% will be less than 75% about 75% of the time, this program will play scissors 75% of the time and paper 25% of the time.

If we instead wanted to randomly return a choice without bias, we could simply use the following command in our choose function:

return random choice;

Example #4 - variables:

global choice c;

proc init() {
     choice r = random choice;
     c = r;
}

choice choose() {
     inc c;
     return c;
}

Programs may contain both local variables within functions as well as global variables at the beginning of the code, before any functions are defined.

A local variable is defined like this:

typevariable-name [ = expression];

And a global variable is defined like this:

global type variable-name [ = expression];

The init function defines a variable r and assigns it a random choice: rock, paper, or scissors. Next, it sets the global function c to be equal to r.

Whenever choose is called, c will first be incremented using an inc statement. For instance, incrementing a rock would give you paper, since paper beats rock. That will make the program play this pattern: ... rock, paper, scissors, rock, paper, scissors, rock, paper, ... Of course, which choice the program starts with is random.

The increment command also works on some of the other data types, such as on numbers. Doing an inc on a variable that was equal to 5 would result in a 6. To decrement a variable, use a dec command in the same way.

Example #5 - using a multi variable (aka a hash)

global multi pattern;
global number curr = 0;

proc init() {
     //fill pattern[1], pattern[2], ... pattern[5] with random choices

     for (number n = 1; n <= 5; inc n) {
          choice pattern[n] = random choice;
     }
}

choice choose() {
     //cycle curr through values between 1 and 5
     inc curr;
     if (curr > 5) {
          curr = 1;
     }

     return pattern[curr];
}

A variable of type multi can contain other variables that are accessed through subscripts. Suppose m is a multi, and you wanted to access the variable at subscript 5. You would use the variable name, m, followed by the subscript in brackets, like this: m[5]. If you wanted to add a variable to a multi, you'd do you declaration the way you typically would, but with the subscript. For example:

string m[5] = "five";

The init function of the example declares choice variables at subscripts 1 through 5 in the pattern variable. Each of the variables at the subscripts is initialized to a random value. Then, whenever the choose function is called, it cycles through the 5 subscripts in pattern, playing each one in turn. Thus, the program will make the same choices for rounds 1 through 5 as it will for rounds 6 through 10, rounds 51 through 55, and rounds 101 through 105, etc.

Subscripts in a multi need not be numbers; in fact, subscripts are converted to strings before they are used. Thus, m[1] is the same as m["1.0"].

Example #6 - reacting to an opponent:

proc init {}

choice choose {
     if (rounds = 0) {
          //no rounds have been played yet, so do something random
          return random choice;
     } else if (outcome[rounds] = win) {
          //we won last time, so lets make the same choice
          return me[rounds];
     } else {
          //we lost or tied - let's do what the opponent did last time
          return you[rounds];
     }
}

Up until this example, all the programs in this guide have made choices that didn't consider what an opponent was doing. To see what an opponent is doing, a program can access certain predefined variables. There is one number variable: rounds. rounds contains the number of rounds that have previously been played.

There are also 3 multi variables: you, me, and outcome. you contains all of the opponents previous choices. For instance, if you wanted the know the opponent did in the first round, you would use you[1]. If you wanted to know they did in the most recent round, you would use you[rounds]. The me variable works just like the you variable, except it holds the choices that the program made itself.

outcome holds the results of each each round. outcome[rounds] gives access to the result variable corresponding to the last played round. A result can be a win, tie, or lose.

Summary:

The important functions are:

The primitive data types are:

The predefined variables are:

Now get coding!