python -m venv .venv
venv_bash: .venv
- bash --init-file <(echo ". ~/.bashrc; source .venv/bin/activate")
+ bash --init-file <(echo ". ~/.bashrc; export TERM=xterm-256color; . .venv/bin/activate")
run:
from amazeing.config.config_parser import Config
+from amazeing.utils import CellCoord
from amazeing.maze import (
- MazeNetworkTracker,
- CellCoord,
- MazeDirtyTracker,
- MazePacmanTracker,
- maze_make_pacman,
- maze_make_perfect,
+ NetworkTracker,
+ DirtyTracker,
+ PacmanTracker,
+ make_pacman,
+ make_perfect,
)
from amazeing.display import TileCycle, TileMaps, extract_pairs
from amazeing.utils import IVec2
+from amazeing.utils.coords import Cardinal
config = Config.parse(open("./example.conf").read())
maze = Maze(dims)
-dirty_tracker = MazeDirtyTracker(maze)
-pacman_tracker = MazePacmanTracker(maze)
-network_tracker = MazeNetworkTracker(maze)
+dirty_tracker = DirtyTracker(maze)
+pacman_tracker = PacmanTracker(maze)
+network_tracker = NetworkTracker(maze)
backend = TTYBackend(dims, config.tilemap_wall_size, config.tilemap_cell_size)
pair_map = extract_pairs(config)
walls_const = set(maze.walls_full())
-maze_make_perfect(maze, network_tracker, callback=display_maze)
-maze_make_pacman(maze, walls_const, pacman_tracker, callback=display_maze)
+make_perfect(maze, network_tracker, callback=display_maze)
+make_pacman(maze, walls_const, pacman_tracker, callback=display_maze)
# pathfind()
while False:
- maze_make_perfect(maze, network_tracker, callback=display_maze)
+ make_perfect(maze, network_tracker, callback=display_maze)
# poll_events(200)
- maze_make_pacman(maze, walls_const, callback=display_maze)
+ make_pacman(maze, walls_const, callback=display_maze)
# maze_make_empty(maze, walls_const, callback=display_maze)
# poll_events(200)
# maze._rebuild()
-while True:
+while False:
poll_events(16)
+
+backend.uninit()
+print(network_tracker.contour_bound((CellCoord(0, 1), Cardinal.EAST)))
def dst_coord_rev(self, pixel: IVec2) -> IVec2:
mod = self.__wall_dim + self.__cell_dim
- return (pixel // mod) * IVec2.splat(2) + IVec2[int].with_op(
- lambda a, b: 0 if a < b else 1
- )(pixel % mod, self.__wall_dim)
+ return (pixel // mod) * IVec2.splat(2) + (pixel % mod).with_op(
+ lambda a, b: 0 if a < b else 1, self.__wall_dim
+ )
def tile_size(self, pos: IVec2) -> IVec2:
return (pos + IVec2.splat(1)) % IVec2.splat(
return IVec2(x, y)
def clamp(self, dims: IVec2) -> None:
- self.__pos = IVec2.with_op(min)(
- IVec2.with_op(max)(self.__pos, dims - self.dims()), IVec2.splat(0)
+ self.__pos = self.__pos.lane_max(dims - self.dims()).lane_min(
+ IVec2.splat(0)
)
def present(self, at: IVec2, into: IVec2, window: curses.window) -> None:
if self.constrained:
self.clamp(into)
- pad_start = IVec2.with_op(max)(
- IVec2.splat(0) - self.__pos, IVec2.splat(0)
- )
- win_start = IVec2.with_op(max)(self.__pos, IVec2.splat(0))
- draw_dim = IVec2.with_op(min)(
- self.dims() - pad_start, into - win_start
- )
+ pad_start = (IVec2.splat(0) - self.__pos).lane_max(IVec2.splat(0))
+ win_start = self.__pos.lane_max(IVec2.splat(0))
+ draw_dim = (self.dims() - pad_start).lane_min(into - win_start)
if draw_dim.x <= 0 or draw_dim.y <= 0:
return
draw_start = at + win_start
self.__filler_boxes: list[DBox] = []
- def filler_box() -> Box:
+ def filler_box(
+ dims: IVec2[BInt] = IVec2(BInt(0, True), BInt(0, True))
+ ) -> Box:
self.__filler_boxes.append(
res := DBox(
FBox(
- IVec2(BInt(0, True), BInt(0, True)),
+ dims,
lambda at, into: (
None
if self.__filler is None
self.__style_bimap: BiMap[int, IVec2] = BiMap()
self.__bg_init: Callable[[IVec2], int] | None = None
+ self.__uninit: bool = False
+
def __del__(self) -> None:
+ self.uninit()
+
+ def uninit(self) -> None:
+ if self.__uninit:
+ return
+ self.__uninit = True
curses.curs_set(1)
curses.nocbreak()
self.__screen.keypad(False)
__author__ = "agilliar & luflores"
from .maze import Maze
-from .maze_pattern import Pattern
-from .maze_coords import Cardinal, Orientation, WallCoord, CellCoord
-from .maze_dirty_tracker import MazeDirtyTracker
-from .maze_pacman_tracker import MazePacmanTracker
-from .maze_network_tracker import MazeNetworkTracker
-from .maze_make_empty import maze_make_empty
-from .maze_make_pacman import maze_make_pacman
-from .maze_make_perfect import maze_make_perfect
+from .pattern import Pattern
+from .dirty_tracker import DirtyTracker
+from .pacman_tracker import PacmanTracker
+from .network_tracker import NetworkTracker
+from .make_empty import make_empty
+from .make_pacman import make_pacman
+from .make_perfect import make_perfect
__all__ = [
"Maze",
"Pattern",
- "Cardinal",
- "Orientation",
- "WallCoord",
- "CellCoord",
- "MazeDirtyTracker",
- "MazePacmanTracker",
- "MazeNetworkTracker",
- "maze_make_empty",
- "maze_make_pacman",
- "maze_make_perfect",
+ "DirtyTracker",
+ "PacmanTracker",
+ "NetworkTracker",
+ "make_empty",
+ "make_pacman",
+ "make_perfect",
]
from collections.abc import Iterable
from amazeing.maze import Maze
-from amazeing.maze import WallCoord
+from amazeing.utils import WallCoord
-class MazeDirtyTracker:
+class DirtyTracker:
def __init__(self, maze: Maze) -> None:
self.__maze: Maze = maze
self.__dirty: set[WallCoord] = set()
from collections.abc import Callable
from amazeing.maze import Maze
-from amazeing.maze import WallCoord
+from amazeing.utils import WallCoord
import random
-def maze_make_empty(
+def make_empty(
maze: Maze,
walls_const: set[WallCoord],
callback: Callable[[Maze], None] = lambda _: None,
from typing import Callable
-from amazeing.maze import Maze, WallCoord
+from amazeing.maze import Maze
+from amazeing.utils import WallCoord
import random
-from amazeing.maze import MazePacmanTracker
+from amazeing.maze import PacmanTracker
-def maze_make_pacman(
+def make_pacman(
maze: Maze,
walls_const: set[WallCoord],
- pacman_tracker: MazePacmanTracker,
+ pacman_tracker: PacmanTracker,
callback: Callable[[Maze], None] = lambda _: None,
iterations: int = 10,
) -> None:
from amazeing.maze import Maze
import random
-from amazeing.maze import MazeNetworkTracker
+from amazeing.maze import NetworkTracker
-def maze_make_perfect(
+def make_perfect(
maze: Maze,
- tracker: MazeNetworkTracker,
+ tracker: NetworkTracker,
callback: Callable[[Maze], None] = lambda _: None,
) -> None:
empty = list(maze.walls_empty())
from typing import Callable, Generator, Iterable
-from amazeing.utils import IVec2
-from .maze_coords import (
+from amazeing.utils import (
CellCoord,
Orientation,
WallCoord,
+ IVec2,
)
type MazeObserver = Callable[[WallCoord], None]
from amazeing.maze import Maze
-from amazeing.maze.maze_coords import (
- SplitWall,
- WallCoord,
+from amazeing.utils.coords import (
split_wall_ccw,
split_wall_opposite,
)
-from amazeing.utils import AVLTree, AVLLeaf
+from amazeing.utils import AVLTree, AVLLeaf, SplitWall, WallCoord
+from amazeing.utils.avl import BVHKey
+from amazeing.utils.quadtree import Rect
class NetworkID:
self,
) -> None:
# Trees are left hand chiral
- self.__trees: set[AVLTree[SplitWall]] = set()
- self.__revmap: dict[SplitWall, AVLLeaf[SplitWall]] = {}
+ self.__trees: set[AVLTree[BVHKey, SplitWall]] = set()
+ self.__revmap: dict[SplitWall, AVLLeaf[BVHKey, SplitWall]] = {}
def __repr__(self) -> str:
return (
if self.get_wall(wall):
return
a_wall, b_wall = wall.to_split_wall()
- a_tree = AVLTree[SplitWall]()
- b_tree = AVLTree[SplitWall]()
- self.__revmap[a_wall] = a_tree.append(a_wall)
- self.__revmap[b_wall] = b_tree.append(b_wall)
+ a_tree = AVLTree[BVHKey, SplitWall]()
+ b_tree = AVLTree[BVHKey, SplitWall]()
+ self.__revmap[a_wall] = a_tree.append(BVHKey.for_wall(a_wall), a_wall)
+ self.__revmap[b_wall] = b_tree.append(BVHKey.for_wall(b_wall), b_wall)
match (self.find_split(a_wall), self.find_split(b_wall)):
case (None, None):
lhs, rhs = b_leaf.split_up()
lhs.rjoin(a_tree)
lhs.rjoin(b_tree)
- self.__revmap[b_split] = lhs.append(b_split)
+ self.__revmap[b_split] = lhs.append(
+ BVHKey.for_wall(b_split), b_split
+ )
lhs.rjoin(rhs)
self.__trees.add(lhs)
case (a_split, None):
lhs, rhs = a_leaf.split_up()
lhs.rjoin(b_tree)
lhs.rjoin(a_tree)
- self.__revmap[a_split] = lhs.append(a_split)
+ self.__revmap[a_split] = lhs.append(
+ BVHKey.for_wall(a_split), a_split
+ )
lhs.rjoin(rhs)
self.__trees.add(lhs)
case (a_split, b_split):
self.__trees.remove(a_leaf.root())
lhs, rhs = a_leaf.split_up()
lhs.rjoin(b_tree)
- self.__revmap[a_split] = rhs.prepend(a_split)
+ self.__revmap[a_split] = rhs.prepend(
+ BVHKey.for_wall(a_split), a_split
+ )
rhs.ljoin(a_tree)
rhs.rjoin(lhs)
lhs, rhs = b_leaf.split_up()
- self.__revmap[b_split] = rhs.prepend(b_split)
+ self.__revmap[b_split] = rhs.prepend(
+ BVHKey.for_wall(b_split), b_split
+ )
self.__trees.add(lhs)
self.__trees.add(rhs)
else:
self.__trees.remove(b_leaf.root())
a_lhs, a_rhs = a_leaf.split_up()
b_lhs, b_rhs = b_leaf.split_up()
- self.__revmap[a_split] = a_rhs.prepend(a_split)
- self.__revmap[b_split] = b_rhs.prepend(b_split)
+ self.__revmap[a_split] = a_rhs.prepend(
+ BVHKey.for_wall(a_split), a_split
+ )
+ self.__revmap[b_split] = b_rhs.prepend(
+ BVHKey.for_wall(b_split), b_split
+ )
res = a_lhs
res.rjoin(b_tree)
res.rjoin(b_rhs)
self.__trees.remove(b_leaf.root())
a_lhs, a_rhs = a_leaf.split_up()
b_lhs, b_rhs = b_leaf.split_up()
- res = AVLTree[SplitWall]()
+ res = AVLTree[BVHKey, SplitWall]()
res.rjoin(a_lhs)
res.rjoin(b_rhs)
res.rjoin(b_lhs)
a_wall, b_wall = wall.to_split_wall()
return a_wall in self.__revmap and b_wall in self.__revmap
+ def contour_bound(self, wall: SplitWall) -> Rect | None:
+ if wall not in self.__revmap:
+ return None
+ leaf = self.__revmap[wall]
+ parent = leaf.root()
+ print(parent)
+ if parent.root is None:
+ raise Exception()
+ return parent.root.key.rect
+
def wall_bisects(self, wall: WallCoord) -> bool:
a_wall, b_wall = wall.to_split_wall()
a_split = self.find_split(a_wall)
return a_leaf.root() is b_leaf.root()
-class MazeNetworkTracker:
+class NetworkTracker:
def __init__(self, maze: Maze) -> None:
self.__maze: Maze = maze
self.__forest: DualForest = DualForest()
def wall_bisects(self, wall: WallCoord) -> bool:
return self.__forest.wall_bisects(wall)
+ def contour_bound(self, wall: SplitWall) -> Rect | None:
+ return self.__forest.contour_bound(wall)
+
def end(self) -> None:
self.__maze.observers.discard(self.__observer)
from collections.abc import Iterable
from amazeing.maze import Maze
-from amazeing.maze import WallCoord
-from amazeing.utils.randset import Randset
+from amazeing.utils import Randset, WallCoord
-class MazePacmanTracker:
+class PacmanTracker:
def __init__(self, maze: Maze) -> None:
self.__maze: Maze = maze
self.__dirty: Randset[WallCoord] = Randset()
--- /dev/null
+class Path:
+ def __init__(self) -> None:
+ pass
from collections.abc import Iterable, Generator, Callable
-from amazeing.utils import IVec2
-from .maze import Maze
-from .maze_coords import CellCoord
+from amazeing.utils import IVec2, CellCoord
+from amazeing.maze import Maze
class Pattern:
from .bi_map import BiMap
-from .avl import Tree as AVLTree, Leaf as AVLLeaf
+from .avl import (
+ Tree as AVLTree,
+ Leaf as AVLLeaf,
+ NoopKey as AVLNoopKey,
+ BVHKey,
+)
from .quadtree import Tree as QuadTree, Rect
from .ivec2 import IVec2
+from .coords import Cardinal, Orientation, WallCoord, CellCoord, SplitWall
+from .randset import Randset
-__all__ = ["BiMap", "AVLTree", "AVLLeaf", "QuadTree", "Rect", "IVec2"]
+__all__ = [
+ "BiMap",
+ "AVLTree",
+ "AVLLeaf",
+ "QuadTree",
+ "Rect",
+ "IVec2",
+ "AVLNoopKey",
+ "BVHKey",
+ "Cardinal",
+ "Orientation",
+ "WallCoord",
+ "CellCoord",
+ "SplitWall",
+ "Randset",
+]
from abc import ABC, abstractmethod
from collections.abc import Callable, Iterator
-from typing import cast
+from typing import Self, cast
import textwrap
+from amazeing.utils.coords import CellCoord, SplitWall
+from amazeing.utils.ivec2 import IVec2
+from amazeing.utils.quadtree import Rect
-class Tree[T]:
+
+class Key(ABC):
+ """
+ This class represents a tree key
+ It is expected to be associative but not commutative
+ """
+
+ @abstractmethod
+ def reconcile(self, rhs: Self) -> Self: ...
+
+
+class NoopKey(Key):
+ instance: Self | None = None
+
+ def __new__(cls) -> Self:
+ if cls.instance is None:
+ cls.instance = super().__new__(cls)
+ return cls.instance
+
+ def reconcile(self, rhs: Key) -> "NoopKey":
+ if not isinstance(rhs, NoopKey):
+ raise Exception()
+ return self
+
+ def __repr__(self) -> str:
+ return "None"
+
+
+class BVHKey(Key):
+ def __init__(self, rect: Rect) -> None:
+ super().__init__()
+ self.rect: Rect = rect
+
+ @staticmethod
+ def for_cell(cell: CellCoord) -> "BVHKey":
+ return BVHKey((cell, cell + IVec2.splat(1)))
+
+ @staticmethod
+ def for_wall(wall: SplitWall) -> "BVHKey":
+ return BVHKey.for_cell(wall[0])
+
+ def reconcile(self, rhs: Key) -> "BVHKey":
+ if not isinstance(rhs, BVHKey):
+ raise Exception()
+ s1, e1 = self.rect
+ s2, e2 = rhs.rect
+ return BVHKey((s1.lane_min(s2), e1.lane_max(e2)))
+
+ def __repr__(self) -> str:
+ return f"{self.rect}"
+
+
+class Tree[K: Key, V]:
def __init__(self) -> None:
- self.root: Node[T] | None = None
+ self.root: Node[K, V] | None = None
def __repr__(self) -> str:
return f"{self.root}" if self.root is not None else "(empty)"
if self.root is not None:
self.root.validate()
- def __iter__(self) -> Iterator[T]:
+ def __iter__(self) -> Iterator[V]:
if self.root is None:
return iter(())
return iter(self.root)
- def append(self, value: T) -> "Leaf[T]":
+ def append(self, key: K, value: V) -> "Leaf[K, V]":
if self.root is None:
- leaf = Leaf(self, value)
+ leaf = Leaf(self, key, value)
self.root = leaf
return leaf
if isinstance(self.root, Branch):
- return self.root.append(value)
+ return self.root.append(key, value)
self.root = Branch(
self,
self.root.with_parent,
- lambda parent: Leaf(parent, value),
+ lambda parent: Leaf(parent, key, value),
)
- return cast(Leaf[T], self.root.rhs)
+ return cast(Leaf[K, V], self.root.rhs)
- def prepend(self, value: T) -> "Leaf[T]":
+ def prepend(self, key: K, value: V) -> "Leaf[K, V]":
if self.root is None:
- leaf = Leaf(self, value)
+ leaf = Leaf(self, key, value)
self.root = leaf
return leaf
if isinstance(self.root, Branch):
- return self.root.prepend(value)
+ return self.root.prepend(key, value)
self.root = Branch(
self,
- lambda parent: Leaf(parent, value),
+ lambda parent: Leaf(parent, key, value),
self.root.with_parent,
)
- return cast(Leaf[T], self.root.lhs)
+ return cast(Leaf[K, V], self.root.lhs)
def height(self) -> int:
return 0 if self.root is None else self.root.height
def is_empty(self) -> bool:
return self.root is None
- def replace(self, node: "Node[T]", by: "Node[T]") -> None:
+ def replace(self, node: "Node[K, V]", by: "Node[K, V]") -> None:
if node is not self.root:
raise Exception("Replace operation with unknown node")
self.root = by
by.parent = self
- def balance_one_propagate(self) -> None:
+ def balance_update_propagate(self) -> None:
return
- def exchange(self, other: "Tree[T]") -> None:
+ def exchange(self, other: "Tree[K, V]") -> None:
a = self.root
b = other.root
if a is not None:
other.root = a
self.root = b
- def ljoin(self, lhs: "Tree[T]") -> None:
+ def ljoin(self, lhs: "Tree[K, V]") -> None:
if self is lhs:
raise Exception("Cannot merge tree with itself")
if self.height() >= lhs.height():
lhs.__rjoin(self)
self.exchange(lhs)
- def rjoin(self, rhs: "Tree[T]") -> None:
+ def rjoin(self, rhs: "Tree[K, V]") -> None:
if self is rhs:
raise Exception("Cannot merge tree with itself")
if self.height() >= rhs.height():
rhs.__ljoin(self)
self.exchange(rhs)
- def __ljoin(self, lhs: "Tree[T]") -> None:
+ def __ljoin(self, lhs: "Tree[K, V]") -> None:
if self.root is None:
self.exchange(lhs)
if self.root is None or lhs.root is None:
while isinstance(curr, Branch) and curr.height > insert.height:
curr = curr.lhs
parent = curr.parent
- new = Branch(parent, insert.with_parent, curr.with_parent)
+ new = Branch(
+ parent,
+ insert.with_parent,
+ curr.with_parent,
+ )
parent.replace(curr, new)
new.update_height()
- parent.balance_one_propagate()
+ parent.balance_update_propagate()
- def __rjoin(self, rhs: "Tree[T]") -> None:
+ def __rjoin(self, rhs: "Tree[K, V]") -> None:
if self.root is None:
self.exchange(rhs)
if self.root is None or rhs.root is None:
while isinstance(curr, Branch) and curr.height > insert.height:
curr = curr.rhs
parent = curr.parent
- new = Branch(parent, curr.with_parent, insert.with_parent)
+ new = Branch(
+ parent,
+ curr.with_parent,
+ insert.with_parent,
+ )
parent.replace(curr, new)
new.update_height()
- parent.balance_one_propagate()
+ parent.balance_update_propagate()
+
+class Node[K: Key, V](ABC):
+ __slots__: tuple[str, ...] = ("parent", "height", "key")
-class Node[T](ABC):
- def __init__(self, parent: "Branch[T] | Tree[T]") -> None:
- self.parent: Branch[T] | Tree[T] = parent
+ def __init__(self, parent: "Branch[K, V] | Tree[K, V]", key: K) -> None:
+ self.parent: Branch[K, V] | Tree[K, V] = parent
+ self.key: K = key
self.height: int = 1
@abstractmethod
- def __iter__(self) -> Iterator[T]: ...
+ def __iter__(self) -> Iterator[V]: ...
def validate(self) -> None:
visited = set()
- border: list[Node[T]] = [self]
+ border: list[Node[K, V]] = [self]
while len(border):
curr = border.pop()
if curr in visited:
border.append(curr.lhs)
border.append(curr.rhs)
- def with_parent(self, parent: "Branch[T] | Tree[T]") -> "Node[T]":
+ def with_parent(self, parent: "Branch[K, V] | Tree[K, V]") -> "Node[K, V]":
self.parent = parent
return self
- def root(self) -> Tree[T]:
+ def root(self) -> Tree[K, V]:
if isinstance(self.parent, Tree):
return self.parent
return self.parent.root()
- def split_up(self) -> tuple[Tree[T], Tree[T]]:
+ def split_up(self) -> tuple[Tree[K, V], Tree[K, V]]:
"""
makes self.parent empty
"""
curr = self
- lhs = Tree[T]()
- rhs = Tree[T]()
+ lhs = Tree[K, V]()
+ rhs = Tree[K, V]()
while isinstance(curr.parent, Node):
curr_parent = curr.parent
- extra = Tree[T]()
+ extra = Tree[K, V]()
if curr_parent.lhs is curr:
extra.root = curr_parent.rhs.with_parent(extra)
rhs.rjoin(extra)
return (lhs, rhs)
-class Branch[T](Node[T]):
+class Branch[K: Key, V](Node[K, V]):
+ __slots__: tuple[str, ...] = ("lhs", "rhs")
+
def __init__(
self,
- parent: "Branch[T] | Tree[T]",
- lhs: Callable[["Branch[T]"], Node[T]],
- rhs: Callable[["Branch[T]"], Node[T]],
+ parent: "Branch[K, V] | Tree[K, V]",
+ lhs: Callable[["Branch[K, V]"], Node[K, V]],
+ rhs: Callable[["Branch[K, V]"], Node[K, V]],
) -> None:
- super().__init__(parent)
- self.lhs: Node[T] = lhs(self)
- self.rhs: Node[T] = rhs(self)
+ self.lhs: Node[K, V] = lhs(self)
+ self.rhs: Node[K, V] = rhs(self)
+ super().__init__(parent, self.lhs.key.reconcile(self.rhs.key))
self.update_height()
- def __iter__(self) -> Iterator[T]:
+ def __iter__(self) -> Iterator[V]:
for e in self.lhs:
yield e
for e in self.rhs:
+ textwrap.indent(str(self.rhs), " ")
)
- def replace(self, node: Node[T], by: Node[T]) -> None:
+ def replace(self, node: Node[K, V], by: Node[K, V]) -> None:
if self.lhs is node:
self.lhs = by
elif self.rhs is node:
raise Exception("Replace operation with unknown node")
by.parent = self
- def get_other(self, node: Node[T]) -> Node[T]:
+ def get_other(self, node: Node[K, V]) -> Node[K, V]:
if self.lhs is node:
return self.rhs
elif self.rhs is node:
raise Exception("Get other operation with unknown node")
def update_height(self) -> None:
- self.height = max(self.rhs.height, self.lhs.height) + 1
+ self.height = max(self.lhs.height, self.rhs.height) + 1
+
+ def update_key(self) -> None:
+ self.key = self.lhs.key.reconcile(self.rhs.key)
def get_balance(self) -> int:
return self.rhs.height - self.lhs.height
n.parent = m
n.update_height()
m.update_height()
+ n.update_key()
+ m.update_key()
m.parent = parent
m.parent.replace(self, m)
m.parent = n
n.update_height()
m.update_height()
+ n.update_key()
+ m.update_key()
n.parent = parent
n.parent.replace(self, n)
self.rotate_ll()
n = self.lhs
- def append(self, value: T) -> "Leaf[T]":
+ def append(self, key: K, value: V) -> "Leaf[K, V]":
if isinstance(self.rhs, Branch):
- return self.rhs.append(value)
- new = Branch[T](
+ return self.rhs.append(key, value)
+ new = Branch[K, V](
self,
self.rhs.with_parent,
- lambda parent: Leaf[T](parent, value),
+ lambda parent: Leaf[K, V](parent, key, value),
)
self.rhs = new
- new_leaf = cast(Leaf[T], new.rhs)
- self.balance_one_propagate()
+ new_leaf = cast(Leaf[K, V], new.rhs)
+ self.balance_update_propagate()
return new_leaf
- def prepend(self, value: T) -> "Leaf[T]":
+ def prepend(self, key: K, value: V) -> "Leaf[K, V]":
if isinstance(self.lhs, Branch):
- return self.lhs.prepend(value)
- new = Branch[T](
+ return self.lhs.prepend(key, value)
+ new = Branch[K, V](
self,
- lambda parent: Leaf[T](parent, value),
+ lambda parent: Leaf[K, V](parent, key, value),
self.lhs.with_parent,
)
self.lhs = new
- new_leaf = cast(Leaf[T], new.lhs)
- self.balance_one_propagate()
+ new_leaf = cast(Leaf[K, V], new.lhs)
+ self.balance_update_propagate()
return new_leaf
def balance_one(self) -> None:
else:
self.rotate_ll()
- def balance_one_propagate(self) -> None:
+ def balance_update_propagate(self) -> None:
init_height = self.height
+ init_key = self.key
self.update_height()
+ self.update_key()
self.balance_one()
- if init_height != self.height:
- self.parent.balance_one_propagate()
+ if init_height != self.height or init_key != self.key:
+ self.parent.balance_update_propagate()
+
+class Leaf[K: Key, V](Node[K, V]):
+ __slots__: tuple[str, ...] = ("value",)
-class Leaf[T](Node[T]):
def __init__(
self,
- parent: Branch[T] | Tree[T],
- value: T,
+ parent: Branch[K, V] | Tree[K, V],
+ key: K,
+ value: V,
) -> None:
- super().__init__(parent)
- self.value: T = value
+ super().__init__(parent, key)
+ self.value: V = value
- def __iter__(self) -> Iterator[T]:
+ def __iter__(self) -> Iterator[V]:
yield self.value
def __repr__(self) -> str:
- return f"leaf: {self.value}"
+ return f"leaf ({self.key}): {self.value}"
def remove(self) -> None:
if isinstance(self.parent, Tree):
return
other = self.parent.get_other(self)
self.parent.parent.replace(self.parent, other)
- other.parent.balance_one_propagate()
+ other.parent.balance_update_propagate()
-from .avl import Tree as AVLTree, Leaf as AVLLeaf
+from amazeing.utils.avl import (
+ Tree as AVLTree,
+ Leaf as AVLLeaf,
+ NoopKey as AVLNoopKey,
+)
class BiMap[K, R]:
def __init__(self) -> None:
- self.__map: dict[K, AVLTree[R]] = {}
- self.__revmap: dict[AVLTree[R], K] = {}
- self.__leafmap: dict[R, AVLLeaf[R]] = {}
+ self.__map: dict[K, AVLTree[AVLNoopKey, R]] = {}
+ self.__revmap: dict[AVLTree[AVLNoopKey, R], K] = {}
+ self.__leafmap: dict[R, AVLLeaf[AVLNoopKey, R]] = {}
def add(self, key: K, revkey: R) -> None:
if self.revcontains(revkey):
self.revremove(revkey)
if not self.contains(key):
- tree = AVLTree[R]()
+ tree = AVLTree[AVLNoopKey, R]()
self.__map[key] = tree
self.__revmap[tree] = key
- self.__leafmap[revkey] = self.__map[key].append(revkey)
+ self.__leafmap[revkey] = self.__map[key].append(AVLNoopKey(), revkey)
def remove(self, key: K) -> None:
for revkey in self.__map[key]:
if root.height() == 0:
self.__map.pop(self.__revmap.pop(root))
- def get(self, key: K) -> AVLTree[R]:
+ def get(self, key: K) -> AVLTree[AVLNoopKey, R]:
return self.__map[key] if self.contains(key) else AVLTree()
def revget(self, revkey: R) -> K:
if src not in self.__map:
return
if dst not in self.__map:
- tree = AVLTree[R]()
+ tree = AVLTree[AVLNoopKey, R]()
self.__map[dst] = tree
self.__revmap[tree] = dst
self.__map[dst].rjoin(self.__map.pop(src))
from enum import Enum, auto
from typing import Iterable, cast, overload
-from amazeing.utils import IVec2
+from amazeing.utils.ivec2 import IVec2
class Orientation(Enum):
class IVec2[T = int]:
+ __slots__: tuple[str, str] = ("x", "y")
+
def copy(self, inner_copy: Callable[[T], T] = lambda e: e) -> "IVec2[T]":
return IVec2(inner_copy(self.x), inner_copy(self.y))
def __repr__(self) -> str:
return f"{self.x, self.y}"
- @staticmethod
def with_op[T2](
- op: Callable[[T, T], T2],
- ) -> Callable[["IVec2[T]", "T | IVec2[T]"], "IVec2[T2]"]:
- return lambda self, other: IVec2(
- op(
- self.x,
- (
- other
- if isinstance(other, IVec2)
- else (other := type(self).splat(other))
- ).x,
- ),
- op(self.y, cast(IVec2[T], other).y),
+ self, op: Callable[[T, T], T2], other: "IVec2[T]"
+ ) -> "IVec2[T2]":
+ return IVec2(
+ op(self.x, other.x),
+ op(self.y, other.y),
)
def innertype(self) -> Type[T]:
def __hash__(self) -> int:
return hash((self.x, self.y))
+ def lane_min(self, other: "IVec2[T]") -> "IVec2[T]":
+ return IVec2(min(self.x, other.x), min(self.y, other.y)) # type:ignore
+
+ def lane_max(self, other: "IVec2[T]") -> "IVec2[T]":
+ return IVec2(max(self.x, other.x), max(self.y, other.y)) # type:ignore
+
def xy(self) -> tuple[T, T]:
return (self.x, self.y)
from collections.abc import Callable, Generator
-from .ivec2 import IVec2
+from amazeing.utils.ivec2 import IVec2
from functools import partial
from itertools import chain
-WIDTH=100
-HEIGHT=100
+WIDTH=25
+HEIGHT=25
ENTRY=0,0
EXIT=24,24
OUTPUT_FILE=test
TILEMAP_FULL="{1000,1000,1000:1000,1000,1000}######"
TILEMAP_FULL="{1000,1000,1000:1000,1000,1000}######"
TILEMAP_FULL="{1000,1000,1000:1000,1000,1000}######"
-TILEMAP_FULL=1"{100,1000,1000:1000,1000,1000}######"
+TILEMAP_FULL=1"{100,1000,1000:1000,1000,1000}███{1000,100,1000:1000,1000,1000}███"
TILEMAP_FULL=1"{100,1000,1000:1000,1000,1000}######"
TILEMAP_FULL=1"{100,1000,1000:1000,1000,1000}######"
TILEMAP_PATH="{100,100,1000:100,100,1000} "