334 lines
6.4 KiB
Go
334 lines
6.4 KiB
Go
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"
|
||
)
|
||
|
||
const (
|
||
LIVEROOM_SEND_GIFT_TALK_TYPE = 1000
|
||
)
|
||
|
||
// 泛型按值删除
|
||
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 ArrayJoin[T any](slice []T, str string) string {
|
||
if len(slice) == 0 {
|
||
return ""
|
||
}
|
||
|
||
var builder strings.Builder
|
||
for i, v := range slice {
|
||
if i > 0 {
|
||
builder.WriteString(str)
|
||
}
|
||
builder.WriteString(fmt.Sprint(v)) // 使用 fmt.Sprint 转换为字符串
|
||
}
|
||
return builder.String()
|
||
}
|
||
|
||
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()
|
||
}
|