From dcc16fb3347757e4b20dc416127a1d648cb235a7 Mon Sep 17 00:00:00 2001 From: Trevor Slocum Date: Sun, 7 Jan 2024 21:28:21 -0800 Subject: [PATCH] Support analyzing tabula games --- analysis.go | 4 +- bei.go | 41 +++++---- board.go | 187 ++++++++++++++++++++++++++++--------- board_test.go | 223 ++++++++++++++++++++++++++++++++++++++++++++- cmd/tabula/main.go | 27 ++++++ go.mod | 2 +- go.sum | 4 +- 7 files changed, 417 insertions(+), 71 deletions(-) diff --git a/analysis.go b/analysis.go index 4f1ac88..386abba 100644 --- a/analysis.go +++ b/analysis.go @@ -72,7 +72,7 @@ type Analysis struct { OppHits float64 OppScore float64 - player int + player int8 hitScore int chance int wg *sync.WaitGroup @@ -90,7 +90,7 @@ func (a *Analysis) _analyze() { if checkers == 1 { hs += PseudoPips(a.player, move[1]) } - a.Board = a.Board.Move(move[0], move[1], a.player).UseRoll(move[0], move[1], a.player) + 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() diff --git a/bei.go b/bei.go index 6e81a64..c3b4a0a 100644 --- a/bei.go +++ b/bei.go @@ -57,22 +57,23 @@ func (s *BEIServer) handleConnection(conn net.Conn) { available, _ := b.Available(1) b.Analyze(available, &analysis) - if len(analysis) == 0 { - log.Printf("error: failed to read from client: zero moves returned for analysis") - conn.Close() - return - } - - move := &bei.Move{} - for _, m := range analysis[0].Moves { - if m[0] == 0 && m[1] == 0 { - break + var move *bei.Move + if len(analysis) > 0 { + move = &bei.Move{} + for _, m := range analysis[0].Moves { + if m[0] == 0 && m[1] == 0 { + break + } + move.Play = append(move.Play, &bei.Play{From: int(m[0]), To: int(m[1])}) } - move.Play = append(move.Play, &bei.Play{From: int(m[0]), To: int(m[1])}) } - buf, err := bei.EncodeEvent(&bei.EventOkMove{ - Moves: []*bei.Move{move}, - }) + result := &bei.EventOkMove{ + Moves: []*bei.Move{}, + } + if move != nil { + result.Moves = append(result.Moves, move) + } + buf, err := bei.EncodeEvent(result) if err != nil { log.Fatalf("error: failed to encode event: %s", err) } @@ -160,16 +161,18 @@ func parseState(buf []byte) (Board, error) { } b[SpaceRoll1] = int8(state.Roll1) b[SpaceRoll2] = int8(state.Roll2) - if state.Roll1 == state.Roll2 { + if int8(state.Variant) != VariantTabula && state.Roll1 == state.Roll2 { b[SpaceRoll3], b[SpaceRoll4] = int8(state.Roll1), int8(state.Roll2) + } else { + b[SpaceRoll3] = int8(state.Roll3) } if int8(state.Variant) != VariantBackgammon { b[SpaceVariant] = int8(state.Variant) - if !state.Entered1 { - b[SpaceEnteredPlayer] = 0 + if state.Entered1 { + b[SpaceEnteredPlayer] = 1 } - if !state.Entered2 { - b[SpaceEnteredOpponent] = 0 + if state.Entered2 { + b[SpaceEnteredOpponent] = 1 } } else { b[SpaceEnteredPlayer] = 1 diff --git a/board.go b/board.go index 1565c78..3a3e4d2 100644 --- a/board.go +++ b/board.go @@ -55,7 +55,7 @@ func (b Board) SetValue(space int, value int8) Board { } // Move moves a checker on the board. -func (b Board) Move(from int8, to int8, player int) Board { +func (b Board) Move(from int8, to int8, player int8) Board { if b[from] == 0 || (player == 1 && b[from] < 0) || (player == 2 && b[from] > 0) { log.Panic("illegal move: no from checker", from, to, player) } else if b[to] != 0 { @@ -76,11 +76,18 @@ func (b Board) Move(from int8, to int8, player int) Board { delta = -1 } b[from], b[to] = b[from]-delta, b[to]+delta + if (player == 1 && from == SpaceHomePlayer && b[SpaceEnteredPlayer] == 0 && b[SpaceHomePlayer] == 0) || (player == 2 && from == SpaceHomeOpponent && b[SpaceEnteredOpponent] == 0 && b[SpaceHomeOpponent] == 0) { + if player == 1 { + b[SpaceEnteredPlayer] = 1 + } else { + b[SpaceEnteredOpponent] = 1 + } + } return b } // checkers returns the number of checkers that belong to the spcified player at the provided space. -func checkers(player int, v int8) int8 { +func checkers(player int8, v int8) int8 { if player == 1 && v > 0 { return v } else if player == 2 && v < 0 { @@ -89,9 +96,11 @@ func checkers(player int, v int8) int8 { return 0 } -func (b Board) MayBearOff(player int) bool { +func (b Board) MayBearOff(player int8) bool { if b[SpaceVariant] != VariantBackgammon && ((player == 1 && b[SpaceEnteredPlayer] == 0) || (player == 2 && b[SpaceEnteredOpponent] == 0)) { return false + } else if b[SpaceVariant] == VariantTabula && !b.SecondHalf(player) { + return false } barSpace := SpaceBarPlayer if player == 2 { @@ -100,46 +109,78 @@ func (b Board) MayBearOff(player int) bool { if checkers(player, b[barSpace]) != 0 { return false } - if player == 1 { - for space := 24; space > 6; space-- { - if checkers(player, b[space]) != 0 { - return false + if b[SpaceVariant] != VariantTabula { + if player == 1 { + for space := 24; space > 6; space-- { + if checkers(player, b[space]) != 0 { + return false + } } - } - } else { - for space := 1; space < 19; space++ { - if checkers(player, b[space]) != 0 { - return false + } else { + for space := 1; space < 19; space++ { + if checkers(player, b[space]) != 0 { + return false + } } } } return true } -func (b Board) spaceDiff(player int, from int8, to int8) int8 { +func (b Board) spaceDiff(player int8, from int8, to int8) int8 { switch { case from < 0 || from > 27 || to < 0 || to > 27: return 0 case to == SpaceBarPlayer || to == SpaceBarOpponent: return 0 - case (from == SpaceBarPlayer || from == SpaceBarOpponent) && (to == SpaceBarPlayer || to == SpaceBarOpponent || to == SpaceHomePlayer || to == SpaceHomeOpponent): + case (from == SpaceHomePlayer || from == SpaceHomeOpponent || from == SpaceBarPlayer || from == SpaceBarOpponent) && (to == SpaceBarPlayer || to == SpaceBarOpponent || to == SpaceHomePlayer || to == SpaceHomeOpponent): return 0 case to == SpaceHomePlayer: + if player == 2 { + return 0 + } + if b[SpaceVariant] == VariantTabula { + if (player == 1 && b[SpaceEnteredPlayer] == 0) || (player == 2 && b[SpaceEnteredOpponent] == 0) || !b.SecondHalf(player) { + return 0 + } + return 25 - from + } return from case to == SpaceHomeOpponent: + if player == 1 { + return 0 + } return 25 - from case from == SpaceHomePlayer || from == SpaceHomeOpponent: - if b[SpaceVariant] != VariantBackgammon { + if (player == 1 && from == SpaceHomeOpponent) || (player == 2 && from == SpaceHomePlayer) { + return 0 + } + switch b[SpaceVariant] { + case VariantAceyDeucey: if player == 1 && from == SpaceHomePlayer && b[SpaceEnteredPlayer] == 0 { return 25 - to } else if player == 2 && from == SpaceHomeOpponent && b[SpaceEnteredOpponent] == 0 { return to } + case VariantTabula: + if (player == 1 && from != SpaceHomePlayer && b[SpaceEnteredPlayer] == 0) || (player == 2 && from != SpaceHomeOpponent && b[SpaceEnteredOpponent] == 0) { + return 0 + } + return to } return 0 case from == SpaceBarPlayer: + if player == 2 { + return 0 + } + if b[SpaceVariant] == VariantTabula { + return to + } return 25 - to case from == SpaceBarOpponent: + if player == 1 { + return 0 + } return to default: diff := to - from @@ -151,7 +192,7 @@ func (b Board) spaceDiff(player int, from int8, to int8) int8 { } // HaveRoll returns whether the player has a sufficient die roll for the specified move. -func (b Board) HaveRoll(from int8, to int8, player int) bool { +func (b Board) HaveRoll(from int8, to int8, player int8) bool { barSpace := SpaceBarPlayer if player == 2 { barSpace = SpaceBarOpponent @@ -160,6 +201,10 @@ func (b Board) HaveRoll(from int8, to int8, player int) bool { return false } + if b[SpaceVariant] == VariantTabula && to > 12 && to < 25 && ((player == 1 && b[SpaceEnteredPlayer] == 0) || (player == 2 && b[SpaceEnteredOpponent] == 0)) { + return false + } + delta := b.spaceDiff(player, from, to) if delta == 0 { return false @@ -190,8 +235,8 @@ func (b Board) HaveRoll(from int8, to int8, player int) bool { return false } -// UseRoll uses a die roll. -func (b Board) UseRoll(from int8, to int8, player int) Board { +// UseRoll uses a die roll. UseRoll must be called before making a move. +func (b Board) UseRoll(from int8, to int8, player int8) Board { delta := b.spaceDiff(player, from, to) if delta == 0 { b.Print() @@ -250,7 +295,7 @@ func (b Board) UseRoll(from int8, to int8, player int) Board { return b } -func (b Board) _available(player int) [][2]int8 { +func (b Board) _available(player int8) [][2]int8 { homeSpace := SpaceHomePlayer barSpace := SpaceBarPlayer opponentBarSpace := SpaceBarOpponent @@ -272,12 +317,11 @@ func (b Board) _available(player int) [][2]int8 { } } } - for from := int8(0); from < 28; from++ { if from == SpaceHomePlayer || from == SpaceHomeOpponent || from == opponentBarSpace || checkers(player, b[from]) == 0 || (onBar && from != barSpace) { continue } - if player == 1 { + if player == 1 && b[SpaceVariant] != VariantTabula { for to := int8(0); to < from; to++ { if to == SpaceBarPlayer || to == SpaceBarOpponent || to == SpaceHomeOpponent || (to == SpaceHomePlayer && !mayBearOff) { continue @@ -290,11 +334,17 @@ func (b Board) _available(player int) [][2]int8 { } } else { // TODO clean up start := from + 1 - if from == SpaceBarOpponent { - start = 0 + if from == SpaceBarPlayer || from == SpaceBarOpponent { + start = 1 } - for to := start; to <= 25; to++ { - if to == SpaceBarPlayer || to == SpaceBarOpponent || to == SpaceHomeOpponent || (to == SpaceHomeOpponent && !mayBearOff) { + for i := start; i <= 25; i++ { + to := i + if player == 1 && to == SpaceHomeOpponent { + to = SpaceHomePlayer + } else if player == 2 && to == SpaceHomePlayer { + to = SpaceHomeOpponent + } + if to == SpaceBarPlayer || to == SpaceBarOpponent || (((player == 1 && to == SpaceHomePlayer) || (player == 2 && to == SpaceHomeOpponent)) && !mayBearOff) { continue } v := b[to] @@ -310,7 +360,7 @@ func (b Board) _available(player int) [][2]int8 { } // Available returns legal moves available. -func (b Board) Available(player int) ([][4][2]int8, []Board) { +func (b Board) Available(player int8) ([][4][2]int8, []Board) { var allMoves [][4][2]int8 resultMutex := &sync.Mutex{} @@ -330,7 +380,7 @@ func (b Board) Available(player int) ([][4][2]int8, []Board) { a := b._available(player) maxLen := 1 for _, move := range a { - newBoard := b.Move(move[0], move[1], player).UseRoll(move[0], move[1], player) + newBoard := b.UseRoll(move[0], move[1], player).Move(move[0], move[1], player) newAvailable := newBoard._available(player) if len(newAvailable) == 0 { moves := [4][2]int8{move} @@ -341,31 +391,35 @@ func (b Board) Available(player int) ([][4][2]int8, []Board) { continue } for _, move2 := range newAvailable { - newBoard2 := newBoard.Move(move2[0], move2[1], player).UseRoll(move2[0], move2[1], player) + newBoard2 := newBoard.UseRoll(move2[0], move2[1], player).Move(move2[0], move2[1], player) newAvailable2 := newBoard2._available(player) if len(newAvailable2) == 0 { moves := [4][2]int8{move, move2} if !movesFound(moves) { allMoves = append(allMoves, moves) boards = append(boards, newBoard2) - maxLen = 2 + if maxLen <= 2 { + maxLen = 2 + } } continue } for _, move3 := range newAvailable2 { - newBoard3 := newBoard2.Move(move3[0], move3[1], player).UseRoll(move3[0], move3[1], player) + newBoard3 := newBoard2.UseRoll(move3[0], move3[1], player).Move(move3[0], move3[1], player) newAvailable3 := newBoard3._available(player) if len(newAvailable3) == 0 { moves := [4][2]int8{move, move2, move3} if !movesFound(moves) { allMoves = append(allMoves, moves) boards = append(boards, newBoard3) - maxLen = 3 + if maxLen <= 2 { + maxLen = 3 + } } continue } for _, move4 := range newAvailable3 { - newBoard4 := newBoard3.Move(move4[0], move4[1], player).UseRoll(move4[0], move4[1], player) + newBoard4 := newBoard3.UseRoll(move4[0], move4[1], player).Move(move4[0], move4[1], player) moves := [4][2]int8{move, move2, move3, move4} if !movesFound(moves) { allMoves = append(allMoves, moves) @@ -379,6 +433,18 @@ func (b Board) Available(player int) ([][4][2]int8, []Board) { var newMoves [][4][2]int8 for i := 0; i < len(allMoves); i++ { l := 0 + if (allMoves[i][3][0] != 0 || allMoves[i][3][1] != 0) && allMoves[i][2][0] == 0 && allMoves[i][2][1] == 0 { + allMoves[i][2][0], allMoves[i][2][1] = allMoves[i][3][0], allMoves[i][3][1] + allMoves[i][2][0], allMoves[i][2][1] = 0, 0 + } + if (allMoves[i][2][0] != 0 || allMoves[i][2][1] != 0) && allMoves[i][1][0] == 0 && allMoves[i][1][1] == 0 { + allMoves[i][1][0], allMoves[i][1][1] = allMoves[i][2][0], allMoves[i][2][1] + allMoves[i][2][0], allMoves[i][2][1] = 0, 0 + } + if (allMoves[i][1][0] != 0 || allMoves[i][1][1] != 0) && allMoves[i][0][0] == 0 && allMoves[i][0][1] == 0 { + allMoves[i][0][0], allMoves[i][0][1] = allMoves[i][1][0], allMoves[i][1][1] + allMoves[i][1][0], allMoves[i][1][1] = 0, 0 + } for j := 0; j < 4; j++ { if allMoves[i][j][0] == 0 && allMoves[i][j][1] == 0 { break @@ -393,7 +459,9 @@ func (b Board) Available(player int) ([][4][2]int8, []Board) { } func (b Board) Past() bool { - if b[SpaceBarPlayer] != 0 || b[SpaceBarOpponent] != 0 { + if b[SpaceBarPlayer] != 0 || b[SpaceBarOpponent] != 0 || b[SpaceVariant] == VariantTabula { + return false + } else if b[SpaceVariant] == VariantAceyDeucey && ((b[SpaceEnteredPlayer] == 0 && b[SpaceHomePlayer] != 0) || (b[SpaceEnteredOpponent] == 0 && b[SpaceHomeOpponent] != 0)) { return false } var playerFirst, opponentLast int @@ -414,7 +482,42 @@ func (b Board) Past() bool { return playerFirst < opponentLast } -func (b Board) Pips(player int) int { +func (b Board) SecondHalf(player int8) bool { + if b[SpaceVariant] != VariantTabula { + return false + } + + switch player { + case 1: + if b[SpaceBarPlayer] != 0 { + return false + } else if b[SpaceEnteredPlayer] == 0 && b[SpaceHomePlayer] != 0 { + return false + } + case 2: + if b[SpaceBarOpponent] != 0 { + return false + } else if b[SpaceEnteredOpponent] == 0 && b[SpaceHomeOpponent] != 0 { + return false + } + default: + log.Panicf("unknown player: %d", player) + } + + for space := 1; space < 13; space++ { + if space == 13 { + log.Fatal("CHECK SPACE", space) + } + v := b[space] + if (player == 1 && v > 0) || (player == 2 && v < 0) { + return false + } + } + + return true +} + +func (b Board) Pips(player int8) int { var pips int if b[SpaceVariant] != VariantBackgammon { if player == 1 && b[SpaceEnteredPlayer] == 0 { @@ -434,7 +537,7 @@ func (b Board) Pips(player int) int { return pips } -func (b Board) Blots(player int) int { +func (b Board) Blots(player int8) int { o := opponent(player) var pips int for space := int8(1); space < 25; space++ { @@ -445,7 +548,7 @@ func (b Board) Blots(player int) int { return pips } -func (b Board) evaluate(player int, hitScore int, a *Analysis) { +func (b Board) evaluate(player int8, hitScore int, a *Analysis) { pips := b.Pips(player) score := float64(pips) var blots int @@ -460,7 +563,7 @@ func (b Board) evaluate(player int, hitScore int, a *Analysis) { a.hitScore = hitScore } -func (b Board) Evaluation(player int, hitScore int, moves [4][2]int8) *Analysis { +func (b Board) Evaluation(player int8, hitScore int, moves [4][2]int8) *Analysis { a := &Analysis{ Board: b, Moves: moves, @@ -549,7 +652,7 @@ func (b Board) Analyze(available [][4][2]int8, result *[]*Analysis) { } } - if b.StartingPosition(1) && b[SpaceRoll1] != b[SpaceRoll2] { + if b[SpaceVariant] != VariantTabula && b.StartingPosition(1) { r1, r2 := b[SpaceRoll1], b[SpaceRoll2] if r2 > r1 { r1, r2 = r2, r1 @@ -630,7 +733,7 @@ func (b Board) Analyze(available [][4][2]int8, result *[]*Analysis) { }) } -func (b Board) StartingPosition(player int) bool { +func (b Board) StartingPosition(player int8) bool { if player == 1 { return b[6] == 5 && b[8] == 3 && b[13] == 5 && b[24] == 2 } @@ -666,14 +769,14 @@ func (b Board) Print() { log.Printf("%+v", b) } -func opponent(player int) int { +func opponent(player int8) int8 { if player == 1 { return 2 } return 1 } -func spaceValue(player int, space int8) int { +func spaceValue(player int8, space int8) int { if space == SpaceHomePlayer || space == SpaceHomeOpponent || space == SpaceBarPlayer || space == SpaceBarOpponent { return 25 } else if player == 1 { @@ -683,7 +786,7 @@ func spaceValue(player int, space int8) int { } } -func PseudoPips(player int, space int8) int { +func PseudoPips(player int8, space int8) int { v := 6 + spaceValue(player, space) + int(math.Exp(float64(spaceValue(player, space))*0.2))*2 if space == SpaceHomePlayer || space == SpaceHomeOpponent || (player == 1 && (space > 6 || space == SpaceBarPlayer)) || (player == 2 && (space < 19 || space == SpaceBarOpponent)) { v += 24 diff --git a/board_test.go b/board_test.go index a001a37..1c7efa0 100644 --- a/board_test.go +++ b/board_test.go @@ -5,7 +5,7 @@ import ( "testing" ) -func TestMove(t *testing.T) { +func TestMoveBackgammon(t *testing.T) { b := NewBoard(VariantBackgammon) b[SpaceRoll1] = 1 b[SpaceRoll2] = 2 @@ -36,32 +36,245 @@ func TestMove(t *testing.T) { } } -func TestPast(t *testing.T) { +func TestMoveTabula(t *testing.T) { + { + b := NewBoard(VariantTabula) + b[SpaceRoll1] = 1 + b[SpaceRoll2] = 2 + + bc := b.Move(SpaceHomePlayer, 23, 1) + got, expected := bc[SpaceHomePlayer], int8(14) + if got != expected { + t.Errorf("unexpected space %d value: expected %d: got %d", SpaceHomePlayer, expected, got) + } + got, expected = bc[23], int8(1) + if got != expected { + t.Errorf("unexpected space %d value: expected %d: got %d", 23, expected, got) + } + bc = bc.Move(SpaceHomePlayer, 22, 1) + got, expected = bc[SpaceHomePlayer], int8(13) + if got != expected { + t.Errorf("unexpected space %d value: expected %d: got %d", SpaceHomePlayer, expected, got) + } + got, expected = bc[22], int8(1) + if got != expected { + t.Errorf("unexpected space %d value: expected %d: got %d", 22, expected, got) + } + } + + { + b := Board{0, 1, 2, 3, 4, 4, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15, 0, 0, 0, 0, 1, 0, 2} + b[SpaceRoll1] = 1 + b[SpaceRoll2] = 2 + + bc := b.UseRoll(1, 2, 1).Move(1, 2, 1) + got, expected := bc[1], int8(0) + if got != expected { + t.Errorf("unexpected space %d value: expected %d: got %d", 1, expected, got) + } + got, expected = bc[2], int8(3) + if got != expected { + t.Errorf("unexpected space %d value: expected %d: got %d", 2, expected, got) + } + + if !bc.HaveRoll(5, 7, 1) { + t.Errorf("unexpected have roll value: expected %v: got %v", false, true) + } + + if bc.HaveRoll(12, 13, 1) { + t.Errorf("unexpected have roll value: expected %v: got %v", false, true) + } + + if !bc.HaveRoll(12, 14, 1) { + t.Errorf("unexpected have roll value: expected %v: got %v", true, true) + } + } + + { + b := Board{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15, 0, 0, 0, 0, 0, 0, 2} + b[SpaceRoll1] = 1 + b[SpaceRoll2] = 2 + + if b.HaveRoll(12, 13, 1) { + t.Errorf("unexpected have roll value: expected %v: got %v", false, true) + } + + bc := b.UseRoll(0, 1, 1).Move(0, 1, 1) + got, expected := bc[0], int8(0) + if got != expected { + t.Errorf("unexpected space %d value: expected %d: got %d", 0, expected, got) + } + got, expected = bc[1], int8(1) + if got != expected { + t.Errorf("unexpected space %d value: expected %d: got %d", 1, expected, got) + } + + if bc.HaveRoll(12, 13, 1) { + t.Errorf("unexpected have roll value: expected %v: got %v", false, false) + } + + if !bc.HaveRoll(12, 14, 1) { + t.Errorf("unexpected have roll value: expected %v: got %v", true, false) + } + } + + { + b := Board{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15, 0, 0, 0, 0, 0, 0, 1, 0, 2} + b[SpaceRoll1] = 1 + b[SpaceRoll2] = 2 + + if !b.HaveRoll(12, 14, 1) { + t.Errorf("unexpected have roll value: expected %v: got %v", true, false) + } + + if b.MayBearOff(1) { + t.Errorf("unexpected bear off value: expected %v: got %v", false, true) + } + + b = b.Move(12, 14, 1).UseRoll(12, 14, 1) + got, expected := b[12], int8(0) + if got != expected { + t.Errorf("unexpected space %d value: expected %d: got %d", 12, expected, got) + } + got, expected = b[14], int8(1) + if got != expected { + t.Errorf("unexpected space %d value: expected %d: got %d", 14, expected, got) + } + + if !b.MayBearOff(1) { + t.Errorf("unexpected bear off value: expected %v: got %v", true, false) + } + + if !b.HaveRoll(14, 15, 1) { + t.Errorf("unexpected have roll value: expected %v: got %v", true, false) + } + + if b.HaveRoll(14, 16, 1) { + t.Errorf("unexpected have roll value: expected %v: got %v", false, true) + } + } + + { + b := Board{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -15, 0, 0, 0, 0, 0, 0, 1, 0, 2} + b[SpaceRoll1] = 1 + b[SpaceRoll2] = 2 + + if !b.HaveRoll(12, 14, 1) { + t.Errorf("unexpected have roll value: expected %v: got %v", true, false) + } + + if b.MayBearOff(1) { + t.Errorf("unexpected bear off value: expected %v: got %v", false, true) + } + + if b.HaveRoll(24, 0, 1) { + t.Errorf("unexpected have roll value: expected %v: got %v", false, true) + } + + b = b.UseRoll(12, 14, 1).Move(12, 14, 1) + got, expected := b[12], int8(0) + if got != expected { + t.Errorf("unexpected space %d value: expected %d: got %d", 12, expected, got) + } + got, expected = b[14], int8(1) + if got != expected { + t.Errorf("unexpected space %d value: expected %d: got %d", 14, expected, got) + } + + if !b.MayBearOff(1) { + t.Errorf("unexpected bear off value: expected %v: got %v", true, false) + } + + if !b.HaveRoll(24, 0, 1) { + t.Errorf("unexpected have roll value: expected %v: got %v", true, false) + } + } +} + +func TestPastBackgammon(t *testing.T) { b := NewBoard(VariantBackgammon) got, expected := b.Past(), false if got != expected { t.Errorf("unexpected past value: expected %v: got %v", expected, got) } - b = Board{0, 1, 3, -1, 0, -1, 0, -2, 0, 0, -1, 0, -3, 3, 0, 0, 0, -2, 0, -5, 4, 0, 2, 2, 0, 0, 0, 0, 5, 5, 5, 5} + b = Board{0, 1, 3, -1, 0, -1, 0, -2, 0, 0, -1, 0, -3, 3, 0, 0, 0, -2, 0, -5, 4, 0, 2, 2, 0, 0, 0, 0, 5, 5, 5, 5, 1, 1, 0} got, expected = b.Past(), false if got != expected { t.Errorf("unexpected past value: expected %v: got %v", expected, got) } - b = Board{0, -1, 1, -2, -1, 2, 4, 0, 0, 0, 0, 0, -1, 2, -1, 0, 0, -1, 3, -3, 0, 3, -1, -3, -1, 0, 0, 0, 4, 3, 0, 0} + b = Board{0, -1, 1, -2, -1, 2, 4, 0, 0, 0, 0, 0, -1, 2, -1, 0, 0, -1, 3, -3, 0, 3, -1, -3, -1, 0, 0, 0, 4, 3, 0, 0, 1, 1, 0} got, expected = b.Past(), 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} + 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, 1, 1, 0} got, expected = b.Past(), true if got != expected { t.Errorf("unexpected past value: expected %v: got %v", expected, got) } } +func TestPastAcey(t *testing.T) { + b := NewBoard(VariantAceyDeucey) + got, expected := b.Past(), false + if got != expected { + t.Errorf("unexpected past value: expected %v: got %v", expected, got) + } + + b = Board{0, 1, 3, -1, 0, -1, 0, -2, 0, 0, -1, 0, -3, 3, 0, 0, 0, -2, 0, -5, 4, 0, 2, 2, 0, 0, 0, 0, 5, 5, 5, 5, 1, 1, 1} + got, expected = b.Past(), false + if got != expected { + t.Errorf("unexpected past value: expected %v: got %v", expected, got) + } + + b = Board{0, -1, 1, -2, -1, 2, 4, 0, 0, 0, 0, 0, -1, 2, -1, 0, 0, -1, 3, -3, 0, 3, -1, -3, -1, 0, 0, 0, 4, 3, 0, 0, 1, 1, 1} + got, expected = b.Past(), 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, 0, 1, 1} + got, expected = b.Past(), 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, 1, 1, 1} + got, expected = b.Past(), true + if got != expected { + t.Errorf("unexpected past value: expected %v: got %v", expected, got) + } +} + +func TestPastTabula(t *testing.T) { + b := NewBoard(VariantTabula) + got, expected := b.Past(), false + if got != expected { + t.Errorf("unexpected past value: expected %v: got %v", expected, got) + } + + b = Board{0, 1, 3, -1, 0, -1, 0, -2, 0, 0, -1, 0, -3, 3, 0, 0, 0, -2, 0, -5, 4, 0, 2, 2, 0, 0, 0, 0, 5, 5, 5, 5, 1, 1, 2} + got, expected = b.Past(), false + if got != expected { + t.Errorf("unexpected past value: expected %v: got %v", expected, got) + } + + b = Board{0, -1, 1, -2, -1, 2, 4, 0, 0, 0, 0, 0, -1, 2, -1, 0, 0, -1, 3, -3, 0, 3, -1, -3, -1, 0, 0, 0, 4, 3, 0, 0, 1, 1, 2} + got, expected = b.Past(), 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, 1, 1, 2} + got, expected = b.Past(), false + if got != expected { + t.Errorf("unexpected past value: expected %v: got %v", expected, got) + } +} + func TestBlots(t *testing.T) { b := NewBoard(VariantBackgammon) got, expected := b.Blots(1), 0 diff --git a/cmd/tabula/main.go b/cmd/tabula/main.go index 4273d5d..37aa9cd 100644 --- a/cmd/tabula/main.go +++ b/cmd/tabula/main.go @@ -4,6 +4,7 @@ import ( "flag" "fmt" "log" + "os" "code.rocket9labs.com/tslocum/tabula" ) @@ -35,6 +36,32 @@ func main() { //b := tabula.Board{0, 0, -2, -2, -2, 4, 0, -1, 0, 0, -2, 4, 0, -2, -1, 0, -2, 4, 0, 2, 0, 0, 0, 0, -1, 0, 1, 0, 4, 1, 0, 0, 1, 1, 1} analysis := make([]*tabula.Analysis, 0, tabula.AnalysisBufferSize) + + // should be 3/1 then 5/0 in acey, then fix backgammon movement + // test cases for issues first then fix until tests pass + b := tabula.Board{0, 5, 2, 5, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, -2, -2, -2, -3, -3, -2, 0, 1, 0, 0, 0, 5, 2, 0, 0, 1, 1, 1} + log.Println(b[24]) + b.Print() + available, _ := b.Available(1) + for i := range available { + log.Println(available[i]) + } + b.Analyze(available, &analysis) + for i := range analysis { + log.Println(analysis[i]) + } + log.Println(b) + b = b.UseRoll(3, 1, 1).Move(3, 1, 1) + log.Println("NEW AVAILABLE") + log.Println(b) + available, _ = b.Available(1) + for i := range available { + log.Println(available[i]) + } + log.Println(b) + os.Exit(0) + + // Print opening moves. for r1 := 1; r1 <= 6; r1++ { for r2 := 1; r2 <= 6; r2++ { b := tabula.NewBoard(tabula.VariantBackgammon) diff --git a/go.mod b/go.mod index 1362015..2163a8b 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module code.rocket9labs.com/tslocum/tabula go 1.17 -require code.rocket9labs.com/tslocum/bei v0.0.0-20240107212214-bcc7b6933a4c +require code.rocket9labs.com/tslocum/bei v0.0.0-20240108012722-6db380cc190b diff --git a/go.sum b/go.sum index 3606e03..94f91b5 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,2 @@ -code.rocket9labs.com/tslocum/bei v0.0.0-20240107212214-bcc7b6933a4c h1:fiH72XLcHof1XvVeBw/1NvaS+iIzoZqCTn7Mv0AAYss= -code.rocket9labs.com/tslocum/bei v0.0.0-20240107212214-bcc7b6933a4c/go.mod h1:tS60/VNAJphKvDBkSLQhKALa15msIAuWWfEKNc4oFZc= +code.rocket9labs.com/tslocum/bei v0.0.0-20240108012722-6db380cc190b h1:Y0a14Kf/hSYepSmp4ZfDeE4CZZGBGBS97CNjCbKJm0c= +code.rocket9labs.com/tslocum/bei v0.0.0-20240108012722-6db380cc190b/go.mod h1:tS60/VNAJphKvDBkSLQhKALa15msIAuWWfEKNc4oFZc=