"""Spawn Tables.
Spawn tables are used to manage the relative frequency of certain content appearing in
the simulation.
"""
from __future__ import annotations
from typing import Any, TypedDict
import polars as pl
from neighborly.ecs import Component
[docs]class CharacterSpawnTableEntry(TypedDict):
"""Data for a single row in a CharacterSpawnTable."""
name: str
"""The name of an entry."""
spawn_frequency: int
"""The relative frequency that this entry should spawn relative to others."""
[docs]class CharacterSpawnTable(Component):
"""Manages the frequency that character defs are spawned."""
__slots__ = ("_table",)
_table: pl.DataFrame
"""Column names mapped to column data."""
def __init__(self, entries: list[CharacterSpawnTableEntry]) -> None:
"""
Parameters
----------
entries
Starting entries.
"""
super().__init__()
# The following line is type ignored since pl.from_dicts(...) expects a
# sequence of dict[str, Any]. Typed dict is not a subclass of that type since
# it does not use arbitrary keys. The Polars maintainers should update the
# type hints for Mapping[str, Any] to allow TypeDict usage.
self._table = pl.from_dicts(
entries, schema=[("name", str), ("spawn_frequency", int)] # type: ignore
)
@property
def table(self) -> pl.DataFrame:
"""Get the spawn table as a data frame."""
return self._table
def __len__(self) -> int:
return len(self._table)
[docs] def to_dict(self) -> dict[str, Any]:
return {}
[docs]class BusinessSpawnTableEntry(TypedDict):
"""A single row of data from a BusinessSpawnTable."""
name: str
"""The name of an entry."""
spawn_frequency: int
"""The relative frequency that this entry should spawn relative to others."""
max_instances: int
"""Max number of instances of the business that may exist."""
min_population: int
"""The minimum settlement population required to spawn."""
instances: int
"""The current number of active instances."""
[docs]class BusinessSpawnTable(Component):
"""Manages the frequency that business types are spawned"""
__slots__ = ("_table",)
_table: pl.DataFrame
"""Table data with entries."""
def __init__(self, entries: list[BusinessSpawnTableEntry]) -> None:
"""
Parameters
----------
entries
Starting entries.
"""
super().__init__()
# See comment in CharacterSpawnTable.__init__ for why this is type ignored
self._table = pl.from_dicts(
entries, # type: ignore
schema=[
("name", str),
("spawn_frequency", int),
("max_instances", int),
("min_population", int),
("instances", int),
],
)
@property
def table(self) -> pl.DataFrame:
"""Get the spawn table as a data frame."""
return self._table
[docs] def increment_count(self, name: str) -> None:
"""Increment the instance count for an entry.
Parameters
----------
name
The name of entry to update
"""
self._table = self._table.with_columns(
instances=pl.when(pl.col("name") == name)
.then(pl.col("instances") + 1)
.otherwise(pl.col("instances"))
)
[docs] def decrement_count(self, name: str) -> None:
"""Increment the instance count for an entry.
Parameters
----------
name
The name of entry to update
"""
self._table = self._table.with_columns(
instances=pl.when(pl.col("name") == name)
.then(pl.col("instances") - 1)
.otherwise(pl.col("instances"))
)
[docs] def to_dict(self) -> dict[str, Any]:
return {}
def __len__(self) -> int:
return len(self._table)
[docs]class ResidenceSpawnTableEntry(TypedDict):
"""Data for a single row in a ResidenceSpawnTable."""
name: str
"""The name of an entry."""
spawn_frequency: int
"""The relative frequency that this entry should spawn relative to others."""
required_population: int
"""The number of people that need to live in the district."""
is_multifamily: bool
"""Is this a multifamily residential building."""
instances: int
"""The number of instances of this residence type"""
max_instances: int
"""Max number of instances of the business that may exist."""
[docs]class ResidenceSpawnTable(Component):
"""Manages the frequency that residence types are spawned"""
__slots__ = ("_table",)
_table: pl.DataFrame
"""Column names mapped to column data."""
def __init__(self, entries: list[ResidenceSpawnTableEntry]) -> None:
"""
Parameters
----------
entries
Starting entries.
"""
super().__init__()
# See comment in CharacterSpawnTable.__init__ for why this is type ignored.
self._table = pl.from_dicts(
entries, # type: ignore
schema=[
("name", str),
("spawn_frequency", int),
("required_population", int),
("is_multifamily", bool),
("instances", int),
("max_instances", int),
],
)
@property
def table(self) -> pl.DataFrame:
"""Get the spawn table as a data frame."""
return self._table
[docs] def increment_count(self, name: str) -> None:
"""Increment the instance count for an entry.
Parameters
----------
name
The name of entry to update
"""
self._table = self._table.with_columns(
instances=pl.when(pl.col("name") == name)
.then(pl.col("instances") + 1)
.otherwise(pl.col("instances"))
)
[docs] def decrement_count(self, name: str) -> None:
"""Increment the instance count for an entry.
Parameters
----------
name
The name of entry to update
"""
self._table = self._table.with_columns(
instances=pl.when(pl.col("name") == name)
.then(pl.col("instances") - 1)
.otherwise(pl.col("instances"))
)
def __len__(self) -> int:
return len(self._table)
[docs] def to_dict(self) -> dict[str, Any]:
return {}