Skip to content

Commit

Permalink
Add unit tests for movement and vision costs
Browse files Browse the repository at this point in the history
Includes tests for changing the terrain costs, and aliased terrains,

(cherry picked from commit 358f564)
  • Loading branch information
stevecotton committed Jul 31, 2019
1 parent efd7905 commit aa9ff15
Show file tree
Hide file tree
Showing 3 changed files with 396 additions and 0 deletions.
13 changes: 13 additions & 0 deletions 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
362 changes: 362 additions & 0 deletions 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

0 comments on commit aa9ff15

Please sign in to comment.