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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use crate::{Algorithm, Grid, Rng, Tile};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
/// Configuration for diamond-square heightmap generation.
pub struct DiamondSquareConfig {
/// Roughness factor controlling height variation. Default: 0.6.
pub roughness: f64,
/// Height threshold for floor/wall cutoff (0.0–1.0). Default: 0.4.
pub threshold: f64,
}
impl Default for DiamondSquareConfig {
fn default() -> Self {
Self {
roughness: 0.6,
threshold: 0.4,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
/// Diamond-square heightmap terrain generator.
pub struct DiamondSquare {
config: DiamondSquareConfig,
}
impl DiamondSquare {
/// Creates a new diamond-square generator with the given config.
pub fn new(config: DiamondSquareConfig) -> Self {
Self { config }
}
}
impl Default for DiamondSquare {
fn default() -> Self {
Self::new(DiamondSquareConfig::default())
}
}
impl Algorithm<Tile> for DiamondSquare {
fn generate(&self, grid: &mut Grid<Tile>, seed: u64) {
let mut rng = Rng::new(seed);
let (w, h) = (grid.width(), grid.height());
// Create heightmap
let mut heights = vec![vec![0.0f64; w]; h];
// Initialize with noise
for row in heights.iter_mut() {
for cell in row.iter_mut() {
*cell = rng.random();
}
}
// Diamond-square iterations to smooth
let mut step = w.max(h) / 2;
let mut scale = self.config.roughness;
while step > 0 {
// Diamond step - set center of each square
let mut y = step;
while y < h {
let mut x = step;
while x < w {
let mut sum = 0.0;
let mut count = 0;
if y >= step && x >= step {
sum += heights[y - step][x - step];
count += 1;
}
if y >= step && x + step < w {
sum += heights[y - step][x + step];
count += 1;
}
if y + step < h && x >= step {
sum += heights[y + step][x - step];
count += 1;
}
if y + step < h && x + step < w {
sum += heights[y + step][x + step];
count += 1;
}
if count > 0 {
heights[y][x] =
(sum / count as f64 + (rng.random() - 0.5) * scale).clamp(0.0, 1.0);
}
x += step * 2;
}
y += step * 2;
}
// Square step - set edge midpoints
for y in 0..h {
let x_start = if (y / step) % 2 == 0 { step } else { 0 };
let mut x = x_start;
while x < w {
let mut sum = 0.0;
let mut count = 0;
if x >= step {
sum += heights[y][x - step];
count += 1;
}
if x + step < w {
sum += heights[y][x + step];
count += 1;
}
if y >= step {
sum += heights[y - step][x];
count += 1;
}
if y + step < h {
sum += heights[y + step][x];
count += 1;
}
if count > 0 {
heights[y][x] =
(sum / count as f64 + (rng.random() - 0.5) * scale).clamp(0.0, 1.0);
}
x += step * 2;
}
}
step /= 2;
scale *= 0.5;
}
// Convert to tiles
for (y, row) in heights.iter().enumerate() {
for (x, &height) in row.iter().enumerate() {
if height > self.config.threshold {
grid.set(x as i32, y as i32, Tile::Floor);
}
}
}
}
fn name(&self) -> &'static str {
"DiamondSquare"
}
}