diff --git a/common/data/Element/element.go b/common/data/Element/element.go index 711d0f9c6..412f450a8 100644 --- a/common/data/Element/element.go +++ b/common/data/Element/element.go @@ -42,15 +42,15 @@ const ( maxMatrixSize = 227 // 矩阵维度(覆盖最大属性ID 226) ) -// 合法单属性ID集合(快速校验) -var validSingleElementIDs = map[int]bool{ +// 合法单属性ID集合(按ID直接索引,避免运行时 map 查找) +var validSingleElementIDs = [maxMatrixSize]bool{ 1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true, 8: true, 9: true, 10: true, 11: true, 12: true, 13: true, 14: true, 15: true, 16: true, 17: true, 18: true, 19: true, 20: true, 221: true, 222: true, 223: true, 224: true, 225: true, 226: true, } -// 元素名称映射(全属性对应,便于日志输出) -var elementNameMap = map[ElementType]string{ +// 元素名称映射(按ID直接索引,便于日志输出) +var elementNameMap = [maxMatrixSize]string{ ElementTypeGrass: "GRASS", ElementTypeWater: "WATER", ElementTypeFire: "FIRE", @@ -198,46 +198,55 @@ type ElementCombination struct { ID int // 组合唯一ID } -// 全局预加载资源(程序启动时init初始化,运行时直接使用) +// 全局预加载资源(程序启动时初始化,运行时只读) var ( - // 元素组合池:key=组合ID,value=组合实例(预加载所有合法组合) - elementCombinationPool = make(map[int]*ElementCombination, 150) // 128双+26单=154,预分配足够容量 - // 单属性克制矩阵(预初始化所有特殊克制关系,默认1.0) - matrix [maxMatrixSize][maxMatrixSize]float64 + validCombinationIDs [maxMatrixSize]bool + elementCombinationPool [maxMatrixSize]ElementCombination + dualElementSecondaryPool [maxMatrixSize]ElementType + matrix [maxMatrixSize][maxMatrixSize]float64 + Calculator *ElementCalculator ) // init 预加载所有资源(程序启动时执行一次,无并发问题) func init() { - // 1. 初始化单属性克制矩阵 initFullTableMatrix() + initElementCombinationPool() + Calculator = NewElementCalculator() +} - // 2. 预加载所有单属性组合 - for id := range validSingleElementIDs { - combo := &ElementCombination{ - Primary: ElementType(id), - Secondary: nil, - ID: id, +func initElementCombinationPool() { + for id, valid := range validSingleElementIDs { + if !valid { + continue + } + validCombinationIDs[id] = true + elementCombinationPool[id] = ElementCombination{ + Primary: ElementType(id), + ID: id, } - elementCombinationPool[id] = combo } - // 3. 预加载所有双属性组合 for dualID, atts := range dualElementMap { primaryID, secondaryID := atts[0], atts[1] - // 按ID升序排序,保证组合一致性 primary, secondary := ElementType(primaryID), ElementType(secondaryID) if primary > secondary { primary, secondary = secondary, primary } - combo := &ElementCombination{ + + dualElementSecondaryPool[dualID] = secondary + validCombinationIDs[dualID] = true + elementCombinationPool[dualID] = ElementCombination{ Primary: primary, - Secondary: &secondary, + Secondary: &dualElementSecondaryPool[dualID], ID: dualID, } - elementCombinationPool[dualID] = combo } } +func isValidCombinationID(id int) bool { + return id > 0 && id < maxMatrixSize && validCombinationIDs[id] +} + // IsDual 判断是否为双属性 func (ec *ElementCombination) IsDual() bool { return ec.Secondary != nil @@ -245,84 +254,82 @@ func (ec *ElementCombination) IsDual() bool { // Elements 获取所有属性列表 func (ec *ElementCombination) Elements() []ElementType { - if ec.IsDual() { - return []ElementType{ec.Primary, *ec.Secondary} + if secondary := ec.Secondary; secondary != nil { + return []ElementType{ec.Primary, *secondary} } return []ElementType{ec.Primary} } // String 友好格式化输出 func (ec *ElementCombination) String() string { - primaryName := elementNameMap[ec.Primary] - if !ec.IsDual() { - return fmt.Sprintf("(%s)", primaryName) + if secondary := ec.Secondary; secondary != nil { + return fmt.Sprintf("(%s, %s)", elementNameMap[ec.Primary], elementNameMap[*secondary]) } - return fmt.Sprintf("(%s, %s)", primaryName, elementNameMap[*ec.Secondary]) + return fmt.Sprintf("(%s)", elementNameMap[ec.Primary]) } -// ElementCalculator 无锁元素克制计算器(依赖预加载资源) +// ElementCalculator 无锁元素克制计算器(所有倍数在初始化阶段预计算) type ElementCalculator struct { - offensiveCache map[string]float64 // 攻击克制缓存(运行时填充,无并发写) + offensiveTable [maxMatrixSize][maxMatrixSize]float64 } -// NewElementCalculator 创建计算器实例(仅初始化缓存) +// NewElementCalculator 创建计算器实例(构建只读查表缓存) func NewElementCalculator() *ElementCalculator { - return &ElementCalculator{ - offensiveCache: make(map[string]float64, 4096), // 预分配大容量缓存 + c := &ElementCalculator{} + c.initOffensiveTable() + return c +} + +func (c *ElementCalculator) initOffensiveTable() { + for attackerID, valid := range validCombinationIDs { + if !valid { + continue + } + attacker := &elementCombinationPool[attackerID] + for defenderID, valid := range validCombinationIDs { + if !valid { + continue + } + defender := &elementCombinationPool[defenderID] + c.offensiveTable[attackerID][defenderID] = c.calculateMultiplier(attacker, defender) + } } } // getMatrixValue 直接返回矩阵值(修复核心问题:不再将0转换为1) func (c *ElementCalculator) getMatrixValue(attacker, defender ElementType) float64 { - return matrix[attacker][defender] // 矩阵默认已初始化1.0,特殊值直接返回 + return matrix[attacker][defender] } -// GetCombination 获取元素组合(直接从预加载池读取) +// GetCombination 获取元素组合(直接按ID索引) func (c *ElementCalculator) GetCombination(id int) (*ElementCombination, error) { - combo, exists := elementCombinationPool[id] - if !exists { + if !isValidCombinationID(id) { return nil, fmt.Errorf("invalid element combination ID: %d", id) } - return combo, nil + return &elementCombinationPool[id], nil } -// GetOffensiveMultiplier 计算攻击方→防御方的克制倍数(缓存优先) +// GetOffensiveMultiplier 计算攻击方→防御方的克制倍数(只读查表) func (c *ElementCalculator) GetOffensiveMultiplier(attackerID, defenderID int) (float64, error) { - // 1. 获取预加载的组合实例 - attacker, err := c.GetCombination(attackerID) - if err != nil { - return 0, fmt.Errorf("attacker invalid: %w", err) + if !isValidCombinationID(attackerID) { + return 0, fmt.Errorf("attacker invalid: invalid element combination ID: %d", attackerID) } - defender, err := c.GetCombination(defenderID) - if err != nil { - return 0, fmt.Errorf("defender invalid: %w", err) + if !isValidCombinationID(defenderID) { + return 0, fmt.Errorf("defender invalid: invalid element combination ID: %d", defenderID) } - - // 2. 缓存键(全局唯一) - cacheKey := fmt.Sprintf("a%d_d%d", attackerID, defenderID) - if val, exists := c.offensiveCache[cacheKey]; exists { - return val, nil - } - - // 3. 核心计算+缓存 - val := c.calculateMultiplier(attacker, defender) - c.offensiveCache[cacheKey] = val - return val, nil + return c.offensiveTable[attackerID][defenderID], nil } // calculateMultiplier 核心克制计算逻辑 func (c *ElementCalculator) calculateMultiplier(attacker, defender *ElementCombination) float64 { - // 场景1:单→单 if !attacker.IsDual() && !defender.IsDual() { return c.getMatrixValue(attacker.Primary, defender.Primary) } - // 场景2:单→双 if !attacker.IsDual() { y1, y2 := defender.Primary, *defender.Secondary m1 := c.getMatrixValue(attacker.Primary, y1) m2 := c.getMatrixValue(attacker.Primary, y2) - switch { case m1 == 2 && m2 == 2: return 4.0 @@ -333,12 +340,10 @@ func (c *ElementCalculator) calculateMultiplier(attacker, defender *ElementCombi } } - // 场景3:双→单 if !defender.IsDual() { return c.calculateDualToSingle(attacker.Primary, *attacker.Secondary, defender.Primary) } - // 场景4:双→双 x1, x2 := attacker.Primary, *attacker.Secondary y1, y2 := defender.Primary, *defender.Secondary coeffY1 := c.calculateDualToSingle(x1, x2, y1) @@ -350,7 +355,6 @@ func (c *ElementCalculator) calculateMultiplier(attacker, defender *ElementCombi func (c *ElementCalculator) calculateDualToSingle(attacker1, attacker2, defender ElementType) float64 { k1 := c.getMatrixValue(attacker1, defender) k2 := c.getMatrixValue(attacker2, defender) - switch { case k1 == 2 && k2 == 2: return 4.0 @@ -361,60 +365,49 @@ func (c *ElementCalculator) calculateDualToSingle(attacker1, attacker2, defender } } -var Calculator = NewElementCalculator() - // TestAllScenarios 全场景测试(验证预加载和计算逻辑) func TestAllScenarios() { - - // 测试1:单→单(草→水) m1, _ := Calculator.GetOffensiveMultiplier(1, 2) fmt.Println("草→水: %.2f(预期2.0)", m1) if math.Abs(m1-2.0) > 0.001 { fmt.Println("测试1失败:实际%.2f", m1) } - // 测试2:特殊单→单(混沌→虚空) m2, _ := Calculator.GetOffensiveMultiplier(222, 226) fmt.Println("混沌→虚空: %.2f(预期0.0)", m2) if math.Abs(m2-0.0) > 0.001 { fmt.Println("测试2失败:实际%.2f", m2) } - // 测试3:单→双(火→冰龙(43)) m3, _ := Calculator.GetOffensiveMultiplier(3, 43) fmt.Println("火→冰龙: %.2f(预期1.5)", m3) if math.Abs(m3-1.5) > 0.001 { fmt.Println("测试3失败:实际%.2f", m3) } - // 测试4:双→特殊单(混沌暗影(92)→神灵(223)) m4, _ := Calculator.GetOffensiveMultiplier(92, 223) fmt.Println("混沌暗影→神灵: %.2f(预期1.25)", m4) if math.Abs(m4-1.25) > 0.001 { fmt.Println("测试4失败:实际%.2f", m4) } - // 测试5:双→双(虚空邪灵(113)→混沌远古(98)) m5, _ := Calculator.GetOffensiveMultiplier(113, 98) fmt.Println("虚空邪灵→混沌远古: %.2f(预期0.875", m5) if math.Abs(m5-0.875) > 0.001 { fmt.Println("测试5失败:实际%.2f", m5) } - // 测试6:缓存命中 m6, _ := Calculator.GetOffensiveMultiplier(113, 98) if math.Abs(m6-m5) > 0.001 { fmt.Println("测试6失败:缓存未命中") } - // 测试7:含无效组合(电→地面) m7, _ := Calculator.GetOffensiveMultiplier(5, 7) fmt.Println("电→地面: %.2f(预期0.0)", m7) if math.Abs(m7-0.0) > 0.001 { fmt.Println("测试7失败:实际%.2f", m7) } - // 测试8:双属性含无效(电战斗→地面) m8, _ := Calculator.GetOffensiveMultiplier(35, 7) fmt.Println("电战斗→地面: %.2f(预期0.25)", m8) if math.Abs(m8-0.25) > 0.001 {