Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.idea/*
cmake-build-*
build-export/*
build/
.DS_Store
74 changes: 46 additions & 28 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,65 +5,83 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Build Commands

```bash
# Configure and build with tests
# Build with tests (manual)
mkdir build && cd build
cmake -DBUILD_TESTS=ON ..
make -j

# Build using CMake presets (preferred on macOS)
cmake --preset clang # Apple clang
cmake --preset clang-llvm # Homebrew LLVM (required for clang-tidy/IWYU)
cmake --build build/clang-llvm -j

# Run all tests
ctest

# Run a single test by name
./lib/GameLogicTests --gtest_filter=Game.moveNorth
# Run a single test by name (lib tests)
./build/clang/lib/GameLogicTests --gtest_filter=GameTest.moveToValidRoomChangesPlayerRoom
```

## Code Quality Tools

The project uses `just` for common tasks (requires [just](https://github.com/casey/just)):

```bash
just build-export # Build with compile_commands.json
just build-export # Generate compile_commands.json (uses Homebrew LLVM)
just clang-tidy # Run static analysis
just clang-tidy-fix # Auto-fix clang-tidy issues
just iwyu # Run Include What You Use
just format # Format code with clang-format
```

Manual equivalents:
```bash
# clang-tidy (after build-export)
clang-tidy -p build-export *.cpp */*.cpp

# IWYU
iwyu_tool.py -p build-export main*.cpp */*.cpp -- -Xiwyu --error -Xiwyu --cxx17ns

# Format
clang-format -style=file -i main*.cpp */*.*pp
```
> **Note**: `just build-export` uses `/opt/homebrew/opt/llvm/bin/clang++`. Apple's system clang cannot find C++ standard headers for IWYU/clang-tidy. Always use the Homebrew LLVM preset for these tools.

## CI Checks

All PRs must pass: build, clang-tidy, IWYU, clang-format. Warnings are treated as errors.
All PRs must pass: build (Ubuntu/gcc), clang-tidy, IWYU, clang-format. Warnings are treated as errors.

## Architecture

The game logic lives in `lib/` as a shared library (`GameLogic`), with `main.cpp` as a thin entry point.
The game logic lives in `lib/` as a shared library (`GameLogic`), with `main.cpp` as a thin entry point that wires up concrete implementations.

**Interfaces** (namespace `adv_sk`) β€” the dependency-inversion layer:
- `IInputHandler` β€” input/output abstraction; `Action` enum defines all user actions
- `IMap` β€” map query abstraction (`next_room`, `get_room`, `get_welcome_message`)
- `IPlayer` β€” player state abstraction (room, inventory)

**Core classes** (namespace `adv_sk`):
- `Game` - Main game controller with game loop, owns Map, Player, and IInputHandler
- `IInputHandler` - Interface for input/output abstraction (enables testing via mock)
- `Map` - Collection of connected rooms, created via `create_map()`
- `Room` - Location with connections and inventory
- `Player` - Tracks current room and inventory
- `Direction` - Enum (North/South/East/West) with `ALL_DIRECTIONS` constant
**Concrete classes**:
- `Game` β€” owns `IMap`, `IPlayer`, `IInputHandler` via `unique_ptr`; drives the game loop
- `Map` / `Room` β€” concrete map with connected rooms; built via `create_map()`
- `Player` β€” tracks current room and inventory (`InventoryItem`)
- `ConsoleInputHandler` β€” terminal I/O implementation
- `Direction` β€” enum (North/South/East/West) with `ALL_DIRECTIONS` constant

**Key patterns**:
- Dependency injection: Game accepts `IInputHandler` for testability
- Tests use `TestInputHandler` mock to script game behavior without console I/O
**Test doubles** (in `lib/`, namespace `adv_sk::test`):
- `MockInputHandler`, `MockMap`, `MockPlayer` β€” GMock mocks for London-style unit tests
- Tests inject mocks via `Game(unique_ptr<IMap>, unique_ptr<IPlayer>, unique_ptr<IInputHandler>)`

## Naming Conventions

Enforced by clang-tidy:
- Classes: `CamelCase`
- Functions/variables: `lower_case`
- Functions/variables: `snake_case`
- Private members: `_prefixed`
- Struct initialization: use designated initializers β€” `InventoryItem{.name = "sword"}` (not positional)

## Known NOLINT Patterns

GMock `MOCK_METHOD` macros generate public member variables, requiring suppression on the surrounding class:

```cpp
// NOLINTBEGIN(misc-non-private-member-variables-in-classes,cppcoreguidelines-non-private-member-variables-in-classes)
class MockFoo : public IFoo { ... };
// NOLINTEND(misc-non-private-member-variables-in-classes,cppcoreguidelines-non-private-member-variables-in-classes)
```

GoogleTest `ASSERT_TRUE` does not satisfy `bugprone-unchecked-optional-access` β€” suppress on the next line accessing the optional:

```cpp
ASSERT_TRUE(opt.has_value());
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
use(*opt);
```
Loading