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