emilgoldsmith / elm-speedcubing / PLL

Types and helper functions to work with the Permutate Last Layer (PLL) algorithm set, the last step of the CFOP method. See https://www.speedsolving.com/wiki/index.php/PLL for further information

Definition And Constructors


type PLL
    = H
    | Ua
    | Ub
    | Z
    | Aa
    | Ab
    | E
    | F
    | Ga
    | Gb
    | Gc
    | Gd
    | Ja
    | Jb
    | Na
    | Nb
    | Ra
    | Rb
    | T
    | V
    | Y

All the cases are represented here. Use the value constructors here or @all to specify a given case in your code, and pass these to any of the helper functions

all : List.Nonempty.Nonempty PLL

A non-empty list of all the PLLs. Can for example be used for randomly selecting a pll

import List.Nonempty

-- All the PLLs are there!
List.Nonempty.length all --> 21

-- We could for example select a random PLL case
-- via this
List.Nonempty.sample all

Helpers

getLetters : PLL -> String

Generates a string of the identifying letters of the case.

This could be used either for serialization purposes, or for building a string to display the user in some instances.

-- Format is always first letter capitalized and
-- the second one lower case if applicable
getLetters Ua --> "Ua"

getLetters H --> "H"

solvedBy : Algorithm -> PLL -> Basics.Bool

Check whether an algorithm solves a PLL case.

Note that actually solving the cube depends on it being in the correct execution angle as well and doing the last AUF. This function just checks if with those correctly aligned the algorithm can solve it. So different algorithms can pass this as seen in these examples:

import Algorithm

Algorithm.fromString "(x) R' U R' D2 R U' R' D2 R2 (x')"
    |> Result.map (\alg -> solvedBy alg Aa)
--> Ok True

Algorithm.fromString "U (x) R' U R' D2 R U' R' D2 R2 (x')"
    |> Result.map (\alg -> solvedBy alg Aa)
--> Ok True

Algorithm.fromString "(x) R' U R' D2 R U' R' D2 R2 (x') U"
    |> Result.map (\alg -> solvedBy alg Aa)
--> Ok True

Algorithm.fromString "U"
    |> Result.map (\alg -> solvedBy alg Aa)
--> Ok False

getAllEquivalentAUFs : ( AUF, PLL, AUF ) -> List.Nonempty.Nonempty ( AUF, AUF )

Calculates and returns all pairs of AUFs that are equivalent to the given pair of AUFs for the given PLL

import AUF
import Expect
import Expect.Extra exposing (equalNonEmptyListMembers)
import List.Nonempty

getAllEquivalentAUFs ( AUF.None, H, AUF.None )
    |> equalNonEmptyListMembers
        (List.Nonempty.Nonempty
            ( AUF.None, AUF.None )
            [ ( AUF.Clockwise, AUF.CounterClockwise )
            , ( AUF.Halfway, AUF.Halfway )
            , ( AUF.CounterClockwise, AUF.Clockwise )
            ]
        )
--> Expect.pass

getAllAUFEquivalencyClasses : PLL -> List.Nonempty.Nonempty (List.Nonempty.Nonempty ( AUF, AUF ))

Returns a list of lists, where each sublist represents an equivalency class of AUF pairs for the given The equivalency classes are also exhaustive, so every possible AUF pair is represented in this list of lists

Two Sided Recognition


type alias RecognitionSpecification =
{ caseRecognition : CaseRecognitionSpecification
, postAUFRecognition : PostAUFRecognitionSpecification 
}

Describes a unique way to recognize the pll case from just the two sides given by the RecognitionAngle. It also describes one or more possibilities for how to recognize which way to do the final AUF.


type alias CaseRecognitionSpecification =
{ patterns : Maybe (List.Nonempty.Nonempty RecognitionPattern)
, absentPatterns : Maybe (List.Nonempty.Nonempty RecognitionPattern)
, oppositelyColored : List ( List.Nonempty.Nonempty RecognitionElement
, List.Nonempty.Nonempty RecognitionElement )
, adjacentlyColored : List ( List.Nonempty.Nonempty RecognitionElement
, List.Nonempty.Nonempty RecognitionElement )
, identicallyColored : List ( RecognitionElement
, RecognitionElement
, List RecognitionElement )
, differentlyColored : List ( RecognitionElement
, RecognitionElement
, List RecognitionElement )
, noOtherStickersMatchThanThese : Maybe (List.Nonempty.Nonempty RecognitionElement)
, noOtherBlocksPresent : Basics.Bool 
}

Describes a unique way to recognize the pll case from just the two sides given by the RecognitionAngle


type alias PostAUFRecognitionSpecification =
List.Nonempty.Nonempty { elementsWithOriginalFace : List.Nonempty.Nonempty ( RecognitionElement
, Cube.Advanced.Face )
, finalFace : Cube.Advanced.Face 
}

Describes one or more possibilities for how to recognize which way to do the final AUF


type RecognitionElement
    = Pattern RecognitionPattern
    | Sticker Sticker

Either a type of pattern on the two sides or just a single sticker on the two sides.


type RecognitionPattern
    = LeftHeadlights
    | RightHeadlights
    | LeftThreeBar
    | RightThreeBar
    | LeftInsideTwoBar
    | RightInsideTwoBar
    | LeftOutsideTwoBar
    | RightOutsideTwoBar
    | Bookends
    | LeftFourChecker
    | RightFourChecker
    | InnerFourChecker
    | LeftFiveChecker
    | RightFiveChecker
    | SixChecker

All the possible patterns that we use to recognize the pll case.


type Sticker
    = FirstStickerFromLeft
    | SecondStickerFromLeft
    | ThirdStickerFromLeft
    | FirstStickerFromRight
    | SecondStickerFromRight
    | ThirdStickerFromRight

The six visible stickers during two sided recognition.

getUniqueTwoSidedRecognitionSpecification : { pllAlgorithmUsed : Algorithm, recognitionAngle : RecognitionAngle, preAUF : AUF, pll : PLL } -> Result RecognitionError RecognitionSpecification

Gets a two sided recognition specification that uniquely identifies the pll case and preAUF


type RecognitionAngle

The angle the cube is being looked at while doing two sided recognition

uflRecognitionAngle : RecognitionAngle

Describes looking at the cube so that the U F and L sides are visible

ufrRecognitionAngle : RecognitionAngle

Describes looking at the cube so that the U F and R sides are visible


type RecognitionError
    = IncorrectPLLAlgorithm PLL Algorithm

Occurs if the pll algorithm provided to getUniqueTwoSidedRecognitionSpecification for the case does not solve the case

Symmetry

getSymmetry : PLL -> PLLWithSymmetryInfo

Get the type of symmetry this PLL case displays


type PLLWithSymmetryInfo
    = FullySymmetric FullySymmetricPLL
    | HalfSymmetric HalfSymmetricPLL
    | NPermSymmetric NPermSymmetricPLL
    | NotSymmetric NonSymmetricPLL

A PLL with the classification of it by the symmetry patterns of the case. The descriptions of the different classes of symmetries can be found below


type FullySymmetricPLL
    = FullSymH

A fully symmetric PLL where you can use the same algorithm to solve it from any angle and doing a preAUF and a postAUF are equivalent. For example (U, pll-alg, U) is equivalent to (no-auf, pll-alg, U2)


type HalfSymmetricPLL
    = HalfSymZ
    | HalfSymE

A half symmetric PLL which has the same AUF properties as a fully symmetric PLL but only opposing faces can be solved with the same PLL algorithm, so AUF transformations can also only happen by adding or subtracting U2s to the AUFs


type NPermSymmetricPLL
    = NPermSymNa
    | NPermSymNb

An N-Perm symmetric PLL (which only includes the N-Perms) can like fully symmetric PLLs be solved with the same algorithm from any angle, but here a transformation through pre-AUF causes the inverse transformation for the post-AUF. It is best explained with an example: With the same example of (U, pll-alg, U) from the fully symmetric case, with an N-perm it would now be equivalent to (no-auf, pll-alg, no-auf)


type NonSymmetricPLL
    = NotSymUa
    | NotSymUb
    | NotSymAa
    | NotSymAb
    | NotSymF
    | NotSymGa
    | NotSymGb
    | NotSymGc
    | NotSymGd
    | NotSymJa
    | NotSymJb
    | NotSymRa
    | NotSymRb
    | NotSymT
    | NotSymV
    | NotSymY

A non-symmetric PLL. Any given PLL algorithm can only solve this case from a single angle and therefore no AUF transformations make sense in this case either

pllWithSymmetryInfoToPLL : PLLWithSymmetryInfo -> PLL

Convert a PLL with symmetry info to a normal PLL type

fullySymmetricPLLToPLL : FullySymmetricPLL -> PLL

Convert a fully symmetrical PLL with symmetry info to a normal PLL type

halfSymmetricPLLToPLL : HalfSymmetricPLL -> PLL

Convert a half symmetrical PLL with symmetry info to a normal PLL type

nPermSymmetricPLLToPLL : NPermSymmetricPLL -> PLL

Convert an N-perm symmetrical PLL with symmetry info to a normal PLL type

nonSymmetricPLLToPLL : NonSymmetricPLL -> PLL

Convert a non-symmetrical PLL with symmetry info to a normal PLL type

Collections


type alias Algorithms =
{ h : Algorithm
, ua : Algorithm
, ub : Algorithm
, z : Algorithm
, aa : Algorithm
, ab : Algorithm
, e : Algorithm
, f : Algorithm
, ga : Algorithm
, gb : Algorithm
, gc : Algorithm
, gd : Algorithm
, ja : Algorithm
, jb : Algorithm
, na : Algorithm
, nb : Algorithm
, ra : Algorithm
, rb : Algorithm
, t : Algorithm
, v : Algorithm
, y : Algorithm 
}

A collection of algorithms that solves the respective case

getAlgorithm : Algorithms -> PLL -> Algorithm

Get the algorithm for the PLL case from an algorithm collection. This helps avoid any typos or need to write your own case statements in order to get the algorithm for a case passed to a function

getAlgorithm referenceAlgorithms Y
--> referenceAlgorithms.y

referenceAlgorithms : Algorithms

Plls verified to be correct so they can be used to verify user selected plls or for displaying a pll case somewhere on the site.

They have been chosen to be the optimally lowest move count in HTM just for a small performance boost.

The example tests below are just meant for an easier to read version of all the algorithms that are verified to be correct. They are also tested via elm-verify-examples so the string versions are correct equivalents to the code below.

import Algorithm

-- Edges Only

Algorithm.fromString "R2 U2 R U2 R2 U2 R2 U2 R U2 R2"
--> Ok referenceAlgorithms.h

Algorithm.fromString "F2 U' (L R') F2 (L' R) U' F2"
--> Ok referenceAlgorithms.ua

Algorithm.fromString "F2 U (R' L) F2 (R L') U F2"
--> Ok referenceAlgorithms.ub

Algorithm.fromString "R B' R' B F R' F B' R' B R F2"
--> Ok referenceAlgorithms.z

-- Corners Only

Algorithm.fromString "R' F R' B2 R F' R' B2 R2"
--> Ok referenceAlgorithms.aa

Algorithm.fromString "R B' R F2 R' B R F2 R2"
--> Ok referenceAlgorithms.ab

Algorithm.fromString "D R' D2 F' D L D' F D2 R D' F' L' F"
--> Ok referenceAlgorithms.e

-- Corners And Edges

Algorithm.fromString "L F R' F' L' F' D2 B' L' B D2 F' R F2"
--> Ok referenceAlgorithms.f

Algorithm.fromString "F2' D (R' U R' U' R) D' F2 L' U L"
--> Ok referenceAlgorithms.ga

Algorithm.fromString "R' U' R B2 D (L' U L U' L) D' B2"
--> Ok referenceAlgorithms.gb

Algorithm.fromString "R2' D' F U' F U F' D R2 B U' B'"
--> Ok referenceAlgorithms.gc

Algorithm.fromString "R U R' F2 D' (L U' L' U L') D F2"
--> Ok referenceAlgorithms.gd

Algorithm.fromString "B2 R' U' R B2 L' D L' D' L2"
--> Ok referenceAlgorithms.ja

Algorithm.fromString "B2 (L U L') B2 (R D' R D) R2"
--> Ok referenceAlgorithms.jb

Algorithm.fromString "L U' R U2 L' U R' L U' R U2 L' U R'"
--> Ok referenceAlgorithms.na

Algorithm.fromString "R' U L' U2 R U' L R' U L' U2 R U' L"
--> Ok referenceAlgorithms.nb

Algorithm.fromString "F2 R' F' U' F' U F R F' U2 F U2 F'"
--> Ok referenceAlgorithms.ra

Algorithm.fromString "R2 F R U R U' R' F' R U2 R' U2 R"
--> Ok referenceAlgorithms.rb

Algorithm.fromString "F2 D R2 U' R2 F2 D' L2 U L2"
--> Ok referenceAlgorithms.t

Algorithm.fromString "R' U R' U' B' R' B2 U' B' U B' R B R"
--> Ok referenceAlgorithms.v

Algorithm.fromString "F2 D R2 U R2 D' R' U' R F2 R' U R"
--> Ok referenceAlgorithms.y