New Rule Completeness Validator

Validates that all necessary code changes are implemented when adding new game rules; use when adding new game rules or variants to ensure no files are missed.

$ インストール

git clone https://github.com/calcitem/Sanmill /tmp/Sanmill && cp -r /tmp/Sanmill/.claude/skills/game-rule-validator ~/.claude/skills/Sanmill

// tip: Run this command in your terminal to install the skill


name: "New Rule Completeness Validator" description: "Validates that all necessary code changes are implemented when adding new game rules; use when adding new game rules or variants to ensure no files are missed."

New Rule Completeness Validator

Purpose

When adding a new game rule or rule variant to Sanmill, you need to modify multiple files (typically 70-80 files, including ~60 localization files). This skill provides a completeness checklist to ensure no necessary code changes are missed.

Reference: docs/guides/ADDING_NEW_GAME_RULES.md

Use Cases

  • Adding a new game rule variant
  • Adding new game mechanics to existing rules (e.g., new capture rules)
  • Modifying rule structure or parameters
  • Reviewing rule-related pull requests

Architecture Philosophy

Sanmill is configuration-based, not inheritance-based. Rule variants are expressed as data (Rule in C++, RuleSettings in Flutter) and toggled at runtime. Any new mechanics must be gated by booleans/params so existing variants remain untouched and fast.

Quick Overview

  • Estimated time: Simple parameter rule 2-4 hours; new mechanics 6-8 hours
  • Complexity: ⭐⭐⭐ Medium-High
  • Touched files: ~70-80 total (incl. ~60 ARB localization files)

Core Validation Checklist (Required Modifications)

1. C++ Engine Rule Definition

File: src/rule.cpp

  • Added new rule definition to the end of RULES[] array
  • Set all required fields:
    • name - Rule name (max 32 chars)
    • description - Rule description (max 512 chars)
    • pieceCount, flyPieceCount, piecesAtLeastCount
    • hasDiagonalLines - Whether diagonal lines exist
    • Mill formation action and phase flags
    • Capture rule configs (custodian, intervention, leap)
    • Flying, draw rules (mayFly, nMoveRule, etc.)

Example structure:

{
  "Your Rule Name",
  "Short description",
  9, 3, 3,                      // pieceCount, flyPieceCount, piecesAtLeastCount
  false,                        // hasDiagonalLines
  MillFormationActionInPlacingPhase::removeOpponentsPieceFromBoard,
  /*mayMoveInPlacingPhase*/ false,
  /*isDefenderMoveFirst*/ false,
  /*mayRemoveMultiple*/ false,
  // ... other fields
  kDefaultCaptureRuleConfig,    // custodian
  kDefaultCaptureRuleConfig,    // intervention
  kDefaultCaptureRuleConfig,    // leap
  /*mayFly*/ true,
  /*nMoveRule*/ 100,
  /*endgameNMoveRule*/ 100,
  /*threefoldRepetitionRule*/ true
}

File: src/rule.h

  • Incremented N_RULES constant (to match new RULES[] length)
  • If new parameters needed, added new fields to Rule struct?
// e.g., if it was 11, now it should be 12
constexpr auto N_RULES = 12;

2. Flutter UI Models

File: lib/rule_settings/models/rule_settings.dart

  • Added new rule variant value to RuleSet enum
  • Created new RuleSettings subclass (e.g., YourNewVariantRuleSettings)
  • All fields in new subclass match 1-to-1 with C++ Rule struct
  • Updated ruleSetDescriptions map with description
  • Updated ruleSetProperties map with default property instance

Example:

enum RuleSet {
  // ... existing variants
  yourNewVariant,  // new
}

class YourNewVariantRuleSettings extends RuleSettings {
  const YourNewVariantRuleSettings({
    // All params must match C++ Rule fields
  }) : super(/* ... */);
}

3. Flutter UI Selection Interface

File: lib/rule_settings/widgets/modals/rule_set_modal.dart

  • Added RadioListTile or similar UI component for selecting new rule
  • UI item correctly references new RuleSet enum value
  • If rule is experimental, marked as "Experimental" in rule_settings_page.dart?

4. Localization (Internationalization)

Files: lib/l10n/intl_*.arb (~60 files)

  • intl_en.arb - Added English translations
  • intl_zh_CN.arb - Added Simplified Chinese translations
  • All other intl_*.arb files - At least copied English version
  • Ran flutter gen-l10n to generate localization code

Key strings:

  • Rule name
  • Rule description
  • Any new UI labels or hints

Conditional Validation Checklist (Based on Mechanic Type)

5. Game Logic Modifications (If Gameplay Changed)

File: src/position.cpp (C++ side)

  • Must modify: If new rule changes placement, movement, or capture logic
  • Used conditional guards for performance:
    if (!rule.yourFeature) return fastPath();
    // new logic
    
  • Handled all edge cases

File: lib/game_page/services/engine/position.dart (Flutter side)

  • Must mirror logic from C++ position.cpp
  • C++ and Dart game logic remain fully symmetric
  • Implemented _putPiece, _movePiece, _removePiece related logic

Important: User-visible game logic must be symmetrically implemented in both C++ and Dart. Move generation (movegen) is C++ only.

6. Move Generation (C++ Only)

File: src/movegen.cpp

  • If custom movement patterns exist (e.g., special move rules), updated move generation logic?
  • Added conditional guards to avoid affecting other rules?

7. Mill Formation

File: src/mills.cpp

  • If rule has non-standard mill formation, updated this file?

8. Engine Options (If Added New Rule Fields)

File: lib/game_page/services/engine/engine.dart

  • Must modify: If Rule struct gained new fields
  • Added logic in setRuleOptions() method to send new parameters
  • Parameter names match UCI option names

File: src/ucioption.cpp

  • Must modify: If Rule struct gained new fields
  • Added corresponding UCI option definitions
  • Added on_change handlers to apply values to global rule object

Example:

// ucioption.cpp
{"YourNewOption", "false", "bool", {}, on_your_new_option}

void on_your_new_option(Option &o) {
    rule.yourNewField = o.get<bool>();
}

9. FEN Format Extension (Only If Dynamic State Persistence Needed)

When to extend FEN: Only when you must persist dynamic state that cannot be recomputed from the board.

Need to extend if:

  • Need to track capture targets (custodian/intervention)
  • Need to persist temporary user choice (e.g., preferredRemoveTarget)
  • Need to store intermediate multi-step state across moves/undo/search
  • Need history-dependent constraints (e.g., "same piece can't move twice")

Do NOT extend for:

  • ✗ Static rule params (piece counts, hasDiagonalLines, etc.)
  • ✗ Anything derivable from board (piece counts, mills)
  • ✗ Global flags (mayFly, mayRemoveMultiple)

Files: src/position.h, src/position.cpp

  • If FEN extension needed:
    • Added new fields in position.h
    • Added export logic in fen() method
    • Added parsing logic in set() method
    • Followed backward compatibility rules: new fields at end, missing fields have safe defaults

File: lib/game_page/services/engine/position.dart

  • Mirrored C++ FEN fields
  • Implemented _getFen() export logic
  • Implemented setFen() parsing logic

Files: tests/test_position.cpp, Flutter integration tests

  • Added FEN round-trip tests (export then reimport, state should match)

10. Evaluation and Search (Rare)

File: src/evaluate.cpp

  • If rule variant requires special evaluation logic, updated?

File: src/search.cpp

  • If rule variant requires special search logic, updated?

Testing Validation Checklist

11. C++ Tests

Files: tests/test_*.cpp

  • Rule loading and bounds tests (set_rule(i) correctly loads new rule)
  • Position/logic unit tests (including new mechanics)
  • If FEN extended, added FEN round-trip tests (tests/test_position.cpp)
  • Ran make test to ensure all tests pass

Run tests:

cd src
make build all
make test

12. Flutter Tests

Widget tests

  • New rule displays correctly in modal
  • Selecting new rule correctly saves and restores

Engine mirror tests

  • Test scenarios cover _putPiece, _movePiece, _removePiece
  • C++ and Dart logic produce same results

Integration tests

  • Select new rule → start new game → verify behavior visible

Run tests:

cd src/ui/flutter_app
flutter test

13. Performance Benchmarks

  • Ran benchmarks (if project has make benchmark)
  • Ensured no performance regression when new feature is off
  • Verified conditional guards enable early exits on hot paths

Build, Format, and Generate

14. Code Formatting

# Format all code
./format.sh
  • C++ code formatted (clang-format)
  • Dart code formatted (dart format)

15. Localization Generation

cd src/ui/flutter_app
flutter gen-l10n
  • Generated localization code without errors

16. Full Build

# C++ build
cd src
make build all

# Flutter build (pick a platform to test)
cd ../src/ui/flutter_app
flutter build apk  # Android
# or
flutter build ios   # iOS
# or
flutter build windows  # Windows
  • All platforms build successfully

Quality & Safety Assurance Checklist

17. Backward Compatibility

  • All existing rule variants remain unchanged
  • All old tests still pass
  • New features don't affect existing game logic (via conditional guards)

18. Performance Parity

  • When new feature is off, no performance regression
  • Hot paths remain efficient (early exits)
  • Benchmarks show acceptable performance

19. Code Symmetry

  • C++ and Dart user-visible logic fully symmetric
  • Same inputs produce same outputs
  • Tests verify symmetry

20. Documentation and Comments

  • Clear docs and comments near each new flag
  • Complex logic has explanatory comments
  • Updated docs/guides/ADDING_NEW_GAME_RULES.md (if needed)

21. Localization Completeness

  • At minimum, includes English (EN) and Simplified Chinese (ZH_CN)
  • Ideally, all ARB files updated
  • All translation strings meaningful with no placeholders

Complete QA Checklist (Summary)

Before submitting PR, confirm all of the following:

  • RULES[] has new entry, N_RULES incremented
  • Flutter RuleSet enum and RuleSettings subclass match C++
  • UI selection interface added, strings localized
  • Game logic uses conditional guards and early exits
  • C++ ↔ Dart position logic symmetric
  • FEN extended if necessary; round-trip tests added
  • If new Rule fields added, engine options updated
  • All tests pass (C++ and Flutter)
  • Benchmarks show no performance drop when feature off
  • Code formatted, builds succeed
  • Documentation and comments clear and complete

Validation Workflow

Phase 1: Planning (1 hour)

  1. Determine specific requirements for rule variant
  2. Determine if simple parameter adjustment or new mechanics needed
  3. List files that need modification

Phase 2: C++ Implementation (2-3 hours)

  1. Modify src/rule.h and src/rule.cpp
  2. If new mechanics needed, modify src/position.cpp, src/movegen.cpp, etc.
  3. If new fields added, update src/ucioption.cpp
  4. Run C++ tests, ensure they pass

Phase 3: Flutter Implementation (2-3 hours)

  1. Modify rule_settings.dart (enum + subclass + mappings)
  2. Modify rule_set_modal.dart (UI selection)
  3. If game logic modified, mirror to position.dart
  4. Update engine.dart's setRuleOptions()
  5. Run Flutter tests, ensure they pass

Phase 4: Localization (30 minutes)

  1. Update intl_en.arb and intl_zh_CN.arb
  2. Batch update other ARB files (can use English initially)
  3. Run flutter gen-l10n

Phase 5: Testing & Validation (1-2 hours)

  1. Run all C++ tests
  2. Run all Flutter tests
  3. Manually test new rule behavior in UI
  4. Check performance benchmarks
  5. Verify backward compatibility

Phase 6: Code Review & Documentation (30 minutes)

  1. Run code formatting
  2. Review all changes
  3. Update docs and comments
  4. Complete QA checklist

Common Pitfalls and Notes

❌ Common Mistakes

  1. Forgot to increment N_RULES

    • Symptom: Runtime crash or rule loading failure
    • Fix: Ensure N_RULES in src/rule.h matches RULES[] length
  2. C++ and Flutter fields don't match

    • Symptom: UI-set rule parameters don't take effect
    • Fix: Ensure RuleSettings fields match Rule struct 1-to-1
  3. Missing UCI options

    • Symptom: New Rule field values always default
    • Fix: Add corresponding options and handlers in ucioption.cpp
  4. Didn't mirror game logic to Dart

    • Symptom: UI preview inconsistent with actual game
    • Fix: Ensure position.dart mirrors position.cpp logic
  5. Incomplete localization

    • Symptom: Some languages show placeholders or English
    • Fix: At minimum complete English and Chinese, others can use English placeholder
  6. Performance regression

    • Symptom: Game slower even when not using new feature
    • Fix: Use conditional guards, ensure early exits
  7. Over-extended FEN

    • Symptom: FEN strings too long, hard to debug
    • Fix: Only extend FEN when must persist dynamic state

✓ Best Practices

  1. Incremental development: Implement C++ first, test, then do Flutter
  2. Frequent testing: Run relevant tests after each file modification
  3. Use conditional guards: Ensure new code doesn't affect existing rules
  4. Maintain symmetry: C++ and Dart logic should be readable side-by-side
  5. Documentation first: Write comments and docs before code
  6. Reference existing rules: Look at other rules in RULES[] as templates

Reference Files and Resources

Core Documentation

  • docs/guides/ADDING_NEW_GAME_RULES.md - Official guide for adding rules (must read)

C++ Files

  • src/rule.h - Rule struct definition
  • src/rule.cpp - RULES[] array
  • src/position.h/.cpp - Game position and logic
  • src/movegen.cpp - Move generation
  • src/ucioption.cpp - UCI options
  • src/mills.cpp - Mill formation logic
  • include/config.h - Default config (rarely modified)

Flutter Files

  • lib/rule_settings/models/rule_settings.dart - Rule models
  • lib/rule_settings/widgets/modals/rule_set_modal.dart - UI selection
  • lib/game_page/services/engine/position.dart - Position mirror
  • lib/game_page/services/engine/engine.dart - Engine communication
  • lib/l10n/intl_*.arb - Localization files

Test Files

  • tests/test_*.cpp - C++ unit tests
  • test/ - Flutter unit and widget tests
  • integration_test/ - Flutter integration tests

Output Format

Validation results should be reported clearly:

✓ [Complete] Core File Modifications
  ✓ src/rule.cpp - RULES[] added
  ✓ src/rule.h - N_RULES incremented
  ✓ rule_settings.dart - RuleSet enum added
  ...

⚠ [Warning] Conditional File Modifications
  ✓ src/position.cpp - Logic updated
  ✗ position.dart - Logic not mirrored (needs fix)
  ...

✓ [Complete] Test Validation
  ✓ C++ tests all passed (25/25)
  ✓ Flutter tests all passed (42/42)
  ...

✗ [Failed] Localization
  ✓ intl_en.arb - Updated
  ✓ intl_zh_CN.arb - Updated
  ✗ Other ARB files - Not updated (needs completion)
  ...

📊 Completion: 75% (15/20 checks passed)
💡 Recommendation: Priority fix position.dart mirror and localization

Summary

Adding a new game rule is a systematic effort requiring careful coordination between the C++ engine and Flutter UI. Using this checklist ensures:

  • ✓ No necessary file modifications are missed
  • ✓ C++ and Flutter stay synchronized
  • ✓ Backward compatibility is not broken
  • ✓ Performance is not affected
  • ✓ Test coverage is adequate
  • ✓ Localization is complete

Remember: When in doubt, refer to docs/guides/ADDING_NEW_GAME_RULES.md and existing rule implementations.