#include "board.hpp" #include #include #include "sprites.hpp" Board::Board() { load_FEN(std::string("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")); } Board::Board(std::string board_fen) { load_FEN(board_fen); } void Board::load_FEN(std::string board_fen) { active_color = WHITE; able_to_castle[0][0] = false; able_to_castle[0][1] = false; able_to_castle[1][0] = false; able_to_castle[1][1] = false; en_passant = -1; half_turn_count = 0; full_turn_count = 0; selected_space = -1; int x=0,y=0; std::string black_team("kqbnrpcos"); std::string white_team("KQBNRPCOS"); for(int i = 0; i < board_fen.size() && !(x==BOARD_SIZE-1 && y==BOARD_SIZE); i++) { if (board_fen[i] == '/') { y++; x=0; } else if (std::isdigit(board_fen[i])) { x += board_fen[i]-'0'; } else { size_t loc = black_team.find(board_fen[i],0); if (std::string::npos != loc) { Visibility vis = SHOWN; if (loc >= UNKNOWN) { vis = HIDDEN; loc -= UNKNOWN; loc += BISHOP; } Type type = (Type)loc; Team team = BLACK; game_board[x][y] = Piece(type, team, vis); } else { loc = white_team.find(board_fen[i],0); if (std::string::npos != loc) { Visibility vis = SHOWN; if (loc >= UNKNOWN) { vis = HIDDEN; loc -= UNKNOWN; loc += BISHOP; } Type type = (Type)loc; Team team = WHITE; game_board[x][y] = Piece(type, team, vis); } } x++; } } size_t active_color_loc = board_fen.find(' ', 0); if (std::string::npos == active_color_loc || active_color_loc+1 >= board_fen.size()) { return; } else { active_color_loc = active_color_loc+1; } active_color = ('w'==board_fen[active_color_loc])?WHITE:BLACK; size_t able_to_castle_loc = active_color_loc+2; while(board_fen[able_to_castle_loc] != ' ') { switch(board_fen[able_to_castle_loc]) { case 'K': able_to_castle[WHITE][KING] = true; break; case 'Q': able_to_castle[WHITE][QUEEN] = true; break; case 'k': able_to_castle[BLACK][KING] = true; break; case 'q': able_to_castle[BLACK][QUEEN] = true; break; } able_to_castle_loc++; } size_t en_passant_loc = able_to_castle_loc+1; if (board_fen[en_passant_loc] != '-') { x = board_fen[en_passant_loc]-'a'; y = board_fen[en_passant_loc+1]-'0'; en_passant = x+y*BOARD_SIZE; en_passant_loc+=2; } else { en_passant_loc++; } size_t half_turn_count_loc = en_passant_loc+1; const char* board_fen_cstr = board_fen.c_str(); char* full_turn_count_loc; half_turn_count = strtol(board_fen_cstr+half_turn_count_loc, &full_turn_count_loc, 10); full_turn_count = strtol(full_turn_count_loc, nullptr, 10); } void Board::draw_board(SDL_Surface* dest_surface) { bool light_tile = true; std::vector target_spaces = get_moves_for_space(selected_space); for (int x = 0; x < BOARD_SIZE; x++) { for (int y = 0; y < BOARD_SIZE; y++) { SDL_Rect dest_rect({SPRITE_SIZE*x,SPRITE_SIZE*y,SPRITE_SIZE,SPRITE_SIZE}); uint32_t color = 0; if (target_spaces.end() == std::find(target_spaces.begin(), target_spaces.end(), x+y*BOARD_SIZE)) { if (light_tile) { if (x+y*BOARD_SIZE == selected_space) { color = SDL_MapRGB(dest_surface->format, 132, 133, 104); } else { color = SDL_MapRGB(dest_surface->format, 235, 236, 208); } } else { if (x+y*BOARD_SIZE == selected_space) { color = SDL_MapRGB(dest_surface->format, 59, 74, 43); } else { color = SDL_MapRGB(dest_surface->format, 119, 149, 86); } } } else { if (CAPTURE == can_move(selected_space, x+y*BOARD_SIZE)) { if (light_tile) { color = SDL_MapRGB(dest_surface->format, 255, 200, 200); } else { color = SDL_MapRGB(dest_surface->format, 128, 100, 100); } } else { if (light_tile) { color = SDL_MapRGB(dest_surface->format, 200, 200, 255); } else { color = SDL_MapRGB(dest_surface->format, 100, 100, 128); } } } SDL_FillRect(dest_surface, &dest_rect, color); SDL_Surface* piece_surface = SDL_CreateRGBSurfaceWithFormat(0, SPRITE_SIZE, SPRITE_SIZE, 32, dest_surface->format->format); SDL_FillRect(piece_surface, nullptr, SDL_MapRGB(piece_surface->format, 0xFF, 0x00, 0xFF)); SDL_SetColorKey(piece_surface, SDL_TRUE, SDL_MapRGB(piece_surface->format, 0xFF, 0x00, 0xFF)); if (0 == Sprite::get(game_board[x][y], piece_surface)) { SDL_BlitSurface(piece_surface, nullptr, dest_surface, &dest_rect); } SDL_FreeSurface(piece_surface); light_tile = !light_tile; } light_tile = !light_tile; } } Team Board::get_active_color() { return active_color; } Piece Board::get_piece(int x, int y) { if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE) return Piece(); return game_board[x][y]; } Piece Board::get_piece(int space) { if (space < 0 || space >= BOARD_SIZE*BOARD_SIZE) return Piece(); return game_board[space%BOARD_SIZE][space/BOARD_SIZE]; } bool Board::get_able_to_castle(Team team, Type board_half) { if ((board_half != KING && board_half != QUEEN) || team == NO_TEAM) return false; return able_to_castle[team][board_half]; } int Board::get_en_passant() { return en_passant; } int Board::get_half_turn_count() { return half_turn_count; } int Board::get_full_turn_count() { return full_turn_count; } void Board::set_selected_space(int x, int y) { if (game_board[x][y].get_team() != WHITE) return; selected_space = x+y*BOARD_SIZE; } void Board::set_selected_space(int space) { if (game_board[space%BOARD_SIZE][space/BOARD_SIZE].get_team() != WHITE) return; selected_space = space; } void Board::get_selected_space(int* x, int* y) { if (nullptr != x) *x = selected_space%BOARD_SIZE; if (nullptr != y) *y = selected_space/BOARD_SIZE; } int Board::get_selected_space() { return selected_space; } std::vector Board::get_moves_for_space(int x, int y) { if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE || game_board[x][y].get_type() == NO_TYPE) return std::vector(); int forward_direction = (game_board[x][y].get_team() == WHITE)?-1:1; std::vector out_spaces; Type piece_type = (game_board[x][y].get_vis() == VIS_NONE)?UNKNOWN:game_board[x][y].get_type(); if (PAWN == piece_type) { if (y == ((game_board[x][y].get_team()==WHITE)?6:1) && game_board[x][y+forward_direction].get_type() == NO_TYPE && FREE == can_move(x+y*BOARD_SIZE, x+(y+2*forward_direction)*BOARD_SIZE)) { out_spaces.push_back(x+(y+2*forward_direction)*BOARD_SIZE); } if (y+forward_direction >= 0 && y+forward_direction < BOARD_SIZE && FREE == can_move(x+y*BOARD_SIZE, x+(y+forward_direction)*BOARD_SIZE)) { out_spaces.push_back(x+(y+forward_direction)*BOARD_SIZE); } if (x-1 >= 0 && x-1 < BOARD_SIZE && y+forward_direction >= 0 && y+forward_direction < BOARD_SIZE && CAPTURE == can_move(x+y*BOARD_SIZE, x+(y+forward_direction)*BOARD_SIZE-1)) { out_spaces.push_back(x+(y+forward_direction)*BOARD_SIZE-1); } if (x+1 >= 0 && x+1 < BOARD_SIZE && y+forward_direction >= 0 && y+forward_direction < BOARD_SIZE && CAPTURE == can_move(x+y*BOARD_SIZE, x+(y+forward_direction)*BOARD_SIZE+1)) { out_spaces.push_back(x+(y+forward_direction)*BOARD_SIZE+1); } if (x+(y+forward_direction)*BOARD_SIZE-1 == en_passant && FREE == can_move(x+y*BOARD_SIZE, x+(y+forward_direction)*BOARD_SIZE-1)) { out_spaces.push_back(x+(y+forward_direction)*BOARD_SIZE-1); } else if (x+(y+forward_direction)*BOARD_SIZE+1 == en_passant && FREE == can_move(x+y*BOARD_SIZE, x+(y+forward_direction)*BOARD_SIZE+1)) { out_spaces.push_back(x+(y+forward_direction)*BOARD_SIZE+1); } } if (QUEEN == piece_type || ROOK == piece_type) { for (int x1 = x-1; x1 > 0; x1--) { MoveType result = can_move(x+y*BOARD_SIZE, x1+y*BOARD_SIZE); if (BLOCKED == result) break; out_spaces.push_back(x1+y*BOARD_SIZE); if (CAPTURE == result) break; } for (int x1 = x+1; x1 < BOARD_SIZE; x1++) { MoveType result = can_move(x+y*BOARD_SIZE, x1+y*BOARD_SIZE); if (BLOCKED == result) break; out_spaces.push_back(x1+y*BOARD_SIZE); if (CAPTURE == result) break; } for (int y1 = y-1; y1 > 0; y1--) { MoveType result = can_move(x+y*BOARD_SIZE, x+y1*BOARD_SIZE); if (BLOCKED == result) break; out_spaces.push_back(x+y1*BOARD_SIZE); if (CAPTURE == result) break; } for (int y1 = y+1; y1 > 0; y1++) { MoveType result = can_move(x+y*BOARD_SIZE, x+y1*BOARD_SIZE); if (BLOCKED == result) break; out_spaces.push_back(x+y1*BOARD_SIZE); if (CAPTURE == result) break; } } if (QUEEN == piece_type || BISHOP == piece_type) { for(int x1 = x-1, y1 = y-1; x1 >= 0 && y1 >= 0; x1--, y1--) { MoveType result = can_move(x+y*BOARD_SIZE, x1+y1*BOARD_SIZE); if (BLOCKED == result) break; out_spaces.push_back(x1+y1*BOARD_SIZE); if (CAPTURE == result) break; } for(int x1 = x-1, y1 = y+1; x1 >= 0 && y1 < BOARD_SIZE; x1--, y1++) { MoveType result = can_move(x+y*BOARD_SIZE, x1+y1*BOARD_SIZE); if (BLOCKED == result) break; out_spaces.push_back(x1+y1*BOARD_SIZE); if (CAPTURE == result) break; } for(int x1 = x+1, y1 = y-1; x1 < BOARD_SIZE && y1 >= 0; x1++, y1--) { MoveType result = can_move(x+y*BOARD_SIZE, x1+y1*BOARD_SIZE); if (BLOCKED == result) break; out_spaces.push_back(x1+y1*BOARD_SIZE); if (CAPTURE == result) break; } for(int x1 = x+1, y1 = y+1; x1 < BOARD_SIZE && y1 < BOARD_SIZE; x1++, y1++) { MoveType result = can_move(x+y*BOARD_SIZE, x1+y1*BOARD_SIZE); if (BLOCKED == result) break; out_spaces.push_back(x1+y1*BOARD_SIZE); if (CAPTURE == result) break; } } if (KNIGHT == piece_type) { int knight_offsets[8][2] = {{-1,2},{1,2},{-1,-2},{1,-2},{-2,1},{2,1},{-2,-1},{2,-1}}; for(int i = 0; i < 8; i++) { int x1 = x+knight_offsets[i][0]; int y1 = y+knight_offsets[i][1]; if (x1 < 0 || x1 >= BOARD_SIZE || y1 < 0 || y1 >= BOARD_SIZE || BLOCKED == can_move(x+y*BOARD_SIZE, x1+y1*BOARD_SIZE)) { continue; } out_spaces.push_back(x1+y1*BOARD_SIZE); } } if (KING == piece_type) { int king_offsets[8][2] = {{-1,-1},{0,-1},{1,-1},{-1,0},{1,0},{1,-1},{1,0},{1,1}}; for(int i = 0; i < 8; i++) { int x1 = x+king_offsets[i][0]; int y1 = y+king_offsets[i][1]; if (x1 < 0 || x1 >= BOARD_SIZE || y1 < 0 || y1 >= BOARD_SIZE || BLOCKED == can_move(x+y*BOARD_SIZE, x1+y1*BOARD_SIZE)) { continue; } Piece old_piece = game_board[x1][y1]; game_board[x1][y1] = game_board[x][y]; game_board[x][y] = Piece(); bool was_check = is_check(game_board[x1][y1].get_team()); game_board[x][y] = game_board[x1][y1]; game_board[x1][y1] = old_piece; if (!was_check) { out_spaces.push_back(x1+y1*BOARD_SIZE); } } if (able_to_castle[game_board[x][y].get_team()][KING] && game_board[7][y].get_team() == game_board[x][y].get_team() && game_board[7][y].get_type() == ROOK && game_board[7][y].get_vis() == SHOWN && game_board[6][y].get_type() == NO_TYPE && game_board[5][y].get_type() == NO_TYPE) { Piece rook_piece = game_board[7][y]; Piece king_piece = game_board[x][y]; game_board[7][y] = king_piece; game_board[x][y] = rook_piece; if (!is_check(game_board[x][y].get_team())) out_spaces.push_back(7+y*BOARD_SIZE); game_board[7][y] = rook_piece; game_board[x][y] = king_piece; } if (able_to_castle[game_board[x][y].get_team()][QUEEN] && game_board[0][y].get_team() == game_board[x][y].get_team() && game_board[0][y].get_type() == ROOK && game_board[0][y].get_vis() == SHOWN && game_board[1][y].get_type() == NO_TYPE && game_board[2][y].get_type() == NO_TYPE && game_board[3][y].get_type() == NO_TYPE) { Piece rook_piece = game_board[0][y]; Piece king_piece = game_board[x][y]; game_board[0][y] = king_piece; game_board[x][y] = rook_piece; if (!is_check(game_board[x][y].get_team())) out_spaces.push_back(y*BOARD_SIZE); game_board[0][y] = rook_piece; game_board[x][y] = king_piece; } } if (UNKNOWN == piece_type) { int unknown_offsets[8][2] = {{-1,-1},{0,-1},{1,-1},{-1,0},{1,0},{1,-1},{1,0},{1,1}}; for(int i = 0; i < 8; i++) { int x1 = x+unknown_offsets[i][0]; int y1 = y+unknown_offsets[i][1]; if (x1 < 0 || x1 >= BOARD_SIZE || y1 < 0 || y1 >= BOARD_SIZE) { continue; } MoveType result = can_move(x+y*BOARD_SIZE, x1+y1*BOARD_SIZE); if (BLOCKED != result) { out_spaces.push_back(x1+y1*BOARD_SIZE); if (CAPTURE != result) { int x1 = x+2*unknown_offsets[i][0]; int y1 = y+2*unknown_offsets[i][1]; if (x1 >= 0 && x1 < BOARD_SIZE && y1 >= 0 && y1 < BOARD_SIZE || BLOCKED == can_move(x+y*BOARD_SIZE, x1+y1*BOARD_SIZE)) { out_spaces.push_back(x1+y1*BOARD_SIZE); } } } } out_spaces.push_back(x+y*BOARD_SIZE); } return out_spaces; } std::vector Board::get_moves_for_space(int space) { if (space < 0 || space >= BOARD_SIZE*BOARD_SIZE) return std::vector(); return get_moves_for_space(space%BOARD_SIZE, space/BOARD_SIZE); } bool Board::is_check(Team team) { int king_space = -1; Team enemy_team = (team == WHITE)?BLACK:WHITE; for(int x = 0; x < BOARD_SIZE && king_space == -1; x++) { for(int y = 0; y < BOARD_SIZE && king_space == -1; y++) { if (game_board[x][y].get_team() == team && game_board[x][y].get_type() == KING) { king_space = x+y*BOARD_SIZE; break; } } } if (-1 == king_space) { return false; } for(int x = 0; x < BOARD_SIZE && king_space == -1; x++) { for(int y = 0; y < BOARD_SIZE && king_space == -1; y++) { if (game_board[x][y].get_team() == enemy_team) { std::vector possible_moves = get_moves_for_space(x,y); if (possible_moves.end() != std::find(possible_moves.begin(), possible_moves.end(), king_space)) { return true; } } } } return false; } MoveType Board::can_move(int space_from, int space_to) { if (space_from < 0 || space_to < 0 || space_from >= BOARD_SIZE*BOARD_SIZE || space_to >= BOARD_SIZE*BOARD_SIZE) { return BLOCKED; } int xf=space_from%BOARD_SIZE,yf=space_from/BOARD_SIZE,xt=space_to%BOARD_SIZE,yt=space_to/BOARD_SIZE; Team enemy_team = (game_board[xf][yf].get_team() == WHITE)?BLACK:WHITE; if (game_board[xt][yt].get_type() == NO_TYPE) { if (game_board[xf][yf].get_type() == PAWN && space_to == en_passant) { return CAPTURE; } else { return FREE; } } if (game_board[xt][yt].get_team() == enemy_team) { return CAPTURE; } return BLOCKED; }