package element import ( "fmt" "math" "sync" "testing" ) // 元素类型枚举(1-17单属性,完整覆盖) 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("id_%d", ec.ID) } // 字符串展示 func (ec *ElementCombination) String() string { if ec.IsDual() { return fmt.Sprintf("(%v, %v)", ec.Primary, *ec.Secondary) } return fmt.Sprintf("(%vv)", ec.Primary) } // 元素计算器(全属性支持+缓存) type ElementCalculator struct { tableMatrix map[ElementType]map[ElementType]float64 // 单属性克制矩阵(全属性) offensiveCache map[string]float64 // 攻击缓存:X→Y combinationPool map[int]*ElementCombination // 组合池 mu sync.RWMutex // 并发锁 } // 创建计算器实例 func NewElementCalculator() *ElementCalculator { return &ElementCalculator{ tableMatrix: initFullTableMatrix(), // 初始化全属性矩阵 offensiveCache: make(map[string]float64), combinationPool: make(map[int]*ElementCombination), } } // 初始化全属性克制矩阵(经双向结果验证) 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 // 草→火 matrix[1][4] = 2.0 // 草→飞行 matrix[1][5] = 0.5 // 草→电 matrix[1][7] = 0.5 // 草→地面 matrix[1][9] = 2.0 // 草→冰 matrix[1][12] = 0.0 // 草→光(免疫) matrix[1][15] = 0.5 // 草→龙 matrix[1][16] = 2.0 // 草→圣灵 matrix[2][1] = 2.0 // 水→草 matrix[2][2] = 0.5 // 水→水 matrix[2][3] = 0.5 // 水→火 matrix[2][5] = 2.0 // 水→电 matrix[2][6] = 0.5 // 水→机械 matrix[2][9] = 0.5 // 水→冰 matrix[2][15] = 0.5 // 水→龙 matrix[2][16] = 2.0 // 水→圣灵 matrix[3][1] = 0.5 // 火→草 matrix[3][2] = 2.0 // 火→水 matrix[3][3] = 0.5 // 火→火 matrix[3][6] = 0.5 // 火→机械 matrix[3][7] = 2.0 // 火→地面 matrix[3][9] = 0.5 // 火→冰 matrix[3][15] = 0.5 // 火→龙 matrix[3][16] = 2.0 // 火→圣灵 // 飞行系 matrix[4][1] = 2 //飞行->草 matrix[4][5] = 0.5 //飞行->电 matrix[4][6] = 0.5 //飞行->机械 matrix[4][11] = 2 //飞行->战斗 matrix[4][17] = 0.5 //飞行->次元 //电系 matrix[5][1] = 0.5 //电->草 matrix[5][2] = 2 //电->水 matrix[5][4] = 2 //电->飞行 matrix[5][5] = 0.5 //电->电 matrix[5][7] = 0 //电->地面 matrix[5][13] = 2 //电->暗影 matrix[5][14] = 0.5 //电->神秘 matrix[5][16] = 0.5 //电->圣灵 matrix[5][17] = 2 //电->次元 //机械 matrix[6][2] = 0.5 //机械->水 matrix[6][3] = 0.5 //机械->火 matrix[6][5] = 0.5 //机械->电 matrix[6][6] = 0.5 //机械->机械 matrix[6][9] = 2 //机械->冰 matrix[6][11] = 2 //机械->战斗 matrix[6][17] = 0.5 //机械->次元 matrix[7][1] = 0.5 //地面->草 matrix[7][3] = 2 //地面->火 matrix[7][4] = 0 //地面->飞行 matrix[7][5] = 2 //地面->电 matrix[7][6] = 2 //地面->机械 matrix[7][10] = 0.5 //地面->超能 matrix[7][13] = 0.5 //地面->暗影 matrix[7][15] = 0.5 //地面->龙 matrix[7][16] = 0.5 //地面->圣灵 matrix[9][1] = 2 //->草 matrix[9][2] = 0.5 //->水 matrix[9][3] = 0.5 //->火 matrix[9][4] = 2 //->飞行 matrix[9][6] = 0.5 //->机械 matrix[9][7] = 2 //->地面 matrix[9][9] = 0.5 //->冰 matrix[9][16] = 0.5 //->圣灵 matrix[9][17] = 2 //->次元 matrix[10][6] = 0.5 //->机械 matrix[10][10] = 0.5 //->超能 matrix[10][11] = 2 //->战斗 matrix[10][12] = 0 //->光 matrix[10][14] = 2 //->神秘 matrix[11][6] = 2 //->机械 matrix[11][9] = 2 //->冰 matrix[11][10] = 0.5 //->超能 matrix[11][11] = 0.5 //->战斗 matrix[11][13] = 0.5 //->暗影 matrix[11][15] = 2 //->龙 matrix[11][16] = 2 //->圣灵 matrix[12][1] = 0 // matrix[12][6] = 0.5 // matrix[12][9] = 0.5 // matrix[12][10] = 2 // matrix[12][12] = 0.5 // matrix[12][13] = 2 // matrix[12][16] = 0.5 // matrix[13][6] = 0.5 // matrix[13][9] = 0.5 // matrix[13][10] = 2 // matrix[13][12] = 0.5 // matrix[13][13] = 2 // matrix[13][16] = 0.5 // matrix[13][17] = 2 // matrix[14][5] = 2 //->电 matrix[14][7] = 0.5 //->地面 matrix[14][11] = 0.5 //->战斗 matrix[14][14] = 2 //->神秘 matrix[14][16] = 2 //->圣灵 matrix[15][1] = 0.5 //->草 matrix[15][2] = 0.5 //->水 matrix[15][3] = 0.5 //->火 matrix[15][5] = 0.5 //->电 matrix[15][9] = 2 //->冰 matrix[15][15] = 2 //->龙 matrix[15][16] = 2 //->圣灵 matrix[16][1] = 2 //->草 matrix[16][2] = 2 //->水 matrix[16][3] = 2 //->火 matrix[16][5] = 2 //->电 matrix[16][9] = 2 //->冰 matrix[16][11] = 0.5 //->战斗 matrix[16][14] = 0.5 //->神秘 matrix[16][15] = 0.5 //->龙 matrix[17][4] = 2 //->飞行 matrix[17][6] = 2 //->机械 matrix[17][9] = 0.5 //->冰 matrix[17][10] = 2 //->超能 matrix[17][13] = 0 //->暗影 return matrix } // 获取元素组合 func (c *ElementCalculator) GetCombination(id int) (*ElementCombination, error) { c.mu.RLock() if combo, exists := c.combinationPool[id]; exists { c.mu.RUnlock() return combo, nil } c.mu.RUnlock() c.mu.Lock() defer c.mu.Unlock() if combo, exists := c.combinationPool[id]; exists { return combo, nil } combo, err := NewElementCombination(id) if err != nil { return nil, err } c.combinationPool[id] = combo return combo, nil } // 计算攻击方X→防御方Y的系数 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) c.mu.RLock() if val, exists := c.offensiveCache[cacheKey]; exists { c.mu.RUnlock() return val, nil } c.mu.RUnlock() result := c.calculateMultiplier(attackerX, defenderY) c.mu.Lock() c.offensiveCache[cacheKey] = result c.mu.Unlock() 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] // 单→双规则:双克制=4,含无效÷4,其他÷2 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] // 补全默认值(未定义的普通关系为1.0) if k1 == 0 && c.tableMatrix[x1][defenderY.Primary] != 0 { k1 = 1.0 } if k2 == 0 && c.tableMatrix[x2][defenderY.Primary] != 0 { k2 = 1.0 } // 双→单规则:双克制=4,含无效÷4,其他÷2 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 // 计算攻击方对防守方第一个单属性(y1)的双→单系数 coeffY1 := c.calculateDualToSingle(x1, x2, y1) // 计算攻击方对防守方第二个单属性(y2)的双→单系数 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] // 补全默认值(未定义的普通关系为1.0) if k1 == 0 && c.tableMatrix[attacker1][defender] != 0 { k1 = 1.0 } if k2 == 0 && c.tableMatrix[attacker2][defender] != 0 { k2 = 1.0 } // 双→单规则应用 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) // 火→冰=2.0,火→龙=1.0 → 平均值=1.5 t.Logf("火→冰龙: %.2f(预期1.5)", m2) if math.Abs(m2-1.5) > 0.001 { t.Errorf("测试2错误: 实际%.2f", m2) } // 测试3:双属性→单属性(飞行超能→草) m3, _ := calculator.GetOffensiveMultiplier(30, 1) // 飞行超能→草 // 飞行→草=2.0,超能→草=1.0 → 平均值=1.5 t.Logf("飞行超能→草: %.2f(预期1.5)", m3) if math.Abs(m3-1.5) > 0.001 { t.Errorf("测试3错误: 实际%.2f", m3) } // 测试4:双属性→双属性(冰暗影→电战斗)预期=1.0 m4, _ := calculator.GetOffensiveMultiplier(45, 35) // 冰→电=1.0,冰→战斗=0.5,暗影→电=0.5,暗影→战斗=2.0 → 总和=4.0 → 平均值=1.0 t.Logf("冰暗影→电战斗: %.4f(预期1.0)", m4) if math.Abs(m4-1.0) > 0.001 { t.Errorf("测试4错误: 实际%.4f", m4) } // 测试5:双属性→双属性(电战斗→冰暗影)预期=1.375 m5, _ := calculator.GetOffensiveMultiplier(35, 45) // 电→冰=1.0,电→暗影=1.0,战斗→冰=2.0,战斗→暗影=1.5 → 总和=5.5 → 平均值=1.375 t.Logf("电战斗→冰暗影: %.4f(预期1.375)", m5) if math.Abs(m5-1.375) > 0.001 { t.Errorf("测试5错误: 实际%.4f", m5) } // 测试6:特殊免疫(飞行→地面) m6, _ := calculator.GetOffensiveMultiplier(4, 7) t.Logf("飞行→地面: %.2f(预期0.0)", m6) if math.Abs(m6-0.0) > 0.001 { t.Errorf("测试6错误: 实际%.2f", m6) } // 测试7:光暗影→暗影(光→暗影=2.0,暗影→暗影=1.0 → 平均值=1.5) m7, _ := calculator.GetOffensiveMultiplier(69, 13) t.Logf("光暗影→暗影: %.2f(预期1.5)", m7) if math.Abs(m7-1.5) > 0.001 { t.Errorf("测试7错误: 实际%.2f", m7) } // 测试8:缓存验证(复用测试4结果) m8, _ := calculator.GetOffensiveMultiplier(46, 25) if m8 != m4 { t.Error("测试8错误: 缓存未命中") } }