218 lines
6.4 KiB
Go
218 lines
6.4 KiB
Go
package livestream
|
||
|
||
import (
|
||
"encoding/json"
|
||
"testing"
|
||
|
||
"bindbox-game/internal/repository/mysql/model"
|
||
)
|
||
|
||
// TestDiscreteRandomDistribution 测试随机离散分布的均匀性
|
||
func TestDiscreteRandomDistribution(t *testing.T) {
|
||
// 模拟实际奖品配置
|
||
jsonData := `[{"id":58,"name":"Nu高达","weight":100},{"id":59,"name":"NT高达","weight":100},{"id":60,"name":"魔礼青","weight":100},{"id":61,"name":"维达尔","weight":100},{"id":62,"name":"玉衡星6号","weight":400},{"id":63,"name":"马克兔","weight":600},{"id":64,"name":"00高达","weight":700},{"id":65,"name":"SD随机款","weight":1100},{"id":66,"name":"兰博基尼","weight":3000},{"id":67,"name":"高达徽章","weight":28000},{"id":68,"name":"冰箱贴","weight":65800}]`
|
||
|
||
var prizes []*model.LivestreamPrizes
|
||
if err := json.Unmarshal([]byte(jsonData), &prizes); err != nil {
|
||
t.Fatalf("解析奖品数据失败: %v", err)
|
||
}
|
||
|
||
// 计算总权重
|
||
var totalWeight int32
|
||
for _, p := range prizes {
|
||
totalWeight += p.Weight
|
||
}
|
||
|
||
t.Logf("总权重: %d", totalWeight)
|
||
|
||
// 生成随机离散位置
|
||
state, err := GenerateDiscretePositions(1, prizes)
|
||
if err != nil {
|
||
t.Fatalf("生成随机离散位置失败: %v", err)
|
||
}
|
||
|
||
// 模拟10000次抽奖
|
||
simulateCount := 10000
|
||
results := make(map[int64]int)
|
||
|
||
for i := 0; i < simulateCount; i++ {
|
||
// 模拟随机值
|
||
randValue := int32(i % int(totalWeight)) // 简化为顺序遍历,实际用crypto/rand
|
||
|
||
// 查找对应的奖品
|
||
for prizeID, positions := range state.PrizePosMap {
|
||
for _, pos := range positions {
|
||
if pos == randValue {
|
||
results[prizeID]++
|
||
break
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 创建奖品ID到名称的映射
|
||
prizeMap := make(map[int64]string)
|
||
for _, p := range prizes {
|
||
prizeMap[p.ID] = p.Name
|
||
}
|
||
|
||
// 输出统计结果
|
||
t.Log("\n========== 随机离散分布测试结果 ==========")
|
||
for _, p := range prizes {
|
||
count := results[p.ID]
|
||
theoryProb := float64(p.Weight) / float64(totalWeight) * 100
|
||
actualProb := float64(count) / float64(simulateCount) * 100
|
||
diff := actualProb - theoryProb
|
||
|
||
t.Logf("%-20s 权重:%6d 理论:%6.2f%% 实际:%6.2f%% 偏差:%+6.2f%% 次数:%5d",
|
||
p.Name, p.Weight, theoryProb, actualProb, diff, count)
|
||
}
|
||
}
|
||
|
||
// TestContinuousVsDiscrete 对比连续区间和随机离散
|
||
func TestContinuousVsDiscrete(t *testing.T) {
|
||
// 简化奖品配置
|
||
prizes := []*model.LivestreamPrizes{
|
||
{ID: 1, Name: "大奖", Weight: 100},
|
||
{ID: 2, Name: "小奖A", Weight: 3000},
|
||
{ID: 3, Name: "小奖B", Weight: 3000},
|
||
{ID: 4, Name: "冰箱贴", Weight: 65800},
|
||
}
|
||
|
||
totalWeight := int32(100 + 3000 + 3000 + 65800)
|
||
|
||
t.Logf("\n========== 连续区间 vs 随机离散对比 ==========")
|
||
t.Logf("总权重: %d", totalWeight)
|
||
t.Log("")
|
||
|
||
// 连续区间模拟
|
||
t.Log("【连续区间方法】")
|
||
t.Log("大奖区间: [0, 99] (0.1%)")
|
||
t.Log("小奖A区间: [100, 3099] (3%)")
|
||
t.Log("小奖B区间: [3100, 6099] (3%)")
|
||
t.Log("冰箱贴区间: [6100, 71899] (65.8%)")
|
||
t.Log("")
|
||
|
||
// 随机离散模拟
|
||
state, err := GenerateDiscretePositions(1, prizes)
|
||
if err != nil {
|
||
t.Fatalf("生成随机离散位置失败: %v", err)
|
||
}
|
||
|
||
t.Log("【随机离散方法】")
|
||
t.Logf("大奖的随机位置数: %d", len(state.PrizePosMap[1]))
|
||
t.Logf("小奖A的随机位置数: %d", len(state.PrizePosMap[2]))
|
||
t.Logf("小奖B的随机位置数: %d", len(state.PrizePosMap[3]))
|
||
t.Logf("冰箱贴的随机位置数: %d", len(state.PrizePosMap[4]))
|
||
|
||
// 验证位置分布
|
||
t.Log("\n位置分布示例(前10个):")
|
||
if positions, ok := state.PrizePosMap[1]; ok && len(positions) > 0 {
|
||
sampleSize := 10
|
||
if len(positions) < sampleSize {
|
||
sampleSize = len(positions)
|
||
}
|
||
t.Logf("大奖位置: %v", positions[:sampleSize])
|
||
}
|
||
|
||
if positions, ok := state.PrizePosMap[4]; ok && len(positions) > 0 {
|
||
sampleSize := 10
|
||
if len(positions) < sampleSize {
|
||
sampleSize = len(positions)
|
||
}
|
||
t.Logf("冰箱贴位置: %v", positions[:sampleSize])
|
||
}
|
||
}
|
||
|
||
// TestEdgeCases 测试边界情况
|
||
func TestEdgeCases(t *testing.T) {
|
||
t.Run("空奖品列表", func(t *testing.T) {
|
||
_, err := GenerateDiscretePositions(1, []*model.LivestreamPrizes{})
|
||
if err == nil {
|
||
t.Error("应该返回错误")
|
||
}
|
||
})
|
||
|
||
t.Run("零权重", func(t *testing.T) {
|
||
prizes := []*model.LivestreamPrizes{
|
||
{ID: 1, Name: "奖品", Weight: 0},
|
||
}
|
||
_, err := GenerateDiscretePositions(1, prizes)
|
||
if err == nil {
|
||
t.Error("应该返回错误")
|
||
}
|
||
})
|
||
|
||
t.Run("单个奖品", func(t *testing.T) {
|
||
prizes := []*model.LivestreamPrizes{
|
||
{ID: 1, Name: "唯一奖品", Weight: 1000},
|
||
}
|
||
state, err := GenerateDiscretePositions(1, prizes)
|
||
if err != nil {
|
||
t.Fatalf("不应该出错: %v", err)
|
||
}
|
||
if len(state.PrizePosMap[1]) != 1000 {
|
||
t.Errorf("位置数量错误,期望1000,实际%d", len(state.PrizePosMap[1]))
|
||
}
|
||
})
|
||
}
|
||
|
||
// TestPositionUniqueness 测试位置的唯一性
|
||
func TestPositionUniqueness(t *testing.T) {
|
||
prizes := []*model.LivestreamPrizes{
|
||
{ID: 1, Name: "大奖", Weight: 100},
|
||
{ID: 2, Name: "小奖", Weight: 3000},
|
||
{ID: 3, Name: "冰箱贴", Weight: 65800},
|
||
}
|
||
|
||
state, err := GenerateDiscretePositions(1, prizes)
|
||
if err != nil {
|
||
t.Fatalf("生成失败: %v", err)
|
||
}
|
||
|
||
// 收集所有位置
|
||
allPositions := make(map[int32]int64)
|
||
for prizeID, positions := range state.PrizePosMap {
|
||
for _, pos := range positions {
|
||
if existingPrizeID, exists := allPositions[pos]; exists {
|
||
t.Errorf("位置 %d 被多个奖品占用: 奖品%d 和 奖品%d", pos, existingPrizeID, prizeID)
|
||
}
|
||
allPositions[pos] = prizeID
|
||
}
|
||
}
|
||
|
||
expectedCount := 100 + 3000 + 65800
|
||
if len(allPositions) != expectedCount {
|
||
t.Errorf("位置总数错误,期望%d,实际%d", expectedCount, len(allPositions))
|
||
}
|
||
|
||
t.Logf("位置唯一性验证通过: %d 个唯一位置", len(allPositions))
|
||
}
|
||
|
||
// BenchmarkDiscreteVsContinuous 性能对比测试
|
||
func BenchmarkDiscreteGeneration(b *testing.B) {
|
||
prizes := []*model.LivestreamPrizes{
|
||
{ID: 1, Name: "大奖", Weight: 100},
|
||
{ID: 2, Name: "小奖A", Weight: 3000},
|
||
{ID: 3, Name: "小奖B", Weight: 3000},
|
||
{ID: 4, Name: "冰箱贴", Weight: 65800},
|
||
}
|
||
|
||
b.ResetTimer()
|
||
for i := 0; i < b.N; i++ {
|
||
_, err := GenerateDiscretePositions(int64(i), prizes)
|
||
if err != nil {
|
||
b.Fatal(err)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 打印结果辅助函数
|
||
func printResults(t *testing.T, results map[int64]int, prizes []*model.LivestreamPrizes, simulateCount int) {
|
||
t.Log("\n========== 测试结果 ==========")
|
||
for _, p := range prizes {
|
||
count := results[p.ID]
|
||
t.Logf("%-20s 中奖次数: %d (%.2f%%)", p.Name, count, float64(count)/float64(simulateCount)*100)
|
||
}
|
||
}
|