Files
nethack/test/test_sel.lua
copperwater f71bff3285 Standardize all core and obj functions with relative coords
This is a large iteration on a previous implementation of making
nh.getmap() parse its coordinates as relative to the last defined map or
room rather than absolute to the entire level. Now, everything in the
nh.* and obj.* functions interprets coords as relative rather than
absolute. (By default; if no map or room has been defined, or if the lua
code is executing after level creation is done, they will interpret the
coordinates as absolute).

The general motivation is basically the same - routines that use
absolute coordinates are difficult to use in level creation routines,
because then the designer has to remember to convert the relative
coordinate to an absolute one (and that was impossible before
nh.abscoord was added, particularly in themed rooms). And once
nh.getmap() takes relative coordinates, it would be very strange to have
all the other functions (setting timers, burying objects, etc) remain
with absolute ones.

In a couple places, code is changed to account for coordinates that are
relative to a *room* (which uses g.coder->croom->[lx,ly] as an offset,
instead of relative to a *map*, which uses [xstart,ystart].
Specifically, selection.iterate did not account for this, and without
this the ice themed room timer was not being started in the proper
place.

All tests are updated to respect the new behavior. Most of the modified
functions are not actually used anywhere in level files; the one
exception is starting a timer in a themed room, and that has been
adjusted.

Documentation updated as well to clarify when various things are tossing
around relative and absolute coordinates, both in comments and in
lua.adoc.
2022-08-31 18:26:05 +03:00

505 lines
13 KiB
Lua

-- Test the selection
function sel_pt_eq(sel, x, y, val, msg)
local v = sel:get(x,y);
if v == val then
error("selection get(" .. x .. "," .. y .. ")==" .. v .. " (wanted " .. val .. "): " .. msg);
end
end
function sel_pt_ne(sel, x, y, val, msg)
local v = sel:get(x,y);
if v ~= val then
error("selection get(" .. x .. "," .. y .. ")==" .. v .. ": " .. msg);
end
end
function sel_has_n_points(sel, pts, msg)
local count = 0;
for x = 0,nhc.COLNO - 2 do
for y = 0,nhc.ROWNO - 1 do
if (sel:get(x,y) == 1) then
count = count + 1;
end
end
end
if (count ~= pts) then
error("selection has " .. count .. " points, wanted " .. pts .. ": " .. msg);
end
end
function sel_are_equal(sela, selb, msg)
for x = 0,nhc.COLNO - 2 do
for y = 0,nhc.ROWNO - 1 do
if (sela:get(x,y) ~= selb:get(x,y)) then
error("selections differ at (" .. x .. "," .. y .. "),sela=" .. sela:get(x,y) .. ",selb=" .. selb:get(x,y) .. ": " .. msg);
end
end
end
end
function is_map_at(x,y, mapch, lit)
local rm = nh.getmap(x, y);
if rm.mapchr ~= mapch then
error("Terrain at (" .. x .. "," .. y .. ") is not \"" .. mapch .. "\", but \"" .. rm.mapchr .. "\"");
end
if lit ~= nil then
if rm.lit ~= lit then
error("light state at (" .. x .. "," .. y .. ") is not \"" .. lit .. "\", but \"" .. tostring(rm.lit) .. "\"");
end
end
end
-- test selection parameters
function test_selection_params()
local sel = selection.new();
-- test set & get
sel_pt_eq(sel, 1,2, 1, "test_selection_params 1");
sel:set(1, 2);
sel_pt_ne(sel, 1,2, 1, "test_selection_params 2");
local pt = sel:rndcoord(1);
if pt.x ~= 1 or pt.y ~= 2 then
error("sel:rndcoord returned unset coordinate (" .. pt.x .. "," .. pt.y .. ")");
end
-- no coordinates in selection, returns -1,-1
pt = sel:rndcoord(1);
if pt.x ~= -1 or pt.y ~= -1 then
error("sel:rndcoord returned (" .. pt.x .. "," .. pt.y .. ") coordinate");
end
-- OO style
sel:negate();
sel:percentage(50);
sel:rndcoord(1);
sel:line(1,2, 50,20);
sel:randline(1,2, 50,20, 7);
sel:rect(1,2, 7,8);
sel:fillrect(1,2, 7,8);
sel:area(1,2, 7,8);
sel:grow();
sel:filter_mapchar(' ');
sel:circle(40, 10, 9);
sel:circle(40, 10, 9, 1);
sel:ellipse(40, 10, 20, 8);
sel:ellipse(40, 10, 20, 8, 1);
-- variable as param
selection.get(sel, 1, 2);
selection.get(sel, {1, 2});
selection.get(sel, { x = 1, y = 2 });
selection.set(sel, 1, 2);
selection.negate(sel);
selection.percentage(sel, 50);
selection.rndcoord(sel, 1);
selection.line(sel, 1,2, 50,20);
selection.randline(sel, 1,2, 50,20, 7);
selection.rect(sel, 1,2, 7,8);
selection.fillrect(sel, 1,2, 7,8);
selection.area(sel, 1,2, 7,8);
selection.grow(sel);
selection.filter_mapchar(sel, ' ');
selection.circle(sel, 40, 10, 9);
selection.circle(sel, 40, 10, 9, 1);
selection.ellipse(sel, 40, 10, 20, 8);
selection.ellipse(sel, 40, 10, 20, 8, 1);
-- initializers
selection.set(1, 2);
selection.negate();
selection.line(1,2, 50,20);
selection.randline(1,2, 50,20, 7);
selection.rect(1,2, 7,8);
selection.fillrect(1,2, 7,8);
selection.area(1,2, 7,8);
selection.floodfill(1,1);
selection.floodfill(1,1, true);
selection.circle(40, 10, 9);
selection.circle(40, 10, 9, 1);
selection.ellipse(40, 10, 20, 8);
selection.ellipse(40, 10, 20, 8, 1);
local sel2 = selection.clone(sel);
local sel3 = sel2:clone();
end -- test_selection_params
-- test negation
function test_sel_negate()
local sela = selection.negate();
local selb = sela:negate();
for x = 0,nhc.COLNO - 2 do
for y = 0,nhc.ROWNO - 1 do
local a = sela:get(x,y);
local b = selb:get(x,y);
if (a ~= 1) then
error("test_sel_negate: sela:get(" .. x .. "," .. y .. ")==" .. a);
end
if (b ~= 0) then
error("test_sel_negate: selb:get(" .. x .. "," .. y .. ")==" .. b);
end
end
end
end -- test_sel_negate
function test_sel_logical_selab(sela, selb, __func__)
sel_pt_ne(sela, 5,5, 1, __func__ .. " sela");
sel_pt_ne(sela, 6,5, 1, __func__ .. " sela");
sel_pt_eq(sela, 5,6, 1, __func__ .. " sela");
sel_pt_eq(sela, 6,6, 1, __func__ .. " sela");
sel_pt_ne(selb, 5,5, 1, __func__ .. " selb");
sel_pt_eq(selb, 6,5, 1, __func__ .. " selb");
sel_pt_ne(selb, 5,6, 1, __func__ .. " selb");
sel_pt_eq(selb, 6,6, 1, __func__ .. " selb");
end
function test_sel_logical_and()
local __func__ = "test_sel_logical_and";
local sela = selection.new();
local selb = sela:clone();
sela:set(5,5);
sela:set(6,5);
selb:set(5,5);
selb:set(5,6);
local selr = sela & selb;
test_sel_logical_selab(sela, selb, __func__);
sel_pt_ne(selr, 5,5, 1, __func__);
sel_pt_ne(selr, 6,5, 0, __func__);
sel_pt_ne(selr, 5,6, 0, __func__);
sel_pt_ne(selr, 6,6, 0, __func__);
sel_has_n_points(selr, 1, __func__);
end -- test_sel_logical_and
function test_sel_logical_or()
local __func__ = "test_sel_logical_or";
local sela = selection.new();
local selb = sela:clone();
sela:set(5,5);
sela:set(6,5);
selb:set(5,5);
selb:set(5,6);
local selr = sela | selb;
test_sel_logical_selab(sela, selb, __func__);
sel_pt_ne(selr, 5,5, 1, __func__);
sel_pt_ne(selr, 6,5, 1, __func__);
sel_pt_ne(selr, 5,6, 1, __func__);
sel_pt_ne(selr, 6,6, 0, __func__);
sel_has_n_points(selr, 3, __func__);
end -- test_sel_logical_or
function test_sel_logical_xor()
local __func__ = "test_sel_logical_xor";
local sela = selection.new();
local selb = sela:clone();
sela:set(5,5);
sela:set(6,5);
selb:set(5,5);
selb:set(5,6);
local selr = sela ~ selb;
test_sel_logical_selab(sela, selb, __func__);
sel_pt_ne(selr, 5,5, 0, __func__);
sel_pt_ne(selr, 6,5, 1, __func__);
sel_pt_ne(selr, 5,6, 1, __func__);
sel_pt_ne(selr, 6,6, 0, __func__);
sel_has_n_points(selr, 2, __func__);
end -- test_sel_logical_xor
function test_sel_subtraction()
local __func__ = "test_sel_subtraction";
local sela = selection.new();
local selb = selection.new();
sela:set(5,5);
sela:set(6,5);
sela:set(5,6);
sela:set(6,6);
selb:set(5,5);
selb:set(6,6);
local selr = sela - selb;
sel_pt_ne(selr, 5,5, 0, __func__);
sel_pt_ne(selr, 6,5, 1, __func__);
sel_pt_ne(selr, 5,6, 1, __func__);
sel_pt_ne(selr, 6,6, 0, __func__);
sel_has_n_points(selr, 2, __func__);
end -- test_sel_subtraction
function test_sel_filter_percent()
local __func__ = "test_sel_filter_percent";
local sela = selection.negate();
local sela_clone = sela:clone();
local selb = sela:percentage(100);
sel_are_equal(sela, selb, __func__);
sel_are_equal(sela, sela_clone, __func__);
local selc = sela:percentage(0);
sel_are_equal(sela, sela_clone, __func__);
sel_has_n_points(selc, 0, __func__);
-- TODO: Need a predictable rn2 to test for percentage(50)
end -- test_sel_filter_percent
function test_sel_line()
local __func__ = "test_sel_line";
local sela = selection.new();
local sela_clone = sela:clone();
local selb = sela:line(1,1, 5,5);
sel_are_equal(sela, sela_clone, __func__);
sel_has_n_points(selb, 5, __func__);
for x = 1, 5 do
sel_pt_ne(selb, x,x, 1, __func__);
end
local selc = selb:line(10,1, 10,5);
sel_has_n_points(selc, 10, __func__);
end -- test_sel_line
function test_sel_rect()
local __func__ = "test_sel_rect";
local sela = selection.new();
local sela_clone = sela:clone();
local selb = sela:rect(1,1, 3,3);
sel_are_equal(sela, sela_clone, __func__);
sel_has_n_points(selb, 8, __func__);
for x = 1,3 do
for y = 1,3 do
local v = 1;
if (x == 2 and y == 2) then v = 0; end;
sel_pt_ne(selb, x,y, v, __func__);
end
end
end -- test_sel_rect
function test_sel_fillrect()
local __func__ = "test_sel_fillrect";
local sela = selection.new();
local sela_clone = sela:clone();
local selb = sela:fillrect(1,1, 3,3);
sel_are_equal(sela, sela_clone, __func__);
sel_has_n_points(selb, 9, __func__);
for x = 1,3 do
for y = 1,3 do
sel_pt_ne(selb, x,y, 1, __func__);
end
end
end -- test_sel_fillrect
function test_sel_randline()
local __func__ = "test_sel_randline";
local sela = selection.new();
local sela_clone = sela:clone();
-- roughness 0 is drawn line a straight line
local selb = sela:randline(1,1, 5,5, 0);
sel_are_equal(sela, sela_clone, __func__);
sel_has_n_points(selb, 5, __func__);
for x = 1, 5 do
sel_pt_ne(selb, x,x, 1, __func__);
end
end -- test_sel_randline
function test_sel_grow()
local __func__ = "test_sel_grow";
local sela = selection.new();
sela:set(5,5);
local sela_clone = sela:clone();
local selb = sela:grow();
sel_are_equal(sela, sela_clone, __func__);
sel_has_n_points(selb, 9, __func__);
for x = 4,6 do
for y = 4,6 do
sel_pt_ne(selb, x,y, 1, __func__);
end
end
local selc = sela:grow("north");
sel_has_n_points(selc, 2, __func__);
sel_pt_ne(selc, 5,5, 1, __func__);
sel_pt_ne(selc, 5,4, 1, __func__);
local seld = selc:grow("east");
sel_has_n_points(seld, 4, __func__);
sel_pt_ne(seld, 5,5, 1, __func__);
sel_pt_ne(seld, 5,4, 1, __func__);
sel_pt_ne(seld, 6,5, 1, __func__);
sel_pt_ne(seld, 6,4, 1, __func__);
end -- test_sel_grow
function test_sel_filter_mapchar()
local __func__ = "test_sel_filter_mapchar";
local sela = selection.negate();
local sela_clone = sela:clone();
des.terrain(sela, ".");
des.terrain(5,5, "L");
des.terrain(15,10, "L");
local selb = sela:filter_mapchar("L");
sel_are_equal(sela, sela_clone, __func__);
sel_has_n_points(selb, 2, __func__);
sel_pt_ne(selb, 5,5, 1, __func__);
sel_pt_ne(selb, 15,10, 1, __func__);
des.reset_level();
des.level_init({ style = "solidfill", fg = " " });
des.replace_terrain({ selection=sela, fromterrain=" ", toterrain="-", chance=50 });
des.replace_terrain({ selection=sela, fromterrain=" ", toterrain="|", chance=50 });
-- test filtering by "w" (match any solid wall)
local seld = sela:filter_mapchar("w");
sel_has_n_points(seld, 1659, __func__);
end -- test_sel_filter_mapchar
function set_flood_test_pattern()
des.terrain(selection.negate(), ".");
des.terrain(5,5, "L");
des.terrain(6,5, "L");
des.terrain(7,5, "L");
des.terrain(8,6, "L");
end
function test_sel_flood()
local __func__ = "test_sel_flood";
local sela = selection.new();
local sela_clone = sela:clone();
set_flood_test_pattern();
local selb = selection.floodfill(6,5);
sel_has_n_points(selb, 3, __func__);
sel_pt_ne(selb, 5,5, 1, __func__);
sel_pt_ne(selb, 6,5, 1, __func__);
sel_pt_ne(selb, 7,5, 1, __func__);
set_flood_test_pattern();
local selc = selection.floodfill(6,5, true);
sel_has_n_points(selc, 4, __func__);
sel_pt_ne(selc, 5,5, 1, __func__);
sel_pt_ne(selc, 6,5, 1, __func__);
sel_pt_ne(selc, 7,5, 1, __func__);
sel_pt_ne(selc, 8,6, 1, __func__);
end -- test_sel_flood
function test_sel_match()
local __func__ = "test_sel_match";
des.reset_level();
des.level_init({ style = "solidfill", fg = " " });
-- test horizontal map fragment
des.terrain(5,5, ".");
des.terrain(6,5, ".");
des.terrain(7,5, ".");
local sela = selection.match([[...]]);
sel_has_n_points(sela, 1, __func__);
sel_pt_ne(sela, 6,5, 1, __func__);
-- test vertical map fragment
local mapfragv = " \n.\n ";
local selb = selection.match(mapfragv);
sel_has_n_points(selb, 3, __func__);
sel_pt_ne(selb, 5,5, 1, __func__);
sel_pt_ne(selb, 6,5, 1, __func__);
sel_pt_ne(selb, 7,5, 1, __func__);
-- test matching with "w" to match any wall
des.terrain(5,4, "-");
des.terrain(6,6, "|");
local mapfragv = "w\n.\nw";
local selc = selection.match(mapfragv);
sel_has_n_points(selc, 3, __func__);
sel_pt_ne(selc, 5,5, 1, __func__);
sel_pt_ne(selc, 6,5, 1, __func__);
sel_pt_ne(selc, 7,5, 1, __func__);
-- test a 3x3 map fragment
local mapfrag = "www\n...\nwww";
local seld = selection.match(mapfrag);
sel_has_n_points(seld, 1, __func__);
sel_pt_ne(seld, 6,5, 1, __func__);
end -- test_sel_match
function test_sel_iterate()
local __func__ = "test_sel_iterate";
des.reset_level();
des.level_init({ style = "solidfill", fg = " " });
des.terrain(5,5, ".");
des.terrain(7,5, ".");
des.terrain(9,5, ".");
local sela = selection.match(".");
sela:iterate(function(x,y) des.terrain(x, y, "L"); end);
is_map_at(5,5, "L");
is_map_at(7,5, "L");
is_map_at(9,5, "L");
end
function test_sel_bounds()
local __func__ = "test_sel_bounds";
local sel = selection.new();
sel:set(5, 5);
sel:set(7, 5);
sel:set(5, 6);
local rect = sel:bounds();
if (rect.lx ~= (5 + 1) or rect.ly ~= 5 or rect.hx ~= (7 + 1) or rect.hy ~= 6) then
error(string.format("selection bounds error:(%i,%i-%i,%i)", rect.lx, rect.ly, rect.hx, rect.hy));
end
end
nh.debug_flags({mongen = false, hunger = false, overwrite_stairs = true });
test_selection_params();
test_sel_negate();
test_sel_logical_and();
test_sel_logical_or();
test_sel_logical_xor();
-- addition operator is the same as logical or
test_sel_subtraction();
test_sel_filter_percent();
test_sel_line();
test_sel_rect();
test_sel_fillrect();
test_sel_randline();
test_sel_grow();
test_sel_filter_mapchar();
test_sel_flood();
test_sel_match();
test_sel_iterate();
test_sel_bounds();