test: cover tests.secretsSweepDiscoversAnAdjacentHiddenDoorEventually...
This commit is contained in:
1 parent
d16250b6f9
commit
4e48ff6c6b
1 file changed
+173
+173
@@ -9298,6 +9298,179 @@ function tests.trapDetectedFieldSurvivesSerialization()
|
|||||||
os.remove(path)
|
os.remove(path)
|
||||||
eq(restored.level.traps[1].detected, true, "a trap's detected flag survives a save/load round trip")
|
eq(restored.level.traps[1].detected, true, "a trap's detected flag survives a save/load round trip")
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
local order = {
|
local order = {
|
||||||
|
|||||||
Reference in new issue
Block a user