From af89c4897028d87fc25e3dde1171795a7af562b1 Mon Sep 17 00:00:00 2001 From: Joachim Date: Sat, 25 Aug 2018 10:24:28 +0200 Subject: [PATCH] Further cleanup, solvers are now separate implementations of CrossSolver --- src/main/kotlin/be/nielandt/CrossSolver.kt | 269 +++--------------- .../kotlin/be/nielandt/CrossSolverBase.kt | 40 +++ .../kotlin/be/nielandt/CrossSolverUpgraded.kt | 43 +++ .../be/nielandt/CrossSolverUpgradedSkip.kt | 45 +++ src/main/kotlin/be/nielandt/EdgeModel.kt | 75 ++--- .../kotlin/be/nielandt/EdgeModelConstants.kt | 25 +- .../kotlin/be/nielandt/EdgeModelFactory.kt | 50 ++++ src/main/kotlin/be/nielandt/Move.kt | 13 + src/test/kotlin/be/nielandt/EdgeModelTest.kt | 14 +- 9 files changed, 292 insertions(+), 282 deletions(-) create mode 100644 src/main/kotlin/be/nielandt/CrossSolverBase.kt create mode 100644 src/main/kotlin/be/nielandt/CrossSolverUpgraded.kt create mode 100644 src/main/kotlin/be/nielandt/CrossSolverUpgradedSkip.kt create mode 100644 src/main/kotlin/be/nielandt/EdgeModelFactory.kt diff --git a/src/main/kotlin/be/nielandt/CrossSolver.kt b/src/main/kotlin/be/nielandt/CrossSolver.kt index 00eef19..f561198 100644 --- a/src/main/kotlin/be/nielandt/CrossSolver.kt +++ b/src/main/kotlin/be/nielandt/CrossSolver.kt @@ -3,6 +3,30 @@ package be.nielandt import java.time.Duration 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> + + fun solveCrossesTimed(edgeModel: EdgeModel): Map> { + 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>) { + println("results: ") + results.forEach { color, moveList -> + println("> color ${colorLetter(color)}, moves $moveList") + } + } + } +} + /** * Let's look for symmetries. */ @@ -37,241 +61,26 @@ fun main(args: Array) { // } - // 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) println("Scramble: $moves") val scrambledModel = EdgeModel(moves) println(scrambledModel) -// doAllCrossMoveCounts(scrambledModel) -// val allCrossMoveCount = allCrossMoveCount(scrambledModel) -// allCrossMoveCount.forEach { color, moves -> -// println("cross for color: ${color} in ${moves.size}: ${moves.joinToString(" ")}") + val baseSolve = CrossSolverBase().solveCrossesTimed(scrambledModel) + CrossSolver.printResults(baseSolve) + + 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? { - val moveCounts = Array?>(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> { - val start = Instant.now() - val moveCounts = mutableMapOf>() - - 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> { - val start = Instant.now() - val moveCounts = mutableMapOf>() - - 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 = 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> { - val start = Instant.now() - val moveCounts = mutableMapOf>() - - 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() -} diff --git a/src/main/kotlin/be/nielandt/CrossSolverBase.kt b/src/main/kotlin/be/nielandt/CrossSolverBase.kt new file mode 100644 index 0000000..e9ca2e5 --- /dev/null +++ b/src/main/kotlin/be/nielandt/CrossSolverBase.kt @@ -0,0 +1,40 @@ +package be.nielandt + +class CrossSolverBase : CrossSolver() { + /** + * Solve the minimal cross for all colors. + */ + override fun solveCrosses(edgeModel: EdgeModel): Map> { + val moveCounts = mutableMapOf>() + + 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 + } + +} \ No newline at end of file diff --git a/src/main/kotlin/be/nielandt/CrossSolverUpgraded.kt b/src/main/kotlin/be/nielandt/CrossSolverUpgraded.kt new file mode 100644 index 0000000..3e8ff1c --- /dev/null +++ b/src/main/kotlin/be/nielandt/CrossSolverUpgraded.kt @@ -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> { + val start = Instant.now() + val moveCounts = mutableMapOf>() + + 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 + } +} \ No newline at end of file diff --git a/src/main/kotlin/be/nielandt/CrossSolverUpgradedSkip.kt b/src/main/kotlin/be/nielandt/CrossSolverUpgradedSkip.kt new file mode 100644 index 0000000..12d21f0 --- /dev/null +++ b/src/main/kotlin/be/nielandt/CrossSolverUpgradedSkip.kt @@ -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> { + val moveCounts = mutableMapOf>() + + 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 + + } +} \ No newline at end of file diff --git a/src/main/kotlin/be/nielandt/EdgeModel.kt b/src/main/kotlin/be/nielandt/EdgeModel.kt index bb9e472..a1825d8 100644 --- a/src/main/kotlin/be/nielandt/EdgeModel.kt +++ b/src/main/kotlin/be/nielandt/EdgeModel.kt @@ -26,7 +26,7 @@ package be.nielandt class EdgeModel { - val model: Array + val model: Array private val F = intArrayOf(13, 18, 7, 20, 3, 0, 1, 2) private val B = intArrayOf(8, 9, 10, 11, 16, 15, 22, 5) @@ -46,12 +46,12 @@ class EdgeModel { } model = arrayOf( - Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN, - Color.RED, Color.RED, Color.RED, Color.RED, - Color.BLUE, Color.BLUE, Color.BLUE, Color.BLUE, - Color.ORANGE, Color.ORANGE, Color.ORANGE, Color.ORANGE, - Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE, - Color.YELLOW, Color.YELLOW, Color.YELLOW, Color.YELLOW + GREEN, GREEN, GREEN, GREEN, + RED, RED, RED, RED, + BLUE, BLUE, BLUE, BLUE, + ORANGE, ORANGE, ORANGE, ORANGE, + WHITE, WHITE, WHITE, WHITE, + YELLOW, YELLOW, YELLOW, YELLOW ) } @@ -61,7 +61,7 @@ class EdgeModel { this.model = doMoves.model } - constructor(model: Array) { + constructor(model: Array) { this.model = model } @@ -154,17 +154,17 @@ class EdgeModel { override fun toString(): String { val trimMargin = """ | --------- - | | ${l(model[16])} | - | | ${l(model[19])} W ${l(model[17])} | - | | ${l(model[18])} | + | | ${colorLetter(model[16])} | + | | ${colorLetter(model[19])} W ${colorLetter(model[17])} | + | | ${colorLetter(model[18])} | |--------------------------------- - || ${l(model[12])} | ${l(model[0])} | ${l(model[4])} | ${l(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])} | - || ${l(model[14])} | ${l(model[2])} | ${l(model[6])} | ${l(model[10])} | + || ${colorLetter(model[12])} | ${colorLetter(model[0])} | ${colorLetter(model[4])} | ${colorLetter(model[8])} | + || ${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])} | + || ${colorLetter(model[14])} | ${colorLetter(model[2])} | ${colorLetter(model[6])} | ${colorLetter(model[10])} | |--------------------------------- - | | ${l(model[20])} | - | | ${l(model[23])} Y ${l(model[21])} | - | | ${l(model[22])} | + | | ${colorLetter(model[20])} | + | | ${colorLetter(model[23])} Y ${colorLetter(model[21])} | + | | ${colorLetter(model[22])} | | --------- """.trimMargin() return trimMargin @@ -182,31 +182,34 @@ class EdgeModel { return this.doMoves(f.toList()) } - fun crossSolved(color: Color): Boolean { + fun crossSolved(color: Int): Boolean { return when (color) { - Color.WHITE -> { - model[WB] == Color.WHITE && model[WG] == Color.WHITE && model[WO] == Color.WHITE && model[WR] == Color.WHITE && - model[BW] == Color.BLUE && model[GW] == Color.GREEN && model[OW] == Color.ORANGE && model[RW] == Color.RED + WHITE -> { + model[WB] == WHITE && model[WG] == WHITE && model[WO] == WHITE && model[WR] == WHITE && + model[BW] == BLUE && model[GW] == GREEN && model[OW] == ORANGE && model[RW] == RED } - Color.YELLOW -> { - model[YB] == Color.YELLOW && model[YG] == Color.YELLOW && model[YO] == Color.YELLOW && model[YR] == Color.YELLOW && - model[BY] == Color.BLUE && model[GY] == Color.GREEN && model[OY] == Color.ORANGE && model[RY] == Color.RED + YELLOW -> { + model[YB] == YELLOW && model[YG] == YELLOW && model[YO] == YELLOW && model[YR] == YELLOW && + model[BY] == BLUE && model[GY] == GREEN && model[OY] == ORANGE && model[RY] == RED } - Color.RED -> { - model[RW] == Color.RED && model[RG] == Color.RED && model[RY] == Color.RED && model[RB] == Color.RED && - model[WR] == Color.WHITE && model[GR] == Color.GREEN && model[YR] == Color.YELLOW && model[BR] == Color.BLUE + RED -> { + model[RW] == RED && model[RG] == RED && model[RY] == RED && model[RB] == RED && + model[WR] == WHITE && model[GR] == GREEN && model[YR] == YELLOW && model[BR] == BLUE } - Color.BLUE -> { - model[BW] == Color.BLUE && model[BR] == Color.BLUE && model[BY] == Color.BLUE && model[BO] == Color.BLUE && - model[WB] == Color.WHITE && model[RB] == Color.RED && model[YB] == Color.YELLOW && model[OB] == Color.ORANGE + BLUE -> { + model[BW] == BLUE && model[BR] == BLUE && model[BY] == BLUE && model[BO] == BLUE && + model[WB] == WHITE && model[RB] == RED && model[YB] == YELLOW && model[OB] == ORANGE } - Color.GREEN -> { - model[GW] == Color.GREEN && model[GO] == Color.GREEN && model[GY] == Color.GREEN && model[GR] == Color.GREEN && - model[WG] == Color.WHITE && model[OG] == Color.ORANGE && model[YG] == Color.YELLOW && model[RG] == Color.RED + GREEN -> { + model[GW] == GREEN && model[GO] == GREEN && model[GY] == GREEN && model[GR] == GREEN && + model[WG] == WHITE && model[OG] == ORANGE && model[YG] == YELLOW && model[RG] == RED } - Color.ORANGE -> { - model[OW] == Color.ORANGE && model[OB] == Color.ORANGE && model[OY] == Color.ORANGE && model[OG] == Color.ORANGE && - model[WO] == Color.WHITE && model[BO] == Color.BLUE && model[YO] == Color.YELLOW && model[GO] == Color.GREEN + ORANGE -> { + model[OW] == ORANGE && model[OB] == ORANGE && model[OY] == ORANGE && model[OG] == ORANGE && + model[WO] == WHITE && model[BO] == BLUE && model[YO] == YELLOW && model[GO] == GREEN + } + else -> { + throw RuntimeException() } } } diff --git a/src/main/kotlin/be/nielandt/EdgeModelConstants.kt b/src/main/kotlin/be/nielandt/EdgeModelConstants.kt index 78880cd..2f82c34 100644 --- a/src/main/kotlin/be/nielandt/EdgeModelConstants.kt +++ b/src/main/kotlin/be/nielandt/EdgeModelConstants.kt @@ -37,8 +37,25 @@ const val OY = 14 /** * Six colors */ -enum class Color(index: Int) { - WHITE(0), YELLOW(1), ORANGE(2), RED(3), GREEN(4), BLUE(5) +const val WHITE = 0 +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 -> "?" + } } - - diff --git a/src/main/kotlin/be/nielandt/EdgeModelFactory.kt b/src/main/kotlin/be/nielandt/EdgeModelFactory.kt new file mode 100644 index 0000000..869b8d7 --- /dev/null +++ b/src/main/kotlin/be/nielandt/EdgeModelFactory.kt @@ -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 = 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() + } +} \ No newline at end of file diff --git a/src/main/kotlin/be/nielandt/Move.kt b/src/main/kotlin/be/nielandt/Move.kt index 8cd1035..a40d66f 100644 --- a/src/main/kotlin/be/nielandt/Move.kt +++ b/src/main/kotlin/be/nielandt/Move.kt @@ -40,6 +40,19 @@ enum class Move { } return res } + + /** + * Parse a set of moves, separated by spaces, using the Move.enum names. + */ + fun parse(s: String): List { + val result = mutableListOf() + s.split(" ", ",", ";").forEach { + if (it.isNotEmpty()) { + result.add(Move.valueOf(it)) + } + } + return result + } } /** diff --git a/src/test/kotlin/be/nielandt/EdgeModelTest.kt b/src/test/kotlin/be/nielandt/EdgeModelTest.kt index 93c6eb0..65195dd 100644 --- a/src/test/kotlin/be/nielandt/EdgeModelTest.kt +++ b/src/test/kotlin/be/nielandt/EdgeModelTest.kt @@ -1,6 +1,6 @@ package be.nielandt -import org.junit.Assert.* +import org.junit.Assert.assertEquals import org.junit.Test class EdgeModelTest { @@ -10,7 +10,7 @@ class EdgeModelTest { // try each move Move.values().forEach { move -> val doMove = EdgeModel().doMove(move) - val count = Color.values().map { color -> + val count = (0..5).map { color -> val crossSolved = doMove.crossSolved(color) crossSolved }.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") - } } \ No newline at end of file