Replace UCT/montecarlo games=N parameter with -t =N main zzgo parameter
[pachi.git] / uct / uct.c
blob9382508738035e3a4d1288fd82ee0e0e564d2072
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 /* Run time-limited MCTS search on foreground. */
428 /* If dyngames, just make sure the tree root has required number of simulations
429 * done (incl. inherited simulations). If !dyngames, full number of simulations
430 * is simulated in this search. */
431 static int
432 uct_search(struct uct *u, struct board *b, struct time_info *ti, enum stone color, struct tree *t, bool dyngames)
434 /* Required games limit as to be seen in the tree root u.playouts. */
435 int games = ti->len.games;
436 if (u->t->root->u.playouts > 0) {
437 if (dyngames) {
438 if (UDEBUGL(2))
439 fprintf(stderr, "<pre-simulated %d games skipped>\n", u->t->root->u.playouts);
440 } else {
441 games += u->t->root->u.playouts;
445 /* Number of last game with progress print. */
446 int last_print = t->root->u.playouts;
447 /* Number of simulations to wait before next print. */
448 int print_interval = TREE_SIMPROGRESS_INTERVAL * (u->thread_model == TM_ROOT ? 1 : u->threads);
449 /* Printed notification about full memory? */
450 bool print_fullmem = false;
452 struct spawn_ctx *ctx = uct_search_start(u, b, color, t);
454 /* The search tree is ctx->t. This is normally == t, but in case of
455 * TM_ROOT, it is one of the trees belonging to the independent
456 * workers. It is important to reference ctx->t directly since the
457 * thread manager will swap the tree pointer asynchronously. */
458 /* XXX: This means TM_ROOT support is suboptimal since single stalled
459 * thread can stall the others in case of limiting the search by game
460 * count. However, TM_ROOT just does not deserve any more extra code
461 * right now. */
463 /* Now, just periodically poll the search tree. */
464 struct timespec busywait_interval = TREE_BUSYWAIT_INTERVAL;
465 while (1) {
466 nanosleep(&busywait_interval, NULL);
467 int i = ctx->t->root->u.playouts;
469 /* Print progress? */
470 if (i - last_print > print_interval) {
471 last_print += print_interval; // keep the numbers tidy
472 uct_progress_status(u, ctx->t, color, last_print);
474 if (!print_fullmem && ctx->t->nodes_size > u->max_tree_size) {
475 if (UDEBUGL(2))
476 fprintf(stderr, "memory limit hit (%ld > %lu)\n", ctx->t->nodes_size, u->max_tree_size);
477 print_fullmem = true;
480 /* Did we play enough games? */
481 if (i > games)
482 break;
483 /* Won situation? */
484 struct tree_node *best = u->policy->choose(u->policy, ctx->t->root, b, color);
485 if (best && ((best->u.playouts >= 2000 && tree_node_get_value(ctx->t, 1, best->u.value) >= u->loss_threshold)
486 || (best->u.playouts >= 500 && tree_node_get_value(ctx->t, 1, best->u.value) >= 0.95)))
487 break;
490 ctx = uct_search_stop();
492 if (UDEBUGL(2))
493 tree_dump(t, u->dumpthres);
494 if (UDEBUGL(0))
495 uct_progress_status(u, t, color, ctx->games);
497 return ctx->games;
501 /* Start pondering background with @color to play. */
502 static void
503 uct_pondering_start(struct uct *u, struct board *b0, struct tree *t, enum stone color)
505 if (UDEBUGL(1))
506 fprintf(stderr, "Starting to ponder with color %s\n", stone2str(stone_other(color)));
508 /* We need a local board copy to ponder upon. */
509 struct board *b = malloc(sizeof(*b)); board_copy(b, b0);
511 /* *b0 did not have the genmove'd move played yet. */
512 struct move m = { t->root->coord, t->root_color };
513 int res = board_play(b, &m);
514 assert(res >= 0);
516 /* Start MCTS manager thread "headless". */
517 uct_search_start(u, b, color, t);
520 /* uct_search_stop() frontend for the pondering (non-genmove) mode. */
521 static void
522 uct_pondering_stop(struct uct *u)
524 if (!thread_manager_running)
525 return;
527 /* Stop the thread manager. */
528 struct spawn_ctx *ctx = uct_search_stop();
529 if (UDEBUGL(1)) {
530 fprintf(stderr, "(pondering) ");
531 uct_progress_status(u, ctx->t, ctx->color, ctx->games);
533 free(ctx->b);
537 void
538 time_prep(struct time_info *ti)
540 if (ti->period == TT_TOTAL) {
541 fprintf(stderr, "Warning: TT_TOTAL time mode not supported, resetting to defaults.\n");
542 ti->period = TT_NULL;
543 } else if (ti->dim == TD_WALLTIME) {
544 fprintf(stderr, "Warning: TD_WALLTIME time mode not supported, resetting to defaults.\n");
545 ti->period = TT_NULL;
547 if (ti->period == TT_NULL) {
548 ti->period = TT_MOVE;
549 ti->dim = TD_GAMES;
550 ti->len.games = MC_GAMES;
554 static coord_t *
555 uct_genmove(struct engine *e, struct board *b, struct time_info *ti, enum stone color, bool pass_all_alive)
557 struct uct *u = e->data;
559 if (b->superko_violation) {
560 fprintf(stderr, "!!! WARNING: SUPERKO VIOLATION OCCURED BEFORE THIS MOVE\n");
561 fprintf(stderr, "Maybe you play with situational instead of positional superko?\n");
562 fprintf(stderr, "I'm going to ignore the violation, but note that I may miss\n");
563 fprintf(stderr, "some moves valid under this ruleset because of this.\n");
564 b->superko_violation = false;
567 time_prep(ti);
569 /* Seed the tree. */
570 uct_pondering_stop(u);
571 prepare_move(e, b, color);
572 assert(u->t);
574 /* Perform the Monte Carlo Tree Search! */
575 int played_games = uct_search(u, b, ti, color, u->t, true);
577 /* Choose the best move from the tree. */
578 struct tree_node *best = u->policy->choose(u->policy, u->t->root, b, color);
579 if (!best) {
580 reset_state(u);
581 return coord_copy(pass);
583 if (UDEBUGL(1))
584 fprintf(stderr, "*** WINNER is %s (%d,%d) with score %1.4f (%d/%d:%d games)\n",
585 coord2sstr(best->coord, b), coord_x(best->coord, b), coord_y(best->coord, b),
586 tree_node_get_value(u->t, 1, best->u.value),
587 best->u.playouts, u->t->root->u.playouts, played_games);
588 if (tree_node_get_value(u->t, 1, best->u.value) < u->resign_ratio && !is_pass(best->coord)) {
589 reset_state(u);
590 return coord_copy(resign);
593 /* If the opponent just passed and we win counting, always
594 * pass as well. */
595 if (b->moves > 1 && is_pass(b->last_move.coord)) {
596 /* Make sure enough playouts are simulated. */
597 while (u->ownermap.playouts < GJ_MINGAMES)
598 uct_playout(u, b, color, u->t);
599 if (uct_pass_is_safe(u, b, color, u->pass_all_alive || pass_all_alive)) {
600 if (UDEBUGL(0))
601 fprintf(stderr, "<Will rather pass, looks safe enough.>\n");
602 best->coord = pass;
606 tree_promote_node(u->t, best);
607 /* After a pass, pondering is harmful for two reasons:
608 * (i) We might keep pondering even when the game is over.
609 * Of course this is the case for opponent resign as well.
610 * (ii) More importantly, the ownermap will get skewed since
611 * the UCT will start cutting off any playouts. */
612 if (u->pondering && !is_pass(best->coord)) {
613 uct_pondering_start(u, b, u->t, stone_other(color));
615 return coord_copy(best->coord);
619 bool
620 uct_genbook(struct engine *e, struct board *b, struct time_info *ti, enum stone color)
622 struct uct *u = e->data;
623 time_prep(ti);
625 if (!u->t) prepare_move(e, b, color);
626 assert(u->t);
628 uct_search(u, b, ti, color, u->t, false);
630 assert(ti->dim == TD_GAMES);
631 tree_save(u->t, b, ti->len.games / 100);
633 return true;
636 void
637 uct_dumpbook(struct engine *e, struct board *b, enum stone color)
639 struct tree *t = tree_init(b, color);
640 tree_load(t, b);
641 tree_dump(t, 0);
642 tree_done(t);
646 struct uct *
647 uct_state_init(char *arg, struct board *b)
649 struct uct *u = calloc(1, sizeof(struct uct));
651 u->debug_level = 1;
652 u->gamelen = MC_GAMELEN;
653 u->mercymin = 0;
654 u->expand_p = 2;
655 u->dumpthres = 1000;
656 u->playout_amaf = true;
657 u->playout_amaf_nakade = false;
658 u->amaf_prior = false;
659 u->max_tree_size = 3072ULL * 1048576;
661 if (board_size(b) - 2 >= 19)
662 u->dynkomi = 200;
663 u->dynkomi_mask = S_BLACK;
665 u->threads = 1;
666 u->thread_model = TM_TREEVL;
667 u->parallel_tree = true;
668 u->virtual_loss = true;
670 u->val_scale = 0.02; u->val_points = 20;
672 if (arg) {
673 char *optspec, *next = arg;
674 while (*next) {
675 optspec = next;
676 next += strcspn(next, ",");
677 if (*next) { *next++ = 0; } else { *next = 0; }
679 char *optname = optspec;
680 char *optval = strchr(optspec, '=');
681 if (optval) *optval++ = 0;
683 if (!strcasecmp(optname, "debug")) {
684 if (optval)
685 u->debug_level = atoi(optval);
686 else
687 u->debug_level++;
688 } else if (!strcasecmp(optname, "mercy") && optval) {
689 /* Minimal difference of black/white captures
690 * to stop playout - "Mercy Rule". Speeds up
691 * hopeless playouts at the expense of some
692 * accuracy. */
693 u->mercymin = atoi(optval);
694 } else if (!strcasecmp(optname, "gamelen") && optval) {
695 u->gamelen = atoi(optval);
696 } else if (!strcasecmp(optname, "expand_p") && optval) {
697 u->expand_p = atoi(optval);
698 } else if (!strcasecmp(optname, "dumpthres") && optval) {
699 u->dumpthres = atoi(optval);
700 } else if (!strcasecmp(optname, "playout_amaf")) {
701 /* Whether to include random playout moves in
702 * AMAF as well. (Otherwise, only tree moves
703 * are included in AMAF. Of course makes sense
704 * only in connection with an AMAF policy.) */
705 /* with-without: 55.5% (+-4.1) */
706 if (optval && *optval == '0')
707 u->playout_amaf = false;
708 else
709 u->playout_amaf = true;
710 } else if (!strcasecmp(optname, "playout_amaf_nakade")) {
711 /* Whether to include nakade moves from playouts
712 * in the AMAF statistics; this tends to nullify
713 * the playout_amaf effect by adding too much
714 * noise. */
715 if (optval && *optval == '0')
716 u->playout_amaf_nakade = false;
717 else
718 u->playout_amaf_nakade = true;
719 } else if (!strcasecmp(optname, "playout_amaf_cutoff") && optval) {
720 /* Keep only first N% of playout stage AMAF
721 * information. */
722 u->playout_amaf_cutoff = atoi(optval);
723 } else if ((!strcasecmp(optname, "policy") || !strcasecmp(optname, "random_policy")) && optval) {
724 char *policyarg = strchr(optval, ':');
725 struct uct_policy **p = !strcasecmp(optname, "policy") ? &u->policy : &u->random_policy;
726 if (policyarg)
727 *policyarg++ = 0;
728 if (!strcasecmp(optval, "ucb1")) {
729 *p = policy_ucb1_init(u, policyarg);
730 } else if (!strcasecmp(optval, "ucb1amaf")) {
731 *p = policy_ucb1amaf_init(u, policyarg);
732 } else {
733 fprintf(stderr, "UCT: Invalid tree policy %s\n", optval);
734 exit(1);
736 } else if (!strcasecmp(optname, "playout") && optval) {
737 char *playoutarg = strchr(optval, ':');
738 if (playoutarg)
739 *playoutarg++ = 0;
740 if (!strcasecmp(optval, "moggy")) {
741 u->playout = playout_moggy_init(playoutarg);
742 } else if (!strcasecmp(optval, "light")) {
743 u->playout = playout_light_init(playoutarg);
744 } else if (!strcasecmp(optval, "elo")) {
745 u->playout = playout_elo_init(playoutarg);
746 } else {
747 fprintf(stderr, "UCT: Invalid playout policy %s\n", optval);
748 exit(1);
750 } else if (!strcasecmp(optname, "prior") && optval) {
751 u->prior = uct_prior_init(optval, b);
752 } else if (!strcasecmp(optname, "amaf_prior") && optval) {
753 u->amaf_prior = atoi(optval);
754 } else if (!strcasecmp(optname, "threads") && optval) {
755 /* By default, Pachi will run with only single
756 * tree search thread! */
757 u->threads = atoi(optval);
758 } else if (!strcasecmp(optname, "thread_model") && optval) {
759 if (!strcasecmp(optval, "root")) {
760 /* Root parallelization - each thread
761 * does independent search, trees are
762 * merged at the end. */
763 u->thread_model = TM_ROOT;
764 u->parallel_tree = false;
765 u->virtual_loss = false;
766 } else if (!strcasecmp(optval, "tree")) {
767 /* Tree parallelization - all threads
768 * grind on the same tree. */
769 u->thread_model = TM_TREE;
770 u->parallel_tree = true;
771 u->virtual_loss = false;
772 } else if (!strcasecmp(optval, "treevl")) {
773 /* Tree parallelization, but also
774 * with virtual losses - this discou-
775 * rages most threads choosing the
776 * same tree branches to read. */
777 u->thread_model = TM_TREEVL;
778 u->parallel_tree = true;
779 u->virtual_loss = true;
780 } else {
781 fprintf(stderr, "UCT: Invalid thread model %s\n", optval);
782 exit(1);
784 } else if (!strcasecmp(optname, "pondering")) {
785 /* Keep searching even during opponent's turn. */
786 u->pondering = !optval || atoi(optval);
787 } else if (!strcasecmp(optname, "force_seed") && optval) {
788 u->force_seed = atoi(optval);
789 } else if (!strcasecmp(optname, "no_book")) {
790 u->no_book = true;
791 } else if (!strcasecmp(optname, "dynkomi")) {
792 /* Dynamic komi in handicap game; linearly
793 * decreases to basic settings until move
794 * #optval. */
795 u->dynkomi = optval ? atoi(optval) : 150;
796 } else if (!strcasecmp(optname, "dynkomi_mask") && optval) {
797 /* Bitmask of colors the player must be
798 * for dynkomi be applied; you may want
799 * to use dynkomi_mask=3 to allow dynkomi
800 * even in games where Pachi is white. */
801 u->dynkomi_mask = atoi(optval);
802 } else if (!strcasecmp(optname, "val_scale") && optval) {
803 /* How much of the game result value should be
804 * influenced by win size. Zero means it isn't. */
805 u->val_scale = atof(optval);
806 } else if (!strcasecmp(optname, "val_points") && optval) {
807 /* Maximum size of win to be scaled into game
808 * result value. Zero means boardsize^2. */
809 u->val_points = atoi(optval) * 2; // result values are doubled
810 } else if (!strcasecmp(optname, "val_extra")) {
811 /* If false, the score coefficient will be simply
812 * added to the value, instead of scaling the result
813 * coefficient because of it. */
814 u->val_extra = !optval || atoi(optval);
815 } else if (!strcasecmp(optname, "root_heuristic") && optval) {
816 /* Whether to bias exploration by root node values
817 * (must be supported by the used policy).
818 * 0: Don't.
819 * 1: Do, value = result.
820 * Try to temper the result:
821 * 2: Do, value = 0.5+(result-expected)/2.
822 * 3: Do, value = 0.5+bzz((result-expected)^2). */
823 u->root_heuristic = atoi(optval);
824 } else if (!strcasecmp(optname, "pass_all_alive")) {
825 /* Whether to consider all stones alive at the game
826 * end instead of marking dead groupd. */
827 u->pass_all_alive = !optval || atoi(optval);
828 } else if (!strcasecmp(optname, "random_policy_chance") && optval) {
829 /* If specified (N), with probability 1/N, random_policy policy
830 * descend is used instead of main policy descend; useful
831 * if specified policy (e.g. UCB1AMAF) can make unduly biased
832 * choices sometimes, you can fall back to e.g.
833 * random_policy=UCB1. */
834 u->random_policy_chance = atoi(optval);
835 } else if (!strcasecmp(optname, "max_tree_size") && optval) {
836 /* Maximum amount of memory [MiB] consumed by the move tree.
837 * Default is 3072 (3 GiB). Note that if you use TM_ROOT,
838 * this limits size of only one of the trees, not all of them
839 * together. */
840 u->max_tree_size = atol(optval) * 1048576;
841 } else if (!strcasecmp(optname, "banner") && optval) {
842 /* Additional banner string. This must come as the
843 * last engine parameter. */
844 if (*next) *--next = ',';
845 u->banner = strdup(optval);
846 break;
847 } else {
848 fprintf(stderr, "uct: Invalid engine argument %s or missing value\n", optname);
849 exit(1);
854 u->resign_ratio = 0.2; /* Resign when most games are lost. */
855 u->loss_threshold = 0.85; /* Stop reading if after at least 5000 playouts this is best value. */
856 if (!u->policy)
857 u->policy = policy_ucb1amaf_init(u, NULL);
859 if (!!u->random_policy_chance ^ !!u->random_policy) {
860 fprintf(stderr, "uct: Only one of random_policy and random_policy_chance is set\n");
861 exit(1);
864 if (!u->prior)
865 u->prior = uct_prior_init(NULL, b);
867 if (!u->playout)
868 u->playout = playout_moggy_init(NULL);
869 u->playout->debug_level = u->debug_level;
871 u->ownermap.map = malloc(board_size2(b) * sizeof(u->ownermap.map[0]));
873 /* Some things remain uninitialized for now - the opening book
874 * is not loaded and the tree not set up. */
875 /* This will be initialized in setup_state() at the first move
876 * received/requested. This is because right now we are not aware
877 * about any komi or handicap setup and such. */
879 return u;
882 struct engine *
883 engine_uct_init(char *arg, struct board *b)
885 struct uct *u = uct_state_init(arg, b);
886 struct engine *e = calloc(1, sizeof(struct engine));
887 e->name = "UCT Engine";
888 e->printhook = uct_printhook_ownermap;
889 e->notify_play = uct_notify_play;
890 e->chat = uct_chat;
891 e->genmove = uct_genmove;
892 e->dead_group_list = uct_dead_group_list;
893 e->done = uct_done;
894 e->data = u;
896 const char banner[] = "I'm playing UCT. When I'm losing, I will resign, "
897 "if I think I win, I play until you pass. "
898 "Anyone can send me 'winrate' in private chat to get my assessment of the position.";
899 if (!u->banner) u->banner = "";
900 e->comment = malloc(sizeof(banner) + strlen(u->banner) + 1);
901 sprintf(e->comment, "%s %s", banner, u->banner);
903 return e;