From 1d8061ef10ab4be9c8c86f907bc78eca889df985 Mon Sep 17 00:00:00 2001 From: Bogomil Vasilev Date: Tue, 30 Sep 2014 17:09:22 +0300 Subject: [PATCH] Initial commit --- Makefile | 35 ++ README | 36 +++ bishop.cpp | 9 + bishop.h | 19 ++ chess.cpp | 930 +++++++++++++++++++++++++++++++++++++++++++++++++++++ chess.h | 34 ++ king.cpp | 12 + king.h | 26 ++ knight.cpp | 7 + knight.h | 19 ++ main.cpp | 54 ++++ pawn.cpp | 8 + pawn.h | 28 ++ piece.h | 71 ++++ queen.cpp | 9 + queen.h | 19 ++ rook.cpp | 7 + rook.h | 22 ++ 18 files changed, 1345 insertions(+) create mode 100644 Makefile create mode 100644 README create mode 100644 bishop.cpp create mode 100644 bishop.h create mode 100644 chess.cpp create mode 100644 chess.h create mode 100644 king.cpp create mode 100644 king.h create mode 100644 knight.cpp create mode 100644 knight.h create mode 100644 main.cpp create mode 100644 pawn.cpp create mode 100644 pawn.h create mode 100644 piece.h create mode 100644 queen.cpp create mode 100644 queen.h create mode 100644 rook.cpp create mode 100644 rook.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ca36715 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +CC=g++ +CPPFLAGS= -Wall \ + -Wextra \ + -pipe \ + -Wmissing-declarations \ + -pedantic \ + -O2 + +LDFLAGS=-lncursesw + +SOURCES=main.cpp \ + chess.cpp \ + bishop.cpp \ + rook.cpp \ + queen.cpp \ + king.cpp \ + knight.cpp \ + pawn.cpp + +OBJECTS=$(SOURCES:.cpp=.o) +EXECUTABLE=chess + +all: $(SOURCES) $(EXECUTABLE) + +$(EXECUTABLE): $(OBJECTS) + @echo ' LD $@' + @$(CC) $(LDFLAGS) $(OBJECTS) -o $@ + +.cpp.o: + @echo ' CC $@' + @$(CC) $(CPPFLAGS) -c $< -o $@ + +clean: + rm -rf $(OBJECTS) + diff --git a/README b/README new file mode 100644 index 0000000..5680dcc --- /dev/null +++ b/README @@ -0,0 +1,36 @@ +This is a C++ STL ncurses CLI implementation of the game "Chess". + + +Recommended terminal settings: +- white/light terminal background color +- black/dark terminal text color +- proper font that fits UTF-8 standards + +How to build and run: + +$ make +$ ./chess + +Changelog: + +* v1.0 (beta): + - Initial version of the game written in plain C + +* v2.0 (beta): + - Full rewrite of the game on C++ using STL + +* v3.0 (beta): + - Removed almost everything from STL + - ncurses support (needs more work) + - Removed the "Chess pieces" table + - New: Show "Removed pieces" + - New: Show "Last move" + +TODO: + - Implement profile import/export + - Network gameplay + - Make the game STL-free + - Optimize ncurses window + - (possibly chat) + +Enjoy! :) diff --git a/bishop.cpp b/bishop.cpp new file mode 100644 index 0000000..ac2a27c --- /dev/null +++ b/bishop.cpp @@ -0,0 +1,9 @@ +#include + +#include "bishop.h" + +void bishop::move(int x, int y) +{ + this->x = x; + this->y = y; +} diff --git a/bishop.h b/bishop.h new file mode 100644 index 0000000..e452f0f --- /dev/null +++ b/bishop.h @@ -0,0 +1,19 @@ +#ifndef BISHOP_H +#define BISHOP_H + +#include "piece.h" + +class bishop : public piece { +public: + bishop(int x, int y, int team) + { + this->x = x; + this->y = y; + this->team = team; + rank = BISHOP; + } + + virtual void move(int, int); +}; + +#endif // BISHOP_H diff --git a/chess.cpp b/chess.cpp new file mode 100644 index 0000000..def3a25 --- /dev/null +++ b/chess.cpp @@ -0,0 +1,930 @@ +#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()); +} diff --git a/chess.h b/chess.h new file mode 100644 index 0000000..fc00bdf --- /dev/null +++ b/chess.h @@ -0,0 +1,34 @@ +#ifndef CHESS_H +#define CHESS_H + +#include + +#include "piece.h" + +class chess { +private: + std::string nickname[2]; + std::string last_move, removed[2]; + int turn; + int status; + piece *spot[8][8]; + piece *King[2]; + + const char* symbol(int, int, bool); + bool input_is_correct(int, int, int, int); + bool king_is_safe(int, int); + int path_status(int, int, int, int); + void draw_chess_board(); + void menu(); + void move(int, int, int, int); + int castling(int); + void check_for_checkmate(); + +public: + chess(const char*, const char*); + ~chess(); + + void play(); +}; + +#endif // CHESS_H diff --git a/king.cpp b/king.cpp new file mode 100644 index 0000000..ae391ad --- /dev/null +++ b/king.cpp @@ -0,0 +1,12 @@ +#include "king.h" + +void king::move(int x, int y) +{ + this->x = x; + this->y = y; +} + +bool king::can_castle(int choice) +{ + return choice == 1 ? !castled_left : !castled_right; +} diff --git a/king.h b/king.h new file mode 100644 index 0000000..24a7f15 --- /dev/null +++ b/king.h @@ -0,0 +1,26 @@ +#ifndef KING_H +#define KING_H + +#include "piece.h" + +class king : public piece { +private: + bool castled_left; + bool castled_right; + +public: + king(int x, int y, int team) + { + this->x = x; + this->y = y; + this->team = team; + rank = KING; + castled_left = false; + castled_right = false; + } + + virtual void move(int, int); + bool can_castle(int); +}; + +#endif // KING_H diff --git a/knight.cpp b/knight.cpp new file mode 100644 index 0000000..490aaa3 --- /dev/null +++ b/knight.cpp @@ -0,0 +1,7 @@ +#include "knight.h" + +void knight::move(int x, int y) +{ + this->x = x; + this->y = y; +} diff --git a/knight.h b/knight.h new file mode 100644 index 0000000..cf5b15e --- /dev/null +++ b/knight.h @@ -0,0 +1,19 @@ +#ifndef KNIGHT_H +#define KNIGHT_H + +#include "piece.h" + +class knight : public piece { +public: + knight(int x, int y, int team) + { + this->x = x; + this->y = y; + this->team = team; + rank = KNIGHT; + } + + virtual void move(int, int); +}; + +#endif // KNIGHT_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..6dd3ff7 --- /dev/null +++ b/main.cpp @@ -0,0 +1,54 @@ +#ifdef USE_NCURSES +#include +#else +#include +#endif + +#include "chess.h" + +int main() +{ + char input[2], nickname[2][40]; + WINDOW *win; + + setlocale(LC_ALL, ""); + initscr(); + start_color(); + noqiflush(); + init_pair(0, COLOR_WHITE, COLOR_BLACK); + win = newwin(0,0,0,0); + + for (;;) { + refresh(); + wrefresh(win); + wattron(win, COLOR_PAIR(0)); + wclear(win); + + wprintw(win, "\t*** Smirky-Chess v3.0 (beta) ***\n\n" + "1. New game.\n" + "2. Export player. (Not yet implemented)\n" + "3. Import player. (Not yet implemented)\n" + "4. Exit.\n" + "\tINPUT: " + ); + wgetnstr(win, input, 2); + + if (input[0] == '1') { + wprintw(win, "Enter nicknames:\n" + "Player 1: "); + wgetnstr(win, nickname[0], sizeof(nickname[0])); + wprintw(win, "Player 2: "); + wgetnstr(win, nickname[1], sizeof(nickname[1])); + wattroff(win, COLOR_PAIR(0)); + + chess chess_game(nickname[0], nickname[1]); + chess_game.play(); + } else + break; + } + + delwin(win); + endwin(); + return 0; +} + diff --git a/pawn.cpp b/pawn.cpp new file mode 100644 index 0000000..0e382c2 --- /dev/null +++ b/pawn.cpp @@ -0,0 +1,8 @@ +#include "pawn.h" + +void pawn::move(int x, int y) +{ + this->x = x; + this->y = y; + first_move_flag = false; +} diff --git a/pawn.h b/pawn.h new file mode 100644 index 0000000..be7a66d --- /dev/null +++ b/pawn.h @@ -0,0 +1,28 @@ +#ifndef PAWN_H +#define PAWN_H + +#include "piece.h" + +class pawn : public piece { +private: + bool first_move_flag; + +public: + pawn(int x, int y, int team) + { + this->x = x; + this->y = y; + this->team = team; + rank = PAWN; + first_move_flag = true; + } + + virtual void move(int, int); + + inline bool first_move() + { + return first_move_flag; + } +}; + +#endif // PAWN_H diff --git a/piece.h b/piece.h new file mode 100644 index 0000000..29577ab --- /dev/null +++ b/piece.h @@ -0,0 +1,71 @@ +#ifndef PIECE_H +#define PIECE_H + +enum team { + WHITE, + BLACK +}; + +enum rank { + KING = 1, + QUEEN, + KNIGHT, + BISHOP, + ROOK, + PAWN +}; + +enum status { + MENU, + MOVING, + CASTLING, + INCORRECT_INPUT, + MOVE_IMPOSSIBLE, + MOVE_SUCCESSFUL, + MOVE_PAWN_IMPOSSIBLE, + MOVE_PAWN_PROMOTION, + MOVE_PATH_PROBLEM, + MOVE_NO_CHANGE, + MOVE_OK, + MOVE_PAWN_PROMOTED, + MOVE_WRONG_TURN, + KING_MOVING, + CASTLING_LEFT, + CASTLING_RIGHT, + CASTLING_FORBIDDEN_LEFT, + CASTLING_FORBIDDEN_RIGHT, + CASTLING_UNDER_CHECK, + CASTLING_PATH_PROBLEM, + CHECK_PREVENT, + CHECK_WARNING, + CHECKMATE +}; + +class piece { +protected: + int x, y; + int rank; + int team; + +public: + virtual ~piece() {} + virtual void move(int, int) = 0; + + inline int rank_is() + { + return rank; + } + + inline int team_is() + { + return team; + } + + inline void get_coords(int &x, int &y) + { + x = this->x; + y = this->y; + } +}; + +#endif // PIECE_H diff --git a/queen.cpp b/queen.cpp new file mode 100644 index 0000000..c9be45b --- /dev/null +++ b/queen.cpp @@ -0,0 +1,9 @@ +#include + +#include "queen.h" + +void queen::move(int x, int y) +{ + this->x = x; + this->y = y; +} diff --git a/queen.h b/queen.h new file mode 100644 index 0000000..74b6f78 --- /dev/null +++ b/queen.h @@ -0,0 +1,19 @@ +#ifndef QUEEN_H +#define QUEEN_H + +#include "piece.h" + +class queen : public piece { +public: + queen(int x, int y, int team) + { + this->x = x; + this->y = y; + this->team = team; + rank = QUEEN; + } + + virtual void move(int, int); +}; + +#endif // QUEEN_H diff --git a/rook.cpp b/rook.cpp new file mode 100644 index 0000000..cc6948b --- /dev/null +++ b/rook.cpp @@ -0,0 +1,7 @@ +#include "rook.h" + +void rook::move(int x, int y) +{ + this->x = x; + this->y = y; +} diff --git a/rook.h b/rook.h new file mode 100644 index 0000000..3511043 --- /dev/null +++ b/rook.h @@ -0,0 +1,22 @@ +#ifndef ROOK_H +#define ROOK_H + +#include "piece.h" + +class rook : public piece { +private: + bool castled; + +public: + rook(int x, int y, int team) + { + castled = false; + this->x = x; + this->y = y; + this->team = team; + rank = ROOK; + } + virtual void move(int, int); +}; + +#endif // ROOK_H