CS 4773 Object Oriented Systems
Chutes and Ladders Design



Class: Spinner
Responsibilities:
   getSpin()
Collaborations:
   none

Class: Board
Responsibilities:
   getCellAt()
   checkWinningPosition()
Collaborations:
   Cell

Class: Player
Responsibilities:
   takeATurn()
   checkWon()
   X getPosition()
Collaborations:
   Spinner
   Board
   Cell

Class: Cell
Responsibilities:
   getDestination()
Collaborations:
   none
   

Class: Game
Responsibilities:
   X checkEnd()  ??
   X getCurrentPlayer()
   startPlaying();
   
Collaborations:
   Player

Implementation outline:
public class Board {

   private Cell cells[];

   public Board() {

      cells = new Cell[101];
      for (int i=1;i < =100;i++)
         cells[i] = new Cell(i);
      cells[1].setDestination(38);
      cells[4].setDestination(14);
      ...

   }

   public Cell getCellAt(int i) {
      if ((i < 1) || (i > 100) ) return null;
      return cells[i];
   }

   public boolean  checkWinningPosition(int i) {
      return (i==100);
   }
}
Need to add the following responsibility to Cell: setDestination();

public class Game {

   private Board board;
   private Player players[];
   private int numPlayers;
   private int currentPlayerIndex;
   private int moveCount;

   public Game(int numPlayers) {

      this.numPlayers = numPlayers;
      board = new Board();
      players = new Player[numPlayers];
      for (int i=0;i < numPlayers;i++)
         players[i] = new Player(board,"Player "+i);
   }

   public void startPlaying() {
      currentPlayerIndex = numPlayers;
      moveCount = 0;
      while( ! players[currentPlayerIndex].checkWinningPosition() ) {
         moveCount++;
         currentPlayerIndex = (currentPlayerIndex + 1) % numPlayers;
         players[currentPlayerIndex].takeATurn();
      }
      System.out.println("Player "+currentPlayerIndex+" won on "+
         moveCount+" moves");
   }
}
public class Player {

   private int currentPosition;
   private String name;
   private Cell newCell;
   private Board board;

   public Player(Board board, String name) {
      this.board = board;
      this.name = name;
      currentPosition = 0;
   }

   public void takeATurn() {
      newCell = board.getCellAt(currentPosition + Spinner.getSpin());
      if (newCell == null) return;
      currentPosition = newCell.getDestination();
   }

   public boolean checkWon() {
      return board.checkWon(currentPosition);
   }
}
Note: getSpin is a static method of the class Spinner.


Revisions to CRC cards after implementation:

Class: Spinner                   no change (getSpin made static)
Responsibilities: 
   getSpin()
Collaborations:
   none

Class: Board                     no change
Responsibilities:
   getCellAt()
   checkWinningPosition()
Collaborations:
   Cell
 
Class: Player                    Added responsibility: getName()
Responsibilities:
   takeATurn()
   checkWon()
   X getPosition()
Collaborations:
   Spinner
   Board
   Cell
 
Class: Cell                      Added responsibility: setDestination()
Responsibilities:
   getDestination()
Collaborations:
   none
 
Class: Game                     Added responsibilities: getMoves(), getWinner()
Responsibilities:               Added Collaboration:    Board
   X checkEnd()  ??
   X getCurrentPlayer()
   startPlaying();
Collaborations:
   Player


Test Program 1:

class Tester {

   public static void main(String args[]) {

      Game g;

      g = new Game(2);
      g.startPlaying();
      System.out.println("Player "+g.getWinner() + " won in "+
                         g.getMoves()+" moves.");
   }
}
Example output:
Player Player 1 won in 32 moves.


Test Program 2:

class TestMany {

   public static void main(String args[]) {

      Game g;
      int numGames;
      double sumMoves;
      double averageMoves;
      int numMoves;
      int minMoves;
      int maxMoves;
      long startTime;
      long endTime;
      long totalTime;

      if (args.length != 1) {
         System.out.println("Usage: TestMany n\n");
         return;
      }
      numGames = Integer.parseInt(args[0]);
      sumMoves = 0;
      numMoves = 0;
      minMoves = Integer.MAX_VALUE;
      maxMoves = 0;
      System.out.println("Starting simulation of "+numGames+" games:");
      startTime = System.currentTimeMillis();
      for (int i=0;i < numGames;i++) {
         g = new Game(2);
         g.startPlaying();
         numMoves = g.getMoves();
         if (minMoves > numMoves)
            minMoves = numMoves;
         if (maxMoves < numMoves)
            maxMoves = numMoves;
         sumMoves += numMoves;
      }
      endTime = System.currentTimeMillis();
      totalTime = endTime - startTime;
      averageMoves = sumMoves/numGames;
      System.out.println("Statistics generated after "+numGames+ " games:");
      System.out.println("Time for simulation: "+(totalTime/1000.0)+" seconds");
      System.out.println("Minimum number of moves is "+minMoves);
      System.out.println("Maximum number of moves is "+maxMoves);
      System.out.println("Average number of moves is "+averageMoves);

   }
}
Sample output:

java -version
java version "1.2.1"
Solaris VM (build Solaris_JDK_1.2.1_04, native threads, sunwjit)

java TestMany 1000000
Starting simulation of 1000000 games:
Statistics generated after 1000000 games:
Time for simulation: 143.602 seconds
Minimum number of moves is 13
Maximum number of moves is 411
Average number of moves is 52.460248

Here is what happens if you turn off the just-in-time compiler:

setenv JAVA_COMPILER NONE
java -version
java version "1.2.1"
Solaris VM (build Solaris_JDK_1.2.1_04, native threads, nojit)

java TestMany 1000000
Starting simulation of 1000000 games:
Statistics generated after 1000000 games:
Time for simulation: 1587.89 seconds
Minimum number of moves is 13
Maximum number of moves is 335
Average number of moves is 52.371593
This takes 11 times as long.