10 #include "tactics/selfatari.h"
11 #include "tactics/dragon.h"
12 #include "tactics/ladder.h"
16 #include "playout/moggy.h"
17 #include "replay/replay.h"
21 static bool board_printed
;
24 board_print_test(int level
, struct board
*b
)
26 if (!DEBUGL(level
) || board_printed
)
28 board_print(b
, stderr
);
33 board_load(struct board
*b
, FILE *f
, unsigned int size
)
35 board_printed
= false;
36 board_resize(b
, size
);
38 for (int y
= size
- 1; y
>= 0; y
--) {
40 if (!fgets(line
, sizeof(line
), f
)) {
41 fprintf(stderr
, "Premature EOF.\n");
44 line
[strlen(line
) - 1] = 0; // chomp
45 if (strlen(line
) != size
* 2 - 1) {
46 fprintf(stderr
, "Line not %d char long: %s\n", size
* 2 - 1, line
);
49 for (unsigned int i
= 0; i
< size
* 2; i
++) {
52 case '.': s
= S_NONE
; break;
53 case 'X': s
= S_BLACK
; break;
54 case 'O': s
= S_WHITE
; break;
55 default: fprintf(stderr
, "Invalid stone '%c'\n", line
[i
]);
59 if (line
[i
] != ' ' && i
/2 < size
- 1) {
60 fprintf(stderr
, "No space after stone %i: '%c'\n", i
/2 + 1, line
[i
]);
63 if (s
== S_NONE
) continue;
64 struct move m
= { .color
= s
, .coord
= coord_xy(b
, i
/2 + 1, y
+ 1) };
65 if (board_play(b
, &m
) < 0) {
66 fprintf(stderr
, "Failed to play %s %s\n",
67 stone2str(s
), coord2sstr(m
.coord
, b
));
68 board_print(b
, stderr
);
73 int suicides
= b
->captures
[S_BLACK
] || b
->captures
[S_WHITE
];
78 set_ko(struct board
*b
, char *arg
)
80 assert(isalpha(*arg
));
82 last
.coord
= str2scoord(arg
, board_size(b
));
83 last
.color
= board_at(b
, last
.coord
);
84 assert(last
.color
== S_BLACK
|| last
.color
== S_WHITE
);
88 group_t g
= group_at(b
, last
.coord
);
89 assert(board_group_info(b
, g
).libs
== 1);
90 assert(group_stone_count(b
, g
, 2) == 1);
91 coord_t lib
= board_group_info(b
, g
).lib
[0];
92 assert(board_is_eyelike(b
, lib
, last
.color
));
95 b
->ko
.color
= stone_other(last
.color
);
100 test_sar(struct board
*b
, char *arg
)
102 enum stone color
= str2stone(arg
);
104 coord_t
*cc
= str2coord(arg
, board_size(b
));
105 coord_t c
= *cc
; coord_done(cc
);
106 arg
+= strcspn(arg
, " ") + 1;
107 int eres
= atoi(arg
);
109 board_print_test(2, b
);
111 printf("sar %s %s %d...\t", stone2str(color
), coord2sstr(c
, b
), eres
);
113 assert(board_at(b
, c
) == S_NONE
);
114 int rres
= is_bad_selfatari(b
, color
, c
);
120 if (debug_level
<= 2) {
121 board_print_test(0, b
);
122 printf("sar %s %s %d...\t", stone2str(color
), coord2sstr(c
, b
), eres
);
124 printf("FAILED (%d)\n", rres
);
131 test_ladder(struct board
*b
, char *arg
)
133 enum stone color
= str2stone(arg
);
135 coord_t
*cc
= str2coord(arg
, board_size(b
));
136 coord_t c
= *cc
; coord_done(cc
);
137 arg
+= strcspn(arg
, " ") + 1;
138 int eres
= atoi(arg
);
140 board_print_test(2, b
);
142 printf("ladder %s %s %d...\t", stone2str(color
), coord2sstr(c
, b
), eres
);
144 assert(board_at(b
, c
) == S_NONE
);
145 group_t atari_neighbor
= board_get_atari_neighbor(b
, c
, color
);
146 assert(atari_neighbor
);
147 int rres
= is_ladder(b
, c
, atari_neighbor
, true);
153 if (debug_level
<= 2) {
154 board_print_test(0, b
);
155 printf("ladder %s %s %d...\t", stone2str(color
), coord2sstr(c
, b
), eres
);
157 printf("FAILED (%d)\n", rres
);
160 return (rres
== eres
);
165 test_two_eyes(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("two_eyes %s %d...\t", coord2sstr(c
, b
), eres
);
175 enum stone color
= board_at(b
, c
);
176 assert(color
== S_BLACK
|| color
== S_WHITE
);
177 int rres
= dragon_is_safe(b
, group_at(b
, c
), color
);
183 if (debug_level
<= 2) {
184 board_print_test(0, b
);
185 printf("two_eyes %s %d...\t", coord2sstr(c
, b
), eres
);
187 printf("FAILED (%d)\n", rres
);
194 test_moggy_moves(struct board
*b
, char *arg
)
198 coord_t
*cc
= str2coord(arg
, board_size(b
));
200 last
.coord
= *cc
; coord_done(cc
);
201 last
.color
= board_at(b
, last
.coord
);
202 assert(last
.color
== S_BLACK
|| last
.color
== S_WHITE
);
203 enum stone color
= stone_other(last
.color
);
204 arg
+= strcspn(arg
, " ") + 1;
207 board_print(b
, stderr
); // Always print board so we see last move
209 char e_arg
[128]; sprintf(e_arg
, "runs=%i", runs
);
210 struct engine
*e
= engine_replay_init(e_arg
, b
);
213 printf("moggy moves %s, %s to play. Sampling moves (%i runs)...\n\n",
214 coord2sstr(last
.coord
, b
), stone2str(color
), runs
);
216 int played_
[b
->size2
+ 2]; memset(played_
, 0, sizeof(played_
));
217 int *played
= played_
+ 2; // allow storing pass/resign
219 replay_sample_moves(e
, b
, color
, played
, &most_played
);
221 /* Show moves stats */
222 for (int k
= most_played
; k
> 0; k
--)
223 for (coord_t c
= resign
; c
< b
->size2
; c
++)
225 printf("%3s: %.2f%%\n", coord2str(c
, b
), (float)k
* 100 / runs
);
228 return true; // Not much of a unit test right now =)
231 #define board_empty(b) ((b)->flen == real_board_size(b) * real_board_size(b))
234 pick_random_last_move(struct board
*b
, enum stone to_play
)
239 int base
= fast_random(board_size2(b
));
240 for (int i
= base
; i
< base
+ board_size2(b
); i
++) {
241 coord_t c
= i
% board_size2(b
);
242 if (board_at(b
, c
) == stone_other(to_play
)) {
243 b
->last_move
.coord
= c
;
244 b
->last_move
.color
= board_at(b
, c
);
252 * moggy status (last_move) coord [coord...]
253 * Play number of random games starting from last_move
255 * moggy status coord [coord...]
256 * moggy status (b) coord [coord...]
257 * Black to play, pick random white last move
259 * moggy status (w) coord [coord...]
260 * White to play, pick random black last move
263 test_moggy_status(struct board
*board
, char *arg
)
266 coord_t status_at
[10];
268 enum stone color
= S_BLACK
;
269 int pick_random
= true; // Pick random last move for each game
271 while (*arg
&& *arg
!= '#') {
272 if (*arg
== ' ' || *arg
== '\t') { arg
++; continue; }
273 if (!strncmp(arg
, "(b)", 3))
275 else if (!strncmp(arg
, "(w)", 3))
277 else if (*arg
== '(') { /* Optional "(last_move)" argument */
278 arg
++; assert(isalpha(*arg
));
281 last
.coord
= str2scoord(arg
, board_size(board
));
282 last
.color
= board_at(board
, last
.coord
);
283 assert(last
.color
== S_BLACK
|| last
.color
== S_WHITE
);
284 color
= stone_other(last
.color
);
285 board
->last_move
= last
;
288 assert(isalpha(*arg
));
289 status_at
[n
++] = str2scoord(arg
, board_size(board
));
291 arg
+= strcspn(arg
, " \t");
294 board_print(board
, stderr
);
296 printf("moggy status ");
297 for (int i
= 0; i
< n
; i
++)
298 printf("%s%s", coord2sstr(status_at
[i
], board
), (i
!= n
-1 ? " " : ""));
299 printf(", %s to play. Playing %i games %s...\n",
300 stone2str(color
), games
, (pick_random
? "(random last move) " : ""));
303 struct playout_policy
*policy
= playout_moggy_init(NULL
, board
, NULL
);
304 struct playout_setup setup
= { .gamelen
= MAX_GAMELEN
};
305 struct board_ownermap ownermap
;
307 ownermap
.playouts
= 0;
308 ownermap
.map
= malloc2(board_size2(board
) * sizeof(ownermap
.map
[0]));
309 memset(ownermap
.map
, 0, board_size2(board
) * sizeof(ownermap
.map
[0]));
312 /* Get final status estimate after a number of moggy games */
314 double time_start
= time_now();
315 for (int i
= 0; i
< games
; i
++) {
317 board_copy(&b
, board
);
319 pick_random_last_move(&b
, color
);
321 int score
= play_random_game(&setup
, &b
, color
, NULL
, &ownermap
, policy
);
322 if (color
== S_WHITE
)
325 board_done_noalloc(&b
);
327 double elapsed
= time_now() - time_start
;
328 printf("moggy status in %.1fs, %i games/s\n\n", elapsed
, (int)((float)games
/ elapsed
));
330 int wr_black
= wr
* 100 / games
;
331 int wr_white
= (games
- wr
) * 100 / games
;
332 if (wr_black
> wr_white
)
333 printf("Winrate: [ black %i%% ] white %i%%\n\n", wr_black
, wr_white
);
335 printf("Winrate: black %i%% [ white %i%% ]\n\n", wr_black
, wr_white
);
337 board_print_ownermap(board
, stderr
, &ownermap
);
339 for (int i
= 0; i
< n
; i
++) {
340 coord_t c
= status_at
[i
];
341 enum stone color
= (ownermap
.map
[c
][S_BLACK
] > ownermap
.map
[c
][S_WHITE
] ? S_BLACK
: S_WHITE
);
342 fprintf(stderr
, "%3s owned by %s: %i%%\n",
343 coord2sstr(c
, board
), stone2str(color
),
344 ownermap
.map
[c
][color
] * 100 / ownermap
.playouts
);
348 playout_policy_done(policy
);
349 return true; // Not much of a unit test right now =)
352 bool board_undo_stress_test(struct board
*orig
, char *arg
);
355 unittest(char *filename
)
357 FILE *f
= fopen(filename
, "r");
367 struct board
*b
= board_init(NULL
);
371 while (fgets(line
, sizeof(line
), f
)) {
372 line
[strlen(line
) - 1] = 0; // chomp
374 case '%': printf("\n%s\n", line
); continue;
375 case '!': printf("%s...\tSKIPPED\n", line
); skipped
++; continue;
378 if (!strncmp(line
, "boardsize ", 10)) {
379 board_load(b
, f
, atoi(line
+ 10)); continue;
381 if (!strncmp(line
, "ko ", 3)) {
382 set_ko(b
, line
+ 3); continue;
386 if (!strncmp(line
, "sar ", 4))
387 passed
+= test_sar(b
, line
+ 4);
388 else if (!strncmp(line
, "ladder ", 7))
389 passed
+= test_ladder(b
, line
+ 7);
390 else if (!strncmp(line
, "two_eyes ", 9))
391 passed
+= test_two_eyes(b
, line
+ 9);
392 else if (!strncmp(line
, "moggy moves ", 12))
393 passed
+= test_moggy_moves(b
, line
+ 12);
394 else if (!strncmp(line
, "moggy status ", 13))
395 passed
+= test_moggy_status(b
, line
+ 13);
396 else if (!strncmp(line
, "board_undo_stress_test", 22))
397 passed
+= board_undo_stress_test(b
, line
+ 22);
399 fprintf(stderr
, "Syntax error: %s\n", line
);
406 printf("\n\n----------- [ %i/%i tests passed (%i%%) ] -----------\n\n", passed
, total
, passed
* 100 / total
);
408 printf("\nAll tests PASSED");
410 printf("\nSome tests FAILED\n");
414 printf(", %d test(s) SKIPPED", skipped
);