summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs613
1 files changed, 0 insertions, 613 deletions
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index fe32d14..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,613 +0,0 @@
-use rand::{Rng, SeedableRng};
-use rand::thread_rng;
-use rand::rngs::SmallRng;
-
-
-type RoomId = u32;
-
-enum LevelBuilderError {
- OutOfBounds,
- InvalidRoomId,
-}
-
-#[derive(Debug)]
-enum Error {
- OutOfBounds,
- MaximumIterations,
- NonEmpty,
- InvalidRoomId,
-}
-
-
-mod Direction {
- pub const UP: u8 = 1 << 0;
- pub const RIGHT: u8 = 1 << 1;
- pub const DOWN: u8 = 1 << 2;
- pub const LEFT: u8 = 1 << 3;
-
- #[derive(Debug)]
- pub enum Error {
- Underflow,
- Overflow,
- }
-
- pub fn opposite(dir: u8) -> u8 {
- match dir {
- UP => DOWN,
- RIGHT => LEFT,
- DOWN => UP,
- LEFT => RIGHT,
- _ => unreachable!()
- }
- }
-
- pub fn step_towards(dir: u8, point: (u32, u32)) -> Result<(u32, u32), Error> {
- let p = match dir {
- UP => if point.1 > 0 { (point.0, point.1 - 1) } else { return Err(Error::Underflow) },
- RIGHT => (point.0 + 1, point.1),
- DOWN => (point.0, point.1 + 1),
- LEFT => if point.0 > 0 { (point.0 - 1, point.1) } else { return Err(Error::Underflow) },
- _ => unreachable!()
-
- };
- Ok(p)
- }
-
-}
-
-struct RoomDescription {
- width: u32,
- height: u32,
- data: Vec<u32>,
-}
-
-struct LevelBuilder { }
-
-trait Region<T> {
- fn width(&self) -> u32;
- fn height(&self) -> u32;
- fn set(&mut self, x: u32, y: u32, value: T) -> Result<(), Error>;
- fn get(&self, x: u32, y: u32) -> Result<&T, Error>;
- fn get_mut(&mut self, x: u32, y: u32) -> Result<&mut T, Error>;
-}
-
-struct VecRegion<T> {
- inner: Vec<T>,
- width: u32,
- height: u32,
-}
-
-impl<T> Region<T> for VecRegion<T> {
- fn width(&self) -> u32 {
- self.width
- }
-
- fn height(&self) -> u32 {
- self.height
- }
-
- fn set(&mut self, x: u32, y: u32, value: T) -> Result<(), Error> {
- if x >= self.width || y >= self.height {
- return Err(Error::OutOfBounds);
- }
-
- self.inner[(y * self.width + x) as usize] = value;
- Ok(())
- }
-
- fn get(&self, x: u32, y:u32) -> Result<&T, Error> {
- if x >= self.width || y >= self.height {
- return Err(Error::OutOfBounds);
- }
-
- Ok(&self.inner[(y * self.width + x) as usize])
- }
-
- fn get_mut(&mut self, x: u32, y: u32) -> Result<&mut T, Error> {
- if x >= self.width || y >= self.height {
- return Err(Error::OutOfBounds);
- }
-
- Ok(&mut self.inner[(y * self.width + x) as usize])
- }
-}
-
-impl<T: Default> Region<T> {
- fn new_with(width: u32, height: u32) -> VecRegion<T> {
- let size = (width * height) as usize;
- let mut inner = Vec::with_capacity(size);
- inner.resize_with(size, T::default);
- VecRegion {
- inner,
- width,
- height,
- }
- }
-}
-
-
-const TILE_IS_PATH: u8 = 0x4;
-
-#[derive(Clone, Debug)]
-struct Tile {
- owner: Owner,
- flags: u8,
- connections: u8,
-}
-
-#[derive(Clone)]
-struct TileTemplate {
- connections: u8
-}
-
-#[derive(Eq, PartialEq, Clone, Debug, Copy)]
-enum Owner {
- Room(RoomId),
- Tunnel(u32),
-}
-
-impl TileTemplate {
- fn into_tile(&self, room: RoomId) -> Tile {
- Tile {
- connections: self.connections,
- owner: Owner::Room(room),
- flags: 0,
- }
- }
-}
-
-//type Level = Region<Option<Tile>>;
-type RoomTemplate = Region<Option<TileTemplate>>;
-
-struct Room {
- x: u32,
- y: u32,
- width: u32,
- height: u32,
-}
-
-struct Level {
- region: Box<dyn Region<Option<Tile>>>,
- rooms: Vec<Room>,
- tunnels: u32,
-}
-
-fn place_template(level: &mut Level, template: &RoomTemplate, x: u32, y: u32) -> Result<RoomId, Error> {
- let region = &mut level.region;
- let width = region.width();
- let height = region.height();
-
- if x >= width || x + template.width() > width || y >= height || y + template.height() > height {
- return Err(Error::OutOfBounds);
- }
-
- for row in 0..template.height() {
- for column in 0..template.width() {
- if region.get(column + x, row + y)?.is_some() {
- return Err(Error::NonEmpty);
- }
- }
- }
-
- let room = Room {
- x,
- y,
- width: template.width(),
- height: template.height(),
- };
- let room_id = level.rooms.len() as u32;
- level.rooms.push(room);
-
- for row in 0..template.height() {
- for column in 0..template.width() {
- region.set(column + x, row + y,
- template.get(column, row)?.clone().map(|t| t.into_tile(room_id)))?;
- }
- }
-
- Ok(room_id)
-}
-
-fn tunnel<R: Rng + ?Sized>(level: &mut Level, from: (u32, u32), to: (u32, u32), owner: Owner, rng: &mut R) -> Result<(), Error> {
-
- let mut from = from;
- let mut connected = false;
-
- while !connected {
-
- let diff: (i64, i64) = (from.0 as i64 - to.0 as i64, from.1 as i64 - to.1 as i64);
- let abs_diff = (i64::abs(diff.0), i64::abs(diff.1));
- let mut direction = if abs_diff.1 < abs_diff.0 {
- if diff.0 < 0 { Direction::RIGHT } else { Direction::LEFT }
- } else {
- if diff.1 < 0 { Direction::DOWN } else { Direction::UP }
- };
-
- let mut steps = match direction {
- Direction::RIGHT | Direction::LEFT => rng.gen_range(0, abs_diff.0) + 1,
- Direction::UP | Direction::DOWN => rng.gen_range(0, abs_diff.1) + 1,
- _ => 1
- };
-
- while steps > 0 {
- let connection = direction;
- let opposite = Direction::opposite(connection);
- let tile = level.region.get_mut(from.0, from.1)?;
- if let Some(tile) = tile {
- tile.connections = tile.connections | connection;
- }
-
- from = Direction::step_towards(direction, from).unwrap();
-
- let tile = level.region.get_mut(from.0, from.1)?;
- match tile {
- Some(tile) => {
- if tile.owner != owner {
- tile.connections = tile.connections | opposite;
- connected = true;
- break;
- }
- },
- None => {
- level.region.set(from.0, from.1, Some(Tile { owner: owner, flags: TILE_IS_PATH, connections: opposite}));
- }
- }
- steps = steps - 1;
- }
- }
- Ok(())
-}
-
-fn connect_rooms<R: Rng + ?Sized>(level: &mut Level, from_id: RoomId, to_id: RoomId, rng: &mut R) -> Result<(), Error> {
-
- let mut candidates = Vec::new();
- let from = level.rooms.get(from_id as usize).ok_or(Error::InvalidRoomId)?;
- let from_center = (from.x + (from.width / 2), from.y + (from.height / 2));
-
- let to = level.rooms.get(to_id as usize).ok_or(Error::InvalidRoomId)?;
- let to_center = (to.x + (to.width / 2), to.y + (to.height / 2));
-
- let diff: (i32, i32) = (from_center.0 as i32 - to_center.0 as i32, from_center.1 as i32 - to_center.1 as i32);
- let abs_diff = (i32::abs(diff.0), i32::abs(diff.1));
-
- let direction = if abs_diff.1 < abs_diff.0 {
- if -1 < diff.0 { Direction::LEFT } else { Direction::RIGHT }
- } else {
- if diff.1 < 0 { Direction::DOWN } else { Direction::UP }
- };
-
- /*Find a suitable starting location*/
- match direction {
- Direction::UP => {
- while candidates.len() < 1 {
- let mut y = from.y;
- for x in from.x..(from.x + from.width) {
- if let Some(tile) = level.region.get(x, y)? {
- candidates.push((x, y));
- }
- }
- y = y + 1;
- }
- },
- Direction::RIGHT => {
- while candidates.len() < 1 {
- let mut x = from.x + from.width - 1;
- for y in from.y..(from.y + from.height) {
- if let Some(tile) = level.region.get(x, y)? {
- candidates.push((x, y));
- }
- }
- x = x - 1;
- }
- },
- Direction::DOWN => {
- while candidates.len() < 1 {
- let mut y = from.y + from.height - 1;
- for x in from.x..(from.x + from.width) {
- if let Some(tile) = level.region.get(x, y)? {
- candidates.push((x, y));
- }
- }
- y = y - 1;
- }
- },
- Direction::LEFT => {
- while candidates.len() < 1 {
- let mut x = from.x;
- for y in from.y..(from.y + from.height) {
- if let Some(tile) = level.region.get(x, y)? {
- candidates.push((x, y));
- }
- }
- x = x + 1;
- }
- },
- _ => unreachable!()
- }
-
- let pos = candidates[rng.gen_range(0, candidates.len())];
- tunnel(level, pos, to_center, Owner::Room(from_id), rng)
-}
-
-fn create_deadend<R: Rng + ?Sized>(level: &mut Level, rng: &mut R) -> Result<(), Error> {
- let mut attempts = 0;
- loop {
- let x = rng.gen_range(0, level.region.width() - 3);
- let y = rng.gen_range(0, level.region.height() - 3);
- let mut empty = true;
-
- for i in 0..3 {
- for j in 0..3 {
- let tile = level.region.get(x + j, y + i)?;
- if tile.is_some() {
- empty = false;
- break;
- }
- }
- }
- attempts = attempts + 1;
- if attempts > 128 {break;}
-
- if empty {
- let rnd_index = rng.gen_range(0, level.rooms.len());
- let room = &mut level.rooms[rnd_index];
- let room_center: (u32, u32) = ((room.x + (room.width/2)), (room.y + (room.height/2)));
- let res = tunnel(level, (x,y), room_center, Owner::Tunnel(level.tunnels), rng);
- level.tunnels += 1;
- return res;
- }
-
- }
- Ok(())
-}
-
-fn empty_neighbours(level: &Level, x: u32, y:u32, options: &mut [u8; 4], neighbours: &mut usize) {
- let region = &level.region;
- if y > 0 && region.get(x, y - 1).unwrap().is_none() {
- options[*neighbours] = Direction::UP;
- *neighbours += 1;
- }
-
- if x + 1 < region.width() && region.get(x + 1, y).unwrap().is_none() {
- options[*neighbours] = Direction::RIGHT;
- *neighbours += 1;
- }
-
- if y + 1 < region.height() && region.get(x, y + 1).unwrap().is_none() {
- options[*neighbours] = Direction::DOWN;
- *neighbours += 1;
- }
-
- if x > 0 && region.get(x - 1, y).unwrap().is_none() {
- options[*neighbours] = Direction::LEFT;
- *neighbours += 1;
- }
-}
-
-fn random_walk<R: Rng + ?Sized>(level: &mut Level, rng: &mut R) -> Result<(), Error> {
- let mut iterations = 0;
- const max_iterations: u32 = 128;
-
- let mut neighbours = 0;
- let mut options = [0; 4];
- let mut x = 0;
- let mut y = 0;
-
- let owner = Owner::Tunnel(level.tunnels);
- level.tunnels += 1;
-
- while neighbours <= 0 && iterations < max_iterations {
- neighbours = 0;
- x = rng.gen_range(0, level.region.width() - 4) + 2;
- y = rng.gen_range(0, level.region.height() - 4) + 2;
-
- if let Some(tile) = level.region.get(x, y)? {
- if tile.flags & TILE_IS_PATH != 0 {
- empty_neighbours(level, x, y, &mut options, &mut neighbours);
- }
- }
- iterations = iterations + 1;
- }
-
- if neighbours <= 0 {
- return Err(Error::NonEmpty);
- }
-
- let direction = options[rng.gen_range(0, neighbours)];
- if let Some(tile) = level.region.get_mut(x, y)? {
- tile.connections |= direction;
- };
-
- let mut pos = (x, y);
- pos = Direction::step_towards(direction, pos).unwrap();
- level.region.set(pos.0, pos.1, Some(Tile { owner: owner, flags: TILE_IS_PATH, connections: Direction::opposite(direction)}));
- let mut iterations = rng.gen_range(0, 2);
-
- while iterations >= 0 {
- neighbours = 0;
- empty_neighbours(level, pos.0, pos.1, &mut options, &mut neighbours);
-
- if neighbours <= 0 {
- return Ok(());
- }
-
- let direction = options[rng.gen_range(0, neighbours)];
- let mut length = rng.gen_range(0, 2) + 1;
- while length > 0 {
- let old_pos = pos.clone();
- let m_pos = Direction::step_towards(direction, pos);
- if let Err(_) = m_pos {
- break;
- } else {
- pos = m_pos.unwrap();
- }
-
- match level.region.get(pos.0, pos.1) {
- Ok(tile) => {
- if tile.is_none() {
- level.region.set(pos.0, pos.1, Some(Tile { owner: owner, flags: TILE_IS_PATH, connections: Direction::opposite(direction)}));
- if let Some(tile) = level.region.get_mut(old_pos.0, old_pos.1)? {
- tile.connections |= direction;
- }
- }
- },
- Err(_) => {
- pos = old_pos;
- length = 0;
- }
- }
- length += -1;
- }
- iterations += -1;
- }
-
- Ok(())
-}
-
-fn pretty_print(level: &Level) {
-
- let region = &level.region;
-
- for x in 0..region.width() { print!(" _"); }
- println!("");
-
- for y in 0..region.height() {
- for x in 0..region.width() {
- if x == 0 { print!("|");}
- let tile = region.get(x, y).unwrap();
- match tile {
- Some(tile) => {
- if tile.connections & Direction::DOWN == 0 { print!("_") } else { print!(" ") }
- if tile.connections & Direction::RIGHT == 0 { print!("|") } else { print!(" ") }
- },
- None => print!("_|"),
- }
- }
- println!("");
- }
-}
-
-fn random_dungeon<R: Rng + ?Sized>(templates: Vec<VecRegion<Option<TileTemplate>>>, mut rng: &mut R) -> Result<Level, Error> {
- let mut region: VecRegion<Option<Tile>> = Region::new_with(15, 15);
- let mut level = Level {
- rooms: Vec::new(),
- region: Box::new(region),
- tunnels: 0,
- };
-
- let mut attempts = 20;
- let first = loop {
- let template = &templates[0];
- let x = rng.gen_range(0, level.region.width() - 2) + 1;
- let y = rng.gen_range(0, level.region.height() - 2) + 1;
- let res = place_template(&mut level, template, x, y);
- if attempts <= 0 && res.is_err() { panic!() }
- if res.is_ok() { break res.unwrap() }
- attempts += -1;
- };
-
- let mut attempts = 20;
- let second = loop {
- let template = &templates[0];
- let x = rng.gen_range(0, level.region.width() - 2) + 1;
- let y = rng.gen_range(0, level.region.height() - 2) + 1;
- let res = place_template(&mut level, template, x, y);
- if attempts <= 0 && res.is_err() { panic!() }
- if res.is_ok() { break res.unwrap() }
- attempts += -1;
- };
-
- connect_rooms(&mut level, first, second, &mut rng)?;
- let mut additional_rooms = rng.gen_range(0, 3) + 4;
-
- while additional_rooms >= 0 {
- let mut attempts = 20;
- let room_id = loop {
- let template = &templates[0];
- let x = rng.gen_range(0, level.region.width() - 2) + 1;
- let y = rng.gen_range(0, level.region.height() - 2) + 1;
- let res = place_template(&mut level, template, x, y);
- if attempts <= 0 && res.is_err() { panic!() }
- if res.is_ok() { break res.unwrap() }
- attempts += -1;
- };
- let to_room = (level.rooms.len() - 1) as u32;
- connect_rooms(&mut level, room_id, rng.gen_range(0, to_room), &mut rng)?;
- additional_rooms += -1;
- }
-
- let mut connections = rng.gen_range(0, 3) + 1;
- while connections > 0 {
- let upper = level.rooms.len();
- let mut from = 0;
- let mut to = 0;
- while to == from {
- from = rng.gen_range(0, upper);
- to = rng.gen_range(0, upper);
- }
- connect_rooms(&mut level, from as u32, to as u32, &mut rng)?;
- connections += -1;
- }
-
- let mut deadends = rng.gen_range(0, 3) + 1;
- while deadends > 0 {
- create_deadend(&mut level, &mut rng)?;
- deadends += -1;
- }
-
- random_walk(&mut level, &mut rng)?;
- random_walk(&mut level, &mut rng)?;
- random_walk(&mut level, &mut rng)?;
-
- Ok(level)
-}
-
-
-fn main() {
- let mut thread_rng = thread_rng();
- //18
- let mut rng = SmallRng::from_seed([14;16]);
-
-
- let mut template:VecRegion<Option<TileTemplate>> = Region::new_with(3, 3);
- use Direction::{UP, DOWN, LEFT, RIGHT};
- template.set(0, 0, Some(TileTemplate { connections: RIGHT | DOWN}));
- template.set(1, 0, Some(TileTemplate { connections: RIGHT | DOWN | LEFT}));
- template.set(2, 0, Some(TileTemplate { connections: DOWN | LEFT}));
-
- template.set(0, 1, Some(TileTemplate { connections: UP | RIGHT | DOWN}));
- template.set(1, 1, Some(TileTemplate { connections: UP | RIGHT | DOWN | LEFT}));
- template.set(2, 1, Some(TileTemplate { connections: UP | DOWN | LEFT}));
-
- template.set(0, 2, Some(TileTemplate { connections: UP | RIGHT}));
- template.set(1, 2, Some(TileTemplate { connections: UP | RIGHT | LEFT}));
- template.set(2, 2, Some(TileTemplate { connections: UP | LEFT}));
-
-
- let mut level: VecRegion<Option<Tile>> = Region::new_with(15, 11);
- let mut level = Level {
- rooms: Vec::new(),
- region: Box::new(level),
- tunnels: 0,
- };
-
- let res = place_template(&mut level, &mut template, 0, 0);
- let res = place_template(&mut level, &mut template, 3, 7);
- let res = place_template(&mut level, &mut template, 5, 2);
- let res = place_template(&mut level, &mut template, 8, 7);
- let res = connect_rooms(&mut level, 0, 1, &mut rng);
- let res = connect_rooms(&mut level, 2, 1, &mut rng);
- let res = connect_rooms(&mut level, 3, 2, &mut rng);
- //let res = connect_rooms(&mut level, 1, 3, &mut rng);
- create_deadend(&mut level, &mut rng);
- random_walk(&mut level, &mut rng);
- random_walk(&mut level, &mut rng);
- random_walk(&mut level, &mut rng);
- //create_deadend(&mut level, &mut rng);
- println!("{:?}", res);
- pretty_print(&level);
-
- let l = random_dungeon(vec!(template), &mut rng).unwrap();
- pretty_print(&l);
- //println!("{:?}", level);
-}