summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs508
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);
+}