Initial commit

This commit is contained in:
2014-09-30 17:09:22 +03:00
commit 1d8061ef10
18 changed files with 1345 additions and 0 deletions

35
Makefile Normal file
View File

@@ -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)

36
README Normal file
View File

@@ -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! :)

9
bishop.cpp Normal file
View File

@@ -0,0 +1,9 @@
#include <cstdlib>
#include "bishop.h"
void bishop::move(int x, int y)
{
this->x = x;
this->y = y;
}

19
bishop.h Normal file
View File

@@ -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

930
chess.cpp Normal file
View File

@@ -0,0 +1,930 @@
#include <algorithm>
#include <sstream>
#include <cstring>
#ifdef USE_NCURSES
#include <ncurses.h>
#else
#include <curses.h>
#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<pawn *>(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<king *>(this->King[0]),
static_cast<king *>(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());
}

34
chess.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef CHESS_H
#define CHESS_H
#include <string>
#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

12
king.cpp Normal file
View File

@@ -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;
}

26
king.h Normal file
View File

@@ -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

7
knight.cpp Normal file
View File

@@ -0,0 +1,7 @@
#include "knight.h"
void knight::move(int x, int y)
{
this->x = x;
this->y = y;
}

19
knight.h Normal file
View File

@@ -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

54
main.cpp Normal file
View File

@@ -0,0 +1,54 @@
#ifdef USE_NCURSES
#include <ncurses.h>
#else
#include <curses.h>
#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;
}

8
pawn.cpp Normal file
View File

@@ -0,0 +1,8 @@
#include "pawn.h"
void pawn::move(int x, int y)
{
this->x = x;
this->y = y;
first_move_flag = false;
}

28
pawn.h Normal file
View File

@@ -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

71
piece.h Normal file
View File

@@ -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

9
queen.cpp Normal file
View File

@@ -0,0 +1,9 @@
#include <cstdlib>
#include "queen.h"
void queen::move(int x, int y)
{
this->x = x;
this->y = y;
}

19
queen.h Normal file
View File

@@ -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

7
rook.cpp Normal file
View File

@@ -0,0 +1,7 @@
#include "rook.h"
void rook::move(int x, int y)
{
this->x = x;
this->y = y;
}

22
rook.h Normal file
View File

@@ -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