Commit Graph

8 Commits

Author SHA1 Message Date
aevgarik
32b894ec08 Post-codex-review-5: align event-log с telemetry spec + UX polish
- MatchTracker (codex #1 Low): event-log поля переименованы под
  telemetry-spec.md v3.10 — ballOwner→ballOwnerBeforeHit,
  matchTimeSec→matchTimeSeconds (для bumper_hit и goal events).
  Future-proof для batched emit через .getEvents(). Тест assertion
  обновлён под новые поля.
- Touch zone labels (codex #2 Low): BOOST 14→18px, alpha все
  лейблы 0.5→0.7 для mobile readability.
- Test comment (codex #3 editorial): «player получает hits через
  ai_setup» → корректная формулировка про defensive trap
  (атакующий игрок кормит AI как защитника).

50/50 tests, typecheck/lint/build .

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 08:13:32 +03:00
aevgarik
bfdb24294a Implement defensive setup scoring v3.10
Реализует финальную модель 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>
2026-05-24 07:27:01 +03:00
aevgarik
a0ed17d814 Post-codex-review-4: flipper geometry fix + HUD repositioning
Critical bug (codex #1 High): концы нижних флипперов скрещивались В gap'е
(left.x=401, right.x=319 — оба в 306..414, причём X-shape) и торчали НИЖЕ
линии ворот (y=1275 vs playerGoalY=1260). Это блокировало корректный
gameplay — флипперы стояли крестом в створе.

Дополнительно: AI флипперы имели НЕВЕРНУЮ ориентацию (формула +180°
вместо y-flip) — concы торчали ВЛЕВО-ВНИЗ от pivot, наружу от AI ворот.
AI просто не отбивал мячи геометрически правильно.

Fixes:
- src/game/flipperGeometry.ts: pure utility — computeFlipperAngleDeg
  (sign × ownerSign × baseAngle = правильный y-flip между player/AI)
  + computeFlipperEnd (вычисление end position).
- src/game/Flipper.ts: использует утилиту; удалено +180 для AI.
- src/scenes/MatchScene.ts: pivot offset 100px по x (за gap edge) и
  80px по y (над goal line). Применено для всех 4 флипперов
  (player+AI × left+right).

HUD (codex #2 Medium): BP перенесён с y=GAME_HEIGHT-60 (= в зоне
флипперов/BOOST overlay) на y=145 (под таймером). Управление и HUD
теперь визуально разнесены.

Tests: добавлен src/game/flipperGeometry.test.ts — 13 проверок
(угол-симметрия player/AI, инвариант «end НЕ в gap И НЕ за линией»,
swing direction, regression-test для бывшего X-скрещивания). Итого
29/29 тестов зелёные.

codex #3 (npm audit) — без изменений, документирован в KNOWN_ISSUES.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 04:29:11 +03:00
aevgarik
a1b0ae9e96 Post-codex-review-3 docs: KNOWN_ISSUES + playtest protocol
- KNOWN_ISSUES.md: dev-only vite/esbuild moderate CVEs с impact analysis
  (production бандл не затронут), mitigation, плановый vite 5→8 + vitest 1→3
  апгрейд перед Phase 5; AI distinguishability caveat с ссылкой на playtest.
- playtest-protocol.md: 20-матчевая blind-attribution методика для закрытия
  Phase 2 DoD «тестер различает 4 личности»; рубрика per-match, expected
  tells (для оценщика, не тестера), randomization protocol, failure-mode
  follow-ups, report template, codex-automation roadmap.
- README.md: новый раздел «Operational docs» с cross-ref на оба файла.

Не код — операционная документация для closure Phase 2 и трекинга backlog.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 04:10:04 +03:00
aevgarik
bb9f7209ae Post-codex-review-2: mobile controls, turbo bug, AI diff, favicon
- Mobile match controls (codex #1 High): 3 touch zones внизу MatchScene
  (L / BOOST / R). Visible semi-transparent overlay'и; работает и на mouse,
  и на touch. Keyboard input теперь optional (this.keys?), не падает на
  headless mobile.
- Turbo first-hit bug (codex #2): guard на lastHitTime > 0 — иначе первый
  удар по turbo в первую секунду давал 0 очков (now < 0 + 1000).
- AI behavior diff (codex #3): добавлен PersonalityModifier (triggerDistBonus,
  reliabilityMul, reactionMul). Defensive: +reliability, -reaction.
  Aggressive: +trigger distance, -reliability. Ghost: 15% «эхо-double-swing»
  (обе стороны одновременно). Trickster уже имел jitter modifier.
  Поведенческие отличия теперь должны быть различимы за 5 матчей.
- Favicon 404 (codex #4 Low): inline SVG favicon в index.html, no more
  console noise.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 03:59:41 +03:00
aevgarik
4e3172de81 Phase 2 DoD closeout: curve physics, AI delta, share, setup UX
- Curve bumper: spin-modifier ±35° на отскоке (детерминированный знак на bumper)
  + визуальный вихрь-индикатор. Теперь curve механически отличается от standard.
- Easy/Medium AI delta: распознавательная дистанция (Easy 150px / Medium 280px),
  reliability (Easy 0.75 / Medium 0.95). difficulty прокидывается из каждой
  personality в shouldActivateFlipperBase. Hard остаётся placeholder для Phase 3.
- Share button на ResultScene: snapshot canvas → PNG blob → platform.shareImage().
  Mock логирует, YG использует SDK share. Telemetry: share_clicked / share_failed.
- Setup UX переписан: палитра типов сверху (4 типа + trash) + tap-палитра-tap-слот
  (unified mobile/desktop) + drag-from-palette ghost (desktop). Long-tap слота без
  выбранного типа удаляет содержимое (быстрый shortcut). Заменил cycle-on-click
  на normalуй setup, как в DoD.

Closes codex finding #2 (Phase 2 DoD gap).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 03:50:16 +03:00
aevgarik
8e49e088b0 Post-codex-review fixes: build, telemetry, last-touch, tests
- Fix #1 (Critical): rename private `data` → `sceneData` in Match/Setup/Result
  scenes (collided с Phaser.Scene.data DataManager, ломало TS build).
  Убран unused `game` var в main.ts. typecheck + build теперь зелёные.
- Fix #3: matchId генерируется один раз в MatchScene.create() и
  переиспользуется в match_start, match_end, MatchResult — telemetry funnel
  теперь связывается.
- Fix #4: AI setup сохраняется в `aiSetupConfig` и кладётся в MatchResult.aiSetup
  (раньше всегда пустой). Также убрана двойная инстанциация AI
  (createAIPlayer() вызывался дважды).
- Fix #5: last-touch меняется на ЛЮБОМ касании флиппера (активном или
  пассивном). Раньше owner не обновлялся при пассивном отскоке → ломалась
  autogoal-логика.
- Lint: убран unused `oldOwner` параметр в spawnBall callback.
- Tests: добавлены 16 unit-тестов для calculateScores
  (`src/scoring/calculateScores.test.ts`) — покрытие baseline, mode/difficulty
  multipliers, penalty стэкинг, double-points (local-only), Anti-A2W
  invariant (base ≤ local при penalty без double-points).
- package-lock.json закоммичен для reproducible build.

Codex findings #2 (Phase 2 DoD: drag-and-drop setup, curve bumper physics,
Share button, Easy/Medium delta) — отдельным заходом.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 03:43:59 +03:00
dc53623001 Phase 0-2: Vite/Phaser 4/TS boilerplate + Platform Adapter + core match (vs 4 AI personalities)
Phase 0 (foundation):
- package.json, tsconfig.json (strict), vite.config.ts (base: './'), .eslintrc.cjs
- index.html, src/main.ts (Phaser game init + platform detection)
- src/config/defaults.ts (Remote Config defaults, palette, constants)
- src/types/index.ts (все schemas из data-contracts: SaveState, MatchResult, RemoteConfig, etc.)
- src/platform/: PlatformAdapter interface + MockPlatformAdapter + YgPlatformAdapter

Phase 1 (core mechanics):
- src/scenes/: Boot, Preload, MainMenu (Quick Match selector), Setup, Match, Result
- src/game/: Table (геометрия + walls + ворота), Ball (last-touch + color tracking), Flipper (player + AI, swing + cooldown), Bumper (4 типа: standard/slingshot/curve/turbo)
- src/scoring/: MatchTracker (last-touch + bumper points), calculateScores (unified scoring pipeline per data-contracts v3.8 requirement)
- src/ai/: AIPlayer interface + DefensiveAI Easy/Medium

Phase 2 (setup + AI personalities):
- src/scenes/SetupScene (5 active slots, click-cycle bumper types, 30s timer)
- src/ai/personalities/: AggressiveAI, TricksterAI, GhostAI (Easy/Medium; Hard cradle-aim = Phase 3)
- src/ai/factory.ts (createAIPlayer)
- MainMenuScene: выбор AI-личности + сложности
- MatchScene использует factory вместо хардкода

Anti-A2W enforcement: calculateScores применяет penalty per v3.5 model
(Continue × 0.5, Campaign Skip × 0.7, +1 заряд × 0.9, реролл × 0.85,
Boost active × 0.7; ×2 sezon очки только в local).

Что НЕ в этом commit'е (следующие фазы):
- Phase 3: бустер «Захват» (cradle + slow-mo), Hard AI (cradle-aim solver), Ghost adaptation, YG Player API cloud-save, achievements
- Phase 4: Campaign (12 матчей), Tournament (8/16 bracket), шейдеры (CRT + bloom + chromatic aberration), 29 AI-арт ассетов, музыка
- Phase 5: реальная YG SDK интеграция (ads + payments), Remote Config Nakama RPC
- Phase 6: YG submission

Pre-greenlit concept-пакет — ~/Knowledge/Projects/pinball-duel/ (19 файлов, 10 audit-раундов).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 03:25:17 +03:00