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, } struct LevelBuilder { } trait Region { 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 { inner: Vec, width: u32, height: u32, } impl Region for VecRegion { 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 Region { fn new_with(width: u32, height: u32) -> VecRegion { 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>; type RoomTemplate = Region>; struct Room { x: u32, y: u32, width: u32, height: u32, } struct Level { region: Box>>, rooms: Vec, } 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 tunnel(level: &mut Level, from: (u32, u32), to: (u32, u32), from_id: RoomId, rng: &mut R) -> Result<(), u32> { let mut from = from; 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)); println!("{:?} {:?}", diff, abs_diff); println!("{:?} {:?}", from, to); 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) + 1 }, Direction::Up | Direction::Down => { rng.gen_range(0, abs_diff.1) + 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 => from.1 = from.1 - 1, Direction::Right => from.0 = from.0 + 1, Direction::Down => from.1 = from.1 + 1, Direction::Left => from.0 = from.0 - 1, _ => {}, } let tile = level.region.get(from.0, from.1)?; match tile { Some(tile) => { if tile.room != from_id { connected = true; break; } }, None => { println!("SET -> {} {}", from.0, from.1); level.region.set(from.0, from.1, Some(Tile { id: 1, room: from_id})); } } steps = steps - 1; } 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)); 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 => { if abs_diff.0 == 0 { 1 } else { rng.gen_range(0, abs_diff.0) + 1 } }, Direction::Up | Direction::Down => { if abs_diff.1 == 0 { 1 } else { rng.gen_range(0, abs_diff.1) + 1 } }, _ => 0 }; } Ok(()) } fn connect_rooms(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())]; tunnel(level, pos, to_center, from_id, rng) } fn create_deadend(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 { Ok(0) } fn add_room(&self, description: RoomDescription, x: u32, y: u32) -> Result { 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 { Ok(0) } fn connect_rooms(from: u32, to: u32) -> Result { Ok(0) } fn add_terminals() -> Result { 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> = Region::new_with(3, 3); let mut level: VecRegion> = 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); }