Spaces:
Sleeping
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.rsengine_rust_src/src/core/logic/game.rsengine_rust_src/src/core/logic/player.rsengine_rust_src/src/core/logic/performance.rsengine_rust_src/src/core/logic/interpreter/handlers/state.rs
Examples:
// Current usage
for slot in 0..3 { ... }
if slot_idx >= 0 && slot_idx < 3 { ... }
stage: [i32; 3], // In player.rs
Recommended Enum:
#[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(lines 251-256, 301-306)engine_rust_src/src/core/logic/performance.rs(lines 85-133)engine_rust_src/src/core/logic/hearts.rsengine_rust_src/src/core/logic/interpreter/handlers/state.rs(lines 976-1025)
Examples:
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:
#[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(lines 56-59)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
Examples:
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(lines 35, 183, 359)engine_rust_src/src/core/logic/interpreter/conditions.rs(line 42)
Examples:
if depth > 5 {
return 0;
}
if depth > 10 {
return false;
}
Recommended Enum:
#[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:
Examples:
// 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(lines 41-42, 95-98)
Examples:
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(lines 116-131)engine_rust_src/src/core/logic/rules.rs(lines 87-93, 232-238)
Examples:
// 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:
#[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(lines 116-127)
Examples:
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.rsengine_rust_src/src/core/logic/interpreter/conditions.rs
Examples:
// 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:
Examples:
let p0_wins = (p0 == 0 && p1 == 2) || (p0 == 1 && p1 == 0) || (p0 == 2 && p1 == 1);
Recommended Enum:
#[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:
if cid == -1 { return false; }
stage[slot] = -1; // Clear slot
Recommendation: Define constants for special card IDs:
pub const NO_CARD_ID: i32 = -1;
pub const INVALID_CARD_ID: i32 = 0;
Priority Recommendations
High Priority (Most Impactful)
- StageSlot Enum - Used in 50+ locations
- Color Enum - Central to game mechanics, heart/blade system
- Recursion Depth Constants - Safety-critical
Medium Priority
- TargetSlot Encoding - Used in interpreter extensively
- Card Type Filter Values - Used in filtering logic
- Zone Constants - Ensure consistent usage with existing enum
Low Priority (Nice to Have)
- RPS Choice Enum - Limited usage
- Special Choice Indices - Already partially defined
Implementation Strategy
- Create new enum definitions in
core/enums.rsor a newcore/logic/game_enums.rs - Add constants for special values in
core/logic/constants.rs - Systematically replace magic numbers with enum variants or constants
- Add tests to verify behavior matches previous magic number behavior
- Update documentation to reflect the new type-safe constants
Existing Good Patterns
The codebase already has good patterns in place:
Phaseenum incore/enums.rsZoneenum incore/enums.rsTriggerType,EffectType,ConditionTypeenumsgenerated_constants.rsfor opcode and action ID basesconstants.rsfor interpreter constants
The task is to extend this pattern to the remaining magic numbers found in this analysis.