My Project
book.c
Go to the documentation of this file.
1
18#include "book.h"
19#include "base.h"
20#include "search.h"
21#include "const.h"
22#include "bit.h"
23#include "options.h"
24#include "util.h"
25
26#include <assert.h>
27#include <time.h>
28#include <stdarg.h>
29#include <limits.h>
30
31#define BOOK_DEBUG 0
32static const int BOOK_INFO_RESOLUTION = 100000;
33
34#define clear_line() bprint(" \r")
35
36bool book_verbose = false;
37
43static void bprint(const char *format, ...)
44{
45 if (book_verbose) {
46 va_list args;
47
48 va_start(args, format);
49 vprintf(format, args);
50 va_end(args);
51 fflush (stdout);
52
53 }
54}
55
57
64static inline bool link_read(Link *link, FILE *f)
65{
66 int r;
67 r = fread(&link->score, 1, 1, f);
68 r += fread(&link->move, 1, 1, f);
69 return r == 2;
70}
71
78static inline bool link_write(const Link *link, FILE *f)
79{
80 int r;
81 r = fwrite(&link->score, 1, 1, f);
82 r += fwrite(&link->move, 1, 1, f);
83 return r == 2;
84}
85
92static inline bool link_is_bad(const Link *link)
93{
94 return link->score == -SCORE_INF;
95}
96
97static Position* book_probe(const Book*, const Board*);
98static void book_add(Book*, const Position*);
99static void position_print(const Position*, const Board*, FILE*);
100
101#define foreach_link(l, p) \
102 for ((l) = (p)->link; (l) < (p)->link + (p)->n_link; ++(l))
103
110static int get_book_depth(const int depth)
111{
112 if (depth <= 10) return 60 - 2 * depth;
113 else if (depth <= 18) return 39;
114 else if (depth <= 24) return 36;
115 else if (depth < 30) return 33;
116 else if (depth < 36) return 30;
117 else if (depth < 42) return 66 - depth;
118 else return 24;
119}
120
121
130static bool position_is_ok(const Position *position)
131{
132 Board board[1];
133 Move move[1];
134 const Link *l;
135 int i, j;
136 char s[4];
137
138 // board is legal ?
139 if (position->board->player & position->board->opponent) {
140 warn("Board is illegal: Two discs on the same square?\n");
141 board_print(position->board, BLACK, stderr);
142 return false;
143 }
144 if (((position->board->player|position->board->opponent) & 0x0000001818000000ULL) != 0x0000001818000000ULL) {
145 warn("Board is illegal: Empty center?\n");
146 board_print(position->board, BLACK, stderr);
147 return false;
148 }
149
150 // is board unique
151 board_unique(position->board, board);
152 if (!board_equal(position->board, board)) {
153 warn("board is not unique\n");
154 position_print(position, position->board, stdout);
155 return false;
156 }
157
158 // are moves legal ?
159 foreach_link(l, position) {
160 if (l->move == PASS) {
161 if (position->n_link > 1
162 || can_move(board->player, board->opponent)
163 || !can_move(board->opponent, board->player)) {
164 warn("passing move is wrong\n");
165 position_print(position, position->board, stdout);
166 return false;
167 }
168 } else {
169 if (/*l->move < A1 ||*/ l->move > H8
170 || board_is_occupied(board, l->move)
171 || board_get_move(board, l->move, move) == 0) {
172 warn("link %s is wrong\n", move_to_string(l->move, WHITE, s));
173 position_print(position, position->board, stdout);
174 return false;
175 }
176 }
177 }
178
179 l = &position->leaf;
180 if (l->move == PASS) {
181 if (position->n_link > 0
182 || can_move(board->player, board->opponent)
183 || !can_move(board->opponent, board->player)) {
184 warn("passing move is wrong\n");
185 position_print(position, position->board, stdout);
186 return false;
187 }
188 } else if (l->move == NOMOVE) {
189 if (get_mobility(position->board->player, position->board->opponent) != position->n_link && !(position->n_link == 1 && position->link->move == PASS)) {
190 warn("nomove is wrong\n");
191 position_print(position, position->board, stdout);
192 return false;
193 }
194 } else if (/*l->move < A1 ||*/ l->move > H8
195 || board_is_occupied(board, l->move)
196 || board_get_move(board, l->move, move) == 0) {
197 warn("leaf %s is wrong\n", move_to_string(l->move, WHITE, s));
198 position_print(position, position->board, stdout);
199 return false;
200 }
201
202 // doublons ?
203 for (i = 0; i < position->n_link; ++i) {
204 for (j = i + 1; j < position->n_link; ++j) {
205 if (position->link[j].move == position->link[i].move) {
206 warn("doublon found in links\n");
207 position_print(position, position->board, stdout);
208 return false;
209 }
210 }
211 if (position->leaf.move == position->link[i].move) {
212 warn("doublon found in links/leaf\n");
213 position_print(position, position->board, stdout);
214 return false;
215 }
216 }
217 return true;
218}
219
225static void position_init(Position *position)
226{
227 position->board->player = position->board->opponent = 0;
228
229 position->leaf = BAD_LINK;
230 position->link = NULL;
231
232 position->n_wins = position->n_draws = position->n_losses = position->n_lines = 0;
233 position->score.value = position->score.lower = -SCORE_INF;
234 position->score.upper = +SCORE_INF;
235
236 position->n_link = 0;
237 position->level = 0;
238 //position->done = true;
239 //position->todo = false;
240 position->flag = FLAG_DONE;
241
242 // add by lavox 2021/8/22
243 position->n_player_bestpaths = position->n_opponent_bestpaths = 0;
244}
245
257static void position_merge(Position *dest, const Position *src)
258{
259 Link *l;
260
261 position_init(dest);
262 *dest->board = *src->board;
263 if (dest->level == src->level) {
264 foreach_link(l, dest) {
265 if (l->move == src->leaf.move) return;
266 }
267 dest->leaf = src->leaf;
268 } else if (dest->level > src->level) {
269 return;
270 } else {
271 dest->leaf = src->leaf;
272 dest->level = src->level;
273 }
274}
275
281static void position_free(Position *position)
282{
283 free(position->link);
284}
285
292static bool position_read(Position *position, FILE *f)
293{
294 int i;
295 int r;
296
297 r = fread(&position->board->player, sizeof (unsigned long long), 1, f);
298 r += fread(&position->board->opponent, sizeof (unsigned long long), 1, f);
299
300 r += fread(&position->n_wins, sizeof (unsigned int), 1, f);
301 r += fread(&position->n_draws, sizeof (unsigned int), 1, f);
302 r += fread(&position->n_losses, sizeof (unsigned int), 1, f);
303 r += fread(&position->n_lines, sizeof (unsigned int), 1, f);
304
305 r += fread(&position->score.value, sizeof (short), 1, f);
306 r += fread(&position->score.lower, sizeof (short), 1, f);
307 r += fread(&position->score.upper, sizeof (short), 1, f);
308
309 r += fread(&position->n_link, 1, 1, f);
310 r += fread(&position->level, 1, 1, f);
311
312 if (r != 11) return false;
313
314 //position->done = position->todo = false;
315 position->flag = 0;
316 // add by lavox 2021/8/22
317 position->n_player_bestpaths = position->n_opponent_bestpaths = 0;
318
319 if (position->n_link) {
320 position->link = (Link*) malloc(sizeof (Link) * position->n_link);
321 if (position->link == NULL) error("cannot allocate opening book position's moves\n");
322 for (i = 0; i < position->n_link; ++i) {
323 if (!link_read(position->link + i, f)) return false;
324 }
325 } else {
326 position->link = NULL;
327 }
328 if (!link_read(&position->leaf, f)) return false;
329
330 return true;
331}
332
339static bool position_import(Position *position, FILE *f)
340{
341 char *line, *s, *old;
342 int value;
343 Move move[1];
344 bool ok = false;
345
346 if ((line = string_read_line(f)) != NULL) {
347 position_init(position);
348 s = parse_board(line, position->board, &value);
349 if (s != line) {
350 s = parse_find(s, ',');
351 if (*s == ',') {
352 value = -1; s = parse_int(old = s + 1, &value); BOUND(value, -1, 60, "level");
353 if (s != old && value != -1) {
354 position->level = value;
355 s = parse_find(s, ',');
356 if (*s == ',') {
357 s = parse_move(old = s + 1, position->board, move);
358 if (s != old) {
359 s = parse_find(s, ',');
360 if (*s == ',') {
361 s = parse_int(old = s + 1, &value);
362 if (s != old) {
363 position->leaf.move = move->x;
364 position->leaf.score = value;
365 }
366 }
367 }
368 }
369 ok = true;
370 } else {
371 warn("wrong level: %s\n", line);
372 }
373 } else {
374 warn("missing ',' after board setting\n");
375 }
376 } else {
377 warn("wrong board: %s\n", line);
378 }
379 }
380
381 if (!ok) warn("=> wrong position\n");
382
383 free(line);
384 return ok;
385}
386
393static bool position_write(const Position *position, FILE* f)
394{
395 int i;
396 int r;
397
398 r = fwrite(&position->board->player, sizeof (unsigned long long), 1, f);
399 r += fwrite(&position->board->opponent, sizeof (unsigned long long), 1, f);
400
401 r += fwrite(&position->n_wins, sizeof (unsigned int), 1, f);
402 r += fwrite(&position->n_draws, sizeof (unsigned int), 1, f);
403 r += fwrite(&position->n_losses, sizeof (unsigned int), 1, f);
404 r += fwrite(&position->n_lines, sizeof (unsigned int), 1, f);
405
406 r += fwrite(&position->score.value, sizeof (short), 1, f);
407 r += fwrite(&position->score.lower, sizeof (short), 1, f);
408 r += fwrite(&position->score.upper, sizeof (short), 1, f);
409
410 r += fwrite(&position->n_link, 1, 1, f);
411 r += fwrite(&position->level, 1, 1, f);
412
413 if (r != 11) return false;
414
415 for (i = 0; i < position->n_link; ++i)
416 if (!link_write(position->link + i, f)) return false;
417 if (!link_write(&position->leaf, f)) return false;
418
419 return true;
420}
421
428static bool position_export(const Position *p, FILE* f)
429{
430 char b[80], m[4];
431
434 return (fprintf(f, "%s,%d,%s,%d\n", b, p->level, m, p->leaf.score) > 0);
435}
436
442static void position_unique(Position *position)
443{
444 Board board[1];
445 int i, s;
446
447 *board = *position->board;
448 if ((s = board_unique(board, position->board)) != 0) {
449 for (i = 0; i < position->n_link; ++i) {
450 position->link[i].move = symetry(position->link[i].move, s);
451 }
452 position->leaf.move = symetry(position->leaf.move, s);
453 }
454}
455
463static int position_get_moves(const Position *position, const Board *board, MoveList *movelist)
464{
465 Move *previous = movelist->move;
466 Move *move = movelist->move + 1;
467 Board sym[1];
468 int i, x, s;
469
470 for (s = 0; s < 8; ++s) {
471 board_symetry(position->board, s, sym);
472
473 if (board_equal(sym, board)) {
474 for (i = 0; i < position->n_link; ++i) {
475 x = symetry(position->link[i].move, s);
476 board_get_move(board, x, move);
477 move->score = position->link[i].score;
478 previous = previous->next = move;
479 ++move;
480 }
481 x = symetry(position->leaf.move, s);
482 if (x != NOMOVE) {
483 board_get_move(board, x, move);
484 move->score = position->leaf.score;
485 previous = previous->next = move;
486 ++move;
487 }
488 previous->next = NULL;
489 movelist->n_moves = move - movelist->move - 1;
490 movelist_sort(movelist);
491 return s;
492 }
493 }
494
495 fatal_error("unreachable code\n");
496 return -1;
497}
498
506static void position_show(const Position *position, const Board *board, FILE *f)
507{
508 MoveList movelist[1];
509 Move *move;
510 const int n_empties = board_count_empties(board);
511 const int color = n_empties & 1;
512 int sym;
513 char s[4];
514
515 board_print(board, color, f);
516
517 fprintf(f, "\nLevel: %d\n", position->level);
518 fprintf(f, "Best score: %+02d [%+02d, %+02d]\n", position->score.value, position->score.lower, position->score.upper);
519 fprintf(f, "Moves:");
520 sym = position_get_moves(position, board, movelist);
521 foreach_move(move, movelist) {
522 move_to_string(move->x, color, s);
523 if (symetry(position->leaf.move, sym) == move->x) {
524 fprintf(f, " <%s:%+02d>", s, move->score);
525 } else {
526 fprintf(f, " [%s:%+02d]", s, move->score);
527 }
528 }
529}
530
538static void position_print(const Position *position, const Board *board, FILE *f)
539{
540 MoveList movelist[1];
541 Move *move;
542 int color = board_count_empties(board) & 1, sym;
543 char b[80], m[4];
544
545 board_to_string(board, color, b);
546 fprintf(f, "{board:%s; ", b);
547 fprintf(f, "level:%d; ", position->level);
548 fprintf(f, "best: %+02d [%+02d, %+02d];", position->score.value, position->score.lower, position->score.upper);
549 fprintf(f, "moves:");
550 sym = position_get_moves(position, board, movelist);
551 foreach_move(move, movelist) {
552 move_to_string(move->x, color, m);
553 if (symetry(position->leaf.move, sym) == move->x) {
554 fprintf(f, " <%s:%+02d>", m, move->score);
555 } else {
556 fprintf(f, " [%s:%+02d]", m, move->score);
557 }
558 }
559 fprintf(f, "}\n");
560}
561
571static void position_get_random_move(const Position *position, const Board *board, Move *move, Random *r, const int randomness)
572{
573 MoveList movelist[1];
574 Move *m;
575 int i, n;
576
577 position_get_moves(position, board, movelist);
578
579 n = 0;
580 foreach_best_move(m, movelist) {
581 if (position->score.value <= m->score + randomness) {
582 ++n;
583 } else break;
584 }
585
586 if (n == 0) { // no move
587 move->x = NOMOVE;
588 move->flipped = 0;
589 return;
590 } else {
591 i = (random_get(r) % n); // is good enough here
592 }
593
594 foreach_best_move(m, movelist) {
595 if (i-- == 0) break;
596 }
597
598 *move = *m;
599}
600
608static bool position_add_link(Position *position, const Link *link)
609{
610 Link *l;
611 int last = position->n_link;
612
613 foreach_link (l, position) {
614 if (l->move == link->move) {
615 l->score = link->score; // update the link ?
616 return false;
617 }
618 }
619
620 l = (Link*) realloc(position->link, sizeof (Link) * (++position->n_link));
621 if (l == NULL) {
622 --position->n_link;
623 error("cannot allocate opening book position's moves\n");
624 return false;
625 }
626 position->link = l;
627 position->link[last] = *link;
628
629 if (link->score > position->score.value) position->score.value = link->score;
630
631 if (link->move == position->leaf.move) position->leaf = BAD_LINK;
632
633 return true;
634}
635
641static void position_sort(Position *position)
642{
643 Link *i, *j, *best;
644
645 if (position->n_link > 1) {
646 for (i = position->link; i < position->link + position->n_link - 1; ++i) {
647 best = i;
648 for (j = i + 1; j < position->link + position->n_link; ++j) {
649 if (j->score > best->score) best = j;
650 }
651 if (best > i) {
652 Link tmp = *best;
653 *best = *i;
654 *i = tmp;
655 }
656 }
657 }
658}
659
668static void position_search(Position *position, Book *book)
669{
670 Search *search = book->search;
671 Link *l;
672 const int n_moves = get_mobility(position->board->player, position->board->opponent);
673 long long time;
674 bool time_per_move;
675
676 if (position->leaf.move != NOMOVE && position_add_link(position, &position->leaf)) {
677 book->need_saving = true;
678 ++book->stats.n_links;
679 }
680
681 if (position->n_link < n_moves || (position->n_link == 0 && n_moves == 0 && position->score.value == -SCORE_INF)) {
682 search_set_board(search, position->board, BLACK);
683 search_set_level(search, position->level, search->n_empties);
684
685 foreach_link (l, position) {
686 movelist_exclude(search->movelist, l->move);
687 }
688
689 if (search->options.verbosity >= 2) {
690 board_print(search->board, search->player, stdout);
691 puts(search->options.header);
692 puts(search->options.separator);
693 }
694
695 time = search->options.time;
696 time_per_move = search->options.time_per_move;
697 search->options.time = TIME_MAX;
698 search->options.time_per_move = true;
699
700 search_run(search);
701
702 search->options.time = time;
703 search->options.time_per_move = time_per_move;
704
705 position->leaf.score = search->result->score;
706 position->leaf.move = search->result->move;
707 if (position->leaf.score > position->score.value) {
708 position->score.value = position->leaf.score;
709 }
710 book->need_saving = true;
711 }
712}
713
722static void position_link(Position *position, Book *book)
723{
724 int x;
725 unsigned long long moves = get_moves(position->board->player, position->board->opponent);
726 Board next[1];
727 Link link[1];
728 Position *child;
729
730 if (moves) {
731 foreach_bit(x, moves) {
732 board_next(position->board, x, next);
733 child = book_probe(book, next);
734 if (child) {
735 link->score = -child->score.value;
736 link->move = x;
737 book->stats.n_links += position_add_link(position, link);
738 }
739 }
740 } else if (can_move(position->board->opponent, position->board->player)) {// pass ?
741 next->player = position->board->opponent;
742 next->opponent = position->board->player;
743 child = book_probe(book, next);
744 if (child) {
745 link->score = -child->score.value;
746 link->move = PASS;
747 book->stats.n_links += position_add_link(position, link);
748 }
749 }
750}
751
762static void position_expand(Position *position, Book *book)
763{
764 Position child[1];
765
766 if (position->leaf.move != NOMOVE) {
767 position_init(child);
768
769 board_next(position->board, position->leaf.move, child->board);
770
771 child->level = position->level;
772 position_link(child, book);
773 search_cleanup(book->search);
774 position_search(child, book);
775 position->leaf.score = -child->score.value;
776 position_search(position, book);
777 position_unique(child);
778 book_add(book, child);
779 }
780}
781
790static int position_negamax(Position *position, Book *book)
791{
792 Link *l;
793 Board target[1];
794 Position *child;
795 int tmp_upper, tmp_lower;
796
797 //if (!position->done) {
798 if (!(position->flag & FLAG_DONE) ) {
799 GameStats stat = {0,0,0,0};
800 const int n_empties = board_count_empties(position->board);
801 const int search_depth = LEVEL[position->level][n_empties].depth;
802 const int bias = (search_depth & 1) - (n_empties & 1);
803
804 //position->done = 1;
805 position->flag |= FLAG_DONE;
806
807 position->score.value = position->score.lower = position->score.upper = -SCORE_INF;
808
809 if (position->leaf.score > -SCORE_INF) {
810 position->score.value = position->leaf.score;
811 // is solving
812 if (search_depth == n_empties && LEVEL[position->level][n_empties].selectivity == NO_SELECTIVITY) {
813 position->score.lower = position->score.upper = position->score.value;
814 if (position->leaf.score > 0) ++stat.n_wins;
815 else if (position->leaf.score < 0) ++stat.n_losses;
816 else ++stat.n_draws;
817 // is pre-solving
818 } else if (search_depth == n_empties) {
819 position->score.lower = position->score.value - book->options.endcut_error;
820 position->score.upper = position->score.value + book->options.endcut_error;
821 } else { // midgame
822 position->score.lower = position->score.value - book->options.midgame_error - bias;
823 position->score.upper = position->score.value + book->options.midgame_error - bias;
824 }
825 ++stat.n_lines;
826 }
827
828 foreach_link(l, position) {
829 board_next(position->board, l->move, target);
830 child = book_probe(book, target);
831 if ( child ) {
832 position_negamax(child, book);
833 if (l->score != -child->score.value) {
834 l->score = -child->score.value;
835 book->need_saving = true;
836 }
837 if (l->score > position->score.value) position->score.value = l->score;
838 if (-child->score.upper > position->score.lower) position->score.lower = -child->score.upper;
839 if (-child->score.lower > position->score.upper) position->score.upper = -child->score.lower;
840
841 stat.n_wins += child->n_losses;
842 stat.n_draws += child->n_draws;
843 stat.n_losses += child->n_wins;
844 stat.n_lines += child->n_lines;
845 } else if (l->score > -SCORE_INF) {
846 if (l->score > position->score.value) position->score.value = l->score;
847
848 // is solving
849 if (search_depth == n_empties && LEVEL[position->level][n_empties].selectivity == NO_SELECTIVITY) {
850 tmp_lower = tmp_upper = position->score.value;
851 if (l->score > 0) ++stat.n_wins;
852 else if (l->score < 0) ++stat.n_losses;
853 else ++stat.n_draws;
854 // is pre-solving
855 } else if (search_depth == n_empties) {
856 tmp_lower = position->score.value - book->options.endcut_error;
857 tmp_upper = position->score.value + book->options.endcut_error;
858 } else { // midgame
859 tmp_lower = position->score.value - book->options.midgame_error - bias;
860 tmp_upper = position->score.value + book->options.midgame_error - bias;
861 }
862 ++stat.n_lines;
863
864 if (tmp_upper > position->score.lower) position->score.lower = tmp_upper;
865 if (tmp_lower > position->score.upper) position->score.upper = tmp_lower;
866 }
867 }
868
869 position->n_wins = (unsigned int) MIN(UINT_MAX, stat.n_wins);
870 position->n_draws = (unsigned int) MIN(UINT_MAX, stat.n_draws);
871 position->n_losses = (unsigned int) MIN(UINT_MAX, stat.n_losses);
872 position->n_lines = (unsigned int) MIN(UINT_MAX, stat.n_lines);
873 }
874
875 return position->score.value;
876}
877
878
889static void position_prune(Position *position, Book *book, const int player_deviation, const int opponent_deviation, const int lower, const int upper)
890{
891 Link *l;
892 Board target[1];
893 Position *child;
894
895 // if position is not done yet & good enough & inside the book height limit
896 if (lower <= position->score.value && position->score.value <= upper && board_count_empties(position->board) >= book->options.n_empties - 1) {
897 //position->done = true; book->stats.n_todo++;
898 position->flag |= FLAG_DONE; book->stats.n_todo++;
899
900 // prune all children close to the best move
901 foreach_link(l, position) {
902 if (position->score.value - l->score <= player_deviation && lower <= l->score && l->score <= upper) {
903 board_next(position->board, l->move, target);
904 child = book_probe(book, target);
905 position_prune(child, book, opponent_deviation, player_deviation, -upper, -lower);
906 }
907 }
908 if (book->stats.n_todo % BOOK_INFO_RESOLUTION == 0) {
909 bprint("Book prune %d to keep\r", book->stats.n_todo);
910
911 }
912 }
913}
914
921static void position_remove_links(Position *position, Book *book)
922{
923 int i, j;
924 Link *l = position->link;
925 Board target[1];
926
927 for (i = 0; i < position->n_link; ++i) {
928 board_next(position->board, l[i].move, target);
929 if (!book_probe(book, target)) {
930 if (l[i].score > position->leaf.score) position->leaf = l[i];
931 for (j = i + 1; j < position->n_link; ++j) l[j - 1] = l[j];
932 --position->n_link;
933 --i;
934 }
935 }
936}
937
955static void position_deviate(Position *position, Book *book, const int player_deviation, const int opponent_deviation, const int lower, const int upper)
956{
957 Link *l;
958 Board target[1];
959 Position *child;
960
961 // if position is not done yet & good enough & inside the book height limit
962 //if (!position->done && lower <= position->score.value && position->score.value <= upper && board_count_empties(position->board) >= book->options.n_empties && !board_is_game_over(position->board)) {
963 if (!(position->flag & FLAG_DONE) && lower <= position->score.value && position->score.value <= upper && board_count_empties(position->board) >= book->options.n_empties && !board_is_game_over(position->board)) {
964 //position->done = true;
965 position->flag |= FLAG_DONE;
966
967 // deviate all children close to the best move
968 foreach_link(l, position) {
969 if (position->score.value - l->score <= player_deviation && lower <= l->score && l->score <= upper) {
970 board_next(position->board, l->move, target);
971 child = book_probe(book, target);
972 position_deviate(child, book, opponent_deviation, player_deviation, -upper, -lower);
973 }
974 }
975
976 // expand the best remaining move
977 if (position->score.value - position->leaf.score <= player_deviation && lower <= position->leaf.score && position->leaf.score <= upper) {
978 //position->todo = true; book->stats.n_todo++;
979 position->flag |= FLAG_TODO; book->stats.n_todo++;
980 if (book->stats.n_todo % 10 == 0) bprint("Book deviate %d todo\r", book->stats.n_todo);
981 }
982 }
983}
984
997static void position_enhance(Position *position, Book *book)
998{
999 Link *l;
1000 Board target[1];
1001 Position *child;
1002
1003 //if (!position->done && board_count_empties(position->board) >= book->options.n_empties && !board_is_game_over(position->board)) {
1004 if (!(position->flag & FLAG_DONE) && board_count_empties(position->board) >= book->options.n_empties && !board_is_game_over(position->board)) {
1005 //position->done = true;
1006 position->flag |= FLAG_DONE;
1007
1008 foreach_link(l, position) {
1009 board_next(position->board, l->move, target);
1010 child = book_probe(book, target);
1011 if (-child->score.upper >= position->score.lower || -child->score.lower >= position->score.upper) {
1012 position_enhance(child, book);
1013 }
1014 }
1015
1016 if (position->leaf.score > -SCORE_INF) {
1017 const int n_empties = board_count_empties(position->board);
1018 const int search_depth = LEVEL[position->level][n_empties].depth;
1019 const int bias = (search_depth & 1) - (n_empties & 1);
1020 int lower, upper;
1021 // is solving
1022 if (search_depth == n_empties && LEVEL[position->level][n_empties].selectivity == NO_SELECTIVITY) {
1023 lower = upper = position->leaf.score;
1024 // is pre-solving
1025 } else if (search_depth == n_empties) {
1026 lower = position->leaf.score - book->options.endcut_error;
1027 upper = position->leaf.score + book->options.endcut_error;
1028 } else { // midgame
1029 lower = position->leaf.score - book->options.midgame_error - bias;
1030 upper = position->leaf.score + book->options.midgame_error - bias;
1031 }
1032
1033 if (lower >= position->score.lower || upper >= position->score.upper) {
1034 //position->todo = true;
1035 position->flag |= FLAG_TODO;
1036 }
1037 }
1038 }
1039}
1040
1051static void board_feed_hash(Board *board, const Book *book, Search *search, const bool is_pv)
1052{
1053 Position *position;
1054 const unsigned long long hash_code = board_get_hash_code(board);
1055 MoveList movelist[1];
1056 Move *m;
1057
1058 position = book_probe(book, board);
1059 if (position) {
1060 const int n_empties = board_count_empties(position->board);
1061 const int depth = LEVEL[position->level][n_empties].depth;
1062 const int selectivity = LEVEL[position->level][n_empties].selectivity;
1063 const int score = position->score.value;
1064 int move = NOMOVE;
1065
1066 position_get_moves(position, board, movelist);
1067 foreach_move(m, movelist) {
1068 if (move == NOMOVE) move = m->x;
1069 board_update(board, m);
1070 board_feed_hash(board, book, search, is_pv && m->score == score);
1071 board_restore(board, m);
1072 }
1073 hash_feed(search->hash_table, board, hash_code, depth, selectivity, score, score, move);
1074 if (is_pv) hash_feed(search->pv_table, board, hash_code, depth, selectivity, score, score, move);
1075 }
1076}
1077
1088static bool board_fill(Board *board, Book *book, int depth)
1089{
1090 if (depth > 0) {
1091 MoveList movelist[1];
1092 Move *m;
1093 bool filled = false;
1094
1095 movelist_get_moves(movelist, board);
1096 if (movelist->n_moves == 0 && can_move(board->opponent, board->player)) {
1097 board_pass(board);
1098 if (board_fill(board, book, depth - 1)) {
1099 book_add_board(book, board);
1100 filled = true;
1101 }
1102 board_pass(board);
1103 } else {
1104 foreach_move(m, movelist) {
1105 board_update(board, m);
1106 if (board_fill(board, book, depth - 1)) {
1107 book_add_board(book, board);
1108 filled = true;
1109 }
1110 board_restore(board, m);
1111 }
1112 }
1113 return filled;
1114 }
1115 return book_probe(book, board) != NULL;
1116}
1117
1126static void position_fix(Position *position, Book *book)
1127{
1128 Board board[1];
1129
1130 if ((position->board->player & position->board->opponent) ||
1131 ((position->board->player | position->board->opponent) & 0x0000001818000000ULL) != 0x0000001818000000ULL) {
1132 position_free(position);
1133 position_init(position);
1134 return;
1135 }
1136 board_unique(position->board, board);
1137 position_free(position);
1138 position_init(position);
1139 *position->board = *board;
1140 position->level = book->options.level;
1141 position_link(position, book);
1142 position_search(position, book);
1143}
1144
1153
1154
1161{
1162 a->size = a->n = 0;
1163 a->positions = NULL;
1164}
1165
1174{
1175 int i;
1176
1177 board_check(p->board);
1178 assert(position_is_ok(p));
1179
1180 for (i = 0; i < a->n; ++i) if (board_equal(a->positions[i].board, p->board)) return false;
1181 if (a->n == a->size) {
1182 Position *n;
1183 a->size += a->size / 2 + 1;
1184 n = (Position*) realloc(a->positions, a->size * sizeof (Position));
1185 if (n == NULL) {
1186 error("cannot add a position to the book\n");
1187 a->size = a->n;
1188 return false;
1189 }
1190 a->positions = n;
1191 }
1192 a->positions[a->n] = *p;
1193 //a->positions[a->n].done = true;
1194 //a->positions[a->n].todo = false;
1195 a->positions[a->n].flag |= FLAG_DONE;
1196 a->positions[a->n].flag &= ~FLAG_TODO;
1197 // add by lavox 2021/8/21
1198 a->positions[a->n].n_player_bestpaths = 0;
1199 a->positions[a->n].n_opponent_bestpaths = 0;
1200 ++a->n;
1201 return true;
1202}
1203
1212{
1213 int i, j;
1214
1215 for (i = 0; i < a->n; ++i) {
1216 if (board_equal(a->positions[i].board, p->board)) {
1217 position_free(a->positions + i);
1218 for (j = i + 1; j < a->n; ++j) {
1219 a->positions[j - 1] = a->positions[j];
1220 }
1221 --a->n;
1222 return true;
1223 }
1224 }
1225 return false;
1226}
1227
1234{
1235 int i;
1236 for (i = 0; i < a->n; ++i) position_free(a->positions + i);
1237 free(a->positions);
1238}
1239
1248{
1249 int i;
1250 for (i = 0; i < a->n; ++i) if (board_equal(a->positions[i].board, board)) return a->positions + i;
1251 return NULL;
1252}
1253
1254#define foreach_position(p, a, b) \
1255 for (a = b->array; a < b->array + b->n; ++a) \
1256 for (p = a->positions; p < a->positions + a->n; ++p)
1257
1263static void book_set_date(Book *book)
1264{
1265 time_t t = time(NULL);
1266 struct tm *tm = localtime(&t);
1267
1268 book->date.year = tm->tm_year + 1900;
1269 book->date.month = tm->tm_mon + 1;
1270 book->date.day = tm->tm_mday;
1271 book->date.hour = tm->tm_hour;
1272 book->date.minute = tm->tm_min;
1273 book->date.second = tm->tm_sec;
1274}
1275
1282static double book_get_age(Book *book)
1283{
1284 struct tm tm;
1285 double t;
1286
1287 tm.tm_year = book->date.year - 1900;
1288 tm.tm_mon = book->date.month - 1;
1289 tm.tm_mday = book->date.day;
1290 tm.tm_hour = book->date.hour;
1291 tm.tm_min = book->date.minute;
1292 tm.tm_sec = book->date.second;
1293 tm.tm_isdst = -1;
1294
1295 t = difftime(time(NULL), mktime(&tm));
1296
1297 return t;
1298}
1299
1300
1301
1309static Position* book_probe(const Book *book, const Board *board)
1310{
1311 Board unique[1];
1312 board_unique(board, unique);
1313 return position_array_probe(book->array + (board_get_hash_code(unique) & (book->n - 1)), unique);
1314}
1315
1322static void book_add(Book *book, const Position *p)
1323{
1324 const unsigned long long i = board_get_hash_code(p->board) & (book->n - 1);
1325
1326 if (position_array_add(book->array + i, p)) {
1327 ++book->n_nodes;
1328 ++book->stats.n_nodes;
1329 }
1330}
1331
1338static void book_remove(Book *book, const Position *p)
1339{
1340 const unsigned long long i = board_get_hash_code(p->board) & (book->n - 1);
1341
1342 if (position_array_remove(book->array + i, p)) {
1343 --book->n_nodes;
1344 --book->stats.n_nodes;
1345 }
1346}
1347
1353static void book_clean(Book *book)
1354{
1355 PositionArray *a;
1356 Position *p;
1357 book->stats.n_nodes = book->stats.n_links = book->stats.n_todo = 0;
1358 foreach_position(p, a, book) {
1359 //p->done = p->todo = false;
1361 p->n_player_bestpaths = p->n_opponent_bestpaths = 0; // add by lavox 2021/8/22
1362 }
1363}
1364
1366{
1367 book_clean(book);
1368}
1369
1382{
1383 Board board[1];
1384
1385 board_init(board);
1386 return book_probe(book, board);
1387}
1388
1396void book_init(Book *book)
1397{
1398 int i;
1399
1400 book_set_date(book);
1401
1402 book->options.level = 21;
1403 book->options.n_empties = 24;
1404 book->options.midgame_error = 2;
1405 book->options.endcut_error = 1;
1406
1407 book->n = 65536;
1408 book->array = (PositionArray*) malloc(book->n * sizeof *book->array);
1409 if (book->array == NULL) fatal_error("cannot allocate space to store the positions");
1410 for (i = 0; i < book->n; ++i) position_array_init(book->array + i);
1411
1412 book->n_nodes = 0;
1413 random_seed(book->random, real_clock());
1414 book->need_saving = false;
1415}
1416
1422void book_free(Book *book)
1423{
1424 int i;
1425 for (i = 0; i < book->n; ++i) {
1426 position_array_free(book->array + i);
1427 }
1428 free(book->array);
1429}
1430
1437{
1438 book_clean(book);
1439}
1440
1450void book_new(Book *book, int level, int n_empties)
1451{
1452 Board board[1];
1453
1454 bprint("New book %d %d...", level, n_empties);
1455 book_init(book);
1456 book->options.level = level;
1457 book->options.n_empties = n_empties;
1458
1459 board_init(board);
1460 book_add_board(book, board);
1461 bprint("...done>\n");
1462 book->need_saving = true;
1463}
1464
1471void book_load(Book *book, const char *file)
1472{
1473 FILE *f = fopen(file, "rb");
1474 if (f) {
1475 Position p;
1476 unsigned int header_edax, header_book;
1477 unsigned char header_version, header_release;
1478 int i;
1479 int r;
1480
1481 info("Loading book from %s...", file);
1482 r = fread(&header_edax, sizeof (unsigned int), 1, f);
1483 r += fread(&header_book, sizeof (unsigned int), 1, f);
1484 if (r != 2 || header_edax != EDAX || header_book != BOOK) {
1485 error("%s is not an edax opening book", file);
1487 return;
1488 }
1489
1490 r = fread(&header_version, 1, 1, f);
1491 r += fread(&header_release, 1, 1, f);
1492 if (r != 2 || header_version != VERSION) {
1493 error("%s is not a compatible version", file);
1495 return;
1496 }
1497
1498 r = fread(&book->date, sizeof book->date, 1, f);
1499 r += fread(&book->options, sizeof book->options, 1, f);
1500 r += fread(&book->n_nodes, sizeof book->n_nodes, 1, f);
1501 if (r != 3) {
1502 error("Cannot read book settings from %s", file);
1504 return;
1505 }
1506
1507 book->n = 65536;
1508 while ((book->n << 4) < book->n_nodes) book->n <<= 1;
1509
1510 book->array = (PositionArray*) malloc(book->n * sizeof (PositionArray));
1511 if (book->array == NULL) {
1512 error("cannot allocate space to store the positions");
1514 return;
1515 }
1516 for (i = 0; i < book->n; ++i) position_array_init(book->array + i);
1517
1518 book->n_nodes = 0;
1519 while (position_read(&p, f)) {
1520 book_add(book, &p);
1521 }
1522
1523 if (!feof(f)) {
1524 error("error while reading %s", file);
1525 }
1526
1527 random_seed(book->random, real_clock());
1528 book->need_saving = false;
1529
1530 info("done\n");
1531 fclose(f);
1532 } else {
1534 }
1535}
1536
1547void book_import(Book *book, const char *file)
1548{
1549 FILE *f = fopen(file, "r");
1550 if (f) {
1551 PositionArray *a;
1552 Position *p, position;
1553 int n_empties;
1554
1555 book_init(book);
1556 while (position_import(&position, f)) {
1557 book_add(book, &position);
1558 if (book->n_nodes % BOOK_INFO_RESOLUTION == 0) bprint("importing book from %s... %d positions\r", file, book->n_nodes);
1559 }
1560 bprint("importing book from %s... %d positions", file, book->n_nodes);
1561
1562 book->options.n_empties = 60;
1563 book->options.level = 0;
1564 foreach_position(p, a, book) {
1565 n_empties = board_count_empties(p->board);
1566 if (p->level > book->options.level) book->options.level = p->level;
1567 if (n_empties < book->options.n_empties) book->options.n_empties = n_empties;
1568 }
1569
1570 random_seed(book->random, real_clock());
1571 book->need_saving = true;
1572
1573 bprint("...done\n");
1574 fclose(f);
1575 } else {
1576 error("cannot open \"%s\" to import the opening book\n", file);
1578 }
1579}
1580
1589void book_export(Book *book, const char *file)
1590{
1591 FILE *f;
1592 PositionArray *a;
1593 Position *p;
1594
1595 f = fopen(file, "w");
1596 if (f == NULL) {
1597 error("cannot open file %s", file);
1598 return;
1599 }
1600
1601 info("Exporting book to %s...", file);
1602 foreach_position(p, a, book) {
1603 if (!position_export(p, f)) {
1604 error("cannot export book to %s", file);
1605 goto book_export_end;
1606 }
1607 }
1608 info("done\n");
1609
1610book_export_end:
1611 fclose(f);
1612}
1613
1622void book_save(Book *book, const char *file)
1623{
1624 unsigned int header_edax = EDAX, header_book = BOOK;
1625 unsigned char header_version = VERSION, header_release = RELEASE;
1626 FILE *f = fopen(file, "wb");
1627 int r;
1628 PositionArray *a;
1629 Position *p;
1630
1631 info("Saving book to %s...", file);
1632 book_set_date(book);
1633
1634 r = fwrite(&header_edax, sizeof (unsigned int), 1, f);
1635 r += fwrite(&header_book, sizeof (unsigned int), 1, f);
1636 r += fwrite(&header_version, 1, 1, f);
1637 r += fwrite(&header_release, 1, 1, f);
1638 r += fwrite(&book->date, sizeof book->date, 1, f);
1639 r += fwrite(&book->options, sizeof book->options, 1, f);
1640 r += fwrite(&book->n_nodes, sizeof book->n_nodes, 1, f);
1641
1642 if (r == 7) {
1643 foreach_position(p, a, book) {
1644 if (!position_write(p, f)) {
1645 error("\nCannot save book to %s", file);
1646 goto book_write_end;
1647 }
1648 }
1649 }
1650 info("done\n");
1651
1652book_write_end:
1653 fclose(f);
1654}
1655
1665void book_merge(Book *dest, const Book *src)
1666{
1667 PositionArray *a;
1668 const Position *p_src;
1669 Position p_dest[1];
1670
1671 foreach_position(p_src, a, src) {
1672 if (!book_probe(dest, p_src->board)) {
1673 position_merge(p_dest, p_src);
1674 book_add(dest, p_dest);
1675 }
1676 }
1677}
1678
1685{
1686 Position *root = book_root(book);
1687
1688 if (root) {
1689 bprint("Negamaxing book...");
1690 book_clean(book);
1691 position_negamax(root, book);
1692 bprint("done\n");
1693 }
1694}
1695
1701void book_link(Book *book)
1702{
1703 PositionArray *a;
1704 Position *p;
1705 int i = 0;
1706
1707 bprint("Linking book...\r");
1708 foreach_position(p, a, book) {
1709 position_link(p, book);
1710 if (p->leaf.move == NOMOVE) {
1711 position_search(p, book);
1712 }
1713 if (++i % BOOK_INFO_RESOLUTION == 0) bprint("Linking book...%d\r", i);
1714 }
1715 bprint("Linking book...%d done\n", i);
1716}
1717
1723void book_fix(Book *book)
1724{
1725 PositionArray *a;
1726 Position *p;
1727 int i = 0;
1728
1729 bprint("Fixing book...\r");
1730 foreach_position(p, a, book) {
1731 if (!position_is_ok(p)) {
1732 position_fix(p, book);
1733 if (++i % BOOK_INFO_RESOLUTION == 0) { bprint("fixing book...%d\r", i); }
1734 }
1735 }
1736 bprint("Fixing book...%d done\n", i);
1737}
1738
1747{
1748 PositionArray *a;
1749 Position *p;
1750 int i = 0;
1751 unsigned long long t = real_clock();
1752 char file[FILENAME_MAX + 1];
1753
1754 file_add_ext(options.book_file, ".dep", file);
1755
1756 bprint("Deepening book...\r");
1757 foreach_position(p, a, book) {
1758 int n_empties = board_count_empties(p->board);
1759 if (LEVEL[p->level][n_empties].depth != LEVEL[book->options.level][n_empties].depth
1760 || LEVEL[p->level][n_empties].selectivity != LEVEL[book->options.level][n_empties].selectivity) { // No! compare depth & selectivity;
1761 p->leaf = BAD_LINK;
1762 position_search(p, book);
1763 if (++i % 10 == 0) {
1764 bprint("Deepening book...%d\r", i);
1765 }
1766 if (real_clock() - t > HOUR) {
1767 book_save(book, file); // save every hour
1768 t = real_clock();
1769 }
1770 }
1771 }
1772 bprint("Deepening book...%d done\n", i);
1773}
1774
1783{
1784 PositionArray *a;
1785 Position *p;
1786 int i = 0;
1787 unsigned long long t = real_clock();
1788 char file[FILENAME_MAX + 1];
1789 Link old_leaf;
1790 int n_error = 0;
1791 char s[4];
1792
1793 file_add_ext(options.book_file, ".err", file);
1794
1795 bprint("Correcting solved positions...\r");
1796 foreach_position(p, a, book) {
1797 int n_empties = board_count_empties(p->board);
1798 if (LEVEL[p->level][n_empties].depth == n_empties && LEVEL[p->level][n_empties].selectivity == NO_SELECTIVITY) { // No! compare depth & selectivity;
1799 old_leaf = p->leaf;
1800 p->leaf = BAD_LINK;
1801 position_search(p, book);
1802 if (p->leaf.score != old_leaf.score) {
1803 ++n_error;
1804 bprint("\nError found:\n");
1805 position_print(p, p->board, stdout);
1806 move_to_string(old_leaf.move, n_empties & 1, s);
1807 bprint("instead of <%s:%d>\n\n", s, old_leaf.score);
1808 }
1809 if (++i % 10 == 0 || p->leaf.score != old_leaf.score) {
1810 bprint("Correcting solved positions...%d (%d error found)\r", i, n_error);
1811 }
1812 if (real_clock() - t > HOUR) {
1813 book_save(book, file); // save every hour
1814 t = real_clock();
1815 }
1816 }
1817 }
1818 bprint("Correcting solved positions...%d done (%d error found)\n", i, n_error);
1819}
1820
1830static void book_expand(Book *book, const char *action, const char *tmp_file)
1831{
1832 PositionArray *a;
1833 Position *p;
1834 int i = 0, k;
1835 unsigned long long t = real_clock();
1836
1837 bprint("%s...\r", action);
1838
1839 for (a = book->array; a < book->array + book->n; ++a)
1840 for (k = 0; k < a->n; ++k) { // do not use foreach_positions here! a->positions may change!
1841 p = a->positions + k;
1842 //if (p->todo) {
1843 if (p->flag & FLAG_TODO) {
1844 position_expand(p, book);
1845 bprint("%s...%d/%d done: %d positions, %d links\r", action, ++i, book->stats.n_todo, book->stats.n_nodes, book->stats.n_links);
1846 if (book->search->options.verbosity >= 2) putchar('\n'); else putchar('\r');
1847
1848 if (real_clock() - t > HOUR) {
1849 book_save(book, tmp_file); // save every hour
1850 t = real_clock();
1851 }
1852 }
1853 }
1854 bprint("%s...%d/%d done: %d positions, %d links\n", action, i, book->stats.n_todo, book->stats.n_nodes, book->stats.n_links);
1855}
1856
1862void book_sort(Book *book)
1863{
1864 PositionArray *a;
1865 Position *p;
1866
1867 bprint("Sorting book...");
1868 foreach_position(p, a, book) {
1869 position_sort(p);
1870 }
1871 bprint("done>\n");
1872}
1873
1882void book_play(Book *book)
1883{
1884 PositionArray *a;
1885 Position *p;
1886 int n_diffs;
1887 char file[FILENAME_MAX + 1];
1888
1889 file_add_ext(options.book_file, ".play", file);
1890 do {
1891 n_diffs = 0;
1892 book->stats.n_nodes = book->stats.n_links = book->stats.n_todo = 0;
1893 foreach_position(p, a, book) {
1894 if (p->n_link == 0 && board_count_empties(p->board) >= book->options.n_empties && !board_is_game_over(p->board)) {
1895 //p->todo = true; ++book->stats.n_todo;
1896 p->flag |= FLAG_TODO; ++book->stats.n_todo;
1897 } else {
1898 //p->todo = false;
1899 p->flag &= ~FLAG_TODO;
1900 }
1901 if (book->stats.n_todo && book->stats.n_todo % BOOK_INFO_RESOLUTION == 0) bprint("Book play...%d todo\r", book->stats.n_todo);
1902 }
1903 bprint("Book play...%d todo\n", book->stats.n_todo);
1904
1905 book_expand(book, "Book play", file);
1906
1907 n_diffs = book->stats.n_nodes + book->stats.n_links;
1908 if (n_diffs) {
1909 book_negamax(book);
1910 book_save(book, file);
1911 }
1912 } while (n_diffs);
1913 bprint("Book play... finished\n");
1914}
1915
1922void book_fill(Book *book, const int depth)
1923{
1924 PositionArray *a;
1925 Position *p;
1926 int n_diffs, n_empties, k;
1927 char file[FILENAME_MAX + 1];
1928
1929 file_add_ext(options.book_file, ".fill", file);
1930
1931 do {
1932 n_diffs = 0;
1933 book->stats.n_nodes = book->stats.n_links = 0;
1934 for (a = book->array; a < book->array + book->n; ++a)
1935 for (k = 0; k < a->n; ++k) { // do not use foreach_positions here! a->positions may change!
1936 p = a->positions + k;
1937 n_empties = board_count_empties(p->board);
1938 if (n_empties >= book->options.n_empties) {
1939 board_fill(p->board, book, depth);
1940 if (n_diffs < book->stats.n_nodes + book->stats.n_links) {
1941 n_diffs = book->stats.n_nodes + book->stats.n_links;
1942 bprint("Book fill...%d %d done\r", book->stats.n_nodes, book->stats.n_links);
1943 }
1944 }
1945 }
1946 bprint("Book fill...%d %d done\n", book->stats.n_nodes, book->stats.n_links);
1947 if (n_diffs) {
1948 book_negamax(book);
1949 book_save(book, file);
1950 }
1951 } while (n_diffs);
1952 bprint("Book fill... finished\n");
1953}
1954
1963void book_deviate(Book *book, Board *board, const int relative_error, const int absolute_error)
1964{
1965 Position *root = book_probe(book, board);
1966 if (root) {
1967 int score;
1968 int n_diffs;
1969 char file[FILENAME_MAX + 1];
1970
1971 file_add_ext(options.book_file, ".dev", file);
1972 book_clean(book);
1973 position_negamax(root, book);
1974
1975 do {
1976 score = root->score.value;
1977
1978 bprint("Book deviate %d %d:\n", relative_error, absolute_error);
1979 book_clean(book);
1980 position_deviate(root, book, relative_error, 0, score - absolute_error, score + absolute_error);
1981 bprint("Book deviate %d todo\n", book->stats.n_todo);
1982
1983 book_expand(book, "Book deviate", file);
1984 n_diffs = book->stats.n_nodes + book->stats.n_links;
1985
1986 bprint("Book deviate %d %d:\n", relative_error, absolute_error);
1987 book_clean(book);
1988 position_deviate(root, book, 0, relative_error, score - absolute_error, score + absolute_error);
1989 bprint("Book deviate %d todo\n", book->stats.n_todo);
1990
1991 book_expand(book, "Book deviate", file);
1992 n_diffs += book->stats.n_nodes + book->stats.n_links;
1993
1994 root = book_probe(book, board);
1995 book_clean(book);
1996 position_negamax(root, book);
1997 if (n_diffs) book_save(book, file);
1998 } while (n_diffs);
1999 bprint("Book deviate %d %d...finished\n", relative_error, absolute_error);
2000 }
2001}
2002
2010void book_prune(Book *book)
2011{
2012 PositionArray *a;
2013 Position *p;
2014 Position *root = book_root(book);
2015 int i;
2016
2017 if (root) {
2018 book_clean(book);
2019 position_negamax(root, book);
2020
2021 book_clean(book);
2022 position_prune(root, book, 2*SCORE_INF, 0, -SCORE_INF, SCORE_INF);
2023 position_print(root, root->board, stdout);
2024 bprint("Book prune %d... done\n", book->stats.n_todo);
2025
2026 position_prune(root, book, 0, 2*SCORE_INF, -SCORE_INF, SCORE_INF);
2027 bprint("Book prune %d... done\n", book->stats.n_todo);
2028 for (a = book->array; a < book->array + book->n; ++a)
2029 //for (i = 0; i < a->n; ++i) if (!a->positions[i].done) {book_remove(book, a->positions + i); --i;}
2030 for (i = 0; i < a->n; ++i) if (!(a->positions[i].flag & FLAG_DONE)) {book_remove(book, a->positions + i); --i;}
2031 foreach_position(p, a, book) position_remove_links(p, book);
2032 bprint("done\n");
2033 }
2034}
2035
2043void book_subtree(Book *book, const Board *board)
2044{
2045 PositionArray *a;
2046 Position *p;
2047 Position *root = book_probe(book, board);
2048 int i;
2049
2050 if (root) {
2051 book_clean(book);
2052 position_negamax(root, book);
2053
2054 book_clean(book);
2056 position_print(root, root->board, stdout);
2057 bprint("Book subtree %d... done\n", book->stats.n_todo);
2058 for (a = book->array; a < book->array + book->n; ++a)
2059 //for (i = 0; i < a->n; ++i) if (!a->positions[i].done) {book_remove(book, a->positions + i); --i;}
2060 for (i = 0; i < a->n; ++i) if (!(a->positions[i].flag & FLAG_DONE)) {book_remove(book, a->positions + i); --i;}
2061 foreach_position(p, a, book) position_remove_links(p, book);
2062 bprint("done\n");
2063 }
2064}
2065
2066
2075void book_enhance(Book *book, Board *board, const int midgame_error, const int endcut_error)
2076{
2077 Position *root = book_probe(book, board);
2078 if (root) {
2079 int n_diffs;
2080 char file[FILENAME_MAX + 1];
2081
2082 file_add_ext(options.book_file, ".enh", file);
2083
2084 book->options.midgame_error = midgame_error;
2085 book->options.endcut_error = endcut_error;
2086
2087 book_clean(book);
2088 position_negamax(root, book);
2089
2090 do {
2091 bprint("Book enhance %d %d...%d %d:\n", midgame_error, endcut_error, book->stats.n_nodes, book->stats.n_links);
2092 book_clean(book);
2093 position_enhance(root, book);
2094 n_diffs = book->stats.n_nodes + book->stats.n_links;
2095 book_expand(book, "Book enhance", file);
2096
2097 root = book_probe(book, board);
2098 book_clean(book);
2099 position_negamax(root, book);
2100 if (n_diffs) book_save(book, file);
2101 } while (n_diffs);
2102 bprint("Book enhance %d %d...finished\n", midgame_error, endcut_error);
2103 }
2104}
2105
2111void book_info(Book *book)
2112{
2113 PositionArray *a;
2114 Position *p;
2115 unsigned long long n_links = 0;
2116 unsigned long long n_leaves = 0;
2117 unsigned long long n_level[61] = {0};
2118 int min_array = book->n_nodes, max_array = 0;
2119 int i;
2120
2121 foreach_position(p, a, book) {
2122 n_links += p->n_link;
2123 if (p->leaf.move != NOMOVE) ++n_leaves;
2124 ++n_level[p->level];
2125 if (p->level != book->options.level) {
2126 position_print(p, p->board, stdout);
2127 }
2128 }
2129
2130 for (a = book->array; a < book->array + book->n; ++a) {
2131 if (a->n > max_array) max_array = a->n;
2132 if (a->n < min_array) min_array = a->n;
2133 }
2134
2135 bprint("Edax Book %d.%d; ", VERSION, RELEASE);
2136 bprint("%d-%d-%d ", book->date.year, book->date.month, book->date.day);
2137 bprint("%d:%02d:%02d;\n", book->date.hour, book->date.minute, book->date.second);
2138 bprint("Positions: %d (moves = %lld links + %lld leaves);\n", book->n_nodes, n_links, n_leaves);
2139 for (i = 0; i < 61; ++i) {
2140 if (n_level[i]) {
2141 bprint("Level %d : %lld nodes\n", i, n_level[i]);
2142 }
2143 }
2144 bprint("Depth: %d\n", 61 - book->options.n_empties);
2145 bprint("Memory occupation: %lld\n", (long long) (book->n_nodes * sizeof (Position) + book->n * sizeof (PositionArray) + n_links * sizeof (Link)));
2146 bprint("Hash balance: %d < %d < %d\n", min_array, (int) (book->n_nodes / book->n), max_array);
2147}
2148
2155void book_show(Book *book, Board *board)
2156{
2157 GameStats stat = {0,0,0,0};
2158 Position *position = book_probe(book, board);
2159 unsigned long long n_games;
2160
2161 if (position) {
2162 position_show(position, board, stdout);
2163 book_get_game_stats(book, board, &stat);
2164 n_games = stat.n_wins + stat.n_draws + stat.n_losses;
2165 if (n_games) {
2166 bprint("\nLines: %lld full games", n_games);
2167 bprint(" with %.2f%% win, %.2f%% draw, %.2f%% loss",
2168 100.0 * stat.n_wins / n_games, 100.0 * stat.n_draws / n_games, 100.0 * stat.n_losses / n_games);
2169 }
2170 bprint("\n %lld incomplete lines.\n\n", stat.n_lines - n_games);
2171 }
2172}
2173
2174// add for libedax by lavox. 2018/5/20
2182{
2183 return book_probe(book, board);
2184}
2185
2186// add by lavox 2021/8/22
2196void count_bestpath(Book *book, Board *board, unsigned short *n_player, unsigned short *n_opponent, bool verbose)
2197{
2198 MoveList movelist[1];
2199 Move *move;
2200 int bestscore;
2201 unsigned short n_next_player;
2202 unsigned short n_next_opponent;
2203
2204 Position *position = book_probe(book, board);
2205 if (position) {
2206 if ( position->n_player_bestpaths != 0 && position->n_opponent_bestpaths != 0 ) {
2207 *n_player = position->n_player_bestpaths;
2208 *n_opponent = position->n_opponent_bestpaths;
2209 } else {
2210 position_get_moves(position, board, movelist);
2211 bestscore = position->score.value;
2212 *n_player = USHRT_MAX;
2213 *n_opponent = 0;
2214 foreach_move(move, movelist) {
2215 if (move->score != bestscore) continue;
2216 if (book->count_bestpath_stop) break;
2217
2218 board_update(board, move);
2219 count_bestpath(book, board, &n_next_player, &n_next_opponent, false);
2220 *n_player = MIN(*n_player, n_next_opponent);
2221 if ( n_next_player <= USHRT_MAX - *n_opponent ) *n_opponent += n_next_player;
2222 else *n_opponent = USHRT_MAX;
2223 board_restore(board, move);
2224 }
2225 if (book->count_bestpath_stop) return;
2226 if ( *n_opponent == 0 ) *n_player = *n_opponent = 1;
2227 position->n_player_bestpaths = *n_player;
2228 position->n_opponent_bestpaths = *n_opponent;
2229 }
2230 } else {
2231 *n_player = *n_opponent = 1;
2232 }
2233
2234 if ( verbose ) {
2235 bprint("\nplayer bestpaths count : %d\n", *n_player);
2236 bprint("opponent bestpaths count : %d\n", *n_opponent);
2237 }
2238}
2239// add by lavox 2022/6/12
2252void count_board_bestpath(Book *book, Board *board, const int p_lower, const int o_lower, const int turn, unsigned short *n_player, unsigned short *n_opponent, bool verbose)
2253{
2254 MoveList movelist[1];
2255 Move *move;
2256 unsigned short n_next_player;
2257 unsigned short n_next_opponent;
2258 int turn_flag = turn == BLACK ? FLAG_BESTPATH_BLACK : 0;
2259
2260 Position *position = book_probe(book, board);
2261 if (position) {
2262 if ( position->n_player_bestpaths != 0 && position->n_opponent_bestpaths != 0 && (position->flag & FLAG_BESTPATH_BLACK) == turn_flag ) {
2263 *n_player = position->n_player_bestpaths;
2264 *n_opponent = position->n_opponent_bestpaths;
2265 } else {
2266 position_get_moves(position, board, movelist);
2267 int lower = p_lower == BESTPATH_BEST ? position->score.value : p_lower;
2268 *n_player = USHRT_MAX;
2269 *n_opponent = 0;
2270 foreach_move(move, movelist) {
2271 if (move->score < lower) continue;
2272 if (book->count_bestpath_stop) break;
2273
2274 board_update(board, move);
2275 count_board_bestpath(book, board, o_lower, p_lower, 1 - turn, &n_next_player, &n_next_opponent, false);
2276 *n_player = MIN(*n_player, n_next_opponent);
2277 if ( n_next_player <= USHRT_MAX - *n_opponent ) *n_opponent += n_next_player;
2278 else *n_opponent = USHRT_MAX;
2279 board_restore(board, move);
2280 }
2281 if (book->count_bestpath_stop) return;
2282 if ( *n_opponent == 0 ) *n_player = *n_opponent = 1;
2283 position->n_player_bestpaths = *n_player;
2284 position->n_opponent_bestpaths = *n_opponent;
2285 if ( turn == BLACK ) {
2286 position->flag |= FLAG_BESTPATH_BLACK;
2287 } else {
2288 position->flag &= ~FLAG_BESTPATH_BLACK;
2289 }
2290 }
2291 } else {
2292 *n_player = *n_opponent = 1;
2293 }
2294
2295 if ( verbose ) {
2296 bprint("\nplayer bestpaths count : %d\n", *n_player);
2297 bprint("opponent bestpaths count : %d\n", *n_opponent);
2298 }
2299}
2300
2301// add by lavox 2021/8/22
2309void book_count_bestpath(Book *book, Board *board, Position *position)
2310{
2311 unsigned short n_player;
2312 unsigned short n_opponent;
2313
2315 count_bestpath(book, board, &n_player, &n_opponent, false);
2316 Position *p = book_probe(book, board);
2317 if (p) {
2318 memcpy(position, p, sizeof(Position));
2319 } else {
2320 position->n_player_bestpaths = position->n_opponent_bestpaths = 1;
2321 }
2322}
2326
2327// add by lavox 2022/6/12
2338void book_count_board_bestpath(Book *book, Board *board, Position *position, const int p_lower, const int o_lower, const int turn)
2339{
2340 unsigned short n_player;
2341 unsigned short n_opponent;
2342
2344 count_board_bestpath(book, board, p_lower, o_lower, turn, &n_player, &n_opponent, false);
2345 Position *p = book_probe(book, board);
2346 if (p) {
2347 memcpy(position, p, sizeof(Position));
2348 } else {
2349 position->n_player_bestpaths = position->n_opponent_bestpaths = 1;
2350 }
2351}
2352
2360bool book_get_moves(Book *book, const Board *board, MoveList *movelist)
2361{
2362 Position *position = book_probe(book, board);
2363 if (position) {
2364 position_get_moves(position, board, movelist);
2365 return true;
2366 }
2367
2368 return false;
2369}
2370
2380int book_get_moves_with_position(Book *book, const Board *board, MoveList *movelist, Position *position)
2381{
2382 Position *p = book_probe(book, board);
2383 if (p) {
2384 int sym;
2385 sym = position_get_moves(p, board, movelist);
2386 memcpy(position, p, sizeof(Position));
2387 return sym;
2388 }
2389
2390 return -1;
2391}
2392
2401void book_get_line(Book *book, const Board *board, const Move *move, Line *line)
2402{
2403 Position *position;
2404 Board b[1];
2405 Move m[1];
2406
2407 line_push(line, move->x);
2408 board_next(board, move->x, b);
2409
2410 while ((position = book_probe(book, b)) != NULL && !board_is_game_over(position->board)) {
2411 position_get_random_move(position, b, m, book->random, 0);
2412 line_push(line, m->x);
2413 board_update(b, m);
2414 }
2415}
2416
2417
2426bool book_get_random_move(Book *book, const Board *board, Move *move, const int randomness)
2427{
2428 Position *position = book_probe(book, board);
2429 if (position) {
2430 position_get_random_move(position, board, move, book->random, randomness);
2431 return true;
2432 }
2433
2434 return false;
2435}
2436
2444void book_get_game_stats(Book *book, const Board *board, GameStats *stat)
2445{
2446 Position *position;
2447
2448 assert(book != NULL);
2449 assert(board !=NULL);
2450 assert(stat != NULL);
2451
2452 stat->n_wins = stat->n_losses = stat->n_draws = stat->n_lines = 0;
2453
2454 position = book_probe(book, board);
2455 if (position) {
2456 if (position->n_wins == UINT_MAX || position->n_losses == UINT_MAX || position->n_draws == UINT_MAX || position->n_lines == UINT_MAX) {
2457 Board target[1];
2458 Link *l;
2459 GameStats child;
2460
2461 foreach_link(l, position) {
2462 board_next(position->board, l->move, target);
2463 book_get_game_stats(book, target, &child);
2464 stat->n_wins += child.n_losses;
2465 stat->n_draws += child.n_draws;
2466 stat->n_losses += child.n_wins;
2467 stat->n_lines += child.n_lines;
2468 }
2469 } else {
2470 stat->n_wins = position->n_wins;
2471 stat->n_draws = position->n_draws;
2472 stat->n_losses = position->n_losses;
2473 stat->n_lines = position->n_lines;
2474 }
2475 }
2476}
2477
2478
2485void book_add_board(Book *book, const Board *board)
2486{
2487 Position position[1];
2488 Position *probe;
2489
2490 if (board_count_empties(board) >= book->options.n_empties - 1) {
2491 probe = book_probe(book, board);
2492 if (probe) {
2493 position_link(probe, book);
2494 if (probe->leaf.move == NOMOVE) position_search(probe, book);
2495 if (BOOK_DEBUG) {printf("update: "); position_print(probe, board, stdout);}
2496 } else {
2497 position_init(position);
2498 *position->board = *board;
2499 position->level = book->options.level;
2500 position_link(position, book);
2501 position_search(position, book);
2502 if (BOOK_DEBUG) {printf("new: "); position_print(position, board, stdout);}
2503 position_unique(position);
2504 book_add(book, position);
2505 }
2506 }
2507}
2508
2515void book_add_game(Book *book, const Game *game)
2516{
2517 Board board[1];
2518 Move stack[99];
2519 int i, n_moves;
2520 char file[FILENAME_MAX + 1];
2521 const int n_stats = book->stats.n_nodes + book->stats.n_links;
2522
2523 file_add_ext(options.book_file, ".gam", file);
2524
2525 board_init(board);
2526 if (!board_equal(board, game->initial_board)) return; // skip non standard game
2527 for (i = n_moves = 0; i < 60 - book->options.n_empties && game->move[i] != NOMOVE; ++i) {
2528 if (!can_move(board->player, board->opponent)) {
2529 stack[n_moves++] = MOVE_PASS;
2530 board_pass(board);
2531 }
2532 if (!board_is_occupied(board, game->move[i]) && board_get_move(board, game->move[i], &stack[n_moves])) {
2533 board_update(board, stack + n_moves);
2534 ++n_moves;
2535 } else {
2536 warn("illegal move in game");
2537 break; // stop, illegal moves
2538 }
2539 }
2540
2541 search_cleanup(book->search);
2542 while (--n_moves >= 0) {
2543 book_add_board(book, board);
2544 board_restore(board, stack + n_moves);
2545 }
2546
2547 if (book->stats.n_nodes + book->stats.n_links > n_stats && book_get_age(book) > 3600) book_save(book, file);
2548}
2549
2556void book_add_base(Book *book, const Base *base)
2557{
2558 int i;
2559 char file[FILENAME_MAX + 1];
2560 long long t0, t;
2561
2562 file_add_ext(options.book_file, ".gam", file);
2563
2564 book_clean(book);
2565 bprint("Adding %d games to book...\n", base->n_games);
2566 t0 = real_clock();
2567 for (i = 0; i < base->n_games; ++i) {
2568 book_add_game(book, base->game + i);
2569 t = real_clock();
2570 if (t - t0 > 1000) {
2571 bprint("Adding games...%d/%d done: %d positions, %d links\r", i + 1, base->n_games, book->stats.n_nodes, book->stats.n_links);
2572 t0 = t;
2573 }
2574 if (book->search->options.verbosity) putchar('\n');
2575
2576 }
2577 bprint("Adding games...%d/%d done: %d positions, %d links\n", i, base->n_games, book->stats.n_nodes, book->stats.n_links);
2578 bprint("%d games added to book\n", i);
2579
2580 book_save(book, file);
2581}
2582
2583typedef struct BookCheckGame {
2584 unsigned long long missing;
2585 unsigned long long good;
2586 unsigned long long bad;
2588
2597void book_check_game(Book *book, MoveHash *hash, const Game *game, BookCheckGame *stat)
2598{
2599 Board board[1];
2600 Move stack[99], *iter;
2601 MoveList movelist[1];
2602 int i, n_moves;
2603 int bestscore;
2604
2605 board_init(board);
2606 if (!board_equal(board, game->initial_board)) return; // skip non standard game
2607 for (i = n_moves = 0; i <= 60 - book->options.n_empties && game->move[i] != NOMOVE; ++i) {
2608 if (!can_move(board->player, board->opponent)) {
2609 stack[n_moves++] = MOVE_PASS;
2610 board_pass(board);
2611 }
2612 if (!board_is_occupied(board, game->move[i]) && board_get_move(board, game->move[i], &stack[n_moves])) {
2613 board_update(board, stack + n_moves);
2614 ++n_moves;
2615 } else {
2616 warn("illegal move in game");
2617 break; // stop, illegal moves
2618 }
2619 }
2620
2621 while (--n_moves >= 0) {
2622 board_restore(board, stack + n_moves);
2623 if (movehash_append(hash, board, stack[n_moves].x)) {
2624 if (book_get_moves(book, board, movelist)) {
2625 bestscore = movelist_first(movelist)->score;
2626 foreach_move(iter, movelist) {
2627 if (iter->x == stack[n_moves].x) {
2628 if (iter->score < bestscore) ++stat->bad;
2629 else ++stat->good;
2630 break;
2631 }
2632 }
2633 } else ++stat->missing;
2634 }
2635 }
2636}
2637
2644void book_check_base(Book *book, const Base *base)
2645{
2646 int i;
2647 BookCheckGame stat = {0, 0, 0};
2648 MoveHash hash;
2649
2650 bprint("Checking %d games to book...\n", base->n_games);
2652 for (i = 0; i < base->n_games; ++i) {
2653 book_check_game(book, &hash, base->game + i, &stat);
2654 }
2655 movehash_delete(&hash);
2656 bprint("Positions : %llu missing, %llu good, %llu bad (%.2f%% bad)\n", stat.missing, stat.good, stat.bad, (100.0 * stat.bad)/(stat.bad + stat.good));
2657}
2658
2659
2671static void extract_skeleton(Book *book, Board *board, Line *pv, Base *base)
2672{
2673 MoveList movelist[1];
2674 Move *move;
2675 Board init[1];
2676 Game game[1];
2677 int bestscore;
2678
2679 if (book_get_moves(book, board, movelist)) {
2680 bestscore = movelist_best(movelist)->score;
2681
2682 foreach_move(move, movelist) {
2683 if (move->score == bestscore) {
2684 board_update(board, move); line_push(pv, move->x);
2685 extract_skeleton(book, board, pv, base);
2686 board_restore(board, move); line_pop(pv);
2687 }
2688 }
2689 } else if (pv->n_moves) {
2690 board_init(init);
2691 line_to_game(init, pv, game);
2692 base_append(base, game);
2693 if (base->n_games % 1000 == 0) {
2694 bprint("extracting %d games\r", base->n_games);
2695
2696 }
2697 }
2698}
2699
2710{
2711 Line pv[1];
2712 Board board[1];
2713
2714 line_init(pv, BLACK);
2715 line_push(pv, F5); line_push(pv, D6); line_push(pv, C4);
2716 board_init(board);
2717 board_next(board, F5, board); board_next(board, D6, board); board_next(board, C4, board);
2718 extract_skeleton(book, board, pv, base);
2719
2720 line_init(pv, BLACK);
2721 line_push(pv, F5); line_push(pv, F6); line_push(pv, E6); line_push(pv, F4);
2722 board_init(board);
2723 board_next(board, F5, board); board_next(board, F6, board);
2724 board_next(board, E6, board); board_next(board, F4, board);
2725 extract_skeleton(book, board, pv, base);
2726 bprint("%d games extracted \n", base->n_games);
2727}
2728
2729
2737void book_extract_positions(Book *book, const int n_empties, const int n_positions)
2738{
2739 PositionArray *a;
2740 Position *p;
2741 MoveList movelist[1];
2742 Move *best, *second_best;
2743 int i = 0;
2744 char s[80];
2745
2746 bprint("Extracting %d positions at %d ...\n", n_positions, n_empties);
2747 foreach_position(p, a, book) {
2748 if (i == n_positions) break;
2749 if (board_count_empties(p->board) == n_empties) {
2750 position_get_moves(p, p->board, movelist);
2751 best = movelist_first(movelist);
2752 if (best) {
2753 second_best = move_next(best);
2754 if (second_best && best->score > second_best->score) {
2755 ++i;
2756 board_to_string(p->board, n_empties & 1, s);
2757 bprint("%s %% bm ", s);
2758 move_print(best->x, n_empties & 1, stdout);
2759 bprint(":%+2d; ba ", best->score);
2760 move_print(second_best->x, n_empties & 1, stdout);
2761 bprint(":%+2d;\n", second_best->score);
2762 }
2763 }
2764 }
2765 }
2766}
2767
2773void book_stats(Book *book)
2774{
2775 PositionArray *a;
2776 Position *p;
2777 int i;
2778 unsigned long long n_hash[256];
2779 unsigned long long n_pos[61], n_leaf[61], n_link[61], n_terminal[61];
2780 unsigned long long n_score[129];
2781
2782 printf("\n\nBook statistics:\n");
2783
2784 printf("\nHash distribution:\n");
2785 for (i = 0; i < 256; ++i) n_hash[i] = 0;
2786 for (a = book->array; a < book->array + book->n; ++a) {
2787 if (a->n < 256) ++n_hash[a->n];
2788 else ++n_hash[255];
2789 }
2790 printf("index positions\n");
2791 for (i = 0; i < 255; ++i) if (n_hash[i]) printf("%5d %12llu\n", i, n_hash[i]);
2792 if (n_hash[i]) printf(">%4d %12llu\n", i - 1, n_hash[i]);
2793
2794 printf("\nStage distribution:\n");
2795 printf("stage positions links leaves terminal nodes\n");
2796 for (i = 0; i < 61; ++i) n_pos[i] = n_leaf[i] = n_link[i] = n_terminal[i] = 0;
2797 foreach_position(p, a, book) {
2798 i = board_count_empties(p->board);
2799 ++n_pos[i];
2800 if (p->leaf.move != NOMOVE) ++n_leaf[i];
2801 if (p->n_link == 0) ++n_terminal[i];
2802 n_link[i] += p->n_link;
2803 }
2804 for (i = 0; i < 61; ++i) if (n_pos[i]) printf("%5d %12llu %12llu %12llu %12llu\n", i, n_pos[i], n_link[i], n_leaf[i], n_terminal[i]);
2805
2806 printf("\nBest Score Distribution:\n");
2807 printf("Score positions\n");
2808 for (i = 0; i < 129; ++i) n_score[i] = 0;
2809 foreach_position(p, a, book) {
2810 ++n_score[64 + p->score.value];
2811 }
2812 for (i = 0; i < 129; ++i) if (n_score[i]) printf("%+5d %12llu\n", i - 64, n_score[i]);
2813 printf("\n\n");
2814 fflush(stdout);
2815}
2816
2824void book_feed_hash(const Book *book, Board *board, Search *search)
2825{
2826 board_feed_hash(board, book, search, true);
2827}
void base_append(Base *base, const Game *game)
Add a game to a game database.
Definition base.c:696
#define foreach_bit(i, b)
Definition bit.h:39
unsigned long long board_get_hash_code(const Board *board)
Compute a hash code.
Definition board.c:1134
void board_restore(Board *board, const Move *move)
Restore a board.
Definition board.c:487
bool board_is_game_over(const Board *board)
Check if the game is over.
Definition board.c:1203
void board_print(const Board *board, const int player, FILE *f)
Print out the board.
Definition board.c:1230
void board_update(Board *board, const Move *move)
Update a board.
Definition board.c:469
int board_count_empties(const Board *board)
Check if the game is over.
Definition board.c:1216
unsigned long long board_get_move(const Board *board, const int x, Move *move)
Compute a move.
Definition board.c:438
int board_unique(const Board *board, Board *unique)
unique board
Definition board.c:379
void board_init(Board *board)
Set a board to the starting position.
Definition board.c:280
char * board_to_string(const Board *board, const int player, char *s)
convert the to a compact string.
Definition board.c:1272
int get_mobility(const unsigned long long P, const unsigned long long O)
Count legal moves.
Definition board.c:833
void board_check(const Board *board)
Check board consistency.
Definition board.c:291
bool board_equal(const Board *b1, const Board *b2)
Compare two board for equality.
Definition board.c:335
DLL_API unsigned long long get_moves(const unsigned long long P, const unsigned long long O)
Get legal moves.
Definition board.c:621
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
void board_pass(Board *board)
Passing move.
Definition board.c:503
bool board_is_occupied(const Board *board, const int x)
Check if a square is occupied.
Definition board.c:1180
void board_symetry(const Board *board, const int s, Board *sym)
symetric board
Definition board.c:347
static void position_link(Position *position, Book *book)
Link a position.
Definition book.c:722
static bool position_read(Position *position, FILE *f)
Read a position.
Definition book.c:292
static Position * book_probe(const Book *, const Board *)
Find a position in the book.
Definition book.c:1309
static void bprint(const char *format,...)
print a message on stdout.
Definition book.c:43
static void position_remove_links(Position *position, Book *book)
Remove bad links after book pruning.
Definition book.c:921
bool book_verbose
Definition book.c:36
void book_extract_positions(Book *book, const int n_empties, const int n_positions)
print a set of position.
Definition book.c:2737
static bool position_add_link(Position *position, const Link *link)
Add a link to this position.
Definition book.c:608
static int position_negamax(Position *position, Book *book)
Negamax a position.
Definition book.c:790
void count_bestpath(Book *book, Board *board, unsigned short *n_player, unsigned short *n_opponent, bool verbose)
count the number of best paths in book.
Definition book.c:2196
static void position_array_free(PositionArray *a)
Free resources used by a position array.
Definition book.c:1233
void book_check_base(Book *book, const Base *base)
Check positions from a game database.
Definition book.c:2644
void book_init(Book *book)
Initialize the opening book.
Definition book.c:1396
static void book_expand(Book *book, const char *action, const char *tmp_file)
Expand a book.
Definition book.c:1830
static bool link_read(Link *link, FILE *f)
read a link.
Definition book.c:64
static Position * position_array_probe(PositionArray *a, const Board *board)
Find a position in the array.
Definition book.c:1247
void book_count_bestpath(Book *book, Board *board, Position *position)
count the number of best paths in book.
Definition book.c:2309
static bool position_export(const Position *p, FILE *f)
write a position.
Definition book.c:428
static bool link_is_bad(const Link *link)
check if a link is unvalid.
Definition book.c:92
static void position_sort(Position *position)
Sort the link moves.
Definition book.c:641
static void position_fix(Position *position, Book *book)
Fix a position.
Definition book.c:1126
static void book_clean(Book *book)
Set all positions as undone.
Definition book.c:1353
static bool position_array_add(PositionArray *a, const Position *p)
Add a position to the array.
Definition book.c:1173
void book_deepen(Book *book)
Deepen a book.
Definition book.c:1746
void book_show(Book *book, Board *board)
Display a position from the book.
Definition book.c:2155
#define foreach_position(p, a, b)
Definition book.c:1254
static Position * book_root(Book *book)
Find the initial position in the book.
Definition book.c:1381
void book_count_board_bestpath(Book *book, Board *board, Position *position, const int p_lower, const int o_lower, const int turn)
count the number of "broad" best paths in book.
Definition book.c:2338
static void position_expand(Position *position, Book *book)
Expand a position.
Definition book.c:762
static double book_get_age(Book *book)
Get book age, in seconds.
Definition book.c:1282
void book_add_board(Book *book, const Board *board)
Add a position.
Definition book.c:2485
void book_import(Book *book, const char *file)
Import an opening book.
Definition book.c:1547
void book_play(Book *book)
Play.
Definition book.c:1882
static void position_get_random_move(const Position *position, const Board *board, Move *move, Random *r, const int randomness)
Chose a move at random from the position.
Definition book.c:571
void book_subtree(Book *book, const Board *board)
Prune a book.
Definition book.c:2043
void book_enhance(Book *book, Board *board, const int midgame_error, const int endcut_error)
Enhance a book.
Definition book.c:2075
void count_board_bestpath(Book *book, Board *board, const int p_lower, const int o_lower, const int turn, unsigned short *n_player, unsigned short *n_opponent, bool verbose)
count the number of "broad" best paths in book.
Definition book.c:2252
struct PositionArray PositionArray
An array with positions.
void book_info(Book *book)
display some book's informations.
Definition book.c:2111
void book_get_line(Book *book, const Board *board, const Move *move, Line *line)
Get a variation from the book.
Definition book.c:2401
void book_correct_solved(Book *book)
Correct wrong solved score in the book.
Definition book.c:1782
#define foreach_link(l, p)
Definition book.c:101
static void extract_skeleton(Book *book, Board *board, Line *pv, Base *base)
Extract book lines to a game base.
Definition book.c:2671
static int position_get_moves(const Position *position, const Board *board, MoveList *movelist)
Get moves from a position.
Definition book.c:463
void book_negamax(Book *book)
Negamax a book.
Definition book.c:1684
bool book_get_random_move(Book *book, const Board *board, Move *move, const int randomness)
Get a move at random from the opening book.
Definition book.c:2426
static bool position_is_ok(const Position *position)
Check if position is ok or need fixing.
Definition book.c:130
static void book_add(Book *, const Position *)
Add a position to the book.
Definition book.c:1322
static void book_remove(Book *book, const Position *p)
Remove a position from the book.
Definition book.c:1338
int book_get_moves_with_position(Book *book, const Board *board, MoveList *movelist, Position *position)
Get a list of moves from the book.
Definition book.c:2380
static void position_merge(Position *dest, const Position *src)
Merge a position with another one.
Definition book.c:257
static void position_prune(Position *position, Book *book, const int player_deviation, const int opponent_deviation, const int lower, const int upper)
Prune a position.
Definition book.c:889
static void position_print(const Position *, const Board *, FILE *)
print a position in a compact but readable format.
Definition book.c:538
void book_export(Book *book, const char *file)
Export an opening book.
Definition book.c:1589
void book_free(Book *book)
Free resources used by the opening book.
Definition book.c:1422
void book_preprocess(Book *book)
clean opening book.
Definition book.c:1436
void book_check_game(Book *book, MoveHash *hash, const Game *game, BookCheckGame *stat)
Check positions from a game.
Definition book.c:2597
void book_save(Book *book, const char *file)
Save an opening book.
Definition book.c:1622
void book_fill(Book *book, const int depth)
Fill a book.
Definition book.c:1922
static void position_show(const Position *position, const Board *board, FILE *f)
print a position in a readable format.
Definition book.c:506
static bool position_write(const Position *position, FILE *f)
Write a position.
Definition book.c:393
static void board_feed_hash(Board *board, const Book *book, Search *search, const bool is_pv)
Feed hash from a position.
Definition book.c:1051
void book_stats_clean(Book *book)
Definition book.c:1365
static bool link_write(const Link *link, FILE *f)
write a link.
Definition book.c:78
bool book_get_moves(Book *book, const Board *board, MoveList *movelist)
Get a list of moves from the book.
Definition book.c:2360
static int get_book_depth(const int depth)
return the number of plies from where the search is solving.
Definition book.c:110
void book_fix(Book *book)
Fix a book.
Definition book.c:1723
void book_sort(Book *book)
Sort a book.
Definition book.c:1862
void book_merge(Book *dest, const Book *src)
Merge two opening books.
Definition book.c:1665
static void position_array_init(PositionArray *a)
Initialize the array.
Definition book.c:1160
static bool position_import(Position *position, FILE *f)
Read a position.
Definition book.c:339
void book_link(Book *book)
Link a book.
Definition book.c:1701
static void position_unique(Position *position)
Make position unique, regarding symetries.
Definition book.c:442
#define BOOK_DEBUG
Definition book.c:31
void book_get_game_stats(Book *book, const Board *board, GameStats *stat)
Get game statistics from a position.
Definition book.c:2444
Position * book_show_for_api(Book *book, Board *board)
Display a position from the book.
Definition book.c:2181
void book_feed_hash(const Book *book, Board *board, Search *search)
feed hash table from the opening book.
Definition book.c:2824
const Link BAD_LINK
Definition book.c:56
static bool position_array_remove(PositionArray *a, const Position *p)
Remove a position from an array.
Definition book.c:1211
void book_prune(Book *book)
Prune a book.
Definition book.c:2010
static bool board_fill(Board *board, Book *book, int depth)
Fill the opening book.
Definition book.c:1088
struct BookCheckGame BookCheckGame
void book_stats(Book *book)
print book statistics.
Definition book.c:2773
void book_extract_skeleton(Book *book, Base *base)
Extract book draws to a game base.
Definition book.c:2709
static void position_search(Position *position, Book *book)
Evaluate a position.
Definition book.c:668
void book_deviate(Book *book, Board *board, const int relative_error, const int absolute_error)
Deviate a book.
Definition book.c:1963
void book_new(Book *book, int level, int n_empties)
Create a new opening book.
Definition book.c:1450
void book_load(Book *book, const char *file)
Load the opening book.
Definition book.c:1471
static const int BOOK_INFO_RESOLUTION
Definition book.c:32
static void position_enhance(Position *position, Book *book)
Enhance a position.
Definition book.c:997
static void position_deviate(Position *position, Book *book, const int player_deviation, const int opponent_deviation, const int lower, const int upper)
Deviate a position.
Definition book.c:955
void book_add_game(Book *book, const Game *game)
Add positions from a game.
Definition book.c:2515
void book_add_base(Book *book, const Base *base)
Add positions from a game database.
Definition book.c:2556
void book_stop_count_bestpath(Book *book)
Definition book.c:2323
static void position_init(Position *position)
Initialize a position.
Definition book.c:225
static void book_set_date(Book *book)
Set book date.
Definition book.c:1263
static void position_free(Position *position)
Free resources used by a position.
Definition book.c:281
#define FLAG_TODO
Definition book.h:99
#define FLAG_DONE
Definition book.h:98
#define FLAG_BESTPATH_BLACK
Definition book.h:100
#define TIME_MAX
Definition const.h:61
#define SCORE_INF
Definition const.h:52
#define VERSION
Definition const.h:86
#define HOUR
Definition const.h:64
#define EDAX
Definition const.h:91
#define RELEASE
Definition const.h:87
@ PASS
Definition const.h:37
@ NOMOVE
Definition const.h:37
@ E6
Definition const.h:34
@ C4
Definition const.h:32
@ F5
Definition const.h:33
@ F4
Definition const.h:32
@ F6
Definition const.h:34
@ D6
Definition const.h:34
@ H8
Definition const.h:36
@ WHITE
Definition const.h:43
@ BLACK
Definition const.h:42
#define BOOK
Definition const.h:90
#define BESTPATH_BEST
Definition const.h:119
@ RUNNING
Definition const.h:71
@ STOP_PONDERING
Definition const.h:73
void line_to_game(const Board *initial_board, const Line *line, Game *game)
Build a game from an initial position and a move sequence.
Definition game.c:415
void hash_feed(HashTable *hash_table, const unsigned long long hash_code, const int depth, const int selectivity, const int lower, const int upper, const int move)
Feed hash table (from Cassio).
Definition hash-lock-free.c:496
Move * movelist_best(MoveList *movelist)
Return the best move of the list.
Definition move.c:411
void line_push(Line *line, const int x)
Add a move to the sequence.
Definition move.c:561
void movehash_delete(MoveHash *hash)
Free the hash table.
Definition move.c:730
void movehash_init(MoveHash *hash, int bitsize)
Initialisation of the hash table.
Definition move.c:715
void move_print(const int x, const int player, FILE *f)
Print out a move.
Definition move.c:110
const Move MOVE_PASS
Definition move.c:26
void movelist_sort(MoveList *movelist)
Sort all moves.
Definition move.c:505
Move * movelist_first(MoveList *movelist)
Return the first move of the list.
Definition move.c:422
bool movehash_append(MoveHash *hash, const Board *b, const int x)
Append a position to the hash table.
Definition move.c:745
Move * move_next(Move *move)
Return the next move from the list.
Definition move.c:400
void line_init(Line *line, const int player)
Initialize a sequence of moves.
Definition move.c:549
void line_pop(Line *line)
Remove the last move from a sequence.
Definition move.c:578
int movelist_get_moves(MoveList *movelist, const Board *board)
Get moves from a position.
Definition move.c:298
Move * movelist_exclude(MoveList *movelist, const int move)
Exclude a move.
Definition move.c:516
char * move_to_string(const int x, const int player, char *s)
Print out a move.
Definition move.c:76
int symetry(int x, const int sym)
Get a symetric square coordinate.
Definition move.c:47
#define foreach_move(iter, movelist)
Definition move.h:78
#define foreach_best_move(iter, movelist)
Definition move.h:82
Options options
Definition options.c:22
void * search_run(void *v)
Search the bestmove of a given board.
Definition root.c:810
void search_set_level(Search *search, const int level, const int n_empties)
Set the search level.
Definition search.c:609
const int NO_SELECTIVITY
Definition search.c:94
struct Level LEVEL[61][61]
Definition search.c:144
void search_cleanup(Search *search)
Clean-up some search data.
Definition search.c:578
void search_set_board(Search *search, const Board *board, const int player)
Set the board to analyze.
Definition search.c:593
Definition base.h:48
Game * game
Definition base.h:49
int n_games
Definition base.h:50
Definition board.h:26
unsigned long long player
Definition board.h:27
unsigned long long opponent
Definition board.h:27
Definition book.c:2583
unsigned long long bad
Definition book.c:2586
unsigned long long missing
Definition book.c:2584
unsigned long long good
Definition book.c:2585
The opening book.
Definition book.h:25
struct Book::@2 stats
int endcut_error
Definition book.h:35
short year
Definition book.h:27
int n
Definition book.h:45
struct PositionArray * array
Definition book.h:43
int n_empties
Definition book.h:33
bool need_saving
Definition book.h:47
int n_links
Definition book.h:40
int level
Definition book.h:32
Random random[1]
Definition book.h:48
char minute
Definition book.h:29
char second
Definition book.h:29
char day
Definition book.h:28
int n_todo
Definition book.h:41
char hour
Definition book.h:29
int n_nodes
Definition book.h:39
struct Book::@0 date
char month
Definition book.h:28
struct Book::@1 options
volatile Stop count_bestpath_stop
Definition book.h:50
int midgame_error
Definition book.h:34
Search * search
Definition book.h:49
Game statistics.
Definition book.h:57
unsigned long long n_draws
Definition book.h:59
unsigned long long n_losses
Definition book.h:60
unsigned long long n_lines
Definition book.h:61
unsigned long long n_wins
Definition book.h:58
Definition game.h:22
char move[60]
Definition game.h:33
Board initial_board[1]
Definition game.h:23
int depth
Definition search.h:89
int selectivity
Definition search.h:90
Definition move.h:35
int n_moves
Definition move.h:37
Definition move.h:93
Definition move.h:29
int n_moves
Definition move.h:31
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
unsigned long long flipped
Definition move.h:21
int x
Definition move.h:22
int level
Definition options.h:40
char * book_file
Definition options.h:59
int hash_table_size
Definition options.h:25
An array with positions.
Definition book.c:1148
int size
Definition book.c:1151
Position * positions
Definition book.c:1149
int n
Definition book.c:1150
A position stored in the book.
Definition book.h:77
unsigned int n_wins
Definition book.h:87
Link leaf
Definition book.h:79
struct Position::@3 score
unsigned int n_draws
Definition book.h:88
unsigned char n_link
Definition book.h:94
unsigned short n_opponent_bestpaths
Definition book.h:85
unsigned char level
Definition book.h:95
unsigned int n_lines
Definition book.h:90
unsigned int n_losses
Definition book.h:89
unsigned short n_player_bestpaths
Definition book.h:84
Board board[1]
Definition book.h:78
short value
Definition book.h:92
short upper
Definition book.h:92
Link * link
Definition book.h:86
short lower
Definition book.h:92
unsigned char flag
Definition book.h:83
Definition util.h:87
int move
Definition search.h:44
int score
Definition search.h:45
Definition search.h:95
struct Search::@24 time
const char * separator
Definition search.h:145
int n_empties
Definition search.h:99
MoveList movelist[1]
Definition search.h:132
Result * result
Definition search.h:151
const char * header
Definition search.h:144
bool time_per_move
Definition search.h:141
int player
Definition search.h:100
struct Search::@25 options
int verbosity
Definition search.h:142
HashTable pv_table[1]
Definition search.h:104
HashTable hash_table[1]
Definition search.h:103
Board board[1]
Definition search.h:96
char * parse_move(const char *string, const Board *board, Move *move)
Parse a move.
Definition util.c:627
char * file_add_ext(const char *base, const char *ext, char *file)
Add an extension to a string.
Definition util.c:907
char * parse_int(const char *string, int *result)
Parse an integer.
Definition util.c:761
char * string_read_line(FILE *f)
Read a line.
Definition util.c:265
unsigned long long random_get(Random *random)
Pseudo-random number generator.
Definition util.c:1043
void random_seed(Random *random, const unsigned long long seed)
Pseudo-random number seed.
Definition util.c:1062
char * parse_find(const char *string, const int c)
Find a char.
Definition util.c:545
char * parse_board(const char *string, Board *board, int *player)
Parse a board.
Definition util.c:682
Miscellaneous utilities header.
#define error(...)
Display an error message as "ERROR : filename : funcname : line number : ...".
Definition util.h:361
long long real_clock(void)
#define MIN(a, b)
Definition util.h:101
#define fatal_error(...)
Display an error message as "FATAL_ERROR : file name : function name : line number : ....
Definition util.h:349
#define info(...)
Display a message.
Definition util.h:382
#define warn(...)
Display a warning message as "WARNING : ... ".
Definition util.h:373
#define BOUND(var, min, max, name)
Definition util.h:104