Created
April 7, 2013 01:56
-
-
Save kuno/5328531 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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])) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment