From 83695f5d1d3550d04cc5e7a49d91a17abc80ad95 Mon Sep 17 00:00:00 2001 From: grimsace Date: Fri, 6 Mar 2026 09:40:22 -0600 Subject: [PATCH] removed an unnessessary genreate button --- README.md | 7 +++++-- src/layout.rs | 27 +++++++++++++++++++-------- src/main.rs | 18 ++++-------------- src/seed.rs | 21 +++++++++++++++++++++ src/ui.rs | 21 ++++++++++++++++----- 5 files changed, 65 insertions(+), 29 deletions(-) create mode 100644 src/seed.rs diff --git a/README.md b/README.md index d92a390..c5aaa37 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Desktop Dungeon Generator -A Rust + `egui` desktop app for generating simple tabletop dungeon layouts on a scalable grid. +A Rust desktop app for generating simple tabletop dungeon layouts. ## Current Features @@ -19,13 +19,16 @@ A Rust + `egui` desktop app for generating simple tabletop dungeon layouts on a - `Corridor Randomness (%)` from `0` to `100` - `Dead-End Rooms (%)` from `0` to `50` - Generate control: - - `Generate New Layout` button increments seed and creates a new layout + - Master seed input (`u64`) + - `Random` seed button + - `Generate New Layout` button generates using the current seed ## Generation Behavior - Rooms are stored as rectangles (`x`, `y`, `width`, `height`) and placed without overlap. - Corridors are stored separately as vector line segments between grid cells. - Every generated room is connected into one navigable network when possible. +- A master seed drives deterministic generation, and internal subsystems derive their own sub-seeds from it. ### Corridor Randomness diff --git a/src/layout.rs b/src/layout.rs index 425c616..24ba22f 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,3 +1,4 @@ +use crate::seed; use std::collections::{HashSet, VecDeque}; #[derive(Debug, Clone)] @@ -37,7 +38,17 @@ pub fn generate_layout( corridor_randomness_percent: usize, dead_end_room_percent: usize, ) -> DungeonLayout { - let mut rng = SimpleRng::new(seed ^ ((cols as u64) << 32) ^ rows as u64); + let layout_salt = ((cols as u64) << 48) + ^ ((rows as u64) << 32) + ^ ((target_room_count as u64) << 16) + ^ (min_room_size as u64) + ^ ((max_room_size as u64) << 8) + ^ ((corridor_randomness_percent as u64) << 56) + ^ ((dead_end_room_percent as u64) << 40); + let base_seed = seed::derive_seed(seed, layout_salt); + let mut room_rng = SimpleRng::new(seed::derive_seed(base_seed, 1)); + let mut graph_rng = SimpleRng::new(seed::derive_seed(base_seed, 2)); + let mut path_rng = SimpleRng::new(seed::derive_seed(base_seed, 3)); let mut rooms = Vec::new(); let mut corridors = Vec::new(); @@ -69,7 +80,7 @@ pub fn generate_layout( } let (width, height) = if square_rooms_only { - let side = rng.range_inclusive(min_size, max_size.min(cols.min(rows))); + let side = room_rng.range_inclusive(min_size, max_size.min(cols.min(rows))); (side, side) } else { let width_max = max_size.min(cols); @@ -78,8 +89,8 @@ pub fn generate_layout( continue; } ( - rng.range_inclusive(min_size, width_max), - rng.range_inclusive(min_size, height_max), + room_rng.range_inclusive(min_size, width_max), + room_rng.range_inclusive(min_size, height_max), ) }; @@ -87,8 +98,8 @@ pub fn generate_layout( continue; } - let x = rng.range_inclusive(0, cols - width); - let y = rng.range_inclusive(0, rows - height); + let x = room_rng.range_inclusive(0, cols - width); + let y = room_rng.range_inclusive(0, rows - height); let candidate = Room { x, y, @@ -112,7 +123,7 @@ pub fn generate_layout( let centers: Vec<(usize, usize)> = rooms.iter().map(Room::center_cell).collect(); let target_dead_end_rooms = ((rooms.len() * dead_end_room_percent.min(50)) + 50) / 100; let room_edges = - build_room_connection_edges(¢ers, randomness, target_dead_end_rooms, &mut rng); + build_room_connection_edges(¢ers, randomness, target_dead_end_rooms, &mut graph_rng); let mut occupied_corridor_cells = HashSet::new(); for (a_idx, b_idx) in room_edges { @@ -122,7 +133,7 @@ pub fn generate_layout( let preferred = if randomness <= 0.001 { shortest_path(a, b, cols, rows).unwrap_or_else(|| vec![a, b]) } else { - noisy_path(a, b, cols, rows, randomness, &mut rng) + noisy_path(a, b, cols, rows, randomness, &mut path_rng) }; if !try_place_cell_path( diff --git a/src/main.rs b/src/main.rs index 888889e..936d628 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod layout; +mod seed; mod ui; use std::collections::HashSet; @@ -20,19 +21,17 @@ fn main() -> eframe::Result<()> { struct DungeonApp { settings: UiSettings, - seed: u64, layout: DungeonLayout, } impl Default for DungeonApp { fn default() -> Self { let settings = UiSettings::default(); - let seed = 1; let layout = generate_layout( settings.cols, settings.rows, settings.room_count, - seed, + settings.seed, settings.min_room_size, settings.max_room_size, settings.square_rooms_only, @@ -40,11 +39,7 @@ impl Default for DungeonApp { settings.dead_end_rooms_percent, ); - Self { - settings, - seed, - layout, - } + Self { settings, layout } } } @@ -56,11 +51,6 @@ impl eframe::App for DungeonApp { self.settings.max_room_size = self.settings.min_room_size; } - if panel_result.generate_clicked { - self.seed = self.seed.wrapping_add(1); - self.regenerate_layout(); - } - if panel_result.settings_changed { self.regenerate_layout(); } @@ -81,7 +71,7 @@ impl DungeonApp { self.settings.cols, self.settings.rows, self.settings.room_count, - self.seed, + self.settings.seed, self.settings.min_room_size, self.settings.max_room_size, self.settings.square_rooms_only, diff --git a/src/seed.rs b/src/seed.rs new file mode 100644 index 0000000..41fb7c6 --- /dev/null +++ b/src/seed.rs @@ -0,0 +1,21 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + +pub fn random_seed() -> u64 { + let nanos = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.as_nanos() as u64) + .unwrap_or(0); + let pid = std::process::id() as u64; + mix64(nanos ^ pid.rotate_left(17)) +} + +pub fn derive_seed(master_seed: u64, stream_id: u64) -> u64 { + mix64(master_seed ^ stream_id.wrapping_mul(0x9E37_79B9_7F4A_7C15)) +} + +fn mix64(mut x: u64) -> u64 { + x = x.wrapping_add(0x9E37_79B9_7F4A_7C15); + x = (x ^ (x >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9); + x = (x ^ (x >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB); + x ^ (x >> 31) +} diff --git a/src/ui.rs b/src/ui.rs index 9a24004..035cbb1 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,3 +1,4 @@ +use crate::seed; use eframe::egui; use egui::RichText; @@ -9,6 +10,7 @@ enum Tab { #[derive(Debug, Clone)] pub struct UiSettings { + pub seed: u64, pub cols: usize, pub rows: usize, pub room_count: usize, @@ -23,6 +25,7 @@ pub struct UiSettings { impl Default for UiSettings { fn default() -> Self { Self { + seed: 1, cols: 24, rows: 18, room_count: 8, @@ -39,7 +42,6 @@ impl Default for UiSettings { #[derive(Debug, Clone, Copy, Default)] pub struct SidePanelResult { pub settings_changed: bool, - pub generate_clicked: bool, } pub fn draw_side_panel(ctx: &egui::Context, settings: &mut UiSettings) -> SidePanelResult { @@ -107,12 +109,21 @@ fn draw_generate_tab(ui: &mut egui::Ui, settings: &mut UiSettings, result: &mut }); ui.add_space(12.0); - ui.label(RichText::new("Generation").strong()); + ui.separator(); + ui.add_space(8.0); + ui.label(RichText::new("Seed Settings").strong()); ui.add_space(8.0); - if ui.button("Generate New Layout").clicked() { - result.generate_clicked = true; - } + ui.label("Master Seed"); + ui.horizontal(|ui| { + result.settings_changed |= ui + .add(egui::DragValue::new(&mut settings.seed).speed(1.0)) + .changed(); + if ui.button("Random").clicked() { + settings.seed = seed::random_seed(); + result.settings_changed = true; + } + }); } fn draw_layout_tab(ui: &mut egui::Ui, settings: &mut UiSettings, result: &mut SidePanelResult) {