Skip to content

scottbedard/twister

Repository files navigation

twister

Build status Codecov NPM Bundle size License

Twister is a library for modeling the state of twisty puzzles. To get started, check out the interactive playground.

Installation

The recommended way to install is through NPM.

npm install @bedard/twister

Alternatively, you can use the CDN. When using the CDN, the library will be exposed globally as Twister.

<script src="https://unpkg.com/@bedard/twister"></script>

Basic usage

Below is the recommended way to instantiate Twister models. Using destructured imports allows for unused puzzles to be tree-shaken from your application.

import { Cube } from '@bedard/twister'

const puzzle = new Cube({ size: 3 })

Once a puzzle has been instantiated, the following methods are available...

apply

Set the puzzle to a given state. State objects can be created using the output method.

puzzle.apply(state)

clone

Create a new puzzle instance with the same state and options.

const puzzle = new Puzzle()

const clone = puzzle.clone()

execute

Updates puzzle state using a parsed turn object. In most situations, it's simpler to use the turn method and make use of turn notation.

const turn = puzzle.parse('R')

puzzle.execute(turn)

generateScramble

Generates a scramble of a default or specified number of moves, but does not execute it.

const scramble = puzzle.generateScramble()

notation

Generate the string representation of a parsed turn object. This can be thought of as the opposite of parse.

const turn = puzzle.parse('R')

puzzle.notation(turn) // 'R'

output

Returns a minified version of the puzzle's state. This method is useful for saving state as JSON, then restoring that state via the apply method.

const state = puzzle.output()

parse

Convert a single piece of puzzle notation to a turn object. This method is generally used to interact with the execute method, but is also useful for testing if notation is valid. To parse a turn in reverse, provide true as the second argument.

const turn = puzzle.parse('R')

parseAlgorithm

Convert a space-delimited string of turns into an array of turn objects. To parse an algorithm in reverse, provide true as the second argument.

const turns = puzzle.parseAlgorithm('R U R-')

reset

Return a puzzle to its solved state.

puzzle.reset()

scramble

Scrambles a puzzle to a default or specified number of moves. This is similar to generateScramble, the only difference being that this method executes the resulting scramble. This method is also available via the CLI, more info here.

puzzle.scramble()

stickers

Get stickers affected by a turn. If no turn notation is provided, all stickers will be returned.

const stickers = puzzle.stickers('R')

test

Test if the puzzle is solved, or matches a specific state.

// test if the puzzle is solved
const solved = puzzle.test()

// test for a specific state
const isSame = puzzle.test(otherPuzzle.output())

turn

Executes an algorithm. This method is also available via the CLI, more info here.

puzzle.turn('R U R-')

unturn

Execute the reverse of an algorithm. Note that unturns are executed from right to left.

const scramble = puzzle.turn('R U R-')

puzzle.unturn('R U R-') // 'R U- R-'

Advanced usage

All stickers are stored as { value } objects. This allows for additional information, such as rendering data, to be attached to the stickers. To do this, simply instantiate the puzzle, and modify the objects that are part of puzzle.state.

import { Cube } from '@bedard/twister'

const puzzle = new Cube({ size: 3 })

puzzle.state.u[0].foo = 'bar'

For applications with advanced scrambling needs, a custom random function can be provided on instantiation. Below is an example using Rando.js to generate cryptographically strong scrambles. The random option must be a function that returns a floating point number between 0 and 1. By default, Math.random is used.

import { Cube } from '@bedard/twister'
import { rando } from '@nastyox/rando.js';

const puzzle = new Cube({
  random: rando,
  size: 3,
});

While this library does it's best to generate strong scrambles, it should never be used in WCA events. Always use the official TNoodle library for WCA purposes.

CLI

The following utilities are available from the command line. Constructor options can be provided via --options. Note that when providing JSON arguments, we use JSON5 syntax for a smoother user experience.

parse

Parse a single piece of turn notation.

$ twister parse cube R

parseAlgorithm

Parse multiple pieces of turn notation.

$ twister parseAlgorithm cube "R U R-"

scramble

Scramble a puzzle.

# scramble a puzzle
$ twister scramble cube

# scramble a puzzle to a specific number of moves
$ twister scramble cube --turns=10

turn

Execute an algorithm. This will be performed on a solved puzzle unless an initial state is provided.

# apply turns to a solved puzzle
$ twister turn cube "R U R-"

# apply turns from an initial state
$ twister turn cube "R U R-" --state="{...}"

# apply turns and test for a particular state
$ twister turn cube "R U R-" --test="{...}"

Puzzles

Cube

This puzzle represents an N-layered face turning cube.

import { Cube } from '@bedard/twister';

const puzzle = new Cube({ size: 3 });

Cube state is represented as an array of sticker objects. Each face array starts from the top left sticker and read sequentially to the bottom right. To picture how these values map onto an actual cube, imagine unfolding a cube while looking at the F face. Notice that the B face has the same orientation as the L, F, and R faces.

Our notation is a superset of WCA notation. Any algorithm produced by a WCA scrambler is compatible with this library. There are however, a couple of extensions we've made to the WCA notation. The first of which is the optional use of a - to indicate counter-clockwise turns. The second is the ability to annotate "slice turns" with a single move. To do this, simply omit the wide segment of a turn. For example, a 3F in our notation system would be equal to 3Fw 2Fw' in WCA notation.

Dodecaminx

This puzzle represents an N-layered face turning dodecahedron.

import { Dodecaminx } from '@bedard/twister';

const puzzle = new Dodecaminx({ size: 3 });

State for a dodecaminx is stored as an array of corner matrices, middle arrays, and a center value. These arrays start from the primary corner of a face, and continue clockwise around that face. The corner matrices are similar to that of a cube face, starting with the corner furthest from the center and reading sequentially towards the center. Middle arrays start with the sticker furthest from the center. Note that for even-layered puzzles, the middle and center values are omitted.

Notation for this puzzle is similar to that of cubes. The main difference is that whole-puzzle rotations are denoted by lowercase letters. Here are a few examples to demonstrate various turns around the U face.

  • U = outer layer turned once clockwise
  • U2 = outer layer turned twice clockwise
  • U2- = outer layer turned twice counter-clockwise
  • 2U = second layer turned once clockwise
  • Uw = second and outer layer turned once clockwise
  • u = entire puzzle rotated once clockwise
  • u2 = entire puzzle rotated twice clockwise
  • u2- = entire puzzle rotated twice counter-clockwise

License

MIT

Copyright (c) 2020-present, Scott Bedard