diff options
Diffstat (limited to 'apps/plugins/goban/sgf.c')
-rw-r--r-- | apps/plugins/goban/sgf.c | 2237 |
1 files changed, 2237 insertions, 0 deletions
diff --git a/apps/plugins/goban/sgf.c b/apps/plugins/goban/sgf.c new file mode 100644 index 0000000000..ad6e4a4e05 --- /dev/null +++ b/apps/plugins/goban/sgf.c | |||
@@ -0,0 +1,2237 @@ | |||
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 "sgf.h" | ||
23 | #include "sgf_storage.h" | ||
24 | #include "types.h" | ||
25 | #include "goban.h" | ||
26 | #include "board.h" | ||
27 | #include "display.h" | ||
28 | #include "game.h" | ||
29 | #include "util.h" | ||
30 | |||
31 | int sgf_fd = -1; | ||
32 | int unhandled_fd = -1; | ||
33 | |||
34 | int tree_head = -1; | ||
35 | int current_node = -1; | ||
36 | int start_node = -1; | ||
37 | |||
38 | bool header_marked = false; | ||
39 | |||
40 | static int add_child_variation (int *variation_number); | ||
41 | |||
42 | static int get_move_from_node (int handle); | ||
43 | |||
44 | static bool is_important_node (int handle); | ||
45 | static bool goto_next_important_node (bool forward); | ||
46 | static bool retreat_node (void); | ||
47 | static bool advance_node (void); | ||
48 | |||
49 | static bool do_add_stones (void); | ||
50 | static void setup_handicap_helper (unsigned short pos); | ||
51 | |||
52 | static int undo_node_helper (void); | ||
53 | |||
54 | static void set_one_mark (unsigned short pos, enum prop_type_t type); | ||
55 | static void set_label_mark (unsigned short pos, char to_set); | ||
56 | |||
57 | bool | ||
58 | play_move_sgf (unsigned short pos, unsigned char color) | ||
59 | { | ||
60 | int handle; | ||
61 | int prop_handle; | ||
62 | int temp; | ||
63 | int temp2; | ||
64 | union prop_data_t temp_data; | ||
65 | int saved = current_node; | ||
66 | |||
67 | if ((color != BLACK && color != WHITE) || | ||
68 | (!on_board (pos) && pos != PASS_POS)) | ||
69 | { | ||
70 | return false; | ||
71 | } | ||
72 | |||
73 | |||
74 | /* go to the node before the next important node (move/add | ||
75 | stone/variation) this is the right place to look for children, add | ||
76 | variations, whatever. (if there is no next, we're already at the | ||
77 | right place) */ | ||
78 | |||
79 | if (get_node (current_node)->next >= 0) | ||
80 | { | ||
81 | current_node = get_node (current_node)->next; | ||
82 | |||
83 | /* true means forward */ | ||
84 | if (goto_next_important_node (true)) | ||
85 | { | ||
86 | current_node = get_node (current_node)->prev; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | |||
91 | if ((temp = get_matching_child_sgf (pos, color)) >= 0) | ||
92 | { | ||
93 | /* don't have to do anything to set up temp as the right variation | ||
94 | number */ | ||
95 | |||
96 | } | ||
97 | else | ||
98 | { | ||
99 | /* now either there were no children, or none matched the one we | ||
100 | want so we have to add a new one */ | ||
101 | |||
102 | /* first test if it's legal. we don't do this above because SGF | ||
103 | files are allowed to have illegal moves in them, and it seems | ||
104 | to make sense to allow traversing those variations without | ||
105 | making the user change to a different play_mode */ | ||
106 | |||
107 | bool suicide_allowed = false; | ||
108 | |||
109 | if (rb->strcmp (header.ruleset, "NZ") == 0 || | ||
110 | rb->strcmp (header.ruleset, "GOE") == 0) | ||
111 | { | ||
112 | suicide_allowed = true; | ||
113 | } | ||
114 | |||
115 | if (play_mode != MODE_FORCE_PLAY && | ||
116 | !legal_move_board (pos, color, suicide_allowed)) | ||
117 | { | ||
118 | return false; | ||
119 | } | ||
120 | |||
121 | handle = add_child_sgf (NULL); | ||
122 | |||
123 | if (handle < 0) | ||
124 | { | ||
125 | current_node = saved; | ||
126 | return false; | ||
127 | } | ||
128 | |||
129 | union prop_data_t temp_prop_data; | ||
130 | temp_prop_data.position = pos; | ||
131 | |||
132 | prop_handle = add_prop_sgf (handle, | ||
133 | color == BLACK ? PROP_BLACK_MOVE : | ||
134 | PROP_WHITE_MOVE, temp_prop_data); | ||
135 | |||
136 | if (prop_handle < 0) | ||
137 | { | ||
138 | /* TODO: add code to completely remove the child which we | ||
139 | added, and then uncomment the following line. probably | ||
140 | doens't matter much since we're out of memory, but | ||
141 | whatever | ||
142 | free_storage_sgf(handle); */ | ||
143 | rb->splash (2 * HZ, | ||
144 | "Out of memory led to invalid state. Please exit."); | ||
145 | current_node = saved; | ||
146 | return false; | ||
147 | } | ||
148 | |||
149 | set_game_modified(); | ||
150 | |||
151 | temp = get_matching_child_sgf (pos, color); | ||
152 | } | ||
153 | |||
154 | /* now, one way or another temp has been set to the child variation | ||
155 | number that we should follow, so all we need to do is "choose" it | ||
156 | and redo_node_sgf */ | ||
157 | |||
158 | current_node = get_node (current_node)->next; | ||
159 | temp_data.number = temp; | ||
160 | |||
161 | temp2 = add_or_set_prop_sgf (current_node, | ||
162 | PROP_VARIATION_CHOICE, temp_data); | ||
163 | /* free up a superfluous prop */ | ||
164 | if (temp == 0) | ||
165 | { | ||
166 | delete_prop_handle_sgf (current_node, temp2); | ||
167 | } | ||
168 | |||
169 | current_node = saved; | ||
170 | return redo_node_sgf (); | ||
171 | } | ||
172 | |||
173 | bool | ||
174 | add_mark_sgf (unsigned short pos, enum prop_type_t type) | ||
175 | { | ||
176 | union prop_data_t temp_data; | ||
177 | int temp_handle; | ||
178 | enum prop_type_t original_type; | ||
179 | |||
180 | if (!on_board (pos) || current_node < 0) | ||
181 | { | ||
182 | return false; | ||
183 | } | ||
184 | |||
185 | if (type == PROP_CIRCLE || | ||
186 | type == PROP_SQUARE || | ||
187 | type == PROP_TRIANGLE || | ||
188 | type == PROP_MARK || type == PROP_DIM || type == PROP_SELECTED) | ||
189 | { | ||
190 | temp_data.position = pos; | ||
191 | |||
192 | if ((temp_handle = get_prop_pos_sgf (type, temp_data)) >= 0) | ||
193 | { | ||
194 | original_type = get_prop (temp_handle)->type; | ||
195 | delete_prop_handle_sgf (current_node, temp_handle); | ||
196 | |||
197 | if (type == original_type) | ||
198 | { | ||
199 | set_one_mark (pos, PROP_INVALID); | ||
200 | return true; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | add_prop_sgf (current_node, type, temp_data); | ||
205 | set_one_mark (pos, type); | ||
206 | |||
207 | return true; | ||
208 | } | ||
209 | else if (type == PROP_LABEL) | ||
210 | { | ||
211 | #define MIN_LABEL 'a' | ||
212 | #define MAX_LABEL 'f' | ||
213 | int temp_prop_handle = get_node (current_node)->props; | ||
214 | |||
215 | while (temp_prop_handle >= 0) | ||
216 | { | ||
217 | struct prop_t *temp_prop = get_prop (temp_prop_handle); | ||
218 | |||
219 | if (temp_prop->type == PROP_LABEL && | ||
220 | temp_prop->data.position == pos) | ||
221 | { | ||
222 | char to_set = temp_prop->data.label_extra; | ||
223 | ++to_set; | ||
224 | if (to_set > MAX_LABEL) | ||
225 | { | ||
226 | delete_prop_handle_sgf (current_node, temp_prop_handle); | ||
227 | set_one_mark (pos, PROP_INVALID); | ||
228 | return true; | ||
229 | } | ||
230 | |||
231 | temp_prop->data.label_extra = to_set; | ||
232 | set_label_mark (pos, to_set); | ||
233 | return true; | ||
234 | } | ||
235 | |||
236 | temp_prop_handle = temp_prop->next; | ||
237 | } | ||
238 | |||
239 | temp_data.label_extra = MIN_LABEL; | ||
240 | temp_data.position = pos; | ||
241 | |||
242 | add_prop_sgf (current_node, type, temp_data); | ||
243 | set_label_mark (pos, MIN_LABEL); | ||
244 | return true; | ||
245 | } | ||
246 | else | ||
247 | { | ||
248 | return false; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | bool | ||
253 | add_stone_sgf (unsigned short pos, unsigned char color) | ||
254 | { | ||
255 | int handle; | ||
256 | int prop_handle; | ||
257 | int saved = current_node; | ||
258 | union prop_data_t temp_data; | ||
259 | enum prop_type_t temp_type; | ||
260 | int var_number; | ||
261 | int temp; | ||
262 | |||
263 | if (!on_board (pos)) | ||
264 | { | ||
265 | return false; | ||
266 | } | ||
267 | |||
268 | if (color == get_point_board (pos)) | ||
269 | { | ||
270 | return false; | ||
271 | } | ||
272 | |||
273 | if ((!is_important_node (current_node) || | ||
274 | (current_node == start_node && get_move_sgf () < 0)) || | ||
275 | (get_prop_sgf (current_node, PROP_ADD_BLACK, NULL) >= 0 || | ||
276 | get_prop_sgf (current_node, PROP_ADD_WHITE, NULL) >= 0 || | ||
277 | get_prop_sgf (current_node, PROP_ADD_EMPTY, NULL) >= 0) || | ||
278 | get_node (current_node)->props < 0) | ||
279 | { | ||
280 | |||
281 | if (color == BLACK) | ||
282 | { | ||
283 | temp_type = PROP_ADD_BLACK; | ||
284 | } | ||
285 | else if (color == WHITE) | ||
286 | { | ||
287 | temp_type = PROP_ADD_WHITE; | ||
288 | } | ||
289 | else | ||
290 | { | ||
291 | temp_type = PROP_ADD_EMPTY; | ||
292 | } | ||
293 | |||
294 | temp_data.position = pos; | ||
295 | |||
296 | handle = get_prop_pos_sgf (temp_type, temp_data); | ||
297 | |||
298 | /* we have to always delete the old one and conditionally create a | ||
299 | new one (instead of trying to reuse the old one by changing | ||
300 | the type of it) because if we don't, our invariant with | ||
301 | respect to like-properties being grouped together in the | ||
302 | property list can easily be violated */ | ||
303 | if (handle >= 0) | ||
304 | { | ||
305 | temp_data.stone_extra = get_prop (handle)->data.stone_extra; | ||
306 | delete_prop_handle_sgf (current_node, handle); | ||
307 | } | ||
308 | else | ||
309 | { | ||
310 | temp_data.stone_extra = 0; | ||
311 | if (get_point_board (pos) == EMPTY) | ||
312 | { | ||
313 | temp_data.stone_extra |= FLAG_ORIG_EMPTY; | ||
314 | } | ||
315 | else if (get_point_board (pos) == BLACK) | ||
316 | { | ||
317 | temp_data.stone_extra |= FLAG_ORIG_BLACK; | ||
318 | } | ||
319 | /* else do nothing */ | ||
320 | } | ||
321 | |||
322 | /* now we've saved the information about what the board was | ||
323 | originally like, we can do the actual set */ | ||
324 | |||
325 | set_point_board (pos, color); | ||
326 | |||
327 | /* test if what we currently did just returned the board back to | ||
328 | its original for this position. if so, we DON'T create a new | ||
329 | PROP_ADD_*, because it's not needed (we already deleted the old | ||
330 | one, so in that case we just return) */ | ||
331 | if (((temp_data.stone_extra & FLAG_ORIG_EMPTY) && color == EMPTY) || | ||
332 | (!(temp_data.stone_extra & FLAG_ORIG_EMPTY) && | ||
333 | (((temp_data.stone_extra & FLAG_ORIG_BLACK) && color == BLACK) || | ||
334 | (!(temp_data.stone_extra & FLAG_ORIG_BLACK) && color == WHITE)))) | ||
335 | { | ||
336 | /* do nothing, set back to original */ | ||
337 | } | ||
338 | else | ||
339 | { | ||
340 | /* we're not set back to original, so add a prop for it */ | ||
341 | add_prop_sgf (current_node, temp_type, temp_data); | ||
342 | } | ||
343 | |||
344 | set_game_modified(); | ||
345 | |||
346 | return true; | ||
347 | } | ||
348 | else | ||
349 | { | ||
350 | /* we have to make a child variation and add stones in it */ | ||
351 | |||
352 | /* go to the node before the next important node (move/add | ||
353 | stone/variation) this is the right place to look for children, | ||
354 | add variations, whatever. (if there is no next, we're already | ||
355 | at the right place) */ | ||
356 | |||
357 | if (get_node (current_node)->next >= 0) | ||
358 | { | ||
359 | current_node = get_node (current_node)->next; | ||
360 | |||
361 | /* true means forward */ | ||
362 | if (goto_next_important_node (true)) | ||
363 | { | ||
364 | current_node = get_node (current_node)->prev; | ||
365 | } | ||
366 | } | ||
367 | |||
368 | handle = add_child_sgf (&var_number); | ||
369 | |||
370 | if (handle < 0) | ||
371 | { | ||
372 | rb->splash (2 * HZ, "Out of memory!"); | ||
373 | return false; | ||
374 | } | ||
375 | |||
376 | temp_data.position = pos; | ||
377 | |||
378 | if (color == BLACK) | ||
379 | { | ||
380 | temp_type = PROP_ADD_BLACK; | ||
381 | } | ||
382 | else if (color == WHITE) | ||
383 | { | ||
384 | temp_type = PROP_ADD_WHITE; | ||
385 | } | ||
386 | else | ||
387 | { | ||
388 | temp_type = PROP_ADD_EMPTY; | ||
389 | } | ||
390 | |||
391 | prop_handle = add_prop_sgf (handle, temp_type, temp_data); | ||
392 | |||
393 | if (prop_handle < 0) | ||
394 | { | ||
395 | /* TODO: add code to completely remove the child which we | ||
396 | added, and then uncomment the following line. probably | ||
397 | doens't matter much since we're out of memory, but | ||
398 | whatever | ||
399 | free_storage_sgf(handle); */ | ||
400 | rb->splash (2 * HZ, "Out of memory!"); | ||
401 | return false; | ||
402 | } | ||
403 | |||
404 | set_game_modified(); | ||
405 | |||
406 | /* now, "choose" the variation that we just added */ | ||
407 | |||
408 | current_node = get_node (current_node)->next; | ||
409 | temp_data.number = var_number; | ||
410 | |||
411 | temp = add_or_set_prop_sgf (current_node, | ||
412 | PROP_VARIATION_CHOICE, temp_data); | ||
413 | |||
414 | /* free up a superfluous prop */ | ||
415 | if (var_number == 0) | ||
416 | { | ||
417 | delete_prop_handle_sgf (current_node, temp); | ||
418 | } | ||
419 | |||
420 | current_node = saved; | ||
421 | |||
422 | /* and follow to our choice, returning since we already did the | ||
423 | work */ | ||
424 | return redo_node_sgf (); | ||
425 | } | ||
426 | |||
427 | return false; | ||
428 | } | ||
429 | |||
430 | bool | ||
431 | undo_node_sgf (void) | ||
432 | { | ||
433 | int result = undo_node_helper (); | ||
434 | |||
435 | /* if we undid a ko threat, we need to figure out what the ko_pos is | ||
436 | there's no simple way to do this except to undo one /more/ move, | ||
437 | and then redo back to this location. (we could store it, but this | ||
438 | isn't that bad) Note: this doesn't need to recurse because we don't | ||
439 | care what previous move's ko positions were (since the tree is | ||
440 | already set in stone basically, it wouldn't change anything). */ | ||
441 | if (result == 1) | ||
442 | { | ||
443 | int backward_move_num = move_num - 1; | ||
444 | int saved_current = current_node; | ||
445 | |||
446 | while (move_num > backward_move_num) | ||
447 | { | ||
448 | result = undo_node_helper (); | ||
449 | |||
450 | if (result < 0) | ||
451 | { | ||
452 | DEBUGF | ||
453 | ("couldn't undo to previous move in ko threat handling!\n"); | ||
454 | return false; | ||
455 | } | ||
456 | } | ||
457 | |||
458 | /* now we're backed up to the previous move before our destination | ||
459 | so, let's go forward again until we get to the node we were at | ||
460 | */ | ||
461 | |||
462 | while (current_node != saved_current) | ||
463 | { | ||
464 | if (!redo_node_sgf ()) | ||
465 | { | ||
466 | DEBUGF | ||
467 | ("redoing to correct node failed on ko threat handling!\n"); | ||
468 | return false; | ||
469 | } | ||
470 | } | ||
471 | } | ||
472 | else if (result < 0) | ||
473 | { | ||
474 | DEBUGF ("initial undo failed!\n"); | ||
475 | return false; | ||
476 | } | ||
477 | |||
478 | set_all_marks_sgf (); | ||
479 | |||
480 | /* if there is a move in this node, move the screen so that it is | ||
481 | visible */ | ||
482 | int handle = get_move_sgf (); | ||
483 | if (handle >= 0) | ||
484 | { | ||
485 | move_display (get_prop (handle)->data.position); | ||
486 | } | ||
487 | |||
488 | return true; | ||
489 | } | ||
490 | |||
491 | static int | ||
492 | undo_node_helper (void) | ||
493 | { | ||
494 | bool ko_threat_move = false; | ||
495 | |||
496 | if (current_node == start_node) | ||
497 | { | ||
498 | /* refuse to undo the initial SGF node, which is tree_head if | ||
499 | handicap == 0 or 1. If handicap >= 2, start_node is the node | ||
500 | with the handicap crap and added moves on it. don't let the | ||
501 | user undo past that */ | ||
502 | DEBUGF ("not undoing start_node\n"); | ||
503 | return -1; | ||
504 | } | ||
505 | |||
506 | struct prop_t *temp_move = get_prop (get_move_sgf ()); | ||
507 | |||
508 | if (temp_move) | ||
509 | { | ||
510 | int undone_caps = 0; | ||
511 | int undone_suicides = 0; | ||
512 | unsigned short move_pos = temp_move->data.position; | ||
513 | unsigned char move_color = temp_move->type == PROP_BLACK_MOVE ? BLACK : | ||
514 | WHITE; | ||
515 | |||
516 | unsigned short flags = temp_move->data.stone_extra; | ||
517 | |||
518 | if (move_pos != PASS_POS) | ||
519 | { | ||
520 | |||
521 | if (flags & FLAG_N_CAP) | ||
522 | { | ||
523 | undone_caps += flood_fill_board (NORTH (move_pos), | ||
524 | OTHER (move_color)); | ||
525 | } | ||
526 | if (flags & FLAG_S_CAP) | ||
527 | { | ||
528 | undone_caps += flood_fill_board (SOUTH (move_pos), | ||
529 | OTHER (move_color)); | ||
530 | } | ||
531 | if (flags & FLAG_E_CAP) | ||
532 | { | ||
533 | undone_caps += flood_fill_board (EAST (move_pos), | ||
534 | OTHER (move_color)); | ||
535 | } | ||
536 | if (flags & FLAG_W_CAP) | ||
537 | { | ||
538 | undone_caps += flood_fill_board (WEST (move_pos), | ||
539 | OTHER (move_color)); | ||
540 | } | ||
541 | |||
542 | if (flags & FLAG_SELF_CAP) | ||
543 | { | ||
544 | undone_suicides += flood_fill_board (move_pos, move_color); | ||
545 | } | ||
546 | |||
547 | if (flags & FLAG_ORIG_EMPTY) | ||
548 | { | ||
549 | set_point_board (move_pos, EMPTY); | ||
550 | } | ||
551 | else if (flags & FLAG_ORIG_BLACK) | ||
552 | { | ||
553 | set_point_board (move_pos, BLACK); | ||
554 | } | ||
555 | else | ||
556 | { | ||
557 | set_point_board (move_pos, WHITE); | ||
558 | } | ||
559 | } | ||
560 | |||
561 | if (move_color == BLACK) | ||
562 | { | ||
563 | black_captures -= undone_caps; | ||
564 | white_captures -= undone_suicides; | ||
565 | } | ||
566 | else | ||
567 | { | ||
568 | white_captures -= undone_caps; | ||
569 | black_captures -= undone_suicides; | ||
570 | } | ||
571 | |||
572 | if (flags & FLAG_KO_THREAT) | ||
573 | { | ||
574 | ko_threat_move = true; | ||
575 | } | ||
576 | |||
577 | --move_num; | ||
578 | current_player = OTHER (current_player); | ||
579 | } | ||
580 | else | ||
581 | { | ||
582 | /* test for added stones! */ | ||
583 | struct prop_t *temp_prop; | ||
584 | |||
585 | temp_prop = get_prop (get_node (current_node)->props); | ||
586 | |||
587 | while (temp_prop) | ||
588 | { | ||
589 | if ((temp_prop->type == PROP_ADD_BLACK || | ||
590 | temp_prop->type == PROP_ADD_WHITE || | ||
591 | temp_prop->type == PROP_ADD_EMPTY) && | ||
592 | on_board (temp_prop->data.position)) | ||
593 | { | ||
594 | if (temp_prop->data.stone_extra & FLAG_ORIG_EMPTY) | ||
595 | { | ||
596 | set_point_board (temp_prop->data.position, EMPTY); | ||
597 | } | ||
598 | else if (temp_prop->data.stone_extra & FLAG_ORIG_BLACK) | ||
599 | { | ||
600 | set_point_board (temp_prop->data.position, BLACK); | ||
601 | } | ||
602 | else | ||
603 | { | ||
604 | set_point_board (temp_prop->data.position, WHITE); | ||
605 | } | ||
606 | } | ||
607 | |||
608 | temp_prop = get_prop (temp_prop->next); | ||
609 | } | ||
610 | } | ||
611 | |||
612 | if (!retreat_node ()) | ||
613 | { | ||
614 | return -1; | ||
615 | } | ||
616 | |||
617 | if (ko_threat_move) | ||
618 | { | ||
619 | return 1; | ||
620 | } | ||
621 | |||
622 | return 0; | ||
623 | } | ||
624 | |||
625 | bool | ||
626 | redo_node_sgf (void) | ||
627 | { | ||
628 | if (!advance_node ()) | ||
629 | { | ||
630 | return false; | ||
631 | } | ||
632 | |||
633 | set_all_marks_sgf (); | ||
634 | |||
635 | int temp_move = get_move_sgf (); | ||
636 | if (temp_move >= 0) | ||
637 | { | ||
638 | struct prop_t *move_prop = get_prop (temp_move); | ||
639 | unsigned short pos = move_prop->data.position; | ||
640 | unsigned char color = | ||
641 | move_prop->type == PROP_BLACK_MOVE ? BLACK : WHITE; | ||
642 | |||
643 | if (color != current_player) | ||
644 | { | ||
645 | DEBUGF ("redo_node_sgf: wrong color!\n"); | ||
646 | } | ||
647 | |||
648 | /* zero out the undo information and set the ko threat flag to the | ||
649 | correct value */ | ||
650 | |||
651 | move_prop->data.stone_extra = 0; | ||
652 | |||
653 | if (ko_pos != INVALID_POS) | ||
654 | { | ||
655 | move_prop->data.stone_extra |= FLAG_KO_THREAT; | ||
656 | } | ||
657 | |||
658 | ko_pos = INVALID_POS; | ||
659 | |||
660 | if (pos == PASS_POS) | ||
661 | { | ||
662 | rb->splashf (HZ / 2, "%s Passes", | ||
663 | color == BLACK ? "Black" : "White"); | ||
664 | } | ||
665 | else | ||
666 | { | ||
667 | int n_cap, s_cap, e_cap, w_cap, self_cap; | ||
668 | |||
669 | n_cap = s_cap = e_cap = w_cap = self_cap = 0; | ||
670 | |||
671 | if (get_point_board (pos) == EMPTY) | ||
672 | { | ||
673 | move_prop->data.stone_extra |= FLAG_ORIG_EMPTY; | ||
674 | } | ||
675 | else if (get_point_board (pos) == BLACK) | ||
676 | { | ||
677 | move_prop->data.stone_extra |= FLAG_ORIG_BLACK; | ||
678 | } | ||
679 | /* else do nothing */ | ||
680 | |||
681 | set_point_board (pos, color); | ||
682 | |||
683 | /* do captures on the 4 cardinal directions, if the opponent | ||
684 | stones are breathless */ | ||
685 | if (get_point_board (NORTH (pos)) == OTHER (color) && | ||
686 | get_liberties_board (NORTH (pos)) == 0) | ||
687 | { | ||
688 | n_cap = flood_fill_board (NORTH (pos), EMPTY); | ||
689 | move_prop->data.stone_extra |= FLAG_N_CAP; | ||
690 | } | ||
691 | if (get_point_board (SOUTH (pos)) == OTHER (color) && | ||
692 | get_liberties_board (SOUTH (pos)) == 0) | ||
693 | { | ||
694 | s_cap = flood_fill_board (SOUTH (pos), EMPTY); | ||
695 | move_prop->data.stone_extra |= FLAG_S_CAP; | ||
696 | } | ||
697 | if (get_point_board (EAST (pos)) == OTHER (color) && | ||
698 | get_liberties_board (EAST (pos)) == 0) | ||
699 | { | ||
700 | e_cap = flood_fill_board (EAST (pos), EMPTY); | ||
701 | move_prop->data.stone_extra |= FLAG_E_CAP; | ||
702 | } | ||
703 | if (get_point_board (WEST (pos)) == OTHER (color) && | ||
704 | get_liberties_board (WEST (pos)) == 0) | ||
705 | { | ||
706 | w_cap = flood_fill_board (WEST (pos), EMPTY); | ||
707 | move_prop->data.stone_extra |= FLAG_W_CAP; | ||
708 | } | ||
709 | |||
710 | /* then check for suicide */ | ||
711 | if (get_liberties_board (pos) == 0) | ||
712 | { | ||
713 | self_cap = flood_fill_board (pos, EMPTY); | ||
714 | move_prop->data.stone_extra |= FLAG_SELF_CAP; | ||
715 | } | ||
716 | |||
717 | |||
718 | /* now check for a ko, with the following requirements: 1) we | ||
719 | captured one opponent stone 2) we placed one stone (not | ||
720 | connected to a larger group) 3) we have one liberty */ | ||
721 | |||
722 | if (!self_cap && | ||
723 | (n_cap + s_cap + e_cap + w_cap == 1) && | ||
724 | get_liberties_board (pos) == 1 && | ||
725 | get_point_board (NORTH (pos)) != color && | ||
726 | get_point_board (SOUTH (pos)) != color && | ||
727 | get_point_board (EAST (pos)) != color && | ||
728 | get_point_board (WEST (pos)) != color) | ||
729 | { | ||
730 | /* We passed all tests, so there is a ko to set. The | ||
731 | ko_pos is our single liberty location */ | ||
732 | |||
733 | if (get_point_board (NORTH (pos)) == EMPTY) | ||
734 | { | ||
735 | ko_pos = NORTH (pos); | ||
736 | } | ||
737 | else if (get_point_board (SOUTH (pos)) == EMPTY) | ||
738 | { | ||
739 | ko_pos = SOUTH (pos); | ||
740 | } | ||
741 | else if (get_point_board (EAST (pos)) == EMPTY) | ||
742 | { | ||
743 | ko_pos = EAST (pos); | ||
744 | } | ||
745 | else | ||
746 | { | ||
747 | ko_pos = WEST (pos); | ||
748 | } | ||
749 | } | ||
750 | |||
751 | if (color == BLACK) | ||
752 | { | ||
753 | black_captures += n_cap + s_cap + e_cap + w_cap; | ||
754 | white_captures += self_cap; | ||
755 | } | ||
756 | else | ||
757 | { | ||
758 | white_captures += n_cap + s_cap + e_cap + w_cap; | ||
759 | black_captures += self_cap; | ||
760 | } | ||
761 | |||
762 | /* this will move the cursor near this move if it was off the | ||
763 | screen */ | ||
764 | move_display (pos); | ||
765 | } | ||
766 | |||
767 | ++move_num; | ||
768 | current_player = OTHER (color); | ||
769 | |||
770 | goto redo_node_sgf_succeeded; | ||
771 | } | ||
772 | else if (do_add_stones ()) | ||
773 | { | ||
774 | goto redo_node_sgf_succeeded; | ||
775 | } | ||
776 | |||
777 | return false; | ||
778 | char comment_buffer[512]; | ||
779 | |||
780 | redo_node_sgf_succeeded: | ||
781 | #if !defined(GBN_TEST) | ||
782 | if (auto_show_comments && | ||
783 | read_comment_sgf (comment_buffer, sizeof (comment_buffer))) | ||
784 | { | ||
785 | unsigned int i; | ||
786 | for (i = 0; i < sizeof (comment_buffer); ++i) | ||
787 | { | ||
788 | /* newlines display badly in rb->splash, so replace them | ||
789 | * with spaces | ||
790 | */ | ||
791 | if (comment_buffer[i] == '\n') | ||
792 | { | ||
793 | comment_buffer[i] = ' '; | ||
794 | } | ||
795 | else if (comment_buffer[i] == '\0') | ||
796 | { | ||
797 | break; | ||
798 | } | ||
799 | } | ||
800 | draw_screen_display(); | ||
801 | rb->splash(HZ / 3, comment_buffer); | ||
802 | rb->button_clear_queue(); | ||
803 | rb->action_userabort(TIMEOUT_BLOCK); | ||
804 | } | ||
805 | #else | ||
806 | (void) comment_buffer; | ||
807 | #endif | ||
808 | |||
809 | return true; | ||
810 | } | ||
811 | |||
812 | int | ||
813 | mark_child_variations_sgf (void) | ||
814 | { | ||
815 | int result; | ||
816 | int saved = current_node; | ||
817 | struct node_t *node = get_node (current_node); | ||
818 | |||
819 | int move_handle; | ||
820 | |||
821 | if (!node) | ||
822 | { | ||
823 | return 0; | ||
824 | } | ||
825 | |||
826 | current_node = node->next; | ||
827 | goto_next_important_node (true); | ||
828 | |||
829 | result = num_variations_sgf (); | ||
830 | |||
831 | if (result > 1) | ||
832 | { | ||
833 | int i; | ||
834 | int branch_node = current_node; | ||
835 | for (i = 0; i < result; ++i) | ||
836 | { | ||
837 | go_to_variation_sgf (i); | ||
838 | goto_next_important_node (true); | ||
839 | |||
840 | move_handle = get_move_sgf (); | ||
841 | |||
842 | if (move_handle >= 0) | ||
843 | { | ||
844 | set_one_mark (get_prop (move_handle)->data.position, | ||
845 | get_prop (move_handle)->type); | ||
846 | } | ||
847 | |||
848 | current_node = branch_node; | ||
849 | } | ||
850 | } | ||
851 | |||
852 | current_node = saved; | ||
853 | |||
854 | return result; | ||
855 | } | ||
856 | |||
857 | void | ||
858 | set_all_marks_sgf (void) | ||
859 | { | ||
860 | struct prop_t *prop = get_prop (get_node (current_node)->props); | ||
861 | |||
862 | while (prop) | ||
863 | { | ||
864 | if (prop->type == PROP_LABEL) | ||
865 | { | ||
866 | set_label_mark (prop->data.position, prop->data.label_extra); | ||
867 | } | ||
868 | else if (prop->type == PROP_COMMENT) | ||
869 | { | ||
870 | set_comment_display (true); | ||
871 | } | ||
872 | else | ||
873 | { | ||
874 | set_one_mark (prop->data.position, prop->type); | ||
875 | } | ||
876 | prop = get_prop (prop->next); | ||
877 | } | ||
878 | } | ||
879 | |||
880 | static void | ||
881 | set_one_mark (unsigned short pos, enum prop_type_t type) | ||
882 | { | ||
883 | switch (type) | ||
884 | { | ||
885 | case PROP_CIRCLE: | ||
886 | set_mark_display (pos, 'c'); | ||
887 | break; | ||
888 | case PROP_SQUARE: | ||
889 | set_mark_display (pos, 's'); | ||
890 | break; | ||
891 | case PROP_TRIANGLE: | ||
892 | set_mark_display (pos, 't'); | ||
893 | break; | ||
894 | case PROP_MARK: | ||
895 | set_mark_display (pos, 'm'); | ||
896 | break; | ||
897 | case PROP_DIM: | ||
898 | set_mark_display (pos, 'd'); | ||
899 | break; | ||
900 | case PROP_SELECTED: | ||
901 | set_mark_display (pos, 'S'); | ||
902 | break; | ||
903 | case PROP_BLACK_MOVE: | ||
904 | set_mark_display (pos, 'b'); | ||
905 | break; | ||
906 | case PROP_WHITE_MOVE: | ||
907 | set_mark_display (pos, 'w'); | ||
908 | break; | ||
909 | case PROP_INVALID: | ||
910 | set_mark_display (pos, ' '); | ||
911 | default: | ||
912 | break; | ||
913 | } | ||
914 | } | ||
915 | |||
916 | static void | ||
917 | set_label_mark (unsigned short pos, char to_set) | ||
918 | { | ||
919 | set_mark_display (pos, to_set | (1 << 7)); | ||
920 | } | ||
921 | |||
922 | static bool | ||
923 | do_add_stones (void) | ||
924 | { | ||
925 | bool ret_val = false; | ||
926 | struct prop_t *temp_prop; | ||
927 | int temp_handle; | ||
928 | |||
929 | if (current_node < 0) | ||
930 | { | ||
931 | return false; | ||
932 | } | ||
933 | |||
934 | temp_handle = get_node (current_node)->props; | ||
935 | temp_prop = get_prop (temp_handle); | ||
936 | |||
937 | while (temp_prop) | ||
938 | { | ||
939 | if (temp_prop->type == PROP_ADD_BLACK || | ||
940 | temp_prop->type == PROP_ADD_WHITE || | ||
941 | temp_prop->type == PROP_ADD_EMPTY) | ||
942 | { | ||
943 | |||
944 | temp_prop->data.stone_extra = 0; | ||
945 | |||
946 | /* TODO: we could delete do-nothing PROP_ADD_*s here */ | ||
947 | |||
948 | if (get_point_board (temp_prop->data.position) == EMPTY) | ||
949 | { | ||
950 | temp_prop->data.stone_extra |= FLAG_ORIG_EMPTY; | ||
951 | } | ||
952 | else if (get_point_board (temp_prop->data.position) == BLACK) | ||
953 | { | ||
954 | temp_prop->data.stone_extra |= FLAG_ORIG_BLACK; | ||
955 | } | ||
956 | /* else, do nothing */ | ||
957 | |||
958 | if (temp_prop->type == PROP_ADD_BLACK || | ||
959 | temp_prop->type == PROP_ADD_WHITE) | ||
960 | { | ||
961 | set_point_board (temp_prop->data.position, | ||
962 | temp_prop->type == PROP_ADD_BLACK ? BLACK : | ||
963 | WHITE); | ||
964 | ret_val = true; | ||
965 | } | ||
966 | else | ||
967 | { | ||
968 | set_point_board (temp_prop->data.position, EMPTY); | ||
969 | |||
970 | ret_val = true; | ||
971 | } | ||
972 | } | ||
973 | |||
974 | temp_handle = temp_prop->next; | ||
975 | temp_prop = get_prop (temp_handle); | ||
976 | } | ||
977 | |||
978 | return ret_val; | ||
979 | } | ||
980 | |||
981 | int | ||
982 | add_child_sgf (int *variation_number) | ||
983 | { | ||
984 | int node_handle; | ||
985 | struct node_t *node; | ||
986 | struct node_t *current = get_node (current_node); | ||
987 | |||
988 | if (current->next < 0) | ||
989 | { | ||
990 | node_handle = alloc_storage_sgf (); | ||
991 | node = get_node (node_handle); | ||
992 | |||
993 | if (node_handle < 0 || !current) | ||
994 | { | ||
995 | return NO_NODE; | ||
996 | } | ||
997 | |||
998 | node->prev = current_node; | ||
999 | node->next = NO_NODE; | ||
1000 | node->props = NO_PROP; | ||
1001 | |||
1002 | current->next = node_handle; | ||
1003 | |||
1004 | if (variation_number) | ||
1005 | { | ||
1006 | *variation_number = 0; | ||
1007 | } | ||
1008 | |||
1009 | return node_handle; | ||
1010 | } | ||
1011 | else | ||
1012 | { | ||
1013 | return add_child_variation (variation_number); | ||
1014 | } | ||
1015 | } | ||
1016 | |||
1017 | int | ||
1018 | add_prop_sgf (int node, enum prop_type_t type, union prop_data_t data) | ||
1019 | { | ||
1020 | int new_prop; | ||
1021 | int temp_prop_handle; | ||
1022 | |||
1023 | if (node < 0) | ||
1024 | { | ||
1025 | return NO_PROP; | ||
1026 | } | ||
1027 | |||
1028 | new_prop = alloc_storage_sgf (); | ||
1029 | |||
1030 | if (new_prop < 0) | ||
1031 | { | ||
1032 | return NO_PROP; | ||
1033 | } | ||
1034 | |||
1035 | get_prop (new_prop)->type = type; | ||
1036 | get_prop (new_prop)->data = data; | ||
1037 | |||
1038 | /* check if the new_prop goes at the start */ | ||
1039 | if (get_node (node)->props == NO_PROP || | ||
1040 | (type == PROP_VARIATION && | ||
1041 | get_prop (get_node (node)->props)->type != PROP_VARIATION)) | ||
1042 | { | ||
1043 | |||
1044 | if (get_node (node)->props >= 0) | ||
1045 | { | ||
1046 | get_prop (new_prop)->next = get_node (node)->props; | ||
1047 | } | ||
1048 | else | ||
1049 | { | ||
1050 | get_prop (new_prop)->next = NO_PROP; | ||
1051 | } | ||
1052 | |||
1053 | get_node (node)->props = new_prop; | ||
1054 | return new_prop; | ||
1055 | } | ||
1056 | |||
1057 | temp_prop_handle = get_node (node)->props; | ||
1058 | |||
1059 | while (1) | ||
1060 | { | ||
1061 | if (get_prop (temp_prop_handle)->next < 0 || | ||
1062 | (get_prop (temp_prop_handle)->type == type && | ||
1063 | get_prop (get_prop (temp_prop_handle)->next)->type != type)) | ||
1064 | { | ||
1065 | /* new_prop goes after the current one either because we're at | ||
1066 | the end of the props list, or because we're adding a prop | ||
1067 | after the ones of its same type */ | ||
1068 | get_prop (new_prop)->next = get_prop (temp_prop_handle)->next; | ||
1069 | get_prop (temp_prop_handle)->next = new_prop; | ||
1070 | |||
1071 | return new_prop; | ||
1072 | } | ||
1073 | |||
1074 | temp_prop_handle = get_prop (temp_prop_handle)->next; | ||
1075 | } | ||
1076 | } | ||
1077 | |||
1078 | int | ||
1079 | get_prop_pos_sgf (enum prop_type_t type, union prop_data_t data) | ||
1080 | { | ||
1081 | int temp_prop_handle; | ||
1082 | struct prop_t *prop; | ||
1083 | |||
1084 | if (current_node < 0) | ||
1085 | { | ||
1086 | return -1; | ||
1087 | } | ||
1088 | |||
1089 | temp_prop_handle = get_node (current_node)->props; | ||
1090 | |||
1091 | while (temp_prop_handle >= 0) | ||
1092 | { | ||
1093 | prop = get_prop (temp_prop_handle); | ||
1094 | |||
1095 | if ((type == PROP_ADD_BLACK || | ||
1096 | type == PROP_ADD_WHITE || | ||
1097 | type == PROP_ADD_EMPTY) | ||
1098 | && | ||
1099 | (prop->type == PROP_ADD_BLACK || | ||
1100 | prop->type == PROP_ADD_WHITE || | ||
1101 | prop->type == PROP_ADD_EMPTY) | ||
1102 | && (prop->data.position == data.position)) | ||
1103 | { | ||
1104 | return temp_prop_handle; | ||
1105 | } | ||
1106 | |||
1107 | if ((type == PROP_CIRCLE || | ||
1108 | type == PROP_SQUARE || | ||
1109 | type == PROP_TRIANGLE || | ||
1110 | type == PROP_MARK || | ||
1111 | type == PROP_DIM || | ||
1112 | type == PROP_SELECTED) && | ||
1113 | (prop->type == PROP_CIRCLE || | ||
1114 | prop->type == PROP_SQUARE || | ||
1115 | prop->type == PROP_TRIANGLE || | ||
1116 | prop->type == PROP_MARK || | ||
1117 | prop->type == PROP_DIM || | ||
1118 | prop->type == PROP_SELECTED) && | ||
1119 | (prop->data.position == data.position)) | ||
1120 | { | ||
1121 | return temp_prop_handle; | ||
1122 | } | ||
1123 | |||
1124 | temp_prop_handle = get_prop (temp_prop_handle)->next; | ||
1125 | } | ||
1126 | |||
1127 | return -1; | ||
1128 | } | ||
1129 | |||
1130 | int | ||
1131 | add_or_set_prop_sgf (int node, enum prop_type_t type, union prop_data_t data) | ||
1132 | { | ||
1133 | int temp_prop_handle; | ||
1134 | |||
1135 | if (node < 0) | ||
1136 | { | ||
1137 | return NO_PROP; | ||
1138 | } | ||
1139 | |||
1140 | temp_prop_handle = get_prop_sgf (node, type, NULL); | ||
1141 | |||
1142 | if (temp_prop_handle >= 0) | ||
1143 | { | ||
1144 | get_prop (temp_prop_handle)->data = data; | ||
1145 | return temp_prop_handle; | ||
1146 | } | ||
1147 | |||
1148 | /* there was no prop to set, so we need to add one */ | ||
1149 | return add_prop_sgf (node, type, data); | ||
1150 | } | ||
1151 | |||
1152 | int | ||
1153 | get_prop_sgf (int node, enum prop_type_t type, int *previous_prop) | ||
1154 | { | ||
1155 | int previous_handle = NO_PROP; | ||
1156 | int current_handle; | ||
1157 | |||
1158 | if (node < 0) | ||
1159 | { | ||
1160 | return NO_PROP; | ||
1161 | } | ||
1162 | |||
1163 | if (get_node (node)->props < 0) | ||
1164 | { | ||
1165 | return NO_PROP; | ||
1166 | } | ||
1167 | |||
1168 | current_handle = get_node (node)->props; | ||
1169 | |||
1170 | while (current_handle >= 0) | ||
1171 | { | ||
1172 | if (get_prop (current_handle)->type == type || type == PROP_ANY) | ||
1173 | { | ||
1174 | if (previous_prop) | ||
1175 | { | ||
1176 | *previous_prop = previous_handle; | ||
1177 | } | ||
1178 | |||
1179 | return current_handle; | ||
1180 | } | ||
1181 | else | ||
1182 | { | ||
1183 | previous_handle = current_handle; | ||
1184 | current_handle = get_prop (current_handle)->next; | ||
1185 | } | ||
1186 | } | ||
1187 | |||
1188 | return NO_PROP; | ||
1189 | } | ||
1190 | |||
1191 | bool | ||
1192 | delete_prop_sgf (int node, enum prop_type_t type) | ||
1193 | { | ||
1194 | if (node < 0) | ||
1195 | { | ||
1196 | return false; | ||
1197 | } | ||
1198 | |||
1199 | return delete_prop_handle_sgf (node, get_prop_sgf (node, type, NULL)); | ||
1200 | } | ||
1201 | |||
1202 | bool | ||
1203 | delete_prop_handle_sgf (int node, int prop) | ||
1204 | { | ||
1205 | int previous; | ||
1206 | |||
1207 | if (prop < 0 || node < 0 || get_node (node)->props < 0) | ||
1208 | { | ||
1209 | return false; | ||
1210 | } | ||
1211 | |||
1212 | if (get_node (node)->props == prop) | ||
1213 | { | ||
1214 | get_node (node)->props = get_prop (get_node (node)->props)->next; | ||
1215 | free_storage_sgf (prop); | ||
1216 | return true; | ||
1217 | } | ||
1218 | |||
1219 | previous = get_node (node)->props; | ||
1220 | |||
1221 | while (get_prop (previous)->next != prop && get_prop (previous)->next >= 0) | ||
1222 | { | ||
1223 | previous = get_prop (previous)->next; | ||
1224 | } | ||
1225 | |||
1226 | if (get_prop (previous)->next < 0) | ||
1227 | { | ||
1228 | return false; | ||
1229 | } | ||
1230 | else | ||
1231 | { | ||
1232 | get_prop (previous)->next = get_prop (get_prop (previous)->next)->next; | ||
1233 | free_storage_sgf (prop); | ||
1234 | return true; | ||
1235 | } | ||
1236 | } | ||
1237 | |||
1238 | int | ||
1239 | read_comment_sgf (char *buffer, size_t buffer_size) | ||
1240 | { | ||
1241 | size_t bytes_read = 0; | ||
1242 | |||
1243 | int prop_handle = get_prop_sgf (current_node, PROP_COMMENT, NULL); | ||
1244 | |||
1245 | if (prop_handle < 0) | ||
1246 | { | ||
1247 | return 0; | ||
1248 | } | ||
1249 | |||
1250 | if (unhandled_fd < 0) | ||
1251 | { | ||
1252 | DEBUGF ("unhandled file is closed?!\n"); | ||
1253 | return 0; | ||
1254 | } | ||
1255 | |||
1256 | if (rb->lseek (unhandled_fd, get_prop (prop_handle)->data.number, | ||
1257 | SEEK_SET) < 0) | ||
1258 | { | ||
1259 | DEBUGF ("couldn't seek in unhandled_fd\n"); | ||
1260 | return -1; | ||
1261 | } | ||
1262 | |||
1263 | if (!read_char_no_whitespace (unhandled_fd) == 'C' || | ||
1264 | !read_char_no_whitespace (unhandled_fd) == '[') | ||
1265 | { | ||
1266 | DEBUGF ("comment prop points to incorrect place in unhandled_fd!!\n"); | ||
1267 | return -1; | ||
1268 | } | ||
1269 | |||
1270 | /* make output a string, the lazy way */ | ||
1271 | rb->memset (buffer, 0, buffer_size); | ||
1272 | ++bytes_read; | ||
1273 | |||
1274 | bool done = false; | ||
1275 | bool escaped = false; | ||
1276 | |||
1277 | while ((buffer_size > bytes_read) && !done) | ||
1278 | { | ||
1279 | int temp = read_char (unhandled_fd); | ||
1280 | |||
1281 | switch (temp) | ||
1282 | { | ||
1283 | case ']': | ||
1284 | if (!escaped) | ||
1285 | { | ||
1286 | done = true; | ||
1287 | break; | ||
1288 | } | ||
1289 | *buffer = temp; | ||
1290 | ++buffer; | ||
1291 | ++bytes_read; | ||
1292 | escaped = false; | ||
1293 | break; | ||
1294 | |||
1295 | case -1: | ||
1296 | DEBUGF ("encountered end of file before end of comment!\n"); | ||
1297 | done = true; | ||
1298 | break; | ||
1299 | |||
1300 | case '\\': | ||
1301 | escaped = !escaped; | ||
1302 | if (escaped) | ||
1303 | { | ||
1304 | break; | ||
1305 | } | ||
1306 | |||
1307 | default: | ||
1308 | *buffer = temp; | ||
1309 | ++buffer; | ||
1310 | ++bytes_read; | ||
1311 | escaped = false; | ||
1312 | break; | ||
1313 | } | ||
1314 | } | ||
1315 | |||
1316 | return bytes_read; | ||
1317 | } | ||
1318 | |||
1319 | int | ||
1320 | write_comment_sgf (char *string) | ||
1321 | { | ||
1322 | char *orig_string = string; | ||
1323 | |||
1324 | int prop_handle = get_prop_sgf (current_node, PROP_COMMENT, NULL); | ||
1325 | |||
1326 | int start_of_comment = -1; | ||
1327 | bool overwriting = false; | ||
1328 | |||
1329 | int bytes_written = 0; | ||
1330 | |||
1331 | set_game_modified(); | ||
1332 | |||
1333 | if (unhandled_fd < 0) | ||
1334 | { | ||
1335 | DEBUGF ("unhandled file is closed?!\n"); | ||
1336 | return 0; | ||
1337 | } | ||
1338 | |||
1339 | if (prop_handle >= 0) | ||
1340 | { | ||
1341 | if ((start_of_comment = rb->lseek (unhandled_fd, | ||
1342 | get_prop (prop_handle)->data.number, | ||
1343 | SEEK_SET)) < 0) | ||
1344 | { | ||
1345 | DEBUGF ("couldn't seek in unhandled_fd\n"); | ||
1346 | return -1; | ||
1347 | } | ||
1348 | else | ||
1349 | { | ||
1350 | overwriting = true; | ||
1351 | } | ||
1352 | } | ||
1353 | else | ||
1354 | { | ||
1355 | overwriting = false; | ||
1356 | } | ||
1357 | |||
1358 | start_of_write_wcs: | ||
1359 | |||
1360 | if (overwriting) | ||
1361 | { | ||
1362 | if (!read_char_no_whitespace (unhandled_fd) == 'C' || | ||
1363 | !read_char_no_whitespace (unhandled_fd) == '[') | ||
1364 | { | ||
1365 | DEBUGF ("non-comment while overwriting!!\n"); | ||
1366 | return -1; | ||
1367 | } | ||
1368 | } | ||
1369 | else | ||
1370 | { | ||
1371 | start_of_comment = rb->lseek (unhandled_fd, 0, SEEK_END); | ||
1372 | |||
1373 | if (start_of_comment < 0) | ||
1374 | { | ||
1375 | DEBUGF ("error seeking to end in write_comment_sgf\n"); | ||
1376 | return -1; | ||
1377 | } | ||
1378 | |||
1379 | write_char (unhandled_fd, 'C'); | ||
1380 | write_char (unhandled_fd, '['); | ||
1381 | } | ||
1382 | |||
1383 | |||
1384 | bool overwrite_escaped = false; | ||
1385 | |||
1386 | while (*string) | ||
1387 | { | ||
1388 | if (overwriting) | ||
1389 | { | ||
1390 | int orig_char = peek_char (unhandled_fd); | ||
1391 | |||
1392 | switch (orig_char) | ||
1393 | { | ||
1394 | case '\\': | ||
1395 | overwrite_escaped = !overwrite_escaped; | ||
1396 | break; | ||
1397 | |||
1398 | case ']': | ||
1399 | if (overwrite_escaped) | ||
1400 | { | ||
1401 | overwrite_escaped = false; | ||
1402 | break; | ||
1403 | } | ||
1404 | /* otherwise, fall through */ | ||
1405 | |||
1406 | case -1: | ||
1407 | |||
1408 | /* we reached the end of the part we can put our comment | ||
1409 | in, but there's more comment to write, so we should | ||
1410 | start again, this time making a new comment (the old | ||
1411 | becomes wasted space in unhandled_fd, but it doesn't | ||
1412 | really hurt anything except extra space on disk */ | ||
1413 | |||
1414 | overwriting = false; | ||
1415 | string = orig_string; | ||
1416 | bytes_written = 0; | ||
1417 | goto start_of_write_wcs; | ||
1418 | break; | ||
1419 | |||
1420 | default: | ||
1421 | overwrite_escaped = false; | ||
1422 | break; | ||
1423 | } | ||
1424 | } | ||
1425 | |||
1426 | switch (*string) | ||
1427 | { | ||
1428 | case '\\': | ||
1429 | case ']': | ||
1430 | write_char (unhandled_fd, '\\'); | ||
1431 | |||
1432 | /* fall through */ | ||
1433 | |||
1434 | default: | ||
1435 | write_char (unhandled_fd, *string); | ||
1436 | break; | ||
1437 | } | ||
1438 | |||
1439 | ++string; | ||
1440 | ++bytes_written; | ||
1441 | } | ||
1442 | |||
1443 | /* finish out the record */ | ||
1444 | write_char (unhandled_fd, ']'); | ||
1445 | write_char (unhandled_fd, ';'); | ||
1446 | |||
1447 | /* and put the reference into the unhandled_fd into the comment prop */ | ||
1448 | union prop_data_t temp_data; | ||
1449 | temp_data.number = start_of_comment; | ||
1450 | |||
1451 | add_or_set_prop_sgf (current_node, PROP_COMMENT, temp_data); | ||
1452 | set_comment_display (true); | ||
1453 | return bytes_written; | ||
1454 | } | ||
1455 | |||
1456 | bool | ||
1457 | has_more_nodes_sgf (void) | ||
1458 | { | ||
1459 | int saved = current_node; | ||
1460 | bool ret_val = false; | ||
1461 | |||
1462 | if (current_node < 0) | ||
1463 | { | ||
1464 | return false; | ||
1465 | } | ||
1466 | |||
1467 | current_node = get_node (current_node)->next; | ||
1468 | |||
1469 | if (current_node >= 0) | ||
1470 | { | ||
1471 | /* returns true if it finds an important node */ | ||
1472 | ret_val = goto_next_important_node (true); | ||
1473 | } | ||
1474 | |||
1475 | current_node = saved; | ||
1476 | return ret_val; | ||
1477 | } | ||
1478 | |||
1479 | /* logic is different here because the first node in a tree is a valid | ||
1480 | place to go */ | ||
1481 | bool | ||
1482 | has_prev_nodes_sgf (void) | ||
1483 | { | ||
1484 | if (current_node < 0) | ||
1485 | { | ||
1486 | return false; | ||
1487 | } | ||
1488 | |||
1489 | return current_node != start_node; | ||
1490 | } | ||
1491 | |||
1492 | int | ||
1493 | get_move_sgf (void) | ||
1494 | { | ||
1495 | int result = -1; | ||
1496 | int saved_current = current_node; | ||
1497 | |||
1498 | goto_next_important_node (true); | ||
1499 | |||
1500 | result = get_move_from_node (current_node); | ||
1501 | |||
1502 | current_node = saved_current; | ||
1503 | return result; | ||
1504 | } | ||
1505 | |||
1506 | static int | ||
1507 | get_move_from_node (int handle) | ||
1508 | { | ||
1509 | int prop_handle; | ||
1510 | |||
1511 | if (handle < 0) | ||
1512 | { | ||
1513 | return -2; | ||
1514 | } | ||
1515 | |||
1516 | prop_handle = get_node (handle)->props; | ||
1517 | |||
1518 | |||
1519 | while (prop_handle >= 0) | ||
1520 | { | ||
1521 | if (get_prop (prop_handle)->type == PROP_BLACK_MOVE || | ||
1522 | get_prop (prop_handle)->type == PROP_WHITE_MOVE) | ||
1523 | { | ||
1524 | return prop_handle; | ||
1525 | } | ||
1526 | |||
1527 | prop_handle = get_prop (prop_handle)->next; | ||
1528 | } | ||
1529 | |||
1530 | return -1; | ||
1531 | } | ||
1532 | |||
1533 | static bool | ||
1534 | retreat_node (void) | ||
1535 | { | ||
1536 | int result = get_node (current_node)->prev; | ||
1537 | |||
1538 | if (current_node == start_node) | ||
1539 | { | ||
1540 | return false; | ||
1541 | } | ||
1542 | |||
1543 | if (result < 0) | ||
1544 | { | ||
1545 | return false; | ||
1546 | } | ||
1547 | else | ||
1548 | { | ||
1549 | clear_marks_display (); | ||
1550 | |||
1551 | current_node = result; | ||
1552 | |||
1553 | /* go backwards to the next important node (move/add | ||
1554 | stone/variation/etc.) */ | ||
1555 | goto_next_important_node (false); | ||
1556 | return true; | ||
1557 | } | ||
1558 | } | ||
1559 | |||
1560 | static bool | ||
1561 | advance_node (void) | ||
1562 | { | ||
1563 | int result = get_node (current_node)->next; | ||
1564 | |||
1565 | if (result < 0) | ||
1566 | { | ||
1567 | return false; | ||
1568 | } | ||
1569 | else | ||
1570 | { | ||
1571 | clear_marks_display (); | ||
1572 | |||
1573 | current_node = result; | ||
1574 | |||
1575 | /* go forward to the next move/add stone/variation/etc. node */ | ||
1576 | goto_next_important_node (true); | ||
1577 | result = get_prop_sgf (current_node, PROP_VARIATION_CHOICE, NULL); | ||
1578 | |||
1579 | if (result >= 0) | ||
1580 | { | ||
1581 | go_to_variation_sgf (get_prop (result)->data.number); | ||
1582 | } | ||
1583 | |||
1584 | return true; | ||
1585 | } | ||
1586 | } | ||
1587 | |||
1588 | int | ||
1589 | num_variations_sgf (void) | ||
1590 | { | ||
1591 | int result = 1; | ||
1592 | struct prop_t *temp_prop; | ||
1593 | struct node_t *temp_node = get_node (current_node); | ||
1594 | |||
1595 | if (temp_node == 0) | ||
1596 | { | ||
1597 | return 0; | ||
1598 | } | ||
1599 | |||
1600 | if (temp_node->prev >= 0) | ||
1601 | { | ||
1602 | temp_node = get_node (get_node (temp_node->prev)->next); | ||
1603 | } | ||
1604 | |||
1605 | temp_prop = get_prop (temp_node->props); | ||
1606 | |||
1607 | while (temp_prop) | ||
1608 | { | ||
1609 | if (temp_prop->type == PROP_VARIATION) | ||
1610 | { | ||
1611 | ++result; | ||
1612 | } | ||
1613 | else | ||
1614 | { | ||
1615 | /* variations are at the beginning of the prop list */ | ||
1616 | break; | ||
1617 | } | ||
1618 | |||
1619 | temp_prop = get_prop (temp_prop->next); | ||
1620 | } | ||
1621 | |||
1622 | return result; | ||
1623 | } | ||
1624 | |||
1625 | bool | ||
1626 | go_to_variation_sgf (unsigned int num) | ||
1627 | { | ||
1628 | int saved = current_node; | ||
1629 | struct node_t *temp_node = get_node (current_node); | ||
1630 | struct prop_t *temp_prop; | ||
1631 | |||
1632 | if (!temp_node) | ||
1633 | { | ||
1634 | return false; | ||
1635 | } | ||
1636 | |||
1637 | temp_node = get_node (temp_node->prev); | ||
1638 | |||
1639 | if (!temp_node) | ||
1640 | { | ||
1641 | return false; | ||
1642 | } | ||
1643 | |||
1644 | temp_node = get_node (current_node = temp_node->next); | ||
1645 | |||
1646 | if (!temp_node) | ||
1647 | { | ||
1648 | current_node = saved; | ||
1649 | return false; | ||
1650 | } | ||
1651 | |||
1652 | temp_prop = get_prop (temp_node->props); | ||
1653 | |||
1654 | while (num) | ||
1655 | { | ||
1656 | if (!temp_prop || temp_prop->type != PROP_VARIATION) | ||
1657 | { | ||
1658 | current_node = saved; | ||
1659 | return false; | ||
1660 | } | ||
1661 | |||
1662 | if (num == 1) | ||
1663 | { | ||
1664 | current_node = temp_prop->data.node; | ||
1665 | break; | ||
1666 | } | ||
1667 | |||
1668 | temp_prop = get_prop (temp_prop->next); | ||
1669 | --num; | ||
1670 | } | ||
1671 | |||
1672 | return true; | ||
1673 | } | ||
1674 | |||
1675 | int | ||
1676 | get_matching_child_sgf (unsigned short pos, unsigned char color) | ||
1677 | { | ||
1678 | struct node_t *temp_node; | ||
1679 | struct prop_t *temp_prop; | ||
1680 | int variation_count = 0; | ||
1681 | int saved; | ||
1682 | |||
1683 | /* set true later in a loop if we want a prop in the first variation | ||
1684 | which means that we shouldn't check that branch for children */ | ||
1685 | bool dont_check_first_var_children = false; | ||
1686 | |||
1687 | temp_node = get_node (current_node); | ||
1688 | |||
1689 | if (!temp_node) | ||
1690 | { | ||
1691 | return -3; | ||
1692 | } | ||
1693 | |||
1694 | temp_node = get_node (temp_node->next); | ||
1695 | |||
1696 | if (!temp_node) | ||
1697 | { | ||
1698 | return -2; | ||
1699 | } | ||
1700 | |||
1701 | temp_prop = get_prop (temp_node->props); | ||
1702 | |||
1703 | while (temp_prop) | ||
1704 | { | ||
1705 | if (temp_prop->type == PROP_VARIATION) | ||
1706 | { | ||
1707 | ++variation_count; | ||
1708 | saved = current_node; | ||
1709 | current_node = temp_prop->data.node; | ||
1710 | |||
1711 | struct prop_t *temp_move = get_prop (get_move_sgf ()); | ||
1712 | |||
1713 | current_node = saved; | ||
1714 | |||
1715 | if (temp_move && | ||
1716 | temp_move->data.position == pos && | ||
1717 | ((color == BLACK && temp_move->type == PROP_BLACK_MOVE) || | ||
1718 | (color == WHITE && temp_move->type == PROP_WHITE_MOVE))) | ||
1719 | { | ||
1720 | return variation_count; | ||
1721 | } | ||
1722 | } | ||
1723 | else if ((temp_prop->type == PROP_BLACK_MOVE && color == BLACK) || | ||
1724 | (temp_prop->type == PROP_WHITE_MOVE && color == WHITE)) | ||
1725 | { | ||
1726 | if (temp_prop->data.position == pos) | ||
1727 | { | ||
1728 | return 0; | ||
1729 | } | ||
1730 | else | ||
1731 | { | ||
1732 | return -4; | ||
1733 | } | ||
1734 | } | ||
1735 | else if (temp_prop->type == PROP_ADD_WHITE || | ||
1736 | temp_prop->type == PROP_ADD_BLACK || | ||
1737 | temp_prop->type == PROP_ADD_EMPTY) | ||
1738 | { | ||
1739 | dont_check_first_var_children = true; | ||
1740 | } | ||
1741 | |||
1742 | temp_prop = get_prop (temp_prop->next); | ||
1743 | } | ||
1744 | |||
1745 | if (dont_check_first_var_children) | ||
1746 | { | ||
1747 | return -1; | ||
1748 | } | ||
1749 | else | ||
1750 | { | ||
1751 | saved = current_node; | ||
1752 | current_node = temp_node->next; | ||
1753 | |||
1754 | struct prop_t *temp_move = get_prop (get_move_sgf ()); | ||
1755 | if (temp_move && | ||
1756 | pos == temp_move->data.position && | ||
1757 | color == (temp_move->type == PROP_BLACK_MOVE ? BLACK : WHITE)) | ||
1758 | { | ||
1759 | current_node = saved; | ||
1760 | return 0; | ||
1761 | } | ||
1762 | |||
1763 | current_node = saved; | ||
1764 | return -1; | ||
1765 | } | ||
1766 | } | ||
1767 | |||
1768 | int | ||
1769 | next_variation_sgf (void) | ||
1770 | { | ||
1771 | int saved = current_node; | ||
1772 | int saved_start = start_node; | ||
1773 | union prop_data_t temp_data; | ||
1774 | int prop_handle; | ||
1775 | int num_vars = 0; | ||
1776 | |||
1777 | if (current_node < 0 || get_node (current_node)->prev < 0) | ||
1778 | { | ||
1779 | return -1; | ||
1780 | } | ||
1781 | |||
1782 | start_node = NO_NODE; | ||
1783 | |||
1784 | |||
1785 | |||
1786 | if (num_variations_sgf () < 2) | ||
1787 | { | ||
1788 | |||
1789 | current_node = saved; | ||
1790 | start_node = saved_start; | ||
1791 | return -2; | ||
1792 | } | ||
1793 | |||
1794 | /* now we're at a branch node which we should go to the next variation | ||
1795 | (we were at the "chosen" one, so go to the next one after that, | ||
1796 | (mod the number of variations)) */ | ||
1797 | |||
1798 | int chosen = 0; | ||
1799 | int branch_node = get_node (get_node (current_node)->prev)->next; | ||
1800 | |||
1801 | prop_handle = get_prop_sgf (branch_node, PROP_VARIATION_CHOICE, NULL); | ||
1802 | |||
1803 | if (prop_handle >= 0) | ||
1804 | { | ||
1805 | chosen = get_prop (prop_handle)->data.number; | ||
1806 | } | ||
1807 | |||
1808 | ++chosen; | ||
1809 | |||
1810 | if (chosen >= (num_vars = num_variations_sgf ())) | ||
1811 | { | ||
1812 | chosen = 0; | ||
1813 | } | ||
1814 | |||
1815 | temp_data.number = chosen; | ||
1816 | add_or_set_prop_sgf (branch_node, PROP_VARIATION_CHOICE, temp_data); | ||
1817 | |||
1818 | if (!undo_node_sgf ()) | ||
1819 | { | ||
1820 | current_node = saved; | ||
1821 | start_node = saved_start; | ||
1822 | return -3; | ||
1823 | } | ||
1824 | |||
1825 | if (redo_node_sgf ()) | ||
1826 | { | ||
1827 | start_node = saved_start; | ||
1828 | return chosen + 1; | ||
1829 | } | ||
1830 | else | ||
1831 | { | ||
1832 | current_node = saved; | ||
1833 | start_node = saved_start; | ||
1834 | return -4; | ||
1835 | } | ||
1836 | } | ||
1837 | |||
1838 | int | ||
1839 | add_child_variation (int *variation_number) | ||
1840 | { | ||
1841 | struct node_t *temp_node = get_node (current_node); | ||
1842 | struct prop_t *temp_prop; | ||
1843 | int temp_prop_handle; | ||
1844 | int new_node = alloc_storage_sgf (); | ||
1845 | int new_prop = alloc_storage_sgf (); | ||
1846 | int temp_variation_number; | ||
1847 | |||
1848 | if (new_node < 0 || new_prop < 0) | ||
1849 | { | ||
1850 | if (new_node >= 0) | ||
1851 | { | ||
1852 | free_storage_sgf (new_node); | ||
1853 | } | ||
1854 | if (new_prop >= 0) | ||
1855 | { | ||
1856 | free_storage_sgf (new_prop); | ||
1857 | } | ||
1858 | |||
1859 | return NO_NODE; | ||
1860 | } | ||
1861 | |||
1862 | if (!temp_node) | ||
1863 | { | ||
1864 | free_storage_sgf (new_node); | ||
1865 | free_storage_sgf (new_prop); | ||
1866 | |||
1867 | return NO_NODE; | ||
1868 | } | ||
1869 | |||
1870 | temp_node = get_node (temp_node->next); | ||
1871 | |||
1872 | if (!temp_node) | ||
1873 | { | ||
1874 | free_storage_sgf (new_node); | ||
1875 | free_storage_sgf (new_prop); | ||
1876 | |||
1877 | return NO_NODE; | ||
1878 | } | ||
1879 | |||
1880 | get_node (new_node)->prev = current_node; | ||
1881 | get_node (new_node)->next = NO_NODE; | ||
1882 | get_node (new_node)->props = NO_PROP; | ||
1883 | |||
1884 | get_prop (new_prop)->type = PROP_VARIATION; | ||
1885 | get_prop (new_prop)->next = NO_PROP; | ||
1886 | get_prop (new_prop)->data.node = new_node; | ||
1887 | |||
1888 | temp_prop_handle = temp_node->props; | ||
1889 | |||
1890 | if (temp_prop_handle < 0) | ||
1891 | { | ||
1892 | temp_node->props = new_prop; | ||
1893 | |||
1894 | if (variation_number) | ||
1895 | { | ||
1896 | *variation_number = 1; | ||
1897 | } | ||
1898 | |||
1899 | return new_node; | ||
1900 | } | ||
1901 | |||
1902 | if (get_prop (temp_prop_handle)->type != PROP_VARIATION) | ||
1903 | { | ||
1904 | get_prop (new_prop)->next = temp_node->props; | ||
1905 | temp_node->props = new_prop; | ||
1906 | |||
1907 | if (variation_number) | ||
1908 | { | ||
1909 | *variation_number = 1; | ||
1910 | } | ||
1911 | |||
1912 | return new_node; | ||
1913 | } | ||
1914 | |||
1915 | /* the lowest it can be, since 1 isn't it */ | ||
1916 | temp_variation_number = 2; | ||
1917 | |||
1918 | while (1) | ||
1919 | { | ||
1920 | temp_prop = get_prop (temp_prop_handle); | ||
1921 | if (temp_prop->next < 0 || | ||
1922 | get_prop (temp_prop->next)->type != PROP_VARIATION) | ||
1923 | { | ||
1924 | get_prop (new_prop)->next = temp_prop->next; | ||
1925 | temp_prop->next = new_prop; | ||
1926 | |||
1927 | if (variation_number) | ||
1928 | { | ||
1929 | *variation_number = temp_variation_number; | ||
1930 | } | ||
1931 | |||
1932 | return new_node; | ||
1933 | } | ||
1934 | |||
1935 | ++temp_variation_number; | ||
1936 | temp_prop_handle = temp_prop->next; | ||
1937 | } | ||
1938 | } | ||
1939 | |||
1940 | static bool | ||
1941 | is_important_node (int handle) | ||
1942 | { | ||
1943 | struct prop_t *temp_prop; | ||
1944 | |||
1945 | if (handle < 0) | ||
1946 | { | ||
1947 | return false; | ||
1948 | } | ||
1949 | |||
1950 | if (handle == start_node) | ||
1951 | { | ||
1952 | return true; | ||
1953 | } | ||
1954 | |||
1955 | if (get_node (handle)->prev < 0) | ||
1956 | { | ||
1957 | return true; | ||
1958 | } | ||
1959 | |||
1960 | temp_prop = get_prop (get_node (handle)->props); | ||
1961 | |||
1962 | while (temp_prop) | ||
1963 | { | ||
1964 | if (temp_prop->type == PROP_BLACK_MOVE || | ||
1965 | temp_prop->type == PROP_WHITE_MOVE || | ||
1966 | temp_prop->type == PROP_ADD_BLACK || | ||
1967 | temp_prop->type == PROP_ADD_WHITE || | ||
1968 | temp_prop->type == PROP_ADD_EMPTY || | ||
1969 | temp_prop->type == PROP_VARIATION) | ||
1970 | { | ||
1971 | return true; | ||
1972 | } | ||
1973 | |||
1974 | temp_prop = get_prop (temp_prop->next); | ||
1975 | } | ||
1976 | |||
1977 | return false; | ||
1978 | } | ||
1979 | |||
1980 | static bool | ||
1981 | goto_next_important_node (bool forward) | ||
1982 | { | ||
1983 | int temp_node = current_node; | ||
1984 | int last_good = temp_node; | ||
1985 | |||
1986 | while (temp_node >= 0 && !is_important_node (temp_node)) | ||
1987 | { | ||
1988 | last_good = temp_node; | ||
1989 | |||
1990 | temp_node = forward ? get_node (temp_node)->next : | ||
1991 | get_node (temp_node)->prev; | ||
1992 | |||
1993 | } | ||
1994 | |||
1995 | if (temp_node < 0) | ||
1996 | { | ||
1997 | current_node = last_good; | ||
1998 | } | ||
1999 | else | ||
2000 | { | ||
2001 | current_node = temp_node; | ||
2002 | } | ||
2003 | |||
2004 | if (temp_node < 0) | ||
2005 | { | ||
2006 | return false; | ||
2007 | } | ||
2008 | else | ||
2009 | { | ||
2010 | return true; | ||
2011 | } | ||
2012 | } | ||
2013 | |||
2014 | bool | ||
2015 | is_handled_sgf (enum prop_type_t type) | ||
2016 | { | ||
2017 | if (type == PROP_BLACK_MOVE || | ||
2018 | type == PROP_WHITE_MOVE || | ||
2019 | type == PROP_ADD_BLACK || | ||
2020 | type == PROP_ADD_WHITE || | ||
2021 | type == PROP_ADD_EMPTY || | ||
2022 | type == PROP_CIRCLE || type == PROP_SQUARE || type == PROP_TRIANGLE || | ||
2023 | #if 0 | ||
2024 | /* these marks are stupid and nobody uses them. if we could find | ||
2025 | a good way to draw them we could do them anyway, but no reason | ||
2026 | to unless it's easy */ | ||
2027 | type == PROP_DIM || type == PROP_SELECTED || | ||
2028 | #endif | ||
2029 | type == PROP_COMMENT || | ||
2030 | type == PROP_MARK || | ||
2031 | type == PROP_LABEL || | ||
2032 | type == PROP_GAME || | ||
2033 | type == PROP_FILE_FORMAT || | ||
2034 | type == PROP_APPLICATION || | ||
2035 | type == PROP_CHARSET || | ||
2036 | type == PROP_SIZE || | ||
2037 | type == PROP_KOMI || | ||
2038 | type == PROP_BLACK_NAME || | ||
2039 | type == PROP_WHITE_NAME || | ||
2040 | type == PROP_BLACK_RANK || | ||
2041 | type == PROP_WHITE_RANK || | ||
2042 | type == PROP_BLACK_TEAM || | ||
2043 | type == PROP_WHITE_TEAM || | ||
2044 | type == PROP_DATE || | ||
2045 | type == PROP_ROUND || | ||
2046 | type == PROP_EVENT || | ||
2047 | type == PROP_PLACE || | ||
2048 | type == PROP_OVERTIME || | ||
2049 | type == PROP_RESULT || | ||
2050 | type == PROP_TIME_LIMIT || | ||
2051 | type == PROP_RULESET || | ||
2052 | type == PROP_HANDICAP || type == PROP_VARIATION_TYPE) | ||
2053 | { | ||
2054 | return true; | ||
2055 | } | ||
2056 | |||
2057 | return false; | ||
2058 | } | ||
2059 | |||
2060 | void | ||
2061 | setup_handicap_sgf (void) | ||
2062 | { | ||
2063 | union prop_data_t temp_data; | ||
2064 | |||
2065 | if (header.handicap <= 1) | ||
2066 | { | ||
2067 | return; | ||
2068 | } | ||
2069 | |||
2070 | current_node = start_node; | ||
2071 | |||
2072 | temp_data.number = header.handicap; | ||
2073 | add_prop_sgf (current_node, PROP_HANDICAP, temp_data); | ||
2074 | |||
2075 | /* now, add the actual stones */ | ||
2076 | |||
2077 | if ((board_width != 19 && board_width != 13 && board_width != 9) || | ||
2078 | board_width != board_height || header.handicap > 9) | ||
2079 | { | ||
2080 | rb->splashf (5 * HZ, | ||
2081 | "Use the 'Add Black' tool to add %d handicap stones!", | ||
2082 | header.handicap); | ||
2083 | return; | ||
2084 | } | ||
2085 | |||
2086 | int handicaps_to_place = header.handicap; | ||
2087 | |||
2088 | int low_coord = 0, mid_coord = 0, high_coord = 0; | ||
2089 | |||
2090 | if (board_width == 19) | ||
2091 | { | ||
2092 | low_coord = 3; | ||
2093 | mid_coord = 9; | ||
2094 | high_coord = 15; | ||
2095 | } | ||
2096 | else if (board_width == 13) | ||
2097 | { | ||
2098 | low_coord = 3; | ||
2099 | mid_coord = 6; | ||
2100 | high_coord = 9; | ||
2101 | } | ||
2102 | else if (board_width == 9) | ||
2103 | { | ||
2104 | low_coord = 2; | ||
2105 | mid_coord = 4; | ||
2106 | high_coord = 6; | ||
2107 | } | ||
2108 | |||
2109 | /* first four go in the corners */ | ||
2110 | handicaps_to_place -= 2; | ||
2111 | setup_handicap_helper (POS (high_coord, low_coord)); | ||
2112 | setup_handicap_helper (POS (low_coord, high_coord)); | ||
2113 | |||
2114 | if (!handicaps_to_place) | ||
2115 | { | ||
2116 | goto done_adding_stones; | ||
2117 | } | ||
2118 | |||
2119 | --handicaps_to_place; | ||
2120 | setup_handicap_helper (POS (high_coord, high_coord)); | ||
2121 | |||
2122 | if (!handicaps_to_place) | ||
2123 | { | ||
2124 | goto done_adding_stones; | ||
2125 | } | ||
2126 | |||
2127 | --handicaps_to_place; | ||
2128 | setup_handicap_helper (POS (low_coord, low_coord)); | ||
2129 | |||
2130 | if (!handicaps_to_place) | ||
2131 | { | ||
2132 | goto done_adding_stones; | ||
2133 | } | ||
2134 | |||
2135 | /* now done with first four, if only one left it goes in the center */ | ||
2136 | if (handicaps_to_place == 1) | ||
2137 | { | ||
2138 | --handicaps_to_place; | ||
2139 | setup_handicap_helper (POS (mid_coord, mid_coord)); | ||
2140 | } | ||
2141 | else | ||
2142 | { | ||
2143 | handicaps_to_place -= 2; | ||
2144 | setup_handicap_helper (POS (high_coord, mid_coord)); | ||
2145 | setup_handicap_helper (POS (low_coord, mid_coord)); | ||
2146 | } | ||
2147 | |||
2148 | if (!handicaps_to_place) | ||
2149 | { | ||
2150 | goto done_adding_stones; | ||
2151 | } | ||
2152 | |||
2153 | /* done with first 6 */ | ||
2154 | |||
2155 | if (handicaps_to_place == 1) | ||
2156 | { | ||
2157 | --handicaps_to_place; | ||
2158 | setup_handicap_helper (POS (mid_coord, mid_coord)); | ||
2159 | } | ||
2160 | else | ||
2161 | { | ||
2162 | handicaps_to_place -= 2; | ||
2163 | setup_handicap_helper (POS (mid_coord, high_coord)); | ||
2164 | setup_handicap_helper (POS (mid_coord, low_coord)); | ||
2165 | } | ||
2166 | |||
2167 | if (!handicaps_to_place) | ||
2168 | { | ||
2169 | goto done_adding_stones; | ||
2170 | } | ||
2171 | |||
2172 | /* done with first eight, there can only be the tengen remaining */ | ||
2173 | |||
2174 | setup_handicap_helper (POS (mid_coord, mid_coord)); | ||
2175 | |||
2176 | done_adding_stones: | ||
2177 | goto_handicap_start_sgf (); | ||
2178 | return; | ||
2179 | } | ||
2180 | |||
2181 | static void | ||
2182 | setup_handicap_helper (unsigned short pos) | ||
2183 | { | ||
2184 | union prop_data_t temp_data; | ||
2185 | |||
2186 | temp_data.position = pos; | ||
2187 | |||
2188 | add_prop_sgf (current_node, PROP_ADD_BLACK, temp_data); | ||
2189 | } | ||
2190 | |||
2191 | void | ||
2192 | goto_handicap_start_sgf (void) | ||
2193 | { | ||
2194 | if (start_node != tree_head) | ||
2195 | { | ||
2196 | current_node = get_node (start_node)->prev; | ||
2197 | redo_node_sgf (); | ||
2198 | } | ||
2199 | } | ||
2200 | |||
2201 | bool | ||
2202 | post_game_setup_sgf (void) | ||
2203 | { | ||
2204 | int temp_handle = alloc_storage_sgf (); | ||
2205 | int saved = current_node; | ||
2206 | |||
2207 | if (temp_handle < 0) | ||
2208 | { | ||
2209 | return false; | ||
2210 | } | ||
2211 | |||
2212 | union prop_data_t temp_data; | ||
2213 | temp_data.number = 0; /* meaningless */ | ||
2214 | |||
2215 | if (!header_marked) | ||
2216 | { | ||
2217 | add_prop_sgf (tree_head, PROP_ROOT_PROPS, temp_data); | ||
2218 | header_marked = true; | ||
2219 | } | ||
2220 | |||
2221 | get_node (temp_handle)->next = current_node; | ||
2222 | get_node (temp_handle)->prev = NO_NODE; | ||
2223 | get_node (temp_handle)->props = NO_PROP; | ||
2224 | |||
2225 | current_node = temp_handle; | ||
2226 | |||
2227 | redo_node_sgf (); | ||
2228 | |||
2229 | if (current_node == temp_handle) | ||
2230 | { | ||
2231 | current_node = saved; | ||
2232 | } | ||
2233 | |||
2234 | free_storage_sgf (temp_handle); | ||
2235 | |||
2236 | return true; | ||
2237 | } | ||