From: Axy Date: Mon, 16 Mar 2026 03:28:43 +0000 (+0100) Subject: Improved avl, among others X-Git-Url: https://git.uwuaxy.net/?a=commitdiff_plain;h=5cec66e1e620dfab7f83152f6405f90131035423;p=axy%2Fft%2Fa-maze-ing.git Improved avl, among others --- diff --git a/__main__.py b/__main__.py index a6a59a2..082ffa2 100644 --- a/__main__.py +++ b/__main__.py @@ -11,36 +11,21 @@ import random from amazeing.config.config_parser import Config -from amazeing.maze_class.maze_coords import Cardinal, CellCoord +from amazeing.maze_class import maze_network_tracker +from amazeing.maze_class.maze_coords import Cardinal, CellCoord, WallCoord from amazeing.maze_display.TTYdisplay import TileCycle, TileMaps, extract_pairs from amazeing.maze_display.backend import CloseRequested, IVec2 from amazeing.utils import AVLTree -tree = AVLTree() - -keys = {i: tree.append(i) for i in range(25)} - -for i in range(1, 5): - keys[i].remove() -for i in range(5, 15, 2): - keys[i].remove() - -tree2 = AVLTree() - -keys2 = {i: tree2.append(i) for i in range(25)} - -for i in range(1, 10, 3): - keys2[i].remove() - -tree.rjoin(tree2) - -print(tree) - -lhs_tree, rhs_tree = keys2[18].split_up() - -print(lhs_tree) -print(rhs_tree) +forest = maze_network_tracker.DualForest() +for wall in CellCoord(0, 0).walls(): + forest.fill_wall(wall) +for wall in CellCoord(1, 0).walls(): + forest.fill_wall(wall) +for wall in CellCoord(3, 0).walls(): + forest.fill_wall(wall) +print(forest) exit(0) diff --git a/amazeing/maze_class/maze_coords.py b/amazeing/maze_class/maze_coords.py index 0e8528b..d6e524d 100644 --- a/amazeing/maze_class/maze_coords.py +++ b/amazeing/maze_class/maze_coords.py @@ -125,16 +125,28 @@ class WallCoord: ) return (IVec2(x, y) for x in x_iter for y in y_iter) - def neighbour_cells(self) -> list["CellCoord"]: + def neighbour_cells(self) -> tuple["CellCoord", "CellCoord"]: if self.orientation == Orientation.HORIZONTAL: - return [ + return ( CellCoord(self.b, self.a), CellCoord(self.b, self.a - 1), - ] - return [ + ) + return ( CellCoord(self.a, self.b), CellCoord(self.a - 1, self.b), - ] + ) + + def to_split_wall( + self, + ) -> tuple["SplitWall", "SplitWall"]: + def find_cardinal(cell: CellCoord) -> Cardinal: + for cardinal in Cardinal.all(): + if cell.get_wall(cardinal) == self: + return cardinal + raise Exception("Find cardinal on wall not on cell") + + a, b = self.neighbour_cells() + return ((a, find_cardinal(a)), (b, find_cardinal(b))) class CellCoord(IVec2): @@ -184,3 +196,18 @@ class CellCoord(IVec2): def neighbours_unchecked(self) -> Iterable["CellCoord"]: return map(self.get_neighbour, Cardinal.all()) + + +type SplitWall = tuple[CellCoord, Cardinal] + + +def split_wall_cw(wall: SplitWall) -> SplitWall: + return (wall[0].get_neighbour(wall[1]), wall[1].right()) + + +def split_wall_ccw(wall: SplitWall) -> SplitWall: + return (wall[0].get_neighbour(wall[1]), wall[1].left()) + + +def split_wall_opposite(wall: SplitWall) -> SplitWall: + return (wall[0].get_neighbour(wall[1]), wall[1].opposite()) diff --git a/amazeing/maze_class/maze_network_tracker.py b/amazeing/maze_class/maze_network_tracker.py index 6589018..5f6ad30 100644 --- a/amazeing/maze_class/maze_network_tracker.py +++ b/amazeing/maze_class/maze_network_tracker.py @@ -1,5 +1,12 @@ from amazeing.maze_class.maze import Maze -from amazeing.maze_class.maze_coords import CellCoord, WallCoord +from amazeing.maze_class.maze_coords import ( + Cardinal, + CellCoord, + SplitWall, + WallCoord, + split_wall_ccw, + split_wall_opposite, +) from amazeing.utils import BiMap from amazeing.utils import AVLTree, AVLLeaf @@ -8,7 +15,7 @@ class NetworkID: pass -class DualForest[T]: +class DualForest: """ A forest of trees that contour networks AVL trees are used to represent the contours, such that split and @@ -19,8 +26,85 @@ class DualForest[T]: def __init__( self, ) -> None: - self.__trees: set[AVLTree[T]] = set() - self.__revmap: dict[T, set[AVLLeaf[T]]] = {} + # Trees are left hand chiral + self.__trees: set[AVLTree[SplitWall]] = set() + self.__revmap: dict[SplitWall, AVLLeaf[SplitWall]] = {} + + def __repr__(self) -> str: + return ( + f"DualForest ({len(self.__trees)}):\n trees:\n" + + f"{self.__trees}\n revmap:\n{self.__revmap}\n" + ) + + def fill_wall(self, wall: WallCoord) -> None: + lhs, rhs = wall.to_split_wall() + if lhs in self.__revmap or rhs in self.__revmap: + return + a_tree = AVLTree() + b_tree = AVLTree() + self.__revmap[lhs] = a_tree.append(lhs) + self.__revmap[rhs] = b_tree.append(rhs) + + def find_split(split_wall: SplitWall) -> SplitWall | None: + split_wall = split_wall_opposite(split_wall) + for _ in range(3): + split_wall = split_wall_ccw(split_wall) + if split_wall in self.__revmap: + return split_wall + return None + + match (find_split(lhs), find_split(rhs)): + case (None, None): + a_tree.rjoin(b_tree) + self.__trees.add(a_tree) + case (None, b_split): + self.__trees.remove(self.__revmap[b_split].root()) + lhs, rhs = self.__revmap[b_split].split_up() + lhs.rjoin(a_tree) + lhs.rjoin(b_tree) + self.__revmap[b_split] = lhs.append(b_split) + lhs.rjoin(rhs) + self.__trees.add(lhs) + case (a_split, None): + self.__trees.remove(self.__revmap[a_split].root()) + lhs, rhs = self.__revmap[a_split].split_up() + lhs.rjoin(b_tree) + lhs.rjoin(a_tree) + self.__revmap[a_split] = lhs.append(a_split) + lhs.rjoin(rhs) + self.__trees.add(lhs) + case (a_split, b_split): + if ( + self.__revmap[a_split].root() + is self.__revmap[b_split].root() + ): + self.__trees.remove(self.__revmap[a_split].root()) + lhs, rhs = self.__revmap[a_split].split_up() + lhs.rjoin(b_tree) + self.__revmap[a_split] = rhs.prepend(a_split) + rhs.ljoin(a_tree) + rhs.rjoin(lhs) + lhs, rhs = self.__revmap[b_split].split_up() + self.__revmap[b_split] = rhs.prepend(b_split) + self.__trees.add(lhs) + self.__trees.add(rhs) + else: + self.__trees.remove(self.__revmap[a_split].root()) + self.__trees.remove(self.__revmap[b_split].root()) + a_lhs, a_rhs = self.__revmap[a_split].split_up() + b_lhs, b_rhs = self.__revmap[b_split].split_up() + self.__revmap[a_split] = a_rhs.prepend(a_split) + self.__revmap[b_split] = b_rhs.prepend(b_split) + res = a_lhs + res.rjoin(b_tree) + res.rjoin(b_rhs) + res.rjoin(b_lhs) + res.rjoin(a_tree) + res.rjoin(a_rhs) + self.__trees.add(res) + + def empty_wall(self, wall: WallCoord) -> None: + pass class MazeNetworkTracker: diff --git a/amazeing/utils/avl.py b/amazeing/utils/avl.py index 26f2a1f..4f0c177 100644 --- a/amazeing/utils/avl.py +++ b/amazeing/utils/avl.py @@ -24,6 +24,20 @@ class Tree[T]: ) return cast(Leaf, self.root.rhs) + def prepend(self, value: T) -> "Leaf[T]": + if self.root is None: + leaf = Leaf(self, value) + self.root = leaf + return leaf + if isinstance(self.root, Branch): + return self.root.prepend(value) + self.root = Branch( + self, + lambda parent: Leaf(parent, value), + self.root.with_parent, + ) + return cast(Leaf, self.root.lhs) + def height(self) -> int: return 0 if self.root is None else self.root.height @@ -71,13 +85,13 @@ class Tree[T]: curr = self.root insert = lhs.root lhs.root = None - while isinstance(curr, Branch) and curr.height > insert.height + 1: + while isinstance(curr, Branch) and curr.height > insert.height: curr = curr.lhs parent = curr.parent - new = Branch(curr.parent, insert.with_parent, curr.with_parent) + new = Branch(parent, insert.with_parent, curr.with_parent) parent.replace(curr, new) new.update_height() - new.parent.balance_one_propagate() + parent.balance_one_propagate() def __rjoin(self, rhs: "Tree[T]") -> None: if self.root is None: @@ -87,13 +101,13 @@ class Tree[T]: curr = self.root insert = rhs.root rhs.root = None - while isinstance(curr, Branch) and curr.height > insert.height + 1: + while isinstance(curr, Branch) and curr.height > insert.height: curr = curr.rhs parent = curr.parent - new = Branch(curr.parent, curr.with_parent, insert.with_parent) + new = Branch(parent, curr.with_parent, insert.with_parent) parent.replace(curr, new) new.update_height() - new.parent.balance_one_propagate() + parent.balance_one_propagate() class Node[T]: @@ -110,7 +124,7 @@ class Node[T]: return self.parent return self.parent.root() - def split_up(self) -> tuple[Tree, Tree]: + def split_up(self) -> tuple[Tree[T], Tree[T]]: """ makes self.parent empty """ @@ -299,11 +313,6 @@ class Branch[T](Node[T]): self.update_height() def append(self, value: T) -> "Leaf[T]": - if self.rhs is None: - leaf = Leaf[T](self, value) - self.rhs = leaf - self.balance_one_propagate() - return leaf if isinstance(self.rhs, Branch): return self.rhs.append(value) new = Branch[T]( @@ -316,6 +325,19 @@ class Branch[T](Node[T]): self.balance_one_propagate() return new_leaf + def prepend(self, value: T) -> "Leaf[T]": + if isinstance(self.lhs, Branch): + return self.lhs.prepend(value) + new = Branch[T]( + self, + lambda parent: Leaf[T](parent, value), + self.lhs.with_parent, + ) + self.lhs = new + new_leaf = cast(Leaf[T], new.lhs) + self.balance_one_propagate() + return new_leaf + def balance_one(self): if abs(self.get_balance()) <= 1: return