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
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
}
}

View File

@@ -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<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.
// (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<PositionClue<ItemClass<*>>>().forEach { position ->
val row = grid[position.item.itemType.companion]
val newSelections = mutableListOf<GameCell<ItemClass<*>>>()
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 <C : ItemClass<C>> GameRow<C>.tryOptionsForClues(grid: Grid, clues: List<Clu
}
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()
forEach { otherCell ->
if (otherCell != cell && otherCell.selection == null) {
filter { otherCell -> otherCell != cell && otherCell.selection == null }.forEach { otherCell ->
otherCell.options.remove(cell.selection)
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>()
// rows.forEach { row ->
// 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>>(
val itemType: C,
@@ -85,4 +86,8 @@ class Item<C : ItemClass<C>>(
override fun hashCode(): Int {
return itemType.hashCode()
}
override fun toString(): String {
return itemType.toString() + symbol
}
}

View File

@@ -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<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
fun `ensure generated game is valid`() {
val game = generateGame()