Отлично, вижу все репозитории. Прежде чем накидывать дорожную карту, я хочу опереться на то, что у нас реально есть в данных и в коде — иначе план будет оторван от реальности (ты сам отметил, что не уверен, как хранятся ставки). Запущу параллельное исследование по трём направлениям: схема БД, фронтенд analytics-ui и backend API.
Запустил разведку по коду в фоне (схема БД, фронт, бэкенд) — это займёт пару минут. Пока она идёт, дам тебе предварительную картину идей, а как вернутся результаты — соберу всё в обоснованную дорожную карту с учётом того, что реально лежит в БД.
Параллельно, чтобы наш разговор шёл предметно, вот как я структурно вижу раздел «Игроки» — обсудим, пока считается разведка.
Из чего состоит раздел «Игроки» (концептуально)
Я бы разделил на три слоя, и это же станет осью дорожной карты:
1. Поиск/список игроков — точка входа. Найти игрока по нику, увидеть топ по рейтингу (у нас уже есть данные сильнейших — это эталон из твоей заметки про win-rate).
2. Профиль игрока — то, что на скриншоте DiceChess: шапка (рейтинг, рейтинг x2, дата регистрации, кол-во партий), статистика (W/L/D), история партий. Фильтр — то, что ты хочешь оставить.
3. Аналитика поверх профиля — то, чего у них нет и где наша ценность: разрезы win-rate по позиции+броску, динамика рейтинга во времени, силовой профиль (против кого выигрывает/проигрывает), привязка к bot:
Про фильтр — твои уточнения принял
- Режим (Classic / X2) — да.
- Цвет — да.
- Оппонент: человек / робот / все — да, по аналогии с цветом (ничего не выбрано = все, иначе фильтр). Это ложится на наш
player_type/негативные id ботов. - Конкретный оппонент — да, через автокомплит.
- Ставка: упростить до
бесплатные (турнирные, ставка=0) / низкая / средняя / высокая+ опционально диапазон. Вот тут как раз ключевой вопрос — хранится ли ставка у нас вообще. Разведка это проверяет.
Дам полную дорожную карту, как только вернётся анализ кода.
Разведка вернулась. Прочитаю полный результат, чтобы собрать дорожную карту на фактах.
Разведка вернулась с полной картиной. Хорошая новость: фундамент под раздел уже частично заложен, и почти все твои хотелки по фильтру ложатся на существующие данные. Давай разберём предметно.
Что у нас уже есть (реальность кода)
- Фронт — dicechess-analytics-ui: SvelteKit 2 + Svelte 5 (runes), TS, Tailwind v4, SPA. Пункт меню «Players» уже есть как заглушка «Coming soon» — +layout.svelte:68. Графической библиотеки нет — все «графики» руками на Tailwind-дивах (горизонтальный win-rate бар в
OpeningsExplorer.svelte, шкала с CI вPositionEquity.svelte). URL = канон состояния фильтров (шарящиеся ссылки). - Готовые кирпичи под наш раздел:
PlayerSearch.svelte(автокомплит игрока с дебаунсом — прямо переиспользуем для выбора оппонента), инлайн фильтр-бар вGameList.svelte, эндпоинтGET /api/players?username=. - Бэк — dicechess-analytics: Scala 3 (http4s + Tapir + Doobie). Есть
/api/games(с фильтрами),/api/players,/api/positions/equityи/continuations. Агрегации по игроку пока нет — её надо написать, но есть точный образец:PositionsRepository.equity(паттернcount(*) FILTER (WHERE ...)). С ~140k партий индексы поwhite_player_id/black_player_idуже есть → matview пока не нужен. - Бонус: в
docs/.../milestones.mdвеха v0.4 (2026-07-06) уже планирует «player rating history graphs» + materialized views. То есть мы не изобретаем — мы попадаем в твою же дорожную карту.
Твой фильтр → что реально лежит в БД
Прямо отвечаю на «не уверен, как мы храним ставки» и остальное:
| Фильтр | Статус | Поле в БД | Нюанс |
|---|---|---|---|
| Режим Classic / X2 | ✅ | games.mode (enum) | уже фильтр-параметр |
| Цвет белые / чёрные | ✅ | производное от white_player_id/black_player_id | отдельной колонки «цвет» нет, выводим из FK |
| Оппонент: человек / робот / все | ✅ | players.player_type оппонента | по твоей логике (ничего не выбрано = все). Caveat: player_type ставится при первой вставке и не обновляется → редкие мислейблы; индекса нет |
| Конкретный оппонент | ✅ | FK + username (GIN-триграмма) | PlayerSearch.svelte готов |
| Дата | ✅ | games.started_at | уже date_from/date_to |
| Результат W/L/D | ✅ | games.result (1/-1/0, бело-центрично) | для игрока комбинируем с цветом |
| Ставка | ⚠️ | initial_stake_amount (raw int), *_money_delta, stake_currency | хранится сырым числом фишек, без бакетов. Лесенка dicechess: 0,1,3,5,10,25,100,300,500,1K,2K,5K,10K,25K,50K. Тиры «низкая/средняя/высокая» определяем мы сами |
| Рейтинг-дельта | ❌ | нет колонки | только снапшоты white_rating/black_rating; дельту считаем оконной LAG по истории |
Важная честность про «бесплатные = турниры»: мы можем отличить бесплатные игры (stake = 0/NULL), но отдельного флага турнира в структурированном виде нет. Турнирную игру от обычной free-партии по колонкам не отличить (только потенциально из metadata_json, который неструктурирован). Так что пункт «бесплатные» сделаем как ставка = 0, но называть это «турнирными» некорректно — обсудим формулировку.
Каталог идей (от базы к продвинутому)
🟢 Паритет с DiceChess (берём как базу):
- Профиль: ник, тип (человек/бот), текущий рейтинг + рейтинг x2, дата первой/последней партии, всего игр.
- W/L/D кольцо/бар + win-rate.
- Список последних партий (переиспользуем
GameCard). - «Player offered x2 / Opponent offered x2» — accepted/declined. У нас есть
game_events(DOUBLE_OFFER/ACCEPT/DECLINE) → делаем. - Средн. число ходов (
total_turns), средн. время партии.
🟡 Разрезы и графики (то, что тебе нравится):
- Win-rate по цвету (за белых vs за чёрных) — классический инсайт.
- По режиму (classic vs x2), по типу оппонента (vs люди / vs боты), по тиру ставки, по контролю времени (
time_initial_sec/increment— данные есть: 1+1, 2m, …). - График рейтинга во времени (из снапшотов по партиям) — то, что DiceChess делает плохо/никак, и это прямо веха v0.4.
- Кривая профита (накопительный
money_delta).
🔴 Наша уникальная ценность (чего на сайте нет вообще):
- Equity vs Result: переигрывал ли игрок свою позицию? У нас есть
positions+equity+ engine-wasm в браузере → можем сказать «выиграл 58%, но позиции тянули на 52% → отличный конвертер / везунчик». Это связка с твоим ядром (позиция+бросок→winrate). - Анализ удачи/дисперсии: распределение бросков (
dice_sorted) у игрока — чисто dice-chess фича. - Сила против рейтинга: win-rate по бакетам рейтинга оппонента (где «потолок» игрока).
- H2H и «любимые жертвы / неудобные оппоненты».
- Сравнение игрока с
bot:<algorithm>(связка с extension) — сила алгоритма против людей. - Сравнение двух игроков рядом; собственные лидерборды (по win-rate / equity-adjusted), лидерборд наших ботов.
Дорожная карта
- M1 — Профиль (MVP, без фильтра). Активируем nav →
/players(поиск + топ по рейтингу) +/players/[id](шапка, W/L/D, последние партии). Бэк:GET /api/players/{id}/stats(зеркалоequity). Доказывает агрегацию end-to-end. - M2 — Фильтр. Инлайн-бар: режим / цвет / оппонент(чел-бот-все) / конкретный оппонент / тир ставки / дата. Стат пересчитывается живьём, URL = канон.
- M3 — Разрезы + графики. Win-rate по цвету/режиму/оппоненту/времени, x2-офферы, средние.
- M4 — Рейтинг во времени + профит-кривая (веха v0.4, тут понадобится hand-rolled SVG line chart).
- M5 — H2H + сила против рейтинга оппонента.
- M6 — Наш дифференциатор: Equity-vs-Result, анализ удачи, сравнение с ботами.
Рекомендация по минимуму
Начать с M1 — это самый дешёвый цельный кусок, который сразу включает раздел и проверяет новый агрегирующий эндпоинт на бэке. Фильтр (M2) логично идёт сразу следом, раз он для тебя ключевой.
Прежде чем кодить, два открытых вопроса к тебе:
- Тиры ставок — задаём свои границы? Предлагаю:
free=0,low=1–25,medium=100–1000,high=2000+(по лесенке dicechess). Ок или другие? - Старт — берём M1 как минимум (профиль read-only), или ты хочешь сразу M1+M2 вместе (профиль с фильтром), раз фильтр для тебя главный?
Как определимся — могу зафиксировать дорожную карту в dicechess-docs/milestones.md и начать с первого пункта.