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 JB |
25 | |
26 | unsigned int current_player(unsigned int round_count) { | |
21b9239c | 27 | |
74e2b93b JB |
28 | if (round_count % 2 != 0) { |
29 | return player_two; | |
30 | } else { | |
31 | return player_one; | |
32 | } | |
33 | } | |
34 | ||
a80646b7 JB |
35 | unsigned int current_opponent(unsigned int current_player) { |
36 | ||
37 | if (current_player == player_one) { | |
38 | return player_two; | |
39 | } else { | |
40 | return player_one; | |
41 | } | |
42 | } | |
43 | ||
44 | /* for consistency with ncurses, the board coordinates are in the following order: | |
45ce2fe3 | 45 | * O--x--> |
74e2b93b JB |
46 | * | |
47 | * y | |
48 | * | | |
21b9239c | 49 | * v |
54f1c58c | 50 | * The origin O has (1, 1) coordinates */ |
74e2b93b JB |
51 | |
52 | unsigned int get_box_value(int y, int x, unsigned int pawn_array[board_size][board_size]) { | |
21b9239c | 53 | |
54f1c58c | 54 | return pawn_array[y-1][x-1]; |
74e2b93b JB |
55 | } |
56 | ||
57 | bool is_box_type(int y, int x, unsigned int pawn_array[board_size][board_size], unsigned int type) { | |
58 | ||
59 | if (type > 2) { | |
60 | return NULL; | |
61 | } | |
62 | if (get_box_value(y, x, pawn_array) == type) { | |
63 | return true; | |
64 | } else { | |
65 | return false; | |
66 | } | |
67 | } | |
68 | ||
a80646b7 | 69 | static bool is_valid_coordinates(int y, int x) { |
b5e9ccd0 | 70 | |
a80646b7 JB |
71 | if ((y > 0 && y < board_size + 1) && \ |
72 | (x > 0 && x < board_size + 1)) { | |
73 | return true; | |
74 | } else { | |
75 | return false; | |
76 | } | |
77 | } | |
78 | ||
79 | /* helper function to set a correct value at the (y, x) coordinates in the pawns array */ | |
80 | void set_pawn(int y, int x, unsigned int type, unsigned int pawn_array[board_size][board_size]) { | |
74e2b93b | 81 | |
a80646b7 JB |
82 | if (type > 0 && type < 3 && \ |
83 | is_valid_coordinates(y, x)) { | |
54f1c58c | 84 | pawn_array[y-1][x-1] = type; |
74e2b93b JB |
85 | } |
86 | } | |
87 | ||
a80646b7 JB |
88 | /* reverse the pawn at (y, x) coordinates if it exists */ |
89 | static void reverse_pawn(int y, int x, unsigned int pawn_array[board_size][board_size]) { | |
21b9239c | 90 | |
a80646b7 JB |
91 | if (is_box_type(y, x, pawn_array, black)) { |
92 | set_pawn(y, x, white, pawn_array); | |
93 | } else if (is_box_type(y, x, pawn_array, white)) { | |
94 | set_pawn(y, x, black, pawn_array); | |
95 | } | |
96 | } | |
97 | ||
98 | void zero_pawns(unsigned int pawn_array[board_size][board_size]) { | |
54f1c58c | 99 | |
a80646b7 JB |
100 | for (int i = 1; i <= board_size; i++) { |
101 | for (int j = 1; j <= board_size; j++) { | |
102 | set_pawn(i, j, empty, pawn_array); | |
74e2b93b JB |
103 | } |
104 | } | |
74e2b93b JB |
105 | } |
106 | ||
107 | /* set the pawns in the start position */ | |
a80646b7 JB |
108 | void init_pawns(unsigned int pawn_array[board_size][board_size]) { |
109 | ||
110 | /* the 2D array zeroing is not necessary if it is properly initialized to zero */ | |
111 | zero_pawns(pawn_array); | |
112 | set_pawn(5, 4, black, pawn_array); | |
113 | set_pawn(4, 5, black, pawn_array); | |
114 | set_pawn(4, 4, white, pawn_array); | |
115 | set_pawn(5, 5, white, pawn_array); | |
74e2b93b JB |
116 | } |
117 | ||
a80646b7 | 118 | unsigned int count_pawns_type(unsigned int pawn_array[board_size][board_size], unsigned int type) { |
74e2b93b JB |
119 | unsigned int count = 0; |
120 | ||
121 | if (type > 2) { | |
a80646b7 | 122 | return 0; |
74e2b93b | 123 | } |
a80646b7 JB |
124 | for (int i = 1; i <= board_size; i++) { |
125 | for (int j = 1; j <= board_size; j++) { | |
45ce2fe3 | 126 | if (is_box_type(i, j, pawn_array, type)) { |
74e2b93b JB |
127 | count++; |
128 | } | |
129 | } | |
130 | } | |
131 | return count; | |
132 | } | |
54f1c58c | 133 | |
a80646b7 JB |
134 | static void direction_to_coordinates(unsigned int direction, int* start_y, int* start_x) { |
135 | ||
136 | if (direction == north) { | |
137 | *start_y = *start_y - 1; | |
138 | } else if (direction == north_east) { | |
139 | *start_y = *start_y - 1; | |
140 | *start_x = *start_x + 1; | |
141 | } else if (direction == east) { | |
142 | *start_x = *start_x + 1; | |
143 | } else if (direction == south_east) { | |
144 | *start_y = *start_y + 1; | |
145 | *start_x = *start_x + 1; | |
146 | } else if (direction == south) { | |
147 | *start_y = *start_y + 1; | |
148 | } else if (direction == south_west) { | |
149 | *start_y = *start_y + 1; | |
150 | *start_x = *start_x - 1; | |
151 | } else if (direction == west) { | |
152 | *start_x = *start_x - 1; | |
153 | } else if (direction == north_west) { | |
154 | *start_y = *start_y - 1; | |
155 | *start_x = *start_x - 1; | |
54f1c58c JB |
156 | } |
157 | } | |
158 | ||
159 | bool is_board_full(unsigned int pawn_array[board_size][board_size]) { | |
160 | ||
45ce2fe3 | 161 | /* an alternate method is to test the round count vs. 60 */ |
a80646b7 JB |
162 | for (int i = 1; i <= board_size; i++) { |
163 | for (int j = 1; j <= board_size; j++) { | |
54f1c58c JB |
164 | if (is_box_type(i, j, pawn_array , empty)) { |
165 | return false; | |
166 | } | |
167 | } | |
168 | } | |
169 | return true; | |
170 | } | |
171 | ||
a8b54576 JB |
172 | unsigned int eval_winner(unsigned int nb_white, unsigned int nb_black) { |
173 | ||
174 | if (nb_white > nb_black) { | |
175 | return player_two; | |
176 | } else if (nb_white < nb_black) { | |
177 | return player_one; | |
178 | } else { | |
179 | return 0; | |
180 | } | |
181 | } | |
182 | ||
bbbe0570 JB |
183 | 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]) { |
184 | unsigned int nb_pawns_reversed = 0; | |
a80646b7 | 185 | int moving_y = y, moving_x = x; |
45ce2fe3 | 186 | |
bbbe0570 | 187 | /* count the pawns to reverse in the chosen direction */ |
a80646b7 JB |
188 | direction_to_coordinates(direction, &moving_y, &moving_x); |
189 | while (true) { | |
190 | if (!is_valid_coordinates(moving_y, moving_x) || is_box_type(moving_y, moving_x, pawn_array, empty)) { | |
bbbe0570 | 191 | return 0; |
a80646b7 JB |
192 | } |
193 | if (is_box_type(moving_y, moving_x, pawn_array, current_player)) { | |
194 | break; | |
195 | } | |
bbbe0570 | 196 | nb_pawns_reversed++; |
a80646b7 JB |
197 | direction_to_coordinates(direction, &moving_y, &moving_x); |
198 | } | |
bbbe0570 JB |
199 | return nb_pawns_reversed; |
200 | } | |
201 | ||
21b9239c | 202 | /* revert the pawns if needed in one direction */ |
bbbe0570 JB |
203 | 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) { |
204 | unsigned int nb_pawns_reversed = 0; | |
205 | int moving_y = y, moving_x = x; | |
206 | ||
207 | nb_pawns_reversed = count_pawn_to_reverse_one_direction(moving_y, moving_x, direction, current_player, pawn_array); | |
a80646b7 | 208 | |
bbbe0570 JB |
209 | /* now reverse the needed pawns */ |
210 | if (nb_pawns_reversed > 0 && !dry_run) { | |
a80646b7 JB |
211 | moving_y = y, moving_x = x; |
212 | direction_to_coordinates(direction, &moving_y, &moving_x); | |
213 | while (!is_box_type(moving_y, moving_x, pawn_array, current_player)) { | |
214 | reverse_pawn(moving_y, moving_x, pawn_array); | |
215 | direction_to_coordinates(direction, &moving_y, &moving_x); | |
216 | } | |
45ce2fe3 | 217 | } |
bbbe0570 JB |
218 | return nb_pawns_reversed; |
219 | } | |
220 | ||
221 | /* loop optimized version of valid_shot changing nothing to the pawns 2D array */ | |
222 | static bool is_legal_shot(int y, int x, unsigned int current_player, unsigned int pawn_array[board_size][board_size]) { | |
223 | unsigned int nb_pawns_reversed = 0; | |
21b9239c | 224 | |
bbbe0570 JB |
225 | if (!is_valid_coordinates(y, x) || !is_box_type(y, x, pawn_array, empty)) { |
226 | return false; | |
227 | } | |
21b9239c | 228 | |
bbbe0570 JB |
229 | for (unsigned int direction = north; direction <= north_west; direction++) { |
230 | nb_pawns_reversed += reverse_one_direction(y, x, direction, current_player, pawn_array, true); | |
231 | if (nb_pawns_reversed > 0) { | |
232 | return true; | |
233 | } | |
234 | } | |
54f1c58c JB |
235 | } |
236 | ||
a80646b7 JB |
237 | /* play the shot if legal and flip or reverse the necessary pawns */ |
238 | unsigned int valid_shot(int y, int x, unsigned int current_player, unsigned int pawn_array[board_size][board_size]) { | |
bbbe0570 | 239 | unsigned int nb_pawns_reversed = 0; |
a80646b7 JB |
240 | |
241 | if (!is_valid_coordinates(y, x) || !is_box_type(y, x, pawn_array, empty)) { | |
242 | return 0; | |
243 | } | |
244 | ||
245 | for (unsigned int direction = north; direction <= north_west; direction++) { | |
bbbe0570 | 246 | nb_pawns_reversed += reverse_one_direction(y, x, direction, current_player, pawn_array, false); |
a80646b7 | 247 | } |
21b9239c | 248 | |
bbbe0570 | 249 | if (nb_pawns_reversed == 0) { |
a80646b7 JB |
250 | return 0; |
251 | } | |
54f1c58c | 252 | |
a80646b7 | 253 | set_pawn(y, x, current_player, pawn_array); |
bbbe0570 | 254 | return nb_pawns_reversed; |
54f1c58c | 255 | } |
7aab9e03 JB |
256 | |
257 | struct shots_list_s build_playable_shots_list(int y, int x, unsigned int current_player, unsigned int pawn_array[board_size][board_size]) { | |
258 | ||
259 | for (unsigned int i = 0; i <= board_size; i++) { | |
260 | for (unsigned int j = 0; j <= board_size; j++) { | |
261 | if (is_legal_shot(y, x, current_player, pawn_array)) { | |
262 | ||
263 | } | |
264 | } | |
265 | } | |
266 | } | |
267 | ||
268 | void create_shots_list_cell(struct shots_list_s* shots_list_cell) { | |
269 | ||
270 | } | |
271 | ||
272 | static void set_shots_list_coordinates(int y, int x, struct shots_list_s* shots_list) { | |
273 | ||
274 | if (is_valid_coordinates(y, x)) { | |
275 | shots_list->y = y; | |
276 | shots_list->x = x; | |
277 | } | |
278 | } | |
279 | ||
280 | void set_shots_lists_cell(int y, int x, unsigned int type, struct shots_list_s* shots_list) { | |
281 | ||
282 | if (type > 0 && type < 5) { | |
283 | set_shots_list_coordinates(y, x, shots_list); | |
284 | shots_list->type = type; | |
285 | } | |
286 | } |