2 Functions that support the use of small 3x3 patterns hand crafted by the authors
3 of GNU Go, MoGo and others over the years.
5 The life of these patterns is as follow:
6 * On startup a pat3 file is loaded with a number of 3x3 patterns suggesting
7 play at the center intersection. The pattern is flipped and rotated and stored
8 in a hash table for both players (with the color inverted for white). They are
9 stored in their 16-bit value form.
11 * In MCTS each candidate position can be transposed to a 3x3 array, which fixed
12 out of bounds codification, fliped and rotated (but the color remains the same)
13 and searched for in the appropriate hash table.
28 #include "hash_table.h"
35 static u16 b_pattern_table
[65536];
36 static u16 w_pattern_table
[65536];
37 static bool pat3_table_inited
= false;
39 static hash_table
* weights_table
= NULL
;
40 static u32 weights_found
= 0;
41 static u32 weights_not_found
= 0;
44 typedef struct __pat3_
{
50 Convert some final symbols; non-final symbols are left as-is here
52 static void clean_symbols(
57 for (u8 i
= 0; i
< 3; ++i
) {
58 for (u8 j
= 0; j
< 3; ++j
) {
66 case SYMBOL_OWN_STONE
:
67 p
[i
][j
] = BLACK_STONE
;
69 case SYMBOL_OPT_STONE
:
70 p
[i
][j
] = WHITE_STONE
;
72 case SYMBOL_OWN_OR_EMPTY
:
73 case SYMBOL_OPT_OR_EMPTY
:
74 case SYMBOL_STONE_OR_EMPTY
:
78 snprintf(s
, MAX_PAGE_SIZ
, "pattern file format error; unknown symbol: '%c', %u\n", p
[i
][j
], p
[i
][j
]);
86 static void pat3_insert(
91 /* patterns from blacks perspective */
92 b_pattern_table
[value
] = weight
;
94 /* patterns from whites perspective */
95 w_pattern_table
[value_inv
] = weight
;
99 Lookup of pattern value for the specified player.
100 RETURNS pattern weight or 0 if not found
106 return is_black
? b_pattern_table
[value
] : w_pattern_table
[value
];
110 const u8 src
[static 3][3],
113 for (u8 i
= 0; i
< 3; ++i
) {
114 for (u8 j
= 0; j
< 3; ++j
) {
115 dst
[i
][j
] = src
[2 - i
][j
];
121 const u8 src
[static 3][3],
129 for (i
= 0; i
< 3; ++i
) {
130 for (j
= 0; j
< 3; ++j
) {
131 dst
[i
][j
] = src
[2 - j
][i
];
136 for (i
= 0; i
< 3; ++i
) {
137 for (j
= 0; j
< 3; ++j
) {
138 dst
[i
][j
] = src
[2 - i
][2 - j
];
143 for (i
= 0; i
< 3; ++i
) {
144 for (j
= 0; j
< 3; ++j
) {
145 dst
[i
][j
] = src
[j
][2 - i
];
151 static void reduce_pattern(
159 rotate((const u8 (*)[3])v
, r
, 1);
162 rotate((const u8 (*)[3])v
, r
, 2);
165 rotate((const u8 (*)[3])v
, r
, 3);
168 flip((const u8 (*)[3])v
, r
);
171 rotate((const u8 (*)[3])v
, f
, 1);
172 flip((const u8 (*)[3])f
, r
);
175 rotate((const u8 (*)[3])v
, f
, 2);
176 flip((const u8 (*)[3])f
, r
);
179 rotate((const u8 (*)[3])v
, f
, 3);
180 flip((const u8 (*)[3])f
, r
);
182 default: /* NOREDUCE */
190 Rotate and flip the pattern to its unique representative.
191 Avoid using, is not optimized.
193 void pat3_reduce_auto(
198 for (u8 reduction
= ROTATE90
; reduction
<= ROTFLIP270
; ++reduction
) {
200 reduce_pattern(b
, ROTATE90
);
202 if (memcmp(b
, v
, 3 * 3) < 0) {
209 Transposes part of an input matrix board into a 3x3 matrix pattern codified,
214 const u8 p
[static TOTAL_BOARD_SIZ
],
217 assert(is_board_move(m
));
218 assert(p
[m
] == EMPTY
);
222 move_to_coord(m
, (u8
*)&x
, (u8
*)&y
);
229 for (j
= y
- 1, kj
= 0; j
<= y
+ 1; ++j
, ++kj
) {
230 for (i
= x
- 1, ki
= 0; i
<= x
+ 1; ++i
, ++ki
) {
231 if (i
>= 0 && j
>= 0 && i
< BOARD_SIZ
&& j
< BOARD_SIZ
) {
232 move n
= coord_to_move(i
, j
);
235 dst
[ki
][kj
] = ILLEGAL
; /* edge of the board */
242 Codifies the pattern in a 16 bit unsigned value.
245 const u8 p
[static 3][3]
247 u16 ret
= p
[0][0] & 3;
248 ret
= (ret
<< 2) + (p
[0][1] & 3);
249 ret
= (ret
<< 2) + (p
[0][2] & 3);
251 ret
= (ret
<< 2) + (p
[1][0] & 3);
252 assert(p
[1][1] == EMPTY
);
253 ret
= (ret
<< 2) + (p
[1][2] & 3);
255 ret
= (ret
<< 2) + (p
[2][0] & 3);
256 ret
= (ret
<< 2) + (p
[2][1] & 3);
257 ret
= (ret
<< 2) + (p
[2][2] & 3);
262 Decodes a 16-bit value into a 3x3 pattern, with empty center.
286 static u8
_count_stones(
287 const u8 p
[static 3][3]
291 for (u8 i
= 0; i
< 3; ++i
) {
292 for (u8 j
= 0; j
< 3; ++j
) {
293 if (p
[i
][j
] == WHITE_STONE
|| p
[i
][j
] == BLACK_STONE
) {
308 for (u8 i
= 0; i
< 3; ++i
) {
309 for (u8 j
= 0; j
< 3; ++j
) {
310 if (p
[i
][j
] == BLACK_STONE
) {
311 p
[i
][j
] = WHITE_STONE
;
312 } else if (p
[i
][j
] == WHITE_STONE
) {
313 p
[i
][j
] = BLACK_STONE
;
319 static void multiply_and_store(
320 const u8 pat
[static 3][3]
325 if (weights_table
!= NULL
) {
326 /* Discover weight */
327 memcpy(p
, pat
, 3 * 3);
329 u16 pattern
= pat3_to_string((const u8 (*)[3])p
);
332 pat3
* tmp2
= (pat3
*)hash_table_find(weights_table
, &tmp
);
335 weight
= (65535 / WEIGHT_SCALE
);
338 weight
= tmp2
->weight
;
344 for (u8 r
= 1; r
< 9; ++r
) {
345 memcpy(p
, pat
, 3 * 3);
346 reduce_pattern(p
, r
);
347 u16 value
= pat3_to_string((const u8 (*)[3])p
);
349 if (pat3_find(value
, true) == 0) {
350 memcpy(p_inv
, p
, 3 * 3);
352 u16 value_inv
= pat3_to_string((const u8 (*)[3])p
);
353 assert(pat3_find(value_inv
, false) == 0);
354 pat3_insert(value
, value_inv
, weight
);
360 The original pattern is expanded into all possible forms, then rotated/flip and
361 saved if unique in the hash table under the correct patterns group (patterns
362 generated from same original p.). For uniqueness the attributes are also taken
365 RETURNS total number of new unique patterns
367 static void expand_pattern(
368 const u8 pat
[static 3][3]
371 memcpy(p
, pat
, 3 * 3);
373 for (u8 i
= 0; i
< 3; ++i
) {
374 for (u8 j
= 0; j
< 3; ++j
) {
376 case SYMBOL_OWN_OR_EMPTY
:
377 p
[i
][j
] = BLACK_STONE
;
378 expand_pattern((const u8 (*)[3])p
);
380 expand_pattern((const u8 (*)[3])p
);
382 case SYMBOL_OPT_OR_EMPTY
:
383 p
[i
][j
] = WHITE_STONE
;
384 expand_pattern((const u8 (*)[3])p
);
386 expand_pattern((const u8 (*)[3])p
);
388 case SYMBOL_STONE_OR_EMPTY
:
389 p
[i
][j
] = BLACK_STONE
;
390 expand_pattern((const u8 (*)[3])p
);
391 p
[i
][j
] = WHITE_STONE
;
392 expand_pattern((const u8 (*)[3])p
);
394 expand_pattern((const u8 (*)[3])p
);
400 if (_count_stones((const u8 (*)[3])p
) < 2) {
401 flog_crit("pat3", "failed to open and expand patterns because the expansion would create patterns with a single stone or less");
404 /* invert color, rotate and flip to generate equivalent pattern
406 multiply_and_store((const u8 (*)[3])p
);
410 static u32
read_pat3_file(
411 const char * restrict filename
,
412 char * restrict buffer
414 d32 chars_read
= read_ascii_file(buffer
, MAX_FILE_SIZ
, filename
);
415 if (chars_read
< 0) {
416 flog_crit("pat3", "couldn't open file for reading");
424 char * init_str
= buffer
;
426 while ((line
= strtok_r(init_str
, "\r\n", &save_ptr
)) != NULL
) {
429 line_cut_before(line
, '#');
436 u16 len
= strlen(line
);
442 pat
[pat_pos
][0] = line
[0];
443 pat
[pat_pos
][1] = line
[1];
444 pat
[pat_pos
][2] = line
[2];
450 /* generate all combinations and store in hash tables */
451 expand_pattern((const u8 (*)[3])pat
);
462 static u32
pat3_hash_function(
465 pat3
* b
= (pat3
*)a
;
470 static int pat3_compare_function(
471 const void * restrict a
,
472 const void * restrict b
474 pat3
* f1
= (pat3
*)a
;
475 pat3
* f2
= (pat3
*)b
;
477 return ((d32
)(f2
->value
)) - ((d32
)(f1
->value
));
480 static u32
read_patern_weights(
483 weights_table
= hash_table_create(1543, sizeof(pat3
), pat3_hash_function
, pat3_compare_function
);
486 char * init_str
= buffer
;
488 while ((line
= strtok_r(init_str
, "\r\n", &save_ptr
)) != NULL
) {
491 line_cut_before(line
, '#');
498 u16 len
= strlen(line
);
504 char * word1
= strtok_r(line
, " ", &save_ptr2
);
509 char * word2
= strtok_r(NULL
, " ", &save_ptr2
);
514 long int tmp1
= strtol(word1
, NULL
, 16);
515 long int tmp2
= strtol(word2
, NULL
, 10);
516 if (tmp1
> 65535 || tmp2
> 65535) {
520 u16 pattern
= (u16
)tmp1
;
521 /* Weight scaling for totals to fit in 16 bit and not having 0s */
522 tmp2
= (tmp2
/ WEIGHT_SCALE
) + 1;
523 u16 weight
= (u16
)tmp2
;
525 pat3
* p
= malloc(sizeof(pat3
));
529 if (!hash_table_exists(weights_table
, p
)) {
530 hash_table_insert_unique(weights_table
, p
);
534 return weights_table
->elements
;
538 Reads a .pat3 patterns file and expands all patterns into all possible and
539 patternable configurations.
542 if (pat3_table_inited
) {
546 pat3_table_inited
= true;
548 char * file_buf
= malloc(MAX_FILE_SIZ
);
549 if (file_buf
== NULL
) {
550 flog_crit("pat3", "system out of memory");
553 char * buf
= alloc();
555 if (USE_PATTERN_WEIGHTS
) {
557 Read pattern weights file
559 char * filename
= alloc();
560 snprintf(filename
, MAX_PAGE_SIZ
, "%s%ux%u.weights", data_folder(), BOARD_SIZ
, BOARD_SIZ
);
562 d32 chars_read
= read_ascii_file(file_buf
, MAX_FILE_SIZ
, filename
);
563 if (chars_read
< 0) {
565 snprintf(s
, MAX_PAGE_SIZ
, "could not read %s", filename
);
566 flog_warn("pat3", s
);
569 u32 weights
= read_patern_weights(file_buf
);
571 snprintf(buf
, MAX_PAGE_SIZ
, "read %s (%u weights)", filename
, weights
);
572 flog_info("pat3", buf
);
581 char * pat3_filenames
[128];
582 u32 files_found
= recurse_find_files(data_folder(), ".pat3", pat3_filenames
, 128);
584 snprintf(buf
, MAX_PAGE_SIZ
, "found %u 3x3 pattern files", files_found
);
585 flog_info("pat3", buf
);
587 for (u32 i
= 0; i
< files_found
; ++i
) {
588 u32 patterns_found
= read_pat3_file(pat3_filenames
[i
], file_buf
);
590 snprintf(buf
, MAX_PAGE_SIZ
, "read %s (%u patterns)", pat3_filenames
[i
], patterns_found
);
591 flog_info("pat3", buf
);
593 free(pat3_filenames
[i
]);
598 if (USE_PATTERN_WEIGHTS
&& weights_table
!= NULL
) {
599 snprintf(buf
, MAX_PAGE_SIZ
, "%u/%u expanded patterns weighted", weights_found
, weights_found
+ weights_not_found
);
600 flog_info("pat3", buf
);
602 hash_table_destroy(weights_table
, true);
603 weights_table
= NULL
;