liyujie
2025-08-28 d9927380ed7c8366f762049be9f3fee225860833
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
 
package main
 
import (
   "fmt"
   "math/rand"
)
 
const (
   win            = 100 // The winning score in a game of Pig
   gamesPerSeries = 10  // The number of games per series to simulate
)
 
// A score includes scores accumulated in previous turns for each player,
// as well as the points scored by the current player in this turn.
type score struct {
   player, opponent, thisTurn int
}
 
// An action transitions stochastically to a resulting score.
type action func(current score) (result score, turnIsOver bool)
 
// roll returns the (result, turnIsOver) outcome of simulating a die roll.
// If the roll value is 1, then thisTurn score is abandoned, and the players'
// roles swap.  Otherwise, the roll value is added to thisTurn.
func roll(s score) (score, bool) {
   outcome := rand.Intn(6) + 1 // A random int in [1, 6]
   if outcome == 1 {
       return score{s.opponent, s.player, 0}, true
   }
   return score{s.player, s.opponent, outcome + s.thisTurn}, false
}
 
// stay returns the (result, turnIsOver) outcome of staying.
// thisTurn score is added to the player's score, and the players' roles swap.
func stay(s score) (score, bool) {
   return score{s.opponent, s.player + s.thisTurn, 0}, true
}
 
// A strategy chooses an action for any given score.
type strategy func(score) action
 
// stayAtK returns a strategy that rolls until thisTurn is at least k, then stays.
func stayAtK(k int) strategy {
   return func(s score) action {
       if s.thisTurn >= k {
           return stay
       }
       return roll
   }
}
 
// play simulates a Pig game and returns the winner (0 or 1).
func play(strategy0, strategy1 strategy) int {
   strategies := []strategy{strategy0, strategy1}
   var s score
   var turnIsOver bool
   currentPlayer := rand.Intn(2) // Randomly decide who plays first
   for s.player+s.thisTurn < win {
       action := strategies[currentPlayer](s)
       s, turnIsOver = action(s)
       if turnIsOver {
           currentPlayer = (currentPlayer + 1) % 2
       }
   }
   return currentPlayer
}
 
// roundRobin simulates a series of games between every pair of strategies.
func roundRobin(strategies []strategy) ([]int, int) {
   wins := make([]int, len(strategies))
   for i := 0; i < len(strategies); i++ {
       for j := i + 1; j < len(strategies); j++ {
           for k := 0; k < gamesPerSeries; k++ {
               winner := play(strategies[i], strategies[j])
               if winner == 0 {
                   wins[i]++
               } else {
                   wins[j]++
               }
           }
       }
   }
   gamesPerStrategy := gamesPerSeries * (len(strategies) - 1) // no self play
   return wins, gamesPerStrategy
}
 
// ratioString takes a list of integer values and returns a string that lists
// each value and its percentage of the sum of all values.
// e.g., ratios(1, 2, 3) = "1/6 (16.7%), 2/6 (33.3%), 3/6 (50.0%)"
func ratioString(vals ...int) string {
   total := 0
   for _, val := range vals {
       total += val
   }
   s := ""
   for _, val := range vals {
       if s != "" {
           s += ", "
       }
       pct := 100 * float64(val) / float64(total)
       s += fmt.Sprintf("%d/%d (%0.1f%%)", val, total, pct)
   }
   return s
}
 
func main() {
   strategies := make([]strategy, win)
   for k := range strategies {
       strategies[k] = stayAtK(k + 1)
   }
   wins, games := roundRobin(strategies)
 
   for k := range strategies {
       fmt.Printf("Wins, losses staying at k =% 4d: %s\n",
           k+1, ratioString(wins[k], games-wins[k]))
   }
}