Браузерная настольная гонка для 2–12 игроков. Игроки бросают кубики и перемещают фишки по круговому треку из 36 клеток. Сочетание удачи (броски кубиков) и стратегии (решение когда ехать, а когда бросить ещё раз).
| Компонент | Технология |
|---|---|
| Игровой движок | Phaser 3 |
| Язык | TypeScript 5 |
| Сборщик (клиент) | Vite 6 |
| Сервер | Node.js + Express 4 |
| Шаблонизатор | Pug 3 |
| Тесты | Vitest |
| Линтинг | ESLint 9 + Prettier |
| Контейнеризация | Docker |
Boot → Game → Menu → Rules → About
- BootScene — инициализация, загрузка лого
- GameScene — основной игровой цикл: поле из 36 клеток, 3 кубика с анимацией, фишки игроков, UI статистики (тоннаж/перегревы, текущий игрок, круг), кнопки Roll/Race, анимация медалей
- MenuScene — управление игроками: добавление/удаление (2–12), настройка AI, просмотр статистики и медалей, настройки игры
- RulesScene — 11-слайдовый туториал с правилами игры
- AboutScene — информация об авторе
Минимальный Express-сервер, отдаёт статику (собранный Vite-бандл, изображения, звуки) и рендерит HTML через Pug.
caferacer/
├── src/
│ ├── client/
│ │ ├── index.ts # Точка входа, конфиг Phaser
│ │ ├── index.html # HTML для Vite dev server
│ │ ├── core/
│ │ │ └── GameState.ts # Централизованное управление состоянием
│ │ ├── entities/
│ │ │ ├── Player.ts # Класс игрока со статистикой
│ │ │ ├── Dice.ts # Логика бросков кубиков
│ │ │ └── Board.ts # Расчёт позиций на поле
│ │ ├── ai/
│ │ │ └── AIPlayer.ts # AI-логика принятия решений
│ │ ├── config/
│ │ │ ├── gameConfig.ts # Константы игры (поле, кубики, UI)
│ │ │ └── assetManifest.ts # Реестр всех ассетов
│ │ ├── scenes/
│ │ │ ├── BootScene.ts # Инициализация
│ │ │ ├── GameScene.ts # Основная игровая сцена
│ │ │ ├── MenuScene.ts # Управление игроками и настройками
│ │ │ ├── RulesScene.ts # Слайды с правилами
│ │ │ └── AboutScene.ts # Информация об авторе
│ │ ├── systems/
│ │ │ ├── AudioManager.ts # Управление звуковыми эффектами
│ │ │ └── SaveManager.ts # Сохранение/загрузка игры
│ │ ├── types/
│ │ │ └── index.ts # TypeScript интерфейсы
│ │ ├── utils/
│ │ │ └── helpers.ts # Утилиты (shuffle и др.)
│ │ └── __tests__/ # Unit-тесты
│ │ ├── AIPlayer.test.ts # Тесты AI-логики
│ │ ├── Board.test.ts # Тесты расчёта позиций
│ │ ├── Dice.test.ts # Тесты бросков кубиков
│ │ ├── GameState.test.ts # Тесты управления состоянием
│ │ ├── Player.test.ts # Тесты механик игрока
│ │ └── helpers.test.ts # Тесты утилит
│ └── server/
│ ├── app.ts # Конфигурация Express
│ ├── bin/
│ │ └── www.ts # Точка входа сервера (порт 4001)
│ └── routes/
│ └── index.ts # Маршрут /
├── public/
│ ├── images/ # 57 PNG (28 UI, 12 фишек, 11 слайдов, 5 спрайтшитов, медали)
│ ├── sounds/ # 6 MP3-файлов
│ └── stylesheets/
│ └── style.css # Стили
├── views/
│ ├── layout.pug # Базовый шаблон
│ ├── index.pug # Главная страница
│ └── error.pug # Страница 404
├── package.json
├── tsconfig.json # TypeScript (общий)
├── tsconfig.server.json # TypeScript (сервер)
├── vite.config.ts # Конфигурация Vite
├── vitest.config.ts # Конфигурация тестов
├── eslint.config.js # ESLint 9 flat config
├── .prettierrc # Prettier
├── Dockerfile # Multi-stage Docker build
└── docker-compose.yml # Docker Compose
- Node.js >= 18
- npm
npm installnpm run devЗапускает параллельно:
- Vite dev server — горячая перезагрузка клиента
- Nodemon — автоперезапуск сервера при изменениях
npm run build
npm startПриложение будет доступно на http://localhost:4001
docker compose up --buildПриложение будет доступно на http://localhost:4001
| Команда | Описание |
|---|---|
npm run dev |
Запуск в режиме разработки (Vite + Nodemon) |
npm run build |
Сборка сервера и клиента |
npm start |
Запуск продакшн-сервера |
npm test |
Запуск тестов (Vitest) |
npm run type-check |
Проверка типов TypeScript |
npm run lint |
Проверка ESLint |
npm run format |
Форматирование Prettier |
42 unit-теста в 6 файлах:
- AIPlayer (8 тестов) — стратегии принятия решений: финиш на последнем круге, бампинг соперников, реакция на перегревы
- Board (10 тестов) — расчёт позиций для всех 36 клеток, смещения при движении
- Dice (4 теста) — случайный выбор граней, валидация результатов
- GameState (11 тестов) — управление игроками, переходы состояний, настройки игры
- Player (4 теста) — отслеживание статистики, сброс данных
- helpers (5 тестов) — утилитарные функции (shuffle и др.)
npm test- 3 кубика за ход, каждый с кастомными гранями
- Кубик 1:
[1, 0, 1, 1, 1, 0]— больше шансов на движение - Кубики 2 и 3:
[1, 0, 1, 0, 1, 0]— 50/50 1= движение,0= перегрев
- Бросок кубиков (с анимацией)
- Решение: Roll (бросить ещё раз для накопления тоннажа) или Race (переместить фишку, завершить ход)
- При повторном броске — накопление тоннажа и перегревов
- 5+ перегревов = штраф, ход заканчивается
- Фишка перемещается по круговому полю из 36 клеток
- Столкновение: попадание на клетку соперника отправляет его в «кафе» (старт)
- Необходимо пройти заданное количество кругов (1–12)
- Первый финишировавший побеждает, остальные ранжируются по порядку финиша
- Включение/выключение бампинга (столкновений) соперников
- Добавляет тактическую глубину
AI использует приоритетное дерево решений:
- Финиш — если последний круг и хватает тоннажа до финиша → Race
- Бампинг — в режиме Hardcore, если можно сбить соперника → Race (пропускает игроков у старта при 5+ участниках)
- Защита от перегрева — если 3+ перегрева и есть тоннаж → Race
- По умолчанию — Roll (безопасный вариант)
- 1-е / 2-е / 3-е место — финишные позиции
- Top Speed — максимальный тоннаж за один ход
- Breaking Player — наибольшее количество перегревов
- Horrible Player — наихудшая удача / больше всех попаданий в кафе
- Loser — наибольшее количество возвратов на старт
- Клавиатура:
M(меню),S(звук),H(hardcore), стрелки (навигация) - Сенсорные кнопки для мобильных устройств
- Отображение статистики в реальном времени
interface PlayerData {
stopz: number; // Перегревы за ход
goz: number; // Тоннаж (очки движения)
posonboard: number; // Позиция на поле (0–36)
lap: number; // Текущий круг
namesprite: string; // Ключ спрайта фишки
robot: boolean; // Управляется AI
nameplayer: string; // Имя игрока
sprite: Phaser.GameObjects.Sprite | null;
posInGame: number; // Позиция в рейтинге
maxTon: number; // Макс. тоннаж за ход
sumLoser: number; // Количество отправок в кафе
sumStopz: number; // Суммарные перегревы
sumBreak: number; // Суммарные торможения
sumStep: number; // Шаги за игру
sumAllStep: number; // Шаги за всё время
medalsPos1: number; // Финиши на 1-м месте
medalsPos2: number; // Финиши на 2-м месте
medalsPos3: number; // Финиши на 3-м месте
medalsfastPlayer: number; // Медали Top Speed
medalsbreakingPlayer: number; // Медали Breaking
medalshorriblePlayer: number; // Медали Horrible
medalsloserPlayer: number; // Медали Loser
}Jirufik — jirufik@gmail.com