Rev 75 | Rev 136 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | // board.cpp |
2 | |||
3 | #include "common.h" |
||
4 | |||
5 | |||
6 | // initial position of a chess game : |
||
7 | // |
||
8 | // +-----------------+ |
||
9 | // 8| R K B Q K B K R | - black |
||
10 | // 7| P P P P P P P P | |
||
11 | // 6| | |
||
12 | // 5| | |
||
13 | // 4| | |
||
14 | // 3| | |
||
15 | // 2| P P P P P P P P | |
||
16 | // 1| R K B Q K B K R | - white |
||
17 | // +-----------------+ |
||
18 | // a b c d e f g h |
||
19 | |||
20 | |||
21 | bool Board_Init (board_t *board, int white_playertype, int black_playertype, wchar_t *fen_string) |
||
22 | { |
||
23 | // this function initializes a new chess game |
||
24 | |||
25 | bool is_success; |
||
26 | |||
27 | // initialize the moves array and blank out the first move's structure |
||
28 | board->moves = (boardmove_t *) SAFE_malloc (1, sizeof (boardmove_t), false); |
||
29 | memset (&board->moves[0], 0, sizeof (boardmove_t)); |
||
30 | |||
31 | board->viewed_move = 0; |
||
32 | board->move_count = 1; |
||
33 | board->game_state = STATE_UNKNOWN; // game has not started yet |
||
34 | board->lastmove_time = 0.0f; |
||
35 | |||
36 | // initialize this board's players |
||
37 | Player_Init (&board->players[COLOR_WHITE], COLOR_WHITE, white_playertype); |
||
38 | Player_Init (&board->players[COLOR_BLACK], COLOR_BLACK, black_playertype); |
||
39 | |||
40 | // can we NOT successfully initialize from the given FEN string ? |
||
41 | is_success = Move_SetupFromFEN (&board->moves[0], fen_string); |
||
42 | if (!is_success) |
||
43 | Move_SetupFromFEN (&board->moves[0], FENSTARTUP_STANDARDCHESS); // if so, fallback to standard chess table |
||
44 | |||
45 | // reset all selection and hovering information |
||
46 | Board_SetSelectedAndHovered (board, -1, -1, -1, -1); |
||
47 | |||
48 | // notify that board was just set up |
||
49 | board->was_setup = true; |
||
50 | board->has_playerchanged = true; |
||
124 | pmbaty | 51 | board->reevaluate = true; |
1 | pmbaty | 52 | |
53 | return (is_success); // finished |
||
54 | } |
||
55 | |||
56 | |||
57 | void Board_Shutdown (board_t *board) |
||
58 | { |
||
59 | // this function ends a chess game |
||
60 | |||
61 | boardmove_t *move; |
||
62 | int move_index; |
||
63 | |||
64 | // reset the moves array |
||
65 | for (move_index = 0; move_index < board->move_count; move_index++) |
||
66 | { |
||
67 | move = &board->moves[move_index]; // quick access to move |
||
68 | SAFE_free ((void **) &move->sides[COLOR_WHITE].takenparts); // free white's taken parts |
||
69 | SAFE_free ((void **) &move->sides[COLOR_BLACK].takenparts); // free black's taken parts |
||
70 | SAFE_free ((void **) &move->comment); // free each move comment |
||
71 | } |
||
72 | SAFE_free ((void **) &board->moves); // and the moves array itself |
||
73 | board->viewed_move = -1; |
||
74 | board->move_count = 0; |
||
75 | |||
76 | // release players |
||
77 | Player_Shutdown (&board->players[COLOR_WHITE]); |
||
78 | Player_Shutdown (&board->players[COLOR_BLACK]); |
||
79 | |||
80 | // game has not started yet |
||
81 | board->game_state = STATE_UNKNOWN; |
||
82 | |||
83 | return; // that's all there is |
||
84 | } |
||
85 | |||
86 | |||
87 | bool Board_Reset (board_t *board, wchar_t *fen_string) |
||
88 | { |
||
89 | // this function initializes a chess game according to a FEN string, WITHOUT changing the players |
||
90 | |||
91 | boardmove_t *move; |
||
92 | int move_index; |
||
93 | bool is_success; |
||
94 | |||
95 | // reset the moves array |
||
96 | for (move_index = 0; move_index < board->move_count; move_index++) |
||
97 | { |
||
98 | move = &board->moves[move_index]; // quick access to move |
||
99 | SAFE_free ((void **) &move->sides[COLOR_WHITE].takenparts); // free white's taken parts |
||
100 | SAFE_free ((void **) &move->sides[COLOR_BLACK].takenparts); // free black's taken parts |
||
101 | SAFE_free ((void **) &move->comment); // free each move comment |
||
102 | } |
||
103 | |||
104 | // resize the moves array and blank out the first move's structure |
||
105 | board->moves = (boardmove_t *) SAFE_realloc (board->moves, board->move_count, 1, sizeof (boardmove_t), false); |
||
106 | memset (&board->moves[0], 0, sizeof (boardmove_t)); |
||
107 | |||
108 | board->viewed_move = 0; |
||
109 | board->move_count = 1; |
||
110 | board->game_state = STATE_UNKNOWN; // game has not started yet |
||
111 | board->lastmove_time = 0.0f; |
||
112 | |||
113 | // reset the players' view angles |
||
114 | Player_ResetView (&board->players[COLOR_BLACK]); |
||
115 | Player_ResetView (&board->players[COLOR_WHITE]); |
||
116 | |||
117 | // can we NOT successfully initialize from the given FEN string ? |
||
118 | is_success = Move_SetupFromFEN (&board->moves[0], fen_string); |
||
119 | if (!is_success) |
||
120 | Move_SetupFromFEN (&board->moves[0], FENSTARTUP_STANDARDCHESS); // if so, fallback to standard chess table |
||
121 | |||
122 | // reset all selection and hovering information |
||
123 | Board_SetSelectedAndHovered (board, -1, -1, -1, -1); |
||
124 | |||
125 | // notify that board was just set up |
||
126 | board->was_setup = true; |
||
127 | board->has_playerchanged = true; |
||
128 | |||
129 | return (is_success); // finished |
||
130 | } |
||
131 | |||
132 | |||
133 | bool Board_Think (board_t *board) |
||
134 | { |
||
135 | // helper function to make both a board's players think |
||
136 | |||
137 | bool do_update; |
||
138 | |||
139 | do_update = false; // don't update scene until told otherwise |
||
140 | |||
141 | do_update |= Player_Think (&board->players[COLOR_WHITE]); // make white player think |
||
142 | do_update |= Player_Think (&board->players[COLOR_BLACK]); // make black player think |
||
143 | |||
144 | // do the board sides need to be swapped ? |
||
145 | if (board->want_playerswap) |
||
146 | { |
||
147 | Board_SwapSides (board); // if so, swap board sides |
||
148 | board->want_playerswap = false; // don't do this all day long |
||
149 | do_update |= true; // and update the scene |
||
150 | } |
||
151 | |||
152 | // clear board notifications (they should have been processed in Player_Think()) |
||
153 | board->was_setup = false; |
||
154 | board->has_playerchanged = false; |
||
155 | |||
156 | return (do_update); // finished, return whether we update the scene or not |
||
157 | } |
||
158 | |||
159 | |||
160 | void Board_SwapSides (board_t *board) |
||
161 | { |
||
162 | // helper function to swap a board's sides |
||
163 | |||
164 | player_t player_to_swap; |
||
165 | |||
166 | // THREAD-SAFE: wait until all buffers are unlocked |
||
167 | while (board->players[COLOR_BLACK].sendbuffer_locked |
||
168 | || board->players[COLOR_WHITE].sendbuffer_locked) |
||
169 | Sleep (10); // test again in 10 milliseconds |
||
170 | |||
171 | // lock all the buffers at once |
||
172 | board->players[COLOR_BLACK].sendbuffer_locked = true; |
||
173 | board->players[COLOR_WHITE].sendbuffer_locked = true; |
||
174 | |||
175 | // swap players structures |
||
176 | memcpy (&player_to_swap, &board->players[COLOR_BLACK], sizeof (player_t)); |
||
177 | memcpy (&board->players[COLOR_BLACK], &board->players[COLOR_WHITE], sizeof (player_t)); |
||
178 | memcpy (&board->players[COLOR_WHITE], &player_to_swap, sizeof (player_t)); |
||
179 | |||
180 | // restore correct colors |
||
181 | board->players[COLOR_BLACK].color = COLOR_BLACK; |
||
182 | board->players[COLOR_WHITE].color = COLOR_WHITE; |
||
183 | |||
184 | // turn their point of view 180 degrees |
||
185 | board->players[COLOR_BLACK].view_yaw = WrapAngle (board->players[COLOR_BLACK].view_yaw + 180.0f); |
||
186 | board->players[COLOR_WHITE].view_yaw = WrapAngle (board->players[COLOR_WHITE].view_yaw + 180.0f); |
||
187 | |||
188 | // now unlock all the players' buffers |
||
189 | board->players[COLOR_BLACK].sendbuffer_locked = false; |
||
190 | board->players[COLOR_WHITE].sendbuffer_locked = false; |
||
191 | |||
192 | board->reevaluate = true; // remember to reevaluate this board |
||
193 | |||
194 | return; // finished |
||
195 | } |
||
196 | |||
197 | |||
198 | void Board_SetSelectedAndHovered (board_t *board, int selected_line, int selected_column, int hovered_line, int hovered_column) |
||
199 | { |
||
200 | // helper function to forcibly set a board's selected and hovered slots (respectively move source and destination) |
||
201 | |||
202 | board->selected_position[0] = selected_line; |
||
203 | board->selected_position[1] = selected_column; |
||
204 | board->hovered_position[0] = hovered_line; |
||
205 | board->hovered_position[1] = hovered_column; |
||
206 | |||
207 | return; // finished |
||
208 | } |
||
209 | |||
210 | |||
211 | char Board_ColorToMove (board_t *board) |
||
212 | { |
||
213 | // helper function that returns the current color to move for the given board |
||
214 | // NOTE: since the return value may be used to address an array, DO NOT return an invalid value ! |
||
215 | |||
216 | if ((board->moves == NULL) || (board->move_count < 1)) |
||
217 | return (COLOR_WHITE); // consistency check |
||
218 | |||
219 | return (1 - board->moves[the_board.move_count - 1].color); // return the opposite color of the last move's color |
||
220 | } |
||
221 | |||
222 | |||
223 | void Board_AppendMove (board_t *board, int from_line, int from_column, int to_line, int to_column, char promotion_type, wchar_t *comment) |
||
224 | { |
||
225 | // this function processes a part movement on the specified board. |
||
226 | |||
227 | boardmove_t *last_move; |
||
228 | boardmove_t new_move; |
||
229 | int side_index; |
||
230 | boardside_t *playing_side; |
||
231 | boardside_t *opposing_side; |
||
232 | |||
233 | // get a quick access to board's last move |
||
234 | last_move = &board->moves[board->move_count - 1]; |
||
235 | |||
236 | // prepare the new move |
||
237 | new_move.color = 1 - last_move->color; // switch colors |
||
238 | new_move.part = last_move->slots[from_line][from_column].part; // save the move part type |
||
239 | new_move.promotion_type = promotion_type; // save the promotion type |
||
240 | new_move.has_captured = false; // assume there's no capture until told otherwise |
||
241 | new_move.is_enpassant = false; // assume no en passant coup is played until told otherwise |
||
242 | new_move.is_check = false; // assume no check until told otherwise |
||
243 | new_move.is_stalemate = false; // assume no stalemate until told otherwise |
||
244 | new_move.source[0] = from_line; // save the move source line and column |
||
245 | new_move.source[1] = from_column; |
||
246 | new_move.target[0] = to_line; // save the move target line and column |
||
247 | new_move.target[1] = to_column; |
||
248 | new_move.pgntext[0] = 0; // will be evaluated at the end of this function |
||
249 | new_move.comment = NULL; // assume no comment until told otherwise |
||
250 | new_move.comment_size = 0; |
||
251 | for (side_index = 0; side_index < 2; side_index++) |
||
252 | { |
||
253 | new_move.sides[side_index].takenparts = (unsigned char *) SAFE_malloc (last_move->sides[side_index].takenpart_count, sizeof (unsigned char), false); |
||
254 | memcpy (new_move.sides[side_index].takenparts, last_move->sides[side_index].takenparts, last_move->sides[side_index].takenpart_count * sizeof (unsigned char)); |
||
255 | new_move.sides[side_index].takenpart_count = last_move->sides[side_index].takenpart_count; // copy side's taken parts |
||
256 | new_move.sides[side_index].shortcastle_allowed = last_move->sides[side_index].shortcastle_allowed; // copy whether side can castle short |
||
257 | new_move.sides[side_index].longcastle_allowed = last_move->sides[side_index].longcastle_allowed; // copy whether side can castle long |
||
258 | } |
||
259 | memcpy (&new_move.slots, last_move->slots, sizeof (last_move->slots)); // copy table disposition |
||
260 | new_move.fen_string[0] = 0; // will be evaluated at the end of this function |
||
261 | |||
262 | // get a quick access to current and opposing sides |
||
263 | playing_side = &new_move.sides[new_move.color]; |
||
264 | opposing_side = &new_move.sides[1 - new_move.color]; |
||
265 | |||
266 | // is a piece being taken ? |
||
267 | if (new_move.slots[to_line][to_column].part != PART_NONE) |
||
268 | { |
||
269 | // was the piece being taken a rook ? |
||
270 | if (new_move.slots[to_line][to_column].part == PART_ROOK) |
||
271 | { |
||
272 | // was it a white tower in A1, a white tower in A7, a black tower in H1 or a black tower in H7 ? |
||
273 | if ((new_move.slots[to_line][to_column].color == COLOR_WHITE) && (to_line == 0) && (to_column == 0)) |
||
274 | new_move.sides[COLOR_WHITE].longcastle_allowed = false; // our opponent can no longer castle queenside |
||
275 | else if ((new_move.slots[to_line][to_column].color == COLOR_WHITE) && (to_line == 0) && (to_column == 7)) |
||
276 | new_move.sides[COLOR_WHITE].shortcastle_allowed = false; // our opponent can no longer castle bishopside |
||
277 | else if ((new_move.slots[to_line][to_column].color == COLOR_BLACK) && (to_line == 7) && (to_column == 0)) |
||
278 | new_move.sides[COLOR_BLACK].longcastle_allowed = false; // our opponent can no longer castle queenside |
||
279 | else if ((new_move.slots[to_line][to_column].color == COLOR_BLACK) && (to_line == 7) && (to_column == 7)) |
||
280 | new_move.sides[COLOR_BLACK].shortcastle_allowed = false; // our opponent can no longer castle bishopside |
||
281 | } |
||
282 | |||
283 | // resize this players' taken parts array and move the taken piece in it |
||
284 | playing_side->takenparts = (unsigned char *) SAFE_realloc (playing_side->takenparts, playing_side->takenpart_count, playing_side->takenpart_count + 1, sizeof (unsigned char), true); |
||
285 | playing_side->takenparts[playing_side->takenpart_count] = new_move.slots[to_line][to_column].part; |
||
286 | playing_side->takenpart_count++; // player has now taken one piece more |
||
287 | new_move.has_captured = true; // remember a part has just been captured |
||
288 | } |
||
289 | |||
290 | // else is it an "en passant" coup ? |
||
291 | else if ((new_move.slots[from_line][from_column].part == PART_PAWN) // we're a pawn |
||
292 | && (abs (to_column - from_column) == 1) // we moved diagonally |
||
293 | && (new_move.slots[to_line][to_column].part == PART_NONE) // no part on destination |
||
294 | && (new_move.slots[from_line][to_column].part == PART_PAWN)) // but a pawn next to us |
||
295 | { |
||
296 | // resize this players' taken parts array and move the taken piece in it |
||
297 | playing_side->takenparts = (unsigned char *) SAFE_realloc (playing_side->takenparts, playing_side->takenpart_count, playing_side->takenpart_count + 1, sizeof (unsigned char), true); |
||
298 | playing_side->takenparts[playing_side->takenpart_count] = new_move.slots[from_line][to_column].part; |
||
299 | memset (&new_move.slots[from_line][to_column], 0, sizeof (boardslot_t)); // "en passant" coup |
||
300 | playing_side->takenpart_count++; // player has now taken one piece more |
||
301 | new_move.has_captured = true; // remember a part has just been captured |
||
302 | new_move.is_enpassant = true; // and that it's an en passant coup |
||
303 | } |
||
304 | |||
305 | // has a promotion been specified ? |
||
306 | if (promotion_type != PART_NONE) |
||
307 | Move_SetSlot (&new_move, from_line, from_column, new_move.color, promotion_type); // promote the pawn to the desired type |
||
308 | |||
309 | // was the moved part the king ? if so, player can't castle anymore |
||
310 | if (new_move.slots[from_line][from_column].part == PART_KING) |
||
311 | { |
||
312 | // did the king castle queenside ? or did it castle bishopside ? |
||
313 | if ((from_column == 4) && (to_column == 2)) |
||
314 | { |
||
315 | // move the rook from A to D (0 -> 3) and wipe its previous location |
||
316 | memcpy (&new_move.slots[to_line][3], &new_move.slots[from_line][0], sizeof (boardslot_t)); |
||
317 | memset (&new_move.slots[from_line][0], 0, sizeof (boardslot_t)); |
||
318 | } |
||
319 | else if ((from_column == 4) && (to_column == 6)) |
||
320 | { |
||
321 | // move the rook from H to F (7 -> 5) and wipe its previous location |
||
322 | memcpy (&new_move.slots[to_line][5], &new_move.slots[from_line][7], sizeof (boardslot_t)); |
||
323 | memset (&new_move.slots[from_line][7], 0, sizeof (boardslot_t)); |
||
324 | } |
||
325 | |||
326 | // no matter whether it castled or not, remember this king cannot castle anymore |
||
327 | playing_side->shortcastle_allowed = false; |
||
328 | playing_side->longcastle_allowed = false; |
||
329 | } |
||
330 | |||
331 | // else was it the rook ? |
||
332 | else if (new_move.slots[from_line][from_column].part == PART_ROOK) |
||
333 | { |
||
334 | // was the tower in A ? or was it in H ? |
||
335 | if (from_column == 0) |
||
336 | playing_side->longcastle_allowed = false; // player can no longer castle queenside |
||
337 | else if (from_column == 7) |
||
338 | playing_side->shortcastle_allowed = false; // player can no longer castle bishopside |
||
339 | } |
||
340 | |||
341 | // move the part and erase the starting slot |
||
342 | memcpy (&new_move.slots[to_line][to_column], &new_move.slots[from_line][from_column], sizeof (boardslot_t)); |
||
343 | memset (&new_move.slots[from_line][from_column], 0, sizeof (boardslot_t)); |
||
344 | |||
345 | // does the new move have a comment ? if so, save it |
||
346 | if ((comment != NULL) && (comment[0] != 0)) |
||
347 | { |
||
348 | new_move.comment_size = wcslen (comment) + 1; // save this move's comment and the size of the mallocated space |
||
349 | new_move.comment = (wchar_t *) SAFE_malloc (new_move.comment_size, sizeof (wchar_t), false); |
||
350 | wcscpy_s (new_move.comment, new_move.comment_size, comment); // copy comment |
||
351 | } |
||
352 | |||
353 | // evaluate check and stalemate status |
||
354 | new_move.is_check = Move_IsCheck (&new_move, 1 - new_move.color); // save whether opponent is in check or not |
||
355 | new_move.is_stalemate = Move_IsStaleMate (&new_move, 1 - new_move.color); // save whether opponent is stalemate |
||
356 | |||
357 | // describe our move in Standard Abbreviated Notation and describe the resulting table in Forsyth-Edwards Notation |
||
358 | Move_DescribeInSAN (&new_move); |
||
359 | Move_DescribeInFEN (&new_move); |
||
360 | |||
361 | // resize the previous moves array and insert our new move at the end of it |
||
362 | board->moves = (boardmove_t *) SAFE_realloc (board->moves, board->move_count, board->move_count + 1, sizeof (boardmove_t), false); |
||
363 | memcpy (&board->moves[board->move_count], &new_move, sizeof (boardmove_t)); |
||
364 | board->move_count++; // we know now one previous move more |
||
365 | |||
366 | board->viewed_move = board->move_count - 1; // set the move to be viewed to be the last move |
||
367 | |||
368 | // if the game hadn't started yet, remember it has now |
||
369 | if (board->game_state == STATE_UNKNOWN) |
||
370 | board->game_state = STATE_PLAYING; |
||
371 | |||
372 | // remember the last move time |
||
373 | board->lastmove_time = current_time; |
||
374 | |||
75 | pmbaty | 375 | // reset stoppage time |
376 | stoppage_time = 0; |
||
377 | |||
1 | pmbaty | 378 | // remember the game state should be reevaluated |
379 | board->reevaluate = true; |
||
380 | |||
381 | return; // finished |
||
382 | } |