slidy/lib.rs
1//! A crate containing various utilities for working with sliding puzzles. The only sliding puzzles
2//! supported are arbitrary-sized versions of the
3//! [15 puzzle](https://en.wikipedia.org/wiki/15_puzzle), other puzzles such as higher dimensional
4//! variants of the 15 puzzle, bandaged sliding puzzles, klotski, sokoban, etc. are not supported.
5//!
6//! # Examples
7//!
8//! ## Apply a sequence of moves to a puzzle
9//!
10//! ```
11//! use std::str::FromStr as _;
12//!
13//! use slidy::{
14//! algorithm::algorithm::Algorithm,
15//! puzzle::{puzzle::Puzzle, sliding_puzzle::SlidingPuzzle},
16//! };
17//!
18//! fn main() -> Result<(), Box<dyn std::error::Error>> {
19//! let mut puzzle = Puzzle::from_str("8 2 0/4 6 1/3 7 5")?;
20//! let algorithm = Algorithm::from_str("R2U2LDLDRURDLULDRULURDLU")?;
21//! puzzle.apply_alg(&algorithm);
22//!
23//! assert!(puzzle.is_solved());
24//!
25//! Ok(())
26//! }
27//! ```
28//!
29//! ## Generate random state scrambles
30//!
31//! ```
32//! use slidy::puzzle::{
33//! puzzle::Puzzle,
34//! scrambler::{RandomState, Scrambler},
35//! size::Size,
36//! };
37//!
38//! # #[cfg(feature = "thread_rng")]
39//! fn main() -> Result<(), Box<dyn std::error::Error>> {
40//! let mut p = Puzzle::new(Size::new(5, 5)?);
41//!
42//! for _ in 0..10 {
43//! // Requires the `thread_rng` feature to be enabled.
44//! // Otherwise, `scramble_with_rng` can be used with a custom `Rng`.
45//! RandomState.scramble(&mut p);
46//! println!("{p}");
47//! }
48//!
49//! Ok(())
50//! }
51//!
52//! # #[cfg(not(feature = "thread_rng"))]
53//! # fn main() {}
54//! ```
55//!
56//! ## Find an optimal solution
57//!
58//! ```
59//! use std::str::FromStr as _;
60//!
61//! use slidy::{
62//! puzzle::{puzzle::Puzzle, sliding_puzzle::SlidingPuzzle},
63//! solver::solver::Solver,
64//! };
65//!
66//! fn main() -> Result<(), Box<dyn std::error::Error>> {
67//! let mut puzzle = Puzzle::from_str("0 10 6 4/1 5 14 15/13 11 8 7/3 2 9 12")?;
68//!
69//! let mut solver = Solver::default();
70//! let solution = solver.solve(&puzzle)?;
71//!
72//! println!("Solution: {} ({} moves)", solution, solution.len_stm::<u64>());
73//!
74//! puzzle.apply_alg(&solution);
75//! assert!(puzzle.is_solved());
76//!
77//! Ok(())
78//! }
79//! ```
80//!
81//! ## Create an SVG image of a puzzle
82//!
83//! ```
84//! use palette::rgb::Rgba;
85//! use slidy::puzzle::{
86//! color_scheme::{ColorScheme, Scheme},
87//! coloring::{Monochrome, Rainbow},
88//! label::label::{SplitFringe, Trivial},
89//! puzzle::Puzzle,
90//! render::{Borders, RendererBuilder, Text},
91//! };
92//!
93//! fn main() -> Result<(), Box<dyn std::error::Error>> {
94//! let scheme = Box::new(Scheme::new(
95//! Trivial,
96//! Monochrome::new(Rgba::new(0.15, 0.15, 0.15, 1.0)),
97//! )) as Box<dyn ColorScheme>;
98//!
99//! let border_scheme =
100//! Box::new(Scheme::new(SplitFringe, Rainbow::default())) as Box<dyn ColorScheme>;
101//!
102//! let text_scheme = Box::new(Scheme::new(
103//! Trivial,
104//! Monochrome::new(Rgba::new(1.0, 1.0, 1.0, 1.0)),
105//! )) as Box<dyn ColorScheme>;
106//!
107//! let renderer = RendererBuilder::with_dyn_scheme(scheme)
108//! .borders(Borders::with_scheme(border_scheme).thickness(5.0))
109//! .text(Text::with_scheme(text_scheme).font_size(40.0))
110//! .background_color(Rgba::new(0.05, 0.05, 0.05, 1.0))
111//! .tile_size(75.0)
112//! .tile_gap(5.0)
113//! .tile_rounding(10.0)
114//! .padding(10.0)
115//! .build();
116//!
117//! let puzzle = Puzzle::default();
118//!
119//! let svg = renderer.render(&puzzle)?;
120//! svg::save("out.svg", &svg)?;
121//!
122//! Ok(())
123//! }
124//! ```
125//!
126//! # Safe, panicking, and unsafe functions
127//!
128//! Some functions defined in this crate have variants with names of the form `foo`, `try_foo`, and
129//! `foo_unchecked`, with the following behavior:
130//!
131//! - The functions `foo` may panic, return invalid results, or create invalid states when given
132//! invalid arguments.
133//! - The functions `try_foo` should return `None` when given invalid arguments, and should never
134//! panic. In most cases, the default implementations of these functions call `foo` with the
135//! appropriate checks included.
136//! - The functions `foo_unchecked` should be considered `unsafe` and are intended for situations
137//! where performance is important. The default implementations of these functions do not contain
138//! any unsafe code, and most of them are just a call to `foo` or a re-implementation of `foo`
139//! using other unchecked functions.
140
141#![cfg_attr(feature = "nightly", feature(test))]
142#![deny(missing_docs)]
143#![deny(rustdoc::broken_intra_doc_links)]
144
145pub mod algorithm;
146pub mod puzzle;
147pub mod solver;