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}