diff --git a/domain/src/main/kotlin/ch/dissem/yaep/domain/Game.kt b/domain/src/main/kotlin/ch/dissem/yaep/domain/Game.kt index 1f59c17..d974df9 100644 --- a/domain/src/main/kotlin/ch/dissem/yaep/domain/Game.kt +++ b/domain/src/main/kotlin/ch/dissem/yaep/domain/Game.kt @@ -1,8 +1,45 @@ package ch.dissem.yaep.domain +import ch.dissem.yaep.domain.Animal.ANT +import ch.dissem.yaep.domain.Animal.DOG +import ch.dissem.yaep.domain.Animal.GOAT +import ch.dissem.yaep.domain.Animal.SLOTH +import ch.dissem.yaep.domain.Animal.SNAIL +import ch.dissem.yaep.domain.Animal.ZEBRA +import ch.dissem.yaep.domain.Dessert.CAKE +import ch.dissem.yaep.domain.Dessert.CUPCAKE +import ch.dissem.yaep.domain.Dessert.CUSTARD +import ch.dissem.yaep.domain.Dessert.DOUGHNUT +import ch.dissem.yaep.domain.Dessert.ICE_CREAM +import ch.dissem.yaep.domain.Dessert.PIE +import ch.dissem.yaep.domain.Drink.BEER +import ch.dissem.yaep.domain.Drink.BEVERAGE +import ch.dissem.yaep.domain.Drink.COFFEE +import ch.dissem.yaep.domain.Drink.MILK +import ch.dissem.yaep.domain.Drink.TEA +import ch.dissem.yaep.domain.Drink.WINE +import ch.dissem.yaep.domain.Fruit.BANANA +import ch.dissem.yaep.domain.Fruit.GRAPES +import ch.dissem.yaep.domain.Fruit.MANGO +import ch.dissem.yaep.domain.Fruit.PEAR +import ch.dissem.yaep.domain.Fruit.PINEAPPLE +import ch.dissem.yaep.domain.Fruit.WATERMELON +import ch.dissem.yaep.domain.Nationality.CANADA +import ch.dissem.yaep.domain.Nationality.ENGLAND +import ch.dissem.yaep.domain.Nationality.JAPAN +import ch.dissem.yaep.domain.Nationality.SPAIN +import ch.dissem.yaep.domain.Nationality.SWITZERLAND +import ch.dissem.yaep.domain.Nationality.UKRAINE +import ch.dissem.yaep.domain.Profession.ASTRONAUT +import ch.dissem.yaep.domain.Profession.HEALTH_WORKER +import ch.dissem.yaep.domain.Profession.ROCK_STAR +import ch.dissem.yaep.domain.Profession.SCIENTIST +import ch.dissem.yaep.domain.Profession.SOFTWARE_DEV +import ch.dissem.yaep.domain.Profession.TEACHER + class Game( val grid: Grid, - val clues: List + val clues: Collection ) { val horizontalClues = clues.filterIsInstance() val verticalClues = clues.filterIsInstance, ItemClass<*>>>() @@ -29,9 +66,61 @@ class Game( fun isValid(): Boolean = areCategoriesValid() && clues.all { it.isValid(grid) } - fun isSolved(): Boolean = grid.cells.all { it.selection != null } + val isSolved: Boolean + get() = grid.rows.all { it.isSolved } override fun toString(): String { return grid.toString() + "\n\n" + clues.joinToString("\n* ", prefix = "* ") } + + companion object { + fun parse(description: String): Game { + // TODO get options from string + val rowOptions = listOf>>( + listOf( + HEALTH_WORKER, + ROCK_STAR, + SCIENTIST, + SOFTWARE_DEV, + ASTRONAUT, + TEACHER + ).map { Item(it) }, + listOf(ANT, DOG, GOAT, SLOTH, SNAIL, ZEBRA).map { Item(it) }, + listOf(WATERMELON, MANGO, PEAR, GRAPES, PINEAPPLE, BANANA).map { Item(it) }, + listOf(CUPCAKE, ICE_CREAM, DOUGHNUT, CAKE, PIE, CUSTARD).map { Item(it) }, + listOf(SWITZERLAND, ENGLAND, JAPAN, UKRAINE, CANADA, SPAIN).map { Item(it) }, + listOf(WINE, BEVERAGE, BEER, COFFEE, TEA, MILK).map { Item(it) } + ) + val optionMap = rowOptions.flatten().associateBy( + keySelector = { it.itemType }, + valueTransform = { it } + ) + val clues: Collection = description.lines() + .filter { it.startsWith("* ") } + .map { line -> + Clue.parse(line) { + optionMap[it] ?: throw IllegalArgumentException("Unknown option $it") + } + } + + return Game( + grid = Grid( + rows = rowOptions + .map { options -> createRow>(options) } + .toList() + ), + clues = clues + ) + } + + private fun > createRow(options: List>): GameRow<*> { + @Suppress("UNCHECKED_CAST") + options as List> + return GameRow( + category = options.first().itemType.companion, + options = options, + cells = options.map { GameCell(options = options.toMutableList()) } + ) + } + } } diff --git a/domain/src/main/kotlin/ch/dissem/yaep/domain/GameCell.kt b/domain/src/main/kotlin/ch/dissem/yaep/domain/GameCell.kt index a6e73c4..9e2f723 100644 --- a/domain/src/main/kotlin/ch/dissem/yaep/domain/GameCell.kt +++ b/domain/src/main/kotlin/ch/dissem/yaep/domain/GameCell.kt @@ -1,8 +1,8 @@ package ch.dissem.yaep.domain class GameCell>( - selection: Item?, - val solution: Item, + selection: Item?=null, + val solution: Item? = null, options: Collection> ) { val selectionChangedListeners = mutableListOf<(Item?) -> Unit>() diff --git a/domain/src/main/kotlin/ch/dissem/yaep/domain/GameRow.kt b/domain/src/main/kotlin/ch/dissem/yaep/domain/GameRow.kt index 23d3893..93d3def 100644 --- a/domain/src/main/kotlin/ch/dissem/yaep/domain/GameRow.kt +++ b/domain/src/main/kotlin/ch/dissem/yaep/domain/GameRow.kt @@ -5,52 +5,53 @@ class GameRow>( val options: List>, val cells: List> ) : List> by cells { + var isSolved = false + private set + fun indexOf(element: C) = indexOfFirst { it.selection?.itemType == element } fun cleanupOptions() { - cells.forEach { - cleanupOptions(it) - } -// do { -// var selectedSingleOption = false -// cells.forEach { cleanupOptions(it) } -// val selections = cells.mapNotNull { it.selection } -// options -// .filter { !selections.contains(it) } -// .forEach { option -> -// if (cells.count { cell -> cell.options.contains(option) } == 1) { -// cells -// .filter { it.selection == null } -// .first { cell -> cell.options.contains(option) } -// .let { it.selection = option } -// selectedSingleOption = true -// } -// } -// } while (selectedSingleOption) - } - - private fun cleanupOptions(cell: GameCell, justSelected: Boolean = true) { - if ((justSelected && cell.selection != null) || (cell.options.size == 1 && cell.selection == null)) { - val selection = cell.selection - if (selection == null) { - cell.selection = cell.options.first() - } else { - cell.options.clear() - cell.options.add(selection) + if ( + isSolved && all { + it.solution != null + && it.options.size == 1 + && it.options.single() == it.selection } - filter { otherCell -> otherCell != cell && otherCell.hasNoSelection() }.forEach { otherCell -> - otherCell.options.remove(cell.selection) - cleanupOptions(otherCell, false) + ) return + do { + var changed = false + val selections = filter { it.selection != null }.let { cellsWithSelection -> + cellsWithSelection + .filter { it.options.size != 1 } + .forEach { + it.options.clear() + it.options.add(it.selection!!) + } + cellsWithSelection.mapNotNull { it.selection }.toMutableSet() } filter { it.selection == null } - .flatMap { c -> c.options.map { o -> o to c } } - .groupBy { it.first } - .filter { it.value.size == 1 } + .forEach { it.options.removeAll(selections) } + filter { it.selection == null && it.options.size == 1 } .forEach { - val c = it.value.single().second - c.selection = it.key - cleanupOptions(c, true) + changed = true + it.selection = it.options.single() } + if (!changed) { + filter { it.selection == null } + .flatMap { c -> c.options.map { o -> o to c } } + .groupBy { it.first } + .filter { it.value.size == 1 } + .forEach { + val c = it.value.single().second + c.selection = it.key + changed = true + } + } + } while (changed) + isSolved = all { + it.solution != null + && it.options.size == 1 + && it.options.single() == it.selection } } diff --git a/domain/src/main/kotlin/ch/dissem/yaep/domain/clues.kt b/domain/src/main/kotlin/ch/dissem/yaep/domain/clues.kt index fa75ffd..571dab9 100644 --- a/domain/src/main/kotlin/ch/dissem/yaep/domain/clues.kt +++ b/domain/src/main/kotlin/ch/dissem/yaep/domain/clues.kt @@ -7,6 +7,26 @@ sealed class Clue { * @return `true` if any option was removed */ abstract fun removeForbiddenOptions(grid: Grid): Boolean + + companion object { + fun > parse(line: String, mapper: (ItemClass<*>) -> Item): Clue { + return NeighbourClue.parse(line, mapper) + ?: OrderClue.parse(line, mapper) + ?: TripletClue.parse(line, mapper) + ?: SameColumnClue.parse(line, mapper) + ?: PositionClue.parse(line, mapper) + ?: throw IllegalStateException("Unknown clue: $line") + } + } +} + +sealed interface ClueParser { + /** + * Parses a string description of a clue into a clue. + * + * Returns `null` if `line` isn't a representation of that clue. + */ + fun > parse(line: String, mapper: (ItemClass<*>) -> Item): Clue? } sealed class HorizontalClue : Clue() @@ -66,6 +86,21 @@ class NeighbourClue, B : ItemClass>(val a: Item, val b: I } override fun toString() = "$aType is next to $bType" + + companion object : ClueParser { + override fun > parse( + line: String, + mapper: (ItemClass<*>) -> Item + ): Clue? { + val regex = Regex("^(\\* )?(?[A-Z_]+) is next to (?[A-Z_]+)$") + val matchResult = regex.matchEntire(line) ?: return null + + val a = ItemClass.parse(matchResult.groups["a"]!!.value) + val b = ItemClass.parse(matchResult.groups["b"]!!.value) + + return NeighbourClue(mapper(a), mapper(b)) + } + } } class OrderClue, R : ItemClass>(val left: Item, val right: Item) : @@ -113,6 +148,21 @@ class OrderClue, R : ItemClass>(val left: Item, val right } override fun toString() = "$leftType is left of $rightType" + + companion object : ClueParser { + override fun > parse( + line: String, + mapper: (ItemClass<*>) -> Item + ): Clue? { + val regex = Regex("^(\\* )?(?[A-Z_]+) is left of (?[A-Z_]+)$") + val matchResult = regex.matchEntire(line) ?: return null + + val left = ItemClass.parse(matchResult.groups["left"]!!.value) + val right = ItemClass.parse(matchResult.groups["right"]!!.value) + + return OrderClue(mapper(left), mapper(right)) + } + } } class TripletClue, B : ItemClass, C : ItemClass>( @@ -242,6 +292,23 @@ class TripletClue, B : ItemClass, C : ItemClass>( override fun toString(): String = "$bType is between the neighbours $aType and $cType to both sides" + + companion object : ClueParser { + override fun > parse( + line: String, + mapper: (ItemClass<*>) -> Item + ): Clue? { + val regex = + Regex("^(\\* )?(?[A-Z_]+) is between the neighbours (?[A-Z_]+) and (?[A-Z_]+) to both sides$") + val matchResult = regex.matchEntire(line) ?: return null + + val a = ItemClass.parse(matchResult.groups["a"]!!.value) + val b = ItemClass.parse(matchResult.groups["b"]!!.value) + val c = ItemClass.parse(matchResult.groups["c"]!!.value) + + return TripletClue(mapper(a), mapper(b), mapper(c)) + } + } } class SameColumnClue, B : ItemClass>(val a: Item, val b: Item) : Clue() { @@ -291,6 +358,22 @@ class SameColumnClue, B : ItemClass>(val a: Item, val b: } override fun toString(): String = "$aType and $bType are in the same column" + + companion object : ClueParser { + override fun > parse( + line: String, + mapper: (ItemClass<*>) -> Item + ): Clue? { + val regex = Regex("^(\\* )?(?[A-Z_]+) and (?[A-Z_]+) are in the same column$") + val matchResult = regex.matchEntire(line) ?: return null + + val a = ItemClass.parse(matchResult.groups["a"]!!.value) + val b = ItemClass.parse(matchResult.groups["b"]!!.value) + + return SameColumnClue(mapper(a), mapper(b)) + } + } + } class PositionClue>(val item: Item, val index: Int) : Clue() { @@ -307,14 +390,31 @@ class PositionClue>(val item: Item, val index: Int) : Clue() val row = grid[itemType.companion] var removed = false row.forEachIndexed { i, cell -> - if (i == index) { - removed = cell.options.retainAll { it == item } || removed - } else { - removed = cell.options.remove(item) || removed + if (cell.hasNoSelection()) { + removed = if (i == index) { + cell.options.retainAll { it == item } || removed + } else { + cell.options.remove(item) || removed + } } } return removed } override fun toString() = "$itemType is at position $index" + + companion object : ClueParser { + override fun > parse( + line: String, + mapper: (ItemClass<*>) -> Item + ): Clue? { + val regex = Regex("^(\\* )?(?[A-Z_]+) is at position (?\\d)$") + val matchResult = regex.matchEntire(line) ?: return null + + val type = ItemClass.parse(matchResult.groups["type"]!!.value) + val pos = matchResult.groups["pos"]!!.value.toInt() + + return PositionClue(mapper(type), pos) + } + } } diff --git a/domain/src/main/kotlin/ch/dissem/yaep/domain/items.kt b/domain/src/main/kotlin/ch/dissem/yaep/domain/items.kt index ce78263..3566f45 100644 --- a/domain/src/main/kotlin/ch/dissem/yaep/domain/items.kt +++ b/domain/src/main/kotlin/ch/dissem/yaep/domain/items.kt @@ -152,6 +152,8 @@ private fun idic(symbol: String): Array = Array(GENDERS.size * SKIN_TONE sealed interface ItemClass> { val symbols: Array + val name: String + val companion: ItemClassCompanion companion object { @@ -168,6 +170,10 @@ sealed interface ItemClass> { fun randomClasses(n: Int): List> { return classes.shuffled().take(n) } + + fun parse(name: String): ItemClass<*> { + return classes.mapNotNull { it.parse(name) }.single() + } } } @@ -177,4 +183,8 @@ sealed interface ItemClassCompanion> { fun randomItems(n: Int): List { return items.shuffled().take(n) } + + fun parse(name: String): C? { + return items.firstOrNull { it.name == name } + } } diff --git a/domain/src/test/kotlin/ch/dissem/yaep/domain/GameTest.kt b/domain/src/test/kotlin/ch/dissem/yaep/domain/GameTest.kt index 4a0c647..d3a1a1d 100644 --- a/domain/src/test/kotlin/ch/dissem/yaep/domain/GameTest.kt +++ b/domain/src/test/kotlin/ch/dissem/yaep/domain/GameTest.kt @@ -30,7 +30,11 @@ class GameTest { game = generateGame() } println("Generated game #$i in ${time.inWholeMilliseconds}ms") - expect(solve(game.grid, game.clues)).toEqual(SOLVABLE) + val solvable = solve(game.grid, game.clues) + if (solvable != SOLVABLE) { + println("Puzzle:\n$game") + } + expect(solvable).toEqual(SOLVABLE) expect(time).toBeLessThan(500.milliseconds) if (time < fastest) { fastest = time @@ -67,8 +71,8 @@ class GameTest { feature(Game::areCategoriesValid).toEqual(true) feature(Game::isValid).toEqual(true) feature(Game::clues) { - feature(List::size).toBeGreaterThan(5) - feature(List::size).toBeLessThan(30) + feature(Collection::size).toBeGreaterThan(5) + feature(Collection::size).toBeLessThan(30) } } println("Clues: ${game.clues.size}") @@ -124,4 +128,42 @@ class GameTest { expect(solve(game.grid, game.clues)).toEqual(SOLVABLE) } + @Test + fun `ensure specific game is solvable`() { + val game = Game.parse(""" + πŸ‘©πŸΏβ€βš•οΈπŸ‘¨πŸ½β€πŸŽ€πŸ‘©πŸΏβ€βš•οΈ πŸ‘©πŸΎβ€πŸš€πŸ§‘πŸΏβ€πŸ« + πŸœπŸ• 🐐 🐐 + πŸ‰πŸ₯­πŸπŸ‡πŸ + 🧁🍨🍩🍰πŸ₯§ + πŸ‡¨πŸ‡­πŸ‡¬πŸ‡§πŸ‡―πŸ‡΅πŸ‡ΊπŸ‡¦πŸ‡¬πŸ‡§πŸ‡¨πŸ‡¦ + πŸ·πŸ§ƒπŸΊπŸ§ƒ + + * ZEBRA is between the neighbours PIE and PEAR to both sides + * WINE is at position 0 + * SLOTH is between the neighbours ZEBRA and COFFEE to both sides + * ICE_CREAM is left of MANGO + * SWITZERLAND is at position 0 + * PIE is at position 4 + * SCIENTIST is between the neighbours ASTRONAUT and PEAR to both sides + * ROCK_STAR is between the neighbours SNAIL and ANT to both sides + * SNAIL is between the neighbours ROCK_STAR and TEA to both sides + * SOFTWARE_DEV is left of SLOTH + * SOFTWARE_DEV is left of HEALTH_WORKER + * MILK is between the neighbours CUSTARD and ZEBRA to both sides + * SLOTH is between the neighbours CUSTARD and CAKE to both sides + * SPAIN is between the neighbours CUSTARD and GRAPES to both sides + * SCIENTIST is between the neighbours SNAIL and SLOTH to both sides + * DOG is between the neighbours CUPCAKE and BEER to both sides + * SNAIL is between the neighbours BANANA and GRAPES to both sides + * SLOTH is between the neighbours GRAPES and CANADA to both sides + * UKRAINE and SCIENTIST are in the same column + * DOG is between the neighbours JAPAN and SWITZERLAND to both sides + * SLOTH is between the neighbours GOAT and TEA to both sides + * ROCK_STAR and ENGLAND are in the same column + * ROCK_STAR is next to DOUGHNUT + * PINEAPPLE is between the neighbours TEACHER and GRAPES to both sides + """.trimIndent()) + + expect(solve(game.grid, game.clues)).toEqual(SOLVABLE) + } } diff --git a/domain/src/test/kotlin/ch/dissem/yaep/domain/NeighbourClueTest.kt b/domain/src/test/kotlin/ch/dissem/yaep/domain/NeighbourClueTest.kt index 9c5684d..f57a44e 100644 --- a/domain/src/test/kotlin/ch/dissem/yaep/domain/NeighbourClueTest.kt +++ b/domain/src/test/kotlin/ch/dissem/yaep/domain/NeighbourClueTest.kt @@ -15,8 +15,8 @@ class NeighbourClueTest : ClueTest() { val a = grid[ia][j - 1] val b = grid[ib][j] - expect(NeighbourClue(a.solution, b.solution).isValid(grid)).toEqual(true) - expect(NeighbourClue(b.solution, a.solution).isValid(grid)).toEqual(true) + expect(NeighbourClue(a.solution!!, b.solution!!).isValid(grid)).toEqual(true) + expect(NeighbourClue(b.solution!!, a.solution!!).isValid(grid)).toEqual(true) } } } @@ -35,8 +35,8 @@ class NeighbourClueTest : ClueTest() { val a = grid[ia][ja] val b = grid[ib][jb] - expect(NeighbourClue(a.solution, b.solution).isValid(grid)).toEqual(false) - expect(NeighbourClue(b.solution, a.solution).isValid(grid)).toEqual(false) + expect(NeighbourClue(a.solution!!, b.solution!!).isValid(grid)).toEqual(false) + expect(NeighbourClue(b.solution!!, a.solution!!).isValid(grid)).toEqual(false) } } } @@ -58,32 +58,32 @@ class NeighbourClueTest : ClueTest() { rowB.forEach { it.selection = null; it.options.clear() } a.selection = null - a.options.add(a.solution) + a.options.add(a.solution!!) b.selection = b.solution b.options.clear() - expect(NeighbourClue(a.solution, b.solution).isValid(grid)).toEqual(true) - expect(NeighbourClue(b.solution, a.solution).isValid(grid)).toEqual(true) + expect(NeighbourClue(a.solution!!, b.solution!!).isValid(grid)).toEqual(true) + expect(NeighbourClue(b.solution!!, a.solution!!).isValid(grid)).toEqual(true) a.selection = a.solution a.options.clear() b.selection = null - b.options.add(b.solution) + b.options.add(b.solution!!) - expect(NeighbourClue(a.solution, b.solution).isValid(grid)).toEqual(true) - expect(NeighbourClue(b.solution, a.solution).isValid(grid)).toEqual(true) + expect(NeighbourClue(a.solution!!, b.solution!!).isValid(grid)).toEqual(true) + expect(NeighbourClue(b.solution!!, a.solution!!).isValid(grid)).toEqual(true) if (j < size - 1) { val notA = rowA[j + 1] a.selection = null a.options.clear() - notA.options.add(a.solution) + notA.options.add(a.solution!!) b.selection = b.solution b.options.clear() - expect(NeighbourClue(a.solution, b.solution).isValid(grid)).toEqual(true) - expect(NeighbourClue(b.solution, a.solution).isValid(grid)).toEqual(true) + expect(NeighbourClue(a.solution!!, b.solution!!).isValid(grid)).toEqual(true) + expect(NeighbourClue(b.solution!!, a.solution!!).isValid(grid)).toEqual(true) } } } @@ -102,8 +102,8 @@ class NeighbourClueTest : ClueTest() { rowB[3].selection = b.solution - expect(NeighbourClue(a.solution, b.solution).isValid(grid)).toEqual(false) - expect(NeighbourClue(b.solution, a.solution).isValid(grid)).toEqual(false) + expect(NeighbourClue(a.solution!!, b.solution!!).isValid(grid)).toEqual(false) + expect(NeighbourClue(b.solution!!, a.solution!!).isValid(grid)).toEqual(false) } } diff --git a/domain/src/test/kotlin/ch/dissem/yaep/domain/OrderClueTest.kt b/domain/src/test/kotlin/ch/dissem/yaep/domain/OrderClueTest.kt index faa2250..c65a08b 100644 --- a/domain/src/test/kotlin/ch/dissem/yaep/domain/OrderClueTest.kt +++ b/domain/src/test/kotlin/ch/dissem/yaep/domain/OrderClueTest.kt @@ -16,7 +16,7 @@ class OrderClueTest : ClueTest() { val a = grid[ia][ja] val b = grid[ib][jb] - expect(OrderClue(a.solution, b.solution).isValid(grid)).toEqual(true) + expect(OrderClue(a.solution!!, b.solution!!).isValid(grid)).toEqual(true) } } } @@ -35,7 +35,7 @@ class OrderClueTest : ClueTest() { val a = grid[ia][ja] val b = grid[ib][jb] - expect(OrderClue(b.solution, a.solution).isValid(grid)).toEqual(false) + expect(OrderClue(b.solution!!, a.solution!!).isValid(grid)).toEqual(false) } } } @@ -49,7 +49,7 @@ class OrderClueTest : ClueTest() { for (rowA in grid.rows) { for (rowB in grid.rows) { for (i in 0 until size) { - expect(OrderClue(rowA[i].solution, rowB[i].solution).isValid(grid)).toEqual(false) + expect(OrderClue(rowA[i].solution!!, rowB[i].solution!!).isValid(grid)).toEqual(false) } } } diff --git a/domain/src/test/kotlin/ch/dissem/yaep/domain/SameColumnClueTest.kt b/domain/src/test/kotlin/ch/dissem/yaep/domain/SameColumnClueTest.kt index e378fa9..0fb5af7 100644 --- a/domain/src/test/kotlin/ch/dissem/yaep/domain/SameColumnClueTest.kt +++ b/domain/src/test/kotlin/ch/dissem/yaep/domain/SameColumnClueTest.kt @@ -14,8 +14,8 @@ class SameColumnClueTest : ClueTest() { val a = grid[ia][j] val b = grid[ib][j] - expect(SameColumnClue(a.solution, b.solution).isValid(grid)).toEqual(true) - expect(SameColumnClue(b.solution, a.solution).isValid(grid)).toEqual(true) + expect(SameColumnClue(a.solution!!, b.solution!!).isValid(grid)).toEqual(true) + expect(SameColumnClue(b.solution!!, a.solution!!).isValid(grid)).toEqual(true) } } } @@ -32,8 +32,8 @@ class SameColumnClueTest : ClueTest() { val a = grid[ia][ja] val b = grid[ib][jb] - expect(SameColumnClue(a.solution, b.solution).isValid(grid)).toEqual(false) - expect(SameColumnClue(b.solution, a.solution).isValid(grid)).toEqual(false) + expect(SameColumnClue(a.solution!!, b.solution!!).isValid(grid)).toEqual(false) + expect(SameColumnClue(b.solution!!, a.solution!!).isValid(grid)).toEqual(false) } } } @@ -54,8 +54,8 @@ class SameColumnClueTest : ClueTest() { a.selection = a.solution b.selection = b.solution - expect(SameColumnClue(a.solution, b.solution).isValid(grid)).toEqual(false) - expect(SameColumnClue(b.solution, a.solution).isValid(grid)).toEqual(false) + expect(SameColumnClue(a.solution!!, b.solution!!).isValid(grid)).toEqual(false) + expect(SameColumnClue(b.solution!!, a.solution!!).isValid(grid)).toEqual(false) } } } @@ -87,8 +87,8 @@ class SameColumnClueTest : ClueTest() { } } - expect(SameColumnClue(a.solution, b.solution).isValid(grid)).toEqual(false) - expect(SameColumnClue(b.solution, a.solution).isValid(grid)).toEqual(false) + expect(SameColumnClue(a.solution!!, b.solution!!).isValid(grid)).toEqual(false) + expect(SameColumnClue(b.solution!!, a.solution!!).isValid(grid)).toEqual(false) } } } diff --git a/domain/src/test/kotlin/ch/dissem/yaep/domain/TripletClueTest.kt b/domain/src/test/kotlin/ch/dissem/yaep/domain/TripletClueTest.kt index fde06fb..c151f04 100644 --- a/domain/src/test/kotlin/ch/dissem/yaep/domain/TripletClueTest.kt +++ b/domain/src/test/kotlin/ch/dissem/yaep/domain/TripletClueTest.kt @@ -17,8 +17,8 @@ class TripletClueTest : ClueTest() { val b = grid[ib][j - 1] val c = grid[ic][j] - expect(TripletClue(a.solution, b.solution, c.solution).isValid(grid)).toEqual(true) - expect(TripletClue(c.solution, b.solution, a.solution).isValid(grid)).toEqual(true) + expect(TripletClue(a.solution!!, b.solution!!, c.solution!!).isValid(grid)).toEqual(true) + expect(TripletClue(c.solution!!, b.solution!!, a.solution!!).isValid(grid)).toEqual(true) } } } @@ -38,14 +38,14 @@ class TripletClueTest : ClueTest() { rowB[0].selection = b.solution - expect(TripletClue(a.solution, b.solution, c.solution).isValid(grid)).toEqual(false) - expect(TripletClue(c.solution, b.solution, a.solution).isValid(grid)).toEqual(false) + expect(TripletClue(a.solution!!, b.solution!!, c.solution!!).isValid(grid)).toEqual(false) + expect(TripletClue(c.solution!!, b.solution!!, a.solution!!).isValid(grid)).toEqual(false) rowB[0].selection = null rowB[grid.size - 1].selection = b.solution - expect(TripletClue(a.solution, b.solution, c.solution).isValid(grid)).toEqual(false) - expect(TripletClue(c.solution, b.solution, a.solution).isValid(grid)).toEqual(false) + expect(TripletClue(a.solution!!, b.solution!!, c.solution!!).isValid(grid)).toEqual(false) + expect(TripletClue(c.solution!!, b.solution!!, a.solution!!).isValid(grid)).toEqual(false) } @Test @@ -61,10 +61,10 @@ class TripletClueTest : ClueTest() { val rowC = grid[1] val c = rowC[ic] - val clue = TripletClue(a.solution, b.solution, c.solution) + val clue = TripletClue(a.solution!!, b.solution!!, c.solution!!) b.selection = b.solution - c.options.add(c.solution) + c.options.add(c.solution!!) rowA.forEachIndexed { index, notA -> @@ -75,7 +75,7 @@ class TripletClueTest : ClueTest() { } index == ic -> { - rowC[ia].options.add(c.solution) + rowC[ia].options.add(c.solution!!) expect(clue.isValid(grid)).toEqual(true) } @@ -97,8 +97,8 @@ class TripletClueTest : ClueTest() { a.selection = a.solution grid[1][4].selection = c.solution - expect(TripletClue(a.solution, b.solution, c.solution).isValid(grid)).toEqual(false) - expect(TripletClue(c.solution, b.solution, a.solution).isValid(grid)).toEqual(false) + expect(TripletClue(a.solution!!, b.solution!!, c.solution!!).isValid(grid)).toEqual(false) + expect(TripletClue(c.solution!!, b.solution!!, a.solution!!).isValid(grid)).toEqual(false) } @Test @@ -115,11 +115,11 @@ class TripletClueTest : ClueTest() { b.options.clear() c.options.clear() - rowB[4].options.add(b.solution) - rowC[5].options.add(c.solution) + rowB[4].options.add(b.solution!!) + rowC[5].options.add(c.solution!!) - expect(TripletClue(a.solution, b.solution, c.solution).isValid(grid)).toEqual(true) - expect(TripletClue(c.solution, b.solution, a.solution).isValid(grid)).toEqual(true) + expect(TripletClue(a.solution!!, b.solution!!, c.solution!!).isValid(grid)).toEqual(true) + expect(TripletClue(c.solution!!, b.solution!!, a.solution!!).isValid(grid)).toEqual(true) } @Test @@ -136,7 +136,7 @@ class TripletClueTest : ClueTest() { rowB[4].selection = rowC[3].solution c.selection = rowC[3].solution - expect(TripletClue(a.solution, b.solution, c.solution).isValid(grid)).toEqual(false) - expect(TripletClue(c.solution, b.solution, a.solution).isValid(grid)).toEqual(false) + expect(TripletClue(a.solution!!, b.solution!!, c.solution!!).isValid(grid)).toEqual(false) + expect(TripletClue(c.solution!!, b.solution!!, a.solution!!).isValid(grid)).toEqual(false) } }