emilgoldsmith/elm-speedcubing/7.2.0

NOTE: the latest version is 14.1.0

Algorithm

Definition

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

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

= 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

= OneQuarter
| Halfway
| ThreeQuarters

Describes how much to turn a turnable in a turn

= Clockwise
| CounterClockwise

Describes which direction to turn a turnable in a turn

Constructors

Create an Algorithm from a list of turns

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

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"

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])
= EmptyAlgorithm
| InvalidTurnable
{ inputString : String
, errorIndex : Int
, invalidTurnable : String
}
| InvalidTurnLength
{ inputString : String
, errorIndex : Int
, invalidLength : String
}
| RepeatedTurnable
{ inputString : String
, errorIndex : Int
}
| WideMoveStylesMixed
{ inputString : String
, errorIndex : Int
, invalidWideMove : String
}
| TurnWouldWorkWithoutInterruption
{ inputString : String
, interruptionStart : Int
, interruptionEnd : Int
}
| ApostropheWrongSideOfLength
{ inputString : String
, errorIndex : Int
}
| UnclosedParenthesis
{ inputString : String
, openParenthesisIndex : Int
}
| UnmatchedClosingParenthesis
{ inputString : String
, errorIndex : Int
}
| EmptyParentheses
{ inputString : String
, errorIndex : Int
}
| NestedParentheses
{ inputString : String
, errorIndex : Int
}
| SpansOverSeveralLines String
| InvalidSymbol
{ inputString : String
, errorIndex : Int
, symbol : Char
}
| UnexpectedError
{ inputString : String
, errorIndex : 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

  • EmptyAlgorithm: There were no turns in the string and this does not make sense for user input.

    If you need behaviour like this just allow the user to not input any algorithm at all

  • InvalidTurnable: A turnable such as U or x was expected but not found

  • InvalidTurnLength: It seems like a turn length such as 2 or 3 was attempted to be specified but wasn't valid

  • RepeatedTurnable: The same turnable was repeated twice in a row which would never make sense in an algorithm. The correct way to describe this is by combining the two into one such as UU becoming U2, or UU' just not being there at all

  • TurnWouldWorkWithoutInterruption: It looks like an otherwise correct turn was specified but a parenthesis, some whitespace or something similar came in the way making it invalid

  • ApostropheWrongSideOfLength: It looks like the apostrophe was put on the wrong side of the length, such as U'2 instead of U2' in an otherwise correct turn, just swapping these would make it valid

  • UnclosedParenthesis: There is an opening parenthesis that was never closed

  • UnmatchedClosingParenthesis: There is a closing parenthesis that doesn't have an opening match

  • EmptyParentheses: There is a set of parentheses that aren't enclosing any turns, which does not make sense as parentheses are used to group turns to help memorization and execution

  • NestedParentheses: A second set of parentheses were started within a set of parentheses. Nested triggers or other nested grouping hasn't seemed to be a relevant need anywhere in the community so it is not allowed until need has been proven

  • SpansOverSeveralLines: An algorithm is not allowed to span over several different lines, the string input should just be a single line

  • InvalidSymbol: A symbol was encountered that does not make any sense in an algorithm anywhere.

  • UnexpectedError: The parsing code behaved in an unexpected way. This should never happen unless we have a bug in our code. If you get this error in production code just either tell the user that the algorithm wasn't valid without any further explanation or tell them something unexpected happen. The debugInfo key is not helpful to show to users, but only for figuring out how to fix the Elm Package to avoid this happening in the future, and logging a Github Issue with the debugInfo would be very appreciated

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

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'"

We append the two arguments, so a ++ b

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

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

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

Enumerations

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 : 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

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

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

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

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