Patternscan new spatial: Debug print
[pachi.git] / uct / uct.c
blobbbe97bb7375547a34e6ee989d5dda6bb01ec650e
1 #include <assert.h>
2 #include <pthread.h>
3 #include <signal.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <time.h>
9 #define DEBUG
11 #include "debug.h"
12 #include "board.h"
13 #include "gtp.h"
14 #include "move.h"
15 #include "mq.h"
16 #include "playout.h"
17 #include "playout/elo.h"
18 #include "playout/moggy.h"
19 #include "playout/light.h"
20 #include "random.h"
21 #include "timeinfo.h"
22 #include "tactics.h"
23 #include "uct/internal.h"
24 #include "uct/prior.h"
25 #include "uct/tree.h"
26 #include "uct/uct.h"
27 #include "uct/walk.h"
29 struct uct_policy *policy_ucb1_init(struct uct *u, char *arg);
30 struct uct_policy *policy_ucb1amaf_init(struct uct *u, char *arg);
31 static void uct_pondering_stop(struct uct *u);
34 /* Default number of simulations to perform per move.
35 * Note that this is now in total over all threads! (Unless TM_ROOT.) */
36 #define MC_GAMES 80000
37 #define MC_GAMELEN MAX_GAMELEN
39 /* How big proportion of ownermap counts must be of one color to consider
40 * the point sure. */
41 #define GJ_THRES 0.8
42 /* How many games to consider at minimum before judging groups. */
43 #define GJ_MINGAMES 500
45 /* How often to inspect the tree from the main thread to check for playout
46 * stop, progress reports, etc. A (struct timespec) initializer. */
47 #define TREE_BUSYWAIT_INTERVAL { .tv_sec = 0, .tv_nsec = 100*1000000 /* 100ms */ }
48 /* Once per how many simulations (per thread) to show a progress report line. */
49 #define TREE_SIMPROGRESS_INTERVAL 10000
52 static void
53 setup_state(struct uct *u, struct board *b, enum stone color)
55 u->t = tree_init(b, color);
56 if (u->force_seed)
57 fast_srandom(u->force_seed);
58 if (UDEBUGL(0))
59 fprintf(stderr, "Fresh board with random seed %lu\n", fast_getseed());
60 //board_print(b, stderr);
61 if (!u->no_book && b->moves == 0) {
62 assert(color == S_BLACK);
63 tree_load(u->t, b);
67 static void
68 reset_state(struct uct *u)
70 assert(u->t);
71 tree_done(u->t); u->t = NULL;
74 static void
75 prepare_move(struct engine *e, struct board *b, enum stone color)
77 struct uct *u = e->data;
79 if (u->t) {
80 /* Verify that we have sane state. */
81 assert(b->es == u);
82 assert(u->t && b->moves);
83 if (color != stone_other(u->t->root_color)) {
84 fprintf(stderr, "Fatal: Non-alternating play detected %d %d\n",
85 color, u->t->root_color);
86 exit(1);
89 } else {
90 /* We need fresh state. */
91 b->es = u;
92 setup_state(u, b, color);
95 if (u->dynkomi && u->dynkomi > b->moves && (color & u->dynkomi_mask))
96 u->t->extra_komi = uct_get_extra_komi(u, b);
98 u->ownermap.playouts = 0;
99 memset(u->ownermap.map, 0, board_size2(b) * sizeof(u->ownermap.map[0]));
102 static void
103 dead_group_list(struct uct *u, struct board *b, struct move_queue *mq)
105 struct group_judgement gj;
106 gj.thres = GJ_THRES;
107 gj.gs = alloca(board_size2(b) * sizeof(gj.gs[0]));
108 board_ownermap_judge_group(b, &u->ownermap, &gj);
109 groups_of_status(b, &gj, GS_DEAD, mq);
112 bool
113 uct_pass_is_safe(struct uct *u, struct board *b, enum stone color, bool pass_all_alive)
115 if (u->ownermap.playouts < GJ_MINGAMES)
116 return false;
118 struct move_queue mq = { .moves = 0 };
119 if (!pass_all_alive)
120 dead_group_list(u, b, &mq);
121 return pass_is_safe(b, color, &mq);
125 static void
126 uct_printhook_ownermap(struct board *board, coord_t c, FILE *f)
128 struct uct *u = board->es;
129 assert(u);
130 const char chr[] = ":XO,"; // dame, black, white, unclear
131 const char chm[] = ":xo,";
132 char ch = chr[board_ownermap_judge_point(&u->ownermap, c, GJ_THRES)];
133 if (ch == ',') { // less precise estimate then?
134 ch = chm[board_ownermap_judge_point(&u->ownermap, c, 0.67)];
136 fprintf(f, "%c ", ch);
139 static char *
140 uct_notify_play(struct engine *e, struct board *b, struct move *m)
142 struct uct *u = e->data;
143 if (!u->t) {
144 /* No state, create one - this is probably game beginning
145 * and we need to load the opening book right now. */
146 prepare_move(e, b, m->color);
147 assert(u->t);
150 /* Stop pondering. */
151 /* XXX: If we are about to receive multiple 'play' commands,
152 * e.g. in a rengo, we will not ponder during the rest of them. */
153 uct_pondering_stop(u);
155 if (is_resign(m->coord)) {
156 /* Reset state. */
157 reset_state(u);
158 return NULL;
161 /* Promote node of the appropriate move to the tree root. */
162 assert(u->t->root);
163 if (!tree_promote_at(u->t, b, m->coord)) {
164 if (UDEBUGL(0))
165 fprintf(stderr, "Warning: Cannot promote move node! Several play commands in row?\n");
166 reset_state(u);
167 return NULL;
170 return NULL;
173 static char *
174 uct_chat(struct engine *e, struct board *b, char *cmd)
176 struct uct *u = e->data;
177 static char reply[1024];
179 cmd += strspn(cmd, " \n\t");
180 if (!strncasecmp(cmd, "winrate", 7)) {
181 if (!u->t)
182 return "no game context (yet?)";
183 enum stone color = u->t->root_color;
184 struct tree_node *n = u->t->root;
185 snprintf(reply, 1024, "In %d playouts at %d threads, %s %s can win with %.2f%% probability",
186 n->u.playouts, u->threads, stone2str(color), coord2sstr(n->coord, b),
187 tree_node_get_value(u->t, -1, n->u.value) * 100);
188 if (abs(u->t->extra_komi) >= 0.5) {
189 sprintf(reply + strlen(reply), ", while self-imposing extra komi %.1f",
190 u->t->extra_komi);
192 strcat(reply, ".");
193 return reply;
195 return NULL;
198 static void
199 uct_dead_group_list(struct engine *e, struct board *b, struct move_queue *mq)
201 struct uct *u = e->data;
203 /* This means the game is probably over, no use pondering on. */
204 uct_pondering_stop(u);
206 if (u->pass_all_alive)
207 return; // no dead groups
209 bool mock_state = false;
211 if (!u->t) {
212 /* No state, but we cannot just back out - we might
213 * have passed earlier, only assuming some stones are
214 * dead, and then re-connected, only to lose counting
215 * when all stones are assumed alive. */
216 /* Mock up some state and seed the ownermap by few
217 * simulations. */
218 prepare_move(e, b, S_BLACK); assert(u->t);
219 for (int i = 0; i < GJ_MINGAMES; i++)
220 uct_playout(u, b, S_BLACK, u->t);
221 mock_state = true;
224 dead_group_list(u, b, mq);
226 if (mock_state) {
227 /* Clean up the mock state in case we will receive
228 * a genmove; we could get a non-alternating-move
229 * error from prepare_move() in that case otherwise. */
230 reset_state(u);
234 static void
235 playout_policy_done(struct playout_policy *p)
237 if (p->done) p->done(p);
238 if (p->data) free(p->data);
239 free(p);
242 static void
243 uct_done(struct engine *e)
245 /* This is called on engine reset, especially when clear_board
246 * is received and new game should begin. */
247 struct uct *u = e->data;
248 uct_pondering_stop(u);
249 if (u->t) reset_state(u);
250 free(u->ownermap.map);
252 free(u->policy);
253 free(u->random_policy);
254 playout_policy_done(u->playout);
255 uct_prior_done(u->prior);
259 /* Pachi threading structure (if uct_playouts_parallel() is used):
261 * main thread
262 * | main(), GTP communication, ...
263 * | starts and stops the search managed by thread_manager
265 * thread_manager
266 * | spawns and collects worker threads
268 * worker0
269 * worker1
270 * ...
271 * workerK
272 * uct_playouts() loop, doing descend-playout until uct_halt
274 * Another way to look at it is by functions (lines denote thread boundaries):
276 * | uct_genmove()
277 * | uct_search() (uct_search_start() .. uct_search_stop())
278 * | -----------------------
279 * | spawn_thread_manager()
280 * | -----------------------
281 * | spawn_worker()
282 * V uct_playouts() */
284 /* Set in thread manager in case the workers should stop. */
285 volatile sig_atomic_t uct_halt = 0;
286 /* ID of the running worker thread. */
287 __thread int thread_id = -1;
288 /* ID of the thread manager. */
289 static pthread_t thread_manager;
290 static bool thread_manager_running;
292 static pthread_mutex_t finish_mutex = PTHREAD_MUTEX_INITIALIZER;
293 static pthread_cond_t finish_cond = PTHREAD_COND_INITIALIZER;
294 static volatile int finish_thread;
295 static pthread_mutex_t finish_serializer = PTHREAD_MUTEX_INITIALIZER;
297 struct spawn_ctx {
298 int tid;
299 struct uct *u;
300 struct board *b;
301 enum stone color;
302 struct tree *t;
303 unsigned long seed;
304 int games;
307 static void *
308 spawn_worker(void *ctx_)
310 struct spawn_ctx *ctx = ctx_;
311 /* Setup */
312 fast_srandom(ctx->seed);
313 thread_id = ctx->tid;
314 /* Run */
315 ctx->games = uct_playouts(ctx->u, ctx->b, ctx->color, ctx->t);
316 /* Finish */
317 pthread_mutex_lock(&finish_serializer);
318 pthread_mutex_lock(&finish_mutex);
319 finish_thread = ctx->tid;
320 pthread_cond_signal(&finish_cond);
321 pthread_mutex_unlock(&finish_mutex);
322 return ctx;
325 /* Thread manager, controlling worker threads. It must be called with
326 * finish_mutex lock held, but it will unlock it itself before exiting;
327 * this is necessary to be completely deadlock-free. */
328 /* The finish_cond can be signalled for it to stop; in that case,
329 * the caller should set finish_thread = -1. */
330 /* After it is started, it will update mctx->t to point at some tree
331 * used for the actual search (matters only for TM_ROOT), on return
332 * it will set mctx->games to the number of performed simulations. */
333 static void *
334 spawn_thread_manager(void *ctx_)
336 /* In thread_manager, we use only some of the ctx fields. */
337 struct spawn_ctx *mctx = ctx_;
338 struct uct *u = mctx->u;
339 struct tree *t = mctx->t;
340 bool shared_tree = u->parallel_tree;
341 fast_srandom(mctx->seed);
343 int played_games = 0;
344 pthread_t threads[u->threads];
345 int joined = 0;
347 uct_halt = 0;
349 /* Spawn threads... */
350 for (int ti = 0; ti < u->threads; ti++) {
351 struct spawn_ctx *ctx = malloc(sizeof(*ctx));
352 ctx->u = u; ctx->b = mctx->b; ctx->color = mctx->color;
353 mctx->t = ctx->t = shared_tree ? t : tree_copy(t);
354 ctx->tid = ti; ctx->seed = fast_random(65536) + ti;
355 pthread_create(&threads[ti], NULL, spawn_worker, ctx);
356 if (UDEBUGL(2))
357 fprintf(stderr, "Spawned worker %d\n", ti);
360 /* ...and collect them back: */
361 while (joined < u->threads) {
362 /* Wait for some thread to finish... */
363 pthread_cond_wait(&finish_cond, &finish_mutex);
364 if (finish_thread < 0) {
365 /* Stop-by-caller. Tell the workers to wrap up. */
366 uct_halt = 1;
367 continue;
369 /* ...and gather its remnants. */
370 struct spawn_ctx *ctx;
371 pthread_join(threads[finish_thread], (void **) &ctx);
372 played_games += ctx->games;
373 joined++;
374 if (!shared_tree) {
375 if (ctx->t == mctx->t) mctx->t = t;
376 tree_merge(t, ctx->t);
377 tree_done(ctx->t);
379 free(ctx);
380 if (UDEBUGL(2))
381 fprintf(stderr, "Joined worker %d\n", finish_thread);
382 pthread_mutex_unlock(&finish_serializer);
385 pthread_mutex_unlock(&finish_mutex);
387 if (!shared_tree)
388 tree_normalize(mctx->t, u->threads);
390 mctx->games = played_games;
391 return mctx;
394 static struct spawn_ctx *
395 uct_search_start(struct uct *u, struct board *b, enum stone color, struct tree *t)
397 assert(u->threads > 0);
398 assert(!thread_manager_running);
400 struct spawn_ctx ctx = { .u = u, .b = b, .color = color, .t = t, .seed = fast_random(65536) };
401 static struct spawn_ctx mctx; mctx = ctx;
402 pthread_mutex_lock(&finish_mutex);
403 pthread_create(&thread_manager, NULL, spawn_thread_manager, &mctx);
404 thread_manager_running = true;
405 return &mctx;
408 static struct spawn_ctx *
409 uct_search_stop(void)
411 assert(thread_manager_running);
413 /* Signal thread manager to stop the workers. */
414 pthread_mutex_lock(&finish_mutex);
415 finish_thread = -1;
416 pthread_cond_signal(&finish_cond);
417 pthread_mutex_unlock(&finish_mutex);
419 /* Collect the thread manager. */
420 struct spawn_ctx *pctx;
421 thread_manager_running = false;
422 pthread_join(thread_manager, (void **) &pctx);
423 return pctx;
427 /* Pre-process time_info for search control. */
428 static void
429 time_prep(struct time_info *ti)
431 if (ti->period == TT_TOTAL) {
432 fprintf(stderr, "Warning: TT_TOTAL time mode not supported, resetting to defaults.\n");
433 ti->period = TT_NULL;
435 if (ti->period == TT_NULL) {
436 ti->period = TT_MOVE;
437 ti->dim = TD_GAMES;
438 ti->len.games = MC_GAMES;
442 /* Run time-limited MCTS search on foreground. */
443 static int
444 uct_search(struct uct *u, struct board *b, struct time_info *ti, enum stone color, struct tree *t)
446 time_prep(ti);
447 if (UDEBUGL(2) && u->t->root->u.playouts > 0)
448 fprintf(stderr, "<pre-simulated %d games skipped>\n", u->t->root->u.playouts);
450 /* Number of last game with progress print. */
451 int last_print = t->root->u.playouts;
452 /* Number of simulations to wait before next print. */
453 int print_interval = TREE_SIMPROGRESS_INTERVAL * (u->thread_model == TM_ROOT ? 1 : u->threads);
454 /* Printed notification about full memory? */
455 bool print_fullmem = false;
457 struct spawn_ctx *ctx = uct_search_start(u, b, color, t);
459 /* The search tree is ctx->t. This is normally == t, but in case of
460 * TM_ROOT, it is one of the trees belonging to the independent
461 * workers. It is important to reference ctx->t directly since the
462 * thread manager will swap the tree pointer asynchronously. */
463 /* XXX: This means TM_ROOT support is suboptimal since single stalled
464 * thread can stall the others in case of limiting the search by game
465 * count. However, TM_ROOT just does not deserve any more extra code
466 * right now. */
468 /* Set up the intervals and deadlines. */
469 struct timespec busywait_stop;
470 if (ti->dim == TD_WALLTIME) {
471 clock_gettime(CLOCK_REALTIME, &busywait_stop);
472 assert(ti->period == TT_MOVE);
473 /* TODO: TT_TOTAL - allocate /(5*(board_size(b)-2)) of total time. */
474 time_add(&busywait_stop, &ti->len.walltime);
475 /* TODO: Safety buffer (2s? but depend on available time if too small). */
477 struct timespec busywait_interval = TREE_BUSYWAIT_INTERVAL;
479 /* Now, just periodically poll the search tree. */
480 while (1) {
481 nanosleep(&busywait_interval, NULL);
482 int i = ctx->t->root->u.playouts;
484 /* Print progress? */
485 if (i - last_print > print_interval) {
486 last_print += print_interval; // keep the numbers tidy
487 uct_progress_status(u, ctx->t, color, last_print);
489 if (!print_fullmem && ctx->t->nodes_size > u->max_tree_size) {
490 if (UDEBUGL(2))
491 fprintf(stderr, "memory limit hit (%ld > %lu)\n", ctx->t->nodes_size, u->max_tree_size);
492 print_fullmem = true;
495 /* Check against time settings. */
496 bool stop = false;
497 assert(ti->period == TT_MOVE);
498 switch (ti->dim) {
499 case TD_WALLTIME:
500 stop = time_passed(&busywait_stop);
501 break;
502 case TD_GAMES:
503 stop = i > ti->len.games;
504 break;
506 if (stop) break;
508 /* Early break in won situation. */
509 struct tree_node *best = u->policy->choose(u->policy, ctx->t->root, b, color);
510 if (best && ((best->u.playouts >= 2000 && tree_node_get_value(ctx->t, 1, best->u.value) >= u->loss_threshold)
511 || (best->u.playouts >= 500 && tree_node_get_value(ctx->t, 1, best->u.value) >= 0.95)))
512 break;
513 /* TODO: Early break if best->variance goes under threshold. */
514 /* TODO: Simulate longer if best of #sims != best of value. */
517 ctx = uct_search_stop();
519 if (UDEBUGL(2))
520 tree_dump(t, u->dumpthres);
521 if (UDEBUGL(0))
522 uct_progress_status(u, t, color, ctx->games);
524 return ctx->games;
528 /* Start pondering background with @color to play. */
529 static void
530 uct_pondering_start(struct uct *u, struct board *b0, struct tree *t, enum stone color)
532 if (UDEBUGL(1))
533 fprintf(stderr, "Starting to ponder with color %s\n", stone2str(stone_other(color)));
535 /* We need a local board copy to ponder upon. */
536 struct board *b = malloc(sizeof(*b)); board_copy(b, b0);
538 /* *b0 did not have the genmove'd move played yet. */
539 struct move m = { t->root->coord, t->root_color };
540 int res = board_play(b, &m);
541 assert(res >= 0);
543 /* Start MCTS manager thread "headless". */
544 uct_search_start(u, b, color, t);
547 /* uct_search_stop() frontend for the pondering (non-genmove) mode. */
548 static void
549 uct_pondering_stop(struct uct *u)
551 if (!thread_manager_running)
552 return;
554 /* Stop the thread manager. */
555 struct spawn_ctx *ctx = uct_search_stop();
556 if (UDEBUGL(1)) {
557 fprintf(stderr, "(pondering) ");
558 uct_progress_status(u, ctx->t, ctx->color, ctx->games);
560 free(ctx->b);
564 static coord_t *
565 uct_genmove(struct engine *e, struct board *b, struct time_info *ti, enum stone color, bool pass_all_alive)
567 struct uct *u = e->data;
569 if (b->superko_violation) {
570 fprintf(stderr, "!!! WARNING: SUPERKO VIOLATION OCCURED BEFORE THIS MOVE\n");
571 fprintf(stderr, "Maybe you play with situational instead of positional superko?\n");
572 fprintf(stderr, "I'm going to ignore the violation, but note that I may miss\n");
573 fprintf(stderr, "some moves valid under this ruleset because of this.\n");
574 b->superko_violation = false;
577 /* Seed the tree. */
578 uct_pondering_stop(u);
579 prepare_move(e, b, color);
580 assert(u->t);
582 /* Perform the Monte Carlo Tree Search! */
583 int played_games = uct_search(u, b, ti, color, u->t);
585 /* Choose the best move from the tree. */
586 struct tree_node *best = u->policy->choose(u->policy, u->t->root, b, color);
587 if (!best) {
588 reset_state(u);
589 return coord_copy(pass);
591 if (UDEBUGL(1))
592 fprintf(stderr, "*** WINNER is %s (%d,%d) with score %1.4f (%d/%d:%d games)\n",
593 coord2sstr(best->coord, b), coord_x(best->coord, b), coord_y(best->coord, b),
594 tree_node_get_value(u->t, 1, best->u.value),
595 best->u.playouts, u->t->root->u.playouts, played_games);
596 if (tree_node_get_value(u->t, 1, best->u.value) < u->resign_ratio && !is_pass(best->coord)) {
597 reset_state(u);
598 return coord_copy(resign);
601 /* If the opponent just passed and we win counting, always
602 * pass as well. */
603 if (b->moves > 1 && is_pass(b->last_move.coord)) {
604 /* Make sure enough playouts are simulated. */
605 while (u->ownermap.playouts < GJ_MINGAMES)
606 uct_playout(u, b, color, u->t);
607 if (uct_pass_is_safe(u, b, color, u->pass_all_alive || pass_all_alive)) {
608 if (UDEBUGL(0))
609 fprintf(stderr, "<Will rather pass, looks safe enough.>\n");
610 best->coord = pass;
614 tree_promote_node(u->t, best);
615 /* After a pass, pondering is harmful for two reasons:
616 * (i) We might keep pondering even when the game is over.
617 * Of course this is the case for opponent resign as well.
618 * (ii) More importantly, the ownermap will get skewed since
619 * the UCT will start cutting off any playouts. */
620 if (u->pondering && !is_pass(best->coord)) {
621 uct_pondering_start(u, b, u->t, stone_other(color));
623 return coord_copy(best->coord);
627 bool
628 uct_genbook(struct engine *e, struct board *b, struct time_info *ti, enum stone color)
630 struct uct *u = e->data;
631 if (!u->t) prepare_move(e, b, color);
632 assert(u->t);
634 if (ti->dim == TD_GAMES) {
635 /* Don't count in games that already went into the book. */
636 ti->len.games += u->t->root->u.playouts;
638 uct_search(u, b, ti, color, u->t);
640 assert(ti->dim == TD_GAMES);
641 tree_save(u->t, b, ti->len.games / 100);
643 return true;
646 void
647 uct_dumpbook(struct engine *e, struct board *b, enum stone color)
649 struct tree *t = tree_init(b, color);
650 tree_load(t, b);
651 tree_dump(t, 0);
652 tree_done(t);
656 struct uct *
657 uct_state_init(char *arg, struct board *b)
659 struct uct *u = calloc(1, sizeof(struct uct));
661 u->debug_level = 1;
662 u->gamelen = MC_GAMELEN;
663 u->mercymin = 0;
664 u->expand_p = 2;
665 u->dumpthres = 1000;
666 u->playout_amaf = true;
667 u->playout_amaf_nakade = false;
668 u->amaf_prior = false;
669 u->max_tree_size = 3072ULL * 1048576;
671 if (board_size(b) - 2 >= 19)
672 u->dynkomi = 200;
673 u->dynkomi_mask = S_BLACK;
675 u->threads = 1;
676 u->thread_model = TM_TREEVL;
677 u->parallel_tree = true;
678 u->virtual_loss = true;
680 u->val_scale = 0.02; u->val_points = 20;
682 if (arg) {
683 char *optspec, *next = arg;
684 while (*next) {
685 optspec = next;
686 next += strcspn(next, ",");
687 if (*next) { *next++ = 0; } else { *next = 0; }
689 char *optname = optspec;
690 char *optval = strchr(optspec, '=');
691 if (optval) *optval++ = 0;
693 if (!strcasecmp(optname, "debug")) {
694 if (optval)
695 u->debug_level = atoi(optval);
696 else
697 u->debug_level++;
698 } else if (!strcasecmp(optname, "mercy") && optval) {
699 /* Minimal difference of black/white captures
700 * to stop playout - "Mercy Rule". Speeds up
701 * hopeless playouts at the expense of some
702 * accuracy. */
703 u->mercymin = atoi(optval);
704 } else if (!strcasecmp(optname, "gamelen") && optval) {
705 u->gamelen = atoi(optval);
706 } else if (!strcasecmp(optname, "expand_p") && optval) {
707 u->expand_p = atoi(optval);
708 } else if (!strcasecmp(optname, "dumpthres") && optval) {
709 u->dumpthres = atoi(optval);
710 } else if (!strcasecmp(optname, "playout_amaf")) {
711 /* Whether to include random playout moves in
712 * AMAF as well. (Otherwise, only tree moves
713 * are included in AMAF. Of course makes sense
714 * only in connection with an AMAF policy.) */
715 /* with-without: 55.5% (+-4.1) */
716 if (optval && *optval == '0')
717 u->playout_amaf = false;
718 else
719 u->playout_amaf = true;
720 } else if (!strcasecmp(optname, "playout_amaf_nakade")) {
721 /* Whether to include nakade moves from playouts
722 * in the AMAF statistics; this tends to nullify
723 * the playout_amaf effect by adding too much
724 * noise. */
725 if (optval && *optval == '0')
726 u->playout_amaf_nakade = false;
727 else
728 u->playout_amaf_nakade = true;
729 } else if (!strcasecmp(optname, "playout_amaf_cutoff") && optval) {
730 /* Keep only first N% of playout stage AMAF
731 * information. */
732 u->playout_amaf_cutoff = atoi(optval);
733 } else if ((!strcasecmp(optname, "policy") || !strcasecmp(optname, "random_policy")) && optval) {
734 char *policyarg = strchr(optval, ':');
735 struct uct_policy **p = !strcasecmp(optname, "policy") ? &u->policy : &u->random_policy;
736 if (policyarg)
737 *policyarg++ = 0;
738 if (!strcasecmp(optval, "ucb1")) {
739 *p = policy_ucb1_init(u, policyarg);
740 } else if (!strcasecmp(optval, "ucb1amaf")) {
741 *p = policy_ucb1amaf_init(u, policyarg);
742 } else {
743 fprintf(stderr, "UCT: Invalid tree policy %s\n", optval);
744 exit(1);
746 } else if (!strcasecmp(optname, "playout") && optval) {
747 char *playoutarg = strchr(optval, ':');
748 if (playoutarg)
749 *playoutarg++ = 0;
750 if (!strcasecmp(optval, "moggy")) {
751 u->playout = playout_moggy_init(playoutarg);
752 } else if (!strcasecmp(optval, "light")) {
753 u->playout = playout_light_init(playoutarg);
754 } else if (!strcasecmp(optval, "elo")) {
755 u->playout = playout_elo_init(playoutarg);
756 } else {
757 fprintf(stderr, "UCT: Invalid playout policy %s\n", optval);
758 exit(1);
760 } else if (!strcasecmp(optname, "prior") && optval) {
761 u->prior = uct_prior_init(optval, b);
762 } else if (!strcasecmp(optname, "amaf_prior") && optval) {
763 u->amaf_prior = atoi(optval);
764 } else if (!strcasecmp(optname, "threads") && optval) {
765 /* By default, Pachi will run with only single
766 * tree search thread! */
767 u->threads = atoi(optval);
768 } else if (!strcasecmp(optname, "thread_model") && optval) {
769 if (!strcasecmp(optval, "root")) {
770 /* Root parallelization - each thread
771 * does independent search, trees are
772 * merged at the end. */
773 u->thread_model = TM_ROOT;
774 u->parallel_tree = false;
775 u->virtual_loss = false;
776 } else if (!strcasecmp(optval, "tree")) {
777 /* Tree parallelization - all threads
778 * grind on the same tree. */
779 u->thread_model = TM_TREE;
780 u->parallel_tree = true;
781 u->virtual_loss = false;
782 } else if (!strcasecmp(optval, "treevl")) {
783 /* Tree parallelization, but also
784 * with virtual losses - this discou-
785 * rages most threads choosing the
786 * same tree branches to read. */
787 u->thread_model = TM_TREEVL;
788 u->parallel_tree = true;
789 u->virtual_loss = true;
790 } else {
791 fprintf(stderr, "UCT: Invalid thread model %s\n", optval);
792 exit(1);
794 } else if (!strcasecmp(optname, "pondering")) {
795 /* Keep searching even during opponent's turn. */
796 u->pondering = !optval || atoi(optval);
797 } else if (!strcasecmp(optname, "force_seed") && optval) {
798 u->force_seed = atoi(optval);
799 } else if (!strcasecmp(optname, "no_book")) {
800 u->no_book = true;
801 } else if (!strcasecmp(optname, "dynkomi")) {
802 /* Dynamic komi in handicap game; linearly
803 * decreases to basic settings until move
804 * #optval. */
805 u->dynkomi = optval ? atoi(optval) : 150;
806 } else if (!strcasecmp(optname, "dynkomi_mask") && optval) {
807 /* Bitmask of colors the player must be
808 * for dynkomi be applied; you may want
809 * to use dynkomi_mask=3 to allow dynkomi
810 * even in games where Pachi is white. */
811 u->dynkomi_mask = atoi(optval);
812 } else if (!strcasecmp(optname, "val_scale") && optval) {
813 /* How much of the game result value should be
814 * influenced by win size. Zero means it isn't. */
815 u->val_scale = atof(optval);
816 } else if (!strcasecmp(optname, "val_points") && optval) {
817 /* Maximum size of win to be scaled into game
818 * result value. Zero means boardsize^2. */
819 u->val_points = atoi(optval) * 2; // result values are doubled
820 } else if (!strcasecmp(optname, "val_extra")) {
821 /* If false, the score coefficient will be simply
822 * added to the value, instead of scaling the result
823 * coefficient because of it. */
824 u->val_extra = !optval || atoi(optval);
825 } else if (!strcasecmp(optname, "root_heuristic") && optval) {
826 /* Whether to bias exploration by root node values
827 * (must be supported by the used policy).
828 * 0: Don't.
829 * 1: Do, value = result.
830 * Try to temper the result:
831 * 2: Do, value = 0.5+(result-expected)/2.
832 * 3: Do, value = 0.5+bzz((result-expected)^2). */
833 u->root_heuristic = atoi(optval);
834 } else if (!strcasecmp(optname, "pass_all_alive")) {
835 /* Whether to consider all stones alive at the game
836 * end instead of marking dead groupd. */
837 u->pass_all_alive = !optval || atoi(optval);
838 } else if (!strcasecmp(optname, "random_policy_chance") && optval) {
839 /* If specified (N), with probability 1/N, random_policy policy
840 * descend is used instead of main policy descend; useful
841 * if specified policy (e.g. UCB1AMAF) can make unduly biased
842 * choices sometimes, you can fall back to e.g.
843 * random_policy=UCB1. */
844 u->random_policy_chance = atoi(optval);
845 } else if (!strcasecmp(optname, "max_tree_size") && optval) {
846 /* Maximum amount of memory [MiB] consumed by the move tree.
847 * Default is 3072 (3 GiB). Note that if you use TM_ROOT,
848 * this limits size of only one of the trees, not all of them
849 * together. */
850 u->max_tree_size = atol(optval) * 1048576;
851 } else if (!strcasecmp(optname, "banner") && optval) {
852 /* Additional banner string. This must come as the
853 * last engine parameter. */
854 if (*next) *--next = ',';
855 u->banner = strdup(optval);
856 break;
857 } else {
858 fprintf(stderr, "uct: Invalid engine argument %s or missing value\n", optname);
859 exit(1);
864 u->resign_ratio = 0.2; /* Resign when most games are lost. */
865 u->loss_threshold = 0.85; /* Stop reading if after at least 5000 playouts this is best value. */
866 if (!u->policy)
867 u->policy = policy_ucb1amaf_init(u, NULL);
869 if (!!u->random_policy_chance ^ !!u->random_policy) {
870 fprintf(stderr, "uct: Only one of random_policy and random_policy_chance is set\n");
871 exit(1);
874 if (!u->prior)
875 u->prior = uct_prior_init(NULL, b);
877 if (!u->playout)
878 u->playout = playout_moggy_init(NULL);
879 u->playout->debug_level = u->debug_level;
881 u->ownermap.map = malloc(board_size2(b) * sizeof(u->ownermap.map[0]));
883 /* Some things remain uninitialized for now - the opening book
884 * is not loaded and the tree not set up. */
885 /* This will be initialized in setup_state() at the first move
886 * received/requested. This is because right now we are not aware
887 * about any komi or handicap setup and such. */
889 return u;
892 struct engine *
893 engine_uct_init(char *arg, struct board *b)
895 struct uct *u = uct_state_init(arg, b);
896 struct engine *e = calloc(1, sizeof(struct engine));
897 e->name = "UCT Engine";
898 e->printhook = uct_printhook_ownermap;
899 e->notify_play = uct_notify_play;
900 e->chat = uct_chat;
901 e->genmove = uct_genmove;
902 e->dead_group_list = uct_dead_group_list;
903 e->done = uct_done;
904 e->data = u;
906 const char banner[] = "I'm playing UCT. When I'm losing, I will resign, "
907 "if I think I win, I play until you pass. "
908 "Anyone can send me 'winrate' in private chat to get my assessment of the position.";
909 if (!u->banner) u->banner = "";
910 e->comment = malloc(sizeof(banner) + strlen(u->banner) + 1);
911 sprintf(e->comment, "%s %s", banner, u->banner);
913 return e;