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}