My Project
ybwc.c
Go to the documentation of this file.
1
30#include "ybwc.h"
31
32#include "move.h"
33#include "options.h"
34#include "util.h"
35#include "search.h"
36#include "stats.h"
37#include "settings.h"
38
39#include <assert.h>
40#include <stdlib.h>
41
42extern Log search_log[1];
43
59void node_init(Node* node, Search *search, const int alpha, const int beta, const int depth, const int n_moves, Node* parent)
60{
61 int i;
62
63 assert(node != NULL);
64 assert(SCORE_MIN <= alpha && alpha <= SCORE_MAX);
65 assert(SCORE_MIN <= beta && beta <= SCORE_MAX);
66 assert(alpha < beta);
67
68 lock_init(node);
69 condition_init(node);
70
71 node->alpha = alpha;
72 node->beta = beta;
73 node->depth = depth;
74 node->height = search->height;
75 node->move = NULL;
76 node->pv_node = false;
77 node->bestmove = NOMOVE;
78 node->bestscore = -SCORE_INF;
79 node->n_moves_todo = n_moves;
80 node->n_moves_done = 0;
81 node->parent = parent;
82 node->search = search;
83 for (i = 0; i < SPLIT_MAX_SLAVES; ++i) node->slave[i] = NULL;
84 node->n_slave = 0;
85 node->is_waiting = false;
86 node->is_helping = false;
87 node->stop_point = false;
88}
89
95void node_free(Node *node)
96{
97 lock_free(node);
98 condition_free(node);
99}
100
112static bool get_helper(Node *master, Node *node, Move *move)
113{
114 Task *task;
115 bool found = false;
116
117 if (master) {
118 if (master->is_waiting && !master->is_helping) {
119 lock(master);
120 if (master->n_slave && master->is_waiting && !master->is_helping) {
121 master->is_helping = true;
122 task = master->help;
123 task_init(task) ;
124 task->is_helping = true;
125 task->node = node;
126 task->move = move;
127 search_clone(task->search, node->search);
128 lock(node);
129 node->slave[node->n_slave++] = task->search;
130 unlock(node);
131 task->run = true;
132 found = true;
133
134 condition_broadcast(master);
135 }
136 unlock(master);
137 } else {
138 found = get_helper(master->parent, node, move);
139 }
140 }
141
142 return found;
143}
144
167bool node_split(Node *node, Move *move)
168{
169 Task *task;
170 Search *search = node->search;
171
172 if (search->allow_node_splitting // split only if parallelism is on
173 && node->depth >= SPLIT_MIN_DEPTH // split if we are deep enough
174 && node->n_moves_done // do not split first move (ybwc main principle).
175 && node->n_slave < SPLIT_MAX_SLAVES // do not split too much at the same point.
176 && node->n_moves_todo >= SPLIT_MIN_MOVES_TODO) { // do not split the last move(s), to diminish waiting time
178
179 if (get_helper(node->parent, node, move)) {
181 return true;
182 } else if ((task = task_stack_get_idle_task(search->tasks)) != NULL) {
183 task->node = node;
184 task->move = move;
185 search_clone(task->search, search);
186 lock(node);
187 node->slave[node->n_slave++] = task->search;
188 unlock(node);
190
191 lock(task);
192 task->run = true;
193 condition_signal(task);
194 unlock(task);
195
196 return true;
197 }
198 }
199 return false;
200}
201
213{
214 int i;
215
216 lock(node);
217 // stop slaves ?
218 if ((node->alpha >= node->beta || node->search->stop) && node->n_slave) {
219 for (i = 0; i < node->n_slave; ++i) {
222 }
223 }
224
225 // wait slaves
227 while (node->n_slave) {
228 node->is_waiting = true;
229 assert(node->is_helping == false);
230 condition_wait(node);
231
232 if (node->is_helping) {
233 assert(node->help->run);
234 task_search(node->help);
235 task_free(node->help);
236 node->is_helping = false;
237 } else {
238 node->is_waiting = false;
239 }
240 }
241
242 // wake-up master thread!
243 if (node->search->stop == STOP_PARALLEL_SEARCH && node->stop_point) {
244 node->search->stop = RUNNING;
245 node->stop_point = false;
247 }
248 unlock(node);
249}
250
251
261void node_update(Node* node, Move *move)
262{
263 Search *search = node->search;
264 const int score = move->score;
265 int i;
266
267 lock(node);
268 if (!search->stop && score > node->bestscore) {
269 node->bestscore = score;
270 node->bestmove = move->x;
271 if (node->height == 0) {
272 record_best_move(search, search->board, move, node->alpha, node->beta, node->depth);
273 search->result->n_moves_left--;
274 }
275 if (score > node->alpha) node->alpha = score;
276 }
277 if (node->alpha >= node->beta && node->n_slave) { // stop slave ?
278 for (i = 0; i < node->n_slave; ++i) {
281 }
282 }
283 unlock(node);
284}
285
298{
299 Move *move;
300 lock(node);
301 node->n_moves_todo = movelist->n_moves;
302 node->n_moves_done = 0;
303 node->move = movelist_first(movelist);
304 if (node->move && !node->search->stop) {
305 assert(node->alpha < node->beta);
306 move = node->move;
307 } else {
308 move = NULL;
309 }
310 unlock(node);
311 return move;
312}
313
325{
326 Move *move;
327 if (node->move && node->alpha < node->beta && !node->search->stop) {
328 ++node->n_moves_done; --node->n_moves_todo;
329 move = node->move = move_next(node->move);
330 } else {
331 move = NULL;
332 }
333
334 return move;
335}
336
346{
347 Move *move;
348 lock(node);
349 move = node_next_move_lockless(node);
350 unlock(node);
351
352 return move;
353}
354
362void task_search(Task *task)
363{
364
365 Node *node = task->node;
366 Search *search = task->search;
367 Move *move = task->move;
368 int i;
369
370 search_set_state(search, node->search->stop);
371
372 YBWC_STATS(++task->n_calls;)
373
374 while (move && !search->stop) {
375 const int alpha = node->alpha;
376 if (alpha >= node->beta) break;
377
378 search_update_midgame(search, move);
379 move->score = -NWS_midgame(search, -alpha - 1, node->depth - 1, node);
380 if (alpha < move->score && move->score < node->beta) {
381 move->score = -PVS_midgame(search, -node->beta, -alpha, node->depth - 1, node);
382 assert(node->pv_node == true);
383 }
384 search_restore_midgame(search, move);
385 if (node->height == 0) {
386 move->cost = search_get_pv_cost(search);
387 move->score = search_bound(search, move->score);
388 if (log_is_open(search_log)) show_current_move(search_log->f, search, move, alpha, node->beta, true);
389 }
390
391 lock(node);
392 if (!search->stop && move->score > node->bestscore) {
393 node->bestscore = move->score;
394 node->bestmove = move->x;
395 if (node->height == 0) {
396 record_best_move(search, search->board, move, alpha, node->beta, node->depth);
397 search->result->n_moves_left--;
398 if (search->options.verbosity == 4) pv_debug(search, move, stdout);
399 }
400 if (node->bestscore > node->alpha) {
401 node->alpha = node->bestscore;
402 if (node->alpha >= node->beta && node->search->stop == RUNNING) { // stop the master thread?
403 node->stop_point = true;
406 }
407 }
408 }
409 move = node_next_move_lockless(node);
410 unlock(node);
411 }
412
413 search_set_state(search, STOP_END);
414
415 spin_lock(search->parent);
416 for (i = 0; i < search->parent->n_child; ++i) {
417 if (search->parent->child[i] == search) {
418 --search->parent->n_child;
419 search->parent->child[i] = search->parent->child[search->parent->n_child];
420 break;
421 }
422 }
423 search->parent->child_nodes += search_count_nodes(search);
424 YBWC_STATS(task->n_nodes += search->n_nodes;)
425 spin_unlock(search->parent);
426
427 lock(node);
428 task->run = false;
429 for (i = 0; i < node->n_slave; ++i) {
430 if (node->slave[i] == search) {
431 --node->n_slave;
432 node->slave[i] = node->slave[node->n_slave];
433 break;
434 }
435 }
436 condition_broadcast(node);
437 unlock(node);
438}
439
440
453void* task_loop(void *param)
454{
455 Task *task = (Task*) param;
456
457 lock(task);
458 task->loop = true;
459
460 while (task->loop) {
461 if (!task->run) {
462 condition_wait(task);
463 }
464 if (task->run) {
465 task_search(task);
467 }
468 }
469
470 unlock(task);
471
472 return NULL;
473}
474
482{
483 Search *search;
484
485 search = (Search*) malloc(sizeof (Search)); // allocate the search attached to this task
486 if (search == NULL) {
487 fatal_error("task_init: cannot allocate the search position.\n");
488 }
489 search->n_nodes = 0;
490 search->n_child = 0;
491 search->parent = NULL;
492 eval_init(search->eval);
493 spin_init(search);
494 search->task = task;
495 search->stop = STOP_END;
496
497 return search;
498}
499
505static void task_search_destroy(Search *search)
506{
507 eval_free(search->eval);
508 spin_free(search);
509 free(search);
510}
511
520void task_init(Task *task)
521{
522 lock_init(task);
523 condition_init(task);
524
525 task->loop = false;
526 task->run = false;
527 task->node = NULL;
528 task->move = NULL;
529 task->n_calls = 0;
530 task->n_nodes = 0;
531 task->search = task_search_create(task);
532}
533
534
540void task_free(Task *task)
541{
542 assert(task->run == false);
543 if (task->loop) {
544 task->loop = false; // stop the main loop
545 condition_signal(task);
546 thread_join(task->thread);
547 }
548 lock_free(task);
549 condition_free(task);
550 task_search_destroy(task->search); // free other resources
551 task->search = NULL;
552}
553
560void task_stack_init(TaskStack *stack, const int n)
561{
562 int i;
563
564 spin_init(stack);
565
566 stack->n = n; // number of additional task
567 stack->n_idle = 0;
568
569 if (stack->n) {
570 // allocate the tasks
571 stack->task = (Task*) malloc(stack->n * sizeof (Task));
572 stack->stack = (Task**) malloc(stack->n * sizeof (Task*));
573 if (stack->task == NULL) {
574 fatal_error("Cannot allocate an array of %d tasks\n", stack->n);
575 }
576 if (stack->stack == NULL) {
577 fatal_error("Cannot allocate a stack of %d entries\n", stack->n);
578 }
579
580 // init the tasks.
581 for (i = 0; i < stack->n; ++i) {
582 if (i) {
583 task_init(stack->task + i);
584 thread_create2(&stack->task[i].thread, task_loop, stack->task + i); // modified for iOS by lavox. 2018/1/16
585 if (options.cpu_affinity) thread_set_cpu(stack->task[i].thread, i); /* CPU 0 to n - 1 */
586 }
587 stack->task[i].container = stack;
588 stack->stack[i] = NULL;
589 }
590
591 // put the tasks onto stack;
592 for (i = 1; i < stack->n; ++i) {
593 task_stack_put_idle_task(stack, stack->task + i);
594 }
595
596 } else { // No parallel search
597 stack->task = NULL;
598 stack->stack = NULL;
599 }
600}
601
608{
609 int i;
610 for (i = 1; i < stack->n; ++i) {
611 task_free(stack->task + i);
612 }
613 free(stack->task); stack->task = NULL;
614 free(stack->stack); stack->stack = NULL;
615 stack->n = 0;
616 stack->n_idle = 0;
617 spin_free(stack);
618}
619
626void task_stack_resize(TaskStack *stack, const int n)
627{
628 task_stack_free(stack);
629 task_stack_init(stack, n);
630}
631
639{
640 Task *task;
641
642 spin_lock(stack);
643
644 if (stack->n_idle) {
645 task = stack->stack[--stack->n_idle];
646 } else {
647 task = NULL;
648 }
649
650 spin_unlock(stack);
651
652 return task;
653}
654
662{
663 spin_lock(stack);
664
665 stack->stack[stack->n_idle++] = task;
666
667 spin_unlock(stack);
668}
669
#define SCORE_MAX
Definition const.h:58
#define SCORE_INF
Definition const.h:52
@ NOMOVE
Definition const.h:37
@ RUNNING
Definition const.h:71
@ STOP_PARALLEL_SEARCH
Definition const.h:72
@ STOP_END
Definition const.h:76
#define SCORE_MIN
Definition const.h:55
void eval_free(Eval *eval)
Free resources used by the evaluation function.
Definition eval.c:519
void eval_init(Eval *eval)
Initialize a new evaluation function.
Definition eval.c:509
int NWS_midgame(Search *search, const int alpha, int depth, Node *parent)
Evaluate a midgame position with a Null Window Search algorithm.
Definition midgame.c:473
int PVS_midgame(Search *search, const int alpha, const int beta, int depth, Node *parent)
Evaluate a position with a deep Principal Variation Search algorithm.
Definition midgame.c:585
Move * movelist_first(MoveList *movelist)
Return the first move of the list.
Definition move.c:422
Move * move_next(Move *move)
Return the next move from the list.
Definition move.c:400
Options options
Definition options.c:22
int search_get_pv_cost(Search *search)
Compute a cost as a combination of node count, depth, etc. from hash_table.
Definition root.c:303
void pv_debug(Search *search, const Move *bestmove, FILE *f)
Debug PV.
Definition root.c:33
void show_current_move(FILE *f, Search *search, const Move *move, const int alpha, const int beta, const bool parallel)
Definition root.c:239
int search_bound(const Search *search, int score)
bound root scores according to stable squares
Definition root.c:253
void record_best_move(Search *search, const Board *init_board, const Move *bestmove, const int alpha, const int beta, const int depth)
Record best move.
Definition root.c:150
unsigned long long search_count_nodes(Search *search)
Return the number of nodes searched.
Definition search.c:1073
void search_clone(Search *search, Search *master)
Clone a search for parallel search.
Definition search.c:540
void search_restore_midgame(Search *search, const Move *move)
Restore the search state as before a move.
Definition search.c:964
void search_update_midgame(Search *search, const Move *move)
Update the search state after a move.
Definition search.c:942
void search_set_state(Search *search, const Stop stop)
Set the search running/waiting state.
Definition search.c:1353
void search_stop_all(Search *search, const Stop stop)
Stop the search.
Definition search.c:1335
#define SPLIT_MIN_MOVES_TODO
Definition settings.h:104
#define SPLIT_MAX_SLAVES
Definition settings.h:107
#define SPLIT_MIN_DEPTH
Definition settings.h:101
Statistics statistics
Definition stats.c:21
Statistics header.
#define YBWC_STATS(x)
Definition stats.h:21
LogFile.
Definition util.h:423
FILE * f
Definition util.h:424
Definition move.h:29
int n_moves
Definition move.h:31
Definition move.h:20
unsigned int cost
Definition move.h:24
int score
Definition move.h:23
int x
Definition move.h:22
Definition ybwc.h:48
volatile int n_slave
Definition ybwc.h:54
struct Node * parent
Definition ybwc.h:61
int beta
Definition ybwc.h:52
volatile bool is_waiting
Definition ybwc.h:56
bool pv_node
Definition ybwc.h:53
volatile bool is_helping
Definition ybwc.h:65
struct Search * search
Definition ybwc.h:59
int height
Definition ybwc.h:58
struct Move * move
Definition ybwc.h:62
volatile int n_moves_done
Definition ybwc.h:63
volatile int alpha
Definition ybwc.h:51
struct Search * slave[SPLIT_MAX_SLAVES]
Definition ybwc.h:60
Task help[1]
Definition ybwc.h:66
volatile int bestscore
Definition ybwc.h:50
volatile bool stop_point
Definition ybwc.h:55
volatile int bestmove
Definition ybwc.h:49
int depth
Definition ybwc.h:57
volatile int n_moves_todo
Definition ybwc.h:64
bool cpu_affinity
Definition options.h:30
int n_moves_left
Definition search.h:52
Definition search.h:95
struct Search * parent
Definition search.h:112
struct TaskStack * tasks
Definition search.h:109
volatile unsigned long long child_nodes
Definition search.h:156
bool allow_node_splitting
Definition search.h:123
struct Search * child[MAX_THREADS]
Definition search.h:113
Result * result
Definition search.h:151
int height
Definition search.h:133
struct Search::@25 options
int verbosity
Definition search.h:142
volatile int n_child
Definition search.h:115
volatile Stop stop
Definition search.h:122
volatile unsigned long long n_nodes
Definition search.h:155
Eval eval[1]
Definition search.h:106
struct Task * task
Definition search.h:110
Board board[1]
Definition search.h:96
unsigned long long n_wake_up
Definition stats.h:101
unsigned long long n_stopped_slave
Definition stats.h:99
unsigned long long n_master_helper
Definition stats.h:97
unsigned long long n_waited_slave
Definition stats.h:98
unsigned long long n_stopped_master
Definition stats.h:100
unsigned long long n_split_success
Definition stats.h:96
unsigned long long n_split_try
Definition stats.h:95
Definition ybwc.h:93
Task ** stack
Definition ybwc.h:96
int n
Definition ybwc.h:97
Task * task
Definition ybwc.h:95
int n_idle
Definition ybwc.h:98
Definition ybwc.h:29
struct Node * node
Definition ybwc.h:34
struct Move * move
Definition ybwc.h:35
unsigned long long n_calls
Definition ybwc.h:37
volatile bool is_helping
Definition ybwc.h:32
unsigned long long n_nodes
Definition ybwc.h:38
struct Search * search
Definition ybwc.h:33
volatile bool loop
Definition ybwc.h:30
Thread thread
Definition ybwc.h:36
volatile bool run
Definition ybwc.h:31
struct TaskStack * container
Definition ybwc.h:41
void thread_create2(Thread *thread, void *(*function)(void *), void *data)
Create a thread.
Definition util.c:922
void thread_join(Thread thread)
Join a thread.
Definition util.c:940
void thread_set_cpu(Thread thread, int i)
Choose a single core or cpu to run on, under linux systems, to avoid context changes.
Definition util.c:967
Miscellaneous utilities header.
#define fatal_error(...)
Display an error message as "FATAL_ERROR : file name : function name : line number : ....
Definition util.h:349
static void atomic_add(volatile unsigned long long *value, long long i)
Definition util.h:327
#define log_is_open(l)
Check if the log stream can be used.
Definition util.h:448
void task_stack_free(TaskStack *stack)
Free resources used by the stack of tasks.
Definition ybwc.c:607
Move * node_next_move(Node *node)
Get the next move of the move list.
Definition ybwc.c:345
static Search * task_search_create(Task *task)
Create a search structure for a task.
Definition ybwc.c:481
void task_init(Task *task)
Initialize a task.
Definition ybwc.c:520
Log search_log[1]
Definition search.c:74
void node_wait_slaves(Node *node)
Wait for slaves termination.
Definition ybwc.c:212
void task_free(Task *task)
Free resources used by a task.
Definition ybwc.c:540
bool node_split(Node *node, Move *move)
Node split.
Definition ybwc.c:167
void node_free(Node *node)
Free Resources allocated by a node.
Definition ybwc.c:95
Move * node_first_move(Node *node, MoveList *movelist)
Get the first move of the move list.
Definition ybwc.c:297
void task_stack_resize(TaskStack *stack, const int n)
Resize the stack of tasks.
Definition ybwc.c:626
void task_stack_init(TaskStack *stack, const int n)
Initialize the stack of tasks.
Definition ybwc.c:560
void node_update(Node *node, Move *move)
Update a node.
Definition ybwc.c:261
void task_search(Task *task)
A parallel search within a Task structure.
Definition ybwc.c:362
void * task_loop(void *param)
The main loop runned by a task.
Definition ybwc.c:453
void task_stack_put_idle_task(TaskStack *stack, Task *task)
Put back an idle task after using it.
Definition ybwc.c:661
void node_init(Node *node, Search *search, const int alpha, const int beta, const int depth, const int n_moves, Node *parent)
Initialize a node.
Definition ybwc.c:59
Task * task_stack_get_idle_task(TaskStack *stack)
Return, if available, an idle task.
Definition ybwc.c:638
static void task_search_destroy(Search *search)
Free a search structure of a task.
Definition ybwc.c:505
static bool get_helper(Node *master, Node *node, Move *move)
Seek for & use an helper node.
Definition ybwc.c:112
static Move * node_next_move_lockless(Node *node)
Get the next move of the move list.
Definition ybwc.c:324
Parallel search header.