# CLAUDE.md ## CRITICAL BASH RULES (NEVER VIOLATE) **NEVER chain bash commands.** Do not use `&&`, `||`, or `;` to combine commands. Each command must be a separate Bash tool call. Use parallel tool calls when commands are independent. **NEVER prefix a command with `cd`.** Run `git`, `gh`, `bazel`, etc. directly from whatever the cwd already is. The cwd is a subdirectory of the worktree; `git`/`gh` find the repo via `.git` discovery and `bazel` finds the workspace via MODULE.bazel discovery — walking up from a subdir works fine. The shell resets cwd after every command anyway, so a `cd` never persists. (And `cd && ` violates the no-chaining rule above and forces an approval prompt every time.) Only `cd` in the rare case a tool genuinely cannot locate its root, and even then as its own Bash call, never a chain. ## CRITICAL GIT RULES (NEVER VIOLATE) **NEVER use `git -C`.** Just run `git` directly from the cwd — it finds the repo via `.git` discovery. Do not `cd` to the repo root either (see the no-`cd` bash rule above). **NEVER push directly to main/master.** No exceptions. Not for "small changes." Not for docs. Not ever. **NEVER merge PRs.** You create PRs. The user merges them. No exceptions. **ALWAYS use this workflow:** 1. Create a feature branch from origin/main 2. Commit to that branch 3. Create a PR with `gh pr create` 4. Wait for user to merge (DO NOT run `gh pr merge`) If you catch yourself about to run `git push origin main` or `git push origin :main`, STOP. You are about to violate a critical rule. Create a PR instead. If you catch yourself about to run `gh pr merge`, STOP. Only the user merges PRs. --- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview Eagle0 is a multi-language gaming system combining strategic turn-based gameplay (Eagle) with tactical hex-based combat (Shardok). The system integrates LLM-based narrative generation and supports both human and AI players. ## Architecture **Three-Tier Game System:** - **Unity Client (C#)**: Real-time strategy game client with integrated tactical combat UI - **Eagle (Scala)**: Strategic layer managing turn-based gameplay, diplomacy, hero progression, and province control - **Shardok (C++)**: Tactical layer handling real-time hex-based combat simulation with performance-critical battle resolution **Communication Flow:** ``` Unity Client ↔ Eagle (gRPC streaming) ↔ Shardok (internal gRPC) ``` **Key Entry Points:** - `/src/main/csharp/net/eagle0/clients/unity/eagle0/` - Unity C# game client - `/src/main/scala/net/eagle0/eagle/Main.scala` - Eagle strategic game server - `/src/main/cpp/net/eagle0/shardok/shardok_server_main.cpp` - Shardok tactical server **Protocol Buffer Architecture:** - Extensive use of protobuf for type-safe communication - Separate packages: `api/` (client-facing), `internal/` (server state), `views/` (client projections) - Event sourcing pattern with immutable action history ## Essential Commands ### Building ```bash # Build Eagle server (Scala strategic layer) bazel build //src/main/scala/net/eagle0/eagle:eagle_server_deploy.jar # Build Shardok server (C++ tactical layer) bazel build -c opt //src/main/cpp/net/eagle0/shardok:shardok-server # Shardok server includes both AI algorithms bazel build //src/main/cpp/net/eagle0/shardok:shardok-server # Build Unity/C# client ./scripts/build_protos.sh # Protocol buffer generation for Unity ./scripts/build_plugins.sh # Native plugins for all platforms ./scripts/build_windows_plugin.sh # Windows-specific plugin build # Unity builds via CI: ci/github_actions/build_unity.sh ``` ### Running Services ```bash # Eagle server (port 40032) bazel run //src/main/scala/net/eagle0/eagle:eagle_server -- --eagle-grpc-port 40032 # Or: ./scripts/eagle_run.sh # Shardok server bazel run //src/main/cpp/net/eagle0/shardok:shardok-server --compilation_mode=opt # Or: ./scripts/shardok_run.sh ``` ### Testing ```bash # Run all tests bazel test //src/test/... //src/main/go/... # Component-specific tests bazel test //src/test/scala/... # Scala Eagle tests bazel test //src/test/cpp/... # C++ Shardok tests ``` ### Code Generation ```bash bazel run gazelle # Update Go build files ./scripts/updateActionResultTypes.sh # Update protocol buffer mappings ``` ### Pre-Commit Checklist **MANDATORY: Before running `git commit`, verify:** 1. **If you modified any BUILD.bazel file:** Run `bazel run gazelle` and stage any changes it makes 2. **If you modified C++ or C# files:** Run `clang-format -i` on the modified files 3. **If you modified Scala files:** scalafmt will run automatically via pre-commit hook 4. **ALWAYS run the full test suite for your language changes before pushing:** - For Scala changes: `bazel test //src/test/scala/...` - For C++ changes: `bazel test //src/test/cpp/...` - This catches exhaustive pattern match errors and other compile-time failures that single-target builds miss The pre-commit hook runs gazelle but only checks if it succeeds - it does NOT verify the BUILD files are in canonical format. The `gazelle_test` will fail if deps are not alphabetically sorted. **Always run gazelle manually after BUILD file changes.** ### Code Formatting ```bash # ALWAYS run clang-format after making any C++ or C# code changes clang-format -i # Format all C++ files in a directory: find . -name "*.cpp" -o -name "*.hpp" | xargs clang-format -i # Format all C# files in a directory: find . -name "*.cs" | xargs clang-format -i ``` ### Static Analysis ```bash # Run clang-tidy static analysis on C++ files # Note: This may show some header include errors but will still analyze the main file bazel run @llvm_toolchain//:clang-tidy -- --checks='readability-*,bugprone-*,clang-analyzer-*' -- -I/Users/dancrosby/CodingProjects/github/eagle0 -std=c++23 # Example for AI files: bazel run @llvm_toolchain//:clang-tidy -- --checks='readability-*,bugprone-*,clang-analyzer-*' /Users/dancrosby/CodingProjects/github/eagle0/src/main/cpp/net/eagle0/shardok/ai/AIUnitScoreCalculator.cpp -- -I/Users/dancrosby/CodingProjects/github/eagle0 -std=c++23 ``` ## AI Algorithm Selection Eagle0 supports two AI algorithms for tactical combat decision-making: ### Iterative Deepening AI (Default) The original minimax-based AI with sophisticated randomness handling: - **Advantages**: Proven, sophisticated randomness evaluation, comprehensive lookahead - **Use cases**: Production builds, scenarios requiring precise evaluation - **Performance**: Single-threaded, thorough evaluation ### Monte Carlo Tree Search AI (MCTS) Modern MCTS-based AI with multithreading support: - **Advantages**: Multithreaded, better performance on modern CPUs, anytime algorithm - **Use cases**: Performance testing, scenarios requiring fast decisions - **Performance**: Multithreaded, adaptive depth based on time budget ### Switching Between Algorithms The algorithm is selected at **runtime** via the ShardokAIClient constructor: ```cpp // Using Iterative Deepening AI (default) ShardokAIClient client(playerId, isDefender, hexMap, settings); // OR explicitly: ShardokAIClient client(playerId, isDefender, hexMap, settings, AIAlgorithmType::ITERATIVE_DEEPENING); // Using MCTS AI ShardokAIClient client(playerId, isDefender, hexMap, settings, AIAlgorithmType::MCTS); ``` ```bash # Build the server (includes both AI algorithms) bazel build //src/main/cpp/net/eagle0/shardok:shardok-server # Test both algorithms bazel test //src/test/cpp/net/eagle0/shardok/ai:ai_iterative_deepening_test bazel test //src/test/cpp/net/eagle0/shardok/ai:ai_mcts_test # If available # Performance tests ./scripts/ai_perf_test.sh # Uses whatever algorithm the server is configured to use ``` Both implementations are compatible with all existing interfaces and produce the same `SearchResult` structure. **Note**: Both implementations are documented in `src/main/cpp/net/eagle0/shardok/ai/AI_SCORING_SYSTEM.md`, including recommendations for improving MCTS randomness handling. The AI algorithm selection is made at runtime when creating ShardokAIClient instances, allowing different AI strategies to be used for different players or game situations within the same server process. ## Language-Specific Patterns **Scala (Strategic Layer):** - Use `EngineImpl.scala` for core game logic modifications - Follow event sourcing pattern - all changes through immutable actions - gRPC streaming for real-time client updates via `EagleServiceImpl.scala` - LLM integration in `/common/llm_integration/` for narrative generation **C++ (Tactical Layer):** - Performance-critical combat in `ShardokEngine.hpp/.cpp` - FlatBuffers for efficient serialization in `/flatbuffer/` directory - AI systems in `/ai/` subdirectory with pluggable strategy selectors - Extensive unit testing with Google Test framework **Protocol Buffers:** - Three-layer structure: `api/` (client), `internal/` (server), `views/` (projections) - Use `shardok_internal_interface.proto` for Eagle-Shardok communication - Maintain backward compatibility when modifying existing messages **C# (Unity Client):** - Located in `/src/main/csharp/net/eagle0/clients/unity/eagle0/` - Uses Unity 6 (6000.4.10f1) with comprehensive protobuf integration (100+ .proto files) - Key components: `EagleConnection.cs` (gRPC client), `EagleGameController.cs` (main game logic) - Real-time bidirectional streaming with server via `PersistentClientConnection.cs` - Strategic map UI in `Assets/Eagle/`, tactical battle UI in `Assets/Shardok/` - Seamless transition between strategic gameplay and hex-based tactical combat - **NEVER add defensive null checks on Unity Inspector fields** - these hide configuration bugs. If a field isn't linked in the editor, it should throw a NullReferenceException so the problem is immediately obvious. Silently skipping code when a required field is null makes bugs harder to find. **Go (Build Tools):** - Build automation and code generation utilities - AWS S3 integration for deployment artifacts ## Testing Strategy - Comprehensive unit tests for both Scala and C++ components - Integration tests for Eagle-Shardok communication - Map validation tests ensure game content integrity - Use `GameSettings_test_utils.cpp` and `ShardokEngineBasedTestData.cpp` for C++ test helpers ### Scala Testing Patterns **Use `inside()` instead of `asInstanceOf` for type matching in tests:** Never use `asInstanceOf` in tests. Instead, use ScalaTest's `inside()` pattern for safe type matching: ```scala // BAD - don't do this val changedHero = result.changedHeroes.head.asInstanceOf[ChangedHeroC] changedHero.heroId shouldBe 19 // GOOD - use inside() pattern import org.scalatest.Inside.inside inside(result.changedHeroes.head) { case changedHero: ChangedHeroC => changedHero.heroId shouldBe 19 changedHero.vigorChange shouldBe StatDelta(17.2) } ``` The `inside()` pattern: - Provides better error messages when the type doesn't match - Is idiomatic ScalaTest - Works with pattern matching for more complex assertions ## Performance Testing When making performance-related changes to the AI or engine: ```bash # 1. Commit your changes to a feature branch git checkout -b performance-improvement-feature git add . && git commit -m "Implement performance improvement" # 2. Run performance tests multiple times on your branch to reduce noise for i in 1 2 3; do echo "=== Run $i ===" ./scripts/ai_perf_test.sh 2>&1 | grep -A 20 "AI Search Performance Summary" done # Save or note the results # 3. Switch to main branch and run the same tests git checkout main for i in 1 2 3; do echo "=== Run $i ===" ./scripts/ai_perf_test.sh 2>&1 | grep -A 20 "AI Search Performance Summary" done # 4. Compare the results between your branch and main # Key metrics to compare: # - Commands evaluated at each depth (e.g., "Depth 3: 169/523 commands") # - Average search depth achieved # - Completion rates at each depth ``` **Important notes:** - Run tests multiple times (3-5) to account for performance variance - Focus on commands evaluated at each depth rather than total commands - Commands at different depths aren't directly comparable (depth 3 is more valuable than depth 2) - **Always test performance changes** - what seems like an optimization may sometimes have unexpected overhead or behavior changes. ## Troubleshooting Scala Build Errors ### MissingType Errors When you see errors like: ``` dotty.tools.dotc.core.MissingType: Cannot resolve reference to type net.eagle0.eagle.internal.game_state.type.GameState ``` **This is NOT a Scala compiler crash.** This is a missing dependency in BUILD.bazel. **How to fix:** 1. Identify the missing type from the error message (e.g., `game_state.GameState`) 2. Find the Bazel target that provides this type (e.g., `//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto`) 3. Add it to the `deps` of the failing target 4. If the type appears in a public method signature, also add it to `exports` so downstream targets can see it **Common pattern:** When adding a method to a class that takes or returns a proto type, the proto dependency often needs to be added to both `deps` AND `exports`. ### Bazel Clean **NEVER run `bazel clean` without asking first.** It rarely fixes actual issues and wastes significant rebuild time. The issues that seem like they need `bazel clean` are usually: - Missing imports in Scala code - Missing dependencies in BUILD.bazel - Missing exports for types used in public signatures ## Game Content **Maps:** `.e0mj` files in `/src/main/resources/net/eagle0/shardok/maps/` **Configuration:** Game parameters in `/src/main/resources/net/eagle0/eagle/game_parameters.json` **Data Files:** TSV format for battalions, heroes, and other game data **NEVER modify hero names.** Do not change names in `heroes.tsv`, `generated_heroes`, or any other hero data files. The names are carefully chosen and are not to be altered. ## Deployment - Bazel handles multi-language builds and dependencies - CI/CD via GitHub Actions with platform-specific build scripts in `/ci/github_actions/` - Docker containerization available via `ci/eagle_run.Dockerfile` - Always run "bazel run //:gazelle" after editing any BUILD.bazel files - *ALWAYS ALWAYS* run "bazel run gazelle" after any change that modifies a BUILD.bazel file