CPS 108, Fall 2001 - OOGA » Group Members: Albert Lee, Steven Poloni, Matthew Yang
Core Classes:
GameApplet: Represents the View in our Model/View Design pattern. Since all of our games are essentially Guis, we decided to colocate the View/Controller so that this class drew the Gui as well as controlled the game. It is structured as an applet in that it has no constructor but uses the function init() to initialize. However, it does have a main function so that it can be run as an application as well as an applet. This functionality is provided by the GameAppletFrame class. All two-player board games will subclass this class.

This class also provides the basic look and feel of a two-player game. It has a Game menu with a default of one Options item. See the OptionsDialog class descriptions for further details on what this Dialog provides. This class also has the generic GameModel and GameGrid and two GamePlayer objects ready and initialized. It also has a message bar at the bottom to allow for output of informational messages.

There are two important abstract methods that a specific game must implement. They are update() and gameOver(). The update() functions needs to implemented because our Model/View classes make use of the Java Observer/Observable classes to facilitate the Model/View design. This method is called whenever the Model changes so that this class can redraw the View to reflect the changes.
GameAppletFrame: Allows a GameApplet to be framed inside a JFrame to be used as an application. In the main method of the applet/application, we make a new frame of this kind. The one catch is that if the program is started by the Java interpreter and not the applet viewer, and it calls getAppletContext, it gets a null pointer because it has not been launched inside a browser. To deal with this, we implement these function, even though we implement them to do nothing. For example, the showStatus(message) method displays no message but it at leastit doesn't crash the program. It turns out that we only needed to implement two interfaces: AppletStub and AppletContext. This class is generic enough to not be subclassed.
GameModel: Represents the Model in our Model/View design patter. For our board games, this class essentially consisted of the GameBoard class which models a typical game board. This class does not need to be subclassed by any games
GameBoard: Represents a typical game board so should be subclassed by any specific two-player board game. Essentially, the main constituent of this class is a single array of Strings that correspond to individual boxes on a game board.
GameGrid: This is the JPanel used to implement the View of each of our games. Each game uses JButton's as the individual components representing game pieces. In a sense, the GameGrid was used as a generic Button Panel. This panel allows for the animation of components as they can be moved across the grid. This structure is also perfectly capable of being used as a plain old ordinary grid (it is used in this fashion in both Chomp and TicTacToe). The animation works with any Component, however clicking detection only works on panels that extend AbstractButton.

Features include:

- Automatic detection and reporting of grid clicks for AbstractButtons in this grid.
- Buffered mass movements of components, that is, one can specify as many movements as one wants and they will all happen simultaneously.
- You can fly components onto the screen from outside the space of the grid, as though they had actually come from that location.
- Same, but vice versa: you can fly existing components off the grid.
- You can fly components past the grid... from non-existing places to non-existing places, whatever you please.
- A RealFunction object can be passed in to affect the style of movements of the components as they fly about.

This class was written by Thomas Finley.
GamePlayer: Represents a generic player for games. This class was made extensible to allow for Human and Machine player implementations. When we sat down and designed this class, we were looking to achieve a GamePlayer that was more than just a placeholder for the name/icon of a player. We wanted to write the classes to model the real life situation in which the players control the flow of the game by deciding when turns have been taken and whose turn it is. We found this extremely difficult to do at first as this lead to the GamePlayer class needing explicit knowlegde of each individual GUI. We knew this was not a desireable result and it lead us to believe that the control of the game should just be implemented in the View/GUI. But, we then looked to Threads, and this gave us exactly the result that we wanted. The GamePlayer class no longer needed specific knowledge of the GUI. Each player first waits until it is his turn while the other player or players makes their moves. When it is a player's turn, the GamePlayer Thread waits until a cell/position has been selected on the GameGrid. The View then informs the GamePlayer that a move has been made and if it is a valid move the GamePlayer Thread informs the next GamePlayer Thread to take its turn. If a valid move has not been made, the current GamePlayer Thread continues to wait until a valid move has been made. These classes worked out beautifully in taking the control of the flow of the game outside of the view and placing it where it belongs - with the players.

The flow of the game is controlled in the run() functions of each of the GamePlayer threads:

    public synchronized void run()
    {
        while(!myTimeToStop)
        {
            // don't continue until it is your turn
            while (turn != this && !myTimeToStop)
            {
                try
                {
                    // wait until it is your turn
                    wait();
                    // wake up from turn method
                }
                catch (InterruptedException ex)
                {
                    return;
                }
            }
            
            if (myTimeToStop)
            {
                return;
            }
            
            // inform the view that it is now your turn
            myView.displayMessage(getPlayerName() + "'s turn");
            
            // continue to make moves until a valid move is made
            while (true)
            {
                // make your move - this function should be overridden in the 
                // HumanPlayer and MachinePlayer subclasses
                int move = makeMove();
                if (myTimeToStop)
                {
                    return;
                }
                if (myView.makeMove(myMove))
                {
                    break;
                }
            }
            
            turn = null;
            // tell the next player that it is now his/her turn
            next.turn();
        }
HumanPlayer: Subclass of GamePlayer which simply overloads the makeMove() function to allow a human to select a cell on the GameGrid as their move. The function looks as follows:
    public synchronized int makeMove()
    {
        try
        {
            wait();
        }
        catch (InterruptedException e) {}

        return myMove;
    }
As you can see, the HumanPlayer Thread simply waits. The idea is that when the user clicks on a cell of the GameGrid to select a move, the view calls the selectCell function of the HumanPlayer:
    /**
     * Sets the move of this player and executes it
     */
    public synchronized void selectCell(int x)
    {
        myMove = x;
        notifyAll();
    }
This function sets the move of the HumanPlayer to the cell that was selected on the GameGrid and then notifyAll() is called. This function awakens all threads that are waiting. Specifically, the HumanPlayer Thread is awoken from its waiting and returns its newly selected move.
MachinePlayer: Subclass of GamePlayer which simply overloads the makeMove() function to allow an artificial intelligence(a machine - specfically, the computer) to select a cell on the GameGrid as their move.
OptionsDialog: This is dialog that pops up when user clicks on Game -> Options. It is a modal dialog which means that the user must press ok or cancel before he/she can resume with the game. All of the two-player games use this dialog to allow the players to make choices in terms of who goes first, which player gets which game piece, and whether it will be a one or two player game. A one player game simply sets the other player to use a machine player. This class easily allows for removal of any of the default options by having a removePanel() method, which was used in Chomp, for example. For each individual option, we made use of the OptionPanel class, which is described below.
OptionPanel: This class is that provides a panel with radio buttons inside a titled border for the OptionsDialog class. Each radio button is a choice the user can pick.