package cs3500.connectn.model; import java.util.Set; /** * An abstract Connect N game, which is a generalization of Connect * Four. The state of a Connect N game must keep track of the current * player and the arrangement of tokens in the grid, and this interface provides * methods for manipulating and observing that state while enforcing the rules * of the game and the integrity of the representation. * *

This interface also includes static factory methods for creating {@code * Model}s and {@link Model.Builder}s. * *

Impl. note: This interfaces responsibilities are limited to the core * domain of the game itself. Presentation code, of course, does not belong here * in the model. */ public interface Model { /** The default width of the grid. */ int DEFAULT_WIDTH = 7; /** The default height of the grid. */ int DEFAULT_HEIGHT = 6; /** The default length of the line needed to win. */ int DEFAULT_GOAL = 4; /** * Constructs a new game model for playing Connect Four with the default * parameters. * * @return the new game model * @see #connectN(int, int, int) * @see #builder() */ static Model connectFour() { return builder().build(); } /** * Constructs a new game model with the given parameters. * * @param width the width of the grid (positive) * @param height the height of the grid (positive) * @param goal the goal line length for the game ({@code > 1}) * @return the new game model * * @see #connectFour() * @see #builder() */ static Model connectN(int width, int height, int goal) { return builder() .width(width) .height(height) .goal(goal) .build(); } /** * Constructs a builder for configuring and then creating a {@code Model} * instance. Defaults to a game of Connect Four with a 7-by-6 grid. * * @return the new builder */ static Builder builder() { return new ModelImpl.BuilderImpl(); } /** * Gets the width of the game. * * @return the width */ int getWidth(); /** * Gets the height of the game. * * @return the height */ int getHeight(); /** * Gets the goal of the game. The goal is the line length required to win, * e.g., 4 for Connect Four. * * @return the goal */ int getGoal(); /** * Represents the status of the game. */ enum Status { /** * The game is ongoing. */ Playing, /** * The game has ended in a stalemate. */ Stalemate, /** * The game has ended with a winner. * * @see #getWinner() */ Won; } /** * Gets the current status of the game. * * @return the status */ Status getStatus(); /** * Determines whether the game is over. * * @return whether the game is over */ default boolean isGameOver() { return getStatus() != Status.Playing; } /** * Gets the player whose turn it is now. * *

PRECONDITION: the game isn't over * * @return the next player * @throws IllegalStateException if {@code isGameOver()} */ Player getNextPlayer(); /** * Returns the winner of the game. * *

PRECONDITION: the game is over and has a winner * * @return the winner * @throws IllegalStateException if {@code getStatus() != Status.Won} */ Player getWinner(); /** * Returns the set of positions in the grid forming the winning line. * *

PRECONDITION: the game is over and has a winner * * @return the set of positions * @throws IllegalStateException if {@code getStatus() != Status.Won} */ Set getWinningPositions(); /** * 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 out of bounds */ Player getPlayerAt(int x, int y); /** * Determines whether the specified column is full and thus cannot be played * in. * * @param which the column to check * @return whether column {@code which} is full * @throws IndexOutOfBoundsException if {@code which} is out of bounds */ default boolean isColumnFull(int which) { return getColumnSize(which) == getHeight(); } /** * Determines the current size of a column, meaning how many tokens are in * it. If the column isn't full, then this is the row index of the first * empty cell in the given column. * * @param which the column * @return the column's size * @throws IndexOutOfBoundsException if {@code which} is out of bounds */ int getColumnSize(int which); /** * Plays a move. Given the player (whose turn it must be) and the column * number (zero-based from the left), attempts to add that player to that * column. If this move ends the game then the game state is updated to * reflect that. * *

The {@code IllegalMoveException}s below are thrown for user errors, * whereas the {@code IllegalStateException}s are thrown for (client) * programmer errors. * * @param who the player who is moving * @param where which column to play in * @return the height of the column after playing * * @throws ColumnFullException if the requested column is full * @throws ColumnOutOfBoundsException if the requested column does not exist * @throws IllegalStateException if the game is over * @throws IllegalStateException if it isn't {@code who}'s turn */ int move(Player who, int where) throws IllegalMoveException; /** * Indicates that the user attempted an illegal move. * * @see ColumnFullException * @see ColumnOutOfBoundsException */ class IllegalMoveException extends Exception {} /** * Indicates that the user attempted to play in a column that was already * full. */ class ColumnFullException extends IllegalMoveException { /** * Constructs a new {@code ColumnFullException} for the given column. * * @param column the number of the full column */ public ColumnFullException(int column) { this.column = column; } public int getColumn() { return column; } private final int column; } /** * Indicates that the user attempted to play in a non-existent column. */ class ColumnOutOfBoundsException extends IllegalMoveException { /** * Constructs a new {@code ColumnOutOfBoundsException} parametrized by the * given highest available column number. * * @param maxColumn the largest in-bounds column number */ public ColumnOutOfBoundsException(int maxColumn) { this.maxColumn = maxColumn; } public int getMaxColumn() { return maxColumn; } private final int maxColumn; } /** * Builds a {@link Model}, allowing the client to configure several * parameters. This is an instance of the builder pattern. */ interface Builder { /** * Builds and returns the specified {@link Model}. * @return a new {@code ConnectNModel} */ Model build(); /** * Sets the width of the game grid. * * @param width the width (positive) * @return {@code this}, for method chaining */ Builder width(int width); /** * Sets the height of the game grid. * * @param height the height (positive) * @return {@code this}, for method chaining */ Builder height(int height); /** * Sets the goal line length for the game. * * @param goal the goal (> 1) * @return {@code this}, for method chaining */ Builder goal(int goal); } }