Improve test and data structure
This commit is contained in:
@@ -5,6 +5,9 @@ class Grid(
|
|||||||
val rows: List<GameRow<*>>
|
val rows: List<GameRow<*>>
|
||||||
) : List<GameRow<ItemClass<*>>> by rows as List<GameRow<ItemClass<*>>> {
|
) : List<GameRow<ItemClass<*>>> by rows as List<GameRow<ItemClass<*>>> {
|
||||||
|
|
||||||
|
val cells: List<GameCell<*>>
|
||||||
|
get() = rows.flatten() as List<GameCell<*>>
|
||||||
|
|
||||||
fun <C : ItemClass<C>> indexOf(element: C): Int {
|
fun <C : ItemClass<C>> indexOf(element: C): Int {
|
||||||
return this[element.companion]
|
return this[element.companion]
|
||||||
.indexOfFirst { it.selection?.itemType == element }
|
.indexOfFirst { it.selection?.itemType == element }
|
||||||
@@ -19,14 +22,6 @@ class Grid(
|
|||||||
return this[item.itemType.companion].first { it.selection == item }
|
return this[item.itemType.companion].first { it.selection == item }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun anyCell(predicate: (GameCell<ItemClass<*>>) -> Boolean): Boolean {
|
|
||||||
return flatMap { it }.any(predicate)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun allCells(predicate: (GameCell<ItemClass<*>>) -> Boolean): Boolean {
|
|
||||||
return flatMap { it }.all(predicate)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return joinToString("\n") { row ->
|
return joinToString("\n") { row ->
|
||||||
row.joinToString("") { it.selection?.symbol ?: " " }
|
row.joinToString("") { it.selection?.symbol ?: " " }
|
||||||
|
|||||||
@@ -107,8 +107,10 @@ class TripletClue<A : ItemClass<A>, B : ItemClass<B>, C : ItemClass<C>>(
|
|||||||
ia + 1 -> {
|
ia + 1 -> {
|
||||||
return if (ic != -1) {
|
return if (ic != -1) {
|
||||||
ic != ia + 2
|
ic != ia + 2
|
||||||
} else {
|
} else if (ia + 2 < grid.size) {
|
||||||
!rowC[ia + 2].mayBe(c)
|
!rowC[ia + 2].mayBe(c)
|
||||||
|
} else {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,10 +62,10 @@ internal fun solve(
|
|||||||
} while (removedOptions)
|
} while (removedOptions)
|
||||||
|
|
||||||
// If any cell has no items left, the puzzle has no solution.
|
// If any cell has no items left, the puzzle has no solution.
|
||||||
if (grid.anyCell { cell -> cell.options.isEmpty() }) return NO_SOLUTION
|
if (grid.cells.any { cell -> cell.options.isEmpty() }) return NO_SOLUTION
|
||||||
|
|
||||||
// If all cells have exactly one item left, the puzzle is solved.
|
// If all cells have exactly one item left, the puzzle is solved.
|
||||||
if (grid.allCells { it.selection != null }) return SOLVABLE
|
if (grid.cells.all { it.selection != null }) return SOLVABLE
|
||||||
|
|
||||||
// If there are still cells with multiple items, pick one and try each item in turn, then go back to step 2.
|
// If there are still cells with multiple items, pick one and try each item in turn, then go back to step 2.
|
||||||
for (i in 0 until grid.size) {
|
for (i in 0 until grid.size) {
|
||||||
|
|||||||
@@ -9,24 +9,35 @@ import ch.dissem.yaep.domain.Nationality.UKRAINE
|
|||||||
import ch.dissem.yaep.domain.Profession.ASTRONAUT
|
import ch.dissem.yaep.domain.Profession.ASTRONAUT
|
||||||
import ch.dissem.yaep.domain.Profession.FARMER
|
import ch.dissem.yaep.domain.Profession.FARMER
|
||||||
import ch.dissem.yaep.domain.Profession.SOFTWARE_DEV
|
import ch.dissem.yaep.domain.Profession.SOFTWARE_DEV
|
||||||
|
import ch.dissem.yaep.domain.PuzzleSolution.SOLVABLE
|
||||||
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.toBeGreaterThan
|
||||||
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.verbs.expect
|
import ch.tutteli.atrium.api.verbs.expect
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
import kotlin.time.measureTime
|
||||||
|
|
||||||
class GameTest {
|
class GameTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ensure generated game is valid`() {
|
fun `ensure generated game is valid`() {
|
||||||
val game = generateGame()
|
val game: Game
|
||||||
|
val time = measureTime {
|
||||||
|
game = generateGame()
|
||||||
|
}
|
||||||
expect(game) {
|
expect(game) {
|
||||||
feature(Game::areCategoriesValid).toEqual(true)
|
feature(Game::areCategoriesValid).toEqual(true)
|
||||||
feature(Game::areRulesViolated).toEqual(false)
|
feature(Game::areRulesViolated).toEqual(false)
|
||||||
feature(Game::clues) {
|
feature(Game::clues) {
|
||||||
|
feature(List<Clue>::size).toBeGreaterThan(5)
|
||||||
feature(List<Clue>::size).toBeLessThan(30)
|
feature(List<Clue>::size).toBeLessThan(30)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
println("Clues: ${game.clues.size}")
|
||||||
|
expect(solve(game.grid, game.clues)).toEqual(SOLVABLE)
|
||||||
|
expect(time).toBeLessThan(1.seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -73,7 +84,7 @@ class GameTest {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(solve(game.grid, game.clues)).toEqual(PuzzleSolution.SOLVABLE)
|
expect(solve(game.grid, game.clues)).toEqual(SOLVABLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user