Commit | Line | Data |
---|---|---|
4ddf6f1a JB |
1 | /* |
2 | * ===================================================================================== | |
3 | * | |
4 | * Filename: othello.c | |
5 | * | |
6 | * Description: Handle the othello board content | |
7 | * | |
8 | * Version: 1.0 | |
9 | * Created: 25/04/2017 15:16:08 | |
10 | * Revision: none | |
11 | * Compiler: gcc | |
12 | * | |
13 | * Author: Jerome Benoit (fraggle), jerome.benoit@piment-noir.org | |
14 | * Organization: Piment Noir | |
15 | * | |
16 | * ===================================================================================== | |
17 | */ | |
18 | ||
74e2b93b JB |
19 | #include <stdlib.h> |
20 | #include <stdbool.h> | |
a80646b7 | 21 | #include <string.h> |
4ddf6f1a | 22 | |
74e2b93b | 23 | #include "othello.h" |
a80646b7 | 24 | #include "debug.h" |
74e2b93b | 25 | |
d7813f1e JB |
26 | /** |
27 | * Get current round player integer | |
28 | * @param round_count current round integer | |
29 | * @return current round player | |
30 | */ | |
74e2b93b | 31 | unsigned int current_player(unsigned int round_count) { |
21b9239c | 32 | |
74e2b93b JB |
33 | if (round_count % 2 != 0) { |
34 | return player_two; | |
35 | } else { | |
36 | return player_one; | |
37 | } | |
38 | } | |
39 | ||
d7813f1e JB |
40 | /** |
41 | * Get current round opponent integer | |
42 | * @param current_player current round player | |
43 | * @return current round opponent integer | |
44 | */ | |
a80646b7 JB |
45 | unsigned int current_opponent(unsigned int current_player) { |
46 | ||
47 | if (current_player == player_one) { | |
48 | return player_two; | |
49 | } else { | |
50 | return player_one; | |
51 | } | |
52 | } | |
53 | ||
54 | /* for consistency with ncurses, the board coordinates are in the following order: | |
45ce2fe3 | 55 | * O--x--> |
74e2b93b JB |
56 | * | |
57 | * y | |
58 | * | | |
21b9239c | 59 | * v |
54f1c58c | 60 | * The origin O has (1, 1) coordinates */ |
74e2b93b | 61 | |
d7813f1e | 62 | /** |
5ec791f6 | 63 | * Get pawn value at coordinates (y,x) |
d7813f1e JB |
64 | * @param y y coordinate |
65 | * @param x x coordinate | |
66 | * @param pawn_array array of played pawns | |
67 | * @return pawn integer type | |
68 | */ | |
74e2b93b | 69 | unsigned int get_box_value(int y, int x, unsigned int pawn_array[board_size][board_size]) { |
21b9239c | 70 | |
54f1c58c | 71 | return pawn_array[y-1][x-1]; |
74e2b93b JB |
72 | } |
73 | ||
74 | bool is_box_type(int y, int x, unsigned int pawn_array[board_size][board_size], unsigned int type) { | |
75 | ||
76 | if (type > 2) { | |
77 | return NULL; | |
78 | } | |
79 | if (get_box_value(y, x, pawn_array) == type) { | |
80 | return true; | |
81 | } else { | |
82 | return false; | |
83 | } | |
84 | } | |
85 | ||
a80646b7 | 86 | static bool is_valid_coordinates(int y, int x) { |
b5e9ccd0 | 87 | |
a80646b7 JB |
88 | if ((y > 0 && y < board_size + 1) && \ |
89 | (x > 0 && x < board_size + 1)) { | |
90 | return true; | |
91 | } else { | |
92 | return false; | |
93 | } | |
94 | } | |
95 | ||
5ec791f6 JB |
96 | /** |
97 | * Helper function to set a correct value at the (y,x) coordinates in the pawns array | |
98 | * @param y [description] | |
99 | * @param x [description] | |
100 | * @param type [description] | |
101 | * @param [name] [description] | |
102 | */ | |
a80646b7 | 103 | void set_pawn(int y, int x, unsigned int type, unsigned int pawn_array[board_size][board_size]) { |
74e2b93b | 104 | |
a80646b7 JB |
105 | if (type > 0 && type < 3 && \ |
106 | is_valid_coordinates(y, x)) { | |
54f1c58c | 107 | pawn_array[y-1][x-1] = type; |
9240af1a | 108 | } //FIXME: else case should set invalid values to permit to catch errors |
74e2b93b JB |
109 | } |
110 | ||
a80646b7 JB |
111 | /* reverse the pawn at (y, x) coordinates if it exists */ |
112 | static void reverse_pawn(int y, int x, unsigned int pawn_array[board_size][board_size]) { | |
21b9239c | 113 | |
a80646b7 JB |
114 | if (is_box_type(y, x, pawn_array, black)) { |
115 | set_pawn(y, x, white, pawn_array); | |
116 | } else if (is_box_type(y, x, pawn_array, white)) { | |
117 | set_pawn(y, x, black, pawn_array); | |
118 | } | |
119 | } | |
120 | ||
121 | void zero_pawns(unsigned int pawn_array[board_size][board_size]) { | |
54f1c58c | 122 | |
a80646b7 JB |
123 | for (int i = 1; i <= board_size; i++) { |
124 | for (int j = 1; j <= board_size; j++) { | |
125 | set_pawn(i, j, empty, pawn_array); | |
74e2b93b JB |
126 | } |
127 | } | |
74e2b93b JB |
128 | } |
129 | ||
5ec791f6 JB |
130 | /** |
131 | * Set the pawns in the start position | |
132 | * @param pawn_array array of played pawns | |
133 | */ | |
a80646b7 JB |
134 | void init_pawns(unsigned int pawn_array[board_size][board_size]) { |
135 | ||
136 | /* the 2D array zeroing is not necessary if it is properly initialized to zero */ | |
137 | zero_pawns(pawn_array); | |
138 | set_pawn(5, 4, black, pawn_array); | |
139 | set_pawn(4, 5, black, pawn_array); | |
140 | set_pawn(4, 4, white, pawn_array); | |
141 | set_pawn(5, 5, white, pawn_array); | |
74e2b93b JB |
142 | } |
143 | ||
a80646b7 | 144 | unsigned int count_pawns_type(unsigned int pawn_array[board_size][board_size], unsigned int type) { |
74e2b93b JB |
145 | unsigned int count = 0; |
146 | ||
147 | if (type > 2) { | |
a80646b7 | 148 | return 0; |
74e2b93b | 149 | } |
a80646b7 JB |
150 | for (int i = 1; i <= board_size; i++) { |
151 | for (int j = 1; j <= board_size; j++) { | |
45ce2fe3 | 152 | if (is_box_type(i, j, pawn_array, type)) { |
74e2b93b JB |
153 | count++; |
154 | } | |
155 | } | |
156 | } | |
157 | return count; | |
158 | } | |
54f1c58c | 159 | |
a80646b7 JB |
160 | static void direction_to_coordinates(unsigned int direction, int* start_y, int* start_x) { |
161 | ||
162 | if (direction == north) { | |
163 | *start_y = *start_y - 1; | |
164 | } else if (direction == north_east) { | |
165 | *start_y = *start_y - 1; | |
166 | *start_x = *start_x + 1; | |
167 | } else if (direction == east) { | |
168 | *start_x = *start_x + 1; | |
169 | } else if (direction == south_east) { | |
170 | *start_y = *start_y + 1; | |
171 | *start_x = *start_x + 1; | |
172 | } else if (direction == south) { | |
173 | *start_y = *start_y + 1; | |
174 | } else if (direction == south_west) { | |
175 | *start_y = *start_y + 1; | |
176 | *start_x = *start_x - 1; | |
177 | } else if (direction == west) { | |
178 | *start_x = *start_x - 1; | |
179 | } else if (direction == north_west) { | |
180 | *start_y = *start_y - 1; | |
181 | *start_x = *start_x - 1; | |
54f1c58c JB |
182 | } |
183 | } | |
184 | ||
185 | bool is_board_full(unsigned int pawn_array[board_size][board_size]) { | |
186 | ||
45ce2fe3 | 187 | /* an alternate method is to test the round count vs. 60 */ |
a80646b7 JB |
188 | for (int i = 1; i <= board_size; i++) { |
189 | for (int j = 1; j <= board_size; j++) { | |
54f1c58c JB |
190 | if (is_box_type(i, j, pawn_array , empty)) { |
191 | return false; | |
192 | } | |
193 | } | |
194 | } | |
195 | return true; | |
196 | } | |
197 | ||
a8b54576 JB |
198 | unsigned int eval_winner(unsigned int nb_white, unsigned int nb_black) { |
199 | ||
200 | if (nb_white > nb_black) { | |
201 | return player_two; | |
202 | } else if (nb_white < nb_black) { | |
203 | return player_one; | |
204 | } else { | |
205 | return 0; | |
206 | } | |
207 | } | |
208 | ||
bbbe0570 JB |
209 | static unsigned int count_pawn_to_reverse_one_direction(int y, int x, int direction, unsigned int current_player, unsigned int pawn_array[board_size][board_size]) { |
210 | unsigned int nb_pawns_reversed = 0; | |
a80646b7 | 211 | int moving_y = y, moving_x = x; |
45ce2fe3 | 212 | |
bbbe0570 | 213 | /* count the pawns to reverse in the chosen direction */ |
a80646b7 JB |
214 | direction_to_coordinates(direction, &moving_y, &moving_x); |
215 | while (true) { | |
216 | if (!is_valid_coordinates(moving_y, moving_x) || is_box_type(moving_y, moving_x, pawn_array, empty)) { | |
bbbe0570 | 217 | return 0; |
a80646b7 JB |
218 | } |
219 | if (is_box_type(moving_y, moving_x, pawn_array, current_player)) { | |
220 | break; | |
221 | } | |
bbbe0570 | 222 | nb_pawns_reversed++; |
a80646b7 JB |
223 | direction_to_coordinates(direction, &moving_y, &moving_x); |
224 | } | |
bbbe0570 JB |
225 | return nb_pawns_reversed; |
226 | } | |
227 | ||
21b9239c | 228 | /* revert the pawns if needed in one direction */ |
bbbe0570 JB |
229 | static unsigned int reverse_one_direction(int y, int x, int direction, unsigned int current_player, unsigned int pawn_array[board_size][board_size], bool dry_run) { |
230 | unsigned int nb_pawns_reversed = 0; | |
231 | int moving_y = y, moving_x = x; | |
232 | ||
233 | nb_pawns_reversed = count_pawn_to_reverse_one_direction(moving_y, moving_x, direction, current_player, pawn_array); | |
a80646b7 | 234 | |
bbbe0570 JB |
235 | /* now reverse the needed pawns */ |
236 | if (nb_pawns_reversed > 0 && !dry_run) { | |
a80646b7 JB |
237 | moving_y = y, moving_x = x; |
238 | direction_to_coordinates(direction, &moving_y, &moving_x); | |
239 | while (!is_box_type(moving_y, moving_x, pawn_array, current_player)) { | |
240 | reverse_pawn(moving_y, moving_x, pawn_array); | |
241 | direction_to_coordinates(direction, &moving_y, &moving_x); | |
242 | } | |
45ce2fe3 | 243 | } |
bbbe0570 JB |
244 | return nb_pawns_reversed; |
245 | } | |
246 | ||
3243371e JB |
247 | /* loop optimized version of valid_shot function changing nothing to the pawns 2D array */ |
248 | bool is_legal_shot(int y, int x, unsigned int current_player, unsigned int pawn_array[board_size][board_size]) { | |
bbbe0570 | 249 | unsigned int nb_pawns_reversed = 0; |
21b9239c | 250 | |
bbbe0570 JB |
251 | if (!is_valid_coordinates(y, x) || !is_box_type(y, x, pawn_array, empty)) { |
252 | return false; | |
253 | } | |
21b9239c | 254 | |
bbbe0570 JB |
255 | for (unsigned int direction = north; direction <= north_west; direction++) { |
256 | nb_pawns_reversed += reverse_one_direction(y, x, direction, current_player, pawn_array, true); | |
257 | if (nb_pawns_reversed > 0) { | |
258 | return true; | |
259 | } | |
260 | } | |
9240af1a | 261 | return false; |
54f1c58c JB |
262 | } |
263 | ||
a80646b7 JB |
264 | /* play the shot if legal and flip or reverse the necessary pawns */ |
265 | unsigned int valid_shot(int y, int x, unsigned int current_player, unsigned int pawn_array[board_size][board_size]) { | |
bbbe0570 | 266 | unsigned int nb_pawns_reversed = 0; |
a80646b7 JB |
267 | |
268 | if (!is_valid_coordinates(y, x) || !is_box_type(y, x, pawn_array, empty)) { | |
269 | return 0; | |
270 | } | |
271 | ||
272 | for (unsigned int direction = north; direction <= north_west; direction++) { | |
bbbe0570 | 273 | nb_pawns_reversed += reverse_one_direction(y, x, direction, current_player, pawn_array, false); |
a80646b7 | 274 | } |
21b9239c | 275 | |
bbbe0570 | 276 | if (nb_pawns_reversed == 0) { |
9240af1a | 277 | return nb_pawns_reversed; |
a80646b7 | 278 | } |
54f1c58c | 279 | |
a80646b7 | 280 | set_pawn(y, x, current_player, pawn_array); |
bbbe0570 | 281 | return nb_pawns_reversed; |
54f1c58c | 282 | } |
7aab9e03 | 283 | |
9240af1a | 284 | static void add_shots_list_cell(int y, int x, unsigned int type, struct shots_list_s* shots_list) { |
e801c2c4 | 285 | struct shots_list_s* list_cell = (struct shots_list_s*)malloc(sizeof(struct shots_list_s)); |
9240af1a JB |
286 | if (!list_cell) { |
287 | exit(EXIT_FAILURE); | |
7aab9e03 | 288 | } |
7aab9e03 | 289 | |
9240af1a JB |
290 | if (type > 0 && type < 5 && is_valid_coordinates(y, x)) { |
291 | list_cell->y = y; | |
292 | list_cell->x = x; | |
293 | list_cell->type = type; | |
294 | list_add_tail(&(list_cell->list), &(shots_list->list)); | |
295 | } | |
7aab9e03 JB |
296 | } |
297 | ||
9240af1a JB |
298 | void free_shots_list(struct shots_list_s* shots_list) { |
299 | struct shots_list_s* list_counter; | |
7aab9e03 | 300 | |
9240af1a JB |
301 | while (!list_empty(&shots_list->list)) { |
302 | list_counter = list_entry(shots_list->list.next, struct shots_list_s, list); | |
e801c2c4 | 303 | list_del(&(list_counter->list)); |
9240af1a | 304 | free(list_counter); |
7aab9e03 | 305 | } |
9240af1a | 306 | |
7aab9e03 JB |
307 | } |
308 | ||
9240af1a | 309 | void build_playable_shots_list(unsigned int current_player, struct shots_list_s* shots_list, unsigned int pawn_array[board_size][board_size]) { |
7aab9e03 | 310 | |
9240af1a JB |
311 | for (unsigned int i = 0; i <= board_size; i++) { |
312 | for (unsigned int j = 0; j <= board_size; j++) { | |
313 | if (is_legal_shot(i, j, current_player, pawn_array)) { | |
314 | add_shots_list_cell(i, j, hint_allowed, shots_list); | |
315 | /* FIXME: a neighbourhood detection is needed | |
316 | } else if (is_box_type(i, j, pawn_array, empty)){ | |
317 | add_shots_list_cell(i, j, hint_forbidden, shots_list); | |
318 | */ | |
319 | } | |
320 | } | |
7aab9e03 JB |
321 | } |
322 | } |