world_map_gen/
middle_gen.rs

1use rand;
2
3use self::rand::seq::SliceRandom;
4use self::rand::Rng;
5use crate::board::{Board, Pos};
6use crate::land::LandKind;
7use crate::slope::SlopeGen;
8use std::collections::HashSet;
9
10pub struct MiddleBoardGen<'a, R: Rng> {
11    rng: &'a mut R,
12    width: usize,
13    height: usize,
14    num_towns: usize,
15    min_distance: usize,
16    down_rate: u8,
17    num_tops: usize,
18}
19
20impl<'a, R: Rng> MiddleBoardGen<'a, R> {
21    pub fn new<'b: 'a>(rng: &'b mut R, width: usize, height: usize) -> Self {
22        let num_towns = width * height / 2048 + rng.gen_range(1, 4);
23        let both = width + height;
24        let min_distance = both.checked_div(num_towns).unwrap_or(both);
25        // Note: Standard value is 20 at 48x36 board
26        let down_rate = 12 + (48 * 36 * 8 / (width * height)) as u8;
27        let num_tops = 3 + both * rng.gen_range(3, 7) / (48 + 36);
28
29        MiddleBoardGen {
30            rng,
31            width,
32            height,
33            num_towns,
34            min_distance,
35            down_rate,
36            num_tops,
37        }
38    }
39
40    #[inline]
41    fn land_kind(altitude: u8) -> LandKind {
42        match altitude {
43            0..=10 => LandKind::Sea,
44            11..=40 => LandKind::Plain,
45            41..=70 => LandKind::Forest,
46            71..=99 => LandKind::Mountain,
47            _ => unreachable!(),
48        }
49    }
50
51    pub fn gen(&mut self) -> Board<'static> {
52        let mut slope = SlopeGen::new(
53            self.rng,
54            self.width,
55            self.height,
56            self.down_rate,
57            self.num_tops,
58        );
59        slope.gen();
60        let altitudes = slope.altitudes;
61        let tops = slope.tops;
62
63        let mut plains = Vec::new();
64        for (h, line) in altitudes.iter().enumerate() {
65            for (w, alt) in line.iter().enumerate() {
66                if Self::land_kind(*alt) == LandKind::Plain {
67                    plains.push(Pos { x: w, y: h });
68                }
69            }
70        }
71        plains.as_mut_slice().shuffle(&mut self.rng);
72        let plains = plains;
73
74        let mut towns = HashSet::with_capacity(self.num_towns);
75
76        for g in plains.iter() {
77            if towns.len() == self.num_towns {
78                break;
79            }
80            if towns
81                .iter()
82                .all(|p: &Pos| p.move_cost(g) > self.min_distance)
83            {
84                towns.insert(*g);
85            }
86        }
87        let towns = towns;
88
89        Board::build(self.width, self.height, |w, h| {
90            let alt = altitudes[h][w];
91            let p = Pos { x: w, y: h };
92            if tops.contains(&p) {
93                LandKind::Top.preset(alt)
94            } else if towns.contains(&p) {
95                LandKind::Town.preset(alt)
96            } else {
97                Self::land_kind(alt).preset(alt)
98            }
99        })
100    }
101}