Files
bl/common/data/Element/element.go

522 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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错误: 缓存未命中")
}
}