CS140 -- Lab 9

This is a recursion lab with two parts. The first part is worth 20 points, and the second is worth 80.

Part 1: Enumerating Strings

Your first job is write a program called enum, which is called as follows:

enum length ones

This program should enumerate all strings of length length that contain ones ones, and (length-ones) zeros, and print them on standard output in sorted order. For example:

UNIX> enum 2 2
11
UNIX> enum 2 1
01
10
UNIX> enum 2 0
00
UNIX> enum 3 1
001
010
100
UNIX> enum 3 2
011
101
110
UNIX> enum 5 3
00111
01011
01101
01110
10011
10101
10110
11001
11010
11100
UNIX> enum 100 100
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
UNIX> enum 100 0
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
UNIX> 
You may puzzle about how to do this, but it is easy with recursion. What I did was define a class called Enum, which has two public methods: It also has two protected pieces of data: My main() processes the command line arguments, and then allocated an instance of Enum with the given length. It then calls the do_enumeration() method with an index of zero and number-of-ones equal to the command line argument.

do_enumeration() is recursive. The base case is when index is equal to length. Then it prints out the string and returns. In other cases, it calculates whether the character at the given index can be a zero. If so, it sets it to zero and recursively calls do_enumeration() with an incremented index, and the same number-of-ones.

When that recursive call returns, it determines if the character at the given index can be a one. If so, it sets it to one and recursively calls do_enumeration() with an incremented index, and the with number-of-ones decremented by one.

That's all there is to it.


Part 2: ShapeShifter!!

The Game

In neopets.com, there is a game called "ShapeShifter." I won't screen-dump their pictures, since that probably violates a few copyrights, so I'll explain the game using my own pictures. You are given a setup like below, consisting of grid of swords and shields, an "active shape", and some "remaining shapes":

You apply the active shape to a part of the grid where it fits. For example, in the picture above, you can apply the active shape to row zero, row one or row two (life is zero-indexed, of course). When you apply the shape, all grid cells that the shape overlaps change swords to shields and vice versa. For example, suppose you apply the active shape to row zero. The state of the system then changes to:

The next shape can be applied to row 0 or row 1. Suppose we apply it to row 1. Now the state of the system is:

There is one more shape, which can be applied to columns 0, 1 or 2. We apply it do column zero, and the grid becomes all swords:

Those are the mechanics of the game. You are given a grid of swords and shields and a collection of shapes. Your goal is to apply all of the shapes to the grid so that you end up with a grid of all swords.

Here's a second example:

There are nine places for that first shape to go. Since I know the solution, I know it gets applied at row 1, column 1:

There are four places for the next shape -- we apply it to row 0, column 1:

I think you can figure out where that last shape goes.


The Interactive Game Player

Since we are computer scientists, we are going to represent sheilds with zeros, and swords with ones. We will represent any rectangular grid of zeros and ones that has r rows and c columns with a vector of r strings. Each string in the vector has exactly c characters, which are either '0' or '1'.

Thus, the grid in our first example is:

{ "100", "101", "000" }

and the grid in the second example is:

{ "100", "101", "001" }

We can also represent shapes as grids of zeros and ones. For example, the shapes in the first example are:

{ "111" }
{ "110", "011" }
{ "1", "1", "1" }

and the shapes in the second example are:

{ "1" }
{ "11", "10" }
{ "01", "11" }

I have written an interactive game player, called ss_player, which takes a prompt and then a grid as command line arguments. After the prompt, each word on the command line specifies a different row of the grid. These words must all be the same size, and they must be composed solely of zeros and ones. Suppose I want to play using the first example above. Then I can play with:

UNIX> ss_player ShapeShifter: 100 101 000

The Grid:

100
101
000

ShapeShifter: 
At the prompt, I enter a shape and where to apply it. Each row of the shape is a separate word, and the last two words must specify a row and a column. Below, I show how you would specify the solution to the first example:
UNIX> ss_player ShapeShifter: 100 101 000

The Grid:

100
101
000

ShapeShifter: 111 0 0

The Grid:

011
101
000

ShapeShifter: 110 011 1 0

The Grid:

011
011
011

ShapeShifter: 1 1 1 0 0

The Grid:

111
111
111

ShapeShifter: <CNTL-D>
UNIX> 
Go ahead and try to do the second example on your own.

If you give a prompt of "-", there will be no prompt, which is useful for when you use the player with a file or output of a program as input.


Your job: ss_solver

Your job is to write the program ss_solver.cpp. It takes a grid on its command line, and then each line of standard input specifies a shape. After reading all of the shapes, your program should output how to apply each shape to solve the puzzle. That means outputting each shape and the row and column at which it was applied. It should output the shapes in the same order as the input file. If there is no solution, it should simply exit with no output.

For example, the file ex1.txt contains the shapes for the first example:

111
110 011
1 1 1

ex2.txt contains the shapes for the second example. Here's the program running the two examples:

UNIX> ss_solver 100 101 000 < ex1.txt
111 0 0
110 011 1 0
1 1 1 0 0
UNIX> ss_solver 100 101 001 < ex2.txt
1 1 1
11 10 0 1
01 11 1 0
UNIX> 
In the first example, shape "111" was applied at grid coordinates (0,0), then shape "110 011" was applied at grid coordinates (1,0), and finally the shape "1 1 1" was applied at grid coordinates (0,0). Note that shapes are printed out in the same format in which they were read from standard input and that the shapes are being printed in the same order as they were read from standard input.

Testing your program

The program ss_random_game generates a random game. It takes four arguments -- rows, columns, number of shapes, and whether the puzzle should be solvable. That last argument should either be "y" for "yes" or "u" for "unknown."

Here are two examples. The first creates a 3x4 grid that is solvable with the given pieces. The second creates a 4x4 grid whose solvability is unknown. The program sed is used to strip out the first two lines of standard input.

UNIX> ss_random_game
usage: ss_random_game rows cols nshapes solvable(y|u)
UNIX> ss_random_game 3 4 4 y > tmp-test1.txt
UNIX> cat tmp-test1.txt
Grid: 0010 1111 1101
Shapes:
1 1
1111 1110
11
1
UNIX> sed 1,2d tmp-test1.txt
1 1
1111 1110
11
1
UNIX> sed 1,2d tmp-test1.txt | ss_solver 0010 1111 1101
1 1 0 2
1111 1110 0 0
11 1 0
1 2 2
UNIX> sed 1,2d tmp-test1.txt | ss_solver 0010 1111 1101 | ss_player - 0010 1111 1101

The Grid:

0010
1111
1101


The Grid:

0000
1101
1101


The Grid:

1111
0011
1101


The Grid:

1111
1111
1101


The Grid:

1111
1111
1111

UNIX> ss_random_game 4 4 4 u > tmp-test2.txt
UNIX> cat tmp-test2.txt
Grid: 1100 1101 0001 1110
Shapes:
101
0111 0010 1110
11
010 111 111 001
UNIX> sed 1,2d tmp-test1.txt | ss_solver 1100 1101 0001 1110
UNIX> 
Since there is no output from ss_solver, the puzzle is not solvable.

ss_tester.sh

In the lab directory, there's a testing shell script called ss_tester.sh. This is what the grading script uses. You call it with four arguments: It then runs both your and my solver. If my solver says its not solvable, yours should too. Otherwise, it uses the player to make sure that your solution works. Your solution should not have any extraneous output.

When it is done running, there will be four new files:

Here's an example -- I set the environment variable l so that I can use the programs in the lab directory, and I don't have to type so much:

UNIX> $l/ss_random_game 4 4 4 y > tmp-test.txt
UNIX> cat tmp-test.txt
Grid: 0110 1010 1110 0100
Shapes:
11
1001
111 010 101
10 11 10
UNIX> sh $l/ss_tester.sh 
usage: sh ss_tester.sh your-solver my-solver player output-of-ss_random_game
UNIX> sh $l/ss_tester.sh ss_solver $l/ss_solver $l/ss_player tmp-test.txt
Correct
UNIX> cat tmp-pieces.txt 
11
1001
111 010 101
10 11 10
UNIX> cat tmp-yourpieces.txt
11
1001
111 010 101
10 11 10
UNIX> cat tmp-youroutput.txt 
11 3 0
1001 0 0
111 010 101 1 1
10 11 10 1 2
UNIX> cat tmp-yourgame.txt

The Grid:

0110
1010
1110
0100


The Grid:

0110
1010
1110
1000


The Grid:

1111
1010
1110
1000


The Grid:

1111
1101
1100
1101


The Grid:

1111
1111
1111
1111

UNIX> 
Here are two more examples. In the first, I have a program called badsolver1, which doesn't solve the problem:
UNIX> sh $l/ss_tester.sh badsolver1 $l/ss_solver $l/ss_player tmp-test.txt
Incorrect -- your solution does not solve the puzzle correctly.
UNIX> cat tmp-youroutput.txt 
11 0 0
1001 0 0
111 010 101 0 0
10 11 10 0 0
UNIX> cat tmp-yourgame.txt

The Grid:

0110
1010
1110
0100


The Grid:

1010
1010
1110
0100


The Grid:

0011
1010
1110
0100


The Grid:

1101
1110
0100
0100


The Grid:

0101
0010
1100
0100

UNIX> 
The second program badsolver2 solves the puzzle, but doesn't use the right pieces:
UNIX> sh $l/ss_tester.sh badsolver2 $l/ss_solver $l/ss_player tmp-test.txt
Incorrect -- your solution does not use the correct pieces in the correct order.
UNIX> cat tmp-youroutput.txt 
1010 0111 0000 0001 0 0
0010 0000 0010 1010 0 0
0100 0010 0010 0000 0 0
0101 0000 0001 0000 0 0
UNIX> 
If the tester fails, try to figure out why with the output files, and with my correct solver.

The grading script

The grading script runs ss_tester.sh on various input files. Some of them may take a few seconds. The gradeall script took 42 seconds for me on a hydra. Yours shouldn't take more than three minutes.

Program Structure

I'm going to let you structure this one yourself. The only requirement is that your program must use recursion. Here's how I structured mine (you don't have to do it this way):

There are obviously other ways to structure this program, but that seemed easiest to me.