Fix tests & clues (WIP)

This commit is contained in:
2024-06-27 05:57:05 +02:00
parent 69bb20d12a
commit f82af3af32
4 changed files with 49 additions and 23 deletions

View File

@@ -123,11 +123,11 @@ class TripletClue<A : ItemClass<A>, B : ItemClass<B>, C : ItemClass<C>>(
if (ib == 0 || ib == rowB.size - 1) return true if (ib == 0 || ib == rowB.size - 1) return true
return when (ic) { return when (ic) {
-1 -> !(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(ia + 2).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 else -> false
} }
} }

View File

@@ -7,17 +7,19 @@ import kotlin.random.Random
fun generateGame(size: Int = 6): Game { fun generateGame(size: Int = 6): Game {
// Generate a random puzzle instance. // Generate a random puzzle instance.
val classes = ItemClass.randomClasses(size) // val classes = ItemClass.randomClasses(size)
val classes = ItemClass.classes.take(size)
val grid: List<List<Item<ItemClass<*>>>> = classes.map { val grid: List<List<Item<ItemClass<*>>>> = 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. // 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 // (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 // 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.) // 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 var i = 0
@@ -46,14 +48,9 @@ private fun solve(
// First, set the positions of the items that are already known. // First, set the positions of the items that are already known.
clues.filterIsInstance<PositionClue<ItemClass<*>>>().forEach { position -> clues.filterIsInstance<PositionClue<ItemClass<*>>>().forEach { position ->
val row = grid[position.item.itemType.companion] val row = grid[position.item.itemType.companion]
val newSelections = mutableListOf<GameCell<ItemClass<*>>>() val cell = row[position.index]
row.forEachIndexed { index, gameCell -> cell.options.retainAll { it == cell.selection }
if (index == position.index) { row.cleanupOptions(cell)
gameCell.selection = position.item
newSelections.add(gameCell)
}
}
newSelections.forEach { row.cleanupOptions(it) }
} }
// For each clue, remove any items that violate the clue. // For each clue, remove any items that violate the clue.
@@ -104,18 +101,16 @@ fun <C : ItemClass<C>> GameRow<C>.tryOptionsForClues(grid: Grid, clues: List<Clu
} }
fun <C : ItemClass<C>> GameRow<C>.cleanupOptions(cell: GameCell<C>) { fun <C : ItemClass<C>> GameRow<C>.cleanupOptions(cell: GameCell<C>) {
if (cell.options.size == 1) { if (cell.options.size == 1 && cell.selection == null) {
cell.selection = cell.options.first() cell.selection = cell.options.first()
forEach { otherCell -> filter { otherCell -> otherCell != cell && otherCell.selection == null }.forEach { otherCell ->
if (otherCell != cell && otherCell.selection == null) {
otherCell.options.remove(cell.selection) otherCell.options.remove(cell.selection)
cleanupOptions(otherCell) cleanupOptions(otherCell)
} }
} }
} }
}
private fun getAllClues(rows: List<List<Item<ItemClass<*>>>>): MutableSet<Clue> { fun getAllClues(rows: List<List<Item<ItemClass<*>>>>): MutableSet<Clue> {
val clues = mutableSetOf<Clue>() val clues = mutableSetOf<Clue>()
// rows.forEach { row -> // rows.forEach { row ->
// row.forEachIndexed { i, item -> // row.forEachIndexed { i, item ->

View File

@@ -68,7 +68,8 @@ class GameCell<C : ItemClass<C>>(
} }
fun <C : ItemClass<C>> GameCell<C>?.mayBe(item: Item<C>) = this != null && (selection == item || (selection == null && options.contains(item))) fun <C : ItemClass<C>> GameCell<C>?.mayBe(item: Item<C>) =
this != null && (selection == item || (selection == null && options.contains(item)))
class Item<C : ItemClass<C>>( class Item<C : ItemClass<C>>(
val itemType: C, val itemType: C,
@@ -85,4 +86,8 @@ class Item<C : ItemClass<C>>(
override fun hashCode(): Int { override fun hashCode(): Int {
return itemType.hashCode() return itemType.hashCode()
} }
override fun toString(): String {
return itemType.toString() + symbol
}
} }

View File

@@ -1,15 +1,41 @@
package domain 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.feature
import ch.tutteli.atrium.api.fluent.en_GB.size 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.toBeLessThan
import ch.tutteli.atrium.api.fluent.en_GB.toEqual 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.fluent.en_GB.toHaveSize
import ch.tutteli.atrium.api.verbs.expect import ch.tutteli.atrium.api.verbs.expect
import kotlin.test.Test import kotlin.test.Test
class GameTest { class GameTest {
// @Test
// fun `try to find the error`() {
// val size = 6
// val classes = ItemClass.randomClasses(size)
//
// val grid: List<List<Item<ItemClass<*>>>> = 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 @Test
fun `ensure generated game is valid`() { fun `ensure generated game is valid`() {
val game = generateGame() val game = generateGame()