From 358f564301273f32338a00508f5121063ae800b1 Mon Sep 17 00:00:00 2001 From: Steve Cotton Date: Sat, 20 Jul 2019 18:01:14 +0200 Subject: [PATCH] Add unit tests for movement and vision costs Includes changing the terrain costs, and aliased terrains. Can be cleanly cherry-picked to 1.14. --- data/test/maps/test_movetype.map | 13 + data/test/scenarios/test_movetype.cfg | 362 ++++++++++++++++++++++++++ wml_test_schedule | 21 ++ 3 files changed, 396 insertions(+) create mode 100644 data/test/maps/test_movetype.map create mode 100644 data/test/scenarios/test_movetype.cfg diff --git a/data/test/maps/test_movetype.map b/data/test/maps/test_movetype.map new file mode 100644 index 00000000000..d4a37fa18b2 --- /dev/null +++ b/data/test/maps/test_movetype.map @@ -0,0 +1,13 @@ +Xv, Xv, Xv, Xv, Xv, Xv, Xv +Xv, Xv, Xv, Xv, Xv, Xv, Xv +Xv, Xv, destination Ce, Wwf^Fpa, Cme, Xv, Xv +Xv, Xv, Xv, Xv, Wwf, Xv, Xv +Xv, Xv, Uh^Fda, Wo^Bw/r, Xv, Xv, Xv +Xv, Xv, Gg^Fda, Xv, Xv, Xv, Xv +Xv, Xv, Xv, Dd^Ftd, Uh, Xv, Xv +Xv, Xv, Xv, Xv, Hh, Xv, Xv +Xv, Xv, Xv, Hh^Fds, Xv, Xv, Xv +Xv, Xv, Gg, Gg^Fds, Gg, Xv, Xv +Xv, Xv, 1 Gg, Xv, 2 Gg, Xv, Xv +Xv, Xv, Xv, Xv, Xv, Xv, Xv +Xv, Xv, Xv, Xv, Xv, Xv, Xv diff --git a/data/test/scenarios/test_movetype.cfg b/data/test/scenarios/test_movetype.cfg new file mode 100644 index 00000000000..8a268ac0b91 --- /dev/null +++ b/data/test/scenarios/test_movetype.cfg @@ -0,0 +1,362 @@ +# This series of tests checks that the C++ movetype class handles changes to movetypes correctly. + +# The map defines a location_id "destination", and from both start points the path to it is +# Flat +# Forest +# Forest Hills +# Hills +# Cave Hills +# Forest Sand +# Flat Frozen Forest +# Cave Hills Frozen Forest +# bridge over deep water (best-of Flat and Deep Water) +# ford (best-of Flat and Water) +# aquatic encampment (best-of Castle and Coastal Reef) +# ford snowy forest (Frozen, Forest, Flat, Shallow Water) +# Keep +# +# Note: in 1.14 (and 1.15 at the time of writing), ford snowy forest's movement is +# the worst of all four types, not the worst of (Frozen, Forest, Ford). +# +# 1, 1, 2, 2, 3, 2, 2, 3, 1, 1, 1, 3, 1 MP for an Elvish Archer +# 1, 2, 2, 1, 2, 2, 2, 2, 1, 1, 1, 3, 1 MP for an Orcish Grunt + +# Bob is always an orc, his movement is checked in every test run to check that +# any effect in ALICE_MODIFICATION hasn't affected his movement costs. +#define ORC_COSTS + 1,2,2,1,2,2,2,2,1,1,1,3,1 +#enddef + +#define MOVETYPE_MOVE_SCEN ID ALICE_TYPE ALICE_MOVE_COSTS BOB_MOVE_COSTS ALICE_MODIFICATION + [test] + name = "Unit Test {ID}" + map_data = "{test/maps/test_movetype.map}" + turns = 1 + id = {ID} + random_start_time = no + is_unit_test = yes + + {DEFAULT_SCHEDULE} + + [side] + side=1 + controller=human + name = "Alice" + type = {ALICE_TYPE} + id=alice + fog=no + shroud=no + share_view=no + [/side] + [side] + side=2 + controller=human + name = "Bob" + type = Orcish Grunt + id=bob + fog=no + shroud=no + share_view=no + [/side] + + [event] + name = prestart + [set_variables] + name=travelers + mode=replace + [split] + list="alice,bob" + key=id + separator="," + [/split] + [/set_variables] + [set_variables] + name=expected_movement_cost + mode=replace + [split] + list={ALICE_MOVE_COSTS} + key=alice + separator="," + [/split] + [/set_variables] + [set_variables] + name=expected_movement_cost + mode=merge + [split] + list={BOB_MOVE_COSTS} + key=bob + separator="," + [/split] + [/set_variables] + {ASSERT {VARIABLE_CONDITIONAL expected_movement_cost.length equals 13}} + [/event] + + [event] + name = side 1 turn 1 + + # This test uses [find_path] to get the movement costs of mixed terrains, + # but if a path needs multiple turns then [find_path] will include the + # cost of movement points that were left unused at the end of all turns + # except the last. To avoid that, give the units enough MP. + [modify_unit] + [filter] + [/filter] + moves="$({UNREACHABLE} - 1)" + max_moves="$({UNREACHABLE} - 1)" + [/modify_unit] + + {ALICE_MODIFICATION} + + [foreach] + array=travelers + variable=traveler + [do] + [find_path] + [traveler] + id=$traveler.id + [/traveler] + [destination] + location_id=destination + [/destination] + allow_multiple_turns=no + variable=path + [/find_path] + # The path's steps include the starting hex, which is why there's + # a +1 here and a +1 in the [for] loop. + {ASSERT {VARIABLE_CONDITIONAL path.hexes not_equals 0}} + {ASSERT {VARIABLE_CONDITIONAL path.step.length equals 14}} + {VARIABLE expected_total 0} + [for] + array=expected_movement_cost + [do] + {VARIABLE i_plus_one "$($i + 1)"} + [set_variable] + name=expected_total + add=$expected_movement_cost[$i].$traveler.id + [/set_variable] + {ASSERT {VARIABLE_CONDITIONAL path.step[$i_plus_one].movement_cost equals $expected_total}} + [/do] + [/for] + [/do] + [/foreach] + + {SUCCEED} + [/event] + [/test] +#enddef + +# This test works by setting Alice's vision points, resetting the fog and then seeing how many hexes are visible. +# The parameter ALICE_VISION_COSTS contains the values of each hex in the path from Alice to the destination, +# so the same order as in the movement tests. Vision will also see the path towards Bob, for which this assumes that +# the terrain adjacent to Bob's hex is the same as the terrain adjacent to Alice's. +# +# The vision cost of the location_id=destination hex will be ignored, as that hex becomes visible based on the cost +# of the adjacent hex, however it's included in the macro argument for easier cut&paste from the MOVETYPE_MOVE_SCEN +# tests. +#define MOVETYPE_VISION_SCEN ID ALICE_TYPE ALICE_VISION_COSTS ALICE_MODIFICATION + [test] + name = "Unit Test {ID}" + map_data = "{test/maps/test_movetype.map}" + turns = 1 + id = {ID} + random_start_time = no + is_unit_test = yes + + {DEFAULT_SCHEDULE} + + [side] + side=1 + controller=human + name = "Alice" + type = {ALICE_TYPE} + id=alice + fog=yes + shroud=no + share_view=no + [/side] + [side] + side=2 + controller=human + name = "Bob" + type = Orcish Grunt + id=bob + fog=yes + shroud=no + share_view=no + [/side] + + [event] + name = prestart + [set_variables] + name=expected_vision_costs + mode=replace + [split] + list={ALICE_VISION_COSTS} + key=alice + separator="," + [/split] + [/set_variables] + {ASSERT {VARIABLE_CONDITIONAL expected_vision_costs.length equals 13}} + + # This could be counted with a location filter, but other assumptions about the + # map already need to be hardcoded in the test. + {VARIABLE walkable_hexes_on_map 10} + [/event] + + [event] + name = side 1 turn 1 + + {ALICE_MODIFICATION} + + {VARIABLE vision_points 0} + {VARIABLE expected_visible_count 0} + [while] + {VARIABLE_CONDITIONAL expected_visible_count less_than $walkable_hexes_on_map} + {VARIABLE_CONDITIONAL ended boolean_equals no} # this is set by ASSERT + [do] + [modify_unit] + [filter] + id=alice + [/filter] + vision=$vision_points + [/modify_unit] + [reset_fog] + [filter_side] + side=1 + [/filter_side] + reset_view=true + [/reset_fog] + [redraw] + side=1 + clear_shroud=true + [/redraw] + + # cumulative sum of the expected_vision_costs + {VARIABLE expected_total 0} + # We can always see the hex Alice is standing on and those adjacent to it, + # for this test we ignore impassible hexes + {VARIABLE expected_visible_count 2} + # How many of the hexes towards the destination can we see? + [for] + array=expected_vision_costs + [do] + [set_variable] + name=expected_total + add=$expected_vision_costs[$i].alice + [/set_variable] + [if] + {VARIABLE_CONDITIONAL vision_points greater_than_equal_to $expected_total} + [then] + [set_variable] + name=expected_visible_count + add=1 + [/set_variable] + [/then] + [/if] + [/do] + [/for] + # Add hexes towards Bob + [if] + {VARIABLE_CONDITIONAL vision_points greater_than_equal_to + "$($expected_vision_costs[0].alice + $expected_vision_costs[1].alice)"} + [then] + [set_variable] + name=expected_visible_count + add=1 + [/set_variable] + [/then] + [/if] + [if] + # Assume that the terrain adjacent to Bob's hex is the same as the terrain adjacent to Alice's. + {VARIABLE_CONDITIONAL vision_points greater_than_equal_to + "$(2 * $expected_vision_costs[0].alice + $expected_vision_costs[1].alice)"} + [then] + [set_variable] + name=expected_visible_count + add=1 + [/set_variable] + [/then] + [/if] + + [store_locations] + terrain=!,X* + [filter_vision] + side=1 + visible=yes + respect_fog=yes + [/filter_vision] + variable=visible_hexes + [/store_locations] + {ASSERT {VARIABLE_CONDITIONAL visible_hexes.length equals $expected_visible_count}} + + [set_variable] + name=vision_points + add=1 + [/set_variable] + [/do] + [/while] + + {SUCCEED} + [/event] + [/test] +#enddef + +#define SET_MOVE_COSTS COSTS + [modify_unit] + [filter] + id=alice + [/filter] + [effect] + apply_to=movement_costs + replace=yes + [movement_costs] + {COSTS} + [/movement_costs] + [/effect] + [/modify_unit] +#enddef + +#define SET_VISION_COSTS COSTS + [modify_unit] + [filter] + id=alice + [/filter] + [effect] + apply_to=vision_costs + replace=yes + [vision_costs] + {COSTS} + [/vision_costs] + [/effect] + [/modify_unit] +#enddef + +{MOVETYPE_MOVE_SCEN test_elf_movement (Elvish Archer) (1,1,2,2,3,2,2,3,1,1,1,3,1) {ORC_COSTS} ()} +{MOVETYPE_MOVE_SCEN test_orc_movement (Orcish Grunt) {ORC_COSTS} {ORC_COSTS} ()} +{MOVETYPE_MOVE_SCEN test_elf_fast_cave_movement (Elvish Archer) (1,1,2,2,2,2,2,2,1,1,1,3,1) {ORC_COSTS} {SET_MOVE_COSTS cave=1}} +{MOVETYPE_MOVE_SCEN test_elf_fast_hills_movement (Elvish Archer) (1,1,1,1,3,2,2,3,1,1,1,3,1) {ORC_COSTS} {SET_MOVE_COSTS hills=1}} +{MOVETYPE_MOVE_SCEN test_elf_fast_cave_and_hills_movement (Elvish Archer) (1,1,1,1,1,2,2,2,1,1,1,3,1) {ORC_COSTS} ({SET_MOVE_COSTS cave=1}{SET_MOVE_COSTS hills=1})} +{MOVETYPE_MOVE_SCEN test_orc_fast_cave_movement (Orcish Grunt) (1,2,2,1,1,2,2,2,1,1,1,3,1) {ORC_COSTS} {SET_MOVE_COSTS cave=1}} +{MOVETYPE_MOVE_SCEN test_orc_fast_forest_movement (Orcish Grunt) (1,1,1,1,2,2,2,2,1,1,1,3,1) {ORC_COSTS} {SET_MOVE_COSTS forest=1}} +{MOVETYPE_MOVE_SCEN test_elf_slow_cave_movement (Elvish Archer) (1,1,2,2,4,2,2,4,1,1,1,3,1) {ORC_COSTS} {SET_MOVE_COSTS cave=4}} + +# changing the vision costs shouldn't affect the movement costs +{MOVETYPE_MOVE_SCEN test_elf_longsighted_movement (Elvish Archer) (1,1,2,2,3,2,2,3,1,1,1,3,1) {ORC_COSTS} {SET_VISION_COSTS cave=1}} +{MOVETYPE_MOVE_SCEN test_orc_longsighted_movement (Orcish Grunt) {ORC_COSTS} {ORC_COSTS} {SET_VISION_COSTS cave=1}} +{MOVETYPE_MOVE_SCEN test_elf_longsighted_fast_cave_movement (Elvish Archer) (1,1,2,2,2,2,2,2,1,1,1,3,1) {ORC_COSTS} ({SET_VISION_COSTS hills=1}{SET_MOVE_COSTS cave=1})} +{MOVETYPE_MOVE_SCEN test_elf_fast_cave_longsighted_movement (Elvish Archer) (1,1,2,2,2,2,2,2,1,1,1,3,1) {ORC_COSTS} ({SET_MOVE_COSTS cave=1}{SET_VISION_COSTS hills=1})} + +{MOVETYPE_VISION_SCEN test_elf_vision (Elvish Archer) (1,1,2,2,3,2,2,3,1,1,1,3,1) ()} +# vision should fall back to movement, so these should match the movement numbers +{MOVETYPE_VISION_SCEN test_elf_fast_cave_vision (Elvish Archer) (1,1,2,2,2,2,2,2,1,1,1,3,1) {SET_MOVE_COSTS cave=1}} +{MOVETYPE_VISION_SCEN test_elf_fast_hills_vision (Elvish Archer) (1,1,1,1,3,2,2,3,1,1,1,3,1) {SET_MOVE_COSTS hills=1}} + +{MOVETYPE_VISION_SCEN test_elf_longsighted_cave_vision (Elvish Archer) (1,1,2,2,2,2,2,2,1,1,1,3,1) {SET_VISION_COSTS cave=1}} +{MOVETYPE_VISION_SCEN test_elf_longsighted_cave_and_hills_vision (Elvish Archer) (1,1,1,1,1,2,2,2,1,1,1,3,1) ({SET_VISION_COSTS cave=1}{SET_VISION_COSTS hills=1})} +{MOVETYPE_VISION_SCEN test_elf_longsighted_cave_slow_cave_vision (Elvish Archer) (1,1,2,2,2,2,2,2,1,1,1,3,1) ({SET_VISION_COSTS cave=1}{SET_MOVE_COSTS cave=4})} + +#undef SET_VISION_COSTS +#undef SET_MOVE_COSTS +#undef MOVETYPE_VISION_SCEN +#undef MOVETYPE_MOVE_SCEN +#undef ORC_COSTS diff --git a/wml_test_schedule b/wml_test_schedule index 08916a23df0..8144ba78bd8 100644 --- a/wml_test_schedule +++ b/wml_test_schedule @@ -120,6 +120,27 @@ 0 characterize_pathfinding_reach_6 0 characterize_pathfinding_reach_7 # +# Movement types, and modifying them +# +0 test_elf_movement +0 test_orc_movement +0 test_elf_fast_cave_movement +0 test_elf_fast_hills_movement +0 test_elf_fast_cave_and_hills_movement +0 test_orc_fast_cave_movement +0 test_orc_fast_forest_movement +0 test_elf_slow_cave_movement +0 test_elf_longsighted_movement +0 test_orc_longsighted_movement +0 test_elf_longsighted_fast_cave_movement +0 test_elf_fast_cave_longsighted_movement +0 test_elf_vision +0 test_elf_fast_cave_vision +0 test_elf_fast_hills_vision +0 test_elf_longsighted_cave_vision +0 test_elf_longsighted_cave_and_hills_vision +0 test_elf_longsighted_cave_slow_cave_vision +# # Attack calculations & codepath tests # 0 alice_kills_bob