Files
ForbiddenStarsApp/frontend/src/pages/HelpPage.tsx
T
NotBigGhostandClaude Opus 5 63a2cddd5e Рейтинг: выбывшие между собой не сравниваются
Решение владельца: в партии все выбывшие проиграли и одинаково слабы, миров у них
нет, поэтому пара двух выбывших в сумму не входит — ни S − E, ни множитель отрыва.
Пары с невыбывшими считаются как прежде, нормировка на N − 1 тоже: при равных
рейтингах результат не меняется. Слабый выбывший больше не получает рейтинг за счёт
сильных выбывших.

Эталон — флаг skip_eliminated_pairs у предложенной системы; документ: 4.3, 4.4,
4.9, пример 5, итоги 7.3 пересчитаны (сдвиг в третьем знаке), решение 8 в разделе 9;
справка. #91

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqSoRj99iwVEH5U5fnZgsd
2026-09-18 23:41:29 +03:00

175 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { ChevronDown, ChevronUp } from "lucide-react";
import { useState } from "react";
/**
* Справка — разделы, раскрывающиеся по кнопке. Пока раздел один: «Рейтинг».
* Текст соответствует backend/app/services/scoring.py и docs/rating/rating-system.md:
* коэффициенты, пороги и примеры менять вместе с ними.
*/
export function HelpPage() {
const [showRating, setShowRating] = useState(false);
return (
<div>
{/* Название страницы уже в верхней панели — в теле только пояснение. */}
<div className="card">
<p className="muted small" style={{ margin: 0 }}>
Здесь со временем появятся правила, подсказки и пояснения. Пока — о том, как
считается рейтинг игроков.
</p>
</div>
{/* Кнопка-карточка раздела — как карточки списка партий. */}
<button
className="card"
style={{ textAlign: "left", width: "100%" }}
onClick={() => setShowRating((v) => !v)}
>
<div className="row-between">
<h3 style={{ margin: 0 }}>Рейтинг</h3>
{showRating ? <ChevronUp size={18} /> : <ChevronDown size={18} />}
</div>
</button>
{showRating && (
<>
<div className="card">
<h3>Рейтинг игроков</h3>
<p className="small">
Рейтинг — оценка силы игрока относительно соперников (система Elo). Каждый начинает
с <b>1500</b>. Разница в <b>400</b> пунктов означает шансы 10 к 1 в пользу более
сильного.
</p>
<p className="small">
Считаются только <b>завершённые</b> партии, по порядку их игры. Правка или удаление
старой партии пересчитывает и все последующие.
</p>
<p className="small">
Партия раскладывается на пары игроков. В каждой паре фактический результат (выше —
1, поровну — 0.5, ниже — 0) сравнивается с ожидаемым по рейтингам до партии:
</p>
<div className="formula">ожидание = 1 / (1 + 10^((R соперника − R игрока) / 400))</div>
<div className="formula" style={{ marginTop: 6 }}>
ΔR = K × G / (N − 1) × Σ M × (результат − ожидание)
</div>
<p className="small" style={{ marginTop: 10 }}>
где <b>N</b> — число игроков, сумма — по всем соперникам. Поэтому победа над сильным
приносит больше, чем над слабым, а поражение от слабого отнимает больше. Выбывшие
делят последнее место, но между собой не сравниваются: в этой партии все они
проиграли, так что пара двух выбывших рейтинг не двигает.
</p>
</div>
<div className="card">
<h3>От чего зависит изменение</h3>
<ul className="small" style={{ margin: 0, paddingLeft: 18 }}>
<li>
<b>K — скорость.</b> У новичка 64, к 20-й партии плавно снижается до 16: новичок
быстро находит свой уровень, а рейтинг опытного игрока не скачет.
</li>
<li>
<b>G — размер стола:</b> 1 + 0.5 × (N − 2) / 4. В дуэли 1.0, вчетвером 1.25,
вшестером 1.5.
</li>
<li>
<b>M — отрыв в паре</b>, от 0.5 до 2. Обычная партия даёт 1. Больше, если:
<ul style={{ paddingLeft: 16 }}>
<li>
партия закончилась раньше — для пар с победителем; обычной считается конец
в предпоследнем раунде;
</li>
<li>разница целей велика относительно числа игроков;</li>
<li>разница миров велика относительно доли миров на игрока.</li>
</ul>
</li>
<li>
<b>Тип победы</b> — для пар с победителем, чем ближе партия, тем меньше:
по целям ×1, по мирам ×0.85, по пластику ×0.7, по ресурсам ×0.6,
последний выживший ×1.
</li>
</ul>
<p className="small" style={{ marginTop: 10 }}>
Раунд окончания, цели и миры вводить <b>необязательно</b>: пропущенное считается
обычным значением и множитель не меняет. «Последний выживший» ставится сам, когда
все соперники выбыли, — отрыв по целям тогда максимальный. Лимит раундов — 8,
с правилом группы «9 раундов» при 5–6 игроках — 9.
</p>
<p className="small" style={{ marginBottom: 0 }}>
Единоличный победитель никогда не теряет рейтинг, а единоличное последнее место
и выбывание никогда его не приносят. Невыбывшие, поделившие место, сравниваются
между собой как в ничьей: слабый может получить рейтинг, сильный — потерять.
</p>
</div>
<div className="card">
<h3>Примеры (опытные игроки)</h3>
<div className="stack" style={{ gap: 6 }}>
<div className="row-between small">
<span>Дуэль 1600 и 1400: победил сильный</span>
<b>+3.8 / −3.8</b>
</div>
<div className="row-between small">
<span>Дуэль 1600 и 1400: победил слабый</span>
<b>+12.2 / −12.2</b>
</div>
<div className="row-between small">
<span>Равные, победа по целям без деталей</span>
<b>+8.0</b>
</div>
<div className="row-between small">
<span>Равные, победа по мирам без деталей</span>
<b>+6.8</b>
</div>
<div className="row-between small">
<span>Разгром на 3-м раунде (цели 2:0, миры 8:2)</span>
<b>+16.0</b>
</div>
<div className="row-between small">
<span>1-е место за столом на 5 равных</span>
<b>+11.0</b>
</div>
<div className="row-between small">
<span>Новичок обыграл равного опытного</span>
<b>+32 / −8</b>
</div>
</div>
<p className="muted small" style={{ marginBottom: 0 }}>
Рейтинг показывается целым числом, но считается без округления.
</p>
</div>
<div className="card">
<h3>Один рейтинг на всё приложение</h3>
<p className="small" style={{ margin: 0 }}>
Рейтинг у игрока один — по всем его партиям во всех группах, поэтому он одинаковый
в общем топе, в профиле и на странице группы. На странице группы по партиям этой
группы считаются только игры, победы, винрейт и среднее место; рейтинг и статус
«Новичок» там общие.
</p>
</div>
<div className="card">
<h3>Попадание в топ</h3>
<p className="small">
Чтобы попасть в ранжированный топ, нужно сыграть минимум <b>10 партий</b>. До этого
игрок показывается в блоке «Новички» — предварительно, без места в рейтинге.
</p>
</div>
<div className="card">
<h3>Другие показатели</h3>
<ul className="small" style={{ margin: 0, paddingLeft: 18 }}>
<li><b>Винрейт (WR)</b> — доля партий, в которых игрок занял 1-е место.</li>
<li><b>Среднее место</b> — среднее по завершённым партиям.</li>
<li><b>Лучшая партия</b> в профиле — та, что принесла больше всего рейтинга.</li>
<li>
При равенстве рейтинга топ сортируется по: винрейту, числу игр, среднему месту
и затем по нику.
</li>
</ul>
</div>
</>
)}
</div>
);
}