add shel
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
# Расписание
|
||||
|
||||
Договорённость на срез после штата, не текущий код.
|
||||
Штат и предметы: [`staffing.md`](staffing.md). Люди: [`people.md`](people.md).
|
||||
Типы и карта: [`defs.md`](defs.md). Экран: [`near-term.md`](near-term.md).
|
||||
|
||||
Часы из учебного плана раскладываются по дням и кабинетам. Школа впервые начинает **жить по
|
||||
времени**: в 10:15 у 5Б математика в кабинете 204, и это видно на карте.
|
||||
|
||||
## Зачем это раньше ходьбы
|
||||
|
||||
Расписание закрывает дыру, которая тянется с первого среза: в панели локации пустуют «Персонажи»
|
||||
и «Сейчас». Заполнить их можно **без системы перемещения** — если урок стоит в расписании, мы
|
||||
знаем, кто в кабинете и чем занят. Ни поиска пути, ни шагов по коридору: таблица и часы.
|
||||
|
||||
Люди по-прежнему не ходят. Они **находятся** там, где их застало расписание.
|
||||
|
||||
## Каркас дня
|
||||
|
||||
В `core`, дефом: первый урок в 8:30, до семи уроков, урок 45 минут, перемены по 10 с одной
|
||||
большой на 20. Одна смена.
|
||||
|
||||
Длина недели — **в настройках**, а не в контенте: пять дней по умолчанию. Это школьное правило,
|
||||
а не свойство мира, и его меняют, чтобы посмотреть, что будет.
|
||||
|
||||
Каникулы — дефами: осенние, зимние, весенние и лето, диапазонами дат внутри учебного года.
|
||||
В каникулы расписание не действует, школа стоит.
|
||||
|
||||
## Учебный план
|
||||
|
||||
Уже есть: `SubjectDef` знает, в каких параллелях преподаётся предмет и сколько часов в неделю.
|
||||
Расписание ничего не решает про количество — только про расстановку.
|
||||
|
||||
## Кто его строит
|
||||
|
||||
**Сервер, сам, детерминированно** — из учебного плана, назначений и карты. Игрок не раскладывает
|
||||
пасьянс на одиннадцать классов; он влияет тем, кого нанял и что назначил.
|
||||
|
||||
Но игрок **может править** готовое: передвинуть урок, поменять кабинет.
|
||||
|
||||
Отсюда правило, без которого правки будут пропадать: **исправленное игроком закрепляется.**
|
||||
Перестроение — а оно случается при каждом найме и назначении — сохраняет закреплённые уроки и
|
||||
пересобирает только остальное. Иначе первый же наём стёр бы всю ручную работу.
|
||||
|
||||
Снять закрепление игрок может явно; тогда урок снова участвует в общей раскладке.
|
||||
|
||||
## Правила раскладки
|
||||
|
||||
Четыре запрета, нарушать которые нельзя:
|
||||
|
||||
1. **Учитель не в двух местах.** Один человек — один урок в слоте.
|
||||
2. **Класс не в двух местах.** Один класс — один урок в слоте.
|
||||
3. **Кабинет не занят дважды.** Два класса в одной комнате одновременно невозможны.
|
||||
4. **Класс влезает в кабинет.** Учеников не больше, чем мест.
|
||||
|
||||
Урок идёт **не обязательно в своём кабинете**. У предмета может быть требование к типу помещения:
|
||||
физкультура — в спортзале, информатика — в компьютерном классе. Без требования урок идёт в
|
||||
кабинете, закреплённом за классом.
|
||||
|
||||
Это и делает карту нужной: один спортзал на одиннадцать классов — узкое место, которое видно в
|
||||
расписании, а не в отчёте.
|
||||
|
||||
## Некому вести — урока нет
|
||||
|
||||
Предмет без учителя не ставится вовсе. В расписании класса на этом месте пусто, а в разделе
|
||||
«Управление» предмет числится непокрытым.
|
||||
|
||||
Так наём получает прямое последствие: нанял человека, назначил ему предмет — в расписании
|
||||
появились уроки. Это единственная обратная связь среза, и она должна быть мгновенной.
|
||||
|
||||
То же самое, если часов больше, чем вытягивают нанятые: лишние часы просто не встают.
|
||||
|
||||
## Что видит игрок
|
||||
|
||||
**В дереве карты** — что происходит в комнате прямо сейчас, скобками рядом с названием:
|
||||
|
||||
```
|
||||
Этаж 2
|
||||
Кабинет 204 (Математика · 5Б)
|
||||
Кабинет 205 (Физика · 9А)
|
||||
Кабинет 206
|
||||
```
|
||||
|
||||
Так классы не сливаются в одинаковые строчки: комната подписана тем, что в ней идёт. Пусто —
|
||||
значит свободна.
|
||||
|
||||
**В панели локации** — «Сейчас» и «Персонажи» наконец не пустуют: урок, класс, учитель.
|
||||
|
||||
**В «Управлении»** — расписание класса: сетка «день × урок» на одну параллель.
|
||||
|
||||
**В карточке учителя** — его личное расписание: где он и когда. Это же карточка, что и в «Людях»,
|
||||
так что расписание видно всюду, где человек открывается.
|
||||
|
||||
Общей сетки на всю школу нет: одиннадцать классов на шесть дней не читаются с экрана, а нужны
|
||||
они только тому, кто раскладывает вручную, — а раскладывает сервер.
|
||||
|
||||
## Где это живёт
|
||||
|
||||
Планировщик — **отдельная библиотека**, как `HSchool.People`: зависит от `HSchool.Content`, не
|
||||
знает ни Arch, ни ASP.NET, отдаёт простую таблицу. Причина та же, что и с генератором людей: то,
|
||||
что строится по детерминированным правилам, проверяется только тестами — «этот штат и эта карта
|
||||
дают вот такую раскладку», — а тест не должен поднимать хост.
|
||||
|
||||
Расписание сохраняется вместе с составом: оно дешевле пересчитывается, чем восстанавливается, но
|
||||
ручные правки восстановить нельзя. Пишется по тем же правилам, что и ростер — при изменении, а не
|
||||
по таймеру.
|
||||
|
||||
## Советы, которые стоит принять сразу
|
||||
|
||||
- Не давать игроку раскладывать всё вручную. Автоматика с правками — это управление; пустая сетка
|
||||
на 66 ячеек — это работа завуча.
|
||||
- Не пересобирать расписание на каждый тик. Оно меняется от найма и назначения, а не от времени.
|
||||
- Не хранить «кто где сейчас» отдельным состоянием. Это вычисляется из расписания и часов; вторая
|
||||
копия немедленно разойдётся с первой.
|
||||
- Не ставить урок, который некому вести. Дыра в расписании честнее, чем урок без учителя.
|
||||
- Требование к типу помещения — у предмета, а не у комнаты. Комната не знает, что в ней физкультура.
|
||||
|
||||
## Заведомо не сейчас
|
||||
|
||||
- Перемещение по карте: люди находятся в комнате, но не идут в неё.
|
||||
- Оценки, посещаемость, домашние задания, успеваемость.
|
||||
- Замены заболевшего учителя.
|
||||
- Вторая смена, деление класса на подгруппы (языки, труд).
|
||||
- Четверти, триместры, экзамены.
|
||||
- Кружки, продлёнка, всё вне уроков.
|
||||
- Предпочтения учителей: «не ставьте мне первым уроком».
|
||||
|
||||
## Зафиксировано этим разговором
|
||||
|
||||
| Тема | Решение |
|
||||
| --- | --- |
|
||||
| Кто строит | Сервер, детерминированно, из плана и назначений |
|
||||
| Правки игрока | Можно; исправленное закрепляется и переживает перестроение |
|
||||
| Каркас дня | `core`: 8:30, до 7 уроков, 45 минут, перемены 10 и одна 20; одна смена |
|
||||
| Длина недели | В настройках, пять дней по умолчанию |
|
||||
| Каникулы | Дефами, диапазонами дат; в каникулы расписания нет |
|
||||
| Учебный план | Часы из `SubjectDef`, расписание их только расставляет |
|
||||
| Запреты | Учитель, класс и кабинет — не более одного урока в слоте; класс влезает в кабинет |
|
||||
| Место урока | Кабинет класса, если у предмета нет требования к типу помещения |
|
||||
| Нет учителя | Урок не ставится; предмет числится непокрытым |
|
||||
| Карта | В дереве рядом с комнатой — что и у кого там сейчас |
|
||||
| Панель локации | «Сейчас» и «Персонажи» заполняются из расписания |
|
||||
| Расписание класса | В разделе «Управление», сетка «день × урок» |
|
||||
| Расписание учителя | В его карточке, там же где навыки и семья |
|
||||
| Общая сетка школы | Не делаем: её читает только тот, кто раскладывает вручную |
|
||||
| Где живёт код | Отдельная библиотека рядом с `HSchool.People` |
|
||||
| Люди и место | Находятся по расписанию; ходьбы нет |
|
||||
@@ -0,0 +1,226 @@
|
||||
# Наём, предметы и бюджет
|
||||
|
||||
Договорённость на следующий срез, не текущий код.
|
||||
Люди: [`people.md`](people.md). Типы и карта: [`defs.md`](defs.md).
|
||||
Экран игрока: [`near-term.md`](near-term.md). Проекты: [`projects.md`](projects.md).
|
||||
|
||||
Первый срез, где игрок **что-то решает**. До сих пор школа собиралась сама: генератор закрывал
|
||||
все должности, игрок смотрел. Теперь школа открывается пустой — без единого учителя, — и её
|
||||
надо укомплектовать: нанять людей из пула соискателей, раздать им предметы и уложиться в
|
||||
выделенные деньги.
|
||||
|
||||
Уроков по-прежнему нет. Предмет — это то, что учитель **числится** вести, а не то, что происходит
|
||||
в кабинете. Расписание, звонки и оценки — следующий разговор.
|
||||
|
||||
## Что меняется в уже принятом
|
||||
|
||||
Три решения прошлых срезов приходится поправить. Это не мелочь, поэтому названы отдельно.
|
||||
|
||||
| Было | Стало | Почему |
|
||||
| --- | --- | --- |
|
||||
| Генератор закрывает все должности | Школа стартует без сотрудников | Иначе игроку нечего решать |
|
||||
| Кабинет — таблица именованных слотов | Кабинет — число мест | Никто не будет выбирать, какой стул у доски |
|
||||
| `PETeacher` — отдельная должность | Должность одна, физкультура — предмет | Иначе на каждый предмет заводится должность |
|
||||
|
||||
## Мебель и вместимость
|
||||
|
||||
Вместимость по-прежнему **физическая**: в классе не может сидеть больше учеников, чем стоит парт.
|
||||
Это не меняется — от неё зависит весь ростер.
|
||||
|
||||
Меняется то, как эта мебель задаётся. У кабинета исчезает таблица именованных слотов — «доска»,
|
||||
«стол учителя», «стул учителя», «парты» — и остаётся одно число: сколько парт в кабинете.
|
||||
Выбирать между стулом и креслом у доски игрок не будет никогда, а таблица из четырёх строк на
|
||||
каждый из одиннадцати кабинетов делает редактор нечитаемым.
|
||||
|
||||
Именованные слоты **остаются** там, где обстановка действительно разная: столовая, медкабинет,
|
||||
кабинет директора, библиотека. Там выбор осмыслен — сколько столов, какая кушетка.
|
||||
|
||||
Правило простое: **слоты — там, где есть из чего выбирать; число — там, где важно только сколько.**
|
||||
|
||||
## Предметы
|
||||
|
||||
Новый вид def — `SubjectDef`. Не навык и не работа: навык — это то, что растёт у ребёнка, работа —
|
||||
то, чем занят человек в комнате, а предмет — то, что стоит в дипломе и в нагрузке.
|
||||
|
||||
`SubjectDef` описывает:
|
||||
|
||||
- **какие навыки развивает и насколько** — список пар «навык + доля»;
|
||||
- **в каких параллелях преподаётся**;
|
||||
- **сколько часов в неделю** в каждой из них — учебный план.
|
||||
|
||||
Часы здесь не задел на расписание, а рабочая величина уже этого среза: из них считается нагрузка
|
||||
учителя, а из нагрузки — зарплата. Расписание ([`schedule.md`](schedule.md)) только расставит эти
|
||||
часы по дням; сколько их — решает учебный план.
|
||||
|
||||
Так «начальные классы» получаются естественно: один предмет, который развивает всё понемногу и
|
||||
преподаётся с первой по четвёртую параллель. Дальше идут предметники: математика с пятой по
|
||||
одиннадцатую, развивает один навык, но сильно.
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"defName": "PrimarySchool",
|
||||
"grades": { "min": 1, "max": 4 },
|
||||
"hoursPerWeek": 20,
|
||||
"skills": [
|
||||
{ "skill": "Mathematics", "share": 0.3 },
|
||||
{ "skill": "RussianLanguage", "share": 0.3 },
|
||||
{ "skill": "Literature", "share": 0.2 },
|
||||
{ "skill": "Biology", "share": 0.2 },
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
Имена предметов и навыков **намеренно совпадают** там, где это одно и то же слово: `SubjectDef`
|
||||
`Mathematics` и `SkillDef` `Mathematics` — разные def'ы разных видов, но подпись у них одна, потому
|
||||
что локали ключуются по имени. Это то, что нужно: игрок не должен видеть «Математика (предмет)».
|
||||
|
||||
Доли не обязаны давать в сумме единицу — это не проценты, а вес влияния. Ни на что в этом срезе
|
||||
они не влияют; они здесь потому, что предмет без ответа на вопрос «что он развивает» не проверишь,
|
||||
а дописывать поле позже — значит переписывать весь ванильный контент.
|
||||
|
||||
## Учитель
|
||||
|
||||
Должность одна — `Teacher`. Предмет не в должности, а в **назначении**: у нанятого учителя
|
||||
один предмет или несколько.
|
||||
|
||||
Отсюда следует важное: **потребность в учителях больше не берётся из комнат.** Раньше кабинет
|
||||
открывал вакансию «учитель», спортзал — «физрук». Теперь кабинет не открывает ничего: он лишь
|
||||
место, где идёт урок. Школе нужны не «люди в комнатах», а **покрытие предметов по параллелям** —
|
||||
на каждый предмет, преподаваемый в существующих параллелях, нужен хотя бы один учитель.
|
||||
|
||||
Нетренерские должности — директор, секретарь, медсестра, библиотекарь, повар — по-прежнему рождаются
|
||||
из комнат, как описано в [`defs.md`](defs.md). Их игрок тоже нанимает, но предметов им не назначают.
|
||||
|
||||
## Соискатели
|
||||
|
||||
Пул соискателей — **общий** список на школу, не «по трое на вакансию».
|
||||
|
||||
Соискатель — **полноценный человек** со всеми слоями: тело, навыки, черты, семья. Иначе выбирать
|
||||
не из чего: весь смысл в том, чтобы сравнить «математика 78, усидчивый» с «математика 41,
|
||||
вспыльчивый».
|
||||
|
||||
Соискателем **может оказаться родитель ученика** — и это не частный случай, а естественное
|
||||
следствие уже принятого: человек может быть в двух ролях сразу. Нанятый родитель становится
|
||||
работником, оставаясь родителем, и никакой второй сущности для него не заводится.
|
||||
|
||||
Пул **обновляется раз в неделю и не целиком**. Часть людей уходит — нашли работу в другом месте, —
|
||||
часть остаётся искать дальше, приходят новые. Полная замена раз в неделю превращала бы выбор в
|
||||
лотерею: понравившийся кандидат исчезал бы, пока игрок думает.
|
||||
|
||||
Не нанятый и не ушедший остаётся тем же человеком: с тем же именем, теми же навыками и тем же
|
||||
запросом по зарплате. Обновление детерминировано от сида школы и номера недели.
|
||||
|
||||
## Деньги
|
||||
|
||||
**Баланса нет.** Сверху выделяют фиксированную сумму на зарплаты в месяц — не на счёт, а как
|
||||
предел. Ничего не копится, сэкономленное не переносится.
|
||||
|
||||
Значит и правило одно: **месячный фонд оплаты не может превысить выделенную сумму.** Выделили
|
||||
10 000, наняли одного за 3 000 и второго за 7 000 — всё, больше никого. Не «ушли в минус», не
|
||||
«долг», а просто нельзя.
|
||||
|
||||
### Сколько просит человек
|
||||
|
||||
Соискатель называет **цену часа**. Она складывается из двух вещей:
|
||||
|
||||
- **навыки** — сильный предметник стоит дороже слабого;
|
||||
- **черты** — самоуверенный просит больше, зажатый меньше, при тех же навыках.
|
||||
|
||||
Черта, влияющая на запрос, — это поле у `TraitDef`, а не список фамилий в коде. Мод, добавивший
|
||||
«скромный», сразу получает более дешёвого кандидата.
|
||||
|
||||
### Сколько он в итоге стоит
|
||||
|
||||
Месячная зарплата — **цена часа × недельная нагрузка**. Нагрузка не выдумывается: она следует из
|
||||
учебного плана. Назначили человеку математику — он забирает все часы математики во всех
|
||||
параллелях, где она преподаётся, и это может оказаться много.
|
||||
|
||||
Отсюда два ограничения вместо одного:
|
||||
|
||||
- **Деньги.** Фонд оплаты не может превысить выделенную сумму.
|
||||
- **Человек.** Никто не может вести больше `MaxWeeklyHours` часов в неделю. Если часов предмета
|
||||
больше, чем вытянет один, нужен второй учитель того же предмета — часы делятся между ними.
|
||||
|
||||
Поэтому «нанять посильнее» и «нагрузить одного вместо двоих» — два разных способа потратить один
|
||||
фонд, и в этом весь выбор среза. Дешёвый учитель, взявший три предмета, упрётся в часы; дорогой
|
||||
с одним предметом съест фонд.
|
||||
|
||||
Цифры — шкала цены часа, влияние черт, предел часов — живут в настройках и в `core`, не в коде.
|
||||
|
||||
## Старт в выходной
|
||||
|
||||
Школа создаётся пустой, и первое, что делает игрок, — нанимает людей. Делать это под тикающие
|
||||
часы учебного дня незачем, поэтому дата по умолчанию переезжает с вторника **3 апреля 2012** на
|
||||
субботу **31 марта 2012**: у игрока есть выходные, чтобы собрать штат до того, как в понедельник
|
||||
придут дети.
|
||||
|
||||
Учебный год при этом тот же, ростер не меняется.
|
||||
|
||||
## Экран
|
||||
|
||||
Не третья вкладка в панели и не модальное окно. Под часами появляется **два верхних раздела**:
|
||||
|
||||
| Раздел | Что в нём |
|
||||
| --- | --- |
|
||||
| **Обзор** | Всё, что есть сейчас: карта / люди слева, просмотр выбранного справа |
|
||||
| **Управление** | Деньги, соискатели, штат и назначение предметов |
|
||||
|
||||
Верхние разделы, а не вкладки внутри панели, потому что управление — это другой режим работы, а
|
||||
не ещё один список. Часы и дата остаются на месте в обоих: наём идёт по игровому времени, пул
|
||||
обновляется по неделям, и прятать календарь на время найма нельзя.
|
||||
|
||||
Внутри «Управления» та же схема, что и в «Обзоре»: список слева, карточка справа. Карточка
|
||||
соискателя — та же карточка человека, что и в «Людях»; он и есть человек, просто пока не ваш.
|
||||
|
||||
Сверху — одна строка про деньги: выделено, расписано, свободно. Ниже — непокрытые предметы,
|
||||
соискатели и нанятый штат.
|
||||
|
||||
## Советы, которые стоит принять сразу
|
||||
|
||||
- Не заводить должность на каждый предмет. Иначе `PositionDef` превращается в справочник
|
||||
специальностей, а назначение предмета становится увольнением и повторным наймом.
|
||||
- Не делать баланс, пока сверху приходит фиксированная сумма. Баланс — это уже экономика:
|
||||
накопления, долги, проценты. Здесь нужен предел, а предел — одно число.
|
||||
- Не обновлять пул целиком. Кандидат, исчезнувший, пока игрок его разглядывал, читается как баг.
|
||||
- Не выкидывать не нанятых людей в ростер школы. Соискатель — не сотрудник и не житель школы;
|
||||
в ростер он попадает только при найме.
|
||||
- Проверять предел в момент действия, а не в конце месяца. «Нельзя нанять» игрок понимает,
|
||||
«в конце месяца что-то случилось» — нет.
|
||||
|
||||
## Заведомо не сейчас
|
||||
|
||||
- Само расписание: когда именно идут эти часы. Следующий срез, [`schedule.md`](schedule.md).
|
||||
Здесь предмет — нагрузка в часах, а не событие в кабинете.
|
||||
- Уроки, звонки, оценки, посещаемость.
|
||||
- Классное руководство: класс закреплён за кабинетом, но не за учителем.
|
||||
- Увольнение и уход по собственному желанию.
|
||||
- Переговоры о зарплате, повышения, стаж.
|
||||
- Любые траты, кроме зарплат: ремонт, закупки, коммуналка.
|
||||
- Накопление денег, долги, штрафы.
|
||||
- Требования к диплому: любой нанятый может вести любой предмет, вопрос только в его навыке.
|
||||
|
||||
## Зафиксировано этим разговором
|
||||
|
||||
| Тема | Решение |
|
||||
| --- | --- |
|
||||
| Старт школы | Без сотрудников вообще; игрок нанимает всех |
|
||||
| Стартовая дата | Суббота 31 марта 2012 — выходные на сборку штата |
|
||||
| Вместимость класса | По-прежнему физическая: не больше учеников, чем парт |
|
||||
| Кабинет | Одно число мест вместо таблицы именованных слотов |
|
||||
| Слоты | Остаются там, где обстановка разная: столовая, медкабинет, кабинет директора |
|
||||
| Должность учителя | Одна, `Teacher`; `PETeacher` упраздняется |
|
||||
| Предмет | Новый `SubjectDef`: какие навыки развивает и в каких параллелях |
|
||||
| Начальные классы | Предмет, развивающий всё понемногу, параллели 1–4 |
|
||||
| Назначение | У учителя один предмет или несколько |
|
||||
| Потребность в учителях | Покрытие предметов по параллелям, а не вакансии в комнатах |
|
||||
| Прочие должности | По-прежнему из комнат; игрок нанимает и их |
|
||||
| Соискатель | Полноценный человек; может быть родителем ученика |
|
||||
| Пул | Общий, обновляется раз в неделю частично, детерминированно |
|
||||
| Учебный план | Часы в неделю по параллелям — поле `SubjectDef`, а не задел |
|
||||
| Запрос по деньгам | Цена часа: от навыков и от черт (самоуверенный просит больше) |
|
||||
| Зарплата | Цена часа × недельная нагрузка из учебного плана |
|
||||
| Предел нагрузки | `MaxWeeklyHours` на человека; сверх — нужен второй учитель предмета |
|
||||
| Бюджет | Фиксированная сумма в месяц из настроек; баланса нет |
|
||||
| Предел | Фонд оплаты не может превысить выделенное; проверка в момент действия |
|
||||
| Экран | Два верхних раздела под часами: «Обзор» и «Управление» |
|
||||
| Внутри управления | Список слева, карточка человека справа — та же, что в «Людях» |
|
||||
@@ -0,0 +1,39 @@
|
||||
# Фаза 10. Предметы и мебель
|
||||
|
||||
## Зависимости
|
||||
|
||||
- [Фаза 5](05-people-defs.md)
|
||||
- [Фаза 7](07-people-in-school.md)
|
||||
|
||||
## Зачем
|
||||
|
||||
Каталог учится описывать предметы, кабинет перестаёт быть таблицей слотов, а должность учителя
|
||||
становится одной на всех. Людей это ещё не двигает: школа собирается как прежде.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [x] `SubjectDef` в `HSchool.Content`: развиваемые навыки с долями, диапазон параллелей;
|
||||
ссылка на неизвестный навык — ошибка загрузки
|
||||
- [x] Ванильные предметы в `core`: начальные классы (1–4, всё понемногу) и предметники по
|
||||
существующим навыкам, включая физкультуру
|
||||
- [x] `PositionDef PETeacher` упраздняется; `Classroom`, `ComputerLab` и `GymHall` больше не
|
||||
открывают учительскую должность
|
||||
- [x] Кабинет-`Homeroom` описывается числом мест, а не таблицей именованных слотов; вместимость
|
||||
по-прежнему считается по мебели
|
||||
- [x] Слоты остаются у столовой, медкабинета, кабинета директора и библиотеки
|
||||
- [x] Редактор карты показывает у кабинета одно поле «мест», у прочих комнат — прежние слоты
|
||||
- [x] Ванильная карта переписана под новую форму, вместимость параллелей не изменилась
|
||||
- [x] `GET /api/catalog` отдаёт предметы — они понадобятся форме назначения
|
||||
- [x] Стартовая дата по умолчанию — суббота 31 марта 2012
|
||||
- [x] Снимок карты и `docs/protocol.md` правятся в том же коммите, если меняется раскладка узла
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- Каталог `core` грузится, предметы проходят валидацию, тест на полноту локалей зелёный
|
||||
- Школа на ванильной карте даёт те же одиннадцать классов и ту же вместимость, что и до фазы
|
||||
- Редактор карты не показывает у кабинета ни доски, ни стула учителя
|
||||
- `dotnet test` и клиентские `npm test` / `build` проходят
|
||||
|
||||
## Стоп
|
||||
|
||||
Не трогать наём. Генератор пока закрывает должности сам — школа опустеет в следующей фазе.
|
||||
@@ -0,0 +1,44 @@
|
||||
# Фаза 11. Пустая школа и пул соискателей
|
||||
|
||||
## Зависимости
|
||||
|
||||
- [Фаза 10](10-subjects.md)
|
||||
- [Фаза 6](06-people-generator.md)
|
||||
|
||||
## Зачем
|
||||
|
||||
Школа открывается без единого сотрудника, а рядом появляется пул людей, готовых наняться.
|
||||
Нанимать пока нечем — это фаза про данные.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] `RosterGenerator` больше не закрывает должности: новая школа получает учеников, родителей
|
||||
и ноль сотрудников
|
||||
- [ ] Пул соискателей в `HSchool.People`: список полноценных `Person`, детерминированный от сида
|
||||
школы и номера недели
|
||||
- [ ] Соискателем может стать родитель ученика — тот же человек, без второй сущности
|
||||
- [ ] Базовый запрос по зарплате считается из навыков соискателя; шкала — в данных, не в коде
|
||||
- [ ] Еженедельное обновление: часть уходит, часть остаётся с теми же именами и запросом,
|
||||
приходят новые. Полная замена запрещена
|
||||
- [ ] Пул живёт в `saves/{id}.people.json` рядом с ростером и пишется по тем же правилам —
|
||||
при изменении состава, не по таймеру
|
||||
- [ ] Воркер публикует пул вместе со снимком ростера
|
||||
|
||||
## Тесты, без которых фаза не закрыта
|
||||
|
||||
- [ ] Тот же сид и та же неделя — тот же пул
|
||||
- [ ] После обновления часть людей та же самая, с теми же навыками и тем же запросом
|
||||
- [ ] Пул не пустеет и не растёт бесконечно за пятьдесят недель
|
||||
- [ ] Запрос по зарплате у сильного кандидата выше, чем у слабого
|
||||
- [ ] Новая школа не имеет ни одного сотрудника, но все ученические места заняты
|
||||
- [ ] Соискатель не попадает в ростер школы, пока не нанят
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- Создать школу — в списке людей есть ученики и родители, работников нет
|
||||
- Перезапуск сервера поднимает тот же пул
|
||||
- `dotnet test` проходит
|
||||
|
||||
## Стоп
|
||||
|
||||
Ни найма, ни денег, ни назначения предметов. Пул только существует и обновляется.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Фаза 12. Наём, предметы и предел бюджета
|
||||
|
||||
## Зависимости
|
||||
|
||||
- [Фаза 11](11-applicants.md)
|
||||
|
||||
## Зачем
|
||||
|
||||
Игрок нанимает людей и раздаёт предметы, а выделенная сумма ограничивает, сколько он может себе
|
||||
позволить. Экрана ещё нет — всё через API.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] Выделяемая на месяц сумма — в `SimulationOptions`, с умолчанием
|
||||
- [ ] Фонд оплаты школы: сумма базовых ставок плюс надбавки за предметы сверх первого
|
||||
- [ ] Наём: соискатель уходит из пула, становится работником, попадает в ростер. Родитель
|
||||
остаётся родителем — новой сущности не заводится
|
||||
- [ ] Назначение предмета нанятому и снятие предмета
|
||||
- [ ] Предел проверяется **в момент действия**: наём или назначение, выводящее фонд за
|
||||
выделенную сумму, отклоняется с внятным кодом ошибки, а не откладывается до конца месяца
|
||||
- [ ] Покрытие предметов: какие предметы преподаются в существующих параллелях и не имеют ни
|
||||
одного учителя
|
||||
- [ ] `GET /api/schools/{id}/staffing` — деньги, покрытие, пул и штат; читает опубликованный
|
||||
снимок, воркер не трогает
|
||||
- [ ] Наём и назначение идут в воркер через мейлбокс с `TaskCompletionSource`, как create и delete
|
||||
- [ ] Состав пишется на диск при изменении, снимок публикуется заново
|
||||
- [ ] `docs/protocol.md` пополняется в том же коммите, что и обработчики
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- Нанять двоих на 3 000 и 7 000 при выделенных 10 000 — третий наём отклонён
|
||||
- Снять предмет — фонд уменьшился, наём снова возможен
|
||||
- Назначение, не влезающее в предел, отклонено, и в ответе видно почему
|
||||
- Перезапуск сервера поднимает школу с тем же штатом и теми же назначениями
|
||||
- Тесты API: наём, повторный наём того же человека, отклонение по пределу, неизвестная школа,
|
||||
неизвестный соискатель
|
||||
- `dotnet test` проходит
|
||||
|
||||
## Стоп
|
||||
|
||||
Не увольнять. Не платить зарплату по месяцам — предел проверяется, деньги не движутся.
|
||||
Не расписание.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Фаза 13. Раздел «Управление»
|
||||
|
||||
## Зависимости
|
||||
|
||||
- [Фаза 12](12-hiring-budget.md)
|
||||
- [Фаза 8](08-people-browser.md)
|
||||
|
||||
## Зачем
|
||||
|
||||
Игрок собирает школу руками: видит деньги, перебирает соискателей, нанимает и раздаёт предметы.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] Под часами появляются два верхних раздела: «Обзор» и «Управление». В «Обзоре» — всё, что
|
||||
есть сейчас; часы и дата видны в обоих
|
||||
- [ ] Строка денег: выделено, расписано, свободно
|
||||
- [ ] Список непокрытых предметов — что школа не может преподавать
|
||||
- [ ] Список соискателей: имя, запрашиваемая ставка, сильные навыки; выбор показывает карточку
|
||||
человека справа — ту же, что в «Людях»
|
||||
- [ ] Кнопка найма; отказ по пределу показывается текстом, а не молчанием
|
||||
- [ ] Список штата: кто нанят, какие предметы ведёт, сколько стоит
|
||||
- [ ] Назначение и снятие предмета с карточки нанятого
|
||||
- [ ] Строки через `t(...)`, обе локали
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- Создать школу, открыть «Управление», нанять учителя, назначить ему математику
|
||||
- Дойти до предела — попытка нанять ещё одного объясняет, что денег нет
|
||||
- Снять предмет и убедиться, что свободных денег стало больше
|
||||
- Перейти в «Обзор» → «Люди» и увидеть нанятого в общем списке с ролью работника
|
||||
- Нанять родителя ученика и увидеть у него обе роли на одной карточке
|
||||
- `dotnet test` и клиентские `npm test` / `build` проходят
|
||||
|
||||
## Стоп
|
||||
|
||||
Не увольнять из интерфейса. Не показывать расписание и нагрузку по часам — только предметы.
|
||||
@@ -0,0 +1,33 @@
|
||||
# Фаза 14. Каркас дня и каникулы
|
||||
|
||||
## Зависимости
|
||||
|
||||
- [Фаза 10](10-subjects.md)
|
||||
|
||||
## Зачем
|
||||
|
||||
Школа получает учебный календарь: во сколько звонок, сколько уроков, какие дни рабочие и когда
|
||||
каникулы. Расписания ещё нет — есть время, в которое его можно будет положить.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] Каркас дня в `core` дефом: начало первого урока, число уроков, длина урока, длины перемен
|
||||
и какая из них большая
|
||||
- [ ] Длина учебной недели — в `SimulationOptions`, пять дней по умолчанию
|
||||
- [ ] Каникулы дефами: осенние, зимние, весенние, лето — диапазонами дат внутри учебного года
|
||||
- [ ] Запрос «какой сейчас слот»: номер урока или перемена, или «вне учебного дня» — по игровому
|
||||
времени, выходным и каникулам
|
||||
- [ ] Требование к типу помещения у `SubjectDef`: физкультура в спортзале, информатика в
|
||||
компьютерном классе; без требования — кабинет класса
|
||||
- [ ] `GET /api/catalog` отдаёт каркас дня — он понадобится сетке расписания
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- В субботу, в каникулы и в 23:00 запрос слота отвечает «занятий нет»
|
||||
- В 10:20 буднего учебного дня отвечает конкретным номером урока
|
||||
- Смена длины недели в настройках делает субботу рабочей
|
||||
- `dotnet test` проходит
|
||||
|
||||
## Стоп
|
||||
|
||||
Ни одного урока не ставится. Ни планировщика, ни экрана.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Фаза 15. Планировщик
|
||||
|
||||
## Зависимости
|
||||
|
||||
- [Фаза 14](14-school-calendar.md)
|
||||
- [Фаза 12](12-hiring-budget.md)
|
||||
|
||||
## Зачем
|
||||
|
||||
Из учебного плана, назначений и карты получается таблица уроков. Библиотека проверяется тестами,
|
||||
без мира и без хоста, как генератор людей.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] Новый проект рядом с `HSchool.People`: зависит только на `HSchool.Content`, без Arch и
|
||||
ASP.NET; отдаёт простую таблицу уроков
|
||||
- [ ] Раскладка часов учебного плана по слотам недели для каждого класса
|
||||
- [ ] Кабинет урока: требование предмета, иначе кабинет класса
|
||||
- [ ] Четыре запрета соблюдаются: учитель, класс и кабинет не заняты дважды, класс влезает в кабинет
|
||||
- [ ] Непокрытые часы не ставятся, а возвращаются отдельным списком — их покажет «Управление»
|
||||
- [ ] Закреплённые игроком уроки принимаются на вход и переживают перестроение; остальное
|
||||
раскладывается вокруг них
|
||||
- [ ] Детерминированность: те же штат, назначения и карта дают ту же таблицу
|
||||
|
||||
## Тесты, без которых фаза не закрыта
|
||||
|
||||
- [ ] Один спортзал на одиннадцать классов — физкультура разъезжается по слотам, а не дублируется
|
||||
- [ ] Учитель с двумя предметами не стоит в двух местах в одном слоте
|
||||
- [ ] Класс на 16 не попадает в кабинет на 12
|
||||
- [ ] Предмет без учителя даёт дыру и запись в непокрытых
|
||||
- [ ] Закреплённый урок остаётся на месте после найма ещё одного учителя
|
||||
- [ ] Те же входы — та же таблица
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- Тесты библиотеки зелёные
|
||||
- В проекте нет упоминаний сокетов, ECS и `DateTime.Now`
|
||||
|
||||
## Стоп
|
||||
|
||||
Не трогать сервер, сейв и клиент. Таблица пока никуда не уезжает.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Фаза 16. Расписание в школе
|
||||
|
||||
## Зависимости
|
||||
|
||||
- [Фаза 15](15-timetable-planner.md)
|
||||
|
||||
## Зачем
|
||||
|
||||
Расписание живёт в школе, переживает перезапуск и отвечает на вопрос «кто где сейчас». Экрана
|
||||
ещё нет.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] Расписание пересобирается при найме, увольнении и изменении назначений — **не по тику**
|
||||
- [ ] Хранится в сейве рядом с составом; пишется при изменении, не по таймеру. Закреплённые
|
||||
игроком уроки сохраняются вместе с ним
|
||||
- [ ] Воркер публикует расписание снимком, как ростер
|
||||
- [ ] «Кто где сейчас» **вычисляется** из расписания и часов, а не хранится вторым состоянием
|
||||
- [ ] Команда закрепления и снятия закрепления урока — через мейлбокс воркера
|
||||
- [ ] `GET /api/schools/{id}/timetable` — расписание класса и расписание человека; читает
|
||||
опубликованный снимок
|
||||
- [ ] Снимок карты отдаёт по узлу, что там сейчас идёт и у кого; протокол и `docs/protocol.md`
|
||||
правятся тем же коммитом, версия бумпится
|
||||
- [ ] Панель локации получает данные для «Сейчас» и «Персонажей»
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- Нанять учителя математики — в расписании появились уроки математики
|
||||
- Снять назначение — уроки исчезли, предмет стал непокрытым
|
||||
- Передвинуть урок, нанять ещё одного человека — передвинутый остался на месте
|
||||
- Перемотать время на ×4 через звонок — «кто где сейчас» меняется вместе с уроком
|
||||
- В каникулы и в выходной никто нигде не находится
|
||||
- Перезапуск сервера поднимает то же расписание с теми же закреплениями
|
||||
- `dotnet test` проходит
|
||||
|
||||
## Стоп
|
||||
|
||||
Не рисовать расписание на экране. Не перемещать людей по карте.
|
||||
@@ -0,0 +1,34 @@
|
||||
# Фаза 17. Расписание на экране
|
||||
|
||||
## Зависимости
|
||||
|
||||
- [Фаза 16](16-timetable-in-school.md)
|
||||
- [Фаза 13](13-management-tab.md)
|
||||
|
||||
## Зачем
|
||||
|
||||
Школа перестаёт быть списком имён: видно, что происходит в комнатах прямо сейчас и как выглядит
|
||||
неделя класса и учителя.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] В дереве карты рядом с комнатой — что и у кого там сейчас: `Кабинет 204 (Математика · 5Б)`.
|
||||
Свободная комната остаётся без скобок
|
||||
- [ ] Панель локации: «Сейчас» — урок и класс, «Персонажи» — учитель и ученики этого класса
|
||||
- [ ] В «Управлении» — расписание класса: сетка «день × урок», выбор параллели
|
||||
- [ ] В карточке человека — его личное расписание, рядом с навыками и семьёй
|
||||
- [ ] Правка урока из сетки: перенос и смена кабинета, видимая отметка «закреплено» и снятие
|
||||
- [ ] Отказ планировщика (некуда поставить, кабинет занят) объясняется текстом, а не молчанием
|
||||
- [ ] Строки через `t(...)`, обе локали
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
- Открыть школу в учебное время и увидеть в дереве занятые кабинеты, а в каникулы — пустые
|
||||
- Открыть расписание 5Б, найти окно на месте непокрытого предмета
|
||||
- Открыть учителя и увидеть те же уроки с его стороны
|
||||
- Передвинуть урок и убедиться, что он не разъехался после найма
|
||||
- `dotnet test` и клиентские `npm test` / `build` проходят
|
||||
|
||||
## Стоп
|
||||
|
||||
Не общая сетка на всю школу. Не оценки и не посещаемость. Не анимация перемещений.
|
||||
@@ -36,3 +36,35 @@
|
||||
| [7. Люди в школе](07-people-in-school.md) | ✅ | Сущности в `World`, ростер на диске, 11 кабинетов в `core` |
|
||||
| [8. Просмотр людей](08-people-browser.md) | ✅ | Панель со списком, фильтрами и карточкой |
|
||||
| [9. Годовой набор](09-yearly-intake.md) | ✅ | Первое сентября: переход, выпуск, набор |
|
||||
|
||||
## Срез 3. Штат
|
||||
|
||||
Дизайн: [`../design/staffing.md`](../design/staffing.md).
|
||||
|
||||
Первый срез, где игрок принимает решения. Школа открывается пустой: надо нанять людей из пула
|
||||
соискателей, раздать им предметы и уложиться в выделенную на месяц сумму. Уроков по-прежнему нет —
|
||||
предмет это запись в нагрузке, а не событие в кабинете.
|
||||
|
||||
Фазы идут строго по порядку: каждая опирается на данные предыдущей.
|
||||
|
||||
| Фаза | Статус | Зачем |
|
||||
| --- | --- | --- |
|
||||
| [10. Предметы и мебель](10-subjects.md) | ✅ | `SubjectDef`, одна учительская должность, кабинет как число мест |
|
||||
| [11. Пустая школа и пул](11-applicants.md) | ⬜ | Школа без сотрудников, соискатели с запросом по зарплате |
|
||||
| [12. Наём и бюджет](12-hiring-budget.md) | ⬜ | Наём, назначение предметов, предел фонда оплаты |
|
||||
| [13. Раздел «Управление»](13-management-tab.md) | ⬜ | Деньги, соискатели, штат и назначения на экране |
|
||||
|
||||
## Срез 4. Расписание
|
||||
|
||||
Дизайн: [`../design/schedule.md`](../design/schedule.md).
|
||||
|
||||
Часы учебного плана раскладываются по дням и кабинетам. Школа начинает жить по времени: видно,
|
||||
что идёт в комнате прямо сейчас. Люди при этом по-прежнему **не ходят** — они находятся там, где
|
||||
их застало расписание.
|
||||
|
||||
| Фаза | Статус | Зачем |
|
||||
| --- | --- | --- |
|
||||
| [14. Каркас дня и каникулы](14-school-calendar.md) | ⬜ | Звонки, длина недели, каникулы |
|
||||
| [15. Планировщик](15-timetable-planner.md) | ⬜ | Раскладка часов по слотам, четыре запрета |
|
||||
| [16. Расписание в школе](16-timetable-in-school.md) | ⬜ | Сейв, снимок, «кто где сейчас» |
|
||||
| [17. Расписание на экране](17-timetable-screen.md) | ⬜ | Скобки в дереве, сетка класса, расписание учителя |
|
||||
|
||||
+15
-6
@@ -35,10 +35,10 @@ Everything the main menu needs in one request.
|
||||
```json
|
||||
{
|
||||
"maxSchools": 6,
|
||||
"defaultStartDate": "2012-04-03T06:00:00Z",
|
||||
"defaultStartDate": "2012-03-31T06:00:00Z",
|
||||
"gameMinutesPerRealSecond": 5,
|
||||
"schools": [
|
||||
{ "id": 1, "name": "Гимназия №14", "gameTime": "2012-04-03T07:35:00Z", "running": false, "speedIndex": 1 }
|
||||
{ "id": 1, "name": "Гимназия №14", "gameTime": "2012-03-31T07:35:00Z", "running": false, "speedIndex": 1 }
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -66,13 +66,22 @@ Placeable (non-abstract) types plus labels in `lang`, and the last-wins `maps/de
|
||||
`core` plus the listed extras. The server always prepends `core`. `mods` is a comma-separated
|
||||
list of extra pack ids; omit it for vanilla. Unknown extras return `400` `unknown-mod`.
|
||||
|
||||
Room slots include a `count` (how many of the default thing the def places). Things carry
|
||||
`pupilSlots` — how many pupils that thing hosts for lessons. The create editor copies both onto
|
||||
the map instance; the snapshot's per-node pupil-slot total is computed on the server.
|
||||
Room defs that are homerooms carry `homeroom`, `seatThing` and `defaultSeats` instead of a
|
||||
slot table. A map classroom stores `seats` — how many of that thing occupy the room. Capacity is
|
||||
still `ThingDef.pupilSlots × seats`. Named slots remain on rooms whose furnishing actually
|
||||
varies (cafeteria, medical office, principal's office, library). The create editor shows a
|
||||
single seats field on a homeroom and the old slot rows on everything else.
|
||||
|
||||
Things carry `pupilSlots` — how many pupils that thing hosts. The snapshot's per-node pupil-slot
|
||||
total is computed on the server.
|
||||
|
||||
`nameSets` is the list of placeable name packs (`defName` + label). The create dialog picks one;
|
||||
it is independent of the UI language.
|
||||
|
||||
`subjects` is the list of placeable subjects (`defName`, label, `gradeMin`/`gradeMax`,
|
||||
`hoursPerWeek`, and `skills` with shares). The assignment form in a later phase reads it; the
|
||||
create editor does not.
|
||||
|
||||
`lang` is the same value Hello carries — not `Accept-Language`. Anything other than `en` is
|
||||
Russian.
|
||||
|
||||
@@ -83,7 +92,7 @@ Body:
|
||||
```json
|
||||
{
|
||||
"name": "Гимназия №14",
|
||||
"startDate": "2012-04-03T06:00:00Z",
|
||||
"startDate": "2012-03-31T06:00:00Z",
|
||||
"modIds": [],
|
||||
"map": null,
|
||||
"nameSetId": "Slavic"
|
||||
|
||||
Reference in New Issue
Block a user