summaryrefslogtreecommitdiff
path: root/dungeon/tests/bsp_tests.rs
blob: 09d2d3a7a42fabb9a184cff5d20d750a31d049f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! Integration Tests for BSP dungeon generation
#[cfg(test)]
mod tests {
	use dungeon::*;
	use pos::Pos;
	use rand::{Rng, SeedableRng};

	/// Generate a set of test seeds for reproducibility with a seeded RNG
	fn generate_test_seeds(seed: u64) -> Vec<u64> {
		let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
		// Generate 100 random u64 seeds
		(0..100).map(|_| rng.random_range(0..u64::MAX)).collect()
	}

	/// Basic integration test for BSP generation
	#[test]
	fn test_bsp_integration() {
		let test_seeds = generate_test_seeds(123456);
		for seed in test_seeds {
			let floor = bsp::generate(seed);
			// Basic integration test: ensure we get valid data
			assert!(!floor.tiles().is_empty());
		}
	}

	/// Test that BSP-generated floors have a valid player start
	#[test]
	fn test_bsp_player_start() {
		let test_seeds = generate_test_seeds(654321);
		for seed in test_seeds {
			let floor = bsp::generate(seed);
			// Ensure player start is a room tile
			let start = floor.player_start();
			assert_eq!(floor.get(start), map::Tile::Room);
		}
	}

	/// Test that BSP-generated floors have at least one room tile
	#[test]
	fn test_bsp_2_or_more_rooms() {
		let test_seeds = generate_test_seeds(111222);
		for seed in test_seeds {
			let floor = bsp::generate(seed);
			// Ensure we have at least one room tile
			let room_count = floor
				.tiles()
				.iter()
				.filter(|&&tile| tile == map::Tile::Room)
				.count();
			assert!(
				room_count >= 1,
				"Expected at least one room tile, found {room_count}"
			);
		}
	}

	/// Test that BSP-generated floors have walls on the borders
	#[test]
	fn test_bsp_walls_on_borders() {
		let test_seeds = generate_test_seeds(777888);
		for seed in test_seeds {
			let floor = bsp::generate(seed);
			// Go through all tiles, and ensure border tiles are walls
			for pos in Pos::values() {
				if pos.is_border() {
					assert_eq!(
						floor.get(pos),
						map::Tile::Wall,
						"Expected wall at border position {pos:?}"
					);
				}
			}
		}
	}

	// Test that BSP-generated floors are reproducible with the same seed
	#[test]
	fn test_bsp_reproducibility() {
		let test_seeds = generate_test_seeds(111111);
		for seed in test_seeds {
			let floor1 = bsp::generate(seed);
			let floor2 = bsp::generate(seed);
			assert_eq!(
				floor1.tiles(),
				floor2.tiles(),
				"Tiles differ for same seed {seed}"
			);
			assert_eq!(
				floor1.player_start(),
				floor2.player_start(),
				"Player starts differ for same seed {seed}"
			);
		}
	}

	// Test that all `air` tiles (`Tile::Room` and `Tile::Hallway`) are reachable from the player start
	#[test]
	fn test_bsp_all_air_tiles_reachable() {
		let test_seeds = generate_test_seeds(333444);
		for seed in test_seeds {
			check_air_tiles_reachable(seed);
		}
	}

	// Helper function to check that all air tiles are reachable from player start
	fn check_air_tiles_reachable(seed: u64) {
		let floor = bsp::generate(seed);

		// BFS to find all reachable air tiles
		let mut visited = vec![false; TILE_COUNT];
		let mut queue = vec![floor.player_start()];
		visited[floor.player_start().idx()] = true;
		while let Some(pos) = queue.pop() {
			for neighbor in pos.neighbors() {
				let idx = neighbor.idx();
				if !visited[idx] && floor.get(neighbor) != map::Tile::Wall {
					visited[idx] = true;
					queue.push(neighbor);
				}
			}
		}
		for (i, &tile) in floor.tiles().iter().enumerate() {
			if tile == map::Tile::Room || tile == map::Tile::Hallway {
				assert!(visited[i], "Unreachable air tile at index {i}");
			}
		}
	}
}