Add deployment script for Theme Editor.
[kugel-rb.git] / apps / plugins / goban / sgf.c
blob42d4da3365dcb18514c8c9cafea97b905937233e
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007-2009 Joshua Simmons <mud at majidejima dot com>
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
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"
31 int sgf_fd = -1;
32 int unhandled_fd = -1;
34 int tree_head = -1;
35 int current_node = -1;
36 int start_node = -1;
38 bool header_marked = false;
40 static int add_child_variation (int *variation_number);
42 static int get_move_from_node (int handle);
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);
49 static bool do_add_stones (void);
50 static void setup_handicap_helper (unsigned short pos);
52 static int undo_node_helper (void);
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);
57 bool
58 play_move_sgf (unsigned short pos, unsigned char color)
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;
67 if ((color != BLACK && color != WHITE) ||
68 (!on_board (pos) && pos != PASS_POS))
70 return false;
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) */
79 if (get_node (current_node)->next >= 0)
81 current_node = get_node (current_node)->next;
83 /* true means forward */
84 if (goto_next_important_node (true))
86 current_node = get_node (current_node)->prev;
91 if ((temp = get_matching_child_sgf (pos, color)) >= 0)
93 /* don't have to do anything to set up temp as the right variation
94 number */
97 else
99 /* now either there were no children, or none matched the one we
100 want so we have to add a new one */
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 */
107 bool suicide_allowed = false;
109 if (rb->strcmp (header.ruleset, "NZ") == 0 ||
110 rb->strcmp (header.ruleset, "GOE") == 0)
112 suicide_allowed = true;
115 if (play_mode != MODE_FORCE_PLAY &&
116 !legal_move_board (pos, color, suicide_allowed))
118 return false;
121 handle = add_child_sgf (NULL);
123 if (handle < 0)
125 current_node = saved;
126 return false;
129 union prop_data_t temp_prop_data;
130 temp_prop_data.position = pos;
132 prop_handle = add_prop_sgf (handle,
133 color == BLACK ? PROP_BLACK_MOVE :
134 PROP_WHITE_MOVE, temp_prop_data);
136 if (prop_handle < 0)
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;
149 set_game_modified();
151 temp = get_matching_child_sgf (pos, color);
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 */
158 current_node = get_node (current_node)->next;
159 temp_data.number = temp;
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)
166 delete_prop_handle_sgf (current_node, temp2);
169 current_node = saved;
170 return redo_node_sgf ();
173 bool
174 add_mark_sgf (unsigned short pos, enum prop_type_t type)
176 union prop_data_t temp_data;
177 int temp_handle;
178 enum prop_type_t original_type;
180 if (!on_board (pos) || current_node < 0)
182 return false;
185 if (type == PROP_CIRCLE ||
186 type == PROP_SQUARE ||
187 type == PROP_TRIANGLE ||
188 type == PROP_MARK || type == PROP_DIM || type == PROP_SELECTED)
190 temp_data.position = pos;
192 if ((temp_handle = get_prop_pos_sgf (type, temp_data)) >= 0)
194 original_type = get_prop (temp_handle)->type;
195 delete_prop_handle_sgf (current_node, temp_handle);
197 if (type == original_type)
199 set_one_mark (pos, PROP_INVALID);
200 return true;
204 add_prop_sgf (current_node, type, temp_data);
205 set_one_mark (pos, type);
207 return true;
209 else if (type == PROP_LABEL)
211 #define MIN_LABEL 'a'
212 #define MAX_LABEL 'f'
213 int temp_prop_handle = get_node (current_node)->props;
215 while (temp_prop_handle >= 0)
217 struct prop_t *temp_prop = get_prop (temp_prop_handle);
219 if (temp_prop->type == PROP_LABEL &&
220 temp_prop->data.position == pos)
222 char to_set = temp_prop->data.label_extra;
223 ++to_set;
224 if (to_set > MAX_LABEL)
226 delete_prop_handle_sgf (current_node, temp_prop_handle);
227 set_one_mark (pos, PROP_INVALID);
228 return true;
231 temp_prop->data.label_extra = to_set;
232 set_label_mark (pos, to_set);
233 return true;
236 temp_prop_handle = temp_prop->next;
239 temp_data.label_extra = MIN_LABEL;
240 temp_data.position = pos;
242 add_prop_sgf (current_node, type, temp_data);
243 set_label_mark (pos, MIN_LABEL);
244 return true;
246 else
248 return false;
252 bool
253 add_stone_sgf (unsigned short pos, unsigned char color)
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;
263 if (!on_board (pos))
265 return false;
268 if (color == get_point_board (pos))
270 return false;
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)
281 if (color == BLACK)
283 temp_type = PROP_ADD_BLACK;
285 else if (color == WHITE)
287 temp_type = PROP_ADD_WHITE;
289 else
291 temp_type = PROP_ADD_EMPTY;
294 temp_data.position = pos;
296 handle = get_prop_pos_sgf (temp_type, temp_data);
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)
305 temp_data.stone_extra = get_prop (handle)->data.stone_extra;
306 delete_prop_handle_sgf (current_node, handle);
308 else
310 temp_data.stone_extra = 0;
311 if (get_point_board (pos) == EMPTY)
313 temp_data.stone_extra |= FLAG_ORIG_EMPTY;
315 else if (get_point_board (pos) == BLACK)
317 temp_data.stone_extra |= FLAG_ORIG_BLACK;
319 /* else do nothing */
322 /* now we've saved the information about what the board was
323 originally like, we can do the actual set */
325 set_point_board (pos, color);
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))))
336 /* do nothing, set back to original */
338 else
340 /* we're not set back to original, so add a prop for it */
341 add_prop_sgf (current_node, temp_type, temp_data);
344 set_game_modified();
346 return true;
348 else
350 /* we have to make a child variation and add stones in it */
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) */
357 if (get_node (current_node)->next >= 0)
359 current_node = get_node (current_node)->next;
361 /* true means forward */
362 if (goto_next_important_node (true))
364 current_node = get_node (current_node)->prev;
368 handle = add_child_sgf (&var_number);
370 if (handle < 0)
372 rb->splash (2 * HZ, "Out of memory!");
373 return false;
376 temp_data.position = pos;
378 if (color == BLACK)
380 temp_type = PROP_ADD_BLACK;
382 else if (color == WHITE)
384 temp_type = PROP_ADD_WHITE;
386 else
388 temp_type = PROP_ADD_EMPTY;
391 prop_handle = add_prop_sgf (handle, temp_type, temp_data);
393 if (prop_handle < 0)
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;
404 set_game_modified();
406 /* now, "choose" the variation that we just added */
408 current_node = get_node (current_node)->next;
409 temp_data.number = var_number;
411 temp = add_or_set_prop_sgf (current_node,
412 PROP_VARIATION_CHOICE, temp_data);
414 /* free up a superfluous prop */
415 if (var_number == 0)
417 delete_prop_handle_sgf (current_node, temp);
420 current_node = saved;
422 /* and follow to our choice, returning since we already did the
423 work */
424 return redo_node_sgf ();
427 return false;
430 bool
431 undo_node_sgf (void)
433 int result = undo_node_helper ();
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)
443 int backward_move_num = move_num - 1;
444 int saved_current = current_node;
446 while (move_num > backward_move_num)
448 result = undo_node_helper ();
450 if (result < 0)
452 DEBUGF
453 ("couldn't undo to previous move in ko threat handling!\n");
454 return false;
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
462 while (current_node != saved_current)
464 if (!redo_node_sgf ())
466 DEBUGF
467 ("redoing to correct node failed on ko threat handling!\n");
468 return false;
472 else if (result < 0)
474 DEBUGF ("initial undo failed!\n");
475 return false;
478 set_all_marks_sgf ();
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)
485 move_display (get_prop (handle)->data.position);
488 return true;
491 static int
492 undo_node_helper (void)
494 bool ko_threat_move = false;
496 if (current_node == start_node)
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;
506 struct prop_t *temp_move = get_prop (get_move_sgf ());
508 if (temp_move)
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;
516 unsigned short flags = temp_move->data.stone_extra;
518 if (move_pos != PASS_POS)
521 if (flags & FLAG_N_CAP)
523 undone_caps += flood_fill_board (NORTH (move_pos),
524 OTHER (move_color));
526 if (flags & FLAG_S_CAP)
528 undone_caps += flood_fill_board (SOUTH (move_pos),
529 OTHER (move_color));
531 if (flags & FLAG_E_CAP)
533 undone_caps += flood_fill_board (EAST (move_pos),
534 OTHER (move_color));
536 if (flags & FLAG_W_CAP)
538 undone_caps += flood_fill_board (WEST (move_pos),
539 OTHER (move_color));
542 if (flags & FLAG_SELF_CAP)
544 undone_suicides += flood_fill_board (move_pos, move_color);
547 if (flags & FLAG_ORIG_EMPTY)
549 set_point_board (move_pos, EMPTY);
551 else if (flags & FLAG_ORIG_BLACK)
553 set_point_board (move_pos, BLACK);
555 else
557 set_point_board (move_pos, WHITE);
561 if (move_color == BLACK)
563 black_captures -= undone_caps;
564 white_captures -= undone_suicides;
566 else
568 white_captures -= undone_caps;
569 black_captures -= undone_suicides;
572 if (flags & FLAG_KO_THREAT)
574 ko_threat_move = true;
577 --move_num;
578 current_player = OTHER (current_player);
580 else
582 /* test for added stones! */
583 struct prop_t *temp_prop;
585 temp_prop = get_prop (get_node (current_node)->props);
587 while (temp_prop)
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))
594 if (temp_prop->data.stone_extra & FLAG_ORIG_EMPTY)
596 set_point_board (temp_prop->data.position, EMPTY);
598 else if (temp_prop->data.stone_extra & FLAG_ORIG_BLACK)
600 set_point_board (temp_prop->data.position, BLACK);
602 else
604 set_point_board (temp_prop->data.position, WHITE);
608 temp_prop = get_prop (temp_prop->next);
612 if (!retreat_node ())
614 return -1;
617 if (ko_threat_move)
619 return 1;
622 return 0;
625 bool
626 redo_node_sgf (void)
628 if (!advance_node ())
630 return false;
633 set_all_marks_sgf ();
635 int temp_move = get_move_sgf ();
636 if (temp_move >= 0)
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;
643 if (color != current_player)
645 DEBUGF ("redo_node_sgf: wrong color!\n");
648 /* zero out the undo information and set the ko threat flag to the
649 correct value */
651 move_prop->data.stone_extra = 0;
653 if (ko_pos != INVALID_POS)
655 move_prop->data.stone_extra |= FLAG_KO_THREAT;
658 ko_pos = INVALID_POS;
660 if (pos == PASS_POS)
662 rb->splashf (HZ / 2, "%s Passes",
663 color == BLACK ? "Black" : "White");
665 else
667 int n_cap, s_cap, e_cap, w_cap, self_cap;
669 n_cap = s_cap = e_cap = w_cap = self_cap = 0;
671 if (get_point_board (pos) == EMPTY)
673 move_prop->data.stone_extra |= FLAG_ORIG_EMPTY;
675 else if (get_point_board (pos) == BLACK)
677 move_prop->data.stone_extra |= FLAG_ORIG_BLACK;
679 /* else do nothing */
681 set_point_board (pos, color);
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)
688 n_cap = flood_fill_board (NORTH (pos), EMPTY);
689 move_prop->data.stone_extra |= FLAG_N_CAP;
691 if (get_point_board (SOUTH (pos)) == OTHER (color) &&
692 get_liberties_board (SOUTH (pos)) == 0)
694 s_cap = flood_fill_board (SOUTH (pos), EMPTY);
695 move_prop->data.stone_extra |= FLAG_S_CAP;
697 if (get_point_board (EAST (pos)) == OTHER (color) &&
698 get_liberties_board (EAST (pos)) == 0)
700 e_cap = flood_fill_board (EAST (pos), EMPTY);
701 move_prop->data.stone_extra |= FLAG_E_CAP;
703 if (get_point_board (WEST (pos)) == OTHER (color) &&
704 get_liberties_board (WEST (pos)) == 0)
706 w_cap = flood_fill_board (WEST (pos), EMPTY);
707 move_prop->data.stone_extra |= FLAG_W_CAP;
710 /* then check for suicide */
711 if (get_liberties_board (pos) == 0)
713 self_cap = flood_fill_board (pos, EMPTY);
714 move_prop->data.stone_extra |= FLAG_SELF_CAP;
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 */
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)
730 /* We passed all tests, so there is a ko to set. The
731 ko_pos is our single liberty location */
733 if (get_point_board (NORTH (pos)) == EMPTY)
735 ko_pos = NORTH (pos);
737 else if (get_point_board (SOUTH (pos)) == EMPTY)
739 ko_pos = SOUTH (pos);
741 else if (get_point_board (EAST (pos)) == EMPTY)
743 ko_pos = EAST (pos);
745 else
747 ko_pos = WEST (pos);
751 if (color == BLACK)
753 black_captures += n_cap + s_cap + e_cap + w_cap;
754 white_captures += self_cap;
756 else
758 white_captures += n_cap + s_cap + e_cap + w_cap;
759 black_captures += self_cap;
762 /* this will move the cursor near this move if it was off the
763 screen */
764 move_display (pos);
767 ++move_num;
768 current_player = OTHER (color);
770 goto redo_node_sgf_succeeded;
772 else if (do_add_stones ())
774 goto redo_node_sgf_succeeded;
777 return false;
778 char comment_buffer[512];
780 redo_node_sgf_succeeded:
781 #if !defined(GBN_TEST)
782 if (auto_show_comments &&
783 read_comment_sgf (comment_buffer, sizeof (comment_buffer)))
785 unsigned int i;
786 for (i = 0; i < sizeof (comment_buffer); ++i)
788 /* newlines display badly in rb->splash, so replace them
789 * with spaces
791 if (comment_buffer[i] == '\n' ||
792 comment_buffer[i] == '\r')
794 comment_buffer[i] = ' ';
796 else if (comment_buffer[i] == '\0')
798 break;
801 draw_screen_display();
802 rb->splash(HZ / 3, comment_buffer);
803 rb->button_clear_queue();
804 rb->action_userabort(TIMEOUT_BLOCK);
806 #else
807 (void) comment_buffer;
808 #endif
810 return true;
814 mark_child_variations_sgf (void)
816 int result;
817 int saved = current_node;
818 struct node_t *node = get_node (current_node);
820 int move_handle;
822 if (!node)
824 return 0;
827 current_node = node->next;
828 goto_next_important_node (true);
830 result = num_variations_sgf ();
832 if (result > 1)
834 int i;
835 int branch_node = current_node;
836 for (i = 0; i < result; ++i)
838 go_to_variation_sgf (i);
839 goto_next_important_node (true);
841 move_handle = get_move_sgf ();
843 if (move_handle >= 0)
845 set_one_mark (get_prop (move_handle)->data.position,
846 get_prop (move_handle)->type);
849 current_node = branch_node;
853 current_node = saved;
855 return result;
858 void
859 set_all_marks_sgf (void)
861 struct prop_t *prop = get_prop (get_node (current_node)->props);
863 while (prop)
865 if (prop->type == PROP_LABEL)
867 set_label_mark (prop->data.position, prop->data.label_extra);
869 else if (prop->type == PROP_COMMENT)
871 set_comment_display (true);
873 else
875 set_one_mark (prop->data.position, prop->type);
877 prop = get_prop (prop->next);
881 static void
882 set_one_mark (unsigned short pos, enum prop_type_t type)
884 switch (type)
886 case PROP_CIRCLE:
887 set_mark_display (pos, 'c');
888 break;
889 case PROP_SQUARE:
890 set_mark_display (pos, 's');
891 break;
892 case PROP_TRIANGLE:
893 set_mark_display (pos, 't');
894 break;
895 case PROP_MARK:
896 set_mark_display (pos, 'm');
897 break;
898 case PROP_DIM:
899 set_mark_display (pos, 'd');
900 break;
901 case PROP_SELECTED:
902 set_mark_display (pos, 'S');
903 break;
904 case PROP_BLACK_MOVE:
905 set_mark_display (pos, 'b');
906 break;
907 case PROP_WHITE_MOVE:
908 set_mark_display (pos, 'w');
909 break;
910 case PROP_INVALID:
911 set_mark_display (pos, ' ');
912 default:
913 break;
917 static void
918 set_label_mark (unsigned short pos, char to_set)
920 set_mark_display (pos, to_set | (1 << 7));
923 static bool
924 do_add_stones (void)
926 bool ret_val = false;
927 struct prop_t *temp_prop;
928 int temp_handle;
930 if (current_node < 0)
932 return false;
935 temp_handle = get_node (current_node)->props;
936 temp_prop = get_prop (temp_handle);
938 while (temp_prop)
940 if (temp_prop->type == PROP_ADD_BLACK ||
941 temp_prop->type == PROP_ADD_WHITE ||
942 temp_prop->type == PROP_ADD_EMPTY)
945 temp_prop->data.stone_extra = 0;
947 /* TODO: we could delete do-nothing PROP_ADD_*s here */
949 if (get_point_board (temp_prop->data.position) == EMPTY)
951 temp_prop->data.stone_extra |= FLAG_ORIG_EMPTY;
953 else if (get_point_board (temp_prop->data.position) == BLACK)
955 temp_prop->data.stone_extra |= FLAG_ORIG_BLACK;
957 /* else, do nothing */
959 if (temp_prop->type == PROP_ADD_BLACK ||
960 temp_prop->type == PROP_ADD_WHITE)
962 set_point_board (temp_prop->data.position,
963 temp_prop->type == PROP_ADD_BLACK ? BLACK :
964 WHITE);
965 ret_val = true;
967 else
969 set_point_board (temp_prop->data.position, EMPTY);
971 ret_val = true;
975 temp_handle = temp_prop->next;
976 temp_prop = get_prop (temp_handle);
979 return ret_val;
983 add_child_sgf (int *variation_number)
985 int node_handle;
986 struct node_t *node;
987 struct node_t *current = get_node (current_node);
989 if (current->next < 0)
991 node_handle = alloc_storage_sgf ();
992 node = get_node (node_handle);
994 if (node_handle < 0 || !current)
996 return NO_NODE;
999 node->prev = current_node;
1000 node->next = NO_NODE;
1001 node->props = NO_PROP;
1003 current->next = node_handle;
1005 if (variation_number)
1007 *variation_number = 0;
1010 return node_handle;
1012 else
1014 return add_child_variation (variation_number);
1019 add_prop_sgf (int node, enum prop_type_t type, union prop_data_t data)
1021 int new_prop;
1022 int temp_prop_handle;
1024 if (node < 0)
1026 return NO_PROP;
1029 new_prop = alloc_storage_sgf ();
1031 if (new_prop < 0)
1033 return NO_PROP;
1036 get_prop (new_prop)->type = type;
1037 get_prop (new_prop)->data = data;
1039 /* check if the new_prop goes at the start */
1040 if (get_node (node)->props == NO_PROP ||
1041 (type == PROP_VARIATION &&
1042 get_prop (get_node (node)->props)->type != PROP_VARIATION))
1045 if (get_node (node)->props >= 0)
1047 get_prop (new_prop)->next = get_node (node)->props;
1049 else
1051 get_prop (new_prop)->next = NO_PROP;
1054 get_node (node)->props = new_prop;
1055 return new_prop;
1058 temp_prop_handle = get_node (node)->props;
1060 while (1)
1062 if (get_prop (temp_prop_handle)->next < 0 ||
1063 (get_prop (temp_prop_handle)->type == type &&
1064 get_prop (get_prop (temp_prop_handle)->next)->type != type))
1066 /* new_prop goes after the current one either because we're at
1067 the end of the props list, or because we're adding a prop
1068 after the ones of its same type */
1069 get_prop (new_prop)->next = get_prop (temp_prop_handle)->next;
1070 get_prop (temp_prop_handle)->next = new_prop;
1072 return new_prop;
1075 temp_prop_handle = get_prop (temp_prop_handle)->next;
1080 get_prop_pos_sgf (enum prop_type_t type, union prop_data_t data)
1082 int temp_prop_handle;
1083 struct prop_t *prop;
1085 if (current_node < 0)
1087 return -1;
1090 temp_prop_handle = get_node (current_node)->props;
1092 while (temp_prop_handle >= 0)
1094 prop = get_prop (temp_prop_handle);
1096 if ((type == PROP_ADD_BLACK ||
1097 type == PROP_ADD_WHITE ||
1098 type == PROP_ADD_EMPTY)
1100 (prop->type == PROP_ADD_BLACK ||
1101 prop->type == PROP_ADD_WHITE ||
1102 prop->type == PROP_ADD_EMPTY)
1103 && (prop->data.position == data.position))
1105 return temp_prop_handle;
1108 if ((type == PROP_CIRCLE ||
1109 type == PROP_SQUARE ||
1110 type == PROP_TRIANGLE ||
1111 type == PROP_MARK ||
1112 type == PROP_DIM ||
1113 type == PROP_SELECTED) &&
1114 (prop->type == PROP_CIRCLE ||
1115 prop->type == PROP_SQUARE ||
1116 prop->type == PROP_TRIANGLE ||
1117 prop->type == PROP_MARK ||
1118 prop->type == PROP_DIM ||
1119 prop->type == PROP_SELECTED) &&
1120 (prop->data.position == data.position))
1122 return temp_prop_handle;
1125 temp_prop_handle = get_prop (temp_prop_handle)->next;
1128 return -1;
1132 add_or_set_prop_sgf (int node, enum prop_type_t type, union prop_data_t data)
1134 int temp_prop_handle;
1136 if (node < 0)
1138 return NO_PROP;
1141 temp_prop_handle = get_prop_sgf (node, type, NULL);
1143 if (temp_prop_handle >= 0)
1145 get_prop (temp_prop_handle)->data = data;
1146 return temp_prop_handle;
1149 /* there was no prop to set, so we need to add one */
1150 return add_prop_sgf (node, type, data);
1154 get_prop_sgf (int node, enum prop_type_t type, int *previous_prop)
1156 int previous_handle = NO_PROP;
1157 int current_handle;
1159 if (node < 0)
1161 return NO_PROP;
1164 if (get_node (node)->props < 0)
1166 return NO_PROP;
1169 current_handle = get_node (node)->props;
1171 while (current_handle >= 0)
1173 if (get_prop (current_handle)->type == type || type == PROP_ANY)
1175 if (previous_prop)
1177 *previous_prop = previous_handle;
1180 return current_handle;
1182 else
1184 previous_handle = current_handle;
1185 current_handle = get_prop (current_handle)->next;
1189 return NO_PROP;
1192 bool
1193 delete_prop_sgf (int node, enum prop_type_t type)
1195 if (node < 0)
1197 return false;
1200 return delete_prop_handle_sgf (node, get_prop_sgf (node, type, NULL));
1203 bool
1204 delete_prop_handle_sgf (int node, int prop)
1206 int previous;
1208 if (prop < 0 || node < 0 || get_node (node)->props < 0)
1210 return false;
1213 if (get_node (node)->props == prop)
1215 get_node (node)->props = get_prop (get_node (node)->props)->next;
1216 free_storage_sgf (prop);
1217 return true;
1220 previous = get_node (node)->props;
1222 while (get_prop (previous)->next != prop && get_prop (previous)->next >= 0)
1224 previous = get_prop (previous)->next;
1227 if (get_prop (previous)->next < 0)
1229 return false;
1231 else
1233 get_prop (previous)->next = get_prop (get_prop (previous)->next)->next;
1234 free_storage_sgf (prop);
1235 return true;
1240 read_comment_sgf (char *buffer, size_t buffer_size)
1242 size_t bytes_read = 0;
1244 int prop_handle = get_prop_sgf (current_node, PROP_COMMENT, NULL);
1246 if (prop_handle < 0)
1248 return 0;
1251 if (unhandled_fd < 0)
1253 DEBUGF ("unhandled file is closed?!\n");
1254 return 0;
1257 if (rb->lseek (unhandled_fd, get_prop (prop_handle)->data.number,
1258 SEEK_SET) < 0)
1260 DEBUGF ("couldn't seek in unhandled_fd\n");
1261 return -1;
1264 if (!read_char_no_whitespace (unhandled_fd) == 'C' ||
1265 !read_char_no_whitespace (unhandled_fd) == '[')
1267 DEBUGF ("comment prop points to incorrect place in unhandled_fd!!\n");
1268 return -1;
1271 /* make output a string, the lazy way */
1272 rb->memset (buffer, 0, buffer_size);
1273 ++bytes_read;
1275 bool done = false;
1276 bool escaped = false;
1278 while ((buffer_size > bytes_read) && !done)
1280 int temp = read_char (unhandled_fd);
1282 switch (temp)
1284 case ']':
1285 if (!escaped)
1287 done = true;
1288 break;
1290 *buffer = temp;
1291 ++buffer;
1292 ++bytes_read;
1293 escaped = false;
1294 break;
1296 case -1:
1297 DEBUGF ("encountered end of file before end of comment!\n");
1298 done = true;
1299 break;
1301 case '\\':
1302 escaped = !escaped;
1303 if (escaped)
1305 break;
1308 default:
1309 *buffer = temp;
1310 ++buffer;
1311 ++bytes_read;
1312 escaped = false;
1313 break;
1317 return bytes_read;
1321 write_comment_sgf (char *string)
1323 char *orig_string = string;
1325 int prop_handle = get_prop_sgf (current_node, PROP_COMMENT, NULL);
1327 int start_of_comment = -1;
1328 bool overwriting = false;
1330 int bytes_written = 0;
1332 set_game_modified();
1334 if (unhandled_fd < 0)
1336 DEBUGF ("unhandled file is closed?!\n");
1337 return 0;
1340 if (prop_handle >= 0)
1342 if ((start_of_comment = rb->lseek (unhandled_fd,
1343 get_prop (prop_handle)->data.number,
1344 SEEK_SET)) < 0)
1346 DEBUGF ("couldn't seek in unhandled_fd\n");
1347 return -1;
1349 else
1351 overwriting = true;
1354 else
1356 overwriting = false;
1359 start_of_write_wcs:
1361 if (overwriting)
1363 if (!read_char_no_whitespace (unhandled_fd) == 'C' ||
1364 !read_char_no_whitespace (unhandled_fd) == '[')
1366 DEBUGF ("non-comment while overwriting!!\n");
1367 return -1;
1370 else
1372 start_of_comment = rb->lseek (unhandled_fd, 0, SEEK_END);
1374 if (start_of_comment < 0)
1376 DEBUGF ("error seeking to end in write_comment_sgf\n");
1377 return -1;
1380 write_char (unhandled_fd, 'C');
1381 write_char (unhandled_fd, '[');
1385 bool overwrite_escaped = false;
1387 while (*string)
1389 if (overwriting)
1391 int orig_char = peek_char (unhandled_fd);
1393 switch (orig_char)
1395 case '\\':
1396 overwrite_escaped = !overwrite_escaped;
1397 break;
1399 case ']':
1400 if (overwrite_escaped)
1402 overwrite_escaped = false;
1403 break;
1405 /* otherwise, fall through */
1407 case -1:
1409 /* we reached the end of the part we can put our comment
1410 in, but there's more comment to write, so we should
1411 start again, this time making a new comment (the old
1412 becomes wasted space in unhandled_fd, but it doesn't
1413 really hurt anything except extra space on disk */
1415 overwriting = false;
1416 string = orig_string;
1417 bytes_written = 0;
1418 goto start_of_write_wcs;
1419 break;
1421 default:
1422 overwrite_escaped = false;
1423 break;
1427 switch (*string)
1429 case '\\':
1430 case ']':
1431 write_char (unhandled_fd, '\\');
1433 /* fall through */
1435 default:
1436 write_char (unhandled_fd, *string);
1437 break;
1440 ++string;
1441 ++bytes_written;
1444 /* finish out the record */
1445 write_char (unhandled_fd, ']');
1446 write_char (unhandled_fd, ';');
1448 /* and put the reference into the unhandled_fd into the comment prop */
1449 union prop_data_t temp_data;
1450 temp_data.number = start_of_comment;
1452 add_or_set_prop_sgf (current_node, PROP_COMMENT, temp_data);
1453 set_comment_display (true);
1454 return bytes_written;
1457 bool
1458 has_more_nodes_sgf (void)
1460 int saved = current_node;
1461 bool ret_val = false;
1463 if (current_node < 0)
1465 return false;
1468 current_node = get_node (current_node)->next;
1470 if (current_node >= 0)
1472 /* returns true if it finds an important node */
1473 ret_val = goto_next_important_node (true);
1476 current_node = saved;
1477 return ret_val;
1480 /* logic is different here because the first node in a tree is a valid
1481 place to go */
1482 bool
1483 has_prev_nodes_sgf (void)
1485 if (current_node < 0)
1487 return false;
1490 return current_node != start_node;
1494 get_move_sgf (void)
1496 int result = -1;
1497 int saved_current = current_node;
1499 goto_next_important_node (true);
1501 result = get_move_from_node (current_node);
1503 current_node = saved_current;
1504 return result;
1507 static int
1508 get_move_from_node (int handle)
1510 int prop_handle;
1512 if (handle < 0)
1514 return -2;
1517 prop_handle = get_node (handle)->props;
1520 while (prop_handle >= 0)
1522 if (get_prop (prop_handle)->type == PROP_BLACK_MOVE ||
1523 get_prop (prop_handle)->type == PROP_WHITE_MOVE)
1525 return prop_handle;
1528 prop_handle = get_prop (prop_handle)->next;
1531 return -1;
1534 static bool
1535 retreat_node (void)
1537 int result = get_node (current_node)->prev;
1539 if (current_node == start_node)
1541 return false;
1544 if (result < 0)
1546 return false;
1548 else
1550 clear_marks_display ();
1552 current_node = result;
1554 /* go backwards to the next important node (move/add
1555 stone/variation/etc.) */
1556 goto_next_important_node (false);
1557 return true;
1561 static bool
1562 advance_node (void)
1564 int result = get_node (current_node)->next;
1566 if (result < 0)
1568 return false;
1570 else
1572 clear_marks_display ();
1574 current_node = result;
1576 /* go forward to the next move/add stone/variation/etc. node */
1577 goto_next_important_node (true);
1578 result = get_prop_sgf (current_node, PROP_VARIATION_CHOICE, NULL);
1580 if (result >= 0)
1582 go_to_variation_sgf (get_prop (result)->data.number);
1585 return true;
1590 num_variations_sgf (void)
1592 int result = 1;
1593 struct prop_t *temp_prop;
1594 struct node_t *temp_node = get_node (current_node);
1596 if (temp_node == 0)
1598 return 0;
1601 if (temp_node->prev >= 0)
1603 temp_node = get_node (get_node (temp_node->prev)->next);
1606 temp_prop = get_prop (temp_node->props);
1608 while (temp_prop)
1610 if (temp_prop->type == PROP_VARIATION)
1612 ++result;
1614 else
1616 /* variations are at the beginning of the prop list */
1617 break;
1620 temp_prop = get_prop (temp_prop->next);
1623 return result;
1626 bool
1627 go_to_variation_sgf (unsigned int num)
1629 int saved = current_node;
1630 struct node_t *temp_node = get_node (current_node);
1631 struct prop_t *temp_prop;
1633 if (!temp_node)
1635 return false;
1638 temp_node = get_node (temp_node->prev);
1640 if (!temp_node)
1642 return false;
1645 temp_node = get_node (current_node = temp_node->next);
1647 if (!temp_node)
1649 current_node = saved;
1650 return false;
1653 temp_prop = get_prop (temp_node->props);
1655 while (num)
1657 if (!temp_prop || temp_prop->type != PROP_VARIATION)
1659 current_node = saved;
1660 return false;
1663 if (num == 1)
1665 current_node = temp_prop->data.node;
1666 break;
1669 temp_prop = get_prop (temp_prop->next);
1670 --num;
1673 return true;
1677 get_matching_child_sgf (unsigned short pos, unsigned char color)
1679 struct node_t *temp_node;
1680 struct prop_t *temp_prop;
1681 int variation_count = 0;
1682 int saved;
1684 /* set true later in a loop if we want a prop in the first variation
1685 which means that we shouldn't check that branch for children */
1686 bool dont_check_first_var_children = false;
1688 temp_node = get_node (current_node);
1690 if (!temp_node)
1692 return -3;
1695 temp_node = get_node (temp_node->next);
1697 if (!temp_node)
1699 return -2;
1702 temp_prop = get_prop (temp_node->props);
1704 while (temp_prop)
1706 if (temp_prop->type == PROP_VARIATION)
1708 ++variation_count;
1709 saved = current_node;
1710 current_node = temp_prop->data.node;
1712 struct prop_t *temp_move = get_prop (get_move_sgf ());
1714 current_node = saved;
1716 if (temp_move &&
1717 temp_move->data.position == pos &&
1718 ((color == BLACK && temp_move->type == PROP_BLACK_MOVE) ||
1719 (color == WHITE && temp_move->type == PROP_WHITE_MOVE)))
1721 return variation_count;
1724 else if ((temp_prop->type == PROP_BLACK_MOVE && color == BLACK) ||
1725 (temp_prop->type == PROP_WHITE_MOVE && color == WHITE))
1727 if (temp_prop->data.position == pos)
1729 return 0;
1731 else
1733 return -4;
1736 else if (temp_prop->type == PROP_ADD_WHITE ||
1737 temp_prop->type == PROP_ADD_BLACK ||
1738 temp_prop->type == PROP_ADD_EMPTY)
1740 dont_check_first_var_children = true;
1743 temp_prop = get_prop (temp_prop->next);
1746 if (dont_check_first_var_children)
1748 return -1;
1750 else
1752 saved = current_node;
1753 current_node = temp_node->next;
1755 struct prop_t *temp_move = get_prop (get_move_sgf ());
1756 if (temp_move &&
1757 pos == temp_move->data.position &&
1758 color == (temp_move->type == PROP_BLACK_MOVE ? BLACK : WHITE))
1760 current_node = saved;
1761 return 0;
1764 current_node = saved;
1765 return -1;
1770 next_variation_sgf (void)
1772 int saved = current_node;
1773 int saved_start = start_node;
1774 union prop_data_t temp_data;
1775 int prop_handle;
1776 int num_vars = 0;
1778 if (current_node < 0 || get_node (current_node)->prev < 0)
1780 return -1;
1783 start_node = NO_NODE;
1787 if (num_variations_sgf () < 2)
1790 current_node = saved;
1791 start_node = saved_start;
1792 return -2;
1795 /* now we're at a branch node which we should go to the next variation
1796 (we were at the "chosen" one, so go to the next one after that,
1797 (mod the number of variations)) */
1799 int chosen = 0;
1800 int branch_node = get_node (get_node (current_node)->prev)->next;
1802 prop_handle = get_prop_sgf (branch_node, PROP_VARIATION_CHOICE, NULL);
1804 if (prop_handle >= 0)
1806 chosen = get_prop (prop_handle)->data.number;
1809 ++chosen;
1811 if (chosen >= (num_vars = num_variations_sgf ()))
1813 chosen = 0;
1816 temp_data.number = chosen;
1817 add_or_set_prop_sgf (branch_node, PROP_VARIATION_CHOICE, temp_data);
1819 if (!undo_node_sgf ())
1821 current_node = saved;
1822 start_node = saved_start;
1823 return -3;
1826 if (redo_node_sgf ())
1828 start_node = saved_start;
1829 return chosen + 1;
1831 else
1833 current_node = saved;
1834 start_node = saved_start;
1835 return -4;
1840 add_child_variation (int *variation_number)
1842 struct node_t *temp_node = get_node (current_node);
1843 struct prop_t *temp_prop;
1844 int temp_prop_handle;
1845 int new_node = alloc_storage_sgf ();
1846 int new_prop = alloc_storage_sgf ();
1847 int temp_variation_number;
1849 if (new_node < 0 || new_prop < 0)
1851 if (new_node >= 0)
1853 free_storage_sgf (new_node);
1855 if (new_prop >= 0)
1857 free_storage_sgf (new_prop);
1860 return NO_NODE;
1863 if (!temp_node)
1865 free_storage_sgf (new_node);
1866 free_storage_sgf (new_prop);
1868 return NO_NODE;
1871 temp_node = get_node (temp_node->next);
1873 if (!temp_node)
1875 free_storage_sgf (new_node);
1876 free_storage_sgf (new_prop);
1878 return NO_NODE;
1881 get_node (new_node)->prev = current_node;
1882 get_node (new_node)->next = NO_NODE;
1883 get_node (new_node)->props = NO_PROP;
1885 get_prop (new_prop)->type = PROP_VARIATION;
1886 get_prop (new_prop)->next = NO_PROP;
1887 get_prop (new_prop)->data.node = new_node;
1889 temp_prop_handle = temp_node->props;
1891 if (temp_prop_handle < 0)
1893 temp_node->props = new_prop;
1895 if (variation_number)
1897 *variation_number = 1;
1900 return new_node;
1903 if (get_prop (temp_prop_handle)->type != PROP_VARIATION)
1905 get_prop (new_prop)->next = temp_node->props;
1906 temp_node->props = new_prop;
1908 if (variation_number)
1910 *variation_number = 1;
1913 return new_node;
1916 /* the lowest it can be, since 1 isn't it */
1917 temp_variation_number = 2;
1919 while (1)
1921 temp_prop = get_prop (temp_prop_handle);
1922 if (temp_prop->next < 0 ||
1923 get_prop (temp_prop->next)->type != PROP_VARIATION)
1925 get_prop (new_prop)->next = temp_prop->next;
1926 temp_prop->next = new_prop;
1928 if (variation_number)
1930 *variation_number = temp_variation_number;
1933 return new_node;
1936 ++temp_variation_number;
1937 temp_prop_handle = temp_prop->next;
1941 static bool
1942 is_important_node (int handle)
1944 struct prop_t *temp_prop;
1946 if (handle < 0)
1948 return false;
1951 if (handle == start_node)
1953 return true;
1956 if (get_node (handle)->prev < 0)
1958 return true;
1961 temp_prop = get_prop (get_node (handle)->props);
1963 while (temp_prop)
1965 if (temp_prop->type == PROP_BLACK_MOVE ||
1966 temp_prop->type == PROP_WHITE_MOVE ||
1967 temp_prop->type == PROP_ADD_BLACK ||
1968 temp_prop->type == PROP_ADD_WHITE ||
1969 temp_prop->type == PROP_ADD_EMPTY ||
1970 temp_prop->type == PROP_VARIATION)
1972 return true;
1975 temp_prop = get_prop (temp_prop->next);
1978 return false;
1981 static bool
1982 goto_next_important_node (bool forward)
1984 int temp_node = current_node;
1985 int last_good = temp_node;
1987 while (temp_node >= 0 && !is_important_node (temp_node))
1989 last_good = temp_node;
1991 temp_node = forward ? get_node (temp_node)->next :
1992 get_node (temp_node)->prev;
1996 if (temp_node < 0)
1998 current_node = last_good;
2000 else
2002 current_node = temp_node;
2005 if (temp_node < 0)
2007 return false;
2009 else
2011 return true;
2015 bool
2016 is_handled_sgf (enum prop_type_t type)
2018 if (type == PROP_BLACK_MOVE ||
2019 type == PROP_WHITE_MOVE ||
2020 type == PROP_ADD_BLACK ||
2021 type == PROP_ADD_WHITE ||
2022 type == PROP_ADD_EMPTY ||
2023 type == PROP_CIRCLE || type == PROP_SQUARE || type == PROP_TRIANGLE ||
2024 #if 0
2025 /* these marks are stupid and nobody uses them. if we could find
2026 a good way to draw them we could do them anyway, but no reason
2027 to unless it's easy */
2028 type == PROP_DIM || type == PROP_SELECTED ||
2029 #endif
2030 type == PROP_COMMENT ||
2031 type == PROP_MARK ||
2032 type == PROP_LABEL ||
2033 type == PROP_GAME ||
2034 type == PROP_FILE_FORMAT ||
2035 type == PROP_APPLICATION ||
2036 type == PROP_CHARSET ||
2037 type == PROP_SIZE ||
2038 type == PROP_KOMI ||
2039 type == PROP_BLACK_NAME ||
2040 type == PROP_WHITE_NAME ||
2041 type == PROP_BLACK_RANK ||
2042 type == PROP_WHITE_RANK ||
2043 type == PROP_BLACK_TEAM ||
2044 type == PROP_WHITE_TEAM ||
2045 type == PROP_DATE ||
2046 type == PROP_ROUND ||
2047 type == PROP_EVENT ||
2048 type == PROP_PLACE ||
2049 type == PROP_OVERTIME ||
2050 type == PROP_RESULT ||
2051 type == PROP_TIME_LIMIT ||
2052 type == PROP_RULESET ||
2053 type == PROP_HANDICAP || type == PROP_VARIATION_TYPE)
2055 return true;
2058 return false;
2061 void
2062 setup_handicap_sgf (void)
2064 union prop_data_t temp_data;
2066 if (header.handicap <= 1)
2068 return;
2071 current_node = start_node;
2073 temp_data.number = header.handicap;
2074 add_prop_sgf (current_node, PROP_HANDICAP, temp_data);
2076 /* now, add the actual stones */
2078 if ((board_width != 19 && board_width != 13 && board_width != 9) ||
2079 board_width != board_height || header.handicap > 9)
2081 rb->splashf (5 * HZ,
2082 "Use the 'Add Black' tool to add %d handicap stones!",
2083 header.handicap);
2084 return;
2087 int handicaps_to_place = header.handicap;
2089 int low_coord = 0, mid_coord = 0, high_coord = 0;
2091 if (board_width == 19)
2093 low_coord = 3;
2094 mid_coord = 9;
2095 high_coord = 15;
2097 else if (board_width == 13)
2099 low_coord = 3;
2100 mid_coord = 6;
2101 high_coord = 9;
2103 else if (board_width == 9)
2105 low_coord = 2;
2106 mid_coord = 4;
2107 high_coord = 6;
2110 /* first four go in the corners */
2111 handicaps_to_place -= 2;
2112 setup_handicap_helper (POS (high_coord, low_coord));
2113 setup_handicap_helper (POS (low_coord, high_coord));
2115 if (!handicaps_to_place)
2117 goto done_adding_stones;
2120 --handicaps_to_place;
2121 setup_handicap_helper (POS (high_coord, high_coord));
2123 if (!handicaps_to_place)
2125 goto done_adding_stones;
2128 --handicaps_to_place;
2129 setup_handicap_helper (POS (low_coord, low_coord));
2131 if (!handicaps_to_place)
2133 goto done_adding_stones;
2136 /* now done with first four, if only one left it goes in the center */
2137 if (handicaps_to_place == 1)
2139 --handicaps_to_place;
2140 setup_handicap_helper (POS (mid_coord, mid_coord));
2142 else
2144 handicaps_to_place -= 2;
2145 setup_handicap_helper (POS (high_coord, mid_coord));
2146 setup_handicap_helper (POS (low_coord, mid_coord));
2149 if (!handicaps_to_place)
2151 goto done_adding_stones;
2154 /* done with first 6 */
2156 if (handicaps_to_place == 1)
2158 --handicaps_to_place;
2159 setup_handicap_helper (POS (mid_coord, mid_coord));
2161 else
2163 handicaps_to_place -= 2;
2164 setup_handicap_helper (POS (mid_coord, high_coord));
2165 setup_handicap_helper (POS (mid_coord, low_coord));
2168 if (!handicaps_to_place)
2170 goto done_adding_stones;
2173 /* done with first eight, there can only be the tengen remaining */
2175 setup_handicap_helper (POS (mid_coord, mid_coord));
2177 done_adding_stones:
2178 goto_handicap_start_sgf ();
2179 return;
2182 static void
2183 setup_handicap_helper (unsigned short pos)
2185 union prop_data_t temp_data;
2187 temp_data.position = pos;
2189 add_prop_sgf (current_node, PROP_ADD_BLACK, temp_data);
2192 void
2193 goto_handicap_start_sgf (void)
2195 if (start_node != tree_head)
2197 current_node = get_node (start_node)->prev;
2198 redo_node_sgf ();
2202 bool
2203 post_game_setup_sgf (void)
2205 int temp_handle = alloc_storage_sgf ();
2206 int saved = current_node;
2208 if (temp_handle < 0)
2210 return false;
2213 union prop_data_t temp_data;
2214 temp_data.number = 0; /* meaningless */
2216 if (!header_marked)
2218 add_prop_sgf (tree_head, PROP_ROOT_PROPS, temp_data);
2219 header_marked = true;
2222 get_node (temp_handle)->next = current_node;
2223 get_node (temp_handle)->prev = NO_NODE;
2224 get_node (temp_handle)->props = NO_PROP;
2226 current_node = temp_handle;
2228 redo_node_sgf ();
2230 if (current_node == temp_handle)
2232 current_node = saved;
2235 free_storage_sgf (temp_handle);
2237 return true;