-
Notifications
You must be signed in to change notification settings - Fork 0
/
alphaBetaPruning.go
133 lines (117 loc) · 3.1 KB
/
alphaBetaPruning.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// package alpha-beta-pruning gives functions to perform
// iterative alpha beta pruning search on a Board structure
package main
import (
"container/list"
"time"
)
const PosInfty int = 2147483647
const NegInfty int = -2147483648
const MaxPlayer bool = true
const MinPlayer bool = false
var semaphore bool = false
type AlphaBetaPruning struct {
moves list.List
timeout int
}
// creates a new Structure AlphaBetaPruning with a depth and timeout value
func NewAlphaBetaPruning(time int) *AlphaBetaPruning {
return new(AlphaBetaPruning).Init(time)
}
// Initialize a structure AlphaBetaPruning
func (abp *AlphaBetaPruning) Init(time int) *AlphaBetaPruning {
abp.timeout = time
return abp
}
// Evaluate function execute a deepening search from a board and
// returns the best move among the iterations according to heuristic
func (abp *AlphaBetaPruning) Evaluate(position *Board) *Action {
var finalPosition, lastPosition *Action
// go routine that stops the program after timeout using a boolean semaphore
go func(timeout int) {
time.Sleep(time.Duration(timeout) * time.Second)
semaphore = true
}(abp.timeout)
for i := 4; !semaphore; i++ {
lastPosition = abp.alphabeta(position, i, NegInfty, PosInfty)
if !semaphore {
finalPosition = lastPosition
}
}
return finalPosition
}
// takes the minimum value between a and b
func min(a, b int) int {
if a <= b {
return a
}
return b
}
// takes the maximum value between a and b
func max(a, b int) int {
if a >= b {
return a
}
return b
}
// alphabeta is a recursive function that takes a current board, depth, alpha and beta values and
// returns the best Action according to heuristic
func (abp *AlphaBetaPruning) alphabeta(position *Board, depth, alpha, beta int) *Action {
moveList := position.GetMoves()
var moveValue int
lastAction := NewAction(0, 0)
if depth == 0 {
moveValue = Evaluate(position)
lastAction.SetValue(moveValue)
return lastAction
}
if moveList.Front() == nil {
moveValue = Evaluate(position)
lastAction.SetValue(moveValue)
lastAction.SetPassMove(true)
return lastAction
}
var nextBoard *Board
if position.GetMaxPlayer() {
moveValue = NegInfty
lastAction.SetValue(moveValue)
for e := moveList.Front(); e != nil; e = e.Next() {
if semaphore {
return NewAction(0, 0)
}
move := e.Value.(*Action)
nextBoard = position.MakeMove(move)
moveValue = max(moveValue, abp.alphabeta(nextBoard, depth-1, alpha, beta).GetValue())
if alpha >= beta {
break
}
if alpha < moveValue {
lastAction = move
alpha = moveValue
}
}
lastAction.SetValue(moveValue)
return lastAction
} else {
moveValue = PosInfty
lastAction := NewAction(0, 0)
lastAction.SetValue(moveValue)
for e := moveList.Front(); e != nil; e = e.Next() {
if semaphore {
return NewAction(0, 0)
}
move := e.Value.(*Action)
nextBoard = position.MakeMove(move)
moveValue = min(moveValue, abp.alphabeta(nextBoard, depth-1, alpha, beta).GetValue())
if beta <= alpha {
break
}
if beta > moveValue {
beta = moveValue
lastAction = move
}
}
lastAction.SetValue(moveValue)
return lastAction
}
}