Pentultimate Solver is a puzzle application to interact with a virtual Pentultimate, a twisty puzzle invented by Jason Smith.
This app is written in Rust, using the bevy
game engine, with the egui
crate used for UI elements.
There's a demo video covering most of the information in this readme on YouTube:
Most of the data for the transformations is organized in a multidimensional array behind the scenes. To help keep track of what the various array indices affect, I adopted use of the biological taxonomy terms.
An "organism index" is a discreet rotation of a piece (pentagonal or triangular) relative to the default rotation for its current position. A "transformation organism" is a single, specific transformation.
A "species index" is a discreet position of a piece (pentagonal or triangular) relative to the origin. A "transformation species" is a group of five transformation organisms, grouped together based on shared relation with the pentagon specified by the corresponding species index.
Together, an organism index and a species index constitute a "half address", which can be used to specify the current position and rotation (collectively known as an "orientation") of any of the pieces of the puzzle (pentagonal or triangular). The 60 possible orientations for the pentagonal pieces can be thought of a discreet quaternion of sorts: they can either specify an orientation, or 3D rotation to apply to an orientation, transforming it into another orientation.
The orientation specified by half address (0, 0)
is known as the "origin" orientation. This is used to put the puzzle state into a "standardized form" (when the pentagon with index 0 is at the origin orientation). Without having a "standardized form" for a puzzle state, comparing puzzle states for equivalence would be extremely inefficient.
A "genus" (plural "genera") is a broader category of transformation, comprising of twelve transformation species (one for each pentagonal species index). Listed above are the transformation genera. Reorientation
moves the camera position to a specific orientation; Simple
rotates a hemisphere of the puzzle either 0τ, 0.2τ, 0.4τ, 0.6τ, or 1τ radians (where τ radians is a full rotation, or 360 degrees); and all the other genera are known as "complex" genera, which comprise of a specific sequence of Simple
transformations.
Together, a genus and a half address constitute a "full address", which specify a specific transformation that can be applied to the puzzle. Aside from any identity transformations ((Reorientation, 0, 0)
and (Simple, *, 0)
all don't affect the puzzle state nor the camera orientation), a full address uniquely identifies a transformation. There are some complex transformations that produce the same net effect change to the puzzle state, but through a different sequence of Simple
transformations to get there.
It was a half lie when I said the list in the image is the transformation genera. Truly, it's the list of transformation families, or groupings of genera, where each family has a genus of the same name. The non-complex transformation families Reorientation
and Simple
both consist of a single genus.
A family consists of:
- The genus of that name
- The "mirror" of that genus
- The "inverse" of that genus (as long as it doesn't overlap with either of the previous two genera)
- The inverse's mirror (as long as it doesn't overlap with either of the previous three genera)
Here, the "mirror" is constructed by mirroring horizontally each comprising Simple
transformation, and the "inverse" is constructed by performing the inverse of each comprising Simple
transformations in the opposite order (the undo functionality just does the inverse of the performed transformation).
The following is the default keypress input mapping, which can be configured in the preferences.
Action | Mapping | Description |
---|---|---|
Pentagon1 |
Numpad0 |
Perform the transformation specified by the toggles at the 1st of 6 pentagons (see Default Species |
Pentagon2 |
Numpad1 |
Perform the transformation specified by the toggles at the 2nd of 6 pentagons (see Default Species |
Pentagon3 |
Numpad4 |
Perform the transformation specified by the toggles at the 3rd of 6 pentagons (see Default Species |
Pentagon4 |
Numpad5 |
Perform the transformation specified by the toggles at the 4th of 6 pentagons (see Default Species |
Pentagon5 |
Numpad6 |
Perform the transformation specified by the toggles at the 5th of 6 pentagons (see Default Species |
Pentagon6 |
Numpad3 |
Perform the transformation specified by the toggles at the 6th of 6 pentagons (see Default Species |
RecenterCamera |
Space |
Reorient the camera to be centered on the closest pentagon, with a side of this pentagon facing directly up |
Undo |
Ctrl+Z |
Undo the most recent transformation |
Redo |
Ctrl+Y |
Redo the most recent transformation |
CycleFamilyUp |
Up |
Cycle the current transformation family up once |
CycleFamilyDown |
Down |
Cycle the current transformation family down once |
CycleGenusUpWithinFamily |
Right |
Cycle the current transformation genus within the current family "up" once |
CycleGenusDownWithinFamily |
Left |
Cycle the current transformation genus within the current family "down" once |
EnableRecentering |
C |
Enable camera recentering as part of transformations |
EnableModifiers |
F |
Enable RotateTwice and CounterClockwise to affect the specified transformation |
RotateTwice |
D |
On Simple transformations, rotate the pentagon twice; on other transformations, use organism index 2 without CounterClockwise /3 with CounterClockwise |
CounterClockwise |
S |
On Simple transformations, rotate the pentagon counter clockwise; on other transformations, use organism index 4 without RotateTwice /3 with RotateTwice |
AlternateHemisphere |
A |
Perform the transformation centered on the pentagon/species opposite the pentagon specified by keypress |
Hold down the scroll wheel to "pan" the puzzle (really this rotates about the axis perpendicular to the mouse travel direction).
Rolling the scroll wheel up rotates the puzzle clockwise (relative to the camera), and vice versa with rolling the scroll wheel down.
The mouse can also be used to interact with the file menu, the preferences, and any active debug tools.
For Simple
transformations, each transformation affects a hemisphere of the puzzle, centered at one of the twelve pentagons. Six of these are directly affected by the Pentagon*
keypress actions, and the other six are affected when AlternateHemisphere
is enabled. Under the default input configuration and default species list for these six pentagons, the image above describes which numpad keys affect which pentagons.
The File menu is somewhat of a misnomer, since only three of the six items in it are related to a "file," but it gets the job done.
file_menu
: Preferences associated with the File menurandomization_params
: Preferences associated with the Randomize buttonrandom_transformation_genera
: Which transformation genera to be used in the shuffle; the default is[ "Simple" ]
random_transformation_count
: How many transformations to apply in the shuffle; the default is30
randomization_type
: EitherFromCurrent
(apply the transformations to the current puzzle state and transformation stack),FromSolved
(apply the transformation to the solved puzzle state, building up a fresh transformation stack), orFromSolvedNoStack
(same asFromSolved
, but with no stack recorded for the applied transformations); the default isFromSolved
input
: Preferences associated with keyboard inputdefault_species
: Which species indices are affected by the pentagon keys by default; default is[0, 5, 10, 1, 8, 4]
key_presses
: See Key Presses
light_and_camera
: Preferences associated with the light and cameralight_pos
: The position of the light within the scene; the default is[ 0.0, 0.0, 10.0 ]
camera_pos
: The position of the camera within the scene; the default is[ 0.0, 0.0, 10.0 ]
persp_proj
: A struct mirroringbevy::render::camera::PerspectiveProjection
fov
: The field of view of the camera; the default is0.5
aspect_ratio
: The aspect ratio of the camera; the default is1.0
near
: The near clipping plane; the default is1.0
far
: The far clipping plane; the default is1000.0
light_illuminance
: The light's strength; the default is4.0
puzzle
: Preferences associated with the puzzle appearancecolor
: Preferences associated with the colors usedcolors_with_mat
: Preferences about colors with associated materialspolyhedron_to_colors
: A map of polyhedron to colors for each face of that polyhedron
design
: See Puzzle Designs; the default isCustomSuperDodecahedron
speed
: Preferences associated with speedcamera
: Preferences associated with the camera speedpan_speed
: How fast the camera rotates with the scroll wheel held down (a number in the range of 1 and 100); the default is50
roll_speed
: How fast the camera rotates when the scroll wheel rolls (a number in the range of 1 and 100); the default is50
animation
: Preferences associated with animation speedrotation_millis
: How many milliseconds a fifth of a rotation takes; the default is250
uniform_transformation_duration
: Whether or not all transformations takerotation_millis
milliseconds vsrotation_millis
for each fifth of a rotation; the default isfalse
animate_undo_and_redo
: Whether or not the undo and redo operations are animated vs immediate; the default istrue
solver
: Preferences associated with the solver functionalitycycle_duration_millis
: How many milliseconds are devoted to working on the solution per frame/update cycle; the default is30.0
tools
: Which tool debug modes are active; the default is[]
(no modes)ui
: Preferences associated with the visual application UIwindow_mode
: An enum mirroringbevy::window::WindowMode
; the default isWindowed
This randomizes the puzzle. This is parametrized by file_menu.randomization_params
in the preferences
This starts the process of generating a sequence of transformations to apply to the puzzle to put it into a solved state. Before the solution is ready, further input is not accepted, aside from adjustment of the preferences. Once the solution is ready, the transformations are sequentially applied to the puzzle, appearing in the stack.
Using the serde
crate for serialization and the rfd
crate for the file dialog, this saves the current puzzle state and transformation stack to a file in the saves
directory. The following are the supported file types:
.bc
: Serialize using thebincode
crate.json
: Serialize using theserde_json
crate.json5
: Serialize using thejson5
crate.ron
: Serialize using theron
crateThe.toml
: Serialize using thetoml
cratetoml
crate is too limited in what types it can serialize and deserialize, so it's not currently supported
This loads up a previous puzzle state and stack saved by the "Save Puzzle State" button.
This quits the application, which can be useful if the application is currently in fullscreen.
All the puzzle meshes are generated at runtime, programmatically, not imported from 3D object files.
This is the commercially available design from manufacturer Mf8, hence the "original" prefix. The term "super" indicates how the pentagonal pieces have extra detail on them such that there's only one correct orientation for that piece, as opposed to 5 (given the 5-fold rotational symmetry).
This was the first design I programmed for the puzzle. It is a custom design that I came up with because I figured it'd be easier to program than the original design.
Instead of having flat faces for the pentagonal pieces flush with pyramidal caps on the triangular pieces, as is the case with the dodecahedron puzzle designs, we can instead base the geometry around the dodecahedron's dual polyhedron, the icosahedron, yielding flat faces for the triangular pieces flush with pyramidal caps on the pentagonal pieces. To distinguish when the triangle are in the correct orientations, extra color was added to indicate the proper orientation.
By using the rhombic triacontahedron as the base polyhedron for the design, only one color is needed per face, which yields a rather clean look.
By taking the previous design and "sanding down" any portion of the puzzle further out from the puzzle's center than where the edge between two rhombi meets one of the cuts through the puzzle's center, this design is created.