Skip to content

Commit f51abf5

Browse files
committed
restructuring of project and adding of engine comparision
1 parent 92fd28e commit f51abf5

20 files changed

+309
-54
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
# the compiled main module
22
main
3+
4+
# the compiled libraries
5+
libgoengine.h
6+
libgoengine.so
7+
goengine
8+
ctypes

benchmarks.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
# Execute the benchmarks
6+
go test -bench=. -run=^$ ./...

build.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Execute the tests
5+
go test ./...
6+
7+
# Evaluate the test results
8+
# If the tests fail, exit with an error code
9+
if [ $? -ne 0 ]; then
10+
echo "Tests failed. Exiting build process."
11+
exit 1
12+
fi
13+
14+
# Build main package (CLI/game)
15+
go build -o goengine ./cmd/main.go
16+
17+
# Build shared library from export/export.go
18+
go build -buildmode=c-shared -o libgoengine.so ./export/export.go

cmd/engine/alpha_beta_engine_benchmark_test.go

Lines changed: 0 additions & 47 deletions
This file was deleted.

main.go renamed to cmd/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import (
88
"time"
99
"unicode"
1010

11-
"github.com/RubikNube/GoInGo/cmd/engine"
12-
"github.com/RubikNube/GoInGo/cmd/game"
11+
"github.com/RubikNube/GoInGo/pkg/engine"
12+
"github.com/RubikNube/GoInGo/pkg/game"
1313
"github.com/jroimartin/gocui"
1414
)
1515

compare_engines.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env python3
2+
from ctypes import cdll, c_uint, c_int
3+
4+
lib = cdll.LoadLibrary('./libgoengine.so')
5+
6+
# Create engines and board
7+
engineA = lib.NewAlphaBetaEngine()
8+
engineB = lib.NewRandomEngine()
9+
board = lib.NewBoard(c_int(9))
10+
11+
# Call CompareEngines with valid handles
12+
result = lib.CompareEngines(
13+
c_uint(engineA),
14+
c_uint(engineB),
15+
c_uint(board),
16+
c_int(1), # firstPlayer
17+
c_int(100) # maxMoves
18+
)
19+
20+
# Print the result and name the winner
21+
if result == 1:
22+
print("Winner: Engine A (Alpha-Beta)")
23+
elif result == 2:
24+
print("Winner: Engine B (Random)")
25+
elif result == 0:
26+
print("Result: Draw")

export/export.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package main
2+
3+
/*
4+
#include <stdint.h>
5+
*/
6+
import "C"
7+
import (
8+
"sync"
9+
10+
"github.com/RubikNube/GoInGo/pkg/compareengines"
11+
"github.com/RubikNube/GoInGo/pkg/engine"
12+
"github.com/RubikNube/GoInGo/pkg/game"
13+
)
14+
15+
var (
16+
engineRegistry = struct {
17+
sync.Mutex
18+
nextID uint64
19+
objects map[uint64]engine.Engine
20+
}{objects: make(map[uint64]engine.Engine)}
21+
boardRegistry = struct {
22+
sync.Mutex
23+
nextID uint64
24+
objects map[uint64]*game.Board
25+
}{objects: make(map[uint64]*game.Board)}
26+
)
27+
28+
//export NewAlphaBetaEngine
29+
func NewAlphaBetaEngine() C.uint64_t {
30+
engineRegistry.Lock()
31+
defer engineRegistry.Unlock()
32+
e := engine.NewAlphaBetaEngine()
33+
id := engineRegistry.nextID
34+
engineRegistry.nextID++
35+
engineRegistry.objects[id] = e
36+
return C.uint64_t(id)
37+
}
38+
39+
//export NewRandomEngine
40+
func NewRandomEngine() C.uint64_t {
41+
engineRegistry.Lock()
42+
defer engineRegistry.Unlock()
43+
e := engine.NewRandomEngine()
44+
id := engineRegistry.nextID
45+
engineRegistry.nextID++
46+
engineRegistry.objects[id] = e
47+
return C.uint64_t(id)
48+
}
49+
50+
//export NewBoard
51+
func NewBoard(size C.int) C.uint64_t {
52+
boardRegistry.Lock()
53+
defer boardRegistry.Unlock()
54+
b := game.NewBoard()
55+
id := boardRegistry.nextID
56+
boardRegistry.nextID++
57+
boardRegistry.objects[id] = &b
58+
return C.uint64_t(id)
59+
}
60+
61+
//export CompareEngines
62+
func CompareEngines(engineAID, engineBID, boardID C.uint64_t, firstPlayer C.int, maxMoves C.int) C.int {
63+
engineRegistry.Lock()
64+
engineA := engineRegistry.objects[uint64(engineAID)]
65+
engineB := engineRegistry.objects[uint64(engineBID)]
66+
engineRegistry.Unlock()
67+
boardRegistry.Lock()
68+
board := boardRegistry.objects[uint64(boardID)]
69+
boardRegistry.Unlock()
70+
if engineA == nil || engineB == nil || board == nil {
71+
return -1 // error code
72+
}
73+
result := compareengines.CompareEngines(engineA, engineB, *board, game.FieldState(firstPlayer), int(maxMoves))
74+
if result == 0 {
75+
return 0 // draw
76+
} else if result > 0 {
77+
return 1 // engineA wins
78+
} else {
79+
return 2 // engineB wins
80+
}
81+
}
82+
83+
func main() {}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package engine
33
import (
44
"sort"
55

6-
"github.com/RubikNube/GoInGo/cmd/game"
6+
"github.com/RubikNube/GoInGo/pkg/game"
77
)
88

99
// AlphaBetaEngine implements Engine using alpha-beta pruning with killer move heuristic.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package engine
2+
3+
import (
4+
"testing"
5+
6+
"github.com/RubikNube/GoInGo/pkg/game"
7+
)
8+
9+
func BenchmarkAlphaBetaEngine_EmptyBoard(b *testing.B) {
10+
engine := NewAlphaBetaEngine()
11+
board := emptyBoard()
12+
for i := 0; i < b.N; i++ {
13+
engine.Move(board, game.Black, nil)
14+
}
15+
}
16+
17+
func BenchmarkAlphaBetaEngine_MidGame(b *testing.B) {
18+
engine := NewAlphaBetaEngine()
19+
board := midGameBoard()
20+
for i := 0; i < b.N; i++ {
21+
engine.Move(board, game.White, nil)
22+
}
23+
}

0 commit comments

Comments
 (0)