10 #include "tactics/selfatari.h"
11 #include "tactics/dragon.h"
12 #include "tactics/ladder.h"
13 #include "tactics/1lib.h"
17 #include "playout/moggy.h"
18 #include "replay/replay.h"
22 static bool board_printed
;
25 board_print_test(int level
, struct board
*b
)
27 if (!DEBUGL(level
) || board_printed
)
29 board_print(b
, stderr
);
34 board_load(struct board
*b
, FILE *f
, unsigned int size
)
36 board_printed
= false;
37 board_resize(b
, size
);
39 for (int y
= size
- 1; y
>= 0; y
--) {
41 if (!fgets(line
, sizeof(line
), f
)) {
42 fprintf(stderr
, "Premature EOF.\n");
45 line
[strlen(line
) - 1] = 0; // chomp
46 if (strlen(line
) != size
* 2 - 1) {
47 fprintf(stderr
, "Line not %d char long: %s\n", size
* 2 - 1, line
);
50 for (unsigned int i
= 0; i
< size
* 2; i
++) {
53 case '.': s
= S_NONE
; break;
54 case 'X': s
= S_BLACK
; break;
55 case 'O': s
= S_WHITE
; break;
56 default: fprintf(stderr
, "Invalid stone '%c'\n", line
[i
]);
60 if (line
[i
] != ' ' && i
/2 < size
- 1) {
61 fprintf(stderr
, "No space after stone %i: '%c'\n", i
/2 + 1, line
[i
]);
64 if (s
== S_NONE
) continue;
65 struct move m
= { .color
= s
, .coord
= coord_xy(b
, i
/2 + 1, y
+ 1) };
66 if (board_play(b
, &m
) < 0) {
67 fprintf(stderr
, "Failed to play %s %s\n",
68 stone2str(s
), coord2sstr(m
.coord
, b
));
69 board_print(b
, stderr
);
74 int suicides
= b
->captures
[S_BLACK
] || b
->captures
[S_WHITE
];
79 set_ko(struct board
*b
, char *arg
)
81 assert(isalpha(*arg
));
83 last
.coord
= str2scoord(arg
, board_size(b
));
84 last
.color
= board_at(b
, last
.coord
);
85 assert(last
.color
== S_BLACK
|| last
.color
== S_WHITE
);
89 group_t g
= group_at(b
, last
.coord
);
90 assert(board_group_info(b
, g
).libs
== 1);
91 assert(group_stone_count(b
, g
, 2) == 1);
92 coord_t lib
= board_group_info(b
, g
).lib
[0];
93 assert(board_is_eyelike(b
, lib
, last
.color
));
96 b
->ko
.color
= stone_other(last
.color
);
101 test_sar(struct board
*b
, char *arg
)
103 enum stone color
= str2stone(arg
);
105 coord_t
*cc
= str2coord(arg
, board_size(b
));
106 coord_t c
= *cc
; coord_done(cc
);
107 arg
+= strcspn(arg
, " ") + 1;
108 int eres
= atoi(arg
);
110 board_print_test(2, b
);
112 printf("sar %s %s %d...\t", stone2str(color
), coord2sstr(c
, b
), eres
);
114 assert(board_at(b
, c
) == S_NONE
);
115 int rres
= is_bad_selfatari(b
, color
, c
);
121 if (debug_level
<= 2) {
122 board_print_test(0, b
);
123 printf("sar %s %s %d...\t", stone2str(color
), coord2sstr(c
, b
), eres
);
125 printf("FAILED (%d)\n", rres
);
132 test_ladder(struct board
*b
, char *arg
)
134 enum stone color
= str2stone(arg
);
136 coord_t
*cc
= str2coord(arg
, board_size(b
));
137 coord_t c
= *cc
; coord_done(cc
);
138 arg
+= strcspn(arg
, " ") + 1;
139 int eres
= atoi(arg
);
141 board_print_test(2, b
);
143 printf("ladder %s %s %d...\t", stone2str(color
), coord2sstr(c
, b
), eres
);
145 assert(board_at(b
, c
) == S_NONE
);
146 group_t atari_neighbor
= board_get_atari_neighbor(b
, c
, color
);
147 assert(atari_neighbor
);
148 int rres
= is_ladder(b
, c
, atari_neighbor
, true);
154 if (debug_level
<= 2) {
155 board_print_test(0, b
);
156 printf("ladder %s %s %d...\t", stone2str(color
), coord2sstr(c
, b
), eres
);
158 printf("FAILED (%d)\n", rres
);
161 return (rres
== eres
);
165 test_can_countercapture(struct board
*b
, char *arg
)
167 coord_t c
= str2scoord(arg
, board_size(b
));
168 arg
+= strcspn(arg
, " ") + 1;
169 int eres
= atoi(arg
);
171 board_print_test(2, b
);
173 printf("can_countercap %s %d...\t", coord2sstr(c
, b
), eres
);
175 enum stone color
= board_at(b
, c
);
176 group_t g
= group_at(b
, c
);
177 assert(color
== S_BLACK
|| color
== S_WHITE
);
178 int rres
= can_countercapture(b
, g
, NULL
, 0);
184 if (debug_level
<= 2) {
185 board_print_test(0, b
);
186 printf("can_countercap %s %d...\t", coord2sstr(c
, b
), eres
);
188 printf("FAILED (%d)\n", rres
);
195 test_two_eyes(struct board
*b
, char *arg
)
197 coord_t c
= str2scoord(arg
, board_size(b
));
198 arg
+= strcspn(arg
, " ") + 1;
199 int eres
= atoi(arg
);
201 board_print_test(2, b
);
203 printf("two_eyes %s %d...\t", coord2sstr(c
, b
), eres
);
205 enum stone color
= board_at(b
, c
);
206 assert(color
== S_BLACK
|| color
== S_WHITE
);
207 int rres
= dragon_is_safe(b
, group_at(b
, c
), color
);
213 if (debug_level
<= 2) {
214 board_print_test(0, b
);
215 printf("two_eyes %s %d...\t", coord2sstr(c
, b
), eres
);
217 printf("FAILED (%d)\n", rres
);
224 test_moggy_moves(struct board
*b
, char *arg
)
228 coord_t
*cc
= str2coord(arg
, board_size(b
));
230 last
.coord
= *cc
; coord_done(cc
);
231 last
.color
= board_at(b
, last
.coord
);
232 assert(last
.color
== S_BLACK
|| last
.color
== S_WHITE
);
233 enum stone color
= stone_other(last
.color
);
234 arg
+= strcspn(arg
, " ") + 1;
237 board_print(b
, stderr
); // Always print board so we see last move
239 char e_arg
[128]; sprintf(e_arg
, "runs=%i", runs
);
240 struct engine
*e
= engine_replay_init(e_arg
, b
);
243 printf("moggy moves %s, %s to play. Sampling moves (%i runs)...\n\n",
244 coord2sstr(last
.coord
, b
), stone2str(color
), runs
);
246 int played_
[b
->size2
+ 2]; memset(played_
, 0, sizeof(played_
));
247 int *played
= played_
+ 2; // allow storing pass/resign
249 replay_sample_moves(e
, b
, color
, played
, &most_played
);
251 /* Show moves stats */
252 for (int k
= most_played
; k
> 0; k
--)
253 for (coord_t c
= resign
; c
< b
->size2
; c
++)
255 printf("%3s: %.2f%%\n", coord2str(c
, b
), (float)k
* 100 / runs
);
258 return true; // Not much of a unit test right now =)
261 #define board_empty(b) ((b)->flen == real_board_size(b) * real_board_size(b))
264 pick_random_last_move(struct board
*b
, enum stone to_play
)
269 int base
= fast_random(board_size2(b
));
270 for (int i
= base
; i
< base
+ board_size2(b
); i
++) {
271 coord_t c
= i
% board_size2(b
);
272 if (board_at(b
, c
) == stone_other(to_play
)) {
273 b
->last_move
.coord
= c
;
274 b
->last_move
.color
= board_at(b
, c
);
282 * moggy status (last_move) coord [coord...]
283 * Play number of random games starting from last_move
285 * moggy status coord [coord...]
286 * moggy status (b) coord [coord...]
287 * Black to play, pick random white last move
289 * moggy status (w) coord [coord...]
290 * White to play, pick random black last move
293 test_moggy_status(struct board
*board
, char *arg
)
296 coord_t status_at
[10];
298 enum stone color
= S_BLACK
;
299 int pick_random
= true; // Pick random last move for each game
301 while (*arg
&& *arg
!= '#') {
302 if (*arg
== ' ' || *arg
== '\t') { arg
++; continue; }
303 if (!strncmp(arg
, "(b)", 3))
305 else if (!strncmp(arg
, "(w)", 3))
307 else if (*arg
== '(') { /* Optional "(last_move)" argument */
308 arg
++; assert(isalpha(*arg
));
311 last
.coord
= str2scoord(arg
, board_size(board
));
312 last
.color
= board_at(board
, last
.coord
);
313 assert(last
.color
== S_BLACK
|| last
.color
== S_WHITE
);
314 color
= stone_other(last
.color
);
315 board
->last_move
= last
;
318 assert(isalpha(*arg
));
319 status_at
[n
++] = str2scoord(arg
, board_size(board
));
321 arg
+= strcspn(arg
, " \t");
324 board_print(board
, stderr
);
326 printf("moggy status ");
327 for (int i
= 0; i
< n
; i
++)
328 printf("%s%s", coord2sstr(status_at
[i
], board
), (i
!= n
-1 ? " " : ""));
329 printf(", %s to play. Playing %i games %s...\n",
330 stone2str(color
), games
, (pick_random
? "(random last move) " : ""));
333 struct playout_policy
*policy
= playout_moggy_init(NULL
, board
, NULL
);
334 struct playout_setup setup
= { .gamelen
= MAX_GAMELEN
};
335 struct board_ownermap ownermap
;
337 ownermap
.playouts
= 0;
338 ownermap
.map
= malloc2(board_size2(board
) * sizeof(ownermap
.map
[0]));
339 memset(ownermap
.map
, 0, board_size2(board
) * sizeof(ownermap
.map
[0]));
342 /* Get final status estimate after a number of moggy games */
344 double time_start
= time_now();
345 for (int i
= 0; i
< games
; i
++) {
347 board_copy(&b
, board
);
349 pick_random_last_move(&b
, color
);
351 int score
= play_random_game(&setup
, &b
, color
, NULL
, &ownermap
, policy
);
352 if (color
== S_WHITE
)
355 board_done_noalloc(&b
);
357 double elapsed
= time_now() - time_start
;
358 printf("moggy status in %.1fs, %i games/s\n\n", elapsed
, (int)((float)games
/ elapsed
));
360 int wr_black
= wr
* 100 / games
;
361 int wr_white
= (games
- wr
) * 100 / games
;
362 if (wr_black
> wr_white
)
363 printf("Winrate: [ black %i%% ] white %i%%\n\n", wr_black
, wr_white
);
365 printf("Winrate: black %i%% [ white %i%% ]\n\n", wr_black
, wr_white
);
367 board_print_ownermap(board
, stderr
, &ownermap
);
369 for (int i
= 0; i
< n
; i
++) {
370 coord_t c
= status_at
[i
];
371 enum stone color
= (ownermap
.map
[c
][S_BLACK
] > ownermap
.map
[c
][S_WHITE
] ? S_BLACK
: S_WHITE
);
372 fprintf(stderr
, "%3s owned by %s: %i%%\n",
373 coord2sstr(c
, board
), stone2str(color
),
374 ownermap
.map
[c
][color
] * 100 / ownermap
.playouts
);
378 playout_policy_done(policy
);
379 return true; // Not much of a unit test right now =)
382 bool board_undo_stress_test(struct board
*orig
, char *arg
);
385 unittest(char *filename
)
387 FILE *f
= fopen(filename
, "r");
397 struct board
*b
= board_init(NULL
);
401 while (fgets(line
, sizeof(line
), f
)) {
402 line
[strlen(line
) - 1] = 0; // chomp
404 case '%': printf("\n%s\n", line
); continue;
405 case '!': printf("%s...\tSKIPPED\n", line
); skipped
++; continue;
408 if (!strncmp(line
, "boardsize ", 10)) {
409 board_load(b
, f
, atoi(line
+ 10)); continue;
411 if (!strncmp(line
, "ko ", 3)) {
412 set_ko(b
, line
+ 3); continue;
416 if (!strncmp(line
, "sar ", 4))
417 passed
+= test_sar(b
, line
+ 4);
418 else if (!strncmp(line
, "ladder ", 7))
419 passed
+= test_ladder(b
, line
+ 7);
420 else if (!strncmp(line
, "can_countercap ", 15))
421 passed
+= test_can_countercapture(b
, line
+ 15);
422 else if (!strncmp(line
, "two_eyes ", 9))
423 passed
+= test_two_eyes(b
, line
+ 9);
424 else if (!strncmp(line
, "moggy moves ", 12))
425 passed
+= test_moggy_moves(b
, line
+ 12);
426 else if (!strncmp(line
, "moggy status ", 13))
427 passed
+= test_moggy_status(b
, line
+ 13);
428 else if (!strncmp(line
, "board_undo_stress_test", 22))
429 passed
+= board_undo_stress_test(b
, line
+ 22);
431 fprintf(stderr
, "Syntax error: %s\n", line
);
438 printf("\n\n----------- [ %i/%i tests passed (%i%%) ] -----------\n\n", passed
, total
, passed
* 100 / total
);
440 printf("\nAll tests PASSED");
442 printf("\nSome tests FAILED\n");
446 printf(", %d test(s) SKIPPED", skipped
);