Incentivize bearing off

This commit is contained in:
Trevor Slocum 2023-12-02 11:25:07 -08:00
parent 2a8f1687c4
commit 165e8ec516
3 changed files with 72 additions and 57 deletions

View file

@ -7,6 +7,7 @@ import (
type Analysis struct {
Board Board
Moves [][]int
Past bool
Score float64
Pips int
@ -19,11 +20,9 @@ type Analysis struct {
OppHits float64
OppScore float64
player int
hitScore int
past bool
}
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)
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)
}

100
board.go
View file

@ -265,7 +265,9 @@ func (b Board) Past(player int) bool {
var playerFirst, opponentLast int
for space := 1; space < 25; space++ {
v := b[space]
if v > 0 {
if v == 0 {
continue
} else if v > 0 {
if player == 1 && playerFirst == 0 {
playerFirst = space
if opponentLast != 0 {
@ -298,49 +300,43 @@ func (b Board) Past(player int) bool {
}
}
func (b Board) Pips(player int) int {
var pips float64
var spaceValue float64
func (b Board) spaceValue(player int, space int) int {
var spaceValue int
if player == 1 {
pips += float64(b.Checkers(SpaceBarPlayer, player)) * 25
spaceValue = space*2 + 7
if space > 6 {
spaceValue += 7
}
} else {
pips += float64(b.Checkers(SpaceBarOpponent, player)) * 25
spaceValue = (25-space)*2 + 7
if space < 19 {
spaceValue += 7
}
}
return spaceValue
}
func (b Board) Pips(player int) int {
var pips int
if player == 1 {
pips += int(b.Checkers(SpaceBarPlayer, player))*50 + 14
} else {
pips += int(b.Checkers(SpaceBarOpponent, player))*50 + 14
}
for space := 1; space < 25; space++ {
if player == 1 {
spaceValue = float64(space)
if space <= 6 {
spaceValue /= 4
} else {
spaceValue += 6
}
} else {
spaceValue = float64(25 - space)
if space >= 19 {
spaceValue /= 4
} else {
spaceValue += 6
}
}
pips += float64(b.Checkers(space, player)) * spaceValue
pips += int(b.Checkers(space, player)) * b.spaceValue(space, player)
}
return int(pips)
return pips
}
func (b Board) Blots(player int) int {
var pips int
var spaceValue int
for space := 1; space < 25; space++ {
checkers := b.Checkers(space, player)
if checkers != 1 {
continue
}
if player == 1 {
spaceValue = 25 - space
} else {
spaceValue = space
}
pips += int(checkers) * spaceValue
pips += int(checkers) * b.spaceValue(space, player)
}
return pips
}
@ -355,7 +351,7 @@ func (b Board) evaluate(player int, hitScore int, a *Analysis) {
pips := b.Pips(player)
score := float64(pips)
var blots int
if !a.past {
if !a.Past {
blots := b.Blots(player)
score += float64(blots)*WeightBlot + float64(hitScore)*WeightHit
}
@ -363,22 +359,21 @@ func (b Board) evaluate(player int, hitScore int, a *Analysis) {
a.Blots = blots
a.Hits = hitScore
a.PlayerScore = score
a.hitScore = hitScore
}
func (b Board) Evaluation(player int, hitScore int, moves [][]int) *Analysis {
past := b.Past(player)
a := &Analysis{
Board: b,
Moves: moves,
hitScore: hitScore,
past: past,
Board: b,
Moves: moves,
Past: past,
}
b.evaluate(player, hitScore, a)
return a
}
func queueAnalysis(a *Analysis, w *sync.WaitGroup, b Board, player int, available [][]int, moves [][]int, found *[][][]int, result *[]*Analysis, resultMutex *sync.Mutex) {
var hs int
for _, move := range available {
move := move
go func() {
@ -396,20 +391,15 @@ func queueAnalysis(a *Analysis, w *sync.WaitGroup, b Board, player int, availabl
resultMutex.Unlock()
checkers := b.Checkers(move[1], opponent(player))
hs = 0
hs := a.hitScore
if checkers == 1 {
if player == 1 {
hs = move[1]
} else {
hs = 25 - move[1]
}
hs += b.spaceValue(player, move[1])
}
a := &Analysis{
Board: b.Move(move[0], move[1], player).UseRoll(move[0], move[1], player),
Moves: newMoves,
player: player,
hitScore: hs,
Board: b.Move(move[0], move[1], player).UseRoll(move[0], move[1], player),
Moves: newMoves,
Past: a.Past,
}
a.Board.evaluate(player, hs, a)
@ -437,7 +427,7 @@ func (b Board) Analyze(player int, available [][]int) []*Analysis {
resultMutex := &sync.Mutex{}
a := &Analysis{
past: b.Past(player),
Past: b.Past(player),
}
b.evaluate(player, 0, a)
@ -459,7 +449,7 @@ func (b Board) Analyze(player int, available [][]int) []*Analysis {
}
}
result = newResult
if player == 1 {
if player == 1 && !a.Past {
oppResults := make([][]*Analysis, len(result))
w.Add(len(result) * 21)
for i := range result {
@ -478,7 +468,7 @@ func (b Board) Analyze(player int, available [][]int) []*Analysis {
}
available := bc.Available(2)
a := &Analysis{
past: b.Past(player),
Past: b.Past(2),
}
w.Add(len(available))
queueAnalysis(a, w, bc, 2, available, nil, &[][][]int{}, &oppResults[i], oppResultMutex)
@ -501,16 +491,20 @@ func (b Board) Analyze(player int, available [][]int) []*Analysis {
oppScore += r.PlayerScore
count++
}
score := result[i].PlayerScore
if !math.IsNaN(oppScore) {
score += result[i].OppScore * WeightOppScore
}
result[i].OppPips = (oppPips / count)
result[i].OppBlots = (oppBlots / count)
result[i].OppHits = (oppHits / count)
result[i].OppScore = (oppScore / count)
score := result[i].PlayerScore
if !math.IsNaN(oppScore) {
score += result[i].OppScore * WeightOppScore
}
result[i].Score = score
}
} else {
for i := range result {
result[i].Score = result[i].PlayerScore
}
}
sort.Slice(result, func(i, j int) bool {
return result[i].Score < result[j].Score

View file

@ -5,7 +5,7 @@ import (
"testing"
)
func TestBoard(t *testing.T) {
func TestMove(t *testing.T) {
b := NewBoard()
b[SpaceRoll1] = 1
b[SpaceRoll2] = 2
@ -36,6 +36,28 @@ func TestBoard(t *testing.T) {
}
}
func TestPast(t *testing.T) {
b := NewBoard()
got, expected := b.Past(1), false
if got != expected {
t.Errorf("unexpected past value: expected %v: got %v", expected, got)
}
got, expected = b.Past(2), false
if got != expected {
t.Errorf("unexpected past value: expected %v: got %v", expected, got)
}
b = Board{7, 2, 2, 4, 0, -2, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, -1, -1, 0, -4, 0, -2, -1, -1, -1, 0, 0, 0, 6, 2, 0, 0}
got, expected = b.Past(1), true
if got != expected {
t.Errorf("unexpected past value: expected %v: got %v", expected, got)
}
got, expected = b.Past(2), true
if got != expected {
t.Errorf("unexpected past value: expected %v: got %v", expected, got)
}
}
func BenchmarkAvailable(b *testing.B) {
type testCase struct {
roll1, roll2, roll3, roll4 int8