diff --git a/src/autotile.rs b/src/autotile.rs index 14d6c24bb8d5a944d151008cd3d79f9c6bf22b9b..0ab76cc2fe0fefc95734925195eab9ddb3fd4caf 100644 --- a/src/autotile.rs +++ b/src/autotile.rs @@ -1,7 +1,7 @@ +use crate::output::TileOutput; +use crate::{TileLayout, TileMatcher}; use std::fmt::{Debug, Formatter}; use std::ops::Add; -use crate::{TileLayout, TileMatcher}; -use crate::output::TileOutput; /// Checks tile layouts against a matcher instance, and uses the output to produce a value #[derive(Clone, Default)] @@ -19,9 +19,17 @@ impl Debug for AutoTileRule { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if f.alternate() { // Perform grid style formatting for matcher value - write!(f, "AutoTileRule \n{:#?}output: {:?}, chance: {:.2}", self.matcher, self.output, self.chance) + write!( + f, + "AutoTileRule \n{:#?}output: {:?}, chance: {:.2}", + self.matcher, self.output, self.chance + ) } else { - write!(f, "AutoTileRule {{ matcher: {:?}, output: {:?}, chance: {:.2} }}", self.matcher, self.output, self.chance) + write!( + f, + "AutoTileRule {{ matcher: {:?}, output: {:?}, chance: {:.2} }}", + self.matcher, self.output, self.chance + ) } } } @@ -62,11 +70,7 @@ impl AutoTileRule { /// Create a rule that will produce one of the values contained in `output_value` /// when the target tile matches `input_value` and the selection chacne is rolled under the /// value of `chance` (0.0 to 1.0) - pub const fn single_any_chance( - input_value: i32, - output_value: Vec<i32>, - chance: f32, - ) -> Self { + pub const fn single_any_chance(input_value: i32, output_value: Vec<i32>, chance: f32) -> Self { AutoTileRule { matcher: TileMatcher::single_match(input_value), output: TileOutput::any(output_value), @@ -141,11 +145,7 @@ impl AutoTileRule { /// Will use the provided RNG to select from a list, if the output resolves to /// a random selection #[cfg(feature = "impl_fastrand")] - pub fn resolve_match_seeded( - &self, - input: &TileLayout, - seeded: &fastrand::Rng, - ) -> Option<i32> { + pub fn resolve_match_seeded(&self, input: &TileLayout, seeded: &fastrand::Rng) -> Option<i32> { self.get_match_seeded(input, seeded) .and_then(|out| out.resolve_with(seeded)) } @@ -263,13 +263,8 @@ impl AutoRuleSet { /// Will use the provided RNG to select from a list, if the output resolves to /// a random selection #[cfg(feature = "impl_fastrand")] - pub fn resolve_match_seeded( - &self, - input: &TileLayout, - seeded: &fastrand::Rng, - ) -> Option<i32> { + pub fn resolve_match_seeded(&self, input: &TileLayout, seeded: &fastrand::Rng) -> Option<i32> { self.get_match_seeded(input, seeded) .and_then(|out| out.resolve_with(seeded)) } } - diff --git a/src/layout.rs b/src/layout.rs index 78badce40ef00e9ecd3046ba6eafe426d8f37a02..48d0c2b31ab5295636d5239ea9102a3155c3c0ff 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,5 +1,5 @@ -use std::fmt::{Debug, Formatter}; use crate::utils::IntoTile; +use std::fmt::{Debug, Formatter}; /// The size of the grid that can be matched; equal to the length of one side of the square grid const RULE_MAGNITUDE: usize = 7; @@ -37,41 +37,38 @@ impl Debug for TileStatus { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Self::Ignore => write!(f, "Any Or No Tile"), - Self::Nothing => write!(f, "No Tile"), - Self::Anything => write!(f, "Any Tile"), - Self::Is(value) => write!(f, "Must Match [{}]", value), - Self::IsNot(value) => write!(f, "Must Not Match [{}]", value), + Self::Nothing => write!(f, "No Tile"), + Self::Anything => write!(f, "Any Tile"), + Self::Is(value) => write!(f, "Must Match [{}]", value), + Self::IsNot(value) => write!(f, "Must Not Match [{}]", value), } } } impl PartialEq<Option<i32>> for TileStatus { fn eq(&self, other: &Option<i32>) -> bool { - let matched = match self { + match self { Self::Ignore => true, Self::Nothing => other.is_none(), Self::Anything => other.is_some(), Self::Is(value) => &Some(*value) == other, Self::IsNot(value) => &Some(*value) != other, - }; - - matched + } } } impl TileStatus { - #[deprecated(since = "0.2.0", note = "Use `TileStatus::into` directly instead")] + #[deprecated(since = "0.2.0", note = "Use `TileStatus::into` directly instead")] pub fn to_ldtk_value(self) -> i64 { - self.into_tile() as i64 + self.into_tile() as i64 } - #[deprecated(since = "0.2.0", note = "Use `TileStatus::from` directly instead")] + #[deprecated(since = "0.2.0", note = "Use `TileStatus::from` directly instead")] pub fn from_ldtk_value(value: i64) -> Self { - Self::from(value) + Self::from(value) } } - /// Holds a grid of raw input data, as a more ideal format for interop and storage #[repr(transparent)] pub struct TileLayout(pub [Option<i32>; TILE_GRID_SIZE]); @@ -95,19 +92,17 @@ impl TileLayout { } /// Filter the layout data so that it only contains the tiles surrounding the target tile. This - /// means that the array index of every entry before the center point will match the original data - /// array, but ever entry after the center point will have its index shifted down by 1 - /// - /// The main utility of this is to perform set operations on every tile _other_ than the target tile. + /// means that the array index of every entry before the center point will match the original data + /// array, but ever entry after the center point will have its index shifted down by 1 + /// + /// The main utility of this is to perform set operations on every tile _other_ than the target tile. /// /// ## Examples /// /// ``` /// # use micro_autotile::TileLayout; /// let layout = TileLayout::single(123); - /// let has_any_surrounding_tiles = layout.surrounding() - /// .iter() - /// .any(|tile| tile.is_some()); + /// let has_any_surrounding_tiles = layout.surrounding().iter().any(|tile| tile.is_some()); /// /// assert_eq!(has_any_surrounding_tiles, false); /// ``` @@ -124,12 +119,8 @@ impl Debug for TileLayout { let lines = as_lines(&self.0); let mut max_width = 1; - for line in lines.iter() { - for value in line.iter() { - if let Some(value) = value { - max_width = max_width.max(value.to_string().len()); - } - } + for value in lines.iter().copied().flatten().flatten() { + max_width = max_width.max(value.to_string().len()); } for line in lines { @@ -139,22 +130,31 @@ impl Debug for TileLayout { } else { write!(f, "{:^max_width$} ", "#")?; } - } - write!(f, "\n")?; + } + writeln!(f)?; } - write!(f, "\n") + writeln!(f) } else { writeln!(f, "{:?}", self.0) } } } -impl <T> TryFrom<&[T]> for TileLayout where T: IntoTile + Copy + Default { +impl<T> TryFrom<&[T]> for TileLayout +where + T: IntoTile + Copy + Default, +{ type Error = NotSquareError; fn try_from(value: &[T]) -> Result<Self, Self::Error> { if is_square(value.len()) { let formatted = transpose(value); - Ok(Self(formatted.map(|t| if t.into_tile() == 0 { None } else { Some(t.into_tile()) }))) + Ok(Self(formatted.map(|t| { + if t.into_tile() == 0 { + None + } else { + Some(t.into_tile()) + } + }))) } else { Err(NotSquareError) } @@ -174,9 +174,9 @@ impl Debug for TileMatcher { for value in line { write!(f, "{:?} ", value)?; } - write!(f, "\n")?; + writeln!(f)?; } - writeln!(f, "\n") + writeln!(f) } else { write!(f, "TileMatcher({:?})", self.0) } @@ -185,8 +185,8 @@ impl Debug for TileMatcher { impl Default for TileMatcher { fn default() -> Self { - TileMatcher([TileStatus::default(); TILE_GRID_SIZE]) - } + TileMatcher([TileStatus::default(); TILE_GRID_SIZE]) + } } impl TileMatcher { @@ -196,7 +196,6 @@ impl TileMatcher { TileMatcher(rules) } - /// Create a 1x1 matcher, with any rule for the target tile pub const fn single_match(value: i32) -> Self { Self::single(TileStatus::Is(value)) @@ -217,7 +216,10 @@ impl TileMatcher { } } -impl <T> TryFrom<&[T]> for TileMatcher where T: IntoTile + Copy + Default { +impl<T> TryFrom<&[T]> for TileMatcher +where + T: IntoTile + Copy + Default, +{ type Error = NotSquareError; fn try_from(value: &[T]) -> Result<Self, Self::Error> { if is_square(value.len()) { @@ -232,10 +234,10 @@ impl TryFrom<&[TileStatus]> for TileMatcher { type Error = NotSquareError; fn try_from(value: &[TileStatus]) -> Result<Self, Self::Error> { if is_square(value.len()) { - Ok(Self(transpose(value))) - } else { - Err(NotSquareError) - } + Ok(Self(transpose(value))) + } else { + Err(NotSquareError) + } } } @@ -287,8 +289,10 @@ impl TryFrom<&[TileStatus]> for TileMatcher { /// 1 1 1 1 1 1 1 /// 1 1 1 1 1 1 1 /// ``` -/// -fn transpose<Value>(input: &[Value]) -> [Value; TILE_GRID_SIZE] where Value: Default + Copy { +fn transpose<Value>(input: &[Value]) -> [Value; TILE_GRID_SIZE] +where + Value: Default + Copy, +{ if input.len() == TILE_GRID_SIZE { match input.try_into() { Ok(output) => return output, @@ -301,7 +305,7 @@ fn transpose<Value>(input: &[Value]) -> [Value; TILE_GRID_SIZE] where Value: Def if !is_square(input.len()) { // Length isn't square == it does not represent a square grid - panic!("Input must be a square grid"); + panic!("Input must be a square grid"); } let input_size = (input.len() as f64).sqrt() as usize; @@ -333,7 +337,7 @@ fn transpose<Value>(input: &[Value]) -> [Value; TILE_GRID_SIZE] where Value: Def let output_idx = adjusted_output_y * RULE_MAGNITUDE + adjusted_output_x; output[output_idx] = input[input_idx]; - } + } } output @@ -356,15 +360,10 @@ mod tests { fn it_transposes_odd_grids() { let input = [2, 2, 2, 2, 2, 2, 2, 2, 2]; let expected = [ - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 2, 2, 0, 0, - 0, 0, 2, 2, 2, 0, 0, - 0, 0, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, + 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; assert_eq!(expected, transpose(&input)); } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 88c7a72a8d83140332e09ccdb82aa54188378dcd..f705f75b833ace158888fd00dd75d5989842c85d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,33 +98,33 @@ //! const OTHER_TILE: i32 = 342; //! //! let wall_rules = AutoRuleSet(vec![ -//! AutoTileRule::single_when(TileMatcher::try_from([ // Top Left Corner -//! TileStatus::IsNot(WALL_TILE), TileStatus::IsNot(WALL_TILE), TileStatus::IsNot(WALL_TILE), -//! TileStatus::IsNot(WALL_TILE), TileStatus::Is(WALL_TILE), TileStatus::Is(WALL_TILE), -//! TileStatus::IsNot(WALL_TILE), TileStatus::Is(WALL_TILE), TileStatus::Is(WALL_TILE), -//! ].as_slice()).unwrap(), 54), -//! AutoTileRule::single_when(TileMatcher::try_from([ // Top Right Corner -//! TileStatus::IsNot(WALL_TILE), TileStatus::IsNot(WALL_TILE), TileStatus::IsNot(WALL_TILE), -//! TileStatus::Is(WALL_TILE), TileStatus::Is(WALL_TILE), TileStatus::IsNot(WALL_TILE), -//! TileStatus::Is(WALL_TILE), TileStatus::Is(WALL_TILE), TileStatus::IsNot(WALL_TILE), -//! ].as_slice()).unwrap(), 55), -//! // ... Etc +//! AutoTileRule::single_when(TileMatcher::try_from([ // Top Left Corner +//! TileStatus::IsNot(WALL_TILE), TileStatus::IsNot(WALL_TILE), TileStatus::IsNot(WALL_TILE), +//! TileStatus::IsNot(WALL_TILE), TileStatus::Is(WALL_TILE), TileStatus::Is(WALL_TILE), +//! TileStatus::IsNot(WALL_TILE), TileStatus::Is(WALL_TILE), TileStatus::Is(WALL_TILE), +//! ].as_slice()).unwrap(), 54), +//! AutoTileRule::single_when(TileMatcher::try_from([ // Top Right Corner +//! TileStatus::IsNot(WALL_TILE), TileStatus::IsNot(WALL_TILE), TileStatus::IsNot(WALL_TILE), +//! TileStatus::Is(WALL_TILE), TileStatus::Is(WALL_TILE), TileStatus::IsNot(WALL_TILE), +//! TileStatus::Is(WALL_TILE), TileStatus::Is(WALL_TILE), TileStatus::IsNot(WALL_TILE), +//! ].as_slice()).unwrap(), 55), +//! // ... Etc //! ]); //! //! let ground_rules = AutoRuleSet(vec![ -//! // Use decorated tiles in 10% of cases -//! AutoTileRule::single_any_chance(GROUND_TILE, vec![45, 46, 47], 0.1), -//! // Fall back to the basic tile if we don't match previously -//! AutoTileRule::exact(GROUND_TILE, 44), +//! // Use decorated tiles in 10% of cases +//! AutoTileRule::single_any_chance(GROUND_TILE, vec![45, 46, 47], 0.1), +//! // Fall back to the basic tile if we don't match previously +//! AutoTileRule::exact(GROUND_TILE, 44), //! ]); //! //! // Easily merge rule sets in an ordered way //! let combined_rules = wall_rules + ground_rules; //! //! let sublayout = TileLayout::try_from([ -//! Some(OTHER_TILE), Some(GROUND_TILE), Some(GROUND_TILE), -//! Some(WALL_TILE), Some(WALL_TILE), Some(OTHER_TILE), -//! Some(WALL_TILE), Some(WALL_TILE), Some(GROUND_TILE), +//! Some(OTHER_TILE), Some(GROUND_TILE), Some(GROUND_TILE), +//! Some(WALL_TILE), Some(WALL_TILE), Some(OTHER_TILE), +//! Some(WALL_TILE), Some(WALL_TILE), Some(GROUND_TILE), //! ].as_slice()).unwrap(); //! //! // We've got a layout that represents the top right corner of a wall, the second rule in our @@ -136,12 +136,11 @@ //! # } //! ``` -mod layout; mod autotile; +mod layout; mod output; mod utils; -pub use layout::*; pub use autotile::*; +pub use layout::*; pub use output::*; - diff --git a/src/output.rs b/src/output.rs index b5704d1bd65dcd589fc12595869ec82392dbc37b..ed16ddd48de60e08a772f6cca126b346b53a7ddc 100644 --- a/src/output.rs +++ b/src/output.rs @@ -41,4 +41,4 @@ impl TileOutput { Self::Random(vals) => vals.get(rng.usize(0..vals.len())).copied(), } } -} \ No newline at end of file +} diff --git a/src/utils.rs b/src/utils.rs index 1c04ee7ae06c3f81ec903a8185068668773a247e..7417ed390c557cb82c1a100af2fd31e40711fb05 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,140 +1,140 @@ use crate::TileStatus; pub trait IntoTile { - fn into_tile(self) -> i32; + fn into_tile(self) -> i32; } // Implementations for unsigned integer types impl IntoTile for u8 { - fn into_tile(self) -> i32 { - self as i32 - } + fn into_tile(self) -> i32 { + self as i32 + } } impl IntoTile for u16 { - fn into_tile(self) -> i32 { - self as i32 - } + fn into_tile(self) -> i32 { + self as i32 + } } impl IntoTile for u32 { - fn into_tile(self) -> i32 { - self as i32 - } + fn into_tile(self) -> i32 { + self as i32 + } } impl IntoTile for u64 { - fn into_tile(self) -> i32 { - self as i32 - } + fn into_tile(self) -> i32 { + self as i32 + } } impl IntoTile for u128 { - fn into_tile(self) -> i32 { - self as i32 - } + fn into_tile(self) -> i32 { + self as i32 + } } impl IntoTile for usize { - fn into_tile(self) -> i32 { - self as i32 - } + fn into_tile(self) -> i32 { + self as i32 + } } // Implementations for signed integer types impl IntoTile for i8 { - fn into_tile(self) -> i32 { - self as i32 - } + fn into_tile(self) -> i32 { + self as i32 + } } impl IntoTile for i16 { - fn into_tile(self) -> i32 { - self as i32 - } + fn into_tile(self) -> i32 { + self as i32 + } } impl IntoTile for i32 { - fn into_tile(self) -> i32 { - self - } + fn into_tile(self) -> i32 { + self + } } impl IntoTile for i64 { - fn into_tile(self) -> i32 { - self as i32 - } + fn into_tile(self) -> i32 { + self as i32 + } } impl IntoTile for i128 { - fn into_tile(self) -> i32 { - self as i32 - } + fn into_tile(self) -> i32 { + self as i32 + } } impl IntoTile for isize { - fn into_tile(self) -> i32 { - self as i32 - } + fn into_tile(self) -> i32 { + self as i32 + } } // We don't actually implement IntoTile for TileStatus, as this would conflict with a later From impl impl TileStatus { - pub fn into_tile(self) -> i32 { - match self { - Self::Ignore => 0, - Self::Nothing => -1000001, - Self::Anything => 1000001, - Self::Is(value) => value, - Self::IsNot(value) => -(value), - } - } + pub fn into_tile(self) -> i32 { + match self { + Self::Ignore => 0, + Self::Nothing => -1000001, + Self::Anything => 1000001, + Self::Is(value) => value, + Self::IsNot(value) => -(value), + } + } } impl From<TileStatus> for i32 { - fn from(value: TileStatus) -> Self { - value.into_tile() - } + fn from(value: TileStatus) -> Self { + value.into_tile() + } } impl From<TileStatus> for i64 { - fn from(value: TileStatus) -> Self { - value.into_tile() as i64 - } -} - -impl <T: IntoTile> IntoTile for Option<T> { - fn into_tile(self) -> i32 { - match self { - Some(value) => value.into_tile(), - None => 0, - } - } -} - -impl <T: IntoTile, E> IntoTile for Result<T, E> { - fn into_tile(self) -> i32 { - match self { - Ok(value) => value.into_tile(), - Err(_) => 0, - } - } -} - -impl <I: IntoTile> From<I> for TileStatus { - fn from(value: I) -> Self { - let value = value.into_tile(); - match value { - 0 => Self::Ignore, - 1000001 => Self::Anything, - -1000001 => Self::Nothing, - other => { - if other > 0 { - Self::Is(other.into_tile()) - } else { - Self::IsNot(other.abs().into_tile()) - } - } - } - } -} \ No newline at end of file + fn from(value: TileStatus) -> Self { + value.into_tile() as i64 + } +} + +impl<T: IntoTile> IntoTile for Option<T> { + fn into_tile(self) -> i32 { + match self { + Some(value) => value.into_tile(), + None => 0, + } + } +} + +impl<T: IntoTile, E> IntoTile for Result<T, E> { + fn into_tile(self) -> i32 { + match self { + Ok(value) => value.into_tile(), + Err(_) => 0, + } + } +} + +impl<I: IntoTile> From<I> for TileStatus { + fn from(value: I) -> Self { + let value = value.into_tile(); + match value { + 0 => Self::Ignore, + 1000001 => Self::Anything, + -1000001 => Self::Nothing, + other => { + if other > 0 { + Self::Is(other.into_tile()) + } else { + Self::IsNot(other.abs().into_tile()) + } + } + } + } +}