from pathlib import Path
import pygame
-from pygame import Vector2, Vector3
+from pygame import Vector3
from pacman.ecs.hierarchy import (
Parent,
)
from pacman.ecs.world import (
Entity,
- Resource,
+ Resource2,
Systems,
World,
WorldIsInit,
def run_minimal_subschedules(world: World) -> None:
- if Resource(WorldIsInit) not in world:
+ if Resource2(WorldIsInit) not in world:
world.tick(StartupSchedule)
- world[Resource(WorldIsInit)] = WorldIsInit()
+ world[Resource2(WorldIsInit)] = WorldIsInit()
world.tick(UpdateSchedule)
class CoordAbs2D:
- vec: Vector2
- depth: float
+ _vec: Vector3
def __init__(self) -> None:
- self.vec = Vector2(0.0)
- self.depth = 0.0
+ self._vec = Vector3(0.0)
class Coord2D(Vector3):
def update_coord(world: World) -> None:
for abs_coord, coord in world.query(tuple[CoordAbs2D, Coord2D]):
- abs_coord.vec = coord.xy
- abs_coord.depth = coord.z
+ abs_coord._vec = coord.xyz
def update_coord_hierarchy(world: World) -> None:
for parent, child in traverse_hierarchy(world, tuple[CoordAbs2D]):
if parent is None:
continue
- child[0].vec += parent[0].vec
+ child[0]._vec += parent[0]._vec
PropagateTransform = SystemSet("propagate-transform")
def init_window(world: World) -> None:
pygame.init()
- world[Resource(Window)] = Window(
+ world[Resource2(Window)] = Window(
pygame.display.set_mode(), pygame.time.Clock()
)
- world[Resource(WindowEvents)] = WindowEvents([])
+ world[Resource2(WindowEvents)] = WindowEvents([])
@dataclass
if pygame.QUIT in (
event.type for event in world.res_s(WindowEvents).events
):
- world[Resource(WorldShouldExit)] = WorldShouldExit()
+ world[Resource2(WorldShouldExit)] = WorldShouldExit()
def render_sprite2d(world: World) -> None:
window = world.res_s(Window)
sprites_raw = sorted(
world[tuple[CoordAbs2D, Sprite2D, ShouldRender]],
- key=lambda e: e[0].depth,
+ key=lambda e: e[0]._vec.z,
reverse=True,
)
- sprites = [(sprite.surface, coord.vec) for coord, sprite, _ in sprites_raw]
+ sprites = [
+ (sprite.surface, coord._vec.xy) for coord, sprite, _ in sprites_raw
+ ]
window.surface.fill((0, 0, 0))
window.surface.blits(sprites)
window.clock.tick(60)
import contextlib
-from collections.abc import Callable, Generator
+from abc import abstractmethod
+from collections.abc import Callable, Generator, Iterable, Iterator
from dataclasses import dataclass
-from typing import Any, Literal, cast, get_args, overload
+from typing import (
+ Any,
+ Literal,
+ Protocol,
+ Self,
+ cast,
+ get_args,
+ overload,
+ override,
+)
from pacman.ecs.schedule import (
MainSchedule,
ScheduleLabel,
SystemSet,
)
+from pacman.utils.variadics_please import VarargCallable, VarargTuple
class Entity:
@dataclass
-class Resource[T]:
+class Resource2[T]:
storage: type[T]
pass
-type System = Callable[["World"], None]
+type RawSystem = Callable[["World"], None]
type Plugin = Callable[["World"], None] | tuple[Plugin, ...]
type ComponentHook[T] = Callable[["World", "Entity", T], None]
+class FromWorld(Protocol):
+ @classmethod
+ @abstractmethod
+ def extract_from_world(cls, world: "World") -> Self | None: ...
+
+
+type SystemParam = VarargTuple[SystemParam] | World | FromWorld
+
+type System = VarargCallable[SystemParam, None]
+
+
+class QueryItem:
+ pass
+
+
+type QueryItems = VarargTuple[QueryItems] | QueryItem
+
+
+class QueryFilter:
+ pass
+
+
+type QueryFilters = VarargTuple[QueryFilters] | QueryFilter
+
+
+class Query[Items: QueryItems, Filters: QueryFilters = tuple[()]](
+ FromWorld, Iterable[Items]
+):
+ def __init__(
+ self, world: "World", items: type[Items], filters: type[Filters]
+ ) -> None:
+ pass
+
+ @override
+ @classmethod
+ def extract_from_world(cls, world: "World") -> Self | None:
+ return cls(world, *get_args(cls))
+
+ @override
+ def __iter__(self) -> Iterator[Items]:
+ if False:
+ yield
+
+
+class Resource(FromWorld):
+ @override
+ @classmethod
+ def extract_from_world(cls, world: "World") -> Self | None:
+
+ pass
+
+
@dataclass
class EntityThunk:
world: "World"
self._entities: dict[Entity, set[type]] = {}
self._components: dict[type, dict[Entity, Any]] = {}
self._resources: dict[type, Any] = {}
- self._schedules: dict[ScheduleLabel, Schedule[System]] = {}
+ self._schedules: dict[ScheduleLabel, Schedule[RawSystem]] = {}
self._insert_hooks: dict[type, ComponentHook[Any]] = {}
self._remove_hooks: dict[type, ComponentHook[Any]] = {}
)
@overload
- def __getitem__[T](self, arg: Resource[T]) -> T: ...
+ def __getitem__[T](self, arg: Resource2[T]) -> T: ...
@overload
def __getitem__[*T](
) -> Any:
if isinstance(arg, Entity):
return self.entity(arg)
- if isinstance(arg, Resource):
+ if isinstance(arg, Resource2):
return self._resources[arg.storage]
return self.query(arg)
@overload
- def __setitem__[T](self, key: Resource[T], val: T) -> None: ...
+ def __setitem__[T](self, key: Resource2[T], val: T) -> None: ...
@overload
def __setitem__[*T](self, key: Entity, val: tuple[*T]) -> None: ...
self._entities[key] = set()
for component in val:
self[key][type(component)] = component
- elif isinstance(key, Resource) and isinstance(val, key.storage):
+ elif isinstance(key, Resource2) and isinstance(val, key.storage):
self._resources[key.storage] = val
else:
raise TypeError()
@overload
- def __delitem__[T](self, key: Resource[T]) -> None: ...
+ def __delitem__[T](self, key: Resource2[T]) -> None: ...
@overload
def __delitem__(self, key: Entity) -> None: ...
- def __delitem__[T](self, key: Entity | Resource[T]) -> None:
+ def __delitem__[T](self, key: Entity | Resource2[T]) -> None:
if isinstance(key, Entity):
components = self._entities[key]
del self._entities[key]
del self._resources[key.storage]
@overload
- def __contains__[T](self, item: Resource[T]) -> bool: ...
+ def __contains__[T](self, item: Resource2[T]) -> bool: ...
@overload
def __contains__(self, item: Entity) -> bool: ...
- def __contains__[T](self, item: Entity | Resource[T]) -> bool:
- if isinstance(item, Resource):
+ def __contains__[T](self, item: Entity | Resource2[T]) -> bool:
+ if isinstance(item, Resource2):
return item.storage in self._resources
return item in self._entities
- def _run_cond(self, _: System | SystemSet) -> bool:
+ def _run_cond(self, _: RawSystem | SystemSet) -> bool:
return True
def tick(self, schedule: ScheduleLabel) -> None:
system(self)
def res_s[T](self, ty: type[T]) -> T:
- if Resource(ty) not in self:
+ if Resource2(ty) not in self:
raise SkipSystemError()
- return self[Resource(ty)]
+ return self[Resource2(ty)]
def run(
self,
def with_systems(
self,
schedule: ScheduleLabel,
- *systems: "System | Systems",
+ *systems: "RawSystem | Systems",
sets: set[SystemSet] | None = None,
) -> "World":
if sets is None:
def run_main(self) -> None:
self.run(
- MainSchedule, lambda world: Resource(WorldShouldExit) in world
+ MainSchedule, lambda world: Resource2(WorldShouldExit) in world
)
class Systems:
- def __init__(self, *systems: "System | SystemSet | Systems") -> None:
+ def __init__(self, *systems: "RawSystem | SystemSet | Systems") -> None:
self._systems = systems
self._chain = False
- self._pre: list[System | SystemSet] = []
- self._post: list[System | SystemSet] = []
+ self._pre: list[RawSystem | SystemSet] = []
+ self._post: list[RawSystem | SystemSet] = []
self._in_set: list[SystemSet] = []
def chain(self) -> "Systems":
self._chain = True
return self
- def after(self, *systems: System | SystemSet) -> "Systems":
+ def after(self, *systems: RawSystem | SystemSet) -> "Systems":
self._pre.extend(systems)
return self
- def before(self, *systems: System | SystemSet) -> "Systems":
+ def before(self, *systems: RawSystem | SystemSet) -> "Systems":
self._post.extend(systems)
return self
self._in_set.extend(sets)
return self
- def _apply(self, sched: Schedule[System]) -> SystemSet:
+ def _apply(self, sched: Schedule[RawSystem]) -> SystemSet:
global _order_groups
_order_groups += 1
label = SystemSet(f"order-group-{_order_groups}")
)
sched.add_ordering((label,), self._pre, self._post, self._in_set)
if self._chain:
- prev: tuple[SystemSet | System] | tuple[()] = ()
+ prev: tuple[SystemSet | RawSystem] | tuple[()] = ()
for system in systems:
sched.add_ordering((system,), prev, (), (label,))
prev = (system,)
--- /dev/null
+# ruff: noqa: ANN401
+
+from collections.abc import Iterable
+from typing import (
+ Any,
+ get_args,
+ get_origin,
+)
+
+
+def map_type_generics(ty: Any, mapping: dict[Any, type]) -> Any:
+ """Map generics for this type, maybe creating a new type."""
+ if ty in mapping:
+ return mapping[ty]
+ if (orig := get_origin(ty)) is None:
+ return ty
+ args = tuple(map_type_generics(e, mapping) for e in get_args(ty))
+ if hasattr(ty, "copy_with") and callable(ty.copy_with):
+ return ty.copy_with(args)
+ return orig[args]
+
+
+def resolve_type_aliases(ty: Any) -> Any:
+ """Resolve any type aliases for a given type, recursively."""
+ while hasattr(ty, "__value__"):
+ if not (
+ hasattr(ty, "__type_params__")
+ and hasattr(ty, "__args__")
+ and isinstance(ty.__args__, Iterable)
+ ):
+ return ty.__value__
+ mapping = dict(zip(ty.__type_params__, ty.__args__, strict=True))
+ ty = map_type_generics(ty.__value__, mapping)
+ return map_type_generics(
+ ty, {e: resolve_type_aliases(e) for e in get_args(ty)}
+ )
+
+
+type B[T] = list[list[T]]
+
+type A[T] = list[tuple[T, B[int]]]
+
+if __name__ == "__main__":
+ alp = A[str]
+ print(alp)
+ al = resolve_type_aliases(alp)
+ print(al)
+ al2 = list[tuple[str, list[list[int]]]]
+ print(al2)
+ al3 = resolve_type_aliases(al2)
+ print(al3)
+ print(al == al2)
--- /dev/null
+from collections.abc import Callable
+
+type VarargCallable[T, U] = (
+ Callable[[T], U]
+ | Callable[[T, T], U]
+ | Callable[[T, T, T], U]
+ | Callable[[T, T, T, T], U]
+ | Callable[[T, T, T, T, T], U]
+ | Callable[[T, T, T, T, T, T], U]
+ | Callable[[T, T, T, T, T, T, T], U]
+ | Callable[[T, T, T, T, T, T, T, T], U]
+ | Callable[[T, T, T, T, T, T, T, T, T], U]
+ | Callable[[T, T, T, T, T, T, T, T, T, T], U]
+ | Callable[[T, T, T, T, T, T, T, T, T, T, T], U]
+ | Callable[[T, T, T, T, T, T, T, T, T, T, T, T], U]
+ | Callable[[T, T, T, T, T, T, T, T, T, T, T, T, T], U]
+ | Callable[[T, T, T, T, T, T, T, T, T, T, T, T, T, T], U]
+ | Callable[[T, T, T, T, T, T, T, T, T, T, T, T, T, T, T], U]
+)
+type VarargTuple[T] = (
+ tuple[()]
+ | tuple[T]
+ | tuple[T, T]
+ | tuple[T, T, T]
+ | tuple[T, T, T, T]
+ | tuple[T, T, T, T, T]
+ | tuple[T, T, T, T, T, T]
+ | tuple[T, T, T, T, T, T, T]
+ | tuple[T, T, T, T, T, T, T, T]
+ | tuple[T, T, T, T, T, T, T, T, T]
+ | tuple[T, T, T, T, T, T, T, T, T, T]
+ | tuple[T, T, T, T, T, T, T, T, T, T, T]
+ | tuple[T, T, T, T, T, T, T, T, T, T, T, T]
+ | tuple[T, T, T, T, T, T, T, T, T, T, T, T, T]
+ | tuple[T, T, T, T, T, T, T, T, T, T, T, T, T, T]
+ | tuple[T, T, T, T, T, T, T, T, T, T, T, T, T, T, T]
+)