Skip to content

Commit

Permalink
wip PairOfPossiblesExaustingTwoLines
Browse files Browse the repository at this point in the history
  • Loading branch information
meikpiep committed Nov 28, 2024
1 parent 3f8167e commit 210ffa8
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.piepmeyer.gauguin.difficulty.human.strategy

import org.piepmeyer.gauguin.difficulty.human.GridLineType
import org.piepmeyer.gauguin.difficulty.human.GridLines
import org.piepmeyer.gauguin.difficulty.human.HumanSolverStrategy
import org.piepmeyer.gauguin.difficulty.human.PossiblesCache
Expand All @@ -18,30 +17,7 @@ class DetectPossibleUsedInLinesByOtherCagesDualLines : HumanSolverStrategy {
val cellsOfLines =
dualLines.map { it.cells() }.flatten()

val cagesIntersectingWithLines =
dualLines
.map { it.cages() }
.flatten()
.filter { it.cells.any { !it.isUserValueSet && cellsOfLines.contains(it) } }
.toSet()

val possiblesInLines =
cagesIntersectingWithLines.associateWith { cage ->
cache
.possibles(cage)
.map {
it.filterIndexed {
index,
_,
->
!cage.cells[index].isUserValueSet && cellsOfLines.contains(cage.cells[index])
}
}.toSet()
}

if (dualLines.all { it.type == GridLineType.ROW } && dualLines.all { it.lineNumber == 6 || it.lineNumber == 7 }) {
println("hau!")
}
val (cagesIntersectingWithLines, possiblesInLines) = GridLineHelper.getIntersectingCagesAndPossibles(dualLines, cache)

cagesIntersectingWithLines.forEach { cage ->
val possiblesForFirstCage = possiblesInLines[cage]!!
Expand All @@ -55,9 +31,6 @@ class DetectPossibleUsedInLinesByOtherCagesDualLines : HumanSolverStrategy {
cagesIntersectingWithLines
.filter { it.id > cage.id }
.forEach { otherCage ->
if (cage.id == 3 && otherCage.id == 15) {
println("Hau2")
}
val possiblesForOtherCage = possiblesInLines[otherCage]!!

val possibleInEachOtherCageCombination =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.piepmeyer.gauguin.difficulty.human.strategy

import org.piepmeyer.gauguin.difficulty.human.GridLine
import org.piepmeyer.gauguin.difficulty.human.PossiblesCache
import org.piepmeyer.gauguin.grid.GridCage

object GridLineHelper {
fun getIntersectingCagesAndPossibles(
dualLines: Set<GridLine>,
cache: PossiblesCache,
): Pair<Set<GridCage>, Map<GridCage, Set<List<Int>>>> {
val cellsOfLines =
dualLines.map { it.cells() }.flatten()

val cagesIntersectingWithLines =
dualLines
.map { it.cages() }
.flatten()
.filter { it.cells.any { !it.isUserValueSet && cellsOfLines.contains(it) } }
.toSet()

val possiblesInLines =
cagesIntersectingWithLines.associateWith { cage ->
cache
.possibles(cage)
.map {
it.filterIndexed {
index,
_,
->
!cage.cells[index].isUserValueSet && cellsOfLines.contains(cage.cells[index])
}
}.toSet()
}

return Pair(cagesIntersectingWithLines, possiblesInLines)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.piepmeyer.gauguin.difficulty.human.strategy

import org.piepmeyer.gauguin.difficulty.human.GridLines
import org.piepmeyer.gauguin.difficulty.human.HumanSolverStrategy
import org.piepmeyer.gauguin.difficulty.human.PossiblesCache
import org.piepmeyer.gauguin.grid.Grid

/**
* Finds a set of two possibles which occur in two lines and counts its minimum existence in all
* combinations.
*/
class PairOfPossiblesExaustingTwoLines : HumanSolverStrategy {
override fun fillCells(
grid: Grid,
cache: PossiblesCache,
): Boolean {
val lines = GridLines(grid).adjacentlines(2)

lines.forEach { dualLines ->

val (cagesIntersectingWithLines, possiblesInLines) = GridLineHelper.getIntersectingCagesAndPossibles(dualLines, cache)

val combinations =
grid.variant.possibleDigits.flatMap { firstPossible ->
(grid.variant.possibleDigits - firstPossible).map { setOf(it, firstPossible) }
}

combinations.forEach { combinationOfPossibles ->
val cageMinimumOccurences =
cagesIntersectingWithLines.associateWith { cage ->
possiblesInLines[cage]!!.minOf { combinationOfPossibles.intersect(it).size }
}

val minimumOccurences = cageMinimumOccurences.values.sum()

if (minimumOccurences > 1) {
cageMinimumOccurences.forEach { (cage, minimumOccurence) ->
val occurencesLeft = 4 - minimumOccurences + minimumOccurence

val invalidPossibles =
possiblesInLines[cage]!!.filter {
combinationOfPossibles
.intersect(
it,
).size > occurencesLeft
}

if (invalidPossibles.isNotEmpty()) {
val validPossibles = possiblesInLines - invalidPossibles

/*val reduced = PossiblesReducer(cage).reduceToPossibleCombinations(validPossibles)
if (reduced) {
return true
}*/
}
}
}
}
}

return false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package org.piepmeyer.gauguin.difficulty.human.strategy

import io.kotest.assertions.assertSoftly
import io.kotest.assertions.withClue
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldContainExactly
import io.kotest.matchers.shouldBe
import org.piepmeyer.gauguin.creation.GridBuilder
import org.piepmeyer.gauguin.creation.cage.GridCageType
import org.piepmeyer.gauguin.difficulty.human.PossiblesCache
import org.piepmeyer.gauguin.grid.GridCageAction

class PairOfPossiblesExaustingTwoLinesTest :
FunSpec({

test("2x6 grid") {
val grid =
GridBuilder(2, 6)
.addCage(
2,
GridCageAction.ACTION_SUBTRACT,
GridCageType.DOUBLE_HORIZONTAL,
0,
).addCage(
1,
GridCageAction.ACTION_SUBTRACT,
GridCageType.DOUBLE_HORIZONTAL,
2,
).addCage(
18,
GridCageAction.ACTION_MULTIPLY,
GridCageType.DOUBLE_HORIZONTAL,
4,
).addSingleCage(2, 6)
.addSingleCage(5, 7)
.addCage(
3,
GridCageAction.ACTION_DIVIDE,
GridCageType.DOUBLE_HORIZONTAL,
8,
).addCage(
9,
GridCageAction.ACTION_ADD,
GridCageType.DOUBLE_HORIZONTAL,
10,
).createGrid()

grid.cells[0].possibles = setOf(1, 3, 4, 5, 6)
grid.cells[1].possibles = setOf(1, 2, 3, 4, 6)
grid.cells[2].possibles = setOf(1, 3, 4, 5)
grid.cells[3].possibles = setOf(2, 3, 4)
grid.cells[4].possibles = setOf(3, 6)
grid.cells[5].possibles = setOf(3, 6)
grid.cells[6].userValue = 2
grid.cells[7].userValue = 5
grid.cells[8].possibles = setOf(1, 3, 6)
grid.cells[9].possibles = setOf(1, 2, 3)
grid.cells[10].possibles = setOf(3, 5, 6)
grid.cells[11].possibles = setOf(3, 4, 6)

val solver = PairOfPossiblesExaustingTwoLines()

println(grid)

solver.fillCells(grid, PossiblesCache(grid)) shouldBe true

println(grid)

assertSoftly {
withClue("possibles should be deleted from other cells") {
grid.cells[0].possibles shouldContainExactly setOf(1, 3, 4, 5, 6)
grid.cells[1].possibles shouldContainExactly setOf(1, 2, 3, 4, 6)
grid.cells[2].possibles shouldContainExactly setOf(1, 3, 4, 5)
grid.cells[3].possibles shouldContainExactly setOf(2, 3, 4)
grid.cells[8].possibles shouldContainExactly setOf(1, 3, 6)
grid.cells[9].possibles shouldContainExactly setOf(1, 2, 3)
grid.cells[10].possibles shouldContainExactly setOf(5)
grid.cells[11].possibles shouldContainExactly setOf(4)
}
}
}
})

0 comments on commit 210ffa8

Please sign in to comment.