Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix path calculation on maps with waterfalls and muddy slopes #643

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions modules/map_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class PathTile:
warps_to: tuple[tuple[int, int], tuple[int, int], Direction | None] | None
waterfall_to: tuple[int, int] | None
muddy_slope_to: tuple[int, int] | None
forced_movement_to: tuple[tuple[int, int], tuple[int, int], tuple] | None
forced_movement_to: tuple[tuple[int, int], tuple[int, int], int] | None
needs_acro_bike: bool
needs_bunny_hop: bool

Expand Down Expand Up @@ -790,7 +790,7 @@ def unroll_path(node: PathNode) -> list[Waypoint]:
# 41 frames for each tile climbed.
is_waterfall = False
if neighbour.waterfall_to is not None:
waterfall_height = neighbour_coordinates[1] - neighbour.waterfall_to[1]
waterfall_height = abs(neighbour.local_coordinates[1] - neighbour.waterfall_to[1])
cost += int(round((195 + 41 * waterfall_height) / 16))
neighbour = tile.map.get_tile(neighbour.waterfall_to)
neighbour_coordinates = neighbour.global_coordinates
Expand All @@ -799,7 +799,7 @@ def unroll_path(node: PathNode) -> list[Waypoint]:
is_muddy_slope = False
if neighbour.muddy_slope_to is not None and direction is Direction.North:
muddy_slope_to = neighbour.map.get_tile(neighbour.muddy_slope_to)
muddy_slope_height = neighbour_coordinates[1] - neighbour.muddy_slope_to[1]
muddy_slope_height = abs(neighbour.local_coordinates[1] - neighbour.muddy_slope_to[1])
cost += muddy_slope_height
neighbour = _find_tile_by_global_coordinates(
(muddy_slope_to.global_coordinates[0], muddy_slope_to.global_coordinates[1] - 2), map_level
Expand All @@ -809,6 +809,10 @@ def unroll_path(node: PathNode) -> list[Waypoint]:

if neighbour.forced_movement_to is not None:
cost += neighbour.forced_movement_to[2]
if neighbour.forced_movement_to[2] < 0:
raise RuntimeError(
f"Encountered a negative-length forced movement from {neighbour.local_coordinates} to {neighbour.forced_movement_to}."
)
neighbour = _find_tile_by_local_coordinates(
neighbour.forced_movement_to[0], neighbour.forced_movement_to[1]
)
Expand Down
Binary file added tests/states/emerald/before_acro_bike_rails.ss1
Binary file not shown.
Binary file added tests/states/emerald/north_of_a_muddy_slope.ss1
Binary file not shown.
Binary file added tests/states/emerald/north_of_a_waterfall.ss1
Binary file not shown.
Binary file added tests/states/emerald/on_land_before_water.ss1
Binary file not shown.
Binary file added tests/states/emerald/on_water_before_land.ss1
Binary file not shown.
Binary file added tests/states/emerald/south_of_a_muddy_slope.ss1
Binary file not shown.
Binary file added tests/states/emerald/south_of_a_waterfall.ss1
Binary file not shown.
Binary file added tests/states/firered/on_land_before_water.ss1
Binary file not shown.
Binary file added tests/states/firered/on_water_before_land.ss1
Binary file not shown.
Binary file added tests/states/ruby/before_acro_bike_rails.ss1
Binary file not shown.
Binary file added tests/states/ruby/north_of_a_muddy_slope.ss1
Binary file not shown.
Binary file added tests/states/ruby/north_of_a_waterfall.ss1
Binary file not shown.
Binary file added tests/states/ruby/on_land_before_water.ss1
Binary file not shown.
Binary file added tests/states/ruby/on_water_before_land.ss1
Binary file not shown.
Binary file added tests/states/ruby/south_of_a_muddy_slope.ss1
Binary file not shown.
Binary file added tests/states/ruby/south_of_a_waterfall.ss1
Binary file not shown.
44 changes: 44 additions & 0 deletions tests/test_pathfinding.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from modules.map_data import MapRSE, MapFRLG
from modules.modes.util import navigate_to
from modules.player import get_player_location
from tests.utility import BotTestCase, with_save_state, with_frame_timeout


Expand Down Expand Up @@ -110,3 +113,44 @@ def test_rse_diving(self):
self.assertEqual(get_map_data_for_current_position().map_type, "Underwater")
yield from surface_from_dive()
self.assertEqual(get_map_data_for_current_position().map_type, "Underground")

@with_save_state(["emerald/south_of_a_muddy_slope.ss1", "ruby/south_of_a_muddy_slope.ss1"])
def test_it_climbs_muddy_slope_on_mach_bike(self):
yield from navigate_to(MapRSE.SAFARI_ZONE_SOUTHWEST, (8, 0))
self.assertEqual(get_player_location(), (MapRSE.SAFARI_ZONE_SOUTHWEST, (8, 0)))

@with_save_state(["emerald/north_of_a_muddy_slope.ss1", "ruby/north_of_a_muddy_slope.ss1"])
def test_it_slides_down_muddy_slope(self):
yield from navigate_to(MapRSE.SAFARI_ZONE_SOUTHWEST, (7, 5))
self.assertEqual(get_player_location(), (MapRSE.SAFARI_ZONE_SOUTHWEST, (7, 5)))

@with_save_state(["emerald/before_acro_bike_rails.ss1", "ruby/before_acro_bike_rails.ss1"])
def test_it_traverses_acro_bike_rails(self):
yield from navigate_to(MapRSE.SAFARI_ZONE_SOUTH, (28, 4))
self.assertEqual(get_player_location(), (MapRSE.SAFARI_ZONE_SOUTH, (28, 4)))

@with_save_state(["emerald/south_of_a_waterfall.ss1", "ruby/south_of_a_waterfall.ss1"])
def test_it_swims_up_a_waterfall(self):
yield from navigate_to(MapRSE.ROUTE119, (18, 23))
self.assertEqual(get_player_location(), (MapRSE.ROUTE119, (18, 23)))

@with_save_state(["emerald/north_of_a_waterfall.ss1", "ruby/north_of_a_waterfall.ss1"])
def test_it_swims_down_a_waterfall(self):
yield from navigate_to(MapRSE.ROUTE119, (18, 30))
self.assertEqual(get_player_location(), (MapRSE.ROUTE119, (18, 30)))

@with_save_state(
["emerald/on_land_before_water.ss1", "ruby/on_land_before_water.ss1", "firered/on_land_before_water.ss1"]
)
def test_it_will_go_on_land_after_surfing(self):
destination = (MapRSE.ROUTE119, (25, 42)) if self.rom.is_rse else (MapFRLG.VIRIDIAN_CITY, (14, 24))
yield from navigate_to(*destination)
self.assertEqual(get_player_location(), destination)

@with_save_state(
["emerald/on_water_before_land.ss1", "ruby/on_water_before_land.ss1", "firered/on_water_before_land.ss1"]
)
def test_it_will_start_to_surf(self):
destination = (MapRSE.ROUTE119, (21, 42)) if self.rom.is_rse else (MapFRLG.VIRIDIAN_CITY, (14, 27))
yield from navigate_to(*destination)
self.assertEqual(get_player_location(), destination)