522 lines
16 KiB
Go
522 lines
16 KiB
Go
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错误: 缓存未命中")
|
||
}
|
||
}
|