Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Lab 11 -

...

Introduction

The game of Tic-Tac-Toe is a often used as an example for two-person games where each player takes turn to place a game piece on a two dimensional game board. The discussion of such a game serves to illustrate the notion game trees, and in particular, the min-max principle: how to compute the values of the game tree nodes and select the best next move. Even though these algorithms are initially described in high-level abstract terms, this is not reflected in their standard implementations.

Traditional Tic-Tac-Toe code from

  • Sun Microsystems
  • Mark Allen Weiss in "Data Structures & Problem Solving Using Java, 2nd ed." Addison Wesley 2001.

As one can see from the above examples of Tic-Tac-Toe code, the algorithm code is intertwined with low-level game board implementation details. This not only obscures the essence of the solution, but also makes it impossible to re-use in different types of games. What essentially "throw-away" code.

To remedy this situation, we seek to design an OO model for two-person games that enables us to express all facets of the games at the highest level of abstraction. The result is a system of cooperating objects that possesses many of the essential qualities in software engineering: correctness, robustness, extensibility, flexibility, and reusability. The demo program below shows the what an OO game model can do.

The Best Little Tic-Tac-Toe Game in Texas

As one can see from the demo, the program is not really about playing Tic-Tac-Toe but is about using a 2-pesron game design as a vehicle to learn BIGGER concepts in computing:

  • Abstraction
  • Design Process
  • Fundamental Principles.

When a player wants to put his/her game piece at some position on the board, it needs a way to communicate to

  • the board model to make such a move,
  • the turn control manager for it to set up the next turn (halt the game, skip a turn, or proceed as usual),
  • the view administrator to announce the winner or the loser or a draw in case the game is in its final state.

To avoid such complicated maneuvering, the player simply talks to an interface called IRequestor passing it the appropriate information and let this requester do the rest of the work internally. In the design pattern language, IRequestor plays the role of a "facade".

Here are some relevant excerpts of code that illustrate how the player interacts with the IRequestor .

Code Block

/** This class represents an abstract player of the game.
  * The players are represented as a circular linked list.
  */

public abstract class APlayer {
  private IRequestor requestor;
  private int player;
  private APlayer nextPlayer = this;

  /** The constructor for the class.
    * @param requestor The  requestor that the player uses to request moves of the board.
    * @param player The player number of this player.
    */
  public APlayer(IRequestor requestor, int player)  {
    this.requestor = requestor;
    this.player = player;
  }
  /** Tells this player to take its turn. */
  public abstract void takeTurn();

  /** other methods elided... */
}

/* This class represents a concrete computer player. */
public class ComputerPlayer extends APlayer {

  private INextMoveStrategy nextMoveStrategy;

  private IModel model;

  /** The constructor for the class.
    * @param requestor The requestor that this player will use to communicate with the model.
    * @param player This player's player number.
    * @param model A reference to the IModel.  Needed to call the getNextMove() method
    * of the INextMoveStrategy.
    * @param nextMoveStrategy The strategy used to calculate this player's next move.
    */

  public ComputerPlayer(IRequestor requestor, int player, IModel model, INextMoveStrategy nextMoveStrategy) {

    super(requestor, player);
    this.model = model;
    this.nextMoveStrategy = nextMoveStrategy;
    System.out.println("ComputerPlayer is using " + nextMoveStrategy);
  }

  /** Used by the TurnControl to tell this player to take its turn.
    * When the computer takes its turn, it will calculate its next move and
    * then request that move of  the model.
    * If the computer makes and invalid move, the computer will print an error
    * message to the screen and then  it will take its turn again.
    */
  public void takeTurn()  {
    System.out.print("Computer player "+ getPlayer() +" ("+ this +") takes turn...");
    final Point p = nextMoveStrategy.getNextMove(model, getPlayer());
    System.out.println("and moves to "+ p);
    getRequestor().setTokenAt (p.y, p.x, getPlayer(), new IRejectCommand() {
      // ANONYMOUS COMMAND!
      public void execute() {
        System.out.println("ComputerPlayer: The move at ("+p.x+", "+p.y+") is invalid.");
        takeTurn();
      }
    });
  }
}

When a player is instantiated, it is given an IRequestor to communicate with the model. As shown in ComputerPlayer.takeTurn() , the computer player gets the best next move from the next move strategy and then asks the requestor ( getRequestor() ) to set the game token at the best move's position, passing to the requestor an anonymous IRejectCommand , in case the move is illegal. This situation may happen if the next move strategy is a totally random move.

The IRejectCommand has only one method called execute()

Code Block

/** A basic command that is executed when a move request has been rejected by the model.
  */
public interface IRejectCommand {
    /** This method is called by the model when a move is rejected. */
    public abstract void execute();
}

The IRequestor has only one method called setTokenAt(...)

Code Block

/** This interface encapsulates the request mechanism used by an APlayer to try
  * to make a move to a given (row, col) by a given player.
  * This is the public (client) end of a "Facade" design pattern that hides the
  * internal workings of the model.
  */
public interface IRequestor {
    /** Requests to the model that a given player's token be placed on the
      * internal board at (row, col).
      *  An IRejectCommand is supplied that should be executed if the request is rejected.
      */
    public abstract void setTokenAt(int row, int col, int player, IRejectCommand rejectCommand);
}

A player gets its requestor at construction time. Who creates the concrete requestor and passes it to the player? The GameModel via its getPlayers() method! The GameModel has access to the board model, the turn control manager, and the view administrator. By creating an IRequestor as an anonymous inner class, the GameModel can thus set up the appropriate context for a player to make a request for a move without knowing anything about the board, the view, and the turn control!

In this lab you are to work with others to try to implement the IRequestor anonymous inner class that is stubbed out the GameModel code. This is part of the upcoming homework 11.

Download and extract the file hw11_12.zip . You should see the following files.

src : a subdirectory containing the source code of the Games program with a few stubbed out code for you to complete.
games.xml : a DrJava project file
P4M2.jar : the Java binary code of the game framework.
tttboard.jar : the Java binary code for the Tic-Tac-Toe gameboard.

...

Generic Mutable Lists and Bi-Lists

Introduction

Today's lab consists of writing visitors for the generic lists and generic bi-directional lists.  This will help you write code for HW10.

Download the code lab11.zip from the course OWLSPACE Resources page.

Lab Exercises

...

Access Permissions: (Please don't edit)

...