simple-go.c
Raw
1#include "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
73
74
75bool group_attachable(const game_state* game, go_coordinate y, go_coordinate x)
76{
77 go_board* friendly_group = create_board(game->board->size);
78 find_group(game->board, friendly_group, y, x);
79 if(count_liberties(game->board, friendly_group) > 1)
80 {
81 delete_board(friendly_group);
82 return true;
83 } else {
84 delete_board(friendly_group);
85 return false;
86 }
87}
88
89bool group_killable(game_state* game, go_coordinate y, go_coordinate x)
90{
91 go_board* enemy_group = create_board(game->board->size);
92 find_group(game->board, enemy_group, y, x);
93 if(count_liberties(game->board, enemy_group) <= 1)
94 {
95 kill_group(game->board, enemy_group);
96 delete_board(enemy_group);
97 return true;
98 } else {
99 delete_board(enemy_group);
100 return false;
101 }
102}
103
104bool play_at(game_state* game, go_coordinate y, go_coordinate x)
105{
106 //check out-of-bounds
107 if(!check_bounds(game->board, y, x))
108 return false;
109
110 //check if field is empty
111 if(get_board_at(game->board, y, x) != EMPTY)
112 return false;
113
114 bool can_place = false;
115
116 go_symbol enemy = game->black_turn ? WHITE : BLACK;
117 go_symbol friendly = game->black_turn ? BLACK : WHITE;
118
119 go_symbol up = y == 0 ? INVALID_FIELD : get_board_at(game->board, y-1, x);
120 go_symbol left = x == 0 ? INVALID_FIELD : get_board_at(game->board, y, x-1);
121 go_symbol down = y == game->board->size-1 ? INVALID_FIELD : get_board_at(game->board, y+1, x);
122 go_symbol right = x == game->board->size-1 ? INVALID_FIELD : get_board_at(game->board, y, x+1);
123
124 //first check for group to kill, then for empty field, and last for group with liberties
125
126 if(up == enemy && group_killable(game, y-1, x))
127 can_place = true;
128 else if(up == EMPTY)
129 can_place = true;
130 else if(up == friendly && group_attachable(game, y-1, x))
131 can_place = true;
132
133 if(left == enemy && group_killable(game, y, x-1))
134 can_place = true;
135 else if(left == EMPTY)
136 can_place = true;
137 else if(left == friendly && group_attachable(game, y, x-1))
138 can_place = true;
139
140 if(down == enemy && group_killable(game, y+1, x))
141 can_place = true;
142 else if(down == EMPTY)
143 can_place = true;
144 else if(down == friendly && group_attachable(game, y+1, x))
145 can_place = true;
146
147 if(right == enemy && group_killable(game, y, x+1))
148 can_place = true;
149 else if(right == EMPTY)
150 can_place = true;
151 else if(right == friendly && group_attachable(game, y, x+1))
152 can_place = true;
153
154 if(!can_place)
155 return false;
156
157 set_board_at(game->board, y, x, game->black_turn ? BLACK : WHITE);
158 game->black_turn = !game->black_turn;
159 return true;
160}
161
162go_symbol get_board_at(const go_board* board, go_coordinate y, go_coordinate x)
163{
164 if(check_bounds(board, y, x))
165 return board->field_array[y*board->size+x];
166 else
167 return INVALID_FIELD;
168}
169
170void set_board_at(go_board* board, go_coordinate y, go_coordinate x, go_symbol item)
171{
172 if(check_bounds(board,y,x))
173 board->field_array[y*board->size+x] = item;
174}
175
176void find_group(const go_board* board, go_board* overlay, go_coordinate y, go_coordinate x)
177{
178 assert(board->size == overlay->size);
179 if(check_bounds(board, y, x))
180 {
181 set_board_at(overlay, y, x, GROUP);
182 go_symbol field = get_board_at(board,y,x);
183
184 if(get_board_at(board,y-1,x) == field && get_board_at(overlay,y-1,x) == EMPTY)
185 find_group(board, overlay, y-1, x);
186
187 if(get_board_at(board,y,x-1) == field && get_board_at(overlay,y,x-1) == EMPTY)
188 find_group(board, overlay, y, x-1);
189
190 if(get_board_at(board,y+1,x) == field && get_board_at(overlay,y+1,x) == EMPTY)
191 find_group(board, overlay, y+1, x);
192
193 if(get_board_at(board,y,x+1) == field && get_board_at(overlay,y,x+1) == EMPTY)
194 find_group(board, overlay, y, x+1);
195 }
196}
197
198unsigned long count_liberties(go_board* board, go_board* overlay)
199{
200 assert(board->size == overlay->size);
201
202 go_board* tmpoverlay = create_board(board->size);
203 memcpy(tmpoverlay->field_array, overlay->field_array, board->size*board->size);
204
205 unsigned long liberties = 0;
206
207 for(go_coordinate y = 0; y < board->size; y++)
208 {
209 for(go_coordinate x = 0; x < board->size; x++)
210 {
211 if(get_board_at(overlay, y, x) == GROUP)
212 {
213 if(get_board_at(board,y-1,x) == EMPTY && get_board_at(tmpoverlay,y-1,x) != COUNTED)
214 {
215 liberties++;
216 set_board_at(tmpoverlay,y-1,x,COUNTED);
217 }
218 if(get_board_at(board,y,x-1) == EMPTY && get_board_at(tmpoverlay,y,x-1) != COUNTED)
219 {
220 liberties++;
221 set_board_at(tmpoverlay,y,x-1,COUNTED);
222 }
223 if(get_board_at(board,y+1,x) == EMPTY && get_board_at(tmpoverlay,y+1,x) != COUNTED)
224 {
225 liberties++;
226 set_board_at(tmpoverlay,y+1,x,COUNTED);
227 }
228 if(get_board_at(board,y,x+1) == EMPTY && get_board_at(tmpoverlay,y,x+1) != COUNTED)
229 {
230 liberties++;
231 set_board_at(tmpoverlay,y,x+1,COUNTED);
232 }
233
234 }
235 }
236 }
237
238 delete_board(tmpoverlay);
239
240 return liberties;
241}
242
243
244