AAYMA
RU
Войти

Аким на 5 часов

Браузерная стратегия управления городом на Go: 100 единиц бюджета, 5 решений по направлениям и районам, расчёт AQLS на сервере, мультиплеерные…

Доведённый продукт
Команда «Beknur» 3💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: Beknur
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Доведённый продукт

Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам

Суть

Браузерная стратегия управления городом на Go: 100 единиц бюджета, 5 решений по направлениям и районам, расчёт AQLS на сервере, мультиплеерные комнаты, AI-разбор партии и экспорт отчётов.

Как устроено

Go-сервер с PostgreSQL проверяет бюджет и ограничения (5 решений, не больше 2 мер одного направления, минимум 3 направления, несовместимости, районы) и считает эффект как effect×(8−lag)/8 с синергиями и ограничением 0–100. Затем взвешенный AQLS. После сдачи сценариев OpenAI делает анализ по grounded-промпту с серверной валидацией. Есть комнаты до 8 игроков через WebSocket, детерминированный бот-оппонент (поиск, не LLM), админка, AI-генерация учебного города и отчёты PDF/DOCX/JSON.

Ключевые решения

  • lagged effect model
  • synergies
  • constraint validation
  • deterministic bot search
  • WebSocket multiplayer
  • PBKDF2-SHA256 auth
  • grounded prompt

Особенности

Единственное в батче мультиплеерное решение кейса: комнаты, лобби, одинаковые замороженные условия, рейтинг побед, режим hot-seat. Подробная инструкция деплоя на VPS с HTTPS через Caddy и бэкапами PostgreSQL.

Стек

Go 1.25 · PostgreSQL · WebSocket · Docker Compose · Caddy

Модели

OpenAI


Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-eb3ba7a0-beknur.

README

README.md

Аким на 5 часов

Браузерная стратегия управления городом: 100 условных единиц, ровно 5 решений, 5 направлений, 8 кварталов последствий. Сервер считает Astana Quality of Life Score, а OpenAI объясняет результат. Учебные показатели не являются официальной статистикой или прогнозом.

Возможности

  • Регистрация, вход, профиль, смена пароля; серверные роли пользователя и администратора.
  • Одиночная игра, комнаты до 8 игроков, локальная игра на одном компьютере, детерминированный AI-бот.
  • Лобби и готовность, обновления WebSocket, одинаковые замороженные условия для участников комнаты.
  • Проверка бюджета, пяти решений, максимум двух мер одного направления, минимум трёх направлений, несовместимостей и районов.
  • Автоматический AI-анализ после сдачи сценариев: достижения, улучшения, риски, компромиссы, районы и рекомендации.
  • Сохранённая история, рейтинг по победам, PDF с кириллицей, DOCX и JSON без повторного вызова AI.
  • Админ: шаблоны, районы, объекты, мероприятия, пользователи, комнаты, результаты и статистика.
  • Ручной город и генерация учебного города через AI с серверной проверкой.

Быстрый запуск

Нужны Go 1.25+ и Docker Desktop с Linux containers.

  1. Скопируйте .env.example в .env, если .env ещё нет. Не перезаписывайте существующий ключ.
  2. Задайте OPENAI_API_KEY, доступную вашему аккаунту модель в OPENAI_MODEL, случайный POSTGRES_PASSWORD и такой же пароль в DATABASE_URL.
  3. Выполните из папки с go.mod:
docker compose up -d db
go run .

Откройте http://127.0.0.1:3000. Если порт занят, задайте другой PORT в .env и перезапустите сервер. При текущей локальной проверке новая версия запущена на http://127.0.0.1:3001.

Полностью через Docker:

docker compose --profile full up --build -d

Не запускайте одновременно два сервера на одном порту. В контейнере используется та же PostgreSQL; данные хранятся в именованных volumes.

Настройки и администратор

Инструкция публикации на сервере: Деплой на VPS.

  • DATABASE_URL: PostgreSQL; локальная БД доступна на 127.0.0.1:54329.
  • POSTGRES_PASSWORD: пароль пользователя БД для Compose. Для примера URL удобно использовать случайные буквы и цифры.
  • OPENAI_API_KEY, OPENAI_MODEL: только сервер. Доступность модели зависит от аккаунта.
  • PORT: по умолчанию 3000; HOST: по умолчанию 127.0.0.1.
  • ADMIN_PASSWORD: минимум 12 символов, задаёт пароль при первом создании администратора.
  • Если ADMIN_PASSWORD пуст, случайный пароль сохраняется в .local/admin-password.txt. Логин: admin. В Docker файл находится в /app/.local/admin-password.txt.
  • PUBLIC_ORIGIN: точный HTTPS origin за reverse proxy.

.env, .local, база, логи и ключи игнорируются Git. Пароли пользователей хешируются PBKDF2-SHA256. Сессии сохраняются на сервере, cookie — HttpOnly и SameSite=Strict. Для публичного развёртывания необходим HTTPS.

Деплой на VPS: Linux + Docker Compose + HTTPS

Приложению нужны постоянно работающие Go-сервер и PostgreSQL. Публикация только папки dist на GitHub Pages не запустит игру: авторизация, расчёты, комнаты и отчёты работают через backend.

1. Подготовьте сервер и домен

На Linux-сервере установите Git, Docker Engine с Compose plugin и Caddy. Инструкции: Docker Engine, Caddy. Go и Node.js на сервере не нужны: Go-приложение собирается внутри Docker.

Настройте DNS-запись A домена, например akim.example.com, на IP сервера. Если используете AAAA, она также должна вести на этот сервер. Откройте входящие TCP-порты 80 и 443 и сохраните доступ по SSH. Приложение и БД в текущем Compose опубликованы только на localhost: 127.0.0.1:3000 и 127.0.0.1:54329.

2. Скачайте код и настройте окружение

Команды далее выполняются в терминале Linux, в папке с compose.yaml:

git clone https://github.com/BAITC-Hacks/hack-eb3ba7a0-beknur.git
cd hack-eb3ba7a0-beknur
git switch main
cp -n .env.example .env
chmod 600 .env
nano .env

Заполните .env реальными значениями вместо примеров:

POSTGRES_PASSWORD=REPLACE_WITH_RANDOM_HEX_PASSWORD
ADMIN_PASSWORD=REPLACE_WITH_ANOTHER_STRONG_PASSWORD
OPENAI_API_KEY=REPLACE_WITH_YOUR_API_KEY
OPENAI_MODEL=REPLACE_WITH_MODEL_AVAILABLE_TO_YOUR_ACCOUNT
PUBLIC_ORIGIN=https://akim.example.com

Для генерации каждого пароля можно отдельно выполнить openssl rand -hex 32. PUBLIC_ORIGIN укажите без завершающего слеша. Он нужен для проверки Origin и Secure-cookie за HTTPS-прокси. Ключ и .env не добавляйте в Git.

При запуске через Compose адрес DATABASE_URL формируется автоматически: PostgreSQL доступен приложению как db:5432. Значение DATABASE_URL из .env.example используется для запуска Go непосредственно на хосте. Порт приложения внутри контейнера фиксирован — 3000; локальный порт 3001 из примера разработки здесь не используется.

3. Добавьте настройки серверного запуска

Создайте рядом с compose.yaml файл compose.production.yaml:

services:
  db:
    restart: unless-stopped
  app:
    restart: unless-stopped
    environment:
      PUBLIC_ORIGIN: ${PUBLIC_ORIGIN:?Set PUBLIC_ORIGIN in .env}

Этот override передаёт PUBLIC_ORIGIN внутрь приложения — одного добавления переменной в .env недостаточно, поскольку базовый Compose её не передаёт. Файл также задаёт политику перезапуска контейнеров. Подход соответствует документации Compose для production.

Запустите сервисы:

docker compose -f compose.yaml -f compose.production.yaml --profile full up -d --build
docker compose -f compose.yaml -f compose.production.yaml --profile full ps
curl --fail http://127.0.0.1:3000/ > /dev/null

При первом запуске сервер создаст таблицы, начальный датасет и аккаунт admin. Его пароль берётся из ADMIN_PASSWORD. Изменение переменной после создания аккаунта не меняет существующий пароль: используйте смену пароля в профиле.

4. Подключите HTTPS через Caddy

При установке Caddy как системной службы добавьте в /etc/caddy/Caddyfile блок, заменив домен на свой. Существующие блоки других сайтов сохраните:

akim.example.com {
    reverse_proxy 127.0.0.1:3000
}
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl enable --now caddy
sudo systemctl reload caddy
curl --fail https://akim.example.com/ > /dev/null

Caddy получает и обновляет HTTPS-сертификат для домена при корректном DNS и доступных портах 80/443. См. официальную инструкцию reverse proxy. Прокси должен сохранять исходный заголовок Host; приведённая конфигурация подходит и для WebSocket-комнат.

5. Проверьте опубликованную игру

Откройте https://akim.example.com, зарегистрируйте двух игроков в разных браузерах, создайте комнату и проверьте присоединение, готовность и сдачу пяти решений. Дождитесь AI-анализа, скачайте PDF/DOCX/JSON и проверьте историю. Для AI нужен исходящий HTTPS-доступ сервера к OpenAI и рабочие ключ, модель и квота.

Логи для диагностики:

docker compose -f compose.yaml -f compose.production.yaml --profile full logs --tail=100 app db
sudo journalctl -u caddy -n 100 --no-pager

502 обычно требует проверки контейнера app; ошибка входа по Origin — проверки PUBLIC_ORIGIN и домена; ошибка AI — проверки настроек API в логах и интерфейсе. Главная страница сама по себе не подтверждает работу БД или AI: выполните игровой сценарий выше.

Обновление и сохранность данных

Перед обновлением сделайте дамп БД. Команда ниже рассчитана на Linux shell:

mkdir -p backups
chmod 700 backups
umask 077
docker compose exec -T db pg_dump -U akim -d akim -Fc > "backups/akim-$(date +%Y%m%d-%H%M%S).dump"

Храните копии отдельно от сервера и проверяйте восстановление на тестовой БД. PostgreSQL хранит игры и AI-анализ в volume postgres_data; локальные файлы приложения — в app_data. Не выполняйте docker compose down -v для рабочей установки: флаг -v удаляет volumes. Пароль PostgreSQL в .env относится к первичной инициализации; его замена не меняет пароль уже существующей БД.

Обновление кода с пересборкой только приложения:

git pull --ff-only origin main
docker compose -f compose.yaml -f compose.production.yaml --profile full up -d --build --no-deps app
docker compose -f compose.yaml -f compose.production.yaml --profile full logs --tail=50 app

Пересоздание контейнера даёт короткий перерыв в работе. Незавершённый AI-анализ после перезапуска можно повторить из сохранённого результата. Сейчас запускайте один экземпляр приложения: встроенные AI-задачи и восстановление их состояния не рассчитаны на несколько реплик.

Как играть

Зарегистрируйтесь → Комнаты → Создать комнату → выберите режим и город. В сетевой комнате отправьте код второму игроку; все нажимают «Я готов», создатель запускает игру. Каждый выбирает пять мероприятий и районы. После сдачи изменить решения нельзя. Когда сдали все, сервер фиксирует места и автоматически запускает AI-анализ. При ошибке ключа/модели/сети расчёт сохраняется, а анализ можно повторить.

В локальной игре экран передачи хода скрывает предыдущий сценарий. Бот использует только исходные данные и те же ограничения; это детерминированный поиск, не LLM-соперник. Отдельная LLM отвечает за разбор игры.

Равные Score делят место. В глобальном рейтинге учитываются завершённые результаты зарегистрированных игроков; одиночные игры не дают побед, локальные гости и боты не ранжируются.

Формула и данные

Seed: data/seed.json, перенесён из исходного main. Исходные PDF из master prompt пока не приложены, поэтому полная сверка с ними не выполнена. Контрольные значения совпадают с ТЗ.

  • Реализованный эффект: effect × (8 − lag) / 8.
  • Синергии: M1+M2 → T1 +2; M10+M12 → B1 +2; M5+M6 → E2 +2, в районе первой меры.
  • Индикаторы ограничиваются 0–100 после эффектов и синергий.
  • Веса: [.10,.10,.09,.11,.11,.11,.09,.09,.10,.10].
  • D_d = Σ(w_k × I'_dk); D_avg = Σ(pop_d × D_d).
  • Score = .7 × D_avg + .3 × min(D_d) − N_crit, где критический индикатор строго меньше 40.
  • Остаток бюджета не даёт бонуса. Итоговый Score не обрезается; применяется точная формула.

Baseline: 52.5577. Пример M7/Нура, M8/Нура, M10/Нура, M12/город, M5/Сарыарка: стоимость 95, Score 56.5431.

Рекомендации получают только проверенные сервером замены одного решения. Это не поиск глобального оптимума. Предельные вклады решений учитывают потерю синергий при исключении меры и не складываются в общую дельту.

Технологии и хранение

Go net/http, PostgreSQL через pgx, WebSocket Gorilla, HTML/CSS/JavaScript без сборщика. PDF: gofpdf и встроенный PT Sans (OFL). DOCX: настоящий пакет Office Open XML.

PostgreSQL хранит аккаунты, сессии, шаблоны, комнаты и результаты как JSONB-агрегаты с первичными ключами и индексами результатов. Снимки сценариев сохраняются в результатах, поэтому изменение шаблона не меняет старые отчёты. Подробнее: архитектура и ограничения.

API

Все пути имеют префикс /api:

  • POST /register, /login, /logout; GET /me.
  • GET/PATCH /profile, POST /profile/password.
  • GET /cities, POST /cities/generate.
  • GET/POST /rooms, GET /rooms/{id}.
  • POST /rooms/{id}/join|leave|ready|start|decisions|submit.
  • WebSocket /rooms/{id}/ws, GET /room-ranking/{id}.
  • GET /results/{id}, POST /results/{id}/retry.
  • GET /results/{id}/pdf|docx|json, /history, /leaderboard.
  • Админ: GET /admin/overview, POST/PUT /admin/cities, DELETE /admin/cities/{id}, GET/PUT /scenario.

Текст AI-промпта открыт в prompts/post-game.txt. Используется Responses API со Structured Outputs, затем сервер проверяет обязательные разделы.

Проверки

go test ./...
go vet ./...

Для интеграционного теста задайте TEST_DATABASE_URL на тестовый PostgreSQL и запустите go test -v ./.... Тест создаёт и удаляет отдельную случайную схему, проверяет регистрацию двух игроков, RBAC, комнату, готовность, сдачу, приватность, AI-контракт, экспорты и рейтинг. AI заменён локальным HTTP mock: тест не расходует API-квоту.

Отдельные тесты проверяют baseline, пример датасета, бюджет, количество решений, повторы, направления, несовместимости, типы мер, лаги, синергии, clipping, штрафы, бота и структуру PDF/DOCX.

Статус и дальнейшее развитие

Это работающий MVP для проверки хакатонного сценария, а не заверенная production-ready система. Для публичного запуска нужны мониторинг, резервные копии, усиленные лимиты нагрузки, пагинация, внешний worker AI и ручная проверка DOCX в Microsoft Word. Живой вызов OpenAI зависит от действительного ключа и доступной модели. Исходные требования сохранены в docs/master-prompt.txt и docs/post-game-requirements.txt.

Звёзды
⭐ 0
Форки
0
Язык
Go
Последний push
в прошлом месяце
Коммиты
13
Авторы
1
Файлы
27
Ветки
2

Авторы коммитов

erdem 13

История коммитов

  • 2026-09-23 17:50Document VPS deployment with Docker Compose and HTTPSerdem
  • 2026-09-23 17:45Build Go city simulation game with multiplayer and AI reportserdem
  • 2026-09-23 16:01Add configurable city scenarios, district presets and plan optimizationerdem
  • 2026-09-23 15:14Verify concise AI instructions reach the modelmr_realist
  • 2026-09-23 15:13Use concise grounded prompt for AI analysismr_realist
  • 2026-09-23 15:11Merge remote-tracking branch 'origin/codex/akim-simulator' into codex/akim-simulatorerdem
  • 2026-09-23 15:11Add server-side AI scenario analysis and validation testserdem
  • 2026-09-23 15:10Add concise grounded scenario analysis promptmr_realist
Все коммиты (13)
  • 2026-09-23 14:15Document local API key setup and current integration statusmr_realist
  • 2026-09-23 14:15Add safe example for OpenAI API configurationmr_realist
  • 2026-09-23 14:14Ignore local API credentialsmr_realist
  • 2026-09-23 13:49Document simulator rules and reproducible score calculationmr_realist
  • 2026-09-23 13:49Add city budget simulatormr_realist
Структура репозитория (27 файлов)
.dockerignore
.env.example
.gitignore
Dockerfile
README.md
ai.go
assets/OFL.txt
assets/PT_Sans-Web-Regular.ttf
city_ai.go
compose.yaml
data/seed.json
database.go
docs/architecture.md
docs/master-prompt.txt
docs/post-game-requirements.txt
features.go
game.go
go.mod
go.sum
integration_test.go
main.go
model.go
model_test.go
prompts/post-game.txt
report.go
server.go
store.go
Ветки (2)

Обсуждение 0 комментариев

Войдите, чтобы комментировать

Пока нет комментариев — будьте первым.