Enhance AI and simulation components with presence management and routing capabilities
ci / server (push) Failing after 3m39s
ci / client (push) Successful in 14s

- Introduced the `HSchool.Ai` project, responsible for routing, day plans, and presence management.
- Updated the `HSchool.Simulation` project to integrate with the new AI functionalities, improving decision-making and presence tracking.
- Added `travelMinutes` to room and territory definitions, ensuring accurate movement calculations within the simulation.
- Enhanced the `School` class to manage presence and implement empty-time skipping functionality.
- Updated documentation to reflect the new AI features and their impact on school simulation.
- Added tests for presence management and routing to ensure robust functionality and reliability.
This commit is contained in:
Leonid Pershin
2026-08-19 16:33:52 +03:00
parent c16c34a83c
commit 4137400621
50 changed files with 1978 additions and 57 deletions
+7 -4
View File
@@ -36,14 +36,15 @@ there is no UI on the server.
| `src/HSchool.Content` | JSONC defs, inheritance, patches, locales, map instance and connectivity. No Arch, no ASP.NET. |
| `src/HSchool.People` | Roster generation from catalog, map and seed. No Arch, no ASP.NET. |
| `src/HSchool.Schedule` | Timetable from curriculum, assignments and map. No Arch, no ASP.NET. |
| `src/HSchool.Ai` | Walk graph, day plans, duty rooms. No Arch, no ASP.NET, no `DateTime.Now`. |
| `src/HSchool.Simulation` | Schools, the game clock, the Arch ECS world. Holds a frozen catalog and map; no HTTP. |
| `src/HSchool.Server` | ASP.NET Core host: the menu API, the WebSocket endpoint, per-school workers, `mods/` and `saves/`. |
| `src/HSchool.ServiceDefaults` | Shared Aspire wiring: OpenTelemetry, health checks, service discovery, resilience. |
| `src/HSchool.AppHost` | Aspire orchestration: which resources run and how they find each other. |
| `src/HSchool.Client` | Vite + TypeScript UI: main menu, creation form, the school screen. |
Dependency direction is one-way: `Protocol ← Server → Simulation → People → Content`, and
`Schedule → Content`. Nothing in `Simulation` or `Content` knows about HTTP, and nothing in
Dependency direction is one-way: `Protocol ← Server → Simulation → Ai → People / Schedule → Content`.
Nothing in `Simulation` or `Content` knows about HTTP, and nothing in
`Protocol` knows about schools.
## Two channels, on purpose
@@ -80,7 +81,9 @@ touching a school itself.
## Schools
A `School` is one save: an id, a name, a `GameClock`, a frozen catalog, a map, a roster and an
Arch `World`. Pupils, staff and parents live in that world as entities; they do not walk yet.
Arch `World`. Pupils, staff and parents live in that world as entities. Presence is a component:
each person occupies a map node or is off campus. Empty-time skip jumps the clock to the next
work morning when the campus is empty and the day frame is closed.
`GameClock` moves while it is running, in fixed steps:
`realSeconds × gameMinutesPerRealSecond × speedMultiplier`. At the defaults that is 5 game minutes
@@ -107,7 +110,7 @@ intake). Pause and speed changes are written
too, but coalesced to at most one write per `MinSaveIntervalMilliseconds`: a client can send those
as fast as the socket allows, and each one is a file write on the school's own thread. Shutdown
always flushes, so a pause is never lost. The clock file also stores the
mod pack ids and the map layout; the catalog is loaded again from `mods/` on start. A missing
mod pack ids, the map layout and each person's place on that map; the catalog is loaded again from `mods/` on start. A missing
mod folder, a map that no longer validates, or a roster that no longer fits the map leaves the
files in place and that school unstarted.
+1 -1
View File
@@ -1,6 +1,6 @@
# Нарезка проектов (целевая)
Сейчас в коде: `Protocol ← Server → Simulation → People → Content`. Каталог не в Simulation (рядом с Arch)
Сейчас в коде: `Protocol ← Server → Simulation → Ai → People / Schedule → Content`. Каталог не в Simulation (рядом с Arch)
и не в Server (рядом с Kestrel) — парсер JSONC и проверка графа не знают, что такое tick.
## Зависимости
+32 -32
View File
@@ -12,61 +12,61 @@
## Задачи
- [ ] Новый проект `HSchool.Ai` рядом с `People` и `Schedule`: зависит на `Content`, `People` и
- [x] Новый проект `HSchool.Ai` рядом с `People` и `Schedule`: зависит на `Content`, `People` и
`Schedule`, без Arch и ASP.NET. Рядом — `tests/HSchool.Ai.Tests`
- [ ] `travelMinutes` на `RoomDef` и `TerritoryDef`; в `core` проставлены кабинет 0.5, коридор
- [x] `travelMinutes` на `RoomDef` и `TerritoryDef`; в `core` проставлены кабинет 0.5, коридор
1.5, лестница 1.5, вестибюль 1, двор 3
- [ ] Маршрутизация по `MapLayout.Links` — в библиотеке: матрица следующих шагов считается
- [x] Маршрутизация по `MapLayout.Links` — в библиотеке: матрица следующих шагов считается
**один раз** при загрузке школы, не на каждом шаге
- [ ] План дня человека — тоже в библиотеке: во сколько выходить, чтобы успеть, и когда уходить.
- [x] План дня человека — тоже в библиотеке: во сколько выходить, чтобы успеть, и когда уходить.
Часы, компоненты и мир туда не проникают
- [ ] Место человека — компонент в `World`: узел, остаток пути, минуты до выхода из узла.
- [x] Место человека — компонент в `World`: узел, остаток пути, минуты до выхода из узла.
Человек всегда в узле; рёбер как состояний нет
- [ ] Состояние «вне школы» — не удаление сущности: человек остаётся в списке и в карточке
- [ ] Приход и уход: у кого сегодня нет уроков, тот не приходит вовсе. Ученик и учитель — по
- [x] Состояние «вне школы» — не удаление сущности: человек остаётся в списке и в карточке
- [x] Приход и уход: у кого сегодня нет уроков, тот не приходит вовсе. Ученик и учитель — по
своим урокам, прочий штат — по дню школы
- [ ] Запас на дорогу свой у каждого: путь от двора плюс несколько минут из своего потока
- [x] Запас на дорогу свой у каждого: путь от двора плюс несколько минут из своего потока
случайности, с поправкой на черты
- [ ] Обязанность как место назначения: урок — кабинет из расписания, должность — комната из
- [x] Обязанность как место назначения: урок — кабинет из расписания, должность — комната из
`RoomDef.works`. Выбора действий пока нет
- [ ] Движение по тику: остаток минут в узле уменьшается, при нуле человек переходит в следующий
- [x] Движение по тику: остаток минут в узле уменьшается, при нуле человек переходит в следующий
узел пути
- [ ] Стабильный порядок обхода людей — собственный список, а не порядок сущностей в Arch
- [ ] Адаптер в `HSchool.Simulation`: собирает значения из компонентов, зовёт библиотеку,
- [x] Стабильный порядок обхода людей — собственный список, а не порядок сущностей в Arch
- [x] Адаптер в `HSchool.Simulation`: собирает значения из компонентов, зовёт библиотеку,
записывает ответ обратно. Горячий путь — движение — границу не пересекает
- [ ] Ориентир в `AGENTS.md`, `docs/architecture.md` и `docs/design/projects.md` знает про новый
- [x] Ориентир в `AGENTS.md`, `docs/architecture.md` и `docs/design/projects.md` знает про новый
проект
- [ ] Место, путь и назначение едут в файл часов; кого в файле нет, тот расставляется по
- [x] Место, путь и назначение едут в файл часов; кого в файле нет, тот расставляется по
обязанности
- [ ] Потолок решений за тик — в `SimulationOptions`, как очередь, а не как отсечка
- [ ] Предикат «школа пуста»: в здании и во дворе нет никого
- [ ] Рабочее окно дня — от шести утра до последнего звонка по **каркасу дня**, а не по
- [x] Потолок решений за тик — в `SimulationOptions`, как очередь, а не как отсечка
- [x] Предикат «школа пуста»: в здании и во дворе нет никого
- [x] Рабочее окно дня — от шести утра до последнего звонка по **каркасу дня**, а не по
фактическому расписанию
- [ ] «Ближайшее шесть утра рабочего дня впереди»: ночь ведёт в то же утро, вечер — в следующее,
- [x] «Ближайшее шесть утра рабочего дня впереди»: ночь ведёт в то же утро, вечер — в следующее,
выходные, праздники и каникулы пропускаются. Поиск ограничен разумным числом дней и
честно отвечает «нет такого»
- [ ] Прыжок часов туда одним шагом: разрешён только при пустой школе и вне рабочего окна,
- [x] Прыжок часов туда одним шагом: разрешён только при пустой школе и вне рабочего окна,
отрабатывает первое сентября и обновление пула за весь пропуск, после себя пишет сейв
## Тесты, без которых фаза не закрыта
- [ ] Путь из кабинета 201 в санузел первого этажа идёт через лестницу и коридор, а не напрямую
- [ ] Сумма `travelMinutes` совпадает с временем, за которое человек реально доходит
- [ ] В воскресенье и в каникулы школа пуста: все вне школы
- [ ] Класс без уроков сегодня не приходит, а его учитель с уроками у другого класса — приходит
- [ ] К началу первого урока ученик находится в своём кабинете
- [ ] Физкультура после урока в главном корпусе: класс доходит до спортзала, и видно, что
- [x] Путь из кабинета 201 в санузел первого этажа идёт через лестницу и коридор, а не напрямую
- [x] Сумма `travelMinutes` совпадает с временем, за которое человек реально доходит
- [x] В воскресенье и в каникулы школа пуста: все вне школы
- [x] Класс без уроков сегодня не приходит, а его учитель с уроками у другого класса — приходит
- [x] К началу первого урока ученик находится в своём кабинете
- [x] Физкультура после урока в главном корпусе: класс доходит до спортзала, и видно, что
перемены на это едва хватает
- [ ] Тот же сид и та же карта дают то же расположение людей на тот же момент
- [ ] Сохранение и загрузка посреди перемены не телепортируют людей
- [ ] Прыжок с субботы попадает в понедельник, 6:00; с трёх ночи вторника — в утро того же
- [x] Тот же сид и та же карта дают то же расположение людей на тот же момент
- [x] Сохранение и загрузка посреди перемены не телепортируют людей
- [x] Прыжок с субботы попадает в понедельник, 6:00; с трёх ночи вторника — в утро того же
вторника; с десяти вечера — в утро среды
- [ ] Прыжок в учебное время отклоняется. В семь утра рабочего дня, когда школа ещё пуста, —
- [x] Прыжок в учебное время отклоняется. В семь утра рабочего дня, когда школа ещё пуста, —
тоже: это уже рабочее окно
- [ ] У школы без единого учителя рабочий день не пропускается
- [ ] Прыжок через летние каникулы проводит первое сентября: классы перешли, выпуск состоялся,
- [x] У школы без единого учителя рабочий день не пропускается
- [x] Прыжок через летние каникулы проводит первое сентября: классы перешли, выпуск состоялся,
набор пришёл
- [ ] Промотанная неделя и прожитая неделя дают одно состояние школы
- [x] Промотанная неделя и прожитая неделя дают одно состояние школы
## Критерий готовности
+1 -1
View File
@@ -82,7 +82,7 @@
| Фаза | Статус | Зачем |
| --- | --- | --- |
| [18. Присутствие и ходьба](18-presence-walking.md) | | `HSchool.Ai`, место человека, маршруты, приход и уход |
| [18. Присутствие и ходьба](18-presence-walking.md) | | `HSchool.Ai`, место человека, маршруты, приход и уход |
| [19. Присутствие на экране](19-presence-screen.md) | ⬜ | Своё сообщение ~2 Гц, числа в дереве, статический снимок карты, пропуск пустого времени |
**Этап B — поведение.** Появляются нужды, действия и выбор между ними и обязанностью.