summaryrefslogtreecommitdiff
path: root/core/src/commonMain/kotlin/coffee/liz/rookery/puzzle/CubeMapping.kt
blob: 86069c4014406aa16039472b5ad515a0ce9e211f (plain) (blame)
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
package coffee.liz.rookery.puzzle

import kotlin.math.abs
import kotlin.math.roundToInt

/**
 * Bridges [TwistyPuzzle] cubes (what the renderer draws) and [NxnCube] slots (what BLD logic and
 * notation-level code use): `slotOf[i]` is the NxnCube slot of generic sticker i.
 */
class CubeMapping(val puzzle: TwistyPuzzle, val n: Int) {
    val slotOf: IntArray = IntArray(puzzle.stickers.size) { i -> slotFor(puzzle.stickers[i].centroid) }
    private val stickerOf: IntArray = IntArray(6 * n * n).also { inv -> slotOf.forEachIndexed { i, s -> inv[s] = i } }

    fun stickerOfSlot(slot: Int): Int = stickerOf[slot]

    /** Render an NxnCube colour array (indexed by slot) as a generic puzzle state. */
    fun state(slotColors: IntArray): PuzzleState = PuzzleState(puzzle, IntArray(puzzle.stickers.size) { slotColors[slotOf[it]] })

    // Inverse of NxnCube.slotPosition, on the unit cube ([-1, 1]^3).
    private fun slotFor(c: V3): Int {
        fun idx(v: Double) = ((v * n + (n - 1)) / 2).roundToInt().coerceIn(0, n - 1)
        val (face, row, col) = when {
            abs(c.y - 1) < 1e-6 -> Triple(0, idx(c.z), idx(c.x))     // U: a = x, b = z
            abs(c.x - 1) < 1e-6 -> Triple(1, idx(-c.y), idx(-c.z))   // R: (n, -b, -a)
            abs(c.z - 1) < 1e-6 -> Triple(2, idx(-c.y), idx(c.x))    // F: (a, -b, n)
            abs(c.y + 1) < 1e-6 -> Triple(3, idx(-c.z), idx(c.x))    // D: (a, -n, -b)
            abs(c.x + 1) < 1e-6 -> Triple(4, idx(-c.y), idx(c.z))    // L: (-n, -b, a)
            else -> Triple(5, idx(-c.y), idx(-c.x))                   // B: (-a, -b, -n)
        }
        return face * n * n + row * n + col
    }
}