Files
gen1recomp/tests/gen2_ice_pathfind_test.lua
T
bryanthaboi ae6cac89e1 G2 support
2026-08-11 11:53:30 -04:00

115 lines
3.6 KiB
Lua

-- Ice-aware Bot:slideRest / planPath. Self-contained:
-- luajit tests/gen2_ice_pathfind_test.lua
--
-- The engine half (CheckForced latch) lives in gen2_world_test.lua. This file
-- proves the planner's graph is over REST positions: a press on ice ends where
-- the slide stops, so a cell the player would skate past is not a node.
package.path = "./?.lua;./?/init.lua;" .. package.path
local S = require("tests.harness").suite("gen2 ice pathfind")
local check, eq = S.check, S.eq
local Permissions = require("src.world.gen2.Permissions")
local Bot = dofile("tests/drivers/gold/bot.lua")
local A = Bot.adapter
local COLL_FLOOR, COLL_ICE, COLL_WALL = 0x00, 0x23, 0x07
local function fakeMap(cells)
return {
id = "ICE_TEST",
inBounds = function(_, x, y)
return x >= 0 and y >= 0 and x < 20 and y < 20
end,
cellCollision = function(_, x, y)
return cells[y * 100 + x] or COLL_FLOOR
end,
}
end
-- A bot stub that only needs passable / slideRest / planPath / wallCost.
local function stubBot(map, sx, sy, cells)
local g = { _map = map, _x = sx, _y = sy }
local bot = setmetatable({
g = g,
walls = {},
surfKnown = false,
}, Bot)
-- Adapter seams the planner reads. Keep them on A so the real methods run.
local realMap, realPos, realWalkable, realIsIce, realIsWater, realNpcAt,
realIsWarp, realSurfing =
A.map, A.pos, A.walkable, A.isIce, A.isWater, A.npcAt, A.isWarpTile, A.surfing
A.map = function() return map end
A.pos = function() return g._x, g._y end
A.mapId = function() return map.id end
A.walkable = function(_, m, x, y)
if not m:inBounds(x, y) then return false end
return Permissions.isWalkable(m:cellCollision(x, y))
end
A.isIce = function(m, x, y)
return Permissions.isIce(m:cellCollision(x, y))
end
A.isWater = function() return false end
A.npcAt = function() return nil end
A.isWarpTile = function() return false end
A.surfing = function() return false end
bot._restore = function()
A.map, A.pos, A.walkable, A.isIce, A.isWater, A.npcAt, A.isWarpTile, A.surfing =
realMap, realPos, realWalkable, realIsIce, realIsWater, realNpcAt,
realIsWarp, realSurfing
end
return bot
end
-- Corridor of ice ending at a wall: press right from floor rests on the last ice.
do
local cells = {
[5 * 100 + 3] = COLL_ICE,
[5 * 100 + 4] = COLL_ICE,
[5 * 100 + 5] = COLL_ICE,
[5 * 100 + 6] = COLL_WALL,
}
local map = fakeMap(cells)
local bot = stubBot(map, 2, 5)
local rx, ry, steps = bot:slideRest(map, 2, 5, "right")
eq(rx, 5, "slideRest stops on the last ice cell")
eq(ry, 5, "same row")
eq(steps, 3, "three cells of travel")
check(bot:slideRest(map, 5, 5, "right") == nil,
"pressing into the wall from the rest cell is no edge")
bot._restore()
end
-- Mid-corridor ice is not a rest node: planPath to it must fail.
do
local cells = {
[5 * 100 + 3] = COLL_ICE,
[5 * 100 + 4] = COLL_ICE,
[5 * 100 + 5] = COLL_ICE,
[5 * 100 + 6] = COLL_WALL,
}
local map = fakeMap(cells)
local bot = stubBot(map, 2, 5)
check(bot:planPath(4, 5) == nil,
"a cell the slide skates past is unreachable as a rest")
local path = bot:planPath(5, 5)
check(path ~= nil, "the rest against the wall is reachable")
eq(#path, 1, "one press")
eq(path[1], "right", "to the east")
bot._restore()
end
-- Off ice, planPath is still ordinary adjacency.
do
local map = fakeMap({})
local bot = stubBot(map, 2, 5)
local path = bot:planPath(4, 5)
check(path ~= nil, "dry floor still paths")
eq(#path, 2, "two single-cell presses")
eq(path[1], "right", "first step")
eq(path[2], "right", "second step")
bot._restore()
end
S.finish()