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
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 ->
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user