|
| 1 | +// Package compareengines provides functionality to compare two Go engines by playing a game. |
| 2 | +package compareengines |
| 3 | + |
| 4 | +import ( |
| 5 | + "github.com/RubikNube/GoInGo/pkg/engine" |
| 6 | + "github.com/RubikNube/GoInGo/pkg/game" |
| 7 | +) |
| 8 | + |
| 9 | +// CompareEngines lets two engines play against each other and returns the winner. |
| 10 | +// Returns: 1 if engineA wins, -1 if engineB wins, 0 for draw. |
| 11 | +func CompareEngines(engineA, engineB engine.Engine, board game.Board, firstPlayer game.FieldState, maxMoves int) int { |
| 12 | + player := firstPlayer |
| 13 | + var ko *game.Point |
| 14 | + moveCount := 0 |
| 15 | + passCount := 0 |
| 16 | + for moveCount < maxMoves && passCount < 2 { |
| 17 | + var move *game.Point |
| 18 | + if player == firstPlayer { |
| 19 | + move = engineA.Move(board, player, ko) |
| 20 | + } else { |
| 21 | + move = engineB.Move(board, player, ko) |
| 22 | + } |
| 23 | + if move == nil { |
| 24 | + passCount++ |
| 25 | + } else { |
| 26 | + passCount = 0 |
| 27 | + board[move.Row][move.Col] = player |
| 28 | + ko = nil // Optionally update ko if needed |
| 29 | + } |
| 30 | + player = opponent(player) |
| 31 | + moveCount++ |
| 32 | + } |
| 33 | + score := evaluate(board, firstPlayer, opponent(firstPlayer)) |
| 34 | + if score > 0 { |
| 35 | + return 1 |
| 36 | + } else if score < 0 { |
| 37 | + return -1 |
| 38 | + } |
| 39 | + return 0 |
| 40 | +} |
| 41 | + |
| 42 | +// opponent returns the opposite FieldState (Black <-> White). |
| 43 | +func opponent(player game.FieldState) game.FieldState { |
| 44 | + if player == game.Black { |
| 45 | + return game.White |
| 46 | + } |
| 47 | + return game.Black |
| 48 | +} |
| 49 | + |
| 50 | +// evaluate returns a score for the board from the perspective of 'player'. |
| 51 | +// Positive means advantage for 'player', negative for opponent. |
| 52 | +func evaluate(board game.Board, player, opp game.FieldState) int { |
| 53 | + playerStones, oppStones := 0, 0 |
| 54 | + playerLibs, oppLibs := 0, 0 |
| 55 | + visited := make(map[game.Point]bool) |
| 56 | + for i := 0; i < 9; i++ { |
| 57 | + for j := 0; j < 9; j++ { |
| 58 | + pt := game.Point{Row: int8(i), Col: int8(j)} |
| 59 | + if visited[pt] || board[i][j] == game.Empty { |
| 60 | + continue |
| 61 | + } |
| 62 | + group, libs := game.Group(board, pt) |
| 63 | + for stone := range group { |
| 64 | + visited[stone] = true |
| 65 | + } |
| 66 | + if board[i][j] == player { |
| 67 | + playerStones += len(group) |
| 68 | + playerLibs += len(libs) |
| 69 | + } else if board[i][j] == opp { |
| 70 | + oppStones += len(group) |
| 71 | + oppLibs += len(libs) |
| 72 | + } |
| 73 | + } |
| 74 | + } |
| 75 | + return (playerStones-oppStones)*10 + (playerLibs-oppLibs)*2 |
| 76 | +} |
0 commit comments