removed enum, increased time!

This commit is contained in:
Joachim 2018-08-25 22:12:56 +02:00
parent ff81c3e7c8
commit 532b1cb85f
10 changed files with 173 additions and 169 deletions

View File

@ -1,16 +1,14 @@
package be.nielandt package be.nielandt
import kotlin.math.min
/** /**
* Counter for X digits of a given base. * Counter for X digits of a given base.
*/ */
open class Counter(size: Int, val base: Int = 10) { open class Counter(size: Int, val base: Int = 18) {
/** /**
* Empty counter, all 0 values for each digit. * Empty counter, all 0 values for each digit.
*/ */
protected var counter: Array<Int> = Array(size) { 0 } var counter: Array<Int> = Array(size) { 0 }
/** /**
* The last (highest significance) index that overflowed and has been changed in the counter. Could be null, if it never overflowed. * The last (highest significance) index that overflowed and has been changed in the counter. Could be null, if it never overflowed.
@ -40,33 +38,6 @@ open class Counter(size: Int, val base: Int = 10) {
return true return true
} }
fun increaseAndSkipInvalid(): Boolean {
var lmi = lastModifiedIndex
var last = increase()
lmi = min(lastModifiedIndex, lmi)
// are we having an invalid situation? this would be two consecutive moves on the same face
while (containsConsecutiveSameFaceMoves() && !atMax()) {
last = increase()
lmi = min(lastModifiedIndex, lmi)
}
// we have to set the lastmodified index to the lowest point that it got to... otherwise we might be skipping some cases
this.lastModifiedIndex = lmi
return last
}
/**
* Are there two moves in the current counter / chain that act on the same face? This would be F+F2 for example.
*/
private fun containsConsecutiveSameFaceMoves(): Boolean {
for (i in 1 until this.counter.size) {
val current = Move.values()[this.counter[i]]
val previous = Move.values()[this.counter[i - 1]]
if (current sameFace previous)
return true
}
return false
}
/** /**
* How many digits does this counter contain? * How many digits does this counter contain?
*/ */

View File

@ -5,7 +5,7 @@ import kotlin.math.min
/** /**
* Counter for X digits of a given base. Skips situations where faces are the same. * Counter for X digits of a given base. Skips situations where faces are the same.
*/ */
class CounterSkipSameFaces(size: Int, base: Int = 10): Counter(size, base) { class CounterSkip(size: Int): Counter(size, 18) {
/** /**
* Increase the counter. * Increase the counter.

View File

@ -7,9 +7,9 @@ open abstract class CrossSolver {
/** /**
* Solve all crosses, look for a minimal set of moves for each color's cross. * Solve all crosses, look for a minimal set of moves for each color's cross.
*/ */
abstract fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Move>> abstract fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Int>>
fun solveCrossesTimed(edgeModel: EdgeModel): Map<Int, List<Move>> { fun solveCrossesTimed(edgeModel: EdgeModel): Map<Int, List<Int>> {
val now = Instant.now() val now = Instant.now()
val solveCrosses = solveCrosses(edgeModel) val solveCrosses = solveCrosses(edgeModel)
val between = Duration.between(now, Instant.now()) val between = Duration.between(now, Instant.now())
@ -18,10 +18,10 @@ open abstract class CrossSolver {
} }
companion object { companion object {
fun printResults(results: Map<Int, List<Move>>) { fun printResults(results: Map<Int, List<Int>>) {
println("results: ") println("results: ")
results.forEach { color, moveList -> results.forEach { color, moveList ->
println("> color ${colorLetter(color)}, moves (${moveList.size}) $moveList") println("> color ${colorLetter(color)}, moves (${moveList.size}) ${moveList.map { it -> decodeMove(it) }}")
} }
} }
} }
@ -62,23 +62,26 @@ fun main(args: Array<String>) {
// do a fixed scramble for testing purposes // do a fixed scramble for testing purposes
val fixedMoves = Move.parse("L L_ R R2") // noskip Move.enum 30s, skip Move.enum 32s
val fixedMoves = parseMoves("L_, D, U, L2, F, D, B, D, U2, D, B_, F2, D2, U_, R, D2, R_, L, B_, R")
println("fixedMoves = ${fixedMoves}") println("fixedMoves = ${fixedMoves}")
// scramble random // scramble random
val moves = Move.random(10) val randomMoves = randomMoves(20)
println("Scramble: $moves")
val scrambledModel = EdgeModel(moves) val moves = fixedMoves
println(scrambledModel) println("Scramble: ${moves.map { decodeMove(it) }}")
val usedModel = EdgeModel(moves)
println(usedModel)
// val baseSolve = CrossSolverBase().solveCrossesTimed(scrambledModel) // val baseSolve = CrossSolverBase().solveCrossesTimed(scrambledModel)
// CrossSolver.printResults(baseSolve) // CrossSolver.printResults(baseSolve)
val upgradedSolve = CrossSolverUpgraded().solveCrossesTimed(scrambledModel) val upgradedSolve = CrossSolverUpgraded().solveCrossesTimed(usedModel)
CrossSolver.printResults(upgradedSolve) CrossSolver.printResults(upgradedSolve)
val upgradedSolveSkip = CrossSolverUpgradedSkip().solveCrossesTimed(scrambledModel) val upgradedSolveSkip = CrossSolverUpgradedSkip().solveCrossesTimed(usedModel)
CrossSolver.printResults(upgradedSolveSkip) CrossSolver.printResults(upgradedSolveSkip)
// val allCrossMoveCountUpgradedSkip = allCrossMoveCountUpgradedSkip(scrambledModel) // val allCrossMoveCountUpgradedSkip = allCrossMoveCountUpgradedSkip(scrambledModel)

View File

@ -4,26 +4,24 @@ class CrossSolverBase : CrossSolver() {
/** /**
* Solve the minimal cross for all colors. * Solve the minimal cross for all colors.
*/ */
override fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Move>> { override fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Int>> {
val moveCounts = mutableMapOf<Int, List<Move>>() val moveCounts = mutableMapOf<Int, List<Int>>()
for (moveCount in 1..8) { for (moveCount in 1..8) {
// build a counter of moveCount big // build a counter of moveCount big
println("allCrossMoveCount basic doing $moveCount") println("allCrossMoveCount basic doing $moveCount")
val counter = Counter(moveCount, Move.values().size) val counter = Counter(moveCount, 18)
// count up, each state of the counter corresponds to a combination of moves // count up, each state of the counter corresponds to a combination of moves
do { do {
// what is the move combination we're looking at?
val moves = Move.combo(counter)
// execute the moves // execute the moves
val afterMoves = edgeModel.doMoves(moves) val afterMoves = edgeModel.doMoves(counter.counter)
// check crosses that have not been found yet // check crosses that have not been found yet
(0..5).forEach { color -> (0..5).forEach { color ->
if (!moveCounts.containsKey(color)) { if (!moveCounts.containsKey(color)) {
val crossSolved = afterMoves.crossSolved(color) val crossSolved = afterMoves.crossSolved(color)
if (crossSolved) { if (crossSolved) {
moveCounts[color] = moves moveCounts[color] = counter.counter.toList()
} }
} }
} }

View File

@ -5,12 +5,12 @@ package be.nielandt
*/ */
class CrossSolverUpgraded : CrossSolver() { class CrossSolverUpgraded : CrossSolver() {
override fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Move>> { override fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Int>> {
val moveCounts = mutableMapOf<Int, List<Move>>() val moveCounts = mutableMapOf<Int, List<Int>>()
for (moveCount in 1..8) { for (moveCount in 1..8) {
println("all cross move count upgrade doing $moveCount") println("all cross move count upgrade doing $moveCount")
// build a counter of moveCount big // build a counter of moveCount big
val counter = Counter(moveCount, Move.values().size) val counter = Counter(moveCount, 18)
val edgeModelFactory = EdgeModelFactory(edgeModel, counter) val edgeModelFactory = EdgeModelFactory(edgeModel, counter)
while (edgeModelFactory.hasNext()) { while (edgeModelFactory.hasNext()) {
@ -23,8 +23,7 @@ class CrossSolverUpgraded : CrossSolver() {
val crossSolved = next.crossSolved(color) val crossSolved = next.crossSolved(color)
if (crossSolved) { if (crossSolved) {
// what is the move combination we're looking at? // what is the move combination we're looking at?
val moves = Move.combo(counter) moveCounts[color] = counter.counter.toList()
moveCounts[color] = moves
} }
} }
} }

View File

@ -5,12 +5,12 @@ package be.nielandt
*/ */
class CrossSolverUpgradedSkip : CrossSolver() { class CrossSolverUpgradedSkip : CrossSolver() {
override fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Move>> { override fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Int>> {
val moveCounts = mutableMapOf<Int, List<Move>>() val moveCounts = mutableMapOf<Int, List<Int>>()
for (moveCount in 1..8) { for (moveCount in 1..8) {
println("all cross move count upgrade doing $moveCount") println("all cross move count upgrade doing $moveCount")
// build a counter of moveCount big // build a counter of moveCount big
val counter = CounterSkipSameFaces(moveCount, Move.values().size) val counter = CounterSkip(moveCount)
val edgeModelFactory = EdgeModelFactory(edgeModel, counter) val edgeModelFactory = EdgeModelFactory(edgeModel, counter)
while (edgeModelFactory.hasNext()) { while (edgeModelFactory.hasNext()) {
@ -23,8 +23,7 @@ class CrossSolverUpgradedSkip : CrossSolver() {
val crossSolved = next.crossSolved(color) val crossSolved = next.crossSolved(color)
if (crossSolved) { if (crossSolved) {
// what is the move combination we're looking at? // what is the move combination we're looking at?
val moves = Move.combo(counter) moveCounts[color] = counter.counter.toList()
moveCounts[color] = moves
} }
} }
} }

View File

@ -9,7 +9,7 @@ package be.nielandt
- | 18 | - | 18 |
------------------------------------- -------------------------------------
| 12 | 00 | 04 | 08 | | 12 | 00 | 04 | 08 |
|15 O 13|03 G 01|07 R 05|11 B 09| |15 O 13|03 G 01|07 R_indices 05|11 B_indices 09|
| 14 | 02 | 06 | 10 | | 14 | 02 | 06 | 10 |
------------------------------------- -------------------------------------
- | 20 | - | 20 |
@ -28,16 +28,16 @@ class EdgeModel {
val model: Array<Int> val model: Array<Int>
private val F = intArrayOf(13, 18, 7, 20, 3, 0, 1, 2) private val F_indices = intArrayOf(13, 18, 7, 20, 3, 0, 1, 2)
private val B = intArrayOf(8, 9, 10, 11, 16, 15, 22, 5) private val B_indices = intArrayOf(8, 9, 10, 11, 16, 15, 22, 5)
private val L = intArrayOf(3, 23, 9, 19, 12, 13, 14, 15) private val L_indices = intArrayOf(3, 23, 9, 19, 12, 13, 14, 15)
private val U = intArrayOf(16, 17, 18, 19, 0, 12, 8, 4) private val U_indices = intArrayOf(16, 17, 18, 19, 0, 12, 8, 4)
private val D = intArrayOf(2, 6, 10, 14, 20, 21, 22, 23) private val D_indices = intArrayOf(2, 6, 10, 14, 20, 21, 22, 23)
private val R = intArrayOf(4, 5, 6, 7, 1, 17, 11, 21) private val R_indices = intArrayOf(4, 5, 6, 7, 1, 17, 11, 21)
constructor() { constructor() {
// do a sanity check // do a sanity check
val entries = mutableListOf(F, B, L, U, D, R).flatMap { it.asList() }.groupBy { it }.entries val entries = mutableListOf(F_indices, B_indices, L_indices, U_indices, D_indices, R_indices).flatMap { it.asList() }.groupBy { it }.entries
// println("entries = ${entries}") // println("entries = ${entries}")
if (entries.any { if (entries.any {
it.value.size != 2 it.value.size != 2
@ -57,7 +57,8 @@ class EdgeModel {
constructor(randomMoves: Int) { constructor(randomMoves: Int) {
val edgeModel = EdgeModel() val edgeModel = EdgeModel()
val doMoves = edgeModel.doMoves(Move.random(randomMoves)) val r: Array<Int> = randomMoves(randomMoves)
val doMoves = edgeModel.doMoves(r)
this.model = doMoves.model this.model = doMoves.model
} }
@ -65,7 +66,7 @@ class EdgeModel {
this.model = model this.model = model
} }
constructor(moves: List<Move>) { constructor(moves: List<Int>) {
val edgeModel = EdgeModel() val edgeModel = EdgeModel()
val newModel = edgeModel.doMoves(moves) val newModel = edgeModel.doMoves(moves)
this.model = newModel.model this.model = newModel.model
@ -74,7 +75,7 @@ class EdgeModel {
/** /**
* Do a single move and calculate the resulting edge model. * Do a single move and calculate the resulting edge model.
*/ */
fun doMove(move: Move): EdgeModel { fun doMove(move: Int): EdgeModel {
val copyOf = this.model.copyOf() val copyOf = this.model.copyOf()
// execute the move // execute the move
@ -126,24 +127,24 @@ class EdgeModel {
} }
when (move) { when (move) {
Move.F -> nonPrime(F) F -> nonPrime(F_indices)
Move.F_ -> prime(F) F_ -> prime(F_indices)
Move.F2 -> double(F) F2 -> double(F_indices)
Move.B -> nonPrime(B) B -> nonPrime(B_indices)
Move.B_ -> prime(B) B_ -> prime(B_indices)
Move.B2 -> double(B) B2 -> double(B_indices)
Move.L -> nonPrime(L) L -> nonPrime(L_indices)
Move.L_ -> prime(L) L_ -> prime(L_indices)
Move.L2 -> double(L) L2 -> double(L_indices)
Move.R -> nonPrime(R) R -> nonPrime(R_indices)
Move.R_ -> prime(R) R_ -> prime(R_indices)
Move.R2 -> double(R) R2 -> double(R_indices)
Move.U -> nonPrime(U) U -> nonPrime(U_indices)
Move.U_ -> prime(U) U_ -> prime(U_indices)
Move.U2 -> double(U) U2 -> double(U_indices)
Move.D -> nonPrime(D) D -> nonPrime(D_indices)
Move.D_ -> prime(D) D_ -> prime(D_indices)
Move.D2 -> double(D) D2 -> double(D_indices)
} }
return EdgeModel(copyOf) return EdgeModel(copyOf)
} }
@ -170,7 +171,7 @@ class EdgeModel {
return trimMargin return trimMargin
} }
fun doMoves(f: Collection<Move>): EdgeModel { fun doMoves(f: Array<Int>) : EdgeModel {
var edgeModel = this var edgeModel = this
f.forEach { f.forEach {
edgeModel = edgeModel.doMove(it) edgeModel = edgeModel.doMove(it)
@ -178,10 +179,21 @@ class EdgeModel {
return edgeModel return edgeModel
} }
fun doMoves(vararg f: Move): EdgeModel { fun doMoves(f: Collection<Int>): EdgeModel {
var edgeModel = this
f.forEach {
edgeModel = edgeModel.doMove(it)
}
return edgeModel
}
fun doMoves(vararg f: Int): EdgeModel {
return this.doMoves(f.toList()) return this.doMoves(f.toList())
} }
/**
* Pass any of the colors WHITE, YELLOW, RED, ...
*/
fun crossSolved(color: Int): Boolean { fun crossSolved(color: Int): Boolean {
return when (color) { return when (color) {
WHITE -> { WHITE -> {

View File

@ -16,9 +16,9 @@ class EdgeModelFactory(val original: EdgeModel, val counter: Counter) {
init { init {
// init the history // init the history
this.history.add(original.doMove(Move.values()[counter.digit(0)])) this.history.add(original.doMove(counter.digit(0)))
for (i in 1 until counter.size()) { for (i in 1 until counter.size()) {
this.history.add(this.history.last().doMove(Move.values()[counter.digit(i)])) this.history.add(this.history.last().doMove(counter.digit(i)))
} }
} }
@ -31,14 +31,13 @@ class EdgeModelFactory(val original: EdgeModel, val counter: Counter) {
val lastOverflowIndex = counter.lastModifiedIndex val lastOverflowIndex = counter.lastModifiedIndex
// we only need to redo everything starting from the lastoverflowindex // we only need to redo everything starting from the lastoverflowindex
// these are our moves, but we can salvage everything up to 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 // we have a history to work with... only redo what's necessary
for (i in counter.lastModifiedIndex until counter.size()) { for (i in counter.lastModifiedIndex until counter.size()) {
var start: EdgeModel = if (i == 0) var start: EdgeModel = if (i == 0)
original original
else else
history[i - 1] history[i - 1]
history[i] = start.doMove(Move.values()[counter.digit(i)]) history[i] = start.doMove(counter.digit(i))
} }
// increase the counter for next time // increase the counter for next time
if (!counter.increase()) { if (!counter.increase()) {

View File

@ -2,75 +2,95 @@ package be.nielandt
import java.util.* import java.util.*
/** // constant values for all the moves
* All the possible moves on the cube. // these are purposely put in this order: Move.index % 6 will result in the same value for the same face
*/ const val F = 0
enum class Move { const val B = 1
// these are purposely put in this order: Move.index % 6 will result in the same value for the same face const val U = 2
F, B, U, D, L, R, const val D = 3
F_, B_, U_, D_, L_, R_, const val L = 4
F2, B2, U2, D2, L2, R2; const val R = 5
/** const val F_ = 6
* Static methods for the Move object. const val B_ = 7
*/ const val U_ = 8
companion object { const val D_ = 9
/** const val L_ = 10
* Make a random set of moves that makes sense. const val R_ = 11
*/
fun random(amount: Int): List<Move> {
val rgen = Random()
val result = mutableListOf<Move>()
while (result.size < amount) {
val randomMove = Move.values()[rgen.nextInt(Move.values().size)]
// check if it makes sense?
if (result.isNotEmpty() && result.last() otherFace randomMove)
result.add(randomMove)
else if (result.isEmpty())
result.add(randomMove)
}
return result
}
/** const val F2 = 12
* Use the given counter, which contains digits that correspond with the amount of Moves, to generate a list of Moves. const val B2 = 13
* Highest significant digits of the counter are added first (are first in the result list). const val U2 = 14
*/ const val D2 = 15
fun combo(counter: Counter): List<Move> { const val L2 = 16
val res = mutableListOf<Move>() const val R2 = 17
for (i in 0 until counter.size()) {
res.add(Move.values()[counter.digit(i)])
}
return res
}
/** fun decodeMove(i: Int): String {
* Parse a set of moves, separated by spaces, using the Move.enum names. return when(i) {
*/ F -> "F"
fun parse(s: String): List<Move> { B -> "B"
val result = mutableListOf<Move>() U -> "U"
s.split(" ", ",", ";").forEach { D -> "D"
if (it.isNotEmpty()) { L -> "L"
result.add(Move.valueOf(it)) R -> "R"
}
} F_ -> "F_"
return result B_ -> "B_"
U_ -> "U_"
D_ -> "D_"
L_ -> "L_"
R_ -> "R_"
F2 -> "F2"
B2 -> "B2"
U2 -> "U2"
D2 -> "D2"
L2 -> "L2"
R2 -> "R2"
else -> {
println("i = ${i}")
throw IllegalArgumentException()
} }
} }
}
/** fun parseMoves(s: String): List<Int> {
* Use this as `Move otherFace Move`, a quick way to check if the moves manipulate the same face or not. return s.split(" ", ",", ";").filter { it?.length > 0 }.map { parseMove(it) }.toList()
* Example: `Move.F otherFace Move.U_ == false` }
*/
infix fun otherFace(otherMove: Move): Boolean { fun parseMove(s: String): Int {
return this.toString().substring(0, 1) != otherMove.toString().substring(0, 1) return when (s) {
"F" -> F
"B" -> B
"U" -> U
"D" -> D
"L" -> L
"R" -> R
"F_" -> F_
"B_" -> B_
"U_" -> U_
"D_" -> D_
"L_" -> L_
"R_" -> R_
"F2" -> F2
"B2" -> B2
"U2" -> U2
"D2" -> D2
"L2" -> L2
"R2" -> R2
else -> {
println("s = ${s}")
throw IllegalArgumentException()
}
} }
}
/** fun randomMoves(amount: Int): Array<Int> {
* Are both moves on the same face? Would be true for F2 and F_, for example. val rgen = Random()
*/ return Array(amount) {
infix fun sameFace(otherMove: Move): Boolean { rgen.nextInt(18)
return !otherFace(otherMove)
} }
}
}

View File

@ -1,21 +1,24 @@
package be.nielandt package be.nielandt
import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
class EdgeModelTest { class EdgeModelTest {
@Test @Test
fun singleMoveAlwaysOneSolvedCross() { fun testSingleMove() {
// try each move val edgeModel = EdgeModel()
Move.values().forEach { move -> (0..5).forEach { color ->
val doMove = EdgeModel().doMove(move) assertTrue(edgeModel.crossSolved(color))
val count = (0..5).map { color ->
val crossSolved = doMove.crossSolved(color)
crossSolved
}.count { it }
assertEquals(1, count)
} }
} }
@Test
fun testSingleMoves() {
val edgeModel = EdgeModel()
println(edgeModel)
val final = edgeModel.doMove(F)
println(final)
assertTrue(final.crossSolved(BLUE))
}
} }