Compare commits
17 Commits
tryingoutm
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| e20ae7fa0b | |||
| 184194d21e | |||
| 5c9dbc991f | |||
| 200fd4e85f | |||
| f7a2e30832 | |||
| e679acbcec | |||
| 1351935159 | |||
|
|
85231d6241 | ||
| c4db0ec011 | |||
| 7e33974d34 | |||
| 80179e9b68 | |||
| 9617ed1a02 | |||
| dc8c433d0d | |||
| 3329e127d9 | |||
| 3c715d33c1 | |||
| ea45836250 | |||
| 0c779c7a5b |
@ -7,9 +7,9 @@ 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<Int>>
|
||||
abstract fun solveCrosses(edgeModel: EdgeModel): Map<Int, IntArray>
|
||||
|
||||
fun solveCrossesTimed(edgeModel: EdgeModel): Map<Int, List<Int>> {
|
||||
fun solveCrossesTimed(edgeModel: EdgeModel): Map<Int, IntArray> {
|
||||
val now = Instant.now()
|
||||
val solveCrosses = solveCrosses(edgeModel)
|
||||
val between = Duration.between(now, Instant.now())
|
||||
@ -18,7 +18,7 @@ open abstract class CrossSolver {
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun printResults(results: Map<Int, List<Int>>) {
|
||||
fun printResults(results: Map<Int, IntArray>) {
|
||||
println("results: ")
|
||||
results.forEach { color, moveList ->
|
||||
println("> color ${colorLetter(color)}, moves (${moveList.size}) ${moveList.map { it -> decodeMove(it) }}")
|
||||
@ -29,64 +29,40 @@ open abstract class CrossSolver {
|
||||
|
||||
/**
|
||||
* Let's look for symmetries.
|
||||
*
|
||||
* - no changes: base times: 72s, 29s, 61s
|
||||
* - changed list<int> in edgemodel to intarray: 51s, 21s, 35s
|
||||
* - new version, optimised now avoid recalc: 47s, 21s, 34s, 14s, (401592291, 147023415, 67382002, 67381995)
|
||||
*/
|
||||
fun main(args: Array<String>) {
|
||||
/**
|
||||
* choose your moves, fixed or random?
|
||||
*/
|
||||
// val moves = parseMoves("U2 F2 U2 D R2 F2 R2 B2 U' D2 L B L2 U2 L B' U L R B".replace('\'', '_'))
|
||||
val moves = randomMoves(20)
|
||||
|
||||
// val u2 = Move.U2
|
||||
// val doMove = edgeModel.doMove(Move.U2)
|
||||
// println(u2)
|
||||
// println(doMove)
|
||||
// println("doMove.whiteCrossSolved() = ${doMove.whiteCrossSolved()}")
|
||||
|
||||
// val message = EdgeModel().doMoves(Move.R, Move.U, Move.R_)
|
||||
// println(message)
|
||||
// println("message.whiteCrossSolved() = ${message.whiteCrossSolved()}")
|
||||
//
|
||||
// val doMoves = EdgeModel().doMoves(Move.random(15))
|
||||
// println("random 15 moves = ${doMoves}")
|
||||
// println("doMoves.whiteCrossSolved() = ${doMoves.whiteCrossSolved()}")
|
||||
|
||||
|
||||
// val begin = Instant.now()
|
||||
// var i: Long = 0
|
||||
// while (Duration.between(begin, Instant.now()) < Duration.ofMinutes(1)) {
|
||||
// EdgeModel().doMoves(Move.random(15))
|
||||
// i++
|
||||
// }
|
||||
// println("i = ${i}")
|
||||
//
|
||||
// val counter = Counter(4, 3)
|
||||
// while (counter.increase()) {
|
||||
// println("counter = ${counter}")
|
||||
// }
|
||||
|
||||
|
||||
// do a fixed scramble for testing purposes
|
||||
// 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}")
|
||||
// scramble random
|
||||
val randomMoves = randomMoves(20)
|
||||
|
||||
val moves = fixedMoves
|
||||
println("Scramble: ${moves.map { decodeMove(it) }}")
|
||||
|
||||
|
||||
val usedModel = EdgeModel(moves)
|
||||
val usedModel = EdgeModel.withMoves(moves)
|
||||
println(usedModel)
|
||||
|
||||
// val baseSolve = CrossSolverBase().solveCrossesTimed(usedModel)
|
||||
// CrossSolver.printResults(baseSolve)
|
||||
|
||||
val upgradedSolve = CrossSolverUpgraded().solveCrossesTimed(usedModel)
|
||||
CrossSolver.printResults(upgradedSolve)
|
||||
// val upgradedSolve = CrossSolverUpgraded().solveCrossesTimed(usedModel)
|
||||
// CrossSolver.printResults(upgradedSolve)
|
||||
//
|
||||
val upgradedSolveSkip = CrossSolverUpgradedSkip().solveCrossesTimed(usedModel)
|
||||
CrossSolver.printResults(upgradedSolveSkip)
|
||||
// val upgradedSolveSkip = CrossSolverUpgradedSkip().solveCrossesTimed(usedModel)
|
||||
// CrossSolver.printResults(upgradedSolveSkip)
|
||||
//
|
||||
// val solveCrossesTimed = CrossSolverIterator().solveCrossesTimed(usedModel)
|
||||
// CrossSolver.printResults(solveCrossesTimed)
|
||||
|
||||
val s3 = CrossSolverIteratorUpgraded().solveCrossesTimed(usedModel)
|
||||
CrossSolver.printResults(s3)
|
||||
|
||||
// val allCrossMoveCountUpgradedSkip = allCrossMoveCountUpgradedSkip(scrambledModel)
|
||||
// allCrossMoveCountUpgradedSkip.forEach { color, moves ->
|
||||
// println("skip upgrade cross for color: ${color} in ${moves.size}: ${moves.joinToString(" ")}")
|
||||
// }
|
||||
// val baseSolve = CrossSolverBase().solveCrossesTimed(usedModel)
|
||||
// CrossSolver.printResults(baseSolve)
|
||||
}
|
||||
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
package be.nielandt
|
||||
|
||||
import be.nielandt.counter.CounterBasic
|
||||
import be.nielandt.counter.CounterTieredFactory
|
||||
|
||||
class CrossSolverBase : CrossSolver() {
|
||||
/**
|
||||
* Solve the minimal cross for all colors.
|
||||
*/
|
||||
override fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Int>> {
|
||||
val moveCounts = mutableMapOf<Int, List<Int>>()
|
||||
override fun solveCrosses(edgeModel: EdgeModel): Map<Int, IntArray> {
|
||||
val moveCounts = mutableMapOf<Int, IntArray>()
|
||||
|
||||
for (moveCount in 1..8) {
|
||||
// build a counter of moveCount big
|
||||
@ -24,7 +23,7 @@ class CrossSolverBase : CrossSolver() {
|
||||
if (!moveCounts.containsKey(color)) {
|
||||
val crossSolved = afterMoves.crossSolved(color)
|
||||
if (crossSolved) {
|
||||
moveCounts[color] = counter.counter.toList()
|
||||
moveCounts[color] = counter.counter.copyOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
54
src/main/kotlin/be/nielandt/CrossSolverIterator.kt
Normal file
54
src/main/kotlin/be/nielandt/CrossSolverIterator.kt
Normal file
@ -0,0 +1,54 @@
|
||||
package be.nielandt
|
||||
|
||||
import be.nielandt.iterator.ValidClassMoveIterator
|
||||
|
||||
class CrossSolverIterator : CrossSolver() {
|
||||
/**
|
||||
* Solve the minimal cross for all colors.
|
||||
*/
|
||||
override fun solveCrosses(edgeModel: EdgeModel): Map<Int, IntArray> {
|
||||
val moveCounts = mutableMapOf<Int, IntArray>()
|
||||
var iteratorCount = 0
|
||||
|
||||
for (moveCount in 1..8) {
|
||||
// build a counter of moveCount big
|
||||
println("crossSolverIterator doing $moveCount")
|
||||
val iterator = ValidClassMoveIterator(moveCount)
|
||||
|
||||
// count up, each state of the counter corresponds to a combination of moves
|
||||
while (iterator.hasNext()) {
|
||||
val moves = iterator.next()
|
||||
iteratorCount++
|
||||
// execute the moves
|
||||
val afterMoves = edgeModel.doMoves(moves.toList())
|
||||
// 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) {
|
||||
println("iteratorCount = ${iteratorCount}")
|
||||
return@solveCrosses moveCounts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println("iteratorCount = ${iteratorCount}")
|
||||
|
||||
return moveCounts
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val start = EdgeModel.solved().doMoves(randomMoves(20))
|
||||
val solver = CrossSolverIterator()
|
||||
val solveCrossesTimed = solver.solveCrossesTimed(start)
|
||||
solveCrossesTimed.forEach { t, u ->
|
||||
println("t $t u ${u.map { decodeMove(it) }}")
|
||||
}
|
||||
}
|
||||
44
src/main/kotlin/be/nielandt/CrossSolverIteratorUpgraded.kt
Normal file
44
src/main/kotlin/be/nielandt/CrossSolverIteratorUpgraded.kt
Normal file
@ -0,0 +1,44 @@
|
||||
package be.nielandt
|
||||
|
||||
import be.nielandt.iterator.ValidClassMoveIterator
|
||||
|
||||
/**
|
||||
* This solver avoids redoing edgemodel manipulations. Should be equivalent to X nested for loops.
|
||||
*/
|
||||
class CrossSolverIteratorUpgraded : CrossSolver() {
|
||||
|
||||
override fun solveCrosses(edgeModel: EdgeModel): Map<Int, IntArray> {
|
||||
val moveCounts = mutableMapOf<Int, IntArray>()
|
||||
var iteratorCount = 0
|
||||
for (moveCount in 1..8) {
|
||||
println("all cross move count upgrade doing $moveCount")
|
||||
// build a counter of moveCount big
|
||||
val counter = ValidClassMoveIterator(moveCount)
|
||||
val edgeModelFactory = EdgeModelFactoryIterator(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()
|
||||
iteratorCount++
|
||||
|
||||
// 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?
|
||||
moveCounts[color] = edgeModelFactory.movesOfCurrent().copyOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
// break if we have found hem all
|
||||
if (moveCounts.keys.size == 6) {
|
||||
println("iteratorCount = $iteratorCount")
|
||||
return moveCounts
|
||||
}
|
||||
}
|
||||
}
|
||||
println("had to do it all... iteratorCount = $iteratorCount")
|
||||
return moveCounts
|
||||
}
|
||||
}
|
||||
@ -7,8 +7,9 @@ import be.nielandt.counter.CounterBasic
|
||||
*/
|
||||
class CrossSolverUpgraded : CrossSolver() {
|
||||
|
||||
override fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Int>> {
|
||||
val moveCounts = mutableMapOf<Int, List<Int>>()
|
||||
override fun solveCrosses(edgeModel: EdgeModel): Map<Int, IntArray> {
|
||||
val moveCounts = mutableMapOf<Int, IntArray>()
|
||||
var iteratorCount = 0
|
||||
for (moveCount in 1..8) {
|
||||
println("all cross move count upgrade doing $moveCount")
|
||||
// build a counter of moveCount big
|
||||
@ -18,6 +19,7 @@ class CrossSolverUpgraded : CrossSolver() {
|
||||
while (edgeModelFactory.hasNext()) {
|
||||
// get the next model, using the internal counter which simply iterates over possible combinations of moves
|
||||
val next = edgeModelFactory.getNext()
|
||||
iteratorCount++
|
||||
|
||||
// check crosses that have not been found yet
|
||||
(0..5).forEach { color ->
|
||||
@ -25,16 +27,18 @@ class CrossSolverUpgraded : CrossSolver() {
|
||||
val crossSolved = next.crossSolved(color)
|
||||
if (crossSolved) {
|
||||
// what is the move combination we're looking at?
|
||||
moveCounts[color] = counter.counter.toList()
|
||||
moveCounts[color] = counter.counter.copyOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
// break if we have found hem all
|
||||
if (moveCounts.keys.size == 6) {
|
||||
println("iteratorCount = ${iteratorCount}")
|
||||
return moveCounts
|
||||
}
|
||||
}
|
||||
}
|
||||
println("iteratorCount = ${iteratorCount}")
|
||||
return moveCounts
|
||||
}
|
||||
}
|
||||
@ -7,8 +7,9 @@ import be.nielandt.counter.CounterSkip
|
||||
*/
|
||||
class CrossSolverUpgradedSkip : CrossSolver() {
|
||||
|
||||
override fun solveCrosses(edgeModel: EdgeModel): Map<Int, List<Int>> {
|
||||
val moveCounts = mutableMapOf<Int, List<Int>>()
|
||||
override fun solveCrosses(edgeModel: EdgeModel): Map<Int, IntArray> {
|
||||
val moveCounts = mutableMapOf<Int, IntArray>()
|
||||
var iteratorCount = 0
|
||||
for (moveCount in 1..8) {
|
||||
println("all cross move count upgrade doing $moveCount")
|
||||
// build a counter of moveCount big
|
||||
@ -18,6 +19,7 @@ class CrossSolverUpgradedSkip : CrossSolver() {
|
||||
while (edgeModelFactory.hasNext()) {
|
||||
// get the next model, using the internal counter which simply iterates over possible combinations of moves
|
||||
val next = edgeModelFactory.getNext()
|
||||
iteratorCount++
|
||||
|
||||
// check crosses that have not been found yet
|
||||
(0..5).forEach { color ->
|
||||
@ -25,16 +27,18 @@ class CrossSolverUpgradedSkip : CrossSolver() {
|
||||
val crossSolved = next.crossSolved(color)
|
||||
if (crossSolved) {
|
||||
// what is the move combination we're looking at?
|
||||
moveCounts[color] = counter.counter.toList()
|
||||
moveCounts[color] = counter.counter.copyOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
// break if we have found hem all
|
||||
if (moveCounts.keys.size == 6) {
|
||||
println("iteratorCount = ${iteratorCount}")
|
||||
return moveCounts
|
||||
}
|
||||
}
|
||||
}
|
||||
println("iteratorCount = ${iteratorCount}")
|
||||
return moveCounts
|
||||
|
||||
}
|
||||
|
||||
@ -26,51 +26,17 @@ package be.nielandt
|
||||
|
||||
class EdgeModel {
|
||||
|
||||
val model: Array<Int>
|
||||
lateinit var model: IntArray
|
||||
|
||||
private val F_indices = intArrayOf(13, 18, 7, 20, 3, 0, 1, 2)
|
||||
private val B_indices = intArrayOf(8, 9, 10, 11, 16, 15, 22, 5)
|
||||
private val L_indices = intArrayOf(3, 23, 9, 19, 12, 13, 14, 15)
|
||||
private val U_indices = intArrayOf(16, 17, 18, 19, 0, 12, 8, 4)
|
||||
private val D_indices = intArrayOf(2, 6, 10, 14, 20, 21, 22, 23)
|
||||
private val R_indices = intArrayOf(4, 5, 6, 7, 1, 17, 11, 21)
|
||||
/**
|
||||
* Create an edgemodel through the companion object.
|
||||
*/
|
||||
private constructor()
|
||||
|
||||
constructor() {
|
||||
// do a sanity check
|
||||
val entries = mutableListOf(F_indices, B_indices, L_indices, U_indices, D_indices, R_indices).flatMap { it.asList() }.groupBy { it }.entries
|
||||
// println("entries = ${entries}")
|
||||
if (entries.any {
|
||||
it.value.size != 2
|
||||
}) {
|
||||
throw RuntimeException("each index should occur exactly twice in the arrays")
|
||||
fun copyOf(model: EdgeModel): EdgeModel {
|
||||
return EdgeModel.withModel(model.model)
|
||||
}
|
||||
|
||||
model = arrayOf(
|
||||
GREEN, GREEN, GREEN, GREEN,
|
||||
RED, RED, RED, RED,
|
||||
BLUE, BLUE, BLUE, BLUE,
|
||||
ORANGE, ORANGE, ORANGE, ORANGE,
|
||||
WHITE, WHITE, WHITE, WHITE,
|
||||
YELLOW, YELLOW, YELLOW, YELLOW
|
||||
)
|
||||
}
|
||||
|
||||
constructor(randomMoves: Int) {
|
||||
val edgeModel = EdgeModel()
|
||||
val r: Array<Int> = randomMoves(randomMoves)
|
||||
val doMoves = edgeModel.doMoves(r)
|
||||
this.model = doMoves.model
|
||||
}
|
||||
|
||||
constructor(model: Array<Int>) {
|
||||
this.model = model
|
||||
}
|
||||
|
||||
constructor(moves: List<Int>) {
|
||||
val edgeModel = EdgeModel()
|
||||
val newModel = edgeModel.doMoves(moves)
|
||||
this.model = newModel.model
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a single move and calculate the resulting edge model.
|
||||
@ -125,7 +91,6 @@ class EdgeModel {
|
||||
copyOf[s[5]] = model[s[7]]
|
||||
copyOf[s[7]] = model[s[5]]
|
||||
}
|
||||
|
||||
when (move) {
|
||||
F -> nonPrime(F_indices)
|
||||
F_ -> prime(F_indices)
|
||||
@ -146,7 +111,7 @@ class EdgeModel {
|
||||
D_ -> prime(D_indices)
|
||||
D2 -> double(D_indices)
|
||||
}
|
||||
return EdgeModel(copyOf)
|
||||
return EdgeModel.withModel(copyOf)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,14 +136,6 @@ class EdgeModel {
|
||||
return trimMargin
|
||||
}
|
||||
|
||||
fun doMoves(f: Array<Int>) : EdgeModel {
|
||||
var edgeModel = this
|
||||
f.forEach {
|
||||
edgeModel = edgeModel.doMove(it)
|
||||
}
|
||||
return edgeModel
|
||||
}
|
||||
|
||||
fun doMoves(f: Collection<Int>): EdgeModel {
|
||||
var edgeModel = this
|
||||
f.forEach {
|
||||
@ -187,8 +144,12 @@ class EdgeModel {
|
||||
return edgeModel
|
||||
}
|
||||
|
||||
fun doMoves(vararg f: Int): EdgeModel {
|
||||
return this.doMoves(f.toList())
|
||||
fun doMoves(f: IntArray): EdgeModel {
|
||||
var edgeModel = this
|
||||
f.forEach {
|
||||
edgeModel = edgeModel.doMove(it)
|
||||
}
|
||||
return edgeModel
|
||||
}
|
||||
|
||||
/**
|
||||
@ -225,5 +186,62 @@ class EdgeModel {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val F_indices = intArrayOf(13, 18, 7, 20, 3, 0, 1, 2)
|
||||
private val B_indices = intArrayOf(8, 9, 10, 11, 16, 15, 22, 5)
|
||||
private val L_indices = intArrayOf(3, 23, 9, 19, 12, 13, 14, 15)
|
||||
private val U_indices = intArrayOf(16, 17, 18, 19, 0, 12, 8, 4)
|
||||
private val D_indices = intArrayOf(2, 6, 10, 14, 20, 21, 22, 23)
|
||||
private val R_indices = intArrayOf(4, 5, 6, 7, 1, 17, 11, 21)
|
||||
|
||||
fun solved(): EdgeModel {
|
||||
val edgeModel = EdgeModel()
|
||||
// do a sanity check
|
||||
val entries = mutableListOf(F_indices, B_indices, L_indices, U_indices, D_indices, R_indices).flatMap { it.asList() }.groupBy { it }.entries
|
||||
// println("entries = ${entries}")
|
||||
if (entries.any {
|
||||
it.value.size != 2
|
||||
}) {
|
||||
throw RuntimeException("each index should occur exactly twice in the arrays")
|
||||
}
|
||||
|
||||
val model = intArrayOf(
|
||||
GREEN, GREEN, GREEN, GREEN,
|
||||
RED, RED, RED, RED,
|
||||
BLUE, BLUE, BLUE, BLUE,
|
||||
ORANGE, ORANGE, ORANGE, ORANGE,
|
||||
WHITE, WHITE, WHITE, WHITE,
|
||||
YELLOW, YELLOW, YELLOW, YELLOW
|
||||
)
|
||||
edgeModel.model = model
|
||||
return edgeModel
|
||||
}
|
||||
|
||||
fun withRandomMoves(randomMoves: Int): EdgeModel {
|
||||
val edgeModel = EdgeModel()
|
||||
val r: IntArray = randomMoves(randomMoves)
|
||||
val doMoves = edgeModel.doMoves(r.toList())
|
||||
edgeModel.model = doMoves.model
|
||||
return edgeModel
|
||||
}
|
||||
|
||||
fun withModel(model: IntArray): EdgeModel {
|
||||
val edgeModel = EdgeModel()
|
||||
edgeModel.model = model
|
||||
return edgeModel
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass a list of moves and do them on a solved edgemodel.
|
||||
*/
|
||||
fun withMoves(moves: IntArray): EdgeModel {
|
||||
val edgeModel = EdgeModel.solved()
|
||||
val newModel = edgeModel.doMoves(moves)
|
||||
edgeModel.model = newModel.model
|
||||
return edgeModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
66
src/main/kotlin/be/nielandt/EdgeModelFactoryIterator.kt
Normal file
66
src/main/kotlin/be/nielandt/EdgeModelFactoryIterator.kt
Normal file
@ -0,0 +1,66 @@
|
||||
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 EdgeModelFactoryIterator(val original: EdgeModel, val counter: Iterator<IntArray>) {
|
||||
// keep a modified version of the edgemodel for each digit in the counter, from left to right
|
||||
private val history: MutableList<EdgeModel> = mutableListOf()
|
||||
private var previous:IntArray
|
||||
private var lastMoves: IntArray
|
||||
|
||||
init {
|
||||
// init the history
|
||||
val firstOne = counter.next()
|
||||
lastMoves = firstOne
|
||||
this.history.add(original.doMove(firstOne[0]))
|
||||
for (i in 1 until firstOne.size) {
|
||||
this.history.add(this.history.last().doMove(firstOne[i]))
|
||||
}
|
||||
// keep track of the 'previous move that was performed'
|
||||
previous = firstOne
|
||||
}
|
||||
|
||||
fun hasNext(): Boolean {
|
||||
return counter.hasNext()
|
||||
}
|
||||
|
||||
fun movesOfCurrent() : IntArray {
|
||||
return previous
|
||||
}
|
||||
|
||||
fun getNext(): EdgeModel {
|
||||
// the next set of moves
|
||||
val moves = counter.next()
|
||||
this.lastMoves = moves
|
||||
// figure out the first index that differs
|
||||
val lastModifiedIndex = firstDifferingIndex(previous, moves)
|
||||
// we only need to redo everything starting from the lastoverflowindex
|
||||
// these are our moves, but we can salvage everything up to lastoverflowindex
|
||||
// we have a history to work with... only redo what's necessary
|
||||
for (i in lastModifiedIndex until moves.size) {
|
||||
var start: EdgeModel = if (i == 0)
|
||||
original
|
||||
else
|
||||
history[i - 1]
|
||||
history[i] = start.doMove(moves[i])
|
||||
}
|
||||
// bump the previous value
|
||||
previous = moves
|
||||
// the last item in the history is now the edgemodel we need to test...
|
||||
return history.last()
|
||||
}
|
||||
|
||||
private fun firstDifferingIndex(previous: IntArray, moves: IntArray): Int {
|
||||
previous.forEachIndexed { index, i ->
|
||||
if(previous[index] != moves[index])
|
||||
return index
|
||||
}
|
||||
// nothing changed.. that's probably impossible
|
||||
throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
@ -26,7 +26,7 @@ const val L2 = 16
|
||||
const val R2 = 17
|
||||
|
||||
fun decodeMove(i: Int): String {
|
||||
return when(i) {
|
||||
return when (i) {
|
||||
F -> "F"
|
||||
B -> "B"
|
||||
U -> "U"
|
||||
@ -48,18 +48,18 @@ fun decodeMove(i: Int): String {
|
||||
L2 -> "L2"
|
||||
R2 -> "R2"
|
||||
else -> {
|
||||
println("i = ${i}")
|
||||
println("i = $i")
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun classOf(move: Int): Int {
|
||||
return (move%6) / 2
|
||||
return (move % 6) / 2
|
||||
}
|
||||
|
||||
fun parseMoves(s: String): List<Int> {
|
||||
return s.split(" ", ",", ";").filter { it?.length > 0 }.map { parseMove(it) }.toList()
|
||||
fun parseMoves(s: String): IntArray {
|
||||
return s.split(" ", ",", ";").filter { it?.length > 0 }.map { parseMove(it) }.toIntArray()
|
||||
}
|
||||
|
||||
fun parseMove(s: String): Int {
|
||||
@ -91,10 +91,46 @@ fun parseMove(s: String): Int {
|
||||
}
|
||||
}
|
||||
|
||||
fun randomMoves(amount: Int): Array<Int> {
|
||||
/**
|
||||
* Get a proper scramble set of moves. Disallow the obvious symmetries.
|
||||
*/
|
||||
fun randomMoves(amount: Int): IntArray {
|
||||
val rgen = Random()
|
||||
return Array(amount) {
|
||||
rgen.nextInt(18)
|
||||
val result = IntArray(amount)
|
||||
for (i in 0 until result.size) {
|
||||
// create valid options for the next one
|
||||
val options = mutableListOf<Int>()
|
||||
if (i == 0) {
|
||||
// all options
|
||||
options.addAll(0..17)
|
||||
}
|
||||
// if there's two before us, we can't add a third of the same class
|
||||
else if (i > 1 && classOf(result[i - 1]) == classOf(result[i - 2])) {
|
||||
options.addAll(
|
||||
(0..17).filter { classOf(it) != classOf(result[i - 1]) }
|
||||
)
|
||||
} else {
|
||||
// all options except the previous one
|
||||
// also disallow same-face solutions
|
||||
options.addAll(
|
||||
(0..17)
|
||||
// can't be the same face move
|
||||
.filter {
|
||||
it % 6 != result[i - 1] % 6
|
||||
}
|
||||
)
|
||||
}
|
||||
result[i] = options[rgen.nextInt(options.size)]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
printMoves(randomMoves(50))
|
||||
}
|
||||
|
||||
fun printMoves(moves: IntArray) {
|
||||
println(moves.map { decodeMove(it) }.joinToString(","))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ open abstract class Counter(size: Int, val base: Int = 18) {
|
||||
/**
|
||||
* Empty counter, all 0 values for each digit.
|
||||
*/
|
||||
var counter: Array<Int> = Array(size) { 0 }
|
||||
var counter: IntArray = IntArray(size) { 0 }
|
||||
|
||||
/**
|
||||
* The last (highest significance) index that overflowed and has been changed in the counter. Could be null, if it never overflowed.
|
||||
|
||||
@ -61,3 +61,12 @@ class CounterBasic(size: Int) : Counter(size, 18) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val counterBasic = CounterBasic(7)
|
||||
var count = 0
|
||||
while(counterBasic.increase())
|
||||
count++
|
||||
println("count = ${count}")
|
||||
|
||||
}
|
||||
|
||||
@ -7,14 +7,14 @@ import be.nielandt.classOf
|
||||
*/
|
||||
class CounterBuffer(size: Int) : Counter(size, 18) {
|
||||
|
||||
val buffer = mutableListOf<Array<Int>>()
|
||||
val buffer = mutableListOf<IntArray>()
|
||||
|
||||
init {
|
||||
// initialise the buffer of valid counters
|
||||
val counterBasic = CounterBasic(size)
|
||||
do {
|
||||
if (counterBasic.isValid()) {
|
||||
buffer.add(counterBasic.counter)
|
||||
buffer.add(counterBasic.counter.copyOf())
|
||||
}
|
||||
} while (counterBasic.increase())
|
||||
println("Init of counter buffer, ${buffer.size} valid combos")
|
||||
|
||||
@ -7,9 +7,9 @@ import be.nielandt.decodeMove
|
||||
* 1) tier1 : axes / move classes (FB/UD/RL) -> three classes, but multiple sizes (1 or 2) possible
|
||||
* 2) tier2: each block of class (22, 11, 2, 11, 0, ...) needs to be expanded into the full range of related moves
|
||||
*/
|
||||
class CounterTiered : Counter {
|
||||
class CounterTiered : Counter(8) {
|
||||
|
||||
private val moveIterator: Iterator<Array<Int>>
|
||||
private val moveIterator: Iterator<IntArray> = listOf<IntArray>().iterator()
|
||||
|
||||
override fun increase(): Boolean {
|
||||
if (!this.moveIterator.hasNext())
|
||||
@ -18,20 +18,14 @@ class CounterTiered : Counter {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
constructor(size: Int) : super(size, 18) {
|
||||
this.moveIterator = moveIterator().iterator()
|
||||
this.increase()
|
||||
}
|
||||
|
||||
override fun atMax(): Boolean {
|
||||
return !this.moveIterator.hasNext()
|
||||
}
|
||||
|
||||
private class MoveIterator(val classes: List<List<Int>>) : Iterator<Array<Int>> {
|
||||
private class MoveIterator(val classes: List<IntArray>) : Iterator<IntArray> {
|
||||
|
||||
var currentClassIndex = 0
|
||||
lateinit var currentMoves: Iterator<Array<Int>>
|
||||
lateinit var currentMoves: Iterator<IntArray>
|
||||
|
||||
|
||||
init {
|
||||
@ -40,11 +34,11 @@ class CounterTiered : Counter {
|
||||
}
|
||||
|
||||
private fun doClass(index: Int) {
|
||||
val fillIn = fillIn(0, classes[index], Array(classes.size) { 0 })
|
||||
val fillIn = fillIn(0, classes[index], IntArray(classes.size) { 0 })
|
||||
currentMoves = fillIn.toList().iterator()
|
||||
}
|
||||
|
||||
override fun next(): Array<Int> {
|
||||
override fun next(): IntArray {
|
||||
val next = currentMoves.next()
|
||||
if (!currentMoves.hasNext()) {
|
||||
nextClass()
|
||||
@ -65,73 +59,17 @@ class CounterTiered : Counter {
|
||||
|
||||
}
|
||||
|
||||
fun moveIterator(): Iterator<Array<Int>> {
|
||||
return MoveIterator(classIterator()).iterator()
|
||||
}
|
||||
|
||||
/**
|
||||
* These are the classes (FB/UD/RL), correctly configured (no illegal combinations here)
|
||||
*/
|
||||
fun classIterator(): List<List<Int>> {
|
||||
val classes = appendRandomClass(this.counter.size, listOf())
|
||||
return classes
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for classIterator()
|
||||
*/
|
||||
private fun appendRandomClass(size: Int, base: List<Int>): List<List<Int>> {
|
||||
val result = mutableListOf<List<Int>>()
|
||||
when {
|
||||
base.size < size - 1 -> // add all classes, but don't repeat a group already there
|
||||
(0..2).filter { if (base.isNotEmpty()) it != base.last() else true }
|
||||
.forEach { theClass ->
|
||||
// and both amounts, 1+2
|
||||
(1..2).forEach { amount ->
|
||||
val l = base.toMutableList()
|
||||
for (i in 1..amount) {
|
||||
l.add(theClass)
|
||||
}
|
||||
result.addAll(appendRandomClass(size, l))
|
||||
}
|
||||
}
|
||||
base.size == size -> {
|
||||
// we're done here
|
||||
result.add(base)
|
||||
}
|
||||
base.size == size - 1 -> // the base is not empty, only add class that is not at the end of the base list
|
||||
(0..2).filter { if (base.isEmpty()) true else it != base.last() }.forEach { theClass ->
|
||||
val l = base.toMutableList()
|
||||
l.add(theClass)
|
||||
result.add(l)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class CounterTieredFactory {
|
||||
companion object {
|
||||
fun create(size: Int): CounterTiered {
|
||||
return CounterTiered(size)
|
||||
return CounterTiered()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
var count = 0
|
||||
CounterTieredFactory.create(4).classIterator().forEach {
|
||||
println("it = ${it}")
|
||||
count++
|
||||
}
|
||||
println("count = ${count}")
|
||||
// CounterTieredFactory.create(7).moveIterator().forEach {
|
||||
// println("Arrays.tostring(it) = ${Arrays.toString(it)} ${it.map { decodeMove(it) }.toList()}")
|
||||
// count++
|
||||
// }
|
||||
// println("count = ${count}")
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the class ints into the full range of moves.
|
||||
*
|
||||
@ -159,8 +97,8 @@ const val D2 = 15
|
||||
const val L2 = 16
|
||||
const val R2 = 17
|
||||
*/
|
||||
fun fillIn(index: Int, classes: List<Int>, moveList: Array<Int>): List<Array<Int>> {
|
||||
val result = mutableListOf<Array<Int>>()
|
||||
fun fillIn(index: Int, classes: IntArray, moveList: IntArray): List<IntArray> {
|
||||
val result = mutableListOf<IntArray>()
|
||||
|
||||
// if the movelist is complete, finish it
|
||||
if (index == classes.size) {
|
||||
|
||||
55
src/main/kotlin/be/nielandt/counter/VariableCounter.kt
Normal file
55
src/main/kotlin/be/nielandt/counter/VariableCounter.kt
Normal file
@ -0,0 +1,55 @@
|
||||
package be.nielandt.counter
|
||||
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* The variable counter has irregular base size for each digit.
|
||||
*
|
||||
* basesize: [1,3,2]
|
||||
* iterates:
|
||||
* - [0,0,0]
|
||||
* - [0,0,1]
|
||||
* - [0,1,0]
|
||||
* - [0,1,1]
|
||||
* - [0,2,0]
|
||||
* - [0,2,1]
|
||||
*/
|
||||
class VariableCounter(internal val baseSizes: IntArray) : Iterator<IntArray> {
|
||||
|
||||
// init the counter with 0's, we only have a maximum as our basesize
|
||||
val counter = IntArray(baseSizes.size) {
|
||||
when (it) {
|
||||
baseSizes.size - 1 -> -1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
// check if all elements in the counter has reached their maximum (basesize - 1)
|
||||
counter.forEachIndexed { index, sh ->
|
||||
if (sh < baseSizes[index] - 1)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun next(): IntArray {
|
||||
for (i in this.counter.size - 1 downTo 0) {
|
||||
this.counter[i]++
|
||||
if (this.counter[i] == baseSizes[i]) {
|
||||
this.counter[i] = 0
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return counter.copyOf()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val counter = VariableCounter(intArrayOf(2, 3, 2))
|
||||
while (counter.hasNext()) {
|
||||
val next = counter.next()
|
||||
println("counter.next() = ${Arrays.toString(next)} ${counter.hasNext()}")
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package be.nielandt.iterator
|
||||
|
||||
class ValidClassMoveIterator(val size: Int) : Iterator<IntArray> {
|
||||
|
||||
// current level of the iterator
|
||||
var classesIterator = ValidClassesIterator(size)
|
||||
var movesIterator: ValidMoveIterator
|
||||
|
||||
init {
|
||||
this.movesIterator = ValidMoveIterator(classesIterator.next())
|
||||
}
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
// check if we have another move in the move iterator
|
||||
return if (this.movesIterator.hasNext()) {
|
||||
true
|
||||
} else {
|
||||
// the moves iterator does not has any more juice, move to the next class
|
||||
if (!this.classesIterator.hasNext()) {
|
||||
false
|
||||
} else {
|
||||
nextClass()
|
||||
this.movesIterator.hasNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun next(): IntArray {
|
||||
// check if there's a valid move left
|
||||
if (!this.movesIterator.hasNext()) {
|
||||
nextClass()
|
||||
}
|
||||
// now we're sure there's something left
|
||||
return this.movesIterator.next()
|
||||
}
|
||||
|
||||
/**
|
||||
* Bump the 'current class'. This means we have a new moves iterator at our disposal.
|
||||
*/
|
||||
private fun nextClass() {
|
||||
val next = this.classesIterator.next()
|
||||
this.movesIterator = ValidMoveIterator(next)
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val validClassMoveIterator = ValidClassMoveIterator(8)
|
||||
var count=0
|
||||
while (validClassMoveIterator.hasNext()) {
|
||||
validClassMoveIterator.next()
|
||||
// println("Arrays.toString(validClassMoveIterator.next()) = ${validClassMoveIterator.next().map { decodeMove(it) }}")
|
||||
count++
|
||||
}
|
||||
println("count = ${count}")
|
||||
}
|
||||
70
src/main/kotlin/be/nielandt/iterator/ValidClassesIterator.kt
Normal file
70
src/main/kotlin/be/nielandt/iterator/ValidClassesIterator.kt
Normal file
@ -0,0 +1,70 @@
|
||||
package be.nielandt.iterator
|
||||
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
class ValidClassesIterator(val size: Int) : Iterator<List<Int>> {
|
||||
|
||||
private var classes: Iterator<List<Int>> = classIterator().iterator()
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return this.classes.hasNext()
|
||||
}
|
||||
|
||||
override fun next(): List<Int> {
|
||||
return this.classes.next()
|
||||
}
|
||||
|
||||
/**
|
||||
* These are the classes (FB/UD/RL), correctly configured (no illegal combinations here)
|
||||
*/
|
||||
private fun classIterator(): List<List<Int>> {
|
||||
val classes = appendRandomClass(size, listOf())
|
||||
return classes
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for classIterator()
|
||||
*/
|
||||
private fun appendRandomClass(size: Int, base: List<Int>): List<List<Int>> {
|
||||
val result = mutableListOf<List<Int>>()
|
||||
when {
|
||||
base.size < size - 1 -> // add all classes, but don't repeat a group already there
|
||||
(0..2).filter { if (base.isNotEmpty()) it != base.last() else true }
|
||||
.forEach { theClass ->
|
||||
// and both amounts, 1+2
|
||||
(1..2).forEach { amount ->
|
||||
val l = base.toMutableList()
|
||||
for (i in 1..amount) {
|
||||
l.add(theClass)
|
||||
}
|
||||
result.addAll(appendRandomClass(size, l))
|
||||
}
|
||||
}
|
||||
base.size == size -> {
|
||||
// we're done here
|
||||
result.add(base)
|
||||
}
|
||||
base.size == size - 1 -> // the base is not empty, only add class that is not at the end of the base list
|
||||
(0..2).filter { if (base.isEmpty()) true else it != base.last() }.forEach { theClass ->
|
||||
val l = base.toMutableList()
|
||||
l.add(theClass)
|
||||
result.add(l)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val now = Instant.now()
|
||||
val iter = ValidClassesIterator(8)
|
||||
var count: Int = 0
|
||||
while(iter.hasNext()) {
|
||||
println("iter.next() = ${iter.next()}")
|
||||
count++
|
||||
}
|
||||
println("count = ${count}")
|
||||
println("Duration.between(Instant.now(), now) = ${Duration.between(now, Instant.now()).toMillis()}")
|
||||
}
|
||||
|
||||
@ -1,13 +1,82 @@
|
||||
package be.nielandt.iterator
|
||||
|
||||
class ValidMoveIterator: Iterator<Array<Int>> {
|
||||
import be.nielandt.counter.VariableCounter
|
||||
import be.nielandt.decodeMove
|
||||
import java.util.*
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
/**
|
||||
* Iterates over the different moves of the given specific classes.
|
||||
*/
|
||||
class ValidMoveIterator(val classes: List<Int>) : Iterator<IntArray> {
|
||||
|
||||
internal var expansionCounter: VariableCounter
|
||||
|
||||
/**
|
||||
* At initialisation time, create the internal expansion counter. This one will count appropriately for each class type,
|
||||
* depending on whether the class is alone or is a duplicate.
|
||||
*/
|
||||
init {
|
||||
// create the variable counter: if a class is 'alone', it can iterate over all 6 values. otherwise, each part can iterate over 3
|
||||
val intArray = IntArray(classes.size)
|
||||
var i = 0
|
||||
while (i < classes.size) {
|
||||
if (i < classes.size - 1 && classes[i] == classes[i + 1]) {
|
||||
intArray[i] = 3
|
||||
intArray[i + 1] = 3
|
||||
i += 2
|
||||
} else {
|
||||
intArray[i] = 6
|
||||
i++
|
||||
}
|
||||
}
|
||||
this.expansionCounter = VariableCounter(intArray)
|
||||
}
|
||||
|
||||
override fun next(): Array<Int> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
override fun hasNext(): Boolean = this.expansionCounter.hasNext()
|
||||
|
||||
override fun next(): IntArray {
|
||||
val next = this.expansionCounter.next()
|
||||
// translate this state into a list of moves
|
||||
var i = 0
|
||||
while (i < next.size) {
|
||||
// process the double situation
|
||||
if (i < classes.size-1 && classes[i] == classes[i + 1]) {
|
||||
// so, we're in the same state, the first counter will get the 'low' value, the second the 'high' value
|
||||
// for class FB, that would be F and B respectively
|
||||
// class 0 (FB) has to expand to 0,6,12 (F,F_F2) and 1,7,13 (B,B_,B2) respectively
|
||||
// class 1 (UD) has to expand to 2,8,14 (U,U_U2) and 3,9,15 (D,D_,D2) respectively
|
||||
// class 2 (LR) has to expand to 4,10,16 (L,L_L2) and 5,11,17 (R,R_,R2) respectively
|
||||
// next will contain 0,1,3, as the classes are a pair
|
||||
val c1 = (classes[i] * 2) + 6 * next[i]
|
||||
val c2 = (classes[i] * 2) + 6 * next[i + 1]+1
|
||||
next[i] = c1
|
||||
next[i + 1] = c2
|
||||
|
||||
// bump up the counter by two
|
||||
i += 2
|
||||
}
|
||||
// now the single situation
|
||||
else {
|
||||
// class 0 (FB) has to expand to 0,1,6,7,12,13 (F,B,F_,B_,F2,B2)
|
||||
// class 1 (UD) has to expand to 2,3,8,9,14,15 (U,D,U_,D_,U2,D2)
|
||||
// class 2 (LR) has to expand to 4,5,10,11,16,17 (L,R,L_,R_,L2,R2)
|
||||
next[i] = (classes[i] * 2) + ((next[i] / 2) * 6) + (next[i] % 2)
|
||||
i++
|
||||
}
|
||||
}
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val validMoveIterator = ValidMoveIterator(listOf(0, 0, 2, 1, 2, 0, 1, 1))
|
||||
println("classes = ${validMoveIterator.classes}")
|
||||
println("basesizes = ${Arrays.toString(validMoveIterator.expansionCounter.baseSizes)}")
|
||||
var count = 0
|
||||
while (validMoveIterator.hasNext()) {
|
||||
println("Arrays.toString(validMoveIterator.next()) = ${Arrays.toString(validMoveIterator.next())}")
|
||||
println("validMoveIterator = ${validMoveIterator.next().map { decodeMove(it) }}")
|
||||
count++
|
||||
}
|
||||
println("count = $count")
|
||||
}
|
||||
@ -7,7 +7,7 @@ class EdgeModelTest {
|
||||
|
||||
@Test
|
||||
fun testSingleMove() {
|
||||
val edgeModel = EdgeModel()
|
||||
val edgeModel = EdgeModel.solved()
|
||||
(0..5).forEach { color ->
|
||||
assertTrue(edgeModel.crossSolved(color))
|
||||
}
|
||||
@ -15,7 +15,7 @@ class EdgeModelTest {
|
||||
|
||||
@Test
|
||||
fun testSingleMoves() {
|
||||
val edgeModel = EdgeModel()
|
||||
val edgeModel = EdgeModel.solved()
|
||||
println(edgeModel)
|
||||
val final = edgeModel.doMove(F)
|
||||
println(final)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user