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#![allow(clippy::module_inception)]
143#![deny(clippy::as_pointer_underscore)]
144#![deny(clippy::as_ptr_cast_mut)]
145#![deny(clippy::bool_to_int_with_if)]
146#![deny(clippy::borrow_as_ptr)]
147#![deny(clippy::branches_sharing_code)]
148#![deny(clippy::checked_conversions)]
149#![deny(clippy::clear_with_drain)]
150#![deny(clippy::cloned_instead_of_copied)]
151#![deny(clippy::collection_is_never_read)]
152#![deny(clippy::deref_by_slicing)]
153#![deny(clippy::derive_partial_eq_without_eq)]
154#![deny(clippy::doc_markdown)]
155#![deny(clippy::double_must_use)]
156#![deny(clippy::elidable_lifetime_names)]
157#![deny(clippy::empty_drop)]
158#![deny(clippy::empty_enum)]
159#![deny(clippy::empty_enum_variants_with_brackets)]
160#![deny(clippy::empty_structs_with_brackets)]
161#![deny(clippy::enum_glob_use)]
162#![deny(clippy::equatable_if_let)]
163#![deny(clippy::error_impl_error)]
164#![deny(clippy::explicit_deref_methods)]
165#![deny(clippy::explicit_into_iter_loop)]
166#![deny(clippy::explicit_iter_loop)]
167#![deny(clippy::expl_impl_clone_on_copy)]
168#![deny(clippy::fallible_impl_from)]
169#![deny(clippy::filter_map_next)]
170#![deny(clippy::flat_map_option)]
171#![deny(clippy::float_cmp)]
172#![deny(clippy::fn_params_excessive_bools)]
173#![deny(clippy::fn_to_numeric_cast_any)]
174#![deny(clippy::format_collect)]
175#![deny(clippy::format_push_string)]
176#![deny(clippy::from_iter_instead_of_collect)]
177#![deny(clippy::get_unwrap)]
178#![deny(clippy::if_not_else)]
179#![deny(clippy::if_then_some_else_none)]
180#![deny(clippy::ignored_unit_patterns)]
181#![deny(clippy::implicit_clone)]
182#![deny(clippy::implicit_hasher)]
183#![deny(clippy::imprecise_flops)]
184#![deny(clippy::inconsistent_struct_constructor)]
185#![deny(clippy::index_refutable_slice)]
186#![deny(clippy::inefficient_to_string)]
187#![deny(clippy::infinite_loop)]
188#![deny(clippy::inline_always)]
189#![deny(clippy::into_iter_without_iter)]
190#![deny(clippy::invalid_upcast_comparisons)]
191#![deny(clippy::items_after_statements)]
192#![deny(clippy::iter_filter_is_ok)]
193#![deny(clippy::iter_filter_is_some)]
194#![deny(clippy::iter_not_returning_iterator)]
195#![deny(clippy::iter_on_empty_collections)]
196#![deny(clippy::iter_on_single_items)]
197#![deny(clippy::iter_with_drain)]
198#![deny(clippy::iter_without_into_iter)]
199#![deny(clippy::large_stack_arrays)]
200#![deny(clippy::large_stack_frames)]
201#![deny(clippy::large_types_passed_by_value)]
202#![deny(clippy::literal_string_with_formatting_args)]
203#![deny(clippy::lossy_float_literal)]
204#![deny(clippy::manual_assert)]
205#![deny(clippy::manual_instant_elapsed)]
206#![deny(clippy::manual_is_power_of_two)]
207#![deny(clippy::manual_is_variant_and)]
208#![deny(clippy::manual_let_else)]
209#![deny(clippy::manual_midpoint)]
210#![deny(clippy::manual_string_new)]
211#![deny(clippy::map_err_ignore)]
212#![deny(clippy::map_unwrap_or)]
213#![deny(clippy::map_with_unused_argument_over_ranges)]
214#![deny(clippy::match_bool)]
215#![deny(clippy::match_wildcard_for_single_variants)]
216#![deny(clippy::mismatching_type_param_order)]
217#![deny(clippy::missing_asserts_for_indexing)]
218#![deny(clippy::mod_module_files)]
219#![deny(clippy::multiple_inherent_impl)]
220#![deny(clippy::mutex_atomic)]
221#![deny(clippy::mutex_integer)]
222#![deny(clippy::mut_mut)]
223#![deny(clippy::needless_bitwise_bool)]
224#![deny(clippy::needless_collect)]
225#![deny(clippy::needless_continue)]
226#![deny(clippy::needless_for_each)]
227#![deny(clippy::needless_pass_by_ref_mut)]
228#![deny(clippy::needless_pass_by_value)]
229#![deny(clippy::needless_raw_string_hashes)]
230#![deny(clippy::needless_raw_strings)]
231#![deny(clippy::negative_feature_names)]
232#![deny(clippy::no_effect_underscore_binding)]
233#![deny(clippy::non_send_fields_in_send_ty)]
234#![deny(clippy::nonstandard_macro_braces)]
235#![deny(clippy::non_std_lazy_statics)]
236#![deny(clippy::non_zero_suggestions)]
237#![deny(clippy::option_as_ref_cloned)]
238#![deny(clippy::option_if_let_else)]
239#![deny(clippy::option_option)]
240#![deny(clippy::or_fun_call)]
241#![deny(clippy::partialeq_to_none)]
242#![deny(clippy::path_buf_push_overwrite)]
243#![deny(clippy::ptr_cast_constness)]
244#![deny(clippy::pub_underscore_fields)]
245#![deny(clippy::rc_buffer)]
246#![deny(clippy::rc_mutex)]
247#![deny(clippy::read_zero_byte_vec)]
248#![deny(clippy::redundant_clone)]
249#![deny(clippy::redundant_else)]
250#![deny(clippy::redundant_feature_names)]
251#![deny(clippy::redundant_pub_crate)]
252#![deny(clippy::redundant_type_annotations)]
253#![deny(clippy::ref_as_ptr)]
254#![deny(clippy::ref_binding_to_reference)]
255#![deny(clippy::ref_option)]
256#![deny(clippy::ref_option_ref)]
257#![deny(clippy::renamed_function_params)]
258#![deny(clippy::rest_pat_in_fully_bound_structs)]
259#![deny(clippy::return_and_then)]
260#![deny(clippy::return_self_not_must_use)]
261#![deny(clippy::same_functions_in_if_condition)]
262#![deny(clippy::same_name_method)]
263#![deny(clippy::semicolon_if_nothing_returned)]
264#![deny(clippy::semicolon_inside_block)]
265#![deny(clippy::set_contains_or_insert)]
266#![deny(clippy::significant_drop_in_scrutinee)]
267#![deny(clippy::significant_drop_tightening)]
268#![deny(clippy::single_option_map)]
269#![deny(clippy::stable_sort_primitive)]
270#![deny(clippy::string_lit_as_bytes)]
271#![deny(clippy::string_lit_chars_any)]
272#![deny(clippy::string_to_string)]
273#![deny(clippy::str_split_at_newline)]
274#![deny(clippy::suspicious_operation_groupings)]
275#![deny(clippy::tests_outside_test_module)]
276#![deny(clippy::trait_duplication_in_bounds)]
277#![deny(clippy::transmute_ptr_to_ptr)]
278#![deny(clippy::transmute_undefined_repr)]
279#![deny(clippy::trivially_copy_pass_by_ref)]
280#![deny(clippy::trivial_regex)]
281#![deny(clippy::tuple_array_conversions)]
282#![deny(clippy::type_repetition_in_bounds)]
283#![deny(clippy::undocumented_unsafe_blocks)]
284#![deny(clippy::uninlined_format_args)]
285#![deny(clippy::unnecessary_box_returns)]
286#![deny(clippy::unnecessary_debug_formatting)]
287#![deny(clippy::unnecessary_join)]
288#![deny(clippy::unnecessary_literal_bound)]
289#![deny(clippy::unnecessary_safety_comment)]
290#![deny(clippy::unnecessary_safety_doc)]
291#![deny(clippy::unnecessary_self_imports)]
292#![deny(clippy::unnecessary_semicolon)]
293#![deny(clippy::unnecessary_struct_initialization)]
294#![deny(clippy::unnecessary_wraps)]
295#![deny(clippy::unneeded_field_pattern)]
296#![deny(clippy::unnested_or_patterns)]
297#![deny(clippy::unsafe_derive_deserialize)]
298#![deny(clippy::unused_async)]
299#![deny(clippy::unused_peekable)]
300#![deny(clippy::unused_result_ok)]
301#![deny(clippy::unused_rounding)]
302#![deny(clippy::unused_self)]
303#![deny(clippy::unused_trait_names)]
304#![deny(clippy::use_debug)]
305#![deny(clippy::used_underscore_binding)]
306#![deny(clippy::used_underscore_items)]
307#![deny(clippy::useless_let_if_seq)]
308#![deny(clippy::use_self)]
309#![deny(clippy::verbose_file_reads)]
310#![deny(clippy::while_float)]
311#![deny(clippy::wildcard_dependencies)]
312#![deny(clippy::wildcard_enum_match_arm)]
313#![deny(clippy::wildcard_imports)]
314#![deny(clippy::zero_sized_map_values)]
315#![warn(clippy::must_use_candidate)]
316#![deny(missing_docs)]
317#![deny(rustdoc::broken_intra_doc_links)]
318
319pub mod algorithm;
320pub mod puzzle;
321pub mod solver;