package model import ( "fmt" "math/rand" "roserver/serverproto" "strconv" "strings" ) const ( LIVEROOM_STAGE_NULL = 0 LIVEROOM_STAGE_IDLE = 1 LIVEROOM_STAGE_READY = 2 LIVEROOM_STAGE_SHOW = 3 LIVEROOM_STAGE_RESULT = 4 ) const ( LIVEROOM_STAGE_IDLE_TIMER = 0 LIVEROOM_STAGE_SHOW_READY_TIMER = 15 LIVEROOM_STAGE_SHOWING_TIMER = 30 LIVEROOM_STAGE_SHOW_END_TIMER = 5 ) const ( LIVEROOM_TYPE_CARD = 1 LIVEROOM_TYPE_SELL = 2 LIVEROOM_TYPE_ANSWER = 3 LIVEROOM_TYPE_GAME = 4 LIVEROOM_TYPE_BID = 5 LIVEROOM_TYPE_SEND_GIFT = 6 ) const ( LIVEROOM_CARD_MAX_READY_NUM = 5 LIVEROOM_SELL_MAX_READY_NUM = 100 LIVEROOM_ANSWER_MAX_READY_NUM = 100 LIVEROOM_GAME_MAX_READY_NUM = 100 LIVEROOM_BID_MAX_READY_NUM = 100 ) const ( LIVEROOM_MAX_PLAYER_NUM = 1000 LIVEROOM_TICK_INTERVAL = 1000 ) const ( LIVEROOM_CMD_TALK = 1 LIVEROOM_CMD_PLAY = 2 ) const ( LIVEROOM_CMD_PLAY_PARAM_BidPreview = "BidPreview" LIVEROOM_CMD_PLAY_PARAM_Bid = "Bid" ) // 泛型按值删除 func SliceRemoveByValue[T comparable](slice []T, value T) []T { result := slice[:0] for _, v := range slice { if v != value { result = append(result, v) } } return truncateSlice(result) } // 截断切片容量,避免内存泄漏 func truncateSlice[T any](slice []T) []T { return slice[:len(slice):len(slice)] } func SliceIsExist[T comparable](slice []T, value T) bool { for _, v := range slice { if v == value { return true } } return false } func WeightedRandomChoice(weights map[string]int) string { total := 0 for _, w := range weights { total += w } r := rand.Intn(total) current := 0 for item, weight := range weights { current += weight if r < current { return item } } var lastItem string for item := range weights { lastItem = item } return lastItem } func SliceContains[T any](slice []T, predicate func(T) bool) bool { for _, v := range slice { if predicate(v) { return true } } return false } func SliceFind[T any](slice []T, predicate func(T) bool) (T, bool) { for _, v := range slice { if predicate(v) { return v, true } } var zero T return zero, false } // 从 map 中随机选择 N 个键 func RandomKeys[K comparable, V any](m map[K]V, n int) []K { if n <= 0 || len(m) == 0 { return []K{} } // 如果请求的数量大于 map 大小,返回所有键 if n >= len(m) { keys := make([]K, 0, len(m)) for k := range m { keys = append(keys, k) } return keys } // 获取所有键 allKeys := make([]K, 0, len(m)) for k := range m { allKeys = append(allKeys, k) } // Fisher-Yates 洗牌算法 rand.Shuffle(len(allKeys), func(i, j int) { allKeys[i], allKeys[j] = allKeys[j], allKeys[i] }) // 返回前 N 个 return allKeys[:n] } func ShuffleInPlace[T any](slice []T) { rand.Shuffle(len(slice), func(i, j int) { slice[i], slice[j] = slice[j], slice[i] }) } func JoinSlice[T any](slice []T, sep string) string { if len(slice) == 0 { return "" } var sb strings.Builder for i, item := range slice { if i > 0 { sb.WriteString(sep) } sb.WriteString(fmt.Sprint(item)) } return sb.String() } func MakeLiveRoomPlayerInfo(uid uint64) *serverproto.LiveRoomPlayerInfo { currPlayer := RoomMgr.GetPlayer(uid) return &serverproto.LiveRoomPlayerInfo{ Uid: uid, Nickname: currPlayer.Name, Level: currPlayer.Level, HeadId: currPlayer.HeadId, HeadTitle: currPlayer.HeadTitle, } } // 获取前N个元素,自动处理边界情况 func GetFirstN[T any](slice []T, n int) []T { if n <= 0 { return []T{} // 返回空切片 } // 如果n大于切片长度,返回整个切片的副本 if n >= len(slice) { result := make([]T, len(slice)) copy(result, slice) return result } // 正常情况:返回前n个元素的副本 result := make([]T, n) copy(result, slice[:n]) return result } type ProbabilityItem struct { ID int Probability int } func parseProbabilityStr(str string) ([]ProbabilityItem, error) { if str == "" { return nil, fmt.Errorf("概率字符串为空") } items := make([]ProbabilityItem, 0) pairs := strings.Split(str, ";") for _, pair := range pairs { if pair == "" { continue } parts := strings.Split(pair, ":") if len(parts) != 2 { return nil, fmt.Errorf("格式错误: %s", pair) } id, err := strconv.Atoi(strings.TrimSpace(parts[0])) if err != nil { return nil, fmt.Errorf("ID转换错误: %s", parts[0]) } prob, err := strconv.Atoi(strings.TrimSpace(parts[1])) if err != nil { return nil, fmt.Errorf("概率转换错误: %s", parts[1]) } if prob < 0 { return nil, fmt.Errorf("概率不能为负数: %d", prob) } items = append(items, ProbabilityItem{ ID: id, Probability: prob, }) } return items, nil } // SelectByProbability 根据概率随机选择一项 func selectByProbability(items []ProbabilityItem) (int, error) { if len(items) == 0 { return 0, fmt.Errorf("概率项列表为空") } // 计算总概率 totalProb := 0 for _, item := range items { totalProb += item.Probability } if totalProb <= 0 { // 如果所有概率都为0,则从所有ID中随机选择一个 randIdx := rand.Intn(len(items)) return items[randIdx].ID, nil } // 生成随机数 randomValue := rand.Intn(totalProb) // 根据概率选择 currentSum := 0 for _, item := range items { currentSum += item.Probability if randomValue < currentSum { return item.ID, nil } } // 理论上不会执行到这里 return items[len(items)-1].ID, nil } // -1:50;601:50;602:30;603:10;604:10;605:5;606:5;607:0;608:0;609:0 func RollForRateCfg(str string) (int, error) { items, _ := parseProbabilityStr(str) return selectByProbability(items) } func MapToString(m map[string]string) string { if len(m) == 0 { return "" } var builder strings.Builder // 有序输出(按key排序) keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } // 排序(可选) //sort.Strings(keys) for i, k := range keys { builder.WriteString(k) builder.WriteString(":") builder.WriteString(m[k]) if i < len(keys)-1 { builder.WriteString(";") } } return builder.String() }