Generator für die Testkarte

Baut aus einer Startzahl eine 64 × 64 Felder große Karte mit Rand aus
Grundgestein, Lichtung am Startpunkt, Felsblock daneben, weichem Fels
innen und hartem Fels außen. Die Regeln sind für 300 Startzahlen geprüft.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Steven Wroblewski
2026-09-30 20:39:47 +02:00
parent 88d8069f8e
commit aae0eccc8b
3 changed files with 237 additions and 0 deletions
+1
View File
@@ -9,4 +9,5 @@
pub mod codec;
pub mod hash;
pub mod map;
pub mod mapgen;
pub mod rng;
+104
View File
@@ -0,0 +1,104 @@
//! Generator for the milestone 1 test map.
//!
//! The broad layout is fixed and the details are random: a bedrock border, a
//! clearing around the spawn point, a rock outcrop right next to the
//! clearing, cave-like soft rock in the inner area and hard rock further out.
use crate::map::{Map, Pos, Terrain, Tile};
use crate::rng::Rng;
/// Edge length of the test map, in tiles.
pub const TEST_MAP_SIZE: u32 = 64;
/// Tiles within this distance of the spawn point are always open ground.
pub const SPAWN_CLEARING_RADIUS: i32 = 5;
/// Edge length of the soft rock outcrop that every test map has.
pub const OUTCROP_SIZE: i32 = 4;
/// Rock at least this many tiles from the spawn point, counted along the
/// longer axis, is hard rock.
pub const HARD_ROCK_DISTANCE: i32 = 22;
const INITIAL_ROCK_PERCENT: u32 = 46;
const SMOOTHING_PASSES: u32 = 4;
/// Keeps the map sequence apart from other uses of the same seed.
const SEED_SALT: u64 = 0x6D61_7067_656E_0001;
/// Spawn point of a map: its centre tile.
pub fn spawn_pos(map: &Map) -> Pos {
Pos::new((map.width() / 2) as i32, (map.height() / 2) as i32)
}
/// Top left tile of the soft rock outcrop next to the spawn clearing.
pub fn outcrop_origin(map: &Map) -> Pos {
let spawn = spawn_pos(map);
Pos::new(spawn.x + SPAWN_CLEARING_RADIUS + 2, spawn.y - 2)
}
/// Builds the test map for `seed`. The same seed always gives the same map.
pub fn generate_test_map(seed: u64) -> Map {
let size = TEST_MAP_SIZE;
let mut rng = Rng::new(seed ^ SEED_SALT);
let mut rock = vec![false; (size as usize) * (size as usize)];
for cell in rock.iter_mut() {
*cell = rng.below(100) < INITIAL_ROCK_PERCENT;
}
for _ in 0..SMOOTHING_PASSES {
rock = smooth(&rock, size);
}
let mut map = Map::filled(size, size, Terrain::Ground);
let spawn = spawn_pos(&map);
let outcrop = outcrop_origin(&map);
let last = size as i32 - 1;
for y in 0..=last {
for x in 0..=last {
let dx = x - spawn.x;
let dy = y - spawn.y;
let on_border = x == 0 || y == 0 || x == last || y == last;
let in_clearing = dx * dx + dy * dy <= SPAWN_CLEARING_RADIUS * SPAWN_CLEARING_RADIUS;
let in_outcrop = (outcrop.x..outcrop.x + OUTCROP_SIZE).contains(&x)
&& (outcrop.y..outcrop.y + OUTCROP_SIZE).contains(&y);
let is_rock = rock[(y as usize) * (size as usize) + (x as usize)];
let terrain = if on_border {
Terrain::Bedrock
} else if in_clearing {
Terrain::Ground
} else if in_outcrop {
Terrain::SoftRock
} else if !is_rock {
Terrain::Ground
} else if dx.abs().max(dy.abs()) >= HARD_ROCK_DISTANCE {
Terrain::HardRock
} else {
Terrain::SoftRock
};
map.set(Pos::new(x, y), Tile::new(terrain));
}
}
map
}
/// One pass of cave smoothing: a cell becomes rock when at least five of the
/// nine cells around it, itself included, are rock. Cells outside the grid
/// count as rock.
fn smooth(rock: &[bool], size: u32) -> Vec<bool> {
let size = size as i32;
let mut out = vec![false; rock.len()];
for y in 0..size {
for x in 0..size {
let mut count = 0;
for ny in y - 1..=y + 1 {
for nx in x - 1..=x + 1 {
let outside = nx < 0 || ny < 0 || nx >= size || ny >= size;
if outside || rock[(ny * size + nx) as usize] {
count += 1;
}
}
}
out[(y * size + x) as usize] = count >= 5;
}
}
out
}