Fix tests & clues (WIP)
This commit is contained in:
@@ -23,7 +23,7 @@ import domain.HorizontalClue
|
||||
import domain.ItemClass
|
||||
import domain.NeighbourClue
|
||||
import domain.OrderClue
|
||||
import domain.SameRowClue
|
||||
import domain.SameColumnClue
|
||||
import domain.TripletClue
|
||||
import domain.generateGame
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
@@ -80,7 +80,7 @@ fun PuzzleGrid(
|
||||
fun PuzzleClues(
|
||||
modifier: Modifier = Modifier,
|
||||
horizontalClues: List<DisplayClue<HorizontalClue>>,
|
||||
verticalClues: List<DisplayClue<SameRowClue<ItemClass<*>>>>
|
||||
verticalClues: List<DisplayClue<SameColumnClue<ItemClass<*>, ItemClass<*>>>>
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
LazyVerticalGrid(
|
||||
@@ -137,7 +137,7 @@ fun PuzzleClues(
|
||||
fun HorizontalClue(modifier: Modifier = Modifier, clue: HorizontalClue) {
|
||||
Column {
|
||||
when (clue) {
|
||||
is NeighbourClue<*> -> {
|
||||
is NeighbourClue<*, *> -> {
|
||||
DrawItem(modifier = Modifier.weight(1f), clue.a)
|
||||
OutlinedCard(modifier = modifier.aspectRatio(1f).weight(1f)) {
|
||||
Image(
|
||||
@@ -148,7 +148,7 @@ fun HorizontalClue(modifier: Modifier = Modifier, clue: HorizontalClue) {
|
||||
DrawItem(modifier = Modifier.weight(1f), clue.b)
|
||||
}
|
||||
|
||||
is OrderClue<*> -> {
|
||||
is OrderClue<*, *> -> {
|
||||
DrawItem(modifier = Modifier.weight(1f), clue.left)
|
||||
OutlinedCard(modifier = modifier.aspectRatio(1f).weight(1f)) {
|
||||
Image(painter = painterResource(Res.drawable.order), contentDescription = null)
|
||||
@@ -156,7 +156,7 @@ fun HorizontalClue(modifier: Modifier = Modifier, clue: HorizontalClue) {
|
||||
DrawItem(modifier = Modifier.weight(1f), clue.right)
|
||||
}
|
||||
|
||||
is TripletClue<*> -> {
|
||||
is TripletClue<*, *, *> -> {
|
||||
DrawItem(modifier = Modifier.weight(1f), clue.a)
|
||||
DrawItem(modifier = Modifier.weight(1f), clue.b)
|
||||
DrawItem(modifier = Modifier.weight(1f), clue.c)
|
||||
@@ -166,7 +166,7 @@ fun HorizontalClue(modifier: Modifier = Modifier, clue: HorizontalClue) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun VerticalClue(modifier: Modifier = Modifier, clue: SameRowClue<*>) {
|
||||
fun VerticalClue(modifier: Modifier = Modifier, clue: SameColumnClue<*, *>) {
|
||||
Column(modifier = modifier) {
|
||||
DrawItem(modifier = Modifier.weight(1f), clue.a)
|
||||
DrawItem(modifier = Modifier.weight(1f), clue.b)
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package domain
|
||||
|
||||
import androidx.compose.ui.util.fastAny
|
||||
|
||||
class Game(
|
||||
val grid: Grid,
|
||||
val clues: List<Clue>
|
||||
) {
|
||||
val horizontalClues = clues.filterIsInstance<HorizontalClue>()
|
||||
val verticalClues = clues.filterIsInstance<SameRowClue<ItemClass<*>>>()
|
||||
val verticalClues = clues.filterIsInstance<SameColumnClue<ItemClass<*>, ItemClass<*>>>()
|
||||
val positionalClues = clues.filterIsInstance<PositionClue<ItemClass<*>>>()
|
||||
|
||||
init {
|
||||
|
||||
@@ -6,7 +6,8 @@ sealed class Clue {
|
||||
|
||||
sealed class HorizontalClue : Clue()
|
||||
|
||||
class NeighbourClue<C : ItemClass<C>>(val a: Item<C>, val b: Item<C>) : HorizontalClue() {
|
||||
class NeighbourClue<A : ItemClass<A>, B : ItemClass<B>>(val a: Item<A>, val b: Item<B>) :
|
||||
HorizontalClue() {
|
||||
private val aType = a.itemType
|
||||
private val bType = b.itemType
|
||||
|
||||
@@ -14,6 +15,19 @@ class NeighbourClue<C : ItemClass<C>>(val a: Item<C>, val b: Item<C>) : Horizont
|
||||
val rowA = grid[aType.companion]
|
||||
val rowB = grid[bType.companion]
|
||||
|
||||
val ia = rowA.indexOf(aType)
|
||||
val ib by lazy { rowB.indexOf(bType) }
|
||||
if (ia != -1) {
|
||||
if (ib != -1) return !(ib == ia - 1 || ib == ia + 1)
|
||||
return !(rowB.getOrNull(ia - 1).mayBe(b) ||
|
||||
rowB.getOrNull(ia + 1).mayBe(b))
|
||||
}
|
||||
|
||||
if (ib != -1) {
|
||||
return !(rowA.getOrNull(ib - 1).mayBe(a) ||
|
||||
rowA.getOrNull(ib + 1).mayBe(a))
|
||||
}
|
||||
|
||||
for (i in 1 until grid.size) {
|
||||
if (rowA[i - 1].mayBe(a) &&
|
||||
rowB[i - 0].mayBe(b)
|
||||
@@ -29,7 +43,8 @@ class NeighbourClue<C : ItemClass<C>>(val a: Item<C>, val b: Item<C>) : Horizont
|
||||
}
|
||||
}
|
||||
|
||||
class OrderClue<C : ItemClass<C>>(val left: Item<C>, val right: Item<C>) : HorizontalClue() {
|
||||
class OrderClue<L : ItemClass<L>, R : ItemClass<R>>(val left: Item<L>, val right: Item<R>) :
|
||||
HorizontalClue() {
|
||||
private val leftType = left.itemType
|
||||
private val rightType = right.itemType
|
||||
|
||||
@@ -37,26 +52,92 @@ class OrderClue<C : ItemClass<C>>(val left: Item<C>, val right: Item<C>) : Horiz
|
||||
val rowLeft = grid[leftType.companion]
|
||||
val rowRight = grid[rightType.companion]
|
||||
|
||||
val iLeft = rowLeft.indexOf(leftType)
|
||||
val iRight by lazy { rowRight.indexOf(rightType) }
|
||||
if (iLeft != -1) {
|
||||
if (iRight != -1) return iRight <= iLeft
|
||||
|
||||
return rowRight.indexOfLast { it.mayBe(right) } > iLeft
|
||||
}
|
||||
|
||||
if (iRight != -1) {
|
||||
return rowLeft.indexOfFirst { it.mayBe(left) } in 0 until iRight
|
||||
}
|
||||
|
||||
return rowLeft.indexOfFirst { it.mayBe(left) } >= rowRight.indexOfLast { it.mayBe(right) }
|
||||
}
|
||||
}
|
||||
|
||||
class TripletClue<C : ItemClass<C>>(val a: Item<C>, val b: Item<C>, val c: Item<C>) :
|
||||
class TripletClue<A : ItemClass<A>, B : ItemClass<B>, C : ItemClass<C>>(
|
||||
val a: Item<A>,
|
||||
val b: Item<B>,
|
||||
val c: Item<C>
|
||||
) :
|
||||
HorizontalClue() {
|
||||
private val aType = a.itemType
|
||||
private val bType = b.itemType
|
||||
private val cType = c.itemType
|
||||
|
||||
override fun isRuleViolated(grid: Grid): Boolean {
|
||||
val rowA = grid[aType.companion]
|
||||
val rowB = grid[bType.companion]
|
||||
val rowC = grid[cType.companion]
|
||||
val rowA by lazy { grid[aType.companion] }
|
||||
val rowB by lazy { grid[bType.companion] }
|
||||
val rowC by lazy { grid[cType.companion] }
|
||||
|
||||
val ia = rowA.indexOf(aType)
|
||||
val ib by lazy { rowB.indexOf(bType) }
|
||||
val ic by lazy { rowC.indexOf(cType) }
|
||||
|
||||
if (ia != -1) {
|
||||
if (ib != -1) {
|
||||
when (ib) {
|
||||
ia - 1 -> {
|
||||
return if (ic != -1) {
|
||||
ic != ia - 2
|
||||
} else {
|
||||
!rowC[ia - 2].mayBe(c)
|
||||
}
|
||||
}
|
||||
|
||||
ia + 1 -> {
|
||||
return if (ic != -1) {
|
||||
ic != ia + 2
|
||||
} else {
|
||||
!rowC[ia + 2].mayBe(c)
|
||||
}
|
||||
}
|
||||
|
||||
else -> return true
|
||||
}
|
||||
}
|
||||
return when (ic) {
|
||||
ia - 2 -> !rowB[ia - 1].mayBe(b)
|
||||
ia + 2 -> !rowB[ia + 1].mayBe(b)
|
||||
-1 -> !(rowB.getOrNull(ia - 1).mayBe(b) && rowC.getOrNull(ia - 2).mayBe(c)) &&
|
||||
!(rowB.getOrNull(ia + 1).mayBe(b) && rowC.getOrNull(ia + 2).mayBe(c))
|
||||
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
if (ib != -1) {
|
||||
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))
|
||||
|
||||
ib - 1 -> rowA[ib + 1].mayBe(a)
|
||||
ib + 1 -> rowA[ib - 1].mayBe(a)
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
if (ic != -1) {
|
||||
return !(rowB.getOrNull(ic - 1).mayBe(b) && rowA.getOrNull(ic - 2).mayBe(a)) &&
|
||||
!(rowB.getOrNull(ic + 1).mayBe(b) && rowA.getOrNull(ic + 2).mayBe(a))
|
||||
}
|
||||
|
||||
for (i in 2 until grid.size) {
|
||||
if (rowA[i-2].selection == a){
|
||||
return rowB[i - 1].mayBe(b) &&
|
||||
rowC[i - 0].mayBe(c)
|
||||
}
|
||||
if (rowA[i - 2].mayBe(a) &&
|
||||
rowB[i - 1].mayBe(b) &&
|
||||
rowC[i - 0].mayBe(c)
|
||||
@@ -73,7 +154,7 @@ class TripletClue<C : ItemClass<C>>(val a: Item<C>, val b: Item<C>, val c: Item<
|
||||
}
|
||||
}
|
||||
|
||||
class SameRowClue<C : ItemClass<C>>(val a: Item<C>, val b: Item<C>) : Clue() {
|
||||
class SameColumnClue<A : ItemClass<A>, B : ItemClass<B>>(val a: Item<A>, val b: Item<B>) : Clue() {
|
||||
private val aType = a.itemType
|
||||
private val bType = b.itemType
|
||||
|
||||
@@ -81,8 +162,23 @@ class SameRowClue<C : ItemClass<C>>(val a: Item<C>, val b: Item<C>) : Clue() {
|
||||
val rowA = grid[aType.companion]
|
||||
val rowB = grid[bType.companion]
|
||||
|
||||
val ia = rowA.indexOf(aType)
|
||||
val ib by lazy { rowB.indexOf(bType) }
|
||||
|
||||
if (ia != -1) {
|
||||
return if (ib != -1) {
|
||||
ib != ia
|
||||
} else {
|
||||
!rowB[ia].mayBe(b)
|
||||
}
|
||||
}
|
||||
|
||||
if (ib != -1) {
|
||||
return !rowA[ib].mayBe(a)
|
||||
}
|
||||
|
||||
for (i in 0 until grid.size) {
|
||||
if (rowA[i].mayBe(a) && rowB[i].mayBe(b)) {
|
||||
if (!rowA[i].mayBe(a) && !rowB[i].mayBe(b)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -92,6 +188,9 @@ class SameRowClue<C : ItemClass<C>>(val a: Item<C>, val b: Item<C>) : Clue() {
|
||||
|
||||
class PositionClue<C : ItemClass<C>>(val item: Item<C>, val index: Int) : Clue() {
|
||||
override fun isRuleViolated(grid: Grid): Boolean {
|
||||
val i = grid.indexOf(item.itemType)
|
||||
if (i != -1) return i != index
|
||||
|
||||
return grid[item].mayBe(item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ private fun getAllClues(rows: List<List<Item<ItemClass<*>>>>): MutableSet<Clue>
|
||||
// Clue: Same Column
|
||||
rows.map { it[j] }.forEach {
|
||||
if (it != item) {
|
||||
clues.add(SameRowClue(item, it))
|
||||
clues.add(SameColumnClue(item, it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ class GameRow<C : ItemClass<C>>(
|
||||
val options: List<Item<C>>,
|
||||
val cells: List<GameCell<C>>
|
||||
) : List<GameCell<C>> by cells {
|
||||
fun indexOf(element: C) = indexOfFirst { it.selection?.itemType == element }
|
||||
|
||||
fun updateOptions() {
|
||||
val selections = mapNotNull { it.selection }
|
||||
forEach { it.options.removeAll(selections) }
|
||||
@@ -64,9 +66,10 @@ class GameCell<C : ItemClass<C>>(
|
||||
val options = options.toMutableStateList()
|
||||
var selection by mutableStateOf(selection)
|
||||
|
||||
fun mayBe(item: Item<C>) = 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,
|
||||
val symbol: String = itemType.symbols.random()
|
||||
|
||||
Reference in New Issue
Block a user