A flattened Boolean array stores the whole board in entries, with cell at position
y*N + x. For the pictured board, its bottom-to-top rows are 111111 | 111100 | 111100 | 110000 | 000000 | 000000.A column-height array uses only entries: column contains exactly the cells below height . The pictured board is , with 16 occupied cells. The alternatives are essentially different because the first stores every cell and the second encodes the boundary of an order ideal.
For the flattened Boolean array, the abstraction function selects the true cells. A suitable representation invariant requires the occupied cells to form a order ideal: an occupied implies occupancy of every with . Deleting a northeast rectangle preserves that property.
For column heights, the invariant is , and the abstraction function is . A legal move replaces every for by , preserving the invariant. This is an order-ideal board representation.
For a fair comparison, allow either representation a scalar cached token count:
Count and Legal then take in both. Without that cache, counting needs Boolean inspections versus height additions. A move takes in the worst case in the flattened representation and with heights. Displaying the full square takes character positions in either. Heights use array entries rather than , so they are superior for the requested operations; their initialization also takes only time.class Chomps(N: Int) {
require(N > 0 && N.toLong * N.toLong <= Int.MaxValue)
private val height = Array.fill(N)(N)
private var remaining = (N.toLong * N.toLong).toInt
def Count(): Int = remaining
def Legal(x: Int, y: Int): Boolean =
0 <= x && x < N && 0 <= y && y < height(x)
def Move(x: Int, y: Int): Unit = {
require(Legal(x, y), "illegal move")
var i = x
while (i < N && height(i) > y) {
remaining -= height(i) - y
height(i) = y
i += 1
}
}
def Display(): Unit = {
require(N < 10, "single-digit display labels required")
var y = N - 1
while (y >= 0) {
print(y + " ")
var x = 0
while (x < N) {
print(if (y < height(x)) "* " else " ")
x += 1
}
println()
y -= 1
}
print(" ")
var x = 0
while (x < N) { print(x + " "); x += 1 }
println()
}
}The strengthened representation invariant includes . The move loop's processed columns have their final truncated heights, unprocessed columns retain their old heights, and each subtraction removes exactly the corresponding height reduction. Because heights are nonincreasing, once a column is no higher than
y, neither is any later column; stopping there is valid. A legal move removes at least one token and leaves a order ideal.The constructor computes in
Long and checks representability before converting, avoiding integer overflow in the cached Int count. Legal rejects out-of-range coordinates before indexing. The methods meet the bounds in part (a); Display additionally checks its stated single-digit-label assumption.Retry invalid inputs without switching players; after a legal move, check whether the board has become empty:
val game = new Chomps(6)
var player = 1
while (game.Count() > 0) {
game.Display()
var accepted = false
var x = 0
var y = 0
while (!accepted) {
try {
println("Player " + player + ": enter x, then y")
x = scala.io.StdIn.readInt()
y = scala.io.StdIn.readInt()
accepted = game.Legal(x, y)
if (!accepted) println("That cell is not a legal move; try again.")
} catch {
case _: NumberFormatException => println("Please enter integer coordinates.")
}
}
game.Move(x, y)
if (game.Count() == 0)
println("Player " + player + " loses; player " + (3 - player) + " wins.")
else player = 3 - player
}Only a move at can remove the poisoned bottom-left token, and that move removes the entire remaining order ideal, so the empty-board test identifies the losing player correctly. Any other legal move preserves that token. Every accepted move strictly decreases the finite token count, giving at most 36 accepted moves. Invalid attempts do not change the board or the turn. As usual for this interactive fragment, the user is assumed eventually to supply another readable input rather than permanently ending the input stream or retrying forever.
Articles by others on the same topic
There are currently no matching articles.