@@ -7,7 +7,7 @@ import (
"testing"
)
// 元素类型枚举(保持不变 )
// 元素类型枚举(1-17单属性, 完整覆盖 )
type ElementType int
const (
@@ -30,7 +30,7 @@ const (
ElementTypeDimension ElementType = 17 // 次元
)
// 元素名称映射(保持不变 )
// 元素名称映射(全属性对应 )
var elementNameMap = map [ ElementType ] string {
ElementTypeGrass : "GRASS" ,
ElementTypeWater : "WATER" ,
@@ -51,7 +51,7 @@ var elementNameMap = map[ElementType]string{
ElementTypeDimension : "DIMENSION" ,
}
// 双属性映射(保持不变 )
// 双属性映射(完整配置,无遗漏 )
var dualElementMap = map [ int ] [ 2 ] int {
21 : { 1 , 10 } , // 草 超能
22 : { 1 , 11 } , // 草 战斗
@@ -93,14 +93,14 @@ var dualElementMap = map[int][2]int{
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 ]
@@ -132,12 +132,12 @@ func NewElementCombination(id int) (*ElementCombination, error) {
} , nil
}
// 判断是否为双属性(保持不变)
// 判断是否为双属性
func ( ec * ElementCombination ) IsDual ( ) bool {
return ec . Secondary != nil
}
// 获取所有属性(保持不变)
// 获取所有属性
func ( ec * ElementCombination ) Elements ( ) [ ] ElementType {
if ec . IsDual ( ) {
return [ ] ElementType { ec . Primary , * ec . Secondary }
@@ -145,60 +145,37 @@ func (ec *ElementCombination) Elements() []ElementType {
return [ ] ElementType { ec . Primary }
}
// 缓存键(保持不变)
// 缓存键
func ( ec * ElementCombination ) CacheKey ( ) string {
return fmt . Sprintf ( "elem _%d" , ec . ID )
return fmt . Sprintf ( "id _%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, %v)" , ec . Primary , * ec . Secondary )
}
return fmt . Sprintf ( "(%v)" , elementNameMap [ ec. Primary ] )
return fmt . Sprintf ( "(%vv )" , 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
tableMatrix map [ ElementType ] map [ ElementType ] float64 // 单属性克制矩阵(全属性 )
offensiveCache map [ string ] float64 // 攻击缓存:X→Y
combinationPool map [ int ] * ElementCombination // 组合池
mu sync . RWMutex // 并发锁
}
// 创建计算器实例(预加载所有元素组合)
// 创建计算器实例
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 )
}
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 )
@@ -210,47 +187,167 @@ func initFullTableMatrix() map[ElementType]map[ElementType]float64 {
}
}
// 以下矩阵初始化逻辑与之前完全一致(省略重复代码,保持原逻辑)
matrix [ 1 ] [ 1 ] = 0.5
matrix [ 1 ] [ 2 ] = 0.5
matrix [ 1 ] [ 3 ] = 2.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
}
// 获取元素组合( 使用sync.Map.Load读取缓存)
// 获取元素组合
func ( c * ElementCalculator ) GetCombination ( id int ) ( * ElementCombination , error ) {
// 先查组合缓存
if val , ok := c . combinationPool . Load ( id ) ; ok {
return val . ( * ElementCombination ) , nil
c . mu . RLock ( )
if combo , exists := c . combinationPool [ id ] ; exists {
c . mu . RUnlock ( )
return combo , nil
}
c . mu . RUnlock ( )
// 再查错误缓存
if val , ok := c . combinationErrCache . Load ( id ) ; ok {
return nil , val . ( error )
c . mu . Lock ( )
defer c . mu . Unlock ( )
if combo , exists := c . combinationPool [ id ] ; exists {
return combo , nil
}
// 双重检查: 避免并发场景下重复创建( 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 )
c . combinationPool [ id ] = combo
return combo , nil
}
// 计算攻击方X→防御方Y的系数( 使用sync.Map缓存)
// 计算攻击方X→防御方Y的系数
func ( c * ElementCalculator ) GetOffensiveMultiplier ( attackerXID , defenderYID int ) ( float64 , error ) {
attackerX , err := c . GetCombination ( attackerXID )
if err != nil {
@@ -262,31 +359,34 @@ func (c *ElementCalculator) GetOffensiveMultiplier(attackerXID, defenderYID int)
}
cacheKey := fmt . Sprintf ( "X%d→Y%d" , attackerXID , defenderYID )
// 尝试从缓存读取
if val , ok := c . offensiveCache . Load ( cacheKey ) ; ok {
return val . ( float64 ) , nil
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 . offensiveCache . Store ( cacheKey , result )
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. 单属性→双属性:拆分防守方
// 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 {
@@ -296,12 +396,21 @@ func (c *ElementCalculator) calculateMultiplier(attackerX, defenderY *ElementCom
}
}
// 3. 双属性→单属性:拆分攻击方
// 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 {
@@ -311,21 +420,33 @@ func (c *ElementCalculator) calculateMultiplier(attackerX, defenderY *ElementCom
}
}
// 4. 双属性→双属性:拆分防守方为两个单属性,分别计算后取平均
// 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 {
@@ -335,7 +456,7 @@ func (c *ElementCalculator) calculateDualToSingle(attacker1, attacker2, defender
}
}
// 测试用例(保持不变)
// 全场景 测试用例
func TestAllScenarios ( t * testing . T ) {
calculator := NewElementCalculator ( )
@@ -348,10 +469,53 @@ func TestAllScenarios(t *testing.T) {
// 测试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错误: 缓存未命中" )
}
}