Add unit tests for movement and vision costs

Includes changing the terrain costs, and aliased terrains.

Can be cleanly cherry-picked to 1.14.
This commit is contained in:
Steve Cotton 2019-07-20 18:01:14 +02:00
parent e62982325a
commit 358f564301
3 changed files with 396 additions and 0 deletions

View file

@ -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

View file

@ -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

View file

@ -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