simple-go.c
Raw
1#include <simple-go/simple-go.h>
2
3go_board* create_board(go_coordinate size)
4{
5 go_board* board = malloc(sizeof(*board));
6 assert(board);
7 board->field_array = malloc(size*size*sizeof(*board->field_array));
8 assert(board->field_array);
9 for(go_coordinate i = 0; i < size*size; i++)
10 {
11 board->field_array[i] = EMPTY;
12 }
13 board->size = size;
14
15 return board;
16}
17
18void delete_board(go_board* board)
19{
20 free(board->field_array);
21 free(board);
22}
23
24game_state* create_game(go_coordinate size, float komi)
25{
26 game_state* game = malloc(sizeof(*game));
27 game->board = create_board(size);
28 game->black_turn = true;
29 game->komi = komi;
30
31 return game;
32}
33
34void delete_game(game_state* game)
35{
36 delete_board(game->board);
37 free(game);
38}
39
40bool check_bounds(const go_board* board, go_coordinate y, go_coordinate x)
41{
42 if(y < board->size && x < board->size)
43 return true;
44 else
45 return false;
46}
47
48void kill_group(go_board* board, go_board* overlay)
49{
50 for(go_coordinate y = 0; y < board->size; y++)
51 {
52 for(go_coordinate x = 0; x < board->size; x++)
53 {
54 if(get_board_at(overlay, y, x) == GROUP)
55 set_board_at(board, y, x, EMPTY);
56 }
57 }
58}
59
60void print_board(go_board* board)
61{
62 for(go_coordinate y = 0; y < board->size; y++)
63 {
64 for(go_coordinate x = 0; x < board->size; x++)
65 {
66 putchar(get_board_at(board,y,x));
67 putchar(' ');
68 }
69 putchar('\n');
70 }
71}
72
73char* board_to_string(go_board* board)
74{
75 size_t str_size = (board->size*2+6)*(board->size+2)+2;
76 char* ret = calloc(str_size+1,1);
77 unsigned int index = 0;
78 unsigned int board_index = 0;
79 ret[index++] = '\n';
80
81 for(unsigned int i = 0; i < board->size+2; i++)
82 {
83 if(i == 0 || i == board->size+1)
84 {
85 ret[index++] = ' ';
86 ret[index++] = ' ';
87 ret[index++] = ' ';
88
89 char current_char = 'A';
90 for(unsigned int j = 0; j < board->size; j++)
91 {
92 ret[index++] = (char)(current_char == 'I' ? current_char++, current_char++ : current_char++);
93 ret[index++] = ' ';
94 }
95
96 ret[index++] = ' ';
97 ret[index++] = ' ';
98 } else {
99 index += (unsigned int)sprintf(ret+index, "%2u", (unsigned int)(board->size+1-i));
100 ret[index++] = ' ';
101 for(unsigned int j = 0; j < board->size; j++)
102 {
103 ret[index++] = board->field_array[board_index++];
104 ret[index++] = ' ';
105 }
106 index += (unsigned int)sprintf(ret+index, "%-2u", (unsigned int)(board->size+1-i));
107 }
108 ret[index++] = '\n';
109 }
110
111
112
113 return ret;
114
115}
116
117
118bool group_attachable(const game_state* game, go_coordinate y, go_coordinate x)
119{
120 go_board* friendly_group = create_board(game->board->size);
121 find_group(game->board, friendly_group, y, x);
122 if(count_liberties(game->board, friendly_group) > 1)
123 {
124 delete_board(friendly_group);
125 return true;
126 } else {
127 delete_board(friendly_group);
128 return false;
129 }
130}
131
132bool group_killable(game_state* game, go_coordinate y, go_coordinate x)
133{
134 go_board* enemy_group = create_board(game->board->size);
135 find_group(game->board, enemy_group, y, x);
136 if(count_liberties(game->board, enemy_group) <= 1)
137 {
138 kill_group(game->board, enemy_group);
139 delete_board(enemy_group);
140 return true;
141 } else {
142 delete_board(enemy_group);
143 return false;
144 }
145}
146
147bool play_at(game_state* game, go_coordinate y, go_coordinate x, go_symbol color)
148{
149 //check out-of-bounds
150 if(!check_bounds(game->board, y, x))
151 return false;
152
153 //check if field is empty
154 if(get_board_at(game->board, y, x) != EMPTY)
155 return false;
156
157 bool can_place = false;
158
159 go_symbol enemy = color == NO_FIELD ? (game->black_turn ? WHITE : BLACK) : (color == BLACK ? WHITE : BLACK);
160 go_symbol friendly = color == NO_FIELD ? (game->black_turn ? BLACK : WHITE) : (color == BLACK ? BLACK : WHITE);
161
162 go_symbol up = y == 0 ? NO_FIELD : get_board_at(game->board, y-1, x);
163 go_symbol left = x == 0 ? NO_FIELD : get_board_at(game->board, y, x-1);
164 go_symbol down = y == game->board->size-1 ? NO_FIELD : get_board_at(game->board, y+1, x);
165 go_symbol right = x == game->board->size-1 ? NO_FIELD : get_board_at(game->board, y, x+1);
166
167 //first check for group to kill, then for empty field, and last for group with liberties
168
169 if(up == enemy && group_killable(game, y-1, x))
170 can_place = true;
171 else if(up == EMPTY)
172 can_place = true;
173 else if(up == friendly && group_attachable(game, y-1, x))
174 can_place = true;
175
176 if(left == enemy && group_killable(game, y, x-1))
177 can_place = true;
178 else if(left == EMPTY)
179 can_place = true;
180 else if(left == friendly && group_attachable(game, y, x-1))
181 can_place = true;
182
183 if(down == enemy && group_killable(game, y+1, x))
184 can_place = true;
185 else if(down == EMPTY)
186 can_place = true;
187 else if(down == friendly && group_attachable(game, y+1, x))
188 can_place = true;
189
190 if(right == enemy && group_killable(game, y, x+1))
191 can_place = true;
192 else if(right == EMPTY)
193 can_place = true;
194 else if(right == friendly && group_attachable(game, y, x+1))
195 can_place = true;
196
197 if(!can_place)
198 return false;
199
200 set_board_at(game->board, y, x, friendly);
201 game->black_turn = !game->black_turn;
202 return true;
203}
204
205go_symbol get_board_at(const go_board* board, go_coordinate y, go_coordinate x)
206{
207 if(check_bounds(board, y, x))
208 return board->field_array[y*board->size+x];
209 else
210 return NO_FIELD;
211}
212
213void set_board_at(go_board* board, go_coordinate y, go_coordinate x, go_symbol item)
214{
215 if(check_bounds(board,y,x))
216 board->field_array[y*board->size+x] = item;
217}
218
219void find_group(const go_board* board, go_board* overlay, go_coordinate y, go_coordinate x)
220{
221 assert(board->size == overlay->size);
222 if(check_bounds(board, y, x))
223 {
224 set_board_at(overlay, y, x, GROUP);
225 go_symbol field = get_board_at(board,y,x);
226
227 if(get_board_at(board,y-1,x) == field && get_board_at(overlay,y-1,x) == EMPTY)
228 find_group(board, overlay, y-1, x);
229
230 if(get_board_at(board,y,x-1) == field && get_board_at(overlay,y,x-1) == EMPTY)
231 find_group(board, overlay, y, x-1);
232
233 if(get_board_at(board,y+1,x) == field && get_board_at(overlay,y+1,x) == EMPTY)
234 find_group(board, overlay, y+1, x);
235
236 if(get_board_at(board,y,x+1) == field && get_board_at(overlay,y,x+1) == EMPTY)
237 find_group(board, overlay, y, x+1);
238 }
239}
240
241unsigned long count_liberties(go_board* board, go_board* overlay)
242{
243 assert(board->size == overlay->size);
244
245 go_board* tmpoverlay = create_board(board->size);
246 memcpy(tmpoverlay->field_array, overlay->field_array, board->size*board->size);
247
248 unsigned long liberties = 0;
249
250 for(go_coordinate y = 0; y < board->size; y++)
251 {
252 for(go_coordinate x = 0; x < board->size; x++)
253 {
254 if(get_board_at(overlay, y, x) == GROUP)
255 {
256 if(get_board_at(board,y-1,x) == EMPTY && get_board_at(tmpoverlay,y-1,x) != COUNTED)
257 {
258 liberties++;
259 set_board_at(tmpoverlay,y-1,x,COUNTED);
260 }
261 if(get_board_at(board,y,x-1) == EMPTY && get_board_at(tmpoverlay,y,x-1) != COUNTED)
262 {
263 liberties++;
264 set_board_at(tmpoverlay,y,x-1,COUNTED);
265 }
266 if(get_board_at(board,y+1,x) == EMPTY && get_board_at(tmpoverlay,y+1,x) != COUNTED)
267 {
268 liberties++;
269 set_board_at(tmpoverlay,y+1,x,COUNTED);
270 }
271 if(get_board_at(board,y,x+1) == EMPTY && get_board_at(tmpoverlay,y,x+1) != COUNTED)
272 {
273 liberties++;
274 set_board_at(tmpoverlay,y,x+1,COUNTED);
275 }
276
277 }
278 }
279 }
280
281 delete_board(tmpoverlay);
282
283 return liberties;
284}
285
286
287