10 #include "tactics/goals.h"
11 #include "tactics/util.h"
14 struct libmap_config libmap_config
= {
15 .pick_mode
= LMP_THRESHOLD
,
16 .pick_threshold
= 0.7,
20 .prior
= { .value
= 0.5, .playouts
= 1 },
21 .tenuki_prior
= { .value
= 0.4, .playouts
= 1 },
23 .mq_merge_groups
= true,
24 .counterattack
= LMC_DEFENSE
| LMC_ATTACK
| LMC_DEFENSE_ATTACK
,
29 libmap_setup(char *arg
)
34 char *optspec
, *next
= arg
;
37 next
+= strcspn(next
, ":");
38 if (*next
) { *next
++ = 0; } else { *next
= 0; }
40 char *optname
= optspec
;
41 char *optval
= strchr(optspec
, '=');
42 if (optval
) *optval
++ = 0;
44 if (!strcasecmp(optname
, "pick_mode") && optval
) {
45 if (!strcasecmp(optval
, "threshold")) {
46 libmap_config
.pick_mode
= LMP_THRESHOLD
;
47 } else if (!strcasecmp(optval
, "ucb")) {
48 libmap_config
.pick_mode
= LMP_UCB
;
50 fprintf(stderr
, "Invalid libmap:pick_mode value %s\n", optval
);
54 } else if (!strcasecmp(optname
, "pick_threshold") && optval
) {
55 libmap_config
.pick_threshold
= atof(optval
);
56 } else if (!strcasecmp(optname
, "pick_epsilon") && optval
) {
57 libmap_config
.pick_epsilon
= atoi(optval
);
58 } else if (!strcasecmp(optname
, "avoid_bad")) {
59 libmap_config
.avoid_bad
= !optval
|| atoi(optval
);
61 } else if (!strcasecmp(optname
, "explore_p") && optval
) {
62 libmap_config
.explore_p
= atof(optval
);
63 } else if (!strcasecmp(optname
, "prior") && optval
&& strchr(optval
, 'x')) {
64 libmap_config
.prior
.value
= atof(optval
);
65 optval
+= strcspn(optval
, "x") + 1;
66 libmap_config
.prior
.playouts
= atoi(optval
);
67 } else if (!strcasecmp(optname
, "tenuki_prior") && optval
&& strchr(optval
, 'x')) {
68 libmap_config
.tenuki_prior
.value
= atof(optval
);
69 optval
+= strcspn(optval
, "x") + 1;
70 libmap_config
.tenuki_prior
.playouts
= atoi(optval
);
72 } else if (!strcasecmp(optname
, "mq_merge_groups")) {
73 libmap_config
.mq_merge_groups
= !optval
|| atoi(optval
);
74 } else if (!strcasecmp(optname
, "counterattack") && optval
) {
75 /* Combination of letters d, a, x (both), these kinds
76 * of hashes are going to be recorded. */
77 /* Note that using multiple letters makes no sense
78 * if mq_merge_groups is set. */
79 libmap_config
.counterattack
= 0;
80 if (strchr(optval
, 'd'))
81 libmap_config
.counterattack
|= LMC_DEFENSE
;
82 if (strchr(optval
, 'a'))
83 libmap_config
.counterattack
|= LMC_ATTACK
;
84 if (strchr(optval
, 'x'))
85 libmap_config
.counterattack
|= LMC_DEFENSE_ATTACK
;
86 } else if (!strcasecmp(optname
, "eval") && optval
) {
87 if (!strcasecmp(optval
, "local")) {
88 libmap_config
.eval
= LME_LOCAL
;
89 } else if (!strcasecmp(optval
, "lvalue")) {
90 libmap_config
.eval
= LME_LVALUE
;
91 } else if (!strcasecmp(optval
, "global")) {
92 libmap_config
.eval
= LME_GLOBAL
;
94 fprintf(stderr
, "Invalid libmap:eval value %s\n", optval
);
97 } else if (!strcasecmp(optname
, "tenuki")) {
98 libmap_config
.tenuki
= !optval
|| atoi(optval
);
100 fprintf(stderr
, "Invalid libmap argument %s or missing value\n", optname
);
108 libmap_init(struct board
*b
)
110 struct libmap_hash
*lm
= calloc2(1, sizeof(*lm
));
118 libmap_put(struct libmap_hash
*lm
)
120 if (__sync_sub_and_fetch(&lm
->refcount
, 1) > 0)
126 libmap_queue_process(struct libmap_hash
*lm
, struct libmap_mq
*lmqueue
, struct board
*b
, enum stone winner
)
128 assert(lmqueue
->mq
.moves
<= MQL
);
129 for (unsigned int i
= 0; i
< lmqueue
->mq
.moves
; i
++) {
130 struct libmap_group
*g
= &lmqueue
->group
[i
];
131 struct move m
= { .coord
= lmqueue
->mq
.move
[i
], .color
= lmqueue
->color
[i
] };
133 if (libmap_config
.eval
== LME_LOCAL
|| libmap_config
.eval
== LME_LVALUE
) {
134 val
= board_local_value(libmap_config
.eval
== LME_LVALUE
, b
, g
->group
, g
->goal
);
136 } else { assert(libmap_config
.eval
== LME_GLOBAL
);
137 val
= winner
== g
->goal
? 1.0 : 0.0;
139 libmap_add_result(lm
, g
->hash
, m
, val
, 1);
141 lmqueue
->mq
.moves
= 0;
145 libmap_add_result(struct libmap_hash
*lm
, hash_t hash
, struct move move
,
146 floating_t result
, int playouts
)
148 /* If hash line is full, replacement strategy is naive - pick the
149 * move with minimum move[0].stats.playouts; resolve each tie
151 unsigned int min_playouts
= INT_MAX
; hash_t min_hash
= hash
;
153 for (ih
= hash
; lm
->hash
[ih
& libmap_hash_mask
].hash
!= hash
; ih
++) {
154 // fprintf(stderr, "%"PRIhash": check %"PRIhash" (%d)\n", hash & libmap_hash_mask, ih & libmap_hash_mask, lm->hash[ih & libmap_hash_mask].moves);
155 if (lm
->hash
[ih
& libmap_hash_mask
].moves
== 0) {
156 lm
->hash
[ih
& libmap_hash_mask
].hash
= hash
;
159 if (ih
>= hash
+ libmap_hash_maxline
) {
160 /* Snatch the least used bucket. */
162 // fprintf(stderr, "clear %"PRIhash"\n", ih & libmap_hash_mask);
163 memset(&lm
->hash
[ih
& libmap_hash_mask
], 0, sizeof(lm
->hash
[0]));
164 lm
->hash
[ih
& libmap_hash_mask
].hash
= hash
;
168 /* Keep track of least used bucket. */
169 assert(lm
->hash
[ih
& libmap_hash_mask
].moves
> 0);
170 unsigned int hp
= lm
->hash
[ih
& libmap_hash_mask
].move
[0].stats
.playouts
;
171 if (hp
< min_playouts
|| (hp
== min_playouts
&& fast_random(2))) {
177 // fprintf(stderr, "%"PRIhash": use %"PRIhash" (%d)\n", hash & libmap_hash_mask, ih & libmap_hash_mask, lm->hash[ih & libmap_hash_mask].moves);
178 struct libmap_context
*lc
= &lm
->hash
[ih
& libmap_hash_mask
];
181 for (int i
= 0; i
< lc
->moves
; i
++) {
182 if (lc
->move
[i
].move
.coord
== move
.coord
183 && lc
->move
[i
].move
.color
== move
.color
) {
184 stats_add_result(&lc
->move
[i
].stats
, result
, playouts
);
189 int moves
= lc
->moves
; // to preserve atomicity
190 if (moves
>= GROUP_REFILL_LIBS
) {
192 fprintf(stderr
, "(%s) too many libs\n", coord2sstr(move
.coord
, lm
->b
));
195 lc
->move
[moves
].move
= move
;
196 stats_add_result(&lc
->move
[moves
].stats
, result
, playouts
);
201 libmap_board_move_stats(struct libmap_hash
*lm
, struct board
*b
, struct move move
)
203 struct move_stats tot
= { .playouts
= 0, .value
= 0 };
204 if (is_pass(move
.coord
))
206 assert(board_at(b
, move
.coord
) != S_OFFBOARD
);
208 neighboring_groups_list(b
, board_at(b
, c
) == S_BLACK
|| board_at(b
, c
) == S_WHITE
,
209 move
.coord
, groups
, groups_n
, groupsbycolor_xxunused
);
210 for (int i
= 0; i
< groups_n
; i
++) {
211 hash_t hash
= group_to_libmap(b
, groups
[i
]);
212 struct move_stats
*lp
= libmap_move_stats(b
->libmap
, hash
, move
);
214 stats_merge(&tot
, lp
);