-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPerfectAgent.cpp
More file actions
90 lines (68 loc) · 2.3 KB
/
Copy pathPerfectAgent.cpp
File metadata and controls
90 lines (68 loc) · 2.3 KB
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
#include "PerfectAgent.h"
Position::position_t PerfectAgent::play(Position &P) {
const Position::position_t possible_moves = P.possible_moves();
Position::position_t best_move;
/**
* It is important to keep track of alpha (our lowest guaranteed score at the current position).
* We pass alpha to the negamax function so it can prune branches faster, resulting in
* a significantly faster search time. The reduction is often 50%!
*/
int alpha = -Position::MAX_SCORE;
for (int cell_num : move_order) {
Position::position_t move = possible_moves & Position::cell_mask(cell_num);
if (!move) continue;
P.play(move);
int score = -negamax(P, Position::MIN_SCORE, -alpha);
P.undo(move);
if (score > alpha) {
alpha = score;
best_move = move;
}
}
return best_move;
}
int PerfectAgent::evaluate(Position &P) {
return negamax(P, Position::MIN_SCORE, Position::MAX_SCORE);
}
int PerfectAgent::negamax(Position &P, int alpha, int beta) {
++nodes_visited;
// Try to retrieve the entry from the transposition table.
std::pair<uint8_t, uint8_t> retrieved = transTable.get(P.key());
// Use the entry only if it exists and its depth is at least the current depth.
// The depth check is important to determine whether the entry is from the current search.
if (retrieved.first && retrieved.second >= P.num_moves()) {
// Derive the actual score from the normalized score.
int actual_value = retrieved.first + Position::MIN_SCORE - 1;
beta = std::min(beta, actual_value);
if (alpha >= beta) {
return actual_value;
}
}
/**
* Check for game end conditions.
*/
if (P.winning()) {
return P.score();
}
if (P.opp_winning()) {
return -P.score();
}
// If the position is not a win or loss but is full, then it must be a draw.
if (P.full()) {
return 0;
}
const Position::position_t possible_moves = P.possible_moves();
for (int cell_num : move_order) {
Position::position_t move = possible_moves & Position::cell_mask(cell_num);
if (!move) continue;
P.play(move);
int value = -negamax(P, -beta, -alpha);
P.undo(move);
alpha = std::max(alpha, value);
if (alpha >= beta) {
break;
}
}
transTable.put(P.key(), alpha - Position::MIN_SCORE + 1, P.num_moves());
return alpha;
}