├── .gitignore ├── ChangeLog ├── Makefile ├── README.md ├── board.c ├── board.h ├── board_util.c ├── cgos ├── README.md ├── config.gtp ├── michi.cfg └── run ├── control.c ├── debug.c ├── doc ├── INSTALL.md ├── Makefile ├── func_map.txt ├── functions.txt ├── img │ ├── img1.png │ ├── img10.png │ ├── img11.png │ ├── img12.png │ ├── img13.png │ ├── img14.png │ ├── img15.png │ ├── img2.png │ ├── img3.png │ ├── img4.png │ ├── img5.png │ ├── img6.png │ ├── img7.png │ ├── img8.png │ └── img9.png ├── manual.html └── manual.rst ├── main.c ├── michi.c ├── michi.h ├── non_portable.h ├── params.c ├── patterns.c ├── sgf.c ├── tests ├── board.tst ├── confs │ ├── config_1.gtp │ └── config_20000.gtp ├── fix_atari.tst ├── large_pat.tst ├── patterns.prob ├── patterns.spat ├── ref │ ├── mcdebug.txt │ └── tsdebug.txt ├── run ├── run_michi ├── sgf │ └── tsumego │ │ ├── ch2_ex2.sgf │ │ ├── ch2_ex2_simpler.sgf │ │ ├── ch2_p10.sgf │ │ ├── simple.sgf │ │ ├── simple_4spaces.sgf │ │ ├── simple_seki.sgf │ │ └── simple_white.sgf ├── sgf1.sgf ├── tsumego.tst └── undo.tst └── ui.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | twogtp/ 3 | michi 4 | michi.log 5 | patterns.prob 6 | patterns.spat 7 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | --------------------------------------- 2 | -- ChangeLog for the michi-c program -- 3 | --------------------------------------- 4 | 5 | 1.4.2 Released May, 31 2017 6 | - bug correction in gogui-setup and gogui-play_sequence gtp commands 7 | - avoid avoid redondant fix_atari tests in mcplayout (small speed improvement) 8 | - handle seki in the tree 9 | - add a new mode : selfplay 10 | - new tests (very simple tsumegos) 11 | - check gtp command parameters 12 | - sgf.c : decode a few new properties, detect if input is not a SGF file 13 | - it is now possible to read a series of sgf files 14 | - bugs corrections 15 | 16 | 1.4.1 Released February, 28 2016 17 | - respect the Superko rule (Positional) 18 | - add storesgf gtp command 19 | - add estimated best reply in the log 20 | - make zobrist hash initialization independant of user defined random seed 21 | - make board a self-contained module 22 | - correction for reading sgf files coming from kgs 23 | - correct a bug with noticeable impact on michi-c strength (point_env8()) 24 | - modifs to remove warnings with newer versions of gcc 25 | - use of "long long" to insure use of 64 bits integers (hash, random generator) 26 | - runtime check of the length of integers (runtime check for portability) 27 | - bugs correction 28 | 29 | 1.4 Released August, 28 2015 30 | - read simple sgf files (no variations) 31 | - live gfx (specific for gogui) 32 | - early passing 33 | - specific version of random_int for 32 bits system (seems useful for windows) 34 | - use specifed color in play and genmove commands 35 | - preliminary (basic) version of dynamic komi 36 | - better modularization of the code 37 | - histogram of playout scores 38 | - new gtp commands for visualisation of the search results with gogui 39 | owner_map, principal_variation, best_moves, visit_count 40 | - new gtp commands 41 | undo, final_score, final_status_list, kgs-genmove_cleanup, set_free_handicap 42 | gogui-setup, gogui-setup_player, gg-undo 43 | - variable boardsize 44 | - works with limited stack size (512 K bytes OK) 45 | - better usage of memory 46 | - speed improvements 47 | - add a minimal documentation of the program structure 48 | - bugs corrections 49 | 50 | 1.3 Released June, 14 2015 (to play on CGOS) 51 | - time management (sudden death only) : partial implementation of Pachi method 52 | - option to play until the end 53 | - move the functions dedicated to user interface in ui.c 54 | - simplify command line interface (backward compatible) 55 | - parameters modifiable by gtp commands 56 | - add configuration file (gtp commands) 57 | - add control of verbosity 58 | - cleanup the gtp loop 59 | - example files to play on CGOS 60 | 61 | 1.2 Released June, 6 2015 62 | - modifications by Horace Ho for IOS app 63 | - removal of all calls to compute_block() 64 | - bug correction (saturation of blocks size to 255) 65 | - add a parameter (nsims) to michi mcdebug 66 | - choose_random_move() stops as soon as a playable move is found 67 | - plays about 2.5 faster in 19x19 than version 1.0 (mcplayout 3 times faster) 68 | - add gtp commands to modify the parameters of the engine 69 | - add gtp command to modify komi 70 | - boardsize still fixed at compilation time 71 | 72 | 1.1 Released May, 28 2015 (Temporary version) 73 | 74 | - functional but not fully validated nor optimized 75 | - incremental update of blocks and liberties at each move (new board.c) 76 | - add regression tests for board 77 | - change color: BLACK and WHITE instead of X of x (to_play and other) 78 | - use blocks and liberties in make_list_neighbor_blocks_in_atari() 79 | 80 | 1.0 Released May, 12 2015 81 | 82 | - exactly the same algorithms and parameters as in michi.py 83 | - single thread 84 | - almost 10 times faster in 13x13 games than michi.py (with pypy and 4 threads) 85 | - regression tests for fix_atari() and large patterns 86 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | # For compiling portable code 3 | #GENCODE=-DPORTABLE 4 | # For compiling more efficient code for a given compiler/processor architecture 5 | GENCODE= 6 | 7 | # Normal compilation options for developping 8 | #CFLAGS= -DNDEBUG -g -march=native -msse4 -fshort-enums -Wall -Wno-char-subscripts 9 | #CFLAGS= -g -march=native -msse4 -fshort-enums -Wall -Wno-char-subscripts 10 | #CFLAGS= -O3 -march=native -msse4 -fshort-enums -Wall -Wno-char-subscripts 11 | 12 | # Normal compilation options for production 13 | CFLAGS= -DNDEBUG -O3 -march=native -msse4 -fshort-enums -Wall -Wno-char-subscripts 14 | 15 | # Compilation options for running valgrind 16 | #CFLAGS=-O0 -g -march=native -msse4 -Wall -std=gnu99 17 | 18 | # Compilation options for profiling with gprof 19 | #CFLAGS=-pg -O3 -DNDEBUG -march=native -msse4 -fshort-enums -Wall -Wno-char-subscripts 20 | 21 | OBJS=ui.o sgf.o control.o michi.o params.o board.o board_util.o patterns.o debug.o main.o 22 | BIN=michi 23 | 24 | all: $(BIN) 25 | 26 | michi: $(OBJS) michi.h board.h 27 | gcc $(CFLAGS) -std=gnu99 -o michi $(OBJS) -lm 28 | 29 | %.o: %.c michi.h board.h 30 | gcc $(GENCODE) $(CFLAGS) -c -std=gnu99 $< 31 | 32 | test: all 33 | tests/run 34 | 35 | test_verbose: 36 | tests/run -long 37 | 38 | test_michi: 39 | tests/run_michi 40 | 41 | valgrind: michi 42 | valgrind --track-origins=yes ./michi tsdebug 43 | 44 | callgrind: michi 45 | valgrind --tool=callgrind ./michi mcbenchmark 46 | 47 | tags: $(BIN) 48 | ctags *.c *.h 49 | 50 | clean: 51 | rm -f $(OBJS) michi.o michi-debug.o 52 | 53 | veryclean: clean 54 | rm -f $(BIN) 55 | rm -rf tests/output tests/tst tests/michi.log michi.log 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Michi-c2 --- Michi clone in C (Go gtp engine) 2 | ============================================= 3 | 4 | This is a recoding in C (for speed) of the michi.py code by Petr Baudis avalaible at https://github.com/pasky/michi. 5 | 6 | A port in C of the original michi has been kept without change at https://github.com/db3108/michi-c with the goal to retain the brevity, clarity and simplicity of the original code. 7 | 8 | Please go on the above projects pages to read more about Michi and to find some information about theory or interesting projects to do. 9 | 10 | *NEWS* 11 | 12 | Version 1.4 has many new features 13 | 14 | - variable boardsize, 15 | - early passing 16 | - graphics in gogui 17 | - read sgf files 18 | - improved portability 19 | - plays handicap games with dynamic komi (linear) 20 | - some speed improvements 21 | 22 | For more details see the ChangeLog. 23 | 24 | The current version is able to play on CGOS. A README file and example 25 | configuration files can be found in the cgos directory. 26 | 27 | Michi-c2 is distributed under the MIT licence. Now go forth, hack and peruse! 28 | 29 | Usage 30 | ----- 31 | 32 | See the user manual by opening *doc/manual.html* in any web browser. 33 | If you are browsing the code on GitHub you can preferably open the 34 | *doc/manual.rst*. 35 | 36 | Install 37 | ------- 38 | 39 | See *doc/INSTALL.md* 40 | 41 | This document describes some tests of michi-c that you should execute before 42 | trying to use it. These tests will check if you get the same results on your 43 | computer as I got on Linux. When these tests pass, we could be fairly confident 44 | that michi-c will work for you. 45 | 46 | *Important Note* It is highly recommended that you download Michi 47 | large-scale pattern files provide by Petr Baudis at 48 | 49 | http://pachi.or.cz/michi-pat/ 50 | 51 | Unpack them and place them (or a link to them) in the working directory when you run michi-c (same as michi.py), otherwise michi-c will be much weaker. 52 | -------------------------------------------------------------------------------- /board.h: -------------------------------------------------------------------------------- 1 | // board.h -- Implementation of Go rules and data for Go heuristics 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "non_portable.h" 10 | //========================= Definition of Data Structures ===================== 11 | // --------------------------- Board Constants -------------------------------- 12 | #define N 19 13 | #define W (N+2) 14 | #define BOARDSIZE ((N+1)*W+1) 15 | #define BOARD_IMIN (N+1) 16 | #define BOARD_IMAX (BOARDSIZE-N-1) 17 | #define LARGE_BOARDSIZE ((N+14)*(N+7)) 18 | 19 | #if N < 11 20 | #define LIBS_SIZE 4 21 | #define MAX_BLOCKS 64 22 | #elif N < 15 23 | #define LIBS_SIZE 8 24 | #define MAX_BLOCKS 128 25 | #else 26 | #define LIBS_SIZE 12 27 | #define MAX_BLOCKS 256 28 | #endif 29 | 30 | #define BUFLEN 4096 31 | #define INVALID_VERTEX 999 32 | //------------------------------- Data Structures ----------------------------- 33 | typedef unsigned char Byte; 34 | typedef unsigned char Block; 35 | typedef unsigned int Code4; // 4 bits code that defines a neighbors selection 36 | typedef unsigned int Info; 37 | typedef unsigned int Libs; // 32 bits unsigned integer 38 | typedef unsigned int Point; 39 | typedef unsigned long long ZobristHash; 40 | typedef Info* Slist; 41 | typedef enum { 42 | PASS_MOVE, RESIGN_MOVE, 43 | COMPUTER_BLACK, COMPUTER_WHITE, COMPUTER_BOTH 44 | } Code; 45 | typedef enum {EMPTY=0, OUT=1, WHITE=2, BLACK=3, BOTH=4} Color; 46 | typedef enum {UNKNOWN, OWN_BY_BLACK, OWN_BY_WHITE, ALIVE, DEAD} Status; 47 | 48 | typedef struct { // ---------------------- Go Position ------------------------ 49 | // Given a board of size NxN (N=9, 19, ...), we represent the position 50 | // as a (N+1)*(N+2)+1 string, with '.' (empty), 'X' (to-play player) 51 | // 'x' (other player), and whitespace (off-board border to make rules 52 | // implementation easier). Coordinates are just indices in this string. 53 | Color color[BOARDSIZE]; // string that hold the state of the board 54 | Byte env4[BOARDSIZE]; // color encoding for the 4 neighbors 55 | Byte env4d[BOARDSIZE]; // color encoding for the 4 diagonal neighbors 56 | Block block[BOARDSIZE]; // block id of the block at point pt (0 if EMPTY) 57 | Byte nlibs[MAX_BLOCKS]; // number of liberties of block b 58 | Byte bsize[MAX_BLOCKS]; // number of stones of block b 59 | Libs libs[MAX_BLOCKS][LIBS_SIZE];// list of liberties of block b(bitfield) 60 | Byte libs_size; // actual size of the libs (32 bits words) 61 | 62 | int size; // current size of the board 63 | int n; // move number 64 | Color to_play; // player that move 65 | Point ko, ko_old; // position of the ko (0 if no ko) 66 | Point last, last2, last3; // position of the last move and the move before 67 | float komi; // komi for the game 68 | float delta_komi; // used for dynamic komi management 69 | int caps[2]; // number of stones captured 70 | Byte undo_capture; // helper for undo_move() 71 | } Position; // Go position 72 | 73 | typedef struct { 74 | int value; 75 | int in_use; 76 | int mark[BOARDSIZE]; 77 | } Mark; 78 | 79 | // -------------------------------- Global Data ------------------------------- 80 | extern Byte bit[8]; 81 | extern Mark *mark1, *mark2; 82 | extern FILE *flog; // FILE to log messages 83 | extern int c1,c2; // counters for messages 84 | extern int verbosity; // 0, 1 or 2 85 | extern char large_board[LARGE_BOARDSIZE]; 86 | extern int large_coord[BOARDSIZE]; // mapping: c in board -> cl in large_board 87 | 88 | //================================== Code ===================================== 89 | //--------------------------- Functions in board.c ---------------------------- 90 | void board_init(void); 91 | void board_finish(void); 92 | void block_compute_libs(Position *pos, Block b, Slist libs, int max_libs); 93 | int cmpint(const void *i, const void *j); 94 | void compute_big_eye(Position *pos, Point pt, Slist points); 95 | void compute_block(Position *pos, Point pt, Slist stones, Slist libs, 96 | int nlibs); 97 | void compute_cfg_distances(Position *pos, Point pt, char cfg_map[BOARDSIZE]); 98 | Byte compute_env4(Position *pos, Point pt, int offset); 99 | int empty_area(Position *pos, Point pt, int dist); 100 | char* empty_position(Position *pos); 101 | char is_eye(Position *pos, Point pt); 102 | char is_eyeish(Position *pos, Point pt); 103 | int line_height(Point pt, int size); 104 | void make_list_last_moves_neighbors(Position *pos, Slist points); 105 | void make_list_neighbor_blocks_in_atari(Position *pos, Block b, Slist blocks, 106 | Point pt); 107 | Position* new_position(); 108 | char* pass_move(Position *pos); 109 | char* play_move(Position *pos, Point pt); 110 | char* undo_move(Position *pos); 111 | //----------------------- Functions in board_util.c -------------------------- 112 | int all_blocks_OK(Position *pos); 113 | int blocks_OK(Position *pos, Point pt); 114 | char* block_liberties_as_string(Position *pos, Block b); 115 | void copy_to_large_board(Position *pos); 116 | int env4_OK(Position *pos); 117 | void fatal_error(const char *msg); 118 | void init_large_board(void); 119 | void log_fmt_i(char type, const char *msg, int n); 120 | void log_fmt_p(char type, const char *msg, Point i); 121 | void log_fmt_s(char type, const char *msg, const char *s); 122 | void* michi_calloc(size_t nmemb, size_t size); 123 | void* michi_malloc(size_t size); 124 | Point parse_coord(char *s); 125 | Point parse_sgf_coord(char *s, int size); 126 | void print_board(Position *pos, FILE *f); 127 | void ppoint(Point pt); 128 | void slist_print_as_point(Slist l); 129 | void slist_print_as_int(Slist l); 130 | char* slist_str_as_point(Slist l); 131 | char* slist_from_str_as_point(Slist l, char *str); 132 | void slist_write_as_int(Slist l, FILE *f); 133 | void slist_write_as_point(Slist l, FILE *f); 134 | char* str_coord(Point pt, char str[5]); 135 | char* str_sgf_coord(Point pt, char str[8], int size); 136 | //-------------------- Functions inlined for performance ---------------------- 137 | #ifndef _MSC_VER 138 | #define __INLINE__ static inline 139 | #else 140 | #define __INLINE__ static __inline 141 | #endif 142 | // 4 bits codes giving the neighbors of a given color 143 | // bit 0(LSB): North, 1:East, 2:South, 3:West bit set(1) if the neighbor match 144 | __INLINE__ Code4 select_black(Byte env4) {return (env4>>4) & env4;} 145 | __INLINE__ Code4 select_empty(Byte env4) {return (~((env4>>4) | env4)) & 0xf;} 146 | __INLINE__ Code4 select_white(Byte env4) {return (env4>>4) & (~env4);} 147 | __INLINE__ int code4_popcnt(Code4 code) {return popcnt_u32(code);} 148 | // Access to board structure attribute 149 | __INLINE__ int block_nlibs(Position *pos, Block b) {return pos->nlibs[b];} 150 | __INLINE__ int block_size(Position *pos, Block b) {return pos->bsize[b];} 151 | __INLINE__ Color board_color_to_play(Position *pos) {return pos->to_play;} 152 | __INLINE__ float board_delta_komi(Position *pos) {return pos->delta_komi;} 153 | __INLINE__ float board_komi(Position *pos) {return pos->komi;} 154 | __INLINE__ Point board_ko(Position *pos) {return pos->ko;} 155 | __INLINE__ Point board_ko_old(Position *pos) {return pos->ko_old;} 156 | __INLINE__ int board_last_move(Position *pos) {return pos->last;} 157 | __INLINE__ Point board_last2(Position *pos) {return pos->last2;} 158 | __INLINE__ Point board_last3(Position *pos) {return pos->last3;} 159 | __INLINE__ Info board_nmoves(Position *pos) {return (Info) pos->n;} 160 | __INLINE__ int board_nstones(Position *pos) 161 | {return pos->n - pos->caps[0] - pos->caps[1];} // ignore PASS moves 162 | __INLINE__ void board_set_color_to_play(Position *pos, Color c) 163 | { pos->to_play = c;} 164 | __INLINE__ void board_place_stone(Position *pos, Point pt, Color c) { 165 | board_set_color_to_play(pos, c); 166 | play_move(pos, pt); 167 | pos->n--; 168 | } 169 | __INLINE__ void board_set_captured_neighbors(Position *pos, int code4) 170 | {pos->undo_capture = code4;} 171 | __INLINE__ void board_set_delta_komi(Position *pos, float dkm) 172 | {pos->delta_komi = dkm;} 173 | __INLINE__ void board_set_komi(Position *pos, float km) {pos->komi = km;} 174 | __INLINE__ void board_set_ko(Position *pos, Point ko) {pos->ko = ko;} 175 | __INLINE__ void board_set_ko_old(Position *pos, Point ko) {pos->ko_old = ko;} 176 | __INLINE__ void board_set_last(Position *pos, Point pt) {pos->last = pt;} 177 | __INLINE__ void board_set_last2(Position *pos, Point pt) {pos->last2 = pt;} 178 | __INLINE__ void board_set_last3(Position *pos, Point pt) {pos->last3 = pt;} 179 | __INLINE__ void board_set_nmoves(Position *pos, int n) {pos->n = n;} 180 | __INLINE__ void board_set_size(Position *pos, int sz) {pos->size = sz;} 181 | __INLINE__ int board_size(Position *pos) {return pos->size;} 182 | __INLINE__ int board_captured_neighbors(Position *pos) 183 | {return pos->undo_capture;} 184 | __INLINE__ Color color_other(Color c) {return c ^ 1;} 185 | __INLINE__ Block point_block(Position *pos, Point pt) {return pos->block[pt];} 186 | __INLINE__ Color point_color(Position *pos, Point pt) {return pos->color[pt];} 187 | __INLINE__ Byte point_env4(Position *pos, Point pt) {return pos->env4[pt];} 188 | __INLINE__ Byte point_env4d(Position *pos, Point pt) {return pos->env4d[pt];} 189 | __INLINE__ int point_env8(Position *pos, Point pt) 190 | {return (pos->env4d[pt] << 8) + pos->env4[pt];} 191 | __INLINE__ int point_is_stone(Position *pos, Point pt) 192 | {return (point_color(pos, pt) & 2) != 0;} 193 | __INLINE__ int point_nlibs(Position *pos, Point pt) 194 | {return code4_popcnt(select_empty(pos->env4[pt]));} 195 | //-------------------------- Definition of Useful Macros ---------------------- 196 | #define FORALL_POINTS(pos,i) for(Point i=BOARD_IMIN ; i0 ; _k--) { \ 206 | int _tmp=random_int(_k+1); SWAP(T, l[_k], l[_tmp]); \ 207 | } 208 | // Assertion check 209 | #ifdef NDEBUG 210 | #define michi_assert(pos, condition) 211 | #else 212 | #define michi_assert(pos, condition) \ 213 | if (!condition) { \ 214 | print_board(pos, flog); \ 215 | sprintf(buf,"Assertion failed: file %s, line %d",__FILE__,__LINE__); \ 216 | log_fmt_s('E',buf,NULL); \ 217 | exit(-1); \ 218 | } 219 | #endif 220 | //------------ Useful utility functions (inlined for performance) ------------- 221 | // Go programs manipulates lists or sets of (small) integers a lot. There are 222 | // many possible implementations that differ by the performance of the various 223 | // operations we need to perform on these data structures. 224 | // insert, remove, enumerate elements, test "is in ?", make the set empty ... 225 | // 226 | // Here are 2 simple implementations of sets 227 | // * Slist is a simple list implementation as integer arrays (not sorted) 228 | // - it is slow for finding or inserting without dupplicate except for very 229 | // small sets. 230 | // - but fast to enumerate elements or insert when there is no need to check 231 | // for dupplicates 232 | // * Mark 233 | // - it is very fast for insert, remove elements, making the set empty or to 234 | // test "is in ?" 235 | // - but slow if we need to enumerate all the elements 236 | // These two representations are necessary for performance even with the goal 237 | // of brevity that is a priority for michi (actually the code is very short). 238 | // 239 | // Note: the pat3_set in patterns.c is a representation of a set as bitfield. 240 | 241 | // Random Number generator (32 bits Linear Congruential Generator) 242 | // Ref: Numerical Recipes in C (W.H. Press & al), 2nd Ed page 284 243 | extern unsigned int idum; 244 | __INLINE__ unsigned int qdrandom(void) {idum=(1664525*idum)+1013904223; return idum;} 245 | __INLINE__ unsigned int random_int(int n) /* random int between 0 and n-1 */ \ 246 | {unsigned long long r=qdrandom(); return (r*n)>>32;} 247 | 248 | // Simple list 249 | __INLINE__ int slist_size(Slist l) {return l[0];} 250 | __INLINE__ void slist_clear(Slist l) {l[0]=0;} 251 | __INLINE__ Info slist_pop(Slist l) {return l[l[0]--];} 252 | __INLINE__ void slist_push(Slist l, Info item) {l[l[0]+1]=item;l[0]++;} 253 | __INLINE__ void slist_range(Slist l,int n) 254 | {l[0]=n; for (int k=0;kin_use = 1; m->value++;} 277 | __INLINE__ void mark_release(Mark *m) {m->in_use = 0;} // for debugging 278 | __INLINE__ void mark(Mark *m, Info i) {m->mark[i] = m->value;} 279 | __INLINE__ int is_marked(Mark *m, Info i) {return m->mark[i] == m->value;} 280 | -------------------------------------------------------------------------------- /board_util.c: -------------------------------------------------------------------------------- 1 | // board_util.c -- Utilities for the board model 2 | #include "board.h" 3 | static char buf[BUFLEN]; 4 | static int delta[] = { -N-1, 1, N+1, -1, -N, W, N, -W}; 5 | 6 | //----------------------------- messages logging ------------------------------ 7 | FILE *flog; // FILE to log messages 8 | int c1,c2; // counters for warning messages 9 | int nmsg; // number of written log entries 10 | 11 | void too_many_msg(void) 12 | // if too many messages have been logged, print a last error and exit 13 | { 14 | fprintf(stderr,"Too many messages have been written in log file " 15 | " (maximum 100000)\n"); 16 | fprintf(flog,"Too many messages (maximum 100000)\n"); 17 | exit(-1); 18 | } 19 | 20 | void log_fmt_s(char type, const char *msg, const char *s) 21 | // Log a formatted message (string parameter) 22 | { 23 | fprintf(flog, "%c %5d/%3.3d ", type, c1, c2); 24 | fprintf(flog, msg, s); fprintf(flog, "\n"); 25 | if(type == 'E') { 26 | fprintf(stderr,"\nERROR - "); 27 | fprintf(stderr, msg, s); fprintf(stderr,"\n"); 28 | exit(-1); 29 | } 30 | if(nmsg++ > 1000000) too_many_msg(); 31 | } 32 | 33 | void log_fmt_i(char type, const char *msg, int n) 34 | // Log a formatted message (int parameter) 35 | { 36 | sprintf(buf, msg, n); 37 | log_fmt_s(type, "%s", buf); 38 | } 39 | 40 | void log_fmt_p(char type, const char *msg, Point pt) 41 | // Log a formatted message (point parameter) 42 | { 43 | char str[8]; 44 | sprintf(buf, msg, str_coord(pt,str)); 45 | log_fmt_s(type,"%s", buf); 46 | } 47 | 48 | void fatal_error(const char *msg) 49 | { 50 | log_fmt_s('E', "FATAL ERROR - %s", msg); 51 | exit(-1); 52 | } 53 | 54 | //--------------------------- Print of the Position --------------------------- 55 | static char* colstr = "@ABCDEFGHJKLMNOPQRST"; 56 | void make_pretty(Position *pos, char pretty_board[BOARDSIZE], int *capB 57 | , int *capW) 58 | { 59 | for (int k=0 ; kcaps[0]; 66 | *capB = pos->caps[1]; 67 | } 68 | 69 | void print_board(Position *pos, FILE *f) 70 | // Print visualization of the given board position 71 | { 72 | char pretty_board[BOARDSIZE], strko[8]; 73 | int capB, capW; 74 | 75 | make_pretty(pos, pretty_board, &capB, &capW); 76 | fprintf(f,"Move: %-3d Black: %d caps White: %d caps Komi: %.1f", 77 | board_nmoves(pos), capB, capW, board_komi(pos)); 78 | if (board_ko(pos)) 79 | fprintf(f," ko: %s", str_coord(board_ko(pos), strko)); 80 | fprintf(f,"\n"); 81 | 82 | int size = board_size(pos), skipped=N-size+1; 83 | int k=skipped*(N+1); 84 | for (int row=skipped ; row<=N ; row++) { 85 | if (board_last_move(pos) == k+1) fprintf(f, " %-2d(", N-row+1); 86 | else fprintf(f, " %-2d ", N-row+1); 87 | k++; 88 | for (int col=1 ; col<=size ; col++, k++) { 89 | fprintf(f, "%c", pretty_board[k]); 90 | if (board_last_move(pos) == k+1) fprintf(f, "("); 91 | else if (board_last_move(pos) == k) fprintf(f, ")"); 92 | else fprintf(f, " "); 93 | } 94 | k += skipped-1; 95 | fprintf(f, "\n"); 96 | } 97 | fprintf(f, " "); 98 | for (int col=1 ; col<=size ; col++) fprintf(f, "%c ", colstr[col]); 99 | fprintf(f, "\n\n"); 100 | } 101 | 102 | //------------------ Memory allocation with error checking -------------------- 103 | void* michi_malloc(size_t size) 104 | // Michi memory allocator - fatal error if allocation failed 105 | { 106 | void* t = malloc(size); 107 | if (t == NULL) { 108 | log_fmt_i('E',"Memory allocation failed (size = %d bytes)", size); 109 | exit(-1); 110 | } 111 | return t; 112 | } 113 | 114 | void* michi_calloc(size_t nmemb, size_t size) 115 | // Michi memory allocator - fatal error if allocation failed 116 | { 117 | void* t = calloc(nmemb, size); 118 | if (t == NULL) { 119 | log_fmt_i('E',"Memory allocation failed (size = %d bytes)", size*nmemb); 120 | exit(-1); 121 | } 122 | return t; 123 | } 124 | 125 | //-------------------- Parsing and writing coordinates ------------------------ 126 | Point parse_coord(char *s) 127 | { 128 | char c, str[10]; 129 | int x,y; 130 | for (int i=0 ; i<9 ; i++) { 131 | if (s[i]==0) { 132 | str[i]=0; 133 | break; 134 | } 135 | str[i] = toupper(s[i]); 136 | } 137 | if(strcmp(str, "PASS") == 0) return PASS_MOVE; 138 | if(isalpha(str[0]) && isdigit(str[1]) 139 | && (str[2]==0 || (isdigit(str[2]) && str[3]==0 ))) { 140 | sscanf(str, "%c%d", &c, &y); 141 | if (c<'J') x = c-'@'; 142 | else x = c-'@'-1; 143 | return (N-y+1)*(N+1) + x; 144 | } 145 | else 146 | return INVALID_VERTEX; 147 | } 148 | 149 | char* str_coord(Point pt, char str[8]) 150 | { 151 | if (pt == PASS_MOVE) strcpy(str, "pass"); 152 | else if (pt == RESIGN_MOVE) strcpy(str, "resign"); 153 | else { 154 | int row = pt/(N+1); int col = (pt%(N+1)); 155 | sprintf(str, "%c%d", '@'+col,N+1-row); 156 | if (str[0] > 'H') str[0]++; 157 | } 158 | return str; 159 | } 160 | 161 | void ppoint(Point pt) { 162 | char str[8]; 163 | str_coord(pt,str); 164 | printf("%s\n",str); 165 | } 166 | 167 | Point parse_sgf_coord(char *s, int size) 168 | { 169 | int x,y; 170 | 171 | if(s[0] == 0) return PASS_MOVE; 172 | x = s[0] - 'a'; 173 | y = s[1] - 'a' + (N - size); 174 | return (y+1)*(N+1) + x + 1; 175 | } 176 | 177 | char* str_sgf_coord(Point pt, char str[8], int size) 178 | { 179 | if (pt == PASS_MOVE) strcpy(str, "pass"); 180 | else if (pt == RESIGN_MOVE) strcpy(str, "resign"); 181 | else { 182 | int row = pt/(N+1) - 1; int col = (pt%(N+1)) - 1; 183 | sprintf(str, "%c%c", 'a' + col, 'a' + row - (N-size)); 184 | } 185 | return str; 186 | } 187 | 188 | //------------------------ Costly functions on Slist -------------------------- 189 | char* slist_str_as_int(Slist l) { 190 | buf[0]=0; 191 | for (int k=1, n=l[0] ; k<=n ; k++) { 192 | char s[32]; 193 | sprintf(s, " %d", l[k]); 194 | strcat(buf, s); 195 | } 196 | return buf; 197 | } 198 | 199 | char* slist_str_as_point(Slist l) { 200 | buf[0]=0; 201 | for (int k=1, n=l[0] ; k<=n ; k++) { 202 | char str[8], s[8]; 203 | sprintf(s, " %s", str_coord(l[k],str)); 204 | strcat(buf, s); 205 | } 206 | return buf; 207 | } 208 | 209 | void slist_write_as_int(Slist l, FILE *f) 210 | { 211 | fprintf(f, "%s", slist_str_as_int(l)); 212 | } 213 | 214 | void slist_write_as_point(Slist l, FILE *f) 215 | { 216 | fprintf(f, "%s", slist_str_as_point(l)); 217 | } 218 | 219 | void slist_print_as_int(Slist l) 220 | { 221 | slist_write_as_int(l, stdout); 222 | printf("\n"); 223 | } 224 | 225 | void slist_print_as_point(Slist l) 226 | { 227 | slist_write_as_point(l, stdout); 228 | printf("\n"); 229 | } 230 | 231 | char* slist_from_str_as_point(Slist l, char *str) 232 | { 233 | char *ret, *s; 234 | Point pt; 235 | 236 | slist_clear(l); 237 | if (str != NULL) { 238 | s = strtok(str, " \t\n"); 239 | if (s != NULL) { 240 | while (s != NULL) { 241 | pt = parse_coord(s); 242 | if (pt == INVALID_VERTEX) break; 243 | slist_push(l, parse_coord(s)); 244 | s=strtok(NULL," \t"); 245 | } 246 | if (pt == INVALID_VERTEX) { 247 | sprintf(buf, "Error - Invalid vertex \"%s\"", s); 248 | ret = buf; 249 | } 250 | else 251 | ret = ""; 252 | } 253 | else 254 | ret = "Error - The list of points is empty"; 255 | } 256 | else { 257 | ret = "Error - The list of points is empty"; 258 | } 259 | return ret; 260 | } 261 | 262 | char* block_liberties_as_string(Position *pos, Block b) 263 | // Return the list of liberties of block b as String (in buf) 264 | { 265 | Point libs[BOARDSIZE]; 266 | block_compute_libs(pos, b, libs, BOARDSIZE); 267 | return slist_str_as_point(libs); 268 | } 269 | 270 | //------------------ Integrity checks of the Position struct ------------------ 271 | void dump_env4(Byte env4, Byte true_env4) 272 | { 273 | for (int i=0 ; i<8 ; i++) { 274 | if (i == 4) fprintf(stderr, " "); 275 | if (env4 & 128) 276 | fprintf(stderr, "1"); 277 | else 278 | fprintf(stderr, "0"); 279 | env4 <<= 1; 280 | } 281 | fprintf(stderr, " (true: "); 282 | for (int i=0 ; i<8 ; i++) { 283 | if (i==4) fprintf(stderr, " "); 284 | if (true_env4 & 128) 285 | fprintf(stderr, "1"); 286 | else 287 | fprintf(stderr, "0"); 288 | true_env4 <<= 1; 289 | } 290 | fprintf(stderr, ")\n"); 291 | } 292 | 293 | int env4_OK(Position *pos) 294 | { 295 | int nerr=0; 296 | FORALL_POINTS(pos,pt) { 297 | if (point_color(pos,pt) == OUT) continue; 298 | if (point_env4(pos,pt) != compute_env4(pos, pt, 0)) { 299 | log_fmt_p('E', "Incorrect env4[%s]", pt); 300 | fprintf(stderr, "%s ERR env4 = ", str_coord(pt,buf)); 301 | dump_env4(point_env4(pos,pt), compute_env4(pos,pt,0)); 302 | if (nerr++>9) break; 303 | } 304 | if (point_env4d(pos,pt) != compute_env4(pos,pt,4)) { 305 | log_fmt_p('E', "Incorrect env4d[%s]", pt); 306 | fprintf(stderr, "%s ERR env4d = ", str_coord(pt,buf)); 307 | dump_env4(point_env4d(pos,pt), compute_env4(pos,pt,4)); 308 | if (nerr++>9) break; 309 | } 310 | } 311 | if (nerr>0) return 0; 312 | else return 1; 313 | } 314 | 315 | int check_block(Position *pos, Point pt) 316 | // return 1 if block at point pt is OK, else 0 317 | { 318 | Block b=point_block(pos, pt); 319 | char str[8], str_pt[8]; 320 | int nerrs=0; 321 | Point stones[BOARDSIZE], libs[BOARDSIZE], libs2[BOARDSIZE]; 322 | 323 | str_coord(pt, str_pt); 324 | block_compute_libs(pos, b, libs2, BOARDSIZE); 325 | 326 | // Check points where there is no stone 327 | if (point_color(pos,pt) == OUT) { 328 | if (point_block(pos, pt) == 0) 329 | return 1; 330 | else { 331 | sprintf(buf,"Wrong block number %d at %s (which is OUT)", b, 332 | str_coord(pt, str)); 333 | log_fmt_s('E', buf, NULL); 334 | return 0; 335 | } 336 | } 337 | if (point_color(pos,pt) == EMPTY) { 338 | if (point_block(pos, pt) == 0) 339 | return 1; 340 | else { 341 | sprintf(buf,"Wrong block number %d at %s (which is EMPTY)", b, 342 | str_coord(pt, str)); 343 | log_fmt_s('E', buf, NULL); 344 | return 0; 345 | } 346 | } 347 | 348 | // Check point where there is a stone 349 | if (point_block(pos, pt) == 0) { 350 | sprintf(buf,"Wrong block number %d at %s (which is NOT EMPTY)", b, 351 | str_coord(pt, str)); 352 | log_fmt_s('E', buf, NULL); 353 | if(nerrs++ > 10) return 0; 354 | } 355 | compute_block(pos, pt, stones, libs, 256); 356 | slist_sort(libs); 357 | 358 | // Global verification 359 | if (block_size(pos,b) != slist_size(stones)) { 360 | if (slist_size(stones)>255 && block_size(pos,b)==255) goto next_check; 361 | sprintf(buf,"Wrong size of block %d at %s (%d instead of %d)", 362 | b, str_coord(pt, str), block_size(pos, b), slist_size(stones)); 363 | log_fmt_s('E', buf, NULL); 364 | if(nerrs++ > 10) return 0; 365 | } 366 | next_check: 367 | if (block_nlibs(pos,b) != slist_size(libs)) { 368 | sprintf(buf,"Wrong nlibs of block %d at %s (%d instead of %d)", 369 | b, str_coord(pt, str), block_nlibs(pos,b), slist_size(libs)); 370 | log_fmt_s('E', buf, NULL); 371 | log_fmt_s('I', block_liberties_as_string(pos, b), NULL); 372 | log_fmt_s('I', slist_str_as_point(libs), NULL); 373 | if(nerrs++ > 10) return 0; 374 | } 375 | if (block_nlibs(pos,b) ==0) { 376 | sprintf(buf,"Error block %d(%s) with 0 liberty", b, str_coord(pt, str)); 377 | log_fmt_s('E', buf, NULL); 378 | if(nerrs++ > 10) return 0; 379 | } 380 | 381 | // Detailed check of stones 382 | b = point_block(pos,pt); 383 | FORALL_IN_SLIST(stones, i) { 384 | if (point_block(pos,i) != b) { 385 | sprintf(buf,"Invalid block id (%d) at point %s ", 386 | point_block(pos,i), str_coord(i, str)); 387 | log_fmt_s('E', buf, NULL); 388 | if(nerrs++ > 10) return 0; 389 | } 390 | } 391 | 392 | // Detailed check of liberties 393 | FORALL_IN_SLIST(libs, l) { 394 | int k = (l-N)>>5, r = (l-N)&31; 395 | if ((pos->libs[b][k] & (1< 10) return 0; 401 | } 402 | } 403 | 404 | if (slist_size(libs2) != slist_size(libs)) { 405 | sprintf(buf,"Bad liberty list size for block %d(%s) : %d instead of %d" 406 | , b, str_coord(pt, str), slist_size(libs2), slist_size(libs)); 407 | log_fmt_s('E', buf, NULL); 408 | if(nerrs++ > 10) return 0; 409 | } 410 | FORALL_IN_SLIST(libs2, l) { 411 | if (point_color(pos, l) != EMPTY) { 412 | sprintf(buf,"Liberty %s of block %d(%s) is not EMPTY", 413 | str_coord(l, str), b, str_pt); 414 | log_fmt_s('E', buf, NULL); 415 | if(nerrs++ > 10) return 0; 416 | } 417 | } 418 | 419 | return nerrs==0; // OK if nerrs == 0 420 | } 421 | 422 | int blocks_OK(Position *pos, Point pt) 423 | // Return 1 (True) if block at point pt and blocks in contact with pt are OK 424 | { 425 | Block b=point_block(pos, pt); 426 | int k; 427 | Point n; 428 | if (check_block(pos, pt)==0) return 0; 429 | 430 | FORALL_NEIGHBORS(pos, pt, k, n) 431 | if (point_block(pos, n) != b && check_block(pos, n)==0) return 0; 432 | return 1; // if all tests succeed blocks around pt are OK 433 | } 434 | 435 | int all_blocks_OK(Position *pos) 436 | // Return 1 (True) if all blocks on the board are OK 437 | { 438 | mark_init(mark2); 439 | mark(mark2, 0); 440 | FORALL_POINTS(pos, pt) { 441 | Block b=point_block(pos, pt); 442 | if (!is_marked(mark2, b)) { 443 | mark(mark2, b); 444 | if (check_block(pos, pt) == 0) return 0; 445 | } 446 | } 447 | mark_release(mark2); 448 | return 1; 449 | } 450 | 451 | //----------------------- Large board (7 point large border) ------------------ 452 | // This large board is used for matching the large patterns 453 | 454 | char large_board[LARGE_BOARDSIZE]; 455 | int large_coord[BOARDSIZE]; // coord in the large board of any points of board 456 | 457 | void compute_large_coord(void) 458 | // Compute the position in the large board of any point on the board 459 | { 460 | int lpt, pt; 461 | for (int y=0 ; y& cgos.log 3 | -------------------------------------------------------------------------------- /control.c: -------------------------------------------------------------------------------- 1 | // control.c -- Overall management of the MCTS search (dynkomi, time management) 2 | #include "michi.h" 3 | char buf[BUFLEN]; 4 | float saved_time_left; 5 | #define max(a, b) ((a)<(b) ? (a) : (b)) 6 | 7 | // -------------------------------- Dynamic komi ------------------------------ 8 | void update_dyn_komi(Game *game) 9 | // This a simplistic approach used only for handicap games 10 | { 11 | double dkm, handi=game->handicap, nstones, moves_to_play, endmove; 12 | Position *pos=game->pos; 13 | 14 | nstones = board_nstones(pos); 15 | moves_to_play = ((N*N*3)/4 - nstones)/2;// Hyp: 25 % of EMPTY points at end 16 | endmove = board_nmoves(pos) + moves_to_play; 17 | dkm = handi*komi_per_handicap_stone*moves_to_play/endmove; 18 | if (dkm < 0.0) dkm = 0.0; 19 | if (board_color_to_play(pos) == WHITE) 20 | dkm = -dkm; 21 | board_set_delta_komi(pos, dkm); 22 | } 23 | 24 | //----------------------------- time management ------------------------------- 25 | int nsims(Game *game) 26 | // Compute the number of simulations for the next tree_search. 27 | // Simplistic time management (portable if not very accurate). 28 | // Loosely based on Petr Baudis Thesis. 29 | { 30 | int nstones, moves_to_play; 31 | 32 | if (game->time_init <= 0) { 33 | start_playouts_sec = (float) clock() / (float) CLOCKS_PER_SEC; 34 | return N_SIMS; // Game is not time limited 35 | } 36 | 37 | if (nplayouts_per_second <= 0.0) { 38 | start_playouts_sec = (float) clock() / (float) CLOCKS_PER_SEC; 39 | nplayouts = N_SIMS; 40 | return N_SIMS; // 1st genmove 41 | } 42 | 43 | // Game is time limited and we know playout speed: nplayouts_per_second 44 | saved_time_left -= stop_playouts_sec - start_playouts_sec; 45 | if (game->time_left_gtp > 0) 46 | game->time_left = game->time_left_gtp; 47 | else 48 | game->time_left = saved_time_left; 49 | 50 | sprintf(buf,"time used: %6d %7.1f", 51 | nplayouts_real, stop_playouts_sec-start_playouts_sec); 52 | log_fmt_s('T', buf, NULL); 53 | start_playouts_sec = (float) clock() / (float) CLOCKS_PER_SEC; 54 | 55 | nstones = board_nstones(game->pos); 56 | moves_to_play = ((N*N*3)/4 - nstones)/2;// Hyp: 25 % of EMPTY points at end 57 | // Most often the thinking time will be doubled in genmove 58 | // We anticipate that fact here in order to allocate most of the thinking 59 | // time for the middle game 60 | float reduction = 1.0; 61 | if (moves_to_play < 30) { 62 | moves_to_play = 30; 63 | reduction = 2.0; 64 | } 65 | if (board_nmoves(game->pos) < 15) 66 | reduction = 2.0; 67 | nplayouts = (float) game->time_left / moves_to_play * nplayouts_per_second; 68 | nplayouts /= reduction; 69 | if (nplayouts > 100000) nplayouts = 100000; 70 | 71 | sprintf(buf,"time allocated: %6.0f %7.2f %7.1f %5d %5d", 72 | nplayouts, nplayouts / nplayouts_per_second, 73 | nplayouts_per_second, moves_to_play, game->time_left); 74 | log_fmt_s('T', buf, NULL); 75 | nplayouts_real = 0; 76 | return (int) nplayouts; 77 | } 78 | 79 | // ----------------------- Is the Position clear enough ? --------------------- 80 | int is_position_clear_enough(void) 81 | // Check if the situation is clear enough after the first tree search. 82 | { 83 | if (bestwr < 0.48) // program is behind 84 | //|| best2 < 2.0 || fabs(bestr) > 0.02) // unclear situation 85 | return 0; // we will try to extend the thinking time 86 | else 87 | return 1; 88 | } 89 | 90 | // ------ Status of Blocks and Points and computation of the final score ----- 91 | // Borrowed from an old version of Pachi 92 | Status mcts_point_status(Point pt, int owner_map[BOARDSIZE]) 93 | // Status of points: OWN_BY_BLACK, OWN_BY_WHITE or UNKNOWN 94 | { 95 | double nsims=nplayouts_real; 96 | Status p_status; 97 | 98 | if ((double)owner_map[pt] > 0.8*nsims) 99 | p_status=OWN_BY_BLACK; 100 | else if ((double)owner_map[pt] < -0.8*nsims) 101 | p_status=OWN_BY_WHITE; 102 | else 103 | p_status=UNKNOWN; 104 | 105 | return p_status; 106 | } 107 | 108 | void compute_all_status(Position *pos, int owner_map[BOARDSIZE], 109 | int score_count[], Status block_status[], Status point_status[]) 110 | // Compute status of of points and blocks (based on owner_map) 111 | // - the points : OWN_BY_BLACK, OWN_BY_WHITE or UNKNOWN 112 | // - the blocks : DEAD, ALIVE or UNKNOWN 113 | { 114 | Color c; 115 | int b; 116 | Status new_g_st, p_st; 117 | TreeNode *tree=new_tree_node(); 118 | 119 | // Launch a full depth MCTS search in order to compute owner_map 120 | double FASTPLAY20_THRES_sav = FASTPLAY20_THRES; 121 | double FASTPLAY5_THRES_sav = FASTPLAY5_THRES; 122 | memset(owner_map, 0, BOARDSIZE*sizeof(int)); 123 | nplayouts_real = 0; 124 | tree_search(pos, tree, 2*N_SIMS, owner_map, score_count, 0); 125 | FASTPLAY20_THRES = FASTPLAY20_THRES_sav; 126 | FASTPLAY5_THRES = FASTPLAY5_THRES_sav; 127 | sprintf(buf, "nsims: %d", nplayouts_real); 128 | log_fmt_s('I',buf,NULL); 129 | 130 | // Reset status of points and blocks 131 | FORALL_POINTS(pos, pt) 132 | point_status[pt] = UNKNOWN; 133 | for (b=1 ; b 0.0 if BLACK wins 168 | // Use approximate area scoring (chinese rules) 169 | { 170 | double score=0.0, unknown_empty=0.0, unknown_stones=0.0; 171 | Position *pos=game->pos; 172 | Score s; 173 | 174 | FORALL_POINTS(pos, pt) { 175 | Block b = point_block(pos, pt); 176 | Color c = point_color(pos, pt); 177 | if (c == BLACK) { 178 | if (block_status[b] == ALIVE) 179 | score += 1.0; 180 | else if (block_status[b] == DEAD) 181 | score -= 1.0; 182 | else if (block_status[b] == UNKNOWN) 183 | unknown_stones += 1.0; 184 | } 185 | else if (c == WHITE) { 186 | if (block_status[b] == ALIVE) 187 | score -= 1.0; 188 | else if (block_status[b] == DEAD) 189 | score += 1.0; 190 | else if (block_status[b] == UNKNOWN) 191 | unknown_stones += 1.0; 192 | } 193 | else if (c == EMPTY) { 194 | if (point_status[pt] == OWN_BY_BLACK) 195 | score += 1.0; 196 | else if (point_status[pt] == OWN_BY_WHITE) 197 | score -= 1.0; 198 | else if (point_status[pt] == UNKNOWN) 199 | unknown_empty += 1.0; 200 | } 201 | } 202 | 203 | score -= board_komi(pos); 204 | s.raw = score; 205 | // optimistic score consider 206 | // * the stones with unknown status 207 | // - as ALIVE if they belong to the computer 208 | // - as DEAD if they blong to the opponent 209 | // * the points with unknown status as belonging to the computer 210 | // pessimistic score is the opposite 211 | if (game->computer_color == BLACK) { 212 | s.optimistic = score + unknown_stones + unknown_empty; 213 | s.pessimistic = score - unknown_stones - unknown_empty; 214 | } 215 | else { 216 | s.optimistic = score - unknown_stones - unknown_empty; 217 | s.pessimistic = score + unknown_stones + unknown_empty; 218 | } 219 | sprintf(buf,"unknown points: %.0lf, scores: %.1lf, %.1lf, %.1lf", 220 | unknown_stones + unknown_empty, s.pessimistic, s.raw, s.optimistic); 221 | log_fmt_s('S', buf, NULL); 222 | return s; 223 | } 224 | 225 | // ---------------------------- "Intelligent" passing ------------------------- 226 | int is_better_to_pass(Game *game, int *owner_map, int *score_count) 227 | // Check if the computer should pass 228 | { 229 | int answer, sure_loss, sure_win; 230 | Position *pos=game->pos; 231 | Score score; 232 | Status block_status[BOARDSIZE], point_status[BOARDSIZE]; 233 | 234 | if (!play_until_the_end 235 | && board_last_move(pos) == PASS_MOVE && board_nmoves(pos)>2) { 236 | 237 | compute_all_status(pos, owner_map, score_count 238 | , block_status, point_status); 239 | score = final_score(game, block_status, point_status); 240 | if (score.optimistic - score.pessimistic < 21.0) { 241 | // Relatively "little" uncertainty 242 | if (game->computer_color == BLACK) { 243 | sure_win = score.pessimistic > 0.0; 244 | sure_loss = score.optimistic < 0.0; 245 | } 246 | else { 247 | sure_win = score.pessimistic < 0.0; 248 | sure_loss = score.optimistic > 0.0; 249 | } 250 | } 251 | else { 252 | // Large uncertainty 253 | if (game->computer_color == BLACK) { 254 | sure_win = score.pessimistic > -0.9; 255 | sure_loss = score.optimistic < 1.9; 256 | } 257 | else { 258 | sure_win = score.pessimistic < 0.9; 259 | sure_loss = score.optimistic > -1.9; 260 | } 261 | } 262 | if (sure_win) 263 | log_fmt_s('I', "Opponent pass and I estimate that I win", NULL); 264 | else if (sure_loss) 265 | log_fmt_s('I', "Opponent pass and I estimate that I loose", NULL); 266 | else 267 | log_fmt_s('I', 268 | "Opponent pass but I estimate that game is not decided", NULL); 269 | answer = sure_win || sure_loss; 270 | } 271 | else 272 | answer = 0; 273 | return answer; 274 | } 275 | 276 | // ------------------------- Generate next move ------------------------------- 277 | Point genmove (Game *game, TreeNode **tree, int *owner_map, int *score_count) 278 | { 279 | Point pt; 280 | Position *pos = game->pos; 281 | 282 | if (is_better_to_pass(game, owner_map, score_count)) { 283 | pt = PASS_MOVE; 284 | } 285 | else { 286 | free_tree(*tree); 287 | *tree = new_tree_node(); 288 | int n = nsims(game); 289 | memset(owner_map, 0, BOARDSIZE*sizeof(int)); 290 | memset(score_count, 0, (2*N*N+1)*sizeof(int)); 291 | if (use_dynamic_komi) 292 | update_dyn_komi(game); 293 | nplayouts_real = 0; 294 | pt = tree_search(pos, *tree, n, owner_map, score_count, 0); 295 | if (is_time_limited(game) 296 | && (nplayouts_real*2 > nplayouts) 297 | && !is_position_clear_enough()) { 298 | // think harder hoping we will recover 299 | log_fmt_s('S', "thinking time extended", NULL); 300 | pt = tree_search(pos, *tree, n, owner_map, score_count, 0); 301 | } 302 | } 303 | return pt; 304 | } 305 | -------------------------------------------------------------------------------- /debug.c: -------------------------------------------------------------------------------- 1 | #include "michi.h" 2 | static char buf[BUFLEN]; 3 | 4 | //============================= debug subcommands ============================= 5 | 6 | char decode_env4(int env4, int pt) 7 | { 8 | int hi, lo; 9 | env4 >>= pt; 10 | hi = (env4>>4) & 1; 11 | lo = env4 & 1; 12 | return (hi<<1) + lo; 13 | } 14 | 15 | char decode_env8(int env8, int pt) 16 | { 17 | int c; 18 | if (pt >= 4) 19 | c = decode_env4(env8>>8, pt-4); 20 | else 21 | c = decode_env4(env8&255, pt); 22 | switch(c) { 23 | case 0: return 'O'; 24 | case 1: return 'X'; 25 | case 2: return '.'; 26 | case 3: return '#'; 27 | } 28 | return 0; 29 | } 30 | 31 | void print_env8(int env8) 32 | { 33 | char src[10]; // src 0 1 2 bits in env8 7 0 4 34 | src[0] = decode_env8(env8, 7); // NW 3 4 5 bit 0=LSB 3 . 1 35 | src[1] = decode_env8(env8, 0); // N 6 7 8 6 2 5 36 | src[2] = decode_env8(env8, 4); // NE 37 | src[3] = decode_env8(env8, 3); // W 38 | src[4] = '.'; // Center 39 | src[5] = decode_env8(env8, 1); // E 40 | src[6] = decode_env8(env8, 6); // SW 41 | src[7] = decode_env8(env8, 2); // S 42 | src[8] = decode_env8(env8, 5); // SE 43 | 44 | printf("env8 = %d\n", env8); 45 | printf("%c %c %c\n", src[0], src[1], src[2]); 46 | printf("%c %c %c\n", src[3], src[4], src[5]); 47 | printf("%c %c %c\n", src[6], src[7], src[8]); 48 | } 49 | 50 | void print_marker(Position *pos, Mark *marker) 51 | { 52 | Position pos2 =*pos; 53 | FORALL_POINTS(pos2, p) 54 | if (is_marked(marker, p)) pos2.color[p]='*'; 55 | print_pos(&pos2, stdout, 0); 56 | } 57 | 58 | char* debug(Game *game, char *command) 59 | // Analyse the subcommands of the gtp debug command 60 | { 61 | char *ret=""; 62 | char *known_commands = "\nblocks_OK\nenv8\nfix_atari\ngen_playout\n" 63 | "match_pat3\nmatch_pat\nplayout\nprint_mark\nsavepos\nsetpos\n" 64 | "to_play"; 65 | int *amaf_map=michi_calloc(BOARDSIZE, sizeof(int)); 66 | int *owner_map=michi_calloc(BOARDSIZE, sizeof(int)); 67 | int *score_count=michi_calloc(2*N*N+1, sizeof(int)); 68 | Info sizes[BOARDSIZE]; 69 | Point moves[BOARDSIZE]; 70 | Position *pos = game->pos; 71 | 72 | if (command == NULL) 73 | return "Error - Missing parameters"; 74 | 75 | if (strcmp(command, "setpos") == 0) { 76 | char *str = strtok(NULL, " \t\n"); 77 | while (str != NULL) { 78 | Info m; 79 | int played=0; 80 | Point pt = parse_coord(str); 81 | if (point_color(pos, pt) == EMPTY) { 82 | ret = play_move(pos, pt); // suppose alternate play 83 | m = pt + (board_captured_neighbors(pos) << 9) 84 | + (board_ko_old(pos) << 13); 85 | played = 1; 86 | } 87 | else if (pt == PASS_MOVE) { 88 | ret = pass_move(pos); 89 | m = pt; 90 | played = 1; 91 | } 92 | else 93 | ret ="Error Illegal move: point not EMPTY\n"; 94 | str = strtok(NULL, " \t\n"); 95 | if (played) 96 | slist_push(game->moves, m); 97 | } 98 | } 99 | else if (strcmp(command, "savepos") == 0) { 100 | char *filename = strtok(NULL, " \t\n"); 101 | FILE *f=fopen(filename, "w"); 102 | print_pos(pos, f, NULL); 103 | fclose(f); 104 | ret = ""; 105 | } 106 | else if (strcmp(command, "playout") == 0) { 107 | memset(score_count, 0, (2*N*N+1)*sizeof(int)); 108 | mcplayout(pos, amaf_map, owner_map, score_count, 1); 109 | } 110 | else if (strcmp(command, "blocks_OK") == 0) { 111 | char *str = strtok(NULL, " \t\n"); 112 | if(str == NULL) ret = "Error missing point"; 113 | else { 114 | Point pt = parse_coord(str); 115 | if (blocks_OK(pos, pt)==1) 116 | ret = "true"; 117 | else 118 | ret = "false"; 119 | } 120 | } 121 | else if (strcmp(command, "libs") == 0) { 122 | char *str = strtok(NULL, " \t\n"); 123 | if(str == NULL) ret = "Error missing block"; 124 | else { 125 | Block b = atoi(str); 126 | Point libs[BOARDSIZE]; 127 | block_compute_libs(pos, b, libs, BOARDSIZE); 128 | ret = slist_str_as_point(libs); 129 | } 130 | } 131 | else if (strcmp(command, "gen_playout") == 0) { 132 | char *suggestion = strtok(NULL, " \t\n"); 133 | if (suggestion != NULL) { 134 | Point last_moves_neighbors[20]; 135 | make_list_last_moves_neighbors(pos, last_moves_neighbors); 136 | if (strcmp(suggestion, "capture") == 0) 137 | gen_playout_moves_capture(pos, last_moves_neighbors, 138 | 1.0, 0, moves, sizes); 139 | else if (strcmp(suggestion, "pat3") == 0) 140 | gen_playout_moves_pat3(pos, last_moves_neighbors, 141 | 1.0, moves); 142 | ret = slist_str_as_point(moves); 143 | } 144 | else 145 | ret = "Error - missing [capture|pat3]"; 146 | } 147 | else if (strcmp(command, "match_pat") == 0) { 148 | char *str = strtok(NULL, " \t\n"); 149 | if(str == NULL) ret = "Error missing point"; 150 | else { 151 | copy_to_large_board(pos); 152 | Point pt = parse_coord(str); 153 | str = strtok(NULL, " \t\n"); 154 | int verbose=1; 155 | if (str == NULL) 156 | verbose=0; 157 | ret = make_list_pat_matching(pt, verbose); 158 | } 159 | } 160 | else if (strcmp(command, "fix_atari") == 0) { 161 | int is_atari; 162 | char *str = strtok(NULL, " \t\n"); 163 | if (str == NULL) { 164 | ret = "Error -- point missing"; 165 | goto finished; 166 | } 167 | Point pt = parse_coord(str); 168 | if (!point_is_stone(pos,pt)) { 169 | ret ="Error given point not occupied by a stone"; 170 | goto finished; 171 | } 172 | is_atari = fix_atari(pos,pt, SINGLEPT_NOK, TWOLIBS_TEST,0,moves, sizes); 173 | ret = slist_str_as_point(moves); 174 | int l = (int)strlen(ret); 175 | for (int k=l+1 ; k>=0 ; k--) ret[k+1] = ret[k]; 176 | if (l>0) 177 | ret[1] = ' '; 178 | if (is_atari) ret[0] = '1'; 179 | else ret[0] = '0'; 180 | } 181 | else if (strcmp(command, "env8") == 0) { 182 | char *str = strtok(NULL, " \t\n"); 183 | if(str == NULL) ret = "Error missing point"; 184 | else { 185 | Point pt = parse_coord(str); 186 | int env8 = point_env8(pos, pt); 187 | print_env8(env8); 188 | } 189 | } 190 | else if (strcmp(command, "pos") == 0) { 191 | char *str = strtok(NULL, " \t\n"); 192 | if (strcmp(str, "last") == 0) 193 | str_coord(board_last_move(pos), buf); 194 | else if (strcmp(str, "last2") == 0) 195 | str_coord(board_last2(pos), buf); 196 | else if (strcmp(str, "last3") == 0) 197 | str_coord(board_last3(pos), buf); 198 | else if (strcmp(str, "ko") == 0) { 199 | if (board_ko(pos)) 200 | str_coord(board_ko(pos), buf); 201 | else 202 | buf[0] = 0; 203 | } 204 | else if (strcmp(str, "to_play") == 0) { 205 | if (board_color_to_play(pos) == BLACK) 206 | sprintf(buf, "BLACK"); 207 | else 208 | sprintf(buf, "WHITE"); 209 | } 210 | ret = buf; 211 | } 212 | else if (strcmp(command, "print_mark") == 0) { 213 | char *str = strtok(NULL, " \t\n"); 214 | Mark *marker=already_suggested; 215 | if (strcmp(str, "mark1") == 0) marker = mark1; 216 | if (strcmp(str, "mark2") == 0) marker = mark2; 217 | print_marker(pos, marker); 218 | ret = ""; 219 | } 220 | else if (strcmp(command, "help") == 0) 221 | ret = known_commands; 222 | else { 223 | sprintf(buf, "unknown debug subcommand - %s", command); 224 | ret = buf; 225 | } 226 | finished: 227 | free(amaf_map); free(score_count); free(owner_map); 228 | return ret; 229 | } 230 | -------------------------------------------------------------------------------- /doc/INSTALL.md: -------------------------------------------------------------------------------- 1 | 2 | 1 Installing on Linux/Unix 3 | ========================== 4 | 5 | When in the directory that contains michi.c and Makefile, just type the command (whithout the $ sign) 6 | 7 | $ make 8 | 9 | This will build the michi executable. 10 | If you get some compilation errors see section 4.1. 11 | 12 | Now it is time to perform simple tests to verify that michi works on your system. 13 | 14 | 1.1 Run the tests of the board module 15 | ------------------------------------- 16 | 17 | If you have gogui (http://gogui.sourceforge.net/) installed, 18 | define the GOGUI variable 19 | 20 | $ export GOGUI=/path/to/gogui/bin 21 | 22 | with the location where the gogui executables can be found. Then 23 | 24 | $ make test 25 | 26 | will perform a few (quick) regression tests of the board module. 27 | The result should be a single line 28 | 29 | tests/run 30 | 31 | If you get message like *Program died* go to section 4 "Troubleshooting" for a 32 | few advices of what you can do to try to solve the problem. 33 | 34 | If you want a list of all the tests that are performed try 35 | 36 | $ make test_verbose 37 | 38 | 1.2 Run the tests of the michi playout and tree search 39 | ------------------------------------------------------ 40 | 41 | After installing the large pattern definitions in the build directory of michi 42 | (see section 3), you can run simple non-regression test 43 | 44 | $ make test_michi 45 | 46 | This test compares the results on your system with a reference that has been 47 | computed on my Linux box. 48 | It is fast (takes less than 20 sec on my old computer). 49 | 50 | It could even be run if you do not have the large patterns installed. 51 | In this case, it will only test the computation of the random playouts 52 | and you will get a message that you can ignore 53 | 54 | *files tests/ref/tsdebug.txt and tests/tst/tsdebug.txt differ* 55 | 56 | 57 | 2 Installing on other systems 58 | ============================= 59 | 60 | This version has been verified to work on Windows Seven 64 bits under mingw. 61 | The same procedure as for UNIX/Linux should work for you. 62 | 63 | 3 Download the large patterns definitions 64 | ========================================= 65 | 66 | One important thing is to download the Michi large-scale pattern files 67 | (patterns.prob.xz, patterns.spat.xz) that Petr Baudis provides at : 68 | 69 | http://pachi.or.cz/michi-pat/ 70 | 71 | Unpack them and place them (or a link to them) in the working directory 72 | where you run michi-c, otherwise it will be much weaker. 73 | 74 | 4 Troubleshooting 75 | ================= 76 | 77 | 4.1 Problems with compilation 78 | ----------------------------- 79 | 80 | Having tried to respect the C standards (C99) and simple coding practices, 81 | I hope that you will not experience compilation errors nor even a lot of 82 | warnings. 83 | 84 | That said, for performance, I have used some gcc intrinsics that your compiler 85 | could not know. In that case, you have two solutions : 86 | 87 | - define the macro PORTABLE and a fully portable code (less efficient) 88 | will be used instead of these intrinsics, 89 | - find the equivalent for your compiler and place these definitions in the file 90 | non_portable.h (pull requests on GitHub are welcomed). 91 | 92 | If you get other errors please tell me. 93 | 94 | 4.2 Problems with the tests of the board module 95 | ----------------------------------------------- 96 | 97 | If you get a "Program died" message, the first thing to do is to know 98 | in which of the 4 tests this happens. 99 | For this you can go into the tests directory and try:: 100 | 101 | $ ../michi gtp < fix_atari.tst 102 | $ ../michi gtp < large_patterns.tst 103 | $ ../michi gtp < board.tst 104 | $ ../michi gtp < undo.tst 105 | 106 | This will run michi exactly as gogui-regress do 107 | (with the same working directory but without checking the results) 108 | but now you will see the messages that gogui-regress captured before. 109 | This could help. 110 | 111 | The second step is to read the michi.log file 112 | (warning: this file is overwritten each time michi is restarted) 113 | 114 | If these steps did not help to solve the problem, or if the results are 115 | different from those expected in the test files, you can reactivate the 116 | internal tests that are available in the code (uncomment the lines 117 | michi_assert()) and compile without defining the variable NDEBUG. 118 | 119 | Note: the execution time is now much much longer ... 120 | This is the reason why I have commented these lines in order to avoid mistakes. 121 | 122 | 4.3 Problems with the tests of the random playouts and tree search 123 | ------------------------------------------------------------------ 124 | 125 | Nothing special to say except that the code is deterministic and it should give 126 | the same answer on all platforms when given the same inputs (except if you 127 | have given the special value -1 to the random_seed parameter). 128 | 129 | If problem, I know no better solutions than to make detailed comparaisons of 130 | the executions of **michi mcdebug** and **michi tsdebug** on different systems. 131 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | 2 | manual.html: manual.rst 3 | rst2html.py manual.rst > manual.html 4 | 5 | clean: 6 | rm manual.html 7 | -------------------------------------------------------------------------------- /doc/func_map.txt: -------------------------------------------------------------------------------- 1 | michi_console (ui.c) 2 | make_pat3set 3 | init_large_patterns 4 | empty_position 5 | gtp_io 6 | mcplayout 7 | mcbenchmark 8 | tree_search 9 | play_move 10 | make_params_default 11 | param_general 12 | param_tree 13 | param_playout 14 | 15 | gtp_io (ui.c) 16 | gtp_play 17 | play_move 18 | pass_move 19 | gtp_genmove 20 | genmove 21 | do_play 22 | gtp_time_left 23 | gtp_cputime 24 | gtp_undo 25 | undo_move 26 | gtp_best_moves 27 | compute_best_moves 28 | gtp_boardsize 29 | gtp_clear_board 30 | empty_position 31 | free_tree 32 | new_tree_node 33 | gtp_final_score 34 | compute_all_status 35 | tree_search 36 | mcts_point_status 37 | final_score 38 | gtp_final_status_list 39 | compute_all_status 40 | final_status_list 41 | gtp_gg_undo 42 | debug 43 | print_pos 44 | mcplayout 45 | blocks_OK 46 | block_compute_libs 47 | make_list_last_moves_neighbors 48 | gen_playout_moves_capture 49 | gen_playout_moves_pat3 50 | copy_to_large_board 51 | make_list_pat_matching 52 | fix_atari 53 | slist_str_as_point 54 | print_marker 55 | gogui_analyse_commands 56 | gogui_play_sequence 57 | gogui_setup 58 | read_next_color_point_pair 59 | gogui_setup_player 60 | gtp_komi 61 | gtp_loadsgf 62 | loadsgf 63 | gtp_sg_compare_float 64 | gtp_owner_map 65 | param_general 66 | param_playout 67 | param_tree 68 | gtp_score_histogram 69 | gtp_set_free_handicap 70 | gtp_time_left 71 | gtp_time_settings 72 | gtp_visit_count 73 | 74 | genmove (control.c) 75 | is_better_to_pass 76 | free_tree 77 | new_tree_node 78 | nsims 79 | update_dyn_komi 80 | tree_search 81 | is_time_limited 82 | is_position_clear_enough 83 | 84 | 85 | loadsgf (sgf.c) 86 | game_clear_board 87 | yylex 88 | Collection 89 | GameTree 90 | Sequence 91 | Node 92 | Properties 93 | Property 94 | PropIdent 95 | PropValue 96 | do_play 97 | board_place_stone 98 | board_set_size 99 | game_clear_board 100 | board_set_color_to_play 101 | PropValues 102 | PropValue 103 | PropValues 104 | Properties 105 | RestOfSequence 106 | Node 107 | RestOfSequence 108 | RestOfCollection 109 | RestOfCollection 110 | GameTree 111 | RestOfCollection 112 | 113 | init_large_patterns (patterns.c) 114 | init_zobrist_hashdata 115 | init_stone_color 116 | init_gridcular 117 | init_large_board 118 | load_prob_file 119 | load_spat_file 120 | gridcular_enumerate 121 | gridcular_enumerate1 122 | gridcular_enumerate2 123 | gridcular_register 124 | init_gridcular 125 | gridcular_index 126 | 127 | permute 128 | zobrist_hash 129 | build_pat 130 | insert_pat 131 | find_pat 132 | 133 | 134 | mcbenchmark (ui.c) 135 | empty_position mcplayout 136 | 137 | 138 | mcplayout (michi.c) 139 | make_list_last_moves_neighbors 140 | choose_capture_move 141 | fix_atari 142 | block_compute_libs 143 | line_height 144 | read_ladder_attack 145 | play_move 146 | fix_atari_r 147 | block_compute_libs 148 | make_list_neighbor_blocks_in_atari 149 | play_move 150 | read_ladder_attack 151 | make_list_neighbor_blocks_in_atari 152 | play_move 153 | undo_move 154 | slist_shuffle 155 | choose_from 156 | gen_playout_moves_pat3 157 | pat3_match slist_push 158 | choose_from 159 | is_eye play_move random_int fix_atari undo_move 160 | choose_random_move 161 | is_eye play_move random_int undo_move 162 | score 163 | 164 | 165 | make_pat3set (patterns.c) 166 | pat_enumerate 167 | rot90 168 | pat_enumerate1 169 | vertflip 170 | pat_enumerate2 171 | horizflip 172 | pat_enumerate3 173 | swapcolor 174 | pat_wildexp 175 | compute_code 176 | 177 | 178 | play_move (board.c) 179 | update_blocks 180 | block_remove_lib 181 | block_capture 182 | remove_stone block_add_lib block_clear_libs 183 | select_empty 184 | select_black 185 | select_white 186 | put_stone 187 | point_create_block 188 | new_blkid 189 | block_merge 190 | block_count_libs 191 | block_remove_lib_extend 192 | block_add_lib 193 | 194 | 195 | tree_search (michi.c) 196 | tree_descend 197 | most_urgent 198 | mcplayout 199 | tree_update 200 | best_move 201 | winrate 202 | collect_infos 203 | -------------------------------------------------------------------------------- /doc/functions.txt: -------------------------------------------------------------------------------- 1 | best_move -- 2 | better_to_pass -- check if it is better to pass than to continue 3 | block_add_lib -- Add liberty l to block b 4 | block_capture -- Remove block b from the board -> number of captured stones 5 | block_clear_libs -- Delete all liberties from block b 6 | block_compute_libs -- Return the list of Block b libs 7 | block_count_libs -- Return the number of liberties of block b 8 | block_merge -- Merge 2 blocks. If b1==b2 nothing to do (this is not an error) 9 | block_remove_lib -- Remove liberty l of block b (call block_capture if needed) 10 | block_remove_lib_extend -- Remove liberty l of block b (no capture is possible) 11 | blocks_OK -- Return 1 if block at point pt and blocks in contact with pt are OK 12 | choose_capture_move -- Replace sequence gen_playout_capture_moves+choose_from 13 | choose_from -- Choose a move randomly from the list moves[] 14 | choose_random_move -- Replace sequence gen_playout_moves_random; choose_from 15 | collect_infos -- Collect infos for the time management 16 | compute_all_status -- Compute status of points and blocks (based on owner_map) 17 | compute_code -- Compute a 16 bits code (env8) that defines the 8 neighbors 18 | copy_to_large_board -- Copy the current position to the large board 19 | debug -- Analyse the subcommands of the gtp debug command 20 | do_play -- Play the move (updating the Game struct) 21 | empty_position -- Reset pos to an initial empty board position 22 | final_score -- Compute the score of a terminated game: score > 0.0 if BLACK wins 23 | final_status_list -- Compute the status of all points (from owner_map) 24 | find_pat -- Return the slot where key is found or the empty slot to put key in 25 | fix_atari -- An atari/capture analysis routine that checks the group at Point pt 26 | fix_atari_r -- Simplified version of fix_atari (used by read_ladder_attack) 27 | game_clear_board -- Clear the game struct 28 | free_tree -- Free memory allocated for the TreeNode struct 29 | free_game -- Free the memory allocated for the Game struct 30 | genmove -- compute the next supposed better move (do not play it) 31 | gen_playout_moves_capture -- Compute list of candidate next moves (capture) 32 | gen_playout_moves_pat3 -- Compute list of candidate next moves (pat3) 33 | gogui_analyse_commands -- Process the gtp command gogui-analyze_commands 34 | gogui_play_sequence -- Play a sequence of moves 35 | gogui_setup -- Setup a series of stones (board must be empty) 36 | gogui_setup_player -- Set the color of the next player (board must be empty) 37 | gtp_boardsize -- Set the board size to NxN and clear the board 38 | gtp_clear_board -- Clear the board 39 | gtp_cputime -- Play a stone of the given color at the given vertex 40 | gtp_final_score -- Compute the score of a finished game 41 | gtp_final_status_list -- Report vertices with a specific final status in a finished game 42 | gtp_genmove -- Generate and play the supposedly best move for either color 43 | gtp_gg_undo -- Undo a number of moves 44 | gtp_io -- Loop that process the gtp commands send by the controller 45 | gtp_komi -- Set the komi 46 | gtp_owner_map -- Generate probability of ownership for all vertices 47 | gtp_play -- Play a stone of the given color at the given vertex 48 | gtp_set_free_handicap -- Put free placement handicap stones and put them on the board 49 | gtp_time_left -- Report remaining time 50 | gtp_time_settings -- Set time allowance 51 | gtp_undo -- Undo one move 52 | init_large_board -- Initialize large board (i.e. board with a 7 point border) 53 | init_large_patterns -- Initialize all the data necessary to use large patterns 54 | insert_pat -- Insert a pattern in the hash table 55 | is_eye -- Test if pt is an eye and return its color or 0 if it is not 56 | is_game_board_empty -- return 1 (true) if the board is empty (no handicap ...) 57 | line_height -- Return the line number above nearest board edge (0 based) 58 | loadsgf -- Load a position from the given SGF file 59 | load_prob_file -- Load the probabilities of large patterns 60 | load_spat_file -- Load the spatial description of large patterns 61 | make_list_last_moves_neighbors -- Make a randomly shuffled list of points 62 | make_list_neighbor_blocks_in_atari -- Make a list of blocks in atari in contact with block b 63 | make_list_pat_matching -- Build the list of large patterns that match at the point pt 64 | make_pat3set -- Build the pat3set set (bitfield of 65536 bits) (see patterns.c) 65 | mcbenchmark -- Run n Monte-Carlo playouts from empty position, return average score 66 | mcplayout -- Start a Monte Carlo playout from a given position, return score for to-play 67 | mcts_point_status -- Status of points: OWN_BY_BLACK, OWN_BY_WHITE or UNKNOWN 68 | michi_console -- main() of michi 69 | most_urgent -- Return the most urgent child to play 70 | new_blkid -- Generate a new block id (i.e. one that is not already in use) 71 | new_game -- Build a new game struct (allocate memory) 72 | new_tree_node -- Build a new tree node initialized with prior EVEN 73 | nsims -- Compute the number of simulations for the next tree_search 74 | param_general -- Implement gtp command that list or modify general parameters 75 | param_playout -- Implement gtp command that list or modify parameters of the playout policy 76 | param_tree -- Implement gtp command that list or modify parameters of the tree policy 77 | pass_move -- Play a Pass move in the current position 78 | pat3_match -- Return 1 if a 3x3 pattern match at point pt, else 0 79 | play_move -- Play a move at point pt (not a Pass move). No change of pos if move is illegal 80 | point_create_block -- Create a new block at point pt (assumed EMPTY) 81 | print_pos -- 82 | put_stone -- Put a stone at point pt. Update env4 and env4d neighbor data 83 | random_int -- Print visualization (ASCII) of the given board position 84 | read_ladder_attack -- Check if a capturable ladder is being pulled out at pt 85 | remove_stone -- Remove a stone at point pt. Update env4 and env4d neighbor data 86 | score -- compute score for to-play player (assume game played by mcplayout) 87 | select_black -- Compute a 4 bits code with bit set to 1 for each BLACK neighbor 88 | select_empty -- Compute a 4 bits code with bit set to 1 for each EMPTY neighbor 89 | select_white -- Compute a 4 bits code with bit set to 1 for each WHITE neighbor 90 | slist_clear -- Make the list empty 91 | slist_insert -- Insert one element into the list if it is not already in 92 | slist_push -- Push one element on the top of the list (can be a dupplicate) 93 | slist_shuffle -- Random shuffle the list 94 | slist_size -- Return the size of the list 95 | slist_sort -- Sort the list in increasing order 96 | slist_str_as_int -- ASCII String with the elements of the list considered as integers 97 | slist_str_as_point -- ASCII String with the elements of the list considered as Points 98 | tree_descend -- Descend through the tree to a leaf 99 | tree_search -- Perform MCTS search from a given position for a given number of iterations 100 | tree_update -- Store simulation result in the tree (nodes is the tree path) 101 | try_search_again -- Check if the situation is clear enough after the first tree search 102 | undo_move -- Undo the last move. WARNING CAN ONLY UNDO ONE MOVE 103 | update_blocks -- Update the blocks if a move is done at point pt. No change if move is illegal 104 | winrate -- Return the winrate (number of wins divided by the number of visits) 105 | zobrist_hash -- Return the Zobrist signature of a large pattern provided as ASCII string 106 | -------------------------------------------------------------------------------- /doc/img/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img1.png -------------------------------------------------------------------------------- /doc/img/img10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img10.png -------------------------------------------------------------------------------- /doc/img/img11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img11.png -------------------------------------------------------------------------------- /doc/img/img12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img12.png -------------------------------------------------------------------------------- /doc/img/img13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img13.png -------------------------------------------------------------------------------- /doc/img/img14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img14.png -------------------------------------------------------------------------------- /doc/img/img15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img15.png -------------------------------------------------------------------------------- /doc/img/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img2.png -------------------------------------------------------------------------------- /doc/img/img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img3.png -------------------------------------------------------------------------------- /doc/img/img4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img4.png -------------------------------------------------------------------------------- /doc/img/img5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img5.png -------------------------------------------------------------------------------- /doc/img/img6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img6.png -------------------------------------------------------------------------------- /doc/img/img7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img7.png -------------------------------------------------------------------------------- /doc/img/img8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img8.png -------------------------------------------------------------------------------- /doc/img/img9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/db3108/michi-c2/d2a4cb80b3151d27fe6e4f16ff89ca69191d7e32/doc/img/img9.png -------------------------------------------------------------------------------- /doc/manual.rst: -------------------------------------------------------------------------------- 1 | ======================= 2 | User Manual for michi-c 3 | ======================= 4 | 5 | Denis Blumstein 6 | 7 | Revision : 1.4.2 8 | 9 | 1. Introduction 10 | *************** 11 | 12 | Michi-c is a *Go gtp engine*. This means that 13 | 14 | - it is a program that plays Go, the ancient asian game. 15 | - it takes its inputs and provides its outputs in text mode using the 16 | so-called *Go Text Protocol* (gtp) [1]. 17 | 18 | Put simply, this means that the program reads commands like 19 | 20 | *play black C3* 21 | which tells him to put a black stone on intersection C3 22 | 23 | *genmove white* 24 | which tells him to answer its move in the current position 25 | 26 | It is perfectly possible to play games with michi-c that way. 27 | But it becomes fastidious very soon. 28 | 29 | Luckily Markus Enzenberger has built a wonderful program named *gogui* [2] 30 | that allows us to play interactively with michi using the mouse and a graphical 31 | display of a board (and much more as we will see in the next section). 32 | 33 | So this manual is for you if you need information 34 | 35 | - on how to use michi-c with gogui (section 2) 36 | - on the behaviour of the gtp commands understood by michi-c (section 3) 37 | - the command line arguments (section 4) 38 | 39 | **References** 40 | 41 | [1] Gtp Go Text Protocol : http://www.lysator.liu.se/~gunnar/gtp/ 42 | 43 | [2] Gogui : http://gogui.sourceforge.net/ 44 | 45 | [3] Smart Game Format : http://www.red-bean.com/sgf 46 | 47 | 2. Playing with michi-c through gogui 48 | ************************************* 49 | 50 | **Prerequisite** we suppose that gogui has been installed on your system and 51 | is working well (tested by playing manually a few moves for example) 52 | 53 | 2.1 Making gogui aware of michi-c 54 | --------------------------------- 55 | 56 | The first thing is to add michi-c to the list of programs known by gogui. 57 | This is done by selecting "New Program" in the "Program" menu 58 | as shown in figure 1 below (See the gogui documentation chapter 4.) 59 | 60 | Then a windows pops up with two text fields to fill (fig.2). 61 | In the *command* field enter 62 | 63 | /absolute/path/to/michi gtp 64 | 65 | This will run michi-c with the default parameters. In section 4 you will see 66 | how these parameters can be changed at initialization time. 67 | 68 | I recommend that you enter the absolute path where the michi executable is. 69 | 70 | In the *working directory* field enter any existing directory you like. 71 | This is where the michi log file will go. 72 | 73 | If you get a new window as shown in fig. 3 when you click OK, then 74 | it seems that you are done. 75 | 76 | .. figure:: img/img1.png 77 | :scale: 75 % 78 | :align: center 79 | 80 | fig1. Definition of an New Program 81 | 82 | .. 83 | 84 | .. figure:: img/img2.png 85 | :scale: 75 % 86 | :align: center 87 | 88 | fig2. Define the command to run michi 89 | 90 | .. 91 | 92 | .. figure:: img/img3.png 93 | :scale: 75 % 94 | :align: center 95 | 96 | fig3. The new program gogui will add in its list 97 | 98 | .. 99 | 100 | What happens is that gogui has began to talk to michi. 101 | It has asked for its name and its version. 102 | The fact that the window has appeared shows that michi has answered correctly. 103 | Before you validate the introduction of michi-c in the list of programs known 104 | by gogui (by clicking OK), you have the opportunity to change the label 105 | which, by default is the name of the program. This can be useful to have many 106 | versions of the same program but with different parameter settings. 107 | (it is always possible to change it afterwards, using the "Edit Program" entry 108 | in the Program menu). 109 | 110 | michi-c is now usable in gogui and you can begin to play with it. 111 | 112 | But before playing, I **strongly** recommend that you install the large patterns 113 | provided by Petr Baudis at http://pachi.or.cz/michi-pat/ (see INSTALL.md). 114 | If you don't do this michi-c will be much weaker and playing experience will be 115 | much worse. 116 | **Once the files are unpacked, patterns.prob and patterns.spat must be 117 | placed in the working directory** 118 | (or make a link on them in the working directory). 119 | 120 | You play just by clicking on the intersection you want to play and as soon as 121 | you have played the program begins to "think" and will reply after a few 122 | seconds (the computer color has been set to white at initialization and can 123 | be changed in the menu *Game*, see fig.4). 124 | 125 | .. figure:: img/img5.png 126 | :scale: 75 % 127 | :align: center 128 | 129 | fig4. How to change the computer color 130 | 131 | .. 132 | 133 | **Troubleshooting** If you have not been as lucky as described in the above 134 | paragraphs, the best advice I could give is to verify that the *command* field 135 | is correct. On Linux or MacOS X the easy way to do it is to try to execute 136 | this command in a terminal. 137 | 138 | Then type **name** and the program should answer:: 139 | 140 | = michi-c 141 | 142 | and wait for another command. You can then try **help** to obtain a list of all 143 | available commands. **quit** will leave michi. 144 | 145 | Normally, if you had trouble in running michi in gogui, something wrong should 146 | happen in the previous sequence. This should give you enough feedback, 147 | either from the shell (if you made an error in the path) or from michi, for you 148 | to undersand what the problem is and to correct it. 149 | 150 | If you are still in trouble, maybe is it time to read the section 4 151 | "Troubleshooting" in the INSTALL file. 152 | 153 | 2.2 What michi think of the position ? 154 | -------------------------------------- 155 | 156 | After you have played some moves with michi, you may want to know what is its 157 | estimate of the position. You can obtain this information from commands 158 | accessible in the *Analyze commands* window (as in fig6. below). 159 | 160 | .. figure:: img/img6.png 161 | :scale: 75 % 162 | :align: center 163 | 164 | fig6. Analyze commands 165 | 166 | .. 167 | 168 | If this window is not visible, you can obtain it by selecting the 169 | "Analyze commands" entry in the Tool Menu. 170 | 171 | .. figure:: img/img7.png 172 | :scale: 75 % 173 | :align: center 174 | 175 | fig7. Make the Analyze Commands window visible 176 | 177 | .. 178 | 179 | Any of the analyze commands can be run either by double clicking on it or by 180 | selecting a command and clicking on the *Run* button. 181 | It is also possible to have a command executed automatically after each move. 182 | 183 | The first three commands are for changing parameters that control the behavior 184 | of michi-c. They will be described in next subsection (2.4). 185 | 186 | The other commands give meaningful answer only just after michi-c has played 187 | its move. 188 | 189 | *Status Dead* 190 | 191 | mark the stones estimated as dead by michi-c 192 | (not always accurate as you will notice). 193 | 194 | *Principal variation* 195 | shows the next 5 best continuation as estimated by mcts. 196 | 197 | .. figure:: img/img14.png 198 | :scale: 75 % 199 | :align: center 200 | 201 | fig8. Best sequence 202 | 203 | .. 204 | 205 | *Best moves* 206 | shows the winrate of the 5 best moves (i.e most visited by MCTS) 207 | 208 | .. figure:: img/img13.png 209 | :scale: 75 % 210 | :align: center 211 | 212 | fig9. Best moves 213 | 214 | .. 215 | 216 | *Owner_Map* 217 | represent for each intersection the percentage of white or black possession 218 | at the end of the playouts (big black square = 100 % black possession, 219 | big white square = 100 % white possession, nothing = around 50 %) 220 | 221 | .. figure:: img/img11.png 222 | :scale: 75 % 223 | :align: center 224 | 225 | fig10. Owner Map 226 | 227 | .. 228 | 229 | *Visit Count* 230 | represent the number of visits for each move in the root node of MCTS. 231 | The size of the square is maximum for the most visited move and the 232 | surface of each square is proportional to the visit count (discretized by 233 | step of 10 %, so 0%, 1%, .., 5% are the same 234 | , 6 %, 7%, ..., 15 % the same , etc.) 235 | 236 | .. figure:: img/img12.png 237 | :scale: 75 % 238 | :align: center 239 | 240 | fig11. Visit count 241 | 242 | .. 243 | 244 | *Histogram of scores* 245 | This is a primitive representation of the histogram of the playout scores. 246 | Should find a good way to show a beautiful python graphic in next version. 247 | 248 | .. figure:: img/img15.png 249 | :scale: 75 % 250 | :align: center 251 | 252 | fig12. Primitive view of histogram of playouts scores 253 | 254 | .. 255 | 256 | 2.3 Live graphics 257 | ----------------- 258 | 259 | All the graphics commands (marked with a gfx prefix in the *Analyze commands* 260 | windows) can also be updated at regular intervals 261 | during the search providing an animation that can be fun to watch. 262 | 263 | This is done by setting the *Live gfx* and *Live gfx interval* parameters in 264 | the *General Parameters* setting window as seen on the figure 12 below. 265 | 266 | 2.4 Changing the michi-c parameters 267 | ----------------------------------- 268 | 269 | By running one of the three commands *General Parameters*, 270 | *Tree Policy Parameters* or *Random Policy Parameters* you will get a new 271 | window (respectively shown in fig.12, fig.13 and fig.14) that will allow you 272 | to change the parameters at any moment when michi is not thinking. 273 | 274 | The modification process is natural and does not need any explanation. 275 | It takes place after you click on OK. 276 | 277 | There has not been any particular thought about the order of the parameters 278 | and it could certainly be improved. 279 | 280 | .. figure:: img/img8.png 281 | :scale: 75 % 282 | :align: center 283 | 284 | fig12. General Parameters settings 285 | 286 | .. 287 | 288 | Definitions:: 289 | 290 | use_dynamic_komi 291 | dynamic komi is used in the current version of michi-c only for 292 | handicap games (linear version). It can be enabled or disabled. 293 | 294 | komi_per_handicap_stone 295 | this value multiplied by the number of handicap stones will be the 296 | delta komi at the beginning of the game 297 | 298 | play_until_the_end 299 | when checked this option disallows early passing (useful on cgos) 300 | 301 | random_seed 302 | random seed. Should normally be a positive integer 303 | -1 generate a true random seed that will be different each time michi-c is restarted 304 | 305 | REPORT_PERIOD 306 | number of playouts between each report by michi on the standard output 307 | note: useful only if verbosity > 0 308 | 309 | verbosity 310 | 0, 1 or 2 : control the verbosity of michi on the standard output 311 | 312 | RESIGN_THRES 313 | winrate threshold (in [0.0,1.0]. 314 | When the winrate becomes below this threshold, michi resign 315 | 316 | FASTPLAY20_THRES 317 | if at 20% playouts winrate is > FASTPLAY20_THRES, stop reading 318 | 319 | FASTPLAY5_THRES 320 | if at 5% playouts winrate is > FASTPLAY5_THRES, stop reading 321 | 322 | Live_gfx 323 | None, best_moves, owner_map, principal_variation or visit_count 324 | if different from None, gogui will display at regular intervals the 325 | same graphics as in figures 8 to 11. 326 | 327 | Live_gfx_interval 328 | the interval (number of playouts) between live graphics refresh 329 | 330 | 331 | .. figure:: img/img9.png 332 | :scale: 75 % 333 | :align: center 334 | 335 | fig13. Tree Policy Parameters settings 336 | 337 | .. 338 | 339 | Definitions:: 340 | 341 | N_SIMS 342 | Number of simulations per move (search). 343 | Note: michi-c use a different value when playing with time limited constraints 344 | 345 | RAVE_EQUIV 346 | number of visits which makes the weight of RAVE simulations and real simulations equal 347 | 348 | EXPAND_VISITS 349 | number of visits before a node is expanded 350 | 351 | PRIOR_EVEN 352 | should be even. This represent a 0.5 prior 353 | 354 | PRIOR_SELFATARI 355 | NEGATIVE prior if the move is a self-atari 356 | 357 | PRIOR_CAPTURE_ONE 358 | prior if the move captures one stone 359 | 360 | PRIOR_CAPTURE_MANY 361 | prior if the move captures many stones 362 | 363 | PRIOR_PAT3 364 | prior if the move match a 3x3 pattern 365 | 366 | PRIOR_LARGEPATTERN 367 | multiplier for the large patterns probability 368 | note: most moves have relatively small probability 369 | 370 | PRIOR_CFG[] 371 | prior for moves in cfg distance 1, 2, 3 372 | 373 | PRIOR_EMPTYAREA 374 | prior for moves in empty area. 375 | Negative for move on the first and second lines 376 | Positive for move on the third and fourth lines 377 | 378 | 379 | .. figure:: img/img10.png 380 | :scale: 75 % 381 | :align: center 382 | 383 | fig14. Random Policy Parameters settings 384 | 385 | .. 386 | 387 | Definitions:: 388 | 389 | PROB_HEURISTIC_CAPTURE (0.90) 390 | probability of heuristic capture suggestions being taken in playout 391 | 392 | PROB_HEURISTIC_PAT3 (0.95) 393 | probability of heuristic 3x3 pattern suggestions being taken in playout 394 | 395 | PROB_SSAREJECT (0.90) 396 | probability of rejecting suggested self-atari in playout 397 | 398 | PROB_RSAREJECT (0.50) 399 | probability of rejecting random self-atari in playout 400 | this is lower than above to allow nakade 401 | 402 | 403 | 3. Reference of michi-c gtp commands 404 | ************************************ 405 | 406 | The list of all gtp commands understood by michi-c (version 1.4) is 407 | described in the following sections. 408 | 409 | 3.1 Standard gtp commands 410 | ------------------------- 411 | 412 | The following standard gtp commands are implemented. 413 | Please refer to [1] for the specification of each command. 414 | 415 | *protocol_version, 416 | name, 417 | version, 418 | known_command, 419 | list_commands, 420 | quit, 421 | boardsize, 422 | clear_board, 423 | komi, 424 | play, 425 | genmove, 426 | set_free_handicap, 427 | loadsgf, 428 | time_left, 429 | time_settings, 430 | final_score, 431 | final_status_list, 432 | undo* 433 | 434 | The standard is implemented exept for 435 | 436 | *time_settings* 437 | only absolute time setting is implemented yet 438 | 439 | *loadsgf* 440 | michi-c can only read simple SGF files, i.e. files with no 441 | variations nor games collections (see [3]) 442 | but this is not carefully checked so 443 | expect some crash if you try to play with the limits. 444 | 445 | These limitations will be removed in some next release. 446 | 447 | 3.2 Gogui specific commands (or extensions used by gogui) 448 | --------------------------------------------------------- 449 | 450 | Please refer to [2] for the specification of each command. 451 | 452 | *gogui-analyze_commands, 453 | gogui-play_sequence, 454 | gogui-setup, 455 | gogui-setup_player, 456 | gg-undo, 457 | kgs-genmove_cleanup* 458 | 459 | 3.3 Commands to get or set parameters 460 | ------------------------------------- 461 | 462 | Maybe the most important commands to know from a user point of view are the 463 | three commands *param_general*, *param_playout* and *param_tree* that allow us 464 | to change the parameters controlling the behavior of michi-c during the game. 465 | 466 | If you give no argument to the command it will simply print the current value 467 | of all the parameters it controls, else you must give two arguments : the name 468 | of a parameter and the value you want to give to it. 469 | 470 | If the given name is not known from the command, it is ignored and the command 471 | behaves as if it was called without argument. 472 | 473 | The names recognized by these commands are those described in section 2.4. 474 | 475 | *param_general*:: 476 | 477 | Function: Read or write internal parameters of michi-c (General) 478 | Arguments: name value or nothing 479 | Fails: No value (if only a name was given) 480 | Returns: A string formatted for gogui analyze command of param type 481 | 482 | *param_playout*:: 483 | 484 | Function: Read or write internal parameters of michi-c (Playout Policy) 485 | Arguments: name value or nothing 486 | Fails: No value (if only a name was given) 487 | Returns: A string formatted for gogui analyze command of param type 488 | 489 | *param_tree*:: 490 | 491 | Function: Read or write internal parameters of michi-c (Tree Policy) 492 | Arguments: name value or nothing 493 | Fails: No value (if only a name was given) 494 | Returns: A string formatted for gogui analyze command of param type 495 | 496 | 497 | 498 | 3.4 Rest of michi-c specific commands 499 | ------------------------------------- 500 | 501 | *best_moves*:: 502 | 503 | Function: Build a list of the 5 best moves (5 couples: point winrate) 504 | Arguments: none 505 | Fails: never 506 | Returns: A string formatted for gogui analyze command of pspair type 507 | 508 | *cputime* 509 | This is a command used by the gogui tool gogui-twogtp. 510 | It returns a time in seconds, whose origin is unspecified. 511 | 512 | *debug * 513 | This command is only used for debugging purpose and regression testing. 514 | People that need it are able to read the code and I believe it is not 515 | necessary to make this manual longer in order to describe it. 516 | 517 | *help* 518 | This is a synonym for list_commands 519 | 520 | *owner_map*:: 521 | 522 | Function: Compute a value in [-1,1] for each point: -1 = 100% white, 1=100 % black 523 | Arguments: none 524 | Fails: never 525 | Returns: A string formatted for gogui analyze command of gfx/INFLUENCE type 526 | 527 | *principal_variation*:: 528 | 529 | Function: Compute the best sequence (5 moves) from the current position 530 | Arguments: none 531 | Fails: never 532 | Returns: A string formatted for gogui analyze command of gfx VAR type 533 | 534 | 535 | *score_histogram*:: 536 | 537 | Function: Build histogram of playout scores 538 | Arguments: none 539 | Fails: never 540 | Returns: A string formatted for gogui analyze command of hstring type 541 | 542 | *visit_count*:: 543 | 544 | Function: Compute a value in [0,1] for each point: 0 = never, 1= most visited 545 | Arguments: none 546 | Fails: never 547 | Returns: A string formatted for gogui analyze command of gfx/INFLUENCE type 548 | 549 | 4. Michi-c command line arguments 550 | ********************************* 551 | 552 | When michi is run from the command line without any parameter or as:: 553 | 554 | $ ./michi -h 555 | 556 | it will write a simple usage message:: 557 | 558 | usage: michi mode [config.gtp] 559 | 560 | where mode = 561 | * gtp play gtp commands read from standard input 562 | * mcbenchmark run a series of playouts (number set in config.gtp) 563 | * mcdebug run a series of playouts (verbose, nb of sims as above) 564 | * tsdebug run a series of tree searches 565 | * defaults write a template of config file on stdout (defaults values) 566 | * selfplay run a sequence of self play games 567 | and 568 | * config.gtp an optional file containing gtp commands 569 | 570 | 571 | The most commonly used mode values are **gtp** and **defaults**. 572 | 573 | Mode **gtp** launches the gtp loop that will be ended by sending the 574 | command *quit* to michi. 575 | 576 | Mode **defaults** print on the standard output the current default value 577 | of every modifiable parameter in michi and leave immediately.:: 578 | 579 | param_general use_dynamic_komi 0 580 | param_general komi_per_handicap_stone 7.0 581 | param_general play_until_the_end 0 582 | param_general random_seed 1 583 | param_general REPORT_PERIOD 200000 584 | param_general verbosity 2 585 | param_general RESIGN_THRES 0.20 586 | param_general FASTPLAY20_THRES 0.80 587 | param_general FASTPLAY5_THRES 0.95 588 | param_general Live_gfx None 589 | param_general Live_gfx_interval 1000 590 | param_tree N_SIMS 2000 591 | param_tree RAVE_EQUIV 3500 592 | param_tree EXPAND_VISITS 8 593 | param_tree PRIOR_EVEN 10 594 | param_tree PRIOR_SELFATARI 10 595 | param_tree PRIOR_CAPTURE_ONE 15 596 | param_tree PRIOR_CAPTURE_MANY 30 597 | param_tree PRIOR_PAT3 10 598 | param_tree PRIOR_LARGEPATTERN 100 599 | param_tree PRIOR_CFG[0] 24 600 | param_tree PRIOR_CFG[1] 22 601 | param_tree PRIOR_CFG[2] 8 602 | param_tree PRIOR_EMPTYAREA 10 603 | param_playout PROB_HEURISTIC_CAPTURE 0.90 604 | param_playout PROB_HEURISTIC_PAT3 0.95 605 | param_playout PROB_SSAREJECT 0.90 606 | param_playout PROB_RSAREJECT 0.50 607 | 608 | This list is interesting in itself. 609 | 610 | In addition by redirecting it to a file, you obtain a configuration file that 611 | you can use as the optional parameter (config.gtp). 612 | 613 | When michi is used in **gtp** mode with this second argument, all the gtp 614 | commands placed in the config.gtp file will be executed at initialization. 615 | 616 | This feature can be used to modify the default parameters or for example to 617 | load a given position from a SGF file. 618 | 619 | The three other modes **mcbenchmark**, **mcdebug**, **tsdebug** or **selfplay** 620 | are, as their name suggest useful mainly for debugging and benchmarking. 621 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | int michi_console(int argc, char *argv[]); 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | return michi_console(argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /michi.h: -------------------------------------------------------------------------------- 1 | // michi.h -- A minimalistic Go-playing engine (from Petr Baudis michi.py) 2 | #include "board.h" 3 | //========================= Definition of Data Structures ===================== 4 | 5 | #define MAX_GAME_LEN (N*N*3) 6 | #define SINGLEPT_OK 1 7 | #define SINGLEPT_NOK 0 8 | #define TWOLIBS_TEST 1 9 | #define TWOLIBS_TEST_NO 0 10 | #define TWOLIBS_EDGE_ONLY 1 11 | #if BOARDSIZE < 141 12 | #define ZOBRIST_HASH_SIZE 141 13 | #else 14 | #define ZOBRIST_HASH_SIZE BOARDSIZE 15 | #endif 16 | // ---------------------------- MCTS Constants -------------------------------- 17 | extern int N_SIMS, RAVE_EQUIV, EXPAND_VISITS; 18 | extern int PRIOR_EVEN, PRIOR_SELFATARI, PRIOR_CAPTURE_ONE, PRIOR_CAPTURE_MANY; 19 | extern int PRIOR_PAT3, PRIOR_LARGEPATTERN, LEN_PRIOR_CFG, PRIOR_EMPTYAREA; 20 | extern int PRIOR_CFG[], LEN_PRIOR_CFG; 21 | extern int REPORT_PERIOD; 22 | extern double PROB_HEURISTIC_CAPTURE, PROB_HEURISTIC_PAT3; 23 | extern double PROB_SSAREJECT, PROB_RSAREJECT; 24 | extern double RESIGN_THRES, FASTPLAY20_THRES, FASTPLAY5_THRES; 25 | //------------------------------- Data Structures ----------------------------- 26 | typedef struct { 27 | char name[2][32]; // players name 28 | char rank[2][4]; // players rank 29 | char rules[32]; // rules as read in the sgf 30 | char result[32]; // result as read in the sgf (ex: W+Time, W+1.5) 31 | char overtime[32]; // overtime as read in the sgf 32 | } GameInfo; 33 | 34 | typedef struct { 35 | Position *pos; 36 | int handicap; 37 | float komi; 38 | Color computer_color; 39 | int time_left; // main time left: maintained internally 40 | int time_left_gtp; // main time left: provided by time_left 41 | int time_init; // main time: initialized by time_settings 42 | GameInfo gi; 43 | Point moves[MAX_GAME_LEN]; 44 | Point placed_black_stones[BOARDSIZE]; 45 | Point placed_white_stones[BOARDSIZE]; 46 | ZobristHash zhash; 47 | ZobristHash zhistory[MAX_GAME_LEN]; 48 | } Game; 49 | 50 | typedef struct tree_node { // ------------ Monte-Carlo tree node -------------- 51 | int v; // number of visits 52 | int w; // number of wins(expected reward is w/v) 53 | int pv; // pv, pw are prior values 54 | int pw; // (node value = w/v + pw/pv) 55 | int av; // av, aw are amaf values ("all moves as first"), 56 | int aw; // used for the RAVE tree policy) 57 | int nchildren; // number of children 58 | Point move; // move that reaches the position represented by the node 59 | struct tree_node **children; 60 | } TreeNode; // Monte-Carlo tree node 61 | 62 | typedef struct { 63 | double pessimistic; 64 | double raw; 65 | double optimistic; 66 | } Score; 67 | 68 | // -------------------------------- Global Data ------------------------------- 69 | extern Byte pat3set[8192]; 70 | extern int npat3; 71 | extern Byte* pat3set_p[15]; 72 | extern Mark* already_suggested; 73 | extern int play_until_the_end; // O or 1 74 | extern int use_dynamic_komi; 75 | extern double komi_per_handicap_stone; 76 | extern char Live_gfx[80]; 77 | extern int Live_gfx_interval; 78 | extern Point allpoints[BOARDSIZE]; 79 | extern float nplayouts, nplayouts_per_second; 80 | extern float start_playouts_sec, stop_playouts_sec; 81 | extern int nplayouts_real; 82 | extern float saved_time_left; 83 | extern float best2, bestr, bestwr; 84 | extern ZobristHash zobrist_hashdata[ZOBRIST_HASH_SIZE][4]; 85 | //================================== Code ===================================== 86 | //------------------------- Functions in control.c ---------------------------- 87 | void compute_all_status(Position *pos, int owner_map[BOARDSIZE], 88 | int score_count[], Status block_status[], Status point_status[]); 89 | Score final_score(Game *game, Status block_status[MAX_BLOCKS], 90 | Status point_status[BOARDSIZE]); 91 | Point genmove (Game *game, TreeNode **tree, int *owner_map, int *score_count); 92 | //-------------------------- Functions in debug.c ----------------------------- 93 | char* debug(Game *game, char *command); 94 | //-------------------------- Functions in michi.c ----------------------------- 95 | TreeNode* best_move(TreeNode *tree, TreeNode **except); 96 | void collect_infos(TreeNode *tree, int n, TreeNode *best 97 | , TreeNode *workspace[], Position *pos); 98 | void compute_best_moves(TreeNode *tree, char sep[2] 99 | , TreeNode *best_node[5], char *can); 100 | void compute_principal_variation(TreeNode *tree, Point moves[6]); 101 | int fix_atari(Position *pos, Point pt, int singlept_ok 102 | , int twolib_test, int twolib_edgeonly, Slist moves, Slist sizes); 103 | void free_tree(TreeNode *tree); 104 | int gen_playout_moves_capture(Position *pos, Slist heuristic_set, float prob, 105 | int expensive_ok, Slist moves, Slist sizes); 106 | int gen_playout_moves_pat3(Position *pos, Slist heuristic_set, float prob, 107 | Slist moves); 108 | double mcplayout(Position *pos, int amaf_map[], int owner_map[], 109 | int score_count[2*N*N], int disp); 110 | TreeNode* new_tree_node(void); 111 | void print_pos(Position *pos, FILE *f, int *owner_map); 112 | Point tree_search(Position *pos, TreeNode *tree, int n, int owner_map[], 113 | int score_count[], int disp); 114 | //-------------------------- Functions in params.c ---------------------------- 115 | void make_params_default(FILE *f); 116 | char* param_general(const char *param); 117 | char* param_playout(const char *param); 118 | char* param_tree(const char *param); 119 | //------------------------- Functions in patterns.c --------------------------- 120 | void free_large_patterns(void); 121 | void make_pat3set(void); 122 | char* make_list_pat3_matching(Position *pos, Point pt); 123 | char* make_list_pat_matching(Point pt, int verbose); 124 | void init_large_patterns(const char *prob, const char *spat); 125 | void log_hashtable_synthesis(); 126 | double large_pattern_probability(Point pt); 127 | void init_zobrist_hashdata(void); 128 | //---------------------------- Functions in sgf.c ----------------------------- 129 | Game* new_game(Position *pos); 130 | void free_game(Game *game); 131 | char* game_clear_board(Game *game); 132 | void game_set_komi(Game *game, float komi); 133 | int is_game_board_empty(Game *game); 134 | char* do_play(Game *game, Color c, Point pt); 135 | char* do_undo(Game *game); 136 | char* loadsgf(Game *game, const char *filename, int nmoves); 137 | char* storesgf(Game *game, const char *filename, const char* version); 138 | //---------------------------- Functions in ui.c ------------------------------ 139 | void display_live_gfx(Position *pos, TreeNode *tree, 140 | int owner_map[BOARDSIZE]); 141 | void gtp_io(Game *game, FILE *f, FILE *out, int owner_map[], int score_count[]); 142 | //-------------------- Functions inlined for simplicity ----------------------- 143 | __INLINE__ int game_handicap(Game *game) { return game->handicap;} 144 | __INLINE__ float game_komi(Game *game) { return game->pos->komi;} 145 | __INLINE__ float game_main_time(Game *game) { return game->time_init;} 146 | __INLINE__ char* game_name(Game *game, Color c) { return game->gi.name[c&1];} 147 | __INLINE__ char* game_overtime(Game *game) { return game->gi.overtime;} 148 | __INLINE__ char* game_rank(Game *game, Color c) { return game->gi.rank[c&1];} 149 | __INLINE__ char* game_result(Game *game) { return game->gi.result;} 150 | __INLINE__ char* game_rules(Game *game) { 151 | if (strlen(game->gi.rules)==3) 152 | return game->gi.rules; 153 | else if (strcmp(game->gi.rules,"Japanese")==0) 154 | return "JPN"; 155 | else if (strcmp(game->gi.rules,"Chinese")==0) 156 | return "CHN"; 157 | else 158 | return "???"; 159 | } 160 | //-------------------- Functions inlined for performance ---------------------- 161 | __INLINE__ int is_time_limited(Game *game) {return game->time_init >0;} 162 | 163 | // Pattern matching 164 | __INLINE__ int pat3_match(Position *pos, Point pt) 165 | // Return 1 if a 3x3 pattern match at point pt, else 0 166 | { 167 | int env8=point_env8(pos, pt), q=env8 >> 3, r=env8 & 7; 168 | return (pat3set[q] & bit[r]) != 0; 169 | } 170 | -------------------------------------------------------------------------------- /non_portable.h: -------------------------------------------------------------------------------- 1 | // non_portable.h -- non portable parts of the code (for efficiency) 2 | 3 | /* =========================================================================== 4 | * If an operaion can be implemented very efficiently on a given architecture 5 | * it must be placed in this file. A portable version of the function must be 6 | * provided. 7 | * 8 | * At the moment, only a few operations available on the Intel processors are 9 | * provided. In the future, more operations using SSE intrinsics or the 10 | * auto vectorization capabilities of the compilers will be defined. 11 | * When/if users of other operating systems/compilers want to contribute they 12 | * can add another case in this file 13 | * 14 | * Will certainly need to be refined and improved in the next future 15 | * =========================================================================== 16 | */ 17 | #ifndef _MSC_VER // for gcc 18 | #define __INLINE__ static inline 19 | #else // for Microsoft Visual Studio 20 | #define __INLINE__ static __inline 21 | #endif 22 | 23 | // ------------------------- Portable definitions ----------------------------- 24 | // of the efficient operations coded below for some (compiler/processor). 25 | #ifdef PORTABLE 26 | __INLINE__ int popcnt_u32(unsigned int m32) 27 | // Compute the number of bits set (i.e. equal to 1) in the 32 bits word m32 28 | // This is a very naive implementation. Much better solutions exist. 29 | { 30 | int sum=0; 31 | for (int i=0 ; i<32 ; i++) 32 | if (m32 & (1< // for INTEL SSE intrinsics 52 | __INLINE__ int popcnt_u32(unsigned int m32) { return __builtin_popcount(m32);} 53 | __INLINE__ int bsf_u32(unsigned int m32) { return __builtin_ctz(m32);} 54 | #elif xxxxxxx 55 | // If useful : add here new implementation for another architecture 56 | #endif // End of case of architectures (compiler/processor) 57 | #endif // PORTABLE 58 | -------------------------------------------------------------------------------- /params.c: -------------------------------------------------------------------------------- 1 | // params.c -- Variable parameters used by the michi program 2 | #include "michi.h" 3 | static char buf[4096]; 4 | static int raw; 5 | // ----------------- Initial Values for Tree Policy parameters ---------------- 6 | int N_SIMS = 2000; 7 | int RAVE_EQUIV = 3500; 8 | int EXPAND_VISITS = 8; 9 | int PRIOR_EVEN = 10; // should be even number; 0.5 prior 10 | int PRIOR_SELFATARI = 10; // negative prior 11 | int PRIOR_CAPTURE_ONE = 15; 12 | int PRIOR_CAPTURE_MANY = 30; 13 | int PRIOR_PAT3 = 10; 14 | int PRIOR_LARGEPATTERN = 100; // most moves have relatively small probability 15 | int PRIOR_CFG[] = {24, 22, 8}; 16 | int LEN_PRIOR_CFG = (sizeof(PRIOR_CFG)/sizeof(int)); 17 | int PRIOR_EMPTYAREA = 10; 18 | // --------------- Initial Values for Random Policy parameters-- -------------- 19 | double PROB_HEURISTIC_CAPTURE = 0.9; // probability of heuristic suggestions 20 | double PROB_HEURISTIC_PAT3 = 0.95; // being taken in playout 21 | double PROB_SSAREJECT = 0.9;// prob of rejecting suggested self-atari in playout 22 | double PROB_RSAREJECT = 0.5;// prob of rejecting random self-atari in playout 23 | // this is lower than above to allow nakade 24 | // ----------------- Initial Values for general parameters -------------------- 25 | int use_dynamic_komi = 0; 26 | double komi_per_handicap_stone = 7.0; 27 | int play_until_the_end = 0; 28 | int random_seed = 1; 29 | int REPORT_PERIOD = 200000; 30 | int verbosity = 2; 31 | double FASTPLAY20_THRES= 0.8;//if at 20% playouts winrate is >this, stop reading 32 | double FASTPLAY5_THRES = 0.95;//if at 5% playouts winrate is >this, stop reading 33 | double RESIGN_THRES = 0.2; 34 | char Live_gfx[80] = "None"; 35 | int Live_gfx_interval = 1000; 36 | //==================================== Code =================================== 37 | int pk_line; 38 | char *margin[] = { 39 | " ", 40 | " ", 41 | " ", 42 | " ", 43 | " ", 44 | " ", 45 | " ", 46 | " ", 47 | " ", 48 | " ", 49 | " ", 50 | " ", 51 | " ", 52 | " ", 53 | " ", 54 | " ", 55 | " ", 56 | " ", 57 | " ", 58 | " ", 59 | }; 60 | #define PRINT_KEY_VALUE(p, fmt) \ 61 | if (raw) \ 62 | sprintf(key_value, "%30s " #fmt "\n", #p, p); \ 63 | else \ 64 | sprintf(key_value, "%s %30s " #fmt "\n", margin[pk_line], #p, p); \ 65 | strcat(buf, key_value); \ 66 | pk_line++; 67 | 68 | #define PRINT_BOOL_KEY_VALUE(p, fmt) \ 69 | if (raw) \ 70 | sprintf(key_value, "%30s " #fmt "\n", #p, p); \ 71 | else \ 72 | sprintf(key_value, "%s[bool] %30s " #fmt "\n", margin[pk_line], #p, p); \ 73 | strcat(buf, key_value); \ 74 | pk_line++; 75 | 76 | #define PRINT_LIST_KEY_VALUE(list, p, fmt) \ 77 | if (raw) \ 78 | sprintf(key_value, "%30s " #fmt "\n", #p, p); \ 79 | else \ 80 | sprintf(key_value, "%s %s " #fmt "\n", list, #p, p); \ 81 | strcat(buf, key_value); \ 82 | pk_line++; 83 | 84 | #define READ_VALUE(p, fmt) { \ 85 | char *value = strtok(NULL," \t\n"); \ 86 | if (value == NULL) \ 87 | ret = "Error! No value"; \ 88 | else { \ 89 | sscanf(value, #fmt, &p); \ 90 | ret = ""; \ 91 | } \ 92 | } 93 | 94 | #define READ_VALUE_STRING(p, fmt) { \ 95 | char *value = strtok(NULL," \t\n"); \ 96 | if (value == NULL) \ 97 | ret = "Error! No value"; \ 98 | else { \ 99 | sscanf(value, #fmt, p); \ 100 | ret = ""; \ 101 | } \ 102 | } 103 | 104 | char* param_playout(const char *param) 105 | // Implement gtp command that list or modify parameters of the playout policy 106 | { 107 | char *ret=buf, key_value[80]; 108 | char *known_params = "\nPROB_HEURISTIC_CAPTURE\nPROB_HEURISTIC_PAT3\n" 109 | "PROB_REJECT_SELF_ATARI_RANDOM\nPROB_REJECT_SELF_ATARI_SUGGESTED\n"; 110 | 111 | if (param == NULL) { // List current values 112 | pk_line = 0; 113 | buf[0] = 0; 114 | PRINT_KEY_VALUE(PROB_HEURISTIC_CAPTURE, %.2f); 115 | PRINT_KEY_VALUE(PROB_HEURISTIC_PAT3, %.2f); 116 | PRINT_KEY_VALUE(PROB_SSAREJECT, %.2f); 117 | PRINT_KEY_VALUE(PROB_RSAREJECT, %.2f); 118 | } 119 | else if (strcmp(param, "help") == 0) 120 | ret = known_params; 121 | else if (strcmp(param, "list") == 0) 122 | ret = known_params; 123 | else if (strcmp(param, "PROB_HEURISTIC_CAPTURE") == 0) 124 | READ_VALUE(PROB_HEURISTIC_CAPTURE, %lf) 125 | else if (strcmp(param, "PROB_HEURISTIC_PAT3") == 0) 126 | READ_VALUE(PROB_HEURISTIC_PAT3, %lf) 127 | else if (strcmp(param, "PROB_SSAREJECT") == 0) 128 | READ_VALUE(PROB_SSAREJECT, %lf) 129 | else if (strcmp(param, "PROB_RSAREJECT") == 0) 130 | READ_VALUE(PROB_RSAREJECT, %lf) 131 | else { 132 | log_fmt_s('W',"%s is not a param_playout", param); 133 | strtok(NULL," \t\n"); // make the strtok buffer empty 134 | ret = "Error - not a param_playout"; 135 | } 136 | return ret; 137 | } 138 | 139 | char* param_tree(const char *param) 140 | // Implement gtp command that list or modify parameters of the tree policy 141 | { 142 | char *ret=buf, key_value[80]; 143 | char *known_params = "\nN_SIMS\nRAVE_EQUIV\nEXPAND_VISITS\n" 144 | "PRIOR_EVEN\nPRIOR_SELFATARI\nPRIOR_CAPTURE_ONE\nPRIOR_CAPTURE_MANY\n" 145 | "PRIOR_PAT3\nPRIOR_LARGEPATTERN\nPRIOR_CFG[0]\nPRIOR_CFG[1]\n" 146 | "PRIOR_CFG[2]\nPRIOR_EMPTYAREA\n"; 147 | 148 | if (param == NULL) { // List current values 149 | pk_line = 0; 150 | buf[0] = 0; 151 | PRINT_KEY_VALUE(N_SIMS, %d); 152 | PRINT_KEY_VALUE(RAVE_EQUIV, %d); 153 | PRINT_KEY_VALUE(EXPAND_VISITS, %d); 154 | PRINT_KEY_VALUE(PRIOR_EVEN, %d); 155 | PRINT_KEY_VALUE(PRIOR_SELFATARI, %d); 156 | PRINT_KEY_VALUE(PRIOR_CAPTURE_ONE, %d); 157 | PRINT_KEY_VALUE(PRIOR_CAPTURE_MANY, %d); 158 | PRINT_KEY_VALUE(PRIOR_PAT3, %d); 159 | PRINT_KEY_VALUE(PRIOR_LARGEPATTERN, %d); 160 | PRINT_KEY_VALUE(PRIOR_CFG[0], %d); 161 | PRINT_KEY_VALUE(PRIOR_CFG[1], %d); 162 | PRINT_KEY_VALUE(PRIOR_CFG[2], %d); 163 | PRINT_KEY_VALUE(PRIOR_EMPTYAREA, %d); 164 | } 165 | else if (strcmp(param, "help") == 0) 166 | ret = known_params; 167 | else if (strcmp(param, "list") == 0) 168 | ret = known_params; 169 | else if (strcmp(param, "N_SIMS") == 0) 170 | READ_VALUE(N_SIMS, %d) 171 | else if (strcmp(param, "RAVE_EQUIV") == 0) 172 | READ_VALUE(RAVE_EQUIV, %d) 173 | else if (strcmp(param, "EXPAND_VISITS") == 0) 174 | READ_VALUE(EXPAND_VISITS, %d) 175 | else if (strcmp(param, "PRIOR_EVEN") == 0) 176 | READ_VALUE(PRIOR_EVEN, %d) 177 | else if (strcmp(param, "PRIOR_SELFATARI") == 0) 178 | READ_VALUE(PRIOR_SELFATARI, %d) 179 | else if (strcmp(param, "PRIOR_CAPTURE_ONE") == 0) 180 | READ_VALUE(PRIOR_CAPTURE_ONE, %d) 181 | else if (strcmp(param, "PRIOR_CAPTURE_MANY") == 0) 182 | READ_VALUE(PRIOR_CAPTURE_MANY, %d) 183 | else if (strcmp(param, "PRIOR_PAT3") == 0) 184 | READ_VALUE(PRIOR_PAT3, %d) 185 | else if (strcmp(param, "PRIOR_LARGEPATTERN") == 0) 186 | READ_VALUE(PRIOR_LARGEPATTERN, %d) 187 | else if (strcmp(param, "PRIOR_CFG[0]") == 0) 188 | READ_VALUE(PRIOR_CFG[0], %d) 189 | else if (strcmp(param, "PRIOR_CFG[1]") == 0) 190 | READ_VALUE(PRIOR_CFG[1], %d) 191 | else if (strcmp(param, "PRIOR_CFG[2]") == 0) 192 | READ_VALUE(PRIOR_CFG[2], %d) 193 | else if (strcmp(param, "PRIOR_EMPTYAREA") == 0) 194 | READ_VALUE(PRIOR_EMPTYAREA, %d) 195 | else { 196 | log_fmt_s('W',"%s is not a param_tree", param); 197 | strtok(NULL," \t\n"); // make the strtok buffer empty 198 | ret = "Error - not a param_tree"; 199 | } 200 | return ret; 201 | } 202 | 203 | unsigned int true_random_seed(void) 204 | // return a true random seed (which depends on the time) 205 | { 206 | unsigned int r1, r2, sec, day; 207 | time_t tm=time(NULL); 208 | struct tm *tcal=localtime(&tm); 209 | sec = tcal->tm_sec + 60*(tcal->tm_min + 60*tcal->tm_hour); 210 | // day is a coarse (but sufficient for the current purpose) approximation 211 | day = tcal->tm_mday + 31*(tcal->tm_mon + 12*tcal->tm_year); 212 | // Park & Miller random generator (same as qdrandom()) 213 | r1 = (1664525*sec) + 1013904223; 214 | r2 = (1664525*day) + 1013904223; 215 | return (r1^r2); 216 | } 217 | 218 | char* param_general(const char *param) 219 | // Implement gtp command that list or modify general parameters 220 | { 221 | char *ret=buf, key_value[800]; 222 | char *known_params = "play_until_the_end\nuse_dynamic_komi\nverbosity\n" 223 | "komi_per_handicap_stone\n" 224 | "REPORT_PERIOD\nRESIGN_THRES\n" 225 | "FASTPLAY20_THRES\nFASTPLAY5_THRES\n"; 226 | 227 | if (param == NULL) { // List current values 228 | pk_line = 0; 229 | buf[0] = 0; 230 | margin[0] = " "; 231 | margin[2] = " "; 232 | PRINT_BOOL_KEY_VALUE(use_dynamic_komi, %d); 233 | PRINT_KEY_VALUE(komi_per_handicap_stone, %.1lf); 234 | PRINT_BOOL_KEY_VALUE(play_until_the_end, %d); 235 | PRINT_KEY_VALUE(random_seed, %d); 236 | PRINT_KEY_VALUE(REPORT_PERIOD, %d); 237 | PRINT_KEY_VALUE(verbosity, %d); 238 | PRINT_KEY_VALUE(RESIGN_THRES, %.2f); 239 | PRINT_KEY_VALUE(FASTPLAY20_THRES, %.2f); 240 | PRINT_KEY_VALUE(FASTPLAY5_THRES, %.2f); 241 | PRINT_LIST_KEY_VALUE("[list/None/best_moves/owner_map/" 242 | "principal_variation/visit_count]", 243 | Live_gfx, %s); 244 | PRINT_KEY_VALUE(Live_gfx_interval, %d); 245 | margin[0] = " "; 246 | margin[2] = " "; 247 | } 248 | else if (strcmp(param, "help") == 0) 249 | ret = known_params; 250 | else if (strcmp(param, "list") == 0) 251 | ret = known_params; 252 | else if (strcmp(param, "play_until_the_end") == 0) 253 | READ_VALUE(play_until_the_end, %d) 254 | else if (strcmp(param, "use_dynamic_komi") == 0) 255 | READ_VALUE(use_dynamic_komi, %d) 256 | else if (strcmp(param, "random_seed") == 0) { 257 | READ_VALUE(random_seed, %d) 258 | if (random_seed == 0) 259 | idum = true_random_seed(); 260 | else 261 | idum = random_seed; 262 | } 263 | else if (strcmp(param, "REPORT_PERIOD") == 0) 264 | READ_VALUE(REPORT_PERIOD, %d) 265 | else if (strcmp(param, "verbosity") == 0) 266 | READ_VALUE(verbosity, %d) 267 | else if (strcmp(param, "RESIGN_THRES") == 0) 268 | READ_VALUE(RESIGN_THRES, %lf) 269 | else if (strcmp(param, "FASTPLAY20_THRES") == 0) 270 | READ_VALUE(FASTPLAY20_THRES, %lf) 271 | else if (strcmp(param, "FASTPLAY5_THRES") == 0) 272 | READ_VALUE(FASTPLAY5_THRES, %lf) 273 | else if (strcmp(param, "Live_gfx") == 0) 274 | READ_VALUE_STRING(Live_gfx, %s) 275 | else if (strcmp(param, "Live_gfx_interval") == 0) 276 | READ_VALUE(Live_gfx_interval, %d) 277 | else { 278 | log_fmt_s('W',"%s is not a param_general", param); 279 | strtok(NULL," \t\n"); // make the strtok buffer empty 280 | ret = "Error - not a param_general"; 281 | } 282 | return ret; 283 | } 284 | 285 | void params_fprint(FILE *f, const char *cmd) 286 | { 287 | char *str = strtok(buf,"\n"); 288 | while (str != NULL) { 289 | fprintf(f, "%-15s %s\n", cmd, str); 290 | str = strtok(NULL,"\n"); 291 | } 292 | } 293 | 294 | void make_params_default(FILE *f) 295 | { 296 | raw = 1; 297 | param_general(NULL); params_fprint(f, "param_general"); 298 | param_tree(NULL); params_fprint(f, "param_tree"); 299 | param_playout(NULL); params_fprint(f, "param_playout"); 300 | raw = 0; 301 | } 302 | -------------------------------------------------------------------------------- /patterns.c: -------------------------------------------------------------------------------- 1 | // patterns.c -- Routines for 3x3 patterns and large patterns for michi program 2 | // 3 | // (c) 2015 Denis Blumstein Petr Baudis 4 | // MIT licence (i.e. almost public domain) 5 | #include "michi.h" 6 | 7 | // There are two types of patterns used in michi : 8 | // 9 | // - 3x3 patterns : given the color of the 8 neighbors of the central point 10 | // (4 neighbors and 4 diagonal neighbors) the pat3_match() function returns 11 | // the answer (yes/no) to the question : 12 | // Is there a 3x3 pattern matching at this point ? 13 | // The recognized patterns are defined by the table pat3_src[] below 14 | // 15 | // - Large patterns : given the color of the points in a certain neighborhood 16 | // of the central point, the large_pattern_probability() function returns the 17 | // probability of play at this position. 18 | // This probability is used to bias the search in the MCTS tree. 19 | // 20 | // The point neighborhoods are defined in [4] (see references in michi.c). 21 | // They are symetric under reflexions and rotations of the board. For each 22 | // point, 12 neighborhoods of increasing size are considered, each 23 | // neighborhood includes all the neighborhoods of lesser size. 24 | // In the program, the neighborhoods are defined by the 2 tables : 25 | // pat_gridcular_seq and pat_gridcular_size. These tables are compiled into 26 | // the pat_gridcular_seq1d array. 27 | 28 | // See below for more details on the pattern code. 29 | // 30 | // ================================ 3x3 patterns ============================== 31 | // 32 | // 1 bit is sufficient to store the fact that a pattern matches for a given 33 | // configuration of the 8 neighbors color. This configuration can be encoded 34 | // as a 16 bits integer called env8 (as 2 bits are sufficient to encode one of 35 | // the 4 possible colors of a point). So the set of 3x3 patterns that are 36 | // recognized is represented by an array of 65536 bits (or 8192 bytes). 37 | // 38 | // The used patterns are symetrical wrt the color so we only need a single array 39 | // (pat3set). A bit is set in this array when at least one pattern matches. 40 | // 41 | // These patterns are used in the random playouts, so pattern matching must be 42 | // very fast. To achieve best speed the 2 components of env8, env4 and env4d, 43 | // are stored as arrays in the struct Position and are incrementally updated by 44 | // the routines that modify the board put_stone() and remove_stone() 45 | // 46 | Byte bit[8]={1,2,4,8,16,32,64,128}; 47 | Byte pat3set[8192]; // Set of the pat3 patterns (defined in pat3src below) 48 | int npat3; // Number of patterns in pat3src 49 | 50 | // A set of 3x3 patterns is defined by an array of ASCII strings (of length 10) 51 | // with the following encoding 52 | // char pat_src[][10]= { 53 | // "XOX" // X : one of BLACK or WHITE 54 | // "..." // O : the other color 55 | // "???", // . : EMPTY 56 | // "XO." // x : not X i.e O or EMPTY or OUT 57 | // ".X." // o : not O i.e X or EMPTY or OUT 58 | // "?.?", // ? : BLACK or WHITE or EMPTY or OUT 59 | // "###" // # : edge of the goban (out of board) 60 | // "###" 61 | // "###" // string that mark the end of input 62 | // } 63 | 64 | char pat3src[][10] = { 65 | "XOX" // 1- hane pattern - enclosing hane 66 | "..." 67 | "???", 68 | "XO." // 2- hane pattern - non-cutting hane 69 | "..." 70 | "?.?", 71 | "XO?" // 3- hane pattern - magari 72 | "X.." 73 | "x.?", 74 | //"XOO", // hane pattern - thin hane 75 | //"...", 76 | //"?.?", "X", - only for the X player 77 | ".O." // 4- generic pattern - katatsuke or diagonal attachment; 78 | //similar to magari 79 | "X.." 80 | "...", 81 | "XO?" // 5- cut1 pattern (kiri] - unprotected cut 82 | "O.o" 83 | "?o?", 84 | "XO?" // 6- cut1 pattern (kiri] - peeped cut 85 | "O.X" 86 | "???", 87 | "?X?" // 7- cut2 pattern (de] 88 | "O.O" 89 | "ooo", 90 | "OX?" // 8- cut keima 91 | "o.O" 92 | "???", 93 | "X.?" // 9- side pattern - chase 94 | "O.?" 95 | "##?", 96 | "OX?" // 10- side pattern - block side cut 97 | "X.O" 98 | "###", 99 | "?X?" // 11- side pattern - block side connection 100 | "x.O" 101 | "###", 102 | "?XO" // 12- side pattern - sagari 103 | "x.x" 104 | "###", 105 | "?OX" // 13- side pattern - cut 106 | "X.O" 107 | "###", 108 | "###" 109 | "###" 110 | "###" // Mark the end of the pattern list 111 | }; 112 | 113 | int nb=0; 114 | 115 | int code(char color, int p) 116 | { 117 | // Bits set for the 4 neighbours North(1), East(5), South(7), West(3) 118 | // or for the 4 diagonal neighbours NE(2), SE(8), SW(6), NE(0) 119 | int code_E[4] = {0x00, 0x00, 0x00, 0x00}; // EMPTY(.) 120 | int code_O[4] = {0x01, 0x02, 0x04, 0x08}; // OUT (#) 121 | int code_W[4] = {0x10, 0x20, 0x40, 0x80}; // WHITE(O) 122 | int code_B[4] = {0x11, 0x22, 0x44, 0x88}; // BLACK(X) 123 | switch(color) { 124 | case 'O': return code_W[p]; 125 | case 'X': return code_B[p]; 126 | case '.': return code_E[p]; 127 | case '#': return code_O[p]; 128 | } 129 | return 0; // can't happen, but make compiler happy 130 | } 131 | 132 | int compute_code(char *src) 133 | // Compute a 16 bits code that completely describes the 3x3 environnement of a 134 | // given point. 135 | // Note: the low 8 bits describe the state of the 4 neighbours, 136 | // the high 8 bits describe the state of the 4 diagonal neighbors 137 | { 138 | // src 0 1 2 bits in env8 7 0 4 139 | int env8=0; // 3 4 5 bit 0=LSB 3 . 1 140 | // 6 7 8 6 2 5 141 | // Neighbours of the central point 142 | env8 |= code(src[1], 0); // North value given by src[1] in position 0 143 | env8 |= code(src[5], 1); // East value given by src[5] in position 1 144 | env8 |= code(src[7], 2); // South value given by src[7] in position 2 145 | env8 |= code(src[3], 3); // West value given by src[3] in position 3 146 | // Diagonal neighbours of the central point 147 | env8 |= code(src[2], 0)<<8;// North/East value given by src[2] in position 0 148 | env8 |= code(src[8], 1)<<8;// South/East value given by src[8] in position 1 149 | env8 |= code(src[6], 2)<<8;// South/West value given by src[6] in position 2 150 | env8 |= code(src[0], 3)<<8;// North/West value given by src[0] in position 3 151 | return env8; 152 | } 153 | 154 | int pat; 155 | void pat_wildexp(char *src, int i) 156 | // Expand wildchar in src[i] 157 | { 158 | char src1[10]; 159 | int env8; 160 | if ( i==9 ) { // all the positions in src are processed -- end of recursion 161 | env8 = compute_code(src); 162 | nb++; 163 | int q = env8 >> 3, r = env8 & 7; 164 | pat3set[q] |= bit[r]; // set the bit corresponding to env8 165 | return; 166 | } 167 | if (src[i] == '?') { 168 | strcpy(src1, src); 169 | src1[i] = 'X'; pat_wildexp(src1, i+1); 170 | src1[i] = 'O'; pat_wildexp(src1, i+1); 171 | src1[i] = '.'; pat_wildexp(src1, i+1); 172 | src1[i] = '#'; pat_wildexp(src1, i+1); 173 | } 174 | else if (src[i] == 'x') { 175 | strcpy(src1, src); 176 | src1[i]='O'; pat_wildexp(src1, i+1); 177 | src1[i]='.'; pat_wildexp(src1, i+1); 178 | src1[i]='#'; pat_wildexp(src1, i+1); 179 | } 180 | else if (src[i] == 'o') { 181 | strcpy(src1, src); 182 | src1[i]='X'; pat_wildexp(src1, i+1); 183 | src1[i]='.'; pat_wildexp(src1, i+1); 184 | src1[i]='#'; pat_wildexp(src1, i+1); 185 | } 186 | else 187 | pat_wildexp(src, i+1); 188 | } 189 | 190 | char *swapcolor(char *src) 191 | { 192 | for (int i=0 ; i<9 ; i++) { 193 | switch (src[i]) { 194 | case 'X': src[i] = 'O'; break; 195 | case 'O': src[i] = 'X'; break; 196 | case 'x': src[i] = 'o'; break; 197 | case 'o': src[i] = 'x'; break; 198 | } 199 | } 200 | return src; 201 | } 202 | 203 | char* horizflip(char *src) 204 | { 205 | SWAP(char, src[0], src[6]); 206 | SWAP(char, src[1], src[7]); 207 | SWAP(char, src[2], src[8]); 208 | return src; 209 | } 210 | 211 | char* vertflip(char *src) 212 | { 213 | SWAP(char, src[0], src[2]); 214 | SWAP(char, src[3], src[5]); 215 | SWAP(char, src[6], src[8]); 216 | return src; 217 | } 218 | 219 | char* rot90(char *src) 220 | { 221 | char t=src[0]; src[0]=src[2]; src[2]=src[8]; src[8]=src[6]; src[6]=t; 222 | t=src[1]; src[1]=src[5]; src[5]=src[7]; src[7]=src[3]; src[3]=t; 223 | return src; 224 | } 225 | 226 | void pat_enumerate3(char *src) 227 | { 228 | char src1[10]; 229 | pat_wildexp(src, 0); 230 | strcpy(src1,src); 231 | pat_wildexp(swapcolor(src1), 0); 232 | } 233 | 234 | void pat_enumerate2(char *src) 235 | { 236 | char src1[10]; 237 | pat_enumerate3(src); 238 | strcpy(src1, src); 239 | pat_enumerate3(horizflip(src1)); 240 | } 241 | 242 | void pat_enumerate1(char *src) 243 | { 244 | char src1[10]; 245 | pat_enumerate2(src); 246 | strcpy(src1, src); 247 | pat_enumerate2(vertflip(src1)); 248 | } 249 | 250 | void pat_enumerate(char *src) 251 | { 252 | char src1[10]; 253 | pat_enumerate1(src); 254 | strcpy(src1, src); 255 | pat_enumerate1(rot90(src1)); 256 | } 257 | 258 | void make_pat3set(void) 259 | // Build the pat3set set (bitfield of 65536 bits). 260 | // See explanations at top of the file 261 | { 262 | npat3 = sizeof(pat3src) / 10 - 1; 263 | if (npat3 > 13) { 264 | fprintf(stderr,"Error npat3 too big (%d)\n", npat3); 265 | exit(-1); 266 | } 267 | memset(pat3set,0,8192); 268 | for(int p=0 ; strcmp(pat3src[p], "#########") != 0 ; p++) { 269 | pat = p; 270 | pat_enumerate(pat3src[p]); 271 | } 272 | } 273 | 274 | // =============================== Large patterns ============================= 275 | // 276 | // The sizes of the neighborhoods are large (up to 141 points). Therefore the 277 | // exact configuration of the colors in the neighborhoods cannot be used for 278 | // pattern matching. Instead, a Zobrist signature (see [4] et [7]) of 64 bits 279 | // is computed from all points in the neighborhoods. Then this signature is 280 | // searched in a big hash table that contains the signatures of the patterns 281 | // read in the file patterns.spat. If successful, the search returns the 282 | // probability of the patterns. 283 | // 284 | // A large board with 7 layers of OUT of board points is used in order to avoid 285 | // tests during the computation of the signature for a given point. This large 286 | // board contains only the information on the color of points. It is build by 287 | // copy_to_large_board() which is called only once in the routine expand() while 288 | // pat_match() is called many times. 289 | // 290 | // With the large board it is an easy matter to compute the signature by 291 | // looping on all the points of the neighborhood thanks to the gridcular_seq1d 292 | // array which stores the displacements with respect to the central point. 293 | // 294 | // The hash table "patterns" is computed by init_patterns(). 295 | // It uses internal chaining with double hashing [9]. 296 | // The performance of this hash table is reported in the log file michi.log 297 | // after the compilation of patterns.spat file and at the end of the execution. 298 | // 299 | // ------------------------ Data Structures ----------------------------------- 300 | // Large pattern entry in the hash table 301 | typedef struct hash_t { 302 | ZobristHash key; // 64 bits Zobrist hash 303 | int id; // id of the pattern 304 | float prob; // probability of move triggered by the pattern 305 | } LargePat; 306 | #define KSIZE 25 // key size in bits 307 | #define LENGTH (1<>20) & KMASK, h2=primes[(key>>(20+KSIZE)) & 15], len=1; 378 | nsearchs++; 379 | while (patterns[h].key != key) { 380 | if (patterns[h].key == 0) { 381 | sum_len_failure += len; 382 | return h; 383 | } 384 | len++; 385 | h+=h2; if (h>LENGTH) h -= LENGTH; 386 | } 387 | nsuccess++; 388 | sum_len_success += len; 389 | return h; 390 | } 391 | 392 | int insert_pat(LargePat p) 393 | // Insert a pattern in the hash table. Return FOUND if the pattern is already in 394 | { 395 | int i = find_pat(p.key); 396 | if (patterns[i].key==0) { 397 | patterns[i] = p; 398 | return i; 399 | } 400 | else 401 | return FOUND; 402 | } 403 | 404 | LargePat build_pat(ZobristHash key, int id, float prob) 405 | { 406 | LargePat pat = {key, id, prob}; 407 | return pat; 408 | } 409 | 410 | // Code: ------------------- Zobrist signature computation -------------------- 411 | void init_stone_color(void) 412 | { 413 | memset(color,0,1024); 414 | color['.'] = 0; // 0: EMPTY 415 | color['#'] = color[' '] = 1; // 1: OUT 416 | color['O'] = color['x'] = 2; // 2: Other or 'x' 417 | color['X'] = 3; // 3: ours 418 | } 419 | 420 | void init_zobrist_hashdata(void) 421 | { 422 | int idum_save = idum; 423 | idum = 55555; // make sure zobrist_hashdata does not depend on user input 424 | // 55555 seems to work reasonably well 425 | for (int d=0 ; did_max) 546 | id_max = id; 547 | } 548 | rewind(f); 549 | return id_max; 550 | } 551 | 552 | void load_prob_file(FILE *f) 553 | // Load the probabilities of large patterns 554 | { 555 | float prob; 556 | int id,t1,t2; 557 | 558 | while (fgets(buf, 255, f) != NULL) { 559 | if (buf[0] == '#') continue; 560 | sscanf(buf,"%f %d %d (s:%d)", &prob, &t1, &t2, &id); 561 | probs[id] = prob; 562 | } 563 | } 564 | 565 | int load_spat_file(FILE *f) 566 | // Load the spatial description of large patterns 567 | { 568 | int d, id, idmax=-1, len, lenmax=0, npats=0; 569 | char strpat[256], strperm[256]; 570 | ZobristHash k; 571 | int permutation[8][141]; 572 | 573 | // compute the 8 permutations of the gridcular positions corresponding 574 | // to the 8 possible reflexions or rotations of pattern 575 | gridcular_enumerate(permutation); 576 | 577 | while (fgets(buf, 255, f) != NULL) { 578 | if (buf[0] == '#') continue; 579 | sscanf(buf,"%d %d %s", &id, &d, strpat); 580 | npats++; 581 | len = (int)strlen(strpat); 582 | if (len > lenmax) { 583 | lenmax = len; 584 | idmax = id; 585 | } 586 | if (id > idmax) 587 | idmax = id; 588 | for (int i=0 ; i< 8 ; i++) { 589 | permute(permutation, i, strpat, strperm); 590 | assert(permutation_OK(permutation)); 591 | k = zobrist_hash(strperm); 592 | LargePat pat = build_pat(k, id, probs[id]); 593 | insert_pat(pat); 594 | } 595 | } 596 | log_fmt_i('I', "read %d patterns", npats); 597 | log_fmt_i('I', "idmax = %d", idmax); 598 | sprintf(buf, "pattern length max = %d (found at %d)", lenmax, idmax); 599 | log_fmt_s('I', buf, NULL); 600 | large_patterns_loaded = 1; 601 | return npats; 602 | } 603 | 604 | // Code: ------------------------- Public functions --------------------------- 605 | void log_hashtable_synthesis() 606 | { 607 | double nkeys=0; 608 | if (!large_patterns_loaded) return; 609 | for (int i=0 ; i 0 && (fprob == NULL || fspat == NULL)) { 656 | fprintf(stderr, "Warning: michi cannot load pattern files, " 657 | "It will be much weaker. "); 658 | if (EXPAND_VISITS > 2) 659 | fprintf(stderr, "Consider lowering EXPAND_VISITS %d->2\n", 660 | EXPAND_VISITS); 661 | else 662 | fprintf(stderr,"\n"); 663 | } 664 | log_fmt_s('I', "=========== Hashtable initialization synthesis ==========", 665 | NULL); 666 | // reset the statistics after logging them 667 | log_hashtable_synthesis(); 668 | nsearchs = nsuccess = 0; 669 | sum_len_success=sum_len_failure=0.0; 670 | } 671 | 672 | void free_large_patterns(void) 673 | { 674 | free(probs); 675 | free(patterns); 676 | } 677 | 678 | double large_pattern_probability(Point pt) 679 | // return probability of large-scale pattern at coordinate pt. 680 | // Multiple progressively wider patterns may match a single coordinate, 681 | // we consider the largest one. 682 | { 683 | double prob=-1.0; 684 | int matched_len=0, non_matched_len=0; 685 | ZobristHash k=0; 686 | 687 | if (large_patterns_loaded) 688 | for (int s=1 ; s<13 ; s++) { 689 | int len = pat_gridcular_size[s]; 690 | k = update_zobrist_hash_at_point(large_coord[pt], s, k); 691 | int i = find_pat(k); 692 | if (patterns[i].key==k) { 693 | prob = patterns[i].prob; 694 | matched_len = len; 695 | } 696 | else if (matched_len < non_matched_len && non_matched_len < len) 697 | break; 698 | else 699 | non_matched_len = len; 700 | } 701 | return prob; 702 | } 703 | 704 | char* make_list_pat_matching(Point pt, int verbose) 705 | // Build the list of large patterns that match at the point pt 706 | { 707 | ZobristHash k=0; 708 | int i; 709 | char id[16]; 710 | 711 | if (!large_patterns_loaded) return ""; 712 | 713 | buf[0] = 0; 714 | for (int s=1 ; s<13 ; s++) { 715 | k = update_zobrist_hash_at_point(large_coord[pt], s, k); 716 | i = find_pat(k); 717 | if (patterns[i].key == k) { 718 | if (verbose) 719 | sprintf(id,"%d(%.3f) ", patterns[i].id, patterns[i].prob); 720 | else 721 | sprintf(id,"%d ", patterns[i].id); 722 | strcat(buf, id); 723 | } 724 | } 725 | return buf; 726 | } 727 | -------------------------------------------------------------------------------- /sgf.c: -------------------------------------------------------------------------------- 1 | // sgf.c -- Update the Game structure and import/export it as SGF file 2 | #include "michi.h" 3 | 4 | FILE *f; // source of the sgf data 5 | Game *game; // game in which the sgf file will be written 6 | int nmoves; // number of moves loaded from the sgf file 7 | Byte size_already_set; 8 | // Displacements towards the neighbors of a point 9 | // North East South West NE SE SW NW 10 | static int delta[] = { -N-1, 1, N+1, -1, -N, W, N, -W}; 11 | static char buf[4046]; 12 | 13 | // ---------------------- Update of the Game struct --------------------------- 14 | Game *new_game(Position *pos) 15 | { 16 | Game *game=michi_calloc(1, sizeof(Game)); 17 | game->pos = pos; 18 | game_clear_board(game); 19 | return game; 20 | } 21 | 22 | void free_game(Game *game) 23 | { 24 | free(game->pos); 25 | free(game); 26 | } 27 | 28 | void game_set_komi(Game *game, float komi) 29 | { 30 | game->komi = komi; 31 | board_set_komi(game->pos, komi); 32 | } 33 | 34 | char* game_clear_board(Game *game) 35 | { 36 | game->handicap = 0; 37 | slist_clear(game->moves); 38 | slist_clear(game->placed_black_stones); 39 | slist_clear(game->placed_white_stones); 40 | game->zhash = 0; 41 | return empty_position(game->pos); 42 | } 43 | 44 | int is_game_board_empty(Game *game) 45 | { 46 | if (slist_size(game->moves) != 0 47 | || slist_size(game->placed_black_stones) != 0 48 | || slist_size(game->placed_white_stones) != 0) 49 | return 0; 50 | else 51 | return 1; 52 | } 53 | 54 | void board_set_size_safe(Position *pos, char *str) 55 | // Set the size of the board with check that it is compatible with compilation 56 | { 57 | int size = atoi(str); 58 | if (size > N) { 59 | sprintf(buf, "Error - Trying to set incompatible boardsize %s" 60 | " (max=%d)", str, N); 61 | log_fmt_s('E', buf, NULL); 62 | } 63 | else { 64 | board_set_size(pos, size); 65 | empty_position(pos); 66 | } 67 | } 68 | 69 | void update_zhash(Game *game, Color c, Point pt) 70 | // Update Zobrist Hash of the position (handle captures) 71 | { 72 | Point points[BOARDSIZE]; 73 | 74 | game->zhash ^= zobrist_hashdata[pt][c]; 75 | if (game->pos->undo_capture) { 76 | Color other = color_other(c); 77 | for (int k=0; k<4 ; k++) { 78 | if (game->pos->undo_capture & (1<pos, n, points); 81 | FORALL_IN_SLIST(points, s) { 82 | game->zhash ^= zobrist_hashdata[s][other]; 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | char* look_for_repetition(Game *game) 90 | { 91 | char *ret=""; 92 | int n=game->pos->n; 93 | for (int k=30 ; kzhash == game->zhistory[k]) 95 | ret = "Error - Positional Superko rule violation"; 96 | game->zhistory[n] = game->zhash; 97 | return ret; 98 | } 99 | 100 | char* do_undo(Game *game) 101 | { 102 | Position *pos = game->pos; 103 | Point move = slist_pop(game->moves); 104 | 105 | board_set_captured_neighbors(pos, (move >> 9) & 15); 106 | board_set_ko_old(pos, (move >> 13) & 511); 107 | update_zhash(game, color_other(pos->to_play), pos->last); 108 | char *ret=undo_move(pos); 109 | board_set_color_to_play(pos, move >> 22); 110 | if (ret[0]==0) c2--; 111 | if (board_nmoves(pos) > 3) 112 | board_set_last3(pos, game->moves[board_nmoves(pos)-2] & 511); 113 | return ret; 114 | } 115 | 116 | char* do_play(Game *game, Color c, Point pt) 117 | // Play the move (updating the Game struct) 118 | { 119 | char *ret="", str[8]; 120 | Color to_play_before; 121 | Info m; 122 | int played=0; 123 | Position *pos = game->pos; 124 | 125 | if (pt == RESIGN_MOVE) 126 | goto finished; 127 | 128 | to_play_before = board_color_to_play(pos); 129 | board_set_color_to_play(pos, c); 130 | if(pt == PASS_MOVE) { 131 | ret = pass_move(pos); 132 | m = pt + (board_captured_neighbors(pos) << 9) 133 | + (board_ko_old(pos) << 13) 134 | + ((to_play_before) << 22); 135 | played = 1; 136 | } 137 | else if (point_color(pos,pt) == EMPTY) { 138 | ret = play_move(pos, pt); 139 | if (ret[0] == 0) { 140 | m = pt + (board_captured_neighbors(pos) << 9) 141 | + (board_ko_old(pos) << 13) 142 | + ((to_play_before) << 22); 143 | played = 1; 144 | } 145 | // else illegal move: nothing played 146 | } 147 | else { 148 | sprintf(buf,"Error Illegal move: %s not EMPTY\n", str_coord(pt,str)); 149 | ret = buf; 150 | } 151 | if (played) { 152 | c2++; 153 | slist_push(game->moves, m); 154 | if (pt != PASS_MOVE) { 155 | ZobristHash zhash_save = game->zhash; 156 | update_zhash(game, c, pt); 157 | ret = look_for_repetition(game); 158 | if (ret[0] != 0) { 159 | log_fmt_p('I',"try illegal move (superko violation) at %s " 160 | "(ignored)", pt); 161 | do_undo(game); 162 | if (game->zhash != zhash_save) 163 | log_fmt_s('W', "Error in undoing zhash", NULL); 164 | } 165 | } 166 | } 167 | finished: 168 | return ret; 169 | } 170 | // ----------------------- Write Game to SGF file ----------------------------- 171 | char* storesgf(Game *game, const char *filename, const char* version) 172 | // Write the sequence of moves stored in the game structure in SGF file 173 | { 174 | char date[24], str[8]; 175 | FILE *f = fopen(filename, "w"); 176 | Position *pos = game->pos; 177 | int size = board_size(pos); 178 | 179 | if (f != NULL) { 180 | // Get the current date 181 | time_t tp=time(NULL); 182 | struct tm *tm=gmtime(&tp); 183 | strftime(date,24,"%Y-%m-%d",tm); 184 | 185 | // write the root node 186 | fprintf(f, "(;FF[4]CA[UTF-8]AP[michi-c:%s]SZ[%d]KM[%.1f]DT[%s]\n", 187 | version, size, board_komi(pos), date); 188 | if (slist_size(game->placed_black_stones) > 0) { 189 | fprintf(f, "AB"); 190 | int n = 0; 191 | FORALL_IN_SLIST(game->placed_black_stones, pt) { 192 | fprintf(f, "[%s]", str_sgf_coord(pt, str, size)); 193 | if (++n % 10 == 0) fprintf(f, "\n "); 194 | } 195 | fprintf(f, "\n"); 196 | } 197 | if (slist_size(game->placed_white_stones) > 0) { 198 | fprintf(f, "AW"); 199 | int n = 0; 200 | FORALL_IN_SLIST(game->placed_white_stones, pt) { 201 | fprintf(f, "[%s]", str_sgf_coord(pt, str, size)); 202 | if (++n % 10 == 0) fprintf(f, "\n "); 203 | } 204 | fprintf(f, "\n"); 205 | } 206 | 207 | // write the moves 208 | FORALL_IN_SLIST(game->moves, m) { 209 | Color c = m >> 22; 210 | m &= 511; 211 | char color = (c == BLACK) ? 'B' : 'W'; 212 | fprintf(f, ";%c[%s]", color, str_sgf_coord(m, str, size)); 213 | } 214 | fprintf(f, ")\n"); 215 | fclose(f); 216 | buf[0] = 0; 217 | } 218 | else 219 | sprintf(buf, "Error - can't open file %s", filename); 220 | 221 | return buf; 222 | } 223 | 224 | // ------------------------ SGF Recursive Parser ------------------------------ 225 | // This code implement a Recursive Descent Parser for the SGF grammar 226 | // References : 227 | // [1] Aho, Lam, Sethi, Ullman. Compilers. 2nd Edition (The Dragon book) 228 | // [2] http://www.red-bean.com/sgf/sgf4.html#2 (SGF) 229 | 230 | // The SGF grammar has been adapted from [2] to make it Right Recursive 231 | // 232 | // The recognized grammar is the following : 233 | // Collection = GameTree RestOfCollection 234 | // RestOfCollection = GameTree RestOfCollection | None 235 | // GameTree = "(" Sequence RestOfCollection ")" 236 | // Sequence = Node RestOfSequence 237 | // RestOfSequence = Node RestOfSequence | None 238 | // Node = ";" Property { Property } 239 | // Property = PropIdent { PropValue } 240 | // PropIdent = see prop_name[] array below 241 | // PropValue = "[" ValueType "]" 242 | // ValueType = Point | Number | Real | Text | None 243 | 244 | // The code is simple. There is a function for each non terminal symbol 245 | // of the grammar with the same name of the non terminal. 246 | // The terminal symbols are handled by the lexical analyser yylex() 247 | 248 | // Note: the code may look very inefficient at first sight with a lot of 249 | // recursive functions. But it is not. Any current optimizing compiler 250 | // (gcc, icc or Microsoft Visual Studio) is able to eliminate easily this 251 | // "tail recursion". 252 | // In addition, parsing sgf file is certainly not the most consuming task 253 | // that michi must accomplish 254 | 255 | 256 | int prop; // current property 257 | int lineno=1; // current line of input 258 | int symbol; // lookahead symbol 259 | char yytext[8192]; // the text of the current token 260 | int yyleng; // length of the string representation of the current token 261 | int yyval; // value of the current token 262 | char sgferr[80]; // Error message 263 | // bad hack for reading the sgf files produced by gogui-twogtp 264 | int read_raw_text=0; 265 | 266 | #define UPPERCASE_STRING 256 267 | #define LOWERCASE_STRING 257 268 | #define STRING 258 269 | #define POINT 260 270 | #define NUMBER 261 271 | #define REAL 262 272 | 273 | char* prop_name[] = { "", "AB", "AW", "B", "W", "C", 274 | "FF", "AP", "CA", "SZ", "KM", "DT", 275 | "PL", "HA", "MULTIGOGM", "RE", "ST", 276 | "N", "AE", "GM", "GN", "US", "GW", 277 | "GB", "DM", "UC", "TE", "BM", "DO", 278 | "IT", "PB", "PW", "BR", "WR", "PC", 279 | "RU", "TM", "OT", 280 | "BL", "WL", // specific to cgos ? 281 | NULL }; 282 | #define NOT_FOUND 0 283 | 284 | int find(char *string, char *strlist[]) 285 | // Return 0 if string is not found in strlist, i != 0 if it is found at pos i 286 | { 287 | for (int i=1 ; strlist[i] != NULL ; i++) { 288 | if (strcmp(string, strlist[i]) == 0) 289 | return i; 290 | } 291 | return 0; // Not Found 292 | } 293 | 294 | int yylex(void) 295 | { 296 | int c, all_upper=1, all_lower=1, number=0, real=0; 297 | while(isspace(c=fgetc(f))) 298 | if (c == '\n') lineno++; 299 | 300 | if (read_raw_text) { 301 | yyleng = 0; 302 | all_upper=0; 303 | all_lower=0; 304 | while ((c=fgetc(f)) != ']') { 305 | if (c == '\\') 306 | c = fgetc(f); // read \] in comments for example 307 | all_lower = all_lower && islower(c); 308 | all_upper = all_upper && isupper(c); 309 | yytext[yyleng++] = c; 310 | } 311 | } 312 | else { 313 | if (!isalnum(c) && c != '+' && c != '-') 314 | return c; 315 | else { 316 | yyleng = 0; 317 | yytext[yyleng++] = c; 318 | if (isdigit(c) || c == '+' || c == '-') { 319 | all_lower = 0; 320 | all_upper = 0; 321 | number = 1; 322 | while ((c=fgetc(f)) != ']' && c != '[') { 323 | if (!isdigit(c)) { 324 | if (c == '.') { 325 | real = 1; 326 | } 327 | else { 328 | number = 0; 329 | real = 0; 330 | } 331 | } 332 | yytext[yyleng++] = c; 333 | } 334 | } 335 | else { 336 | while ((c=fgetc(f)) != ']' && c != '[') { 337 | all_lower = all_lower && islower(c); 338 | all_upper = all_upper && isupper(c); 339 | yytext[yyleng++] = c; 340 | } 341 | } 342 | } 343 | } 344 | ungetc(c, f); 345 | yytext[yyleng] = 0; 346 | 347 | if (real) return REAL; 348 | if (number) return NUMBER; 349 | 350 | if (all_upper) { 351 | yyval = find(yytext, prop_name); 352 | return UPPERCASE_STRING; 353 | } 354 | else if (all_lower) { 355 | if (yyleng == 2) 356 | return POINT; 357 | else 358 | return LOWERCASE_STRING; 359 | } 360 | return STRING; 361 | } 362 | 363 | char* token_name(int tok) 364 | { 365 | if (tok < 256) { 366 | buf[0] = tok; buf[1] = 0; 367 | } 368 | else if (tok == POINT) 369 | sprintf(buf,"POINT"); 370 | else if (tok == STRING) 371 | sprintf(buf,"STRING"); 372 | else if (tok == UPPERCASE_STRING) 373 | sprintf(buf,"UPPERCASE_STRING"); 374 | else if (tok == LOWERCASE_STRING) 375 | sprintf(buf,"LOWERCASE_STRING"); 376 | else if (tok == NUMBER) 377 | sprintf(buf,"NUMBER"); 378 | else if (tok == REAL) 379 | sprintf(buf,"REAL"); 380 | else 381 | sprintf(buf,"token not known"); 382 | return buf; 383 | } 384 | 385 | void accept(int tok) 386 | { 387 | if (symbol == tok) symbol = yylex(); 388 | else { 389 | fprintf(stderr, "line %d: Syntax Error while reading sgf file\n", 390 | lineno); 391 | fprintf(stderr, "expected symbol: \"%s\"", token_name(tok)); 392 | if (symbol < 256) { 393 | fprintf(stderr, ", read \"%s\"\n",token_name(symbol)); 394 | sprintf(buf, "sgf line %d: expected symbol: \"%s\", read \"%s\"", 395 | lineno, token_name(tok), token_name(symbol)); 396 | log_fmt_s('W', buf, NULL); 397 | } 398 | else { 399 | fprintf(stderr, ", read \"%s\" %s\n",token_name(symbol), yytext); 400 | sprintf(buf, "sgf line %d: expected symbol: \"%s\", read \"%s\" %s" 401 | , lineno, token_name(tok), token_name(symbol), yytext); 402 | log_fmt_s('W', buf, NULL); 403 | } 404 | } 405 | //fprintf(stderr, "%s", token_name(symbol)); 406 | //if (symbol==UPPERCASE_STRING || symbol==STRING 407 | // || symbol==NUMBER || symbol==REAL) 408 | // fprintf(stderr, ": %s", yytext); 409 | //fprintf(stderr, "\n"); 410 | } 411 | 412 | void ValueType(void) 413 | // ValueType = Point | Number | Real | Text | None 414 | { 415 | if (symbol == POINT || symbol == STRING 416 | || symbol == UPPERCASE_STRING || symbol == LOWERCASE_STRING 417 | || symbol == NUMBER || symbol == REAL) { 418 | accept(symbol); 419 | //fprintf(stderr, "%s ", yytext); 420 | } 421 | } 422 | 423 | int set_default_size(Position *pos) { 424 | board_set_size_safe(pos, "19"); // default value in sgf spec 425 | game_clear_board(game); 426 | size_already_set = 1; 427 | return 19; 428 | } 429 | 430 | void PropValue(void) 431 | // PropValue = "[" ValueType "]" 432 | { 433 | Position *pos=game->pos; 434 | 435 | accept('['); 436 | //fprintf(stderr, "%s\n", yytext); 437 | read_raw_text = 0; // reset after the raw text has been read (lookahead) 438 | ValueType(); 439 | 440 | // Property Value was read : use it to modify the game 441 | int size = board_size(pos); 442 | if (board_nmoves(pos) < nmoves-1 443 | && strcmp(prop_name[prop], "B") == 0) { 444 | if (!size_already_set) 445 | size = set_default_size(pos); 446 | if (yytext[0] != 'B') 447 | do_play(game, BLACK, parse_sgf_coord(yytext, size)); 448 | else 449 | do_play(game, BLACK, PASS_MOVE); 450 | } 451 | else if (board_nmoves(pos) < nmoves-1 452 | && strcmp(prop_name[prop], "W") == 0) { 453 | if (!size_already_set) 454 | size = set_default_size(pos); 455 | if (yytext[0] != 'W') 456 | do_play(game, WHITE, parse_sgf_coord(yytext, size)); 457 | else 458 | do_play(game, WHITE, PASS_MOVE); 459 | } 460 | else if (strcmp(prop_name[prop], "AB") == 0) { 461 | if (!size_already_set) 462 | size = set_default_size(pos); 463 | Point pt = parse_sgf_coord(yytext, size); 464 | slist_push(game->placed_black_stones, pt); 465 | board_place_stone(pos, pt, BLACK); 466 | } 467 | else if (strcmp(prop_name[prop], "AW") == 0) { 468 | if (!size_already_set) 469 | size = set_default_size(pos); 470 | Point pt = parse_sgf_coord(yytext, size); 471 | slist_push(game->placed_white_stones, pt); 472 | board_place_stone(pos, pt, WHITE); 473 | } 474 | else if (strcmp(prop_name[prop], "KM") == 0) 475 | game_set_komi(game, atof(yytext)); 476 | else if (strcmp(prop_name[prop], "SZ") == 0) { 477 | if (is_game_board_empty(game)) { 478 | board_set_size_safe(pos, yytext); 479 | game_clear_board(game); 480 | } 481 | else { 482 | // Can happen if SZ occurs after AB or AW 483 | Point bstones[BOARDSIZE], wstones[BOARDSIZE]; 484 | 485 | slist_clear(bstones); slist_clear(wstones); 486 | slist_append(bstones, game->placed_black_stones); 487 | slist_append(wstones, game->placed_white_stones); 488 | 489 | board_set_size_safe(pos, yytext); 490 | game_clear_board(game); 491 | 492 | FORALL_IN_SLIST(bstones, pt) { 493 | board_set_color_to_play(pos, BLACK); 494 | play_move(pos, pt); 495 | slist_push(game->placed_black_stones, pt); 496 | } 497 | FORALL_IN_SLIST(wstones, pt) { 498 | board_set_color_to_play(pos, WHITE); 499 | play_move(pos, pt); 500 | slist_push(game->placed_white_stones, pt); 501 | } 502 | } 503 | size_already_set = 1; 504 | } 505 | else if (strcmp(prop_name[prop], "HA") == 0) 506 | game->handicap = atoi(yytext); 507 | else if (strcmp(prop_name[prop], "OT") == 0) 508 | strncpy(game->gi.overtime,yytext,31); 509 | else if (strcmp(prop_name[prop], "PL") == 0) { 510 | if (strcmp(yytext, "B") == 0) 511 | board_set_color_to_play(pos, BLACK); 512 | else 513 | board_set_color_to_play(pos, WHITE); 514 | } 515 | else if (strcmp(prop_name[prop], "PW") == 0) 516 | strncpy(game->gi.name[WHITE&1],yytext,31); 517 | else if (strcmp(prop_name[prop], "PB") == 0) 518 | strncpy(game->gi.name[BLACK&1],yytext,31); 519 | else if (strcmp(prop_name[prop], "WR") == 0) 520 | strncpy(game->gi.rank[WHITE&1],yytext,3); 521 | else if (strcmp(prop_name[prop], "BR") == 0) 522 | strncpy(game->gi.rank[BLACK&1],yytext,3); 523 | else if (strcmp(prop_name[prop], "RE") == 0) 524 | strncpy(game->gi.result,yytext,31); 525 | else if (strcmp(prop_name[prop], "RU") == 0) 526 | strncpy(game->gi.rules,yytext,31); 527 | else if (strcmp(prop_name[prop], "TM") == 0) 528 | game->time_init = atoi(yytext); 529 | 530 | accept(']'); 531 | } 532 | 533 | void PropValues(void) 534 | // Propvalues = { PropValue } 535 | { 536 | if (symbol == '[') { 537 | PropValue(); 538 | if (sgferr[0] !=0) return; // early abort 539 | PropValues(); 540 | } 541 | } 542 | 543 | void PropIdent(void) 544 | { 545 | int will_read_raw_text = 0; 546 | if (symbol == UPPERCASE_STRING) { 547 | prop = yyval; 548 | if (strcmp(yytext,"C")==0 || strcmp(yytext,"PC")==0) 549 | will_read_raw_text = 1; // raw text after '[' 550 | //if (yyval > 0 ) 551 | // fprintf(stderr, "prop %s: ", yytext); 552 | //else 553 | // fprintf(stderr, "unknown %s: ", yytext); 554 | if (yyval <=0 ) 555 | log_fmt_s('W',"read unknown property %s in sgf file", yytext); 556 | accept(UPPERCASE_STRING); 557 | read_raw_text = will_read_raw_text; // will be reset by PropValue 558 | } 559 | } 560 | 561 | void Property(void) 562 | // Property = PropIdent { PropValue } 563 | { 564 | PropIdent(); PropValue(); 565 | if (sgferr[0] !=0) return; // early abort 566 | PropValues(); 567 | //fprintf(stderr, "\n"); 568 | } 569 | 570 | void Properties(void) 571 | // Properties = { Property } 572 | { 573 | if (symbol == UPPERCASE_STRING) { 574 | Property(); 575 | if (sgferr[0] !=0) return; // early abort 576 | Properties(); 577 | } 578 | } 579 | 580 | void Node(void) 581 | // Node = ";" Properties 582 | { 583 | accept(';'); Properties(); 584 | } 585 | 586 | void RestOfSequence(void) 587 | // RestOfSequence = Node RestOfSequence | None 588 | { 589 | if (symbol == ';') { 590 | Node(); 591 | if (sgferr[0] !=0) return; // early abort 592 | RestOfSequence(); 593 | } 594 | } 595 | 596 | void Sequence(void) 597 | // Sequence = Node RestOfSequence 598 | { 599 | Node(); 600 | if (sgferr[0] !=0) return; // early abort 601 | RestOfSequence(); 602 | } 603 | 604 | void RestOfCollection(void); 605 | void GameTree(void) 606 | // GameTree = "(" Sequence RestOfCollection ")" 607 | { 608 | accept('('); 609 | Sequence(); 610 | if (sgferr[0] !=0) 611 | return; // early abort 612 | RestOfCollection(); 613 | accept(')'); 614 | } 615 | 616 | void RestOfCollection(void) 617 | // RestOfCollection = GameTree RestOfCollection | None 618 | { 619 | if (symbol == '(') { 620 | GameTree(); 621 | if (sgferr[0] !=0) return; // early abort 622 | RestOfCollection(); 623 | } 624 | } 625 | 626 | void Collection(void) 627 | // Collection = GameTree RestOfCollection 628 | { 629 | GameTree(); 630 | if (sgferr[0] !=0) return; // early abort 631 | RestOfCollection(); 632 | } 633 | 634 | void check_sgf_validity(const char *filename) 635 | { 636 | char c1=fgetc(f), c2=fgetc(f); 637 | if (c1 != '(' || c2 != ';') 638 | log_fmt_s('E', "File %s does not seem to be a SGF file", filename); 639 | ungetc(c2, f); ungetc(c1, f); 640 | } 641 | 642 | char *loadsgf(Game *sgf_game, const char *filename, int sgf_nmoves) 643 | // Load a position from the SGF file filename using a recursive parser for the 644 | // SGF grammar which is described above. See ref [1]. 645 | // 646 | // The modifications of the game struct is done in ProPValue() 647 | // 648 | // Note: the registred game is supposed to be a legal game (no check) 649 | { 650 | f = fopen(filename, "r"); // File shared between all the routines 651 | game = sgf_game; // Game in which all the actions take place 652 | nmoves = sgf_nmoves; // Number of moves loaded from sgf file 653 | 654 | game_clear_board(game); 655 | sgferr[0] = 0; 656 | lineno = 1; 657 | buf[0] = 0; 658 | size_already_set = 0; 659 | if (f != NULL) { 660 | check_sgf_validity(filename); 661 | symbol = yylex(); 662 | Collection(); 663 | assert(symbol == '\n' || symbol == EOF); 664 | if (sgferr[0]!=0) 665 | sprintf(buf, "%s", sgferr); 666 | else { 667 | if (board_color_to_play(game->pos) == BLACK) 668 | strcpy(buf,"B"); 669 | else 670 | strcpy(buf,"W"); 671 | } 672 | fclose(f); 673 | } 674 | else { 675 | sprintf(buf, "Error - can't open file %s", filename); 676 | } 677 | return buf; 678 | } 679 | -------------------------------------------------------------------------------- /tests/board.tst: -------------------------------------------------------------------------------- 1 | #----------------------------------- 2 | # tests of the michi board subsystem 3 | #----------------------------------- 4 | 5 | boardsize 13 6 | 7 | # initial board 8 | # ------------- 9 | 10 debug blocks_OK c4 10 | #? [true] 11 | 20 debug blocks_OK a2 12 | #? [true] 13 | 30 debug blocks_OK a13 14 | #? [true] 15 | 16 | # block creation 17 | # -------------- 18 | play b c4 19 | 110 debug blocks_OK c4 20 | #? [true] 21 | play w d4 22 | 120 debug blocks_OK d4 23 | #? [true] 24 | play w a1 25 | 130 debug blocks_OK d4 26 | #? [true] 27 | play b a5 28 | 140 debug blocks_OK d4 29 | #? [true] 30 | 31 | # block extension 32 | # --------------- 33 | clear_board 34 | debug setpos c4 d4 35 | play b c5 36 | 210 debug blocks_OK c5 37 | #? [true] 38 | debug setpos a1 b1 pass b2 39 | play w a2 40 | 220 debug blocks_OK a2 41 | #? [true] 42 | play b a4 43 | play w a3 44 | 230 debug blocks_OK a3 45 | #? [true] 46 | 47 | # block merge 48 | # ----------- 49 | clear_board 50 | debug setpos c4 pass c6 pass 51 | play b c5 52 | 310 debug blocks_OK c5 53 | #? [true] 54 | debug setpos f1 pass h1 pass g2 pass 55 | play w g1 56 | 320 debug blocks_OK g1 57 | #? [true] 58 | debug setpos e5 pass f5 pass f4 pass e3 pass 59 | play b e4 60 | 330 debug blocks_OK e4 61 | #? [true] 62 | 63 | # block capture 64 | # ------------- 65 | clear_board 66 | debug setpos a2 a1 67 | play b b1 68 | 410 debug blocks_OK a1 69 | #? [true] 70 | debug setpos e5 e6 f6 f5 e7 f7 71 | play w d6 72 | 420 debug blocks_OK d6 73 | #? [true] 74 | debug setpos g6 pass e4 pass d5 pass 75 | play b e6 76 | 430 debug blocks_OK e6 77 | #? [true] 78 | debug setpos a8 a9 b8 b9 c9 c10 a11 b11 b10 79 | play b a10 80 | 440 debug blocks_OK a10 81 | #? [true] 82 | play w b10 83 | 450 debug blocks_OK b10 84 | #? [true] 85 | 86 | # test suicide and ko 87 | # ------------------- 88 | clear_board 89 | debug setpos e5 pass f6 f5 e7 f7 d6 90 | play w e6 91 | 510 debug blocks_OK e6 92 | #? [true] 93 | debug setpos g6 94 | play w e6 95 | 520 debug blocks_OK e6 96 | #? [true] 97 | 530 debug pos ko 98 | #? [F6] 99 | 100 | # Query block data 101 | # ---------------- 102 | clear_board 103 | debug setpos a1 e1 c4 104 | 610 debug libs 1 105 | #? [A2 B1] 106 | 620 debug libs 2 107 | #? [E2 D1 F1] 108 | 630 debug libs 3 109 | #? [C5 B4 D4 C3] 110 | -------------------------------------------------------------------------------- /tests/confs/config_1.gtp: -------------------------------------------------------------------------------- 1 | param_tree N_SIMS 1 2 | -------------------------------------------------------------------------------- /tests/confs/config_20000.gtp: -------------------------------------------------------------------------------- 1 | param_tree N_SIMS 20000 2 | -------------------------------------------------------------------------------- /tests/fix_atari.tst: -------------------------------------------------------------------------------- 1 | #------------------------------------- 2 | # tests of the michi fix_atari routine 3 | #------------------------------------- 4 | 5 | boardsize 13 6 | 7 | # escape 8 | # ------ 9 | debug setpos C8 C9 E9 B8 F9 D8 10 | 10 debug fix_atari C8 11 | #? [1 C7] 12 | 13 | debug setpos C1 G7 B2 B1 14 | 20 debug fix_atari B1 15 | #? [1 A1] 16 | 17 | play b e5 18 | 30 debug fix_atari B1 19 | #? [1] 20 | 21 | # counter capture 22 | # --------------- 23 | clear_board 24 | debug setpos A1 E5 B2 A2 25 | 110 debug fix_atari A1 26 | #? [1 A3 B1] 27 | 28 | # shicho (ladder) 29 | # --------------- 30 | clear_board 31 | debug setpos A1 A2 32 | 210 debug fix_atari A1 33 | #? [1] 34 | 35 | debug setpos G1 36 | 220 debug fix_atari A1 37 | #? [1 B1] 38 | 39 | debug setpos D2 40 | 230 debug fix_atari A1 41 | #? [1] 42 | 43 | clear_board 44 | debug setpos G5 F5 A1 G4 A2 H4 A3 G6 H5 45 | 240 debug fix_atari G5 46 | #? [0 J5] 47 | 48 | clear_board 49 | debug setpos E5 D5 A1 E4 A2 F4 A3 E6 F5 50 | 250 debug fix_atari E5 51 | #? [0 G5] 52 | 53 | clear_board 54 | debug setpos D3 F3 E3 G3 F2 E2 G2 H2 D2 55 | 260 debug fix_atari E2 56 | #? [1] 57 | 58 | # Geta (net) 59 | # ---------- 60 | clear_board 61 | debug setpos G7 G8 A1 F8 A2 E8 A3 H7 A4 H6 A5 G6 A6 E6 F7 62 | 310 debug fix_atari G7 63 | #? [0]* 64 | 65 | debug setpos A9 D7 66 | 320 debug fix_atari G7 67 | #? [0 E7] 68 | 69 | play w A8 70 | 330 debug fix_atari G7 71 | #? [0 E7]* 72 | 73 | # 2 libs semeai 74 | # ------------- 75 | clear_board 76 | debug setpos D3 F3 E3 G3 F2 E2 G2 H2 77 | 410 debug fix_atari E2 78 | #? [0 E1|0 D2]* 79 | -------------------------------------------------------------------------------- /tests/large_pat.tst: -------------------------------------------------------------------------------- 1 | #------------------------------------------- 2 | # tests of the michi large patterns routines 3 | #------------------------------------------- 4 | 5 | boardsize 13 6 | 7 | # size 5 center pattern 8 | # --------------------- 9 | debug setpos D6 E6 D5 E5 D4 E3 F6 A1 F5 A2 F4 A3 10 | 10 debug match_pat E4 11 | #? [410926] 12 | 13 | # size 4 side pattern 14 | # ------------------- 15 | clear_board 16 | debug setpos D1 D2 D3 C2 E3 F3 E4 F1 17 | 20 debug match_pat E2 18 | #? [923280] 19 | 20 | # Idem with 90 deg rotation 21 | # ------------------------- 22 | clear_board 23 | debug setpos A5 B5 C5 B6 C4 C3 D4 A3 24 | 30 debug match_pat B4 25 | #? [923280] 26 | 27 | # Idem with 180 deg rotation 28 | # -------------------------- 29 | clear_board 30 | debug setpos F13 F12 F11 G12 E11 D11 E10 D13 31 | 40 debug match_pat E12 32 | #? [923280] 33 | 34 | # Idem with 270 deg rotation 35 | # -------------------------- 36 | clear_board 37 | debug setpos N8 M8 L8 M7 L9 L10 K9 N10 38 | 50 debug match_pat M9 39 | #? [923280] 40 | 41 | # Idem with vertical flip 42 | # ----------------------- 43 | clear_board 44 | debug setpos J1 J2 J3 K2 H3 G3 H4 G1 45 | 60 debug match_pat H2 46 | #? [923280] 47 | 48 | # Large pattern in the corner 49 | # --------------------------- 50 | clear_board 51 | debug setpos B2 A2 C3 B3 D3 C2 D2 C4 E2 D4 F2 E4 F3 F4 F1 E3 G2 G3 52 | 70 debug match_pat B1 53 | #? [125951] 54 | -------------------------------------------------------------------------------- /tests/patterns.prob: -------------------------------------------------------------------------------- 1 | 1.000 2 2 (s:410926) 2 | 1.000 2 2 (s:923280) 3 | 0.026 8 309 (s:125951) 4 | -------------------------------------------------------------------------------- /tests/patterns.spat: -------------------------------------------------------------------------------- 1 | # Pachi spatial patterns dictionary v1.0 maxdist 14 2 | # Point order: d=0 3 | # Point order: d=1 0,0 4 | # Point order: d=2 0,1 0,-1 1,0 -1,0 5 | # Point order: d=3 1,1 -1,1 1,-1 -1,-1 6 | # Point order: d=4 0,2 0,-2 2,0 -2,0 7 | # Point order: d=5 1,2 -1,2 1,-2 -1,-2 2,1 -2,1 2,-1 -2,-1 8 | # Point order: d=6 0,3 0,-3 2,2 -2,2 2,-2 -2,-2 3,0 -3,0 9 | # Point order: d=7 1,3 -1,3 1,-3 -1,-3 3,1 -3,1 3,-1 -3,-1 10 | # Point order: d=8 0,4 0,-4 2,3 -2,3 2,-3 -2,-3 3,2 -3,2 3,-2 -3,-2 4,0 -4,0 11 | # Point order: d=9 1,4 -1,4 1,-4 -1,-4 3,3 -3,3 3,-3 -3,-3 4,1 -4,1 4,-1 -4,-1 12 | # Point order: d=10 0,5 0,-5 2,4 -2,4 2,-4 -2,-4 4,2 -4,2 4,-2 -4,-2 5,0 -5,0 13 | # Point order: d=11 1,5 -1,5 1,-5 -1,-5 3,4 -3,4 3,-4 -3,-4 4,3 -4,3 4,-3 -4,-3 5,1 -5,1 5,-1 -5,-1 14 | # Point order: d=12 0,6 0,-6 2,5 -2,5 2,-5 -2,-5 4,4 -4,4 4,-4 -4,-4 5,2 -5,2 5,-2 -5,-2 6,0 -6,0 15 | # Point order: d=13 1,6 -1,6 1,-6 -1,-6 3,5 -3,5 3,-5 -3,-5 5,3 -5,3 5,-3 -5,-3 6,1 -6,1 6,-1 -6,-1 16 | # Point order: d=14 0,7 0,-7 2,6 -2,6 2,-6 -2,-6 4,5 -4,5 4,-5 -4,-5 5,4 -5,4 5,-4 -5,-4 6,2 -6,2 6,-2 -6,-2 7,0 -7,0 17 | 410926 5 .OOXXXX..O...XX...... bd31fe8 fad3be8 bd31fe8 fad3be8 1c0bcac8 15deb048 1c0bcac8 15deb048 18 | 923280 4 .X..OOXOXX#.O 2ef42615 3e703fd5 242f8065 34ab99a5 3eb53b3d 2e37369d 39c4475d 29464afd 19 | 125951 14 .X#..OO##O#.#X.##X###.#X###.#O.##X###.#O###O###X#..##O###X###.#.###X###.#..##.###O###X###.#.###.###O###.#..##.###.###.###.#.###.###.###.###.# 2da5cdfb 363b4632 3f1b04bb 24262172 18c4d1f2 251704db 3d19be3b 20 | -------------------------------------------------------------------------------- /tests/ref/mcdebug.txt: -------------------------------------------------------------------------------- 1 | Move: 0 Black: 0 caps White: 0 caps Komi: 7.5 2 | 19 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 | 18 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 | 17 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 | 16 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 | 15 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 | 14 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 | 13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 | 12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 | 11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 | 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 | 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 | 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 | 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 | 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 | 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 | 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 | 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 | 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 | 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 | A B C D E F G H J K L M N O P Q R S T 22 | 23 | Move: 458 Black: 84 caps White: 71 caps Komi: 7.5 24 | 19 O O O O O . O . O X X X . X X X X X O 25 | 18 O . O O . O O O O O X X X X . X O O O 26 | 17 . O O . O . O O X X X X X X X X O . O 27 | 16 O O . O . O O X X . X X X X X . X O O 28 | 15 . O O . O O O X X X X X . X X X X O . 29 | 14 O O O O O X X . X X . X X X . X X O O 30 | 13 O O O O O O X X . X X X . X X X X X X 31 | 12 O O O O O O X . X X X X X . X X X X X 32 | 11 O O . O O O X X X . X X . X . X . X X 33 | 10 O . O . O . O O X X X X X X X X X X X 34 | 9 . O O O O O O X . X X . X O X . X O X 35 | 8 O O O . O . O X X . X X X O O X X O O 36 | 7 O . O O O O O X . X . X . X O O X O O 37 | 6 . O O O O O X . X X X X X X X O O O . 38 | 5 O O O O . O X X X X . X X X O O O . O 39 | 4 O O O O O O X . X . X X X X O O . O O 40 | 3 O O O X X X X X X X X . X X X O O O O 41 | 2 . O O O X X X X X X X X X X O O O . O 42 | 1 O O . O O X X X X X . X X O O O . O . 43 | A B C D E F G H J K L M N O P Q R S T 44 | 45 | mcplayout 0 2930833534 3.5 46 | -------------------------------------------------------------------------------- /tests/ref/tsdebug.txt: -------------------------------------------------------------------------------- 1 | Move: 0 Black: 0 caps White: 0 caps Komi: 7.5 2 | 19 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 | 18 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 | 17 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 | 16 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 | 15 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 | 14 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 | 13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 | 12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 | 11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 | 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 | 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 | 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 | 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 | 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 | 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 | 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 | 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 | 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 | 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 | A B C D E F G H J K L M N O P Q R S T 22 | 23 | +- pass 0.519 ( 10373/20000 , prior 5/10 , rave 0/0 = nan, urgency 0.519) 24 | +- E7 0.473 ( 219/463 , prior 5/10 , rave 4868/9938 =0.490, urgency 0.487) 25 | +- F14 0.486 ( 839/1725 , prior 5/10 , rave 5228/10711 =0.488, urgency 0.487) 26 | +- G3 0.471 ( 372/790 , prior 15/20 , rave 4896/10157 =0.482, urgency 0.481) 27 | +- R12 0.488 ( 5040/10328 , prior 15/20 , rave 7271/15050 =0.483, urgency 0.487) 28 | +- R8 0.486 ( 291/599 , prior 15/20 , rave 4785/9878 =0.484, urgency 0.486) 29 | +- L11 0.487 ( 782/1607 , prior 5/10 , rave 5110/10497 =0.487, urgency 0.487) 30 | +- K3 0.474 ( 259/546 , prior 15/20 , rave 4902/10084 =0.486, urgency 0.486) 31 | +- D14 0.481 ( 386/802 , prior 5/10 , rave 4881/10124 =0.482, urgency 0.482) 32 | +- C13 0.479 ( 338/705 , prior 15/20 , rave 4843/10002 =0.484, urgency 0.485) 33 | [20000] winrate 0.488 | seq R12 F12 F13 G12 E12| can R12(0.488) F14(0.486) L11(0.487) D14(0.481) G3(0.471) 34 | move = R12 35 | Move: 1 Black: 0 caps White: 0 caps Komi: 7.5 36 | 19 . . . . . . . . . . . . . . . . . . . 37 | 18 . . . . . . . . . . . . . . . . . . . 38 | 17 . . . . . . . . . . . . . . . . . . . 39 | 16 . . . . . . . . . . . . . . . . . . . 40 | 15 . . . . . . . . . . . . . . . . . . . 41 | 14 . . . . . . . . . . . . . . . . . . . 42 | 13 . . . . . . . . . . . . . . . . . . . 43 | 12 . . . . . . . . . . . . . . . .(X). . 44 | 11 . . . . . . . . . . . . . . . . . . . 45 | 10 . . . . . . . . . . . . . . . . . . . 46 | 9 . . . . . . . . . . . . . . . . . . . 47 | 8 . . . . . . . . . . . . . . . . . . . 48 | 7 . . . . . . . . . . . . . . . . . . . 49 | 6 . . . . . . . . . . . . . . . . . . . 50 | 5 . . . . . . . . . . . . . . . . . . . 51 | 4 . . . . . . . . . . . . . . . . . . . 52 | 3 . . . . . . . . . . . . . . . . . . . 53 | 2 . . . . . . . . . . . . . . . . . . . 54 | 1 . . . . . . . . . . . . . . . . . . . 55 | A B C D E F G H J K L M N O P Q R S T 56 | 57 | -------------------------------------------------------------------------------- /tests/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$GOGUI" == "" ]; then 4 | export GOGUI="$HOME/prg/go/gogui-1.4.9/bin" 5 | fi 6 | 7 | export GOGUI_REGRESS="$GOGUI/gogui-regress" 8 | $GOGUI_REGRESS "./michi gtp" -output tests/output $1 tests/fix_atari.tst 9 | $GOGUI_REGRESS "./michi gtp" -output tests/output $1 tests/large_pat.tst 10 | $GOGUI_REGRESS "./michi gtp" -output tests/output $1 tests/board.tst 11 | $GOGUI_REGRESS "./michi gtp" -output tests/output $1 tests/undo.tst 12 | $GOGUI_REGRESS "./michi gtp" -output tests/output $1 tests/tsumego.tst 13 | -------------------------------------------------------------------------------- /tests/run_michi: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p tests/tst 4 | ./michi mcdebug tests/confs/config_1.gtp >& tests/tst/mcdebug.txt 5 | ./michi tsdebug tests/confs/config_20000.gtp >& tests/tst/tsdebug.txt 6 | echo "Michi -- (simple) regression test OK if nothing appears after this line" 7 | diff --brief tests/ref tests/tst 8 | -------------------------------------------------------------------------------- /tests/sgf/tsumego/ch2_ex2.sgf: -------------------------------------------------------------------------------- 1 | (;FF[4]CA[UTF-8]AP[GoGui:1.4.9]SZ[9] 2 | KM[0]DT[2017-01-26] 3 | AB[he][gd][fe][ff][ef][eg][eh][fa][fb][gb][hb][ib][ha][ec][ed][gc][id] 4 | AW[fh][fg][gf][hg][ba][ab][bb][cb][db][da][bc][bd][be][bf][bg][bh][bi][dc][dd][de][df][dg][ch] 5 | C["Tsumego", Motoki Noguchi, Praxeo 2009 6 | 7 | exemple 2, p 14] 8 | PL[B]) 9 | -------------------------------------------------------------------------------- /tests/sgf/tsumego/ch2_ex2_simpler.sgf: -------------------------------------------------------------------------------- 1 | (;FF[4]CA[UTF-8]AP[GoGui:1.4.9]SZ[9] 2 | KM[0]DT[2017-01-26] 3 | AB[he][gd][fe][ff][ef][eg][eh][fa][fb][gb][hb][ib][ha][ec][ed][gc][id] 4 | AW[fh][fg][gf][hg][ba][ab][bb][cb][db][da][bc][bd][be][bf][bg][bh][bi][dc][dd][de][df][dg][ch] 5 | C["Tsumego", Motoki Noguchi, Praxeo 2009 6 | 7 | exemple 2, p 14] 8 | PL[B];B[hc];W[ad];B[fc];W[af];B[ge];W[ah];B[ea];W[cd];B[eb];W[cf] 9 | ;B[ee]) 10 | -------------------------------------------------------------------------------- /tests/sgf/tsumego/ch2_p10.sgf: -------------------------------------------------------------------------------- 1 | (;FF[4]CA[UTF-8]AP[GoGui:1.4.9]SZ[9] 2 | KM[0]DT[2017-01-27]RE[W+4.0] 3 | AB[bd][cd][dd][ec][fc][gc][gb][fa][fi][fh][gh][hh][ih][hi][fg][ff][gf][ge][gd][ed][ee][hg][ig] 4 | AW[bc][cc][dc][eb][fb][da][ah][bh][ch][dh][bi][df][cf][bf][af][hb][hc][hd][ib][he][hf][if][id][ef][eg][eh][ei] 5 | C[chapitre 2, p 10]PL[W]) 6 | -------------------------------------------------------------------------------- /tests/sgf/tsumego/simple.sgf: -------------------------------------------------------------------------------- 1 | (;FF[4]CA[UTF-8]AP[GoGui:1.4.9]SZ[9] 2 | KM[0]DT[2016-12-28] 3 | ;B[ah];W[ab];B[bh];W[bb];B[ch];W[cb];B[dh];W[db];B[eh];W[eb] 4 | ;B[fh];W[fb];B[gh];W[gb];B[hh];W[hb];B[ih];W[ib];B[bi];W[ba] 5 | ;B[di];W[da];B[fi];W[fa];B[hi];W[ha];B[cd];W[ac];B[dd];W[bc] 6 | ;B[ed];W[cc];B[fd];W[dc];B[gd];W[ec];B[ge];W[fc];B[gf];W[gc] 7 | ;B[ff];W[hc];B[ef];W[ic];B[df];W[id];B[cf];W[ie];B[ce];W[if] 8 | ;B[bd];W[ig];B[be];W[hg];B[bf];W[gg];B[ad];W[fg];B[ae];W[eg] 9 | ;B[af];W[dg];B[hd];W[cg];B[he];W[bg];B[hf];W[ag]) 10 | -------------------------------------------------------------------------------- /tests/sgf/tsumego/simple_4spaces.sgf: -------------------------------------------------------------------------------- 1 | (;FF[4]CA[UTF-8]AP[GoGui:1.4.9]SZ[9] 2 | KM[0]DT[2016-12-29]RE[B+5] 3 | ;B[ah];W[ag];B[bh];W[bg];B[ch];W[cg];B[dh];W[dg];B[eh];W[eg] 4 | ;B[ei];W[fg];B[ia];W[fh];B[ib];W[fi];B[hb];W[gh];B[gb];W[hh] 5 | ;B[fb];W[ih];B[eb];W[hi];B[ea];W[gg];B[ga];W[hg];B[da];W[ig] 6 | ;B[db];W[if];B[cb];W[hf];B[ca];W[ge];B[ba];W[fe];B[bb];W[ee] 7 | ;B[aa];W[de];B[ab];W[ce];B[ac];W[be];B[bc];W[ae];B[cc];W[bf] 8 | ;B[dc];W[df];B[ec];W[ff];B[fc];W[hd];B[gc];W[id];B[hc];W[gd] 9 | ;B[ic];W[fd];B[ad];W[he];B[bd];W[ie];B[cd];W[ed];B[dd]) 10 | -------------------------------------------------------------------------------- /tests/sgf/tsumego/simple_seki.sgf: -------------------------------------------------------------------------------- 1 | (;FF[4]CA[UTF-8]AP[GoGui:1.4.9]SZ[9] 2 | KM[0]DT[2016-12-29] 3 | ;B[ei];W[di];B[eh];W[dh];B[eg];W[dg];B[ci];W[df];B[ch];W[ef] 4 | ;B[cg];W[ff];B[cf];W[gf];B[ce];W[gg];B[de];W[gh];B[ee];W[gi] 5 | ;B[fe];W[fh];B[ge];W[ab];B[he];W[bb];B[hf];W[cb];B[hg];W[db] 6 | ;B[hh];W[eb];B[hi];W[fb];B[ih];W[gb];B[if];W[hb];B[ie];W[ib] 7 | ;B[bi];W[ha];B[ah];W[fa];B[bg];W[da];B[af];W[ba];B[be];W[ad] 8 | ;B[ai];W[bd];B[bh];W[cd];B[ag];W[dd];B[bf];W[ed];B[ae];W[bc] 9 | ;B[hd];W[dc];B[hc];W[ec];B[ic];W[fd];B[gd];W[fc];B[gc]) 10 | -------------------------------------------------------------------------------- /tests/sgf/tsumego/simple_white.sgf: -------------------------------------------------------------------------------- 1 | (;FF[4]CA[UTF-8]AP[GoGui:1.4.9]SZ[9] 2 | KM[0]DT[2016-12-28] 3 | ;W[ah];B[ab];W[bh];B[bb];W[ch];B[cb];W[dh];B[db];W[eh];B[eb] 4 | ;W[fh];B[fb];W[gh];B[gb];W[hh];B[hb];W[ih];B[ib];W[bi];B[ba] 5 | ;W[di];B[da];W[fi];B[fa];W[hi];B[ha];W[cd];B[ac];W[dd];B[bc] 6 | ;W[ed];B[cc];W[fd];B[dc];W[gd];B[ec];W[ge];B[fc];W[gf];B[gc] 7 | ;W[ff];B[hc];W[ef];B[ic];W[df];B[id];W[cf];B[ie];W[ce];B[if] 8 | ;W[bd];B[ig];W[be];B[hg];W[bf];B[gg];W[ad];B[fg];W[ae];B[eg] 9 | ;W[af];B[dg];W[hd];B[cg];W[he];B[bg];W[hf];B[ag]) 10 | -------------------------------------------------------------------------------- /tests/sgf1.sgf: -------------------------------------------------------------------------------- 1 | (;FF[4]CA[UTF-8]AP[GoGui:1.4.9]SZ[13] 2 | KM[7.5]DT[2015-08-22] 3 | AB[dj][dd][jd][jj]PL[W];B[fk];W[ch];B[gd];W[jg]) 4 | -------------------------------------------------------------------------------- /tests/tsumego.tst: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------- 2 | # tests of the bgo mcts subsystem (testing answer to tsumegos) 3 | #------------------------------------------------------------- 4 | 5 | param_general play_until_the_end 1 6 | 7 | # Simple tests 8 | # ------------ 9 | loadsgf sgf/tsumego/simple.sgf 10 | 11 | 010 genmove b 12 | #? [E5] 13 | 14 | loadsgf sgf/tsumego/simple.sgf 15 | 16 | play b pass 17 | 015 genmove w 18 | #? [E5] 19 | 20 | loadsgf sgf/tsumego/simple_white.sgf 21 | 22 | 020 genmove w 23 | #? [E5] 24 | 25 | loadsgf sgf/tsumego/simple_white.sgf 26 | play w pass 27 | 025 genmove b 28 | #? [E5] 29 | 30 | loadsgf sgf/tsumego/simple_4spaces.sgf 31 | 32 | 030 genmove w 33 | #? [resign] 34 | 35 | play w pass 36 | 033 genmove b 37 | #? [B1|C1] 38 | 39 | loadsgf sgf/tsumego/simple_4spaces.sgf 40 | play b C1 41 | 036 genmove W 42 | #? [resign] 43 | 44 | # Seki detection 45 | # -------------- 46 | loadsgf sgf/tsumego/simple_seki.sgf 47 | 040 genmove w 48 | #? [pass] 49 | 50 | loadsgf sgf/tsumego/simple_seki.sgf 51 | play w pass 52 | 045 genmove b 53 | #? [pass|resign] 54 | -------------------------------------------------------------------------------- /tests/undo.tst: -------------------------------------------------------------------------------- 1 | #------------------------------------------- 2 | # tests of undo in the michi board subsystem 3 | #------------------------------------------- 4 | 5 | boardsize 13 6 | 7 | # ------------------- 8 | # undo block creation 9 | # ------------------- 10 | play b c4 11 | undo 12 | 110 debug blocks_OK c4 13 | #? [true] 14 | play w d4 15 | 120 debug blocks_OK d4 16 | #? [true] 17 | play w a1 18 | 130 debug blocks_OK d4 19 | #? [true] 20 | play b a5 21 | 140 debug blocks_OK d4 22 | #? [true] 23 | 24 | # -------------------- 25 | # undo block extension 26 | # -------------------- 27 | clear_board 28 | debug setpos c4 d4 29 | play b c5 30 | undo 31 | 210 debug blocks_OK c5 32 | #? [true] 33 | debug setpos a1 b1 pass b2 34 | play b a2 35 | undo 36 | 220 debug blocks_OK a2 37 | #? [true] 38 | debug setpos n2 m1 m2 39 | play w n1 40 | undo 41 | 230 debug blocks_OK n1 42 | #? [true] 43 | 44 | # ---------------- 45 | # undo block merge 46 | # ---------------- 47 | debug setpos k1 a3 48 | play w l1 49 | undo 50 | 310 debug blocks_OK l1 51 | #? [true] 52 | play w e5 53 | play b a2 54 | undo 55 | 320 debug blocks_OK a2 56 | #? [true] 57 | debug setpos a9 f4 a8 e3 a7 58 | play w e4 59 | undo 60 | 320 debug blocks_OK e4 61 | #? [true] 62 | 63 | # ------------------ 64 | # undo block capture 65 | # ------------------ 66 | clear_board 67 | debug setpos b1 a1 68 | play b a2 69 | undo 70 | 410 debug blocks_OK a2 71 | #? [true] 72 | debug setpos n1 n2 m1 m2 l1 l2 k1 k2 pass 73 | play w j1 74 | undo 75 | 420 debug blocks_OK j1 76 | #? [true] 77 | debug setpos b2 pass a3 78 | play b a2 79 | 430 debug pos ko 80 | #? [A1] 81 | undo 82 | 431 debug blocks_OK a2 83 | #? [true] 84 | 432 debug pos ko 85 | #? [] 86 | debug setpos a2 pass pass a1 87 | 440 debug pos ko 88 | #? [A2] 89 | play b a9 90 | undo 91 | 441 debug blocks_OK a1 92 | #? [true] 93 | 442 debug pos ko 94 | #? [A2] 95 | play b pass 96 | undo 97 | 450 debug blocks_OK a1 98 | #? [true] 99 | 100 | # --------------------------- 101 | # Multiple undo in succession 102 | # --------------------------- 103 | 104 | clear_board 105 | debug setpos b1 a1 a9 106 | 107 | 510 debug pos last 108 | #? [A9] 109 | 511 debug pos last2 110 | #? [A1] 111 | 512 debug pos last3 112 | #? [B1] 113 | 513 debug pos ko 114 | #? [] 115 | 116 | undo 117 | 520 debug pos last 118 | #? [A1] 119 | 521 debug pos last2 120 | #? [B1] 121 | 522 debug pos last3 122 | #? [B1] 123 | 523 debug pos ko 124 | #? [] 125 | 126 | play b a2 127 | undo 128 | 530 debug pos last 129 | #? [A1] 130 | 531 debug pos last2 131 | #? [B1] 132 | 532 debug pos last3 133 | #? [B1] 134 | 533 debug pos ko 135 | #? [] 136 | 137 | play b a9 138 | play w a3 139 | play b a8 140 | play w b2 141 | play b a2 142 | play w j1 143 | play b j2 144 | play w a1 145 | play b h1 146 | play w c1 147 | 148 | undo 149 | 540 debug pos last 150 | #? [H1] 151 | 541 debug pos last2 152 | #? [A1] 153 | 542 debug pos last3 154 | #? [J2] 155 | 543 debug pos ko 156 | #? [] 157 | 158 | undo 159 | 550 debug pos last 160 | #? [A1] 161 | 551 debug pos last2 162 | #? [J2] 163 | 552 debug pos last3 164 | #? [J1] 165 | 553 debug pos ko 166 | #? [A2] 167 | 168 | undo 169 | 560 debug pos last 170 | #? [J2] 171 | 561 debug pos last2 172 | #? [J1] 173 | 562 debug pos last3 174 | #? [A2] 175 | 563 debug pos ko 176 | #? [] 177 | 178 | undo 179 | undo 180 | 570 debug pos last 181 | #? [A2] 182 | 571 debug pos last2 183 | #? [B2] 184 | 572 debug pos last3 185 | #? [A8] 186 | 573 debug pos ko 187 | #? [A1] 188 | 189 | # -------------------------------------------- 190 | # undo moves with the same color in succession 191 | # -------------------------------------------- 192 | play b g7 193 | play b g6 194 | undo 195 | 600 debug pos to_play 196 | #? [WHITE] 197 | 198 | --------------------------------------------------------------------------------