Explorar o código

Added comments and reformatted

Vsevolod Levitan hai 11 meses
pai
achega
be817c1009

+ 233 - 74
Checkers/Checkers/CheckerBoard.cpp

@@ -1,12 +1,13 @@
 #include "CheckerBoard.h"
 #include <iostream>
 
-
-CheckerBoard::CheckerBoard() {
+CheckerBoard::CheckerBoard()
+{
     lastMove = "NONE";
 }
 
-CheckerBoard CheckerBoard::loadFromFile(string filename) {
+CheckerBoard CheckerBoard::loadFromFile(string filename)
+{
     CheckerBoard board = CheckerBoard();
 
     ifstream file(filename);
@@ -14,68 +15,100 @@ CheckerBoard CheckerBoard::loadFromFile(string filename) {
 
     int color = WHITE;
 
-    while (getline(file, line)) {
-        if (line.find("White:") != string::npos) {
+    while (getline(file, line))
+    {
+        // Toggle color mode if color flag is found
+        if (line.find("White:") != string::npos)
+        {
             color = WHITE;
         }
 
-        else if (line.find("Black:") != string::npos) {
+        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);
+        else if (!line.empty())
+        {
+            // If the checker position starts with M (which marks a king), skip the first character of the position and create a king
+            if (line[0] == 'M')
+                board.placeChecker(CheckerPosition(line.substr(1, 2)), color == WHITE ? CheckerPiece::WHITE_KING : CheckerPiece::BLACK_KING);
+            // otherwise, use full position string and create a regular checker
+            else
+                board.placeChecker(CheckerPosition(line), color == WHITE ? CheckerPiece::WHITE : CheckerPiece::BLACK);
         }
     }
     file.close();
 
+    // Iterate over whole board and assign CheckerPiece::EMPTY where no checker is present
     for (int i = 0; i < BOARD_SIZE; i++)
     {
         for (int j = 0; j < BOARD_SIZE; j++)
         {
             CheckerPosition pos(i, j);
             CheckerPiece piece = board.getCheckerAt(pos);
-            if (piece != WHITE && piece != WHITE_KING && piece != BLACK && piece != BLACK_KING) board.setCheckerAt(pos, CheckerPiece::NONE);
+            if (piece != WHITE && piece != WHITE_KING && piece != BLACK && piece != BLACK_KING)
+                board.setCheckerAt(pos, CheckerPiece::NONE);
         }
     }
 
     return board;
 }
 
-CheckerPiece CheckerBoard::getCheckerAt(CheckerPosition pos) const {
-    if (!pos.isValid()) return CheckerPiece::NONE;
+CheckerPiece CheckerBoard::getCheckerAt(CheckerPosition pos) const
+{
+    // Always return empty space when the position is not valid
+    if (!pos.isValid())
+        return CheckerPiece::NONE;
 
     return board[pos.x][pos.y];
 }
 
 void CheckerBoard::setCheckerAt(CheckerPosition pos, CheckerPiece checker)
 {
+    // Skip if position is invalid
+    if (!pos.isValid())
+        return;
+
     board[pos.x][pos.y] = checker;
 }
 
-void CheckerBoard::moveChecker(CheckerPosition from, CheckerPosition to) {
+void CheckerBoard::moveChecker(CheckerPosition from, CheckerPosition to)
+{
     CheckerPiece checker = getCheckerAt(from);
+
+    // Remove the checker from its original position
     setCheckerAt(from, CheckerPiece::NONE);
+
+    // Calculate movement direction
     int cx = to.x - from.x > 0 ? 1 : -1;
     int cy = to.y - from.y > 0 ? 1 : -1;
+
+    // Current position for iterating
     int sx = from.x + cx;
     int sy = from.y + cy;
 
+    // Remove all pieces between the starting and ending point
     while (sx != to.x)
     {
         setCheckerAt(CheckerPosition(sx, sy), CheckerPiece::NONE);
         sx += cx;
         sy += cy;
     }
+
+    // Insert the checker into new position
     setCheckerAt(to, checker);
 
-    if (checker == CheckerPiece::WHITE && to.y == BOARD_SIZE - 1) setCheckerAt(to, CheckerPiece::WHITE_KING);
-    else  if (checker == CheckerPiece::BLACK && to.y == 0) setCheckerAt(to, CheckerPiece::BLACK_KING);
+    // If reached opposite edge of the board, promote checker to king
+    if (checker == CheckerPiece::WHITE && to.y == BOARD_SIZE - 1)
+        setCheckerAt(to, CheckerPiece::WHITE_KING);
+    else if (checker == CheckerPiece::BLACK && to.y == 0)
+        setCheckerAt(to, CheckerPiece::BLACK_KING);
 }
 
 void CheckerBoard::print() const
 {
+    // Legend at the top
     wcout << "--A---B---C---D---E---F---G---H---" << endl;
     for (int y = BOARD_SIZE - 1; y >= 0; y--)
     {
@@ -102,17 +135,27 @@ void CheckerBoard::print() const
             }
         }
 
-        wcout << "| " << y+1 << endl << "-------------------------------- " << endl;
+        // Print out the last border, the Y-axis legend and the separator row
+        wcout << "| " << y + 1 << endl
+              << "-------------------------------- " << endl;
     }
 }
 
-std::vector<CheckerBoard> CheckerBoard::generateLegalMoves(bool isWhite) const {
+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) {
+
+    // Iterate over all board cells
+    for (int i = 0; i < BOARD_SIZE; ++i)
+    {
+        for (int j = 0; j < BOARD_SIZE; ++j)
+        {
             CheckerPiece checker = getCheckerAt(CheckerPosition(i, j));
+
+            // If the checker is of corresponding color, generate all possible moves for it
             if ((isWhite && (checker == WHITE || checker == WHITE_KING)) ||
-                (!isWhite && (checker == BLACK || checker == BLACK_KING))) {
+                (!isWhite && (checker == BLACK || checker == BLACK_KING)))
+            {
                 generateMovesForPiece(CheckerPosition(i, j), moves);
             }
         }
@@ -121,62 +164,101 @@ std::vector<CheckerBoard> CheckerBoard::generateLegalMoves(bool isWhite) const {
     return moves;
 }
 
-void CheckerBoard::applyMove(const CheckerBoard& move) {
-    for (int i = 0; i < BOARD_SIZE; ++i) {
-        for (int j = 0; j < BOARD_SIZE; ++j) {
+void CheckerBoard::applyMove(const CheckerBoard &move)
+{
+    // Iterate over all board cells and copy values from the alternative board
+    for (int i = 0; i < BOARD_SIZE; ++i)
+    {
+        for (int j = 0; j < BOARD_SIZE; ++j)
+        {
             setCheckerAt(CheckerPosition(i, j), move.getCheckerAt(CheckerPosition(i, j)));
         }
     }
 }
 
-int CheckerBoard::evaluate() const {
+int CheckerBoard::evaluate() const
+{
     int score = 0;
-    for (int i = 0; i < BOARD_SIZE; ++i) {
-        for (int j = 0; j < BOARD_SIZE; ++j) {
+
+    // Iterate over all board cells and calculate the score of the board
+    for (int i = 0; i < BOARD_SIZE; ++i)
+    {
+        for (int j = 0; j < BOARD_SIZE; ++j)
+        {
             CheckerPiece checker = getCheckerAt(CheckerPosition(i, j));
-            if (checker == WHITE) score += 1;
-            else if (checker == WHITE_KING) score += 3;
-            else if (checker == BLACK) score -= 1;
-            else if (checker == BLACK_KING) score -= 3;
+
+            // Kings are more valuable
+            if (checker == WHITE)
+                score += 1;
+            else if (checker == WHITE_KING)
+                score += 3;
+            else if (checker == BLACK)
+                score -= 1;
+            else if (checker == BLACK_KING)
+                score -= 3;
         }
     }
     return score;
 }
 
-bool CheckerBoard::isGameOver() const {
+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;
+
+    // Iterate over all board cells and find if any checkers of each side still exist
+    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;
         }
     }
+
+    // The game is over when one of the sides is cleansed
     return !whiteExists || !blackExists;
 }
 
-void CheckerBoard::generateMovesForPiece(CheckerPosition pos, std::vector<CheckerBoard>& moves) const {
+void CheckerBoard::generateMovesForPiece(CheckerPosition pos, std::vector<CheckerBoard> &moves) const
+{
     CheckerPiece piece = getCheckerAt(pos);
-    int directions[4][2] = { {1, 1}, {1, -1}, {-1, 1}, {-1, -1} };
 
-    for (auto& dir : directions) {
+    // Possible directions are: bottom-left, top-left, top-right, bottom-right
+    int directions[4][2] = {{1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
+
+    for (auto &dir : directions)
+    {
+        // Position after the move
         CheckerPosition newpos(pos.x + dir[0], pos.y + dir[1]);
+
+        // Position after the move if an enemy's checker is captured
         CheckerPosition cpos(pos.x + 2 * dir[0], pos.y + 2 * dir[1]);
 
-        if (isValidMove(pos, newpos, piece)) {
+        if (isValidMove(pos, newpos, piece))
+        {
+            // Copy the board and apply the move
             CheckerBoard newBoard = *this;
             newBoard.moveChecker(pos, newpos);
+
+            // Save data about the move
             newBoard.lastMove = pos.to_num_string() + " -> " + newpos.to_num_string();
             newBoard.moveOldPos = pos;
             newBoard.moveNewPos = newpos;
 
-            if ((piece == WHITE && newpos.y == BOARD_SIZE - 1) || (piece == BLACK && newpos.y == 0)) {
+            // Promote to king if the opposite edge is reached
+            if ((piece == WHITE && newpos.y == BOARD_SIZE - 1) || (piece == BLACK && newpos.y == 0))
+            {
                 newBoard.setCheckerAt(newpos, (piece == WHITE) ? WHITE_KING : BLACK_KING);
             }
 
+            // Save the move
             moves.push_back(newBoard);
 
-            CheckerPosition cpos(pos.x + 2 * dir[0], pos.y + 2 * dir[1]);
-            if (isValidCapture(pos, newpos, cpos, piece)) {
+            // If a capture is possible, count it as a different move
+            if (isValidCapture(pos, newpos, cpos, piece))
+            {
                 CheckerBoard newBoard = *this;
                 CheckerPiece checker = newBoard.getCheckerAt(pos);
 
@@ -184,19 +266,24 @@ void CheckerBoard::generateMovesForPiece(CheckerPosition pos, std::vector<Checke
                 newBoard.moveOldPos = pos;
                 newBoard.moveNewPos = newpos;
 
+                // Move the checker and remove one that was captured
                 newBoard.setCheckerAt(newpos, CheckerPiece::NONE);
                 newBoard.setCheckerAt(pos, CheckerPiece::NONE);
                 newBoard.setCheckerAt(cpos, checker);
 
-                if ((piece == WHITE && newpos.y == BOARD_SIZE - 1) || (piece == BLACK && cpos.y == 0)) {
+                // If the opposite edge of the board is reached, promote to king
+                if ((piece == WHITE && newpos.y == BOARD_SIZE - 1) || (piece == BLACK && cpos.y == 0))
+                {
                     newBoard.setCheckerAt(cpos, (piece == WHITE) ? WHITE_KING : BLACK_KING);
                 }
 
                 moves.push_back(newBoard);
-                //generateAdditionalCaptures(cpos, newBoard, moves);
+                // generateAdditionalCaptures(cpos, newBoard, moves);
             }
         }
-        else if (isValidCapture(pos, newpos, cpos, piece)) {
+        // If the move is not valid, the capture still might be
+        else if (isValidCapture(pos, newpos, cpos, piece))
+        {
             CheckerBoard newBoard = *this;
             CheckerPiece checker = newBoard.getCheckerAt(pos);
 
@@ -208,15 +295,18 @@ void CheckerBoard::generateMovesForPiece(CheckerPosition pos, std::vector<Checke
             newBoard.setCheckerAt(pos, CheckerPiece::NONE);
             newBoard.setCheckerAt(cpos, checker);
 
-            if ((piece == WHITE && newpos.y == BOARD_SIZE - 1) || (piece == BLACK && cpos.y == 0)) {
+            // Promote to king if the opposite edge is reached
+            if ((piece == WHITE && newpos.y == BOARD_SIZE - 1) || (piece == BLACK && cpos.y == 0))
+            {
                 newBoard.setCheckerAt(cpos, (piece == WHITE) ? WHITE_KING : BLACK_KING);
             }
 
             moves.push_back(newBoard);
-            //generateAdditionalCaptures(cpos, newBoard, moves);
+            // generateAdditionalCaptures(cpos, newBoard, moves);
         }
     }
 
+    // Print out moves for debug purposes
     /*for (auto& move : moves)
     {
         wcout << "Move: \n";
@@ -224,70 +314,120 @@ void CheckerBoard::generateMovesForPiece(CheckerPosition pos, std::vector<Checke
     }*/
 }
 
-bool CheckerBoard::isValidMove(CheckerPosition oldPos, CheckerPosition newPos, CheckerPiece piece) const {
-    if (newPos.x < 0 || newPos.x >= BOARD_SIZE) return false;
-    if (newPos.y < 0 || newPos.y >= BOARD_SIZE) return false;
+bool CheckerBoard::isValidMove(CheckerPosition oldPos, CheckerPosition newPos, CheckerPiece piece) const
+{
+    // If the new position is not valid, the move is not valid either
+    if (!newPos.isValid())
+        return false;
 
+    // Checker piece at the current position
     CheckerPiece at = getCheckerAt(oldPos);
+    // Checker piece at the new position
     CheckerPiece atnew = getCheckerAt(newPos);
 
-    if (at == CheckerPiece::NONE) return false;
-    if (atnew != CheckerPiece::NONE) return false;
+    // There should be a checker in the current position (the one we're moving)
+    if (at == CheckerPiece::NONE)
+        return false;
+    // You can't move to a cell if it's occupied
+    if (atnew != CheckerPiece::NONE)
+        return false;
 
+    // Calculate position delta
     int dx = newPos.x - oldPos.x, dy = newPos.y - oldPos.y;
+
+    // Calcule movement direction
     int cx = dx > 0 ? 1 : -1, cy = dy > 0 ? 1 : -1;
+
     bool isWhite = at == CheckerPiece::WHITE || at == CheckerPiece::WHITE_KING;
 
-    if (abs(dx) != abs(dy) || dx == 0) return false;
+    // Checkers may only move diagonally
+    if (abs(dx) != abs(dy) || dx == 0)
+        return false;
 
+    // King-specific conditions
     if (piece == CheckerPiece::WHITE_KING || piece == CheckerPiece::BLACK_KING)
     {
         int sx = oldPos.x + cx, sy = oldPos.y + cy;
 
+        // There shouldn't be an ally checker in the way
         while (sx != newPos.x || sy != newPos.y)
         {
             CheckerPiece between = getCheckerAt(CheckerPosition(sx, sy));
 
-            if (isWhite && !(between == CheckerPiece::BLACK || between == CheckerPiece::BLACK_KING || between == CheckerPiece::NONE)) return false;
-            if (!isWhite && !(between == CheckerPiece::WHITE || between == CheckerPiece::WHITE_KING || between == CheckerPiece::NONE)) return false;
+            if (isWhite && !(between == CheckerPiece::BLACK || between == CheckerPiece::BLACK_KING || between == CheckerPiece::NONE))
+                return false;
+            if (!isWhite && !(between == CheckerPiece::WHITE || between == CheckerPiece::WHITE_KING || between == CheckerPiece::NONE))
+                return false;
 
             sx += cx;
             sy += cy;
         }
     }
+    // Conditions for regular checkers
     else
     {
-        if (isWhite && dy == -1) return false;
-        else if (!isWhite && dy == 1) return false;
+        // Regular checkers may not go backwards
+        if (isWhite && dy == -1)
+            return false;
+        else if (!isWhite && dy == 1)
+            return false;
+
+        // If moved 2 cells (capture scenario)
         else if (abs(dx) == 2)
         {
+            // Checker in-between the current and the new position
             CheckerPiece between = getCheckerAt(CheckerPosition(oldPos.x + cx, oldPos.y + cy));
-            if (between == CheckerPiece::NONE) return false;
-            if (getCheckerAt(newPos) != CheckerPiece::NONE) return false;
-            if (isWhite && !(between == CheckerPiece::BLACK || between == CheckerPiece::BLACK_KING)) return false;
-            if (!isWhite && !(between == CheckerPiece::WHITE || between == CheckerPiece::WHITE_KING)) return false;
+
+            // There should be a checker to capture
+            if (between == CheckerPiece::NONE)
+                return false;
+
+            // There shouldn't be any checker in the new position
+            if (getCheckerAt(newPos) != CheckerPiece::NONE)
+                return false;
+
+            // The captured checker shall not be ally
+            if (isWhite && !(between == CheckerPiece::BLACK || between == CheckerPiece::BLACK_KING))
+                return false;
+            if (!isWhite && !(between == CheckerPiece::WHITE || between == CheckerPiece::WHITE_KING))
+                return false;
         }
-        else if (abs(dx) > 2) return false;
+        // Regular checkers may not move more than 2 cells in any situation
+        else if (abs(dx) > 2)
+            return false;
     }
 
     return true;
 }
 
-void CheckerBoard::generateAdditionalCaptures(CheckerPosition pos, CheckerBoard& currentBoard, std::vector<CheckerBoard>& moves) const {
+void CheckerBoard::generateAdditionalCaptures(CheckerPosition pos, CheckerBoard &currentBoard, std::vector<CheckerBoard> &moves) const
+{
     CheckerPiece piece = currentBoard.getCheckerAt(pos);
-    int directions[4][2] = { {1, 1}, {1, -1}, {-1, 1}, {-1, -1} };
 
-    for (auto& dir : directions) {
+    // Top-left, top-right, bottom-right, bottom-left of the checker
+    int directions[4][2] = {{1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
+
+    // Analyze all directions
+    for (auto &dir : directions)
+    {
+        // The position of the captured checker
         CheckerPosition npos(pos.x + dir[0], pos.y + dir[1]);
+        // Final position after capture
         CheckerPosition cpos(pos.x + 2 * dir[0], pos.y + 2 * dir[0]);
-        if (isValidCapture(pos, npos, cpos, piece)) {
+
+        // If the capture is valid
+        if (isValidCapture(pos, npos, cpos, piece))
+        {
             CheckerBoard newBoard = currentBoard;
 
+            // Move the checker and remove captured
             newBoard.setCheckerAt(cpos, newBoard.getCheckerAt(pos));
             newBoard.setCheckerAt(pos, CheckerPiece::NONE);
             newBoard.setCheckerAt(npos, CheckerPiece::NONE);
 
-            if ((piece == WHITE && cpos.x == BOARD_SIZE-1) || (piece == BLACK && cpos.x == 0)) {
+            // Promote to king if the opposite edge is reached
+            if ((piece == WHITE && cpos.x == BOARD_SIZE - 1) || (piece == BLACK && cpos.x == 0))
+            {
                 newBoard.setCheckerAt(cpos, (piece == WHITE) ? WHITE_KING : BLACK_KING);
             }
 
@@ -297,17 +437,36 @@ void CheckerBoard::generateAdditionalCaptures(CheckerPosition pos, CheckerBoard&
     }
 }
 
-bool CheckerBoard::isValidCapture(CheckerPosition pos, CheckerPosition newPos, CheckerPosition cpos, CheckerPiece piece) const {
+bool CheckerBoard::isValidCapture(CheckerPosition pos, CheckerPosition newPos, CheckerPosition cpos, CheckerPiece piece) const
+{
+    // Current checker piece
     CheckerPiece oldC = getCheckerAt(pos);
+    // Checker piece at the end position
     CheckerPiece newC = getCheckerAt(newPos);
+    // Checker piece at the capture position
     CheckerPiece cC = getCheckerAt(cpos);
-    if (!cpos.isValid()) return false;
-    if (cC != CheckerPiece::NONE) return false;
-    if (newC == CheckerPiece::NONE) return false;
 
-    if (piece == WHITE && (newC == BLACK || newC == BLACK_KING)) return true;
-    if (piece == BLACK && (newC == WHITE || newC == 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;
+    // Capture position should be valid
+    if (!cpos.isValid())
+        return false;
+
+    // There should be a checker to capture
+    if (cC != CheckerPiece::NONE)
+        return false;
+
+    // There should be a checker at the current position
+    if (newC == CheckerPiece::NONE)
+        return false;
+
+    // Valid if we're capturing the enemy checker
+    if (piece == WHITE && (newC == BLACK || newC == BLACK_KING))
+        return true;
+    if (piece == BLACK && (newC == WHITE || newC == WHITE_KING))
+        return true;
+
+    // Not sure about this part
+    if ((piece == WHITE_KING || piece == BLACK_KING) && abs(cpos.y - pos.y) == 2 && abs(cpos.x - pos.x) == 2)
+        return true;
 
     return false;
 }

+ 5 - 4
Checkers/Checkers/CheckerBoard.h

@@ -15,7 +15,8 @@ const int BOARD_SIZE = 8;
 /// <summary>
 /// Represents a standard checker board
 /// </summary>
-class CheckerBoard {
+class CheckerBoard
+{
 public:
     std::string lastMove = "";
     CheckerPosition moveOldPos = CheckerPosition(-1, -1);
@@ -67,7 +68,7 @@ public:
     /// Apply the future branch of the board
     /// </summary>
     /// <param name="move">A board copy representing a possible branch in the future</param>
-    void applyMove(const CheckerBoard& move);
+    void applyMove(const CheckerBoard &move);
 
     /// <summary>
     /// Evaluate the board
@@ -106,7 +107,7 @@ private:
     /// </summary>
     /// <param name="position">The position of the piece</param>
     /// <param name="moves">A vector of possible alternative future boards</param>
-    void generateMovesForPiece(CheckerPosition position, std::vector<CheckerBoard>& moves) const;
+    void generateMovesForPiece(CheckerPosition position, std::vector<CheckerBoard> &moves) const;
 
     /// <summary>
     /// Generate combination captures (>1 per move)
@@ -114,7 +115,7 @@ private:
     /// <param name="position">The position of the checker</param>
     /// <param name="currentBoard">Current board</param>
     /// <param name="moves">Possible future moves</param>
-    void generateAdditionalCaptures(CheckerPosition position, CheckerBoard& currentBoard, std::vector<CheckerBoard>& moves) const;
+    void generateAdditionalCaptures(CheckerPosition position, CheckerBoard &currentBoard, std::vector<CheckerBoard> &moves) const;
 };
 
 #endif

+ 11 - 5
Checkers/Checkers/CheckerPosition.cpp

@@ -4,20 +4,26 @@
 
 CheckerPosition::CheckerPosition(int x, int y) : x(x), y(y) {}
 
-CheckerPosition::CheckerPosition(std::string pos) {
-    x = pos[0] - 'A';
-    y = pos[1] - '1';
+CheckerPosition::CheckerPosition(std::string pos)
+{
+    x = pos[0] - 'A'; // 'A' = 0, 'B' = 2, 'C' = 3, ...
+    y = pos[1] - '1'; // '1' = 0, '2' = 1, '3' = 2, ...
 }
 
-std::string CheckerPosition::to_string() const {
+std::string CheckerPosition::to_string() const
+{
+    // Chess-like serialization
     return std::string(1, 'A' + x) + std::to_string(y + 1);
 }
 
-bool CheckerPosition::isValid() const {
+bool CheckerPosition::isValid() const
+{
+    // The position is valid if it's within the board
     return x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE;
 }
 
 std::string CheckerPosition::to_num_string() const
 {
+    // Coordinate-like serialization
     return "(" + std::to_string(x) + ", " + std::to_string(y) + ")";
 }

+ 2 - 1
Checkers/Checkers/CheckerPosition.h

@@ -4,7 +4,8 @@
 /// <summary>
 /// Describes a checker board position
 /// </summary>
-class CheckerPosition {
+class CheckerPosition
+{
 public:
     int x, y;
 

+ 59 - 21
Checkers/Checkers/CheckersAI.cpp

@@ -1,60 +1,98 @@
 #include "CheckersAI.h"
 
-
-CheckerBoard CheckersAI::findBestMove(const CheckerBoard& board, int depth) {
+CheckerBoard CheckersAI::findBestMove(const CheckerBoard &board, int depth)
+{
+    // Alpha is smallest possible number
     int alpha = std::numeric_limits<int>::min();
+    // Beta is highest possible number
     int beta = std::numeric_limits<int>::max();
+
+    // Best value is the biggest for white and the smallest for black
     int bestValue = isWhite ? std::numeric_limits<int>::min() : std::numeric_limits<int>::max();
     CheckerBoard bestMove;
 
+    // Generate all possible moves of all checkers for the current side
     std::vector<CheckerBoard> moves = board.generateLegalMoves(isWhite);
 
-    if (moves.size() == 0) throw exception();
+    // Throw exception if there are no possible moves (change this to forfeit later)
+    if (moves.size() == 0)
+        throw exception();
 
-    for (const auto& move : moves) {
+    // Iterate over all possible moves
+    for (const auto &move : moves)
+    {
+        // Calculate the minimax score for this move
         int value = minimax(move, depth - 1, alpha, beta, !isWhite);
-        if (isWhite && value > bestValue) {
-            bestValue = value;
-            bestMove = move;
-            alpha = std::max(alpha, bestValue);
+
+        // If the value is better than current best
+        if (isWhite && value > bestValue)
+        {
+            bestValue = value;                  // Save the value
+            bestMove = move;                    // Save the move
+            alpha = std::max(alpha, bestValue); // Update the alpha
         }
-        else if (!isWhite && value < bestValue) {
-            bestValue = value;
-            bestMove = move;
-            beta = std::min(beta, bestValue);
+        // The best value is the smallest value for black
+        else if (!isWhite && value < bestValue)
+        {
+            bestValue = value;                // Save the value
+            bestMove = move;                  // Save the move
+            beta = std::min(beta, bestValue); // Update the beta
         }
-        if (alpha >= beta) break;
+        // Alpha-beta pruning
+        if (alpha >= beta)
+            break;
     }
     wcout << "Best move: " << bestMove.lastMove.c_str() << endl;
 
     return bestMove;
 }
 
-int CheckersAI::minimax(CheckerBoard board, int depth, int alpha, int beta, bool isMaximizing) {
-    if (depth == 0 || board.isGameOver()) {
+int CheckersAI::minimax(CheckerBoard board, int depth, int alpha, int beta, bool isMaximizing)
+{
+    // When reached the cut-out leaf of the decision tree, evaluate the score
+    if (depth == 0 || board.isGameOver())
+    {
         return board.evaluate();
     }
 
     std::vector<CheckerBoard> moves = board.generateLegalMoves(isMaximizing);
 
-    if (isMaximizing) {
+    // Maximizing for blacks
+    if (isMaximizing)
+    {
         int maxEval = std::numeric_limits<int>::min();
-        for (const auto& move : moves) {
+        for (const auto &move : moves)
+        {
+            // Go further down the decision tree
             int eval = minimax(move, depth - 1, alpha, beta, false);
+            // Keep current max evaluated score
             maxEval = std::max(maxEval, eval);
+            // Update the alpha
             alpha = std::max(alpha, eval);
-            if (beta <= alpha) break;
+            // Alpha-beta pruning
+            if (beta <= alpha)
+                break;
         }
+        // Return max evaluated score for blacks
         return maxEval;
     }
-    else {
+    // Minimizing for whites
+    else
+    {
         int minEval = std::numeric_limits<int>::max();
-        for (const auto& move : moves) {
+        for (const auto &move : moves)
+        {
+            // Go further down the decision tree
             int eval = minimax(move, depth - 1, alpha, beta, true);
+            // Keep current max evaluated score
             minEval = std::min(minEval, eval);
+            // Update ethe beta
             beta = std::min(beta, eval);
-            if (beta <= alpha) break;
+            // Alpha-beta pruning
+            if (beta <= alpha)
+                break;
         }
+        // Return min evaluated score for whites
         return minEval;
     }
 }

+ 3 - 3
Checkers/Checkers/CheckersAI.h

@@ -1,8 +1,8 @@
 #pragma once
 #include "CheckerBoard.h"
 
-
-class CheckersAI {
+class CheckersAI
+{
 public:
     CheckersAI(bool isWhite) : isWhite(isWhite) {}
 
@@ -12,7 +12,7 @@ public:
     /// <param name="board">The board setup</param>
     /// <param name="depth">Minimax search depth</param>
     /// <returns>Updated board</returns>
-    CheckerBoard findBestMove(const CheckerBoard& board, int depth);
+    CheckerBoard findBestMove(const CheckerBoard &board, int depth);
 
 private:
     bool isWhite;

+ 37 - 13
Checkers/Checkers/main.cpp

@@ -8,72 +8,96 @@ int main()
 {
     _setmode(_fileno(stdout), _O_U16TEXT);
 
+    // Load the board from file
     CheckerBoard board = CheckerBoard::loadFromFile("board - Copy.checkers");
 
+    // Print out the initial board setup
     board.print();
 
+    // Separator line
     wcout << "\n\n-----------\n\n";
 
     CheckersAI whiteAI(true);
     CheckersAI blackAI(false);
 
+    // Whites move first
     bool whiteTurn = true;
-    while (!board.isGameOver()) {
+
+    // Run the game until one of the sides wins
+    while (!board.isGameOver())
+    {
         wcout << L"\n\n";
 
-        if (whiteTurn) {
+        // AI plays white
+        if (whiteTurn)
+        {
             wcout << "White: ";
+            // Find the best move
             CheckerBoard bestMove = whiteAI.findBestMove(board, 7);
+
+            // If no move is good enough, give up
             if (bestMove.lastMove == "NONE")
             {
                 wcout << "White forfeit";
                 return 0;
             }
+
+            // Apply the best move to the board
             board.applyMove(bestMove);
         }
         // Раскомментировать для игры ИИ против ИИ
         /*else {
             wcout << "Black: ";
+            // Find the best move
             CheckerBoard bestMove = blackAI.findBestMove(board, 7);
+
+            // If no move is good enough, give up
             if (bestMove.lastMove == "NONE")
             {
                 wcout << "Black forfeit";
                 return 0;
             }
+
+            // Apply the best move to the board
             board.applyMove(bestMove);
         }*/
         // Раскомментировать для игры Человек против ИИ
         else
         {
-            while (true) {
-                wcout << "Old pos: ";
+            while (true)
+            {
+                // Read initial checker position
+                wcout << "Checker position: ";
                 std::string inp;
                 cin >> inp;
                 CheckerPosition pos(inp);
 
-                wcout << "New pos: ";
+                // Read desired new checker position
+                wcout << "Move to: ";
                 std::string inp2;
                 cin >> inp2;
                 CheckerPosition newPos(inp2);
 
-                int dx = newPos.x - pos.x;
-                int dy = newPos.y - pos.y;
-                int cx = newPos.x + dx;
-                int cy = newPos.y + dy;
-
-                if (!board.isValidMove(pos, newPos, board.getCheckerAt(pos))/* && !board.isValidCapture(pos, newPos, CheckerPosition(cx, cy), board.getCheckerAt(pos))*/)
+                // Check if the move is valid
+                if (!board.isValidMove(pos, newPos, board.getCheckerAt(pos)) /* && !board.isValidCapture(pos, newPos, CheckerPosition(cx, cy), board.getCheckerAt(pos))*/)
                 {
-                    wcout << "Wrong position" << endl;
+                    wcout << "Wrong move" << endl;
+                    // If the move is not valid, ask again
                     continue;
                 }
 
+                // If the move is valid, apply it
                 board.moveChecker(pos, newPos);
+
+                // Exit the user-interaction loop
                 break;
             }
         }
+        // Change turns
         whiteTurn = !whiteTurn;
+
+        // Print out the board
         board.print();
-        //return 0;
     }
 
     wcout << L"Game over!\n";