world_map_gen/
middle_gen.rs1use 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 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}