]> Untitled Git - axy/ft/pacman.git/commitdiff
Small update to fix union support in queries
authorAxy <gilliardmarthey.axel@gmail.com>
Tue, 22 Sep 2026 22:43:13 +0000 (00:43 +0200)
committerAxy <gilliardmarthey.axel@gmail.com>
Tue, 22 Sep 2026 22:43:13 +0000 (00:43 +0200)
src/pacman/ecs/__init__.py
src/pacman/ecs/world.py
src/pacman/utils/type_resolve.py

index d963f97b07ec0d9a5a17ec0b89c2ad3df1457fb8..c23136385bb389e045898277aae6499bb63c3465 100644 (file)
@@ -126,7 +126,8 @@ def close_on_close(world: World, events: WindowEvents) -> None:
 
 
 def render_sprite2d(
-    window: Window, sprites_q: Query[tuple[CoordAbs2D, Sprite2D, ShouldRender]]
+    window: Window,
+    sprites_q: Query[tuple[CoordAbs2D, Sprite2D], ShouldRender],
 ) -> None:
     sprites_raw = sorted(
         sprites_q,
@@ -134,7 +135,9 @@ def render_sprite2d(
         reverse=True,
     )
     sprites = [
-        (sprite.surface, coord._vec.xy) for coord, sprite, _ in sprites_raw
+        (sprite.surface, coord._vec.xy)
+        for coord, sprite in sprites_raw
+        if sprite is not None
     ]
     window.surface.fill((0, 0, 0))
     window.surface.blits(sprites)
index f6a6a9c724f27929ab464a6369a12ff5af08f8e9..7f0a8624c90419fdb5f2311e9f5379941a388b0e 100644 (file)
@@ -225,6 +225,13 @@ class QueryFilter:
 type QueryFilters = RecursiveTuple[QueryFilter]
 
 
+class _NoFetchCls:
+    pass
+
+
+_NoFetch = _NoFetchCls()
+
+
 class QueryItem(QueryFilter):
     @override
     @classmethod
@@ -242,10 +249,10 @@ class QueryItem(QueryFilter):
     @abstractmethod
     def filter_fetch(
         cls, ty: type
-    ) -> Callable[[Archetype], None | Callable[[int], Self]]: ...
+    ) -> Callable[[Archetype], None | Callable[[int], Self | _NoFetchCls]]: ...
 
 
-type QueryItems = RecursiveTuple[QueryItem]
+type QueryItems = RecursiveTuple[QueryItem | None]
 
 
 class Component(QueryItem):
@@ -258,7 +265,7 @@ class Component(QueryItem):
     @classmethod
     def filter_fetch(
         cls, ty: type
-    ) -> Callable[[Archetype], None | Callable[[int], Self]]:
+    ) -> Callable[[Archetype], None | Callable[[int], Self | _NoFetchCls]]:
         return lambda archetype: (
             None
             if (idx := archetype._components.get(ty)) is None
@@ -340,25 +347,27 @@ class Query[Items, Filters = tuple[()]](
     ) -> None:
         if filters is None:
             filters = cast(type[Filters], tuple[()])
+        resolved_fetchers = resolve_type_aliases(fetchers)
+        resolved_filters = resolve_type_aliases(filters)
         self._world: World = world
         self._fetchers_raw = cast(
-            Callable[[Archetype], Callable[[int], Items] | None],
-            Query.preprocess_fetchers(resolve_type_aliases(fetchers)),
-        )
-        self._filters_raw = Query.preprocess_filters(
-            resolve_type_aliases(filters)
+            Callable[[Archetype], Callable[[int], Items | _NoFetchCls] | None],
+            Query.preprocess_fetchers(resolved_fetchers),
         )
+        self._filters_raw = Query.preprocess_filters(resolved_filters)
         self._seen_archetypes: int = 0
         self._archetypes_idx: dict[Archetype, int] = {}
         self._archetypes: list[Archetype] = []
         self._filters: list[Callable[[int], bool]] = []
-        self._fetchers: list[Callable[[int], Items]] = []
+        self._fetchers: list[Callable[[int], Items | _NoFetchCls]] = []
         self.updated_from_world()
 
     @staticmethod
     def preprocess_fetchers(
         fetcher: type[QueryItems],
-    ) -> Callable[[Archetype], Callable[[int], QueryItems] | None]:
+    ) -> Callable[
+        [Archetype], Callable[[int], QueryItems | _NoFetchCls] | None
+    ]:
         args = list(map(Query.preprocess_fetchers, get_args(fetcher)))
 
         def tuple_constructor(
@@ -370,12 +379,20 @@ class Query[Items, Filters = tuple[()]](
                 if curr is None:
                     return None
                 lst.append(curr)
-            return fast_tuple_constructor(tuple(lst))
+            constr = fast_tuple_constructor(tuple(lst))
+            return lambda i: cast(
+                QueryItems,
+                (
+                    res
+                    if all(map(lambda e: e is not _NoFetch, res := constr(i)))
+                    else _NoFetch
+                ),
+            )
 
         def union_constructor(
             archetype: Archetype,
-        ) -> Callable[[int], QueryItems] | None:
-            lst: list[Callable[[int], QueryItems]] = []
+        ) -> Callable[[int], QueryItems | _NoFetchCls] | None:
+            lst: list[Callable[[int], QueryItems | _NoFetchCls]] = []
             for arg in args:
                 curr = arg(archetype)
                 if curr is None:
@@ -383,12 +400,17 @@ class Query[Items, Filters = tuple[()]](
                 lst.append(curr)
             if len(lst) == 0:
                 return None
-            return lst.pop()
+            return lambda i: next(
+                (res for e in lst if (res := e(i)) is not _NoFetch), _NoFetch
+            )
 
-        if origin_or_cls(fetcher) is tuple:
+        orig = origin_or_cls(fetcher)
+        if orig is tuple:
             return tuple_constructor
-        if origin_or_cls(fetcher) is Union:
+        if orig is Union:
             return union_constructor
+        if orig is None or orig is type(None):
+            return lambda _: None
 
         return cast(QueryItem, fetcher).filter_fetch(fetcher)
 
@@ -419,7 +441,7 @@ class Query[Items, Filters = tuple[()]](
                     return False
                 cond = composed_by(cond, curr, bool.__and__)
             if cond is None:
-                return lambda _: True
+                return True
             return cond
 
         def union_constructor(
@@ -434,13 +456,16 @@ class Query[Items, Filters = tuple[()]](
                     continue
                 cond = composed_by(cond, curr, bool.__or__)
             if cond is None:
-                return lambda _: False
+                return False
             return cond
 
-        if origin_or_cls(filter) is tuple:
+        orig = origin_or_cls(filter)
+        if orig is tuple:
             return tuple_constructor
-        if origin_or_cls(filter) is Union:
+        if orig is Union:
             return union_constructor
+        if orig is None or orig is type(None):
+            return lambda _: True
 
         return cast(QueryFilter, filter).filter(filter)
 
@@ -479,12 +504,12 @@ class Query[Items, Filters = tuple[()]](
     @override
     def __iter__(self) -> Iterator[Items]:
         return (
-            fetch(i)
+            cast(Items, res)
             for filt, fetch, arch in zip(
                 self._filters, self._fetchers, self._archetypes, strict=True
             )
             for i in range(len(arch._entities))
-            if filt(i)
+            if filt(i) and (res := fetch(i)) is not _NoFetch
         )
 
     @override
index 1367910001005e74a311725364cb16599016a124..7b9d8b303332dfc2e9b72e8732425205dca867fd 100644 (file)
@@ -5,16 +5,10 @@ import typing
 from collections.abc import Iterable
 from typing import (
     Any,
-    cast,
     get_args,
     get_origin,
 )
 
-from pacman.utils.variadics_please import (
-    RecursiveTuple,
-    recursive_tuple_create,
-)
-
 
 def map_type_generics(ty: Any, mapping: dict[Any, type]) -> Any:
     """Map generics for this type, maybe creating a new type."""
@@ -46,20 +40,6 @@ def resolve_type_aliases(ty: Any) -> Any:
     )
 
 
-def resolve_ty_to_tup[T](
-    tup: type[RecursiveTuple[T]],
-) -> RecursiveTuple[type[T]]:
-    def inner(
-        ty: type[RecursiveTuple[T]],
-    ) -> type[T] | tuple[type[RecursiveTuple[T]], ...]:
-        base = get_origin(ty)
-        if base is tuple:
-            return cast(tuple[type[RecursiveTuple[T]], ...], get_args(ty))
-        return cast(type[T], ty)
-
-    return recursive_tuple_create(tup, inner)
-
-
 def origin_or_cls(ty: Any) -> Any:
     res = get_origin(ty)
     if res is None:
@@ -74,4 +54,4 @@ type A[T] = list[tuple[T, B[int]]]
 if __name__ == "__main__":
     ty = tuple[int, tuple[float, str], tuple[int, int] | float]
     print(ty)
-    print(resolve_ty_to_tup(resolve_type_aliases(ty)))
+    print(resolve_type_aliases(ty))