emilgoldsmith / elm-speedcubing / Algorithm

Definition


type Algorithm

Any sequence of turns on a 3x3 Rubik's Cube. Usually used to describe an algorithm used to solve a specific case for speedcubing.

The notation used is based on: https://www.speedsolving.com/wiki/index.php/Notation

It is meant to be used for speedcubing so it includes options such as cube rotations, wide moves and three-quarter turns even if those aren't necessary to be able to solve the cube, but can be important in describing the fastest way to solve a given case for a human


type Turn
    = Turn Turnable TurnLength TurnDirection

Describes a single turn, which is rotating any turnable in a given direction for a given amount of degrees


type Turnable
    = U
    | D
    | L
    | R
    | F
    | B
    | M
    | S
    | E
    | Uw
    | Dw
    | Lw
    | Rw
    | Fw
    | Bw
    | X
    | Y
    | Z

Describes anything that can be turned on a Rubik's Cube such as a face, a slice, a rotation of the whole cube or several slices together also known as a wide move


type TurnLength
    = OneQuarter
    | Halfway
    | ThreeQuarters

Describes how much to turn a turnable in a turn


type TurnDirection
    = Clockwise
    | CounterClockwise

Describes which direction to turn a turnable in a turn

Constructors

fromTurnList : List Turn -> Algorithm

Create an Algorithm from a list of turns

fromTurnList
    [ Turn U OneQuarter CounterClockwise
    , Turn B Halfway Clockwise
    ]

empty : Algorithm

An empty algorithm

(De)Serialization

toString : Algorithm -> String

Display an algorithm in a standard format readable by humans

fromTurnList
    [ Turn U Halfway CounterClockwise
    , Turn F OneQuarter Clockwise
    ]
    |> toString
--> "U2' F"

fromString : String -> Result FromStringError Algorithm

Parses user input and either returns the algorithm described by the string or a detailed error description of why it failed

fromString "" --> Err EmptyAlgorithm

fromString "U" --> Ok (fromTurnList [Turn U OneQuarter Clockwise])


type FromStringError
    = EmptyAlgorithm
    | InvalidTurnable ({ inputString : String, errorIndex : Basics.Int, invalidTurnable : String })
    | InvalidTurnLength ({ inputString : String, errorIndex : Basics.Int, invalidLength : String })
    | RepeatedTurnable ({ inputString : String, errorIndex : Basics.Int })
    | WideMoveStylesMixed ({ inputString : String, errorIndex : Basics.Int, invalidWideMove : String })
    | TurnWouldWorkWithoutInterruption ({ inputString : String, interruptionStart : Basics.Int, interruptionEnd : Basics.Int })
    | ApostropheWrongSideOfLength ({ inputString : String, errorIndex : Basics.Int })
    | UnclosedParenthesis ({ inputString : String, openParenthesisIndex : Basics.Int })
    | UnmatchedClosingParenthesis ({ inputString : String, errorIndex : Basics.Int })
    | EmptyParentheses ({ inputString : String, errorIndex : Basics.Int })
    | NestedParentheses ({ inputString : String, errorIndex : Basics.Int })
    | SpansOverSeveralLines String
    | InvalidSymbol ({ inputString : String, errorIndex : Basics.Int, symbol : Char })
    | UnexpectedError ({ inputString : String, errorIndex : Basics.Int, debugInfo : String })

The different descriptions of in which way a string is not a valid algorithm string. Note that all these errors assume that the string is user input, and so makes some opinionated decisions about when and how to error based on that.

If you want to programatically create arbitrary algorithms you should use constructors such as fromTurnList

For an example of how to handle and display these errors to a user on user input, make sure to check out this full user input example

debugFromStringError : FromStringError -> String

Describes the error as a string, and is not recommended to be displayed to end-users, but rather to be used in error mesages logged for developer's eyes etc.

case fromString algorithmString of
    Ok _ ->
        doSomethingOnSuccess

    Err error ->
        logError (debugFromStringError error)

Helpers

inverse : Algorithm -> Algorithm

Get the inverse of the algorithm.

By definition this means that if one was to apply the algorithm followed by its inverse one would arrive at the same state one started at.

Result.map inverse <| fromString "UB'"
--> fromString "BU'"

append : Algorithm -> Algorithm -> Algorithm

We append the two arguments, so a ++ b

Result.map2 append (fromString "U") (fromString "B'")
--> fromString "UB'"

reverseAppend : Algorithm -> Algorithm -> Algorithm

We append the two algorithms in reverse, so b ++ a

Result.map2 reverseAppend (fromString "U") (fromString "B'")
--> fromString "B'U"

Enumerations

allCubeAngles : List.Nonempty.Nonempty Algorithm

24 algorithms to rotate a cube to any of the 24 unique angles it can be positioned in

import List.Nonempty

List.Nonempty.length allCubeAngles --> 24

allTurns : List.Nonempty.Nonempty Turn

All possible combinations of turnables, lengths and directions

Can for example be used if you ever need to list all possible turns to a user, or if you need to select a turn at random

import List.Nonempty

List.Nonempty.sample allTurns

List.Nonempty.length allTurns
--> List.Nonempty.length allTurnables
-->     * List.Nonempty.length allTurnLengths
-->     * List.Nonempty.length allTurnDirections

allTurnables : List.Nonempty.Nonempty Turnable

All possible turnables

Can for example be used if you ever need to list all possible turnables to a user, or if you need to select a turnable at random

import List.Nonempty

List.Nonempty.sample allTurnables

List.Nonempty.length allTurnables --> 18

allTurnLengths : List.Nonempty.Nonempty TurnLength

All possible turn lengths

Can for example be used if you ever need to list all possible turn lengths to a user, or if you need to select a turn length at random

import List.Nonempty

List.Nonempty.sample allTurnLengths

List.Nonempty.length allTurnLengths --> 3

allTurnDirections : List.Nonempty.Nonempty TurnDirection

All possible turn directions

Can for example be used if you ever need to list all possible turn directions to a user, or if you need to select a turn direction at random

import List.Nonempty

List.Nonempty.sample allTurnDirections

List.Nonempty.length allTurnDirections --> 2

Advanced

toTurnList : Algorithm -> List Turn

You should not need this for by far most use cases.

It will let you introspect the algorithm which can be useful in some advanced usecases, but in general you should avoid this and just pass around the algorithm type