]> Untitled Git - axy/ft/a-maze-ing.git/commitdiff
Improved avl, among others
authorAxy <gilliardmarthey.axel@gmail.com>
Mon, 16 Mar 2026 03:28:43 +0000 (04:28 +0100)
committerAxy <gilliardmarthey.axel@gmail.com>
Mon, 16 Mar 2026 03:28:43 +0000 (04:28 +0100)
__main__.py
amazeing/maze_class/maze_coords.py
amazeing/maze_class/maze_network_tracker.py
amazeing/utils/avl.py

index a6a59a2c3e269ea98e6b4060afc496484d441a89..082ffa25de35986542aa89317ec54470ec0c77db 100644 (file)
@@ -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)
 
index 0e8528bd7c54ecf3a37712dd01b922e840724000..d6e524da72e111bec942459913006767f8cba67f 100644 (file)
@@ -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())
index 65890187f0874387e6c2411f9aa647eed96fb9ac..5f6ad301af5278b594e6e1b1c5b1064953a4ab00 100644 (file)
@@ -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:
index 26f2a1f47f0c42e73b1786f5436c4e42c06e332f..4f0c1775867244ad6766b0efc8c70158c80e6ad0 100644 (file)
@@ -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