Fix tests & clues (WIP)
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ->
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user