Dijkstra algorithm with binary grid (#8802)

* Create TestShiva

* Delete TestShiva

* Implementation of the Dijkstra-Algorithm in a binary grid

* Update double_ended_queue.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update least_common_multiple.py

* Update sol1.py

* Update pyproject.toml

* Update pyproject.toml

* https://github.com/astral-sh/ruff-pre-commit v0.0.274

---------

Co-authored-by: ShivaDahal99 <130563462+ShivaDahal99@users.noreply.github.com>
Co-authored-by: jlhuhn <134317018+jlhuhn@users.noreply.github.com>
Co-authored-by: Christian Clauss <cclauss@me.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Linus M. Henkel 2023-06-22 13:49:09 +02:00 committed by GitHub
parent 07e6812888
commit 5b0890bd83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 106 additions and 16 deletions

View File

@ -15,8 +15,8 @@ repos:
hooks: hooks:
- id: auto-walrus - id: auto-walrus
- repo: https://github.com/charliermarsh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.272 rev: v0.0.274
hooks: hooks:
- id: ruff - id: ruff

View File

@ -32,7 +32,7 @@ class Deque:
the number of nodes the number of nodes
""" """
__slots__ = ["_front", "_back", "_len"] __slots__ = ("_front", "_back", "_len")
@dataclass @dataclass
class _Node: class _Node:
@ -54,7 +54,7 @@ class Deque:
the current node of the iteration. the current node of the iteration.
""" """
__slots__ = ["_cur"] __slots__ = "_cur"
def __init__(self, cur: Deque._Node | None) -> None: def __init__(self, cur: Deque._Node | None) -> None:
self._cur = cur self._cur = cur

View File

@ -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()

View File

@ -67,7 +67,7 @@ def benchmark():
class TestLeastCommonMultiple(unittest.TestCase): class TestLeastCommonMultiple(unittest.TestCase):
test_inputs = [ test_inputs = (
(10, 20), (10, 20),
(13, 15), (13, 15),
(4, 31), (4, 31),
@ -77,8 +77,8 @@ class TestLeastCommonMultiple(unittest.TestCase):
(12, 25), (12, 25),
(10, 25), (10, 25),
(6, 9), (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): def test_lcm_function(self):
for i, (first_num, second_num) in enumerate(self.test_inputs): for i, (first_num, second_num) in enumerate(self.test_inputs):

View File

@ -47,18 +47,18 @@ import os
class PokerHand: class PokerHand:
"""Create an object representing a Poker Hand based on an input of a """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. and board cards.
Attributes: (read-only) Attributes: (read-only)
hand: string representing the hand consisting of five cards hand: a string representing the hand consisting of five cards
Methods: Methods:
compare_with(opponent): takes in player's hand (self) and compare_with(opponent): takes in player's hand (self) and
opponent's hand (opponent) and compares both hands according to opponent's hand (opponent) and compares both hands according to
the rules of Texas Hold'em. the rules of Texas Hold'em.
Returns one of 3 strings (Win, Loss, Tie) based on whether 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 hand_name(): Returns a string made up of two parts: hand name
and high card. and high card.
@ -66,11 +66,11 @@ class PokerHand:
Supported operators: Supported operators:
Rich comparison operators: <, >, <=, >=, ==, != Rich comparison operators: <, >, <=, >=, ==, !=
Supported builtin methods and functions: Supported built-in methods and functions:
list.sort(), sorted() list.sort(), sorted()
""" """
_HAND_NAME = [ _HAND_NAME = (
"High card", "High card",
"One pair", "One pair",
"Two pairs", "Two pairs",
@ -81,10 +81,10 @@ class PokerHand:
"Four of a kind", "Four of a kind",
"Straight flush", "Straight flush",
"Royal flush", "Royal flush",
] )
_CARD_NAME = [ _CARD_NAME = (
"", # placeholder as lists are zero indexed "", # placeholder as tuples are zero-indexed
"One", "One",
"Two", "Two",
"Three", "Three",
@ -99,7 +99,7 @@ class PokerHand:
"Queen", "Queen",
"King", "King",
"Ace", "Ace",
] )
def __init__(self, hand: str) -> None: def __init__(self, hand: str) -> None:
""" """

View File

@ -103,6 +103,7 @@ max-complexity = 17 # default: 10
"machine_learning/linear_discriminant_analysis.py" = ["ARG005"] "machine_learning/linear_discriminant_analysis.py" = ["ARG005"]
"machine_learning/sequential_minimum_optimization.py" = ["SIM115"] "machine_learning/sequential_minimum_optimization.py" = ["SIM115"]
"matrix/sherman_morrison.py" = ["SIM103", "SIM114"] "matrix/sherman_morrison.py" = ["SIM103", "SIM114"]
"other/l*u_cache.py" = ["RUF012"]
"physics/newtons_second_law_of_motion.py" = ["BLE001"] "physics/newtons_second_law_of_motion.py" = ["BLE001"]
"project_euler/problem_099/sol1.py" = ["SIM115"] "project_euler/problem_099/sol1.py" = ["SIM115"]
"sorts/external_sort.py" = ["SIM115"] "sorts/external_sort.py" = ["SIM115"]