From dca6747f83d6861071f230a4c934234f9f2c29c9 Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Sun, 8 Dec 2024 17:50:56 +0200 Subject: [PATCH] feat(maze): use seed --- Cargo.lock | 13 ++++--- Cargo.toml | 1 + src/dev_tools.rs | 74 +++++++++++++++++++++++++++++++---- src/maze/assets.rs | 9 ++--- src/maze/resources.rs | 77 ++++++++++++++++++++++++++----------- src/maze/systems/despawn.rs | 3 ++ src/maze/systems/setup.rs | 4 +- src/maze/systems/spawn.rs | 14 ++++--- 8 files changed, 146 insertions(+), 49 deletions(-) create mode 100644 src/maze/systems/despawn.rs diff --git a/Cargo.lock b/Cargo.lock index eee077a..5b82855 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2327,7 +2327,7 @@ dependencies = [ "hexx", "rand", "rand_chacha", - "thiserror 2.0.3", + "thiserror 2.0.5", ] [[package]] @@ -2682,6 +2682,7 @@ dependencies = [ "hexx", "log", "rand", + "thiserror 2.0.5", "tracing", ] @@ -3862,11 +3863,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "643caef17e3128658ff44d85923ef2d28af81bb71e0d67bbfe1d76f19a73e053" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.5", ] [[package]] @@ -3882,9 +3883,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "995d0bbc9995d1f19d28b7215a9352b0fc3cd3a2d2ec95c2cadc485cdedbcdde" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 8f92d8a..18a1a8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ hexx = { version = "0.18", features = ["bevy_reflect", "grid"] } hexlab = { version = "0.1", features = ["bevy"] } bevy-inspector-egui = { version = "0.27", optional = true } bevy_egui = { version = "0.30", optional = true } +thiserror = "2.0" [features] diff --git a/src/dev_tools.rs b/src/dev_tools.rs index 14e85db..52bea5c 100644 --- a/src/dev_tools.rs +++ b/src/dev_tools.rs @@ -12,9 +12,12 @@ use bevy::{ use bevy_inspector_egui::{bevy_egui::EguiContext, DefaultInspectorConfigPlugin}; -use crate::{maze::events::RecreateMazeEvent, screens::Screen}; +use crate::{ + maze::{events::RecreateMazeEvent, MazeConfig}, + screens::Screen, +}; use bevy_egui::{ - egui::{self, Button, Color32, ScrollArea}, + egui::{self, Button, Color32, DragValue, ScrollArea}, EguiPlugin, }; @@ -52,14 +55,71 @@ fn inspector_ui(world: &mut World) { ScrollArea::vertical().show(ui, |ui| { bevy_inspector_egui::bevy_inspector::ui_for_world(world, ui); }); + }); - ui.add_space(8.); + egui::Window::new("Maze Controls").show(egui_context.get_mut(), |ui| { + if let Some(mut maze_config) = world.get_resource_mut::() { + ui.heading("Maze Configuration"); - let button = Button::new("Recreate maze").fill(Color32::from_rgb(108, 108, 108)); + // radius controls + ui.horizontal(|ui| { + ui.label("Radius:"); + ui.add( + DragValue::new(&mut maze_config.radius) + .speed(1) + .range(1..=100), + ); + }); - if ui.add(button).clicked() { - if let Some(mut event_writer) = world.get_resource_mut::>() { - event_writer.send(RecreateMazeEvent { floor: 1 }); + // height controls + ui.horizontal(|ui| { + ui.label("Height:"); + ui.add( + DragValue::new(&mut maze_config.height) + .speed(0.5) + .range(1.0..=50.), + ); + }); + + // start position + ui.horizontal(|ui| { + ui.label("Start Position:"); + ui.add( + DragValue::new(&mut maze_config.start_pos.x) + .speed(1) + .prefix("x: "), + ); + ui.add( + DragValue::new(&mut maze_config.start_pos.y) + .speed(1) + .prefix("y: "), + ); + }); + + // end position + ui.horizontal(|ui| { + ui.label("End Position:"); + ui.add( + DragValue::new(&mut maze_config.end_pos.x) + .speed(1) + .prefix("x: "), + ); + ui.add( + DragValue::new(&mut maze_config.end_pos.y) + .speed(1) + .prefix("y: "), + ); + }); + + ui.add_space(8.); + + let button = Button::new("Recreate maze").fill(Color32::from_rgb(108, 108, 108)); + if ui.add(button).clicked() { + if let Some(mut event_writer) = + world.get_resource_mut::>() + { + event_writer.send(RecreateMazeEvent { floor: 1 }); + } } } }); diff --git a/src/maze/assets.rs b/src/maze/assets.rs index 3ae52fa..5a2d31d 100644 --- a/src/maze/assets.rs +++ b/src/maze/assets.rs @@ -2,10 +2,7 @@ use std::f32::consts::FRAC_PI_2; use bevy::prelude::*; -use super::{ - resources::{HEX_SIZE, WALL_SIZE}, - MazeConfig, -}; +use super::{resources::WALL_SIZE, MazeConfig}; pub(crate) struct MazeAssets { pub(crate) hex_mesh: Handle, @@ -20,8 +17,8 @@ pub(crate) fn create_base_assets( config: &MazeConfig, ) -> MazeAssets { MazeAssets { - hex_mesh: meshes.add(generate_hex_mesh(HEX_SIZE, config.height)), - wall_mesh: meshes.add(generate_square_mesh(HEX_SIZE)), + hex_mesh: meshes.add(generate_hex_mesh(config.size, config.height)), + wall_mesh: meshes.add(generate_square_mesh(config.size)), hex_material: materials.add(white_material()), wall_material: materials.add(Color::BLACK), } diff --git a/src/maze/resources.rs b/src/maze/resources.rs index ccc9d6b..77bcb0d 100644 --- a/src/maze/resources.rs +++ b/src/maze/resources.rs @@ -1,39 +1,63 @@ +use std::num::TryFromIntError; + use bevy::prelude::*; use hexx::{Hex, HexLayout, HexOrientation}; -use rand::{thread_rng, Rng}; +use rand::{rngs::StdRng, thread_rng, Rng, SeedableRng}; +use thiserror::Error; -pub(crate) const HEX_SIZE: f32 = 6.; pub(crate) const WALL_SIZE: f32 = 1.0; +#[derive(Debug, Error)] +pub enum MazeConfigError { + #[error("Failed to convert radius from u32 to i32: {0}")] + RadiusConverions(#[from] TryFromIntError), +} #[derive(Debug, Reflect, Resource)] #[reflect(Resource)] pub struct MazeConfig { pub radius: u32, pub height: f32, + pub size: f32, pub start_pos: Hex, pub end_pos: Hex, + pub seed: u64, +} + +impl MazeConfig { + fn new( + radius: u32, + height: f32, + size: f32, + seed: Option, + ) -> Result { + let seed = seed.unwrap_or_else(|| thread_rng().gen()); + let mut rng = StdRng::seed_from_u64(seed); + + let start_pos = generate_pos(radius, &mut rng)?; + let end_pos = generate_pos(radius, &mut rng)?; + + debug!("Start pos: ({},{})", start_pos.x, start_pos.y); + debug!("End pos: ({},{})", end_pos.x, end_pos.y); + + Ok(Self { + radius: radius as u32, + height, + size, + start_pos, + end_pos, + seed, + }) + } + + pub fn new_unchecked(radius: u32, height: f32, hex_size: f32, seed: Option) -> Self { + Self::new(radius, height, hex_size, seed) + .expect("Failed to create MazeConfig with supposedly safe values") + } } impl Default for MazeConfig { fn default() -> Self { - let mut rng = thread_rng(); - let radius = 7; - let start_pos = Hex::new( - rng.gen_range(-radius..radius), - rng.gen_range(-radius..radius), - ); - let end_pos = Hex::new( - rng.gen_range(-radius..radius), - rng.gen_range(-radius..radius), - ); - debug!("Start pos: ({},{})", start_pos.x, start_pos.y); - debug!("End pos: ({},{})", end_pos.x, end_pos.y); - Self { - radius: radius as u32, - height: 20., - start_pos, - end_pos, - } + Self::new_unchecked(7, 20., 6., None) } } @@ -42,11 +66,20 @@ impl Default for MazeConfig { pub struct Layout(pub HexLayout); impl FromWorld for Layout { - fn from_world(_world: &mut World) -> Self { + fn from_world(world: &mut World) -> Self { + let config = world.resource::(); Self(HexLayout { orientation: HexOrientation::Flat, - hex_size: Vec2::splat(HEX_SIZE), + hex_size: Vec2::splat(config.size), ..default() }) } } + +fn generate_pos(radius: u32, rng: &mut R) -> Result { + let radius = i32::try_from(radius)?; + Ok(Hex::new( + rng.gen_range(-radius..radius), + rng.gen_range(-radius..radius), + )) +} diff --git a/src/maze/systems/despawn.rs b/src/maze/systems/despawn.rs new file mode 100644 index 0000000..ad25077 --- /dev/null +++ b/src/maze/systems/despawn.rs @@ -0,0 +1,3 @@ +use bevy::prelude::*; + +use crate::maze::components::MazeFloor; diff --git a/src/maze/systems/setup.rs b/src/maze/systems/setup.rs index 8e7da5e..96556ad 100644 --- a/src/maze/systems/setup.rs +++ b/src/maze/systems/setup.rs @@ -26,7 +26,7 @@ pub(super) fn setup_maze( ) { let maze = MazeBuilder::new() .with_radius(config.radius) - // .with_seed(0) + .with_seed(config.seed) .with_generator(GeneratorType::RecursiveBacktracking) .build() .expect("Something went wrong while creating maze"); @@ -43,7 +43,7 @@ pub(super) fn setup_maze( )) .with_children(|parent| { for tile in maze.values() { - spawn_single_hex_tile(parent, &assets, tile, &layout.0, config.height) + spawn_single_hex_tile(parent, &assets, tile, &layout.0, &config) } }); } diff --git a/src/maze/systems/spawn.rs b/src/maze/systems/spawn.rs index 2ec9259..ca6b8d8 100644 --- a/src/maze/systems/spawn.rs +++ b/src/maze/systems/spawn.rs @@ -7,7 +7,8 @@ use hexx::HexOrientation; use crate::maze::{ assets::MazeAssets, components::{MazeTile, MazeWall}, - resources::{HEX_SIZE, WALL_SIZE}, + resources::WALL_SIZE, + MazeConfig, }; pub(super) fn spawn_single_hex_tile( @@ -15,7 +16,7 @@ pub(super) fn spawn_single_hex_tile( assets: &MazeAssets, tile: &HexTile, layout: &HexLayout, - hex_height: f32, + config: &MazeConfig, ) { let world_pos = tile.to_vec3(layout); let rotation = match layout.orientation { @@ -34,11 +35,12 @@ pub(super) fn spawn_single_hex_tile( ..default() }, )) - .with_children(|parent| spawn_walls(parent, assets, hex_height / 2., &tile.walls())); + .with_children(|parent| spawn_walls(parent, assets, config, &tile.walls())); } -fn spawn_walls(parent: &mut ChildBuilder, assets: &MazeAssets, y_offset: f32, walls: &Walls) { +fn spawn_walls(parent: &mut ChildBuilder, assets: &MazeAssets, config: &MazeConfig, walls: &Walls) { let z_rotation = Quat::from_rotation_z(-FRAC_PI_2); + let y_offset = config.height / 2.; for i in 0..6 { if !walls.contains(i) { @@ -47,8 +49,8 @@ fn spawn_walls(parent: &mut ChildBuilder, assets: &MazeAssets, y_offset: f32, wa let wall_angle = -FRAC_PI_3 * i as f32; - let x_offset = (HEX_SIZE - WALL_SIZE) * f32::cos(wall_angle); - let z_offset = (HEX_SIZE - WALL_SIZE) * f32::sin(wall_angle); + let x_offset = (config.size - WALL_SIZE) * f32::cos(wall_angle); + let z_offset = (config.size - WALL_SIZE) * f32::sin(wall_angle); let pos = Vec3::new(x_offset, y_offset, z_offset); let x_rotation = Quat::from_rotation_x(wall_angle + FRAC_PI_2);