static void MoveAndReplaceTarget(USER_DATA *ud, BOARD b, int x, int y, int dx, int dy) { PIECE *p = GetPieceAt(b, x, y); PIECE old_p = *p; // clear drawing at old location LvzActivate(ud, NULL, p->color, p->type, x, y, false); // unset target piece PIECE *t = GetPieceAt(b, dx, dy); if (t->type != TYPE_NONE) { LvzActivate(ud, NULL, t->color, t->type, dx, dy, false); } // clear old location SetPieceAt(b, x, y, COLOR_NONE, TYPE_NONE); // copy old piece to new location *t = old_p; // draw it LvzActivate(ud, NULL, t->color, t->type, dx, dy, true); }
/* * 'c' is the attackers color. * kingx and kingy is the target king location. */ static bool IsCheckmatedBy(BOARD b, COLOR c, int kingx, int kingy) { struct timeval tv; gettimeofday(&tv, NULL); COLOR defender = GetOppositeColor(c); PIECE *p; // for each piece on the board that is defending for (int x = 0; x < 8; ++x) for (int y = 0; y < 8; ++y) { p = GetPieceAt(b, x, y); if (p->color != defender) { continue; } // for each tile on the board, see if that piece can move there for (int j = 0; j < 8; ++j) for (int i = 0; i < 8; ++i) { if (PieceCanAttack(b, p->color, p->type, x, y, i, j) == true && GetPieceAt(b, i, j)->color != p->color) { PIECE old_p = *GetPieceAt(b, x, y); PIECE old_t = *GetPieceAt(b, i, j); // temporarily make the move SetPieceAt(b, x, y, COLOR_NONE, TYPE_NONE); SetPieceAt(b, i, j, old_p.color, old_p.type); // see if the move cancels check bool is_mate = true; int kx, ky; FindKing(b, defender, &kx, &ky); if (IsCoordAttackedBy(b, c, kx, ky) == false) { is_mate = false; } // revert the pieces SetPieceAt(b, x, y, old_p.color, old_p.type); SetPieceAt(b, i, j, old_t.color, old_t.type); if (is_mate == false) { struct timeval tv2; gettimeofday(&tv2, NULL); LogFmt(OP_MOD, "Checkmate Calculation: %lu.%06lu", tv2.tv_sec - tv.tv_sec, tv2.tv_usec - tv.tv_usec); return false; } } } } struct timeval tv2; gettimeofday(&tv2, NULL); LogFmt(OP_MOD, "Checkmate Calculation: %lu.%06lu", tv2.tv_sec - tv.tv_sec, tv2.tv_usec - tv.tv_usec); return true; }
static void LvzDrawAll(USER_DATA *ud, PLAYER *p, bool show) { PIECE *piece; for (int j = 0; j < 8; ++j) for (int i = 0; i < 8; ++i) { piece = GetPieceAt(ud->board, i, j); if (piece->type == TYPE_NONE) { continue; } LvzActivate(ud, p, piece->color, piece->type, i, j, show); } }
static bool IsCoordAttackedBy(BOARD b, COLOR c, int x, int y) { PIECE *p; for (int j = 0; j < 8; ++j) { for (int i = 0; i < 8; ++i) { p = GetPieceAt(b, i, j); if (p->color == c && PieceCanAttack(b, c, p->type, i, j, x, y) == true) { return true; } } } return false; }
/* * This function is no longer necessary with LVZ graphics but * may still be useful for debugging at some point. */ static void DrawBoard(BOARD b) { text display char line[128]; int linei = 0; ArenaMessage("+---------------------+"); ArenaMessage("| a b c d e f g h |"); PIECE *p; for (int y = 7; y >= 0; --y) { line[linei++] = '|'; line[linei++] = ' '; line[linei++] = '1' + y; line[linei++] = '|'; for (int x = 0; x < 8; ++x) { p = GetPieceAt(b, x, y); char c; if (p->type == TYPE_NONE) { c = ' '; } else { if (p->color == COLOR_WHITE) { c = p->type; } else { c = p->type + ('a' - 'A'); } } line[linei++] = c; line[linei++] = '|'; } line[linei++] = '1' + y; line[linei++] = ' '; line[linei++] = '|'; line[linei] = '\0'; linei = 0; ArenaMessage("| +-+-+-+-+-+-+-+-+ |"); ArenaMessage(line); } ArenaMessage("| +-+-+-+-+-+-+-+-+ |"); ArenaMessage("| a b c d e f g h |"); ArenaMessage("|_____________________|"); }
static void FindKing(BOARD b, COLOR c, int *x, int *y) { *x = 0; *y = 0; PIECE *p; for (int i = 0; i < 8; ++i) { for (int j = 0; j < 8; ++j) { p = GetPieceAt(b, j, i); if (p->type == TYPE_KING && p->color == c) { *x = j; *y = i; } } } }
void Board::Print() const { bool colour_toggle = false; char rank = 8; for (; rank >= 1; rank--) { std::cout << char('0' + rank) << " "; char file = 'A'; for (; file <= 'H'; file++) { Piece p = GetPieceAt(rank, file); if (p.GetType() == Piece::None) { std::cout << (colour_toggle ? '.' : ' '); } else { std::cout << p.GetText(); } colour_toggle = !colour_toggle; std::cout << ' '; } std::cout << std::endl; colour_toggle = !colour_toggle; } std::cout << " A B C D E F G H " << std::endl; }
/* * TODO: rewrite this to remove duplicated code */ static char* TryCastle(USER_DATA *ud, COLOR c, int x, int y, int dx, int dy) { BOARD b = ud->board; // coordinates were already verified by IsCastleMove() COLOR opp = GetOppositeColor(c); if (IsCoordAttackedBy(b, opp, x, y) == true) { return "Can't castle out of check"; } if (c == COLOR_WHITE) { if (dx == 6) { if (IsCoordAttackedBy(b, opp, 5, 0) || IsCoordAttackedBy(b, opp, 6, 0)) { return "Can't castle through or into check"; } if (GetPieceAt(b, 5, 0)->type != TYPE_NONE || GetPieceAt(b, 6, 0)->type != TYPE_NONE) { return "Can't castle through pieces"; } if (GetPieceAt(b, 7, 0)->type != TYPE_ROOK || GetPieceAt(b, 7, 0)->color != COLOR_WHITE) { return "No rook"; } MoveAndReplaceTarget(ud, b, x, y, dx, dy); MoveAndReplaceTarget(ud, b, 7, 0, 5, 0); ArenaMessageFmt("%s (%s) Moves: Castle kingside", GetPlayerName(ud, c), GetColorText(c)); return NULL; } else if (dx == 2) { // castle queenside if (IsCoordAttackedBy(b, opp, 2, 0) || IsCoordAttackedBy(b, opp, 3, 0)) { return "Can't castle through or into check"; } if (GetPieceAt(b, 1, 0)->type != TYPE_NONE || GetPieceAt(b, 2, 0)->type != TYPE_NONE || GetPieceAt(b, 3, 0)->type != TYPE_NONE) { return "Can't castle through pieces"; } if (GetPieceAt(b, 0, 0)->type != TYPE_ROOK || GetPieceAt(b, 0, 0)->color != COLOR_WHITE) { return "No rook"; } MoveAndReplaceTarget(ud, b, x, y, dx, dy); MoveAndReplaceTarget(ud, b, 0, 0, 3, 0); ArenaMessageFmt("%s (%s) Moves: Castle queenside", GetPlayerName(ud, c), GetColorText(c)); return NULL; } } else if (c == COLOR_BLACK) { if (dx == 6) { // castle kingside if (IsCoordAttackedBy(b, opp, 5, 7) || IsCoordAttackedBy(b, opp, 6, 7)) { return "Can't castle through or into check"; } if (GetPieceAt(b, 5, 7)->type != TYPE_NONE || GetPieceAt(b, 6, 7)->type != TYPE_NONE) { return "Can't castle through pieces"; } if (GetPieceAt(b, 7, 7)->type != TYPE_ROOK || GetPieceAt(b, 7, 7)->color != COLOR_BLACK) { return "No rook"; } MoveAndReplaceTarget(ud, b, x, y, dx, dy); MoveAndReplaceTarget(ud, b, 7, 7, 5, 7); ArenaMessageFmt("%s (%s) Moves: Castle kingside", GetPlayerName(ud, c), GetColorText(c)); return NULL; } else if (dx == 2) { // castle queenside if (IsCoordAttackedBy(b, opp, 2, 7) || IsCoordAttackedBy(b, opp, 3, 7)) { return "Can't castle through or into check"; } if (GetPieceAt(b, 1, 7)->type != TYPE_NONE || GetPieceAt(b, 2, 7)->type != TYPE_NONE || GetPieceAt(b, 3, 7)->type != TYPE_NONE) { return "Can't castle through pieces"; } if (GetPieceAt(b, 0, 7)->type != TYPE_ROOK || GetPieceAt(b, 0, 7)->color != COLOR_BLACK) { return "No rook"; } MoveAndReplaceTarget(ud, b, x, y, dx, dy); MoveAndReplaceTarget(ud, b, 0, 7, 3, 7); ArenaMessageFmt("%s (%s) Moves: Castle queenside", GetPlayerName(ud, c), GetColorText(c)); return NULL; } } else { return "Error castling"; } return "Could not castle"; }
static bool PieceCanAttack(BOARD b, COLOR c, TYPE t, int x, int y, int dx, int dy) { if (dx < 0 || dx >= 8) { return false; } else if (dy < 0 || dy >= 8) { return false; } else if (x == dx && y == dy) { // cant attack self return false; } int tx, ty; // temp x and y values PIECE *p; switch (t) { case TYPE_KING: for (int j = x - 1; j <= x + 1; ++j) for (int i = y - 1; i <= y + 1; ++i) { if (j == dx && i == dy) { return true; } } break; case TYPE_QUEEN: return PieceCanAttack(b, c, TYPE_BISHOP, x, y, dx, dy) || PieceCanAttack(b, c, TYPE_ROOK, x, y, dx, dy); case TYPE_ROOK: // search up for (int i = x + 1; i < 8; ++i) { if (i == dx && y == dy) { return true; } p = GetPieceAt(b, i, y); if (p->type != TYPE_NONE) { break; } } // search down for (int i = x - 1; i >= 0; --i) { if (i == dx && y == dy) { return true; } p = GetPieceAt(b, i, y); if (p->type != TYPE_NONE) { break; } } // search right for (int i = y + 1; i < 8; ++i) { if (i == dy && x == dx) { return true; } p = GetPieceAt(b, x, i); if (p->type != TYPE_NONE) { break; } } // search left for (int i = y - 1; i >= 0; --i) { if (i == dy && x == dx) { return true; } p = GetPieceAt(b, x, i); if (p->type != TYPE_NONE) { break; } } break; case TYPE_BISHOP: // search up-right for (int i = 1; x + i < 8 && y + i < 8; ++i) { if (x + i == dx && y + i == dy) { return true; } p = GetPieceAt(b, x + i, y + i); if (p->type != TYPE_NONE) { break; } } // search down-right for (int i = 1; x + i < 8 && y - i >= 0; ++i) { if (x + i == dx && y - i == dy) { return true; } p = GetPieceAt(b, x + i, y - i); if (p->type != TYPE_NONE) { break; } } // search up-left for (int i = 1; x - i >= 0 && y + i < 8; ++i) { if (x - i == dx && y + i == dy) { return true; } p = GetPieceAt(b, x - i, y + i); if (p->type != TYPE_NONE) { break; } } // search down-left for (int i = 1; x - i >= 0 && y - i >= 0; ++i) { if (x - i == dx && y - i == dy) { return true; } p = GetPieceAt(b, x - i, y - i); if (p->type != TYPE_NONE) { break; } } break; case TYPE_KNIGHT: tx = abs(dx - x); ty = abs(dy - y); if ((tx == 2 && ty == 1) || (tx == 1 && ty == 2)) { return true; } break; case TYPE_PAWN: // pawn special-case gheyness if (c == COLOR_WHITE) { if (y == 7) { return false; } if (x == dx) { // moving forward if (GetPieceAt(b, x, y + 1)->type == TYPE_NONE && y + 1 == dy) { return true; } // initial 2 square move option if (y == 1 && GetPieceAt(b, x, y + 1)->type == TYPE_NONE && GetPieceAt(b, x, y + 2)->type == TYPE_NONE && y + 2 == dy) { return true; } } if (x > 0) { // check attack left if (GetPieceAt(b, x - 1, y + 1)->color == COLOR_BLACK && GetPieceAt(b, x - 1, y + 1)->type != TYPE_NONE) { if (dx == x - 1 && dy == y + 1) { return true; } } } if (x < 7) { // check attack right if (GetPieceAt(b, x + 1, y + 1)->color == COLOR_BLACK && GetPieceAt(b, x + 1, y + 1)->type != TYPE_NONE) { if (dx == x + 1 && dy == y + 1) { return true; } } } } else if (c == COLOR_BLACK) { if (y == 0) { return false; } if (x == dx) { // moving forward if (GetPieceAt(b, x, y - 1)->type == TYPE_NONE && y - 1 == dy) { return true; } // initial 2 square move option if (y == 6 && GetPieceAt(b, x, y - 1)->type == TYPE_NONE && GetPieceAt(b, x, y - 2)->type == TYPE_NONE && y - 2 == dy) { return true; } } if (x > 0) { // check attack left if (GetPieceAt(b, x - 1, y - 1)->color == COLOR_WHITE && GetPieceAt(b, x - 1, y - 1)->type != TYPE_NONE) { if (dx == x - 1 && dy == y - 1) { return true; } } } if (x < 7) { // check attack right if (GetPieceAt(b, x + 1, y - 1)->color == COLOR_WHITE && GetPieceAt(b, x + 1, y - 1)->type != TYPE_NONE) { if (dx == x + 1 && dy == y - 1) { return true; } } } } break; } return false; }
// TODO: Handle en passant static char* TryMove(USER_DATA *ud, COLOR c, int x, int y, int dx, int dy) { BOARD b = ud->board; PIECE *p = GetPieceAt(ud->board, x, y); if (p->type == TYPE_NONE) { return "No piece at that position!"; } else if (p->color != c) { return "That piece is not yours!"; } // check if this move is a castle attempt // TODO: check for rook movements if (p->type == TYPE_KING && IsCastleMove(c, x, y, dx, dy)) { if (ColorCanCastle(ud, c) == false) { return "You can no longer castle"; } return TryCastle(ud, c, x, y, dx, dy); } // check that the piece can make this movement if (PieceCanAttack(ud->board, c, p->type, x, y, dx, dy) == false) { return "Piece can't make that movement"; } // target piece PIECE *t = GetPieceAt(ud->board, dx, dy); if (t->type != TYPE_NONE && t->color == c) { return "You already have a piece in the destination position!"; } // make the movements to check for check, but store the data // in case the board needs to be reverted PIECE old_p = *p; PIECE old_t = *t; SetPieceAt(b, dx, dy, p->color, p->type); SetPieceAt(b, x, y, COLOR_NONE, TYPE_NONE); // find the king and test for check int kingx, kingy; FindKing(b, c, &kingx, &kingy); if (IsCoordAttackedBy(b, GetOppositeColor(c), kingx, kingy) == true) { // restore saved positions SetPieceAt(b, x, y, old_p.color, old_p.type); SetPieceAt(b, dx, dy, old_t.color, old_t.type); return "That move would place your King in check!"; } // // At this point, the move was successful // // make the pieces blink // TODO: this will erroneously leave pieces on if 2 moves // are made quickly uint32_t objid = LvzGetObjId(old_p.color, old_p.type, dx, dy); SetTimer(500, (void*)objid, (void*)0); SetTimer(1000, (void*)objid, (void*)1); SetTimer(1500, (void*)objid, (void*)0); SetTimer(2000, (void*)objid, (void*)1); SetTimer(2500, (void*)objid, (void*)0); SetTimer(3000, (void*)objid, (void*)1); if (old_t.type != TYPE_NONE) { // flash the captured piece objid = LvzGetObjId(old_t.color, old_t.type, dx, dy); SetTimer(500, (void*)objid, (void*)1); SetTimer(1000, (void*)objid, (void*)0); SetTimer(1500, (void*)objid, (void*)1); SetTimer(2000, (void*)objid, (void*)0); SetTimer(2500, (void*)objid, (void*)1); SetTimer(3000, (void*)objid, (void*)0); } if (old_p.type == TYPE_KING) { // kings cant castle once theyve moved if (old_p.color == COLOR_WHITE) { ud->white_can_castle = false; } else if (old_p.color == COLOR_BLACK) { ud->black_can_castle = false; } } // set if a piece has been captured bool capture = old_t.type != TYPE_NONE; // remove drawing of the old pieces LvzActivate(ud, NULL, old_p.color, old_p.type, x, y, false); if (capture) { LvzActivate(ud, NULL, old_t.color, old_t.type, dx, dy, false); } // check for pawn promotions // TODO: handle underpromotions bool promoted = false; p = GetPieceAt(b, dx, dy); if (p->type == TYPE_PAWN) { if (p->color == COLOR_WHITE && dy == 7) { p->type = TYPE_QUEEN; promoted = true; } else if (p->color == COLOR_BLACK && dy == 0) { p->type = TYPE_QUEEN; promoted = true; } } // draw the new piece LvzActivate(ud, NULL, old_p.color, old_p.type, dx, dy, true); objid = LvzGetObjId(old_p.color, old_p.type, dx, dy); // create move notation char move[6]; move[0] = x + 'a'; move[1] = y + '1'; move[2] = ','; move[3] = dx + 'a'; move[4] = dy + '1'; move[5] = '\0'; // announce the move char line[256]; snprintf(line, 256, "%s (%s) Moves: %s", GetPlayerName(ud, c), GetColorText(ud->to_move), move); if (capture) { // add capture line char capture[64]; snprintf(capture, 64, ", capturing a %s!", GetTypeName(old_t.type)); strlcat(line, capture, 256); } if (promoted == true) { // add promition line strlcat(line, " Pawn promoted to Queen!", 256); } FindKing(b, GetOppositeColor(c), &kingx, &kingy); if (IsCoordAttackedBy(b, c, kingx, kingy) == true) { if (IsCheckmatedBy(b, c, kingx, kingy) == true) { strlcat(line, " Checkmate!", 256); ArenaMessage(line); char gametime[32]; TicksToText(gametime, 32, GetTicksMs() - ud->start_tick); StopGame(ud, "%s Wins in %s!", GetPlayerName(ud, c), gametime); } else { strlcat(line, " Check!", 256); ArenaMessage(line); } } else { ArenaMessage(line); } return NULL; }