use petgraph::algo::min_spanning_tree; use petgraph::data::*; use petgraph::graph::Edge; use petgraph::graph::Graph; use petgraph::graph::UnGraph; use rand::Rng; use rand::{rngs::ThreadRng, seq::SliceRandom}; use crate::artifacts::Artifact; use crate::artifacts::Chest; use crate::artifacts::Potion; use crate::constants::get_monsters_per_level; use crate::constants::ROOM_HEIGHT; use crate::constants::ROOM_WIDTH; use crate::monster::create_monster_by_type; use crate::monster::Monster; use crate::position::Position; use crate::room::Connection; use crate::{ constants::{ get_room_type_per_level, LEVEL_HEIGHT, LEVEL_WIDTH, ROOMS_HORIZONTAL, ROOMS_VERTICAL, }, level::{Level, StructureElement}, room::{Room, RoomType}, }; type GeneratorGraph = Graph<(usize, usize), usize, petgraph::Undirected>; pub struct LevelGenerator { level: usize, rooms: [[Room; ROOMS_VERTICAL]; ROOMS_HORIZONTAL], rng: ThreadRng, } enum Direction { Horizontal, Vertical, } impl LevelGenerator { fn generate_rooms_to_place( rng: &mut ThreadRng, level: usize, first: bool, last: bool, ) -> Vec { let mut rooms_to_place: Vec = Vec::with_capacity(ROOMS_VERTICAL * ROOMS_HORIZONTAL); let mut start_room = Room::new(rng); if first { start_room.kind = RoomType::Start; } else { start_room.kind = RoomType::StairUp; } rooms_to_place.push(start_room); for _ in 2..ROOMS_HORIZONTAL * ROOMS_VERTICAL { let mut room = Room::new(rng); room.kind = LevelGenerator::select_room_type(level, rng); if room.kind != RoomType::EmptyRoom { rooms_to_place.push(room); } } let mut end_room = Room::new(rng); if last { end_room.kind = RoomType::End; } else { end_room.kind = RoomType::StairDown; } rooms_to_place.push(end_room); rooms_to_place } fn place_rooms( rng: &mut ThreadRng, rooms_to_place: &mut Vec, ) -> [[Room; ROOMS_VERTICAL]; ROOMS_HORIZONTAL] { let mut rooms: [[Room; 7]; 8] = [[Room::new(rng); ROOMS_VERTICAL]; ROOMS_HORIZONTAL]; let mut room_row = rng.gen_range(0..ROOMS_VERTICAL); let mut room_col = rng.gen_range(0..ROOMS_HORIZONTAL); rooms[room_col][room_row] = rooms_to_place.pop().unwrap(); while let Some(room) = rooms_to_place.pop() { let mut placed = false; // randomize going horizontal or vertical let mut directions_to_try = vec![Direction::Horizontal, Direction::Vertical]; directions_to_try.shuffle(rng); while !directions_to_try.is_empty() { match directions_to_try.pop().unwrap() { Direction::Horizontal => { let mut free_cols: Vec = vec![]; for (col, room_col) in rooms.iter().enumerate().take(ROOMS_HORIZONTAL) { if room_col[room_row].kind == RoomType::EmptyRoom { free_cols.push(col); } } if free_cols.is_empty() { continue; } free_cols.shuffle(rng); room_col = *free_cols.first().unwrap(); rooms[room_col][room_row] = room; placed = true; break; } Direction::Vertical => { let mut free_rows: Vec = vec![]; for row in 0..ROOMS_VERTICAL { if rooms[room_col][row].kind == RoomType::EmptyRoom { free_rows.push(row); } } if free_rows.is_empty() { continue; } free_rows.shuffle(rng); room_row = *free_rows.first().unwrap(); rooms[room_col][room_row] = room; placed = true; break; } } } // all fields in the row/column was full so we can place it at any empty position if !placed { let mut free_pos: Vec<(usize, usize)> = vec![]; for (col, room_col) in rooms.iter().enumerate().take(ROOMS_HORIZONTAL) { for (row, room) in room_col.iter().enumerate().take(ROOMS_VERTICAL) { if room.kind == RoomType::EmptyRoom { free_pos.push((col, row)); } } } let selected_pos = free_pos[rng.gen_range(0..free_pos.len())]; rooms[selected_pos.0][selected_pos.1] = room; } } rooms } fn create_mst(rooms: &[[Room; ROOMS_VERTICAL]; ROOMS_HORIZONTAL]) -> GeneratorGraph { // calculate the weight of an edge fn calc_edge_weight(delta: usize) -> usize { delta * delta } let mut graph: GeneratorGraph = UnGraph::<(usize, usize), usize>::default(); for (col, tgt_col) in rooms.iter().enumerate().take(ROOMS_HORIZONTAL) { for (row, room) in tgt_col.iter().enumerate().take(ROOMS_VERTICAL) { if room.kind != RoomType::EmptyRoom { graph.add_node((col, row)); } } } for col in 0..ROOMS_HORIZONTAL { for row in 0..ROOMS_VERTICAL { if rooms[col][row].kind == RoomType::EmptyRoom { continue; } if let Some(src_index) = graph.node_indices().find(|i| graph[*i] == (col, row)) { for (col_1, tgt_col) in rooms .iter() .enumerate() .take(ROOMS_HORIZONTAL) .skip(col + 1) { if tgt_col[row].kind != RoomType::EmptyRoom { if let Some(tgt_index) = graph.node_indices().find(|i| graph[*i] == (col_1, row)) { graph.add_edge( src_index, tgt_index, calc_edge_weight(col.abs_diff(col_1)), ); break; } } } for row_1 in row + 1..ROOMS_VERTICAL { if rooms[col][row_1].kind != RoomType::EmptyRoom { if let Some(tgt_index) = graph.node_indices().find(|i| graph[*i] == (col, row_1)) { graph.add_edge( src_index, tgt_index, calc_edge_weight(row.abs_diff(row_1)), ); break; } } } } } } let mut mst: GeneratorGraph = Graph::from_elements(min_spanning_tree(&graph)); // readd some of the short edges that were remove (all between a weight of 1 to 10) let initial_edges = graph.raw_edges(); let minimal_edges = mst.raw_edges(); let mut extra_edges: Vec> = vec![]; for init_edge in initial_edges { let start_node = init_edge.source(); let end_node = init_edge.target(); let mut found = false; for min_edge in minimal_edges { if min_edge.source() == start_node && min_edge.target() == end_node { found = true; break; } } if !found { extra_edges.push(init_edge.clone()); } } extra_edges.sort_by(|a, b| a.weight.cmp(&b.weight)); for e in extra_edges .iter() .filter(|x| x.weight > 1) .filter(|x| x.weight < 7) { mst.add_edge(e.source(), e.target(), e.weight); } mst } fn create_connections( rooms: &mut [[Room; ROOMS_VERTICAL]; ROOMS_HORIZONTAL], mst: &GeneratorGraph, ) { for edge in mst.raw_edges() { // the tuples are (col, row) let (src_node_col, src_node_row) = mst[edge.source()]; let (tgt_node_col, tgt_node_row) = mst[edge.target()]; let src_room = rooms[src_node_col][src_node_row]; let tgt_room = rooms[tgt_node_col][tgt_node_row]; if src_node_col == tgt_node_col { // Right let start_col = src_node_col * ROOM_WIDTH + src_room.offset_x + (src_room.width + 1) / 2; let start_row = src_node_row * ROOM_HEIGHT + src_room.offset_y + src_room.height + 1; let end_col = tgt_node_col * ROOM_WIDTH + tgt_room.offset_x + (tgt_room.width + 1) / 2; let end_row = 1 + tgt_node_row * ROOM_HEIGHT - 1 + tgt_room.offset_y; rooms[src_node_col][src_node_row].connection_down = Some(Connection { start_pos: (start_col, start_row), end_pos: (end_col, end_row), }); } else { // Down let start_col = 1 + (src_node_col * ROOM_WIDTH) + src_room.offset_x + src_room.width; let start_row = 1 + src_node_row * ROOM_HEIGHT + src_room.offset_y + (src_room.height - 1) / 2; let end_col = 1 + tgt_node_col * ROOM_WIDTH - 1 + tgt_room.offset_x; let end_row = 1 + tgt_node_row * ROOM_HEIGHT + tgt_room.offset_y + (tgt_room.height - 1) / 2; rooms[src_node_col][src_node_row].connection_right = Some(Connection { start_pos: (start_col, start_row), end_pos: (end_col, end_row), }); } } } pub fn generate(level: usize, first: bool, last: bool) -> Self { let mut rng = rand::thread_rng(); let mut rooms_to_place: Vec = LevelGenerator::generate_rooms_to_place(&mut rng, level, first, last); let mut rooms: [[Room; 7]; 8] = LevelGenerator::place_rooms(&mut rng, &mut rooms_to_place); let mst: GeneratorGraph = LevelGenerator::create_mst(&rooms); LevelGenerator::create_connections(&mut rooms, &mst); LevelGenerator { level, rooms, rng } } fn select_monster(position: Position, rng: &mut ThreadRng) -> Box { let level = position.get_level(); let value = rng.gen_range(1..=100); let t = get_monsters_per_level(); if level < t.len() { for (mtype, range) in &t[level] { if range.contains(&value) { return create_monster_by_type(mtype, position); } } } panic!("no monster selectable!"); } fn select_room_type(level: usize, rng: &mut ThreadRng) -> RoomType { let value = rng.gen_range(1..=100); let t = get_room_type_per_level(); if level < t.len() { for (mtype, range) in &t[level] { if range.contains(&value) { return *mtype; } } } panic!("no room selectable!"); } pub fn render(&mut self) -> Level { let mut structure = [[StructureElement::Wall; LEVEL_HEIGHT]; LEVEL_WIDTH]; let mut start_pos = (0, 0); let mut end_pos = (0, 0); let mut monsters: Vec> = Vec::with_capacity(10); let mut artifacts: Vec> = Vec::with_capacity(10); for col in 0..ROOMS_HORIZONTAL { for row in 0..ROOMS_VERTICAL { let room = self.rooms[col][row]; let position = room.render(&mut structure, col, row); match room.kind { RoomType::Start => start_pos = position, RoomType::End => end_pos = position, RoomType::StairUp => start_pos = position, RoomType::StairDown => end_pos = position, RoomType::BasicRoom => {} RoomType::ArtifactRoom => { match self.rng.gen_range(1..=100) { 1..=50 => { artifacts.push(Box::new(Chest::new(Position::new( self.level, position.0, position.1, )))); } _ => { artifacts.push(Box::new(Potion::new(Position::new( self.level, position.0, position.1, )))); } }; } RoomType::MonsterRoom => { monsters.push(LevelGenerator::select_monster( Position::new(self.level, position.0, position.1), &mut self.rng, )); } RoomType::EmptyRoom => {} } } } for col in 0..ROOMS_HORIZONTAL { for row in 0..ROOMS_VERTICAL { if let Some(connection) = self.rooms[col][row].connection_down { // println!("down"); connection.render(&mut self.rng, &mut structure); } if let Some(connection) = self.rooms[col][row].connection_right { // println!("right"); connection.render(&mut self.rng, &mut structure); } } } Level { level: self.level, structure, discovered: [[false; LEVEL_HEIGHT]; LEVEL_WIDTH], monsters, artifacts, start: start_pos, end: end_pos, rng: rand::thread_rng(), } } } #[test] fn test_level_gen() { for _ in 0..1000 { LevelGenerator::generate(0, true, false).render(); } } #[test] fn test_level_gen_respects_level() { let level = LevelGenerator::generate(0, true, false).render(); assert_eq!(0, level.level); let level = LevelGenerator::generate(1, true, false).render(); assert_eq!(1, level.level); } #[cfg(test)] fn find_room_types(rooms: &Vec) -> (bool, bool, bool, bool) { let mut start_found = false; let mut end_found: bool = false; let mut down_found: bool = false; let mut up_found: bool = false; for room in rooms { if room.kind == RoomType::Start { start_found = true; } if room.kind == RoomType::End { end_found = true; } if room.kind == RoomType::StairDown { down_found = true; } if room.kind == RoomType::StairUp { up_found = true; } } (start_found, up_found, down_found, end_found) } #[test] fn test_rooms_to_place_first_level() { let mut rng = rand::thread_rng(); let res = LevelGenerator::generate_rooms_to_place(&mut rng, 0, true, false); assert!( res.len() <= ROOMS_HORIZONTAL * ROOMS_VERTICAL, "too many rooms created" ); assert!(0 < res.len(), "too many rooms created"); let (start_found, up_found, down_found, end_found) = find_room_types(&res); assert!(start_found); assert!(!end_found); assert!(down_found); assert!(!up_found); } #[test] fn test_rooms_to_place_middle_level() { let mut rng = rand::thread_rng(); let res = LevelGenerator::generate_rooms_to_place(&mut rng, 1, false, false); assert!( res.len() <= ROOMS_HORIZONTAL * ROOMS_VERTICAL, "too many rooms created" ); assert!(0 < res.len(), "too many rooms created"); let (start_found, up_found, down_found, end_found) = find_room_types(&res); assert!(!start_found); assert!(!end_found); assert!(down_found); assert!(up_found); } #[test] fn test_rooms_to_place_last_level() { let mut rng = rand::thread_rng(); let res = LevelGenerator::generate_rooms_to_place(&mut rng, 2, false, true); assert!( res.len() <= ROOMS_HORIZONTAL * ROOMS_VERTICAL, "too many rooms created" ); assert!(0 < res.len(), "too many rooms created"); let (start_found, up_found, down_found, end_found) = find_room_types(&res); assert!(!start_found); assert!(end_found); assert!(!down_found); assert!(up_found); } #[cfg(test)] fn check_valid_placement(rooms: &[[Room; ROOMS_VERTICAL]; ROOMS_HORIZONTAL]) -> bool { for col in 0..ROOMS_HORIZONTAL { for row in 0..ROOMS_VERTICAL { if rooms[col][row].kind != RoomType::EmptyRoom { let mut count = 0; for test_col in 0..ROOMS_HORIZONTAL { if rooms[test_col][row].kind != RoomType::EmptyRoom { count += 1; } } for test_row in 0..ROOMS_VERTICAL { if rooms[col][test_row].kind != RoomType::EmptyRoom { count += 1; } } if count < 3 { return false; } } } } true } #[cfg(test)] fn count_rooms(rooms: &[[Room; ROOMS_VERTICAL]; ROOMS_HORIZONTAL]) -> usize { let mut res = 0; for col in 0..ROOMS_HORIZONTAL { for row in 0..ROOMS_VERTICAL { if rooms[col][row].kind != RoomType::EmptyRoom { res += 1; } } } res } #[test] fn test_place_rooms() { let mut rng = rand::thread_rng(); for count in 2..ROOMS_HORIZONTAL * ROOMS_VERTICAL { let mut rooms: Vec = vec![Room::new(&mut rng), Room::new(&mut rng)]; rooms[0].kind = RoomType::Start; rooms[1].kind = RoomType::End; for t in 2..count { rooms.push(Room::new(&mut rng)); rooms[t].kind = RoomType::BasicRoom; } let res = LevelGenerator::place_rooms(&mut rng, &mut rooms); assert_eq!(count_rooms(&res), count, "counting {}", count); assert!(check_valid_placement(&res)); } } #[test] fn test_create_mst() { let mut rng = rand::thread_rng(); let mut rooms = [[Room::new(&mut rng); ROOMS_VERTICAL]; ROOMS_HORIZONTAL]; let res: GeneratorGraph = LevelGenerator::create_mst(&rooms); assert_eq!(res.node_count(), 0); assert_eq!(res.edge_count(), 0); rooms[1][1].kind = RoomType::BasicRoom; let res = LevelGenerator::create_mst(&rooms); assert_eq!(res.node_count(), 1); assert_eq!(res.edge_count(), 0); rooms[1][3].kind = RoomType::BasicRoom; let res = LevelGenerator::create_mst(&rooms); assert_eq!(res.node_count(), 2); assert_eq!(res.edge_count(), 1); rooms[3][1].kind = RoomType::BasicRoom; let res = LevelGenerator::create_mst(&rooms); assert_eq!(res.node_count(), 3); assert_eq!(res.edge_count(), 2); rooms[3][3].kind = RoomType::BasicRoom; let res = LevelGenerator::create_mst(&rooms); assert_eq!(res.node_count(), 4); assert_eq!(res.edge_count(), 4); rooms[3][5].kind = RoomType::BasicRoom; let res = LevelGenerator::create_mst(&rooms); assert_eq!(res.node_count(), 5); assert_eq!(res.edge_count(), 5); } #[test] fn test_create_connections() { let mut rng = rand::thread_rng(); let mut rooms = [[Room::new(&mut rng); ROOMS_VERTICAL]; ROOMS_HORIZONTAL]; rooms[1][0].width = ROOM_WIDTH - 1; rooms[1][0].offset_x = 0; rooms[1][0].height = ROOM_HEIGHT - 1; rooms[1][0].offset_y = 0; rooms[1][0].kind = RoomType::BasicRoom; rooms[3][0].width = ROOM_WIDTH - 1; rooms[3][0].offset_x = 0; rooms[3][0].height = ROOM_HEIGHT - 1; rooms[3][0].offset_y = 0; rooms[3][0].kind = RoomType::BasicRoom; let mst: GeneratorGraph = LevelGenerator::create_mst(&rooms); println!("{:?}", mst); LevelGenerator::create_connections(&mut rooms, &mst); assert!(rooms[1][0].connection_right.is_some()); assert_eq!( rooms[1][0].connection_right.unwrap().start_pos, (18, 3), "wrong start" ); assert_eq!( rooms[1][0].connection_right.unwrap().end_pos, (27, 3), "wrong end" ); assert!(rooms[1][0].connection_down.is_none()); assert!(rooms[3][0].connection_down.is_none()); assert!(rooms[3][0].connection_right.is_none()); } #[test] fn test_create_connections_2() { let mut rng = rand::thread_rng(); let mut rooms = [[Room::new(&mut rng); ROOMS_VERTICAL]; ROOMS_HORIZONTAL]; rooms[1][0].width = ROOM_WIDTH - 1; rooms[1][0].offset_x = 0; rooms[1][0].height = ROOM_HEIGHT - 1; rooms[1][0].offset_y = 0; rooms[1][0].kind = RoomType::BasicRoom; rooms[3][0].width = ROOM_WIDTH - 2; rooms[3][0].offset_x = 1; rooms[3][0].height = ROOM_HEIGHT - 1; rooms[3][0].offset_y = 0; rooms[3][0].kind = RoomType::BasicRoom; let mst: GeneratorGraph = LevelGenerator::create_mst(&rooms); LevelGenerator::create_connections(&mut rooms, &mst); assert!(rooms[1][0].connection_right.is_some()); assert_eq!( rooms[1][0].connection_right.unwrap().start_pos, (18, 3), "wrong start" ); assert_eq!( rooms[1][0].connection_right.unwrap().end_pos, (28, 3), "wrong end" ); assert!(rooms[1][0].connection_down.is_none()); assert!(rooms[3][0].connection_down.is_none()); assert!(rooms[3][0].connection_right.is_none()); } #[test] fn test_create_connections_d_1() { let mut rng = rand::thread_rng(); let mut rooms = [[Room::new(&mut rng); ROOMS_VERTICAL]; ROOMS_HORIZONTAL]; rooms[0][1].width = ROOM_WIDTH - 1; rooms[0][1].offset_x = 0; rooms[0][1].height = ROOM_HEIGHT - 1; rooms[0][1].offset_y = 0; rooms[0][1].kind = RoomType::BasicRoom; rooms[0][3].width = ROOM_WIDTH - 1; rooms[0][3].offset_x = 0; rooms[0][3].height = ROOM_HEIGHT - 1; rooms[0][3].offset_y = 0; rooms[0][3].kind = RoomType::BasicRoom; let mst: GeneratorGraph = LevelGenerator::create_mst(&rooms); println!("{:?}", mst); LevelGenerator::create_connections(&mut rooms, &mst); assert!(rooms[0][1].connection_down.is_some()); assert!(rooms[0][1].connection_right.is_none()); assert!(rooms[0][3].connection_down.is_none()); assert!(rooms[0][3].connection_right.is_none()); assert_eq!( rooms[0][1].connection_down.unwrap().start_pos, (4, 12), "wrong start" ); assert_eq!( rooms[0][1].connection_down.unwrap().end_pos, (4, 18), "wrong end" ); } #[test] fn test_create_connections_d_2() { // test full sized rooms for downwards connection let mut rng = rand::thread_rng(); let mut rooms = [[Room::new(&mut rng); ROOMS_VERTICAL]; ROOMS_HORIZONTAL]; rooms[1][1].width = ROOM_WIDTH - 1; rooms[1][1].offset_x = 0; rooms[1][1].height = ROOM_HEIGHT - 1; rooms[1][1].offset_y = 0; rooms[1][1].kind = RoomType::BasicRoom; rooms[1][3].width = ROOM_WIDTH - 1; rooms[1][3].offset_x = 0; rooms[1][3].height = ROOM_HEIGHT - 1; rooms[1][3].offset_y = 0; rooms[1][3].kind = RoomType::BasicRoom; let mst: GeneratorGraph = LevelGenerator::create_mst(&rooms); LevelGenerator::create_connections(&mut rooms, &mst); assert!(rooms[1][1].connection_down.is_some()); assert_eq!( rooms[1][1].connection_down.unwrap().start_pos, (13, 12), "wrong start" ); assert_eq!( rooms[1][1].connection_down.unwrap().end_pos, (13, 18), "wrong end" ); assert!(rooms[1][1].connection_right.is_none()); assert!(rooms[1][2].connection_down.is_none()); assert!(rooms[1][2].connection_right.is_none()); } #[test] fn test_create_connections_d_3() { // test reduced width rooms (with overlap) for downwards connection let mut rng = rand::thread_rng(); let mut rooms = [[Room::new(&mut rng); ROOMS_VERTICAL]; ROOMS_HORIZONTAL]; rooms[1][1].width = 5; rooms[1][1].offset_x = 0; rooms[1][1].height = ROOM_HEIGHT - 1; rooms[1][1].offset_y = 0; rooms[1][1].kind = RoomType::BasicRoom; rooms[1][3].width = 5; rooms[1][3].offset_x = 4; rooms[1][3].height = ROOM_HEIGHT - 1; rooms[1][3].offset_y = 0; rooms[1][3].kind = RoomType::BasicRoom; let mst: GeneratorGraph = LevelGenerator::create_mst(&rooms); LevelGenerator::create_connections(&mut rooms, &mst); assert!(rooms[1][1].connection_down.is_some()); assert_eq!( rooms[1][1].connection_down.unwrap().start_pos, (ROOM_WIDTH + 3, ROOM_HEIGHT * 2), "wrong start" ); assert_eq!( rooms[1][1].connection_down.unwrap().end_pos, (ROOM_WIDTH + 7, ROOM_HEIGHT * 3), "wrong end" ); assert!(rooms[1][1].connection_right.is_none()); assert!(rooms[1][3].connection_down.is_none()); assert!(rooms[1][3].connection_right.is_none()); } #[test] fn test_create_connections_d_4() { // test reduced width rooms (with NO overlap) for downwards connection let mut rng = rand::thread_rng(); let mut rooms = [[Room::new(&mut rng); ROOMS_VERTICAL]; ROOMS_HORIZONTAL]; rooms[1][1].width = 3; rooms[1][1].offset_x = 0; rooms[1][1].height = ROOM_HEIGHT - 1; rooms[1][1].offset_y = 0; rooms[1][1].kind = RoomType::BasicRoom; rooms[1][3].width = 3; rooms[1][3].offset_x = 5; rooms[1][3].height = ROOM_HEIGHT - 1; rooms[1][3].offset_y = 0; rooms[1][3].kind = RoomType::BasicRoom; let mst: GeneratorGraph = LevelGenerator::create_mst(&rooms); LevelGenerator::create_connections(&mut rooms, &mst); assert!(rooms[1][1].connection_down.is_some()); assert_eq!( rooms[1][1].connection_down.unwrap().start_pos, (ROOM_WIDTH + 2, ROOM_HEIGHT * 2), "wrong start" ); assert_eq!( rooms[1][1].connection_down.unwrap().end_pos, (ROOM_WIDTH + 7, ROOM_HEIGHT * 3), "wrong end" ); assert!(rooms[1][1].connection_right.is_none()); assert!(rooms[1][3].connection_down.is_none()); assert!(rooms[1][3].connection_right.is_none()); } #[test] fn test_create_connections_d_5() { // test reduced and moved width rooms (with NO overlap) for downwards connection let mut rng = rand::thread_rng(); let mut rooms = [[Room::new(&mut rng); ROOMS_VERTICAL]; ROOMS_HORIZONTAL]; rooms[1][1].width = 3; rooms[1][1].offset_x = 0; rooms[1][1].height = 4; rooms[1][1].offset_y = 0; rooms[1][1].kind = RoomType::BasicRoom; rooms[1][3].width = 3; rooms[1][3].offset_x = 5; rooms[1][3].height = 4; rooms[1][3].offset_y = 1; rooms[1][3].kind = RoomType::BasicRoom; let mst: GeneratorGraph = LevelGenerator::create_mst(&rooms); LevelGenerator::create_connections(&mut rooms, &mst); assert!(rooms[1][1].connection_down.is_some()); assert_eq!( rooms[1][1].connection_down.unwrap().start_pos, (11, 11), "wrong start" ); assert_eq!( rooms[1][1].connection_down.unwrap().end_pos, (16, 19), "wrong end" ); assert!(rooms[1][1].connection_right.is_none()); assert!(rooms[1][3].connection_down.is_none()); assert!(rooms[1][3].connection_right.is_none()); } #[test] fn test_create_connections_right_1() { let mut rng = rand::thread_rng(); let mut rooms = [[Room::new(&mut rng); ROOMS_VERTICAL]; ROOMS_HORIZONTAL]; rooms[1][1].width = ROOM_WIDTH - 1; rooms[1][1].offset_x = 0; rooms[1][1].height = ROOM_HEIGHT - 1; rooms[1][1].offset_y = 0; rooms[1][1].kind = RoomType::BasicRoom; rooms[3][1].width = ROOM_WIDTH - 1; rooms[3][1].offset_x = 0; rooms[3][1].height = ROOM_HEIGHT - 1; rooms[3][1].offset_y = 0; rooms[3][1].kind = RoomType::BasicRoom; let mst: GeneratorGraph = LevelGenerator::create_mst(&rooms); LevelGenerator::create_connections(&mut rooms, &mst); assert!(rooms[1][1].connection_down.is_none()); assert!(rooms[1][1].connection_right.is_some()); assert_eq!( rooms[1][1].connection_right.unwrap().start_pos, (18, 9), "wrong start" ); assert_eq!( rooms[1][1].connection_right.unwrap().end_pos, (27, 9), "wrong end" ); assert!(rooms[3][1].connection_down.is_none()); assert!(rooms[3][1].connection_right.is_none()); } #[test] fn test_create_connections_right_2() { // test reduced width rooms (with overlap) for downwards connection let mut rng = rand::thread_rng(); let mut rooms = [[Room::new(&mut rng); ROOMS_VERTICAL]; ROOMS_HORIZONTAL]; rooms[1][1].width = ROOM_WIDTH - 1; rooms[1][1].offset_x = 0; rooms[1][1].height = 4; rooms[1][1].offset_y = 0; rooms[1][1].kind = RoomType::BasicRoom; rooms[3][1].width = ROOM_WIDTH - 1; rooms[3][1].offset_x = 0; rooms[3][1].height = 4; rooms[3][1].offset_y = 2; rooms[3][1].kind = RoomType::BasicRoom; let mst: GeneratorGraph = LevelGenerator::create_mst(&rooms); LevelGenerator::create_connections(&mut rooms, &mst); assert!(rooms[1][1].connection_down.is_none()); assert!(rooms[1][1].connection_right.is_some()); assert_eq!( rooms[1][1].connection_right.unwrap().start_pos, (18, 8), "wrong start" ); assert_eq!( rooms[1][1].connection_right.unwrap().end_pos, (27, 10), "wrong end" ); assert!(rooms[1][2].connection_down.is_none()); assert!(rooms[1][2].connection_right.is_none()); } #[test] fn test_create_connections_right_3() { // test reduced width rooms (with NO overlap) for downwards connection let mut rng = rand::thread_rng(); let mut rooms = [[Room::new(&mut rng); ROOMS_VERTICAL]; ROOMS_HORIZONTAL]; rooms[1][1].width = ROOM_WIDTH - 1; rooms[1][1].offset_x = 0; rooms[1][1].height = 2; rooms[1][1].offset_y = 0; rooms[1][1].kind = RoomType::BasicRoom; rooms[3][1].width = ROOM_WIDTH - 1; rooms[3][1].offset_x = 0; rooms[3][1].height = 2; rooms[3][1].offset_y = 4; rooms[3][1].kind = RoomType::BasicRoom; let mst: GeneratorGraph = LevelGenerator::create_mst(&rooms); LevelGenerator::create_connections(&mut rooms, &mst); assert!(rooms[1][1].connection_down.is_none()); assert!(rooms[1][1].connection_right.is_some()); assert_eq!( rooms[1][1].connection_right.unwrap().start_pos, (18, 7), "wrong start" ); assert_eq!( rooms[1][1].connection_right.unwrap().end_pos, (27, 11), "wrong end" ); assert!(rooms[1][3].connection_down.is_none()); assert!(rooms[1][3].connection_right.is_none()); } #[test] fn test_create_connections_right_4() { // test reduced width rooms (with NO overlap) for downwards connection let mut rng = rand::thread_rng(); let mut rooms = [[Room::new(&mut rng); ROOMS_VERTICAL]; ROOMS_HORIZONTAL]; rooms[1][1].width = 4; rooms[1][1].offset_x = 3; rooms[1][1].height = 2; rooms[1][1].offset_y = 0; rooms[1][1].kind = RoomType::BasicRoom; rooms[3][1].width = 3; rooms[3][1].offset_x = 3; rooms[3][1].height = 2; rooms[3][1].offset_y = 4; rooms[3][1].kind = RoomType::BasicRoom; let mst: GeneratorGraph = LevelGenerator::create_mst(&rooms); LevelGenerator::create_connections(&mut rooms, &mst); assert!(rooms[1][1].connection_down.is_none()); assert!(rooms[1][1].connection_right.is_some()); assert_eq!( rooms[1][1].connection_right.unwrap().start_pos, (17, 7), "wrong start" ); assert_eq!( rooms[1][1].connection_right.unwrap().end_pos, (30, 11), "wrong end" ); assert!(rooms[1][3].connection_down.is_none()); assert!(rooms[1][3].connection_right.is_none()); } // /* // println!(" 0 1 2 3 4 5 6 7"); // for r in 0..ROOMS_VERTICAL { // print!("{} ", r); // for c in 0..ROOMS_HORIZONTAL { // match res[c][r].kind { // RoomType::Start => print!("S "), // RoomType::End => print!("E "), // RoomType::StairUp => print!("< "), // RoomType::StairDown => print!("> "), // RoomType::BasicRoom => print!("_ "), // RoomType::ArtifactRoom => print!("A "), // RoomType::MonsterRoom => print!("M "), // RoomType::EmptyRoom => print!(" "), // }; // } // println!(); // } // println!(); // */