diff --git a/src/hex.rs b/src/hex.rs new file mode 100644 index 0000000..2f48253 --- /dev/null +++ b/src/hex.rs @@ -0,0 +1,14 @@ +use bevy::{color::palettes::css::BLACK, prelude::*}; + +use bevy_prototype_lyon::{ + draw::{Fill, Stroke}, + entity::ShapeBundle, + path::PathBuilder, + plugin::ShapePlugin, +}; +use rand::{thread_rng, Rng}; + +pub(super) fn plugin(app: &mut App) { + app.add_plugins(ShapePlugin); + app.add_systems(Startup, setup_system); +} diff --git a/src/hexgrid/direction.rs b/src/hexgrid/direction.rs index cc0551a..7522a5f 100644 --- a/src/hexgrid/direction.rs +++ b/src/hexgrid/direction.rs @@ -4,7 +4,7 @@ use hexx::EdgeDirection; pub(super) fn plugin(_app: &mut App) {} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)] -pub enum HexDirection { +pub enum Direction { Top, TopRight, BottomRight, @@ -13,12 +13,8 @@ pub enum HexDirection { TopLeft, } -impl HexDirection { - pub fn to_hexx_direction(self) -> EdgeDirection { - self.into() - } - - pub const ALL: [HexDirection; 6] = [ +impl Direction { + pub const ALL: [Direction; 6] = [ Self::Top, Self::TopRight, Self::BottomRight, @@ -38,16 +34,3 @@ impl HexDirection { } } } - -impl From for EdgeDirection { - fn from(value: HexDirection) -> Self { - match value { - HexDirection::Top => Self::FLAT_NORTH, - HexDirection::TopRight => Self::FLAT_NORTH_EAST, - HexDirection::BottomRight => Self::FLAT_SOUTH_EAST, - HexDirection::Bottom => Self::FLAT_SOUTH, - HexDirection::BottomLeft => Self::FLAT_SOUTH_WEST, - HexDirection::TopLeft => Self::FLAT_NORTH_WEST, - } - } -} diff --git a/src/hexgrid/grid.rs b/src/hexgrid/grid.rs index 287444d..757e829 100644 --- a/src/hexgrid/grid.rs +++ b/src/hexgrid/grid.rs @@ -1,4 +1,8 @@ -use bevy::{color::palettes::css::BLACK, prelude::*}; +use bevy::{ + color::palettes::css::{BLACK, GREEN, RED}, + prelude::*, + utils::hashbrown::HashMap, +}; use bevy_prototype_lyon::{ draw::{Fill, Stroke}, @@ -6,7 +10,18 @@ use bevy_prototype_lyon::{ path::PathBuilder, plugin::ShapePlugin, }; -use rand::{thread_rng, Rng}; +use rand::{prelude::SliceRandom, rngs::ThreadRng, thread_rng}; + +use super::tile::{AxialCoord, Tile}; + +const DIRECTIONS: [AxialCoord; 6] = [ + AxialCoord { q: 1, r: 0 }, // Right + AxialCoord { q: 1, r: -1 }, // Top-right + AxialCoord { q: 0, r: -1 }, // Top-left + AxialCoord { q: -1, r: 0 }, // Left + AxialCoord { q: -1, r: 1 }, // Bottom-left + AxialCoord { q: 0, r: 1 }, // Bottom-right +]; pub struct HexGrid; @@ -18,45 +33,45 @@ impl Plugin for HexGrid { } pub(super) fn setup_system(mut commands: Commands) { - let radius = 5; - let hex_positions = generate_hex_grix(radius); + let radius = 7; + let mut grid = generate_hex_grix(radius); - let hex_size = 30.; - let hex_height = hex_size * 2.; - let hex_width = (3.0_f32).sqrt() * hex_size; + let start_coord = AxialCoord::new(-radius, 0); + let end_coord = AxialCoord::new(radius, 0); - for (q, r) in hex_positions { - let x = hex_width * (q as f32 + r as f32 / 2.); - let y = hex_height * (r as f32 * 3. / 4.); - let mut rng = thread_rng(); - let walls: [bool; 6] = [ - rng.gen(), - rng.gen(), - rng.gen(), - rng.gen(), - rng.gen(), - rng.gen(), - ]; + let mut rng = thread_rng(); + generate_maze(&mut grid, start_coord, &mut rng); - add_hex_tile(&mut commands, Vec2::new(x, y), hex_size, walls); - } + render_maze(&mut commands, &mut grid, radius, start_coord, end_coord); } -fn generate_hex_grix(radius: i32) -> Vec<(i32, i32)> { - let mut positions = Vec::new(); +fn generate_hex_grix(radius: i32) -> HashMap { + let mut grid = HashMap::new(); for q in -radius..=radius { let r1 = (-radius).max(-q - radius); let r2 = radius.min(-q + radius); for r in r1..=r2 { - positions.push((q, r)); + let coord = AxialCoord::new(q, r); + let tile = Tile { + position: coord, + walls: [true; 6], + visited: false, + }; + grid.insert(coord, tile); } } - positions + grid } -fn add_hex_tile(commands: &mut Commands, position: Vec2, size: f32, walls: [bool; 6]) { +fn add_hex_tile( + commands: &mut Commands, + position: Vec2, + size: f32, + walls: [bool; 6], + fill_color: Color, +) { let hex_points = (0..6) .map(|i| { let angle_deg = 60. * i as f32 - 30.; @@ -73,6 +88,7 @@ fn add_hex_tile(commands: &mut Commands, position: Vec2, size: f32, walls: [bool path_builder.close(); let hexagon = path_builder.build(); + // Create the hexagon fill commands.spawn(( ShapeBundle { path: hexagon, @@ -82,9 +98,10 @@ fn add_hex_tile(commands: &mut Commands, position: Vec2, size: f32, walls: [bool }, ..default() }, - Fill::color(Color::srgb(0.8, 0.8, 0.8)), + Fill::color(fill_color), )); + // Draw walls for i in 0..6 { if walls[i] { let start = hex_points[i]; @@ -108,3 +125,70 @@ fn add_hex_tile(commands: &mut Commands, position: Vec2, size: f32, walls: [bool } } } + +fn generate_maze( + grid: &mut HashMap, + current_coord: AxialCoord, + rng: &mut ThreadRng, +) { + { + let current_tile = grid.get_mut(¤t_coord).unwrap(); + current_tile.visit(); + } + + let mut directions = DIRECTIONS.clone(); + directions.shuffle(rng); + + for (i, direction) in directions.iter().enumerate() { + let neightbor_coord = AxialCoord { + q: current_coord.q + direction.q, + r: current_coord.r + direction.r, + }; + + if let Some(neightbor_tile) = grid.get(&neightbor_coord) { + if !neightbor_tile.visited { + // Remove the wall between current_tile and neighbor_tile + { + let current_tile = grid.get_mut(¤t_coord).unwrap(); + current_tile.walls[i] = false; + } + { + let neightbor_tile = grid.get_mut(&neightbor_coord).unwrap(); + neightbor_tile.walls[opposite_wall(i)] = false; + } + // Recurse with the neighbor tile + generate_maze(grid, neightbor_coord, rng); + } + } + } +} + +fn render_maze( + commands: &mut Commands, + grid: &mut HashMap, + radius: i32, + start_coord: AxialCoord, + end_coord: AxialCoord, +) { + let hex_size = 30.; + let hex_height = hex_size * 2.; + let hex_width = (3.0_f32).sqrt() * hex_size; + + for tile in grid.values() { + let (q, r) = (tile.position.q, tile.position.r); + let x = hex_width * (q as f32 + r as f32 / 2.); + let y = hex_height * (r as f32 * 3. / 4.); + let mut fill_color = Color::srgb(0.8, 0.8, 0.8); + if tile.position == start_coord { + fill_color = GREEN.into(); + } else if tile.position == end_coord { + fill_color = RED.into(); + } + + add_hex_tile(commands, Vec2::new(x, y), hex_size, tile.walls, fill_color); + } +} + +fn opposite_wall(index: usize) -> usize { + (index + 3) % 6 +} diff --git a/src/hexgrid/mod.rs b/src/hexgrid/mod.rs index a18b576..0dd34ee 100644 --- a/src/hexgrid/mod.rs +++ b/src/hexgrid/mod.rs @@ -4,7 +4,9 @@ use bevy::{ }; use bevy_prototype_lyon::plugin::ShapePlugin; use grid::setup_system; +pub mod direction; pub mod grid; +pub mod tile; pub struct HexGrid; diff --git a/src/hexgrid/tile.rs b/src/hexgrid/tile.rs new file mode 100644 index 0000000..ad13baf --- /dev/null +++ b/src/hexgrid/tile.rs @@ -0,0 +1,24 @@ +#[derive(Debug, Clone)] +pub struct Tile { + pub position: AxialCoord, + pub walls: [bool; 6], + pub visited: bool, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct AxialCoord { + pub q: i32, + pub r: i32, +} + +impl Tile { + pub fn visit(&mut self) { + self.visited = true + } +} + +impl AxialCoord { + pub fn new(q: i32, r: i32) -> Self { + Self { q, r } + } +}