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