1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//! Utility for creating procedurally generated maps
//!
//! # Quick Start
//! 
//! ```rust
//! use procedural_generation::Generator;
//! 
//! fn main() {
//!     Generator::new()
//!         .with_size(5, 10)
//!         .spawn_repeated(1, 5)
//!         .spawn_repeated(2, 3)
//!         .show();
//! }
//! ```
//!
//! Produces the following:
//!
//! ```bash
//! [0, 0, 0, 0, 0]
//! [2, 2, 0, 0, 0]
//! [0, 0, 0, 0, 0]
//! [0, 0, 0, 0, 1]
//! [1, 0, 0, 0, 0]
//! [0, 0, 0, 0, 1]
//! [1, 2, 2, 0, 0]
//! [1, 2, 2, 2, 2]
//! [0, 0, 2, 2, 2]
//! [0, 0, 0, 2, 2]
//! ```

use rand::prelude::*;
use rand::rngs::ThreadRng;

#[derive(Debug, Default)]
pub struct Generator {
    pub map: Vec<usize>,
    pub width: usize,
    pub height: usize,
    pub rng: ThreadRng,
}

impl Generator {
    pub fn new() -> Self {
        Self::default()
    }
    pub fn with_size(mut self, width: usize, height: usize) -> Self {
        self.map = vec![0; width * height];
        self.width = width;
        self.height = height;
        self
    }
    pub fn show(&self) {
        for i in 0..self.height {
            let anchor = i * self.width;
            let slice = &self.map[anchor..anchor + self.width];
            println!("{:?}", slice);
        }
    }
    pub fn spawn(&mut self, number: usize) -> &mut Self {
        let start = self.rng.gen_range(0, self.map.len());
        self.map[start] = number;
        self.populate(start, 0.5);
        self
    }
    pub fn spawn_repeated(&mut self, number: usize, repeats: usize) -> &mut Self {
        for _ in 0..repeats {
            self.spawn(number);
        }
        self
    }
    pub fn populate(&mut self, start: usize, probability: f64) {
        let number = self.map[start];
        let candidates = vec![start.saturating_sub(1), start + 1, start.saturating_sub(self.width), start + self.width];
        for candidate in candidates {
            let remainder = candidate % self.width;
            if candidate > 0 && candidate < self.map.len() && remainder > 0 && remainder < self.width {
                let should_spawn = self.rng.gen_bool(probability);
                if should_spawn {
                    self.map[candidate] = number;
                    self.populate(candidate, probability / 2.);
                }
            }
        }
    }
}