2026-06-12 20:12:47 +08:00

169 lines
6.3 KiB
Go

package selfgame
import "testing"
func TestEvaluateRobotStrategyBlackPoolSkipsRobot(t *testing.T) {
decision, err := EvaluateRobotStrategy(baseStrategyInput(87, 88), func(int) (int, error) {
t.Fatal("black pool must not roll strategy probability")
return 0, nil
})
if err != nil {
t.Fatalf("evaluate strategy failed: %v", err)
}
if decision.StrategyCode != StrategyCodePoolGuard || decision.ReasonCode != "POOL_BLACK" || decision.Decision != DecisionNone {
t.Fatalf("black pool decision mismatch: %+v", decision)
}
}
func TestEvaluateRobotStrategyBlackPoolCanForceRobotWin(t *testing.T) {
input := baseStrategyInput(87, 88)
input.NewUserPolicy.BlackPoolRobotForceWinEnabled = true
decision, err := EvaluateRobotStrategy(input, func(int) (int, error) {
t.Fatal("black pool robot force-win must not roll strategy probability")
return 0, nil
})
if err != nil {
t.Fatalf("evaluate strategy failed: %v", err)
}
if decision.StrategyCode != StrategyCodePoolGuard ||
decision.ReasonCode != "POOL_BLACK_ROBOT_FORCE_WIN" ||
decision.Decision != DecisionForceHumanLose ||
decision.ForceResult != ForceResultHumanLose {
t.Fatalf("black pool force robot win decision mismatch: %+v", decision)
}
}
func TestEvaluateRobotStrategyDisabledPoolSkipsEvenWhenBlackForceEnabled(t *testing.T) {
input := baseStrategyInput(200, 88)
input.StakePool.Status = "disabled"
input.NewUserPolicy.BlackPoolRobotForceWinEnabled = true
decision, err := EvaluateRobotStrategy(input, func(int) (int, error) {
t.Fatal("disabled pool must not roll strategy probability")
return 0, nil
})
if err != nil {
t.Fatalf("evaluate strategy failed: %v", err)
}
if decision.StrategyCode != StrategyCodePoolGuard || decision.ReasonCode != "POOL_BLACK" || decision.Decision != DecisionNone {
t.Fatalf("disabled pool should still skip robot, got %+v", decision)
}
}
func TestEvaluateRobotStrategyNewUserProtectionHit(t *testing.T) {
decision, err := EvaluateRobotStrategy(baseStrategyInput(200, 88), func(max int) (int, error) {
if max != 100 {
t.Fatalf("strategy roll max = %d, want 100", max)
}
return 0, nil
})
if err != nil {
t.Fatalf("evaluate strategy failed: %v", err)
}
if decision.Decision != DecisionForceHumanWin || decision.ForceResult != ForceResultHumanWin || !decision.ShouldConsumeProtection {
t.Fatalf("new user protection decision mismatch: %+v", decision)
}
}
func TestEvaluateRobotStrategyNewUserLoseStreakProtection(t *testing.T) {
input := baseStrategyInput(200, 88)
input.UserStats.LoseStreak = 2
decision, err := EvaluateRobotStrategy(input, func(int) (int, error) {
t.Fatal("lose streak protection must not roll positive-feedback probability")
return 0, nil
})
if err != nil {
t.Fatalf("evaluate strategy failed: %v", err)
}
if decision.Decision != DecisionForceHumanWin || decision.ForceResult != ForceResultHumanWin || !decision.ShouldConsumeProtection {
t.Fatalf("lose streak protection decision mismatch: %+v", decision)
}
if decision.ReasonCode != "NEW_USER_LOSE_STREAK_PROTECTION" {
t.Fatalf("lose streak protection reason mismatch: %+v", decision)
}
}
func TestEvaluateRobotStrategyQuotaShortFallsBack(t *testing.T) {
input := baseStrategyInput(200, 88)
input.ProtectionState.LifetimeSubsidyCoin = 29950
decision, err := EvaluateRobotStrategy(input, func(int) (int, error) {
t.Fatal("quota short must not roll strategy probability")
return 0, nil
})
if err != nil {
t.Fatalf("evaluate strategy failed: %v", err)
}
if decision.Decision != DecisionNone || decision.ReasonCode != "NEW_USER_LIFETIME_QUOTA_SHORT" {
t.Fatalf("quota short decision mismatch: %+v", decision)
}
}
func TestEvaluateRobotStrategyOldUserFallsBackToNormalRandom(t *testing.T) {
input := baseStrategyInput(200, 88)
input.UserStats = UserStats{EffectiveBotGameCount: 31, WinStreak: 9, TodayNetWinCoin: 900000, SevenDayNetWinCoin: 1800000}
input.ProtectionState.UsedRounds = 30
input.NewUserPolicy.NormalPhaseTargetWinRatePercent = 50
decision, err := EvaluateRobotStrategy(input, nil)
if err != nil {
t.Fatalf("evaluate strategy failed: %v", err)
}
if decision.Decision != DecisionNone || decision.ForceResult != "" || decision.ReasonCode != "NORMAL_PHASE_TARGET_BASELINE" {
t.Fatalf("old user must keep normal random decision, got %+v", decision)
}
}
func TestEvaluateRobotStrategyNormalPhaseDiceTargetHit(t *testing.T) {
input := baseStrategyInput(200, 88)
input.UserStats = UserStats{EffectiveBotGameCount: 31}
input.ProtectionState.UsedRounds = 30
input.NewUserPolicy.NormalPhaseTargetWinRatePercent = 53
decision, err := EvaluateRobotStrategy(input, func(max int) (int, error) {
if max != 10_000 {
t.Fatalf("normal phase should roll bps, max=%d", max)
}
return 599, nil
})
if err != nil {
t.Fatalf("evaluate strategy failed: %v", err)
}
if decision.Decision != DecisionForceHumanWin || decision.ForceResult != ForceResultHumanWinWithoutProtection || decision.ShouldConsumeProtection {
t.Fatalf("normal phase target hit must force win without subsidy consume: %+v", decision)
}
if decision.ReasonCode != "NORMAL_PHASE_TARGET_HIT" {
t.Fatalf("normal phase hit reason mismatch: %+v", decision)
}
}
func TestEvaluateRobotStrategyNormalPhaseRockTargetMiss(t *testing.T) {
input := baseStrategyInput(200, 88)
input.AllowsDraw = true
input.UserStats = UserStats{EffectiveBotGameCount: 31}
input.ProtectionState.UsedRounds = 30
input.NewUserPolicy.NormalPhaseTargetWinRatePercent = 53
decision, err := EvaluateRobotStrategy(input, func(max int) (int, error) {
if max != 10_000 {
t.Fatalf("normal phase should roll bps, max=%d", max)
}
return 409, nil
})
if err != nil {
t.Fatalf("evaluate strategy failed: %v", err)
}
if decision.Decision != DecisionNone || decision.ForceResult != "" || decision.ReasonCode != "NORMAL_PHASE_TARGET_MISS" {
t.Fatalf("normal phase target miss must stay random: %+v", decision)
}
}
func baseStrategyInput(balance int64, required int64) StrategyInput {
return StrategyInput{
AppCode: "lalu",
GameID: "dice",
MatchID: "match_1",
UserID: 101,
StakeCoin: 100,
RequiredPoolCoin: required,
StakePool: StakePool{AppCode: "lalu", GameID: "dice", StakeCoin: 100, BalanceCoin: balance, Status: "active"},
NewUserPolicy: DefaultNewUserPolicy("lalu", "dice", 1700000000000),
ProtectionState: ProtectionState{AppCode: "lalu", GameID: "dice", UserID: 101, Status: "active"},
}
}