1 Purpose
The primary goal of this assignment is to practice thinking carefully about invariants, including
identifying invariants in existing code,
designing invariants for new code, and
writing new code with the invariants that you design.
2 Invariants Warm-Up
This section of the assignment is about identifying class invariants and method preconditions.
2.1 Class DoubleVar
The class DoubleVar
implements a continuous statistical variable—you can think of it as an array of double
s that also knows how to compute some statistical functions such as average and standard deviation.1 The class relies on a simple but undocumented class invariant, and several methods have undocumented preconditions that determine when they will return a value rather than throw an exception. Your task is as follows:
Identify
DoubleVar
’s class invariant, and document it in the class’s Javadoc comment.For each method, identity and document (in Javadoc, of course) the preconditions under which the method will not throw an exception.
The class defines a public field, which is something that we have eschewed in the past. Is this a problem? Why or why not? Add a comment above the public field
count
explaining your position.
2.2 Class Lazy
The class Lazy
implements lazy evaluation, which is a strategy for computing a value just once, when it’s needed, and then saving it in case it’s needed again. (This is like what we did in the Square
class in lecture when we added the areaValid
field to avoid computing area
any more than strictly necessary.)
To construct a Lazy<T>
, there are two constructors available:
public Lazy(Supplier<T> thunk)
takes an object having one method,public T get()
, that when invoked will compute the deferred value. It storesthunk
so that itsget
method can be invoked when the value is first needed.Lazy(T value)
is a convenience2 constructor for when we already have the value on hand and don’t need to defer computing it. This constructor is not strictly necessary, since we could always use the previous constructor like so:new Lazy<T>(new Supplier<> () { public T get() { return value; } })
Or the same thing using Java 8’s lambda syntax:
new Lazy<T>(() -> value)
The Lazy
class relies on a class invariant for correctness. Your task is as follows:
Identify
Lazy
’s class invariant, and document it in the class’s Javadoc comment.
For several small examples of how Lazy
works, it may be helpful to see the LazyTest
class.
3 A Coin Game
In the rest of this problem set, you will design and implement a simple coin game (or rather, a family of coin games).
3.1 The Rules
The coin game is played on a “board” consisting of a strip of paper divided into a sequence of squares. (You can think of it as a one-dimensional grid of boxes.) Each square may be occupied by a single coin, or empty. For convenience, we can write down the state of a coin game using a simple notation of -
for empty squares andO
for squares containing a coin. For example:
----
is a board with four squares and no coinsO--O-
is a board of five squares, with coins in the 0th and 3rd positions. (We count from 0.)OOOOOOOO
is a board of eight squares, full with eight coins.
We can refer to the coins by numbering them from the left, starting from 0. So for example, in board configuration --O-O
, coin 0 is in position 2 and coin 1 is in position 4.
A move in the game consists of moving a coin from a square to an unoccupied square. (Two coins cannot be placed in the same square.) How coins are allowed to be moved depends on the variant of the game. You will implement two variants:
In the strict coin game, a coin can be moved any number of squares to the left, but it cannot pass another coin. Thus, if the state of the board is
-O--O
, the following moves are valid:However, coin 1 cannot be moved past coin 0, so configuration
OO---
cannot be reached in only one move.Coin 0 can be moved to position 0, yielding
O---O
.Coin 1 can be moved to position 2, yielding
-OO--
.Coin 1 can be moved to position 3, yielding
-O-O-
.In the lax coin game, coins move to the left as in the strict coin game, but they can also skip past other coins. Thus, given configuration
-O--O
, the three moves valid in the strict coin game shown above are also valid in the lax coin game. But moving coin 1 to position 0, skipping over coin 0 and yielding configurationOO---
, is valid in the lax variant as well.
Game play proceeds by players taking turns, and ends when no move can be made. The last player to make a move wins.
3.2 The Interface
The interface CoinGame
declares the operations for a coin game implementation. It includes observer methods for finding out the number of squares on the board, the number of coins, the position of each coin, and whether the game is over. It has one mutator method, move(int coinIndex, int newPosition)
, for making moves. (It doesn’t keep track of whose turn it is or who wins.) The interface also defines a nested exception class IllegalMoveException
, which move
must throw when the requested move is illegal (as defined by whichever variant of the game is implemented).
3.3 Your Implementation
Your task is to implement CoinGame
in two classes, StrictCoinGame
and LaxCoinGame
, which enforce the strict and lax versions of the rules, respectively. What you need to do (for each class):
Carefully design a representation for the game state. Your choice of representation may significantly affect how easy or difficult it is to implement the necessary operations.
Implement all the methods in the interface. (Hint: You needn’t implement each method independently in each concrete class, if you can somehow factor out common functionality.)
Override the
toString()
method so that it returns the board represented in the-
-and-O
notation used to discuss board configurations above.Implement public constructors
StrictCoinGame(String board)
andLaxCoinGame(String board)
. These take a board written in-
-and-O
notation and initialize the board (however you choose to represent it) to match. Ifboard
contains any characters other than'-'
and'O'
, it does not represent a valid configuration, so the constructors raise anIllegalArgumentException
.Write sufficient tests to be confident that your code is correct.
Properly document your code, with Javadoc, as appropriate. Method implementations that inherit Javadoc need not provide their own unless their contract differs from the inherited documentation. Be sure to document all class invariants and preconditions.