summaryrefslogtreecommitdiff
path: root/dungeon/tests/bsp.rs
blob: f7919c506167ea86272cbe4e2798f4d715e2e889 (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
129
130
131
132
133
134
135
136
137
138
//! Integration Tests for BSP dungeon generation
#[cfg(test)]
mod tests {
	use dungeon::{
		bsp,
		map::{self, TILE_COUNT},
		pos::Pos,
	};
	use rand::{Rng, SeedableRng, rngs::SmallRng};

	/// Generate a set of test seeds for reproducibility with a seeded RNG
	fn generate_test_seeds(seed: u64) -> Vec<u64> {
		let mut rng = SmallRng::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 mut rng = SmallRng::seed_from_u64(seed);
			let floor = bsp::generate(&mut rng);
			// 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 mut rng = SmallRng::seed_from_u64(seed);
			let floor = bsp::generate(&mut rng);
			// 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 mut rng = SmallRng::seed_from_u64(seed);
			let floor = bsp::generate(&mut rng);
			// 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 mut rng = SmallRng::seed_from_u64(seed);
			let floor = bsp::generate(&mut rng);
			// 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 mut rng1 = SmallRng::seed_from_u64(seed);
			let mut rng2 = SmallRng::seed_from_u64(seed);
			let floor1 = bsp::generate(&mut rng1);
			let floor2 = bsp::generate(&mut rng2);
			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 mut rng = SmallRng::seed_from_u64(seed);
		let floor = bsp::generate(&mut rng);

		// 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}");
			}
		}
	}
}