#include #include #include #ifdef USE_NCURSES #include #else #include #endif #include "chess.h" #include "pawn.h" #include "king.h" #include "queen.h" #include "bishop.h" #include "rook.h" #include "knight.h" #define W_PAWN "\xe2\x99\x99" #define W_KNIGHT "\xe2\x99\x98" #define W_BISHOP "\xe2\x99\x97" #define W_ROOK "\xe2\x99\x96" #define W_QUEEN "\xe2\x99\x95" #define W_KING "\xe2\x99\x94" #define B_PAWN "\xe2\x99\x9f" #define B_KNIGHT "\xe2\x99\x9e" #define B_BISHOP "\xe2\x99\x9d" #define B_ROOK "\xe2\x99\x9c" #define B_QUEEN "\xe2\x99\x9b" #define B_KING "\xe2\x99\x9a" WINDOW *win; void chess::menu() { switch (status) { case MOVING: return; case CASTLING: wprintw(win, "Castling with:\n" "1. Left rook.\n" "2. Right rook.\n" "\tINPUT: " ); return; case INCORRECT_INPUT: wprintw(win, "Incorrect input!\n"); break; case MOVE_SUCCESSFUL: wprintw(win, "Move successful!\n"); break; case MOVE_IMPOSSIBLE: wprintw(win, "No such move!\n"); break; case MOVE_NO_CHANGE: wprintw(win, "You can't move to the same spot!\n"); break; case MOVE_PATH_PROBLEM: wprintw(win, "An element is in the way!\n"); break; case MOVE_PAWN_IMPOSSIBLE: wprintw(win, "First move, already made on this pawn!\n"); break; case MOVE_PAWN_PROMOTION: wprintw(win, "Promote pawn to:\n\n" "1. Bishop.\n" "2. Rook.\n" "3. Knight.\n" "4. Queen.\n\n" "\tINPUT: " ); return; case MOVE_PAWN_PROMOTED: wprintw(win, "Pawn, successfully promoted!\n"); break; case MOVE_WRONG_TURN: wprintw(win, "That's not your piece!\n" "It's %s's turn.\n", nickname[turn].c_str() ); break; case CASTLING_FORBIDDEN_LEFT: wprintw(win, "Left rook is forbidden to castle!\n"); break; case CASTLING_FORBIDDEN_RIGHT: wprintw(win, "Right rook is forbidden to castle!\n"); break; case CASTLING_UNDER_CHECK: wprintw(win, "Careful! Castling area is attackable.\n"); break; case CASTLING_PATH_PROBLEM: wprintw(win, "Path for castling isn't clear!\n"); break; case CHECK_PREVENT: wprintw(win, "Careful! That move leads to check!\n"); break; case CHECK_WARNING: wprintw(win, "Your king is under check!\n"); break; case CHECKMATE: wprintw(win, "\n\tCheckmate!!!\n\n" "\t%s has won the game!", nickname[!turn].c_str() ); return; } wprintw(win, "\n" "1. Move.\n" "2. Castling.\n" "3. Forfeit.\n\n" "\tINPUT: " ); } chess::chess(const char *player1_name, const char *player2_name) { nickname[0] = player1_name; nickname[1] = player2_name; last_move = "N/A"; turn = WHITE; status = MENU; for (int i = 2; i < 6; ++i) for (int j = 0; j < 8; ++j) spot[i][j] = 0; spot[0][0] = new rook(0, 0, WHITE); spot[0][1] = new knight(0, 1, WHITE); spot[0][2] = new bishop(0, 2, WHITE); spot[0][3] = new queen(0, 3, WHITE); spot[0][4] = new king(0, 4, WHITE); spot[0][5] = new bishop(0, 5, WHITE); spot[0][6] = new knight(0, 6, WHITE); spot[0][7] = new rook(0, 7, WHITE); for (int i = 0; i < 8; ++i) spot[1][i] = new pawn(1, i, WHITE); spot[7][0] = new rook(7, 0, BLACK); spot[7][1] = new knight(7, 1, BLACK); spot[7][2] = new bishop(7, 2, BLACK); spot[7][3] = new queen(7, 3, BLACK); spot[7][4] = new king(7, 4, BLACK); spot[7][5] = new bishop(7, 5, BLACK); spot[7][6] = new knight(7, 6, BLACK); spot[7][7] = new rook(7, 7, BLACK); for (int i = 0; i < 8; ++i) spot[6][i] = new pawn(6, i, BLACK); King[WHITE] = spot[0][4]; King[BLACK] = spot[7][4]; } chess::~chess() { for (int i = 0; i < 8; ++i) for (int j = 0; j < 8; ++j) if (spot[i][j]) delete spot[i][j]; } void chess::play() { char a[3], b[3]; char choice[2]; int from_x, from_y, to_x, to_y; setlocale(LC_ALL, ""); initscr(); start_color(); noqiflush(); init_pair(1,COLOR_BLACK, COLOR_WHITE); init_pair(2,COLOR_BLACK, (has_colors() && can_change_color()) ? 8 : 3); win = newwin(0,0,0,0); for (;;) { wattroff(win, COLOR_PAIR(A_REVERSE)); refresh(); wrefresh(win); wattron(win, COLOR_PAIR(A_REVERSE)); wclear(win); draw_chess_board(); menu(); if (status == CHECKMATE) { getch(); wattroff(win, COLOR_PAIR(A_REVERSE)); refresh(); wrefresh(win); delwin(win); endwin(); return; } if (status == CASTLING) { wgetstr(win, choice); if (choice[0] == '1') status = castling(CASTLING_LEFT); else if (choice[0] == '2') status = castling(CASTLING_RIGHT); continue; } if (status != MOVING) { wgetstr(win, choice); if (choice[0] == '1') { status = MOVING; continue; } if (choice[0] == '2') { status = CASTLING; continue; } if (choice[0] == '3') { wprintw(win, "Are you sure? [y\\n]: "); wgetstr(win, choice); if (choice[0] == 'y' || choice[0] == 'Y') break; continue; } } else { wprintw(win, "From: "); wgetnstr(win, a, 2); wprintw(win, "To: "); wgetnstr(win, b, 2); if (strlen(a) != 2 || strlen(b) != 2) { status = INCORRECT_INPUT; continue; } } from_x = a[1] - '1'; from_y = tolower(a[0]) - 'a'; to_x = b[1] - '1'; to_y = tolower(b[0]) - 'a'; if (input_is_correct(from_x, from_y, to_x, to_y)) { if (spot[from_x][from_y]->team_is() != turn) { status = MOVE_WRONG_TURN; continue; } status = path_status(from_x, from_y, to_x, to_y); if (status == MOVE_OK) move(from_x, from_y, to_x, to_y); } else status = INCORRECT_INPUT; } wattroff(win, COLOR_PAIR(A_REVERSE)); refresh(); wrefresh(win); delwin(win); endwin(); } bool chess::input_is_correct(int from_x, int from_y, int to_x, int to_y) { if (from_x < 0 || from_x > 7 || from_y < 0 || from_y > 7) return false; if (to_x < 0 || to_x > 7 || to_y < 0 || to_y > 7) return false; if (!spot[from_x][from_y]) return false; return true; } bool chess::king_is_safe(int to_x, int to_y) { int x, y, i, j, spot_rank, spot_team; if (status == KING_MOVING) { x = to_x; y = to_y; if (x + 2 < 8) { if (y + 1 < 8) if (spot[x+2][y+1] && spot[x+2][y+1]->team_is() != turn && spot[x+2][y+1]->rank_is() == KNIGHT) return false; if (y - 1 >= 0) if (spot[x+2][y-1] && spot[x+2][y-1]->team_is() != turn && spot[x+2][y-1]->rank_is() == KNIGHT) return false; } if (x - 2 >= 0) { if (y + 1 < 8) if (spot[x-2][y+1] && spot[x-2][y+1]->team_is() != turn && spot[x-2][y+1]->rank_is() == KNIGHT) return false; if (y - 1 >= 0) if (spot[x-2][y-1] && spot[x-2][y-1]->team_is() != turn && spot[x-2][y-1]->rank_is() == KNIGHT) return false; } if (x + 1 < 8) { if (y + 2 < 8) if (spot[x+1][y+2] && spot[x+1][y+2]->team_is() != turn && spot[x+1][y+2]->rank_is() == KNIGHT) return false; if (y - 2 >= 0) if (spot[x+1][y-2] && spot[x+1][y-2]->team_is() != turn && spot[x+1][y-2]->rank_is() == KNIGHT) return false; } if (x - 1 >= 0) { if (y + 2 < 8) if (spot[x-1][y+2] && spot[x-1][y+2]->team_is() != turn && spot[x-1][y+2]->rank_is() == KNIGHT) return false; if (y - 2 >= 0) if (spot[x-1][y-2] && spot[x-1][y-2]->team_is() != turn && spot[x-1][y-2]->rank_is() == KNIGHT) return false; } } else King[turn]->get_coords(x, y); // Diagonal: right-up for (i = x + 1, j = y + 1; i < 8 && j < 8; ++i, ++j) { if (spot[i][j]) { spot_rank = spot[i][j]->rank_is(); spot_team = spot[i][j]->team_is(); if (spot_rank == KING && spot_team == turn) continue; if (spot_team != turn) { if (spot_rank == QUEEN) return false; if (spot_rank == BISHOP) return false; } break; } if (to_x == i && to_y == j) break; } // Diagonal: right-down for (i = x - 1, j = y + 1; i >= 0 && j < 8; --i, ++j) { if (spot[i][j]) { spot_rank = spot[i][j]->rank_is(); spot_team = spot[i][j]->team_is(); if (spot_rank == KING && spot_team == turn) continue; if (spot_team != turn) { if (spot_rank == QUEEN) return false; if (spot_rank == BISHOP) return false; } break; } if (to_x == i && to_y == j) break; } // Diagonal: left-up for (i = x + 1, j = y - 1; i < 8 && j >= 0; ++i, --j) { if (spot[i][j]) { spot_rank = spot[i][j]->rank_is(); spot_team = spot[i][j]->team_is(); if (spot_rank == KING && spot_team == turn) continue; if (spot_team != turn) { if (spot_rank == QUEEN) return false; if (spot_rank == BISHOP) return false; } break; } if (to_x == i && to_y == j) break; } // Diagonal: left-down for (i = x - 1, j = y - 1; i >= 0 && j >= 0; --i, --j) { if (spot[i][j]) { spot_rank = spot[i][j]->rank_is(); spot_team = spot[i][j]->team_is(); if (spot_rank == KING && spot_team == turn) continue; if (spot_team != turn) { if (spot_rank == QUEEN) return false; if (spot_rank == BISHOP) return false; } break; } if (to_x == i && to_y == j) break; } // Upward path for (i = x + 1, j = y; i < 8; ++i) { if (spot[i][j]) { spot_rank = spot[i][j]->rank_is(); spot_team = spot[i][j]->team_is(); if (spot_rank == KING && spot_team == turn) continue; if (spot_team != turn) { if (spot_rank == QUEEN) return false; if (spot_rank == ROOK) return false; } break; } if (to_x == i && to_y == j) break; } // Downward path for (i = x - 1; i >= 0; --i) { if (spot[i][j]) { spot_rank = spot[i][j]->rank_is(); spot_team = spot[i][j]->team_is(); if (spot_rank == KING && spot_team == turn) continue; if (spot_team != turn) { if (spot_rank == QUEEN) return false; if (spot_rank == ROOK) return false; } break; } if (to_x == i && to_y == j) break; } // Right path for (i = x, j = y + 1; j < 8; ++j) { if (spot[i][j]) { spot_rank = spot[i][j]->rank_is(); spot_team = spot[i][j]->team_is(); if (spot_rank == KING && spot_team == turn) continue; if (spot_team != turn) { if (spot_rank == QUEEN) return false; if (spot_rank == ROOK) return false; } break; } if (to_x == i && to_y == j) break; } // Left path for (j = y - 1; j >= 0; --j) { if (spot[i][j]) { spot_rank = spot[i][j]->rank_is(); spot_team = spot[i][j]->team_is(); if (spot_rank == KING && spot_team == turn) continue; if (spot_team != turn) { if (spot_rank == QUEEN) return false; if (spot_rank == ROOK) return false; } break; } if (to_x == i && to_y == j) break; } return true; } int chess::path_status(int a_x, int a_y, int b_x, int b_y) { #define SPOT_CHECK() do { \ if (i == b_x && j == b_y) { \ if (flag) \ return MOVE_PATH_PROBLEM; \ \ if (king_is_safe(i, j)) \ return MOVE_OK; \ \ return CHECK_PREVENT; \ } \ \ if (spot[i][j] && i != b_x && j != b_y) \ flag = 1; \ } while (0) // macro end int i, j, flag; int rank_a = spot[a_x][a_y]->rank_is(); if (a_x == b_x && a_y == b_y) return MOVE_NO_CHANGE; if (spot[b_x][b_y]) if (spot[a_x][a_y]->team_is() == spot[b_x][b_y]->team_is()) return MOVE_PATH_PROBLEM; switch (rank_a) { case KING: status = KING_MOVING; if (a_x + 1 == b_x || a_x - 1 == b_x || a_x == b_x) if (a_y + 1 == b_y || a_y - 1 == b_y || a_y == b_y) { if (king_is_safe(b_x, b_y)) return MOVE_OK; return CHECK_PREVENT; } return MOVE_IMPOSSIBLE; case QUEEN: for (flag = 0, i = a_x + 1, j = a_y + 1; i < 8 && j < 8; ++i, ++j) SPOT_CHECK(); for (flag = 0, i = a_x - 1, j = a_y + 1; i >= 0 && j < 8; --i, ++j) SPOT_CHECK(); for (flag = 0, i = a_x + 1, j = a_y - 1; i < 8 && j >= 0; ++i, --j) SPOT_CHECK(); for (flag = 0, i = a_x - 1, j = a_y - 1; i >= 0 && j >= 0; --i, --j) SPOT_CHECK(); for (flag = 0, i = a_x + 1, j = a_y; i < 8; ++i) SPOT_CHECK(); for (flag = 0, i = a_x - 1; i >= 0; --i) SPOT_CHECK(); for (flag = 0, i = a_x, j = a_y + 1; j < 8; ++j) SPOT_CHECK(); for (flag = 0, j = a_y - 1; j >= 0; --j) SPOT_CHECK(); return MOVE_IMPOSSIBLE; case KNIGHT: if (a_x + 2 == b_x || a_x - 2 == b_x) if (a_y + 1 == b_y || a_y - 1 == b_y) { if (king_is_safe(b_x, b_y)) return MOVE_OK; return CHECK_PREVENT; } if (a_x + 1 == b_x || a_x - 1 == b_x) if (a_y + 2 == b_y || a_y - 2 == b_y) { if (king_is_safe(b_x, b_y)) return MOVE_OK; return CHECK_PREVENT; } return MOVE_IMPOSSIBLE; case BISHOP: for (flag = 0, i = a_x + 1, j = a_y + 1; i < 8 && j < 8; ++i, ++j) SPOT_CHECK(); for (flag = 0, i = a_x - 1, j = a_y + 1; i >= 0 && j < 8; --i, ++j) SPOT_CHECK(); for (flag = 0, i = a_x + 1, j = a_y - 1; i < 8 && j >= 0; ++i, --j) SPOT_CHECK(); for (flag = 0, i = a_x - 1, j = a_y - 1; i >= 0 && j >= 0; --i, --j) SPOT_CHECK(); return MOVE_IMPOSSIBLE; case ROOK: for (flag = 0, i = a_x + 1, j = a_y; i < 8; ++i) SPOT_CHECK(); for (flag = 0, i = a_x - 1; i >= 0; --i) SPOT_CHECK(); for (flag = 0, i = a_x, j = a_y + 1; j < 8; ++j) SPOT_CHECK(); for (flag = 0, j = a_y - 1; j >= 0; --j) SPOT_CHECK(); return MOVE_IMPOSSIBLE; default: // case PAWN: pawn *Pawn = static_cast(spot[a_x][a_y]); switch (turn) { case WHITE: if (a_x + 1 == b_x && a_y == b_y && !spot[b_x][b_y]) { if (king_is_safe(b_x, b_y)) return MOVE_OK; return CHECK_PREVENT; } if (a_x + 1 == b_x && (a_y + 1 == b_y || a_y - 1 == b_y)) if (spot[b_x][b_y] && spot[b_x][b_y]->team_is() != turn) { if (king_is_safe(b_x, b_y)) return MOVE_OK; return CHECK_PREVENT; } if (a_x + 2 == b_x && a_y == b_y && !spot[b_x][b_y]) { if (Pawn->first_move() == false) return MOVE_PAWN_IMPOSSIBLE; if (king_is_safe(b_x, b_y)) return MOVE_OK; return CHECK_PREVENT; } break; case BLACK: if (a_x - 1 == b_x && a_y == b_y && !spot[b_x][b_y]) { if (king_is_safe(b_x, b_y)) return MOVE_OK; return CHECK_PREVENT; } if (a_x - 1 == b_x && (a_y + 1 == b_y || a_y - 1 == b_y)) if (spot[b_x][b_y] && spot[b_x][b_y]->team_is() != turn) { if (king_is_safe(b_x, b_y)) return MOVE_OK; return CHECK_PREVENT; } if (a_x - 2 == b_x && a_y == b_y && !spot[b_x][b_y]) { if (Pawn->first_move() == false) return MOVE_PAWN_IMPOSSIBLE; if (king_is_safe(b_x, b_y)) return MOVE_OK; return CHECK_PREVENT; } break; } return MOVE_IMPOSSIBLE; } } int chess::castling(int choice) { king *King[2] = { static_cast(this->King[0]), static_cast(this->King[1]) }; if (King[turn]->can_castle(choice) == false) return choice == CASTLING_LEFT ? CASTLING_FORBIDDEN_LEFT : CASTLING_FORBIDDEN_RIGHT; int row = turn == WHITE ? 0 : 7; if (choice == CASTLING_LEFT) { if (spot[row][1] || spot[row][2] || spot[row][3]) return CASTLING_PATH_PROBLEM; for (int i = 0; i < 5; ++i) if (!king_is_safe(row, i)) return CASTLING_UNDER_CHECK; King[turn]->move(row, 2); spot[row][2] = King[turn]; spot[row][4] = 0; spot[row][3] = spot[row][0]; spot[row][0] = 0; } else { if (spot[row][5] || spot[row][6]) return CASTLING_PATH_PROBLEM; for (int i = 4; i < 8; ++i) if (!king_is_safe(row, i)) return CASTLING_UNDER_CHECK; King[turn]->move(row, 6); spot[row][6] = King[turn]; spot[row][4] = 0; spot[row][5] = spot[row][0]; spot[row][7] = 0; } turn = turn == WHITE ? BLACK : WHITE; last_move = choice == CASTLING_LEFT ? "Castling (Left rook)" : "Castling (Right rook)"; return MOVE_SUCCESSFUL; } void chess::check_for_checkmate() { int x, y, checks = 0; King[!turn]->get_coords(x, y); if (!king_is_safe(x, y)) { status = CHECK_WARNING; if (x + 1 < 8) { if (y + 1 < 8) { if (!king_is_safe(x+1, y+1)) checks++; } else checks++; if (y - 1 >= 0) { if (!king_is_safe(x+1, y-1)) checks++; } else checks++; if (!king_is_safe(x+1, y)) checks++; } else checks += 3; if (x - 1 >= 0) { if (y + 1 < 8) { if (!king_is_safe(x-1, y+1)) checks++; } else checks++; if (y - 1 >= 0) { if (!king_is_safe(x-1, y-1)) checks++; } else checks++; if (!king_is_safe(x-1, y)) checks++; } else checks += 3; if (y + 1 < 8) { if (!king_is_safe(x, y+1)) checks++; } else checks++; if (y - 1 >= 0) { if (!king_is_safe(x, y-1)) checks++; } else checks++; if (checks == 8) status = CHECKMATE; } } const char* chess::symbol(int x, int y, bool rm) { if ( (rm && spot[x][y]->team_is() == BLACK) || (!rm && spot[x][y]->team_is() == WHITE)) switch (spot[x][y]->rank_is()) { case QUEEN: return W_QUEEN; case KING: return W_KING; case ROOK: return W_ROOK; case BISHOP: return W_BISHOP; case PAWN: return W_PAWN; default: return W_KNIGHT; } else switch (spot[x][y]->rank_is()) { case QUEEN: return B_QUEEN; case KING: return B_KING; case ROOK: return B_ROOK; case BISHOP: return B_BISHOP; case PAWN: return B_PAWN; default: return B_KNIGHT; } } void chess::move(int a_x, int a_y, int b_x, int b_y) { piece *&last_pos = spot[a_x][a_y]; piece *&new_pos = spot[b_x][b_y]; if (new_pos) { removed[!turn] += std::string(symbol(b_x, b_y, true)) + " "; delete new_pos; } new_pos = last_pos; new_pos->move(b_x, b_y); last_pos = NULL; if (new_pos->rank_is() == PAWN && (b_x == 0 || b_x == 7)) { status = MOVE_PAWN_PROMOTION; int val; for (;;) { char *e, choice[2]; wattroff(win, COLOR_PAIR(A_REVERSE)); refresh(); wrefresh(win); wattron(win, COLOR_PAIR(A_REVERSE)); wclear(win); draw_chess_board(); menu(); wgetnstr(win, choice, 2); val = strtol(choice, &e, 10); if (*e == '\0' && val > 0 && val < 5) break; } delete new_pos; std::ostringstream __out; __out << (char)(a_y + 'A') << a_x + 1 << " -> " << (char)(b_y + 'A') << b_x + 1 << " (Pawn promoted to "; switch (val) { case 1: new_pos = new bishop(b_x, b_y, turn); __out << "bishop"; break; case 2: new_pos = new rook(b_x, b_y, turn); __out << "rook"; break; case 3: new_pos = new knight(b_x, b_y, turn); __out << "knight"; break; case 4: new_pos = new queen(b_x, b_y, turn); __out << "queen"; break; } __out << ")."; last_move = __out.str(); status = MOVE_PAWN_PROMOTED; } else { if (new_pos->rank_is() == KING) King[turn] = new_pos; status = MOVE_SUCCESSFUL; std::ostringstream __out; __out << (char)(a_y + 'A') << a_x + 1 << " -> " << (char)(b_y + 'A') << b_x + 1; last_move = __out.str(); } turn = !turn; //check_for_checkmate(); } void chess::draw_chess_board() { static const char *idxs = "\t A B C D E F G H"; wprintw(win, "\t*** Smirky-Chess v3.0 (beta) ***\n" "\t\t%s vs %s\n\n%s\n", nickname[0].c_str(), nickname[1].c_str(), idxs ); wattroff(win, COLOR_PAIR(A_REVERSE)); for (int i = 7; i >= 0; --i) { wprintw(win, "\t%d ", i + 1); for (int j = 0; j < 8; j++) { std::ostringstream __out; wattron(win, COLOR_PAIR((i+j) % 2 ? 1 : 2)); __out << " " << (spot[i][j] ? symbol(i, j, false) : " ") << " "; wprintw(win, "%s", __out.str().c_str()); wattroff(win, COLOR_PAIR((i+j) % 2 ? 1 : 2)); } wattron(win, COLOR_PAIR(A_REVERSE)); wprintw(win, " %d\t", i + 1); switch (i) { case 7: wprintw(win, "Removed pieces:"); break; case 5: wprintw(win, "%s", removed[0].c_str()); break; case 4: wprintw(win, "%s", removed[1].c_str()); break; case 2: wprintw(win, "Last move: %s", last_move.c_str()); break; } wprintw(win, "\n"); wattroff(win, COLOR_PAIR(A_REVERSE)); } wprintw(win, "%s\t%s's turn!\n", idxs, nickname[turn].c_str()); }