CS140 -- Lab D


Tue Nov 27 16:37:58 EST 2007

The Superball Challenge - Results

What you hand in

You need to submit the source code for two programs: sb-analyze.c and sb-play.c.

A Superball Game Player

Do the following from your shell:

UNIX> cp -r /home/plank/Superball $HOME
Then you can run superball with:
UNIX> ~/Superball &
You'll have to be on a machine that has tcl/tk installed (our lab machines will work, as will macintosh's. I haven't tested it on cygwin.) If the high score doesn't work, go into the file hscore and change "mozilla" to whatever web browser you have installed.

Disjoint Sets

The disjoint sets header and object files are in the appropriate class directories. For reference, I include all of disjoint.h here.

#ifndef _DISJOINT_
#define _DISJOINT_

typedef struct {
  int *links;
  int *sizes;
  int *ranks;
  int maxindex;
  int nsets;
} DisjointSet;
  
extern DisjointSet *new_disjoint_set(int maxindex);
extern void free_disjoint_set(DisjointSet *dj);
extern void disjoint_makeset(DisjointSet *dj, int index);
extern int disjoint_union(DisjointSet *dj, int s1, int s2);
extern int disjoint_find(DisjointSet *dj, int index);

#endif

Note that sizes and ranks are only well defined for root nodes of sets (those whose value of links is -1).


Superball

Superball is a simplistic game that was part of a games CD for my old Windows 95 box. It works as follows. You have a 8x10 grid which is the game board. Each cell of the game board may be empty or hold a color:

The board starts with five random colors set. On your turn, you may do one of two things:

I have a tcl/tk/shell-scripted Superball player at /home/plank/Superball. Simply copy that directory to your home directory:

UNIX> cp -r /home/plank/Superball $HOME

Then you can play it with ~/Superball/Superball. The high score probably won't work -- you'll have to change the open command in the file hscore to the name of your web browser.

Let's look at some screen shots. Suppose we fire up Superball:

The "goal" cells are marked with asterisks, and there are five non-empty cells. Our only legal action is to swap two cells -- I'm going to swap cells [6,3] and [7,5]. This will make those two blue cells contiguous. In the game, I do that by clicking on the two cells that I want to swap. Afterwards, five new cells are put on the screen. Here's the screen shot:

I do a bunch more swaps and end up with the following board: (In case you're wondering, I haven't really figured out this screen shot thing, but it should be clear enough).

I can score the red cells by clicking on cell [2,0] or [2,1] and then clicking "collect". This will score that group of nine red squares, which gets me 45 points (9*5), and three new cells will be added:

There are no cells to score here (the purple ones in the upper right-hand corner are only a group of four). So I revert to swapping. Suppose I keep doing so until I reach:

I'm in trouble -- I can score those purple ones in the upper right-hand corner, but when I'm done, and three new cells are filled in, there will be only four empty cells:

You'll note, even though there's a group of six light blue cells, none of them are in the goal squares, so I can't score them. I'm stuck -- I have to simply switch two cells, and the game will be over:

Oh well.


For this lab, we are going to deal with a text-based version of the game. Our programs will have the following parameters:

Now, try the interactive game player:

UNIX> cd /home/plank/cs140/Labs/LabD/
UNIX> sb-player
usage: sb-player rows cols min-score-size colors player interactive(y|n) output(y|n) seed
UNIX> sb-player 8 10 5 pbyrg - y y -
Empty Cells: 75     Score: 0

..b.......
.......g..
**......**
**......**
Y*......**
**..b...**
......y...
..........

Your Move: SWAP 0 2 4 0
Empty Cells: 70     Score: 0

..y.......
......yg..
**.....b**
**..rp..**
B*......**
**..b...**
....y.y...
..........

Your Move: 
It works the same way, only it's much more tedious. However, give it a try. When a letter is capitalized, it is on a goal cell. Dots and asterisks stand for empty cells -- asterisks are on the goal cells. Note, if you click on the Print Boards button in the tcl/tk game, it will print out each board on standard output. When you want to score a cell, you type SCORE row col. That cell must be a goal cell.

Program #1: Sb-read

I have written sb-read.c for you. This program takes the four parameters detailed above, reads in a game board with those parameters and prints out some very basic information. For example, the following board:

May be represented by the following text (in input-1.txt):

...yyryy.p
y.rg.yppyp
**gg.yrpPP
GGgbgybp**
R*bg.yrp*P
G*gygyypY*
yyybpby.pb
.pgg.yp.bb

When we run sb-read on it, we get the following:

UNIX> sb-read 8 10 5 pbyrg < input-1.txt
Empty cells:                    20
Non-Empty cells:                60
Number of pieces in goal cells:  8
Sum of their values:            33
UNIX> 
There are three purple pieces in goal cells, one yellow, three green and one red. That makes a total of 3*2 + 4 + 5 + 3*6 = 33.

You should take a look at sb-read.c. In particular, look at the Superball struct.

typedef struct {
  int r;
  int c;
  int mss;
  int empty;
  int *board;
  int *goals;
  int *colors;
} Superball;

mss is min-score-size. empty is the number of empty cells in the board. board is an array of r * c integers. The element in [i,j] is in entry board[i*c+j], and is either '.', '*' or a lower case letter. goals is another array of r * c integers. It is equal to -1 if the cell is not a goal cell, and it is equal to the goal's index if it is a goal cell. Colors is an array of 256 elements, which should be indexed by a letter. Its value is the value of the letter (e.g. in the above example, colors['p'] = 2).

sb-read does all manner of error checking for you. It is a nice program on which to build your other programs.


Program #2: Sb-analyze

With this program, you are to start with sb-read.c as a base, and augment it so that it prints out all possible scoring sets. For example, in the above game board (represented by input-1.txt), there are two scoring sets -- the set of 10 purple cells in the upper right-hand corner, and the set of 5 green cells on the left side of the screen. Here is the output to sb_analyze:

UNIX> sb-analyze
usage: sb-analyze rows cols min-score-size colors
UNIX> sb-analyze 8 10 5 pbyrg < input-1.txt
Scoring sets:
  Size: 10  Char: p  Scoring Cell: 2,8
  Size:  6  Char: g  Scoring Cell: 3,0
UNIX> 
Note, each set must be printed exactly once, but in any order, and with any legal goal cell. Thus, the following output would also be ok:
UNIX> sb-analyze 8 10 5 pbyrg < input-1.txt
Scoring sets:
  Size:  6  Char: g  Scoring Cell: 3,1
  Size: 10  Char: p  Scoring Cell: 2,7
UNIX> 
Think about how you would use the disjoint sets data structure to implement this. I would recommend augmenting your Superball struct with a DisjointSet, and then having a procedure called analyze_superball(), which performs the analysis. Give this some thought -- it's a little subtle, but it's not that bad.

Here's another example:

This is in the file input-2.txt:

yyggyryybp
ggrgpyppyp
RBgggyrpPP
GGgggybpPP
RGygryrpBP
YGyygyypYB
yyybpbyppb
ppggyypbbb

UNIX> sb-analyze 8 10 5 pbyrg < input-2.txt
Scoring sets:
  Size: 14  Char: g  Scoring Cell: 5,1
  Size: 15  Char: p  Scoring Cell: 4,9
  Size:  7  Char: y  Scoring Cell: 5,0
  Size:  5  Char: b  Scoring Cell: 5,9
UNIX> 

Program #3: Sb-play

Your next program takes the same arguments and input as sb-analyze. However, now its job is to print a single move as would be accepted as input for the sb-player program. In other words, it needs to output a SWAP or SCORE line with legal values.

Note, if you have fewer than five pieces and cannot score any, you will lose the game -- you should do that by swapping two legal pieces so that the game can end.

The sb-player program takes as its 5th argument the name of a program that it will use for input. I also have three programs - sb-play, sb-play2 and sb-play3 in that directory. sb-play simply swaps two random cells until there are fewer than five empty, then it scores a set if it can. The other two are smarter, but are by no means the best one can do.

Here's sb-player running on sb-play2 (note, sb-player creates a temporary file, so you must run it from your own directory):

UNIX> /home/plank/cs140/Labs/LabD/sb-player 8 10 5 pbyrg /home/plank/cs140/Labs/LabD/sb-play2 y y -
Empty Cells: 75     Score: 0

g.........
..........
**......**
*Pr.....**
**......**
**..p...**
........b.
..........

Type Return for the next play
Note, it waits for you to press the return key. When you do so, it will send the game board to /home/plank/cs140/Labs/LabD/sp-play2 and perform the output. Here's what happens:
Move is: SWAP 5 4 3 2

Empty Cells: 70     Score: 0

g........g
.......y..
**......**
*Pp.....**
**......G*
**..r...**
..g.....b.
........g.

Type Return for the next play
You can bet that the next move will swap that b with one of the g's:
Move is: SWAP 6 8 0 0

Empty Cells: 65     Score: 0

b........g
.......y..
**..b...**
*Pp.g...**
**.....gG*
**..r...**
..g.....g.
.p...p..g.

Type Return for the next play
And so on. If you run it with n for the 6th argument, it will simply run the program without your input:
UNIX> /home/plank/cs140/Labs/LabD/sb-player 8 10 5 pbyrg /home/plank/cs140/Labs/LabD/sb-play2 n y -
Empty Cells: 75     Score: 0

..........
..........
**......**
**y..y..**
**......**
*P......**
..........
......p.g.

Move is: SWAP 3 5 3 2

... a bunch of output skipped...

Empty Cells:  1     Score: 505

yyrrgggpyy
grrbppg.yg
GYbgygggPB
GBggpgbpPB
PPgggggrYB
YBbybgpbYR
pprrrggggr
byyrppppgg

Move is: SWAP 0 1 7 5

Game over.  Final score = 505
UNIX> 
You'll note that even though there were no good moves at the end, the program did a final SWAP so that the game could finish.

If you run with the 7th argument as n, it will only print out the end result, and the last argument can specify a seed (it uses the current time if that argument is "-"), so that you can compare multiple players on the same game:

UNIX> /home/plank/cs140/Labs/LabD/sb-player 8 10 5 pbyrg /home/plank/cs140/Labs/LabD/sb-play n n 1
Game over.  Final score = 38
UNIX> /home/plank/cs140/Labs/LabD/sb-player 8 10 5 pbyrg /home/plank/cs140/Labs/LabD/sb-play2 n n 1
Game over.  Final score = 855
UNIX> /home/plank/cs140/Labs/LabD/sb-player 8 10 5 pbyrg /home/plank/cs140/Labs/LabD/sb-play3 n n 1
Game over.  Final score = 2572
UNIX> 
It can take a while for these to run -- if it appears to be hanging, send the process a QUIT signal and it will print out what the current score is.

Shell Script to Run Multiple Times

The file run_multiple.sh is a shell script to run the player on multiple seeds and average the results. Examples:
UNIX> sh run_multiple.sh 8 10 5 pbyrg sb-play 10
Run   1 - Score:     38  - Average     38.000
Run   2 - Score:      0  - Average     19.000
Run   3 - Score:      0  - Average     12.667
Run   4 - Score:     57  - Average     23.750
Run   5 - Score:      0  - Average     19.000
Run   6 - Score:      0  - Average     15.833
Run   7 - Score:     89  - Average     26.286
Run   8 - Score:     15  - Average     24.875
Run   9 - Score:      0  - Average     22.111
Run  10 - Score:     20  - Average     21.900
UNIX> sh run_multiple.sh 8 10 5 pbyrg sb-play2 10
Run   1 - Score:    855  - Average    855.000
Run   2 - Score:    979  - Average    917.000
Run   3 - Score:    650  - Average    828.000
Run   4 - Score:    833  - Average    829.250
Run   5 - Score:    832  - Average    829.800
Run   6 - Score:   3326  - Average   1245.833
Run   7 - Score:   1507  - Average   1283.143
Run   8 - Score:   3643  - Average   1578.125
Run   9 - Score:    610  - Average   1470.556
Run  10 - Score:    862  - Average   1409.700
UNIX> sh run_multiple.sh 8 10 5 pbyrg sb-play3 10
Run   1 - Score:   2572  - Average   2572.000
Run   2 - Score:   2708  - Average   2640.000
Run   3 - Score:    745  - Average   2008.333
Run   4 - Score:    424  - Average   1612.250
Run   5 - Score:   1888  - Average   1667.400
Run   6 - Score:   7140  - Average   2579.500
Run   7 - Score:   3475  - Average   2707.429
Run   8 - Score:   1701  - Average   2581.625
Run   9 - Score:   2699  - Average   2594.667
Run  10 - Score:   2291  - Average   2564.300
UNIX> 
Obviously, to get a meaningful average, many more runs (than 10) will be required.

The Superball Challenge

Your player needs to average in the hundreds to get full credit. I will run a Superball tournament with all of your players with extra lab points going to the winners:

I'll post the results a few days after the lab is due.