Skip to content

Commit

Permalink
Extracts classes from MathDokuDLX
Browse files Browse the repository at this point in the history
  • Loading branch information
meikpiep committed Aug 2, 2023
1 parent 5a4d8ef commit d1e6166
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 145 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.holokenmod.creation.dlx

class ConstraintsFromGridCagesCalculator(
private val dlxGrid: DLXGrid,
private val numberOfCages: Int
) {
fun calculateConstraints(): List<BooleanArray> {
val contraints = mutableListOf<BooleanArray>()

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
}
}
Original file line number Diff line number Diff line change
@@ -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<BooleanArray> {
if (dlxGrid.gridSize.isSquare) {
return emptyList()
}

var cageId = dlxGrid.creators.size

val contraints = mutableListOf<BooleanArray>()

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<Int>, numberOfCopies: Int): Set<Set<Int>> {
return UniqueIndexSetsOfGivenLength(values, numberOfCopies).calculateProduct()
}

fun numberOfNodes(): Int {
return if (dlxGrid.gridSize.isSquare) {
0
} else {
dlxGrid.gridSize.largestSide() * cartesianProductOfRectangularPossibles.size * 200
}
}
}
Original file line number Diff line number Diff line change
@@ -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<Int, Int> {
return columnAndRowConstraints(
indexOfDigit,
creator.getCell(cellOfCage).column,
creator.getCell(cellOfCage).row
)
}

fun columnAndRowConstraints(
indexOfDigit: Int,
column: Int,
row: Int
): Pair<Int, Int> {
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
}

}
163 changes: 18 additions & 145 deletions holoken-core/src/main/kotlin/com/holokenmod/creation/dlx/MathDokuDLX.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.holokenmod.creation.dlx

import com.holokenmod.creation.cage.GridSingleCageCreator
import com.holokenmod.grid.Grid
import mu.KotlinLogging

Expand All @@ -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<BooleanArray>()

private var numberOfCages = 0

private fun initializeDLX() {
private fun initialize() {
// Number of columns = number of constraints =
// BOARD * BOARD (for columns) +
// BOARD * BOARD (for rows) +
Expand All @@ -36,51 +24,30 @@ 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
} else {
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<BooleanArray>) {
if (logger.isInfoEnabled) {
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))
Expand Down Expand Up @@ -112,126 +79,32 @@ class MathDokuDLX(
}
}

private fun cartesianProduct(values: List<Int>, numberOfCopies: Int): Set<Set<Int>> {
return UniqueIndexSetsOfGivenLength(values, numberOfCopies).calculateProduct()
}

private fun constraintsFromRectangular(creators: List<GridSingleCageCreator>): List<BooleanArray> {
if (grid.gridSize.isSquare) {
return emptyList()
}

var cageId = creators.size

val contraints = mutableListOf<BooleanArray>()

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<GridSingleCageCreator>): List<BooleanArray> {
val contraints = mutableListOf<BooleanArray>()

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<Int, Int> {
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<Int, Int> {
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)
}
}

0 comments on commit d1e6166

Please sign in to comment.