1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
package coffee.liz.rookery.scramble
import coffee.liz.rookery.model.Event
import org.worldcubeassociation.tnoodle.scrambles.PuzzleRegistry
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
import java.util.concurrent.Future
/**
* WCA-official random-state scrambles from tnoodle-lib, with one scramble generated ahead per event
* so pressing "next" is instant even for 4x4 (whose tables take a few seconds to build).
*/
class TnoodleScrambler : Scrambler {
private val pool = Executors.newSingleThreadExecutor { r -> Thread(r, "scrambler").apply { isDaemon = true; priority = Thread.MIN_PRIORITY } }
private val ahead = ConcurrentHashMap<Event, Future<String>>()
private fun registry(e: Event): PuzzleRegistry = when (e) {
Event.E333, Event.E333OH -> PuzzleRegistry.THREE
Event.E222 -> PuzzleRegistry.TWO
Event.E444 -> PuzzleRegistry.FOUR
Event.E555 -> PuzzleRegistry.FIVE
Event.E666 -> PuzzleRegistry.SIX
Event.E777 -> PuzzleRegistry.SEVEN
Event.E333BF, Event.E333MBF -> PuzzleRegistry.THREE_NI
Event.E444BF -> PuzzleRegistry.FOUR_NI
Event.E555BF -> PuzzleRegistry.FIVE_NI
Event.E333FM -> PuzzleRegistry.THREE_FM
Event.CLOCK -> PuzzleRegistry.CLOCK
Event.MINX -> PuzzleRegistry.MEGA
Event.PYRAM -> PuzzleRegistry.PYRA
Event.SKEWB -> PuzzleRegistry.SKEWB
Event.SQ1 -> PuzzleRegistry.SQ1
Event.FTO -> PuzzleRegistry.FTO
}
private fun submit(e: Event): Future<String> = pool.submit<String> { registry(e).scrambler.generateScramble() }
override fun warm(event: Event) { ahead.computeIfAbsent(event, ::submit) }
override fun next(event: Event): String {
val f = ahead.remove(event) ?: submit(event)
val s = f.get()
ahead[event] = submit(event)
return s
}
private val search by lazy { cs.min2phase.Search() }
override fun solve3x3(facelets: String): String? {
if (facelets == "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB") return ""
val s = synchronized(search) { search.solution(facelets, 21, 100_000, 0, 0) }
return if (s.startsWith("Error")) null else s.trim().replace(Regex("\\s+"), " ")
}
override fun previewSvg(event: Event, scramble: String): String? = runCatching {
registry(event).scrambler.drawScramble(scramble, null).toString()
}.getOrNull()
}
|