2023-12-01 21:45:06 +00:00
|
|
|
package tabula
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2023-12-06 22:02:04 +00:00
|
|
|
"math"
|
|
|
|
"sync"
|
2023-12-01 21:45:06 +00:00
|
|
|
)
|
|
|
|
|
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},
|
|
|
|
}
|
|
|
|
|
2023-12-01 21:45:06 +00:00
|
|
|
type Analysis struct {
|
|
|
|
Board Board
|
|
|
|
Moves [][]int
|
2023-12-02 19:25:07 +00:00
|
|
|
Past bool
|
2023-12-01 21:45:06 +00:00
|
|
|
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
|
2023-12-01 21:45:06 +00:00
|
|
|
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()
|
2023-12-01 21:45:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2023-12-01 21:45:06 +00:00
|
|
|
}
|