From d1e61669c4c318f9fcbe4b81225ec32025e9efb8 Mon Sep 17 00:00:00 2001 From: meikpiep Date: Wed, 2 Aug 2023 20:22:09 +0200 Subject: [PATCH] Extracts classes from MathDokuDLX --- .../dlx/ConstraintsFromGridCagesCalculator.kt | 50 ++++++ ...nstraintsFromRectangularGridsCalculator.kt | 68 ++++++++ .../com/holokenmod/creation/dlx/DLXGrid.kt | 47 +++++ .../holokenmod/creation/dlx/MathDokuDLX.kt | 163 ++---------------- 4 files changed, 183 insertions(+), 145 deletions(-) create mode 100644 holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/ConstraintsFromGridCagesCalculator.kt create mode 100644 holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/ConstraintsFromRectangularGridsCalculator.kt create mode 100644 holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/DLXGrid.kt diff --git a/holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/ConstraintsFromGridCagesCalculator.kt b/holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/ConstraintsFromGridCagesCalculator.kt new file mode 100644 index 00000000..fcd8c571 --- /dev/null +++ b/holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/ConstraintsFromGridCagesCalculator.kt @@ -0,0 +1,50 @@ +package com.holokenmod.creation.dlx + +class ConstraintsFromGridCagesCalculator( + private val dlxGrid: DLXGrid, + private val numberOfCages: Int +) { + fun calculateConstraints(): List { + val contraints = mutableListOf() + + for (creator in dlxGrid.creators) { + for (possibleCageCombination in creator.possibleNums) { + val constraint = BooleanArray( + dlxGrid.possibleDigits.size * (dlxGrid.gridSize.width + dlxGrid.gridSize.height) + + numberOfCages + ) + + for (i in possibleCageCombination.indices) { + val indexOfDigit = dlxGrid.digitSetting.indexOf(possibleCageCombination[i]) + + val (columnConstraint, rowConstraint) = dlxGrid.columnAndRowConstraints( + indexOfDigit, + creator, + i + ) + + constraint[columnConstraint] = true + constraint[rowConstraint] = true + } + + val cageConstraint = dlxGrid.cageConstraint(creator.id) + + constraint[cageConstraint] = true + + contraints += constraint + } + } + + return contraints + } + + fun numberOfNodes(): Int { + var result = 0 + + for (creator in dlxGrid.creators) { + result += creator.possibleNums.size * (2 * creator.numberOfCells + 1) + } + + return result + } +} diff --git a/holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/ConstraintsFromRectangularGridsCalculator.kt b/holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/ConstraintsFromRectangularGridsCalculator.kt new file mode 100644 index 00000000..ea1a4d9f --- /dev/null +++ b/holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/ConstraintsFromRectangularGridsCalculator.kt @@ -0,0 +1,68 @@ +package com.holokenmod.creation.dlx + +class ConstraintsFromRectangularGridsCalculator( + private val dlxGrid: DLXGrid, + private val numberOfCages: Int +) { + private val cartesianProductOfRectangularPossibles = if (dlxGrid.gridSize.isSquare) { + emptyList() + } else { + uniqueIndexSetsOfGivenLength( + dlxGrid.possibleDigits.indices.toList(), + dlxGrid.gridSize.largestSide() - dlxGrid.gridSize.smallestSide() + ) + } + + fun calculateConstraints(): List { + if (dlxGrid.gridSize.isSquare) { + return emptyList() + } + + var cageId = dlxGrid.creators.size + + val contraints = mutableListOf() + + for (rowOrColumn in 0 until dlxGrid.gridSize.largestSide()) { + for (indexesOfDigits in cartesianProductOfRectangularPossibles) { + val constraint = BooleanArray( + dlxGrid.possibleDigits.size * (dlxGrid.gridSize.width + dlxGrid.gridSize.height) + + numberOfCages + ) + + for (indexOfDigit in indexesOfDigits) { + val (columnConstraint, rowConstraint) = dlxGrid.columnAndRowConstraints( + indexOfDigit, + rowOrColumn, + rowOrColumn + ) + + if (dlxGrid.gridSize.width < dlxGrid.gridSize.height) { + constraint[rowConstraint] = true + } else { + constraint[columnConstraint] = true + } + + val cageConstraint = dlxGrid.cageConstraint(cageId) + constraint[cageConstraint] = true + } + + contraints += constraint + } + cageId++ + } + + return contraints + } + + private fun uniqueIndexSetsOfGivenLength(values: List, numberOfCopies: Int): Set> { + return UniqueIndexSetsOfGivenLength(values, numberOfCopies).calculateProduct() + } + + fun numberOfNodes(): Int { + return if (dlxGrid.gridSize.isSquare) { + 0 + } else { + dlxGrid.gridSize.largestSide() * cartesianProductOfRectangularPossibles.size * 200 + } + } +} diff --git a/holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/DLXGrid.kt b/holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/DLXGrid.kt new file mode 100644 index 00000000..45538d1a --- /dev/null +++ b/holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/DLXGrid.kt @@ -0,0 +1,47 @@ +package com.holokenmod.creation.dlx + +import com.holokenmod.creation.cage.GridSingleCageCreator +import com.holokenmod.grid.Grid + +class DLXGrid( + val grid: Grid +) { + val gridSize = grid.gridSize + val digitSetting = grid.options.digitSetting + val possibleDigits = digitSetting.getPossibleDigits(grid.gridSize) + + val creators = grid.cages.map { + GridSingleCageCreator(grid.variant, it) + } + + fun columnAndRowConstraints( + indexOfDigit: Int, + creator: GridSingleCageCreator, + cellOfCage: Int + ): Pair { + return columnAndRowConstraints( + indexOfDigit, + creator.getCell(cellOfCage).column, + creator.getCell(cellOfCage).row + ) + } + + fun columnAndRowConstraints( + indexOfDigit: Int, + column: Int, + row: Int + ): Pair { + val columnConstraint = grid.gridSize.width * indexOfDigit + column + val rowConstraint = ( + grid.gridSize.width * possibleDigits.size + + grid.gridSize.height * indexOfDigit + row + ) + + return Pair(columnConstraint, rowConstraint) + } + + fun cageConstraint(cageId: Int): Int { + return possibleDigits.size * (grid.gridSize.width + grid.gridSize.height) + cageId + } + +} diff --git a/holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/MathDokuDLX.kt b/holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/MathDokuDLX.kt index 0d7c3743..6908b7b6 100644 --- a/holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/MathDokuDLX.kt +++ b/holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/MathDokuDLX.kt @@ -1,6 +1,5 @@ package com.holokenmod.creation.dlx -import com.holokenmod.creation.cage.GridSingleCageCreator import com.holokenmod.grid.Grid import mu.KotlinLogging @@ -9,23 +8,12 @@ private val logger = KotlinLogging.logger {} class MathDokuDLX( private val grid: Grid ) { - private val digitSetting = grid.options.digitSetting - private val possibleDigits = digitSetting.getPossibleDigits(grid.gridSize) - private val cartesianProductOfRectingularPossibles = if (grid.gridSize.isSquare) { - emptyList() - } else { - cartesianProduct( - possibleDigits.indices.toList(), - grid.gridSize.largestSide() - grid.gridSize.smallestSide() - ) - } + private val dlxGrid = DLXGrid(grid) - private var dlx: DLX? = null - private var constraints = emptyList() private var numberOfCages = 0 - private fun initializeDLX() { + private fun initialize() { // Number of columns = number of constraints = // BOARD * BOARD (for columns) + // BOARD * BOARD (for rows) + @@ -36,19 +24,7 @@ class MathDokuDLX( // num_cells column constraints + // num_cells row constraints + // 1 (cage constraint) - var numberOfNodes = 0 - - val creators = grid.cages.map { - GridSingleCageCreator(grid.variant, it) - } - for (creator in creators) { - numberOfNodes += creator.possibleNums.size * (2 * creator.numberOfCells + 1) - } - - if (!grid.gridSize.isSquare) { - numberOfNodes += grid.gridSize.largestSide() * cartesianProductOfRectingularPossibles.size * 200 - } val extraRectingularCages = if (grid.gridSize.isSquare) { 0 @@ -56,16 +32,7 @@ class MathDokuDLX( grid.gridSize.largestSide() } - numberOfCages = creators.size + extraRectingularCages - - constraints = constraintsFromCages(creators) + constraintsFromRectangular(creators) - - dlx = DLX( - possibleDigits.size * (grid.gridSize.width + grid.gridSize.height) + numberOfCages, - numberOfNodes - ) - - logConstraints(constraints) + numberOfCages = dlxGrid.creators.size + extraRectingularCages } private fun logConstraints(constraints: List) { @@ -73,14 +40,14 @@ class MathDokuDLX( val headerCellId = StringBuilder() val headerValue = StringBuilder() - for (value in possibleDigits) { + for (value in dlxGrid.possibleDigits) { for (column in 0 until grid.gridSize.width) { headerCellId.append("c$column".padEnd(4)) headerValue.append("$value".padEnd(4)) } } - for (value in possibleDigits) { + for (value in dlxGrid.possibleDigits) { for (row in 0 until grid.gridSize.height) { headerCellId.append("r$row".padEnd(4)) headerValue.append("$value".padEnd(4)) @@ -112,126 +79,32 @@ class MathDokuDLX( } } - private fun cartesianProduct(values: List, numberOfCopies: Int): Set> { - return UniqueIndexSetsOfGivenLength(values, numberOfCopies).calculateProduct() - } - - private fun constraintsFromRectangular(creators: List): List { - if (grid.gridSize.isSquare) { - return emptyList() - } - - var cageId = creators.size - - val contraints = mutableListOf() - - for (rowOrColumn in 0 until grid.gridSize.largestSide()) { - for (indexesOfDigits in cartesianProductOfRectingularPossibles) { - val constraint = BooleanArray( - possibleDigits.size * (grid.gridSize.width + grid.gridSize.height) + - numberOfCages - ) - - for (indexOfDigit in indexesOfDigits) { - val (columnConstraint, rowConstraint) = columnAndRowConstraints( - indexOfDigit, - rowOrColumn, - rowOrColumn - ) - - if (grid.gridSize.width < grid.gridSize.height) { - constraint[rowConstraint] = true - } else { - constraint[columnConstraint] = true - } - - val cageConstraint = cageConstraint(cageId) - constraint[cageConstraint] = true - } - - contraints += constraint - } - cageId++ - } - - return contraints - } - - private fun constraintsFromCages(creators: List): List { - val contraints = mutableListOf() - - for (creator in creators) { - for (possibleCageCombination in creator.possibleNums) { - val constraint = BooleanArray( - possibleDigits.size * (grid.gridSize.width + grid.gridSize.height) + - numberOfCages - ) - - for (i in possibleCageCombination.indices) { - val indexOfDigit = digitSetting.indexOf(possibleCageCombination[i]) - - val (columnConstraint, rowConstraint) = columnAndRowConstraints( - indexOfDigit, - creator, - i - ) - - constraint[columnConstraint] = true - constraint[rowConstraint] = true - } - - val cageConstraint = cageConstraint(creator.id) + fun solve(type: DLX.SolveType): Int { + initialize() - constraint[cageConstraint] = true + val constraintCageCalculator = ConstraintsFromGridCagesCalculator(dlxGrid, numberOfCages) + val constraintRectangularCalculator = ConstraintsFromRectangularGridsCalculator(dlxGrid, numberOfCages) - contraints += constraint - } - } + val constraints = constraintCageCalculator.calculateConstraints() + + constraintRectangularCalculator.calculateConstraints() - return contraints - } + logConstraints(constraints) - private fun cageConstraint(cageId: Int): Int { - return possibleDigits.size * (grid.gridSize.width + grid.gridSize.height) + cageId - } + val numberOfNodes = constraintCageCalculator.numberOfNodes() + constraintRectangularCalculator.numberOfNodes() - private fun columnAndRowConstraints( - indexOfDigit: Int, - creator: GridSingleCageCreator, - cellOfCage: Int - ): Pair { - return columnAndRowConstraints( - indexOfDigit, - creator.getCell(cellOfCage).column, - creator.getCell(cellOfCage).row + val dlx = DLX( + dlxGrid.possibleDigits.size * (grid.gridSize.width + grid.gridSize.height) + numberOfCages, + numberOfNodes ) - } - - private fun columnAndRowConstraints( - indexOfDigit: Int, - column: Int, - row: Int - ): Pair { - val columnConstraint = grid.gridSize.width * indexOfDigit + column - val rowConstraint = ( - grid.gridSize.width * possibleDigits.size + - grid.gridSize.height * indexOfDigit + row - ) - - return Pair(columnConstraint, rowConstraint) - } - - fun solve(type: DLX.SolveType): Int { - initializeDLX() for ((currentCombination, constraint) in constraints.withIndex()) { for (constraintIndex in constraint.indices) { if (constraint[constraintIndex]) { - dlx!!.addNode(constraintIndex, currentCombination) + dlx.addNode(constraintIndex, currentCombination) } } } - return dlx!!.solve(type) + return dlx.solve(type) } }