/// Ĉi-tiu modulo enhavas la elementojn por kub-koordinato. /// /// This code is based on https://www.redblobgames.com/grids/hexagons/ use crate::Error; use std::collections::HashSet; /// An address within the hex coordinate system #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct HexAddr { q: i32, r: i32, } impl HexAddr { /// Create a new axial coordinate address. pub fn new(q: i32, r: i32) -> Self { Self { q, r } } pub fn origin() -> HexAddr { HexAddr { q: 0, r: 0 } } pub fn q(&self) -> i32 { self.q } pub fn r(&self) -> i32 { self.r } pub fn s(&self) -> i32 { -self.q - self.r } /// Get a list of coordinates adjacent to this one. pub fn adjacencies(&self) -> impl Iterator { vec![ HexAddr::new(self.q + 1, self.r), HexAddr::new(self.q, self.r + 1), HexAddr::new(self.q - 1, self.r + 1), HexAddr::new(self.q - 1, self.r), HexAddr::new(self.q, self.r - 1), HexAddr::new(self.q + 1, self.r - 1), ] .into_iter() } /// Test whether a coordinate is adjacent to this one pub fn is_adjacent(&self, dest: &HexAddr) -> bool { dest.adjacencies().collect::>().contains(&self) } /// Measure the distance to a destination pub fn distance(&self, dest: &HexAddr) -> usize { (((self.q() - dest.q()).abs() + (self.r() - dest.r()).abs() + (self.s() - dest.s()).abs()) / 2) as usize } /// Get an iteration to all of the coordinates within the specified distance of this one. pub fn within(&self, distance: usize) -> impl Iterator { let eko = self.clone(); let mut rezulto: HashSet = HashSet::new(); let mut vico: Vec = Vec::new(); vico.push(eko); while vico.len() > 0 { let elem = vico.remove(0); for adj in elem.adjacencies() { if self.distance(&adj) <= distance && !rezulto.contains(&adj) { vico.push(adj.clone()); } } rezulto.insert(elem); } rezulto.remove(self); rezulto.into_iter() } } #[cfg(test)] mod tests { use super::*; use proptest::prelude::*; use std::collections::HashSet; #[test] fn distance_0_is_empty() { let addr = HexAddr::origin(); let lst: Vec = addr.within(0).collect(); assert_eq!(lst.len(), 0); } #[test] fn distance_1_has_six_addresses() { let hexaddr = HexAddr::origin(); let lst: Vec = hexaddr.within(1).collect(); assert_eq!(lst.len(), 6); } #[test] fn distance_2_has_18_addresses() { let hexaddr = HexAddr::origin(); let lst: Vec = hexaddr.within(2).collect(); assert_eq!(lst.len(), 18); } fn address() -> (std::ops::Range, std::ops::Range) { (-65536_i32..65535, -65536_i32..65535) } proptest! { #[test] fn produces_adjacent_coordinates((x, y) in address(), idx in 0_usize..6) { let coord1 = HexAddr::new(x, y); let lst1: HashSet = coord1.adjacencies().collect(); assert_eq!(lst1.len(), 6); let lst1 = lst1.into_iter().collect::>(); let coord2 = &lst1[idx]; let lst2: Vec = coord2.adjacencies().collect(); assert!(lst2.contains(&coord1)); } #[test] fn tests_adjacencies((q, r) in address(), idx in 0_usize..6) { let coord1 = HexAddr::new(q, r); let lst1: Vec = coord1.adjacencies().collect(); assert_eq!(lst1.len(), 6); let coord2 = &lst1[idx]; assert!(coord2.is_adjacent(&coord1)); assert!(coord1.is_adjacent(&coord2)); } #[test] fn measures_distance((q1, r1) in address(), (q2, r2) in address()) { let hexaddr_1 = HexAddr::new(q1, r1); let hexaddr_2 = HexAddr::new(q2, r2); let s1 = -q1 - r1; let s2 = -q2 - r2; let expected = (((q1 - q2).abs() + (r1 - r2).abs() + (s1 - s2).abs()) / 2) as usize; assert_eq!(hexaddr_1.distance(&hexaddr_2), expected); assert_eq!(hexaddr_2.distance(&hexaddr_1), expected); } #[test] fn calculates_distance((q, r) in address(), distance in 0_usize..6) { let hexaddr = HexAddr::new(q, r); let en_distancaj_hexaddr: Vec = hexaddr.within(distance).collect(); let expected_cnt = ((0..distance+1).map(|v| v * 6).fold(0, |acc, val| acc + val)) as usize; assert_eq!(en_distancaj_hexaddr.len(), expected_cnt); for c in en_distancaj_hexaddr { assert!(c.distance(&hexaddr) <= distance as usize); } } } }