169 lines
6.3 KiB
Go
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"},
|
|
}
|
|
}
|