test: cover tests.secretsSweepDiscoversAnAdjacentHiddenDoorEventually...

This commit is contained in:
dualfroz committed 2026-09-11 19:46:25 +02:00
1 parent d16250b6f9
commit 4e48ff6c6b
1 file changed
+173
+173
View File
@@ -9298,6 +9298,179 @@ function tests.trapDetectedFieldSurvivesSerialization()
os.remove(path)
eq(restored.level.traps[1].detected, true, "a trap's detected flag survives a save/load round trip")
end
function tests.secretsSweepDiscoversAnAdjacentHiddenDoorEventuallyAcrossAHandfulOfSeeds()
local discovered = false
for seed = 1, 100 do
local level = arena(21, 11)
level.secretDoors = { { x = 5, y = 5, discovered = false } }
if #Secrets.sweep(level, Rng.new(seed), 5, 6) > 0 then discovered = true break end
end
ok(discovered, "an adjacent secret door is eventually discovered across a handful of seeds")
end
function tests.secretsSweepNeverDiscoversADoorTwoStepsAway()
for seed = 1, 200 do
local level = arena(21, 11)
level.secretDoors = { { x = 5, y = 5, discovered = false } }
eq(#Secrets.sweep(level, Rng.new(seed), 7, 7), 0,
"a secret door two steps away is never discovered regardless of the roll")
end
end
function tests.secretsSweepNeverRediscoversAnAlreadyDiscoveredDoor()
local level = arena(21, 11)
level.secretDoors = { { x = 5, y = 5, discovered = true } }
for seed = 1, 50 do
eq(#Secrets.sweep(level, Rng.new(seed), 5, 5), 0, "an already-discovered door is never swept again")
end
end
function tests.secretsSweepOpensTheWallIntoAPlainClosedDoorOnDiscovery()
local level = arena(21, 11)
level.map:set(5, 5, Tiles.WALL)
level.secretDoors = { { x = 5, y = 5, discovered = false } }
local found
for seed = 1, 100 do
local door = Secrets.sweep(level, Rng.new(seed), 5, 6)[1]
if door then found = door break end
end
ok(found ~= nil, "the door is eventually discovered")
eq(found.discovered, true, "the discovered door's own flag is flipped")
eq(level.map:get(5, 5), Tiles.DOOR_CLOSED, "the wall tile becomes a plain closed door once discovered")
end
local function orthogonalNeighbours(x, y)
return { { x + 1, y }, { x - 1, y }, { x, y + 1 }, { x, y - 1 } }
end
local function reachableFrom(map, fromX, fromY, x, y)
return Astar.find(map, fromX, fromY, x, y, function(cx, cy)
return not map:isPassable(cx, cy, false)
end) ~= nil
end
function tests.builderOccasionallyPlacesASecretDoorGuardingAGenuinelyUnreachableAlcove()
local checked = 0
for seed = 1, 200 do
local level = Builder.build(3, Rng.new(seed), {})
if #level.secretDoors == 1 then
checked = checked + 1
local door = level.secretDoors[1]
eq(door.discovered, false, "a freshly built floor's secret door always starts undiscovered")
eq(level.map:get(door.x, door.y), Tiles.WALL, "an undiscovered secret door still reads as an ordinary wall")
-- Exactly one of the door's own orthogonal neighbours is the alcove
-- itself: real floor, yet unreachable from the stairs while the door
-- between them is still a plain, impassable wall.
local alcoveX, alcoveY
for _, n in ipairs(orthogonalNeighbours(door.x, door.y)) do
local nx, ny = n[1], n[2]
if level.map:isWalkable(nx, ny) and not reachableFrom(level.map, level.upX, level.upY, nx, ny) then
alcoveX, alcoveY = nx, ny
end
end
ok(alcoveX ~= nil, "seed " .. seed .. ": the secret door hides a genuinely unreachable alcove cell")
if alcoveX then
level.map:set(door.x, door.y, Tiles.DOOR_CLOSED)
ok(reachableFrom(level.map, level.upX, level.upY, alcoveX, alcoveY),
"seed " .. seed .. ": the same alcove opens up the instant its door is discovered")
end
if checked >= 10 then break end
end
end
ok(checked > 0, "at least one of the sampled seeds actually places a secret door")
end
function tests.builderNeverPlacesASecretDoorAboveDepthTwo()
for seed = 1, 10 do
local level = Builder.build(1, Rng.new(seed), {})
eq(#level.secretDoors, 0, "no secret door appears on the first floor")
end
end
function tests.gameEndPlayerTurnEventuallyDiscoversAnAdjacentSecretDoorAcrossAHandfulOfSeeds()
local discovered = false
for seed = 1, 100 do
local game = stagedGame(1, arena(21, 11), 3, 6)
game.rng = Rng.new(seed)
game.level.map:set(4, 6, Tiles.WALL)
game.level.secretDoors = { { x = 4, y = 6, discovered = false } }
game:endPlayerTurn()
if game.level.secretDoors[1].discovered then
local reported = false
for _, line in ipairs(game.log:recent(6)) do
if contains(line, "hidden door") then reported = true end
end
ok(reported, "a freshly discovered secret door is reported somewhere in the turn's log lines")
eq(game.level.map:get(4, 6), Tiles.DOOR_CLOSED, "the discovered door's tile is a plain closed door")
discovered = true
break
end
end
ok(discovered, "an adjacent secret door is eventually discovered over a handful of seeded turns")
end
function tests.secretDoorFieldsSurviveSerialization()
local game = Game.new(5)
game.level.secretDoors = { { x = 3, y = 3, discovered = false } }
local path = os.tmpname()
game:save(path)
local restored = Game.load(path)
os.remove(path)
eq(#restored.level.secretDoors, 1, "the secret door list survives a save/load round trip")
eq(restored.level.secretDoors[1].discovered, false, "a secret door's discovered flag survives a round trip")
end
function tests.chestsNewLootAlwaysIncludesGoldAndExactlyOneGearPiece()
for seed = 1, 30 do
local chest = Chests.new(Rng.new(seed), 3)
local goldCount, gearCount = 0, 0
for _, item in ipairs(chest.loot) do
if item.kind == Items.GOLD then goldCount = goldCount + 1 end
if item.kind == Items.WEAPON or item.kind == Items.ARMOR then gearCount = gearCount + 1 end
end
eq(goldCount, 1, "every chest carries exactly one pile of gold")
eq(gearCount, 1, "every chest carries exactly one weapon or armor piece")
end
end
function tests.chestsNewNeverRollsTrappedOnAnUnlockedChest()
local sawLockedTrapped, sawLockedUntrapped = false, false
for seed = 1, 100 do
local chest = Chests.new(Rng.new(seed), 3)
ok(chest.locked or not chest.trapped, "an unlocked chest is never also trapped")
if chest.locked and chest.trapped then sawLockedTrapped = true end
if chest.locked and not chest.trapped then sawLockedUntrapped = true end
end
ok(sawLockedTrapped, "a locked chest eventually rolls trapped across a handful of seeds")
ok(sawLockedUntrapped, "a locked chest eventually rolls untrapped across a handful of seeds")
end
function tests.chestsIsLockedIsTrueForALockedUnopenedChestAndFalseOnceOpened()
local chest = { locked = true, trapped = false, opened = false, loot = { Items.gold(5) } }
eq(Chests.isLocked(chest), true, "a locked, unopened chest reports locked")
chest.opened = true
eq(Chests.isLocked(chest), false, "an opened chest never reports locked, whatever its own flag says")
end
function tests.chestsOpenGrantsEveryLootItemAndEmptiesTheChest()
local player = { inventory = {} }
local chest = { locked = false, trapped = false, opened = false, loot = { Items.gold(5), Items.potionOf("heal") } }
local success, line = Chests.open(player, chest)
ok(success, "opening an unlocked chest succeeds")
eq(#player.inventory, 2, "every loot item lands in the inventory")
eq(chest.opened, true, "the chest is marked opened")
eq(#chest.loot, 0, "the chest's own loot list is emptied")
ok(contains(line, "open"), "the opening is reported")
end
function tests.chestsOpenRefusesAnAlreadyEmptiedChest()
local player = { inventory = {} }
local chest = { locked = false, trapped = false, opened = true, loot = {} }
local success, line = Chests.open(player, chest)
eq(success, false, "opening an already-emptied chest fails")
eq(#player.inventory, 0, "nothing is granted from an already-emptied chest")
end
local order = {