diff options
author | David Blajda <blajda@hotmail.com> | 2019-08-22 02:34:37 +0000 |
---|---|---|
committer | David Blajda <blajda@hotmail.com> | 2019-08-22 02:34:37 +0000 |
commit | c248994052485e62c617f4184c877560c6b92623 (patch) | |
tree | 3da59a98bd219161421e23e0f5750322d473ad13 /src/main.rs |
Init repo
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ad75eaa --- /dev/null +++ b/src/main.rs @@ -0,0 +1,508 @@ +use rand::{Rng, SeedableRng}; +use rand::thread_rng; +use rand::rngs::SmallRng; + + +/*Notes + * + * It's entirely possible that the compiler inlined some functions + * so I dont need to to strive 1-to-1 parity. + * Tunelling procedure performed in deadend and connect rooms + * can be placed in a separate definition. + */ + +type RoomId = u32; + +enum LevelBuilderError { + OutOfBounds, + InvalidRoomId, +} + +#[derive(Debug)] +enum Direction { + None = 0, + Up = 1 << 1, + Right = 1 << 2, + Down = 1 << 3, + Left = 1 << 4, +} + +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<(), u32>; + fn get(&self, x: u32, y: u32) -> Result<&T, u32>; + fn get_mut(&mut self, x: u32, y: u32) -> Result<&mut T, u32>; +} + +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<(), u32> { + if x >= self.width || y >= self.height { + return Err(1); + } + + self.inner[(y * self.width + x) as usize] = value; + Ok(()) + } + + fn get(&self, x: u32, y:u32) -> Result<&T, u32> { + if x >= self.width || y >= self.height { + return Err(1); + } + + Ok(&self.inner[(y * self.width + x) as usize]) + } + + fn get_mut(&mut self, x: u32, y: u32) -> Result<&mut T, u32> { + if x >= self.width || y >= self.height { + return Err(1); + } + + 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, + } + } +} + +#[derive(Clone, Debug)] +struct Tile { + id: u16, + room: RoomId, +} + +#[derive(Clone)] +struct TileTemplate { + id: u16, +} + +impl TileTemplate { + fn into_tile(&self, room: RoomId) -> Tile { + Tile { + id: self.id, + room: room, + } + } +} + +//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>, +} + +fn place_template(level: &mut Level, template: &RoomTemplate, x: u32, y: u32) -> Result<(RoomId), (u32)> { + let region = &mut level.region; + if x >= region.width() || y >= region.height() { + return Err(1); + } + + for row in 0..template.height() { + for column in 0..template.width() { + if region.get(column + x, row + y)?.is_some() { + return Err(2); + } + } + } + + 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(row, column)?.clone().map(|t| t.into_tile(room_id)))?; + } + } + + Ok(room_id) +} + +fn connect_rooms<R: Rng + ?Sized>(level: &mut Level, from_id: RoomId, to_id: RoomId, rng: &mut R) -> Result<(), u32> { + + let mut candidates = Vec::new(); + let from = level.rooms.get(from_id as usize).ok_or(2 as u32)?; + let from_center = (from.x + (from.width / 2), from.y + (from.height / 2)); + + let to = level.rooms.get(to_id as usize).ok_or(2 as u32)?; + 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 } + }; + + println!("{:?}", direction); + /*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; + } + }, + Direction::None => unreachable!("direction is somehow None") + } + + /*TODO: Pick a random point*/ + let mut pos = candidates[rng.gen_range(0, candidates.len())]; + 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 } + }; + println!("{:?}", direction); + + /*TODO: Pick an random number of steps*/ + let mut steps = match direction { + Direction::Right | Direction::Left => { + rng.gen_range(0, abs_diff.0 - (from.width / 2) as i32) + 1 + }, + Direction::Up | Direction::Down => { + rng.gen_range(0, abs_diff.1 - (from.height / 2) as i32) + 1 + }, + Direction::None => unreachable!("direction is somehow None") + }; + + /*Start tunneling*/ + let mut connected = false; + while !connected { + while steps > 0 { + /*TODO: Locate possible link neighbours*/ + match direction { + Direction::Up => pos.1 = pos.1 - 1, + Direction::Right => pos.0 = pos.0 + 1, + Direction::Down => pos.1 = pos.1 + 1, + Direction::Left => pos.0 = pos.0 - 1, + _ => {}, + } + let tile = level.region.get(pos.0, pos.1)?; + match tile { + Some(tile) => { + if tile.room != from_id { + connected = true; + break; + } + }, + None => { + println!("SET -> {} {}", pos.0, pos.1); + level.region.set(pos.0, pos.1, Some(Tile { id: 1, room: from_id})); + } + } + steps = steps - 1; + } + + let diff: (i32, i32) = (pos.0 as i32 - to_center.0 as i32, pos.1 as i32 - to_center.1 as i32); + let abs_diff = (i32::abs(diff.0), i32::abs(diff.1)); + + direction = if abs_diff.1 < abs_diff.0 { + if -1 < diff.0 { Direction::Left } else { Direction::Right } + } else { + if -1 < diff.1 { Direction::Up } else { Direction::Down } + }; + println!("{:?}", direction); + + steps = match direction { + Direction::Right | Direction::Left => { + let diff = i32::abs(abs_diff.0 - (from.width / 2) as i32); + if diff == 0 { 1 } else { rng.gen_range(0, diff) + 1 } + }, + Direction::Up | Direction::Down => { + let diff = i32::abs(abs_diff.1 - (from.height / 2) as i32); + if diff == 0 { 1 } else { rng.gen_range(0, diff) + 1 } + }, + _ => 0 + }; + } + + + Ok(()) +} + +fn create_deadend<R: Rng + ?Sized>(level: &mut Level, rng: &mut R) -> Result<(), u32> { + 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: (i32, i32) = ((room.x + (room.width/2)) as i32, (room.y + (room.height/2)) as i32); + let diff: (i32, i32) = (x as i32 - room_center.0, y as i32 - room_center.1); + let diff_abs: (i32, i32) = (i32::abs(diff.0), i32::abs(diff.1)); + + let mut direction = if diff_abs.1 < diff_abs.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, diff_abs.0) + 1 + }, + Direction::Up | Direction::Down => { + rng.gen_range(0, diff_abs.1) + 1 + }, + Direction::None => unreachable!("direction is somehow None") + }; + + /*Only major difference*/ + /* + if 1 < steps { + steps = 2; + } + */ + + /*The tunneler subroutine is simillar to before*/ + let mut connected = false; + let mut pos = (x, y); + while !connected { + while 0 < steps { + /*TODO: Locate possible link neighbours*/ + match direction { + Direction::Up => pos.1 = pos.1 - 1, + Direction::Right => pos.0 = pos.0 + 1, + Direction::Down => pos.1 = pos.1 + 1, + Direction::Left => pos.0 = pos.0 - 1, + _ => {}, + } + let tile = level.region.get(pos.0, pos.1)?; + match tile { + Some(tile) => { + if tile.room != 9 { + connected = true; + steps = 0; + break; + } + }, + None => { + println!("SET -> {} {} to {}", pos.0, pos.1, 9); + level.region.set(pos.0, pos.1, Some(Tile { id: 1, room: 9})); + } + } + steps = steps - 1; + } + + let diff: (i32, i32) = (pos.0 as i32 - room_center.0 as i32, pos.1 as i32 - room_center.1 as i32); + let abs_diff = (i32::abs(diff.0), i32::abs(diff.1)); + + direction = if abs_diff.1 < abs_diff.0 { + if -1 < diff.0 { Direction::Left } else { Direction::Right } + } else { + if -1 < diff.1 { Direction::Up } else { Direction::Down } + }; + println!("{:?}", direction); + + steps = match direction { + Direction::Right | Direction::Left => { + let diff = i32::abs((pos.0 as i32 - room_center.0) as i32); + if diff == 0 { 1 } else { rng.gen_range(0, diff) + 1 } + }, + Direction::Up | Direction::Down => { + let diff = i32::abs((pos.1 as i32 - room_center.1) as i32); + if diff == 0 { 1 } else { rng.gen_range(0, diff) + 1 } + }, + _ => 0 + }; + } + return Ok(()); + } + + } + Ok(()) +} + + +fn print_level(level: &Level) { + let region = &level.region; + + for y in 0..region.height() { + for x in 0..region.width() { + let tile = region.get(x, y).unwrap(); + match tile { + Some(tile) => print!("{:?}", tile.room), + None => print!("+"), + } + } + println!(""); + } +} + +/* +impl LevelBuilder { + + fn build(self) -> Result<u32, LevelBuilderError> { + Ok(0) + } + + fn add_room(&self, description: RoomDescription, x: u32, y: u32) -> Result<RoomId, LevelBuilderError> { + if x > self + Ok(0) + } + + fn connect_rooms(&self, from: RoomId, to: RoomId) -> Result<(), LevelBuilderError> { + Ok(()) + } +} + +/* +LevelBuilder::new() + .add_room(r1) + .add_room(r2) + .connect(r1, r2) + .add_room(r3) + .connect(r2, r3) + .add_terminals() + .build().unwrap() + */ + +fn add_room(room_desc: u32) -> Result<u32, u32> { + Ok(0) +} + +fn connect_rooms(from: u32, to: u32) -> Result<u32, u32> { + Ok(0) +} + +fn add_terminals() -> Result<u32, u32> { + Ok(0) +} +*/ + +fn main() { + println!("Hello, world!"); + let mut thread_rng = thread_rng(); + //18 + let mut rng = SmallRng::from_seed([18;16]); + + + let mut template:VecRegion<Option<TileTemplate>> = Region::new_with(3, 3); + let mut level: VecRegion<Option<Tile>> = Region::new_with(15, 10); + let mut level = Level { + rooms: Vec::new(), + region: Box::new(level), + }; + + for row in 0..3 { + for col in 0..3 { + template.set(row, col, Some(TileTemplate { id: 1})); + } + } + 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); + print_level(&level); + let res = connect_rooms(&mut level, 0, 1, &mut rng); + let res = connect_rooms(&mut level, 1, 2, &mut rng); + let res = connect_rooms(&mut level, 3, 2, &mut rng); + create_deadend(&mut level, &mut rng); + create_deadend(&mut level, &mut rng); + println!("{:?}", res); + print_level(&level); + //println!("{:?}", level); +} |