package element import ( "fmt" "math" "sync" "testing" ) // 元素类型枚举(保持不变) type ElementType int const ( ElementTypeGrass ElementType = 1 // 草 ElementTypeWater ElementType = 2 // 水 ElementTypeFire ElementType = 3 // 火 ElementTypeFlying ElementType = 4 // 飞行 ElementTypeElectric ElementType = 5 // 电 ElementTypeSteel ElementType = 6 // 机械 ElementTypeGround ElementType = 7 // 地面 ElementTypeNormal ElementType = 8 // 普通 ElementTypeIce ElementType = 9 // 冰 ElementTypePsychic ElementType = 10 // 超能 ElementTypeFighting ElementType = 11 // 战斗 ElementTypeLight ElementType = 12 // 光 ElementTypeDark ElementType = 13 // 暗影 ElementTypeMythic ElementType = 14 // 神秘 ElementTypeDragon ElementType = 15 // 龙 ElementTypeSaint ElementType = 16 // 圣灵 ElementTypeDimension ElementType = 17 // 次元 ) // 元素名称映射(保持不变) var elementNameMap = map[ElementType]string{ ElementTypeGrass: "GRASS", ElementTypeWater: "WATER", ElementTypeFire: "FIRE", ElementTypeFlying: "FLYING", ElementTypeElectric: "ELECTRIC", ElementTypeSteel: "STEEL", ElementTypeGround: "GROUND", ElementTypeNormal: "NORMAL", ElementTypeIce: "ICE", ElementTypePsychic: "PSYCHIC", ElementTypeFighting: "FIGHTING", ElementTypeLight: "LIGHT", ElementTypeDark: "DARK", ElementTypeMythic: "MYTHIC", ElementTypeDragon: "DRAGON", ElementTypeSaint: "SAINT", ElementTypeDimension: "DIMENSION", } // 双属性映射(保持不变) var dualElementMap = map[int][2]int{ 21: {1, 10}, // 草 超能 22: {1, 11}, // 草 战斗 23: {1, 13}, // 草 暗影 24: {2, 10}, // 水 超能 25: {2, 13}, // 水 暗影 26: {2, 15}, // 水 龙 27: {3, 4}, // 火 飞行 28: {3, 15}, // 火 龙 29: {3, 10}, // 火 超能 30: {4, 10}, // 飞行 超能 31: {12, 4}, // 光 飞行 32: {4, 15}, // 飞行 龙 33: {5, 3}, // 电 火 34: {5, 9}, // 电 冰 35: {5, 11}, // 电 战斗 36: {13, 5}, // 暗影 电 37: {6, 7}, // 机械 地面 38: {6, 10}, // 机械 超能 39: {6, 15}, // 机械 龙 40: {7, 15}, // 地面 龙 41: {11, 7}, // 战斗 地面 42: {7, 13}, // 地面 暗影 43: {9, 15}, // 冰 龙 44: {9, 12}, // 冰 光 45: {9, 13}, // 冰 暗影 46: {10, 9}, // 超能 冰 47: {11, 3}, // 战斗 火 48: {11, 13}, // 战斗 暗影 49: {12, 14}, // 光 神秘 50: {13, 14}, // 暗影 神秘 51: {14, 10}, // 神秘 超能 52: {16, 12}, // 圣灵 光 53: {4, 14}, // 飞行 神秘 54: {7, 10}, // 地面 超能 55: {13, 15}, // 暗影 龙 56: {16, 13}, // 圣灵 暗影 66: {2, 11}, // 水 战斗 69: {12, 13}, // 光 暗影 } // 元素组合结构体(保持不变) type ElementCombination struct { Primary ElementType // 主属性(1-17) Secondary *ElementType // 副属性(1-17,双属性时非空) ID int // 组合ID } // 创建元素组合(保持不变) func NewElementCombination(id int) (*ElementCombination, error) { if atts, isDual := dualElementMap[id]; isDual { primaryID, secondaryID := atts[0], atts[1] if primaryID < 1 || primaryID > 17 { return nil, fmt.Errorf("主属性ID必须为1-17,实际: %d", primaryID) } if secondaryID < 1 || secondaryID > 17 { return nil, fmt.Errorf("副属性ID必须为1-17,实际: %d", secondaryID) } primary := ElementType(primaryID) secondary := ElementType(secondaryID) if primary > secondary { primary, secondary = secondary, primary } return &ElementCombination{ Primary: primary, Secondary: &secondary, ID: id, }, nil } if id < 1 || id > 17 { return nil, fmt.Errorf("单属性ID必须为1-17,实际: %d", id) } return &ElementCombination{ Primary: ElementType(id), Secondary: nil, ID: id, }, nil } // 判断是否为双属性(保持不变) func (ec *ElementCombination) IsDual() bool { return ec.Secondary != nil } // 获取所有属性(保持不变) func (ec *ElementCombination) Elements() []ElementType { if ec.IsDual() { return []ElementType{ec.Primary, *ec.Secondary} } return []ElementType{ec.Primary} } // 缓存键(保持不变) func (ec *ElementCombination) CacheKey() string { return fmt.Sprintf("elem_%d", ec.ID) } // 字符串展示(保持不变) func (ec *ElementCombination) String() string { if ec.IsDual() { return fmt.Sprintf("(%v, %v)", elementNameMap[ec.Primary], elementNameMap[*ec.Secondary]) } return fmt.Sprintf("(%v)", elementNameMap[ec.Primary]) } // 元素计算器(用sync.Map替代map+锁) type ElementCalculator struct { tableMatrix map[ElementType]map[ElementType]float64 // 单属性克制矩阵(非共享,无需sync.Map) offensiveCache sync.Map // 攻击系数缓存:key=attackerKey_defenderKey,value=float64 combinationPool sync.Map // 元素组合缓存:key=int(ID),value=*ElementCombination combinationErrCache sync.Map // 元素组合错误缓存:key=int(ID),value=error } // 创建计算器实例(预加载所有元素组合) func NewElementCalculator() *ElementCalculator { calc := &ElementCalculator{ tableMatrix: initFullTableMatrix(), } calc.preloadCombinations() // 预加载所有单/双属性组合 return calc } // 预加载所有元素组合(使用sync.Map.Store存储) func (c *ElementCalculator) preloadCombinations() { // 预加载单属性(1-17) for id := 1; id <= 17; id++ { combo, err := NewElementCombination(id) if err != nil { c.combinationErrCache.Store(id, err) } else { c.combinationPool.Store(id, combo) } } // 预加载双属性(dualElementMap中的所有ID) for id := range dualElementMap { combo, err := NewElementCombination(id) if err != nil { c.combinationErrCache.Store(id, err) } else { c.combinationPool.Store(id, combo) } } } // 初始化全属性克制矩阵(保持不变) func initFullTableMatrix() map[ElementType]map[ElementType]float64 { // 初始化17×17矩阵,默认系数1.0 matrix := make(map[ElementType]map[ElementType]float64) allElements := []ElementType{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17} for _, x := range allElements { matrix[x] = make(map[ElementType]float64) for _, y := range allElements { matrix[x][y] = 1.0 } } // 以下矩阵初始化逻辑与之前完全一致(省略重复代码,保持原逻辑) matrix[1][1] = 0.5 matrix[1][2] = 0.5 matrix[1][3] = 2.0 // ... 其余矩阵初始化代码(与原代码相同) return matrix } // 获取元素组合(使用sync.Map.Load读取缓存) func (c *ElementCalculator) GetCombination(id int) (*ElementCombination, error) { // 先查组合缓存 if val, ok := c.combinationPool.Load(id); ok { return val.(*ElementCombination), nil } // 再查错误缓存 if val, ok := c.combinationErrCache.Load(id); ok { return nil, val.(error) } // 双重检查:避免并发场景下重复创建(sync.Map无锁,但仍需防止重复计算) // 先尝试再次读取(可能其他goroutine已创建) if val, ok := c.combinationPool.Load(id); ok { return val.(*ElementCombination), nil } if val, ok := c.combinationErrCache.Load(id); ok { return nil, val.(error) } // 创建新组合并缓存 combo, err := NewElementCombination(id) if err != nil { c.combinationErrCache.Store(id, err) return nil, err } c.combinationPool.Store(id, combo) return combo, nil } // 计算攻击方X→防御方Y的系数(使用sync.Map缓存) func (c *ElementCalculator) GetOffensiveMultiplier(attackerXID, defenderYID int) (float64, error) { attackerX, err := c.GetCombination(attackerXID) if err != nil { return 0, fmt.Errorf("攻击方无效: %v", err) } defenderY, err := c.GetCombination(defenderYID) if err != nil { return 0, fmt.Errorf("防御方无效: %v", err) } cacheKey := fmt.Sprintf("X%d→Y%d", attackerXID, defenderYID) // 尝试从缓存读取 if val, ok := c.offensiveCache.Load(cacheKey); ok { return val.(float64), nil } // 缓存未命中,计算后存入缓存 result := c.calculateMultiplier(attackerX, defenderY) c.offensiveCache.Store(cacheKey, result) return result, nil } // 核心计算逻辑(保持不变) func (c *ElementCalculator) calculateMultiplier(attackerX, defenderY *ElementCombination) float64 { // 1. 单属性→单属性:直接查表 if !attackerX.IsDual() && !defenderY.IsDual() { return c.tableMatrix[attackerX.Primary][defenderY.Primary] } // 2. 单属性→双属性:拆分防守方 if !attackerX.IsDual() { y1, y2 := defenderY.Primary, *defenderY.Secondary m1 := c.tableMatrix[attackerX.Primary][y1] m2 := c.tableMatrix[attackerX.Primary][y2] if m1 == 2 && m2 == 2 { return 4.0 } else if m1 == 0 || m2 == 0 { return (m1 + m2) / 4.0 } else { return (m1 + m2) / 2.0 } } // 3. 双属性→单属性:拆分攻击方 if !defenderY.IsDual() { x1, x2 := attackerX.Primary, *attackerX.Secondary k1 := c.tableMatrix[x1][defenderY.Primary] k2 := c.tableMatrix[x2][defenderY.Primary] if k1 == 2 && k2 == 2 { return 4.0 } else if k1 == 0 || k2 == 0 { return (k1 + k2) / 4.0 } else { return (k1 + k2) / 2.0 } } // 4. 双属性→双属性:拆分防守方为两个单属性,分别计算后取平均 x1, x2 := attackerX.Primary, *attackerX.Secondary y1, y2 := defenderY.Primary, *defenderY.Secondary coeffY1 := c.calculateDualToSingle(x1, x2, y1) coeffY2 := c.calculateDualToSingle(x1, x2, y2) return (coeffY1 + coeffY2) / 2.0 } // 辅助函数:双属性攻击单属性的核心计算(保持不变) func (c *ElementCalculator) calculateDualToSingle(attacker1, attacker2, defender ElementType) float64 { k1 := c.tableMatrix[attacker1][defender] k2 := c.tableMatrix[attacker2][defender] if k1 == 2 && k2 == 2 { return 4.0 } else if k1 == 0 || k2 == 0 { return (k1 + k2) / 4.0 } else { return (k1 + k2) / 2.0 } } // 测试用例(保持不变) func TestAllScenarios(t *testing.T) { calculator := NewElementCalculator() // 测试1:单属性→单属性(草→水) m1, _ := calculator.GetOffensiveMultiplier(1, 2) t.Logf("草→水: %.2f(预期2.0)", m1) if math.Abs(m1-2.0) > 0.001 { t.Errorf("测试1错误: 实际%.2f", m1) } // 测试2:单属性→双属性(火→冰龙) m2, _ := calculator.GetOffensiveMultiplier(3, 43) // 火→冰龙(9+15) t.Logf("火→冰龙: %.2f(预期1.5)", m2) if math.Abs(m2-1.5) > 0.001 { t.Errorf("测试2错误: 实际%.2f", m2) } // 其余测试用例与之前一致... }