diff options
Diffstat (limited to 'apps/plugins/goban/util.c')
-rw-r--r-- | apps/plugins/goban/util.c | 885 |
1 files changed, 885 insertions, 0 deletions
diff --git a/apps/plugins/goban/util.c b/apps/plugins/goban/util.c new file mode 100644 index 0000000000..e9966311ef --- /dev/null +++ b/apps/plugins/goban/util.c | |||
@@ -0,0 +1,885 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007-2009 Joshua Simmons <mud at majidejima dot com> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "util.h" | ||
23 | #include "game.h" | ||
24 | |||
25 | |||
26 | void metadata_summary (void) | ||
27 | { | ||
28 | char buffer[256] = ""; | ||
29 | |||
30 | if (rb->strlen (header.black) || | ||
31 | rb->strlen (header.white) || | ||
32 | rb->strlen (header.black_rank) || | ||
33 | rb->strlen (header.white_rank)) | ||
34 | rb->snprintf (buffer, sizeof(buffer), | ||
35 | "%s [%s] v. %s [%s] ", | ||
36 | header.black, header.black_rank, | ||
37 | header.white, header.white_rank); | ||
38 | |||
39 | if (header.handicap > 1) | ||
40 | { | ||
41 | rb->snprintf (buffer + rb->strlen(buffer), | ||
42 | sizeof (buffer) - rb->strlen (buffer), | ||
43 | "%d stones ", header.handicap); | ||
44 | } | ||
45 | |||
46 | if (header.komi != 0 && !(header.komi == 1 && header.handicap > 1)) | ||
47 | { | ||
48 | snprint_fixed (buffer + rb->strlen(buffer), | ||
49 | sizeof (buffer) - rb->strlen (buffer), | ||
50 | header.komi); | ||
51 | rb->snprintf (buffer + rb->strlen(buffer), | ||
52 | sizeof (buffer) - rb->strlen (buffer), | ||
53 | " komi "); | ||
54 | } | ||
55 | |||
56 | if (rb->strlen(header.result)) | ||
57 | { | ||
58 | rb->snprintf (buffer + rb->strlen(buffer), | ||
59 | sizeof (buffer) - rb->strlen (buffer), | ||
60 | "(%s)", header.result); | ||
61 | } | ||
62 | |||
63 | /* waiting for user input messes up the testing code, so ifdef it*/ | ||
64 | #if !defined(GBN_TEST) | ||
65 | if (rb->strlen(buffer)) | ||
66 | { | ||
67 | rb->splash(0, buffer); | ||
68 | rb->action_userabort(TIMEOUT_BLOCK); | ||
69 | } | ||
70 | #endif | ||
71 | } | ||
72 | |||
73 | void * | ||
74 | align_buffer (void *buffer, size_t * buffer_size) | ||
75 | { | ||
76 | unsigned int wasted = (-(long) buffer) & 3; | ||
77 | |||
78 | if (!buffer || !buffer_size) | ||
79 | { | ||
80 | return NULL; | ||
81 | } | ||
82 | |||
83 | if (*buffer_size <= wasted) | ||
84 | { | ||
85 | *buffer_size = 0; | ||
86 | return NULL; | ||
87 | } | ||
88 | |||
89 | *buffer_size -= wasted; | ||
90 | |||
91 | return (void *) (((char *) buffer) + wasted); | ||
92 | } | ||
93 | |||
94 | |||
95 | |||
96 | bool | ||
97 | setup_stack (struct stack_t *stack, void *buffer, size_t buffer_size) | ||
98 | { | ||
99 | if (!stack || !buffer || !buffer_size) | ||
100 | { | ||
101 | DEBUGF ("INVALID STACK SETUP!!\n"); | ||
102 | return false; | ||
103 | } | ||
104 | |||
105 | buffer = align_buffer (buffer, &buffer_size); | ||
106 | |||
107 | if (!buffer || !buffer_size) | ||
108 | { | ||
109 | DEBUGF ("Buffer disappeared after alignment!\n"); | ||
110 | return false; | ||
111 | } | ||
112 | |||
113 | stack->buffer = buffer; | ||
114 | stack->size = buffer_size; | ||
115 | stack->sp = 0; | ||
116 | |||
117 | return true; | ||
118 | } | ||
119 | |||
120 | bool | ||
121 | push_stack (struct stack_t * stack, void *buffer, size_t buffer_size) | ||
122 | { | ||
123 | if (stack->sp + buffer_size > stack->size) | ||
124 | { | ||
125 | DEBUGF ("stack full!!\n"); | ||
126 | return false; | ||
127 | } | ||
128 | |||
129 | rb->memcpy (&stack->buffer[stack->sp], buffer, buffer_size); | ||
130 | |||
131 | stack->sp += buffer_size; | ||
132 | |||
133 | return true; | ||
134 | } | ||
135 | |||
136 | bool | ||
137 | pop_stack (struct stack_t * stack, void *buffer, size_t buffer_size) | ||
138 | { | ||
139 | if (!peek_stack (stack, buffer, buffer_size)) | ||
140 | { | ||
141 | return false; | ||
142 | } | ||
143 | |||
144 | stack->sp -= buffer_size; | ||
145 | |||
146 | return true; | ||
147 | } | ||
148 | |||
149 | bool | ||
150 | peek_stack (struct stack_t * stack, void *buffer, size_t buffer_size) | ||
151 | { | ||
152 | if (stack->sp < buffer_size) | ||
153 | { | ||
154 | return false; | ||
155 | } | ||
156 | |||
157 | rb->memcpy (buffer, &stack->buffer[stack->sp - buffer_size], buffer_size); | ||
158 | |||
159 | return true; | ||
160 | } | ||
161 | |||
162 | void | ||
163 | empty_stack (struct stack_t *stack) | ||
164 | { | ||
165 | stack->sp = 0; | ||
166 | } | ||
167 | |||
168 | bool | ||
169 | push_pos_stack (struct stack_t *stack, unsigned short pos) | ||
170 | { | ||
171 | return push_stack (stack, &pos, sizeof (pos)); | ||
172 | } | ||
173 | |||
174 | bool | ||
175 | push_int_stack (struct stack_t *stack, int num) | ||
176 | { | ||
177 | return push_stack (stack, &num, sizeof (num)); | ||
178 | } | ||
179 | |||
180 | bool | ||
181 | push_char_stack (struct stack_t *stack, char num) | ||
182 | { | ||
183 | return push_stack (stack, &num, sizeof (num)); | ||
184 | } | ||
185 | |||
186 | |||
187 | |||
188 | /* IMPORTANT: keep in sync with the enum prop_type_t enum in types.h */ | ||
189 | char *prop_names[] = { | ||
190 | /* look up the SGF specification for the meaning of these */ | ||
191 | "B", "W", | ||
192 | "AB", "AW", "AE", | ||
193 | |||
194 | "PL", "C", | ||
195 | |||
196 | "DM", "GB", "GW", "HO", "UC", "V", | ||
197 | |||
198 | "BM", "DO", "IT", "TE", | ||
199 | |||
200 | "CR", "SQ", "TR", "DD", "MA", "SL", "LB", "N", | ||
201 | |||
202 | "AP", "CA", "FF", "GM", "ST", "SZ", | ||
203 | |||
204 | "AN", "PB", "PW", "HA", "KM", "TB", "TW", "BR", "WR", | ||
205 | "BT", "WT", "CP", "DT", "EV", "RO", "GN", "GC", "ON", | ||
206 | "OT", "PC", "RE", "RU", "SO", "TM", "US", | ||
207 | |||
208 | "BL", "WL", "OB", "OW", "FG", "PM", "VW" | ||
209 | }; | ||
210 | |||
211 | /* These seems to be specified by the SGF specification. You can do free | ||
212 | form ones as well, but I haven't implemented that (and don't plan to) */ | ||
213 | char *ruleset_names[] = { "AGA", "Japanese", "Chinese", "NZ", "GOE" }; | ||
214 | |||
215 | |||
216 | |||
217 | int | ||
218 | create_or_open_file (const char *filename) | ||
219 | { | ||
220 | int fd; | ||
221 | |||
222 | if (!rb->file_exists (filename)) | ||
223 | { | ||
224 | fd = rb->creat (filename); | ||
225 | } | ||
226 | else | ||
227 | { | ||
228 | fd = rb->open (filename, O_RDWR); | ||
229 | } | ||
230 | |||
231 | return fd; | ||
232 | } | ||
233 | |||
234 | |||
235 | int | ||
236 | snprint_fixed (char *buffer, int buffer_size, int fixed) | ||
237 | { | ||
238 | return rb->snprintf (buffer, buffer_size, "%s%d.%d", | ||
239 | fixed < 0 ? "-" : "", | ||
240 | abs (fixed) >> 1, 5 * (fixed & 1)); | ||
241 | } | ||
242 | |||
243 | |||
244 | int | ||
245 | peek_char (int fd) | ||
246 | { | ||
247 | char peeked_char; | ||
248 | |||
249 | int result = rb->read (fd, &peeked_char, 1); | ||
250 | |||
251 | if (result != 1) | ||
252 | { | ||
253 | return -1; | ||
254 | } | ||
255 | |||
256 | result = rb->lseek (fd, -1, SEEK_CUR); | ||
257 | |||
258 | if (result < 0) | ||
259 | { | ||
260 | return -1; | ||
261 | } | ||
262 | |||
263 | return peeked_char; | ||
264 | } | ||
265 | |||
266 | |||
267 | int | ||
268 | read_char (int fd) | ||
269 | { | ||
270 | char read_char; | ||
271 | |||
272 | int result = rb->read (fd, &read_char, 1); | ||
273 | |||
274 | if (result != 1) | ||
275 | { | ||
276 | return -1; | ||
277 | } | ||
278 | |||
279 | return read_char; | ||
280 | } | ||
281 | |||
282 | |||
283 | bool | ||
284 | write_char (int fd, char to_write) | ||
285 | { | ||
286 | int result = write_file (fd, &to_write, 1); | ||
287 | |||
288 | if (result != 1) | ||
289 | { | ||
290 | return false; | ||
291 | } | ||
292 | |||
293 | return true; | ||
294 | } | ||
295 | |||
296 | ssize_t | ||
297 | write_file (int fd, const void *buf, size_t count) | ||
298 | { | ||
299 | const char *buffer = buf; | ||
300 | int result; | ||
301 | int ret_val = count; | ||
302 | |||
303 | while (count) | ||
304 | { | ||
305 | result = rb->write (fd, buffer, count); | ||
306 | |||
307 | if (result < 0) | ||
308 | { | ||
309 | return -1; | ||
310 | } | ||
311 | |||
312 | count -= result; | ||
313 | buffer += result; | ||
314 | } | ||
315 | |||
316 | return ret_val; | ||
317 | } | ||
318 | |||
319 | ssize_t | ||
320 | read_file (int fd, void *buf, size_t count) | ||
321 | { | ||
322 | char *buffer = buf; | ||
323 | int result; | ||
324 | int ret_val = count; | ||
325 | |||
326 | while (count) | ||
327 | { | ||
328 | result = rb->read (fd, buffer, count); | ||
329 | |||
330 | if (result <= 0) | ||
331 | { | ||
332 | return -1; | ||
333 | } | ||
334 | |||
335 | count -= result; | ||
336 | buffer += result; | ||
337 | } | ||
338 | |||
339 | return ret_val; | ||
340 | } | ||
341 | |||
342 | int | ||
343 | read_char_no_whitespace (int fd) | ||
344 | { | ||
345 | int result = peek_char_no_whitespace (fd); | ||
346 | |||
347 | read_char (fd); | ||
348 | |||
349 | return result; | ||
350 | } | ||
351 | |||
352 | int | ||
353 | peek_char_no_whitespace (int fd) | ||
354 | { | ||
355 | int result; | ||
356 | |||
357 | while (is_whitespace (result = peek_char (fd))) | ||
358 | { | ||
359 | read_char (fd); | ||
360 | } | ||
361 | |||
362 | return result; | ||
363 | } | ||
364 | |||
365 | |||
366 | void | ||
367 | close_file (int *fd) | ||
368 | { | ||
369 | if (*fd >= 0) | ||
370 | { | ||
371 | rb->close (*fd); | ||
372 | } | ||
373 | |||
374 | *fd = -1; | ||
375 | } | ||
376 | |||
377 | bool | ||
378 | is_whitespace (int value) | ||
379 | { | ||
380 | if (value == ' ' || | ||
381 | value == '\t' || | ||
382 | value == '\n' || value == '\r' || value == '\f' || value == '\v') | ||
383 | { | ||
384 | return true; | ||
385 | } | ||
386 | else | ||
387 | { | ||
388 | return false; | ||
389 | } | ||
390 | } | ||
391 | |||
392 | void | ||
393 | sanitize_string (char *string) | ||
394 | { | ||
395 | bool escaped = false; | ||
396 | |||
397 | if (!string) | ||
398 | { | ||
399 | return; | ||
400 | } | ||
401 | |||
402 | while (1) | ||
403 | { | ||
404 | switch (*string) | ||
405 | { | ||
406 | case '\0': | ||
407 | return; | ||
408 | case '\\': | ||
409 | escaped = !escaped; | ||
410 | break; | ||
411 | case ']': | ||
412 | if (!escaped) | ||
413 | { | ||
414 | *string = ']'; | ||
415 | } | ||
416 | escaped = false; | ||
417 | break; | ||
418 | default: | ||
419 | break; | ||
420 | }; | ||
421 | ++string; | ||
422 | } | ||
423 | } | ||
424 | |||
425 | |||
426 | bool | ||
427 | get_header_string_and_size (struct header_t *header, | ||
428 | enum prop_type_t type, char **buffer, int *size) | ||
429 | { | ||
430 | if (buffer == 0 || header == 0) | ||
431 | { | ||
432 | return false; | ||
433 | } | ||
434 | |||
435 | if (type == PROP_BLACK_NAME) | ||
436 | { | ||
437 | *buffer = header->black; | ||
438 | *size = MAX_NAME; | ||
439 | } | ||
440 | else if (type == PROP_WHITE_NAME) | ||
441 | { | ||
442 | *buffer = header->white; | ||
443 | *size = MAX_NAME; | ||
444 | } | ||
445 | else if (type == PROP_BLACK_RANK) | ||
446 | { | ||
447 | *buffer = header->black_rank; | ||
448 | *size = MAX_RANK; | ||
449 | } | ||
450 | else if (type == PROP_WHITE_RANK) | ||
451 | { | ||
452 | *buffer = header->white_rank; | ||
453 | *size = MAX_RANK; | ||
454 | } | ||
455 | else if (type == PROP_BLACK_TEAM) | ||
456 | { | ||
457 | *buffer = header->black_team; | ||
458 | *size = MAX_TEAM; | ||
459 | } | ||
460 | else if (type == PROP_WHITE_TEAM) | ||
461 | { | ||
462 | *buffer = header->white_team; | ||
463 | *size = MAX_TEAM; | ||
464 | } | ||
465 | else if (type == PROP_DATE) | ||
466 | { | ||
467 | *buffer = header->date; | ||
468 | *size = MAX_DATE; | ||
469 | } | ||
470 | else if (type == PROP_ROUND) | ||
471 | { | ||
472 | *buffer = header->round; | ||
473 | *size = MAX_ROUND; | ||
474 | } | ||
475 | else if (type == PROP_EVENT) | ||
476 | { | ||
477 | *buffer = header->event; | ||
478 | *size = MAX_EVENT; | ||
479 | } | ||
480 | else if (type == PROP_PLACE) | ||
481 | { | ||
482 | *buffer = header->place; | ||
483 | *size = MAX_PLACE; | ||
484 | } | ||
485 | else if (type == PROP_OVERTIME) | ||
486 | { | ||
487 | *buffer = header->overtime; | ||
488 | *size = MAX_OVERTIME; | ||
489 | } | ||
490 | else if (type == PROP_RESULT) | ||
491 | { | ||
492 | *buffer = header->result; | ||
493 | *size = MAX_RESULT; | ||
494 | } | ||
495 | else if (type == PROP_RULESET) | ||
496 | { | ||
497 | *buffer = header->ruleset; | ||
498 | *size = MAX_RULESET; | ||
499 | } | ||
500 | else | ||
501 | { | ||
502 | return false; | ||
503 | } | ||
504 | |||
505 | return true; | ||
506 | } | ||
507 | |||
508 | |||
509 | /* TEST CODE BEGINS HERE define GBN_TEST to run this, either in goban.h or | ||
510 | in the CFLAGS. The tests will be run when the plugin starts, after | ||
511 | which the plugin will exit. Any error stops testing since many tests | ||
512 | depend on previous setup. Note: The testing can take a while as there | ||
513 | are some big loops. Be patient. */ | ||
514 | |||
515 | #ifdef GBN_TEST | ||
516 | |||
517 | #include "goban.h" | ||
518 | #include "types.h" | ||
519 | #include "board.h" | ||
520 | #include "game.h" | ||
521 | #include "sgf.h" | ||
522 | #include "sgf_storage.h" | ||
523 | |||
524 | /* If this isn't on a single line, the line numbers it reports will be wrong. | ||
525 | * | ||
526 | * I'm sure there's a way to make it better, but it's not really worth it. | ||
527 | */ | ||
528 | #define gbn_assert(test) if (test) {DEBUGF("%d passed\n", __LINE__);} else {DEBUGF("%d FAILED!\n", __LINE__); rb->splashf(10 * HZ, "Test on line %d of util.c failed!", __LINE__); return;} | ||
529 | |||
530 | void | ||
531 | run_tests (void) | ||
532 | { | ||
533 | rb->splash (3 * HZ, "Running tests. Failures will stop testing."); | ||
534 | |||
535 | |||
536 | |||
537 | /* allocating and freeing storage units */ | ||
538 | |||
539 | gbn_assert (alloc_storage_sgf ()); | ||
540 | |||
541 | int prevent_infinite = 100000000; | ||
542 | |||
543 | int count = 1; | ||
544 | while (alloc_storage_sgf () >= 0 && --prevent_infinite) | ||
545 | { | ||
546 | ++count; | ||
547 | } | ||
548 | |||
549 | gbn_assert (prevent_infinite); | ||
550 | gbn_assert (count > 100); | ||
551 | |||
552 | /* make sure it fails a few times */ | ||
553 | gbn_assert (alloc_storage_sgf () < 0); | ||
554 | gbn_assert (alloc_storage_sgf () < 0); | ||
555 | gbn_assert (alloc_storage_sgf () < 0); | ||
556 | |||
557 | free_storage_sgf (0); | ||
558 | |||
559 | gbn_assert (alloc_storage_sgf () == 0); | ||
560 | |||
561 | gbn_assert (alloc_storage_sgf () < 0); | ||
562 | |||
563 | int i; | ||
564 | for (i = 0; i <= count; ++i) | ||
565 | { | ||
566 | free_storage_sgf (i); | ||
567 | } | ||
568 | |||
569 | gbn_assert (alloc_storage_sgf () >= 0); | ||
570 | --count; | ||
571 | |||
572 | for (i = 0; i < count; ++i) | ||
573 | { | ||
574 | gbn_assert (alloc_storage_sgf () >= 0); | ||
575 | } | ||
576 | |||
577 | free_tree_sgf (); | ||
578 | |||
579 | |||
580 | |||
581 | /* setting up, saving and loading */ | ||
582 | gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, 15)); | ||
583 | gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, -30)); | ||
584 | gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 4, 1)); | ||
585 | gbn_assert (setup_game (MIN_BOARD_SIZE, MIN_BOARD_SIZE, 1, 1)); | ||
586 | |||
587 | gbn_assert (setup_game (MIN_BOARD_SIZE, MAX_BOARD_SIZE, 1, 1)); | ||
588 | gbn_assert (setup_game (MAX_BOARD_SIZE, MIN_BOARD_SIZE, 1, 1)); | ||
589 | |||
590 | gbn_assert (!setup_game (MAX_BOARD_SIZE + 1, MAX_BOARD_SIZE + 1, 0, 15)); | ||
591 | gbn_assert (!setup_game (MIN_BOARD_SIZE - 1, MIN_BOARD_SIZE - 1, 0, 15)); | ||
592 | gbn_assert (!setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, -1, 15)); | ||
593 | |||
594 | gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 1, 1)); | ||
595 | gbn_assert (save_game (DEFAULT_SAVE_DIR "/t1.sgf")); | ||
596 | gbn_assert (load_game (DEFAULT_SAVE_DIR "/t1.sgf")); | ||
597 | gbn_assert (save_game (DEFAULT_SAVE_DIR "/t2.sgf")); | ||
598 | gbn_assert (load_game (DEFAULT_SAVE_DIR "/t2.sgf")); | ||
599 | |||
600 | gbn_assert (!save_game ("/DIR_DOESNT_EXIST/blah.sgf")); | ||
601 | gbn_assert (!load_game ("/DIR_DOESNT_EXIST/blah.sgf")); | ||
602 | gbn_assert (!load_game (DEFAULT_SAVE_DIR "/DOESNT_EXIST.sgf")); | ||
603 | |||
604 | |||
605 | |||
606 | /* test of a long game, captures, illegal moves */ | ||
607 | gbn_assert (load_game (DEFAULT_SAVE_DIR "/long.sgf")); | ||
608 | while (move_num < 520) | ||
609 | { | ||
610 | gbn_assert (num_variations_sgf () == 1); | ||
611 | gbn_assert (redo_node_sgf ()); | ||
612 | } | ||
613 | |||
614 | gbn_assert (play_move_sgf (POS (2, 0), BLACK)); | ||
615 | gbn_assert (play_move_sgf (POS (2, 1), WHITE)); | ||
616 | |||
617 | gbn_assert (move_num == 522); | ||
618 | |||
619 | gbn_assert (white_captures == 261 && black_captures == 0); | ||
620 | |||
621 | gbn_assert (play_move_sgf (PASS_POS, BLACK)); | ||
622 | gbn_assert (play_move_sgf (PASS_POS, WHITE)); | ||
623 | |||
624 | gbn_assert (move_num == 524); | ||
625 | |||
626 | int x, y; | ||
627 | int b_count, w_count, e_count; | ||
628 | b_count = w_count = e_count = 0; | ||
629 | for (x = 0; x < 19; ++x) | ||
630 | { | ||
631 | for (y = 0; y < 19; ++y) | ||
632 | { | ||
633 | gbn_assert (!legal_move_board (POS (x, y), BLACK, false)); | ||
634 | gbn_assert (!play_move_sgf (POS (x, y), BLACK)); | ||
635 | switch (get_point_board (POS (x, y))) | ||
636 | { | ||
637 | case BLACK: | ||
638 | ++b_count; | ||
639 | break; | ||
640 | case WHITE: | ||
641 | ++w_count; | ||
642 | break; | ||
643 | case EMPTY: | ||
644 | ++e_count; | ||
645 | break; | ||
646 | default: | ||
647 | gbn_assert (false); | ||
648 | } | ||
649 | } | ||
650 | } | ||
651 | |||
652 | gbn_assert (b_count == 0 && w_count == 261 && e_count == 19 * 19 - 261); | ||
653 | |||
654 | gbn_assert (undo_node_sgf ()); | ||
655 | gbn_assert (move_num == 523); | ||
656 | |||
657 | int infinite_prevention = 0; | ||
658 | while (move_num > 0) | ||
659 | { | ||
660 | gbn_assert (undo_node_sgf ()); | ||
661 | |||
662 | ++infinite_prevention; | ||
663 | gbn_assert (infinite_prevention < 100000); | ||
664 | } | ||
665 | |||
666 | gbn_assert (save_game (DEFAULT_SAVE_DIR "/long_out.sgf")); | ||
667 | |||
668 | |||
669 | /* test of basic moves, legal moves, adding and removing stones */ | ||
670 | gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, 0)); | ||
671 | gbn_assert (play_move_sgf | ||
672 | (POS (MAX_BOARD_SIZE / 2, MAX_BOARD_SIZE / 2), BLACK)); | ||
673 | gbn_assert (move_num == 1 && current_player == WHITE); | ||
674 | gbn_assert (!legal_move_board | ||
675 | (POS (MAX_BOARD_SIZE / 2, MAX_BOARD_SIZE / 2), WHITE, true)); | ||
676 | |||
677 | int saved_node = current_node; | ||
678 | gbn_assert (add_stone_sgf (POS (0, 0), BLACK)); | ||
679 | gbn_assert (current_node != saved_node); | ||
680 | gbn_assert (get_point_board (POS (0, 0)) == BLACK); | ||
681 | gbn_assert (move_num == 1 && current_player == WHITE); | ||
682 | |||
683 | saved_node = current_node; | ||
684 | gbn_assert (add_stone_sgf (POS (0, 1), WHITE)); | ||
685 | gbn_assert (current_node == saved_node); | ||
686 | gbn_assert (get_point_board (POS (0, 1)) == WHITE); | ||
687 | |||
688 | gbn_assert (add_stone_sgf (POS (0, 0), EMPTY)); | ||
689 | gbn_assert (add_stone_sgf (POS (0, 1), EMPTY)); | ||
690 | gbn_assert (get_point_board (POS (0, 0)) == EMPTY); | ||
691 | gbn_assert (get_point_board (POS (0, 1)) == EMPTY); | ||
692 | |||
693 | |||
694 | /* test captures */ | ||
695 | gbn_assert (load_game (DEFAULT_SAVE_DIR "/cap.sgf")); | ||
696 | gbn_assert (play_move_sgf (POS (0, 0), BLACK)); | ||
697 | gbn_assert (black_captures == 8); | ||
698 | gbn_assert (undo_node_sgf ()); | ||
699 | gbn_assert (black_captures == 0); | ||
700 | |||
701 | gbn_assert (!play_move_sgf (POS (0, 0), WHITE)); | ||
702 | play_mode = MODE_FORCE_PLAY; | ||
703 | gbn_assert (play_move_sgf (POS (0, 0), WHITE)); | ||
704 | play_mode = MODE_PLAY; | ||
705 | |||
706 | gbn_assert (black_captures == 9); | ||
707 | gbn_assert (get_point_board (POS (0, 0)) == EMPTY); | ||
708 | gbn_assert (undo_node_sgf ()); | ||
709 | gbn_assert (black_captures == 0); | ||
710 | |||
711 | gbn_assert (play_move_sgf (POS (9, 9), BLACK)); | ||
712 | gbn_assert (black_captures == 44); | ||
713 | |||
714 | for (x = 0; x < 19; ++x) | ||
715 | { | ||
716 | for (y = 0; y < 19; ++y) | ||
717 | { | ||
718 | gbn_assert (get_point_board (POS (x, y)) == BLACK || | ||
719 | add_stone_sgf (POS (x, y), BLACK)); | ||
720 | } | ||
721 | } | ||
722 | |||
723 | gbn_assert (get_point_board (POS (0, 0)) == BLACK); | ||
724 | gbn_assert (add_stone_sgf (POS (9, 9), EMPTY)); | ||
725 | gbn_assert (play_move_sgf (POS (9, 9), WHITE)); | ||
726 | gbn_assert (white_captures == 360); | ||
727 | |||
728 | gbn_assert (undo_node_sgf ()); | ||
729 | gbn_assert (white_captures == 0); | ||
730 | |||
731 | play_mode = MODE_FORCE_PLAY; | ||
732 | gbn_assert (play_move_sgf (POS (9, 9), BLACK)); | ||
733 | play_mode = MODE_PLAY; | ||
734 | gbn_assert (white_captures == 361); | ||
735 | |||
736 | for (x = 0; x < 19; ++x) | ||
737 | { | ||
738 | for (y = 0; y < 19; ++y) | ||
739 | { | ||
740 | gbn_assert (get_point_board (POS (x, y)) == EMPTY); | ||
741 | } | ||
742 | } | ||
743 | |||
744 | |||
745 | /* test ko */ | ||
746 | gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, 15)); | ||
747 | |||
748 | /* | ||
749 | * Set up the board to look like this: | ||
750 | * -X------ | ||
751 | * XO------ | ||
752 | * O------- | ||
753 | * -------- | ||
754 | */ | ||
755 | gbn_assert (add_stone_sgf (POS (0, 1), BLACK)); | ||
756 | gbn_assert (add_stone_sgf (POS (1, 0), BLACK)); | ||
757 | gbn_assert (add_stone_sgf (POS (1, 1), WHITE)); | ||
758 | gbn_assert (add_stone_sgf (POS (0, 2), WHITE)); | ||
759 | |||
760 | /* take the ko and make sure black can't take back */ | ||
761 | gbn_assert (play_move_sgf (POS (0, 0), WHITE)); | ||
762 | gbn_assert (!play_move_sgf (POS (0, 1), BLACK)); | ||
763 | |||
764 | /* make sure white can fill, even with the ko_pos set */ | ||
765 | gbn_assert (play_move_sgf (POS (0, 1), WHITE)); | ||
766 | /* and make sure undo sets the ko again */ | ||
767 | gbn_assert (undo_node_sgf ()); | ||
768 | gbn_assert (!play_move_sgf (POS (0, 1), BLACK)); | ||
769 | |||
770 | /* make sure ko threats clear the ko */ | ||
771 | gbn_assert (play_move_sgf (POS (2, 2), BLACK)); /* ko threat */ | ||
772 | gbn_assert (play_move_sgf (POS (2, 3), WHITE)); /* response */ | ||
773 | gbn_assert (play_move_sgf (POS (0, 1), BLACK)); /* take ko */ | ||
774 | |||
775 | gbn_assert (undo_node_sgf ()); | ||
776 | gbn_assert (undo_node_sgf ()); | ||
777 | gbn_assert (undo_node_sgf ()); | ||
778 | |||
779 | /* make sure a pass is counted as a ko threat */ | ||
780 | gbn_assert (!play_move_sgf (POS (0, 1), BLACK)); | ||
781 | gbn_assert (play_move_sgf (PASS_POS, BLACK)); | ||
782 | gbn_assert (play_move_sgf (PASS_POS, WHITE)); | ||
783 | gbn_assert (play_move_sgf (POS (0, 1), BLACK)); | ||
784 | |||
785 | /* and finally let's make sure that white can't directly retake */ | ||
786 | gbn_assert (!play_move_sgf (POS (0, 0), WHITE)); | ||
787 | |||
788 | |||
789 | |||
790 | /* test some header information saving/loading as well as comment | ||
791 | saving loading */ | ||
792 | char some_comment[] = | ||
793 | "blah blah blah i am a stupid comment. here's some annoying characters: 01234567890!@#$%^&*()[[[[\\\\\\]ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||
794 | /* that bit near the end is literally this: \\\] which tests escaping | ||
795 | of ]s */ | ||
796 | char read_buffer[256]; | ||
797 | |||
798 | gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 5, -20)); | ||
799 | |||
800 | /* this also tests that ko_pos is reset by setuping up a new game */ | ||
801 | gbn_assert (play_move_sgf (POS (0, 0), WHITE)); | ||
802 | gbn_assert (write_comment_sgf (some_comment) > 0); | ||
803 | gbn_assert (play_move_sgf (POS (0, 1), BLACK)); | ||
804 | rb->strcpy (header.black, "Jack Black"); | ||
805 | rb->strcpy (header.white, "Jill White"); | ||
806 | |||
807 | gbn_assert (save_game (DEFAULT_SAVE_DIR "/head.sgf")); | ||
808 | |||
809 | gbn_assert (setup_game (MIN_BOARD_SIZE, MIN_BOARD_SIZE, 1, 1)); | ||
810 | gbn_assert (load_game (DEFAULT_SAVE_DIR "/head.sgf")); | ||
811 | |||
812 | gbn_assert (header.komi == -20 && header.handicap == 5); | ||
813 | gbn_assert (board_width == MAX_BOARD_SIZE | ||
814 | && board_height == MAX_BOARD_SIZE); | ||
815 | gbn_assert (rb->strcmp (header.black, "Jack Black") == 0); | ||
816 | gbn_assert (rb->strcmp (header.white, "Jill White") == 0); | ||
817 | gbn_assert (redo_node_sgf ()); | ||
818 | gbn_assert (read_comment_sgf (read_buffer, sizeof (read_buffer))); | ||
819 | gbn_assert (rb->strcmp (read_buffer, some_comment) == 0); | ||
820 | gbn_assert (redo_node_sgf ()); | ||
821 | gbn_assert (get_point_board (POS (0, 0)) == WHITE); | ||
822 | gbn_assert (get_point_board (POS (0, 1)) == BLACK); | ||
823 | |||
824 | |||
825 | |||
826 | /* test saving and loading a file with unhandled SGF properties. this | ||
827 | test requires that the user diff unhnd.sgf with unhnd_out.sgf (any | ||
828 | substantial difference is a bug and should be reported) the | ||
829 | following are NOT substantial differences: - reordering of | ||
830 | properties in a node - whitespace changes outside of a comment | ||
831 | value or other property value - reordering of property values */ | ||
832 | gbn_assert (load_game (DEFAULT_SAVE_DIR "/unhnd.sgf")); | ||
833 | gbn_assert (save_game (DEFAULT_SAVE_DIR "/unhnd_out.sgf")); | ||
834 | |||
835 | |||
836 | |||
837 | /* Test variations a bit */ | ||
838 | gbn_assert (setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, 13)); | ||
839 | /* start at a move, otherwise add_stone won't create a variation */ | ||
840 | gbn_assert (play_move_sgf (POS (5, 5), BLACK)); | ||
841 | /* make sure it doesn't */ | ||
842 | gbn_assert (undo_node_sgf ()); | ||
843 | gbn_assert (add_stone_sgf (POS (4, 5), WHITE)); | ||
844 | gbn_assert (!undo_node_sgf ()); | ||
845 | gbn_assert (num_variations_sgf () == 1); | ||
846 | gbn_assert (play_move_sgf (POS (5, 5), BLACK)); | ||
847 | |||
848 | gbn_assert (play_move_sgf (POS (0, 0), BLACK)); | ||
849 | gbn_assert (num_variations_sgf () == 1); | ||
850 | gbn_assert (undo_node_sgf ()); | ||
851 | gbn_assert (play_move_sgf (POS (0, 1), BLACK)); | ||
852 | gbn_assert (num_variations_sgf () == 2); | ||
853 | gbn_assert (undo_node_sgf ()); | ||
854 | gbn_assert (play_move_sgf (POS (0, 1), BLACK)); | ||
855 | gbn_assert (num_variations_sgf () == 2); | ||
856 | gbn_assert (undo_node_sgf ()); | ||
857 | gbn_assert (play_move_sgf (POS (0, 2), BLACK)); | ||
858 | gbn_assert (num_variations_sgf () == 3); | ||
859 | gbn_assert (undo_node_sgf ()); | ||
860 | gbn_assert (play_move_sgf (POS (0, 3), WHITE)); | ||
861 | gbn_assert (num_variations_sgf () == 4); | ||
862 | gbn_assert (undo_node_sgf ()); | ||
863 | gbn_assert (play_move_sgf (PASS_POS, BLACK)); | ||
864 | gbn_assert (num_variations_sgf () == 5); | ||
865 | gbn_assert (undo_node_sgf ()); | ||
866 | gbn_assert (add_stone_sgf (POS (1, 1), BLACK)); | ||
867 | gbn_assert (add_stone_sgf (POS (1, 2), BLACK)); | ||
868 | gbn_assert (add_stone_sgf (POS (1, 3), WHITE)); | ||
869 | gbn_assert (num_variations_sgf () == 6); | ||
870 | gbn_assert (undo_node_sgf ()); | ||
871 | gbn_assert (add_stone_sgf (POS (1, 1), BLACK)); | ||
872 | gbn_assert (add_stone_sgf (POS (1, 2), BLACK)); | ||
873 | gbn_assert (add_stone_sgf (POS (1, 3), WHITE)); | ||
874 | gbn_assert (num_variations_sgf () == 7); | ||
875 | gbn_assert (next_variation_sgf ()); | ||
876 | gbn_assert (get_point_board (POS (0, 0)) == BLACK); | ||
877 | gbn_assert (get_point_board (POS (0, 1)) == EMPTY); | ||
878 | gbn_assert (get_point_board (POS (0, 2)) == EMPTY); | ||
879 | gbn_assert (get_point_board (POS (1, 1)) == EMPTY); | ||
880 | gbn_assert (get_point_board (POS (1, 2)) == EMPTY); | ||
881 | gbn_assert (get_point_board (POS (1, 3)) == EMPTY); | ||
882 | |||
883 | rb->splash (10 * HZ, "All tests passed. Exiting"); | ||
884 | } | ||
885 | #endif /* GBN_TEST */ | ||