From 165e8ec516185c8e50781ddaa3fc7c6899859158 Mon Sep 17 00:00:00 2001 From: Trevor Slocum Date: Sat, 2 Dec 2023 11:25:07 -0800 Subject: [PATCH] Incentivize bearing off --- analysis.go | 5 +-- board.go | 100 ++++++++++++++++++++++++-------------------------- board_test.go | 24 +++++++++++- 3 files changed, 72 insertions(+), 57 deletions(-) diff --git a/analysis.go b/analysis.go index c2dd15c..f50ed45 100644 --- a/analysis.go +++ b/analysis.go @@ -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) } diff --git a/board.go b/board.go index 15f0873..ff6f869 100644 --- a/board.go +++ b/board.go @@ -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 diff --git a/board_test.go b/board_test.go index d1c17de..a1fc7c6 100644 --- a/board_test.go +++ b/board_test.go @@ -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