micro_games_kit/
pcg.rs

1use noise::NoiseFn;
2use std::ops::{Add, Div, Mul, Range, Sub};
3use vek::{Mat4, Vec2};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum GridDirection {
7    North,
8    NorthEast,
9    East,
10    SouthEast,
11    South,
12    SouthWest,
13    West,
14    NorthWest,
15}
16
17#[derive(Clone)]
18pub struct Grid<T: Copy> {
19    size: Vec2<usize>,
20    buffer: Vec<T>,
21}
22
23impl<T: Copy> Grid<T> {
24    pub fn new(size: Vec2<usize>, fill_value: T) -> Self {
25        Self {
26            size,
27            buffer: vec![fill_value; size.x * size.y],
28        }
29    }
30
31    pub fn with_buffer(size: Vec2<usize>, buffer: Vec<T>) -> Option<Self> {
32        if buffer.len() == size.x * size.y {
33            Some(Self { size, buffer })
34        } else {
35            None
36        }
37    }
38
39    pub fn generate(size: Vec2<usize>, generator: impl GridGenetator<T>) -> Self
40    where
41        T: Default,
42    {
43        let mut result = Self::new(size, Default::default());
44        result.apply_all(generator);
45        result
46    }
47
48    pub fn fork(&self, fill_value: T) -> Self {
49        Self {
50            size: self.size,
51            buffer: vec![fill_value; self.size.x * self.size.y],
52        }
53    }
54
55    pub fn fork_generate(&self, generator: impl GridGenetator<T>) -> Self {
56        let mut result = self.clone();
57        result.apply_all(generator);
58        result
59    }
60
61    pub fn apply(
62        &mut self,
63        from: impl Into<Vec2<usize>>,
64        to: impl Into<Vec2<usize>>,
65        mut generator: impl GridGenetator<T>,
66    ) {
67        if self.buffer.is_empty() {
68            return;
69        }
70        let from = from.into();
71        let to = to.into();
72        for y in from.y..to.y {
73            for x in from.x..to.x {
74                let location = Vec2::new(x, y);
75                let index = self.index(location);
76                self.buffer[index] =
77                    generator.generate(location, self.size, self.buffer[index], self);
78            }
79        }
80    }
81
82    pub fn apply_all(&mut self, generator: impl GridGenetator<T>) {
83        self.apply(0, self.size, generator);
84    }
85
86    pub fn map<U: Copy>(&self, mut f: impl FnMut(Vec2<usize>, Vec2<usize>, T) -> U) -> Grid<U> {
87        Grid {
88            size: self.size,
89            buffer: self
90                .buffer
91                .iter()
92                .enumerate()
93                .map(|(index, value)| f(self.location(index), self.size, *value))
94                .collect(),
95        }
96    }
97
98    pub fn into_inner(self) -> (Vec2<usize>, Vec<T>) {
99        (self.size, self.buffer)
100    }
101
102    pub fn size(&self) -> Vec2<usize> {
103        self.size
104    }
105
106    pub fn buffer(&self) -> &[T] {
107        &self.buffer
108    }
109
110    pub fn buffer_mut(&mut self) -> &mut [T] {
111        &mut self.buffer
112    }
113
114    pub fn iter(&self) -> impl Iterator<Item = (Vec2<usize>, usize, T)> + '_ {
115        self.buffer
116            .iter()
117            .copied()
118            .enumerate()
119            .map(|(index, value)| (self.location(index), index, value))
120    }
121
122    pub fn index(&self, location: impl Into<Vec2<usize>>) -> usize {
123        let location = location.into();
124        (location.y % self.size.y) * self.size.x + (location.x % self.size.x)
125    }
126
127    pub fn location(&self, index: usize) -> Vec2<usize> {
128        Vec2 {
129            x: index % self.size.x,
130            y: (index / self.size.y) % self.size.y,
131        }
132    }
133
134    pub fn location_offset(
135        &self,
136        mut location: Vec2<usize>,
137        direction: GridDirection,
138        distance: usize,
139    ) -> Option<Vec2<usize>> {
140        if distance == 0 {
141            return None;
142        }
143        match direction {
144            GridDirection::North => {
145                if let Some(y) = location.y.checked_sub(distance) {
146                    location.y = y;
147                } else {
148                    return None;
149                }
150            }
151            GridDirection::NorthEast => {
152                if location.x + distance < self.size.x {
153                    location.x += distance;
154                } else {
155                    return None;
156                }
157                if let Some(y) = location.y.checked_sub(distance) {
158                    location.y = y;
159                } else {
160                    return None;
161                }
162            }
163            GridDirection::East => {
164                if location.x + distance < self.size.x {
165                    location.x += distance;
166                } else {
167                    return None;
168                }
169            }
170            GridDirection::SouthEast => {
171                if location.x + distance < self.size.x {
172                    location.x += distance;
173                } else {
174                    return None;
175                }
176                if location.y + distance < self.size.y {
177                    location.y += distance;
178                } else {
179                    return None;
180                }
181            }
182            GridDirection::South => {
183                if location.y + distance < self.size.y {
184                    location.y += distance;
185                } else {
186                    return None;
187                }
188            }
189            GridDirection::SouthWest => {
190                if let Some(x) = location.x.checked_sub(distance) {
191                    location.x = x;
192                } else {
193                    return None;
194                }
195                if location.y + distance < self.size.y {
196                    location.y += distance;
197                } else {
198                    return None;
199                }
200            }
201            GridDirection::West => {
202                if let Some(x) = location.x.checked_sub(distance) {
203                    location.x = x;
204                } else {
205                    return None;
206                }
207            }
208            GridDirection::NorthWest => {
209                if let Some(x) = location.x.checked_sub(distance) {
210                    location.x = x;
211                } else {
212                    return None;
213                }
214                if let Some(y) = location.y.checked_sub(distance) {
215                    location.y = y;
216                } else {
217                    return None;
218                }
219            }
220        }
221        Some(location)
222    }
223
224    pub fn neighbors(
225        &self,
226        location: impl Into<Vec2<usize>>,
227        range: Range<usize>,
228    ) -> impl Iterator<Item = (GridDirection, Vec2<usize>, T)> + '_ {
229        let location = location.into();
230        range.flat_map(move |distance| {
231            [
232                GridDirection::North,
233                GridDirection::NorthEast,
234                GridDirection::East,
235                GridDirection::SouthEast,
236                GridDirection::South,
237                GridDirection::SouthWest,
238                GridDirection::West,
239                GridDirection::NorthWest,
240            ]
241            .into_iter()
242            .filter_map(move |direction| {
243                let location = self.location_offset(location, direction, distance)?;
244                Some((direction, location, self.get(location)?))
245            })
246        })
247    }
248
249    pub fn get(&self, location: impl Into<Vec2<usize>>) -> Option<T> {
250        let index = self.index(location);
251        self.buffer.get(index).copied()
252    }
253
254    pub fn set(&mut self, location: impl Into<Vec2<usize>>, value: T) {
255        let index = self.index(location);
256        if let Some(item) = self.buffer.get_mut(index) {
257            *item = value;
258        }
259    }
260}
261
262pub trait GridGenetator<T: Copy> {
263    fn generate(
264        &mut self,
265        location: Vec2<usize>,
266        size: Vec2<usize>,
267        current: T,
268        grid: &Grid<T>,
269    ) -> T;
270}
271
272impl<T: Copy, F: FnMut(Vec2<usize>, Vec2<usize>, T) -> T> GridGenetator<T> for F {
273    fn generate(&mut self, location: Vec2<usize>, size: Vec2<usize>, current: T, _: &Grid<T>) -> T {
274        self(location, size, current)
275    }
276}
277
278pub struct ConstGenerator<T: Copy>(pub T);
279
280impl<T: Copy> GridGenetator<T> for ConstGenerator<T> {
281    fn generate(&mut self, _: Vec2<usize>, _: Vec2<usize>, _: T, _: &Grid<T>) -> T {
282        self.0
283    }
284}
285
286pub struct OffsetLocationGenerator<'a, T: Copy> {
287    pub generator: &'a mut dyn GridGenetator<T>,
288    pub offsets: &'a Grid<Vec2<isize>>,
289}
290
291impl<T: Copy> GridGenetator<T> for OffsetLocationGenerator<'_, T> {
292    fn generate(
293        &mut self,
294        mut location: Vec2<usize>,
295        size: Vec2<usize>,
296        current: T,
297        grid: &Grid<T>,
298    ) -> T {
299        let offset = self.offsets.get(location).unwrap_or_default();
300        if offset.x >= 0 {
301            location.x = (location.x + offset.x as usize) % size.x;
302        } else {
303            location.x = (location.x + size.x - offset.x.unsigned_abs() % size.x) % size.x;
304        }
305        if offset.y >= 0 {
306            location.y = (location.y + offset.y as usize) % size.y;
307        } else {
308            location.y = (location.y + size.y - offset.y.unsigned_abs() % size.y) % size.y;
309        }
310        self.generator.generate(location, size, current, grid)
311    }
312}
313
314pub struct NoiseGenerator<T: NoiseFn<f64, 2>> {
315    pub noise: T,
316    pub transform: Mat4<f64>,
317}
318
319impl<T: NoiseFn<f64, 2>> NoiseGenerator<T> {
320    pub fn new(noise: T) -> Self {
321        Self {
322            noise,
323            transform: Mat4::identity(),
324        }
325    }
326
327    pub fn transform(mut self, transform: Mat4<f64>) -> Self {
328        self.transform = transform;
329        self
330    }
331}
332
333impl<T: NoiseFn<f64, 2>> GridGenetator<f64> for NoiseGenerator<T> {
334    fn generate(&mut self, location: Vec2<usize>, _: Vec2<usize>, _: f64, _: &Grid<f64>) -> f64 {
335        let point = self.transform.mul_point(Vec2 {
336            x: location.x as f64,
337            y: location.y as f64,
338        });
339        self.noise.get(point.into_array())
340    }
341}
342
343pub struct CopyGenerator<'a, T: Copy> {
344    pub other: &'a Grid<T>,
345}
346
347impl<T: Copy + Add<Output = T> + Default> GridGenetator<T> for CopyGenerator<'_, T> {
348    fn generate(&mut self, location: Vec2<usize>, _: Vec2<usize>, _: T, _: &Grid<T>) -> T {
349        self.other.get(location).unwrap_or_default()
350    }
351}
352
353pub struct AddGenerator<'a, T: Copy> {
354    pub other: &'a Grid<T>,
355}
356
357impl<T: Copy + Add<Output = T> + Default> GridGenetator<T> for AddGenerator<'_, T> {
358    fn generate(&mut self, location: Vec2<usize>, _: Vec2<usize>, current: T, _: &Grid<T>) -> T {
359        current + self.other.get(location).unwrap_or_default()
360    }
361}
362
363pub struct SubGenerator<'a, T: Copy> {
364    pub other: &'a Grid<T>,
365}
366
367impl<T: Copy + Sub<Output = T> + Default> GridGenetator<T> for SubGenerator<'_, T> {
368    fn generate(&mut self, location: Vec2<usize>, _: Vec2<usize>, current: T, _: &Grid<T>) -> T {
369        current - self.other.get(location).unwrap_or_default()
370    }
371}
372
373pub struct MulGenerator<'a, T: Copy> {
374    pub other: &'a Grid<T>,
375}
376
377impl<T: Copy + Mul<Output = T> + Default> GridGenetator<T> for MulGenerator<'_, T> {
378    fn generate(&mut self, location: Vec2<usize>, _: Vec2<usize>, current: T, _: &Grid<T>) -> T {
379        current * self.other.get(location).unwrap_or_default()
380    }
381}
382
383pub struct DivGenerator<'a, T: Copy> {
384    pub other: &'a Grid<T>,
385}
386
387impl<T: Copy + Div<Output = T> + Default> GridGenetator<T> for DivGenerator<'_, T> {
388    fn generate(&mut self, location: Vec2<usize>, _: Vec2<usize>, current: T, _: &Grid<T>) -> T {
389        current / self.other.get(location).unwrap_or_default()
390    }
391}
392
393pub struct MinGenerator<'a, T: Copy> {
394    pub other: &'a Grid<T>,
395}
396
397impl<T: Copy + Div<Output = T> + Ord + Default> GridGenetator<T> for MinGenerator<'_, T> {
398    fn generate(&mut self, location: Vec2<usize>, _: Vec2<usize>, current: T, _: &Grid<T>) -> T {
399        current.min(self.other.get(location).unwrap_or_default())
400    }
401}
402
403pub struct MaxGenerator<'a, T: Copy> {
404    pub other: &'a Grid<T>,
405}
406
407impl<T: Copy + Div<Output = T> + Ord + Default> GridGenetator<T> for MaxGenerator<'_, T> {
408    fn generate(&mut self, location: Vec2<usize>, _: Vec2<usize>, current: T, _: &Grid<T>) -> T {
409        current.max(self.other.get(location).unwrap_or_default())
410    }
411}
412
413pub struct ClampGenerator<T: Copy> {
414    pub min: T,
415    pub max: T,
416}
417
418impl<T: Copy + Ord + Default> GridGenetator<T> for ClampGenerator<T> {
419    fn generate(&mut self, _: Vec2<usize>, _: Vec2<usize>, current: T, _: &Grid<T>) -> T {
420        current.clamp(self.min, self.max)
421    }
422}
423
424pub struct RemapGenerator<T: Copy> {
425    pub from: Range<T>,
426    pub to: Range<T>,
427}
428
429impl<T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>>
430    GridGenetator<T> for RemapGenerator<T>
431{
432    fn generate(&mut self, _: Vec2<usize>, _: Vec2<usize>, current: T, _: &Grid<T>) -> T {
433        let factor = (current - self.from.start) / (self.from.end - self.from.start);
434        (self.to.end - self.to.start) * factor + self.to.start
435    }
436}
437
438pub enum ThresholdGenerator<'a, T: Copy> {
439    Constant {
440        threshold: T,
441        value_upper: T,
442        value_lower: T,
443    },
444    Samples {
445        thresholds: &'a Grid<T>,
446        value_upper: T,
447        value_lower: T,
448    },
449}
450
451impl<T: Copy + PartialOrd + Default> GridGenetator<T> for ThresholdGenerator<'_, T> {
452    fn generate(&mut self, location: Vec2<usize>, _: Vec2<usize>, current: T, _: &Grid<T>) -> T {
453        match self {
454            Self::Constant {
455                threshold,
456                value_upper,
457                value_lower,
458            } => {
459                if current > *threshold {
460                    *value_upper
461                } else {
462                    *value_lower
463                }
464            }
465            Self::Samples {
466                thresholds,
467                value_upper,
468                value_lower,
469            } => {
470                if current > thresholds.get(location).unwrap_or_default() {
471                    *value_upper
472                } else {
473                    *value_lower
474                }
475            }
476        }
477    }
478}
479
480pub struct Kernel33Generator<'a, T: Copy> {
481    pub other: &'a Grid<T>,
482    pub kernel: [T; 9],
483}
484
485impl<'a> Kernel33Generator<'a, f64> {
486    pub fn identity(other: &'a Grid<f64>) -> Self {
487        Self {
488            other,
489            kernel: [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0],
490        }
491    }
492
493    pub fn ridge(other: &'a Grid<f64>) -> Self {
494        Self {
495            other,
496            kernel: [0.0, -1.0, 0.0, -1.0, 4.0, -1.0, 0.0, -1.0, 0.0],
497        }
498    }
499
500    pub fn edge_detection(other: &'a Grid<f64>) -> Self {
501        Self {
502            other,
503            kernel: [-1.0, -1.0, -1.0, -1.0, 8.0, -1.0, -1.0, -1.0, -1.0],
504        }
505    }
506
507    pub fn sharpen(other: &'a Grid<f64>) -> Self {
508        Self {
509            other,
510            kernel: [0.0, -1.0, 0.0, -1.0, 5.0, -1.0, 0.0, -1.0, 0.0],
511        }
512    }
513
514    pub fn emboss(other: &'a Grid<f64>) -> Self {
515        Self {
516            other,
517            kernel: [-2.0, -1.0, 0.0, -1.0, 1.0, 1.0, 0.0, 1.0, 2.0],
518        }
519    }
520
521    pub fn box_blur(other: &'a Grid<f64>) -> Self {
522        Self {
523            other,
524            kernel: [
525                1.0 / 9.0,
526                1.0 / 9.0,
527                1.0 / 9.0,
528                1.0 / 9.0,
529                1.0 / 9.0,
530                1.0 / 9.0,
531                1.0 / 9.0,
532                1.0 / 9.0,
533                1.0 / 9.0,
534            ],
535        }
536    }
537
538    pub fn gaussian_blur(other: &'a Grid<f64>) -> Self {
539        Self {
540            other,
541            kernel: [
542                1.0 / 16.0,
543                2.0 / 16.0,
544                1.0 / 16.0,
545                2.0 / 16.0,
546                4.0 / 16.0,
547                2.0 / 16.0,
548                1.0 / 16.0,
549                2.0 / 16.0,
550                1.0 / 16.0,
551            ],
552        }
553    }
554}
555
556impl<T: Copy + Add<Output = T> + Mul<Output = T> + Default> GridGenetator<T>
557    for Kernel33Generator<'_, T>
558{
559    fn generate(&mut self, location: Vec2<usize>, size: Vec2<usize>, _: T, _: &Grid<T>) -> T {
560        let region = [
561            self.other
562                .get(location + Vec2::new(size.x - 1, size.y - 1))
563                .unwrap_or_default(),
564            self.other
565                .get(location + Vec2::new(0, size.y - 1))
566                .unwrap_or_default(),
567            self.other
568                .get(location + Vec2::new(1, size.y - 1))
569                .unwrap_or_default(),
570            self.other
571                .get(location + Vec2::new(size.x - 1, 0))
572                .unwrap_or_default(),
573            self.other.get(location).unwrap_or_default(),
574            self.other
575                .get(location + Vec2::new(1, 0))
576                .unwrap_or_default(),
577            self.other
578                .get(location + Vec2::new(size.x - 1, 1))
579                .unwrap_or_default(),
580            self.other
581                .get(location + Vec2::new(0, 1))
582                .unwrap_or_default(),
583            self.other
584                .get(location + Vec2::new(1, 1))
585                .unwrap_or_default(),
586        ];
587        region
588            .into_iter()
589            .zip(self.kernel)
590            .fold(Default::default(), |accumulator, (value, kernel)| {
591                value * kernel + accumulator
592            })
593    }
594}
595
596#[cfg(test)]
597mod tests {
598    use super::{
599        Grid, GridDirection, GridGenetator, Kernel33Generator, NoiseGenerator,
600        OffsetLocationGenerator, RemapGenerator, SubGenerator, ThresholdGenerator,
601    };
602    use image::{GrayImage, RgbImage};
603    use noise::{Fbm, MultiFractal, ScalePoint, SuperSimplex, Worley};
604    use vek::Vec2;
605
606    const SIZE: usize = 512;
607
608    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
609    enum Terrain {
610        Water,
611        Sand,
612        Grass,
613        Mountain,
614    }
615
616    fn gradient_generator(location: Vec2<usize>, size: Vec2<usize>, _: f64) -> f64 {
617        let center = size / 2;
618        let x = if location.x >= center.x {
619            location.x - center.x
620        } else {
621            center.x - location.x
622        } as f64;
623        let y = if location.y >= center.y {
624            location.y - center.y
625        } else {
626            center.y - location.y
627        } as f64;
628        let result = (x / center.x as f64).max(y / center.y as f64);
629        result * result
630    }
631
632    struct OffsetsGenerator<'a> {
633        pub source: &'a Grid<f64>,
634        pub scale: f64,
635    }
636
637    impl GridGenetator<Vec2<isize>> for OffsetsGenerator<'_> {
638        fn generate(
639            &mut self,
640            location: Vec2<usize>,
641            _: Vec2<usize>,
642            _: Vec2<isize>,
643            _: &Grid<Vec2<isize>>,
644        ) -> Vec2<isize> {
645            let left = self
646                .source
647                .get(
648                    self.source
649                        .location_offset(location, GridDirection::West, 1)
650                        .unwrap_or(location),
651                )
652                .unwrap_or_default();
653            let right = self
654                .source
655                .get(
656                    self.source
657                        .location_offset(location, GridDirection::East, 1)
658                        .unwrap_or(location),
659                )
660                .unwrap_or_default();
661            let top = self
662                .source
663                .get(
664                    self.source
665                        .location_offset(location, GridDirection::North, 1)
666                        .unwrap_or(location),
667                )
668                .unwrap_or_default();
669            let bottom = self
670                .source
671                .get(
672                    self.source
673                        .location_offset(location, GridDirection::South, 1)
674                        .unwrap_or(location),
675                )
676                .unwrap_or_default();
677            Vec2 {
678                x: ((right - left) * self.scale) as isize,
679                y: ((bottom - top) * self.scale) as isize,
680            }
681        }
682    }
683
684    fn generate_terrain(size: Vec2<usize>) -> Grid<Terrain> {
685        let mut grid = Grid::<f64>::generate(
686            size,
687            NoiseGenerator::new(
688                Fbm::<SuperSimplex>::default()
689                    .set_octaves(9)
690                    .set_frequency(0.008),
691            ),
692        );
693        grid.apply_all(RemapGenerator {
694            from: -1.0..1.0,
695            to: 0.0..1.0,
696        });
697        let gradient = grid.fork_generate(gradient_generator);
698        grid.apply_all(SubGenerator { other: &gradient });
699        grid.map(|_, _, value| {
700            if value > 0.5 {
701                Terrain::Mountain
702            } else if value > 0.2 {
703                Terrain::Grass
704            } else if value > 0.15 {
705                Terrain::Sand
706            } else {
707                Terrain::Water
708            }
709        })
710    }
711
712    fn generate_tunnels(size: Vec2<usize>) -> Grid<bool> {
713        let offsets = Grid::<f64>::generate(
714            size,
715            NoiseGenerator::new(ScalePoint::new(SuperSimplex::default()).set_scale(0.04)),
716        );
717        let offsets = Grid::<Vec2<isize>>::generate(
718            offsets.size(),
719            OffsetsGenerator {
720                source: &offsets,
721                scale: 20.0,
722            },
723        );
724        let mut thresholds = Grid::<f64>::generate(
725            size,
726            NoiseGenerator::new(ScalePoint::new(SuperSimplex::default()).set_scale(0.02)),
727        );
728        thresholds.apply_all(RemapGenerator {
729            from: -1.0..1.0,
730            to: 0.0..0.4,
731        });
732        let mut grid = Grid::<f64>::generate(
733            size,
734            OffsetLocationGenerator {
735                generator: &mut NoiseGenerator::new(Worley::default().set_frequency(0.04)),
736                offsets: &offsets,
737            },
738        );
739        grid.apply_all(RemapGenerator {
740            from: -1.0..1.0,
741            to: 0.0..1.0,
742        });
743        grid.apply_all(Kernel33Generator::edge_detection(&grid.clone()));
744        grid.apply_all(ThresholdGenerator::Constant {
745            threshold: 1.0e-4,
746            value_upper: 1.0,
747            value_lower: 0.0,
748        });
749        for _ in 0..1 {
750            grid.apply_all(Kernel33Generator::gaussian_blur(&grid.clone()));
751        }
752        grid.apply_all(ThresholdGenerator::Samples {
753            thresholds: &thresholds,
754            value_upper: 1.0,
755            value_lower: 0.0,
756        });
757        grid.map(|_, _, value| value >= 0.5)
758    }
759
760    #[test]
761    fn test_pcg_island() {
762        let terrain = generate_terrain(SIZE.into());
763        let tunnels = generate_tunnels(SIZE.into());
764
765        let (size, buffer) = terrain.into_inner();
766        let buffer = buffer
767            .into_iter()
768            .enumerate()
769            .flat_map(|(index, value)| match value {
770                Terrain::Mountain => {
771                    let location = tunnels.location(index);
772                    if tunnels.get(location).unwrap() {
773                        [64, 64, 64]
774                    } else {
775                        [128, 128, 128]
776                    }
777                }
778                Terrain::Grass => [0, 128, 0],
779                Terrain::Sand => [192, 192, 128],
780                Terrain::Water => [0, 0, 128],
781            })
782            .collect();
783        let image = RgbImage::from_vec(size.x as _, size.y as _, buffer).unwrap();
784        image.save("./resources/island.png").unwrap();
785    }
786
787    #[test]
788    fn test_pcg_tunnels() {
789        let tunnels = generate_tunnels(SIZE.into());
790
791        let (size, buffer) = tunnels.into_inner();
792        let buffer = buffer
793            .into_iter()
794            .map(|value| if value { 255 } else { 0 })
795            .collect();
796        let image = GrayImage::from_vec(size.x as _, size.y as _, buffer).unwrap();
797        image.save("./resources/caves.png").unwrap();
798    }
799}