1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
23 #include "sgf_storage.h"
32 int unhandled_fd
= -1;
35 int current_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
);
58 play_move_sgf (unsigned short pos
, unsigned char color
)
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
))
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
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
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
))
121 handle
= add_child_sgf (NULL
);
125 current_node
= saved
;
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
);
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
142 free_storage_sgf(handle); */
144 "Out of memory led to invalid state. Please exit.");
145 current_node
= saved
;
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
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 */
166 delete_prop_handle_sgf (current_node
, temp2
);
169 current_node
= saved
;
170 return redo_node_sgf ();
174 add_mark_sgf (unsigned short pos
, enum prop_type_t type
)
176 union prop_data_t temp_data
;
178 enum prop_type_t original_type
;
180 if (!on_board (pos
) || current_node
< 0)
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
);
204 add_prop_sgf (current_node
, type
, temp_data
);
205 set_one_mark (pos
, type
);
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
;
224 if (to_set
> MAX_LABEL
)
226 delete_prop_handle_sgf (current_node
, temp_prop_handle
);
227 set_one_mark (pos
, PROP_INVALID
);
231 temp_prop
->data
.label_extra
= to_set
;
232 set_label_mark (pos
, to_set
);
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
);
253 add_stone_sgf (unsigned short pos
, unsigned char color
)
257 int saved
= current_node
;
258 union prop_data_t temp_data
;
259 enum prop_type_t temp_type
;
268 if (color
== get_point_board (pos
))
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)
283 temp_type
= PROP_ADD_BLACK
;
285 else if (color
== WHITE
)
287 temp_type
= PROP_ADD_WHITE
;
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 */
305 temp_data
.stone_extra
= get_prop (handle
)->data
.stone_extra
;
306 delete_prop_handle_sgf (current_node
, handle
);
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 */
340 /* we're not set back to original, so add a prop for it */
341 add_prop_sgf (current_node
, temp_type
, temp_data
);
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
);
372 rb
->splash (2 * HZ
, "Out of memory!");
376 temp_data
.position
= pos
;
380 temp_type
= PROP_ADD_BLACK
;
382 else if (color
== WHITE
)
384 temp_type
= PROP_ADD_WHITE
;
388 temp_type
= PROP_ADD_EMPTY
;
391 prop_handle
= add_prop_sgf (handle
, temp_type
, temp_data
);
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
399 free_storage_sgf(handle); */
400 rb
->splash (2 * HZ
, "Out of memory!");
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 */
417 delete_prop_handle_sgf (current_node
, temp
);
420 current_node
= saved
;
422 /* and follow to our choice, returning since we already did the
424 return redo_node_sgf ();
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). */
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 ();
453 ("couldn't undo to previous move in ko threat handling!\n");
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 ())
467 ("redoing to correct node failed on ko threat handling!\n");
474 DEBUGF ("initial undo failed!\n");
478 set_all_marks_sgf ();
480 /* if there is a move in this node, move the screen so that it is
482 int handle
= get_move_sgf ();
485 move_display (get_prop (handle
)->data
.position
);
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");
506 struct prop_t
*temp_move
= get_prop (get_move_sgf ());
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
:
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
),
526 if (flags
& FLAG_S_CAP
)
528 undone_caps
+= flood_fill_board (SOUTH (move_pos
),
531 if (flags
& FLAG_E_CAP
)
533 undone_caps
+= flood_fill_board (EAST (move_pos
),
536 if (flags
& FLAG_W_CAP
)
538 undone_caps
+= flood_fill_board (WEST (move_pos
),
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
);
557 set_point_board (move_pos
, WHITE
);
561 if (move_color
== BLACK
)
563 black_captures
-= undone_caps
;
564 white_captures
-= undone_suicides
;
568 white_captures
-= undone_caps
;
569 black_captures
-= undone_suicides
;
572 if (flags
& FLAG_KO_THREAT
)
574 ko_threat_move
= true;
578 current_player
= OTHER (current_player
);
582 /* test for added stones! */
583 struct prop_t
*temp_prop
;
585 temp_prop
= get_prop (get_node (current_node
)->props
);
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
);
604 set_point_board (temp_prop
->data
.position
, WHITE
);
608 temp_prop
= get_prop (temp_prop
->next
);
612 if (!retreat_node ())
628 if (!advance_node ())
633 set_all_marks_sgf ();
635 int temp_move
= get_move_sgf ();
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
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
;
662 rb
->splashf (HZ
/ 2, "%s Passes",
663 color
== BLACK
? "Black" : "White");
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 */
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
)
753 black_captures
+= n_cap
+ s_cap
+ e_cap
+ w_cap
;
754 white_captures
+= self_cap
;
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
768 current_player
= OTHER (color
);
770 goto redo_node_sgf_succeeded
;
772 else if (do_add_stones ())
774 goto redo_node_sgf_succeeded
;
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
)))
786 for (i
= 0; i
< sizeof (comment_buffer
); ++i
)
788 /* newlines display badly in rb->splash, so replace them
791 if (comment_buffer
[i
] == '\n' ||
792 comment_buffer
[i
] == '\r')
794 comment_buffer
[i
] = ' ';
796 else if (comment_buffer
[i
] == '\0')
801 draw_screen_display();
802 rb
->splash(HZ
/ 3, comment_buffer
);
803 rb
->button_clear_queue();
804 rb
->action_userabort(TIMEOUT_BLOCK
);
807 (void) comment_buffer
;
814 mark_child_variations_sgf (void)
817 int saved
= current_node
;
818 struct node_t
*node
= get_node (current_node
);
827 current_node
= node
->next
;
828 goto_next_important_node (true);
830 result
= num_variations_sgf ();
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
;
859 set_all_marks_sgf (void)
861 struct prop_t
*prop
= get_prop (get_node (current_node
)->props
);
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);
875 set_one_mark (prop
->data
.position
, prop
->type
);
877 prop
= get_prop (prop
->next
);
882 set_one_mark (unsigned short pos
, enum prop_type_t type
)
887 set_mark_display (pos
, 'c');
890 set_mark_display (pos
, 's');
893 set_mark_display (pos
, 't');
896 set_mark_display (pos
, 'm');
899 set_mark_display (pos
, 'd');
902 set_mark_display (pos
, 'S');
904 case PROP_BLACK_MOVE
:
905 set_mark_display (pos
, 'b');
907 case PROP_WHITE_MOVE
:
908 set_mark_display (pos
, 'w');
911 set_mark_display (pos
, ' ');
918 set_label_mark (unsigned short pos
, char to_set
)
920 set_mark_display (pos
, to_set
| (1 << 7));
926 bool ret_val
= false;
927 struct prop_t
*temp_prop
;
930 if (current_node
< 0)
935 temp_handle
= get_node (current_node
)->props
;
936 temp_prop
= get_prop (temp_handle
);
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
:
969 set_point_board (temp_prop
->data
.position
, EMPTY
);
975 temp_handle
= temp_prop
->next
;
976 temp_prop
= get_prop (temp_handle
);
983 add_child_sgf (int *variation_number
)
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
)
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;
1014 return add_child_variation (variation_number
);
1019 add_prop_sgf (int node
, enum prop_type_t type
, union prop_data_t data
)
1022 int temp_prop_handle
;
1029 new_prop
= alloc_storage_sgf ();
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
;
1051 get_prop (new_prop
)->next
= NO_PROP
;
1054 get_node (node
)->props
= new_prop
;
1058 temp_prop_handle
= get_node (node
)->props
;
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
;
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)
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
||
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
;
1132 add_or_set_prop_sgf (int node
, enum prop_type_t type
, union prop_data_t data
)
1134 int temp_prop_handle
;
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
;
1164 if (get_node (node
)->props
< 0)
1169 current_handle
= get_node (node
)->props
;
1171 while (current_handle
>= 0)
1173 if (get_prop (current_handle
)->type
== type
|| type
== PROP_ANY
)
1177 *previous_prop
= previous_handle
;
1180 return current_handle
;
1184 previous_handle
= current_handle
;
1185 current_handle
= get_prop (current_handle
)->next
;
1193 delete_prop_sgf (int node
, enum prop_type_t type
)
1200 return delete_prop_handle_sgf (node
, get_prop_sgf (node
, type
, NULL
));
1204 delete_prop_handle_sgf (int node
, int prop
)
1208 if (prop
< 0 || node
< 0 || get_node (node
)->props
< 0)
1213 if (get_node (node
)->props
== prop
)
1215 get_node (node
)->props
= get_prop (get_node (node
)->props
)->next
;
1216 free_storage_sgf (prop
);
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)
1233 get_prop (previous
)->next
= get_prop (get_prop (previous
)->next
)->next
;
1234 free_storage_sgf (prop
);
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)
1251 if (unhandled_fd
< 0)
1253 DEBUGF ("unhandled file is closed?!\n");
1257 if (rb
->lseek (unhandled_fd
, get_prop (prop_handle
)->data
.number
,
1260 DEBUGF ("couldn't seek in unhandled_fd\n");
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");
1271 /* make output a string, the lazy way */
1272 rb
->memset (buffer
, 0, buffer_size
);
1276 bool escaped
= false;
1278 while ((buffer_size
> bytes_read
) && !done
)
1280 int temp
= read_char (unhandled_fd
);
1297 DEBUGF ("encountered end of file before end of comment!\n");
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");
1340 if (prop_handle
>= 0)
1342 if ((start_of_comment
= rb
->lseek (unhandled_fd
,
1343 get_prop (prop_handle
)->data
.number
,
1346 DEBUGF ("couldn't seek in unhandled_fd\n");
1356 overwriting
= false;
1363 if (!read_char_no_whitespace (unhandled_fd
) == 'C' ||
1364 !read_char_no_whitespace (unhandled_fd
) == '[')
1366 DEBUGF ("non-comment while overwriting!!\n");
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");
1380 write_char (unhandled_fd
, 'C');
1381 write_char (unhandled_fd
, '[');
1385 bool overwrite_escaped
= false;
1391 int orig_char
= peek_char (unhandled_fd
);
1396 overwrite_escaped
= !overwrite_escaped
;
1400 if (overwrite_escaped
)
1402 overwrite_escaped
= false;
1405 /* otherwise, fall through */
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
;
1418 goto start_of_write_wcs
;
1422 overwrite_escaped
= false;
1431 write_char (unhandled_fd
, '\\');
1436 write_char (unhandled_fd
, *string
);
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
;
1458 has_more_nodes_sgf (void)
1460 int saved
= current_node
;
1461 bool ret_val
= false;
1463 if (current_node
< 0)
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
;
1480 /* logic is different here because the first node in a tree is a valid
1483 has_prev_nodes_sgf (void)
1485 if (current_node
< 0)
1490 return current_node
!= start_node
;
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
;
1508 get_move_from_node (int handle
)
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
)
1528 prop_handle
= get_prop (prop_handle
)->next
;
1537 int result
= get_node (current_node
)->prev
;
1539 if (current_node
== start_node
)
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);
1564 int result
= get_node (current_node
)->next
;
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
);
1582 go_to_variation_sgf (get_prop (result
)->data
.number
);
1590 num_variations_sgf (void)
1593 struct prop_t
*temp_prop
;
1594 struct node_t
*temp_node
= get_node (current_node
);
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
);
1610 if (temp_prop
->type
== PROP_VARIATION
)
1616 /* variations are at the beginning of the prop list */
1620 temp_prop
= get_prop (temp_prop
->next
);
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
;
1638 temp_node
= get_node (temp_node
->prev
);
1645 temp_node
= get_node (current_node
= temp_node
->next
);
1649 current_node
= saved
;
1653 temp_prop
= get_prop (temp_node
->props
);
1657 if (!temp_prop
|| temp_prop
->type
!= PROP_VARIATION
)
1659 current_node
= saved
;
1665 current_node
= temp_prop
->data
.node
;
1669 temp_prop
= get_prop (temp_prop
->next
);
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;
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
);
1695 temp_node
= get_node (temp_node
->next
);
1702 temp_prop
= get_prop (temp_node
->props
);
1706 if (temp_prop
->type
== PROP_VARIATION
)
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
;
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
)
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
)
1752 saved
= current_node
;
1753 current_node
= temp_node
->next
;
1755 struct prop_t
*temp_move
= get_prop (get_move_sgf ());
1757 pos
== temp_move
->data
.position
&&
1758 color
== (temp_move
->type
== PROP_BLACK_MOVE
? BLACK
: WHITE
))
1760 current_node
= saved
;
1764 current_node
= saved
;
1770 next_variation_sgf (void)
1772 int saved
= current_node
;
1773 int saved_start
= start_node
;
1774 union prop_data_t temp_data
;
1778 if (current_node
< 0 || get_node (current_node
)->prev
< 0)
1783 start_node
= NO_NODE
;
1787 if (num_variations_sgf () < 2)
1790 current_node
= saved
;
1791 start_node
= saved_start
;
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)) */
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
;
1811 if (chosen
>= (num_vars
= num_variations_sgf ()))
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
;
1826 if (redo_node_sgf ())
1828 start_node
= saved_start
;
1833 current_node
= saved
;
1834 start_node
= saved_start
;
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)
1853 free_storage_sgf (new_node
);
1857 free_storage_sgf (new_prop
);
1865 free_storage_sgf (new_node
);
1866 free_storage_sgf (new_prop
);
1871 temp_node
= get_node (temp_node
->next
);
1875 free_storage_sgf (new_node
);
1876 free_storage_sgf (new_prop
);
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;
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;
1916 /* the lowest it can be, since 1 isn't it */
1917 temp_variation_number
= 2;
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
;
1936 ++temp_variation_number
;
1937 temp_prop_handle
= temp_prop
->next
;
1942 is_important_node (int handle
)
1944 struct prop_t
*temp_prop
;
1951 if (handle
== start_node
)
1956 if (get_node (handle
)->prev
< 0)
1961 temp_prop
= get_prop (get_node (handle
)->props
);
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
)
1975 temp_prop
= get_prop (temp_prop
->next
);
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
;
1998 current_node
= last_good
;
2002 current_node
= temp_node
;
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
||
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
||
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
)
2062 setup_handicap_sgf (void)
2064 union prop_data_t temp_data
;
2066 if (header
.handicap
<= 1)
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!",
2087 int handicaps_to_place
= header
.handicap
;
2089 int low_coord
= 0, mid_coord
= 0, high_coord
= 0;
2091 if (board_width
== 19)
2097 else if (board_width
== 13)
2103 else if (board_width
== 9)
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
));
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
));
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
));
2178 goto_handicap_start_sgf ();
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
);
2193 goto_handicap_start_sgf (void)
2195 if (start_node
!= tree_head
)
2197 current_node
= get_node (start_node
)->prev
;
2203 post_game_setup_sgf (void)
2205 int temp_handle
= alloc_storage_sgf ();
2206 int saved
= current_node
;
2208 if (temp_handle
< 0)
2213 union prop_data_t temp_data
;
2214 temp_data
.number
= 0; /* meaningless */
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
;
2230 if (current_node
== temp_handle
)
2232 current_node
= saved
;
2235 free_storage_sgf (temp_handle
);