Skip to content

Dijkstra algorithm with binary grid #8802

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

Merged
merged 12 commits into from
Jun 22, 2023
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ repos:
hooks:
- id: auto-walrus

- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.272
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.274
hooks:
- id: ruff

Expand Down
4 changes: 2 additions & 2 deletions data_structures/queue/double_ended_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Deque:
the number of nodes
"""

__slots__ = ["_front", "_back", "_len"]
__slots__ = ("_front", "_back", "_len")

@dataclass
class _Node:
Expand All @@ -54,7 +54,7 @@ class _Iterator:
the current node of the iteration.
"""

__slots__ = ["_cur"]
__slots__ = "_cur"

def __init__(self, cur: Deque._Node | None) -> None:
self._cur = cur
Expand Down
89 changes: 89 additions & 0 deletions graphs/dijkstra_binary_grid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"""
This script implements the Dijkstra algorithm on a binary grid.
The grid consists of 0s and 1s, where 1 represents
a walkable node and 0 represents an obstacle.
The algorithm finds the shortest path from a start node to a destination node.
Diagonal movement can be allowed or disallowed.
"""

from heapq import heappop, heappush

import numpy as np


def dijkstra(
grid: np.ndarray,
source: tuple[int, int],
destination: tuple[int, int],
allow_diagonal: bool,
) -> tuple[float | int, list[tuple[int, int]]]:
"""
Implements Dijkstra's algorithm on a binary grid.

Args:
grid (np.ndarray): A 2D numpy array representing the grid.
1 represents a walkable node and 0 represents an obstacle.
source (Tuple[int, int]): A tuple representing the start node.
destination (Tuple[int, int]): A tuple representing the
destination node.
allow_diagonal (bool): A boolean determining whether
diagonal movements are allowed.

Returns:
Tuple[Union[float, int], List[Tuple[int, int]]]:
The shortest distance from the start node to the destination node
and the shortest path as a list of nodes.

>>> dijkstra(np.array([[1, 1, 1], [0, 1, 0], [0, 1, 1]]), (0, 0), (2, 2), False)
(4.0, [(0, 0), (0, 1), (1, 1), (2, 1), (2, 2)])

>>> dijkstra(np.array([[1, 1, 1], [0, 1, 0], [0, 1, 1]]), (0, 0), (2, 2), True)
(2.0, [(0, 0), (1, 1), (2, 2)])

>>> dijkstra(np.array([[1, 1, 1], [0, 0, 1], [0, 1, 1]]), (0, 0), (2, 2), False)
(4.0, [(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)])
"""
rows, cols = grid.shape
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
if allow_diagonal:
dx += [-1, -1, 1, 1]
dy += [-1, 1, -1, 1]

queue, visited = [(0, source)], set()
matrix = np.full((rows, cols), np.inf)
matrix[source] = 0
predecessors = np.empty((rows, cols), dtype=object)
predecessors[source] = None

while queue:
(dist, (x, y)) = heappop(queue)
if (x, y) in visited:
continue
visited.add((x, y))

if (x, y) == destination:
path = []
while (x, y) != source:
path.append((x, y))
x, y = predecessors[x, y]
path.append(source) # add the source manually
path.reverse()
return matrix[destination], path

for i in range(len(dx)):
nx, ny = x + dx[i], y + dy[i]
if 0 <= nx < rows and 0 <= ny < cols:
next_node = grid[nx][ny]
if next_node == 1 and matrix[nx, ny] > dist + 1:
heappush(queue, (dist + 1, (nx, ny)))
matrix[nx, ny] = dist + 1
predecessors[nx, ny] = (x, y)

return np.inf, []


if __name__ == "__main__":
import doctest

doctest.testmod()
6 changes: 3 additions & 3 deletions maths/least_common_multiple.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def benchmark():


class TestLeastCommonMultiple(unittest.TestCase):
test_inputs = [
test_inputs = (
(10, 20),
(13, 15),
(4, 31),
Expand All @@ -77,8 +77,8 @@ class TestLeastCommonMultiple(unittest.TestCase):
(12, 25),
(10, 25),
(6, 9),
]
expected_results = [20, 195, 124, 210, 1462, 60, 300, 50, 18]
)
expected_results = (20, 195, 124, 210, 1462, 60, 300, 50, 18)

def test_lcm_function(self):
for i, (first_num, second_num) in enumerate(self.test_inputs):
Expand Down
18 changes: 9 additions & 9 deletions project_euler/problem_054/sol1.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,30 +47,30 @@

class PokerHand:
"""Create an object representing a Poker Hand based on an input of a
string which represents the best 5 card combination from the player's hand
string which represents the best 5-card combination from the player's hand
and board cards.

Attributes: (read-only)
hand: string representing the hand consisting of five cards
hand: a string representing the hand consisting of five cards

Methods:
compare_with(opponent): takes in player's hand (self) and
opponent's hand (opponent) and compares both hands according to
the rules of Texas Hold'em.
Returns one of 3 strings (Win, Loss, Tie) based on whether
player's hand is better than opponent's hand.
player's hand is better than the opponent's hand.

hand_name(): Returns a string made up of two parts: hand name
and high card.

Supported operators:
Rich comparison operators: <, >, <=, >=, ==, !=

Supported builtin methods and functions:
Supported built-in methods and functions:
list.sort(), sorted()
"""

_HAND_NAME = [
_HAND_NAME = (
"High card",
"One pair",
"Two pairs",
Expand All @@ -81,10 +81,10 @@ class PokerHand:
"Four of a kind",
"Straight flush",
"Royal flush",
]
)

_CARD_NAME = [
"", # placeholder as lists are zero indexed
_CARD_NAME = (
"", # placeholder as tuples are zero-indexed
"One",
"Two",
"Three",
Expand All @@ -99,7 +99,7 @@ class PokerHand:
"Queen",
"King",
"Ace",
]
)

def __init__(self, hand: str) -> None:
"""
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ max-complexity = 17 # default: 10
"machine_learning/linear_discriminant_analysis.py" = ["ARG005"]
"machine_learning/sequential_minimum_optimization.py" = ["SIM115"]
"matrix/sherman_morrison.py" = ["SIM103", "SIM114"]
"other/l*u_cache.py" = ["RUF012"]
"physics/newtons_second_law_of_motion.py" = ["BLE001"]
"project_euler/problem_099/sol1.py" = ["SIM115"]
"sorts/external_sort.py" = ["SIM115"]
Expand Down