ludic/packages/ludic.nav/tests/nav_test.ludic
Orkuncakilkaya f65eda8b47 ludic.nav (17.10): the time paths take, counted in the shim
nav_us() gives the microseconds spent in nav_path across the loaded meshes. The shim times each path with the OS's monotonic clock (clock_gettime, or QueryPerformanceCounter from KERNEL32), so the DLL still needs no C++ runtime. Both libraries are rebuilt, and 13 tests pass on the Mac and the PC.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 22:41:30 +03:00

137 lines
6.1 KiB
Text

# nav_test.ludic - ludic.nav on ground built by hand: a 40 m meadow of metre squares, a post in its
# middle, a river across it (area 0) with a ford or without one. A path goes round the post and over
# the ford, stops at the bank when there is none, and a saved mesh answers as the built one did.
import "ludic.nav"
import "ludic.base"
import "fakes/meadow.ludic"
program NavTest {
numbers float
function meadow(nav_st: mut NavState, kind: int, river: int, post: bool) -> bool {
let g = ground(40, river)
if post { g.cyl = post_at(20.0, 20.0, 2.0) }
if post { g.nc = 1 }
return nav_build(nav_st, kind, g, new NavConfig)
}
function last_x(nav_st: NavState) -> float { return nav_corner_x(nav_st, nav_corners(nav_st) - 1) }
function last_z(nav_st: NavState) -> float { return nav_corner_z(nav_st, nav_corners(nav_st) - 1) }
test "a path goes round a post, and the straight line through it is not walkable" (nav_st: mut NavState) {
expect(meadow(nav_st, NAV_PERSON, 0, true))
expect(nav_polygons(nav_st, NAV_PERSON) > 0)
let n = nav_path(nav_st, NAV_PERSON, 0, 5.0, 0.0, 20.0, 35.0, 0.0, 20.0)
expect(n >= 3)
expect(not nav_partial(nav_st, NAV_PERSON))
expect(Math.abs(last_x(nav_st) - 35.0) < 0.1)
for i in 0 .. n {
let dx = nav_corner_x(nav_st, i) - 20.0
let dz = nav_corner_z(nav_st, i) - 20.0
expect(dx * dx + dz * dz > 2.0 * 2.0)
}
expect(not nav_straight(nav_st, NAV_PERSON, 0, 5.0, 0.0, 20.0, 35.0, 20.0))
expect(nav_straight(nav_st, NAV_PERSON, 0, 5.0, 0.0, 5.0, 35.0, 5.0))
}
test "a river is crossed at its ford" (nav_st: mut NavState) {
expect(meadow(nav_st, NAV_PERSON, 1, false))
let n = nav_path(nav_st, NAV_PERSON, 0, 5.0, 0.0, 10.0, 35.0, 0.0, 10.0)
expect(n >= 3)
expect(not nav_partial(nav_st, NAV_PERSON))
var ford = false
for i in 0 .. n { if nav_corner_z(nav_st, i) > 29.0 { ford = true } }
expect(ford)
}
test "without a ford the path stops at the near bank and says so" (nav_st: mut NavState) {
expect(meadow(nav_st, NAV_PERSON, 2, false))
let n = nav_path(nav_st, NAV_PERSON, 0, 5.0, 0.0, 10.0, 35.0, 0.0, 10.0)
expect(n >= 1)
expect(nav_partial(nav_st, NAV_PERSON))
expect(last_x(nav_st) < 18.0)
}
test "the nearest walkable point, and nothing off the mesh" (nav_st: mut NavState) {
expect(meadow(nav_st, NAV_PERSON, 0, false))
expect(nav_nearest(nav_st, NAV_PERSON, 10.0, 1.0, 10.0))
expect(Math.abs(nav_near_y(nav_st)) < 0.3)
expect(Math.abs(nav_near_x(nav_st) - 10.0) < 0.1)
expect(not nav_nearest(nav_st, NAV_PERSON, -50.0, 0.0, -50.0))
expect_eq(nav_path(nav_st, NAV_PERSON, 0, -50.0, 0.0, -50.0, 10.0, 0.0, 10.0), -1)
expect_eq(nav_path(nav_st, NAV_LARGE, 0, 5.0, 0.0, 5.0, 10.0, 0.0, 10.0), -1)
}
test "a random point near is always one a walker there can reach, and a seed is always the same point" (nav_st: mut NavState) {
expect(meadow(nav_st, NAV_PERSON, 2, false))
for seed in 0 .. 50 {
expect(nav_random_near(nav_st, NAV_PERSON, 0, 5.0, 0.0, 10.0, 30.0, seed))
expect(nav_near_x(nav_st) < 18.0)
}
nav_random_near(nav_st, NAV_PERSON, 0, 5.0, 0.0, 10.0, 30.0, 7)
let x7 = nav_near_x(nav_st)
let z7 = nav_near_z(nav_st)
nav_random_near(nav_st, NAV_PERSON, 0, 5.0, 0.0, 10.0, 30.0, 8)
expect(Math.abs(nav_near_x(nav_st) - x7) + Math.abs(nav_near_z(nav_st) - z7) > 0.01)
nav_random_near(nav_st, NAV_PERSON, 0, 5.0, 0.0, 10.0, 30.0, 7)
expect_eq(nav_near_x(nav_st), x7)
expect_eq(nav_near_z(nav_st), z7)
}
test "the next corner is the path's second, read back as the nearest point" (nav_st: mut NavState) {
expect(meadow(nav_st, NAV_PERSON, 0, true))
let n = nav_path(nav_st, NAV_PERSON, 0, 5.0, 0.0, 20.0, 35.0, 0.0, 20.0)
let cx = nav_corner_x(nav_st, 1)
let cz = nav_corner_z(nav_st, 1)
expect(n >= 3)
expect(nav_next_corner(nav_st, NAV_PERSON, 0, 5.0, 0.0, 20.0, 35.0, 0.0, 20.0))
expect_near(nav_near_x(nav_st), cx, 0.0001)
expect_near(nav_near_z(nav_st), cz, 0.0001)
expect(not nav_next_corner(nav_st, NAV_PERSON, 0, -50.0, 0.0, -50.0, 35.0, 0.0, 20.0))
}
test "a filter that dislikes a band of thicket walks round it; the plain one goes through" (nav_st: mut NavState) {
expect(meadow(nav_st, NAV_PERSON, 3, false))
nav_area_cost(nav_st, NAV_PERSON, 1, 2, 20.0)
nav_path(nav_st, NAV_PERSON, 0, 5.0, 0.0, 10.0, 35.0, 0.0, 10.0)
expect_eq(nav_corners(nav_st), 2)
let n = nav_path(nav_st, NAV_PERSON, 1, 5.0, 0.0, 10.0, 35.0, 0.0, 10.0)
var round = false
for i in 0 .. n { if nav_corner_z(nav_st, i) > 29.0 { round = true } }
expect(round)
expect(not nav_partial(nav_st, NAV_PERSON))
}
test "a boulder's footprint is walked round" (nav_st: mut NavState) {
let g = ground(40, 0)
g.foot = square_at(20.0, 20.0, 3.0)
g.nf = len(g.foot)
expect(nav_build(nav_st, NAV_PERSON, g, new NavConfig))
expect(not nav_straight(nav_st, NAV_PERSON, 0, 5.0, 0.0, 20.0, 35.0, 20.0))
expect(nav_path(nav_st, NAV_PERSON, 0, 5.0, 0.0, 20.0, 35.0, 0.0, 20.0) >= 3)
}
test "a mesh saved and loaded answers as the built one did" (nav_st: mut NavState) {
expect(meadow(nav_st, NAV_PERSON, 1, true))
let path = Os.temp_dir() + "/ludic_nav_test.navmesh"
expect(nav_save_file(nav_st, NAV_PERSON, path))
expect(nav_load_file(nav_st, NAV_SMALL, path))
let a = nav_path(nav_st, NAV_PERSON, 0, 5.0, 0.0, 10.0, 35.0, 0.0, 10.0)
let ax = last_x(nav_st)
let az = last_z(nav_st)
let b = nav_path(nav_st, NAV_SMALL, 0, 5.0, 0.0, 10.0, 35.0, 0.0, 10.0)
expect_eq(a, b)
expect_near(last_x(nav_st), ax, 0.0001)
expect_near(last_z(nav_st), az, 0.0001)
nav_reset(nav_st)
expect_eq(nav_polygons(nav_st, NAV_SMALL), 0)
}
test "the time paths take is counted, and only while a mesh is loaded" (nav_st: mut NavState) {
expect_eq(nav_us(nav_st), 0)
expect(meadow(nav_st, NAV_PERSON, 0, true))
for i in 0 .. 2000 { nav_path(nav_st, NAV_PERSON, 0, 5.0, 0.0, 20.0, 35.0, 0.0, 20.0) }
expect(nav_us(nav_st) > 0)
expect(nav_us(nav_st) < 2000000)
nav_reset(nav_st)
expect_eq(nav_us(nav_st), 0)
}
}