commit 1d8061ef10ab4be9c8c86f907bc78eca889df985 Author: Bogomil Vasilev Date: Tue Sep 30 17:09:22 2014 +0300 Initial commit 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