Отлично, вижу все репозитории. Прежде чем накидывать дорожную карту, я хочу опереться на то, что у нас реально есть в данных и в коде — иначе план будет оторван от реальности (ты сам отметил, что не уверен, как хранятся ставки). Запущу параллельное исследование по трём направлениям: схема БД, фронтенд analytics-ui и backend API.

Запустил разведку по коду в фоне (схема БД, фронт, бэкенд) — это займёт пару минут. Пока она идёт, дам тебе предварительную картину идей, а как вернутся результаты — соберу всё в обоснованную дорожную карту с учётом того, что реально лежит в БД.

Параллельно, чтобы наш разговор шёл предметно, вот как я структурно вижу раздел «Игроки» — обсудим, пока считается разведка.

Из чего состоит раздел «Игроки» (концептуально)

Я бы разделил на три слоя, и это же станет осью дорожной карты:

1. Поиск/список игроков — точка входа. Найти игрока по нику, увидеть топ по рейтингу (у нас уже есть данные сильнейших — это эталон из твоей заметки про win-rate).

2. Профиль игрока — то, что на скриншоте DiceChess: шапка (рейтинг, рейтинг x2, дата регистрации, кол-во партий), статистика (W/L/D), история партий. Фильтр — то, что ты хочешь оставить.

3. Аналитика поверх профиля — то, чего у них нет и где наша ценность: разрезы win-rate по позиции+броску, динамика рейтинга во времени, силовой профиль (против кого выигрывает/проигрывает), привязка к bot: из extension.

Про фильтр — твои уточнения принял

  • Режим (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 / X2games.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/Dgames.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) логично идёт сразу следом, раз он для тебя ключевой.

Прежде чем кодить, два открытых вопроса к тебе:

  1. Тиры ставок — задаём свои границы? Предлагаю: free=0, low=1–25, medium=100–1000, high=2000+ (по лесенке dicechess). Ок или другие?
  2. Старт — берём M1 как минимум (профиль read-only), или ты хочешь сразу M1+M2 вместе (профиль с фильтром), раз фильтр для тебя главный?

Как определимся — могу зафиксировать дорожную карту в dicechess-docs/milestones.md и начать с первого пункта.