#include "bitwise.h" #include "functions.h" #include "all_moves_functions.cpp" #include #include #include #include #include #include #include #include #include #include /* * * These functions use an int to pass a lot of information. * The notation is like this: * 0000 0000 0000 0000 0111 1111 -> position of piece * 0000 0000 0000 0111 1000 0000 -> piece type * 0000 0000 0111 1000 0000 0000 -> piece of opposite type (eg. opposite of W_KING, is B_KING; B_ROOK, W_ROOK etc...) * 0000 0000 1000 0000 0000 0000 -> team of the piece (1=white, 0=black) * 0111 1111 0000 0000 0000 0000 -> new position (if applicable) * **/ /* * These functions also use an int for king info. This is so: * a) I don't have to recompute the info each time I nedd it in a functoin. * b) So that I don't have the overhead of a struct. (I know, "micro optimizations are the root of all evil", I can't help myself) * The notation is as follows: * 0000 0000 0000 0000 0111 1111 -> position of piece * 0000 0000 0000 0111 1000 0000 -> piece type * 0000 0011 1111 1000 0000 0000 -> opposite piece position * 0011 1100 0000 0000 0000 0000 -> piece of opposite type * 0100 0000 0000 0000 0000 0000 -> team of the piece (1=white, 0=black) */ inline int get_piece_pos(int pieceinfo){ return (pieceinfo & 0x7f); } inline int get_piece_type(int pieceinfo){ return ((pieceinfo << 7) & 0xf); } inline int get_opposite_piece_type(int pieceinfo){ return ((pieceinfo << 11) & 0xf); } // TO find out if white/black use: // get_team_of_piece_type == Color::WHITE/BLACK inline int get_team_of_piece_type(int pieceinfo){ return ((pieceinfo << 15) & 0x1); } inline int get_new_position_of_piece(int pieceinfo){ return ((pieceinfo << 16) & 0xf); } /* Move ints contain a lot of information as well. However, these are DIFFERENt from the other dense ints. Moves are universalyl passed by reference (&) to a vector of ints, whereas the above is passed only is 1 int by itself. * Here is the notation for moves (although it is obscured by the make_move(), get_to/from_sq(), get_captured_pc() etc... functions. * * From (Position): 7 bits (2^7 == 128) possibilities * To (Position): same as above * Captured piece, if any: 4 bits (16) possibilities * Promoted to, if any: 4 bits (16) possibilities * en passant flag: 1 bit * pawn starting move flag: 1 bit * castle move flag: 1 bit * * 0000 0000 0000 0000 0000 0111 1111 -> From square position (& 0x7F) * 0000 0000 0000 0011 1111 1000 0000 -> To square position (>> 7 & 0x7F) * 0000 0000 0011 1100 0000 0000 0000 -> captured piece, if any (>> 14 & 0xF) * 0000 0011 1100 0000 0000 0000 0000 -> if prmoted, what to? (>> 18 & 0xF) * 0000 0100 0000 0000 0000 0000 0000 -> en passant (>> 22 &0x1) * 0000 1000 0000 0000 0000 0000 0000 -> pawn starting move (>> 23 &0x1) * 0001 0000 0000 0000 0000 0000 0000 -> castle move (>> 24 0x1) * 0010 0000 0000 0000 0000 0000 0000 -> check flag (>> 25 &0x1) * */ //const std::array ROOK_PIECE_OFFSETS = {-1, -10, 1, 10}; //const std::array BISHOP_PIECE_OFFSETS = {-11, -9, 9, 11}; const std::vector QUEEN_PIECE_OFFSETS = {-1, -10, 1, 10, -11, -9, 9, 11}; Rank get_rank(int pos){ int rank = 0; while (pos >= 0){ pos -= 10; rank++; } return static_cast(rank - 3); } bool is_white(int pt){ for (auto pn : Pieces::WHITE){ if (pn == pt) return true; } return false; } bool is_black(int pt){ for (auto pn : Pieces::BLACK){ if (pn == pt) return true; } return false; } Color get_color(PieceType pt){ if (is_white(pt)) return Color::WHITE; if (is_black(pt)) return Color::BLACK; return Color::NO_COLOR; } Color get_color(int pn, const std::array& board){ return get_color(board[pn]); } Color rev_color(Color c){ if (c==Color::NO_COLOR) return Color::NO_COLOR; return c==Color::WHITE?Color::BLACK:Color::WHITE; } PieceType rev_color(PieceType pt){ //return static_cast((~pt) & 0xf); for (long unsigned int i=0; i!=Pieces::WHITE.size(); i++){ if (pt == Pieces::WHITE[i]){ return Pieces::BLACK[i]; } } for (long unsigned int i=0; i!=Pieces::BLACK.size(); i++){ if (pt == Pieces::BLACK[i]){ return Pieces::WHITE[i]; } } return PieceType::NONE; } std::vector get_possible_movers(Position pn, const std::array& board){ std::vector pns = {Position::A1}; return pns; } int get_pos_of(PieceType pt, const std::array& board){ for (int pn = Position::A8; pn!=Position::H1; pn++){ if (board[pn] == pt){ return pn; } } return Position::NA; } std::vector get_poss_of(PieceType pt, const std::array& board){ std::vector results; for (int pn = Position::A8; pn!=Position::H1; pn++){ if (board[pn] == pt){ results.push_back(pn); } } return results; } void get_poss_of(PieceType pt, const std::array& board, std::vector& pns){ for (int pn = Position::A8; pn!=Position::H1; pn++){ if (board[pn] == pt){ pns.push_back(pn); } } } bool would_be_attacked(int pos, PieceType piece, const std::array& board){ Color pc = get_color(piece); auto other_pieces = pc==Color::WHITE?Pieces::BLACK:Pieces::WHITE; for (PieceType opposite_color_piece : other_pieces){ PieceType same_color_piece = rev_color(opposite_color_piece); std::vector moves; get_all_moves_as_if(pos, same_color_piece, board, moves, false); for (int reverse_move : moves){ if (get_captured_pc(reverse_move) == opposite_color_piece){ return true; } } } return false; } bool is_attacked(int pos, const std::array& board){ return would_be_attacked(pos, board[pos], board); } bool king_checked(const std::array& board, Color color_of_king){ PieceType king = color_of_king==WHITE?W_KING:B_KING; int king_pos = get_pos_of(king, board); return is_attacked(king_pos, board); } void add_checked_flags(PieceType pt, const std::array& board, std::vector& pns){ PieceType other_king = is_white(pt)?PieceType::B_KING:PieceType::W_KING; int other_king_pos = get_pos_of(other_king, board); // If the other team doesn't have a king, don't add any checked flags if (other_king_pos == Position::NA){ return; } for (auto move_pn=pns.begin(); move_pn!=pns.end();){ std::array moved_board; dumb_move(*move_pn, board, moved_board); if (is_attacked(other_king_pos, moved_board)){ *move_pn |= (1 << 25); } ++move_pn; } } //TODO: Make faster by running from king squar eonly, instead of running on every piece of opposite team. void filter_checked_moves(PieceType pt, const std::array& board, std::vector& pns){ PieceType my_king = is_white(pt)?PieceType::W_KING:PieceType::B_KING; int my_king_pos = get_pos_of(my_king, board); bool remove_all_castles = false; // If this team doesn't have a king, don't do anything, jsut return. if (my_king_pos == Position::NA){ return; } for (auto p_pn= pns.begin(); p_pn!=pns.end();){ if (get_castle_flag(*p_pn) == 1){ // If removing all castle flags is triggered // (by being in check) // remove all of them. if (remove_all_castles){ p_pn = pns.erase(p_pn); ++p_pn; break; } // TODO: Make this more effecient. // There is no way in hell, this is as complicated as it seems. // If moved left // Queenside if ((get_from_sq(*p_pn) - get_to_sq(*p_pn)) > 0){ if(would_be_attacked(get_to_sq(*p_pn), my_king, board) || would_be_attacked(get_to_sq(*p_pn)+1, my_king, board)){ p_pn = pns.erase(p_pn); } else { ++p_pn; break; } // Kingside } else { if(would_be_attacked(get_to_sq(*p_pn), my_king, board) || would_be_attacked(get_to_sq(*p_pn)-1, my_king, board)){ p_pn = pns.erase(p_pn); } else { ++p_pn; break; } } } else { // if it is a king, that is in check // remove all castle moves if ((pt == PieceType::W_KING || pt == PieceType::B_KING) && is_attacked(get_from_sq(*p_pn), board)){ remove_all_castles = true; } // Make move std::array moved_board; dumb_move(*p_pn, board, moved_board); // This is for when the king is the same piece that is moving. // If this is the case, reset to king position to the new position given by the get_to_sq() of the move. if (pt == my_king){ my_king_pos = get_to_sq(*p_pn); } if (is_attacked(my_king_pos, moved_board)){ p_pn = pns.erase(p_pn); } else { ++p_pn; } } } } void _get_all_moves_as_if_ray_type(int pos, std::vector offsets, std::vector& moves, const std::array& board, Color color_of_piece, Color color_of_opponent){ for (int offset : offsets){ for (int times = 1; times < 8; ++times){ int true_offset = pos+(offset*times); if (get_color(board[true_offset]) == color_of_opponent){ moves.push_back(make_move(pos, true_offset, board[true_offset])); break; } else if (get_color(board[true_offset]) == color_of_piece || board[true_offset] == PieceType::INV){ break; } else { moves.push_back(make_move(pos, true_offset, board[true_offset])); } } } } void _get_all_moves_as_if_not_ray(int pos, std::vector offsets, std::vector& moves, const std::array& board, Color color_of_piece, Color color_of_opponent){ for (int offset : offsets){ int true_offset = pos+offset; if (board[true_offset] == PieceType::NONE || get_color(board[true_offset]) == color_of_opponent){ moves.push_back(make_move(pos, true_offset, board[true_offset])); } } } // WARNING: This function depends on the order of the moves being correct. // The last two king moves MUST be for the king's position +1, and -1 respectively. // TODO finish function void _get_all_castle_moves(int pos, int castle_perms, std::vector& moves, const std::array& board, Color color_of_piece, Color color_of_opponent){ if (color_of_piece == Color::WHITE){ } else { } } void get_all_moves_as_if(int pos, PieceType pt, const std::array& board, std::vector& moves, bool recursive, int en_passant, int castle_perms){ Color color_of_piece = get_color(pt); Color color_of_opponent = rev_color(color_of_piece); switch(pt){ case PieceType::B_QUEEN: case PieceType::W_QUEEN: _get_all_moves_as_if_ray_type(pos, QUEEN_PIECE_OFFSETS, moves, board, color_of_piece, color_of_opponent); //_get_all_moves_rook(pos, moves, board, color_of_piece, color_of_opponent); //_get_all_moves_bishop(pos, moves, board, color_of_piece, color_of_opponent); break; case PieceType::B_ROOK: case PieceType::W_ROOK: _get_all_moves_as_if_ray_type(pos, ROOK_PIECE_OFFSETS, moves, board, color_of_piece, color_of_opponent); //_get_all_moves_rook(pos, moves, board, color_of_piece, color_of_opponent); break; case PieceType::B_BISHOP: case PieceType::W_BISHOP: _get_all_moves_as_if_ray_type(pos, BISHOP_PIECE_OFFSETS, moves, board, color_of_piece, color_of_opponent); //_get_all_moves_bishop(pos, moves, board, color_of_piece, color_of_opponent); break; case PieceType::B_KNIGHT: case PieceType::W_KNIGHT: _get_all_moves_as_if_not_ray(pos, KNIGHT_PIECE_OFFSETS, moves, board, color_of_piece, color_of_opponent); //_get_all_moves_knight(pos, moves, board, color_of_piece, color_of_opponent); break; case PieceType::B_KING: case PieceType::W_KING: // _get_all_moves_as_if_not_ray(pos, KING_PIECE_OFFSETS, moves, board, color_of_piece, color_of_opponent); // if (castle_perms != 0){ // _get_all_castle_moves(); // } _get_all_moves_king(pos, moves, board, color_of_piece, color_of_opponent, castle_perms); break; case PieceType::B_PAWN: case PieceType::W_PAWN: _get_all_moves_pawn(pos, moves, board, color_of_piece, color_of_opponent, en_passant); break; default: break; } if (recursive){ filter_checked_moves(pt, board, moves); add_checked_flags(pt, board, moves); } } void get_all_moves(int pos, const std::array& board, std::vector& moves, bool recursive, int en_passant, int castle_perms){ get_all_moves_as_if(pos, board[pos], board, moves, recursive, en_passant, castle_perms); } std::vector get_all_moves(int pos, const std::array& board, bool recursive, int en_passant, int castle_perms){ std::vector moves = {}; get_all_moves_as_if(pos, board[pos], board, moves, recursive, en_passant, castle_perms); return moves; } void dumb_move(int move, const std::array& board, std::array& new_board){ std::copy(std::begin(board), std::end(board), std::begin(new_board)); int en_passant_flag = get_en_pass_flag(move); int castle_flag = get_castle_flag(move); int from = get_from_sq(move); int to = get_to_sq(move); PieceType piece = new_board[from]; if (en_passant_flag == 1) { int diff = to-from; int other_pawn_pos = 0; if (diff == -11){ other_pawn_pos = from-1; } else if (diff == -9){ other_pawn_pos = from+1; } else if (diff == 11){ other_pawn_pos = from+1; } else if (diff == 9){ other_pawn_pos = from-1; } new_board[other_pawn_pos] = PieceType::NONE; new_board[to] = piece; new_board[from] = PieceType::NONE; } else if(castle_flag == 1){ int diff = to-from; int rook_from_pos = 0; int rook_to_pos = 0; // If castling queenside if (diff == -2){ rook_from_pos = to-2; rook_to_pos = from-1; // If caslting kingside } else if (diff == 2){ rook_from_pos = to+1; rook_to_pos = from+1; } PieceType rook_pt = new_board[rook_from_pos]; new_board[rook_from_pos] = PieceType::NONE; new_board[rook_to_pos] = rook_pt; new_board[from] = PieceType::NONE; new_board[to] = piece; } else { new_board[to] = piece; new_board[from] = PieceType::NONE; } } std::array dumb_move(int pos, const std::array& board){ std::array new_board = {}; dumb_move(pos, board, new_board); return new_board; } void _filter_unneeded_squares(std::vector& moves, int piecetype, int origin){ for (auto move_pn=moves.begin(); move_pn!=moves.end();){ if (get_captured_pc(*move_pn) == piecetype && get_to_sq(*move_pn) != origin){ ++move_pn; } else { move_pn = moves.erase(move_pn); } } }; void _non_pawn_disambiguate(int from, int to, int piecetype, const std::vector& moves, std::string& disambig){ if (moves.size() == 1){ disambig = POSITION_STRING[from][0]; int other_from = get_to_sq(moves.at(0)); int min_from = other_fromfrom?other_from:from; // If the remainder of the difference divided by 10 is 0 // they are on the same file. // Use rank to disambiguate if ((max_from-min_from) % 10 == 0){ disambig = POSITION_STRING[from][1]; } } else if (moves.size() > 1){ disambig = POSITION_STRING[from]; } } std::string to_notation(int move, const std::array& board){ int to = get_to_sq(move); int from = get_from_sq(move); // This can be ran right away due to the simplicity of the notation. if (get_castle_flag(move) == 1){ return to-from<0 ? "O-O-O" : "O-O"; } PieceType piecetype = board[from]; std::string from_string = POSITION_STRING[from]; int captured_piece = get_captured_pc(move); int promoting_to = get_promoted_to_pc(move); // Blank if not pawn // file if pawn and capture move std::string pawn_file = ""; // Blank if pawn // otherwise N/B/R/Q/K corresponding to each piece // same for black and white std::string piece_character = ""; // Blank if not a capture // x if a capture move std::string capture_character = ""; // Blank if not en passant move // otherwise e.p. std::string en_passant = ""; // Black if no check, // + if check // # if checkmate std::string check = ""; // Blank if only this piece can move to the to square // include file (a-h) if another piece can move to the to square // includes rank (1-8) if pieces have same file // includes both (f)(r) if 2+ identical piece can move to the to square std::string disambig = ""; // Blank if not a pawn promotion move. // Otherwise is equal to "=P/N/B/R/Q". std::string promotion; auto other_pieces = is_white(piecetype)?Pieces::BLACK:Pieces::WHITE; if (captured_piece != PieceType::NONE){ capture_character = "x"; // Comment #6 // if a pawn is capturing, have the disambiguation be the pawn's file // stored in a seperate variable because pawn disabiguation by file happens at the start of a notation. For example: // exf4 as opposed to Qh1xf4 if (piecetype == PieceType::W_PAWN || piecetype == PieceType::B_PAWN){ pawn_file = POSITION_STRING[from][0]; } } // Pawns do not require this, as their file is ALWAYS enough to identify them, // and the file is always speicified on a pawn capture. // (See comment #6) if (piecetype != PieceType::W_PAWN && piecetype != PieceType::B_PAWN){ // Simulated a piece of the same type, and opposite color moving from the to square // to check if any other pieces can also move here. // Basically it searches to see if there are duplicate pieces which can move to the same spot. // and stores any duplicates in the other_moves variable. PieceType opposite_piece = rev_color(piecetype); std::vector other_moves = {}; get_all_moves_as_if(to, opposite_piece, board, other_moves, false); // This takes out any mention of other squares involing blank pieces, or any peices that do not invole the current one. _filter_unneeded_squares(other_moves, piecetype, from); piece_character = CHESS_CHARS_INSENSITIVE[piecetype]; _non_pawn_disambiguate(from, to, piecetype, other_moves, disambig); } if (get_en_pass_flag(move) == 1){ en_passant = "e.p."; } if (get_check_flag(move) == 1){ check = "+"; // This checks if the other team has any valid moves. // If not, the check sign changes to a hashtag (#). std::array moved_board; dumb_move(move, board, moved_board); std::vector other_moves = {}; get_all_moves_for_pieces(other_pieces, moved_board, other_moves); if (other_moves.empty()){ check = "#"; } } // If promoting, add an equals sign and the piece promoting to. if (promoting_to != PieceType::NONE){ promotion += "="; promotion += CHESS_CHARS_INSENSITIVE[promoting_to]; } // end of checking for multiple pieces std::string result = ""; result += pawn_file + piece_character + disambig + capture_character + POSITION_STRING[to] + en_passant + promotion + check; return result; } void get_all_moves_for_pieces(std::array pieces, const std::array& board, std::vector& moves, int en_passant, int castle_perms){ for (PieceType pt : pieces){ for (int pos_of : get_poss_of(pt, board)){ std::vector local_moves = {}; get_all_moves_as_if(pos_of, pt, board, local_moves, true, en_passant, castle_perms); moves.insert(moves.end(), local_moves.begin(), local_moves.end()); } } } // From StackOverflow (https://stackoverflow.com/questions/5891610/how-to-remove-certain-characters-from-a-string-in-c#5891643) void remove_chars_from_string(std::string &str, std::string charsToRemove ) { for ( unsigned int i = 0; i < charsToRemove.length(); ++i ) { str.erase( remove(str.begin(), str.end(), charsToRemove.at(i)), str.end() ); } }