Subversion Repositories Games.Chess Giants

Rev

Rev 177 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  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 *game_rules, 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.    // set the game rules to the specified one
  49.    wcscpy_s (board->game_rules, WCHAR_SIZEOF (board->game_rules), game_rules);
  50.  
  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.  
  55.    // notify that board was just set up
  56.    board->was_setup = true;
  57.    board->players[COLOR_WHITE].should_wakeup = true;
  58.    board->players[COLOR_BLACK].should_wakeup = true;
  59.    board->reevaluate = true;
  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;
  135.    board->players[COLOR_WHITE].should_wakeup = true;
  136.    board->players[COLOR_BLACK].should_wakeup = true;
  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.  
  161.    // clear board setup notification
  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.  
  196.    // now unlock all the players' buffers and have them sleep for 2 seconds
  197.    board->players[COLOR_BLACK].sendbuffer_locked = false;
  198.    board->players[COLOR_WHITE].sendbuffer_locked = false;
  199.    board->players[COLOR_BLACK].sleep_time = current_time + 2.0f;
  200.    board->players[COLOR_WHITE].sleep_time = current_time + 2.0f;
  201.  
  202.    board->reevaluate = true; // remember to reevaluate this board
  203.  
  204.    return; // finished
  205. }
  206.  
  207.  
  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
  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"));
  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.  
  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
  265.    memset (&new_move, 0, sizeof (new_move));
  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
  387.    Move_DescribeInSAN (&new_move, last_move, new_move.pgntext, WCHAR_SIZEOF (new_move.pgntext), false); // don't use the localized part abbreviations
  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.  
  404.    // reset stoppage time
  405.    stoppage_time = 0;
  406.  
  407.    // remember the game state should be reevaluated
  408.    board->reevaluate = true;
  409.  
  410.    return; // finished
  411. }
  412.