diff --git a/common/utils/help.go b/common/utils/help.go index 9ae308b4..7aaaea9e 100644 --- a/common/utils/help.go +++ b/common/utils/help.go @@ -1,5 +1,12 @@ package utils +import ( + "errors" + + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/grand" +) + func FindWithIndex[T any](slice []T, predicate func(item T) bool) (int, *T, bool) { for i := range slice { if predicate(slice[i]) { @@ -24,3 +31,74 @@ func RemoveLast(s string) string { runes := []rune(s) return string(runes[:len(runes)-1]) } + +// ************************** 函数1:核心函数(双数组+int型概率,泛型)************************** +// randomByIntProbs 核心概率计算函数:接收任意类型元素数组 + int型概率数组,实现权重随机 +// T any:支持任意类型的元素切片,职责单一,只处理纯int概率的核心逻辑 +func RandomByIntProbs[T any](natureSet []T, probs []int) (T, error) { + // 定义泛型零值,用于错误返回 + var zeroT T + + // 1. 合法性校验:元素数组为空 或 概率数组与元素数组长度不匹配 + if len(natureSet) == 0 { + return zeroT, errors.New("natureSet is empty") + } + if len(probs) == 0 || len(natureSet) != len(probs) { + // 长度不匹配,降级为等概率随机(兼容原有逻辑) + return natureSet[grand.Intn(len(natureSet))], nil + } + + // 2. 校验概率值非负,并计算总概率 + totalProb := 0 + for i, p := range probs { + if p < 0 { + return zeroT, errors.New("invalid prob value: index " + gconv.String(i) + " (must be non-negative integer)") + } + totalProb += p + } + + // 3. 总概率为0,降级为等概率随机 + if totalProb == 0 { + return natureSet[grand.Intn(len(natureSet))], nil + } + + // 4. 计算前缀和(权重随机核心逻辑) + prefixSum := make([]int, len(probs)) + prefixSum[0] = probs[0] + for i := 1; i < len(probs); i++ { + prefixSum[i] = prefixSum[i-1] + probs[i] + } + + // 5. 生成随机数匹配前缀和,返回对应元素 + randVal := grand.Intn(totalProb) + for i, sum := range prefixSum { + if randVal < sum { + return natureSet[i], nil + } + } + + // 极端情况兜底,返回第一个元素 + return natureSet[0], nil +} + +// ************************** 函数2:封装函数(兼容string型概率,泛型)************************** +// randomByProbs 兼容原有入参格式:接收任意类型元素数组 + string型概率数组 +// 内部完成 string[] -> int[] 的转换,然后调用核心函数 randomByIntProbs +func RandomByProbs[T any](natureSet []T, probs []string) (T, error) { + //var zeroT T + + // 1. 若string概率数组为空,直接调用核心函数(核心函数会处理降级逻辑) + if len(probs) == 0 { + return RandomByIntProbs(natureSet, []int{}) + } + + // 2. string[] 转换为 int[](使用 gconv.Int 完成转换) + probInts := make([]int, len(probs)) + for i, pStr := range probs { + // gconv.Int 灵活转换,失败返回0 + probInts[i] = gconv.Int(pStr) + } + + // 3. 调用核心函数,复用概率计算逻辑 + return RandomByIntProbs(natureSet, probInts) +} diff --git a/logic/service/item/petuse.go b/logic/service/item/petuse.go index 70201c35..03d7ef9d 100644 --- a/logic/service/item/petuse.go +++ b/logic/service/item/petuse.go @@ -2,9 +2,8 @@ package item import ( "blazing/common/data/xmlres" + "blazing/common/utils" "blazing/modules/player/model" - "errors" - "strconv" "strings" "github.com/gogf/gf/v2/util/gconv" @@ -101,7 +100,7 @@ func nvfunc(itemid uint32, onpet *model.PetInfo) bool { } // 4. 按概率选值(核心改造:替换原有随机逻辑) - selectedVal, err := randomByProbs(rr, probList) + selectedVal, err := utils.RandomByProbs(rr, probList) if err != nil { return false } @@ -215,54 +214,3 @@ func init() { return false }) } - -// 替换你原有性格转换的代码段,新增概率解析+权重随机逻辑 -// 注:保留你原有的变量名(如rr)、函数调用(grand.Intn),仅扩展概率逻辑 - -// 先定义一个辅助函数:按概率从列表中选值(贴合你的代码风格) -func randomByProbs(natureSet []string, probs []string) (string, error) { - // 1. 若概率配置为空/长度不匹配,降级为等概率随机(兼容原有逻辑) - if len(probs) == 0 || len(natureSet) != len(probs) { - if len(natureSet) == 0 { - return "", errors.New("natureSet is empty") - } - // 修复你原有代码的索引越界问题:grand.Intn(len(rr))-1 → 改为grand.Intn(len(rr)) - // 因为grand.Intn(n)返回0~n-1,直接作为索引即可,无需-1 - return natureSet[grand.Intn(len(natureSet))], nil - } - - // 2. 解析概率为int数组,校验非负 - probInts := make([]int, len(probs)) - totalProb := 0 - for i, p := range probs { - val, err := strconv.Atoi(p) - if err != nil || val < 0 { - return "", errors.New("invalid prob value: " + p) - } - probInts[i] = val - totalProb += val - } - - // 3. 总概率为0,降级为等概率 - if totalProb == 0 { - return natureSet[grand.Intn(len(natureSet))], nil - } - - // 4. 计算前缀和(权重随机核心) - prefixSum := make([]int, len(probInts)) - prefixSum[0] = probInts[0] - for i := 1; i < len(probInts); i++ { - prefixSum[i] = prefixSum[i-1] + probInts[i] - } - - // 5. 生成随机数匹配前缀和 - randVal := grand.Intn(totalProb) - for i, sum := range prefixSum { - if randVal < sum { - return natureSet[i], nil - } - } - - // 理论上不会走到这里 - return natureSet[0], nil -} diff --git a/modules/config/service/pet_fusion_service.go b/modules/config/service/pet_fusion_service.go index 7a70d0c8..8c37e6c4 100644 --- a/modules/config/service/pet_fusion_service.go +++ b/modules/config/service/pet_fusion_service.go @@ -1,12 +1,11 @@ package service import ( + "blazing/common/utils" "blazing/cool" "blazing/modules/config/model" - "fmt" "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/util/grand" ) @@ -28,12 +27,6 @@ func NewPetFusionService() *PetFusionService { //获取主副精灵融合的id,如果不存在,那就给一个保底的id -func init() { - g.DB().SetDebug(true) - r := NewPetFusionService().getData(577, 2083) - fmt.Println(r) - -} func (s *PetFusionService) Data(p1, p2, rand uint32) uint32 { if !grand.Meet(int((rand)/2), 100) { @@ -42,24 +35,29 @@ func (s *PetFusionService) Data(p1, p2, rand uint32) uint32 { } pet := s.getData(p1, p2) - for _, v := range pet { - rr := grand.Intn(100) - if rr < int(v.Probability) { - return uint32(v.ResultPetID) + if len(pet) != 0 { + var pets, props []int + + for _, v := range pet { + pets = append(pets, int(v.ResultPetID)) + props = append(props, int(v.Probability)) + } - } - //说明是失败,直接返回失败 - if len(pet) > 0 { + utils.RandomByIntProbs(pets, props) + //说明是失败,直接返回失败 + } else { + + pets := s.def() + res := pets[grand.Intn(len(pets))] + rr := grand.Intn(100) + if rr < int(res.Probability+int32(rand)) { + return uint32(res.ResultPetID) + } + //到这里相当于直接失败 return 0 } - pets := s.def() - res := pets[grand.Intn(len(pets))] - rr := grand.Intn(100) - if rr < int(res.Probability+int32(rand)) { - return uint32(res.ResultPetID) - } - //到这里相当于直接失败 + return 0 }