package tabula import ( "fmt" "runtime" "sync" ) var Verbose bool var QueueBufferSize = 4096000 var ( WeightBlot = 1.025 WeightHit = -1.0 WeightOppScore = -3.0 ) // rollProbabilities is a table of the probability of each roll combination. var rollProbabilities = [21][3]int{ {1, 1, 1}, {1, 2, 2}, {1, 3, 2}, {1, 4, 2}, {1, 5, 2}, {1, 6, 2}, {2, 2, 1}, {2, 3, 2}, {2, 4, 2}, {2, 5, 2}, {2, 6, 2}, {3, 3, 1}, {3, 4, 2}, {3, 5, 2}, {3, 6, 2}, {4, 4, 1}, {4, 5, 2}, {4, 6, 2}, {5, 5, 1}, {5, 6, 2}, {6, 6, 1}, } var analysisQueue = make(chan *Analysis, QueueBufferSize) func init() { cpus := runtime.NumCPU() if cpus < 1 { cpus = 1 } for i := 0; i < cpus; i++ { go analyzer() } } type Analysis struct { Board Board Moves [4][2]int8 Past bool Score float64 Pips int Blots int Hits int PlayerScore float64 result *[]*Analysis resultMutex *sync.Mutex OppPips float64 OppBlots float64 OppHits float64 OppScore float64 player int8 hitScore int chance int wg *sync.WaitGroup } func (a *Analysis) _analyze() { var hs int o := opponent(a.player) for i := 0; i < 4; i++ { move := a.Moves[i] if move[0] == 0 && move[1] == 0 { break } checkers := checkers(o, a.Board[move[1]]) if checkers == 1 { hs += PseudoPips(a.player, move[1], a.Board[SpaceVariant]) } a.Board = a.Board.UseRoll(move[0], move[1], a.player).Move(move[0], move[1], a.player) } if !a.Past { a.Past = a.Board.Past() } a.Board.evaluate(a.player, hs, a) if a.player == 1 && !a.Past { a.wg.Add(21) for j := 0; j < 21; j++ { j := j go func() { check := rollProbabilities[j] bc := a.Board bc[SpaceRoll1], bc[SpaceRoll2] = int8(check[0]), int8(check[1]) if check[0] == check[1] { bc[SpaceRoll3], bc[SpaceRoll4] = int8(check[0]), int8(check[1]) } else { bc[SpaceRoll3], bc[SpaceRoll4] = 0, 0 } available, _ := bc.Available(2) if len(available) == 0 { { a := &Analysis{ Board: bc, Past: a.Past, player: 2, chance: check[2], result: a.result, resultMutex: a.resultMutex, } bc.evaluate(a.player, 0, a) a.resultMutex.Lock() for i := 0; i < a.chance; i++ { *a.result = append(*a.result, a) } a.resultMutex.Unlock() } a.wg.Done() return } a.wg.Add(len(available)) for _, moves := range available { a := &Analysis{ Board: bc, Moves: moves, Past: a.Past, player: 2, chance: check[2], result: a.result, resultMutex: a.resultMutex, wg: a.wg, } analysisQueue <- a } a.wg.Done() }() } } else if a.player == 2 { a.resultMutex.Lock() for i := 0; i < a.chance; i++ { *a.result = append(*a.result, a) } a.resultMutex.Unlock() } a.wg.Done() } func (a *Analysis) String() string { return fmt.Sprintf("Moves: %s Score: %.2f - Score: %.2f Pips: %d Blots: %d Hits: %d / Score: %.2f Pips: %.2f Blots: %.2f Hits: %.2f Past: %v", fmt.Sprint(a.Moves), a.Score, a.PlayerScore, a.Pips, a.Blots, a.Hits, a.OppScore, a.OppPips, a.OppBlots, a.OppHits, a.Past) } func analyzer() { var a *Analysis for { a = <-analysisQueue a._analyze() } }