Files
bl/modules/player/model/pet.go
xinian 61a135b3a7
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
fix: 修复宠物升级经验显示与动态结算
2026-04-15 15:34:16 +08:00

798 lines
21 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 model
import (
"blazing/common/data"
"blazing/common/data/xmlres"
"blazing/common/utils"
"blazing/cool"
"encoding/json"
"blazing/modules/config/model"
"blazing/modules/config/service"
"errors"
"fmt"
"math"
"time"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
"github.com/jinzhu/copier"
"github.com/samber/lo"
)
const TableNamePet = "player_pet"
// Pet mapped from table <pet>
type Pet struct {
Base
PlayerID uint32 `gorm:"not null;index:idx_pet_by_player_id;comment:'所属玩家ID'" json:"player_id"`
Free int `gorm:"not null;default:0;comment:'是否放生'" json:"free"` //"0为放入仓库1为放生,2为上架
CatchTime uint32 `gorm:"not null;comment:'捕捉时间'" json:"catch_time"` //唯一键
SalePrice uint32 `gorm:"not null;default:0;comment:'出售价格'" json:"sale_price"`
SaleCount uint32 `gorm:"not null;default:0;comment:'出售次数'" json:"sale_count"`
// Owner uint32 `struc:"skip"` //仅作为存储
// FreedTime uint32 `struc:"skip"` //放生时间
//是否可交易这里应该定义在精灵ID里
//是否上架
Data PetInfo `gorm:"type:jsonb;not null;comment:'精灵全部数据'" json:"data"`
}
// GetOffShelfFee 计算商品下架扣费规则
// 返回值allowOffShelf(是否允许下架), feeRate(扣费比例), err(错误信息)
func (tt *Pet) GetOffShelfFee() (bool, float64, error) {
// 获取当前时间
now := time.Now()
// 计算上架到现在的时长(小时)
duration := now.Sub(tt.UpdateTime.Time)
hours := duration.Hours()
// 阶段10-24小时展示期可下架但阶梯扣费
if hours >= 0 && hours < 24 {
switch {
case hours < 6:
// 0-6小时0扣费允许下架
return true, 0.0, nil
case hours < 12:
// 6-12小时扣30%,允许下架
return true, 0.3, nil
case hours < 18:
// 12-18小时扣60%,允许下架
return true, 0.6, nil
case hours < 24:
// 18-24小时扣90%,允许下架
return true, 0.9, nil
}
}
// 阶段224-30小时禁止下架
if hours >= 24 && hours < 30 {
return false, 0.0, errors.New("24-30小时交易期禁止下架")
}
// 阶段330小时后自由下架0扣费
if hours >= 30 {
return true, 0.0, nil
}
// 异常情况(上架时间在未来)
return false, 0.0, errors.New("商品未到上架时间,无法操作下架")
}
// CalculateOffShelfAmount 根据扣费比例计算实际扣费金额
func (tt *Pet) CalculateOffShelfAmount(feeRate float64) float64 {
return float64(tt.SalePrice) * feeRate
}
type Attr uint32
func (r Attr) sub() uint32 {
if r > 0 {
return uint32(r) - 1
}
return 0
}
// PetInfo 精灵信息结构(合并后的优化版本)
type PetInfo struct {
ID uint32 `fieldDesc:"精灵编号" `
// 名字默认为全0补齐到16字节固定长度 → [16]byte
Name string `struc:"[16]byte" json:"Name,omitempty"`
Gender int `struc:"uint16" fieldDesc:"性别" ` //0无性别1雄性 2雌性
//generation
Generation uint16 `fieldDesc:"世代" `
Dv uint32 `struc:"uint32" `
Nature uint32 `fieldDesc:"性格" `
Level uint32 `fieldDesc:"等级" `
Exp int64 `struc:"uint32"`
LvExp int64 `struc:"uint32"`
NextLvExp int64 `struc:"uint32"`
Hp uint32
MaxHp uint32 `fieldDesc:"最大生命" `
// * battle_lv: atk(0), def(1), sp_atk(2), sp_def(3), spd(4), accuracy(5)
Prop [5]uint32 `fieldDesc:"属性" `
// * ev:生命学习力,攻击学习力,防御学习力,特攻学习力,特防学习力,速度学习力
Ev [6]uint32 `fieldDesc:"属性" `
SkillListLen uint32 `struc:"sizeof=SkillList" json:"-"`
// 技能信息固定4条空则赋值0固定长度List → [4]SkillInfo零值即符合“赋值0”
SkillList []SkillInfo
CatchTime uint32 //显式忽略,不参与序列化
OldCatchTime uint32 `struc:"skip" fieldDesc:"旧捕捉时间" `
CatchMap uint32 `json:"CatchMap,omitempty"`
CatchRect uint32 `json:"CatchRect,omitempty"`
CatchLevel uint32 `fieldDesc:"捕获等级 默认为0" `
EffectInfoLen uint16 `struc:"sizeof=EffectInfo" json:"-"`
// 特性列表长度用UShort存储变长List → []PetEffectInfo + 长度前缀规则) 第一个一定是特性
EffectInfo []PetEffectInfo
SkinID uint32 `fieldDesc:"皮肤id默认为0" `
ShinyLen uint32 `struc:"sizeof=ShinyInfo"`
ShinyInfo []data.GlowFilter `json:"ShinyInfo,omitempty"`
//时间轮转然后effect根据type同时只共存一个特性是1 特质是1柱子是两种魂印是一个然后异色字段然后特训技能字段
ExtSKill []uint32 `struc:"skip"` //特训技能
ExtSkin []uint32 `struc:"skip"` //可用皮肤
}
func (pet *PetInfo) ConfigBoss(bm model.PetBaseConfig) {
var color data.GlowFilter
err := json.Unmarshal([]byte(bm.Color), &color)
if err == nil && color.Alpha != 0 {
pet.ShinyInfo = append(pet.ShinyInfo, color)
}
if bm.Hp != 0 {
pet.Hp = uint32(bm.Hp)
pet.MaxHp = uint32(bm.Hp)
}
if len(bm.Prop) == 5 {
for i := 0; i < 5; i++ {
if bm.Prop[i] != 0 {
pet.Prop[i] = bm.Prop[i]
}
}
//monster.Prop = [5]uint32(bm.Prop)
}
if len(bm.SKill) != 0 {
for i := 0; i < 4; i++ {
if bm.SKill[i] != 0 {
pet.SkillList[i].ID = bm.SKill[i]
}
}
}
}
func (pet *PetInfo) Type() int {
return xmlres.PetMAP[int(pet.ID)].Type
}
func (pet *PetInfo) ModelHP(tt int64) {
if pet.Hp <= 0 {
if tt > int64(pet.Hp) {
pet.Hp = 0
} else {
pet.Hp += uint32(tt)
}
} else {
pet.Hp += uint32(tt)
if pet.Hp > pet.MaxHp {
pet.Hp = pet.MaxHp
}
}
}
func (pet *PetInfo) HealPP(value int) {
for i := 0; i < len(pet.SkillList); i++ {
if value == -1 {
pet.SkillList[i].PP = uint32(xmlres.SkillMap[int(pet.SkillList[i].ID)].MaxPP)
} else {
pet.SkillList[i].PP += uint32(value)
pet.SkillList[i].PP = utils.Min(pet.SkillList[i].PP, uint32(xmlres.SkillMap[int(pet.SkillList[i].ID)].MaxPP))
}
}
}
// 定义常量,提升可维护性(避免魔法数字)
const (
maxSingleEV uint32 = 255 // 单个EV最大值
maxTotalEV uint32 = 510 // 6个EV总和最大值
evFieldCount = 6 // EV字段数量固定6个
)
// AddEV 优化后的EV值增加方法符合Go命名规范大写导出动词开头
// 功能为宠物6个EV值增加增量保证单个≤255、总和≤510
// 参数evadd - 6个EV字段的增量数组长度必须为6
// 返回error - 参数非法/逻辑异常时返回错误bool - 是否触发了超额削减(方便业务监控)
func (pet *PetInfo) AddEV(ev_add []int64) (bool, error) {
// 1. 参数安全校验避免数组越界panic
if len(ev_add) != evFieldCount {
return false, fmt.Errorf("evadd长度必须为%d当前为%d", evFieldCount, len(ev_add))
}
if len(pet.Ev) != evFieldCount {
return false, errors.New("pet.Ev未初始化或长度不为6")
}
// 2. 第一步:直接添加并限制单项最大值(按索引顺序处理)
var tempEV [evFieldCount]uint32
for i := 0; i < evFieldCount; i++ {
// 直接累加增量
tempEV[i] = pet.Ev[i] + uint32(ev_add[i])
// 单项不超过255
if tempEV[i] > maxSingleEV {
tempEV[i] = maxSingleEV
}
}
// 3. 计算增量后的总和检查是否超过510
totalTemp := lo.Sum(tempEV[:])
// 4. 若总和超额按索引顺序0→5削减优先削减前面的字段
hasCut := false
if totalTemp > maxTotalEV {
overTotal := totalTemp - maxTotalEV // 需要削减的总量
hasCut = true
// 按索引顺序遍历削减从第0个字段开始依次处理
for i := 0; i < evFieldCount && overTotal > 0; i++ {
// 可削减的最大值最多削减到原始值不触碰添加前的基础EV
cutAble := tempEV[i] - pet.Ev[i]
if cutAble <= 0 {
continue // 该字段无增量可削减,跳过
}
// 实际削减量:取"可削减量"和"剩余需削减量"的较小值
cut := cutAble
if cut > overTotal {
cut = overTotal
}
// 执行削减
tempEV[i] -= cut
overTotal -= cut
}
// 极端情况即使削减所有增量后仍超额如原始EV总和已超510继续按顺序削减原始值
if overTotal > 0 {
for i := 0; i < evFieldCount && overTotal > 0; i++ {
// 此时可削减到0根据业务需求调整也可返回错误
cutAble := tempEV[i]
if cutAble <= 0 {
continue
}
cut := cutAble
if cut > overTotal {
cut = overTotal
}
tempEV[i] -= cut
overTotal -= cut
}
}
}
// 5. 将处理后的结果赋值给原EV数组
copy(pet.Ev[:], tempEV[:])
return hasCut, nil
}
func (pet *PetInfo) Cure() {
pet.Hp = pet.MaxHp
for i := 0; i < len(pet.SkillList); i++ {
maxPP, ok := xmlres.SkillMap[int(pet.SkillList[i].ID)]
// 恢复至最大PP值从配置表获取
if pet.SkillList[i].ID != 0 && ok {
pet.SkillList[i].PP = uint32(maxPP.MaxPP)
}
}
}
// 强制选色,这个是给熔炉用的
func (pet *PetInfo) FixShiny() bool {
co := service.NewShinyService().RandShiny(pet.ID)
if co != nil {
pet.ShinyInfo = []data.GlowFilter{*co}
return true
}
return false
}
// 比重融合
func (pet *PetInfo) RandomByWeightShiny() {
co := service.NewShinyService().RandomByWeightShiny(pet.ID)
if co != nil {
pet.ShinyInfo = []data.GlowFilter{*co}
}
}
func (pet *PetInfo) IsShiny() bool {
return len(pet.ShinyInfo) > 0
}
// 随机特性
func (pet *PetInfo) RnadEffect() {
for _, v := range xmlres.PlayerEffectMAP {
if gconv.Int(v.StarLevel) == 0 {
ret := &PetEffectInfo{
Idx: uint16(gconv.Int16(v.Idx)),
Status: 1,
EID: uint16(gconv.Int16(v.Eid)),
Args: v.ArgsS,
}
_, eff, ok := pet.GetEffect(1)
if ok {
if eff.Idx == ret.Idx {
continue
}
copier.Copy(eff, ret)
} else {
pet.EffectInfo = append(pet.EffectInfo, *ret)
}
break
}
}
}
// 0: Boss特性
// 1: 特性
// 2: 能量珠
// 3: 爆发特效
// 4: 异能精灵特质
// 5: 特训
// 6: 魂印
// 7 :繁殖加成
// 8 :体力提升加成
const (
maxHPUpEffectIdx uint16 = 60000
maxHPUpEffectStatus byte = 8
maxHPUpEffectEID uint16 = 26
maxHPUpEffectCap = 20
trainingEffectStatus byte = 5
trainingAttrEffectIdx uint16 = 60001
trainingPowerEffectIdx uint16 = 60002
trainingAttrEffectEID uint16 = 247
trainingPowerEffectEID uint16 = 239
)
// 繁殖加成,体力提升加成 ,这里是防止和其他重复所以定义不同类别,但是实际上,能量珠那些事调用不同id的effect实现
// <!-- Stat: 精灵特效Stat: 0: 无效(默认值), 1: 永久, 2: 有`有效次数'的特效 3: 爆发特效 4: 异能精灵特质5特训6魂印-->
func (pet *PetInfo) GetEffect(ptype int) (int, *PetEffectInfo, bool) {
return utils.FindWithIndex(pet.EffectInfo, func(item PetEffectInfo) bool {
return int(item.Status) == ptype
})
}
func (pet *PetInfo) getEffectByStatusAndEID(status byte, eid uint16) (int, *PetEffectInfo, bool) {
return utils.FindWithIndex(pet.EffectInfo, func(item PetEffectInfo) bool {
return item.Status == status && item.EID == eid
})
}
func ensureEffectArgsLen(args []int, size int) []int {
if len(args) >= size {
return args
}
next := make([]int, size)
copy(next, args)
return next
}
func (pet *PetInfo) addTrainingEffectDelta(idx uint16, eid uint16, argsLen int, argIndex int, value int) bool {
if pet == nil || value <= 0 || argIndex < 0 || argIndex >= argsLen {
return false
}
if _, eff, ok := pet.getEffectByStatusAndEID(trainingEffectStatus, eid); ok {
if eff.Idx == 0 {
eff.Idx = idx
}
eff.Status = trainingEffectStatus
eff.EID = eid
eff.Args = ensureEffectArgsLen(eff.Args, argsLen)
eff.Args[argIndex] += value
return true
}
args := make([]int, argsLen)
args[argIndex] = value
pet.EffectInfo = append(pet.EffectInfo, PetEffectInfo{
Idx: idx,
Status: trainingEffectStatus,
EID: eid,
Args: args,
})
return true
}
func (pet *PetInfo) AddTrainingAttrBonus(attr int, value int) bool {
return pet.addTrainingEffectDelta(trainingAttrEffectIdx, trainingAttrEffectEID, 6, attr, value)
}
func (pet *PetInfo) AddTrainingPowerBonus(value int) bool {
return pet.addTrainingEffectDelta(trainingPowerEffectIdx, trainingPowerEffectEID, 2, 0, value)
}
func (pet *PetInfo) AddMaxHPUpEffect(itemID uint32, value int) bool {
if pet == nil || value <= 0 {
return false
}
if _, eff, ok := pet.GetEffect(int(maxHPUpEffectStatus)); ok {
current := 0
if len(eff.Args) >= 2 && eff.Args[0] == 0 && eff.Args[1] > 0 {
current = eff.Args[1]
}
if current >= maxHPUpEffectCap {
return false
}
next := current + value
if next > maxHPUpEffectCap {
next = maxHPUpEffectCap
}
eff.ItemID = itemID
eff.Idx = maxHPUpEffectIdx
eff.Status = maxHPUpEffectStatus
eff.EID = maxHPUpEffectEID
eff.Args = []int{0, next}
return next > current
}
if value > maxHPUpEffectCap {
value = maxHPUpEffectCap
}
pet.EffectInfo = append(pet.EffectInfo, PetEffectInfo{
ItemID: itemID,
Idx: maxHPUpEffectIdx,
Status: maxHPUpEffectStatus,
EID: maxHPUpEffectEID,
Args: []int{0, value},
})
return true
}
func (pet *PetInfo) Downgrade(level uint32) {
for pet.Level > uint32(level) {
basic, ok := xmlres.PetMAP[int(pet.ID)]
if ok {
if basic.EvolvesFrom != 0 && xmlres.PetMAP[int(basic.EvolvesFrom)].EvolvFlag == 0 {
pet.ID = uint32(basic.EvolvesFrom)
}
}
pet.Level--
//进行降级操作
}
pet.NextLvExp = 0
pet.Update(false)
}
// 执行进化逻辑 ,是否进化
func (petinfo *PetInfo) Update(isup bool) {
// 最大进化次数限制(防止配置表闭环导致死循环)
maxEvolveTimes := 1
evolveCount := 0
// 循环进化:直到不满足进化条件 或 达到最大进化次数
for {
// 防止死循环,超出次数直接退出
if evolveCount >= maxEvolveTimes {
break
}
// 获取当前宠物形态的配置
basic, ok := xmlres.PetMAP[int(petinfo.ID)]
// 配置不存在,直接退出循环
if !ok {
break
}
if !isup {
petinfo.LvExp = calculatePreviousLevelExperience(petinfo.Level, basic.GetBasic())
petinfo.NextLvExp = calculateExperience(petinfo.Level, basic.GetBasic())
return
}
// 升级时保留上一等级需求,供经验结算使用。
petinfo.LvExp = petinfo.NextLvExp
petinfo.NextLvExp = calculateExperience(petinfo.Level, basic.GetBasic())
// 检查是否满足进化条件
canEvolve := basic.EvolvesTo != 0 && // 有明确的进化目标
int(petinfo.Level) >= basic.EvolvingLv && // 等级达到进化要求
basic.IsLarge == 0 // 非最终形态
// 不满足进化条件,退出循环
if !canEvolve {
break
}
// 执行进化更新宠物ID为进化后形态
petinfo.ID = uint32(basic.EvolvesTo)
evolveCount++ // 进化次数+1
}
}
func calculatePreviousLevelExperience(level uint32, baseValue uint32) int64 {
if level <= 1 {
return 0
}
return calculateExperience(level-1, baseValue)
}
// calculateExperience 计算指定等级和种族值所需的经验值
// level: 当前等级
// baseValue: 种族值
func calculateExperience(level uint32, baseValue uint32) int64 {
// 计算 A 部分:向上取整(3.75 * a * (a + 1))
partA := math.Ceil(3.75 * float64(level) * float64(level+1))
// 计算 B 部分:向上取整(b * log(1 + a / 100))
// 这里使用自然对数 math.Log如果想换底数可以用换底公式
partB := math.Log(1.0 + float64(level)/100.0)
partB = float64(baseValue) * partB
partB = math.Ceil(partB)
// 总经验是两部分之和,并向上取整
totalExp := math.Ceil(partA + partB)
return int64(totalExp)
}
// PetEffectInfo 精灵特性信息结构
// <!-- NewSeIdx: 精灵特效索引 (默认0: 无效) -->
// <!-- Type: 0 - 仅单人战斗; 1 - 仅组队战斗; 2 - both; (默认0: 仅单人) -->
// <!-- Eid: 精灵特效eid (默认0: 无效) -->
// <!-- Stat: 精灵特效Stat: 0: 无效(默认值), 1: 永久, 2: 有`有效次数'的特效 3: 爆发特效 4: 异能精灵特质-->
// <!-- Times: 精灵特效可使用次数: 当type==2时有效 (默认值:0) -->
// <!-- Args: 特效参数, 不超过8个 (注意: 每个参数不能超过 65535) -->
// <!-- AdditionType:特效加成类型 1 种族值加成 2 技能威力加成 -->
type PetEffectInfo struct {
ItemID uint32 `struc:"uint32" json:"item_id"` //如果是能量珠,就显示
Idx uint16 `struc:"skip" json:"new_se_idx"`
//Type byte `struc:"skip" json:"type"` //pvp pve特性区分,通过具体effect实现
Status byte `struc:"byte" json:"status"` //特性为1,能量珠为2
LeftCount byte `struc:"byte" json:"left_count"` //剩余次数
EID uint16 `struc:"uint16" json:"effect_id"` //特效ID
ArgsLen uint32 `struc:"sizeof=Args" json:"-"`
Args []int ` json:"Args"` //自定义参数装载
}
// SkillInfo 精灵技能信息结构SkillInfo
type SkillInfo struct {
ID uint32
PP uint32
}
func (s *SkillInfo) Use(count int) {
s.PP -= uint32(count)
}
// TableName Pet's table name
func (*Pet) TableName() string {
return TableNamePet
}
// GroupName Pet's table group
func (*Pet) GroupName() string {
return "default"
}
// NewPet create a new Pet
func NewPet() *Pet {
return &Pet{
Base: *NewBase(),
}
}
// init 创建表
func init() {
_ = cool.CreateTable(&Pet{})
// fmt.Println(err)
}
// GenPetInfo 生成一个新的精灵实例
// - 参数为 -1 时表示随机生成对应属性
// * @param petTypeId 精灵类型ID
// * @param individualValue 个体值0-31
// * @param natureId 性格ID0-24
// * @param abilityTypeEnum 特性类型ID0=无, >0=指定, -1=随机)
// * @param shinyid 闪光ID-1=随机)
// * @param level 等级1-100
// * @return 生成的精灵实体
func GenPetInfo(
id int,
dv, natureId, abilityTypeEnum, level int, shinyid []data.GlowFilter,
gen int,
) *PetInfo {
if id == 0 {
return nil
}
// 创建随机源
//rng := rand.New(rand.NewSource(time.Now().UnixNano()))
// 初始化精灵
p := &PetInfo{
ID: uint32(id),
CatchTime: uint32(time.Now().Unix()),
Level: uint32(level),
EffectInfo: make([]PetEffectInfo, 0),
}
// ---- 处理闪光 ----
if shinyid != nil {
//todo 待实现异色字段
p.ShinyInfo = shinyid
// r := service.NewShinyService().GetShiny(shinyid)
// if r != nil {
// p.ShinyInfo = append(p.ShinyInfo, *r)
// }
// p.Shiny = uint32(shinyid)
}
if gen == -1 {
p.Gender = grand.N(1, 4)
if cool.Config.ServerInfo.IsVip != 0 {
p.Gender = grand.N(1, 2)
}
if p.Gender == 3 || p.Gender == 4 {
p.Gender = 0
}
}
// ---- 性格 ----
if natureId == -1 {
p.Nature = uint32(grand.Intn(25))
} else {
p.Nature = uint32(natureId)
}
// ---- 个体值DV----
if dv == -1 {
p.Dv = uint32(CalculateIndividualValue())
} else {
if dv < 0 {
dv = 0
} else if dv > 31 {
dv = 31
}
p.Dv = uint32(dv)
}
// ---- 特性 ----
switch {
case abilityTypeEnum == 0:
// 无特性
case abilityTypeEnum > 0:
// 指定特性
if v, ok := xmlres.PlayerEffectMAP[int(abilityTypeEnum)]; ok {
p.EffectInfo = append(p.EffectInfo, PetEffectInfo{
Idx: uint16(gconv.Int16(v.Idx)),
Status: 1,
EID: uint16(gconv.Int16(v.Eid)),
Args: v.ArgsS,
})
}
case abilityTypeEnum == -1:
for _, v := range xmlres.PlayerEffectMAP {
if gconv.Int(v.StarLevel) == 0 {
p.EffectInfo = append(p.EffectInfo, PetEffectInfo{
Idx: uint16(gconv.Int16(v.Idx)),
Status: 1,
EID: uint16(gconv.Int16(v.Eid)),
Args: v.ArgsS,
})
break
}
}
}
// ---- 技能学习 ----
skills := utils.LastFourElements(p.GetLevelRangeCanLearningSkills(0, p.Level), 4) // 最后四个技能
for i := 0; i < len(skills) && i < 4; i++ {
skillID := skills[i]
if info, ok := xmlres.SkillMap[int(skillID)]; ok {
p.SkillList = append(p.SkillList, SkillInfo{ID: skillID, PP: uint32(info.MaxPP)})
}
}
if len(p.SkillList) > 4 {
p.SkillList = p.SkillList[:4]
}
// ---- 属性计算 ----
p.CalculatePetPane(0)
p.Update(false)
return p
}
// 除数数组放大100倍
// 数组按递增顺序排列,用于判断个体值等级
var divisors = []int{
600, 1200, 1900, 2700, 3600, 4600, 5700, 6900, 8200, 9600,
11100, 12700, 14400, 16200, 18100, 20100, 22100, 24000,
25800, 27500, 29100, 30600, 32000, 33300, 34500, 35600,
36600, 37500, 38300, 39000, 39600,
}
// CalculateIndividual 根据给定的a值计算个体值
// 返回值表示a大于等于多少个除数范围0-31
func CalculateIndividual(a int) int {
individual := 0
for _, divisor := range divisors {
if a >= divisor {
individual++
} else {
break // 数组是递增的,可提前跳出循环
}
}
return individual
}
// CalculateIndividualValue 计算个体值0-31
// 接收外部随机数生成器,便于控制随机性和复用
func CalculateIndividualValue() int {
// 生成0-40000的随机数作为个体值计算的输入
a := grand.Intn(40001)
return CalculateIndividual(a)
}