rabukasim / docs /plans /magic_numbers_enum_analysis.md
trioskosmos's picture
chore: remove large files for HF Space
9bd4ce5
# Hardcoded Magic Numbers Analysis Report
## Overview
This report identifies hardcoded magic numbers in the Loveca card game engine (Rust codebase) that could be converted to enums for better code maintainability and type safety.
## Summary of Findings
The analysis found **numerous hardcoded magic numbers** across multiple files that are candidates for enum conversion. The codebase already has a good foundation with `core/enums.rs` and `core/generated_constants.rs`, but many game-specific constants remain scattered as magic numbers.
---
## Candidate Magic Numbers for Enum Conversion
### 1. Stage Slot Constants
**Current State:** Hardcoded `3` used throughout the codebase for stage slots.
**Files with magic numbers:**
- [`engine_rust_src/src/core/logic/rules.rs`](engine_rust_src/src/core/logic/rules.rs)
- [`engine_rust_src/src/core/logic/game.rs`](engine_rust_src/src/core/logic/game.rs)
- [`engine_rust_src/src/core/logic/player.rs`](engine_rust_src/src/core/logic/player.rs)
- [`engine_rust_src/src/core/logic/performance.rs`](engine_rust_src/src/core/logic/performance.rs)
- [`engine_rust_src/src/core/logic/interpreter/handlers/state.rs`](engine_rust_src/src/core/logic/interpreter/handlers/state.rs)
**Examples:**
```rust
// Current usage
for slot in 0..3 { ... }
if slot_idx >= 0 && slot_idx < 3 { ... }
stage: [i32; 3], // In player.rs
```
**Recommended Enum:**
```rust
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StageSlot {
Left = 0,
Center = 1,
Right = 2,
}
impl StageSlot {
pub const COUNT: usize = 3;
pub fn all() -> impl Iterator<Item = Self> { ... }
}
```
---
### 2. Color Constants (Card Colors)
**Current State:** Colors 0-6 used extensively with magic numbers like `color < 7`, `color == 7` (wildcard).
**Files with magic numbers:**
- [`engine_rust_src/src/core/logic/rules.rs`](engine_rust_src/src/core/logic/rules.rs) (lines 251-256, 301-306)
- [`engine_rust_src/src/core/logic/performance.rs`](engine_rust_src/src/core/logic/performance.rs) (lines 85-133)
- [`engine_rust_src/src/core/logic/hearts.rs`](engine_rust_src/src/core/hearts.rs)
- [`engine_rust_src/src/core/logic/interpreter/handlers/state.rs`](engine_rust_src/src/core/logic/interpreter/handlers/state.rs) (lines 976-1025)
**Examples:**
```rust
if color < 7 { ... }
if color == 7 { color = ctx.selected_color as usize; }
// attr 1-7 = colors 0-6. attr 0 = Generic/Any (index 6).
let idx = if attr == 0 || attr == 7 { 6 } else if attr <= 6 { attr - 1 } else { 99 };
```
**Recommended Enum:**
```rust
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum Color {
#[default]
Smile = 0, // Red
Pure = 1, // Green
Cool = 2, // Blue
Active = 3,
Natural = 4,
Elegant = 5,
All = 6, // Wildcard/Any color
}
impl Color {
pub const COUNT: usize = 7;
pub fn is_valid(idx: usize) -> bool { idx < Self::COUNT }
}
```
---
### 3. Choice Index Constants
**Current State:** Special choice indices like `99` (Done/Cancel) and `999` (All) used throughout.
**Files with magic numbers:**
- [`engine_rust_src/src/core/logic/constants.rs`](engine_rust_src/src/core/logic/constants.rs) (lines 56-59)
- [`engine_rust_src/src/core/logic/interpreter/handlers/state.rs`](engine_rust_src/src/core/logic/interpreter/handlers/state.rs) (lines 76, 89, 366, 474, etc.)
- [`engine_rust_src/src/core/logic/interpreter/handlers/flow.rs`](engine_rust_src/src/core/logic/interpreter/handlers/flow.rs)
**Examples:**
```rust
pub const CHOICE_DONE: i16 = 99;
pub const CHOICE_ALL: i16 = 999;
// Usage
if ctx.choice_index == 99 { ... }
```
**Recommendation:** These are already defined in `constants.rs` as `CHOICE_DONE` and `CHOICE_ALL`. Consider converting to an enum for stronger typing.
---
### 4. Depth Limits (Recursion Guards)
**Current State:** Magic numbers `5`, `10` used for recursion depth limits.
**Files with magic numbers:**
- [`engine_rust_src/src/core/logic/rules.rs`](engine_rust_src/src/core/logic/rules.rs) (lines 35, 183, 359)
- [`engine_rust_src/src/core/logic/interpreter/conditions.rs`](engine_rust_src/src/core/logic/interpreter/conditions.rs) (line 42)
**Examples:**
```rust
if depth > 5 {
return 0;
}
if depth > 10 {
return false;
}
```
**Recommended Enum:**
```rust
#[derive(Debug, Clone, Copy)]
pub enum RecursionDepth {
BladeCalculation = 5,
ConditionCheck = 10,
Interpreter = 8, // MAX_DEPTH in interpreter/mod.rs
}
```
---
### 5. Action ID Base Ranges
**Current State:** Already partially defined in `generated_constants.rs` but some ranges used directly as magic numbers.
**Files with magic numbers:**
- [`engine_rust_src/src/core/logic/action_factory.rs`](engine_rust_src/src/core/logic/action_factory.rs)
- [`engine_rust_src/src/core/logic/handlers.rs`](engine_rust_src/src/core/logic/handlers.rs)
**Examples:**
```rust
// Already defined but used directly
ACTION_BASE_HAND + (h_idx as i32) * 10 + (slot as i32)
action >= 600 && action <= 602 // Live slot selection
action >= 100 && action < 200 // Discard index
action >= 200 && action < 300 // Hand index
```
**Recommendation:** The constants in `generated_constants.rs` are good. Ensure all code uses these constants instead of magic numbers.
---
### 6. Interpreter Limits
**Current State:** Magic numbers for interpreter step limits.
**Files with magic numbers:**
- [`engine_rust_src/src/core/logic/interpreter/mod.rs`](engine_rust_src/src/core/logic/interpreter/mod.rs) (lines 41-42, 95-98)
**Examples:**
```rust
pub const MAX_DEPTH: usize = 8;
pub const MAX_BYTECODE_LOG_SIZE: usize = 500;
if executor.steps >= 1000 {
println!("[ERROR] Interpreter infinite loop detected (1000 steps)");
}
```
**Recommendation:** Already well-defined. Consider adding `MAX_INTERPRETER_STEPS = 1000` to constants.
---
### 7. Target Slot Encoding
**Current State:** Magic numbers for special slot values like `0`, `4`, `10`, `-1`.
**Files with magic numbers:**
- [`engine_rust_src/src/core/logic/interpreter/suspension.rs`](engine_rust_src/src/core/logic/interpreter/suspension.rs) (lines 116-131)
- [`engine_rust_src/src/core/logic/rules.rs`](engine_rust_src/src/core/logic/rules.rs) (lines 87-93, 232-238)
**Examples:**
```rust
// From suspension.rs
pub fn resolve_target_slot(target_slot: i32, ctx: &AbilityContext) -> usize {
if target_slot == 0 && ctx.target_slot >= 0 {
return ctx.target_slot as usize;
}
if target_slot == 4 && ctx.area_idx >= 0 {
ctx.area_idx as usize
} else if target_slot == -1 || target_slot == 4 {
// Fallback to 0
}
}
// From rules.rs - target area encoding
let target_area = s & 0xFF;
if target_area == 1 { targets_us = true; }
else if (target_area == 4 || target_area == 0) && other_slot == slot_idx { ... }
else if target_area == 10 && slot_idx as i16 == ctx.target_slot { ... }
```
**Recommended Enum:**
```rust
#[derive(Debug, Clone, Copy)]
pub enum TargetSlot {
ThisSlot = 0,
AllSlots = 1,
OpponentSlot = 2,
AreaIndex = 4, // Use context.area_idx
TargetSlot = 10, // Use context.target_slot
None = -1,
}
```
---
### 8. Card Type Values
**Current State:** Magic numbers `1` (Member), `2` (Live) used in filter checks.
**Files with magic numbers:**
- [`engine_rust_src/src/core/logic/filter.rs`](engine_rust_src/src/core/logic/filter.rs) (lines 116-127)
**Examples:**
```rust
if self.card_type == 1 {
// Member
if !db.members.contains_key(&cid) { return false; }
} else if self.card_type == 2 {
// Live
if !db.lives.contains_key(&cid) { return false; }
}
```
**Recommendation:** These should use the existing `TargetType` enum or create a `CardType` enum.
---
### 9. Zone Values
**Current State:** Magic numbers for zones like `6` (Hand), `7` (Discard), `4` (Stage), etc.
**Files with magic numbers:**
- [`engine_rust_src/src/core/logic/interpreter/handlers/movement.rs`](engine_rust_src/src/core/logic/interpreter/handlers/movement.rs)
- [`engine_rust_src/src/core/logic/interpreter/conditions.rs`](engine_rust_src/src/core/logic/interpreter/conditions.rs)
**Examples:**
```rust
// Zone encoding
source_zone == 6 || source_zone == 7 // Hand or Discard
source_zone == 15 // Yell
if dest_slot == 6 { ... } // Hand
else if dest_slot == 7 { ... } // Discard
```
**Recommendation:** The `Zone` enum already exists in `enums.rs`. Ensure all code uses it instead of magic numbers.
---
### 10. RPS (Rock-Paper-Scissors) Values
**Current State:** Magic numbers `0`, `1`, `2` for RPS choices.
**Files with magic numbers:**
- [`engine_rust_src/src/core/logic/handlers.rs`](engine_rust_src/src/core/logic/handlers.rs)
- [`engine_rust_src/src/core/logic/game.rs`](engine_rust_src/src/core/logic/game.rs)
**Examples:**
```rust
let p0_wins = (p0 == 0 && p1 == 2) || (p0 == 1 && p1 == 0) || (p0 == 2 && p1 == 1);
```
**Recommended Enum:**
```rust
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum RpsChoice {
Rock = 0,
Paper = 1,
Scissors = 2,
}
```
---
### 11. Special Card ID Values
**Current State:** Magic numbers like `-1` (empty/no card), special card IDs.
**Files with magic numbers:**
- Multiple files
**Examples:**
```rust
if cid == -1 { return false; }
stage[slot] = -1; // Clear slot
```
**Recommendation:** Define constants for special card IDs:
```rust
pub const NO_CARD_ID: i32 = -1;
pub const INVALID_CARD_ID: i32 = 0;
```
---
## Priority Recommendations
### High Priority (Most Impactful)
1. **StageSlot Enum** - Used in 50+ locations
2. **Color Enum** - Central to game mechanics, heart/blade system
3. **Recursion Depth Constants** - Safety-critical
### Medium Priority
4. **TargetSlot Encoding** - Used in interpreter extensively
5. **Card Type Filter Values** - Used in filtering logic
6. **Zone Constants** - Ensure consistent usage with existing enum
### Low Priority (Nice to Have)
7. **RPS Choice Enum** - Limited usage
8. **Special Choice Indices** - Already partially defined
---
## Implementation Strategy
1. **Create new enum definitions** in `core/enums.rs` or a new `core/logic/game_enums.rs`
2. **Add constants** for special values in `core/logic/constants.rs`
3. **Systematically replace magic numbers** with enum variants or constants
4. **Add tests** to verify behavior matches previous magic number behavior
5. **Update documentation** to reflect the new type-safe constants
---
## Existing Good Patterns
The codebase already has good patterns in place:
- `Phase` enum in [`core/enums.rs`](engine_rust_src/src/core/enums.rs)
- `Zone` enum in [`core/enums.rs`](engine_rust_src/src/core/enums.rs)
- `TriggerType`, `EffectType`, `ConditionType` enums
- `generated_constants.rs` for opcode and action ID bases
- `constants.rs` for interpreter constants
The task is to extend this pattern to the remaining magic numbers found in this analysis.