Captive Game Server

Updated 2023-07-14 for GS ver 6.010

Test the Rule Game

To play a game without installing the CGS, CLICK HERE!

What is the Captive Game Server?

The Captive Game Server (CGS) is a small Java application that contains the same game engine as the Web Game Server, but does not have a SQL database, does not write transcript files, and does not communicate with clients using HTTP. It can be used by a Machine Learning researcher (in particular, a participant in the The Machine Learning Challenge (MLC)) who wants a ML program to learn how to play the Game of Hidden Rules (GOHR, a.k.a. the Rule Game) as a counterparty for his ML program to play with.

The Captive Game Server can be run in two ways: pipe-based and socket-based.

The pipe-based CGS is to be started, as a child process, by the client program (such as a machine learning (ML) program that wants to play a game), and communicates with the client using standard input and standard output (i.e. UNIX pipes). For testing purposes, it can also be run directly from the console by a person who wants to play the game himself, as a human player.

Since the Captive Game Server is run by the owner of the ML program, the latter has complete control how many times it's run, and with what data. It is a complete "honor system", so to speak. Thus, it is up to the owner of the ML program to keep track of how the CGS is used, how many game episodes were played, and how many moves were attempted in each episode.

For the convenience of some application, we also have the socket-based version of the CGS, discussed later in this document, which communicates with clients over sockets, rather than a pipe.

The Rule Game / Game of Hidden Rules

More information on the Rule Game (a.k.a. the Game of Hidden Rules, GOHR), is provided in the following documents:

The Machine Learning Challenge (MLC)

For further information on the Machine Learning Challenge (MLC), see:

Installation

If you're one of our own team members, and use the CGS on a server where it's already installed, you can safely ignore this section. But if you are installing the CGS on your own machine (especially at a different institution), please follow the instructions below to obtain the Captive Game Server distribution and the necessary third-party files.

Main prerequisites

We are normally using the CGS on Linux machines, but if you want, you probably can install it on a MacOS or MS Windows machine as well, likely with some minimal changes to the shell scripts used to run it.

You need to have a Java Runtime Environment (JRE) (i.e. the system for running pre-compiled Java code) installed on your machine. Type which java on your console to see if you have one already; if you don't, you can get one from Oracle (How do I install Java?) or from OpenJDK (How to download and install prebuilt OpenJDK packages).

Our shell scripts are written in csh, so your system should have csh installed. (Most Linux / UNIX system already have, and if yours don't, you should be able to easily install it). Alternatively, you can re-write the scripts for your favorite shell (sh, bash, ksh, whatever).

Choosing a directory

You can install the Captive Game Server anywhere on you computer. For the rest of the discussion, let's assume that you have decided to install it under ~/captive

Third-part JAR files

You need to obtain a set of third-party JAR files that are used for working with JSON-format data (a "JAX-RS RI bundle"). This is how:

Download and install CGS

Download a recent version of CGS from our server's Download Area. Choose a file with a name such as captive-5.001-2022-05-26.zip. Unzip it under ~/captive:

	cd ~/captive
	unzip ~/captive-5.001-2022-05-26.zip
      
This will create the directory ~/captive/game, with all the CGS stuff in it.

Sample data

To play with the CGS, you need to tell it what game to play. This is done by means of giving it some experiment control files: at the very least, a rule set file, and possibly some other files as well.

If you're a UWM team member working on sapir, you have access to the full set of our experiment control files in /opt/tomcat/game-data (or another directory, as per the variable FILES_GAME_DATA in the master config file).

If you are running the CGS on your own machine, but you have also installed the web-based Game Server on that machine, you have the experiment control files in the same or similar location as well (its location may be overridden by the paramter FILES_GAME_DATA in the master config file.

Otherwise, you can use a smaller set of sample experiment control files that came with the CGS distribution; they are found in ~/captive/game/game-data.

Starting the Captive Game Server

The following assumes that you have installed the CGS as described above, in ~/captive. If it was installed elsewhere, adjust the commands as appropriate.

The CGS can play one game at a time. An easy way to choose a game is to provide the CGS with a game rule file (a few of which can be found in ~/captive/game/game-data/rules/ or ~/captive/game/game-data/rules/, as the case may be) and the initial number of pieces.

To use CGS, you need to add the directory in which your Java interpreter (java) is installed (such as /opt/jdk-14.0.2/bin, on sapir) into your PATH. Alternatively, you can modify the shell scripts mentioned below, so that they use /opt/jdk-14.0.2/bin/java rather than just java.

You can run the CGS from the command line as follows, with a csh script:

~/captive/game/captive-full.sh ~/captive/game/game-data/rules/rules-02.txt 8
      
In this example, you will start a session using the rule description from the specified file; each episode will use a random initial board with 8 pieces. During the session, you can play one or several episodes.

During the session, you will be able to type commands on the console, and the program will send the output to your screen. Type HELP to see the list of command, or EXIT to exit.

To use the CGS with your ML program, use the standard mechanism for spawning a child process, starting the script (captive-full.sh, captive-standard.sh, or captive-brief.sh), or the underlying Java program, as a child process from your program, with appropriate command-line arguments. You can capture the CGS's standard input and standard output via pipes, and communicate with it via writing into one pipe and reading from the other.

Command-line option format

A number of options (name-value pairs) will be discussed below. For all of them, there are two alternative ways of passing them to the CGS, e.g.

    java -Dlog=foo.csv edu.wisc.game.engine.Captive ....
  
or
    java edu.wisc.game.engine.Captive log=foo.csv ....
The latter format is more convenient if you are passing the options through a shell script, e.g.
    ./captive-full.sh log=foo.csv ....
Command-line parameters: specifying the rule set and the initial board

The command line arguments for the scripts such as captive-full.sh are the same as for the underlying Java program, edu.wisc.game.engine.Captive. These arguments are used to specify, directly or indirectly, two things:

There are several ways to provide this information, as illustraed below. In examples 1 thru 6, the first argument is the rule set file, specified as either a relative path (relative to the current directory), or an absolute path; it is followed by arguments specifying the initial board(s) for the session's episodes, in one of several ways. In example 7, the first argument is a trial list file, which contains information that both specifies the rule set and the initial board generation process.

In Game Server 2.*, the designers of human-subject experiments have a wide choice of experiment plan structures. Each parameter set in their trial lists may define a 6-paramter random board generator (specifying ranges of the number of pieces, the number of shapes, and the number of colors), with legacy or custom colors and shapes. Alternatively, a parameter set may define a sequence of predefined boards. Whichever structure the the parameter set has, using one of the methods described above will ensure that the captive server uses exactly the same random board generation process as is used by web-based Game Server in the human-subject experiments.

1. Specify a pre-defined board file identified by name

cd ~/captive/game	  
./captive-full.sh game-data/rules/farthest.txt game-data/boards/four-corners.json 
	
If you do that, every episode in your session will start with the same identical initial board.

2. Specify only the number of pieces, as a single number

./captive-full.sh game-data/rules/farthest.txt 3
	
In this example, every episode will start with a random board with the same number (3) pieces. The shapes and colors of each piece will be selected independently from each other from the entire set of shapes and colors available, which means that the number of shapes and colors will vary in each episode over the entire possible range.

3. Specify only the number of pieces, as a single number or a range

./captive-full.sh game-data/rules/farthest.txt 2:4
      
In the above example, for each episode the number of pieces will be selected randomly, with a uniform distribution, from the speciified range, meaning that, on average, one-third of all initial boards will have 2 pieces, one-third will have 3, and one-third will have 4.

4. Specify the number of pieces, the number of shapes, and the number of colors, as single numbers

 ./captive-full.sh game-data/rules/farthest.txt 5 2 3
	
In the above example, every initial board will have 5 pieces, with exactly 2 distinct shapes and 3 distinct colors

5. Same as the above, but with using ranges for any of the three quantities.

Ranges and single numbers can be combined in arbitrary ways; essentially, a single number n is equivalent to the range n:n. E.g.

./captive-full.sh game-data/rules/farthest.txt 5 1:3 3:4
	
In the above example, every initial board will have 5 pieces, with 1, 2, or 3 distinct shapes (with equal probability) and 3 or 4 distinct colors.

When using ranges, make sure that the upper bound of range of the number of shapes the upper bound of the range of the number of colors do not exceed the lower bound of the range of the number of pieces. Otherwise, the random board generator may be occasionally faced with an impossible task of creating a board which has more distinct colors or shapes than it has pieces!

When you specify the random boad generation model in this way, the random board generator works in exactly the same way when you provide the parameters for the same Six-parameter distribution in a trial list file for human player experiments.

6. Same as the above, but with custom shapes and/or custom colors.

To specify the sets from which colors and/or shapes will be drawn by the initial board generator, you can add additional parameters colors=... and/or shapes=... to the command line. The values of the parameters are semicolon-separated lists of colors and/or shapes, respectively. Make sure to use single quotes as shown below, since semicolons would be interpreted by the UNIX shell as command separators otherwise.

./captive-full.sh /opt/tomcat/game-data/rules/arrows/rule-01.txt 3 2 2 'colors=red;pink' 'shapes=arrows/arrow-up-left;arrows/arrow-up-right;arrows/arrow-down-right;arrows/arrow-down-left'
	   

One is allowed to use * to mean "use all shapes for which SVG files exist in the appropriate subdirectory of the main shapes directory". Thus, 'shapes=arrows/*;weather/*' is equivalent to listing every shape from /opt/tomcat/game-data/shapes/arrows and /opt/tomcat/game-data/shapes/weather

If you're using custom shapes and/or custom colors, it is necessary that a list of colors and a set of shapes SVG files are found at the appropriate locations under /opt/tomcat/game-data, as explained in the document Using custom shapes and colors in Rule Game Server 2.*. If you're running your Captive Game Server on sapir (the server used for the human subjects), and are playing a game that human subjects are already playing, then you're all set, because the human-player experiment team has already set up the necessary files in /opt/tomcat/game-data. If you're running your Captive Game Server on another host, you may choose to create a copy of sapir's /opt/tomcat/game-data directory with control files on your host, or you can use ~/captive/game/game-data as you control file directory.

If the root of your server data directory is somewhere else than in /opt/tomcat/game-data directory, you can specify this by an additional parameter on the command line. This can be specified with e.g. -inputDir ~/captive/game/game-data as a parameter to a script such as captive-full.sh, or with -DinputDir=~/captive/game/game-data as a parameter to java in your own shell script. (Alternatively, the same effect can be achieved by setting the value of the variable FILES_GAME_DATA in the master config file).

7. Use a trial list file

This is the easiest way to emulate the behavior of the web-based Game Server (with which humans play). To do this, you can specify a trial list file and the (1-based) row number, e.g.

./captive-full.sh /opt/tomcat/game-data/trial-lists/vmColorTest/trial_1.csv 1
The CGS figures the file type based on the extension: the ".csv" extension means you are providing a trial list file, while the ".txt" extension (as in items 1 thru 6, above) refers to a rule set file.

Any of the valid trial list files prepared by our human-subject experiment team can be used with the CGS. To decide which games you want to play, you may ask Gary or Aria about various trial list files that can be found in the trial list directory, /opt/tomcat/game-data/trial-lists on sapir.

The CGS will read the trial list file and extract the parameter set with the specified number. It will then create a game generator with the same parameters that the web-based Game Server would use when running on that parameter set; depending on the parameter set, that may involve random initial boards (with appropriate shapes, colors, and number of pieces) or predefined initial boards. As you play more episodes (starting each episode, after the first one, with the NEW command), the CGS will generate a sequence of initial boards in the same way as a web-based Game Server would, i.e. eiher randomly or by following a prescribed sequence of predefined boards. As in the web-based Game Server, the rule set specified in that parameter set will be used in each episode.

Since the trial list file implicitly refers to files in various directories under the server data directory (by default, /opt/tomcat/game-data), you must either have that directory, with all relevant files, on your computer, or use the -inputDir parameter on the command line, just as in Example 6 above.

8. Combine a rule set file and a modifer file

Since Captive Game Server 5.003, it is possible to combine a rule set file with a "modifier file", which is like a trial list file, but only has columns for the parameters you need for the initial board generation. One can call it a pro forma trial list file. (If you are also configuring games for human players on the web-based Game Server, you will realize that this is very similar to the technique knowns as the R:-type dynamic experiment plan in that space).

The syntax is, you put

    R:ruleSet:modifier
  
as the main argument on your command line. Both ruleSet and modifier can be specified as absolute file paths (i.e. starting with a / or with a ~). Alternatively, you can use the inputDir parameter to specify the root input data directory; if you do that, the ruleSet and modifier can be specified as relative paths, relative to the rules and modifiers subdirectories of the input data directory.

Here's an example, which uses a sample rule set file (game-data/rules/MLC/BMK/colOrd_nearby.txt) and a sample modifier file that we distribute for the The Machine Learning Challenge (MLC) participants:

  cd ~/w2020/game     
  ./captive-full.sh inputDir=game-data R:MLC/BMK/colOrd_nearby.txt:MLC/BMK/bmk.csv

9. Train/test: prohibiting or enforcing certain board properties when using the 6-parameter distribution

Based on a discussion with Paul on March 8, 2023, a new feature has been introduced in the CGS starting from version 6.010. It is now possible to run the CGS, with the usual 6-parameter random board generator whose behavior is modified in the following way:

Note that the sets of the allowed training-mode boards and of the allowed test-mode boards are complementary; in other words, each board that may be generated under the current parameter settings is either a legal training board, or a legal testing board, but it may never be both.

Specifying the conditions. The conditions are described by the so-called "condition file", whose format (although not semantics) are similar to that of rule set files. (For detailed guide on the rule file syntax, see rule file syntax in GS 4.*, and how it is extended in GS 5.*). For example, the set of three conditions desribed in the example above can be specified by the condition file that looks as follows:

    (pos:[31,36])
    (color:red)
    (shape:square)
  
Note that you must put one condition per line, but a condition may consist of multiple atoms. Thus the above file, with each condition made out of a single atom, prohibits (in the training mode) all red pieces and all squares. If, on the other hand, you have a condition line with multiple atoms, e.g. in the following file with two conditions:
    (pos:[31,36]) (shape:circle)
    (color:red) (shape:[square,triangle])
  
then you are prohibiting (in the training mode) circles (but no objects of other shapes) in cells no. 31 and 36, and you are prohibiting red squares and red triangles. In the testing mode, all boards will have either at least one circle in cell no. 31 or 36, or at least one red square or red triangle.

Sample condition files: game-data/cond/vm/cond-01.txt, game-data/cond/vm/cond-02.txt. (They are in the main code-and-sample-data repository, deployed under ~vmenkov/w2020/game on sapir, not in the main game data repository).

Specifying the training or test mode. To run the CGS starting in the training mode, you need to include condTrain=conditionFileName.txt on the command line, for example

      ./captive-full.sh condTrain=game-data/cond/vm/cond-01.txt game-data/rules/farthest.txt 5 1:3 3:4
    
All boards then will have no game pieces satisfying any of the listed conditions.

To run the CGS starting in the testing mode, you need to include condTest=conditionFileName.txt on the command line, for example

      ./captive-full.sh condTest=game-data/cond/vm/cond-02.txt game-data/rules/farthest.txt 5 1:3 3:4
    
All boards then will have at least one game piece satisfying at least one of the listed conditions.

If the CGS has been started in the training mode, the ML program can issue the command COND test to switch to the testing mode. It is most convenient to issue this command right before the NEW command of your first testing episode. Once the mode has been switched, the CGS will change its behavior accordingly, i.e. on all subseqent NEW commands it will generate boards with some game pieces satisfying some of the listed conditions, rather than boards in which no piece satisfies any condition.

Note: If you specify conditions that are impossible to fulfill (e.g. prohibiting red and black pieces while requiring that each board contains pieces of at least 3 colors), the CGS won't detect this problem automatically. Instead, it will sit in a tight loop for a while, and then will terminate, with an error message going to its stderr. Thus before letting your ML program to play with the CGS using a particularly involved set of conditions, you may do well by testing the board generation parameters and the condition file, by playing at least one training episode and at least one testing episode from the command ine. (You don't even need to play the episode to the end; just make sure that a board is produced for you!)

Specifying the random generator seed

Starting in ver. 1.026, it is possible to specify the seed of the random number generator, so that the same random boards would be generated on repeated runs. To do that, insert -Dseed=N (where N is a positive integer) into the Java command found in a script such as captive-full.sh, or used during the child-process spawning in your ML application. For example, you can modify the last line of this script to look as follows:

      
  java -Dseed=1 -Doutput=FULL edu.wisc.game.engine.Captive  $argv[1-]

It is also possible to specify the seed on the command line as follows:

      
 ./captive-full.sh /opt/tomcat/game-data/rules/MLC/vm/test-05.txt 3 seed=5

Passing the seed value 0 will start the random number generator with a random seed (i.e. it will produce a different sequence of random numbers on different runs).

Generating a results file

For the convenience of MLC participants, you can get the Captive Game Server produce a results file, each line of which will record the outcome of one episode. (This is the file the MLC participants are asked to submit so that their results can be compared to those of other participants' algortithms). Sample usage:

  
  ./captive-full.sh inputDir=game-data log=sample.csv log.nickname=JohnDoe log.run=0  R:MLC/BMK/colOrd_nearby.txt:MLC/BMK/bmk.csv

Here,

Compact format

Several results file formats are supported. As of ver. 6.002 (Jan 2023), the default format (which the MLC participants are advised to use) is the so-called "compact format", which looks like this:

#.nickname,rule_name,trial_id
#number_of_moves,number_of_errors,if_clear
.RandomTest,alternateShape2Bucket_color2Bucket,0
47,38,1
36,27,1
29,20,1
35,26,1
45,36,1
.RandomTest,alternateShape2Bucket_color2Bucket,1
31,22,1
40,31,1
61,52,1
42,33,1
30,21,1
   ..... etc ....

Here, the first 2 lines are the header. The rest of the file consists of sections, one section per run. Each run's section starts with the "run introduction line", which begins with a dot ("."), followed by the algorithm's name (the same throughout the entire file), the rule set name, and the zero-based run number.

The run introduction line is followed by lines describing the outcome of individual episodes. Each line has 3 numbers: the number of moves (counting all move and pick attempts in the episode, both successful and failed), the number of errors (the failed move and pick attempts), and the "if_clear" bit, which is 1 if the board has been cleared at the end of the episode and 0 otherwise (i.e. if the algorithm gave up on this board, or a stalemate has occurred).

You can compare the content of the results file produced by the server with what your own ML algorithm has calculated as its number of moves and errors in each episode. If there is a mismatch, then there is something wrong with the accounting, either in the server or in your code!

Long format

For backward compatibility with older versions, the CGS also supports the "long format" of the results file, which has more redundant information. If for some reason you desire to use it, include log.format=Long into the CGS command line, along with all the other log-related options.

Below is an example of a long-format results file that has been generated after 2 runs:

nickname,rule_name,trial_id,board_id,number_of_pieces,number_of_moves,move_acc,if_clear
JohnDoe,colOrd_nearby,0,0,9,3,1.0,0
JohnDoe,colOrd_nearby,0,1,9,1,1.0,0
JohnDoe,colOrd_nearby,0,2,9,1,1.0,0
JohnDoe,colOrd_nearby,1,0,9,0,0.0,0
JohnDoe,colOrd_nearby,1,1,9,9,1.0,1
JohnDoe,colOrd_nearby,1,2,9,2,0.5,0

Your Machine Learning application talking to the CGS

It is likely that you have installed the CGS so that your ML application can "play" with it. Since the pipe-based CGS interacts with your ML application via pipes, the general idea is as following:

Additional details are found below.

Talking to the CGS from a Java application

If your ML application is in Java, you can use the ProcessBuilder API to spawn child processes and deal with their I/O. You create a Process object, write your commands to the stream returned by Process.getInputStream(), and read responses from the stream returned by Process.getOutputStream()

Talking to the CGS from a Python application

If your ML application is in Python, you can use subprocess.run for the same purpose.

For the convenience of ML researchers who may want their ML application written in Python to use the CGS, we provide a sample Python client that spawns a CGS and communicates with it via pipes.

To see how this works, run the script

	./captive-python.sh
      
with no arguments. It will set the CLASSPATH as needed to include all relevant JAR files, and will then invoke python/client.py with some arguments (rule file and piece count), which in its turn will spawn a CGS and play an episode of the specified game with it, communicating via pipes. The script python/client.py can take all other command-line arguments that the shell scripts discussed above do.

You may want to modify the script captive-python-socket.sh as needed to use a different rule file.

In the Python code, after python/client.py spawns the CGS process, it uses code from python/gameLoop.py to do the actual communication and playing. In the latter file, the method chooseMove() is the one deciding on the next move. In the sample code, the move is done by picking a random piece and trying to put it to a randomly chosen bucket; in a real ML application, some appropriate learning logic would appear here instead.

Below is the console output from a sample session (from a different, earlier version of gameLoop.py, which "cheats" a bit by going only for moveable pieces).

:~/w2020/game> ./captive-python.sh
Rule file=./rules/rules-01.txt, #pieces=5
Received: 6 0 0
Received: # Hello. This is Captive Game Server ver. 1.003. Starting a new game (no. 1)
Received: {"id":0,"value":[{"id":0,"color":"BLACK","shape":"SQUARE","x":2,"y":3,"buckets":[]},{"id":0,"color":"RED","shape":"SQUARE","x":6,"y":4,"buckets":[]},{"id":0,"color":"RED","shape":"SQUARE","x":5,"y":5,"bu
ckets":[]},{"id":0,"color":"YELLOW","shape":"STAR","x":1,"y":6,"buckets":[0]},{"id":0,"color":"RED","shape":"TRIANGLE","x":3,"y":6,"buckets":[]}]}
Code=6, status=0, stepNo=0
5 pieces still on the board
Sending: MOVE 6 1 0 0
Received: 4 0 1
Received: {"id":0,"value":[{"id":0,"color":"BLACK","shape":"SQUARE","x":2,"y":3,"buckets":[]},{"id":0,"color":"RED","shape":"SQUARE","x":6,"y":4,"buckets":[]},{"id":0,"color":"RED","shape":"SQUARE","x":5,"y":5,"bu
ckets":[]},{"id":0,"color":"YELLOW","shape":"STAR","x":1,"y":6,"buckets":[0]},{"id":0,"color":"RED","shape":"TRIANGLE","x":3,"y":6,"buckets":[]}]}
Code=4, status=0, stepNo=1
5 pieces still on the board
Sending: MOVE 6 1 0 0
Received: 4 0 2
Received: {"id":0,"value":[{"id":0,"color":"BLACK","shape":"SQUARE","x":2,"y":3,"buckets":[]},{"id":0,"color":"RED","shape":"SQUARE","x":6,"y":4,"buckets":[]},{"id":0,"color":"RED","shape":"SQUARE","x":5,"y":5,"bu
ckets":[]},{"id":0,"color":"YELLOW","shape":"STAR","x":1,"y":6,"buckets":[0]},{"id":0,"color":"RED","shape":"TRIANGLE","x":3,"y":6,"buckets":[]}]}
Code=4, status=0, stepNo=2
5 pieces still on the board
Sending: MOVE 6 1 7 0
Received: 0 0 3
Received: {"id":0,"value":[{"id":0,"color":"BLACK","shape":"SQUARE","x":2,"y":3,"buckets":[]},{"id":0,"color":"RED","shape":"SQUARE","x":6,"y":4,"buckets":[]},{"id":0,"color":"RED","shape":"SQUARE","x":5,"y":5,"bu
ckets":[]},{"id":0,"color":"RED","shape":"TRIANGLE","x":3,"y":6,"buckets":[0,1]}]}
Code=0, status=0, stepNo=3
4 pieces still on the board
  .... .... ....
1 pieces still on the board
Sending: MOVE 3 2 0 7
Received: 4 0 11
Received: {"id":0,"value":[{"id":0,"color":"BLACK","shape":"SQUARE","x":2,"y":3,"buckets":[3]}]}
Code=4, status=0, stepNo=11
1 pieces still on the board
Sending: MOVE 3 2 0 0
Received: 0 1 12
Received: # Game finished - the board is clear
Received: {"id":0,"value":[]}
Code=0, status=1, stepNo=12
Cleared board in 12 steps

Python version

The sample python app described above begins with the line

  #!/usr/bin/python
, which means that it will use whichever Python executable you have at that location. We have tested it both with Pyton 2 (specifically Python 2.7.17) and Python 3 (specicically Python 3.6.9). If the Python executable is installed at a different location on your system, you may need to edit the first line of this Pythin script (and any other Python scripts with a similar line) as appropriate.

Sample Python app that also does MLC logging

We have provided a sample shell script for the Machine Learning Challenge (MLC) participants who wants their ML program to play with the Captive Game Server and to save a results file for submission to the MLC Leader Board. The can model their scripts on the csh script scrupts/captive-python-mlc.sh, which in its turn uses python/client-2.py.

Usage:

	~/w2020/game/scripts/captive-python-mlc.sh
      

If you are an MLC participants, you may want to read this script (and the Python scripts it invokes), and insert functionally similar code into your application, so that it gets the CGS to generate necessary results files.

If you look inside this script, you'll see that it:

In each run, the script python/client-2.py runs 5 episodes. Thus, the results file (test.csv) has 10 lines for each of the 15 rule set files. The results file may look like this:

nickname,rule_name,trial_id,board_id,number_of_pieces,number_of_moves,move_acc,if_clear
RandomTest,alternateShape2Bucket_color2Bucket,0,0,9,29,0.3103448275862069,1
RandomTest,alternateShape2Bucket_color2Bucket,0,1,9,20,0.45,1
RandomTest,alternateShape2Bucket_color2Bucket,0,2,9,70,0.12857142857142856,1
RandomTest,alternateShape2Bucket_color2Bucket,0,3,9,39,0.23076923076923078,1
RandomTest,alternateShape2Bucket_color2Bucket,0,4,9,55,0.16363636363636364,1
RandomTest,alternateShape2Bucket_color2Bucket,1,0,9,36,0.25,1
RandomTest,alternateShape2Bucket_color2Bucket,1,1,9,58,0.15517241379310345,1
RandomTest,alternateShape2Bucket_color2Bucket,1,2,9,21,0.42857142857142855,1
RandomTest,alternateShape2Bucket_color2Bucket,1,3,9,63,0.14285714285714285,1
RandomTest,alternateShape2Bucket_color2Bucket,1,4,9,25,0.36,1
RandomTest,colOrd_nearby,0,0,9,111,0.08108108108108109,1
RandomTest,colOrd_nearby,0,1,9,137,0.06569343065693431,1
RandomTest,colOrd_nearby,0,2,9,76,0.11842105263157894,1
...
      

The sample Python screen is a fairly poor player, as it makes its moves completely at random (picking a random game piece and trying to move it to a randomly chosen bucket). If your ML program is any good, you should hope to obtain, on average, better values of move_acc, at least once it has learned something!

Game description

This section is somewhat obsolete and mostly superfluous now, as a more detailed and updated description of the rule file syntax is now available elsewehere.

A few game rule description files have been supplied in ~/captive/game/rules/. The rule description language is mostly described in a Google Sheets documents ( Design Checklist; if you cannot access it, ask the article authors for a share link). There are some extensions to this language though. In particular:

For more information, see the detailed guide for the Rule file syntax and semantics.

Commands (input) and output

The input to CGS consists of one-line commands, some with arguments. The output consists of "comment lines" (intended for debugging or human consumption), preceded with '#', and data lines, which do not have a '#'. Your ML program should ignore all comment lines.

Three output modes are available: brief, standard, and full. The full mode contains a large number of comment lines (in particular, a graphic board display); the other two modes don't have comment lines, or only have very few of them. The CGS can be started in any of these modes using one of the 3 scripts provided (captive-full.sh, captive-standard.sh, or captive-brief.sh).

The CGS API

The following are the commands you'll need

The implicit "NEW" command: when you start the CGS, an episode starts immediately, and you get 2 lines of output right away which are in the same format as for the "NEW" command (below).

NEW

Starts a new episode. You don't need this command at the very beginning of a session, since the first episode starts automatically. Use this command for every subsequent game. This command can be used after the previous episode has completed (with a "win" (clearing the board) or a stalemate (no piece can be moved anymore); it can also be used in the middle of an episode if you give up and want to start a new one.

Response: two lines. The first line contains 3 numbers, as described in the "MOVE" section below, reflecting the acceptance of the command and the current state. The second line describes the current display. It is in JSON format, and looks as follows:
{"id":0,"value":[{"id":0,"color":"BLUE","shape":"CIRCLE","x":4,"y":1,"buckets":[]},{"id":0,"color":"RED","shape":"CIRCLE","x":4,"y":3,"buckets":[0]},{"id":0,"color":"BLUE","shape":"SQUARE","x":3,"y":5,"buckets":[]},{"id":0,"color":"YELLOW","shape":"CIRCLE","x":3,"y":6,"buckets":[]}]}
Here, at the top level the only field that matters is "value". Its value is an array, each elements represents a piece. For example, the piece described as {"id":0,"color":"RED","shape":"CIRCLE","x":4,"y":3,"buckets":[0]} has color RED, shape CIRCLE, is located in row 3, column 4. The field "buckets" is an array containing the list of buckets (see above for numbering!) into which this piece can be moved. If the array size is zero, it means that the piece is not moveable. It is up to your program and its "honor system" to which extent it wants to look into this array!

DISPLAY

Response: one line, displaying the current board in the JSON format, in the same format as in the NEW command.

MOVE row col B_row B_col
This command attempts picks the piece located at (row,col), and to move it to the bucket at (B_row, B_col). The values for the first two coordinates can be in the range [1..6]; the values for each of the last two coordinates must be 0 or 7.

Response: two lines (in the standard mode) or one line (in the brief mode). The first line contains 3 numbers, as follows:

  response_code game_state move_count
The response_code is 0 for acceptance (successful move), positive for rejection (command is understood and is legal, but the move is denied), negative for an error (command arguments are not legal). More specifically:

    public static class CODE {
	public static final int
	// move accepted and processed
	    ACCEPT = 0,
	// Move rejected, and no other move is possible
	// (stalemate). This means that the rule set is bad, and we
	// owe an apology to the player
	    STALEMATE=2,
	// move rejected, because there is no piece in the cell
	    EMPTY_CELL= 3,
        // Move rejected, because this destination is not allowed
        // (even though the piece can perhaps be moved to some other bucket)
	    DENY = 4,
	// Exit requested
	    EXIT = 5,
	// New game requested
            NEW_GAME = 6,
        // Move rejected, because this game piece is immovable (cannot
        // be moved to any bucket).
	    IMMOVABLE = 7;

	public static final int
	    INVALID_COMMAND= -1,
	    INVALID_ARGUMENTS= -2,
	    INVALID_POS= -3,
	// No game is on now. Start a game first!
	    NO_GAME = -4,
	// Used in socket server GAME  command
	    INVALID_RULES = -5,
	// Used in web app, when trying to access a non-existent episode
	    NO_SUCH_EPISODE = -6,
	// The number of preceding attempts does not match. This may indicate
	// that some HTTP requests have been lost, or a duplicate request
            ATTEMPT_CNT_MISMATCH = -7,
    	// This code is returned on successful DISPLAY calls, to
	// indicate that it was a display (no actual move requested)
	// and not a MOVE	    
	    JUST_A_DISPLAY = -8;

   }	    

When a "competent" (human or robotic) player (i.e. one who does not mistype command, does not grab at empty cells, etc) plays with the CGS, the only response code that can come back in response to a MOVE command are 0 (ACCEPT, indicating a successful move), 4 (DENY), and 7 (IMMOVABLE), the last two indicating a failed move attempt.

Note: Prior to GS 5.007 (July 2022), response code 7 (IMMOVABLE) did not exist, and attempts to move an immovable game piece resulted in response code 4 (DENY). These two codes have been "split" since GS 5.007 in order to enable a ML program to identify an immovable game piece in a single move attempt (rather than 4 attempts). This will make robots' playing experience more similar to that of humans playing via the web GUI, who can identify immovable pieces in a single attempt.

If you are designing a ML algorithm playing the Game of Hidden Rules with the CGS, it is to your advantage to fully use the information provided by these two response code. The IMMOVABLE code means that not only the game piece you tried to move cannot be moved to your chosen bucket, but it also cannot be moved to any other bucket at this point in the game. On the other hand, the DENY code tells you that the game piece can be moved to some other bucket.

The second number, the finish code reports the current state: 0=can play (there are pieces on the board, and some can be moved), 1=finish (no pieces left on the board), 2=stalemate (there are pieces on the board, but none of them can be moved anymore).

The third number is the move count, i.e. the total number of attempted and successful moves in this episode so far.

In the standard mode, the status line is followed by a line with the description of the current display, in the same format as in the NEW command. In the brief mode, this line is omitted. You can use the brief mode if your program keeps track of the board itself, and you want to reduce the cost of communication with the CGS.

COND train
COND test
Starting with CGS ver. 6.010, these commands can be used to switch the server between the training mode and the testing mode. (Or back, although that's unlikely to be needed). For the explanation of the training and testing modes, see Example 9. Train/test. As that example indicates, it only makese sense to talk about the training and testing modes when a condition file has been provided to the CGS, so that it actually will behave differently in the two modes.

Response: #OK, COND=train or #OK, COND=test, as appropriate, indicating the new mode.

COND
The COND command with no argument simply reports the current mode, in the same format as COND with an argument does.

EXIT
Exits the CGS immediately.

HELP
Prints some human-readable help info. An ML program is unlikely to ever use this command.

VERSION
Prints the version number.

Human-readable output

In the "full" output mode, human-readable display is printed with each "DISPLAY", "NEW", or "MOVE" command, as a number of comment lines. In the other modes, the same display is available with the "DISPLAYFULL" command.

The following "ASCII art" notation is used: O, #, T, * are circles, squares, triangle, and stars, respectively. The color is either not marked at all, or is expressed by a lowercase letter (bLACK, yELLOW, rED, and g for BLUE). Dots are empty cells. Round parentheses () surround pieces that can be moved presently. Square bracket surround the cell to which the last MOVE was applied; the cell will be empty [.] if the move was successful (the piece has been removed), or non-empty, e.g. [O], if the move was rejected.

Logging

Until ver 5.003, there was no logging. The ML program was to count its games, and to record displays (e.g. the initial boards) and the playing statistics if it so desired.

Some elementary logging (one line per episode) was introduced in CGS 5.003; see above.

Socket interface

An alternative way of running CGS is as a process in a separate console window (or even by a different user, or on a different host) and communicating with it over the socket interface. Unlike the "normal" CGS, the socket game server can serve multiple clients, with a different game rule set for each one; however, the client-server session facing each client (and running in a separate thread) is still completely controlled by the client and thus can still be described as "captive".

Starting the socket-based CGS

In a new console window, run the script socket-server.sh with the desired port number as an argument, e.g.

	~/w2020/game>    ./socket-server.sh 7501
      

The socket server will normally not produce any messages, other than reports on rule set files being read at the beginning of each session, if everything goes on normally. Any error messages will go to the screen.

At this point (ver 1.003) there is no inactive session management in the server, so the clients ought to close their sessions as appropriate. When you don't need the server running anymore, you can kill it with Ctrl-C, or with a kill command.

The socket-based CGS API

The only difference from the normal (pipe-based) CGS API is that the socket CGS has one more command,

GAME "rule-file.txt" nPieces
e.g.
GAME "rules/rules-01.txt" 5
(The quotes around the file name are optional)

This command must be used as the first command of the session. It specifies the rule set file to use (the rule file name can specify the absolute file name on the server, or a path relative to the directory where the server was started), and the number of pieces. These arguments are analogous to those used on the command line when starting a pipe-based CGS; however, the rule file name must be double-quoted.

As of ver. 4.002, the GAME command in the socket CGS API can also use various other arguments, in the same way as they can be used on the command line of a pipe-based CGS (See Examples 1 thru 7 in Command-line parameters). For example:

GAME rules/rules-01.txt 5 1:2 1:3
or
GAME /opt/tomcat/game-data/trial-lists/vmColorTest/trial_1.csv 1

After the GAME command is sent, the socket CGS behaves exactly like the pipe-based CGS does; it reads in a rule set file and any other required control files, and starts the first episode, sending back to the client the status line and the JSON display line. Thereafter, the same commands (MOVE, NEW, and finally EXIT) should be used by the client to play a desired number of episodes, exactly as their are in the pipe-based CGS.

Talking to the socket CGS from the console

You can test the socket CGS by running the telnet command on the appropriate port, e.g.

	telnet localhost 7501
      

You can then enter the GAME command, followed by MOVE, NEW, and EXIT as appropriate. For example:

      
:~> telnet localhost 7501
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GAME "rules/rules-01.txt" 2
6 0 0
# Hello. This is Captive Game Server ver. 1.003. Starting a new game (no. 1)
{"id":0,"value":[{"id":0,"color":"BLUE","shape":"TRIANGLE","x":6,"y":5,"buckets":[]},{"id":0,"color":"RED","shape":"TRIANGLE","x":1,"y":6,"buckets":[0]}]}
MOVE 6 1 7 0
0 0 1
{"id":0,"value":[{"id":0,"color":"BLUE","shape":"TRIANGLE","x":6,"y":5,"buckets":[1]}]}
MOVE 5 6 7 7 
0 1 2
# Game finished - the board is clear
{"id":0,"value":[]}
NEW
6 0 0
# Hello. This is Captive Game Server ver. 1.003. Starting a new game (no. 2)
{"id":0,"value":[{"id":0,"color":"BLUE","shape":"TRIANGLE","x":5,"y":1,"buckets":[2]},{"id":0,"color":"YELLOW","shape":"CIRCLE","x":6,"y":5,"buckets":[1]}]}
....
EXIT
5 0 0
# Goodbye
Connection closed by foreign host.

Talking to the socket CGS from a Python client

For the convenience of ML researchers who may want their ML application written in Python communicate with the socket CGS, we provide a sample Python client doing exactly that.

To see how this works, run the script captive-python-socket.sh, indicating the host and port to connect, and the game to play, e.g.:

	 ./captive-python-socket.sh localhost 7501 /opt/tomcat/game-data/rules/Rule-001.txt 4
      
It will set the CLASSPATH as needed to include all relevant JAR files, and will then invoke python/client-socket.py with the same arguments (host name, port number, rule file and piece count), which opens a socket connection and plays an episode of the specified game.

You may want to modify the script captive-python-socket.sh as needed to use a different host, port, or rule file.