The Model
CS 3500, Fall 2019
Structuring a GUI
Model–View–Controller
A common OO technique for structuring graphical programs:
Controller: takes input from the user and decides what to do
View: knows how to display the interface to the user
Model: the domain information that the user manipulates
The Model
Often enforces information integrity
For games, this means enforcing the rules
It's where the action is!
It's where we'll start
Tic-tac-toe
An example game
│ │
─┼─┼─
│ │
─┼─┼─
│ │
An example game
│ │ X│ │
─┼─┼─ ─┼─┼─
│ │ │ │
─┼─┼─ ─┼─┼─
│ │ │ │
An example game
│ │ X│ │ X│ │O
─┼─┼─ ─┼─┼─ ─┼─┼─
│ │ │ │ │ │
─┼─┼─ ─┼─┼─ ─┼─┼─
│ │ │ │ │ │
An example game
│ │ X│ │ X│ │O X│ │O
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│ │ │ │ │ │ │ │
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│ │ │ │ │ │ │ │X
An example game
│ │ X│ │ X│ │O X│ │O
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│ │ │ │ │ │ │ │
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│ │ │ │ │ │ │ │X
X│ │O
─┼─┼─
│O│
─┼─┼─
│ │X
An example game
│ │ X│ │ X│ │O X│ │O
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│ │ │ │ │ │ │ │
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│ │ │ │ │ │ │ │X
X│ │O X│ │O
─┼─┼─ ─┼─┼─
│O│ │O│
─┼─┼─ ─┼─┼─
│ │X X│ │X
An example game
│ │ X│ │ X│ │O X│ │O
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│ │ │ │ │ │ │ │
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│ │ │ │ │ │ │ │X
X│ │O X│ │O X│ │O
─┼─┼─ ─┼─┼─ ─┼─┼─
│O│ │O│ │O│
─┼─┼─ ─┼─┼─ ─┼─┼─
│ │X X│ │X X│O│X
An example game: X wins!
│ │ X│ │ X│ │O X│ │O
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│ │ │ │ │ │ │ │
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│ │ │ │ │ │ │ │X
X│ │O X│ │O X│ │O X│ │O
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│O│ │O│ │O│ X│O│
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│ │X X│ │X X│O│X X│O│X
Another example: stalemate
│ │ X│ │ X│ │ X│ │ X│ │
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│ │ │ │ │O│ │O│ │O│O
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│ │ │ │ │ │ │ │X │ │X
X│ │ X│ │ X│ │X X│O│X X│O│X
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
X│O│O X│O│O X│O│O X│O│O X│O│O
─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─ ─┼─┼─
│ │X O│ │X O│ │X O│ │X O│X│X
Object-oriented analysis
What does our Tic-tac-toe model need to do?
Play a move as X
Play a move as O
Find out whose turn it is
Find out the contents of the grid
Find out whether the game is over
Find out who the winner is (if any)
More analysis: error conditions
Play out of turn
Play in an occupied cell
Play after the game has ended
Play in a cell that doesn't exist
Designing an interface: moves
/**
* Places an X mark in the specified cell.
*
* @param column the column of the cell
* @param row the row of the cell
* @throws IllegalStateException
* if it's O's turn, if the game is over, or if there
* is already a mark in the cell
* @throws IndexOutOfBoundsException
* if the cell is out of bounds
*/
void moveAsX(int column, int row);
/** [mutatis mutandis] */
void moveAsY(int column, int row);
Designing an interface: moves
/**
* Places an X or O mark in the specified cell. Whether it
* places an X or O depends on which player's turn it is.
*
* @param column the column of the cell
* @param row the row of the cell
* @throws IllegalStateException
* if the game is over, or if there's already a mark
* in the cell
* @throws IndexOutOfBoundsException
* if the cell is out of bounds
*/
void move(int column, int row);
Interface: whose turn?
/**
* Determines whether it's player X's turn to move.
*
* @return whether X can play now
*/
boolean isXsTurn();
/** [mutatis mutandis] */
boolean isYsTurn();
Interface: whose turn?
/**
* Returns the player whose turn is next.
*
* @return the next player
* @throws IllegalStateException if the game is over
*/
❓ nextPlayer();
Representing players
type
char
, with'X'
for X and'O'
for Oclass
String
, with"X"
for X and"O"
for Otype
boolean
, withtrue
for X andfalse
for Otype
boolean
, withtrue
for O andfalse
for Xan enumeration defined as
enum Player { X, O }
Interface: the grid
/**
* Returns a two-dimensional array representing the
* state of the grid. The first index is the column
* and the second the row. The resulting array is newly
* allocated and unconnected to the model; thus, mutat-
* ing it will have no effect on the model or subequent
* calls to {@code getGrid}.
*
* @return a copy of the grid
*/
Player[][] getGrid();
Interface: the grid
/**
* Returns the {@link Player} whose mark is in the cell at the given
* coordinates, or {@code null} if that cell is empty.
*
* @param column the column of the cell (zero-based)
* @param row the row of the cell (zero-based)
* @return the {@code Player} or {@code null}
* @throws IndexOutOfBoundsException
* if the cell is out of bounds
*/
Player getMarkAt(int column, int row);
Interface: the results
/**
* Determines whether the game is over.
*
* @return whether the game is over
*/
boolean isGameOver();
/**
* Returns the winner of the game, or {@code null} if
* the game is a tie.
*
* @return the winner or {@code null}
* @throws IllegalStateException if the game isn't over
*/
Player getWinner();
Connect $N$
A generalization of Connect Four
Generalize:
the grid dimensions
the goal (line length to win)
the number of players
Example: 2-player, 3-by-4 Connect Three
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │W│ │ │ │W│ │
│ │ │ │ │W│ │ │ │W│R│ │ │W│R│ │ │W│R│R│
└─┴─┴─┘ └─┴─┴─┘ └─┴─┴─┘ └─┴─┴─┘ └─┴─┴─┘
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │W│
│W│W│ │ │W│W│ │ │W│W│R│ │W│W│R│
│W│R│R│ │W│R│R│ │W│R│R│ │W│R│R│
└─┴─┴─┘ └─┴─┴─┘ └─┴─┴─┘ └─┴─┴─┘
Object-oriented analysis
Play a move
Find out about the configuration parameters:
dimensions
goal line length
players
Find out about the state of the game:
whose turn
grid contents
whether the game is over
who the winner is (if any)
Moving
Moving
/**
* Plays a move. Given the player and the column number,
* attempts to add a token for that player to that column.
*
* @param who the player who is moving (must be current)
* @param where which column to play in (0-based from left)
* @return the row where the token ends up
*
* @throws IllegalStateException
* if the game is over
* @throws IllegalStateException
* if it isn't {@code who}'s turn
* @throws IllegalStateException
* if the requested column is full
* @throws IndexOutOfBoundsException
* if the requested column does not exist
*/
int move(int who, int where);
Querying the configuration
int getWidth();
int getHeight();
int getGoal();
String[] getPlayers();
The game status
The game is always in one of three different statuses:
Playing, when the game isn't over yet
Stalemate, when the game has ended in a tie
Won, when the game has ended with a winner
Representing the status
enum Status { Playing, Stalemate, Won }
There are exactly three values of type Status
:
Status.Playing
Status.Stalemate
Status.Won
Getting the status
Status getStatus();
/**
* Determines whether the game is over.
*
* @return whether the game is over
*/
boolean isGameOver();
Getting the winner
/**
* Returns the winner of the game.
*
* <p><strong>PRECONDITION:</strong>
* the game is over and has a winner
*
* @return the winner
* @throws IllegalStateException
* if {@code getStatus() != Status.Won}
*/
int getWinner();
Observing the grid
/**
* Gets the player whose token is at the given column
* and row. The coordinates are zero-based and start
* in the lower left. Returns {@code null} if there is
* no token in the given position.
*
* @param x the column coordinate ({@code 0 <= x < width})
* @param y the row coordinate ({@code 0 <= y < height})
* @return the player in the given position, or {@code null}
* @throws IndexOutOfBoundsException if (x, y) is oob
*/
Integer getPlayerAt(int x, int y);