Assignment 5
This commit is contained in:
parent
13bf15cbf7
commit
68d2319224
6 changed files with 326 additions and 3 deletions
|
@ -27,12 +27,17 @@ tasks.named<Test>("test") {
|
|||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
tasks.register<JavaExec>("Assignment3") {
|
||||
classpath = sourceSets.main.get().runtimeClasspath
|
||||
main = "com.anthonycicchetti.cs5004.assignment3.Main"
|
||||
}
|
||||
|
||||
tasks.register<JavaExec>("Assignment4") {
|
||||
classpath = sourceSets.main.get().runtimeClasspath
|
||||
main = "com.anthonycicchetti.cs5004.assignment4.Main"
|
||||
}
|
||||
|
||||
tasks.register<JavaExec>("Assignment3") {
|
||||
tasks.register<JavaExec>("Assignment5") {
|
||||
classpath = sourceSets.main.get().runtimeClasspath
|
||||
main = "com.anthonycicchetti.cs5004.assignment3.Main"
|
||||
main = "com.anthonycicchetti.cs5004.assignment5.Main"
|
||||
}
|
130
src/main/kotlin/com/anthonycicchetti/cs5004/assignment5/Board.kt
Normal file
130
src/main/kotlin/com/anthonycicchetti/cs5004/assignment5/Board.kt
Normal file
|
@ -0,0 +1,130 @@
|
|||
package com.anthonycicchetti.cs5004.assignment5
|
||||
|
||||
import com.anthonycicchetti.cs5004.assignment5.model.Tile
|
||||
import java.lang.IllegalArgumentException
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
class Board() : MarbleSolitaireModel {
|
||||
val backingState: MutableList<Tile>
|
||||
|
||||
private fun invalidBoardTile(row: Int, column: Int): Boolean {
|
||||
return ((row <= 1 || row >= 5)
|
||||
&& (column <= 1 || column >= 5))
|
||||
}
|
||||
|
||||
constructor(emptyRow: Int, emptyColumn: Int) : this() {
|
||||
if (!invalidBoardTile(emptyRow, emptyColumn)) {
|
||||
this.backingState.remove(Tile(3, 3, false))
|
||||
this.backingState.add(Tile(3, 3, true))
|
||||
this.backingState.remove(Tile(emptyRow, emptyColumn, true))
|
||||
this.backingState.add(Tile(emptyRow, emptyColumn, false))
|
||||
} else {
|
||||
throw IllegalArgumentException("Invalid empty cell position ($emptyRow, $emptyColumn)")
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val eventualBackingState = mutableListOf<Tile>()
|
||||
for (column in 0..6) {
|
||||
for (row in 0..6) {
|
||||
if (invalidBoardTile(row, column)) {
|
||||
eventualBackingState.add(Tile(row, column, null))
|
||||
} else {
|
||||
eventualBackingState.add(Tile(row, column, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (eventualBackingState.contains(Tile(3, 3, true))) {
|
||||
eventualBackingState.remove(Tile(3, 3, true))
|
||||
eventualBackingState.add(Tile(3, 3, false))
|
||||
}
|
||||
backingState = eventualBackingState.sorted().toMutableList()
|
||||
}
|
||||
|
||||
private fun tileIsOccupied(row: Int, column: Int): Boolean {
|
||||
return backingState.contains(Tile(row, column, true))
|
||||
}
|
||||
|
||||
private fun getTileAt(row: Int, column: Int): Tile {
|
||||
return backingState.filter { it.row == row && it.column == column }[0]
|
||||
}
|
||||
|
||||
private fun validMove(fromRow: Int, fromCol: Int, toRow: Int, toCol: Int): Boolean {
|
||||
if ((fromRow - toRow).absoluteValue != 2 && (fromCol - toCol == 0)
|
||||
|| (fromCol - toCol).absoluteValue != 2 && (fromRow - toRow == 0)) {
|
||||
return false
|
||||
}
|
||||
if (invalidBoardTile(fromRow, fromCol) || invalidBoardTile(toRow, toCol)) {
|
||||
return false
|
||||
}
|
||||
val avgRow = (fromRow + toRow) / 2
|
||||
val avgCol = (fromCol + toCol) / 2
|
||||
if (tileIsOccupied(avgRow, avgCol)
|
||||
&& !tileIsOccupied(toRow, toCol)
|
||||
&& tileIsOccupied(fromRow, fromCol)){
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun move(fromRow: Int, fromCol: Int, toRow: Int, toCol: Int) {
|
||||
if (validMove(fromRow, fromCol, toRow, toCol)) {
|
||||
val avgRow = (fromRow + toRow) / 2
|
||||
val avgCol = (fromCol + toCol) / 2
|
||||
|
||||
backingState.remove(Tile(fromRow, fromCol, true))
|
||||
backingState.add(Tile(fromRow, fromCol, false))
|
||||
backingState.remove(Tile(avgRow, avgCol, true))
|
||||
backingState.add(Tile(avgRow, avgCol, false))
|
||||
backingState.remove(Tile(toRow, toCol, false))
|
||||
backingState.add(Tile(toRow, toCol, true))
|
||||
}
|
||||
|
||||
backingState.sort()
|
||||
}
|
||||
|
||||
override fun isGameOver(): Boolean {
|
||||
if (getScore() == 1) {
|
||||
return true
|
||||
} else {
|
||||
for (tile in backingState) {
|
||||
val localRow = tile.row
|
||||
val localCol = tile.column
|
||||
val plusRow = localRow + 2
|
||||
val plusCol = tile.column + 2
|
||||
val minusRow = localRow - 2
|
||||
val minusCol = tile.column - 2
|
||||
if (validMove(localRow, localCol, plusRow, localCol)
|
||||
|| validMove(localRow, localCol, localRow, plusCol)
|
||||
|| validMove(localRow, localCol, minusRow, localCol)
|
||||
|| validMove(localRow, localCol, localRow, minusCol)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getGameState(): String {
|
||||
val sb = StringBuilder()
|
||||
var rowBuilder = StringBuilder()
|
||||
|
||||
for (row in 0..6) {
|
||||
backingState.filter { it.row == row }.sorted().forEach {
|
||||
rowBuilder.append(
|
||||
when (it.occupied) {
|
||||
true -> "O"
|
||||
false -> "X"
|
||||
null -> " "
|
||||
}
|
||||
).append(" ")
|
||||
}
|
||||
sb.append(rowBuilder.toString()).append("\n")
|
||||
rowBuilder = StringBuilder()
|
||||
}
|
||||
return sb.toString().trimEnd('\n')
|
||||
}
|
||||
|
||||
override fun getScore(): Int = backingState.count { it.occupied ?: false }
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.anthonycicchetti.cs5004.assignment5
|
||||
|
||||
object Main {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
val boardOne = Board()
|
||||
println(boardOne.getGameState())
|
||||
|
||||
val boardTwo = Board(2, 3)
|
||||
println(boardTwo.getGameState())
|
||||
|
||||
val board = Board()
|
||||
board.move(3,1, 3,3)
|
||||
|
||||
println(board.getGameState())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.anthonycicchetti.cs5004.assignment5
|
||||
|
||||
/**
|
||||
* This interface represents the operations offered by the marble solitaire
|
||||
* model. One object of the model represents one game of marble solitaire
|
||||
*/
|
||||
public interface MarbleSolitaireModel {
|
||||
/**
|
||||
* Move a single marble from a given position to another given position.
|
||||
* A move is valid only if the from and to positions are valid. Specific
|
||||
* implementations may place additional constraints on the validity of a move.
|
||||
* @param fromRow the row number of the position to be moved from
|
||||
* (starts at 0)
|
||||
* @param fromCol the column number of the position to be moved from
|
||||
* (starts at 0)
|
||||
* @param toRow the row number of the position to be moved to
|
||||
* (starts at 0)
|
||||
* @param toCol the column number of the position to be moved to
|
||||
* (starts at 0)
|
||||
* @throws IllegalArgumentException if the move is not possible
|
||||
*/
|
||||
fun move(fromRow: Int, fromCol: Int, toRow: Int, toCol: Int): Unit
|
||||
|
||||
/**
|
||||
* Determine and return if the game is over or not. A game is over if no
|
||||
* more moves can be made.
|
||||
* @return true if the game is over, false otherwise
|
||||
*/
|
||||
fun isGameOver(): Boolean
|
||||
|
||||
/**
|
||||
* Return a string that represents the current state of the board. The
|
||||
* string should have one line per row of the game board. Each slot on the
|
||||
* game board is a single character (O, X or space for a marble, empty and
|
||||
* invalid position respectively). Slots in a row should be separated by a
|
||||
* space. Each row has no space before the first slot and after the last slot.
|
||||
* @return the game state as a string
|
||||
*/
|
||||
fun getGameState(): String;
|
||||
|
||||
/**
|
||||
* Return the number of marbles currently on the board.
|
||||
* @return the number of marbles currently on the board
|
||||
*/
|
||||
fun getScore(): Int;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.anthonycicchetti.cs5004.assignment5.model
|
||||
|
||||
data class Tile(val row: Int, val column: Int, val occupied: Boolean?): Comparable<Tile> {
|
||||
override fun compareTo(other: Tile): Int {
|
||||
return when {
|
||||
this.row < other.row -> return -1
|
||||
this.row == other.row -> when {
|
||||
this.column < other.column -> return -1
|
||||
this.column == other.column -> return 0
|
||||
this.column > other.column -> return 1
|
||||
else -> 0
|
||||
}
|
||||
this.row > other.row -> return 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package com.anthonycicchetti.cs5004.assignment5
|
||||
|
||||
import com.anthonycicchetti.cs5004.assignment5.Board
|
||||
import com.anthonycicchetti.cs5004.assignment5.model.Tile
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
|
||||
internal class BoardTest {
|
||||
@Test
|
||||
fun `A Valid Board is Created By Default`() {
|
||||
val board = Board()
|
||||
val expected =
|
||||
""" O O O
|
||||
O O O
|
||||
O O O O O O O
|
||||
O O O X O O O
|
||||
O O O O O O O
|
||||
O O O
|
||||
O O O """
|
||||
|
||||
assertEquals(expected, board.getGameState())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `A Valid Board is Created With a Different Empty Hole`() {
|
||||
val board = Board(2, 3)
|
||||
|
||||
assertFalse(board.backingState.contains(Tile(2, 3, true)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `A Different-Holed Board is Printed Correctly`() {
|
||||
val board = Board(2,3)
|
||||
val expected =
|
||||
""" O O O
|
||||
O O O
|
||||
O O O X O O O
|
||||
O O O O O O O
|
||||
O O O O O O O
|
||||
O O O
|
||||
O O O """
|
||||
|
||||
assertEquals(expected, board.getGameState())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Board is printed correctly after a move`() {
|
||||
val board = Board()
|
||||
val movedBoard =
|
||||
""" O O O
|
||||
O O O
|
||||
O O O O O O O
|
||||
O X X O O O O
|
||||
O O O O O O O
|
||||
O O O
|
||||
O O O """
|
||||
|
||||
board.move(3,1,3,3)
|
||||
assertEquals(movedBoard, board.getGameState())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Score is counted correctly on new board`() {
|
||||
val board = Board()
|
||||
|
||||
assertEquals(32, board.getScore())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Score is counted correct on new non-default board`() {
|
||||
val board = Board(1,4)
|
||||
|
||||
assertEquals(32, board.getScore())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Score is counted correctly on board with missing marbles`() {
|
||||
val board = Board()
|
||||
|
||||
board.move(3, 1, 3, 3)
|
||||
assertEquals(31, board.getScore())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Game is not over when game is started`() {
|
||||
val board = Board()
|
||||
|
||||
assertFalse(board.isGameOver())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Game is not over when game is started with a nonstandard board`() {
|
||||
val board = Board(2,3)
|
||||
|
||||
assertFalse(board.isGameOver())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Game is not over after one move`() {
|
||||
val board = Board()
|
||||
|
||||
board.move(3, 1, 3, 3)
|
||||
assertFalse(board.isGameOver())
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue