diff --git a/composeApp/src/commonMain/kotlin/domain/clues.kt b/composeApp/src/commonMain/kotlin/domain/clues.kt index 5b0a184..233544f 100644 --- a/composeApp/src/commonMain/kotlin/domain/clues.kt +++ b/composeApp/src/commonMain/kotlin/domain/clues.kt @@ -123,11 +123,11 @@ class TripletClue, B : ItemClass, C : ItemClass>( if (ib == 0 || ib == rowB.size - 1) return true return when (ic) { - -1 -> !(rowA.getOrNull(ib - 1).mayBe(a) && rowC.getOrNull(ia - 2).mayBe(c)) && - !(rowA.getOrNull(ib + 1).mayBe(a) && rowC.getOrNull(ia + 2).mayBe(c)) + -1 -> !(rowA.getOrNull(ib - 1).mayBe(a) && rowC.getOrNull(ib + 1).mayBe(c)) && + !(rowA.getOrNull(ib + 1).mayBe(a) && rowC.getOrNull(ib - 1).mayBe(c)) - ib - 1 -> rowA[ib + 1].mayBe(a) - ib + 1 -> rowA[ib - 1].mayBe(a) + ib - 1 -> !rowA[ib + 1].mayBe(a) + ib + 1 -> !rowA[ib - 1].mayBe(a) else -> false } } diff --git a/composeApp/src/commonMain/kotlin/domain/generator.kt b/composeApp/src/commonMain/kotlin/domain/generator.kt index 65f2169..099d4cb 100644 --- a/composeApp/src/commonMain/kotlin/domain/generator.kt +++ b/composeApp/src/commonMain/kotlin/domain/generator.kt @@ -7,17 +7,19 @@ import kotlin.random.Random fun generateGame(size: Int = 6): Game { // Generate a random puzzle instance. - val classes = ItemClass.randomClasses(size) +// val classes = ItemClass.randomClasses(size) + val classes = ItemClass.classes.take(size) val grid: List>>> = classes.map { - it.randomItems(size).map { item -> Item(item) } +// it.randomItems(size).map { item -> Item(item) } + it.items.take(size).map { item -> Item(item) } } // Build a set C of all possible clues that pertain to this puzzle instance. // (There are a finite and in fact quite small number of possible clues: for example // if there are 5 houses, there are 5 possible clues of the form "Person A lives in // house B", 8 possible clues of the form "Person A lives next to house B", and so on.) - var clues = getAllClues(grid).shuffled() + var clues = getAllClues(grid).toList() //.shuffled() var i = 0 @@ -46,14 +48,9 @@ private fun solve( // First, set the positions of the items that are already known. clues.filterIsInstance>>().forEach { position -> val row = grid[position.item.itemType.companion] - val newSelections = mutableListOf>>() - row.forEachIndexed { index, gameCell -> - if (index == position.index) { - gameCell.selection = position.item - newSelections.add(gameCell) - } - } - newSelections.forEach { row.cleanupOptions(it) } + val cell = row[position.index] + cell.options.retainAll { it == cell.selection } + row.cleanupOptions(cell) } // For each clue, remove any items that violate the clue. @@ -104,18 +101,16 @@ fun > GameRow.tryOptionsForClues(grid: Grid, clues: List> GameRow.cleanupOptions(cell: GameCell) { - if (cell.options.size == 1) { + if (cell.options.size == 1 && cell.selection == null) { cell.selection = cell.options.first() - forEach { otherCell -> - if (otherCell != cell && otherCell.selection == null) { - otherCell.options.remove(cell.selection) - cleanupOptions(otherCell) - } + filter { otherCell -> otherCell != cell && otherCell.selection == null }.forEach { otherCell -> + otherCell.options.remove(cell.selection) + cleanupOptions(otherCell) } } } -private fun getAllClues(rows: List>>>): MutableSet { +fun getAllClues(rows: List>>>): MutableSet { val clues = mutableSetOf() // rows.forEach { row -> // row.forEachIndexed { i, item -> diff --git a/composeApp/src/commonMain/kotlin/domain/grid.kt b/composeApp/src/commonMain/kotlin/domain/grid.kt index 19701bf..be94b2a 100644 --- a/composeApp/src/commonMain/kotlin/domain/grid.kt +++ b/composeApp/src/commonMain/kotlin/domain/grid.kt @@ -68,7 +68,8 @@ class GameCell>( } -fun > GameCell?.mayBe(item: Item) = this != null && (selection == item || (selection == null && options.contains(item))) +fun > GameCell?.mayBe(item: Item) = + this != null && (selection == item || (selection == null && options.contains(item))) class Item>( val itemType: C, @@ -85,4 +86,8 @@ class Item>( override fun hashCode(): Int { return itemType.hashCode() } + + override fun toString(): String { + return itemType.toString() + symbol + } } \ No newline at end of file diff --git a/composeApp/src/commonTest/kotlin/domain/GameTest.kt b/composeApp/src/commonTest/kotlin/domain/GameTest.kt index 575f64f..a20a8f6 100644 --- a/composeApp/src/commonTest/kotlin/domain/GameTest.kt +++ b/composeApp/src/commonTest/kotlin/domain/GameTest.kt @@ -1,15 +1,41 @@ package domain +import androidx.compose.runtime.TestOnly +import ch.tutteli.atrium.api.fluent.en_GB.extractSubject import ch.tutteli.atrium.api.fluent.en_GB.feature import ch.tutteli.atrium.api.fluent.en_GB.size import ch.tutteli.atrium.api.fluent.en_GB.toBeLessThan import ch.tutteli.atrium.api.fluent.en_GB.toEqual +import ch.tutteli.atrium.api.fluent.en_GB.toHaveElementsAndAll +import ch.tutteli.atrium.api.fluent.en_GB.toHaveElementsAndNone import ch.tutteli.atrium.api.fluent.en_GB.toHaveSize import ch.tutteli.atrium.api.verbs.expect import kotlin.test.Test class GameTest { +// @Test +// fun `try to find the error`() { +// val size = 6 +// val classes = ItemClass.randomClasses(size) +// +// val grid: List>>> = classes.map { +// it.randomItems(size).map { item -> Item(item) } +// } +// +// // Build a set C of all possible clues that pertain to this puzzle instance. +// // (There are a finite and in fact quite small number of possible clues: for example +// // if there are 5 houses, there are 5 possible clues of the form "Person A lives in +// // house B", 8 possible clues of the form "Person A lives next to house B", and so on.) +// val clues = getAllClues(grid).shuffled() +// +// val gameGrid = grid.toGrid() +// gameGrid.flatMap { it }.forEach { it.selection = it.solution } +// +// expect(clues).toHaveElementsAndAll { +// feature { f(it::isRuleViolated, gameGrid) }.toEqual(false) +// } +// } @Test fun `ensure generated game is valid`() { val game = generateGame()