Basic pathfinding: Complete!
This commit is contained in:
parent
873d622d25
commit
4c95e515e0
|
@ -0,0 +1,31 @@
|
|||
from emis_funky_funktions import *
|
||||
|
||||
from sys import argv
|
||||
from operator import eq
|
||||
|
||||
from a_star import pathfind
|
||||
from read_in import load_world_from_paths
|
||||
from world import Point, World
|
||||
|
||||
def pathfind_world(world: World, start: Point, end: Point) -> Option[Tuple[List[Point], int]]:
|
||||
"Given a `World` with a start and finish point embedded, find the best route"
|
||||
return pathfind(
|
||||
world.neighbors,
|
||||
p(world.heuristic, end),
|
||||
p(eq, end),
|
||||
start
|
||||
)
|
||||
|
||||
def main(terrain_path: str, elevation_path: str, path_output: str, image_output: str):
|
||||
print(
|
||||
pathfind_world(
|
||||
unwrap_r(
|
||||
load_world_from_paths(terrain_path, elevation_path)
|
||||
),
|
||||
Point(200, 475),
|
||||
Point(200, 200)
|
||||
)
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(*argv[1:])
|
|
@ -1,6 +1,6 @@
|
|||
from emis_funky_funktions import *
|
||||
|
||||
from shared import Terrain
|
||||
from world import Terrain, World
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
@ -202,7 +202,7 @@ def load_elevations(path: str) -> Result[Iterator[int], UnparsableElevation]:
|
|||
with open(path) as file:
|
||||
return read_elevations(file)
|
||||
|
||||
def load_world_from_paths(map_path: str, elevation_path: str) -> Result[Iterable[Tuple[Terrain, int]], UnparsableElevation | UnrecognizedColor]:
|
||||
def load_world_from_paths(map_path: str, elevation_path: str) -> Result[World, UnparsableElevation | UnrecognizedColor]:
|
||||
"""
|
||||
Read world information (terrain and elevation) from file paths.
|
||||
|
||||
|
@ -214,7 +214,7 @@ def load_world_from_paths(map_path: str, elevation_path: str) -> Result[Iterable
|
|||
return bind_res(
|
||||
lambda terrain_data:
|
||||
map_res(
|
||||
p(zip, terrain_data),
|
||||
lambda elevation_data: World((*zip(terrain_data, elevation_data),)),
|
||||
load_elevations(elevation_path)
|
||||
),
|
||||
load_image(map_path)
|
||||
|
|
34
world.py
34
world.py
|
@ -62,12 +62,6 @@ class World:
|
|||
the elevation in millimeters at that point.
|
||||
"""
|
||||
|
||||
start: Point
|
||||
"Where routing should start"
|
||||
|
||||
finish: Point
|
||||
"Where routing should finish"
|
||||
|
||||
width: int
|
||||
"The number of pixels wide the map is"
|
||||
|
||||
|
@ -82,15 +76,11 @@ class World:
|
|||
|
||||
def __init__(self,
|
||||
tiles: Sequence[Tuple[Terrain, int]],
|
||||
start: Point,
|
||||
finish: Point,
|
||||
width: int = 395,
|
||||
lon_scale: int = 10_290,
|
||||
lat_scale: int = 7_550
|
||||
):
|
||||
self.tiles = tiles
|
||||
self.start = start
|
||||
self.finish = finish
|
||||
self.width = width
|
||||
self.lon_scale = lon_scale
|
||||
self.lat_scale = lat_scale
|
||||
|
@ -107,7 +97,7 @@ class World:
|
|||
This does not take into account the presence, value, or elevation of tiles
|
||||
represented on these points.
|
||||
|
||||
>>> world = World([], None, None, lon_scale=10_290, lat_scale=7_550)
|
||||
>>> world = World([], lon_scale=10_290, lat_scale=7_550)
|
||||
>>> world._adjacency(Point(13, 12)) #doctest: +NORMALIZE_WHITESPACE
|
||||
[((12, 11), 12762),
|
||||
((13, 11), 7550),
|
||||
|
@ -141,7 +131,7 @@ class World:
|
|||
|
||||
Points do not need to be adjacent!
|
||||
|
||||
>>> world = World([(Terrain.BRUSH, 1_000), (Terrain.BRUSH, 1_100)], None, None)
|
||||
>>> world = World([(Terrain.BRUSH, 1_000), (Terrain.BRUSH, 1_100)])
|
||||
>>> world.elevation_difference(Point(0, 0), Point(1, 0))
|
||||
100
|
||||
"""
|
||||
|
@ -175,8 +165,6 @@ class World:
|
|||
... [ (Terrain.OPEN_LAND, 1_000), (Terrain.OOB, 950)
|
||||
... , (Terrain.ROUGH_MEADOW, 1_080), (Terrain.EASY_FOREST, 1_010)
|
||||
... ],
|
||||
... None, # We're not interested in a start state
|
||||
... None, # nor an end state
|
||||
... width = 2, # Our simple world is only two tiles wide
|
||||
... lon_scale = 500, # Pick an easy number for example
|
||||
... lat_scale = 400 # and remember that these are in millimeters!
|
||||
|
@ -261,9 +249,9 @@ class World:
|
|||
and self[adj_point][0] != Terrain.OOB
|
||||
]
|
||||
|
||||
def heuristic(self, p: Point) -> int:
|
||||
def heuristic(self, a: Point, b: Point) -> int:
|
||||
"""
|
||||
Estimate the time it will take to travel between a point and the finish
|
||||
Estimate the time it will take to travel between two points
|
||||
|
||||
The following assumptions are made to speed up the process, at the cost of
|
||||
accuracy:
|
||||
|
@ -279,14 +267,14 @@ class World:
|
|||
access world data at all, and the results of the computation depend exclusively on
|
||||
the start point, the finish point, and the longitudinal/latitudinal scales.
|
||||
|
||||
>>> world = World(None, None, Point(3, 5), lon_scale = 500, lat_scale = 400)
|
||||
>>> world.heuristic(Point(0, 0))
|
||||
>>> world = World([], lon_scale = 500, lat_scale = 400)
|
||||
>>> world.heuristic(Point(0, 0), Point(3, 5))
|
||||
1632000
|
||||
"""
|
||||
|
||||
# Taxicab distance in each direction
|
||||
lon_tiles_raw = abs(p.x - self.finish.x)
|
||||
lat_tiles_raw = abs(p.y - self.finish.y)
|
||||
lon_tiles_raw = abs(a.x - b.x)
|
||||
lat_tiles_raw = abs(a.y - b.y)
|
||||
|
||||
# The number of moves necessary, allowing diagonal moves
|
||||
lon_moves_real = max(0, lon_tiles_raw - lat_tiles_raw)
|
||||
|
@ -306,12 +294,6 @@ class World:
|
|||
|
||||
return estimated_speed * total_flat_distance
|
||||
|
||||
def goal(self, p: Point) -> bool:
|
||||
"""
|
||||
Equivalent to `p == world.finish`
|
||||
"""
|
||||
return p == self.finish
|
||||
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
doctest.testmod()
|
Loading…
Reference in New Issue