Реализует финальную модель scoring из data-contracts.md v3.10:
fixed center bumpers — по last-touch; setup-bumpers — defensive trap
(награждают владельца только когда мяч принадлежит сопернику).
Self-farm запрещён. Neutral ball не даёт очков нигде.
Code changes:
- types/index.ts: BumperScoreSource union ('fixed_last_touch' |
'player_setup' | 'ai_setup'); MatchResult.aiBumperPointsEarned +
aiBumperHitsCount.
- Bumper.ts: новый required field source: BumperScoreSource;
передаётся в onHit callback через bumper.source.
- scoring/MatchTracker.ts: pure helper computeCreditedTo(source, ballOwner)
→ реализует 9-cell credit matrix; recordBumperHit принимает
(rawPoints, source, ballOwner, type, ...) и возвращает {creditedTo,
pointsEarned}; новые getter'ы getAIBumperHits + getUncreditedBumperHits.
- scenes/MatchScene.ts:
* Fixed bumpers создаются с source='fixed_last_touch'
* placeSetupBumpers передаёт source per side
* handleBumperHit (новая сигнатура: bumper, points) рутит в tracker,
emit'ит spawn-particles + telemetry bumper_hit event с полным
набором полей per telemetry-spec.md v3.10
* MatchResult заполняется с AI BP/hits
* match_end telemetry также включает aiBumperPointsEarned/HitsCount
* HUD: BP теперь сравнительный «P x / AI y»
- scenes/ResultScene.ts: breakdown показывает обе стороны BP и hits.
Particle effects (DoD v3.10):
- 12 псевдо-частиц радиально от bumper'а (cubic ease-out, 320ms)
- цвет: player=magenta, ai=cyan, null=grey
- screenshake 80ms/0.0025 ТОЛЬКО на credited hit
(иначе экран дрожит на каждом нейтральном отскоке)
Tests: scoring/MatchTracker.test.ts — 21 теста:
- 9-cell credit matrix (computeCreditedTo, все source × ballOwner)
- recordBumperHit с counter assertions
- многократные hits (накопление BP per сторона)
- симметрия player/AI defensive trap scenarios
- fixed bumpers через смену владения
- events log для telemetry
- recordGoal с autogoal-flag
Итого 50/50 unit-тестов (16 calculateScores + 13 flipperGeometry +
21 MatchTracker). typecheck/lint/build все зелёные.
Несоответствия с docs остающиеся:
- DoD: «12 частиц per credited side» — done; SFX bumper hit (DoD line 247)
— TODO Phase 4 (asset pack).
- Particle effects через psecdoupartials (Arc + tween), не через
ParticleEmitter — упрощение, визуально эквивалентно.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
7.8 KiB
Playtest Protocol — Phase 2 AI Distinguishability
Цель. Закрыть Phase 2 DoD «тестер различает 4 AI-личности после 5 матчей» через blind attribution. Требуется внешний тестер (НЕ разработчик и НЕ автор AI кода).
Pass-bar: ≥ 75 % правильных attribution-ответов (≥ 15 / 20) ИЛИ ≥ 1 distinct tell per личность, названный тестером без подсказки.
Подготовка
| Шаг | Команда / действие |
|---|---|
| Build | npm run build && npm run preview (production build на localhost:4173) |
| Difficulty | Medium — Easy скрывает поведение за reaction-задержкой; Hard не реализован (Phase 3) |
| Mode | Quick Match → выбор личности → Medium |
| Device | Минимум 1 desktop + 1 mobile session (mobile важно — touch zones там единственный input) |
| Telemetry | Mock adapter логирует trackEvent в консоль — оставить открытой DevTools для пост-анализа |
Per-match observation rubric
После каждого матча тестер заполняет строку:
| Поле | Что фиксировать | Источник |
|---|---|---|
match_index |
1..20 | manual |
setup_pattern |
Какие bumper-типы AI поставил, где (углы / центр / далеко) | визуально, AI половина |
reaction_distance |
«early / normal / late» — насколько близко мяч долетает до AI-флипперов | субъективно |
miss_rate |
«низкий / средний / высокий» — как часто AI пропускает свой удар | визуально |
special_behaviour |
Любой характерный приём (двойной свинг, странный угол отскока, агрессивная атака центром) | free-text |
final_score |
Player : AI | HUD post-match |
duration_sec |
Длина матча | HUD timer / match_end telemetry |
personality_guess |
Бэтти / Турбо / Хитрый Хо / Эхо | blind по завершению ВСЕХ 20 матчей |
Expected tells (для оценщика; тестеру НЕ показывать до конца сессии)
| Личность | Tells |
|---|---|
| Defensive (Бэтти) | Setup: 2 slingshot в углах ворот + 1 турбо центр + 1 curve + 1 пусто. Поведение: реакция надёжная (reliabilityMul: 1.1, capped 1.0 = почти 100 %), чуть быстрее (reactionMul: 0.9). AI пропускает редко; матчи длиннее обычного. |
| Aggressive (Турбо) | Setup: 2 турбо в своей зоне (defensive trap против мяча игрока) + 1 slingshot угол + 1 curve + 1 standard. Поведение: реагирует с большего расстояния (triggerDistBonus: +80px), чаще промахивается (reliabilityMul: 0.85). Быстрые матчи; игрок, застрявший в setup Турбо, быстро отдаёт ему очки. |
| Trickster (Хитрый Хо) | Setup: 4 curve bumper'а (лабиринт) + 1 турбо центр. Поведение: 15 % случайный спайк jitter (×1.3) — реакция «дёргается». Непредсказуемый счёт; необычные траектории мяча. |
| Ghost (Эхо) | Setup: balanced (2 slingshot + 1 турбо + 1 curve + 1 standard). Поведение: 15 % «echo double-swing» — обе стороны активируются одновременно, даже если мяч идёт только в одну. Медианный счёт; характерный двойной flip. |
Процедура
-
Randomize order. Использовать random.org pick из
[defensive, aggressive, trickster, ghost], 5 раз для каждой → 20 матчей. Не группировать по личности — рассыпать. Пример последовательности:[T, D, G, A, T, A, D, G, T, A, D, G, A, T, D, G, A, D, G, T]. -
Играть в выбранном порядке. После каждого матча — заполнить строку рубрики. НЕ смотреть expected tells между матчами.
-
Blind attribution. По завершении всех 20 матчей — для каждой строки заполнить
personality_guessбез обращения к памяти о выборе перед матчем. -
Score. Сравнить
personality_guessс фактическим выбором. Подсчитать accuracy. -
Subjective rating. Тестер ставит общую оценку «AI feels different» по 5-балльной шкале (≥ 4 = pass).
-
Report. Заполнить шаблон ниже, приложить к Phase 2 sign-off.
Failure modes & follow-ups
| Результат | Что делать |
|---|---|
| Accuracy < 50 % | Модификаторы слишком слабые. Удвоить triggerDistBonus, опустить reliabilityMul ниже 0.7. |
| Accuracy 50-74 % | Две личности путаются между собой. Развести их setup'ом сильнее (например, поменять Defensive curve → second slingshot для большего «защитного» паттерна). |
| Accuracy ≥ 75 %, subjective < 4 | Поведение различимо, но не интересно. Это не блокер Phase 2, но завести задачу на «character polish» в Phase 4. |
| Тестер жалуется «AI слишком сложный/лёгкий на Medium» | Тюнить reactionMs в src/config/defaults.ts — не personality-specific. |
Codex automation (опционально)
codex exec потенциально может прогнать матчи через headless Phaser
и replay-log; но для Phase 2 sign-off достаточно ручного playtest'а.
Маппинг рубрики в telemetry events:
| Поле рубрики | Telemetry source |
|---|---|
setup_pattern (AI) |
MatchResult.aiSetup.slots |
reaction_distance |
derivable: ball trajectory + goal_scored.ballOwnershipBeforeGoal |
miss_rate |
1 − (AI flipper hit count / ball-passes-through-AI-zone count) (нужен новый event flipper_hit — не в Phase 2 scope) |
final_score |
match_end.goalsScored / goalsAgainst |
duration_sec |
match_end.durationSeconds |
Автоматизация — backlog Phase 5+.
Report template
# Playtest Report — Phase 2 AI Distinguishability
**Tester:** <имя / handle>
**Date:** <YYYY-MM-DD>
**Build:** HEAD <git-sha>
**Devices:** desktop=<browser/os>, mobile=<browser/os>
## Match log
| # | Personality (actual) | Setup observed | Reaction | Miss rate | Special | Score | Duration | Guess | Match? |
|---|---|---|---|---|---|---|---|---|---|
| 1 | trickster | 4 curve + turbo | normal | medium | hectic angles | 5:3 | 4m12s | trickster | ✅ |
| 2 | ... | | | | | | | | |
## Results
- Accuracy: __ / 20 = __ %
- Subjective rating: __ / 5
- Per-personality breakdown:
- Defensive: __ / 5
- Aggressive: __ / 5
- Trickster: __ / 5
- Ghost: __ / 5
## Free-text observations
<распознанные tells, неожиданные моменты, баги>
## Verdict
- [ ] Phase 2 AI DoD PASS (≥75% accuracy + ≥4/5 subjective)
- [ ] Phase 2 AI DoD FAIL — see follow-ups