My Project
endgame.c
Go to the documentation of this file.
1
12#include "search.h"
13
14#include "bit.h"
15#include "settings.h"
16#include "stats.h"
17#include "ybwc.h"
18
19#include <assert.h>
20
30static inline int board_solve(const Board *board, const int n_empties)
31{
32 const int n_discs_p = bit_count(board->player);
33 const int n_discs_o = 64 - n_empties - n_discs_p;
34 register int score = n_discs_p - n_discs_o;
35
37
38 if (score < 0) score -= n_empties;
39 else if (score > 0) score += n_empties;
40
41 return score;
42}
43
52int search_solve(const Search *search)
53{
54 return board_solve(search->board, search->n_empties);
55}
56
65static inline int board_solve_0(const Board *board)
66{
68
69 return 2 * bit_count(board->player) - SCORE_MAX;
70}
71
80int search_solve_0(const Search *search)
81{
82 return board_solve_0(search->board);
83}
84
96inline int board_score_1(const Board *board, const int beta, const int x)
97{
98 register int score, n_flips;
99
100 score = 2 * bit_count(board->opponent) - SCORE_MAX;
101
102 if ((n_flips = count_last_flip(x, board->player)) != 0) {
103 score -= n_flips;
104 } else {
105 if (score >= 0) {
106 score += 2;
107 if (score < beta) { // lazy cut-off
108 n_flips = count_last_flip(x, board->opponent);
109 score += n_flips;
110 }
111 } else {
112 if (score < beta) { // lazy cut-off
113 if ((n_flips = count_last_flip(x, board->opponent)) != 0) {
114 score += n_flips + 2;
115 }
116 }
117 }
118 }
119
120 return score;
121}
122
135static int board_solve_2(Board *board, int alpha, const int x1, const int x2, Search *search)
136{
137 Board next[1];
138 register int score, bestscore;
139 const int beta = alpha + 1;
140
143
144 if ((NEIGHBOUR[x1] & board->opponent) && board_next(board, x1, next)) {
146 bestscore = board_score_1(next, beta, x2);
147 } else bestscore = -SCORE_INF;
148
149 if (bestscore < beta) {
150 if ((NEIGHBOUR[x2] & board->opponent) && board_next(board, x2, next)) {
152 score = board_score_1(next, beta, x1);
153 if (score > bestscore) bestscore = score;
154 }
155
156 // pass
157 if (bestscore == -SCORE_INF) {
158
159 if ((NEIGHBOUR[x1] & board->player) && board_pass_next(board, x1, next)) {
161 bestscore = -board_score_1(next, -alpha, x2);
162 } else bestscore = SCORE_INF;
163
164 if (bestscore > alpha) {
165 if ((NEIGHBOUR[x2] & board->player) && board_pass_next(board, x2, next)) {
167 score = -board_score_1(next, -alpha, x1);
168 if (score < bestscore) bestscore = score;
169 }
170 // gameover
171 if (bestscore == SCORE_INF) bestscore = board_solve(board, 2);
172 }
173 }
174 }
175
176 assert(SCORE_MIN <= bestscore && bestscore <= SCORE_MAX);
177 assert((bestscore & 1) == 0);
178 return bestscore;
179}
180
190static int search_solve_3(Search *search, const int alpha)
191{
192 Board *board = search->board;
193 Board next[1];
194 SquareList *empty = search->empties->next;
195 int x1 = empty->x;
196 int x2 = (empty = empty->next)->x;
197 int x3 = empty->next->x;
198 register int score, bestscore;
199 const int beta = alpha + 1;
200
203
204 // parity based move sorting
205 if (!(search->parity & QUADRANT_ID[x1])) {
206 if (search->parity & QUADRANT_ID[x2]) { // case 1(x2) 2(x1 x3)
207 int tmp = x1; x1 = x2; x2 = tmp;
208 } else { // case 1(x3) 2(x1 x2)
209 int tmp = x1; x1 = x3; x3 = x2; x2 = tmp;
210 }
211 }
212
213 // best move alphabeta search
214 if ((NEIGHBOUR[x1] & board->opponent) && board_next(board, x1, next)) {
215 bestscore = -board_solve_2(next, -beta, x2, x3, search);
216 if (bestscore >= beta) return bestscore;
217 } else bestscore = -SCORE_INF;
218
219 if ((NEIGHBOUR[x2] & board->opponent) && board_next(board, x2, next)) {
220 score = -board_solve_2(next, -beta, x1, x3, search);
221 if (score >= beta) return score;
222 else if (score > bestscore) bestscore = score;
223 }
224
225 if ((NEIGHBOUR[x3] & board->opponent) && board_next(board, x3, next)) {
226 score = -board_solve_2(next, -beta, x1, x2, search);
227 if (score > bestscore) bestscore = score;
228 }
229
230 // pass ?
231 if (bestscore == -SCORE_INF) {
232 // best move alphabeta search
233 if ((NEIGHBOUR[x1] & board->player) && board_pass_next(board, x1, next)) {
234 bestscore = board_solve_2(next, alpha, x2, x3, search);
235 if (bestscore <= alpha) return bestscore;
236 } else bestscore = SCORE_INF;
237
238 if ((NEIGHBOUR[x2] & board->player) && board_pass_next(board, x2, next)) {
239 score = board_solve_2(next, alpha, x1, x3, search);
240 if (score <= alpha) return score;
241 else if (score < bestscore) bestscore = score;
242 }
243
244 if ((NEIGHBOUR[x3] & board->player) && board_pass_next(board, x3, next)) {
245 score = board_solve_2(next, alpha, x1, x2, search);
246 if (score < bestscore) bestscore = score;
247 }
248
249 // gameover
250 if (bestscore == SCORE_INF) bestscore = board_solve(board, 3);
251 }
252
253 assert(SCORE_MIN <= bestscore && bestscore <= SCORE_MAX);
254 return bestscore;
255}
256
266static int search_solve_4(Search *search, const int alpha)
267{
268 Board *board = search->board;
269 SquareList *empty = search->empties->next;
270 int x1 = empty->x;
271 int x2 = (empty = empty->next)->x;
272 int x3 = (empty = empty->next)->x;
273 int x4 = empty->next->x;
274 Move move[1];
275 int score, bestscore;
276 const int beta = alpha + 1;
277
280
281 // stability cutoff
282 if (search_SC_NWS(search, alpha, &score)) return score;
283
284 // parity based move sorting.
285 // The following hole sizes are possible:
286 // 4 - 1 3 - 2 2 - 1 1 2 - 1 1 1 1
287 // Only the 1 1 2 case needs move sorting.
288 if (!(search->parity & QUADRANT_ID[x1])) {
289 if (search->parity & QUADRANT_ID[x2]) {
290 if (search->parity & QUADRANT_ID[x3]) { // case 1(x2) 1(x3) 2(x1 x4)
291 int tmp = x1; x1 = x2; x2 = x3; x3 = tmp;
292 } else { // case 1(x2) 1(x4) 2(x1 x3)
293 int tmp = x1; x1 = x2; x2 = x4; x4 = x3; x3 = tmp;
294 }
295 } else if (search->parity & QUADRANT_ID[x3]) { // case 1(x3) 1(x4) 2(x1 x2)
296 int tmp = x1; x1 = x3; x3 = tmp; tmp = x2; x2 = x4; x4 = tmp;
297 }
298 } else {
299 if (!(search->parity & QUADRANT_ID[x2])) {
300 if (search->parity & QUADRANT_ID[x3]) { // case 1(x1) 1(x3) 2(x2 x4)
301 int tmp = x2; x2 = x3; x3 = tmp;
302 } else { // case 1(x1) 1(x4) 2(x2 x3)
303 int tmp = x2; x2 = x4; x4 = x3; x3 = tmp;
304 }
305 }
306 }
307
308 // best move alphabeta search
309 if ((NEIGHBOUR[x1] & board->opponent) && board_get_move(board, x1, move)) {
310 search_update_endgame(search, move);
311 bestscore = -search_solve_3(search, -beta);
312 search_restore_endgame(search, move);
313 if (bestscore >= beta) return bestscore;
314 } else bestscore = -SCORE_INF;
315
316 if ((NEIGHBOUR[x2] & board->opponent) && board_get_move(board, x2, move)) {
317 search_update_endgame(search, move);
318 score = -search_solve_3(search, -beta);
319 search_restore_endgame(search, move);
320 if (score >= beta) return score;
321 else if (score > bestscore) bestscore = score;
322 }
323
324 if ((NEIGHBOUR[x3] & board->opponent) && board_get_move(board, x3, move)) {
325 search_update_endgame(search, move);
326 score = -search_solve_3(search, -beta);
327 search_restore_endgame(search, move);
328 if (score >= beta) return score;
329 else if (score > bestscore) bestscore = score;
330 }
331
332 if ((NEIGHBOUR[x4] & board->opponent) && board_get_move(board, x4, move)) {
333 search_update_endgame(search, move);
334 score = -search_solve_3(search, -beta);
335 search_restore_endgame(search, move);
336 if (score > bestscore) bestscore = score;
337 }
338
339 // no move
340 if (bestscore == -SCORE_INF) {
341 if (can_move(board->opponent, board->player)) { // pass
342 search_pass_endgame(search);
343 bestscore = -search_solve_4(search, -beta);
344 search_pass_endgame(search);
345 } else { // gameover
346 bestscore = search_solve(search);
347 }
348 }
349
350 assert(SCORE_MIN <= bestscore && bestscore <= SCORE_MAX);
351 return bestscore;
352}
353
366static int search_shallow(Search *search, const int alpha)
367{
368 Board *board = search->board;
369 SquareList *empty;
370 Move move;
371 int score, bestscore = -SCORE_INF;
372 const int beta = alpha + 1;
373
374 assert(SCORE_MIN <= alpha && alpha <= SCORE_MAX);
375 assert(0 <= search->n_empties && search->n_empties <= DEPTH_TO_SHALLOW_SEARCH);
376
379
380 // stability cutoff
381 if (search_SC_NWS(search, alpha, &score)) return score;
382
383 if (search->parity > 0 && search->parity < 15) {
384
385 foreach_odd_empty (empty, search->empties, search->parity) {
386 if ((NEIGHBOUR[empty->x] & board->opponent)
387 && board_get_move(board, empty->x, &move)) {
388 search_update_endgame(search, &move);
389 if (search->n_empties == 4) score = -search_solve_4(search, -beta);
390 else score = -search_shallow(search, -beta);
391 search_restore_endgame(search, &move);
392 if (score >= beta) return score;
393 else if (score > bestscore) bestscore = score;
394 }
395 }
396
397 foreach_even_empty (empty, search->empties, search->parity) {
398 if ((NEIGHBOUR[empty->x] & board->opponent)
399 && board_get_move(board, empty->x, &move)) {
400 search_update_endgame(search, &move);
401 if (search->n_empties == 4) score = -search_solve_4(search, -beta);
402 else score = -search_shallow(search, -beta);
403 search_restore_endgame(search, &move);
404 if (score >= beta) return score;
405 else if (score > bestscore) bestscore = score;
406 }
407 }
408 } else {
409 foreach_empty (empty, search->empties) {
410 if ((NEIGHBOUR[empty->x] & board->opponent)
411 && board_get_move(board, empty->x, &move)) {
412 search_update_endgame(search, &move);
413 if (search->n_empties == 4) score = -search_solve_4(search, -beta);
414 else score = -search_shallow(search, -beta);
415 search_restore_endgame(search, &move);
416 if (score >= beta) return score;
417 else if (score > bestscore) bestscore = score;
418 }
419 }
420 }
421
422 // no move
423 if (bestscore == -SCORE_INF) {
424 if (can_move(board->opponent, board->player)) { // pass
425 search_pass_endgame(search);
426 bestscore = -search_shallow(search, -beta);
427 search_pass_endgame(search);
428 } else { // gameover
429 bestscore = search_solve(search);
430 }
431 }
432
433 assert(SCORE_MIN <= bestscore && bestscore <= SCORE_MAX);
434 return bestscore;
435}
436
449int NWS_endgame(Search *search, const int alpha)
450{
451 int score;
452 HashTable *hash_table = search->hash_table;
453 unsigned long long hash_code;
454 const int beta = alpha + 1;
455 HashData hash_data[1];
456 Board *board = search->board;
457 MoveList movelist[1];
458 Move *move, *bestmove;
459 long long cost;
460
461 if (search->stop) return alpha;
462
463 assert(search->n_empties == bit_count(~(search->board->player|search->board->opponent)));
464 assert(SCORE_MIN <= alpha && alpha <= SCORE_MAX);
465
467
468 if (search->n_empties <= DEPTH_TO_SHALLOW_SEARCH) return search_shallow(search, alpha);
469
471
472 // stability cutoff
473 if (search_SC_NWS(search, alpha, &score)) return score;
474
475 // transposition cutoff
476 hash_code = board_get_hash_code(board);
477 if (hash_get(hash_table, board, hash_code, hash_data) && search_TC_NWS(hash_data, search->n_empties, NO_SELECTIVITY, alpha, &score)) return score;
478
479 search_get_movelist(search, movelist);
480
481 cost = -search->n_nodes;
482 // special cases
483 if (movelist_is_empty(movelist)) {
484 bestmove = movelist->move->next = movelist->move + 1;
485 bestmove->next = 0;
486 if (can_move(board->opponent, board->player)) { // pass
487 search_pass_endgame(search);
488 bestmove->score = -NWS_endgame(search, -beta);
489 search_pass_endgame(search);
490 bestmove->x = PASS;
491 } else { // game over
492 bestmove->score = search_solve(search);
493 bestmove->x = NOMOVE;
494 }
495 } else {
496 movelist_evaluate(movelist, search, hash_data, alpha, 0);
497
498 bestmove = movelist->move; bestmove->score = -SCORE_INF;
499 // loop over all moves
500 foreach_best_move(move, movelist) {
501 search_update_endgame(search, move);
502 move->score = -NWS_endgame(search, -beta);
503 search_restore_endgame(search, move);
504 if (move->score > bestmove->score) {
505 bestmove = move;
506 if (bestmove->score >= beta) break;
507 }
508 }
509 }
510
511 if (!search->stop) {
512 cost += search->n_nodes;
513 hash_store(hash_table, board, hash_code, search->n_empties, NO_SELECTIVITY, last_bit(cost), alpha, beta, bestmove->score, bestmove->x);
514 if (SQUARE_STATS(1) + 0) {
515 foreach_move(move, movelist)
517 if (bestmove->score > alpha) ++statistics.n_good_square[search->n_empties][SQUARE_TYPE[bestmove->score]];
518 }
519 assert(SCORE_MIN <= bestmove->score && bestmove->score <= SCORE_MAX);
520 assert((bestmove->score & 1) == 0);
521 return bestmove->score;
522 }
523
524 return alpha;
525}
526
DLL_API int bit_count(unsigned long long b)
Count the number of bits set to one in an unsigned long long.
Definition bit.c:72
const unsigned long long NEIGHBOUR[]
Definition bit.c:39
DLL_API int last_bit(unsigned long long b)
Search the last bit set (same as log2()).
Definition bit.c:264
unsigned long long board_get_hash_code(const Board *board)
Compute a hash code.
Definition board.c:1134
unsigned long long board_pass_next(const Board *board, const int x, Board *next)
Compute a board resulting of an opponent move played on a previous board.
Definition board.c:538
unsigned long long board_get_move(const Board *board, const int x, Move *move)
Compute a move.
Definition board.c:438
DLL_API bool can_move(const unsigned long long P, const unsigned long long O)
Check if a player can move.
Definition board.c:797
unsigned long long board_next(const Board *board, const int x, Board *next)
Compute a board resulting of a move played on a previous board.
Definition board.c:517
#define SCORE_MAX
Definition const.h:58
#define SCORE_INF
Definition const.h:52
@ PASS
Definition const.h:37
@ NOMOVE
Definition const.h:37
#define SCORE_MIN
Definition const.h:55
int(* count_last_flip[])(const unsigned long long)
Definition count_flip.c:1234
#define foreach_empty(empty, list)
Definition empty.h:46
#define foreach_odd_empty(empty, list, parity)
Definition empty.h:54
#define foreach_even_empty(empty, list, parity)
Definition empty.h:50
static int search_solve_3(Search *search, const int alpha)
Get the final score.
Definition endgame.c:190
static int board_solve_2(Board *board, int alpha, const int x1, const int x2, Search *search)
Get the final score.
Definition endgame.c:135
int NWS_endgame(Search *search, const int alpha)
Evaluate an endgame position with a Null Window Search algorithm.
Definition endgame.c:449
static int board_solve(const Board *board, const int n_empties)
Get the final score.
Definition endgame.c:30
int board_score_1(const Board *board, const int beta, const int x)
Get the final score.
Definition endgame.c:96
int search_solve(const Search *search)
Get the final score.
Definition endgame.c:52
static int board_solve_0(const Board *board)
Get the final score.
Definition endgame.c:65
static int search_shallow(Search *search, const int alpha)
Evaluate a position using a shallow NWS.
Definition endgame.c:366
int search_solve_0(const Search *search)
Get the final score.
Definition endgame.c:80
static int search_solve_4(Search *search, const int alpha)
Get the final score.
Definition endgame.c:266
bool hash_get(HashTable *hash_table, const unsigned long long hash_code, HashData *data)
Find an hash table entry according to the evaluated board hash codes.
Definition hash-lock-free.c:600
void hash_store(HashTable *hash_table, const unsigned long long hash_code, const int depth, const int selectivity, const int cost, const int alpha, const int beta, const int score, const int move)
Store an hashtable item.
Definition hash-lock-free.c:543
bool movelist_is_empty(const MoveList *movelist)
Check if the list is empty.
Definition move.c:537
void movelist_evaluate(MoveList *movelist, Search *search, const HashData *hash_data, const int alpha, const int depth)
Evaluate a list of move in order to sort it.
Definition move.c:436
#define foreach_move(iter, movelist)
Definition move.h:78
#define foreach_best_move(iter, movelist)
Definition move.h:82
const int NO_SELECTIVITY
Definition search.c:94
bool search_TC_NWS(HashData *data, const int depth, const int selectivity, const int alpha, int *score)
Transposition Cutoff (TC).
Definition search.c:1230
void search_pass_endgame(Search *search)
Update the search state after a passing move.
Definition search.c:928
void search_get_movelist(const Search *search, MoveList *movelist)
Get a list of legal moves.
Definition search.c:875
const int QUADRANT_ID[]
Definition search.c:81
void search_restore_endgame(Search *search, const Move *move)
Restore the search state as before a move.
Definition search.c:915
void search_update_endgame(Search *search, const Move *move)
Update the search state after a move.
Definition search.c:900
const int SQUARE_TYPE[]
Definition search.c:132
bool search_SC_NWS(Search *search, const int alpha, int *score)
Stability Cutoff (TC).
Definition search.c:1170
#define DEPTH_TO_SHALLOW_SEARCH
Definition settings.h:83
Statistics statistics
Definition stats.c:21
Statistics header.
#define SQUARE_STATS(x)
Definition stats.h:29
#define SEARCH_UPDATE_INTERNAL_NODES()
Definition stats.h:40
#define SEARCH_STATS(x)
Definition stats.h:27
Definition board.h:26
unsigned long long player
Definition board.h:27
unsigned long long opponent
Definition board.h:27
Definition hash.h:24
Definition hash.h:47
Definition move.h:29
Move move[MAX_MOVE+2]
Definition move.h:30
Definition move.h:20
int score
Definition move.h:23
struct Move * next
Definition move.h:25
int x
Definition move.h:22
Definition search.h:95
unsigned int parity
Definition search.h:120
int n_empties
Definition search.h:99
SquareList empties[BOARD_SIZE+2]
Definition search.h:97
HashTable hash_table[1]
Definition search.h:103
volatile Stop stop
Definition search.h:122
volatile unsigned long long n_nodes
Definition search.h:155
Board board[1]
Definition search.h:96
Definition empty.h:15
struct SquareList * next
Definition empty.h:20
int x
Definition empty.h:17
unsigned long long n_NWS_endgame
Definition stats.h:79
unsigned long long n_search_solve_4
Definition stats.h:86
unsigned long long n_board_solve_2
Definition stats.h:84
unsigned long long n_search_solve_3
Definition stats.h:85
unsigned long long n_search_solve_0
Definition stats.h:83
unsigned long long n_search_solve
Definition stats.h:82
unsigned long long n_NWS_shallow
Definition stats.h:81
unsigned long long n_played_square[BOARD_SIZE][10]
Definition stats.h:110
unsigned long long n_good_square[BOARD_SIZE][10]
Definition stats.h:111
Parallel search header.