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

424 lines
14 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"
)
// 元素类型枚举覆盖所有单属性ID与原配置完全对齐
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 // 次元
ElementTypeAncient ElementType = 18 // 远古
ElementTypeDemon ElementType = 19 // 邪灵
ElementTypeNature ElementType = 20 // 自然
ElementTypeKing ElementType = 221 // 王
ElementTypeChaos ElementType = 222 // 混沌
ElementTypeDeity ElementType = 223 // 神灵
ElementTypeSamsara ElementType = 224 // 轮回
ElementTypeInsect ElementType = 225 // 虫
ElementTypeVoid ElementType = 226 // 虚空
)
// 全局常量定义(统一管理合法范围)
const (
maxMatrixSize = 227 // 矩阵维度覆盖最大属性ID 226
)
// 合法单属性ID集合快速校验
var validSingleElementIDs = map[int]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{
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",
ElementTypeAncient: "ANCIENT",
ElementTypeDemon: "DEMON",
ElementTypeNature: "NATURE",
ElementTypeKing: "KING",
ElementTypeChaos: "CHAOS",
ElementTypeDeity: "DEITY",
ElementTypeSamsara: "SAMSARA",
ElementTypeInsect: "INSECT",
ElementTypeVoid: "VOID",
}
// 双属性映射key=双属性IDvalue=组成的两个单属性ID
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}, // 圣灵 暗影
57: {18, 11}, // 远古 战斗
58: {3, 14}, // 火 神秘
59: {12, 11}, // 光 战斗
60: {14, 11}, // 神秘 战斗
61: {17, 11}, // 次元 战斗
62: {19, 14}, // 邪灵 神秘
63: {18, 15}, // 远古 龙
64: {12, 17}, // 光 次元
65: {18, 16}, // 远古 圣灵
66: {2, 11}, // 水 战斗
67: {5, 15}, // 电 龙
68: {12, 3}, // 光 火
69: {12, 13}, // 光 暗影
70: {19, 15}, // 邪灵 龙
71: {18, 14}, // 远古 神秘
72: {6, 17}, // 机械 次元
73: {11, 15}, // 战斗 龙
74: {11, 20}, // 战斗 自然
75: {19, 6}, // 邪灵 机械
76: {5, 17}, // 电 次元
77: {18, 3}, // 远古 火
78: {16, 11}, // 圣灵 战斗
79: {16, 17}, // 圣灵 次元
80: {16, 5}, // 圣灵 电
81: {18, 7}, // 远古 地面
82: {18, 1}, // 远古 草
83: {20, 15}, // 自然 龙
84: {9, 14}, // 冰 神秘
85: {4, 13}, // 飞行 暗影
86: {9, 3}, // 冰 火
87: {9, 4}, // 冰 飞行
88: {20, 16}, // 自然 圣灵
89: {222, 16}, // 混沌 圣灵
90: {18, 19}, // 远古 邪灵
91: {20, 9}, // 自然 冰
92: {222, 13}, // 混沌 暗影
93: {222, 11}, // 混沌 战斗
94: {222, 10}, // 混沌 超能
95: {16, 10}, // 圣灵 超能
96: {222, 7}, // 混沌 地面
97: {13, 19}, // 暗影 邪灵
98: {222, 18}, // 混沌 远古
99: {222, 19}, // 混沌 邪灵
100: {16, 7}, // 圣灵 地面
101: {3, 13}, // 火 暗影
102: {12, 10}, // 光 超能
103: {6, 11}, // 机械 战斗
104: {4, 5}, // 飞行 电
105: {222, 4}, // 混沌 飞行
106: {222, 15}, // 混沌 龙
107: {222, 3}, // 混沌 火
108: {16, 3}, // 圣灵 火
109: {7, 14}, // 地面 神秘
110: {222, 17}, // 混沌 次元
111: {222, 9}, // 混沌 冰
112: {20, 14}, // 自然 神秘
113: {226, 19}, // 虚空 邪灵
114: {226, 222}, // 虚空 混沌
115: {16, 224}, // 圣灵 轮回
116: {2, 17}, // 水 次元
117: {16, 14}, // 圣灵 神秘
118: {6, 14}, // 机械 神秘
119: {2, 14}, // 水 神秘
120: {17, 15}, // 次元 龙
121: {20, 10}, // 自然 超能
122: {5, 6}, // 电 机械
123: {14, 224}, // 神秘 轮回
124: {2, 6}, // 水 机械
125: {3, 6}, // 火 机械
126: {1, 6}, // 草 机械
127: {18, 5}, // 远古 电
128: {16, 4}, // 圣灵 飞行
}
// 元素组合结构体
type ElementCombination struct {
Primary ElementType // 主属性按ID升序排序
Secondary *ElementType // 副属性(双属性非空)
ID int // 组合唯一ID
}
// 全局预加载资源程序启动时init初始化运行时直接使用
var (
// 元素组合池key=组合IDvalue=组合实例(预加载所有合法组合)
elementCombinationPool = make(map[int]*ElementCombination, 150) // 128双+26单=154预分配足够容量
// 单属性克制矩阵预初始化所有特殊克制关系默认1.0
matrix [maxMatrixSize][maxMatrixSize]float64
)
// init 预加载所有资源(程序启动时执行一次,无并发问题)
func init() {
// 1. 初始化单属性克制矩阵
initFullTableMatrix()
// 2. 预加载所有单属性组合
for id := range validSingleElementIDs {
combo := &ElementCombination{
Primary: ElementType(id),
Secondary: nil,
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{
Primary: primary,
Secondary: &secondary,
ID: dualID,
}
elementCombinationPool[dualID] = combo
}
}
// IsDual 判断是否为双属性
func (ec *ElementCombination) IsDual() bool {
return ec.Secondary != nil
}
// Elements 获取所有属性列表
func (ec *ElementCombination) Elements() []ElementType {
if ec.IsDual() {
return []ElementType{ec.Primary, *ec.Secondary}
}
return []ElementType{ec.Primary}
}
// String 友好格式化输出
func (ec *ElementCombination) String() string {
primaryName := elementNameMap[ec.Primary]
if !ec.IsDual() {
return fmt.Sprintf("(%s)", primaryName)
}
return fmt.Sprintf("(%s, %s)", primaryName, elementNameMap[*ec.Secondary])
}
// ElementCalculator 无锁元素克制计算器(依赖预加载资源)
type ElementCalculator struct {
offensiveCache map[string]float64 // 攻击克制缓存(运行时填充,无并发写)
}
// NewElementCalculator 创建计算器实例(仅初始化缓存)
func NewElementCalculator() *ElementCalculator {
return &ElementCalculator{
offensiveCache: make(map[string]float64, 4096), // 预分配大容量缓存
}
}
// getMatrixValue 直接返回矩阵值修复核心问题不再将0转换为1
func (c *ElementCalculator) getMatrixValue(attacker, defender ElementType) float64 {
return matrix[attacker][defender] // 矩阵默认已初始化1.0,特殊值直接返回
}
// GetCombination 获取元素组合(直接从预加载池读取)
func (c *ElementCalculator) GetCombination(id int) (*ElementCombination, error) {
combo, exists := elementCombinationPool[id]
if !exists {
return nil, fmt.Errorf("invalid element combination ID: %d", id)
}
return combo, nil
}
// 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)
}
defender, err := c.GetCombination(defenderID)
if err != nil {
return 0, fmt.Errorf("defender invalid: %w", err)
}
// 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
}
// 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
case m1 == 0 || m2 == 0:
return (m1 + m2) / 4.0
default:
return (m1 + m2) / 2.0
}
}
// 场景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)
coeffY2 := c.calculateDualToSingle(x1, x2, y2)
return (coeffY1 + coeffY2) / 2.0
}
// calculateDualToSingle 辅助函数:双→单计算
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
case k1 == 0 || k2 == 0:
return (k1 + k2) / 4.0
default:
return (k1 + k2) / 2.0
}
}
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 {
fmt.Println("测试8失败实际%.2f", m8)
}
}