197 lines
5.7 KiB
Kotlin
197 lines
5.7 KiB
Kotlin
package domain
|
|
|
|
sealed class Clue {
|
|
abstract fun isRuleViolated(grid: Grid): Boolean
|
|
}
|
|
|
|
sealed class HorizontalClue : Clue()
|
|
|
|
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
|
|
|
|
override fun isRuleViolated(grid: Grid): Boolean {
|
|
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)
|
|
)
|
|
return false
|
|
|
|
if (rowA[i - 0].mayBe(a) &&
|
|
rowB[i - 1].mayBe(b)
|
|
)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
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
|
|
|
|
override fun isRuleViolated(grid: Grid): Boolean {
|
|
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<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 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].mayBe(a) &&
|
|
rowB[i - 1].mayBe(b) &&
|
|
rowC[i - 0].mayBe(c)
|
|
)
|
|
return false
|
|
|
|
if (rowA[i - 0].mayBe(a) &&
|
|
rowB[i - 1].mayBe(b) &&
|
|
rowC[i - 2].mayBe(c)
|
|
)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
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
|
|
|
|
override fun isRuleViolated(grid: Grid): Boolean {
|
|
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)) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|