Skip to content

Add Doubles Battle Support to Engine#62

Open
sudo-owen wants to merge 11 commits intomainfrom
claude/merge-double-battle-main-EhNdh
Open

Add Doubles Battle Support to Engine#62
sudo-owen wants to merge 11 commits intomainfrom
claude/merge-double-battle-main-EhNdh

Conversation

@sudo-owen
Copy link
Collaborator

@sudo-owen sudo-owen commented Feb 17, 2026

Summary

This PR implements full support for Doubles battles in the Engine, allowing two players to each control two simultaneously. The implementation is additive and maintains backward compatibility with existing Singles battles.

Key Changes

Core Data Structures

  • Added GameMode enum (Singles/Doubles) to Enums.sol
  • Extended BattleData struct with slotSwitchFlagsAndGameMode field to track game mode and slot-specific state
  • Updated activeMonIndex packing to support 4-bit-per-slot encoding for doubles (p0 slot 0, p0 slot 1, p1 slot 0, p1 slot 1)
  • Added p0Move2 and p1Move2 fields to BattleConfig for second slot moves in doubles
  • Added gameMode field to Battle and ProposedBattle structs

Engine Execution

  • Implemented _executeDoubles() internal function to handle doubles-specific battle logic with parallel slot execution
  • Added executeWithMovesForDoubles() public function for atomic move submission and execution in doubles battles
  • Refactored hook execution into _runEngineHooks() helper to reduce code duplication
  • Added _isDoublesMode() helper and branching logic in _executeInternal() to route to doubles execution path
  • Implemented switchActiveMonForSlot() to switch individual slots in doubles battles
  • Updated _packMoveDecision() helper for consistent move encoding across singles and doubles

Validation & Commit Management

  • Extended DefaultValidator.validateSwitch() with doubles-specific checks (mon can't be in both slots)
  • Added validateSwitchForSlot() to ValidatorLogic library for per-slot switch validation
  • Implemented revealMovePairForDoubles() in DefaultCommitManager for atomic dual-slot move reveals
  • Added DualSignedRevealDoubles struct and related logic to SignedCommitLib for dual-signed doubles reveals
  • Updated SignedCommitManager with doubles-specific error handling

Test Infrastructure

  • Added comprehensive DoublesValidationTest.sol covering move validation, switches, and edge cases
  • Added DoublesCommitManagerTest.sol for commit/reveal flows in doubles
  • Created mock moves and effects for doubles testing:
    • DoublesTargetedAttack - targets specific opponent slots
    • DoublesSlotAttack - slot-aware damage calculation
    • DoublesEffectAttack - applies effects to specific slots
    • DoublesForceSwitchMove - forces opponent slot switches
    • MonIndexTrackingEffect - verifies effects run on correct mons
    • EffectApplyingAttack - applies effects for testing

Documentation

  • Added PLAN.md detailing the design principles and implementation phases
  • Updated comments throughout to clarify doubles-specific behavior
  • Added inline documentation for new functions and packing schemes

Notable Implementation Details

  • Backward Compatibility: Singles battles use the same code paths with gameMode == GameMode.Singles; the 4-bit packing in activeMonIndex is backward compatible with 8-bit packing for singles
  • Atomic Operations: executeWithMovesForDoubles() allows both slots' moves to be set and executed in a single transaction, matching the commit-reveal pattern
  • Slot-Aware Effects: Effects now receive explicit monIndex parameters to ensure they run on the correct Pokémon in doubles
  • Parallel Execution: Doubles battles execute both slots' moves in parallel with proper priority ordering and interaction handling
  • Minimal Interface Changes: New functions added rather than modifying existing signatures; getGameMode() getter added for querying battle mode

Testing

All existing tests pass with the new code. New test suites comprehensively cover doubles-specific scenarios including move validation, switching, commit/reveal flows, and effect application.

Detailed plan for re-implementing doubles battle support on current main,
covering data structures, engine execution, effects, commit manager, and tests.

https://claude.ai/code/session_01MdUWjZNL2QrK4utE8Lma7H
…anded contexts)

Adds GameMode enum, doubles constants (ACTIVE_MON_INDEX_BITS, slot switch flags),
and expands Battle/BattleData/BattleConfig/BattleContext/CommitContext structs
with doubles support fields (gameMode, slotSwitchFlagsAndGameMode, p0Move2/p1Move2).
All existing struct constructors updated with Singles default.

https://claude.ai/code/session_01MdUWjZNL2QrK4utE8Lma7H
- Engine.sol: add slot-aware helpers (_getActiveMonIndexForSlot,
  _setActiveMonIndexForSlot, _isDoublesMode), switchActiveMonForSlot,
  setMoveForSlot, getGameMode/getActiveMonIndexForSlot getters, update
  getBattleContext/getCommitContext to populate doubles fields, clear
  slot 1 moves in end-of-turn cleanup, set gameMode bit in startBattle
- IEngine.sol: add switchActiveMonForSlot, setMoveForSlot, getGameMode,
  getActiveMonIndexForSlot to interface
- IValidator.sol: add validatePlayerMoveForSlot and
  validatePlayerMoveForSlotWithClaimed for doubles slot validation
- DefaultValidator.sol: implement slot-aware validation methods with
  duplicate switch target prevention across slots
- DefaultCommitManager.sol: add revealMovePair for doubles commit/reveal
  with per-slot validation and dual slot move setting
- ICommitManager.sol: add revealMovePair to interface
- Remove unused GameMode import from DefaultMatchmaker.sol

All 237 existing tests pass. Singles behavior unchanged.

https://claude.ai/code/session_01MdUWjZNL2QrK4utE8Lma7H
… bug

Port DoublesCommitManagerTest (10 tests) and DoublesValidationTest (37 tests)
along with 6 mock contracts, adapting them to the unified DefaultCommitManager
architecture (commitMove/revealMovePair, new hash format, updated interfaces).

Fix critical bug in DefaultMatchmaker.proposeBattle where gameMode field was
not stored, causing all doubles battles to be created as singles.

31 DoublesValidationTest tests are skipped pending Engine doubles slot 1 support.

https://claude.ai/code/session_01MdUWjZNL2QrK4utE8Lma7H
Port the doubles execution pipeline from the double-battle branch:
- Add _executeDoubles() with 4-slot move ordering and per-slot execution
- Decompose _handleSwitch into core/switchIn/slotForSlot components
- Add _handleMoveForSlot for slot-aware move handling in doubles
- Add _runEffects 7-param overload for explicit monIndex targeting
- Add doubles helpers: _checkForGameOver, _checkForGameOverOrKO_Doubles,
  _computeMoveOrderForDoubles, _playerNeedsSwitchTurn, slot switch flags
- Initialize doubles activeMonIndex with p0s0=0, p0s1=1, p1s0=0, p1s1=1
- Fix _unpackActiveMonIndex to use 4-bit mask (compatible with both packings)
- Pass explicit monIndex in dealDamage/updateMonState for correct effect targeting

Result: 25/37 DoublesValidationTest pass (was 6/37), all 10 DoublesCommitManagerTest pass.
12 tests remain skipped pending Validator/Effect/Mock updates for doubles.

https://claude.ai/code/session_01MdUWjZNL2QrK4utE8Lma7H
…ulator, and StaminaRegen fixes

- Rewrite validatePlayerMoveForSlot with proper doubles NO_OP logic:
  allow NO_OP when slot's mon is KO'd and no valid switch targets exist
- Add _hasValidSwitchTargetForSlot, _validateSwitchForSlot helpers
- Add slot-aware getDamageCalcContext overload to Engine/IEngine
- Add slot-aware _calculateDamage overload to AttackCalculator
- Update DoublesSlotAttack mock to use slot indices from extraData
- Update StaminaRegen to regen both slots in doubles mode
- Update validateSwitch to check other slot's active mon in doubles

All 284 tests pass (37 doubles, 0 skipped).

https://claude.ai/code/session_01MdUWjZNL2QrK4utE8Lma7H
… inline gas savings

Extract validateSwitchForSlot, validatePlayerMoveBasicsForSlot, and
hasValidSwitchTargetForSlotBitmap into the shared ValidatorLogic library
so both DefaultValidator and Engine can use them. This follows the same
pattern as the existing singles validation (validateSwitch,
validatePlayerMoveBasics) where pure logic lives in the library for
compiler inlining when the Engine uses address(0) validator.

- Engine.switchActiveMonForSlot now uses ValidatorLogic.validateSwitchForSlot
  instead of ad-hoc inline checks (which were missing other-slot checks)
- DefaultValidator delegates to library for switch and move basics validation

https://claude.ai/code/session_01MdUWjZNL2QrK4utE8Lma7H
…lidator gap

- Extract _prepareMoveSet() and _packMoveDecision() helpers to deduplicate
  setMove/setMoveForSlot/executeWithMoves move-packing logic
- Add ValidatorLogic.checkGameOver() and unify _checkForGameOverOrKO (singles)
  with _checkForGameOver (doubles) to eliminate duplicated KO bitmap comparison
- Add IValidator.validateSwitchForSlot() and implement in DefaultValidator so
  doubles switchActiveMonForSlot uses slot-aware validation for external validators
- Document per-player (not per-slot) salt semantics in setMoveForSlot

https://claude.ai/code/session_01MdUWjZNL2QrK4utE8Lma7H
- _unpackMoveIndex(): consolidates 4 identical move-index unpacking sites
- _getPriorityAndSpeed(): unifies priority/speed logic between singles and doubles
- _validateMoveSelection(): deduplicates inline-vs-external validator dispatch
- _runEngineHooks(): replaces 4 hook-loop instances (startBattle, executeInternal, handleGameOver)
  Note: _executeDoubles keeps inline loops to avoid Yul stack-too-deep

Net: -38 lines, 284 tests pass

https://claude.ai/code/session_01MdUWjZNL2QrK4utE8Lma7H
- getCommitAuthForDualSigned: drop startTimestamp check to eliminate 2
  SLOADs (storageKey lookup + config read). winnerIndex != 2 already
  catches never-started battles. Return gameMode for free from same slot.

- Engine.executeWithMovesForDoubles: new function that sets all 4 slot
  moves (p0Move, p0Move2, p1Move, p1Move2) and executes in one call.

- SignedCommitLib.DualSignedRevealDoubles: new EIP-712 struct covering
  2 move indices + 2 extra data fields per revealer for doubles.

- SignedCommitManager.executeWithDualSignedMovesForDoubles: doubles
  variant of the dual-signed flow. Committer hash uses revealMovePair
  preimage format. Validates gameMode to prevent singles/doubles mismatch.

https://claude.ai/code/session_01MdUWjZNL2QrK4utE8Lma7H
@netlify
Copy link

netlify bot commented Feb 17, 2026

Deploy Preview for monchain-wiki canceled.

Name Link
🔨 Latest commit c6d7570
🔍 Latest deploy log https://app.netlify.com/projects/monchain-wiki/deploys/699425f8d508080007ac8360

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments