nil_core/continent/
mod.rs1pub mod coord;
5pub mod distance;
6pub mod field;
7pub mod index;
8pub mod size;
9
10#[cfg(test)]
11mod tests;
12
13use crate::city::{City, CitySearch};
14use crate::continent::coord::Coord;
15use crate::continent::distance::Distance;
16use crate::continent::field::Field;
17use crate::continent::index::{ContinentIndex, ContinentKey};
18use crate::continent::size::ContinentSize;
19use crate::error::{Error, Result};
20use crate::ruler::Ruler;
21use serde::{Deserialize, Serialize};
22
23#[derive(Clone, Debug, Deserialize, Serialize)]
24#[serde(rename_all = "camelCase")]
25#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
26pub struct Continent {
27 fields: Box<[Field]>,
28 size: ContinentSize,
29}
30
31impl Continent {
32 pub(crate) fn new(size: u8) -> Self {
33 let size = ContinentSize::new(size);
34 let capacity = usize::from(size.get()).pow(2);
35 let mut fields = Vec::with_capacity(capacity);
36 fields.resize_with(capacity, Field::default);
37
38 Self {
39 fields: fields.into_boxed_slice(),
40 size,
41 }
42 }
43
44 #[inline]
45 pub fn size(&self) -> ContinentSize {
46 self.size
47 }
48
49 #[inline]
50 pub fn radius(&self) -> u8 {
51 self.size.get().div_ceil(2)
52 }
53
54 #[inline]
55 pub fn center(&self) -> Coord {
56 Coord::splat(self.radius())
57 }
58
59 pub fn field(&self, key: impl ContinentKey) -> Result<&Field> {
60 let index = key.into_index(self.size);
61 self
62 .fields
63 .get(usize::from(index))
64 .ok_or(Error::IndexOutOfBounds(index))
65 }
66
67 pub(crate) fn field_mut(&mut self, key: impl ContinentKey) -> Result<&mut Field> {
68 let index = key.into_index(self.size);
69 self
70 .fields
71 .get_mut(usize::from(index))
72 .ok_or(Error::IndexOutOfBounds(index))
73 }
74
75 pub fn fields(&self) -> impl Iterator<Item = &Field> {
76 self.fields.iter()
77 }
78
79 fn fields_mut(&mut self) -> impl Iterator<Item = &mut Field> {
80 self.fields.iter_mut()
81 }
82
83 pub fn enumerate_fields(&self) -> impl Iterator<Item = (ContinentIndex, &Field)> {
84 self
85 .fields()
86 .enumerate()
87 .map(|(idx, field)| (ContinentIndex::new(idx), field))
88 }
89
90 pub fn city(&self, key: impl ContinentKey) -> Result<&City> {
91 let index = key.into_index(self.size);
92 if let Some(city) = self.field(index)?.city() {
93 Ok(city)
94 } else {
95 let coord = index.to_coord(self.size)?;
96 Err(Error::CityNotFound(coord))
97 }
98 }
99
100 pub fn city_mut(&mut self, key: impl ContinentKey) -> Result<&mut City> {
101 let size = self.size;
102 let index = key.into_index(size);
103 if let Some(city) = self.field_mut(index)?.city_mut() {
104 Ok(city)
105 } else {
106 let coord = index.to_coord(size)?;
107 Err(Error::CityNotFound(coord))
108 }
109 }
110
111 pub fn cities(&self) -> impl Iterator<Item = &City> {
112 self.fields().filter_map(Field::city)
113 }
114
115 pub fn cities_mut(&mut self) -> impl Iterator<Item = &mut City> {
116 self.fields_mut().filter_map(Field::city_mut)
117 }
118
119 pub fn cities_by<F>(&self, f: F) -> impl Iterator<Item = &City>
120 where
121 F: Fn(&City) -> bool,
122 {
123 self.cities().filter(move |city| f(city))
124 }
125
126 pub fn cities_of<R>(&self, owner: R) -> impl Iterator<Item = &City>
127 where
128 R: Into<Ruler>,
129 {
130 let owner: Ruler = owner.into();
131 self.cities_by(move |city| city.owner() == &owner)
132 }
133
134 pub fn cities_within(&self, origin: Coord, distance: Distance) -> impl Iterator<Item = &City> {
135 self.cities_by(move |city| origin.is_within_distance(city.coord(), distance))
136 }
137
138 pub fn coords_by<F>(&self, f: F) -> impl Iterator<Item = Coord>
139 where
140 F: Fn(&City) -> bool,
141 {
142 self.cities_by(f).map(City::coord)
143 }
144
145 pub fn coords_of<R>(&self, owner: R) -> impl Iterator<Item = Coord>
146 where
147 R: Into<Ruler>,
148 {
149 let owner: Ruler = owner.into();
150 self.coords_by(move |city| city.owner() == &owner)
151 }
152
153 pub fn city_coords(&self) -> impl Iterator<Item = Coord> {
154 self.cities().map(City::coord)
155 }
156
157 pub fn owner_of(&self, key: impl ContinentKey) -> Result<&Ruler> {
158 self.city(key).map(City::owner)
159 }
160
161 pub fn search<S>(&self, search: S) -> Result<Vec<&City>>
162 where
163 S: Into<CitySearch>,
164 {
165 let mut found = Vec::new();
166 let mut search: CitySearch = search.into();
167 search.name = search
168 .name
169 .into_iter()
170 .map(|name| name.trim().to_lowercase().into())
171 .collect();
172
173 'outer: for city in self.cities() {
174 if search.coord.contains(&city.coord()) {
175 found.push(city);
176 continue;
177 }
178
179 if !search.name.is_empty() {
180 let city_name = city.name().to_lowercase();
181 for name in &search.name {
182 if city_name.contains(name.as_str()) {
183 found.push(city);
184 continue 'outer;
185 }
186 }
187 }
188 }
189
190 Ok(found)
191 }
192}
193
194impl Default for Continent {
195 fn default() -> Self {
196 Self::new(ContinentSize::default().get())
197 }
198}