-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathboardNodeLearning.cpp
198 lines (182 loc) · 6.71 KB
/
boardNodeLearning.cpp
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#include "boardNodeLearning.hpp"
/**
* Constructs a child node
* @param parentBoard Board from parent node
* @param m Move made to get to this node from parent
*/
BoardNodeLearning::BoardNodeLearning(Board* parentBoard, Move m) : lastMove(BLACK) {
board = parentBoard->copy();
principalBoard = nullptr;
board->doMove(m);
lastMove = m;
sideToMove = !m.getSide();
children = vector<BoardNodeLearning*>();
}
/**
* Constructs a base node
* @param board Current board for the game
* @param ourSide The side that our player is on
*/
BoardNodeLearning::BoardNodeLearning(Board* board, bool ourSide) :
BoardNodeLearning(board, NULL_MOVE(!ourSide)) {
}
/**
* Deconstructs node
*/
BoardNodeLearning::~BoardNodeLearning(){
if (board) delete board;
if (principalBoard) delete principalBoard;
for(int i = 0; i < (int)children.size(); i++){
delete children[i];
}
children.clear();
}
/**
* Gets the move that was used to get from the previous board to this one
* @return Move that was used to get from previous board to this one
*/
Move BoardNodeLearning::getMove(){
return lastMove;
}
/**
* Gets the children of a node
* @return Vector of node's children
*/
vector<BoardNodeLearning*> BoardNodeLearning::getChildren(){
return children;
}
Board* BoardNodeLearning::getPrincipalBoard() {
if (principalBoard) {
return principalBoard->copy();
}
return nullptr;
}
/**
* Searches a tree using negamax and A/B pruning to find the heuristic score
* for this board
* @param depth How deep to search the node tree
* @param alpha The highest overall score found so far
* @param beta The opponent's best overall score found so far
* @param heuristic Heuristic function that defines the score of a board
* @return Score of the board accounting for future possible moves
*/
float BoardNodeLearning::searchTreeAB(int depth, float alpha, float beta,
Heuristic* heuristic){
if(depth == 0){
return heuristic->getScore(board, sideToMove);
}
vector<Move> possibleMoves = board->possibleMoves(sideToMove);
for(int i = 0; i < (int)possibleMoves.size(); i++){
children.push_back(new BoardNodeLearning(board, possibleMoves[i]));
float score = -children[i]->searchTreeAB(depth - 1, -beta, -alpha, heuristic);
alpha = max(alpha, score);
if(alpha >= beta) break;
}
for(int i = 0; i < (int)children.size(); i++){
delete children[i];
}
children.clear();
possibleMoves.clear();
return alpha;
}
/**
* Searches a tree using negamax and PVS pruning to find the heuristic score
* for this board
* @param depth How deep to search the node tree
* @param alpha The highest overall score found so far
* @param beta The opponent's best overall score found so far
* @param heuristic Heuristic function that defines the score of a board
* @return Score of the board accounting for future possible moves
*/
float BoardNodeLearning::searchTreePVS(int depth, float alpha, float beta,
Heuristic* heuristic){
if(depth == 0){
if (principalBoard) delete principalBoard;
principalBoard = board->copy();
return heuristic->getScore(board, sideToMove);
}
vector<Move> possibleMoves = board->possibleMoves(sideToMove);
if(depth > 4){
possibleMoves = sortMoves(possibleMoves, heuristic, 1);
}
for(int i = 0; i < (int)possibleMoves.size(); i++){
children.push_back(new BoardNodeLearning(board, possibleMoves[i]));
float score;
if(i == 0){
score = -children[i]->searchTreePVS(depth - 1, -beta, -alpha, heuristic);
if (principalBoard) delete principalBoard;
principalBoard = children[i]->getPrincipalBoard();
}
else{
score = -children[i]->searchTreePVS(depth - 1, -alpha-PVS_WINDOW, -alpha, heuristic);
if(alpha < score && score < beta){
score = -children[i]->searchTreePVS(depth - 1, -beta, -alpha, heuristic);
}
}
if (score > alpha) {
if (principalBoard) delete principalBoard;
principalBoard = children[i]->getPrincipalBoard();
alpha = score;
}
if(alpha >= beta) break;
}
for(int i = 0; i < (int)children.size(); i++){
delete children[i];
}
children.clear();
possibleMoves.clear();
return alpha;
}
/**
* Finds the best move to make this round using minimax and A/B pruning
* @param depth Depth to search in the node tree
* @param heuristic Heuristic function that defines the score for each position
* @param ourSide The side that you are playing on. Passed into heuristic function.
* @return The most optimal move based on the heuristic function
*/
Move BoardNodeLearning::getBestChoice(int depth, Heuristic* heuristic){
vector<Move> possibleMoves = board->possibleMoves(sideToMove);
for(int i = 0; i < (int)possibleMoves.size(); i++){
children.push_back(new BoardNodeLearning(board, possibleMoves[i]));
}
float alpha = -numeric_limits<float>::max();
float beta = numeric_limits<float>::max();
Move ret = NULL_MOVE(sideToMove);
for(int i = 0; i < (int)children.size(); i++){
float score = -children[i]->searchTreePVS(depth - 1, -beta, -alpha, heuristic);
if(score > alpha){
alpha = score;
if (principalBoard) delete principalBoard;
principalBoard = children[i]->getPrincipalBoard();
ret = children[i]->getMove();
}
}
for(int i = 0; i < (int)children.size(); i++){
delete children[i];
}
children.clear();
return ret;
}
/**
* Sorts a vector of moves by predicted heuristic value through a short negamax scan
* @param moves Vector of moves to sort
* @param heuristic Heuristic function to use while sorting
* @param depth How deep the search will go
* @return The sorted vector of moves
*/
vector<Move> BoardNodeLearning::sortMoves(vector<Move> moves, Heuristic* heuristic,
int depth){
vector<pair<float, int>> indexScores;
float alpha = -numeric_limits<float>::max();
float beta = numeric_limits<float>::max();
for(int i = 0; i < (int)moves.size(); i++){
BoardNodeLearning node = BoardNodeLearning(board, moves[i]);
indexScores.push_back(make_pair(node.searchTreeAB(depth, alpha, beta, heuristic), i));
}
std::sort(indexScores.begin(), indexScores.end());
vector<Move> sorted;
for(int i = 0; i < (int)indexScores.size(); i++){
sorted.push_back(moves[indexScores[i].second]);
}
return sorted;
}