Запускаю Age of Poleis, чтобы проверить одну мелочь. Получилось ли у меня в онбординге передать смысл продолжать разведку или нет. И вот я уже исследую весь остров в поисках нужного для старта артефакта. Странное чувство. Игру придумал я. Я знаю, какие здания откроются следующими, где экономика начнёт скрипеть и к чему всё идёт. Спойлеров у меня больше, чем у кого угодно. И всё равно иногда закрываю чек-лист и продолжаю строить город — просто потому что хочется.
Age of Poleis — стратегия про древнегреческий полис. Строительство, ресурсы, рабочие и производство здесь сцеплены: поменяешь одну механику — поедут ещё три. Код пишет ИИ, а я придумываю, решаю и играю.
Ниже — как я устроил процесс, чтобы агент работал почти без меня, и два случая из реального проекта. Первый — про правило, которое я устал повторять. Второй — про тридцать зелёных тестов, которые ничего не доказывали.
Если объясняешь второй раз — это уже правило
Бэкенд на Django, интерфейс на React и TypeScript, между ними API. Контракт описывается на бэкенде, из него получается OpenAPI-схема, из схемы генерируются TypeScript-типы.
Django → OpenAPI → TypeScript-типы → код интерфейса
Ловушка тут очевидная. Тип на фронте не сходится с тем, что пришло из API, и его очень хочется просто поправить руками: одна строка, минута работы. А при следующей генерации правка тихо исчезнет, и баг вернётся.
Когда я поймал себя на том, что объясняю это в чате не в первый раз, правило переехало в постоянные инструкции агента. Сгенерированные файлы руками не трогать. Поменялся контракт — меняй источник и перегенерируй. Повторный запуск генерации не должен ничего менять.
- Fix root causes; do not silence failures with skipped tests, broad assertions, duplicated
flows, generated-file edits, or an alternate database.
- API-owned TypeScript types come from generated OpenAPI. Client-owned types remain local.
- API boundary: backend source change, OpenAPI/type regeneration, frontend validation.
Но правило в тексте — это пока пожелание. Поэтому в общем скрипте проверки проекта есть шаг, который генерирует схему и типы во временные файлы и сравнивает их с тем, что лежит в репозитории. Разошлось — проверка красная.
```python
def run_frontend_contract(gate: Gate, temporary: Path, generated_types: Path) -> None:
if gate.run(
"Generate TypeScript API types into an isolated file",
[NPM, "exec", "--", "openapi-typescript", str(OPENAPI), "-o", str(generated_types)],
cwd=FRONTEND,
):
gate.check(
"TypeScript API types drift",
lambda: assert_same(
"frontend/src/types/generated/api.ts", generated_types, GENERATED_TYPES
),
)
```
С тех пор у меня простая схема: правило, способ его выполнить и проверка. Без проверки правило соблюдается, пока агент о нём помнит. С проверкой — всегда. Так же закрывается форматирование, локализация и любая другая рутина, которая повторяется.
Тридцать зелёных тестов, которые проверяли не то
Windows-версия запускалась так: сначала маленькое окно загрузки, потом отдельное игровое. На выходе — ещё одно окно ожидания. Работало, но выглядело как три разные программы. Хотелось по-человечески: одно окно, в нём загрузка, потом игра. Закрыл — окно исчезло.
На словах задача на одну строку. В коде — несколько состояний: запуск, загрузка, закрытие посреди запуска, сохранение, повторный старт.
Самое интересное случилось ещё до первой строчки кода. Все 30 существующих тестов оболочки и управления окнами были зелёными. И это логично: они проверяли старое поведение — то самое, от которого я хотел избавиться. Зелёный цвет означал «работает как раньше», а мне нужно было «работает иначе».
Поэтому первым делом появился новый тест жизненного цикла окна. На старом коде он падал: искал основное окно, а находил окно загрузки. После переделки прошёл. Вот теперь можно сказать, что задача решена, а не просто «ничего не сломали».
Чтобы агент мог проверить такое сам, я перевожу пожелание в то, что можно увидеть:
-
загрузка и игра идут в одном окне;
-
закрытие во время запуска не открывает новое окно;
-
сохранённая кампания на месте после перезапуска.
В итоге прошли 96 JavaScript-тестов оболочки, а агент отдельно прогнал настоящее Electron-приложение. Одно окно от загрузки до игры, закрытие без лишних окон, кампания жива после перезапуска. Ручное сохранение пережило закрытие с тем же идентификатором, названием и игровым моментом.
И ещё одна строчка, которую я теперь требую в каждом отчёте: что не проверялось. Здесь это установка на чистую Windows, она живёт отдельной задачей. Через месяц такая строчка спасает от ложной уверенности.
В этой же задаче нашёлся хороший пример того, почему мало просто получить скриншот от агента. Первый кадр, который должен был подтвердить вход в кампанию, ещё показывал меню её подготовки. Картинка была, а доказательства запуска игры — нет. Подтверждением стал только более поздний кадр из самой кампании вместе с проверкой настоящего окна приложения. Теперь в правилах отдельно записано: успешное создание скриншота ещё не доказывает, что на нём нужное состояние.
Моя работа — играть
Когда техническая часть проверяется без меня, игру я запускаю с другими вопросами. Не «работает ли кнопка», а «хочется ли её нажать».
Механика может быть технически безупречной и при этом скучной. Новая возможность иногда просто добавляет ещё один клик. Экономика посложнее легко превращается в бухгалтерию. И ни один тест этого не поймает.
Поэтому во время игры я слежу за собой: где потерял цель, где потянулся ускорить время, где впервые задумался над ходом. А момент, когда забыл про тест и просто играю, разбираю отдельно. Что именно затянуло — то и стоит усиливать.
Был момент, когда я понял, что подбирать очередного жителя для очередной требуемой профессии по началу было весело, а спустя 100 игровых периодов уже утомляет. Итогом стало автоматический подбор по критериям, теперь я просто как управленец “распоряжаюсь” и “приказываю” обучаться.
Честности ради: то, что нравится мне, ещё ничего не говорит о других игроках. Для этого нужны чужие прохождения. Но если автору самому не хочется выходить из своей игры, это хороший знак.
Перфекционизм против рандома
Есть у меня и совсем личная проблема, из-за которой производство растягивается очень сильно. Перфекционизм.
Вижу кнопку, которая стоит в диалоговом окне чуть неровно. Панель, сдвинутую совсем немного. Текст события, из которого не до конца понятно, как работает механика и где она сработает ещё. И всё: лезу исправлять. А потом начинаю партию заново, чтобы снова пройти весь путь целиком.
Так я переигрывал игру десятки раз. И пару месяцев в основном просто исправляю то что режет глаз, а не то что действительно важно.
И каждый раз упираюсь в одно и то же. Сколько бы я ни шлифовал идеальный старт, случайность, которую я сам заложил в игру, делает каждую партию другой — и на старте, и дальше, на всех этапах. Отточить прохождение «насовсем» нельзя: следующая партия всё равно пойдёт по-своему.
Получается забавно. Переигрывание — главный тормоз моего производства. И оно же лучше всего доказывает, что игра живая: даже я не знаю заранее, как сложится следующая партия.
Чего ИИ за меня не сделает
Сборки, подписи, сертификация, требования площадок — у каждой из этих задач свой процесс и свои способы убедиться, что всё в порядке. ИИ помогает и тут, но это отдельная история.
С маркетингом ещё сложнее. Набросать описание с ИИ — дело пяти минут. Понять, кому эта игра нужна, что показать первым и как объяснить её человеку, который слышит о ней впервые, — уже нет. Игра плюс пачка постов ещё не аудитория.
Так что моя работа понемногу расползается: геймдизайн, устройство проекта, правила для агента, релиз, разговоры с будущими игроками. Время на это появилось ровно потому, что технические задачи проходят полный круг — от кода до проверки и исправления — без меня.
А теперь пойду запущу игру. Только проверить одну мелочь.
Бесплатную демку можно скачать тут: https://age-of-poleis.itch.io/age-of-poleis
Автор: sharben


