Computer Science 105 Lab 9: Designing an Interaction Using the Grid Class
Due 5:00pm April 25, 2003

Objectives and Overview: This lab asks you to design a program that monitors an interaction, taken from the following list of alternatives:
  1. An interactive Tic-Tac-Toe game
  2. An interactive Nim game
  3. An interaction that demonstrates the Game of Life
  4. A solution to the "mouse in a maze" problem
  5. A different interaction of your own choosing (e.g., Connect-4, Othello, etc.).
All of these interactions share the common characteristic of being played on a rectangular board.

To facilitate this design, new classes Grid and Cell have been designed with methods that enable the interaction to define a rectangular board, make plays on the board, examine the state of the board, and so forth. Part 1 is a brief tutorial on these classes.

Part 1 -- The Grid and Cell Classes: a Quick Tutorial

To introduce you to the functionality of the Grid class, the folder Boardgame should first be dragged from the CS105 (Tucker) folder to the desktop. This folder contains a project with the program files Boardgame.java, Grid.java, Cell.java and Boardgame.html. Run this project by double-clicking on Boardgame.pbproj to achieve the following initial frame:

These four objects should be familiar to you: a Button, a menu of Choices, a TextArea, and a TextField. These are designed to allow you to exercise the methods of the Grid and Cell classes.

A "cell" is a rectangle inside a grid that has one of four possible values - X, O, ON or OFF - and one of several possible colors. The key public variables and methods for the class Cell are summarized as follows:

final char OFF = ' ', ON = '*', EX = 'X', OH = 'O'; // possible values of a cell
Cell (int x, int y, int size, int val); // x-y coordinates of a cell, its size,
// and its initial value
char get(); // current value
void set (char val); // set the value of
Color getColor(); // current color
void setColor(Color c);
boolean contains (int x, int y); // true if cell contains pixel at x-y
void paint(); // display the cell in the frame

A "grid" is a square array of cells. The key public variables and methods for the class Grid are summarized as follows:

Grid (int rows, int cellSize, int offset); // number or rows (and columns), size
// of a cell in pixels, and offset from
// upper left-hand corner of the frame

void loadFromFile(String filename) // grid cells are set according to
// array of settings in 'filename'
void clear () // all cells are set to OFF
void paint () // all cells are redrawn)
boolean contains (int x, int y) // set row and col if pixel at x-y is in the
// grid; otherwise, set row = col = -1

void set(int r, int c, char val) // assign val to grid cell at row r col c
char get(int r, int c) // return the char value at row r col c
void setColor(Color cc) // set the color of future cell settings

Now click the New Game button to achieve the following display:

This display is achieved in the BoardGame program by declaring and initializing the variable named theBoard . The actionPerformed handler initializes this variable as a new Grid in response to a selection of the button "New Game":

b = new Grid(5, 15, 120);
This statement calls for the creation of a 5x5 grid whose cells are 15 pixels in size, and which is displaced 120 pixels in both directions from the upper left-hand corner of the Frame. The rows and columns of this array are numbered from 0 to 4, reading vertically from the top and horizontally from the left.

Any cell in the grid may be set by a single mouse click, but only after a particular choice from the menu has been selected:

Select X
Select O
Fill Square
Erase Square

For example, select the choice "Select X" and then click the mouse in row 4, column 1. The program should give you the following response.

The itemStateChanged event handler for the choice "Select X" contains the following line of code, which sets the char variable current_player to EX (the phrase Cell.EX says that the constant EX is defined inside theCell class):

        if (current_choice.equals("Select X")) 
current_player = Cell.EX;
The mouseClicked handler contains the following phrase, which actually sets the designated square to X, but only if the x-y pixel coordinates of the click are somewhere inside the boundaries of the grid b:
       if (b.contains(last_x, last_y))
{ b.setColor(current_color);
b.set(b.row, b.col, current_player); repaint(); }
The contains method call does two things: it determines whether or not the coordinates of the pixel at the point (last_x, last_y) are inside the grid, and if so it translates that point to a corresponding row and column number for that point (given by b.row and b.col , respectively).

The setColor and set method calls establish the color and then draw a value in the cell at b.row and b.col, given by the char variable current_player . Thus, the cell in row 4, column 1 is set to the value X in the above picture.

Now try making a series of additional selections and identify the places in the code of the BoardGame program that cause these selections to occur. As an exercise, try to achieve the following board display after a series of selections and moves.

Answer the following questions before moving to Part 2.

  1. Find a statement in the program BoardGame that uses the method loadFromFile and try to figure out what this statement does. Hint: there is a file called 'maze4' that contains a pre-set array of cell values. Try loading this file into the BoardGame interaction.
  2. What events (user actions) and program statements come into play when you want to fill a cell with the color green?
  3. The == operator is used to test whether the value of the cell c is currently set to a particular character (c.EX, c.OH, c.ON, or c.OFF). Write a statement that will test to see if the cell in a particular row and column of theBoard is currently clear (OFF), and if so will change it to EX.
  4. Alter the BoardGame program so that it will allow the user to change a cell to EX or OH, but only if it is currently OFF. Otherwise, no change should be allowed.

Part 2 - Design a New Interaction

The Grid class and the BoardGame interaction provide a foundation for designing a wide range of applets that monitor the play of various 2-player games. Here are some general guidelines for designing these kinds of interactions.

The game should have a clear set of rules for defining when the game is over. Typically, a GameOver method can be written that interprets these rules and delivers a boolean value, true or false , depending on whether no further moves by either player can be made. At that point, your program should also report the identity of the winner (Player 1 or Player 2) or a tie game in the TextArea part of the frame.

The game should have a clear definition of what it means for a player to make a legal move and complete his/her turn. It should not allow illegal moves to be made. When one player completes a turn, the interaction should provide a prompt that tells whose turn is next. The variables that define the state of the game should include one whose value changes, say between 0 and 1, every time one player's turn ends and the other's is about to begin.

The interaction should report to the players, through the TextArea display, exactly what's going on as each move is made and each turn is completed. For instance, when Player 1 completes a turn, it should display a message like "Player 2's turn." When the game is over it should display a message like "Game over, Player 1 wins."

An interactive Tic-Tac-Toe game
This problem requires you to develop a program that monitors a game of Tic Tac Toe between two players. A Java program that monitors this game can begin by displaying a 3x3 Grid, as shown below:

(A skeleton tic-tac-toe program is given in the files TicTacToe.java and TicTacToe.html inside the myApplet folder) Each player (Player X or Player O) takes a turn by clicking the mouse to place an X or O on one of the unoccupied squares; player X plays first. The winner is the player who first places 3 X's or 3 O's in a row, either horizontally, vertically, or diagonally. A tie game occurs when the board is full and noone has 3 X's or O's in a row.

The program should conduct a dialogue with the two players using the TextArea for displaying appropriate messages. It should allow each player to click on any empty square and replace that square with an X or an O (depending on whose move it is).

To monitor this game, your program should be able to

  1. Keep track of whose turn (Player X or Player O) it is and report that information in the TextArea at the beginning of each turn,
  2. Accept a mouse click only if it is a legal move (in an unoccupied square on the board),
  3. Determine when the game is over (and whether Player 1, Player 2, or no-one has won) and report that event in the TextArea, and
  4. Allow a new game to be started at any time, perhaps by providing a Choice or a Button that will cause a handler restore the board to the state shown above.
The program should have a method called GameOver that deteermines whether or not the game is over (the board is full or there are 3 X's or 3 O's in a row). That method should be callled at an appropriate time by the mouseClicked handler to tell whether or not to report the result or continue monitoring the next move.

The state of the program should include, among other things, a variable whoseTurn which tells whose turn it is -- Player X or Player O -- and that variable should be changed whenever the mouseClicked handler determines that a player has concluded their turn.

Optional extra credit: Think of a straegy by which your program may assume the role of Player X and play a competitive game of Tic Tac Toe. (Do this part only after you have completed the basic part described above.)

An interactive Nim game
This problem requires you to develop a program that monitors a game of Nim between two players. This game can be played on a board that has three rows of pebbles, as shown below:

o o o o o o o
o o o o o
o o o

Each player takes her turn by removing one or more pebbles from any one of the three rows. The winner is the player who picks up the last pebble. A Java program can monitor this game by displaying a 7x7 Grid in which the first three rows contain O's in locations where the pebbles are initially placed, as shown below:

The program should conduct a dialogue with the two players using the TextArea for displaying appropriate messages. It should allow each player to click on the particular pebble(s) to be removed during that player's turn, but it should not allow a pebble to be picked up in two different rows during the same turn. To mark a "removed" pebble, the program should replace its O with an X on the same position of the board.

To monitor this game, your program should be able to

  1. Keep track of whose turn (Player 1 or Player 2) it is and report that information in the TextArea at the beginning of each turn,
  2. Accept a series of mouse clicks that, together, constitute a legal move (the end of a player's move may be signaled by a mouse click outside the grid),
  3. Determine when the game is over (and which of Player 1 or Player 2 is the winner) and report that event in the TextArea, and
  4. Allow a new game to be started at any time, perhaps by providing a Choice or a button that will cause a handler restore the board to the state shown above.
The program should have a method called GameOver that deteermines whether or not the game is over (the board contains only X's and spaces in the first three rows). That method should be callled at an appropriate time by the mouseClicked handler to tell whether or not to report a winner.

The state of the program should include, among other things, a variable whoseTurn which tells whose turn it is -- Player 1 or Player 2 -- and that variable should be changed whenever the musetClicked handler determines that a player has concluded her turn (clicked outside the board).

Optional extra credit: Think of a straegy by which your program may assume the role of Player 1 and play a competitive game of Nim. (Do this part only after you have completed the basic part described above.)

An interaction that demonstrates the Game of Life
This interaction is not a competitive game, in the sense of Tic Tac Toe or Nim. Instead, it demonstrates a model of population growth and decay over a series of "generations." The model is called the "Game of Life" and was designed by John Conway in 1970. It uses a grid to display a population of individuals in a single generation, such as the 8x8 grid shown below.

A square is filled in on the grid to indicate a living individual, and is empty to indicate the absence of a living individual. Each square on the grid has up to eight immediate "neighbors,"
one immediately on each side and one immediately on each diagonal. For example, the eight neighbors of the cell at position (2, 3) -- that is, row 2 and column 3 -- are the cells at positions (1, 2), (1, 3), (1, 4), (2, 2), (2, 4), (3, 2), (3, 3), and (3, 4).

The play of the game is simply to track what happens to the grid from one generation to the next, over a series of generations. We start with an initial generation of individuals, such as the one shown in the lefthand grid below.

To predict which individuals will be living in the next generation, we apply the following rules to each cell in the lefthand grid.

  1. Survival. An individual survives into the next generation if it has two or three living neighbors in the current generation.
  2. Birth. An individual is born in the next generation if (a) its cell is empty in the current generation, and (b) its cell has exactly three living neighbors in the current generation.
  3. Death by loneliness. An indivual dies in the next generation if it has fewer than two living neighbors in the current generation.
  4. Death by overcrowding. An individual dies in the next generation if it has four or more living neighbors in the current generation.
The righthand grid above shows the result of applying these four rules to each of the cells in the lefthand grid. This result is the second generation. As you can see, various cells have survived [e.g., the one in position (1, 1)], been born [e.g., the one in position (0, 1)], died of loneliness [e.g., the one in position (5, 3)], or died of overcrowding [e.g., the one in position (4. 5)]. Other cells have remained unoccupied from the first generation to the second.

Repeating this process over a series of generations gives some interesting patterns of population growth and decay. For instance, the pattern in the upper lefthand corner of the grid will repeat itself over a series of generations, while the one in the lower lefthand corner will completely disappear after the second generation. The pattern in the middle of the screen will change shape and position in an interesting way across a series of generations.

To visualize these population shifts, your interaction should provide two grids side-by-side on the board, and then ask the player to define the first generation by clicking on a series of squares in the lefthand grid. Completion of that activity may be signaled by the user's clicking outside the grid, or perhaps selecting a different alternative in a Choice menu on the screen. Next, the user should be able to ask the program to compute and display the second generation on the righthand grid, the third generation on the lefthand grid, the fourth on the right, and so on. The end of the simulation is reached by any one of the following events:

  1. The entire grid becomes empty (the whole generation has died,
  2. Two successive generations are identical in their populations, or
  3. The user selects a Choice that stops the simulation.
The user should also be given the opportunity to start a new simulation; in this event, the program should clear both grids and allow the user to start a new game by selecting a different initial configuration.

Running a Maze

A simple algorithm for running a maze is sketched below in pseudocode (The variable 'out' is true or false, telling whether or not the runner is out of the maze):

runTheMaze from the square (x, y) {

    set the current square to 'X' and 'out' to false.
    step forward
    while not out {
        turn right
        while (facing a wall)
            turn left
        step forward
        if (x, y) is at the edge of the grid
           set 'out' to true

    }
}

Using the Grid class and the skeleton program boardgame.java , the following interaction can be used as a starting point for modeling and visualizing the maze-running process.

This particular grid can be initialized by selecting the "Load grid from file" menu option, and then typing the name of the file "maze360" in the TextField window. This simplifies the process of initializing a maze by avoiding the need to mouse click every square that should be turned ON. By convention, the maze is defined by the configuration of all grid squares that are OFF.  A path through the maze can be represented by marking an 'X' at each step of the path.

There's another maze-running algorithm, which is recursive, that also does the job.  Here is a summary of that algorithm, given a square in row x and column y on the grid and assuming that an adjacent square to the North, East, South, or West has not yet been visited.

boolean runTheMaze from a given square (x, y) {
    set the current square to X and the variable 'out' to false.
    if (x, y) is not at the edge of the grid {
       if the square to the West has not yet been visited,
          out = run the maze from that square
       if not out and the square to the North has not yet been visited,
          out = run the maze from that square
       if not out and the square to the East has not yet been visited,
          out = run the maze from that square
       if not out and the square to the South has not yet been visited,
          out = run the maze from that square
    }
    else out = true
       // we've run the maze!
    return out
}

Modeling maze-running as an interaction can allow the user to single-step the maze-running process, displaying the new maze after each step is taken. The interaction may also identify steps over squares already visited (this is called "backtracking") by marking them with a different symbol, such as "O."

You can define any maze that you like, no matter how complicated or how large, by using the conventions shown in the file "maze360" and making your maze as a separate file. In a maze file, a wall is indicated by an asterisk '*' and an unoccupied square is indicated by a space ' '. Your maze file should have the same number of lines as it has characters in each line (that is, grids must be square).  Just for variety, run your program with one of the other files in the folder 'mazes'.

A different interaction of your own choosing (e.g., Connect-4, Othello, etc.).
You may propose to substitute a different interactive board game in place of all three of these. If you do that, be sure that the game satisfies the general guidelines outlined at the beginning of Part 2, and that it can be graded using the criteria outlined below.

Part 3 - Completion and Grading of this Project

Parts 1 and 2 of this lab may be done individually or in teams of 2 or 3 people. Your programs will be graded using the following criteria.
  1. The interaction works correctly under all circumstances.
  2. The interaction gives the user a correct prompt after each play has been completed.
  3. The interaction has a method GameOver which is called at appropriate times to determine whether or not the game is over (and who is the winner).
  4. The program is clearly organized and formatted, with the "state" well defined by the variables at the top, the "frame layout" well-defined by the init method, and the "event handlers" clearly describing the different events that they are designed to handle.
  5. If a team works together on the project, the names of the team members and what each one contributed should be written down as comments at the top of the program.
A hard copy of the completed program should be turned in to my office on the due date. An electronic copy should also be submitted to the Drop Box in the CS105 folder at that time.

PS  If you want to set up your applet on your Web page, so that others around campus can share your creations, follow the instructions in the document Setting up a Personal Web Page.