librarylibs/Eigencube/src/Eigencube.xtl

Eigencube: a Rubik's cube as rotation matrices, turned by matrix products and solved by search. A port of Steffen Smolka's eigencube.py (github.com/smolkaj/eigencube, MIT). Each of the 26 cubelets is the integer point c of {-1, 0, 1}^3 where it sits in the solved cube and carries a rotation matrix R: it sits now at R c. A quarter turn of the face with outward normal v moves the cubelets with v . (R c) > 0, multiplying R by one rotation matrix. A batch of cubes is one array, n by 26 by 3 by 3, so all twelve turns of every cube in a search are two matrix products. Import it with an alias of your choice: "ec:" u_se< "Eigencube". Put libs/Eigencube/src on XETAL_PATH ("just path"); the reference is libs/Eigencube/docs. Names with l: are exported; those under h: are private to this file.

Moves are numbered 1 to 12: move 2f-1 turns face f clockwise as seen facing it, move 2f counterclockwise, the faces in the order of ec:faces, "UDFBRL" (up +z, front +x, right +y); ec:n_ame writes them in the usual notation (U, U', D, ...) and ec:p_arse reads it.

source

The cube

pts : Int

value (private) · line 22

The 27 integer points of {-1, 0, 1}^3, one per row (x slowest).

pts ← (o̲\ 3 3 3 e̲ncode o̲ffsets 27) − 1
Used in: C, at, layer

C : Int

value (private) · line 25

The 26 cubelets, each named by its place in the solved cube: every point but the hidden center.

C ← (0 < '+ r̲/₂ a̲bs pts) r̲eplicate pts
Used in: kind, z, ʰp̲os, at, Dsolved, posDev

kind : Int

value (private) · line 28

Each cubelet's 1-norm, its number of stickers: 1 center, 2 edge, 3 corner.

kind ← '+ r̲/₂ a̲bs C
Used in: sets

z : Int

value (private) · line 30

Each cubelet's layer in the solved cube: 1 top, 0 middle, -1 bottom.

z ← 3 s̲elect₂ C
Used in: sets

I : Int

value (private) · line 32

The 3-by-3 identity.

I ← 3 3 r̲eshape 1 0 0 0 1 0 0 0 1
Used in: ˡsolved, G, colDev

ˡsolved : Int

value · line 34

The solved cube: every cubelet unrotated, a batch of one, 1 26 3 3.

ˡsolved ← 1 26 3 3 r̲eshape I
Used in: x

The twelve quarter turns

N : Int

value (private) · line 40

The six faces' outward normals, named U D F B R L: up is +z, front +x, right +y. Faces 2a-1 and 2a share axis a.

N ← 6 3 r̲eshape 0 0 1  0 0 -1  1 0 0  -1 0 0  0 1 0  0 -1 0
Used in: V, ˡs̲tickers

ˡfaces : Char

value · line 42

The face names, in the order of N.

ˡfaces ← "UDFBRL"

V : Int

value (private) · line 45

The move vectors: move 2f-1 turns face f clockwise as seen facing it, move 2f counterclockwise.

V ← 2 r̲eplicate N
Used in: Mf, ʰc̲hildren, layer

dir : Int

value (private) · line 47

Each move's direction: 1 clockwise, -1 counterclockwise.

dir ← 12 r̲eshape 1 -1
Used in: Mf

E : Int

value (private) · line 51

The cross-product matrices, flattened: row k holds minus the Levi-Civita symbol eps(i, j, k) over (i, j), so that row v of V '+ '× i̲nner E is [v]x, the matrix with [v]x u = v x u.

E ← 3 9 r̲eshape 0 0 0  0 0 -1  0 1 0  0 0 1  0 0 0  -1 0 0  0 -1 0  1 0 0  0 0 0
Used in: Mf

Mf : Int

value (private) · line 55

The twelve rotation matrices, flattened, one per row: a quarter turn clockwise about v (seen from v) is v v^T - [v]x, Rodrigues' formula at -90 degrees; counterclockwise flips the sign of [v]x.

Mf ← (((3 r̲eplicate 1 2 3) s̲elect₂ V) × (9 r̲eshape 1 2 3) s̲elect₂ V) − (12 9 r̲eshape 9 r̲eplicate dir) × V '+ '× i̲nner E
Used in: M

M : Int

value (private) · line 58

The twelve matrices stacked into one 36 by 3 matrix: rows 3m-2 to 3m are move m's. One product with it turns a batch every way at once.

M ← 36 3 r̲eshape Mf
Used in: ʰt̲urns

ʰt̲urns : Int -> Int

function (private) · line 63

h:t_urns R: every quarter turn applied to every 3-by-3 matrix of R, a batch of n (n 3 3); the result is 12 n 3 3, turn first. The batch is laid out 3 n 3, so one matrix product with M turns them all.

ʰt̲urns ← { R →
  n ← t̲ally R
  1 3 2 4 t̲ranspose (12 3 c̲at n c̲at 3) r̲eshape M '+ '× i̲nner 2 1 3 t̲ranspose R
}

ʰp̲os : Int -> Int

function (private) · line 70

h:p_os S: where each cubelet of each cube of the batch S (n 26 3 3) sits, R c; the result is n 26 3.

ʰp̲os ← { S → '+ r̲/₄ S × (s̲hape S) r̲eshape 2 1 3 t̲ranspose 3 26 3 r̲eshape C }

ʰc̲hildren : Int -> Int

function (private) · line 78

h:c_hildren S: all twelve turns of every cube of the batch S, 12n cubes, move-major: cube r is move 1 + (r - 1) div n of cube 1 + (r - 1) mod n. A cubelet moves when the move vector's dot product with where it sits is positive (one matrix product for every move and cubelet); a moved cubelet's matrix is the turn's matrix times its own (the other product, in h:t_urns).

ʰc̲hildren ← { S →
  n ← t̲ally S
  sh ← 12 c̲at n c̲at 26 3 3
  sel ← 0 < V '+ '× i̲nner o̲\ ((n × 26) c̲at 3) r̲eshape ʰp̲os S
  T ← sh r̲eshape ʰt̲urns ((n × 26) c̲at 3 3) r̲eshape S
  S12 ← sh r̲eshape S
  ((12 × n) c̲at 26 3 3) r̲eshape S12 + (sh r̲eshape 9 r̲eplicate r̲avel sel) × T − S12
}
Used in: ˡt̲urn

ˡt̲urn : Int -> Int -> Int

function · line 91

l:t_urn m S: move m (1 to 12) applied to every cube of the batch S.

      ᵉᶜ⁼u̲se< "Eigencube"
      ᵉᶜsolved m̲atch 4 '{ S → 5 ᵉᶜt̲urn S } p̲ower ᵉᶜsolved
1
ˡt̲urn ← { m S → (((m − 1) × t̲ally S) + r̲ange t̲ally S) s̲elect ʰc̲hildren S }
Used in: ˡd̲o

ˡd̲o : Int -> Int -> Int

function · line 98

l:d_o ms S: the moves ms (a vector of 1 to 12) applied in order to every cube of the batch S.

      ᵉᶜ⁼u̲se< "Eigencube"
      ᵉᶜsolved m̲atch 12 11 10 9 ᵉᶜd̲o 9 10 11 12 ᵉᶜd̲o ᵉᶜsolved
1
ˡd̲o ← { ms S →
  0 = t̲ally ms ? S
  (1 d̲rop ms) ˡd̲o (f̲irst ms) ˡt̲urn S
}

Stickers: the cube as six faces of nine colors

across : Int

value (private) · line 108

Each face's two directions across the page, as a cube net draws it: rightward, then downward, seen from outside the face (U with F below it, D with F above it, the side faces with U above them).

across ← 6 3 r̲eshape 0 1 0  0 1 0  0 1 0  0 -1 0  -1 0 0  1 0 0
Used in: ˡs̲tickers

down : Int

value (private) · line 109
down ← 6 3 r̲eshape 1 0 0  -1 0 0  0 0 -1  0 0 -1  0 0 -1  0 0 -1
Used in: ˡs̲tickers

ˡs̲tickers : Int -> Int

function · line 117

l:s_tickers S: the colors of the cube S (a batch of one) as a 6 by 9 matrix, a row per face (U D F B R L), its cells row by row as the face is seen; a color is the face of the solved cube it belongs to (1 to 6, the order of N). Cubelet k shows a sticker on face v when v . (R c) = 1; the sticker's color is the direction it faced in the solved cube, R^T v, so the colors are one more matrix product.

ˡs̲tickers ← { S →
  P ← 26 3 r̲eshape ʰp̲os S
  on ← r̲avel 1 = N '+ '× i̲nner o̲\ P
  cell ← (3 × 1 + down '+ '× i̲nner o̲\ P) + 2 + across '+ '× i̲nner o̲\ P
  key ← r̲avel (10 × 6 26 r̲eshape 26 r̲eplicate r̲ange 6) + cell
  home ← N '+ '× i̲nner 2 1 3 t̲ranspose 26 3 3 r̲eshape S
  color ← N i̲ndexOf 156 3 r̲eshape home
  6 9 r̲eshape (g̲rade on r̲eplicate key) s̲elect on r̲eplicate color
}

The 24 orientations, and how far each cubelet is from home

ʰg̲row : Int -> Int

function (private) · line 131

h:g_row G: the rotations G (flattened rows of 9) and every turn of them, without repeats.

ʰg̲row ← { G → u̲nique G c̲at ((12 × t̲ally G) c̲at 9) r̲eshape ʰt̲urns ((t̲ally G) c̲at 3 3) r̲eshape G }
Used in: G

G : Int

value (private) · line 134

The cube's 24 rotations, flattened, one per row: what the twelve turns reach from the identity (three rounds reach them all).

G ← 3 'ʰg̲row p̲ower 1 9 r̲eshape I

Tm : Int

value (private) · line 137

The turns' Cayley table: row m, column g is the index in G of the rotation that move m makes of rotation g.

Tm ← 12 24 r̲eshape G i̲ndexOf 288 9 r̲eshape ʰt̲urns 24 3 3 r̲eshape G

ʰo̲rient : Int -> Int

function (private) · line 140

h:o_rient S: the index in G of each cubelet's rotation, n 26.

ʰo̲rient ← { S → ((t̲ally S) c̲at 26) r̲eshape G i̲ndexOf ((26 × t̲ally S) c̲at 9) r̲eshape S }

at : Int

value (private) · line 144

Where each cubelet sits in each rotation, 26 24: the index in pts of G c, from one matrix product of every rotation with every name.

at ← 26 24 r̲eshape pts i̲ndexOf 624 3 r̲eshape 2 3 1 t̲ranspose 24 3 26 r̲eshape (72 3 r̲eshape G) '+ '× i̲nner o̲\ C
Used in: ʰn̲ext

layer : Int

value (private) · line 147

Which points each move turns, 12 27: the dot product of the move vector with the point is positive.

layer ← 1 × 0 < V '+ '× i̲nner o̲\ pts
Used in: ʰn̲ext

ʰn̲ext : Int -> Int

function (private) · line 153

h:n_ext O: h:c_hildren for cubes given by their orientations O (n 26), as orientations (12n 26): the same turns, read from the two tables the matrices made (at, layer and the Cayley table Tm), so the search moves 26 small integers per cube instead of 26 matrices.

ʰn̲ext ← { O →
  n ← t̲ally O
  m ← (n × 26) r̲eplicate o̲ffsets 12
  o ← (12 × n × 26) r̲eshape r̲avel O
  P ← ((r̲avel O) + 24 × (n × 26) r̲eshape o̲ffsets 26) s̲elect r̲avel at
  sel ← ((27 × m) + (12 × n × 26) r̲eshape P) s̲elect r̲avel layer
  ((12 × n) c̲at 26) r̲eshape o + sel × (((24 × m) + o) s̲elect r̲avel Tm) − o
}

ʰr̲elax : Num a => a -> a

function (private) · line 165

h:r_elax d: a table of distances (cubelet by rotation) relaxed along every turn until it stops changing, so d[k; g] becomes the fewest quarter turns that take rotation g to one where cubelet k is done.

ʰr̲elax ← { d →
  e ← d m̲in 1 + 'm̲in r̲/₂ 26 12 24 r̲eshape (r̲avel Tm) s̲elect₂ d
  e m̲atch d ? d
  ʰr̲elax e
}

ʰd̲istances : Num a => a -> a

function (private) · line 172

h:d_istances done: the distance table from a 26 by 24 table of 1s where the cubelet counts as done in that rotation.

ʰd̲istances ← { done → ʰr̲elax 99 × 1 − done }
Used in: Dsolved, Dplace

colDev : Int

value (private) · line 175

How far column j of each rotation is from e_j, 24 3.

colDev ← '+ r̲/₂ a̲bs (24 3 3 r̲eshape G) − 24 3 3 r̲eshape I

Dsolved : Int

value (private) · line 179

Solved distances, 26 24 (eigencube.py's min_moves_to_solved): a cubelet is solved when its rotation fixes each axis it has stickers on, the nonzero coordinates of its name.

Dsolved ← ʰd̲istances 0 = (a̲bs C) '+ '× i̲nner o̲\ colDev

ˡs̲olved? : Truthy a => Int -> a

function · line 186

l:s_olved? S: whether every cubelet of the cube S (a batch of one) is solved (eigencube.py's is_cube_solved): a center may still be turned about its own axis, which no sticker shows.

      ᵉᶜ⁼u̲se< "Eigencube"
      (ᵉᶜs̲olved? ᵉᶜsolved) c̲at ᵉᶜs̲olved? 1 ᵉᶜt̲urn ᵉᶜsolved
1 0
ˡs̲olved? ← { S → 0 = '+ r̲/ ((r̲avel ʰo̲rient S) + 24 × o̲ffsets 26) s̲elect r̲avel Dsolved }

posDev : Int

value (private) · line 188

How far each rotation moves each home point, 24 26.

posDev ← '+ r̲/₂ a̲bs (24 3 26 r̲eshape (72 3 r̲eshape G) '+ '× i̲nner o̲\ C) − 24 3 26 r̲eshape o̲\ C
Used in: Dplace

Dplace : Int

value (private) · line 191

Place distances, 26 24 (eigencube.py's min_moves_to_position): a cubelet is in place when R c = c, its stickers perhaps twisted.

Dplace ← ʰd̲istances 0 = o̲\ posDev
Used in: ʰs̲core

yellowDown : Int

value (private) · line 193

Whether each rotation keeps the bottom face (yellow) down: R fixes z.

yellowDown ← 1 × 0 = 3 s̲elect₂ colDev

The stages: goals and heuristics as matrix products

sets : Int

value (private) · line 201

The cubelet sets the stages count over, one per row: 1 none, 2 top edges, 3 top layer, 4 top and middle layers, 5 bottom edges, 6 bottom corners, 7 middle layer, 8 bottom layer but its corners, 9 all but the bottom corners.

sets ← 9 26 r̲eshape (26 r̲eshape 0) c̲at ((z = 1) ∧ kind = 2) c̲at (z = 1) c̲at (z ≥ 0) c̲at ((z = -1) ∧ kind = 2) c̲at ((z = -1) ∧ kind = 3) c̲at (z = 0) c̲at ((z = -1) ∧ kind < 3) c̲at n̲ot (z = -1) ∧ kind = 3

ʰs̲pec : Num a => a -> a

function (private) · line 213

h:s_pec s: stage s (1 to 29) as 7 rows of 3. A goal is three counts at least their thresholds: count c is how many cubelets of set row1[c] are solved, plus of set row2[c] have yellow down, plus of set row3[c] are in place; row 6 holds the thresholds. The heuristic is a sum of three terms, term t (sum of sqrt d)^2 / row7[t], the sum over set row4[t] of solved distances and over set row5[t] of place distances (eigencube.py's p-norms with p = 1/2). Stages 1-17 solve the top and middle layers a cubelet at a time, 18-25 the bottom edges (yellow down, then solved), 26-29 put the bottom corners in place.

ʰs̲pec ← { s →
  s ≤ 9 ? 7 3 r̲eshape (2 3 4 1 1 1 1 1 1 3 1 1 1 1 1) c̲at (4 m̲in s) c̲at s c̲at s c̲at 8 1 1
  s ≤ 17 ? 7 3 r̲eshape (2 3 4 1 1 1 1 1 1 4 1 1 1 1 1 4 9) c̲at s c̲at 4 1 1
  s ≤ 25 ? 7 3 r̲eshape (4 1 5 1 5 1 1 1 1 9 1 1 1 1 1 17) c̲at (4 m̲in s − 17) c̲at (s − 21) c̲at 3 1 1
  7 3 r̲eshape (4 5 1 1 1 1 1 1 6 3 7 8 1 1 6 17 4) c̲at (s − 25) c̲at 5 3 8
}
Used in: ʰs̲tage

ʰo̲f : Int -> Int -> Int

function (private) · line 222

h:o_f r spec: the cubelet sets named by row r of a stage's spec, as the columns of a 26 by 3 matrix of 1s and 0s.

ʰo̲f ← { r spec → o̲\ (r s̲elect spec) s̲elect sets }
Used in: ʰs̲core

ʰr̲elevant : Truthy a => Int -> a

function (private) · line 226

h:r_elevant spec: the cubelets a stage looks at, 1s and 0s over the 26: those of every set its goal and heuristic name (rows 1 to 5).

ʰr̲elevant ← { spec → 0 < '+ r̲/ (r̲avel 5 t̲ake spec) s̲elect sets }

ʰp̲roject : Num a => a -> a -> a

function (private) · line 231

h:p_roject rel O: the orientations O with every cubelet the stage does not look at (rel 0) set to 1, the identity. A turn moves each cubelet by its own position and rotation alone, never by the others, so cubes that differ only there are one state to the stage's search.

ʰp̲roject ← { rel O → 1 + (O − 1) × (s̲hape O) r̲eshape rel }

ʰs̲core : Int -> Int -> Float

function (private) · line 236

h:s_core spec O: each cube's search score, from its cubelets' orientations O (n 26, h:o_rient): -1 for a cube that meets the stage's goal, else its heuristic, a Float per cube.

ʰs̲core ← { spec O →
  n ← t̲ally O
  o ← r̲avel O
  ix ← o + 24 × (n × 26) r̲eshape o̲ffsets 26
  ds ← (n c̲at 26) r̲eshape ix s̲elect r̲avel Dsolved
  dp ← (n c̲at 26) r̲eshape ix s̲elect r̲avel Dplace
  yd ← (n c̲at 26) r̲eshape o s̲elect yellowDown
  cnt ← ((ds = 0) '+ '× i̲nner (1 ʰo̲f spec)) + (yd '+ '× i̲nner (2 ʰo̲f spec)) + (dp = 0) '+ '× i̲nner (3 ʰo̲f spec)
  goal ← '∧ r̲/₂ cnt ≥ (n c̲at 3) r̲eshape 6 s̲elect spec
  q ← (((f̲loat ds) ^ 0.5) '+ '× i̲nner f̲loat 4 ʰo̲f spec) + ((f̲loat dp) ^ 0.5) '+ '× i̲nner f̲loat 5 ʰo̲f spec
  h ← (q × q) '+ '× i̲nner 1 ÷ 7 s̲elect spec
  ((f̲loat n̲ot goal) × h + 1.0) − 1.0
}

Search: A*, a batch of cubes at a time

batch : Int

value (private) · line 253

How many of the best open cubes each step of the search expands.

batch ← 256
Used in: ʰs̲tep

greed : Float

value (private) · line 255

How much more the heuristic weighs than the moves made (weighted A*).

greed ← 1.0
Used in: ʰs̲tep

ˡi̲nverse : Int -> Int

function · line 258

l:i_nverse m: the move that undoes move m (0, no move, has none).

ˡi̲nverse ← { m → m + (2 × m m̲od 2) − 1 }
Used in: ʰs̲tep

ʰf̲ace : Int -> Int

function (private) · line 260

h:f_ace m: the face move m turns, 1 to 6 (0 for no move).

ʰf̲ace ← { m → (m + 1) d̲iv 2 }
Used in: ʰs̲tep, ˡn̲ame

ʰc̲ubes : Int -> Int

function (private) · line 264

h:c_ubes O: the cubes whose cubelets' orientations are O (n 26), their rotation matrices looked up in G, n 26 3 3.

ʰc̲ubes ← { O → ((t̲ally O) c̲at 26 3 3) r̲eshape (r̲avel O) s̲elect G }

ʰk̲ey : Num a => a -> a

function (private) · line 267

h:k_ey O: each cube's key, n 2: its 26 orientations (1 to 24) as two 13-digit numbers in base 24, so one i_ndexOf finds a cube.

ʰk̲ey ← { O → o̲\ (2 c̲at t̲ally O) r̲eshape (24 d̲ecode o̲\ 13 t̲ake₂ O − 1) c̲at 24 d̲ecode o̲\ -13 t̲ake₂ O − 1 }

macros : Box Int

value (private) · line 281

The macros: the solver's moves for the middle and bottom layers, each a fixed sequence of turns. 1 to 3: D, D' and D2. 4 to 8: the classic sequences for the last layer, mirrored from the top to the bottom (U and D swap, R and L swap): F L D L' D' F' flips two bottom edges, Sune (L D L' D L D2 L') and its mirror cycle edges and twist corners, D L D' R' D L' D' R and its inverse cycle three bottom corners. 9 to 16: a middle edge brought up from the bottom into each of the four slots, from either side (U R U' R' U' F' U F and its mirror, mirrored the same way and turned about the vertical axis). Each keeps the top layer whole, and each of 1 to 8 the top two layers, so a search over them builds on what is solved (as the endgame's twist does in eigencube.py).

macros ← (e̲nclose 1 r̲eshape 3) c̲at (e̲nclose 1 r̲eshape 4) c̲at (e̲nclose 3 3) c̲at (e̲nclose 5 11 3 12 4 6) c̲at (e̲nclose 11 3 12 3 11 3 3 12) c̲at (e̲nclose 11 3 3 12 4 11 4 12) c̲at (e̲nclose 3 11 4 10 3 12 4 9) c̲at (e̲nclose 10 3 11 4 9 3 12 4) c̲at (e̲nclose 3 11 4 12 4 6 3 5) c̲at (e̲nclose 3 5 4 6 4 10 3 9) c̲at (e̲nclose 3 9 4 10 4 8 3 7) c̲at (e̲nclose 3 7 4 8 4 12 3 11) c̲at (e̲nclose 4 10 3 9 3 5 4 6) c̲at (e̲nclose 4 8 3 7 3 9 4 10) c̲at (e̲nclose 4 12 3 11 3 7 4 8) c̲at e̲nclose 4 6 3 5 3 11 4 12

middle : Int

value (private) · line 284

The macros the middle layer's stages search over: D turns and the insertions.

middle ← 1 2 3 9 10 11 12 13 14 15 16
Used in: ʰa̲ctions

bottom : Int

value (private) · line 287

The macros the bottom layer's stages search over: D turns and the last-layer sequences.

bottom ← 1 2 3 4 5 6 7 8
Used in: ʰa̲ctions

ʰa̲ctions : (Num a, Num b) => a -> b -> Int

function (private) · line 292

h:a_ctions hybrid s: what stage s searches over: none for the twelve turns (all stages when hybrid is 0, eigencube.py's search alone; the top layer always), else the middle or the bottom layer's macros.

ʰa̲ctions ← { hybrid s →
  (hybrid = 0) ∨ s ≤ 9 ? o̲ffsets 0
  s ≤ 17 ? middle
  bottom
}
Used in: ʰs̲tage

ʰa̲long : Int -> Int -> Int

function (private) · line 300

h:a_long ms O: the moves ms applied in order to the cubes O (n 26), as orientations.

ʰa̲long ← { ms O →
  0 = t̲ally ms ? O
  (1 d̲rop ms) ʰa̲long ((((f̲irst ms) − 1) × t̲ally O) + r̲ange t̲ally O) s̲elect ʰn̲ext O
}

ʰm̲acros : Int -> Int -> Int

function (private) · line 306

h:m_acros set O: every macro of the set (indices into macros) applied to every cube of O, macro-major as h:n_ext orders the turns.

ʰm̲acros ← { set O →
  0 = t̲ally set ? 0 26 r̲eshape 0
  ((d̲isclose (f̲irst set) s̲elect macros) ʰa̲long O) c̲at (1 d̲rop set) ʰm̲acros O
}

ʰe̲xpand : Int -> Int -> Int

function (private) · line 312

h:e_xpand set O: the cubes one action from O: the twelve turns for an empty set, else the set's macros.

ʰe̲xpand ← { set O →
  0 = t̲ally set ? ʰn̲ext O
  set ʰm̲acros O
}
Used in: ʰs̲tep

ʰf̲latten : Int -> Int

function (private) · line 317

h:f_latten acts: macros (indices into macros) as the moves they make.

ʰf̲latten ← { acts →
  0 = t̲ally acts ? o̲ffsets 0
  (d̲isclose (f̲irst acts) s̲elect macros) c̲at ʰf̲latten 1 d̲rop acts
}

ʰs̲tep : Num a => (Int, Int) -> (Int, Int, Int, Int, a, Float, Int) -> Int

function (private) · line 331

h:s_tep spec table: one step of the search. The table holds every cube found, a row each: its key, its orientations, the cube it was reached from, the move, its moves from the start g and its priority g + greed * h; then the open cubes, best first. A step takes the best batch open cubes, turns each every way at once, drops a move that undoes the last one or turns two opposite faces out of order (they commute; eigencube.py prunes the same two), and adds the cubes not found before. It stops on a cube that meets the goal, giving the moves to it.

ʰs̲tep ← { (spec, set) (keys, O, par, mv, g, f, open) →
  0 = t̲ally open ? "stuck" ⎕S̲IGNAL "the search ran out of cubes"
  pick ← (batch m̲in t̲ally open) t̲ake open
  n ← t̲ally pick
  r ← o̲ffsets n × (t̲ally set) + 12 × 0 = t̲ally set
  cm ← 1 + r d̲iv n
  cp ← (1 + r m̲od n) s̲elect pick
  last ← cp s̲elect mv
  ok ← w̲here (0 < t̲ally set) ∨ n̲ot (cm = ˡi̲nverse last) ∨ (ʰf̲ace last) = 1 + (ʰf̲ace cm) × 1 = 2 m̲od ʰf̲ace cm
  KO ← (ʰr̲elevant spec) ʰp̲roject ok s̲elect set ʰe̲xpand pick s̲elect O
  cp ← ok s̲elect cp
  cm ← ok s̲elect cm
  sc ← spec ʰs̲core KO
  hit ← w̲here sc < 0
  0 < t̲ally hit ? ((par, mv) ʰt̲race f̲irst hit s̲elect cp) c̲at f̲irst hit s̲elect cm
  KK ← ʰk̲ey KO
  new ← w̲here ((KK i̲ndexOf KK) = r̲ange t̲ally KK) ∧ (keys i̲ndexOf KK) > t̲ally keys
  ng ← 1 + new s̲elect cp s̲elect g
  f2 ← f c̲at (f̲loat ng) + greed × new s̲elect sc
  rest ← (batch d̲rop open) c̲at (t̲ally keys) + r̲ange t̲ally new
  (spec, set) ʰs̲tep (keys c̲at new s̲elect KK, O c̲at new s̲elect KO, par c̲at new s̲elect cp, mv c̲at new s̲elect cm, g c̲at ng, f2, (g̲rade rest s̲elect f2) s̲elect rest)
}

ʰt̲race : (Int, Int) -> Int -> Int

function (private) · line 356

h:t_race (par, mv) i: the moves from the start (cube 1) to cube i, by following each cube back to the one it was reached from.

ʰt̲race ← { (par, mv) i →
  1 = i ? o̲ffsets 0
  ((par, mv) ʰt̲race i s̲elect par) c̲at i s̲elect mv
}

ʰs̲earch : (Int, Int) -> Int -> Int

function (private) · line 364

h:s_earch (spec, set) S: the moves taking the cube S (a batch of one) to the stage's goal, found by h:s_tep from a table holding S alone, over the twelve turns or the set's macros.

ʰs̲earch ← { (spec, set) S →
  O ← (ʰr̲elevant spec) ʰp̲roject ʰo̲rient S
  0 > f̲irst spec ʰs̲core O ? o̲ffsets 0
  acts ← (spec, set) ʰs̲tep (ʰk̲ey O, O, 1 r̲eshape 0, 1 r̲eshape 0, 1 r̲eshape 0, 1 r̲eshape 0.0, 1 r̲eshape 1)
  0 = t̲ally set ? acts
  ʰf̲latten acts s̲elect set
}
Used in: ʰs̲tage

Solving

ʰs̲tage : Num a => a -> (Int, Int, Int) -> (Int, Int, Int)

function (private) · line 377

h:s_tage hybrid (S, ms, lens): the next search stage run on the cube S, its moves added to the moves so far ms and its length to the stage lengths (h:a_ctions says what it searches over).

ʰs̲tage ← { hybrid (S, ms, lens) →
  s ← 1 + t̲ally lens
  next ← ((ʰs̲pec s), hybrid ʰa̲ctions s) ʰs̲earch S
  (next ˡd̲o S, ms c̲at next, lens c̲at t̲ally next)
}
Used in: ˡs̲olve

twist : Int

value (private) · line 384

The endgame's twist: L' U' L U, twice (eigencube.py's routine).

twist ← 12 2 11 1 12 2 11 1
Used in: ʰc̲orner

bottomTurn : Int

value (private) · line 386

The bottom turn eigencube.py makes between corners, D' here.

bottomTurn ← 1 r̲eshape 4

ʰo̲riented? : Int -> Int

function (private) · line 391

h:o_riented? S: whether the corner at front-left-bottom, (1 -1 -1), has its yellow sticker down, so that turns of the bottom alone solve it (eigencube.py's is_corner_oriented): its rotation fixes z.

ʰo̲riented? ← { S →
  k ← (26 3 r̲eshape ʰp̲os S) i̲ndexOf 1 -1 -1
  (k s̲elect r̲avel ʰo̲rient S) s̲elect yellowDown
}
Used in: ʰc̲orner

ʰc̲orner : (Int, Int) -> (Int, Int)

function (private) · line 398

h:c_orner (S, ms): twist the front-left-bottom corner until it is oriented, then turn the bottom to bring the next corner there.

ʰc̲orner ← { (S, ms) →
  ʰo̲riented? S ? (bottomTurn ˡd̲o S, ms c̲at bottomTurn)
  ʰc̲orner (twist ˡd̲o S, ms c̲at twist)
}

ʰf̲inish : (Int, Int) -> (Int, Int)

function (private) · line 404

h:f_inish (S, ms): turn the bottom until the cube is solved.

ʰf̲inish ← { (S, ms) →
  ˡs̲olved? S ? (S, ms)
  ʰf̲inish (bottomTurn ˡd̲o S, ms c̲at bottomTurn)
}

ˡs̲olve : Num a => a -> Int -> (Int, Int, Int)

function · line 417

l:s_olve hybrid S: a solution of the cube S (a batch of one), after eigencube.py's solve: 29 search stages (the top and middle layers a cubelet at a time, then the bottom edges and the bottom corners' places), then the endgame's corner twists. With hybrid 1 the middle and bottom stages search over macros, as a person solving by hand uses known sequences (seconds); with 0 every stage searches the twelve turns, eigencube.py's search alone (minutes in X_eTaL). The moves, each stage's length, and the endgame's length.

ˡs̲olve ← { hybrid S →
  (S1, ms, lens) ← 29 '{ st → hybrid ʰs̲tage st } p̲ower (S, o̲ffsets 0, o̲ffsets 0)
  (_, all) ← ʰf̲inish 4 'ʰc̲orner p̲ower (S1, ms)
  (all, lens, (t̲ally all) − t̲ally ms)
}
Used in: %10

Notation

ˡn̲ame : Int -> Char

function · line 427

l:n_ame ms: moves as text in the usual notation, U for the top face clockwise, U' counterclockwise, separated by spaces.

ˡn̲ame ← { ms →
  n ← t̲ally ms
  marks ← (1 + ms m̲od 2) s̲elect "' "
  txt ← r̲avel o̲\ (3 c̲at n) r̲eshape ((ʰf̲ace ms) s̲elect ˡfaces) c̲at marks c̲at n r̲eshape " "
  -1 d̲rop (r̲avel o̲\ (3 c̲at n) r̲eshape (n r̲eshape 1) c̲at (marks = f̲irst "'") c̲at n r̲eshape 1) r̲eplicate txt
}

ˡp̲arse : Char -> Int

function · line 442

ec:p_arse t: moves written in the usual notation as numbers: each letter of ec:faces is a move, counterclockwise when a ' follows it; anything else between moves is ignored. The inverse of ec:n_ame.

      ᵉᶜ⁼u̲se< "Eigencube"
      ᵉᶜp̲arse "U R' F B'"
1 10 5 8
      (ᵉᶜp̲arse ᵉᶜn̲ame 3 12 7) m̲atch 3 12 7
1
ˡp̲arse ← { t →
  at ← w̲here t m̲ember? ˡfaces
  f ← ˡfaces i̲ndexOf at s̲elect t
  (2 × f) − n̲ot ((1 + at) s̲elect t c̲at " ") = f̲irst "'"
}