diff options
Diffstat (limited to 'apps/plugins/goban/board.c')
-rw-r--r-- | apps/plugins/goban/board.c | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/apps/plugins/goban/board.c b/apps/plugins/goban/board.c new file mode 100644 index 0000000000..bc6c5347dc --- /dev/null +++ b/apps/plugins/goban/board.c | |||
@@ -0,0 +1,354 @@ | |||
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 "goban.h" | ||
23 | #include "board.h" | ||
24 | #include "display.h" | ||
25 | #include "types.h" | ||
26 | #include "game.h" | ||
27 | #include "util.h" | ||
28 | |||
29 | #include "plugin.h" | ||
30 | |||
31 | unsigned int board_width = MAX_BOARD_SIZE; | ||
32 | unsigned int board_height = MAX_BOARD_SIZE; | ||
33 | |||
34 | /* Board has 'invalid' markers around each border */ | ||
35 | unsigned char board_data[(MAX_BOARD_SIZE + 2) * (MAX_BOARD_SIZE + 2)]; | ||
36 | int white_captures = 0; | ||
37 | int black_captures = 0; | ||
38 | |||
39 | uint8_t board_marks[(MAX_BOARD_SIZE + 2) * (MAX_BOARD_SIZE + 2)]; | ||
40 | uint8_t current_mark = 255; | ||
41 | |||
42 | /* there can't be any changes off of the board, so no need to add the | ||
43 | borders */ | ||
44 | uint8_t board_changes[MAX_BOARD_SIZE * MAX_BOARD_SIZE]; | ||
45 | |||
46 | unsigned short ko_pos = INVALID_POS; | ||
47 | |||
48 | /* forward declarations */ | ||
49 | static void setup_marks (void); | ||
50 | static void make_mark (unsigned short pos); | ||
51 | static int get_liberties_helper (unsigned short pos, unsigned char orig_color); | ||
52 | static int flood_fill_helper (unsigned short pos, unsigned char orig_color, | ||
53 | unsigned char color); | ||
54 | |||
55 | |||
56 | /* these aren't "board marks" in the marks on the SGF sense, they are used | ||
57 | internally to mark already visited points and the like (such as when | ||
58 | doing liberty counting for groups) */ | ||
59 | static void | ||
60 | setup_marks (void) | ||
61 | { | ||
62 | unsigned int x, y; | ||
63 | |||
64 | current_mark++; | ||
65 | |||
66 | if (current_mark == 0) | ||
67 | { | ||
68 | current_mark++; | ||
69 | |||
70 | for (y = 0; y < board_height; ++y) | ||
71 | { | ||
72 | for (x = 0; x < board_width; ++x) | ||
73 | { | ||
74 | board_marks[POS (x, y)] = 0; | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | } | ||
79 | |||
80 | static void | ||
81 | make_mark (unsigned short pos) | ||
82 | { | ||
83 | board_marks[pos] = current_mark; | ||
84 | } | ||
85 | |||
86 | static bool | ||
87 | is_marked (unsigned short pos) | ||
88 | { | ||
89 | return board_marks[pos] == current_mark; | ||
90 | } | ||
91 | |||
92 | void | ||
93 | clear_board (void) | ||
94 | { | ||
95 | unsigned int i, x, y; | ||
96 | |||
97 | /* for the borders */ | ||
98 | for (i = 0; i < (2 + MAX_BOARD_SIZE) * (2 + MAX_BOARD_SIZE); ++i) | ||
99 | { | ||
100 | board_data[i] = INVALID; | ||
101 | } | ||
102 | |||
103 | /* now make the actual board part */ | ||
104 | for (y = 0; y < board_height; ++y) | ||
105 | { | ||
106 | for (x = 0; x < board_width; ++x) | ||
107 | { | ||
108 | board_data[POS (x, y)] = EMPTY; | ||
109 | } | ||
110 | } | ||
111 | |||
112 | white_captures = 0; | ||
113 | black_captures = 0; | ||
114 | |||
115 | ko_pos = INVALID_POS; | ||
116 | } | ||
117 | |||
118 | bool | ||
119 | set_size_board (int width, int height) | ||
120 | { | ||
121 | if (width < MIN_BOARD_SIZE || width > MAX_BOARD_SIZE || | ||
122 | height < MIN_BOARD_SIZE || height > MAX_BOARD_SIZE) | ||
123 | { | ||
124 | return false; | ||
125 | } | ||
126 | else | ||
127 | { | ||
128 | board_width = width; | ||
129 | board_height = height; | ||
130 | setup_display (); | ||
131 | return true; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | unsigned char | ||
136 | get_point_board (unsigned short pos) | ||
137 | { | ||
138 | return board_data[pos]; | ||
139 | } | ||
140 | |||
141 | void | ||
142 | set_point_board (unsigned short pos, unsigned char color) | ||
143 | { | ||
144 | board_data[pos] = color; | ||
145 | } | ||
146 | |||
147 | bool | ||
148 | on_board (unsigned short pos) | ||
149 | { | ||
150 | if (pos < POS (0, 0) || | ||
151 | pos > POS (board_width - 1, board_height - 1) || | ||
152 | get_point_board (pos) == INVALID) | ||
153 | { | ||
154 | return false; | ||
155 | } | ||
156 | else | ||
157 | { | ||
158 | return true; | ||
159 | } | ||
160 | } | ||
161 | |||
162 | int | ||
163 | get_liberties_board (unsigned short pos) | ||
164 | { | ||
165 | if (!on_board (pos) || get_point_board (pos) == EMPTY) | ||
166 | { | ||
167 | return -1; | ||
168 | } | ||
169 | |||
170 | setup_marks (); | ||
171 | |||
172 | int ret_val = 0; | ||
173 | unsigned char orig_color = get_point_board (pos); | ||
174 | |||
175 | empty_stack (&parse_stack); | ||
176 | push_pos_stack (&parse_stack, pos); | ||
177 | |||
178 | /* Since we only ever test for liberties in order to determine | ||
179 | captures and the like, there's no reason to count any liberties | ||
180 | higher than 2 (we sometimes need to know if something has 1 liberty | ||
181 | for dealing with ko) */ | ||
182 | while (pop_pos_stack (&parse_stack, &pos) && ret_val < 2) | ||
183 | { | ||
184 | ret_val += get_liberties_helper (NORTH (pos), orig_color); | ||
185 | ret_val += get_liberties_helper (SOUTH (pos), orig_color); | ||
186 | ret_val += get_liberties_helper (EAST (pos), orig_color); | ||
187 | ret_val += get_liberties_helper (WEST (pos), orig_color); | ||
188 | } | ||
189 | |||
190 | /* if there's more than two liberties, the stack isn't empty, so empty | ||
191 | it */ | ||
192 | empty_stack (&parse_stack); | ||
193 | |||
194 | return ret_val; | ||
195 | } | ||
196 | |||
197 | static int | ||
198 | get_liberties_helper (unsigned short pos, unsigned char orig_color) | ||
199 | { | ||
200 | if (on_board (pos) && | ||
201 | get_point_board (pos) != OTHER (orig_color) && !is_marked (pos)) | ||
202 | { | ||
203 | make_mark (pos); | ||
204 | |||
205 | if (get_point_board (pos) == EMPTY) | ||
206 | { | ||
207 | return 1; | ||
208 | } | ||
209 | else | ||
210 | { | ||
211 | push_pos_stack (&parse_stack, pos); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | |||
219 | int | ||
220 | flood_fill_board (unsigned short pos, unsigned char color) | ||
221 | { | ||
222 | if (!on_board (pos) || get_point_board (pos) == color) | ||
223 | { | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | empty_stack (&parse_stack); | ||
228 | |||
229 | int ret_val = 0; | ||
230 | |||
231 | unsigned char orig_color = get_point_board (pos); | ||
232 | |||
233 | set_point_board (pos, color); | ||
234 | ++ret_val; | ||
235 | push_pos_stack (&parse_stack, pos); | ||
236 | |||
237 | while (pop_pos_stack (&parse_stack, &pos)) | ||
238 | { | ||
239 | ret_val += flood_fill_helper (NORTH (pos), orig_color, color); | ||
240 | ret_val += flood_fill_helper (SOUTH (pos), orig_color, color); | ||
241 | ret_val += flood_fill_helper (EAST (pos), orig_color, color); | ||
242 | ret_val += flood_fill_helper (WEST (pos), orig_color, color); | ||
243 | } | ||
244 | |||
245 | return ret_val; | ||
246 | } | ||
247 | |||
248 | |||
249 | static int | ||
250 | flood_fill_helper (unsigned short pos, unsigned char orig_color, | ||
251 | unsigned char color) | ||
252 | { | ||
253 | if (on_board (pos) && get_point_board (pos) == orig_color) | ||
254 | { | ||
255 | set_point_board (pos, color); | ||
256 | push_pos_stack (&parse_stack, pos); | ||
257 | return 1; | ||
258 | } | ||
259 | |||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | bool | ||
264 | legal_move_board (unsigned short pos, unsigned char color, bool allow_suicide) | ||
265 | { | ||
266 | /* you can always pass */ | ||
267 | if (pos == PASS_POS) | ||
268 | { | ||
269 | return true; | ||
270 | } | ||
271 | |||
272 | if (!on_board (pos) || (color != BLACK && color != WHITE)) | ||
273 | { | ||
274 | return false; | ||
275 | } | ||
276 | |||
277 | if (pos == ko_pos && color == current_player) | ||
278 | { | ||
279 | return false; | ||
280 | } | ||
281 | |||
282 | if (get_point_board (pos) != EMPTY) | ||
283 | { | ||
284 | return false; | ||
285 | } | ||
286 | |||
287 | /* don't need to save the current state, because it's always empty | ||
288 | since we tested for that above */ | ||
289 | set_point_board (pos, color); | ||
290 | |||
291 | /* if we have liberties, it can't be illegal */ | ||
292 | if (get_liberties_board (pos) > 0 || | ||
293 | /* if we can capture something, it can't be illegal */ | ||
294 | (get_point_board (NORTH (pos)) == OTHER (color) && | ||
295 | !get_liberties_board (NORTH (pos))) || | ||
296 | (get_point_board (SOUTH (pos)) == OTHER (color) && | ||
297 | !get_liberties_board (SOUTH (pos))) || | ||
298 | (get_point_board (EAST (pos)) == OTHER (color) && | ||
299 | !get_liberties_board (EAST (pos))) || | ||
300 | (get_point_board (WEST (pos)) == OTHER (color) && | ||
301 | !get_liberties_board (WEST (pos))) || | ||
302 | /* if we're allowed to suicide, only multi-stone suicide is legal | ||
303 | (no ruleset allows single-stone suicide that I know of) */ | ||
304 | (allow_suicide && (get_point_board (NORTH (pos)) == color || | ||
305 | get_point_board (SOUTH (pos)) == color || | ||
306 | get_point_board (EAST (pos)) == color || | ||
307 | get_point_board (WEST (pos)) == color))) | ||
308 | { | ||
309 | /* undo our previous set */ | ||
310 | set_point_board (pos, EMPTY); | ||
311 | return true; | ||
312 | } | ||
313 | else | ||
314 | { | ||
315 | /* undo our previous set */ | ||
316 | set_point_board (pos, EMPTY); | ||
317 | return false; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | |||
322 | unsigned short | ||
323 | WRAP (unsigned short pos) | ||
324 | { | ||
325 | int x, y; | ||
326 | if (on_board (pos)) | ||
327 | { | ||
328 | return pos; | ||
329 | } | ||
330 | else | ||
331 | { | ||
332 | x = I (pos); | ||
333 | y = J (pos); | ||
334 | |||
335 | if (x < 0) | ||
336 | { | ||
337 | x = board_width - 1; | ||
338 | } | ||
339 | else if ((unsigned int) x >= board_width) | ||
340 | { | ||
341 | x = 0; | ||
342 | } | ||
343 | |||
344 | if (y < 0) | ||
345 | { | ||
346 | y = board_height - 1; | ||
347 | } | ||
348 | else if ((unsigned int) y >= board_height) | ||
349 | { | ||
350 | y = 0; | ||
351 | } | ||
352 | return POS (x, y); | ||
353 | } | ||
354 | } | ||