Skip to content

Commit

Permalink
wip PossibleMustBeContainedInSingleCageInLineDeleteFromOtherCages
Browse files Browse the repository at this point in the history
  • Loading branch information
meikpiep committed Nov 2, 2024
1 parent d868fab commit be812d3
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import org.piepmeyer.gauguin.difficulty.human.strategy.DetectPossibleUsedInLines
import org.piepmeyer.gauguin.difficulty.human.strategy.DetectPossiblesBreakingOtherCagesPossiblesDualLines
import org.piepmeyer.gauguin.difficulty.human.strategy.GridSumEnforcesCageSum
import org.piepmeyer.gauguin.difficulty.human.strategy.LineSingleCagePossiblesSumSingle
import org.piepmeyer.gauguin.difficulty.human.strategy.LineTwoCellsPossiblesSumSingle
import org.piepmeyer.gauguin.difficulty.human.strategy.LinesSingleCagePossiblesSumDual
import org.piepmeyer.gauguin.difficulty.human.strategy.LinesSingleCagePossiblesSumTriple
import org.piepmeyer.gauguin.difficulty.human.strategy.NakedPair
import org.piepmeyer.gauguin.difficulty.human.strategy.NakedTriple
import org.piepmeyer.gauguin.difficulty.human.strategy.NumberOfCagesWithPossibleForcesPossibleInCage
import org.piepmeyer.gauguin.difficulty.human.strategy.OddEvenCheckGridSum
import org.piepmeyer.gauguin.difficulty.human.strategy.OddEvenCheckSumDual
import org.piepmeyer.gauguin.difficulty.human.strategy.OddEvenCheckSumSingle
Expand All @@ -22,6 +22,9 @@ import org.piepmeyer.gauguin.difficulty.human.strategy.RemovePossibleWithoutComb
import org.piepmeyer.gauguin.difficulty.human.strategy.SinglePossibleInCage
import org.piepmeyer.gauguin.difficulty.human.strategy.SinglePossibleInCell
import org.piepmeyer.gauguin.difficulty.human.strategy.SinglePossibleInLine
import org.piepmeyer.gauguin.difficulty.human.strategy.TwoCellsPossiblesSumSingleLine
import org.piepmeyer.gauguin.difficulty.human.strategy.TwoCellsPossiblesSumThreeLines
import org.piepmeyer.gauguin.difficulty.human.strategy.TwoCellsPossiblesSumTwoLines
import org.piepmeyer.gauguin.difficulty.human.strategy.XWing

enum class HumanSolverStrategies(
Expand All @@ -41,7 +44,12 @@ enum class HumanSolverStrategies(
ANakedTriple(50, NakedTriple()),

ASingleLinePossiblesSum(80, LineSingleCagePossiblesSumSingle()),
ALineTwoCellsPossiblesSumSingle(85, LineTwoCellsPossiblesSumSingle()),
ATwoCellsPossiblesSumSingleLine(85, TwoCellsPossiblesSumSingleLine()),
ATwoCellsPossiblesSumTwoLines(86, TwoCellsPossiblesSumTwoLines()),
ATwoCellsPossiblesSumThreeLines(87, TwoCellsPossiblesSumThreeLines()),

ANumberOfCagesWithPossibleForcesPossibleInCage(89, NumberOfCagesWithPossibleForcesPossibleInCage()),

AOddEvenCheckSumSingle(90, OddEvenCheckSumSingle()),
ADetectPossiblesBreakingOtherCagesPossiblesDualLines(95, DetectPossiblesBreakingOtherCagesPossiblesDualLines()),
ADetectPossibleUsedInLinesByOtherCagesDualLines(98, DetectPossibleUsedInLinesByOtherCagesDualLines()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import org.piepmeyer.gauguin.difficulty.human.PossiblesCache
import org.piepmeyer.gauguin.grid.Grid
import org.piepmeyer.gauguin.grid.GridCell

abstract class AbstractLinesTwoCellsPossiblesSum(
abstract class AbstractTwoCellsPossiblesSum(
private val numberOfLines: Int,
) : HumanSolverStrategy {
override fun fillCells(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package org.piepmeyer.gauguin.difficulty.human.strategy

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

class NumberOfCagesWithPossibleForcesPossibleInCage : HumanSolverStrategy {
override fun fillCells(
grid: Grid,
cache: PossiblesCache,
): Boolean {
grid.variant.possibleDigits.forEach { possible ->
val numberOfPossiblesLeft = grid.variant.gridSize.smallestSide() - grid.cells.count { it.userValue == possible }

val cagesWithPossible =
grid.cages
.filter { it.cells.any { !it.isUserValueSet } }
.filter { it.cells.any { it.possibles.contains(possible) } }

val cagesWithStaticNumberOfPossible =
cagesWithPossible.filter {
val firstAmountOfPossible =
cache
.calculatePossibles(it)
.first()
.filterIndexed { index, _ -> !it.cells[index].isUserValueSet }
.count { it == possible }

cache.calculatePossibles(it).all { it.count { it == possible } == firstAmountOfPossible }
}

val staticNumberOfPossibles =
cagesWithStaticNumberOfPossible.sumOf {
cache
.calculatePossibles(it)
.first()
.filterIndexed { index, _ -> !it.cells[index].isUserValueSet }
.count { it == possible }
}

val cagesWithDynamicNumberOfPossible = cagesWithPossible - cagesWithStaticNumberOfPossible

if (staticNumberOfPossibles == numberOfPossiblesLeft) {
/*
* All possibles are already contained in the static cages, so we delete
* the possible from all other cages.
*/
cagesWithDynamicNumberOfPossible.forEach { dynamicCage ->
val reduced =
PossiblesReducer(dynamicCage).reduceToPossibleCombinations(
cache
.calculatePossibles(dynamicCage)
.filter {
it
.filterIndexed { index, value ->
!dynamicCage.cells[index].isUserValueSet && value == possible
}.isEmpty()
},
)

if (reduced) {
return true
}
}
} else if (cagesWithDynamicNumberOfPossible.size == 1 && staticNumberOfPossibles == numberOfPossiblesLeft - 1) {
val dynamicCage = cagesWithDynamicNumberOfPossible.first()

val reduced =
PossiblesReducer(dynamicCage).reduceToPossibleCombinations(
cache
.calculatePossibles(dynamicCage)
.filter {
it
.filterIndexed { index, _ -> !dynamicCage.cells[index].isUserValueSet }
.count { it == possible } == 1
},
)

if (reduced) {
return true
}
}
}

return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ package org.piepmeyer.gauguin.difficulty.human.strategy
* excluding one part of cage. The sum of this part of cages is calculated all enforced by deleting
* deviant possibles.
*/
class LineTwoCellsPossiblesSumSingle : AbstractLinesTwoCellsPossiblesSum(1)
class TwoCellsPossiblesSumSingleLine : AbstractTwoCellsPossiblesSum(1)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.piepmeyer.gauguin.difficulty.human.strategy

/**
* Scans three lines to find that each part of cage contained in this lines has a static sum
* excluding one part of cage. The sum of this part of cages is calculated all enforced by deleting
* deviant possibles.
*/
class TwoCellsPossiblesSumThreeLines : AbstractTwoCellsPossiblesSum(3)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.piepmeyer.gauguin.difficulty.human.strategy

/**
* Scans two lines to find that each part of cage contained in this lines has a static sum
* excluding one part of cage. The sum of this part of cages is calculated all enforced by deleting
* deviant possibles.
*/
class TwoCellsPossiblesSumTwoLines : AbstractTwoCellsPossiblesSum(2)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package org.piepmeyer.gauguin.difficulty.human
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import org.piepmeyer.gauguin.creation.MergingCageGridCalculator
import org.piepmeyer.gauguin.creation.RandomCageGridCalculator
import org.piepmeyer.gauguin.creation.RandomPossibleDigitsShuffler
import org.piepmeyer.gauguin.creation.SeedRandomizerMock
import org.piepmeyer.gauguin.grid.GridSize
Expand All @@ -13,10 +12,10 @@ import org.piepmeyer.gauguin.options.GameVariant
class HumanDifficultySolverHandpickedTest :
FunSpec({
test("seed random grid should be solved") {
val randomizer = SeedRandomizerMock(9184)
val randomizer = SeedRandomizerMock(3252) // 1495, 2281

val calculator =
RandomCageGridCalculator(
MergingCageGridCalculator(
GameVariant(
GridSize(4, 4),
GameOptionsVariant.createClassic(),
Expand All @@ -28,7 +27,7 @@ class HumanDifficultySolverHandpickedTest :
val grid = calculator.calculate()
grid.cells.forEach { it.possibles = grid.variant.possibleDigits }

val solver = HumanSolver(grid)
val solver = HumanSolver(grid, true)

solver.solveAndCalculateDifficulty()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import io.kotest.assertions.withClue
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeGreaterThan
import io.kotest.matchers.shouldBe
import org.piepmeyer.gauguin.creation.RandomCageGridCalculator
import org.piepmeyer.gauguin.creation.MergingCageGridCalculator
import org.piepmeyer.gauguin.creation.RandomPossibleDigitsShuffler
import org.piepmeyer.gauguin.creation.SeedRandomizerMock
import org.piepmeyer.gauguin.game.save.SaveGame
Expand All @@ -16,15 +16,15 @@ import java.io.File
class HumanDifficultySolverTest :
FunSpec({
for (seed in 0..9999) {
// 10_000 of 4x4, random: 6 left unsolved
// 10_000 of 4x4, merge: 27 left unsolved
// 10_000 of 4x4, random: 4 left unsolved
// 10_000 of 4x4, merge: 19 left unsolved
// 10_000 of 5x5, merge: 153 left unsolved
withClue("seed $seed") {
test("seed random grid should be solved") {
val randomizer = SeedRandomizerMock(seed)

val calculator =
RandomCageGridCalculator(
MergingCageGridCalculator(
GameVariant(
GridSize(4, 4),
GameOptionsVariant.createClassic(),
Expand Down

0 comments on commit be812d3

Please sign in to comment.