aboutsummaryrefslogtreecommitdiff
path: root/kennel
diff options
context:
space:
mode:
authorElizabeth Hunt <elizabeth@simponic.xyz>2024-09-07 20:20:07 -0700
committerElizabeth Hunt <elizabeth@simponic.xyz>2024-09-12 17:23:30 -0700
commit8ec7f5368232d59f344e1067e1bad5e48dbcb7ae (patch)
tree1ad2df4dc00773f2307d1525cc80ac7410ea8fba /kennel
parente4e31978bae7e45be57b376415a4b925ac8cbc03 (diff)
downloadkennel.hatecomputers.club-8ec7f5368232d59f344e1067e1bad5e48dbcb7ae.tar.gz
kennel.hatecomputers.club-8ec7f5368232d59f344e1067e1bad5e48dbcb7ae.zip
get "cats" up there
Diffstat (limited to 'kennel')
-rw-r--r--kennel/config.py2
-rw-r--r--kennel/engine/components/component.py1
-rw-r--r--kennel/engine/components/markov_transition_state.py24
-rw-r--r--kennel/engine/entities/cat.py14
-rw-r--r--kennel/engine/entities/entity.py1
-rw-r--r--kennel/engine/systems/markov_transition_state_system.py13
-rw-r--r--kennel/engine/systems/system.py3
-rw-r--r--kennel/kennel.py81
-rw-r--r--kennel/kennelcats.py6
-rw-r--r--kennel/main.py3
10 files changed, 139 insertions, 9 deletions
diff --git a/kennel/config.py b/kennel/config.py
index a731c84..8a09500 100644
--- a/kennel/config.py
+++ b/kennel/config.py
@@ -17,5 +17,7 @@ class Config:
COOKIE_MAX_AGE = int(os.getenv("COOKIE_MAX_AGE", 60 * 60 * 24 * 7)) # 1 week
+ KENNEL_CATS_POLL_SEC = int(os.getenv("KENNEL_CATS_POLL_SEC", 10))
+
config = Config()
diff --git a/kennel/engine/components/component.py b/kennel/engine/components/component.py
index 9f56407..90506b9 100644
--- a/kennel/engine/components/component.py
+++ b/kennel/engine/components/component.py
@@ -5,6 +5,7 @@ from enum import Enum
class ComponentType(str, Enum):
POSITION = "POSITION"
CONTROLLABLE = "CONTROLLABLE"
+ MARKOV = "MARKOV"
class Component:
diff --git a/kennel/engine/components/markov_transition_state.py b/kennel/engine/components/markov_transition_state.py
new file mode 100644
index 0000000..cec8223
--- /dev/null
+++ b/kennel/engine/components/markov_transition_state.py
@@ -0,0 +1,24 @@
+from kennel.engine.components.component import Component, ComponentType
+
+
+class MarkovTransitionState(Component):
+ def __init__(
+ self,
+ state_names: dict[int, str],
+ initial_state_vector: list[float],
+ transition_matrix: list[list[float]],
+ ):
+ # TODO: Poll rate per state?
+ # TODO: State being an enum instead of a vector, just choose max and map
+ self.state_names = state_names
+ self.state = initial_state_vector
+ self.transition_matrix = transition_matrix
+
+ super().__init__(ComponentType.MARKOV)
+
+ def get_max_state_name(self, state_vector: list[float]):
+ max_val = max(state_vector)
+ return self.state_names[state_vector.index(max_val)]
+
+ def to_dict(self):
+ return {"state": self.get_max_state_name(self.state)}
diff --git a/kennel/engine/entities/cat.py b/kennel/engine/entities/cat.py
index 02fdb33..edcde37 100644
--- a/kennel/engine/entities/cat.py
+++ b/kennel/engine/entities/cat.py
@@ -1,6 +1,18 @@
+from kennel.engine.components.position import Position
+
+from .entity import Entity, EntityType
+
+
+class Cat(Entity):
+ def __init__(self, id: str):
+ components = [Position(0, 0)]
+
+ super().__init__(EntityType.CAT, id, components)
+
+
#
# # IDLE, FROLICKING, EEPY, ALERT, CHASING_CURSOR, CHASING_CAT, SCRATCHING, ITCHY
-# state_stochastic_matrix = [
+# state_stochastic_matrix = [ [1, 0]
# # IDLE
# [0.5, 0.1, 0.1, 0.1, 0.1, 0.05, 0.05, 0],
# # FROLICKING
diff --git a/kennel/engine/entities/entity.py b/kennel/engine/entities/entity.py
index d40ab19..30e325a 100644
--- a/kennel/engine/entities/entity.py
+++ b/kennel/engine/entities/entity.py
@@ -6,6 +6,7 @@ from kennel.engine.components.component import Component, ComponentType
class EntityType(str, Enum):
LASER = "LASER"
+ CAT = "CAT"
class Entity:
diff --git a/kennel/engine/systems/markov_transition_state_system.py b/kennel/engine/systems/markov_transition_state_system.py
new file mode 100644
index 0000000..ad3de65
--- /dev/null
+++ b/kennel/engine/systems/markov_transition_state_system.py
@@ -0,0 +1,13 @@
+from kennel.engine.components.component import ComponentType
+from kennel.engine.entities.entity import EntityManager
+from kennel.engine.systems.system import System, SystemType
+from kennel.engine.systems.network import NetworkSystem
+
+
+class MarkovTransitionStateSystem(System):
+ def __init__(self, network_system: NetworkSystem):
+ super().__init__(SystemType.MARKOV)
+
+ def update(self, entity_manager: EntityManager, delta_time: float):
+ entity_manager.get_entities_with_component(ComponentType.MARKOV)
+ return
diff --git a/kennel/engine/systems/system.py b/kennel/engine/systems/system.py
index e3ddb27..8c577a6 100644
--- a/kennel/engine/systems/system.py
+++ b/kennel/engine/systems/system.py
@@ -7,6 +7,7 @@ from kennel.engine.entities.entity import EntityManager
class SystemType(str, Enum):
NETWORK = "NETWORK"
WORLD = "WORLD"
+ MARKOV = "MARKOV"
class System:
@@ -14,7 +15,7 @@ class System:
self.system_type = system_type
@abstractmethod
- async def update(self, entity_manager: EntityManager, delta_time: float):
+ async def update(self, entity_manager: EntityManager, delta_time: float) -> None:
pass
diff --git a/kennel/kennel.py b/kennel/kennel.py
index b4d7b7b..7123b7f 100644
--- a/kennel/kennel.py
+++ b/kennel/kennel.py
@@ -1,18 +1,27 @@
+import asyncio
import uuid
+import time
from typing import List, Optional
from kennel.config import config
from kennel.engine.components.component import ComponentType
from kennel.engine.entities.entity import Entity, EntityManager
from kennel.engine.entities.laser import Laser
+from kennel.engine.entities.cat import Cat
from kennel.engine.game import Game
+from kennel.engine.systems.markov_transition_state_system import (
+ MarkovTransitionStateSystem,
+)
from kennel.engine.systems.network import (
EntityPositionUpdateEvent,
+ EntityBornEvent,
+ EntityDeathEvent,
Event,
EventType,
NetworkSystem,
UpstreamEventProcessor,
)
+from kennel.kennelcats import KennelCatService, KennelCat
from kennel.engine.systems.system import SystemManager
from kennel.engine.systems.world import WorldSystem
@@ -58,12 +67,76 @@ class KennelEventProcessor(UpstreamEventProcessor):
return event
-system_manager.add_system(WorldSystem(config.WORLD_WIDTH, config.WORLD_HEIGHT))
-system_manager.add_system(
- NetworkSystem(KennelEventProcessor()),
-)
+class KennelCatsManager:
+ kennel_cat_service: KennelCatService
+ entity_manager: EntityManager
+ network_system: NetworkSystem
+ last_seen: set[str]
+ poll_interval_sec: int
+ running: bool
+
+ def __init__(
+ self,
+ kennel_cat_service: KennelCatService,
+ entity_manager: EntityManager,
+ network_system: NetworkSystem,
+ poll_interval_sec: int,
+ ):
+ self.kennel_cat_service = kennel_cat_service
+ self.entity_manager = entity_manager
+ self.network_system = network_system
+ self.poll_interval_sec = poll_interval_sec
+
+ self.last_seen = set()
+ self.running = False
+
+ async def start(self) -> None:
+ logger.info("starting kennel cats manager")
+ if self.running:
+ return
+
+ self.running = True
+ while self.running:
+ logger.info("polling kennel cats service")
+ cats = self.kennel_cat_service.get_kennel()
+ cats_table = {cat.id: cat for cat in cats}
+ cat_ids = set([cat.id for cat in cats])
+
+ removed_cats = [cats_table[id] for id in self.last_seen.difference(cat_ids)]
+ added_cats = [cats_table[id] for id in cat_ids.difference(self.last_seen)]
+ logger.info(f"removing {removed_cats}")
+ logger.info(f"adding {added_cats}")
+
+ for removed in removed_cats:
+ self.entity_manager.remove_entity(removed)
+ entity_death = EntityDeathEvent(removed.id)
+ self.network_system.server_global_event(entity_death)
+
+ for added in added_cats:
+ new_cat = Cat(added.id)
+ self.entity_manager.add_entity(new_cat)
+ entity_born = EntityBornEvent(new_cat)
+ self.network_system.server_global_event(entity_born)
+
+ self.last_seen = cat_ids
+ await asyncio.sleep(self.poll_interval_sec)
+
+ def stop(self) -> None:
+ logger.info("stopping kennel cats manager")
+ self.running = False
+
+
+network_system = NetworkSystem(KennelEventProcessor())
+world_system = WorldSystem(config.WORLD_WIDTH, config.WORLD_HEIGHT)
+markov_transition_state_system = MarkovTransitionStateSystem(network_system)
+system_manager.add_system(network_system)
+system_manager.add_system(world_system)
kennel = Game(entity_manager, system_manager, config.MIN_TIME_STEP)
+kennel_cat_service = KennelCatService(config.HATECOMPUTERS_ENDPOINT)
+kennel_cats_manager = KennelCatsManager(
+ kennel_cat_service, entity_manager, network_system, config.KENNEL_CATS_POLL_SEC
+)
def create_session_controllable_entities(session: str) -> List[Entity]:
diff --git a/kennel/kennelcats.py b/kennel/kennelcats.py
index 970e44f..2cb2f29 100644
--- a/kennel/kennelcats.py
+++ b/kennel/kennelcats.py
@@ -37,10 +37,10 @@ class KennelCat:
class KennelCatService:
- def __init__(self, endpoint: str):
- self.endpoint = endpoint
+ def __init__(self, hc_endpoint: str):
+ self.hc_endpoint = hc_endpoint
def get_kennel(self) -> List[KennelCat]:
- response = requests.get(f"{self.endpoint}/kennel")
+ response = requests.get(f"{self.hc_endpoint}/kennel")
response.raise_for_status()
return [KennelCat.from_dict(cat) for cat in response.json()]
diff --git a/kennel/main.py b/kennel/main.py
index 22d3d39..49d62d4 100644
--- a/kennel/main.py
+++ b/kennel/main.py
@@ -30,6 +30,7 @@ from kennel.kennel import (
create_session_controllable_entities,
entity_manager,
kennel,
+ kennel_cats_manager,
system_manager,
)
@@ -43,12 +44,14 @@ loop = asyncio.get_event_loop()
async def startup_event():
logger.info("Starting Kennel...")
loop.create_task(kennel.run())
+ loop.create_task(kennel_cats_manager.start())
@app.on_event("shutdown")
async def shutdown_event():
logger.info("Stopping Kennel...")
kennel.stop()
+ kennel_cats_manager.stop()
loop.stop()
logger.info("Kennel stopped")