AAYMA
RU
Войти

Аким на 5 часов · Astana Lab

Игровой городской симулятор на Streamlit.

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

О проекте

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

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

Суть

Игровой городской симулятор на Streamlit. Интерактивная карта пяти районов Астаны, распределение бюджета в 100 единиц по 5 направлениям или выбор 5 указов из каталога мер, расчёт Score и AI-разбор решений.

Как устроено

Работают два режима. В режиме каталога мер нужно выбрать ровно 5 мер (не больше двух на направление). Эффект мер учитывает лаг (8−лаг)/8 на горизонте 8 кварталов, фиксированные синергии (M1+M2, M10+M12, M5+M6) и несовместимости, итог ограничивается диапазоном 0–100 и пересчитывается в Score через score_indicators. В режиме слайдеров действует авторская формула с убывающей отдачей f_s = 0.35·(1−exp(−b_s/(B·W_s))). Карта Folium строится по контурам районов из OpenStreetMap (GeoJSON). Отчёт с сильными сторонами, рисками и вердиктом пишет OpenAI gpt-4o-mini через AsyncOpenAI с таймаутами и обработкой ошибок.

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

  • rule-based scoring model
  • diminishing returns formula
  • synergy/conflict constraints
  • lagged effects
  • Folium choropleth map
  • OSM GeoJSON

Метрики

Базовый Score 52.56; пример плана (95 ед.) даёт 56.54307 с синергией M10+M12

Особенности

Режим слайдеров и режим каталога мер явно разделены, потому что правила кейса расходятся, а результаты предлагается сравнивать только внутри одного режима. AI-текст никогда не подменяется заготовкой: без ключа работают только расчёты. Контуры районов взяты из реальных данных OpenStreetMap.

Стек

Python · Streamlit · Folium · streamlit-lottie · OpenAI SDK · pytest

Модели

GPT-4o-mini


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

README

README.md

Аким на 5 часов · Astana Lab

Веб-приложение на Python и Streamlit для кейса HackAlem AI: распределите 100 условных единиц, оцените последствия для пяти условных районов и получите экспертный отчет OpenAI.

Игровой ситуационный центр: интерактивная карта Астаны на главном экране, HUD с казной и QoL, пять карточек районов, пять слотов решений и магазин из 14 указов. Одна игровая монета равна одной условной единице кейса; внутренние суммы и JSON сохраняют прежний формат в тенге.

Карта и карточки районов выбирают общий район для новых указов. Три слабейших показателя и предупреждения вычисляются из отображаемого состояния «До / После»; все десять показателей скрыты в «📊 Детали района». Цвета карточек используют ту же функцию и выбранный слой, что и карта. Подписи на карте показывают число запланированных указов, включая городские во всех районах.

Магазин показывает реальные стоимость, тип, полный эффект и лаг; подробности содержат неизменённый ID и вклад с учётом лага. Поле района видно до применения указа. Недоступные действия сопровождаются точной причиной из validate_decisions. Кнопка «Запустить симуляцию» повторно проверяет план и использует simulate_decisions. Живой прогноз доступен при пяти допустимых решениях; toast и шарики появляются только при первом явном запуске данного сценария за игровую сессию, а не при автоматическом обновлении. Синергии отображаются только при наличии в результате калькулятора.

Расчётная модель не изменена. В этом проекте функцию итогового Score зовут score_indicators (внешнее имя calculate_score не используется). Датасет, цены, лаги, веса, конфликты, синергии, штраф и правила остаются в прежних src/data.py, src/model.py, src/planner.py.

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

Симулятор изолирован в папке astana_simulator/. Его зависимости, настройки Streamlit, данные и тесты не требуют изменений файлов другого проекта в корне командного репозитория.

Сначала перейдите из корня репозитория в папку симулятора:

cd astana_simulator

Все следующие команды выполняются внутри этой папки. Это также обеспечивает загрузку собственной темы из .streamlit/config.toml и отдельного файла .env.

Проверено на Python 3.12.14. Зависимости закреплены в requirements.txt, полный снимок окружения — в requirements-lock.txt.

В подготовленном рабочем каталоге Windows

Локальное окружение .venv уже создано. Выполните из корня проекта:

.\.venv\Scripts\python.exe -m streamlit run streamlit_app.py

Откройте http://127.0.0.1:8501. Для расчета и просмотра графиков ключи не нужны.

Также можно выполнить python -m streamlit run app.py из папки astana_simulator/: этот вход запускает тот же интерфейс. Корневой app.py репозитория относится к другому проекту и не изменяется.

Установка с нуля

Используйте стандартный CPython 3.12 с python.org, а не Python из MSYS2.

python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
Copy-Item .env.example .env  # только если .env еще не существует
.\.venv\Scripts\python.exe -m streamlit run streamlit_app.py

Linux / macOS:

python3.12 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
cp -n .env.example .env
.venv/bin/python -m streamlit run streamlit_app.py

Для полного повторения проверенного окружения можно установить requirements-lock.txt вместо requirements.txt. Этот снимок включает тестовые зависимости и сделан на Windows / Python 3.12.

Подключение OpenAI

В локальном файле .env задайте:

OPENAI_API_KEY=ваш_ключ_OpenAI

Ключ OpenAI: https://platform.openai.com/api-keys. Доступ к модели, квоты и оплата определяются вашим аккаунтом. Не отправляйте ключ в чат и не добавляйте .env в Git — файл уже исключен через .gitignore.

Единственный AI-сервис — OpenAI, модель gpt-4o-mini, адрес https://api.openai.com/v1. Файл именно этого проекта загружается через load_dotenv(ROOT / ".env", override=False, interpolate=False), затем ключ читается через os.getenv("OPENAI_API_KEY"). Ключ находится только на сервере и не выводится в браузер, экспорт или сообщения об ошибках.

После изменения .env перезапустите процесс Streamlit, затем нажмите «Получить AI-разбор» под результатами. Переменная окружения имеет приоритет перед .env. Статус «ключ задан» означает наличие значения, а не проверку доступа.

  • По нажатию кнопки создается один AsyncOpenAI-клиент и один отчет.
  • OpenAI пишет сильные стороны, скрытые риски, корректировки и вердикт.
  • Модель получает только синтетические исходные данные, бюджет, результаты, дельты и вклад мер. Ключ в содержимое запроса и экспорт не попадает.
  • На HTTP-попытку установлен таймаут 25 секунд, допускается один повтор SDK. Общий предел для каждого отчета — 55 секунд.
  • Обработаны отсутствие ключа, 401/403, 404, 429, 5xx, проблемы подключения, таймауты, пустой ответ и обрыв по лимиту токенов. Ошибки не выводят необработанный текст исключений или секреты.
  • Отчеты хранятся только в текущей сессии Streamlit. Успешный отчет не запрашивается повторно при движении слайдеров или обновлении страницы внутри сессии. Изменение сценария скрывает устаревший отчет. Кнопка повторения повторяет только неудавшиеся запросы.
  • Без ключей работают настоящие расчеты, графики и экспорт; AI-текст не подменяется заготовленным отчетом.

Перезагрузка вкладки с потерей сессии удаляет кеш AI-ответов; следующий явный запрос создаст новый отчет.

Стартовый обзор города

HUD и карта сразу показывают базовый Score 52.56, 100 монет и пять районов. Карточки районов находятся под картой; «Управлять» выбирает район для магазина, а ссылка рядом с картой переводит к указам. «Новый сценарий» очищает планы, AI-отчёты и историю игровых уведомлений.

Подробный первоначальный обзор с рекомендациями сохранён в «📈 Результаты и методика» → «Исходное состояние». Данные учебные и синтетические. Критический порог показателей — строго ниже 40; диагностический порог 50 в первоначальном обзоре не меняет Score.

Два режима

Запрос на пять слайдеров отличается от правил прикрепленного каталога: в каталоге разрешены две меры в одном направлении и отсутствие мер в другом. Поэтому режимы представлены отдельно и явно подписаны.

Распределение бюджета — дополнительный режим

Пять слайдеров: транспорт, озеленение, социальная инфраструктура, безопасность, городской сервис. Значения от 0 до 100 ед. с шагом 1 ед. Все пять решений присутствуют; нулевое финансирование допустимо. Стартовый сценарий — по 20 ед. на направление. Есть три готовых распределения.

Если сумма превышает 100 ед., Score, AI-анализ и экспорт недоступны. Проверка есть и в UI, и в расчетной функции. Для сохранения расчетной модели внутренние значения и JSON используют тенге: 1 ед. = 10 млн ₸, полный бюджет — 1 млрд ₸. Интерфейс и AI-отчет используют условные единицы.

Для направления s и каждого его показателя в каждом районе:

W_s = сумма весов двух показателей направления
f_s = 0.35 × (1 − exp(−b_s / (B × W_s)))
I'_dk = I_dk + (100 − I_dk) × f_s

b_s — выделенный бюджет, B = 1 000 000 000. Одинаковая программа закрывает одинаковую долю дефицита во всех районах. При меньшем исходном значении абсолютный прирост больше. По мере увеличения финансирования предельная отдача снижается; нулевое финансирование не меняет показатели. Остаток средств не дает бонуса.

Это авторская непрерывная учебная модель, а не формула эффекта из каталога. Коэффициент 0.35 — допущение на горизонт двух лет, не эмпирическая оценка. Она использует исходный датасет и итоговую формулу Score из кейса. Лаги и синергии мероприятий в этом режиме не применяются.

Каталог мероприятий — основной режим

Полностью воспроизводит каталог из второго документа:

  • Бюджет 100 условных единиц, или 1 млрд ₸; 1 единица = 10 млн ₸.
  • Ровно пять уникальных мероприятий; не более двух из одного направления.
  • Для районной меры обязателен район, для городской он не задается.
  • Горизонт 8 кварталов; эффект умножается на (8 − лаг) / 8.
  • Синергии M1+M2, M10+M12 и M5+M6 фиксированы и применяются в районе первой меры.
  • M1 и M3 несовместимы в любом районе; M4/M7 и M5/M13 несовместимы в одном районе.
  • Отрицательный эффект M11 на T1 сохранен. Все эффекты суммируются, затем показатели ограничиваются диапазоном 0–100. Порядок выбора не влияет на результат.
  • Любое нарушение блокирует расчет и AI.

Приложение открывается с пустым планом. Фильтр показывает карточки по направлениям; карточка содержит цену, лаг и эффекты. Недопустимое добавление блокируется с объяснением. Изменение района выбранной меры повторно проверяет ограничения; при конфликте сохраняется исходный район. Удалить меру можно в карточке магазина или одном из пяти слотов плана.

Кнопка «Загрузить пример» добавляет M7/Нура, M8/Нура, M10/Нура, M12/город, M5/Сарыарка. Этот набор стоит 95 ед. и дает 56.54307 балла. Сработавшая синергия — M10+M12. Пока в плане меньше пяти мер, карта показывает базу, а итоговый Score, AI и экспорт недоступны.

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

Интерактивная карта Астаны

На главном экране карты показаны Есиль, Алматы, Сарыарка, Байконур и Нура — пять районов датасета. Карта использует настоящие контуры OpenStreetMap, сохраненные в data/astana_districts.geojson, и синтетические показатели кейса. Это выборка районов кейса, а не полная текущая административная карта города.

  • Нажатие на контур или подпись выбирает район, выделяет границу и обновляет панель с баллом и двумя самыми слабыми показателями.
  • Выбранный район подставляется в новые районные карточки. Уже добавленные меры сохраняют свои назначения.
  • Через «Перенести выбранную меру» можно назначить существующую меру выбранному району; бюджет и ограничения проверяются снова, результат пересчитывается.
  • Слои показывают районный балл, прирост к базе или наличие критических показателей. Переключатель «До / После» сравнивает состояния; в неполном плане доступна только база.
  • Масштаб меняется колесом и кнопками, карта перемещается перетаскиванием. «Весь город» возвращает исходный вид. Район также можно выбрать из списка с клавиатуры.
  • Контуры, подписи и выбор района работают без подложки. Подложка улиц CARTO требует доступа к интернету и отключается переключателем.

Контуры © OpenStreetMap contributors, лицензия ODbL 1.0. Дата снимка и ссылки на исходные отношения OSM сохранены в GeoJSON. Геометрию можно обновить командой .\.venv\Scripts\python.exe scripts/fetch_districts.py; она обращается к Overpass API и требует интернета. Описание источников — в data/README.md.

Формула Astana Quality of Life Score

Все показатели ориентированы одинаково: больше — лучше. Веса T1, T2, E1, E2, S1, S2, B1, B2, C1, C2 соответственно:

0.10, 0.10, 0.09, 0.11, 0.11, 0.11, 0.09, 0.09, 0.10, 0.10

D_d   = Σ w_k × I'_dk
D_avg = Σ pop_d × D_d
Ncrit = число пар (район, показатель), где I'_dk < 40
Score = clip(0.7 × D_avg + 0.3 × min(D_d) − Ncrit, 0, 100)

Базовый результат без действий: 52.55768, средний районный балл: 56.8624, минимум: 49.18 в Нуре, критических значений: 2. Пример из документа: 56.54307. Баланс 20 ед. на направление в непрерывной модели: 64.58 после округления.

Промежуточные вычисления не округляются. AI не задает и не меняет числовой Score. Это симуляция синтетических данных, а не прогноз реального бюджета или состояния Астаны.

Структура

streamlit_app.py                    Streamlit UI, графики, состояние сессии, экспорт
src/data.py               исходные показатели, веса, 14 мер и синергии
src/model.py              чистые расчетные функции и валидаторы
src/planner.py            добавление, удаление и перенос мер с проверками
src/catalogue.py          карточки, фильтры и синхронизация выбранного района
src/game_ui.py            игровой HUD, карточки районов, слоты и обратная связь запуска
src/landing.py            карточки первоначального обзора
src/geography.py          GeoJSON, цвета, подписи и разбор выбора на карте
src/map_view.py           карта Pydeck и панель выбранного района
src/ai.py                 безопасная загрузка ключа и отчет OpenAI
app.py                    альтернативный вход в тот же Streamlit UI
assets/catalogue.css      оформление каталога, плана и карты
assets/game.css           игровая тема, адаптивная сетка и состояния карточек
assets/landing.css        сохранённое оформление прежнего первого экрана
assets/city-hero.svg       сохранённая панорама прежнего первого экрана
data/                     локальные границы районов и сведения об источнике
scripts/fetch_districts.py загрузка и сборка геометрии OSM
.streamlit/config.toml    тема, localhost, отключенная телеметрия
.env.example              образец настройки без секретов
docs/case.md              копия условий кейса
docs/dataset-source.md    копия датасета
tests/                    расчетная модель, API-контракты и Streamlit UI

Приложение привязано к localhost по умолчанию. Для развертывания в контейнере можно запустить python -m streamlit run streamlit_app.py --server.address 0.0.0.0 --server.port 8501 и передать ключи через секреты платформы как переменные окружения. Публичное размещение и управление доступом выполняются отдельно.

Проверка

.\.venv\Scripts\python.exe -m pip install -r requirements-dev.txt
.\.venv\Scripts\python.exe -m pytest -q

Тесты проверяют контрольные значения датасета, бюджетные границы, неверные типы, убывающую отдачу, порог <40, все несовместимости, отрицательные эффекты, синергии, независимость от порядка и отсутствие мутации исходных данных. API-тесты используют реальный SDK OpenAI с локальным MockTransport, проверяют один запрос OpenAI, формат тела, таймаут и безопасную обработку ошибок. AppTest проверяет каталог, слайдеры, перенос и откат конфликтной меры, сохранение планов между режимами, блокировку невалидного набора, кеширование, устаревшие отчеты и повтор отчета после ошибки. Географические тесты проверяют замкнутые контуры, расположение подписей внутри районов, соответствие цветов и чисел расчетной модели, события выбора и параметры кириллических подписей.

Тесты не отправляют запросы внешним сервисам и не расходуют API-квоты. Для проверки реальных AI-ответов требуются рабочий ключ и доступ сервера к OpenAI. Игровые UI-тесты дополнительно проверяют бюджет, слоты, маркеры указов, выбор района, реальные критические предупреждения, синергии, повторную валидацию запуска и отсутствие повторных уведомлений.

Источники

Материалы прочитаны 23 сентября 2026 года. Локальные копии условий сохраняют исходную синтетическую постановку; приложение не загружает Google Docs при запуске.

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

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

LordGibrat 18baurdanial 8baurdanial 3

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

  • 2026-09-23 18:12Revise README with project details and setup instructionsLordGibrat
  • 2026-09-23 18:00Merge Astana simulator into mainLordGibrat
  • 2026-09-23 18:00Document Astana simulator for hackathon judgesbaurdanial
  • 2026-09-23 17:56Update simulator README for current interface and setupbaurdanial
  • 2026-09-23 17:53Merge team navigation changes with tested catalogue layoutbaurdanial
  • 2026-09-23 17:50Restore editorial landing and expose scenario cataloguebaurdanial
  • 2026-09-23 17:43Fix simulator navigation and catalogue accessLordGibrat
  • 2026-09-23 17:39Update README.mdbaurdanial
Все коммиты (29)
  • 2026-09-23 16:40Build game-style Astana command center without changing simulation rulesbaurdanial
  • 2026-09-23 16:29Redesign simulator landing and use 100-unit budgetsbaurdanial
  • 2026-09-23 16:11Use OpenAI only and add city deficit overviewbaurdanial
  • 2026-09-23 15:58Add isolated Astana city simulatorbaurdanial
  • 2026-09-23 15:29Create sdf,txtLordGibrat
  • 2026-09-23 15:24Refine map click handling and launch animationLordGibrat
  • 2026-09-23 15:23Switch decree selection to interactive map clicksLordGibrat
  • 2026-09-23 15:17Harden animation session state initializationLordGibrat
  • 2026-09-23 15:16Add streamlit-lottie dependencyLordGibrat
  • 2026-09-23 15:16Add map markers, motion feedback and Lottie reactionsLordGibrat
  • 2026-09-23 15:08Delete .envbaurdanial
  • 2026-09-23 15:03Add Folium map dependenciesLordGibrat
  • 2026-09-23 15:03Replace dashboard with interactive Astana mapLordGibrat
  • 2026-09-23 14:55Add OpenAI API key to .env filebaurdanial
  • 2026-09-23 14:31Add Streamlit launch configurationLordGibrat
  • 2026-09-23 14:31Configure project Python interpreterLordGibrat
  • 2026-09-23 14:31Add application dependenciesLordGibrat
  • 2026-09-23 14:31Add game-style Streamlit simulatorLordGibrat
  • 2026-09-23 14:31Add scenario validation and score calculatorLordGibrat
  • 2026-09-23 14:31Add Python and local secret ignoresLordGibrat
  • 2026-09-23 14:30Document simulator setup and rulesLordGibrat
Структура репозитория (45 файлов)
.gitignore
README.md
app.py
astana_simulator/.env.example
astana_simulator/.gitignore
astana_simulator/.streamlit/config.toml
astana_simulator/README.md
astana_simulator/app.py
astana_simulator/assets/catalogue.css
astana_simulator/assets/city-hero.svg
astana_simulator/assets/game.css
astana_simulator/assets/landing.css
astana_simulator/assets/skyline.svg
astana_simulator/assets/style.css
astana_simulator/assets/workspace.css
astana_simulator/data/README.md
astana_simulator/data/astana_districts.geojson
astana_simulator/docs/case.md
astana_simulator/docs/dataset-source.md
astana_simulator/pytest.ini
astana_simulator/requirements-dev.txt
astana_simulator/requirements-lock.txt
astana_simulator/requirements.txt
astana_simulator/scripts/fetch_districts.py
astana_simulator/src/__init__.py
astana_simulator/src/ai.py
astana_simulator/src/catalogue.py
astana_simulator/src/data.py
astana_simulator/src/game_ui.py
astana_simulator/src/geography.py
astana_simulator/src/landing.py
astana_simulator/src/map_view.py
astana_simulator/src/model.py
astana_simulator/src/planner.py
astana_simulator/streamlit_app.py
astana_simulator/tests/test_ai.py
astana_simulator/tests/test_app.py
astana_simulator/tests/test_catalogue_app.py
astana_simulator/tests/test_game_ui.py
astana_simulator/tests/test_geography.py
astana_simulator/tests/test_model.py
astana_simulator/tests/test_planner.py
calculator.py
requirements.txt
sdf,txt
Ветки (2)

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

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

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