diff options
Diffstat (limited to 'src/types')
-rw-r--r-- | src/types/collision.rs | 9 | ||||
-rw-r--r-- | src/types/command.rs | 41 | ||||
-rw-r--r-- | src/types/direction.rs | 13 | ||||
-rw-r--r-- | src/types/entity_map.rs | 430 | ||||
-rw-r--r-- | src/types/menu.rs | 31 | ||||
-rw-r--r-- | src/types/mod.rs | 504 |
6 files changed, 0 insertions, 1028 deletions
diff --git a/src/types/collision.rs b/src/types/collision.rs deleted file mode 100644 index 59c60e69ee50..000000000000 --- a/src/types/collision.rs +++ /dev/null @@ -1,9 +0,0 @@ -/// Describes a kind of game collision -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Collision { - /// Stop moving - you can't move there! - Stop, - - /// Moving into an entity at the given position indicates combat - Combat, -} diff --git a/src/types/command.rs b/src/types/command.rs deleted file mode 100644 index 17ca4d280fd8..000000000000 --- a/src/types/command.rs +++ /dev/null @@ -1,41 +0,0 @@ -use super::Direction; -use super::Direction::*; -use termion::event::Key; -use termion::event::Key::{Char, Ctrl}; - -pub enum Command { - /// Quit the game - Quit, - - /// Move the character in a direction - Move(Direction), - - /// Pick up any item(s) at the current position - PickUp, - - /// Display the previous message - PreviousMessage, -} - -impl Command { - pub fn from_key(k: Key) -> Option<Command> { - use Command::*; - match k { - Char('q') => Some(Quit), - - Char('h') | Char('a') | Key::Left => Some(Move(Left)), - Char('k') | Char('w') | Key::Up => Some(Move(Up)), - Char('j') | Char('s') | Key::Down => Some(Move(Down)), - Char('l') | Char('d') | Key::Right => Some(Move(Right)), - Char('y') => Some(Move(UpLeft)), - Char('u') => Some(Move(UpRight)), - Char('b') => Some(Move(DownLeft)), - Char('n') => Some(Move(DownRight)), - - Ctrl('p') => Some(PreviousMessage), - Char(',') => Some(PickUp), - - _ => None, - } - } -} diff --git a/src/types/direction.rs b/src/types/direction.rs deleted file mode 100644 index 9b5c0991da8d..000000000000 --- a/src/types/direction.rs +++ /dev/null @@ -1,13 +0,0 @@ -use proptest_derive::Arbitrary; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)] -pub enum Direction { - Left, - Up, - Down, - Right, - UpLeft, - UpRight, - DownRight, - DownLeft, -} diff --git a/src/types/entity_map.rs b/src/types/entity_map.rs deleted file mode 100644 index 202d8b593e15..000000000000 --- a/src/types/entity_map.rs +++ /dev/null @@ -1,430 +0,0 @@ -use crate::entities::entity::Identified; -use crate::entities::EntityID; -use crate::types::Neighbors; -use crate::types::Position; -use crate::types::Positioned; -use crate::types::PositionedMut; -use alga::general::{ - AbstractMagma, AbstractMonoid, AbstractSemigroup, Additive, Identity, -}; -use std::collections::{hash_map, BTreeMap, HashMap}; -use std::iter::FromIterator; - -#[derive(Debug, Clone, Default)] -pub struct EntityMap<A> { - by_position: BTreeMap<Position, Vec<EntityID>>, - by_id: HashMap<EntityID, A>, - last_id: EntityID, -} - -impl<A: PartialEq> PartialEq for EntityMap<A> { - fn eq(&self, other: &Self) -> bool { - self.by_position == other.by_position && self.by_id == other.by_id - } -} -impl<A: Eq> Eq for EntityMap<A> {} - -const BY_POS_INVARIANT: &str = - "Invariant: All references in EntityMap.by_position should point to existent references in by_id"; - -impl<A> EntityMap<A> { - pub fn new() -> EntityMap<A> { - EntityMap { - by_position: BTreeMap::new(), - by_id: HashMap::new(), - last_id: 0, - } - } - - pub fn len(&self) -> usize { - self.by_id.len() - } - - /// Returns a list of all entities at the given position - pub fn at<'a>(&'a self, pos: Position) -> Vec<&'a A> { - self.by_position - .get(&pos) - .iter() - .flat_map(|eids| { - eids.iter() - .map(|eid| self.by_id.get(eid).expect(BY_POS_INVARIANT)) - }) - .collect() - } - - /// Remove all entities at the given position - pub fn remove_all_at(&mut self, pos: Position) { - if let Some(eids) = self.by_position.remove(&pos) { - for eid in eids { - self.by_id.remove(&eid).expect(BY_POS_INVARIANT); - } - } - } - - pub fn get(&self, id: EntityID) -> Option<&A> { - self.by_id.get(&id) - } - - pub fn get_mut(&mut self, id: EntityID) -> Option<&mut A> { - self.by_id.get_mut(&id) - } - - pub fn entities(&self) -> impl Iterator<Item = &A> { - self.by_id.values() - } - - pub fn entities_mut(&mut self) -> impl Iterator<Item = &mut A> { - self.by_id.values_mut() - } - - pub fn ids(&self) -> hash_map::Keys<'_, EntityID, A> { - self.by_id.keys() - } - - pub fn drain(&mut self) -> Drain<'_, A> { - let ids = self.ids().copied().collect::<Vec<_>>(); - Drain { - map: self, - ids_iter: Box::new(ids.into_iter()), - } - } - - fn next_id(&mut self) -> EntityID { - self.last_id += 1; - self.last_id - } -} - -impl<A: Positioned + Identified<EntityID>> EntityMap<A> { - pub fn insert(&mut self, mut entity: A) -> EntityID { - let pos = entity.position(); - let entity_id = self.next_id(); - entity.set_id(entity_id); - self.by_id.entry(entity_id).or_insert(entity); - self.by_position - .entry(pos) - .or_insert_with(Vec::new) - .push(entity_id); - entity_id - } - - /// Remove the entity with the given ID - pub fn remove(&mut self, id: EntityID) -> Option<A> { - self.by_id.remove(&id).map(|e| { - let mut empty = false; - let position = e.position(); - - if let Some(es) = self.by_position.get_mut(&position) { - es.retain(|e| *e != id); - if es.is_empty() { - empty = true; - } - } - - if empty { - self.by_position.remove(&position); - } - e - }) - } - - /// Moves all elements from `other` into `Self`, leathing `other` empty. - pub fn append(&mut self, other: &mut Self) { - // TODO there's probably some perf opportunities here by calling - // reserve() on stuff - for (_, entity) in other.drain() { - self.insert(entity); - } - } - - /// Gets all 8 neighbors of the given position. - pub fn neighbors<'a>( - &'a self, - position: Position, - ) -> Neighbors<Vec<(EntityID, &'a A)>> { - Neighbors::of_position(position) - .map(|pos| self.at(*pos)) - .mapmap(&|e| (e.id(), *e)) - } - - pub fn neighbor_entities<'a>( - &'a self, - position: Position, - ) -> Neighbors<Vec<&'a A>> { - self.neighbors(position).mapmap(&|(_eid, ent)| *ent) - } - - pub fn check_invariants(&self) { - for (id, ent) in &self.by_id { - assert_eq!(*id, ent.id()); - } - - for (pos, ents) in &self.by_position { - for eid in ents { - let ent = self.by_id.get(eid).unwrap(); - assert_eq!(*pos, ent.position()) - } - } - } -} - -impl<'a, A: Positioned + Identified<EntityID>> IntoIterator - for &'a EntityMap<A> -{ - type Item = (&'a EntityID, &'a A); - type IntoIter = std::collections::hash_map::Iter<'a, EntityID, A>; - fn into_iter(self) -> Self::IntoIter { - (&self.by_id).iter() - } -} - -impl<A: Positioned + Identified<EntityID>> IntoIterator for EntityMap<A> { - type Item = (EntityID, A); - type IntoIter = std::collections::hash_map::IntoIter<EntityID, A>; - fn into_iter(self) -> Self::IntoIter { - self.by_id.into_iter() - } -} - -impl<A: Positioned + Identified<EntityID>> FromIterator<A> for EntityMap<A> { - fn from_iter<I: IntoIterator<Item = A>>(iter: I) -> Self { - let mut em = EntityMap::new(); - for ent in iter { - em.insert(ent); - } - em - } -} - -impl<A: Positioned + Identified<EntityID> + Eq + Clone> AbstractMagma<Additive> - for EntityMap<A> -{ - fn operate(&self, right: &Self) -> Self { - let mut by_position = self.by_position.clone(); - by_position.append(&mut right.by_position.clone()); - - let mut by_id = self.by_id.clone(); - for (k, v) in right.by_id.clone() { - by_id.insert(k, v); - } - - EntityMap { - by_position, - by_id, - last_id: self.last_id.max(right.last_id), - } - } -} - -impl<A: Positioned + Identified<EntityID> + Eq + Clone> - AbstractSemigroup<Additive> for EntityMap<A> -{ -} - -impl<A: Positioned + Identified<EntityID> + Eq> Identity<Additive> - for EntityMap<A> -{ - fn identity() -> Self { - EntityMap::new() - } -} - -impl<A: Positioned + Identified<EntityID> + Eq + Clone> AbstractMonoid<Additive> - for EntityMap<A> -{ -} - -impl<A: PositionedMut> EntityMap<A> { - pub fn update_position( - &mut self, - entity_id: EntityID, - new_position: Position, - ) { - let mut old_pos = None; - if let Some(entity) = self.by_id.get_mut(&entity_id) { - if entity.position() == new_position { - return; - } - old_pos = Some(entity.position()); - entity.set_position(new_position); - } - - if let Some(p) = old_pos { - if let Some(es) = self.by_position.get_mut(&p) { - es.retain(|e| *e != entity_id); - } - - self.by_position - .entry(new_position) - .or_insert_with(Vec::new) - .push(entity_id); - } - } -} - -pub struct Drain<'a, A> { - map: &'a mut EntityMap<A>, - ids_iter: Box<dyn Iterator<Item = EntityID> + 'a>, -} - -impl<A: Positioned + Identified<EntityID>> Iterator for Drain<'_, A> { - type Item = (EntityID, A); - - fn next(&mut self) -> Option<Self::Item> { - self.ids_iter - .next() - .map(|eid| (eid, self.map.remove(eid).expect(BY_POS_INVARIANT))) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::types::PositionedMut; - use proptest::prelude::*; - use proptest_derive::Arbitrary; - - #[derive(Debug, Arbitrary, PartialEq, Eq, Clone)] - struct TestEntity { - _id: Option<EntityID>, - position: Position, - name: String, - } - - impl Positioned for TestEntity { - fn position(&self) -> Position { - self.position - } - } - - impl PositionedMut for TestEntity { - fn set_position(&mut self, pos: Position) { - self.position = pos - } - } - - impl Identified<EntityID> for TestEntity { - fn opt_id(&self) -> Option<EntityID> { - self._id - } - - fn set_id(&mut self, id: EntityID) { - self._id = Some(id); - } - } - - fn gen_entity_map() -> BoxedStrategy<EntityMap<TestEntity>> { - any::<Vec<TestEntity>>() - .prop_map(|ents| { - ents.iter().cloned().collect::<EntityMap<TestEntity>>() - }) - .boxed() - } - - proptest! { - #![proptest_config(ProptestConfig::with_cases(10))] - - #[test] - fn test_entity_map_len(items: Vec<TestEntity>) { - let mut map = EntityMap::new(); - assert_eq!(map.len(), 0); - for ent in &items { - map.insert(ent.clone()); - } - assert_eq!(map.len(), items.len()); - } - - #[test] - fn test_entity_map_getset( - mut em in gen_entity_map(), - ent: TestEntity - ) { - em.insert(ent.clone()); - assert!(em.at(ent.position).iter().any(|e| e.name == ent.name)) - } - - #[test] - fn test_entity_map_set_iter_contains( - mut em in gen_entity_map(), - ent: TestEntity - ) { - em.insert(ent.clone()); - assert!(em.entities().any(|e| e.name == ent.name)) - } - - #[test] - fn test_update_position( - mut em in gen_entity_map(), - ent: TestEntity, - new_position: Position, - ) { - let original_position = ent.position(); - let entity_id = em.insert(ent.clone()); - em.update_position(entity_id, new_position); - - if new_position != original_position { - assert!(em.at(original_position).iter().all(|e| e.name != ent.name)); - } - assert_eq!( - em.get(entity_id).map(|e| e.position()), - Some(new_position) - ); - assert!( - em.at(new_position).iter().map( - |e| e.name.clone()).any(|en| en == ent.name), - ) - } - - #[test] - fn test_remove_all_at( - mut em in gen_entity_map(), - pos: Position, - ) { - em.remove_all_at(pos); - assert_eq!(em.at(pos).len(), 0); - } - - #[test] - fn test_entity_map_semigroup_laws( - em1 in gen_entity_map(), - em2 in gen_entity_map(), - em3 in gen_entity_map(), - ) { - assert!(AbstractSemigroup::prop_is_associative((em1, em2, em3))); - } - - fn test_entity_map_monoid_laws( - em in gen_entity_map(), - ) { - assert!( - AbstractMonoid::prop_operating_identity_element_is_noop((em,)) - ); - } - - #[test] - fn test_entity_map_append( - mut target in gen_entity_map(), - mut source in gen_entity_map(), - ) { - let orig_target = target.clone(); - let orig_source = source.clone(); - - target.append(&mut source); - target.check_invariants(); - - assert_eq!(source, EntityMap::new()); - - for ent in orig_source.entities() { - assert!( - target.at(ent.position()).iter().any(|e| e.name == ent.name) - ); - } - - for ent in orig_target.entities() { - assert!( - target.at(ent.position()).iter().any(|e| e.name == ent.name) - ); - } - } - } -} diff --git a/src/types/menu.rs b/src/types/menu.rs deleted file mode 100644 index 63abc837788e..000000000000 --- a/src/types/menu.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::types::Dimensions; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MenuInfo { - pub prompt: String, - pub options: Vec<String>, -} - -impl MenuInfo { - pub fn new(prompt: String, options: Vec<String>) -> Self { - MenuInfo { prompt, options } - } - - /// Returns the inner dimensions of a box necessary to draw this menu. Will - /// not trim either dimension to the size of the terminal - pub fn dimensions(&self) -> Dimensions { - Dimensions { - w: self - .options - .iter() - .map(|s| s.len()) - .max() - .unwrap_or(0) - .max(self.prompt.len()) as u16 - + 4, - h: self.options.len() as u16 - + if self.prompt.is_empty() { 0 } else { 2 } - + 4, - } - } -} diff --git a/src/types/mod.rs b/src/types/mod.rs deleted file mode 100644 index d417e873d8ca..000000000000 --- a/src/types/mod.rs +++ /dev/null @@ -1,504 +0,0 @@ -#![allow(clippy::unit_arg)] -#![allow(clippy::identity_conversion)] - -use std::cmp::max; -use std::cmp::Ordering; -use std::ops; -use std::rc::Rc; - -pub mod collision; -pub mod command; -pub mod direction; -pub mod entity_map; -pub mod menu; - -pub use collision::Collision; -pub use direction::Direction; -pub use direction::Direction::*; -use proptest_derive::Arbitrary; -use termion::cursor; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)] -pub struct Dimensions { - #[proptest(strategy = "std::ops::Range::<u16>::from(0..100)")] - pub w: u16, - - #[proptest(strategy = "std::ops::Range::<u16>::from(0..100)")] - pub h: u16, -} - -pub const ZERO_DIMENSIONS: Dimensions = Dimensions { w: 0, h: 0 }; -pub const UNIT_DIMENSIONS: Dimensions = Dimensions { w: 1, h: 1 }; - -impl Default for Dimensions { - fn default() -> Self { - Dimensions { w: 80, h: 20 } - } -} - -impl ops::Sub<Dimensions> for Dimensions { - type Output = Dimensions; - fn sub(self, dims: Dimensions) -> Dimensions { - Dimensions { - w: self.w - dims.w, - h: self.h - dims.h, - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)] -pub struct BoundingBox { - pub dimensions: Dimensions, - pub position: Position, -} - -impl BoundingBox { - pub fn at_origin(dimensions: Dimensions) -> BoundingBox { - BoundingBox { - dimensions, - position: ORIGIN, - } - } - - pub fn from_corners( - top_left: Position, - lower_right: Position, - ) -> BoundingBox { - BoundingBox { - position: top_left, - dimensions: Dimensions { - w: (lower_right.x - top_left.x) as u16, - h: (lower_right.y - top_left.y) as u16, - }, - } - } - - pub fn lr_corner(self) -> Position { - self.position - + (Position { - x: self.dimensions.w as i16, - y: self.dimensions.h as i16, - }) - } - - pub fn ll_corner(self) -> Position { - self.position - + (Position { - x: 0, - y: self.dimensions.h as i16, - }) - } - - /// Returns a bounding box representing the *inside* of this box if it was - /// drawn on the screen. - pub fn inner(self) -> BoundingBox { - self + UNIT_POSITION - UNIT_DIMENSIONS - UNIT_DIMENSIONS - } - - /// Moves the top right corner of the bounding box by the offset specified - /// by the given position, keeping the lower right corner in place - pub fn move_tr_corner(self, offset: Position) -> BoundingBox { - self + offset - - Dimensions { - w: offset.x as u16, - h: offset.y as u16, - } - } -} - -impl ops::Add<Position> for BoundingBox { - type Output = BoundingBox; - fn add(self, pos: Position) -> BoundingBox { - BoundingBox { - position: self.position + pos, - ..self - } - } -} - -impl ops::Sub<Dimensions> for BoundingBox { - type Output = BoundingBox; - fn sub(self, dims: Dimensions) -> BoundingBox { - BoundingBox { - dimensions: self.dimensions - dims, - ..self - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary, Hash, Ord)] -pub struct Position { - /// x (horizontal) position - #[proptest(strategy = "std::ops::Range::<i16>::from(0..100)")] - pub x: i16, - - #[proptest(strategy = "std::ops::Range::<i16>::from(0..100)")] - /// y (vertical) position - pub y: i16, -} - -pub fn pos(x: i16, y: i16) -> Position { - Position { x, y } -} - -pub const ORIGIN: Position = Position { x: 0, y: 0 }; -pub const UNIT_POSITION: Position = Position { x: 1, y: 1 }; - -impl Position { - /// Returns true if this position exists within the bounds of the given box, - /// inclusive - pub fn within(self, b: BoundingBox) -> bool { - (self > b.position - UNIT_POSITION) && self < (b.lr_corner()) - } - - /// Returns a sequence of ASCII escape characters for moving the cursor to - /// this Position - pub fn cursor_goto(self) -> cursor::Goto { - // + 1 because Goto is 1-based, but position is 0-based - cursor::Goto(self.x as u16 + 1, self.y as u16 + 1) - } - - /// Converts this position to the number of `Tiles` away from the origin it - /// represents. Usually done after subtracting two positions. Gives distance - /// as the crow flies - pub fn as_tiles(self) -> Tiles { - Tiles(max(self.x.abs(), self.y.abs()).into()) - } -} - -impl PartialOrd for Position { - fn partial_cmp(&self, other: &Position) -> Option<Ordering> { - if self.x == other.x && self.y == other.y { - Some(Ordering::Equal) - } else if self.x > other.x && self.y > other.y { - Some(Ordering::Greater) - } else if self.x < other.x && self.y < other.y { - Some(Ordering::Less) - } else { - None - } - } -} - -/// Implements (bounded) addition of a Dimension to a position. -/// -/// # Examples -/// -/// ``` -/// let pos = Position { x: 1, y: 10 } -/// -/// let left_pos = pos + Direction::Left -/// assert_eq!(left, Position { x: 0, y: 10 }) -/// -/// let right_pos = pos + Direction::Right -/// assert_eq!(right_pos, Position { x: 0, y: 10 }) -/// ``` -#[allow(clippy::suspicious_arithmetic_impl)] -impl ops::Add<Direction> for Position { - type Output = Position; - fn add(self, dir: Direction) -> Position { - match dir { - Left => { - if self.x > std::i16::MIN { - Position { - x: self.x - 1, - ..self - } - } else { - self - } - } - Right => { - if self.x < std::i16::MAX { - Position { - x: self.x + 1, - ..self - } - } else { - self - } - } - Up => { - if self.y > std::i16::MIN { - Position { - y: self.y - 1, - ..self - } - } else { - self - } - } - Down => { - if self.y < std::i16::MAX { - Position { - y: self.y + 1, - ..self - } - } else { - self - } - } - UpLeft => self + Up + Left, - UpRight => self + Up + Right, - DownLeft => self + Down + Left, - DownRight => self + Down + Right, - } - } -} - -impl ops::Add<Position> for Position { - type Output = Position; - fn add(self, pos: Position) -> Position { - Position { - x: self.x + pos.x, - y: self.y + pos.y, - } - } -} - -impl ops::Sub<Position> for Position { - type Output = Position; - fn sub(self, pos: Position) -> Position { - Position { - x: self.x - pos.x, - y: self.y - pos.y, - } - } -} - -impl Positioned for Position { - fn position(&self) -> Position { - *self - } -} - -pub trait Positioned { - fn x(&self) -> i16 { - self.position().x - } - - fn y(&self) -> i16 { - self.position().y - } - - fn position(&self) -> Position { - Position { - x: self.x(), - y: self.y(), - } - } -} - -pub trait PositionedMut: Positioned { - fn set_position(&mut self, pos: Position); -} - -// impl<A, I> Positioned for A where A : Deref<Target = I>, I: Positioned { -// fn position(&self) -> Position { -// self.position() -// } -// } - -impl<T: Positioned> Positioned for Box<T> { - fn position(&self) -> Position { - (**self).position() - } -} - -impl<'a, T: Positioned> Positioned for &'a T { - fn position(&self) -> Position { - (**self).position() - } -} - -impl<'a, T: Positioned> Positioned for &'a mut T { - fn position(&self) -> Position { - (**self).position() - } -} - -impl<'a, T: Positioned> Positioned for Rc<T> { - fn position(&self) -> Position { - (**self).position() - } -} - -impl<'a, T: PositionedMut> PositionedMut for &'a mut T { - fn set_position(&mut self, pos: Position) { - (**self).set_position(pos) - } -} - -#[macro_export] -macro_rules! positioned { - ($name:ident) => { - positioned!($name, position); - }; - ($name:ident, $attr:ident) => { - impl $crate::types::Positioned for $name { - fn position(&self) -> $crate::types::Position { - self.$attr - } - } - }; -} - -#[macro_export] -macro_rules! positioned_mut { - ($name:ident) => { - positioned_mut!($name, position); - }; - ($name:ident, $attr:ident) => { - impl crate::types::PositionedMut for $name { - fn set_position(&mut self, pos: $crate::types::Position) { - self.$attr = pos; - } - } - }; -} - -/// A number of ticks -#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)] -pub struct Ticks(pub u16); - -/// A number of tiles -/// -/// Expressed in terms of a float to allow moving partial tiles in a number of -/// ticks -#[derive(Clone, Copy, Debug, PartialEq, Arbitrary)] -pub struct Tiles(pub f32); - -/// The speed of an entity, expressed in ticks per tile -#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary, Deserialize)] -#[serde(transparent)] -pub struct Speed(pub u32); - -impl Speed { - /// Returns the number of tiles that would be moved in the given number of - /// ticks at this speed - pub fn ticks_to_tiles(self, ticks: Ticks) -> Tiles { - Tiles(f32::from(ticks.0) / self.0 as f32) - } - - /// Returns the number of ticks required to move the given number of tiles - /// at this speed - pub fn tiles_to_ticks(self, tiles: Tiles) -> Ticks { - Ticks(tiles.0 as u16 * self.0 as u16) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)] -pub struct Neighbors<A> { - pub top_left: A, - pub top: A, - pub top_right: A, - pub left: A, - pub right: A, - pub bottom_left: A, - pub bottom: A, - pub bottom_right: A, -} - -impl Neighbors<Position> { - fn of_position(pos: Position) -> Self { - Neighbors { - top_left: pos + Direction::UpLeft, - top: pos + Direction::Up, - top_right: pos + Direction::UpRight, - left: pos + Direction::Left, - right: pos + Direction::Right, - bottom_left: pos + Direction::DownLeft, - bottom: pos + Direction::Down, - bottom_right: pos + Direction::DownRight, - } - } -} - -impl<A> Neighbors<A> { - /// it's a functor, yo - pub fn map<B, F: Fn(&A) -> B>(&self, f: F) -> Neighbors<B> { - Neighbors { - top_left: f(&self.top_left), - top: f(&self.top), - top_right: f(&self.top_right), - left: f(&self.left), - right: f(&self.right), - bottom_left: f(&self.bottom_left), - bottom: f(&self.bottom), - bottom_right: f(&self.bottom_right), - } - } -} - -impl<A> Neighbors<Vec<A>> { - pub fn mapmap<B, F: Fn(&A) -> B>(&self, f: &F) -> Neighbors<Vec<B>> { - self.map(|xs| xs.iter().map(f).collect()) - } -} - -#[cfg(test)] -mod tests { - #![allow(clippy::unnecessary_operation)] - use super::*; - use proptest::prelude::*; - - proptest! { - #[test] - fn position_partialord_lt_transitive( - a: Position, - b: Position, - c: Position - ) { - if a < b && b < c { - assert!(a < c) - } - } - - #[test] - fn position_partialord_eq_transitive( - a: Position, - b: Position, - c: Position - ) { - if a == b && b == c { - assert!(a == c) - } - } - - #[test] - fn position_partialord_gt_transitive( - a: Position, - b: Position, - c: Position, - ) { - if a > b && b > c { - assert!(a > c) - } - } - - #[test] - fn position_partialord_antisymmetric(a: Position, b: Position) { - if a < b { - assert!(!(a > b)) - } else if a > b { - assert!(!(a < b)) - } - } - - #[test] - fn test_position_plus_dimension_as_tiles_monoid_action( - pos: Position, - dir: Direction, - ) { - prop_assume!(pos.y > 0 && pos.x > 0); - assert_eq!(((pos + dir) - pos).as_tiles(), Tiles(1.0)); - } - } - - #[test] - fn test_position_as_tiles() { - assert_eq!(pos(0, 0).as_tiles(), Tiles(0.0)); - assert_eq!(pos(1, 1).as_tiles(), Tiles(1.0)); - assert_eq!(pos(1, 2).as_tiles(), Tiles(2.0)); - } -} |