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>() 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 = pool.submit { 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() }