Further cleanup, solvers are now separate implementations of CrossSolver
This commit is contained in:
parent
16dfcc33cd
commit
af89c48970
@ -3,6 +3,30 @@ package be.nielandt
|
|||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
|
open abstract class CrossSolver {
|
||||||
|
/**
|
||||||
|
* Solve all crosses, look for a minimal set of moves for each color's cross.
|
||||||
|
*/
|
||||||
|
abstract fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Move>>
|
||||||
|
|
||||||
|
fun solveCrossesTimed(edgeModel: EdgeModel): Map<Int, List<Move>> {
|
||||||
|
val now = Instant.now()
|
||||||
|
val solveCrosses = solveCrosses(edgeModel)
|
||||||
|
val between = Duration.between(now, Instant.now())
|
||||||
|
println("Solving crosses took ${between.seconds}s")
|
||||||
|
return solveCrosses
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun printResults(results: Map<Int, List<Move>>) {
|
||||||
|
println("results: ")
|
||||||
|
results.forEach { color, moveList ->
|
||||||
|
println("> color ${colorLetter(color)}, moves $moveList")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Let's look for symmetries.
|
* Let's look for symmetries.
|
||||||
*/
|
*/
|
||||||
@ -37,241 +61,26 @@ fun main(args: Array<String>) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
// make a beginning model, then start doing crazy shit
|
// do a fixed scramble for testing purposes
|
||||||
|
val fixedMoves = Move.parse("L L_ R R2")
|
||||||
|
println("fixedMoves = ${fixedMoves}")
|
||||||
|
|
||||||
|
|
||||||
|
// scramble random
|
||||||
val moves = Move.random(20)
|
val moves = Move.random(20)
|
||||||
println("Scramble: $moves")
|
println("Scramble: $moves")
|
||||||
val scrambledModel = EdgeModel(moves)
|
val scrambledModel = EdgeModel(moves)
|
||||||
println(scrambledModel)
|
println(scrambledModel)
|
||||||
|
|
||||||
// doAllCrossMoveCounts(scrambledModel)
|
val baseSolve = CrossSolverBase().solveCrossesTimed(scrambledModel)
|
||||||
// val allCrossMoveCount = allCrossMoveCount(scrambledModel)
|
CrossSolver.printResults(baseSolve)
|
||||||
// allCrossMoveCount.forEach { color, moves ->
|
|
||||||
// println("cross for color: ${color} in ${moves.size}: ${moves.joinToString(" ")}")
|
val upgradedSolve = CrossSolverUpgraded().solveCrossesTimed(scrambledModel)
|
||||||
|
CrossSolver.printResults(upgradedSolve)
|
||||||
|
|
||||||
|
// val allCrossMoveCountUpgradedSkip = allCrossMoveCountUpgradedSkip(scrambledModel)
|
||||||
|
// allCrossMoveCountUpgradedSkip.forEach { color, moves ->
|
||||||
|
// println("skip upgrade cross for color: ${color} in ${moves.size}: ${moves.joinToString(" ")}")
|
||||||
// }
|
// }
|
||||||
val allCrossMoveCountUpgraded = allCrossMoveCountUpgraded(scrambledModel)
|
|
||||||
allCrossMoveCountUpgraded.forEach { color, moves ->
|
|
||||||
println("upgrade cross for color: ${color} in ${moves.size}: ${moves.joinToString(" ")}")
|
|
||||||
}
|
|
||||||
val allCrossMoveCountUpgradedSkip = allCrossMoveCountUpgradedSkip(scrambledModel)
|
|
||||||
allCrossMoveCountUpgradedSkip.forEach { color, moves ->
|
|
||||||
println("skip upgrade cross for color: ${color} in ${moves.size}: ${moves.joinToString(" ")}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Do the color solves separately. Not very efficient, this rehashes a lot of things.
|
|
||||||
*/
|
|
||||||
fun doAllCrossMoveCounts(edgeModel: EdgeModel) {
|
|
||||||
for (c: Color in Color.values()) {
|
|
||||||
val crossMoveCount = crossMoveCount(edgeModel, c)
|
|
||||||
println("${c} in ${crossMoveCount?.size}: ${crossMoveCount?.joinToString()}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For a single color, go 8 deep and try and find the minimal amount of moves to solve that cross.
|
|
||||||
*/
|
|
||||||
fun crossMoveCount(edgeModel: EdgeModel, color: Color): List<Move>? {
|
|
||||||
val moveCounts = Array<List<Move>?>(6) { null }
|
|
||||||
|
|
||||||
for (moveCount in 1..8) {
|
|
||||||
// build a counter of moveCount big
|
|
||||||
val counter = Counter(moveCount, Move.values().size)
|
|
||||||
|
|
||||||
// count up, each state of the counter corresponds to a combination of moves
|
|
||||||
do {
|
|
||||||
// what is the move combination we're looking at?
|
|
||||||
val moves = Move.combo(counter)
|
|
||||||
// execute the moves
|
|
||||||
val afterMoves = edgeModel.doMoves(moves)
|
|
||||||
val crossSolved = afterMoves.crossSolved(color)
|
|
||||||
if (crossSolved) {
|
|
||||||
return@crossMoveCount moves
|
|
||||||
}
|
|
||||||
|
|
||||||
} while (counter.increase())
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Solve the minimal cross for all colors. Try to upgrade the method... Can we cache the 'previous results'?
|
|
||||||
*/
|
|
||||||
fun allCrossMoveCountUpgradedSkip(edgeModel: EdgeModel): Map<Color, List<Move>> {
|
|
||||||
val start = Instant.now()
|
|
||||||
val moveCounts = mutableMapOf<Color, List<Move>>()
|
|
||||||
|
|
||||||
for (moveCount in 1..8) {
|
|
||||||
println("all cross move count upgrade doing $moveCount")
|
|
||||||
// build a counter of moveCount big
|
|
||||||
val counter = Counter(moveCount, Move.values().size)
|
|
||||||
val edgeModelFactory = EdgeModelFactory(edgeModel, counter, true)
|
|
||||||
|
|
||||||
println("moveCounts = ${moveCounts}")
|
|
||||||
|
|
||||||
while (edgeModelFactory.hasNext()) {
|
|
||||||
// get the next model, using the internal counter which simply iterates over possible combinations of moves
|
|
||||||
val next = edgeModelFactory.getNext()
|
|
||||||
|
|
||||||
// check crosses that have not been found yet
|
|
||||||
Color.values().forEach { color ->
|
|
||||||
if (!moveCounts.containsKey(color)) {
|
|
||||||
val crossSolved = next.crossSolved(color)
|
|
||||||
if (crossSolved) {
|
|
||||||
// what is the move combination we're looking at?
|
|
||||||
val moves = Move.combo(counter)
|
|
||||||
moveCounts[color] = moves
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// break if we have found hem all
|
|
||||||
if (moveCounts.keys.size == Color.values().size) {
|
|
||||||
println("Execution time: ${Duration.between(start, Instant.now()).toMillis() / 1000}s")
|
|
||||||
// println("counter.skipInvalidCount = ${counter.skipInvalidCount}")
|
|
||||||
return moveCounts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println("Execution time: ${Duration.between(start, Instant.now()).toMillis() / 1000}s")
|
|
||||||
return moveCounts
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Solve the minimal cross for all colors. Try to upgrade the method... Can we cache the 'previous results'?
|
|
||||||
*/
|
|
||||||
fun allCrossMoveCountUpgraded(edgeModel: EdgeModel): Map<Color, List<Move>> {
|
|
||||||
val start = Instant.now()
|
|
||||||
val moveCounts = mutableMapOf<Color, List<Move>>()
|
|
||||||
|
|
||||||
for (moveCount in 1..8) {
|
|
||||||
println("all cross move count upgrade doing $moveCount")
|
|
||||||
// build a counter of moveCount big
|
|
||||||
val counter = Counter(moveCount, Move.values().size)
|
|
||||||
val edgeModelFactory = EdgeModelFactory(edgeModel, counter)
|
|
||||||
|
|
||||||
while (edgeModelFactory.hasNext()) {
|
|
||||||
// get the next model, using the internal counter which simply iterates over possible combinations of moves
|
|
||||||
val next = edgeModelFactory.getNext()
|
|
||||||
|
|
||||||
// check crosses that have not been found yet
|
|
||||||
Color.values().forEach { color ->
|
|
||||||
if (!moveCounts.containsKey(color)) {
|
|
||||||
val crossSolved = next.crossSolved(color)
|
|
||||||
if (crossSolved) {
|
|
||||||
// what is the move combination we're looking at?
|
|
||||||
val moves = Move.combo(counter)
|
|
||||||
moveCounts[color] = moves
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// break if we have found hem all
|
|
||||||
if (moveCounts.keys.size == Color.values().size) {
|
|
||||||
println("Execution time: ${Duration.between(start, Instant.now()).toMillis() / 1000}s")
|
|
||||||
return moveCounts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println("Execution time: ${Duration.between(start, Instant.now()).toMillis() / 1000}s")
|
|
||||||
return moveCounts
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This thing helps us to create edgemodels using a counter. The advantage is that the edgemodel doesn't need to be calculated
|
|
||||||
* completely from scratch: previous states are kept, so if, e.g., the third digit changes in the counter (of length 5),
|
|
||||||
* the previous state that was calculated using the first two states is used to perform move 3,4,5 on.
|
|
||||||
*
|
|
||||||
* This is probably equivalent to 8 nested for loops, you'd be able to keep track of temporary solutions there too....
|
|
||||||
*/
|
|
||||||
class EdgeModelFactory(val original: EdgeModel, val counter: Counter, val skip: Boolean = false) {
|
|
||||||
// keep a modified version of the edgemodel for each digit in the counter, from left to right
|
|
||||||
private val history: MutableList<EdgeModel> = mutableListOf()
|
|
||||||
|
|
||||||
// we always have one in the beginning: the initial state of the counter
|
|
||||||
private var hasNext: Boolean = true
|
|
||||||
|
|
||||||
init {
|
|
||||||
// init the history
|
|
||||||
this.history.add(original.doMove(Move.values()[counter.digit(0)]))
|
|
||||||
for (i in 1 until counter.size()) {
|
|
||||||
this.history.add(this.history.last().doMove(Move.values()[counter.digit(i)]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun hasNext(): Boolean {
|
|
||||||
return hasNext
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getNext(): EdgeModel {
|
|
||||||
// the counter was increased, hooray
|
|
||||||
val lastOverflowIndex = counter.getLastModifiedIndex()
|
|
||||||
// we only need to redo everything starting from the lastoverflowindex
|
|
||||||
// these are our moves, but we can salvage everything up to lastoverflowindex
|
|
||||||
val moves = Move.combo(counter)
|
|
||||||
// we have a history to work with... only redo what's necessary
|
|
||||||
for (i in counter.getLastModifiedIndex() until counter.size()) {
|
|
||||||
var start: EdgeModel?
|
|
||||||
start = if (i == 0)
|
|
||||||
original
|
|
||||||
else
|
|
||||||
history[i - 1]
|
|
||||||
history[i] = start.doMove(Move.values()[counter.digit(i)])
|
|
||||||
}
|
|
||||||
// increase the counter for next time
|
|
||||||
if(!skip) {
|
|
||||||
if (!counter.increase()) {
|
|
||||||
this.hasNext = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!counter.increaseAndSkipInvalid()) {
|
|
||||||
this.hasNext = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// the last item in the history is now the edgemodel we need to test...
|
|
||||||
return history.last()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Solve the minimal cross for all colors.
|
|
||||||
*/
|
|
||||||
fun allCrossMoveCount(edgeModel: EdgeModel): Map<Color, List<Move>> {
|
|
||||||
val start = Instant.now()
|
|
||||||
val moveCounts = mutableMapOf<Color, List<Move>>()
|
|
||||||
|
|
||||||
for (moveCount in 1..8) {
|
|
||||||
// build a counter of moveCount big
|
|
||||||
println("allCrossMoveCount basic doing $moveCount")
|
|
||||||
val counter = Counter(moveCount, Move.values().size)
|
|
||||||
|
|
||||||
// count up, each state of the counter corresponds to a combination of moves
|
|
||||||
do {
|
|
||||||
// what is the move combination we're looking at?
|
|
||||||
val moves = Move.combo(counter)
|
|
||||||
// execute the moves
|
|
||||||
val afterMoves = edgeModel.doMoves(moves)
|
|
||||||
// check crosses that have not been found yet
|
|
||||||
Color.values().forEach { color ->
|
|
||||||
if (!moveCounts.containsKey(color)) {
|
|
||||||
val crossSolved = afterMoves.crossSolved(color)
|
|
||||||
if (crossSolved) {
|
|
||||||
moveCounts[color] = moves
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (moveCounts.keys.size == Color.values().size) {
|
|
||||||
println("Execution time: ${Duration.between(start, Instant.now()).toMillis() / 1000}s")
|
|
||||||
return@allCrossMoveCount moveCounts
|
|
||||||
}
|
|
||||||
|
|
||||||
} while (counter.increase())
|
|
||||||
}
|
|
||||||
println("Execution time: ${Duration.between(start, Instant.now()).toMillis() / 1000}s")
|
|
||||||
return moveCounts
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the color into a single digit for print purposes.
|
|
||||||
*/
|
|
||||||
fun l(c: Color): String {
|
|
||||||
return c.name.first().toString()
|
|
||||||
}
|
|
||||||
|
|||||||
40
src/main/kotlin/be/nielandt/CrossSolverBase.kt
Normal file
40
src/main/kotlin/be/nielandt/CrossSolverBase.kt
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package be.nielandt
|
||||||
|
|
||||||
|
class CrossSolverBase : CrossSolver() {
|
||||||
|
/**
|
||||||
|
* Solve the minimal cross for all colors.
|
||||||
|
*/
|
||||||
|
override fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Move>> {
|
||||||
|
val moveCounts = mutableMapOf<Int, List<Move>>()
|
||||||
|
|
||||||
|
for (moveCount in 1..8) {
|
||||||
|
// build a counter of moveCount big
|
||||||
|
println("allCrossMoveCount basic doing $moveCount")
|
||||||
|
val counter = Counter(moveCount, Move.values().size)
|
||||||
|
|
||||||
|
// count up, each state of the counter corresponds to a combination of moves
|
||||||
|
do {
|
||||||
|
// what is the move combination we're looking at?
|
||||||
|
val moves = Move.combo(counter)
|
||||||
|
// execute the moves
|
||||||
|
val afterMoves = edgeModel.doMoves(moves)
|
||||||
|
// check crosses that have not been found yet
|
||||||
|
(0..5).forEach { color ->
|
||||||
|
if (!moveCounts.containsKey(color)) {
|
||||||
|
val crossSolved = afterMoves.crossSolved(color)
|
||||||
|
if (crossSolved) {
|
||||||
|
moveCounts[color] = moves
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moveCounts.keys.size == 6) {
|
||||||
|
return@solveCrosses moveCounts
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (counter.increase())
|
||||||
|
}
|
||||||
|
return moveCounts
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
43
src/main/kotlin/be/nielandt/CrossSolverUpgraded.kt
Normal file
43
src/main/kotlin/be/nielandt/CrossSolverUpgraded.kt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package be.nielandt
|
||||||
|
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This solver avoids redoing edgemodel manipulations. Should be equivalent to X nested for loops.
|
||||||
|
*/
|
||||||
|
class CrossSolverUpgraded : CrossSolver() {
|
||||||
|
|
||||||
|
override fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Move>> {
|
||||||
|
val start = Instant.now()
|
||||||
|
val moveCounts = mutableMapOf<Int, List<Move>>()
|
||||||
|
|
||||||
|
for (moveCount in 1..8) {
|
||||||
|
println("all cross move count upgrade doing $moveCount")
|
||||||
|
// build a counter of moveCount big
|
||||||
|
val counter = Counter(moveCount, Move.values().size)
|
||||||
|
val edgeModelFactory = EdgeModelFactory(edgeModel, counter)
|
||||||
|
|
||||||
|
while (edgeModelFactory.hasNext()) {
|
||||||
|
// get the next model, using the internal counter which simply iterates over possible combinations of moves
|
||||||
|
val next = edgeModelFactory.getNext()
|
||||||
|
|
||||||
|
// check crosses that have not been found yet
|
||||||
|
(0..5).forEach { color ->
|
||||||
|
if (!moveCounts.containsKey(color)) {
|
||||||
|
val crossSolved = next.crossSolved(color)
|
||||||
|
if (crossSolved) {
|
||||||
|
// what is the move combination we're looking at?
|
||||||
|
val moves = Move.combo(counter)
|
||||||
|
moveCounts[color] = moves
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// break if we have found hem all
|
||||||
|
if (moveCounts.keys.size == 6) {
|
||||||
|
return moveCounts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return moveCounts
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/main/kotlin/be/nielandt/CrossSolverUpgradedSkip.kt
Normal file
45
src/main/kotlin/be/nielandt/CrossSolverUpgradedSkip.kt
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package be.nielandt
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This solver avoids redoing edgemodel manipulations. Should be equivalent to X nested for loops.
|
||||||
|
*/
|
||||||
|
class CrossSolverUpgradedSkip : CrossSolver() {
|
||||||
|
|
||||||
|
override fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Move>> {
|
||||||
|
val moveCounts = mutableMapOf<Int, List<Move>>()
|
||||||
|
|
||||||
|
for (moveCount in 1..8) {
|
||||||
|
println("all cross move count upgrade doing $moveCount")
|
||||||
|
// build a counter of moveCount big
|
||||||
|
val counter = Counter(moveCount, Move.values().size)
|
||||||
|
// TODO skip the counter
|
||||||
|
val edgeModelFactory = EdgeModelFactory(edgeModel, counter)
|
||||||
|
|
||||||
|
println("moveCounts = ${moveCounts}")
|
||||||
|
|
||||||
|
while (edgeModelFactory.hasNext()) {
|
||||||
|
// get the next model, using the internal counter which simply iterates over possible combinations of moves
|
||||||
|
val next = edgeModelFactory.getNext()
|
||||||
|
|
||||||
|
// check crosses that have not been found yet
|
||||||
|
(0..5).forEach { color ->
|
||||||
|
if (!moveCounts.containsKey(color)) {
|
||||||
|
val crossSolved = next.crossSolved(color)
|
||||||
|
if (crossSolved) {
|
||||||
|
// what is the move combination we're looking at?
|
||||||
|
val moves = Move.combo(counter)
|
||||||
|
moveCounts[color] = moves
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// break if we have found hem all
|
||||||
|
if (moveCounts.keys.size == 6) {
|
||||||
|
// println("counter.skipInvalidCount = ${counter.skipInvalidCount}")
|
||||||
|
return moveCounts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return moveCounts
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -26,7 +26,7 @@ package be.nielandt
|
|||||||
|
|
||||||
class EdgeModel {
|
class EdgeModel {
|
||||||
|
|
||||||
val model: Array<Color>
|
val model: Array<Int>
|
||||||
|
|
||||||
private val F = intArrayOf(13, 18, 7, 20, 3, 0, 1, 2)
|
private val F = intArrayOf(13, 18, 7, 20, 3, 0, 1, 2)
|
||||||
private val B = intArrayOf(8, 9, 10, 11, 16, 15, 22, 5)
|
private val B = intArrayOf(8, 9, 10, 11, 16, 15, 22, 5)
|
||||||
@ -46,12 +46,12 @@ class EdgeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model = arrayOf(
|
model = arrayOf(
|
||||||
Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN,
|
GREEN, GREEN, GREEN, GREEN,
|
||||||
Color.RED, Color.RED, Color.RED, Color.RED,
|
RED, RED, RED, RED,
|
||||||
Color.BLUE, Color.BLUE, Color.BLUE, Color.BLUE,
|
BLUE, BLUE, BLUE, BLUE,
|
||||||
Color.ORANGE, Color.ORANGE, Color.ORANGE, Color.ORANGE,
|
ORANGE, ORANGE, ORANGE, ORANGE,
|
||||||
Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE,
|
WHITE, WHITE, WHITE, WHITE,
|
||||||
Color.YELLOW, Color.YELLOW, Color.YELLOW, Color.YELLOW
|
YELLOW, YELLOW, YELLOW, YELLOW
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ class EdgeModel {
|
|||||||
this.model = doMoves.model
|
this.model = doMoves.model
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(model: Array<Color>) {
|
constructor(model: Array<Int>) {
|
||||||
this.model = model
|
this.model = model
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,17 +154,17 @@ class EdgeModel {
|
|||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
val trimMargin = """
|
val trimMargin = """
|
||||||
| ---------
|
| ---------
|
||||||
| | ${l(model[16])} |
|
| | ${colorLetter(model[16])} |
|
||||||
| | ${l(model[19])} W ${l(model[17])} |
|
| | ${colorLetter(model[19])} W ${colorLetter(model[17])} |
|
||||||
| | ${l(model[18])} |
|
| | ${colorLetter(model[18])} |
|
||||||
|---------------------------------
|
|---------------------------------
|
||||||
|| ${l(model[12])} | ${l(model[0])} | ${l(model[4])} | ${l(model[8])} |
|
|| ${colorLetter(model[12])} | ${colorLetter(model[0])} | ${colorLetter(model[4])} | ${colorLetter(model[8])} |
|
||||||
|| ${l(model[15])} O ${l(model[13])} | ${l(model[3])} G ${l(model[1])} | ${l(model[7])} R ${l(model[5])} | ${l(model[11])} B ${l(model[9])} |
|
|| ${colorLetter(model[15])} O ${colorLetter(model[13])} | ${colorLetter(model[3])} G ${colorLetter(model[1])} | ${colorLetter(model[7])} R ${colorLetter(model[5])} | ${colorLetter(model[11])} B ${colorLetter(model[9])} |
|
||||||
|| ${l(model[14])} | ${l(model[2])} | ${l(model[6])} | ${l(model[10])} |
|
|| ${colorLetter(model[14])} | ${colorLetter(model[2])} | ${colorLetter(model[6])} | ${colorLetter(model[10])} |
|
||||||
|---------------------------------
|
|---------------------------------
|
||||||
| | ${l(model[20])} |
|
| | ${colorLetter(model[20])} |
|
||||||
| | ${l(model[23])} Y ${l(model[21])} |
|
| | ${colorLetter(model[23])} Y ${colorLetter(model[21])} |
|
||||||
| | ${l(model[22])} |
|
| | ${colorLetter(model[22])} |
|
||||||
| ---------
|
| ---------
|
||||||
""".trimMargin()
|
""".trimMargin()
|
||||||
return trimMargin
|
return trimMargin
|
||||||
@ -182,31 +182,34 @@ class EdgeModel {
|
|||||||
return this.doMoves(f.toList())
|
return this.doMoves(f.toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun crossSolved(color: Color): Boolean {
|
fun crossSolved(color: Int): Boolean {
|
||||||
return when (color) {
|
return when (color) {
|
||||||
Color.WHITE -> {
|
WHITE -> {
|
||||||
model[WB] == Color.WHITE && model[WG] == Color.WHITE && model[WO] == Color.WHITE && model[WR] == Color.WHITE &&
|
model[WB] == WHITE && model[WG] == WHITE && model[WO] == WHITE && model[WR] == WHITE &&
|
||||||
model[BW] == Color.BLUE && model[GW] == Color.GREEN && model[OW] == Color.ORANGE && model[RW] == Color.RED
|
model[BW] == BLUE && model[GW] == GREEN && model[OW] == ORANGE && model[RW] == RED
|
||||||
}
|
}
|
||||||
Color.YELLOW -> {
|
YELLOW -> {
|
||||||
model[YB] == Color.YELLOW && model[YG] == Color.YELLOW && model[YO] == Color.YELLOW && model[YR] == Color.YELLOW &&
|
model[YB] == YELLOW && model[YG] == YELLOW && model[YO] == YELLOW && model[YR] == YELLOW &&
|
||||||
model[BY] == Color.BLUE && model[GY] == Color.GREEN && model[OY] == Color.ORANGE && model[RY] == Color.RED
|
model[BY] == BLUE && model[GY] == GREEN && model[OY] == ORANGE && model[RY] == RED
|
||||||
}
|
}
|
||||||
Color.RED -> {
|
RED -> {
|
||||||
model[RW] == Color.RED && model[RG] == Color.RED && model[RY] == Color.RED && model[RB] == Color.RED &&
|
model[RW] == RED && model[RG] == RED && model[RY] == RED && model[RB] == RED &&
|
||||||
model[WR] == Color.WHITE && model[GR] == Color.GREEN && model[YR] == Color.YELLOW && model[BR] == Color.BLUE
|
model[WR] == WHITE && model[GR] == GREEN && model[YR] == YELLOW && model[BR] == BLUE
|
||||||
}
|
}
|
||||||
Color.BLUE -> {
|
BLUE -> {
|
||||||
model[BW] == Color.BLUE && model[BR] == Color.BLUE && model[BY] == Color.BLUE && model[BO] == Color.BLUE &&
|
model[BW] == BLUE && model[BR] == BLUE && model[BY] == BLUE && model[BO] == BLUE &&
|
||||||
model[WB] == Color.WHITE && model[RB] == Color.RED && model[YB] == Color.YELLOW && model[OB] == Color.ORANGE
|
model[WB] == WHITE && model[RB] == RED && model[YB] == YELLOW && model[OB] == ORANGE
|
||||||
}
|
}
|
||||||
Color.GREEN -> {
|
GREEN -> {
|
||||||
model[GW] == Color.GREEN && model[GO] == Color.GREEN && model[GY] == Color.GREEN && model[GR] == Color.GREEN &&
|
model[GW] == GREEN && model[GO] == GREEN && model[GY] == GREEN && model[GR] == GREEN &&
|
||||||
model[WG] == Color.WHITE && model[OG] == Color.ORANGE && model[YG] == Color.YELLOW && model[RG] == Color.RED
|
model[WG] == WHITE && model[OG] == ORANGE && model[YG] == YELLOW && model[RG] == RED
|
||||||
}
|
}
|
||||||
Color.ORANGE -> {
|
ORANGE -> {
|
||||||
model[OW] == Color.ORANGE && model[OB] == Color.ORANGE && model[OY] == Color.ORANGE && model[OG] == Color.ORANGE &&
|
model[OW] == ORANGE && model[OB] == ORANGE && model[OY] == ORANGE && model[OG] == ORANGE &&
|
||||||
model[WO] == Color.WHITE && model[BO] == Color.BLUE && model[YO] == Color.YELLOW && model[GO] == Color.GREEN
|
model[WO] == WHITE && model[BO] == BLUE && model[YO] == YELLOW && model[GO] == GREEN
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
throw RuntimeException()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,8 +37,25 @@ const val OY = 14
|
|||||||
/**
|
/**
|
||||||
* Six colors
|
* Six colors
|
||||||
*/
|
*/
|
||||||
enum class Color(index: Int) {
|
const val WHITE = 0
|
||||||
WHITE(0), YELLOW(1), ORANGE(2), RED(3), GREEN(4), BLUE(5)
|
const val YELLOW = 1
|
||||||
|
const val ORANGE = 2
|
||||||
|
const val RED = 3
|
||||||
|
const val GREEN = 4
|
||||||
|
const val BLUE = 5
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the color into a single digit for print purposes.
|
||||||
|
*/
|
||||||
|
fun colorLetter(c: Int): String {
|
||||||
|
return when (c) {
|
||||||
|
WHITE -> "W"
|
||||||
|
YELLOW -> "Y"
|
||||||
|
RED -> "R"
|
||||||
|
BLUE -> "B"
|
||||||
|
GREEN -> "G"
|
||||||
|
ORANGE -> "O"
|
||||||
|
else -> "?"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
50
src/main/kotlin/be/nielandt/EdgeModelFactory.kt
Normal file
50
src/main/kotlin/be/nielandt/EdgeModelFactory.kt
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package be.nielandt
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This thing helps us to create edgemodels using a counter. The advantage is that the edgemodel doesn't need to be calculated
|
||||||
|
* completely from scratch: previous states are kept, so if, e.g., the third digit changes in the counter (of length 5),
|
||||||
|
* the previous state that was calculated using the first two states is used to perform move 3,4,5 on.
|
||||||
|
*
|
||||||
|
* This is probably equivalent to 8 nested for loops, you'd be able to keep track of temporary solutions there too....
|
||||||
|
*/
|
||||||
|
class EdgeModelFactory(val original: EdgeModel, val counter: Counter) {
|
||||||
|
// keep a modified version of the edgemodel for each digit in the counter, from left to right
|
||||||
|
private val history: MutableList<EdgeModel> = mutableListOf()
|
||||||
|
|
||||||
|
// we always have one in the beginning: the initial state of the counter
|
||||||
|
private var hasNext: Boolean = true
|
||||||
|
|
||||||
|
init {
|
||||||
|
// init the history
|
||||||
|
this.history.add(original.doMove(Move.values()[counter.digit(0)]))
|
||||||
|
for (i in 1 until counter.size()) {
|
||||||
|
this.history.add(this.history.last().doMove(Move.values()[counter.digit(i)]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasNext(): Boolean {
|
||||||
|
return hasNext
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNext(): EdgeModel {
|
||||||
|
// the counter was increased, hooray
|
||||||
|
val lastOverflowIndex = counter.getLastModifiedIndex()
|
||||||
|
// we only need to redo everything starting from the lastoverflowindex
|
||||||
|
// these are our moves, but we can salvage everything up to lastoverflowindex
|
||||||
|
val moves = Move.combo(counter)
|
||||||
|
// we have a history to work with... only redo what's necessary
|
||||||
|
for (i in counter.getLastModifiedIndex() until counter.size()) {
|
||||||
|
var start: EdgeModel = if (i == 0)
|
||||||
|
original
|
||||||
|
else
|
||||||
|
history[i - 1]
|
||||||
|
history[i] = start.doMove(Move.values()[counter.digit(i)])
|
||||||
|
}
|
||||||
|
// increase the counter for next time
|
||||||
|
if (!counter.increase()) {
|
||||||
|
this.hasNext = false
|
||||||
|
}
|
||||||
|
// the last item in the history is now the edgemodel we need to test...
|
||||||
|
return history.last()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -40,6 +40,19 @@ enum class Move {
|
|||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a set of moves, separated by spaces, using the Move.enum names.
|
||||||
|
*/
|
||||||
|
fun parse(s: String): List<Move> {
|
||||||
|
val result = mutableListOf<Move>()
|
||||||
|
s.split(" ", ",", ";").forEach {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
result.add(Move.valueOf(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
package be.nielandt
|
package be.nielandt
|
||||||
|
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class EdgeModelTest {
|
class EdgeModelTest {
|
||||||
@ -10,7 +10,7 @@ class EdgeModelTest {
|
|||||||
// try each move
|
// try each move
|
||||||
Move.values().forEach { move ->
|
Move.values().forEach { move ->
|
||||||
val doMove = EdgeModel().doMove(move)
|
val doMove = EdgeModel().doMove(move)
|
||||||
val count = Color.values().map { color ->
|
val count = (0..5).map { color ->
|
||||||
val crossSolved = doMove.crossSolved(color)
|
val crossSolved = doMove.crossSolved(color)
|
||||||
crossSolved
|
crossSolved
|
||||||
}.count { it }
|
}.count { it }
|
||||||
@ -18,14 +18,4 @@ class EdgeModelTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun findAtLeastOneCombo() {
|
|
||||||
val white = Color.WHITE
|
|
||||||
val doMoves = EdgeModel().doMoves(Move.random(20))
|
|
||||||
val crossMoveCount = crossMoveCount(doMoves, Color.WHITE)
|
|
||||||
assertNotNull(crossMoveCount)
|
|
||||||
assertTrue(crossMoveCount!!.size<9)
|
|
||||||
println(doMoves)
|
|
||||||
println("crossMoveCount = $crossMoveCount")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user