package domain sealed class Clue { abstract fun isRuleViolated(grid: Grid): Boolean } sealed class HorizontalClue : Clue() class NeighbourClue, B : ItemClass>(val a: Item, val b: Item) : 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, R : ItemClass>(val left: Item, val right: Item) : 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, B : ItemClass, C : ItemClass>( val a: Item, val b: Item, val c: Item ) : 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, B : ItemClass>(val a: Item, val b: Item) : 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>(val item: Item, 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) } }