babalcore/level.rs
1use std::collections::VecDeque;
2
3use super::consts::*;
4use super::level_query::*;
5use super::row::*;
6use super::row_generator::*;
7use super::skill::*;
8use super::slab_kind::*;
9
10/// Contains the level definition, where are slabs, and of what type.
11pub struct Level {
12 seed: u64,
13 rows: VecDeque<Row>,
14 row0: isize,
15 w: usize,
16 skl: Skill,
17 now_msec: i64,
18}
19
20impl Level {
21 /// Create a new level, with a given size
22 ///
23 /// # Examples
24 ///
25 /// ```
26 /// use babalcore::*;
27 ///
28 /// let _level = Level::new(0, Skill::Easy);
29 /// ```
30 pub fn new(seed: u64, skill: Skill) -> Level {
31 let width = skill.width();
32 let w = if width > WIDTH_MAX as usize {
33 WIDTH_MAX
34 } else {
35 width
36 };
37 Level {
38 seed,
39 rows: VecDeque::with_capacity(2 * SLIDING_LENGTH_MAX),
40 row0: 0,
41 w,
42 skl: skill,
43 now_msec: 0,
44 }
45 }
46
47 /// Push a row at the end of the level.
48 ///
49 /// # Examples
50 ///
51 /// ```
52 /// use babalcore::*;
53 ///
54 /// let mut level = Level::new(0, Skill::Easy);
55 /// level.push(Row::new(SlabDef::Floor));
56 /// assert_eq!(1, level.len());
57 /// ```
58 pub fn push(&mut self, row: Row) {
59 if self.rows.len() >= ABSOLUTE_LENGTH_MAX {
60 return;
61 }
62 self.rows.push_back(row);
63 while self.rows.len() > SLIDING_LENGTH_MAX {
64 self.row0 += 1;
65 self.rows.pop_front();
66 }
67 }
68
69 /// Generate new rows and append them to the level.
70 ///
71 /// # Examples
72 ///
73 /// ```
74 /// use babalcore::*;
75 ///
76 /// let mut level = Level::new(0, Skill::Easy);
77 /// level.generate(2);
78 /// assert_eq!(2, level.len());
79 /// level.generate(3);
80 /// assert_eq!(5, level.len());
81 /// ```
82 pub fn generate(&mut self, n: usize) {
83 if self.rows.len() >= ABSOLUTE_LENGTH_MAX {
84 return;
85 }
86 for _ in 0..n {
87 let j = self.rows.len();
88 let row = if j == 0 {
89 generate_first_row(self.seed, self.w, self.skl)
90 } else {
91 generate_next_row(self.seed, j, &self.rows[j - 1], self.skl)
92 };
93 self.push(row);
94 }
95 }
96
97 /// Get a level content, at a given time.
98 ///
99 /// # Examples
100 ///
101 /// ```
102 /// use babalcore::*;
103 ///
104 /// let mut level = Level::new(0, Skill::Easy);
105 /// level.generate(2);
106 /// assert_eq!(SlabKind::Floor, level.slab_kind(5, 1, false));
107 /// ```
108 pub fn slab_kind(&self, col: isize, row: isize, boost: bool) -> SlabKind {
109 let real_row = row + self.row0;
110 if col < 0 || col >= WIDTH_MAX as isize {
111 return SlabKind::Void;
112 }
113 if real_row < 0 || real_row >= self.len() as isize {
114 return SlabKind::Void;
115 }
116 match self.rows[real_row as usize].slab_kind(col, self.now_msec) {
117 SlabKind::Boost => {
118 if boost {
119 SlabKind::Overdrive
120 } else {
121 SlabKind::Boost
122 }
123 }
124 v => v,
125 }
126 }
127
128 pub fn set_now_msec(&mut self, now_msec: i64) {
129 self.now_msec = now_msec;
130 }
131
132 pub fn set_now_sec(&mut self, now_sec: f64) {
133 self.now_msec = (now_sec * 1000.0) as i64;
134 }
135}
136
137impl LevelQuery for Level {
138 /// Get the level width. It is fixed for a given game session.
139 ///
140 /// # Examples
141 ///
142 /// ```
143 /// use babalcore::*;
144 ///
145 /// let level = Level::new(0, Skill::Easy);
146 /// assert_eq!(9, level.width());
147 /// ```
148 fn width(&self) -> usize {
149 self.w
150 }
151
152 /// Get the level length. Length typically increases as
153 /// the ball rolls on. However it is capped at some point
154 /// and history at the beginning of the level is removed.
155 /// View this as a sliding window.
156 ///
157 /// # Examples
158 ///
159 /// ```
160 /// use babalcore::*;
161 ///
162 /// let level = Level::new(0, Skill::Easy);
163 /// assert_eq!(0, level.len());
164 /// ```
165 fn len(&self) -> usize {
166 self.rows.len()
167 }
168
169 /// Get the level first row, the row with the lowest index.
170 ///
171 /// # Examples
172 ///
173 /// ```
174 /// use babalcore::*;
175 ///
176 /// let level = Level::new(0, Skill::Easy);
177 /// assert_eq!(0, level.first());
178 /// ```
179 fn first(&self) -> isize {
180 self.row0
181 }
182
183 /// Get the level last row, more precisely its index plus one.
184 /// So if last is equal to first, then len is 0 an there are no rows at all.
185 ///
186 /// # Examples
187 ///
188 /// ```
189 /// use babalcore::*;
190 ///
191 /// let level = Level::new(0, Skill::Easy);
192 /// assert_eq!(0, level.last());
193 /// ```
194 fn last(&self) -> isize {
195 self.row0 + self.rows.len() as isize
196 }
197
198 /// Get a level content, returns a plain integer.
199 ///
200 /// # Examples
201 ///
202 /// ```
203 /// use babalcore::*;
204 ///
205 /// let mut level = Level::new(0, Skill::Easy);
206 /// level.generate(2);
207 /// assert_eq!(0, level.item(5, 1, false));
208 /// ```
209 fn item(&self, col: isize, row: isize, boost: bool) -> i64 {
210 self.slab_kind(col, self.row0 + row, boost).as_item()
211 }
212
213 /// Find a starting spot on a level.
214 ///
215 /// # Examples
216 ///
217 /// ```
218 /// use babalcore::*;
219 ///
220 /// let mut level = Level::new(42, Skill::Easy);
221 /// assert_eq!(None, level.find_start_spot(2, 1));
222 /// level.generate(100);
223 /// assert_eq!(Some((2,1)), level.find_start_spot(2, 1));
224 /// ```
225 fn find_start_spot(&self, col: isize, row: isize) -> Option<(isize, isize)> {
226 let r = std::cmp::max(self.first(), std::cmp::min(self.last() - 1, row));
227 let c = std::cmp::max(0, std::cmp::min(self.width() as isize - 1, col));
228
229 // Check if the curent spot is OK, if yes, use this.
230 if self.slab_kind(c, r, false) == SlabKind::Floor {
231 return Some((c, r));
232 }
233
234 // Ok, it did not work out, find another one by walking
235 // all the level down to first row.
236 let mut r2 = r;
237 let width = self.width() as isize;
238 let mut d = 1;
239 while r2 >= self.first() {
240 let limit = if r2 == self.first() {
241 // At the beginning of the level, try any slab
242 width
243 } else {
244 // Try to stay in the same lane, as much as possible.
245 std::cmp::min(d, width)
246 };
247 for c2 in 0..limit {
248 let c3 = if c < self.width() as isize / 2 {
249 (c + c2) % width
250 } else {
251 (width + c - c2) % width
252 };
253 if self.slab_kind(c3, r2, false) == SlabKind::Floor {
254 return Some((c3, r2));
255 }
256 }
257 r2 -= 1;
258 d += 1;
259 }
260 None
261 }
262
263 /// Get the level skill.
264 ///
265 /// # Examples
266 ///
267 /// ```
268 /// use babalcore::*;
269 ///
270 /// let level = Level::new(42, Skill::Easy);
271 /// assert_eq!(Skill::Easy, level.skill());
272 /// ```
273 fn skill(&self) -> Skill {
274 self.skl
275 }
276}
277
278impl std::fmt::Display for Level {
279 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
280 let str_list: Vec<String> = self
281 .rows
282 .iter()
283 .map(|x| format!("{}", x)[0..self.w].to_string())
284 .collect();
285 write!(f, "{}", str_list.join("\n"))
286 }
287}
288
289#[cfg(test)]
290mod tests {
291 use crate::*;
292 use std::collections::HashMap;
293
294 #[test]
295 fn test_fmt() {
296 assert_eq!("", format!("{}", Level::new(0, Skill::Easy)));
297 let mut lvl = Level::new(0, Skill::Noob);
298 lvl.generate(3);
299 assert_eq!("########\n########\n########", format!("{}", lvl));
300 }
301
302 #[test]
303 fn test_sliding_length_max() {
304 let mut lvl = Level::new(0, Skill::Noob);
305 assert_eq!(0, lvl.len());
306 let row = Row::new(SlabDef::Floor);
307 lvl.generate(SLIDING_LENGTH_MAX - 1);
308 assert_eq!(SLIDING_LENGTH_MAX - 1, lvl.len());
309 lvl.generate(2);
310 assert_eq!(SLIDING_LENGTH_MAX, lvl.len());
311 assert_eq!(1, lvl.first());
312 assert_eq!((SLIDING_LENGTH_MAX + 1) as isize, lvl.last());
313 lvl.push(row);
314 assert_eq!(SLIDING_LENGTH_MAX, lvl.len());
315 assert_eq!(2, lvl.first());
316 assert_eq!((SLIDING_LENGTH_MAX + 2) as isize, lvl.last());
317 }
318
319 #[test]
320 fn test_generate() {
321 const L: usize = 3;
322 let mut expected = HashMap::<u64, &str>::new();
323 expected.insert(0, "### ######\n####### ##\n#^##### ##");
324 expected.insert(123, "# ## # ##\n^ ## #^##\n### # ##");
325
326 for (key, value) in expected.into_iter() {
327 let mut lvl = Level::new(key, Skill::Medium);
328 lvl.generate(L);
329 assert_eq!(L, lvl.len());
330 assert_eq!(value, format!("{}", lvl));
331 }
332 }
333}