pig.go 3.3 KB

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