-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathplayer.cpp
236 lines (201 loc) · 7.11 KB
/
player.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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#include "player.hpp"
/*
* Constructor for the player; initialize everything here. The side your AI is
* on (BLACK or WHITE) is passed in as "side". The constructor must finish
* within 30 seconds.
*/
Player::Player(Side side)
{
// Will be set to true in test_minimax.cpp.
testingMinimax = false;
board = new Board();
this->side = side;
if (side == BLACK)
{
opponentsSide = WHITE;
}
else if (side == WHITE)
{
opponentsSide = BLACK;
}
}
/*
* Destructor for the player.
*/
Player::~Player()
{
delete board;
}
/**
* Accessor function for test_minimax.cpp used to set the initial board state.
*
* @param data The data array that holds the initial board state.
*/
void Player::setBoard(char data[])
{
board->setBoard(data);
}
/*
* Compute the next move given the opponent's last move. Your AI is
* expected to keep track of the board on its own. If this is the first move,
* or if the opponent passed on the last move, then opponentsMove will be
* nullptr.
*
* msLeft represents the time your AI has left for the total game, in
* milliseconds. doMove() must take no longer than msLeft, or your AI will
* be disqualified! An msLeft value of -1 indicates no time limit.
*
* The move returned must be legal; if there are no valid moves for your side,
* return nullptr.
*/
Move *Player::doMove(Move *opponentsMove, int msLeft)
{
// Mark the opponent's move on the board.
board->doMove(opponentsMove, opponentsSide);
// Check whether there are valid moves left.
if (!board->hasMoves(side))
{
return nullptr;
}
Move *bestMove = nullptr;
int bestValue = INT_MIN;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
Move *move = new Move(i, j);
if (board->checkMove(move, side))
{
Board *boardCopy = board->copy();
boardCopy->doMove(move, side);
// Use depth 2 for the minimax test and depth 6 in real
// gameplay.
int depth = testingMinimax ? 2 : 6;
// no more bad_alloc, we just stop the recursion if it's too much memory used
int currentValue = minimax(boardCopy, depth, INT_MIN, INT_MAX, opponentsSide);
if(currentValue > bestValue || bestValue == INT_MIN) {
bestValue = currentValue;
bestMove = new Move(i, j);
delete move;
}
delete boardCopy;
}
else
{
delete move;
}
}
}
board->doMove(bestMove, side);
return bestMove;
}
/**
* @brief Use the minimax algorithm with alpha-beta pruning to assign a
* heuristic value to a node (board state). At non-terminal nodes of maximum
* search depth, a heuristic value is determined by using one of two possible
* heuristic functions, depending on whether the algorithm is being tested.
*
* @param board The board state (node)
* @param depth The depth of the node (how many levels of nodes below this node
* to search)
* @param side The side of the player whose turn it is
* @param alpha The maximum heuristic value that the maximizing player is
* assured of
* @param beta The minimum heuristic value that the minimizing player is assured
* of
*
* @return A heuristic value that represents the favorability of the node, where
* larger values denote more favorable odds for this player and smaller values
* denote less favorable odds for this player.
*/
int Player::minimax(Board *board, int depth, int alpha, int beta, Side side)
{
if (depth == 0 || board->isDone())
{
if (testingMinimax)
{
// Use the standard heuristic for the minimax test that is the
// difference of the number of stones this player has and the
// number of stones the opponent has.
return board->count(this->side) - board->count(this->opponentsSide);
}
// Otherwise, use the special heuristic implemented in board.cpp.
return board->getHeuristic(this->side, this->opponentsSide);
}
// Determine the side of the opponent of the player whose turn it is.
Side opponentsSide = (side == BLACK) ? WHITE : BLACK;
if (this->side == side)
{
// It is the turn of this player, the maximizing player in minimax.
int bestValue = INT_MIN;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
Move *move = new Move(i, j);
if (board->checkMove(move, side))
{
// no more bad_alloc hopefully
try {
Board *boardCopy = board->copy();
boardCopy->doMove(move, side);
int v = minimax(boardCopy, depth - 1, alpha, beta, \
opponentsSide);
delete boardCopy;
bestValue = (v > bestValue) ? v : bestValue;
}
catch (std::bad_alloc) {
delete move;
return bestValue;
}
alpha = (alpha > bestValue) ? alpha : bestValue;
if (beta <= alpha)
{
// This node and all of its child nodes do not need
// to be examined.
break;
}
}
delete move;
}
}
return bestValue;
}
else
{
// It is the turn of the opponent, the minimizing player in minimax.
int bestValue = INT_MAX;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
Move *move = new Move(i, j);
if (board->checkMove(move, side))
{
// no more bad_alloc hopefully
try {
Board *boardCopy = board->copy();
boardCopy->doMove(move, side);
int v = minimax(boardCopy, depth - 1, alpha, beta, \
opponentsSide);
delete boardCopy;
bestValue = (v < bestValue) ? v : bestValue;
}
catch (std::bad_alloc) {
delete move;
return bestValue;
}
beta = (beta < bestValue) ? beta : bestValue;
if (beta <= alpha)
{
// This node and all of its child nodes do not need
// to be examined.
break;
}
}
delete move;
}
}
return bestValue;
}
}