О проекте
Команда: Latitude51N
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Рабочий MVP
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Городской симулятор на Next.js: пользователь выбирает 5 мер из каталога M1–M14 в пределах бюджета 100, видит последствия по 5 районам Астаны на 2D- и 3D-карте, ищет лучшую замену одной меры и получает AI-анализ плана.
Как устроено
Движок dataset.ts задаёт 5 районов, 10 показателей, доли населения, 14 мер, лаги, синергии и несовместимости. Эффект меры = эффект×(8−лаг)/8 на горизонте 8 кварталов. Score = 0.7×D_avg + 0.3×min(D_d) − N_crit. validate.ts проверяет план, improve.ts перебирает допустимые замены одной меры. Сервер /api/analyze заново считает план и передаёт в OpenAI только проверенные числа для объяснения.
Ключевые решения
- weighted district scoring
- lag/synergy model
- single-swap improvement search
- server-side revalidation
Метрики
Контрольные значения: базовый Score 52.55768; эталонный план организаторов (стоимость 95) даёт Score 56.54307.
Особенности
Движок проверен контрольными значениями из примера организаторов. Сервер не доверяет стоимости и Score, пришедшим из браузера, и пересчитывает их сам. Есть 3D-карта районов на GeoJSON и поиск лучшей замены одной меры.
Стек
TypeScript · React · Next.js · Tailwind CSS · MapLibre GL · Three.js · React Three Fiber · Vitest
Модели
GPT-4o-mini
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-2c07d2ee-latitude51n.
README
README.mdLatitude 51 — «Аким на 5 часов»
HackAlem AI-симулятор управления городом от команды Latitude51N.
1. Назначение
Latitude 51 помогает пользователю в роли акима выбрать пять городских мер в рамках бюджета 100, увидеть последствия по пяти районам Астаны и сравнить компромиссы. Целевая аудитория текущей версии — участники демо и жюри хакатона: сценарий показывает, как ограниченный бюджет и лаг реализации влияют на качество решений.
Показатели районов в симуляторе синтетические и относятся к модели задачи, а не к официальному прогнозу города.
2. Что реализовано
- выбор ровно пяти разных мер из каталога M1–M14;
- бюджет до 100 единиц, районные и городские меры;
- валидация районов, направлений, бюджета и несовместимых сочетаний;
- детерминированный расчёт Astana Quality of Life Score;
- визуализация пяти районов на карте и отображение изменений показателей;
- готовый сценарий организаторов стоимостью 95;
- поиск лучшей допустимой замены одной выбранной меры;
- серверный AI-анализ сильных сторон, рисков и компромиссов рассчитанного плана.
3. Пользовательский сценарий
- Открыть
/и перейти в симулятор или сразу открыть/simulator. - Выбрать район для районной меры и заполнить пять слотов. Городские меры применяются ко всем районам.
- Получить подсказки о недоступных мерах и увидеть стоимость, остаток бюджета и изменения районов.
- После заполнения пяти слотов получить Score и детализацию по районам.
- Запустить поиск одной замены, при необходимости применить её, затем запросить AI-анализ.
Score и все числовые последствия вычисляются кодом. LLM получает готовый результат и формулирует объяснение; она не является источником чисел.
4. Модель и данные
Движок использует пять районов, десять показателей, доли населения, 14 мер, веса, лаги, синергии и несовместимости, заданные в src/lib/engine/dataset.ts.
- горизонт модели: 8 кварталов;
- эффект меры:
эффект × (8 − лаг) / 8; - после суммирования эффектов и синергий показатель ограничивается диапазоном 0–100;
- районная мера действует в выбранном районе, городская — во всех пяти и оплачивается один раз;
D_d— взвешенная оценка района;D_avg— оценка, взвешенная по доле населения;N_crit— число значений показателей строго ниже 40;Score = 0.7 × D_avg + 0.3 × min(D_d) − N_crit.
Данные и формула реализованы локально. Файл GeoJSON в public/geo/astana-districts.geojson используется для границ районов. Внешняя карта MapLibre может загружать картографическую подложку OpenFreeMap при наличии сети.
5. Технологии и интеграции
- TypeScript и React 19;
- Next.js 16 App Router;
- Tailwind CSS 4;
- MapLibre GL, Three.js, React Three Fiber и drei;
- Motion и Phosphor Icons;
- Vitest для тестов;
- OpenAI Chat Completions API для
/api/analyze.
Модель OpenAI задаётся переменной OPENAI_MODEL; по умолчанию используется gpt-4o-mini. API-ключ используется только серверным route handler. Статического AI-fallback в текущей версии нет: без ключа endpoint возвращает ошибку 503.
6. Архитектура
src/lib/engine/dataset.ts данные, веса, меры и ограничения
src/lib/engine/validate.ts правила допустимости плана
src/lib/engine/score.ts расчёт Score и районных результатов
src/lib/engine/improve.ts перебор допустимых замен одной меры
src/lib/engine/engine.test.ts контрольные и поведенческие тесты
src/app/page.tsx лендинг
src/app/simulator/page.tsx страница симулятора
src/components/Simulator.tsx состояние пользовательского сценария
src/components/PlanBuilder.tsx выбор мер и районов
src/components/CityMapPanel.tsx карта и визуализация районов
src/components/ResultsPanel.tsx Score, ошибки, улучшение и AI-анализ
src/app/api/analyze/route.ts серверная валидация, расчёт и запрос OpenAI
Клиент отправляет в /api/analyze только список решений. Сервер заново вызывает валидатор и движок, рассчитывает улучшение и формирует prompt из проверенных данных. Стоимость, эффекты и Score из браузера не принимаются.
7. Установка и запуск
Требуется Node.js 20 или новее.
npm install
cp .env.local.example .env.local
# при необходимости отредактируйте .env.local и добавьте OPENAI_API_KEY
npm run dev
Откройте http://localhost:3000. Симулятор находится по адресу http://localhost:3000/simulator.
Переменные окружения:
OPENAI_API_KEY=ключ OpenAI для серверного анализа
OPENAI_MODEL=gpt-4o-mini
.env.local исключён из Git, а .env.local.example содержит только шаблонные значения.
8. Как проверить
Автоматические команды:
npm test
npm run lint
npm run build
Повторяемый демо-сценарий:
- Откройте
/simulator?plan=referenceили нажмите «Загрузить пример организаторов». - Проверьте пять решений: M7 в Нуре, M8 в Нуре, M10 в Нуре, M12 для города, M5 в Сарыарке.
- Убедитесь, что стоимость равна 95, а Score после округления до пяти знаков равен
56.54307. - Нажмите «Найти лучшую замену», затем при наличии ключа — «Объяснить сценарий».
- Нажмите «Начать заново» и проверьте, что план и производные результаты сбрасываются.
Контрольные значения движка: базовый Score без действий — 52.55768; тесты также проверяют правила, лаг, городские меры, порядок решений и улучшение одной замены.
9. Ограничения текущей версии
- модель использует синтетические показатели и не является прогнозом реальных городских показателей;
- полный план требует ровно пяти решений, хотя остаток бюджета может быть положительным;
- поиск улучшения проверяет только замены одной меры и не доказывает глобальный оптимум;
- AI-анализ требует
OPENAI_API_KEY, сетевого доступа к OpenAI и доступной модели; - карта с внешней подложкой зависит от сети; локальный GeoJSON является частью репозитория;
- автоматические браузерные E2E-тесты в репозитории не настроены.
10. Deployed-версия
В текущем репозитории ссылка на deployed-версию не указана. Для проверки используется локальный запуск npm run dev.
11. Официальные материалы
HACKALEM_BRIEF.md находится в корне репозитория и содержит рабочую сводку требований. Каталог docs/ с официальными PDF и DOCX в текущем checkout отсутствует; README не делает утверждений о содержимом этих файлов.
Авторы коммитов
История коммитов
- 2026-09-23 18:09Rename project from QALA to Latitude 51Diyarsh
- 2026-09-23 18:02Simulator: interactive district schematic, richer decision slots, results revealDiyarsh
- 2026-09-23 17:37UX polish: touch targets, press feedback, a11y landmarks, glass nav, 404 and iconDiyarsh
- 2026-09-23 17:27Add scroll-driven 3D landing and move simulator to /simulatorDiyarsh
- 2026-09-23 16:40Add 3D Astana map, hero model and 21st.dev componentsDiyarsh
- 2026-09-23 16:24Harden rules, server-side scoring and plan builder UXDiyarsh
- 2026-09-23 15:34Add QALA simulator with verified score engine and AI explainDiyarsh
Структура репозитория (96 файлов)
.cursor/skills/frontend-design/LICENSE.txt .cursor/skills/frontend-design/SKILL.md .cursor/skills/ui-ux-pro-max/LICENSE .cursor/skills/ui-ux-pro-max/SKILL.md .cursor/skills/ui-ux-pro-max/data/app-interface.csv .cursor/skills/ui-ux-pro-max/data/catalog-summary.json .cursor/skills/ui-ux-pro-max/data/charts.csv .cursor/skills/ui-ux-pro-max/data/colors.csv .cursor/skills/ui-ux-pro-max/data/data-provenance.json .cursor/skills/ui-ux-pro-max/data/google-font-licenses.json .cursor/skills/ui-ux-pro-max/data/google-fonts.csv .cursor/skills/ui-ux-pro-max/data/icons.csv .cursor/skills/ui-ux-pro-max/data/landing.csv .cursor/skills/ui-ux-pro-max/data/motion.csv .cursor/skills/ui-ux-pro-max/data/phosphor-icons-upstream.json .cursor/skills/ui-ux-pro-max/data/products.csv .cursor/skills/ui-ux-pro-max/data/react-performance.csv .cursor/skills/ui-ux-pro-max/data/stacks/angular.csv .cursor/skills/ui-ux-pro-max/data/stacks/astro.csv .cursor/skills/ui-ux-pro-max/data/stacks/avalonia.csv .cursor/skills/ui-ux-pro-max/data/stacks/flutter.csv .cursor/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv .cursor/skills/ui-ux-pro-max/data/stacks/javafx.csv .cursor/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv .cursor/skills/ui-ux-pro-max/data/stacks/laravel.csv .cursor/skills/ui-ux-pro-max/data/stacks/nextjs.csv .cursor/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv .cursor/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv .cursor/skills/ui-ux-pro-max/data/stacks/react-native.csv .cursor/skills/ui-ux-pro-max/data/stacks/react.csv .cursor/skills/ui-ux-pro-max/data/stacks/shadcn.csv .cursor/skills/ui-ux-pro-max/data/stacks/svelte.csv .cursor/skills/ui-ux-pro-max/data/stacks/swiftui.csv .cursor/skills/ui-ux-pro-max/data/stacks/threejs.csv .cursor/skills/ui-ux-pro-max/data/stacks/uno.csv .cursor/skills/ui-ux-pro-max/data/stacks/uwp.csv .cursor/skills/ui-ux-pro-max/data/stacks/vue.csv .cursor/skills/ui-ux-pro-max/data/stacks/winui.csv .cursor/skills/ui-ux-pro-max/data/stacks/wpf.csv .cursor/skills/ui-ux-pro-max/data/styles.csv .cursor/skills/ui-ux-pro-max/data/typography.csv .cursor/skills/ui-ux-pro-max/data/ui-reasoning.csv .cursor/skills/ui-ux-pro-max/data/ux-guidelines.csv .cursor/skills/ui-ux-pro-max/references/pro-rules.md .cursor/skills/ui-ux-pro-max/references/quick-reference.md .cursor/skills/ui-ux-pro-max/scripts/core.py .cursor/skills/ui-ux-pro-max/scripts/design_system.py .cursor/skills/ui-ux-pro-max/scripts/reasoning_contract.py .cursor/skills/ui-ux-pro-max/scripts/search.py .cursor/skills/ui-ux-pro-max/scripts/validate_data.py .env.local.example .gitignore AGENTS.md CLAUDE.md HACKALEM_BRIEF.md README.md eslint.config.mjs next.config.ts package-lock.json package.json postcss.config.mjs public/geo/astana-districts.geojson scripts/copy-maplibre-worker.mjs src/app/api/analyze/route.ts src/app/globals.css src/app/icon.svg src/app/layout.tsx src/app/not-found.tsx src/app/page.tsx src/app/simulator/page.tsx src/components/CityMap3D.tsx src/components/CityMapPanel.tsx src/components/CityModel3D.tsx src/components/DistrictSchematic.tsx src/components/PlanBuilder.tsx src/components/ResultsPanel.tsx src/components/Simulator.tsx src/components/landing/LandingStory.tsx src/components/landing/Sections.tsx src/components/landing/StartButton.tsx src/components/ui/animated-tabs.tsx src/components/ui/magnetic.tsx src/components/ui/number-ticker.tsx src/components/ui/text-effect.tsx src/lib/engine/dataset.ts src/lib/engine/engine.test.ts src/lib/engine/improve.ts src/lib/engine/index.ts src/lib/engine/score.ts src/lib/engine/types.ts src/lib/engine/validate.ts src/lib/geo.ts src/lib/landing.ts src/lib/utils.ts tsconfig.json vitest.config.ts
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.