|
@@ -0,0 +1,204 @@
|
|
|
+#include "CheckerBoard.h"
|
|
|
+
|
|
|
+
|
|
|
+void CheckerBoard::placeChecker(CheckerPosition position, CheckerPiece checker) {
|
|
|
+ board[position.y][position.x] = checker;
|
|
|
+}
|
|
|
+
|
|
|
+CheckerBoard::CheckerBoard() {}
|
|
|
+
|
|
|
+CheckerBoard CheckerBoard::loadFromFile(string filename) {
|
|
|
+ CheckerBoard board = CheckerBoard();
|
|
|
+
|
|
|
+ ifstream file(filename);
|
|
|
+ string line;
|
|
|
+
|
|
|
+ int color = WHITE;
|
|
|
+
|
|
|
+ while (getline(file, line)) {
|
|
|
+ if (line.find("White:") != string::npos) {
|
|
|
+ color = WHITE;
|
|
|
+ }
|
|
|
+
|
|
|
+ else if (line.find("Black:") != string::npos) {
|
|
|
+ color = BLACK;
|
|
|
+ }
|
|
|
+
|
|
|
+ else if (!line.empty()) {
|
|
|
+ if (line[0] == 'M') board.placeChecker(CheckerPosition(line.substr(1, 2)), color == WHITE ? CheckerPiece::WHITE_KING : CheckerPiece::BLACK_KING);
|
|
|
+ else board.placeChecker(CheckerPosition(line), color == WHITE ? CheckerPiece::WHITE : CheckerPiece::BLACK);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ file.close();
|
|
|
+
|
|
|
+ return board;
|
|
|
+}
|
|
|
+
|
|
|
+int CheckerBoard::getCheckerAt(CheckerPosition pos) const {
|
|
|
+ if (!pos.isValid()) return 0;
|
|
|
+
|
|
|
+ return board[pos.y][pos.x];
|
|
|
+}
|
|
|
+
|
|
|
+void CheckerBoard::moveChecker(CheckerPosition from, CheckerPosition to) {
|
|
|
+ CheckerPiece checker = board[from.y][from.x];
|
|
|
+ board[from.y][from.x] = CheckerPiece::NONE;
|
|
|
+ board[to.y][to.x] = checker;
|
|
|
+}
|
|
|
+
|
|
|
+void CheckerBoard::print() const
|
|
|
+{
|
|
|
+ wcout << "--------------------------------" << endl;
|
|
|
+ for (int y = BOARD_SIZE-1; y >= 0; y--)
|
|
|
+ {
|
|
|
+ for (int x = 0; x < BOARD_SIZE; x++)
|
|
|
+ {
|
|
|
+ wcout << '|';
|
|
|
+ switch (board[y][x])
|
|
|
+ {
|
|
|
+ case CheckerPiece::BLACK:
|
|
|
+ wcout << L" ⬤ ";
|
|
|
+ break;
|
|
|
+ case CheckerPiece::WHITE:
|
|
|
+ wcout << L" ◯ ";
|
|
|
+ break;
|
|
|
+ case CheckerPiece::BLACK_KING:
|
|
|
+ wcout << L" ◆ ";
|
|
|
+ break;
|
|
|
+ case CheckerPiece::WHITE_KING:
|
|
|
+ wcout << L" ◇ ";
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ wcout << L" ";
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ wcout << '|' << endl << "--------------------------------" << endl;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+std::vector<CheckerBoard> CheckerBoard::generateLegalMoves(bool isWhite) const {
|
|
|
+ std::vector<CheckerBoard> moves;
|
|
|
+ for (int i = 0; i < BOARD_SIZE; ++i) {
|
|
|
+ for (int j = 0; j < BOARD_SIZE; ++j) {
|
|
|
+ if ((isWhite && (board[i][j] == WHITE || board[i][j] == WHITE_KING)) ||
|
|
|
+ (!isWhite && (board[i][j] == BLACK || board[i][j] == BLACK_KING))) {
|
|
|
+ generateMovesForPiece(CheckerPosition(i, j), moves);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return moves;
|
|
|
+}
|
|
|
+
|
|
|
+void CheckerBoard::applyMove(const CheckerBoard& move) {
|
|
|
+ for (int i = 0; i < BOARD_SIZE; ++i) {
|
|
|
+ for (int j = 0; j < BOARD_SIZE; ++j) {
|
|
|
+ board[i][j] = move.board[i][j];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int CheckerBoard::evaluate() const {
|
|
|
+ int score = 0;
|
|
|
+ for (int i = 0; i < BOARD_SIZE; ++i) {
|
|
|
+ for (int j = 0; j < BOARD_SIZE; ++j) {
|
|
|
+ if (board[i][j] == WHITE) score += 1;
|
|
|
+ else if (board[i][j] == WHITE_KING) score += 3;
|
|
|
+ else if (board[i][j] == BLACK) score -= 1;
|
|
|
+ else if (board[i][j] == BLACK_KING) score -= 3;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return score;
|
|
|
+}
|
|
|
+
|
|
|
+bool CheckerBoard::isGameOver() const {
|
|
|
+ bool whiteExists = false, blackExists = false;
|
|
|
+ for (int i = 0; i < BOARD_SIZE; ++i) {
|
|
|
+ for (int j = 0; j < BOARD_SIZE; ++j) {
|
|
|
+ if (board[i][j] == WHITE || board[i][j] == WHITE_KING) whiteExists = true;
|
|
|
+ if (board[i][j] == BLACK || board[i][j] == BLACK_KING) blackExists = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return !whiteExists || !blackExists;
|
|
|
+}
|
|
|
+
|
|
|
+void CheckerBoard::generateMovesForPiece(CheckerPosition pos, std::vector<CheckerBoard>& moves) const {
|
|
|
+ CheckerPiece piece = board[pos.y][pos.x];
|
|
|
+ int directions[4][2] = { {1, 1}, {1, -1}, {-1, 1}, {-1, -1} };
|
|
|
+
|
|
|
+ for (auto& dir : directions) {
|
|
|
+ CheckerPosition newpos(pos.x + dir[0], pos.y + dir[1]);
|
|
|
+
|
|
|
+ if (isValidMove(pos, newpos, piece)) {
|
|
|
+ CheckerBoard newBoard = *this;
|
|
|
+ newBoard.board[newpos.y][newpos.x] = newBoard.board[pos.y][pos.x];
|
|
|
+ newBoard.board[pos.y][pos.x] = CheckerPiece::NONE;
|
|
|
+
|
|
|
+ if ((piece == WHITE && newpos.y == 0) || (piece == BLACK && newpos.y == BOARD_SIZE - 1)) {
|
|
|
+ newBoard.board[newpos.y][newpos.x] = (piece == WHITE) ? WHITE_KING : BLACK_KING;
|
|
|
+ }
|
|
|
+
|
|
|
+ moves.push_back(newBoard);
|
|
|
+
|
|
|
+ CheckerPosition cpos(pos.x + 2 * dir[0], pos.y + 2 * dir[1]);
|
|
|
+ if (isValidCapture(pos, newpos, cpos, piece)) {
|
|
|
+ CheckerBoard newBoard = *this;
|
|
|
+ newBoard.board[cpos.y][cpos.x] = newBoard.board[pos.y][pos.x];
|
|
|
+ newBoard.board[pos.y][pos.x] = CheckerPiece::NONE;
|
|
|
+ newBoard.board[newpos.y][newpos.x] = CheckerPiece::NONE;
|
|
|
+
|
|
|
+ if ((piece == WHITE && cpos.y == 0) || (piece == BLACK && cpos.y == BOARD_SIZE - 1)) {
|
|
|
+ newBoard.board[cpos.y][cpos.x] = (piece == WHITE) ? WHITE_KING : BLACK_KING;
|
|
|
+ }
|
|
|
+
|
|
|
+ moves.push_back(newBoard);
|
|
|
+ generateAdditionalCaptures(cpos, newBoard, moves);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool CheckerBoard::isValidMove(CheckerPosition oldPos, CheckerPosition newPos, CheckerPiece piece) const {
|
|
|
+ if (newPos.x < 0 || newPos.x >= BOARD_SIZE || newPos.y < 0 || newPos.y >= BOARD_SIZE) return false;
|
|
|
+ if (board[newPos.y][newPos.x] != CheckerPiece::NONE) return false;
|
|
|
+ if (piece == WHITE && newPos.y <= oldPos.y) return false;
|
|
|
+ if (piece == BLACK && newPos.y >= oldPos.y) return false;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+void CheckerBoard::generateAdditionalCaptures(CheckerPosition pos, CheckerBoard& currentBoard, std::vector<CheckerBoard>& moves) const {
|
|
|
+ CheckerPiece piece = currentBoard.board[pos.y][pos.x];
|
|
|
+ int directions[4][2] = { {1, 1}, {1, -1}, {-1, 1}, {-1, -1} };
|
|
|
+
|
|
|
+ for (auto& dir : directions) {
|
|
|
+ CheckerPosition npos(pos.x + dir[0], pos.y + dir[1]);
|
|
|
+ CheckerPosition cpos(pos.x + 2 * dir[0], pos.y + 2 * dir[0]);
|
|
|
+ if (isValidCapture(pos, npos, cpos, piece)) {
|
|
|
+ CheckerBoard newBoard = currentBoard;
|
|
|
+ newBoard.board[cpos.y][cpos.x] = newBoard.board[pos.y][pos.x];
|
|
|
+ newBoard.board[pos.y][pos.x] = CheckerPiece::NONE;
|
|
|
+ newBoard.board[npos.y][npos.x] = CheckerPiece::NONE;
|
|
|
+
|
|
|
+ if ((piece == WHITE && cpos.y == 0) || (piece == BLACK && cpos.y == BOARD_SIZE - 1)) {
|
|
|
+ newBoard.board[cpos.y][cpos.x] = (piece == WHITE) ? WHITE_KING : BLACK_KING;
|
|
|
+ }
|
|
|
+
|
|
|
+ moves.push_back(newBoard);
|
|
|
+ generateAdditionalCaptures(cpos, newBoard, moves);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool CheckerBoard::isValidCapture(CheckerPosition pos, CheckerPosition newPos, CheckerPosition cpos, CheckerPiece piece) const {
|
|
|
+ if (cpos.y < 0 || cpos.y >= BOARD_SIZE || cpos.x < 0 || cpos.x >= BOARD_SIZE) return false;
|
|
|
+ if (board[cpos.y][cpos.x] != CheckerPiece::NONE) return false;
|
|
|
+ if (board[newPos.y][newPos.x] == CheckerPiece::NONE) return false;
|
|
|
+
|
|
|
+ if (piece == WHITE && (board[newPos.y][newPos.x] == BLACK || board[newPos.y][newPos.x] == BLACK_KING)) return true;
|
|
|
+ if (piece == BLACK && (board[newPos.y][newPos.x] == WHITE || board[newPos.y][newPos.x] == WHITE_KING)) return true;
|
|
|
+ if ((piece == WHITE_KING || piece == BLACK_KING) && abs(cpos.y - pos.y) == 2 && abs(cpos.x - pos.x) == 2) return true;
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|