SkillAgentSearch skills...

Cubecode

Store data on Rubik's cubes

Install / Use

/learn @EvanZhouDev/Cubecode
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<picture> <source media="(prefers-color-scheme: dark)" srcset="./assets/logo-dark.svg"> <img src="./assets/logo-light.svg" alt="CubeCode Logo" width="300"> </picture>

Store data on Rubik's cubes.

CubeCode is a proof-of-concept method of storing data in Rubik's cubes, enabled by a one-to-one mapping of every single possible Rubik's cube state to an integer between $0$ and $43,252,003,274,489,856,000 - 1$.

This enables interesting functionality like being able to share a state of a cube with just a (relatively) short number, being able to store a secret message with ASCII on a single 3x3, and even being able to store larger pieces of data on multiple 3x3s.

Watch How it was Made

Do you think you know Rubik's cubes well? Up for a challenge? Decode the Rubik's cube shown in the video at the start and put it into the Secret Page to unlock a secret message from me!

A basic overview of CubeCode and some interesting usecases are documented in the YouTube video. Check it out!

Try it Out

You don't need to know how to solve a Rubik's cube to use the CubeCode demo! Interactive tutorials and a built-in solver will help you use it without any prior knowledge.

You can try CubeCode's functionality right now with the demo at cubecode.vercel.app, which allows you to store secret messages in Rubik's cubes with CubeCode.

This demo has the following features:

  • Encode: Get a custom set of moves which encodes a custom secret message into your Rubik's cube.
  • Decode: Given a Rubik's cube state, figure out the corresponding CubeCode number and it's secret message.
  • Solve: An animated Rubik's cube solver to help you solve your cube after you try out CubeCode.

This will help introduce you to some of the nice features of CubeCode.

NPM Package

The main features of CubeCode, going between a cubestate and an integer between $0$ and $43,252,003,274,489,856,000 - 1$, is available as a NPM package. Install it on NPM, or your favorite package manager:

npm i cubecode

It's built with TypeScript and is incredibly easy to use. Read the documentation below.

API Quickstart

import { decodeCube, encodeCube } from "cubecode";

// Returns a single number representing this cube. (JSON format similar to cubejs, https://github.com/ldez/cubejs, without the `center` property).
encodeCube({
	cp: [0, 1, 2, 3, 4, 5, 6, 7],
	co: [0, 0, 0, 0, 0, 0, 0, 0],
	ep: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
	eo: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
});

// Returns a JSON with cp, co, ep, and eo similar to above.
decodeCube(1234567n);

API Reference

There are only 2 main functions in CubeCode (as well as an extra function meant for caching). If you want to learn more about the detailed math, check out the section below.

As a warning, CubeCode is always must be provided and will always be returned as a BigInt. See the corresponding section below for more information.

Cube Format

With CubeCode, we will always be representing the cube in a simple format (referred to hereon just as "Cube Format") consisting of:

  • cp (Corner Permutation): An array of only numbers from 0 to 7 (inclusive), in some arbitrary order
  • ep (Edge Permutation): An array of only numbers from 0 to 11 (inclusive), in some arbitrary order
  • co (Corner Orientation): An array of 8 numbers, each from 0 to 2 (inclusive)
  • eo (Edge Orientation): An array of 12 numbers, each from 0 to 1 (inclusive)

This data is very arbitrarily defined. CubeCode itself does not say what number each piece or orientation is assigned to. However, we recommend the convention from Cube.js.

In essence, for permutation each number is assigned a specific piece (either corner or edge) and their orders represents the order of the corners/edges around the cube.

For orientation, each of the numbers represents how each piece is "rotated" or oriented at a specific slot in the cube. Again, the orientation is defined arbitrarily, but you can use the Cube.js standard.

Here is an example of a solved cube:

{
	cp: [0, 1, 2, 3, 4, 5, 6, 7],
	co: [0, 0, 0, 0, 0, 0, 0, 0],
	ep: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
	eo: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
}

encodeCube

This function takes a Rubik's cube in the standard Cube Format, and returns a single number representing that state's CubeCode. Here's what it looks like in action:

encodeCube({
	cp: [0, 1, 2, 3, 4, 5, 6, 7],
	co: [0, 0, 0, 0, 0, 0, 0, 0],
	ep: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
	eo: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
});

encodeCube does numerous checks to ensure the input is valid before giving you the CubeCode, including ensuring the Cube Format is done correctly, and that the Parity of the Orientations and Permutations are valid (that is, no "corner twists" or illegal states in general).

Then, it simply does a calculation which will yield you a single BigInt integer between $0$ and $43,252,003,274,489,856,000 - 1$.

Note on Semantics: When we say encodeCube, we mean literally encoding a Rubik's cube state into a number. This is different from "encoding a message" as is seen in the Demo Website, in which case it refers to encoding a message into a Rubik's cube. Thus, the encodeCube function is actually used in the Decode page of the website (since it "decodes the message from the Rubik's cube"). A similar idea can be applied to decodeCube

encodeCube also supports explicitly passing in a cache with its second parameter. Learn more in the dedicated section below.

decodeCube

This function takes a BigInt (learn more about that below) as the CubeCode and returns the corresponding cubestate, in the Cube Format. Here's what it looks like in action:

decodeCube(1234567n);

Remembers that CubeCodes are always between $0$ and $43,252,003,274,489,856,000 - 1$, inclusive. decodeCube will reject anything outside these bounds.

Then, decodeCube will take that CubeCode and turn it into a valid Cube Format to return to you.

decodeCube also supports explicitly passing in a cache with its second parameter. Learn more in the dedicated section below.

generateCornerCache and Caching

Under the hood, both decodeCube and encodeCube require a special cache to speed up the conversion of the CubeCode to the Cube Format and vice versa (You can learn more about why this is in the CubeCode's Math section below).

However, since this cache is not very big, by default, the first time either encode or decode are called, the cache will be newly generated and stored in-memory (so the next time the same process calls the method, it'll just use the existing cache). But if you need to optimize for performance and fractions of seconds matter (sometimes that happens!), CubeCode provides a easy way to "actually cache" the data. Here's what it looks like:

let cornerCache = generateCornerCache();

decodeCube(1234567n, cornerCache);
encodeCube(
	{
		cp: [0, 1, 2, 3, 4, 5, 6, 7],
		co: [0, 0, 0, 0, 0, 0, 0, 0],
		ep: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
		eo: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
	},
	cornerCache
);

The generateCornerCache function will create the necessary cache. That cache can then be passed as a optional second parameter to both decode and encode. Of course, it wouldn't make much sense to regenerate the cache every single time. Instead, it is recommended to store the cache in the proper location depending on your environment (i.e. a JSON file, or in localStorage) and use the cache from that instead of regenerating.

BigInt Return Format

Due to the insanely large number of states on a Rubik's cube, the CubeCode must be stored in a BigInt. For the sake of consistency and the Principle of Least Astonishment, this BitInt rule is enforced in both input and output. This means that:

  • decodeCube must take a BigInt
  • encodeCube returns a BitInt

Attempts to use a normal number may lead to literal runtime/compile time errors, or just incorrect values.

CubeCode's Math

This section serves as a deep dive into the math behind CubeCode and how it works. Note that this reading is not necessary if you are simply using CubeCode, but you may find it interesting.

CubeCode is a proof-of-concept method that is able to encode a Rubik's cube into a single number in a consecutive integer sequence, and turn that number back into a Rubik's cube. Here are the requirements:

  • The possible CubeCodes must be exactly between $0$ and $43,252,003,274,489,856,000 - 1$, inclusive. That is, the CubeCodes of all states are all consecutive in this sequence.
  • All CubeCodes must be unique, and CubeCode to cubestate must be a 1-to-1 bijective mapping
  • No illegal cubestates are in the sequence
  • This computation should be relatively cheap and not incredibly intensive/slow to run

Prerequisites

Keep those key ideas in mind. But before we start, let's go over some prerequisites.

  • General Rubik's Cube Lingo: You don't have to be a pro cuber, but ensure you understand phrases like:
    • Corner Permutation (CP): The way the corners on the cube are "arranged" around the cube
    • Edge Permutation (EP): The way the edges on the cube are "arranged" around the cube
    • Corner Orientation (CO): The way the corners on the cube are "turned" or oriented

Related Skills

View on GitHub
GitHub Stars9
CategoryDevelopment
Updated3mo ago
Forks1

Languages

TypeScript

Security Score

87/100

Audited on Dec 18, 2025

No findings