diff --git a/.github/workflows/rust_build.yml b/.github/workflows/rust_build.yml new file mode 100644 index 0000000..3146136 --- /dev/null +++ b/.github/workflows/rust_build.yml @@ -0,0 +1,88 @@ +name: RustBuild +on: [push] + +jobs: + build: + name: Build release + + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + rust: [nightly] + python-version: [3.6, 3.7, 3.8] + + steps: + # https://github.com/actions-rs/toolchain + - uses: actions/checkout@v1 + + # Cache + - name: Cache cargo registry + uses: actions/cache@v1 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo index + uses: actions/cache@v1 + with: + path: ~/.cargo/git + key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo build + uses: actions/cache@v1 + with: + path: target + key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} + + - name: Install latest nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly-2019-11-01 + override: true + + - name: Run cargo check + uses: actions-rs/cargo@v1 + with: + command: check + + - name: Run clippy + run: | + rustup component add clippy + cargo clippy + + - name: Test + run: cargo test --verbose --release + if: matrix.os == 'windows-latest' + + - name: Bench + run: cargo bench + if: matrix.os == 'windows-latest' + + - name: Build + run: cargo build --verbose --release + + - name: Create artifact directory + run: mkdir artifacts + + # TODO only one .so file needs to be built and it can be run from any python version on the linux distribution, and this probably also works on mac + - name: Create archive for Linux + run: | + sudo apt-get install tree + tree target/release + mv ./target/release/libsc2pathlib.so ./artifacts/sc2pathlib.so + if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.7' + + - name: Create archive for Windows + run: | + dir target/release + dir + python ./rename_output.py ${{ matrix.python-version }} + if: matrix.os == 'windows-latest' + + # See - https://github.com/actions/upload-artifact/issues/39 + - uses: actions/upload-artifact@v1 + name: Upload archive + with: + name: ${{ matrix.os }}_python${{ matrix.python-version }} + path: artifacts/ + if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.7' || matrix.os == 'windows-latest' diff --git a/.gitignore b/.gitignore index f3dedba..fcf349b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..057ed3b --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,359 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f56c476256dc249def911d6f7580b5fc7e875895b5d7ee88f5d602208035744" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "ctor" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" + +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[package]] +name = "ghost" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a36606a68532b5640dc86bb1f33c64b45c4682aad4c50f3937b317ea387f3d6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "indexmap" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b54058f0a6ff80b6803da8faf8997cde53872b38f4023728f6830b06cd3c0dc" +dependencies = [ + "autocfg", +] + +[[package]] +name = "indoc" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9553c1e16c114b8b77ebeb329e5f2876eed62a8d51178c8bc6bff0d65f98f8" +dependencies = [ + "indoc-impl", + "proc-macro-hack", +] + +[[package]] +name = "indoc-impl" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b714fc08d0961716390977cdff1536234415ac37b509e34e5a983def8340fb75" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", + "unindent", +] + +[[package]] +name = "inventory" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf98296081bd2cb540acc09ef9c97f22b7e487841520350293605db1b2c7a27" +dependencies = [ + "ctor", + "ghost", + "inventory-impl", +] + +[[package]] +name = "inventory-impl" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a8e30575afe28eea36a9a39136b70b2fb6b0dd0a212a5bd1f30a498395c0274" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "itertools" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" + +[[package]] +name = "memchr" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +dependencies = [ + "autocfg", +] + +[[package]] +name = "paste" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" +dependencies = [ + "paste-impl", + "proc-macro-hack", +] + +[[package]] +name = "paste-impl" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pathfinding" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c84b3f8a97e544e03c0b8865ddff93de5d8c3c96962b51bfc9639e5dddf299b" +dependencies = [ + "fixedbitset", + "indexmap", + "itertools", + "num-traits", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "pyo3" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1bfe257586436fbe1296d917f14a167d4253d0873bf43e2c9b9bdd58a3f9f35" +dependencies = [ + "indoc", + "inventory", + "lazy_static", + "libc", + "num-traits", + "paste", + "pyo3cls", + "regex", + "serde", + "serde_json", + "spin", + "unindent", + "version_check", +] + +[[package]] +name = "pyo3-derive-backend" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4882d8237fd8c7373cc25cb802fe0dab9ff70830fd56f47ef6c7f3f287fcc057" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pyo3cls" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf321cfab555f7411298733c86d21e5136f5ded13f5872fabf9de3337beecda" +dependencies = [ + "proc-macro2", + "pyo3-derive-backend", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90" + +[[package]] +name = "ryu" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" + +[[package]] +name = "sc2pathlib" +version = "0.3.1" +dependencies = [ + "pathfinding", + "pyo3", +] + +[[package]] +name = "serde" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab8f15f15d6c41a154c1b128a22f2dfabe350ef53c40953d84e36155c91192b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "unindent" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63f18aa3b0e35fed5a0048f029558b1518095ffe2a0a31fb87c93dece93a4993" + +[[package]] +name = "version_check" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" diff --git a/kite_test.py b/kite_test.py index b606f23..72cbe18 100644 --- a/kite_test.py +++ b/kite_test.py @@ -3,10 +3,11 @@ from typing import List from math import floor + def read_maze(file_name: str) -> List[List[int]]: - with open(file_name, 'r') as text: + with open(file_name, "r") as text: m = text.read() - lines = m.split('\n') + lines = m.split("\n") final_maze = [] for y in range(0, len(lines[0])): maze_line = [] @@ -15,11 +16,12 @@ def read_maze(file_name: str) -> List[List[int]]: maze_line.append(int(lines[x][y])) return final_maze + maze = read_maze("tests/empty10x10.txt") pf = sc2pathlibp.PathFinder(maze) pf.normalize_influence(1) enemy_pos = (4, 0) -start_pos = (5,5) +start_pos = (5, 5) pf.add_influence_walk([enemy_pos], 100, 7) end_result = pf.find_low_inside_walk(start_pos, enemy_pos, 5) print(end_result) @@ -28,4 +30,4 @@ def read_maze(file_name: str) -> List[List[int]]: end_point_int = (floor(end_point[0]), floor(end_point[1])) pf.plot([start_point_int, end_point_int], resize=40) -input("Press Enter to continue...") \ No newline at end of file +input("Press Enter to continue...") diff --git a/rename_output.py b/rename_output.py new file mode 100644 index 0000000..a4dfacb --- /dev/null +++ b/rename_output.py @@ -0,0 +1,6 @@ +import shutil +import sys + +version = sys.argv[1].replace(".", "") + +shutil.move("./target/release/sc2pathlib.dll", "./artifacts/sc2pathlib.cp{}-win_amd64.pyd".format(version)) diff --git a/sc2pathlibp/__init__.py b/sc2pathlibp/__init__.py index 8f68791..7a515d2 100644 --- a/sc2pathlibp/__init__.py +++ b/sc2pathlibp/__init__.py @@ -1 +1 @@ -from .path_finder import PathFinder \ No newline at end of file +from .path_finder import PathFinder diff --git a/sc2pathlibp/path_finder.py b/sc2pathlibp/path_finder.py index de2a2a4..f786713 100644 --- a/sc2pathlibp/path_finder.py +++ b/sc2pathlibp/path_finder.py @@ -1,15 +1,17 @@ -from . sc2pathlib import PathFind -#from . import _sc2pathlib -#import sc2pathlib +from .sc2pathlib import PathFind + +# from . import _sc2pathlib +# import sc2pathlib import numpy as np from typing import Union, List, Tuple from math import floor + def to_float2(original: Tuple[int, int]) -> Tuple[float, float]: return (original[0] + 0.5, original[1] + 0.5) -class PathFinder(): +class PathFinder: def __init__(self, maze: Union[List[List[int]], np.array]): """ pathing values need to be integers to improve performance. @@ -17,7 +19,7 @@ def __init__(self, maze: Union[List[List[int]], np.array]): """ self._path_find = PathFind(maze) self.heuristic_accuracy = 1 # Octile distance - + def normalize_influence(self, value: int): """ Normalizes influence to integral value. @@ -25,14 +27,14 @@ def normalize_influence(self, value: int): influence values to specified value without changing available paths. """ self._path_find.normalize_influence(value) - + @property def width(self) -> int: """ :return: Width of the defined map """ return self._path_find.width - + @property def height(self) -> int: """ @@ -46,7 +48,7 @@ def map(self) -> List[List[int]]: :return: map as list of lists [x][y] in python readable format """ return self._path_find.map - + def reset(self): """ Reset the pathfind map data to it's original state @@ -55,7 +57,7 @@ def reset(self): def set_map(self, data: List[List[int]]): self._path_find.map = data - + def create_block(self, center: Union[Tuple[float, float], List[Tuple[float, float]]], size: Tuple[int, int]): if isinstance(center, list): self._path_find.create_blocks(center, size) @@ -68,7 +70,9 @@ def remove_block(self, center: Union[Tuple[float, float], List[Tuple[float, floa else: self._path_find.remove_block(center, size) - def find_path(self, start: (float, float), end: (float, float), large: bool = False) -> Tuple[List[Tuple[int, int]], float]: + def find_path( + self, start: (float, float), end: (float, float), large: bool = False + ) -> Tuple[List[Tuple[int, int]], float]: """ Finds a path ignoring influence. @@ -82,8 +86,10 @@ def find_path(self, start: (float, float), end: (float, float), large: bool = Fa if large: return self._path_find.find_path_large(start_int, end_int, self.heuristic_accuracy) return self._path_find.find_path(start_int, end_int, self.heuristic_accuracy) - - def find_path_influence(self, start: (float, float), end: (float, float), large: bool = False) -> (List[Tuple[int, int]], float): + + def find_path_influence( + self, start: (float, float), end: (float, float), large: bool = False + ) -> (List[Tuple[int, int]], float): """ Finds a path that takes influence into account @@ -101,16 +107,16 @@ def find_path_influence(self, start: (float, float), end: (float, float), large: def safest_spot(self, destination_center: (float, float), walk_distance: float) -> (Tuple[int, int], float): destination_int = (floor(destination_center[0]), floor(destination_center[1])) return self._path_find.lowest_influence_walk(destination_int, walk_distance) - + def lowest_influence_in_grid(self, destination_center: (float, float), radius: int) -> (Tuple[int, int], float): destination_int = (floor(destination_center[0]), floor(destination_center[1])) return self._path_find.lowest_influence(destination_int, radius) - + def add_influence(self, points: List[Tuple[float, float]], value: float, distance: float, flat: bool = False): list = [] for point in points: list.append((floor(point[0]), floor(point[1]))) - + if flat: self._path_find.add_influence_flat(list, value, distance) else: @@ -120,13 +126,15 @@ def add_influence_walk(self, points: List[Tuple[float, float]], value: float, di list = [] for point in points: list.append((floor(point[0]), floor(point[1]))) - + if flat: self._path_find.add_walk_influence_flat(list, value, distance) else: self._path_find.add_walk_influence(list, value, distance) - def find_low_inside_walk(self, start: (float, float), target: (float, float), distance: Union[int, float]) -> (Tuple[float, float], float): + def find_low_inside_walk( + self, start: (float, float), target: (float, float), distance: Union[int, float] + ) -> (Tuple[float, float], float): """ Finds a compromise where low influence matches with close position to the start position. @@ -152,10 +160,11 @@ def plot(self, path: List[Tuple[int, int]], image_name: str = "map", resize: int :return: None """ import cv2 - image = np.array(self._path_find.map, dtype = np.uint8) + + image = np.array(self._path_find.map, dtype=np.uint8) for point in path: image[point] = 255 image = np.rot90(image, 1) resized = cv2.resize(image, dsize=None, fx=resize, fy=resize) cv2.imshow(image_name, resized) - cv2.waitKey(1) \ No newline at end of file + cv2.waitKey(1) diff --git a/src/angles.rs b/src/angles.rs index aa27a41..269232c 100644 --- a/src/angles.rs +++ b/src/angles.rs @@ -2,12 +2,15 @@ const pi2: f64 = std::f64::consts::PI * 2.0; #[inline] pub fn angle_between(first: (usize, usize), other: (usize, usize)) -> f64 { - point_angle((other.0 as f64 - first.0 as f64, other.1 as f64 - first.1 as f64)) + point_angle(( + other.0 as f64 - first.0 as f64, + other.1 as f64 - first.1 as f64, + )) } #[inline] pub fn angle_between_f64(first: (f64, f64), other: (f64, f64)) -> f64 { - point_angle((other.0 - first.0 , other.1 - first.1)) + point_angle((other.0 - first.0, other.1 - first.1)) } // (x,y) = (1,0) => -pi /2 @@ -15,20 +18,17 @@ pub fn angle_between_f64(first: (f64, f64), other: (f64, f64)) -> f64 { // (x,y) = (0,1) => pi // (x,y) = (-1,0) => pi / 2 #[inline] -pub fn point_angle(point: (f64, f64)) -> f64{ +pub fn point_angle(point: (f64, f64)) -> f64 { let mut angle: f64; if point.1 == 0.0 { if point.0 > 0.0 { angle = std::f64::consts::FRAC_PI_2 - } - else if point.1 < 0.0 { + } else if point.1 < 0.0 { angle = -std::f64::consts::FRAC_PI_2 - } - else { + } else { angle = 0.0f64 } - } - else { + } else { angle = (point.0 as f64 / point.1 as f64).atan(); if point.1 >= 0.0 { angle += std::f64::consts::PI @@ -38,31 +38,28 @@ pub fn point_angle(point: (f64, f64)) -> f64{ angle } -pub fn wrap_angle(angle:f64) -> f64{ +pub fn wrap_angle(angle: f64) -> f64 { let mut angle = angle % pi2; - if angle < -std::f64::consts::PI{ + if angle < -std::f64::consts::PI { angle += pi2 - } - else if angle > std::f64::consts::PI{ + } else if angle > std::f64::consts::PI { angle -= pi2 } angle } -pub fn angle_distance(angle1: f64, angle2:f64) -> f64 { +pub fn angle_distance(angle1: f64, angle2: f64) -> f64 { let angle1 = wrap_angle(angle1); let angle2 = wrap_angle(angle2); let mut d = f64::abs(angle2 - angle1); - if d <= std::f64::consts::PI{ + if d <= std::f64::consts::PI { // Do nothing - } - else if angle1 < angle2{ + } else if angle1 < angle2 { d = f64::abs(angle2 - (angle1 + std::f64::consts::FRAC_PI_2)) - } - else { + } else { d = f64::abs(angle2 + std::f64::consts::FRAC_PI_2 - angle1) } d -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 7b20986..1682f9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,11 +2,11 @@ use pyo3::prelude::*; //use pyo3::wrap_pyfunction; use pathfinding::prelude::{absdiff, astar, dijkstra_all, dijkstra_partial}; // use std::cmp::{min, max}; +mod angles; mod pos; mod pos_large; mod rectangle; mod search_grid; -mod angles; #[pyclass] pub struct PathFind { @@ -16,24 +16,23 @@ pub struct PathFind { height: usize, normal_influence: usize, auto_correct: bool, - free_finder: search_grid::FreeFinder + free_finder: search_grid::FreeFinder, } #[inline] -pub fn octile_distance(first: (usize, usize), other: (usize, usize)) -> usize{ +pub fn octile_distance(first: (usize, usize), other: (usize, usize)) -> usize { let dx = absdiff(first.0, other.0); let dy = absdiff(first.1, other.1); - if dx > dy{ + if dx > dy { pos::MULT * dx + pos::DIAGONAL_MINUS_CARDINAL * dy - } - else{ + } else { pos::MULT * dy + pos::DIAGONAL_MINUS_CARDINAL * dx } } #[inline] -pub fn octile_distance_f64(first: (usize, usize), other: (usize, usize)) -> f64{ +pub fn octile_distance_f64(first: (usize, usize), other: (usize, usize)) -> f64 { (octile_distance(first, other) as f64) / pos::MULTF64 } @@ -56,49 +55,57 @@ impl PathFind { let auto_correct: bool = true; let free_finder = search_grid::FreeFinder::new(); - obj.init(PathFind { map, original_map, width, height, normal_influence, auto_correct, free_finder }) + obj.init(PathFind { + map, + original_map, + width, + height, + normal_influence, + auto_correct, + free_finder, + }) } // object.width #[getter(width)] - fn get_width(&self)-> PyResult{ + fn get_width(&self) -> PyResult { Ok(self.width) } // object.height #[getter(height)] - fn get_height(&self)-> PyResult{ + fn get_height(&self) -> PyResult { Ok(self.height) } // object.normal_influence #[getter(normal_influence)] - fn get_normal_influence(&self)-> PyResult{ + fn get_normal_influence(&self) -> PyResult { Ok(self.normal_influence) } // object.map #[getter(map)] - fn get_map(&self)-> PyResult>>{ + fn get_map(&self) -> PyResult>> { Ok(self.map.clone()) } // object.map(2dArray) #[setter(map)] - fn set_map(&mut self, value:Vec>) -> PyResult<()> { + fn set_map(&mut self, value: Vec>) -> PyResult<()> { self.map = value; Ok(()) } - // object.auto_correct + // object.auto_correct #[getter(auto_correct)] - fn get_auto_correct(&self)-> PyResult{ + fn get_auto_correct(&self) -> PyResult { Ok(self.auto_correct) } // object.map(bool) #[setter(auto_correct)] - fn set_auto_correct(&mut self, value:bool) -> PyResult<()> { + fn set_auto_correct(&mut self, value: bool) -> PyResult<()> { self.auto_correct = value; Ok(()) } @@ -110,12 +117,11 @@ impl PathFind { // Creates a block on the grid that is not pathable // center = center of building - fn create_block(&mut self, center: (f32,f32), size: (usize, usize)) { - + fn create_block(&mut self, center: (f32, f32), size: (usize, usize)) { let rect = rectangle::Rectangle::init_from_center(center, size, self.width, self.height); for x in rect.x..rect.x_end { - for y in rect.y..rect.y_end{ + for y in rect.y..rect.y_end { self.map[x][y] = 0; } } @@ -123,13 +129,13 @@ impl PathFind { // Creates a block on the grid that is not pathable // center = center of building - fn create_blocks(&mut self, centers: Vec<(f32,f32)>, size: (usize, usize)) { - + fn create_blocks(&mut self, centers: Vec<(f32, f32)>, size: (usize, usize)) { for center in centers { - let rect = rectangle::Rectangle::init_from_center(center, size, self.width, self.height); + let rect = + rectangle::Rectangle::init_from_center(center, size, self.width, self.height); for x in rect.x..rect.x_end { - for y in rect.y..rect.y_end{ + for y in rect.y..rect.y_end { self.map[x][y] = 0; } } @@ -138,11 +144,11 @@ impl PathFind { // Removes a block on the grid and makes it pathable // center = center of block - fn remove_block(&mut self, center: (f32,f32), size: (usize, usize)) { + fn remove_block(&mut self, center: (f32, f32), size: (usize, usize)) { let rect = rectangle::Rectangle::init_from_center(center, size, self.width, self.height); for x in rect.x..rect.x_end { - for y in rect.y..rect.y_end{ + for y in rect.y..rect.y_end { self.map[x][y] = self.normal_influence; } } @@ -150,13 +156,13 @@ impl PathFind { // Removes multiple blocks on the grid and makes it pathable // center = center of block - fn remove_blocks(&mut self, centers: Vec<(f32,f32)>, size: (usize, usize)) { - + fn remove_blocks(&mut self, centers: Vec<(f32, f32)>, size: (usize, usize)) { for center in centers { - let rect = rectangle::Rectangle::init_from_center(center, size, self.width, self.height); + let rect = + rectangle::Rectangle::init_from_center(center, size, self.width, self.height); for x in rect.x..rect.x_end { - for y in rect.y..rect.y_end{ + for y in rect.y..rect.y_end { self.map[x][y] = self.normal_influence; } } @@ -165,7 +171,7 @@ impl PathFind { fn normalize_influence(&mut self, value: usize) { self.normal_influence = value; - + for x in 0..self.width { for y in 0..self.height { if self.map[x][y] > 0 { @@ -176,16 +182,26 @@ impl PathFind { } /// Adds influence based on euclidean distance - fn add_influence(&mut self, positions: Vec<(usize, usize)>, max: f32, distance: f32) -> PyResult<()> { + fn add_influence( + &mut self, + positions: Vec<(usize, usize)>, + max: f32, + distance: f32, + ) -> PyResult<()> { let mult = 1.0 / (distance * pos::MULTF64 as f32); let diameter = (distance as usize) + 1; let rect_size = (diameter, diameter); for position in positions { - let rect = rectangle::Rectangle::init_from_center2(position, rect_size, self.width, self.height); + let rect = rectangle::Rectangle::init_from_center2( + position, + rect_size, + self.width, + self.height, + ); for x in rect.x..rect.x_end { - for y in rect.y..rect.y_end{ + for y in rect.y..rect.y_end { let value = max * (1.0 - (octile_distance(position, (x, y)) as f32) * mult); if value > 0.0 { self.map[x][y] += value as usize; @@ -193,12 +209,17 @@ impl PathFind { } } } - + Ok(()) } /// Adds influence based on euclidean distance - fn add_influence_flat(&mut self, positions: Vec<(usize, usize)>, max: f32, distance: f32) -> PyResult<()> { + fn add_influence_flat( + &mut self, + positions: Vec<(usize, usize)>, + max: f32, + distance: f32, + ) -> PyResult<()> { let value = max as usize; let mult_distance = distance * pos::MULTF64 as f32; @@ -206,29 +227,38 @@ impl PathFind { let rect_size = (diameter, diameter); for position in positions { - let rect = rectangle::Rectangle::init_from_center2(position, rect_size, self.width, self.height); + let rect = rectangle::Rectangle::init_from_center2( + position, + rect_size, + self.width, + self.height, + ); for x in rect.x..rect.x_end { - for y in rect.y..rect.y_end{ + for y in rect.y..rect.y_end { if (octile_distance(position, (x, y)) as f32) < mult_distance { self.map[x][y] += value; } } } } - + Ok(()) } /// Adds influence based on walk distance - fn add_walk_influence(&mut self, positions: Vec<(usize, usize)>, max: f64, distance: f64) -> PyResult<()> { + fn add_walk_influence( + &mut self, + positions: Vec<(usize, usize)>, + max: f64, + distance: f64, + ) -> PyResult<()> { let mult = 1.0 / distance; let max_int = max as usize; for position in &positions { - if self.map[position.0][position.1] == 0 { - continue; + continue; } let destinations = self.find_destinations_in_inline(*position, distance); @@ -244,19 +274,24 @@ impl PathFind { } } } - + Ok(()) } /// Adds influence based on walk distance - fn add_walk_influence_flat(&mut self, positions: Vec<(usize, usize)>, max: f64, distance: f64) -> PyResult<()> { + fn add_walk_influence_flat( + &mut self, + positions: Vec<(usize, usize)>, + max: f64, + distance: f64, + ) -> PyResult<()> { let max_int = max as usize; for position in &positions { let corrected_position = self.get_closest_pathable(*position); if self.map[corrected_position.0][corrected_position.1] == 0 { - continue; + continue; } let destinations = self.find_destinations_in_inline(corrected_position, distance); @@ -267,19 +302,27 @@ impl PathFind { self.map[end_point.0][end_point.1] += max_int } } - + Ok(()) } /// Finds the first reachable position within specified walking distance from the center point with lowest value - fn lowest_influence_walk(&self, center: (usize, usize), distance: f64) -> PyResult<((usize, usize), f64)> { + fn lowest_influence_walk( + &self, + center: (usize, usize), + distance: f64, + ) -> PyResult<((usize, usize), f64)> { let corrected_center = self.get_closest_pathable(center); Ok(self.lowest_influence_walk_inline(corrected_center, distance)) } #[inline] - fn lowest_influence_walk_inline(&self, center: (usize, usize), distance: f64) -> ((usize, usize), f64) { + fn lowest_influence_walk_inline( + &self, + center: (usize, usize), + distance: f64, + ) -> ((usize, usize), f64) { let destinations = self.find_destinations_in_inline(center, distance); let mut min_value = std::usize::MAX; @@ -289,29 +332,39 @@ impl PathFind { for destination in destinations { let pos = destination.0; let new_val = self.map[pos.0][pos.1]; - if new_val == 0 { continue; } + if new_val == 0 { + continue; + } let distance = destination.1; - + if new_val < min_value || (new_val == min_value && distance < min_distance) { min_value = new_val; min_distance = distance; min_position = pos; } } - + (min_position, min_distance) } - /// Finds the first reachable position within specified distance from the center point with lowest value - fn lowest_influence(&self, center: (f32, f32), distance: usize) -> PyResult<((usize, usize), f64)> { + fn lowest_influence( + &self, + center: (f32, f32), + distance: usize, + ) -> PyResult<((usize, usize), f64)> { Ok(self.inline_lowest_value(center, distance)) } #[inline] fn inline_lowest_value(&self, center: (f32, f32), distance: usize) -> ((usize, usize), f64) { - let rect = rectangle::Rectangle::init_from_center(center, (distance, distance), self.width, self.height); + let rect = rectangle::Rectangle::init_from_center( + center, + (distance, distance), + self.width, + self.height, + ); let mut min_value = std::usize::MAX; let mut min_distance = std::usize::MAX; @@ -321,23 +374,30 @@ impl PathFind { for x in rect.x..rect.x_end { for y in rect.y..rect.y_end { let new_val = self.map[x][y]; - if new_val == 0 { continue; } + if new_val == 0 { + continue; + } + + let distance = octile_distance((x, y), target_pos); - let distance = octile_distance((x,y), target_pos); - if new_val < min_value || (new_val == min_value && distance < min_distance) { min_value = new_val; min_distance = distance; - min_position = (x,y); + min_position = (x, y); } } } (min_position, min_distance as f64 / pos::MULTF64) } - - /// Find the shortest path values without considering influence and returns the path and distance - fn find_path(&self, start: (usize, usize), end: (usize, usize), possible_heuristic: Option) -> PyResult<(Vec<(usize, usize)>, f64)> { + + /// Find the shortest path values without considering influence and returns the path and distance + fn find_path( + &self, + start: (usize, usize), + end: (usize, usize), + possible_heuristic: Option, + ) -> PyResult<(Vec<(usize, usize)>, f64)> { let corrected_start = self.get_closest_pathable(start); let corrected_end = self.get_closest_pathable(end); @@ -347,32 +407,57 @@ impl PathFind { let result: Option<(Vec, usize)>; match possible_heuristic.unwrap_or(0) { - 0 => result = astar(&start, |p| p.successors(grid), |p| p.manhattan_distance(&goal), |p| *p == goal), - 1 => result = astar(&start, |p| p.successors(grid), |p| p.octile_distance(&goal), |p| *p == goal), - _ => result = astar(&start, |p| p.successors(grid), |p| p.euclidean_distance(&goal), |p| *p == goal), + 0 => { + result = astar( + &start, + |p| p.successors(grid), + |p| p.manhattan_distance(&goal), + |p| *p == goal, + ) + } + 1 => { + result = astar( + &start, + |p| p.successors(grid), + |p| p.octile_distance(&goal), + |p| *p == goal, + ) + } + _ => { + result = astar( + &start, + |p| p.successors(grid), + |p| p.euclidean_distance(&goal), + |p| *p == goal, + ) + } }; - + let mut path: Vec<(usize, usize)>; let distance: f64; - if result.is_none(){ + if result.is_none() { path = Vec::<(usize, usize)>::new(); distance = 0.0 - } - else { + } else { let unwrapped = result.unwrap(); distance = (unwrapped.1 as f64) / pos::MULTF64; path = Vec::<(usize, usize)>::with_capacity(unwrapped.0.len()); for pos in unwrapped.0 { - path.push((pos.0, pos.1)) + path.push((pos.0, pos.1)) } } - + Ok((path, distance)) } /// Find the shortest path values without considering influence and returns the path and distance - fn find_path_large(&self, start: (usize, usize), end: (usize, usize), possible_heuristic: Option) -> PyResult<(Vec<(usize, usize)>, f64)> { + fn find_path_large( + &self, + start: (usize, usize), + end: (usize, usize), + possible_heuristic: Option, + ) -> PyResult<(Vec<(usize, usize)>, f64)> { let corrected_start = self.get_closest_pathable(start); let corrected_end = self.get_closest_pathable(end); @@ -382,40 +467,70 @@ impl PathFind { let result: Option<(Vec, usize)>; match possible_heuristic.unwrap_or(0) { - 0 => result = astar(&start, |p| p.successors(grid), |p| p.manhattan_distance(&goal), |p| *p == goal), - 1 => result = astar(&start, |p| p.successors(grid), |p| p.octile_distance(&goal), |p| *p == goal), - _ => result = astar(&start, |p| p.successors(grid), |p| p.euclidean_distance(&goal), |p| *p == goal), + 0 => { + result = astar( + &start, + |p| p.successors(grid), + |p| p.manhattan_distance(&goal), + |p| *p == goal, + ) + } + 1 => { + result = astar( + &start, + |p| p.successors(grid), + |p| p.octile_distance(&goal), + |p| *p == goal, + ) + } + _ => { + result = astar( + &start, + |p| p.successors(grid), + |p| p.euclidean_distance(&goal), + |p| *p == goal, + ) + } }; - + let mut path: Vec<(usize, usize)>; let distance: f64; - if result.is_none(){ + if result.is_none() { path = Vec::<(usize, usize)>::new(); distance = 0.0 - } - else { + } else { let unwrapped = result.unwrap(); distance = (unwrapped.1 as f64) / pos::MULTF64; path = Vec::<(usize, usize)>::with_capacity(unwrapped.0.len()); for pos in unwrapped.0 { - path.push((pos.0, pos.1)) + path.push((pos.0, pos.1)) } } - + Ok((path, distance)) } /// Find the path using influence values and returns the path and distance - fn find_path_influence(&self, start: (usize, usize), end: (usize, usize), possible_heuristic: Option) -> PyResult<(Vec<(usize, usize)>, f64)> { + fn find_path_influence( + &self, + start: (usize, usize), + end: (usize, usize), + possible_heuristic: Option, + ) -> PyResult<(Vec<(usize, usize)>, f64)> { let corrected_start = self.get_closest_pathable(start); let corrected_end = self.get_closest_pathable(end); - + Ok(self.find_path_influence_inline(corrected_start, corrected_end, possible_heuristic)) } #[inline] - fn find_path_influence_inline(&self, corrected_start: (usize, usize), corrected_end: (usize, usize), possible_heuristic: Option) -> (Vec<(usize, usize)>, f64) { + fn find_path_influence_inline( + &self, + corrected_start: (usize, usize), + corrected_end: (usize, usize), + possible_heuristic: Option, + ) -> (Vec<(usize, usize)>, f64) { let start = pos::InfluencedPos(corrected_start.0, corrected_start.1); let goal = pos::InfluencedPos(corrected_end.0, corrected_end.1); let grid: &Vec> = &self.map; @@ -424,24 +539,44 @@ impl PathFind { let result: Option<(Vec, usize)>; match possible_heuristic.unwrap_or(0) { - 0 => result = astar(&start, |p| p.successors(grid), |p| p.manhattan_distance(&goal, infl), |p| *p == goal), - 1 => result = astar(&start, |p| p.successors(grid), |p| p.octile_distance(&goal, infl), |p| *p == goal), - _ => result = astar(&start, |p| p.successors(grid), |p| p.euclidean_distance(&goal, infl), |p| *p == goal), + 0 => { + result = astar( + &start, + |p| p.successors(grid), + |p| p.manhattan_distance(&goal, infl), + |p| *p == goal, + ) + } + 1 => { + result = astar( + &start, + |p| p.successors(grid), + |p| p.octile_distance(&goal, infl), + |p| *p == goal, + ) + } + _ => { + result = astar( + &start, + |p| p.successors(grid), + |p| p.euclidean_distance(&goal, infl), + |p| *p == goal, + ) + } }; - + let mut path: Vec<(usize, usize)>; let distance: f64; - if result.is_none(){ + if result.is_none() { path = Vec::<(usize, usize)>::new(); distance = 0.0 - } - else { + } else { let unwrapped = result.unwrap(); distance = (unwrapped.1 as f64) / pos::MULTF64; path = Vec::<(usize, usize)>::with_capacity(unwrapped.0.len()); for pos in unwrapped.0 { - path.push((pos.0, pos.1)) + path.push((pos.0, pos.1)) } } @@ -449,7 +584,12 @@ impl PathFind { } /// Find the path using influence values and returns the path and distance - fn find_path_influence_large(&self, start: (usize, usize), end: (usize, usize), possible_heuristic: Option) -> PyResult<(Vec<(usize, usize)>, f64)> { + fn find_path_influence_large( + &self, + start: (usize, usize), + end: (usize, usize), + possible_heuristic: Option, + ) -> PyResult<(Vec<(usize, usize)>, f64)> { let corrected_start = self.get_closest_pathable(start); let corrected_end = self.get_closest_pathable(end); @@ -461,27 +601,47 @@ impl PathFind { let result: Option<(Vec, usize)>; match possible_heuristic.unwrap_or(0) { - 0 => result = astar(&start, |p| p.successors(grid), |p| p.manhattan_distance(&goal, infl), |p| *p == goal), - 1 => result = astar(&start, |p| p.successors(grid), |p| p.octile_distance(&goal, infl), |p| *p == goal), - _ => result = astar(&start, |p| p.successors(grid), |p| p.euclidean_distance(&goal, infl), |p| *p == goal), + 0 => { + result = astar( + &start, + |p| p.successors(grid), + |p| p.manhattan_distance(&goal, infl), + |p| *p == goal, + ) + } + 1 => { + result = astar( + &start, + |p| p.successors(grid), + |p| p.octile_distance(&goal, infl), + |p| *p == goal, + ) + } + _ => { + result = astar( + &start, + |p| p.successors(grid), + |p| p.euclidean_distance(&goal, infl), + |p| *p == goal, + ) + } }; - + let mut path: Vec<(usize, usize)>; let distance: f64; - if result.is_none(){ + if result.is_none() { path = Vec::<(usize, usize)>::new(); distance = 0.0 - } - else { + } else { let unwrapped = result.unwrap(); distance = (unwrapped.1 as f64) / pos::MULTF64; path = Vec::<(usize, usize)>::with_capacity(unwrapped.0.len()); for pos in unwrapped.0 { - path.push((pos.0, pos.1)) + path.push((pos.0, pos.1)) } } - + Ok((path, distance)) } @@ -491,7 +651,8 @@ impl PathFind { let grid: &Vec> = &self.map; let result = dijkstra_all(&start, |p| p.successors(&grid)); - let mut destination_collection: Vec<((usize, usize), f64)> = Vec::<((usize, usize), f64)>::with_capacity(result.len()); + let mut destination_collection: Vec<((usize, usize), f64)> = + Vec::<((usize, usize), f64)>::with_capacity(result.len()); for found_path in result { let x = ((found_path.1).0).0; @@ -504,20 +665,33 @@ impl PathFind { } /// Finds all reachable destinations from selected start point. Ignores influence. - fn find_destinations_in(&self, start: (usize, usize), distance: f64 ) -> PyResult> { + fn find_destinations_in( + &self, + start: (usize, usize), + distance: f64, + ) -> PyResult> { Ok(self.find_destinations_in_inline(start, distance)) } #[inline] - fn find_destinations_in_inline(&self, start: (usize, usize), distance: f64 ) -> Vec<((usize, usize), f64)> { + fn find_destinations_in_inline( + &self, + start: (usize, usize), + distance: f64, + ) -> Vec<((usize, usize), f64)> { let start: pos::Pos = pos::Pos(start.0, start.1); let grid: &Vec> = &self.map; - let u_distance = (distance * pos::MULTF64) as usize; + let u_distance = (distance * pos::MULTF64) as usize; - let result = dijkstra_partial(&start, |p| p.successors(&grid), |p| p.octile_distance(&start) > u_distance); + let result = dijkstra_partial( + &start, + |p| p.successors(&grid), + |p| p.octile_distance(&start) > u_distance, + ); let hash_map = result.0; - let mut destination_collection: Vec<((usize, usize), f64)> = Vec::<((usize, usize), f64)>::with_capacity(hash_map.len()); + let mut destination_collection: Vec<((usize, usize), f64)> = + Vec::<((usize, usize), f64)>::with_capacity(hash_map.len()); for found_path in hash_map { let x = (found_path.0).0; @@ -530,15 +704,24 @@ impl PathFind { } #[inline] - fn find_destinations_in_inline_influence(&self, start: (usize, usize), distance: f64 ) -> Vec<((usize, usize), f64)> { + fn find_destinations_in_inline_influence( + &self, + start: (usize, usize), + distance: f64, + ) -> Vec<((usize, usize), f64)> { let start: pos::InfluencedPos = pos::InfluencedPos(start.0, start.1); let grid: &Vec> = &self.map; - let u_distance = (distance * (self.normal_influence as f64) * pos::MULTF64) as usize; + let u_distance = (distance * (self.normal_influence as f64) * pos::MULTF64) as usize; - let result = dijkstra_partial(&start, |p| p.successors(&grid), |p| p.octile_distance(&start, self.normal_influence) > u_distance); + let result = dijkstra_partial( + &start, + |p| p.successors(&grid), + |p| p.octile_distance(&start, self.normal_influence) > u_distance, + ); let hash_map = result.0; - let mut destination_collection: Vec<((usize, usize), f64)> = Vec::<((usize, usize), f64)>::with_capacity(hash_map.len()); + let mut destination_collection: Vec<((usize, usize), f64)> = + Vec::<((usize, usize), f64)>::with_capacity(hash_map.len()); for found_path in hash_map { let x = (found_path.0).0; @@ -554,15 +737,21 @@ impl PathFind { fn get_closest_pathable(&self, start: (usize, usize)) -> (usize, usize) { if self.auto_correct || self.map[start.0][start.1] > 0 { start - } - else { - let result = self.free_finder.find_free(start, &self.map, self.width, self.height); + } else { + let result = self + .free_finder + .find_free(start, &self.map, self.width, self.height); result } } /// Finds a compromise where low influence matches with close position to the start position. - fn find_low_inside_walk(&self, start: (f64, f64), target:(f64, f64), distance: f64)-> PyResult<((f64, f64), f64)> { + fn find_low_inside_walk( + &self, + start: (f64, f64), + target: (f64, f64), + distance: f64, + ) -> PyResult<((f64, f64), f64)> { let start_int = (start.0 as usize, start.1 as usize); let target_int = (target.0 as usize, target.1 as usize); @@ -572,7 +761,12 @@ impl PathFind { let corrected_target = self.get_closest_pathable(target_int); let angle = angles::angle_between_f64(start, target); let u_distance = distance as usize; - let rect = rectangle::Rectangle::init_from_center2(corrected_target, (u_distance, u_distance), self.width, self.height); + let rect = rectangle::Rectangle::init_from_center2( + corrected_target, + (u_distance, u_distance), + self.width, + self.height, + ); let mut destinations = Vec::<((usize, usize), usize)>::new(); @@ -588,16 +782,15 @@ impl PathFind { if destinations.len() == 0 { // Cannot find path to target Ok(((0.0, 0.0), -1.0)) - } - else { - let mut best_target: ((f64, f64), f64) = ((0.0,0.0), -1.0); + } else { + let mut best_target: ((f64, f64), f64) = ((0.0, 0.0), -1.0); // Get a backup position that's closest to start up position for destination in destinations { let point = destination.0; let distance_from_start = octile_distance_f64(start_int, point); - if distance_from_start < best_target.1 || best_target.1 < 0.0{ + if distance_from_start < best_target.1 || best_target.1 < 0.0 { let point_f64 = (point.0 as f64 + 0.5, point.1 as f64 + 0.5); best_target = (point_f64, distance_from_start); } @@ -606,10 +799,13 @@ impl PathFind { //let best_path = self.find_path_influence_inline(corrected_start, best_target.0, Some(1u8)); if current_distance < distance + 4.0 { - let best_influence = self.map[(best_target.0).0 as usize][(best_target.0).1 as usize]; + let best_influence = + self.map[(best_target.0).0 as usize][(best_target.0).1 as usize]; //let mut best_distance_from_target = octile_distance_f64(best_target.0, target_int); - let destinations_from_start = self.find_destinations_in_inline(corrected_start, 5.0); - let mut angle_distance = angles::angle_distance(angle, angles::angle_between_f64(best_target.0, target)); + let destinations_from_start = + self.find_destinations_in_inline(corrected_start, 5.0); + let mut angle_distance = + angles::angle_distance(angle, angles::angle_between_f64(best_target.0, target)); let mut best_score = best_influence as f64 * (1.0 + angle_distance * 0.25); for destination in destinations_from_start { @@ -617,7 +813,8 @@ impl PathFind { let point_f64 = (point.0 as f64 + 0.5, point.1 as f64 + 0.5); let influence = self.map[point.0][point.1]; //let distance_from_target = euclidean_distance(point_f64, target); - angle_distance = angles::angle_distance(angle, angles::angle_between_f64(point_f64, target)); + angle_distance = + angles::angle_distance(angle, angles::angle_between_f64(point_f64, target)); let score = influence as f64 * (1.0 + angle_distance * 0.25); if score < best_score { @@ -632,10 +829,9 @@ impl PathFind { } } - /// This module is a python module implemented in Rust. #[pymodule] fn sc2pathlib(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; Ok(()) -} \ No newline at end of file +} diff --git a/src/pos.rs b/src/pos.rs index 357dcfe..5a9ba1d 100644 --- a/src/pos.rs +++ b/src/pos.rs @@ -26,14 +26,13 @@ impl Pos { } #[inline] - pub fn octile_distance(&self, other: &Pos) -> usize{ + pub fn octile_distance(&self, other: &Pos) -> usize { let dx = absdiff(self.0, other.0); let dy = absdiff(self.1, other.1); - if dx > dy{ + if dx > dy { MULT * dx + DIAGONAL_MINUS_CARDINAL * dy - } - else{ + } else { MULT * dy + DIAGONAL_MINUS_CARDINAL * dx } } @@ -49,7 +48,7 @@ impl Pos { let mut val_up: bool = false; if x > 0 { - val_left = grid[x - 1][y]> 0; + val_left = grid[x - 1][y] > 0; } if y > 0 { @@ -134,14 +133,13 @@ impl InfluencedPos { } #[inline] - pub fn octile_distance(&self, other: &InfluencedPos, normal_influence: usize) -> usize{ + pub fn octile_distance(&self, other: &InfluencedPos, normal_influence: usize) -> usize { let dx = absdiff(self.0, other.0); let dy = absdiff(self.1, other.1); - if dx > dy{ + if dx > dy { (MULT * dx + DIAGONAL_MINUS_CARDINAL * dy) * normal_influence - } - else{ + } else { (MULT * dy + DIAGONAL_MINUS_CARDINAL * dx) * normal_influence } } @@ -157,7 +155,7 @@ impl InfluencedPos { let mut val_up: usize = 0; if x > 0 { - val_left = grid[x - 1][y]; + val_left = grid[x - 1][y]; } if y > 0 { @@ -222,4 +220,4 @@ impl InfluencedPos { arr } -} \ No newline at end of file +} diff --git a/src/pos_large.rs b/src/pos_large.rs index e63904e..9aec032 100644 --- a/src/pos_large.rs +++ b/src/pos_large.rs @@ -29,14 +29,13 @@ impl PosLarge { } #[inline] - pub fn octile_distance(&self, other: &PosLarge) -> usize{ + pub fn octile_distance(&self, other: &PosLarge) -> usize { let dx = absdiff(self.0, other.0); let dy = absdiff(self.1, other.1); - if dx > dy{ + if dx > dy { MULT * dx + DIAGONAL_MINUS_CARDINAL * dy - } - else{ + } else { MULT * dy + DIAGONAL_MINUS_CARDINAL * dx } } @@ -80,7 +79,7 @@ impl PosLarge { } if val_up { - val_left_up = grid[x - 1][y - 1] > 0; + val_left_up = grid[x - 1][y - 1] > 0; } } @@ -150,14 +149,13 @@ impl InfluencedPosLarge { } #[inline] - pub fn octile_distance(&self, other: &InfluencedPosLarge, normal_influence: usize) -> usize{ + pub fn octile_distance(&self, other: &InfluencedPosLarge, normal_influence: usize) -> usize { let dx = absdiff(self.0, other.0); let dy = absdiff(self.1, other.1); - if dx > dy{ + if dx > dy { (MULT * dx + DIAGONAL_MINUS_CARDINAL * dy) * normal_influence - } - else{ + } else { (MULT * dy + DIAGONAL_MINUS_CARDINAL * dx) * normal_influence } } @@ -166,7 +164,6 @@ impl InfluencedPosLarge { let &InfluencedPosLarge(x, y) = self; let mut arr = Vec::<(InfluencedPosLarge, usize)>::with_capacity(8); - let mut val_left: usize = 0; let mut val_down: usize = 0; let mut val_right: usize = 0; @@ -199,12 +196,12 @@ impl InfluencedPosLarge { } if val_up > 0 { - val_left_up = grid[x - 1][y - 1]; + val_left_up = grid[x - 1][y - 1]; } } if val_right > 0 { - if val_down > 0 { + if val_down > 0 { val_right_down = grid[x + 1][y - 1]; } @@ -217,7 +214,7 @@ impl InfluencedPosLarge { arr.push((InfluencedPosLarge(x, y + 1), val_up * MULT)); } - if val_down > 0 && (val_left_down > 0|| val_right_down > 0) { + if val_down > 0 && (val_left_down > 0 || val_right_down > 0) { arr.push((InfluencedPosLarge(x, y - 1), val_down * MULT)); } @@ -225,7 +222,7 @@ impl InfluencedPosLarge { arr.push((InfluencedPosLarge(x - 1, y), val_left * MULT)); } - if val_right > 0 && (val_right_up > 0 || val_right_down > 0) { + if val_right > 0 && (val_right_up > 0 || val_right_down > 0) { arr.push((InfluencedPosLarge(x + 1, y), val_right * MULT)); } @@ -247,4 +244,4 @@ impl InfluencedPosLarge { arr } -} \ No newline at end of file +} diff --git a/src/rectangle.rs b/src/rectangle.rs index de6f805..f1a38f0 100644 --- a/src/rectangle.rs +++ b/src/rectangle.rs @@ -1,19 +1,24 @@ -use std::cmp::{min, max}; +use std::cmp::{max, min}; #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Rectangle { pub x: usize, pub y: usize, pub x_end: usize, - pub y_end: usize + pub y_end: usize, } impl Rectangle { fn new(x: usize, y: usize, x_end: usize, y_end: usize) -> Rectangle { - Rectangle { x, y, x_end, y_end} + Rectangle { x, y, x_end, y_end } } - pub fn init_from_center(center: (f32,f32), size: (usize, usize), width: usize, height: usize) -> Rectangle { + pub fn init_from_center( + center: (f32, f32), + size: (usize, usize), + width: usize, + height: usize, + ) -> Rectangle { let pos_x: usize = center.0 as usize; let pos_y: usize = center.1 as usize; @@ -21,13 +26,18 @@ impl Rectangle { let h: usize = size.1; let x: usize = max(0, (pos_x as f32 - (w as f32 / 2 as f32)).ceil() as usize); let y: usize = max(0, (pos_y as f32 - (h as f32 / 2 as f32)).ceil() as usize); - let x_end : usize = min(width, w + x); - let y_end : usize = min(height, h + y); + let x_end: usize = min(width, w + x); + let y_end: usize = min(height, h + y); - Rectangle { x, y, x_end, y_end} + Rectangle { x, y, x_end, y_end } } - pub fn init_from_center2(center: (usize,usize), size: (usize, usize), width: usize, height: usize) -> Rectangle { + pub fn init_from_center2( + center: (usize, usize), + size: (usize, usize), + width: usize, + height: usize, + ) -> Rectangle { let pos_x: usize = center.0; let pos_y: usize = center.1; @@ -35,9 +45,9 @@ impl Rectangle { let h: usize = size.1; let x: usize = f32::max(0.0, (pos_x as f32 - (w as f32 / 2 as f32)).ceil()) as usize; let y: usize = f32::max(0.0, (pos_y as f32 - (h as f32 / 2 as f32)).ceil()) as usize; - let x_end : usize = min(width, w + x); - let y_end : usize = min(height, h + y); + let x_end: usize = min(width, w + x); + let y_end: usize = min(height, h + y); - Rectangle { x, y, x_end, y_end} + Rectangle { x, y, x_end, y_end } } -} \ No newline at end of file +} diff --git a/src/search_grid.rs b/src/search_grid.rs index 7715e59..74a0169 100644 --- a/src/search_grid.rs +++ b/src/search_grid.rs @@ -1,16 +1,21 @@ - #[derive(Clone, Debug)] -pub struct FreeFinder{ - closest_grid: Vec::<(i64, i64)> +pub struct FreeFinder { + closest_grid: Vec<(i64, i64)>, } impl FreeFinder { pub fn new() -> FreeFinder { - let closest_grid = create_search_grid(); + let closest_grid = create_search_grid(); FreeFinder { closest_grid } } - pub fn find_free(&self, lookup: (usize, usize), map: &Vec>, width: usize, height: usize) -> (usize, usize) { + pub fn find_free( + &self, + lookup: (usize, usize), + map: &Vec>, + width: usize, + height: usize, + ) -> (usize, usize) { let mut result = (lookup.0, lookup.1); for offset in &self.closest_grid { @@ -19,7 +24,7 @@ impl FreeFinder { if adjusted.0 >= 0 && adjusted.1 >= 0 { let adjusted_usize = (adjusted.0 as usize, adjusted.1 as usize); - if adjusted_usize.0 < width &&adjusted_usize.1 < height { + if adjusted_usize.0 < width && adjusted_usize.1 < height { if map[adjusted_usize.0][adjusted_usize.1] > 0 { result = adjusted_usize; break; @@ -42,8 +47,8 @@ impl FreeFinder { /// _854345__ /// __87678__ /// ___A9A___ -fn create_search_grid() -> Vec::<(i64, i64)> { - let mut search_grid: Vec::<(i64, i64)> = Vec::<(i64, i64)>::new(); +fn create_search_grid() -> Vec<(i64, i64)> { + let mut search_grid: Vec<(i64, i64)> = Vec::<(i64, i64)>::new(); // 0, Not checked //search_grid.push((0, 0)); diff --git a/test.py b/test.py index f346bee..bdf1e8b 100644 --- a/test.py +++ b/test.py @@ -2,10 +2,11 @@ import time from typing import List + def read_maze(file_name: str) -> List[List[int]]: - with open(file_name, 'r') as text: + with open(file_name, "r") as text: m = text.read() - lines = m.split('\n') + lines = m.split("\n") final_maze = [] for y in range(0, len(lines[0])): maze_line = [] @@ -14,51 +15,62 @@ def read_maze(file_name: str) -> List[List[int]]: maze_line.append(int(lines[x][y])) return final_maze + maze = read_maze("tests/maze4x4.txt") pf = sc2pathlibp.PathFinder(maze) print(pf.map) print(pf.width) print(pf.height) -print(pf.find_path((0,0), (0,2))) +print(pf.find_path((0, 0), (0, 2))) pf.normalize_influence(100) -print(pf.lowest_influence_in_grid((2,2), 5)) -print(pf.find_path((0,0), (0,2))) +print(pf.lowest_influence_in_grid((2, 2), 5)) +print(pf.find_path((0, 0), (0, 2))) maze = read_maze("tests/AutomatonLE.txt") pf = sc2pathlibp.PathFinder(maze) pf.normalize_influence(10) pf.heuristic_accuracy = 0 -result = pf.find_path((32, 51),(150, 129)) +result = pf.find_path((32, 51), (150, 129)) print(f"path distance 0: {result[1]} for path: {result[0]}") pf.heuristic_accuracy = 1 -result = pf.find_path((32, 51),(150, 129)) +result = pf.find_path((32, 51), (150, 129)) print(f"path distance 1: {result[1]} for path: {result[0]}") pf.heuristic_accuracy = 2 -result = pf.find_path((32, 51),(150, 129)) +result = pf.find_path((32, 51), (150, 129)) print(f"path distance 2: {result[1]} for path: {result[0]}") pf.heuristic_accuracy = 0 -result = pf.find_path_influence((32, 51),(150, 129)) +result = pf.find_path_influence((32, 51), (150, 129)) print(f"path influenced distance 0: {result[1]} for path: {result[0]}") pf.heuristic_accuracy = 1 -result = pf.find_path_influence((32, 51),(150, 129)) +result = pf.find_path_influence((32, 51), (150, 129)) print(f"path influenced distance 1: {result[1]} for path: {result[0]}") pf.heuristic_accuracy = 2 -result = pf.find_path_influence((32, 51),(150, 129)) +result = pf.find_path_influence((32, 51), (150, 129)) print(f"path influenced distance 2: {result[1]} for path: {result[0]}") -expansions = [(29, 65), (35, 34), -(63, 26), (56, 65), -(98, 26), (80, 66), -(33, 105), (129, 28), -(54, 151), (150, 74), -(103, 113), (85, 153), -(127, 114), (120, 153), -(148, 145), (154, 114)] +expansions = [ + (29, 65), + (35, 34), + (63, 26), + (56, 65), + (98, 26), + (80, 66), + (33, 105), + (129, 28), + (54, 151), + (150, 74), + (103, 113), + (85, 153), + (127, 114), + (120, 153), + (148, 145), + (154, 114), +] total_distance = 0 ns_pf = time.perf_counter_ns() @@ -88,10 +100,10 @@ def read_maze(file_name: str) -> List[List[int]]: print(f"adding influence by walking distance took {ns_pf / 1000 / 1000} ms.") result = pf.find_path_influence((29, 65), (154, 114)) -#print(pf.map) +# print(pf.map) # pf.reset() # pf.normalize_influence(100) pf.plot(result[0]) -pf.create_block([(11.5,11.5), (21.5,21.5), (31.5,31.5), (31.5,31.5)], (2, 1)) +pf.create_block([(11.5, 11.5), (21.5, 21.5), (31.5, 31.5), (31.5, 31.5)], (2, 1)) pf.plot(result[0]) -input("Press Enter to continue...") \ No newline at end of file +input("Press Enter to continue...")