tabula/analysis.go

163 lines
3.3 KiB
Go
Raw Normal View History

package tabula
import (
"fmt"
2023-12-06 22:02:04 +00:00
"math"
"sync"
)
2023-12-06 22:02:04 +00:00
var (
WeightBlot = 1.03
WeightHit = -0.5
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},
}
type Analysis struct {
Board Board
Moves [][]int
2023-12-02 19:25:07 +00:00
Past bool
Score float64
Pips int
Blots int
Hits int
PlayerScore float64
OppPips float64
OppBlots float64
OppHits float64
OppScore float64
2023-12-06 22:02:04 +00:00
player int
hitScore int
2023-12-06 22:02:04 +00:00
chance int
}
func (a *Analysis) _analyze(result *[]*Analysis, resultMutex *sync.Mutex, w *sync.WaitGroup) {
var hs int
o := opponent(a.player)
for i := 0; i < len(a.Moves); i++ {
move := a.Moves[i]
checkers := a.Board.Checkers(o, move[1])
if checkers == 1 {
hs += pseudoPips(o, move[1])
}
a.Board = a.Board.Move(move[0], move[1], a.player).UseRoll(move[0], move[1], a.player)
}
a.Board.evaluate(a.player, hs, a)
if a.player == 1 && !a.Past {
const bufferSize = 1024
oppResults := make([]*Analysis, 0, bufferSize)
oppResultMutex := &sync.Mutex{}
wg := &sync.WaitGroup{}
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],
}
bc.evaluate(a.player, 0, a)
oppResultMutex.Lock()
for i := 0; i < check[2]; i++ {
oppResults = append(oppResults, a)
}
oppResultMutex.Unlock()
wg.Done()
return
}
wg.Add(len(available) - 1)
for _, moves := range available {
a := &Analysis{
Board: bc,
Moves: moves,
Past: a.Past,
player: 2,
chance: check[2],
}
go a._analyze(&oppResults, oppResultMutex, wg)
}
}()
}
wg.Wait()
var oppPips float64
var oppBlots float64
var oppHits float64
var oppScore float64
var count float64
for _, r := range oppResults {
oppPips += float64(r.Pips)
oppBlots += float64(r.Blots)
oppHits += float64(r.Hits)
oppScore += r.PlayerScore
count++
}
if count == 0 {
a.Score = a.PlayerScore
} else {
a.OppPips = (oppPips / count)
a.OppBlots = (oppBlots / count)
a.OppHits = (oppHits / count)
a.OppScore = (oppScore / count)
score := a.PlayerScore
if !math.IsNaN(oppScore) {
score += a.OppScore * WeightOppScore
}
a.Score = score
}
} else {
a.Score = a.PlayerScore
}
resultMutex.Lock()
for i := 0; i < a.chance; i++ {
*result = append(*result, a)
}
resultMutex.Unlock()
w.Done()
}
func (a *Analysis) String() string {
2023-12-02 19:25:07 +00:00
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)
}