rust/landgen/src/wavefront_collapse/wavefront_collapse.rs
author unC0Rr
Thu, 02 Feb 2023 08:41:31 +0100
branchtransitional_engine
changeset 15945 8f093b1b18bc
parent 15943 c5684cc62de8
child 15946 e82de0410da5
permissions -rw-r--r--
Add loading of tiles from png
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
     1
use integral_geometry::Size;
15945
8f093b1b18bc Add loading of tiles from png
unC0Rr
parents: 15943
diff changeset
     2
use std::collections::HashMap;
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
     3
use vec2d::Vec2D;
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
     4
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
     5
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
     6
pub enum Tile {
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
     7
    Empty,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
     8
    Outside,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
     9
    Numbered(u32),
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    10
}
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    11
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    12
impl Tile {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    13
    fn is(&self, i: u32) -> bool {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    14
        *self == Tile::Numbered(i)
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    15
    }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    16
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    17
    fn is_empty(&self) -> bool {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    18
        match self {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    19
            Tile::Empty => true,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    20
            Tile::Outside => true,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    21
            _ => false,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    22
        }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    23
    }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    24
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    25
    fn is_empty_or(&self, i: u32) -> bool {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    26
        match self {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    27
            Tile::Numbered(n) => *n == i,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    28
            Tile::Empty => true,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    29
            _ => false,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    30
        }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    31
    }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    32
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    33
    fn is_void_or(&self, i: u32) -> bool {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    34
        match self {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    35
            Tile::Numbered(n) => *n == i,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    36
            _ => true,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    37
        }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    38
    }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    39
}
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    40
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    41
impl Default for Tile {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    42
    fn default() -> Self {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    43
        Tile::Outside
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    44
    }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    45
}
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    46
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    47
struct CollapseRule {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    48
    to_tile: Tile,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    49
    condition: fn([Tile; 4]) -> bool,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    50
}
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    51
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    52
#[derive(Default)]
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
    53
pub struct WavefrontCollapse {
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    54
    rules: HashMap<Tile, Vec<CollapseRule>>,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    55
}
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    56
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    57
impl WavefrontCollapse {
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
    58
    pub fn generate_map<I: Iterator<Item = u32>, F: FnOnce(&mut Vec2D<Tile>)>(
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    59
        &mut self,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    60
        map_size: &Size,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    61
        seed_fn: F,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    62
        random_numbers: &mut I,
15945
8f093b1b18bc Add loading of tiles from png
unC0Rr
parents: 15943
diff changeset
    63
    ) -> Vec2D<Tile> {
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
    64
        let mut land = Vec2D::new(&map_size, Tile::Empty);
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    65
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    66
        seed_fn(&mut land);
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    67
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    68
        while self.collapse_step(&mut land, random_numbers) {}
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    69
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    70
        land
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    71
    }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    72
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    73
    fn add_rule(&mut self, from_tile: Tile, to_tile: Tile, condition: fn([Tile; 4]) -> bool) {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    74
        let rule = CollapseRule { to_tile, condition };
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    75
        self.rules
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    76
            .entry(from_tile)
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    77
            .or_insert_with(Vec::new)
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    78
            .push(rule);
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    79
    }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    80
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    81
    fn collapse_step<I: Iterator<Item = u32>>(
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    82
        &self,
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
    83
        land: &mut Vec2D<Tile>,
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    84
        random_numbers: &mut I,
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    85
    ) -> bool {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    86
        let mut collapse_occurred = false;
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    87
        for x in 0..land.width() {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    88
            for y in 0..land.height() {
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
    89
                let current_tile = land.get(y, x).expect("valid iteration range");
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    90
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    91
                if let Some(rules) = self.rules.get(&current_tile) {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    92
                    for rule in rules
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    93
                        .iter()
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    94
                        .cycle()
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    95
                        .skip(
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    96
                            random_numbers.next().unwrap_or_default() as usize % (rules.len() + 1),
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    97
                        )
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    98
                        .take(rules.len())
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
    99
                    {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   100
                        let neighbors = self.get_neighbors(&land, x, y);
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   101
                        let have_neighbors = neighbors.iter().any(|t| !t.is_empty());
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   102
                        if have_neighbors && (rule.condition)(neighbors) {
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
   103
                            *land.get_mut(y, x).expect("valid iteration range") = rule.to_tile;
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   104
                            collapse_occurred = true;
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   105
                            break;
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   106
                        }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   107
                    }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   108
                }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   109
            }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   110
        }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   111
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   112
        collapse_occurred
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   113
    }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   114
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
   115
    fn get_neighbors(&self, land: &Vec2D<Tile>, x: usize, y: usize) -> [Tile; 4] {
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   116
        [
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
   117
            land.get(y, x + 1).map(|p| *p).unwrap_or_default(),
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
   118
            land.get(y + 1, x).map(|p| *p).unwrap_or_default(),
15945
8f093b1b18bc Add loading of tiles from png
unC0Rr
parents: 15943
diff changeset
   119
            land.get(y, x.wrapping_sub(1))
8f093b1b18bc Add loading of tiles from png
unC0Rr
parents: 15943
diff changeset
   120
                .map(|p| *p)
8f093b1b18bc Add loading of tiles from png
unC0Rr
parents: 15943
diff changeset
   121
                .unwrap_or_default(),
8f093b1b18bc Add loading of tiles from png
unC0Rr
parents: 15943
diff changeset
   122
            land.get(y.wrapping_sub(1), x)
8f093b1b18bc Add loading of tiles from png
unC0Rr
parents: 15943
diff changeset
   123
                .map(|p| *p)
8f093b1b18bc Add loading of tiles from png
unC0Rr
parents: 15943
diff changeset
   124
                .unwrap_or_default(),
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   125
        ]
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   126
    }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   127
}
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   128
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   129
#[cfg(test)]
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   130
mod tests {
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
   131
    use super::{Tile, WavefrontCollapse};
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   132
    use integral_geometry::Size;
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
   133
    use vec2d::Vec2D;
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   134
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   135
    #[test]
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   136
    fn test_wavefront_collapse() {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   137
        let size = Size::new(4, 4);
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   138
        let mut rnd = [0u32; 64].into_iter();
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   139
        let mut wfc = WavefrontCollapse::default();
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   140
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
   141
        let empty_land = Vec2D::new(&size, Tile::Empty);
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
   142
        let no_rules_land = wfc.generate_map(&size, |_| {}, &mut rnd);
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   143
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
   144
        assert_eq!(empty_land.as_slice(), no_rules_land.as_slice());
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   145
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   146
        wfc.add_rule(Tile::Empty, Tile::Numbered(0), |neighbors| {
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   147
            neighbors.iter().filter(|&n| *n == Tile::Empty).count() >= 2
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   148
        });
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
   149
        let ruled_map = wfc.generate_map(&size, |_| {}, &mut rnd);
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   150
15943
c5684cc62de8 Switch to Vec2D in wavefront algorithm
unC0Rr
parents: 15942
diff changeset
   151
        assert_eq!(ruled_map.as_slice(), empty_land.as_slice());
15942
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   152
    }
6e22f4390b7e Add basics of wavefront collapse algorithm
unC0Rr
parents:
diff changeset
   153
}