Compare commits

..

17 Commits

18 changed files with 599 additions and 202 deletions

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.
*/
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)
}

View File

@ -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()
}
}
}

View 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) }}")
}
}

View 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
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}
}
}

View 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()
}
}

View File

@ -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(","))
}

View File

@ -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.

View File

@ -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}")
}

View File

@ -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")

View File

@ -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) {

View 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()}")
}
}

View File

@ -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}")
}

View 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()}")
}

View File

@ -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")
}

View File

@ -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)