extends GutTest ## Generator properties. The important one is reachability: a map where the ## boss cannot be walked to is a run that cannot be finished, and it would show ## up as a player wandering confused rather than as an error. func _flood_reachable(grid: MapGrid, from: Vector2i) -> Dictionary: var seen := {} var queue: Array[Vector2i] = [from] seen[from] = true while not queue.is_empty(): var t: Vector2i = queue.pop_back() for d in [Vector2i(1, 0), Vector2i(-1, 0), Vector2i(0, 1), Vector2i(0, -1)]: var n: Vector2i = t + d if seen.has(n) or not grid.in_bounds(n.x, n.y): continue if grid.blocks_move(n.x, n.y): continue seen[n] = true queue.append(n) return seen func test_the_boss_is_always_reachable_from_the_spawn() -> void: # Many seeds, because "usually connected" is the failure mode that survives # a single-seed test and ruins one run in twenty. for s in range(1, 40): var m := MapGen.generate(s * 7919, 1 + (s % 5)) var grid: MapGrid = m["grid"] var spawn_tile := grid.to_tile(m["spawn"]) var boss_tile := grid.to_tile(m["boss_pos"]) var reachable := _flood_reachable(grid, spawn_tile) assert_true(reachable.has(boss_tile), "seed %d: the boss room is walled off from the spawn" % s) func test_generation_is_deterministic() -> void: var a := MapGen.generate(12345, 3) var b := MapGen.generate(12345, 3) assert_eq((a["grid"] as MapGrid).tiles, (b["grid"] as MapGrid).tiles, "same seed and depth must give the same map, or the server cannot " + "just send two numbers") assert_eq(a["spawn"], b["spawn"]) assert_eq(a["boss_pos"], b["boss_pos"]) func test_different_seeds_give_different_maps() -> void: var a := MapGen.generate(1, 2) var b := MapGen.generate(2, 2) assert_ne((a["grid"] as MapGrid).tiles, (b["grid"] as MapGrid).tiles) func test_maps_grow_with_depth() -> void: var shallow: MapGrid = MapGen.generate(99, 1)["grid"] var deep: MapGrid = MapGen.generate(99, 6)["grid"] assert_gt(deep.width, shallow.width) assert_gt(deep.height, shallow.height) func test_the_map_is_sealed_at_its_border() -> void: var grid: MapGrid = MapGen.generate(4242, 2)["grid"] for x in grid.width: assert_true(grid.blocks_move(x, 0), "top border leaks at x=%d" % x) assert_true(grid.blocks_move(x, grid.height - 1), "bottom border leaks at x=%d" % x) for y in grid.height: assert_true(grid.blocks_move(0, y), "left border leaks at y=%d" % y) assert_true(grid.blocks_move(grid.width - 1, y), "right border leaks at y=%d" % y) func test_spawn_and_boss_stand_on_open_ground() -> void: for s in range(1, 20): var m := MapGen.generate(s * 104729, 1 + (s % 4)) var grid: MapGrid = m["grid"] assert_false(grid.circle_blocked(m["spawn"], SimConfig.PLAYER_RADIUS), "seed %d: player spawns inside geometry" % s) assert_false(grid.circle_blocked(m["boss_pos"], 40.0), "seed %d: boss spawns inside geometry" % s) func test_rooms_do_not_overlap() -> void: var rooms: Array = MapGen.generate(777, 4)["rooms"] for i in rooms.size(): for j in range(i + 1, rooms.size()): assert_false((rooms[i] as Rect2i).intersects(rooms[j]), "rooms %d and %d overlap" % [i, j])