Search code examples
c++tic-tac-toeminimax

Implementing Minimax Algorithm for Tic-Tac-Toe in C++


I've been working on a C++ implementation of a Tic-Tac-Toe Minimax AI and I'm struggling to force the AI to play moves to block the player from winning. I was hoping some people can help me by finding out how to enforce this.

Node Code

void Node::addToNodeList(int turn) {
  childNodes.push_back(Node(G));
  for (auto& child : childNodes) {
    child.addToNodeList(-turn);

    if (child.childNodes.empty()) {
      float tempScore = child.determineMinimaxValue();

      if (tempScore < score) {
        score = tempScore;
      }
    } else {
      for (int i = 0; i < child.childNodes.size(); i++) {
        if (child.childNodes[i].score < score) {
          score = child.childNodes[i].score;
        }
      }
    }
  }
}

float Node::determineMinimaxValue()
{
  if (childNodes.empty()) {
    if (G.getWinner() == 0) {
      return 0.f;
    } else if (G.getWinner() == -1) {
      return -1.f;
    } else if (G.getWinner() == 1) {
      return 1.f;
    }
  } else {
    for (auto& child : childNodes) {
      child.determineMinimaxValue();
    }
  }
  return 0;
}

AI Player Code

Node AIPlayer::minimaxMove(int player, GameBoard GB)
{
  G = GB;
  N = Node(G); 
  N.addToNodeList(player);
  return N;
}

int AIPlayer::makeTheMove(GameBoard *GB, Node ND)
{
  float smallest = 100;
  for (unsigned int i = 0; i < ND.childNodes.size(); i++) {
    smallest = std::min(smallest, ND.childNodes[i].score);

    if (i == ND.childNodes.size() - 1) {
      for (unsigned int j = 0; j < ND.childNodes.size(); j++) {
        if (smallest == ND.childNodes[j].score) {
          for (int x = 0; x < 3; x++) {
            for (int y = 0; y < 3; y++) {
              for (int z = 0; z < 3; z++) {
                GB->playerBox[x][y][z] = ND.childNodes[j].G.playerBox[x][y][z];
                GB->winCondition[x][y][z] = ND.childNodes[j].G.winCondition[x][y][z];
              }
            }
          }
        }
      }
    }
  }
return 1;
}

Solution

  • Managed to get the answer to this by using a little bit of help from my tutor and some liberal use of for (auto &child : childNodes)

    Node Code

    void Node::addToNodeList(int turn) {
      for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
          //for (int k = 0; k < 3; k++) {
            if (G.playerBox[i][j][1] == 0) {
              G.playerBox[i][j][1] = turn;
              G.winCondition[i][j][1] = true;
    
              childNodes.push_back(Node(G));
    
              G.playerBox[i][j][1] = 0;
              G.winCondition[i][j][1] = false;
            }
          //}
        }
      }
    
    for (auto &child : childNodes) {
      child.addToNodeList(-turn);
    
      if (child.childNodes.empty()) {
        int tempScore = child.determineMinimaxValue();
    
        if (tempScore < score) {
          score = tempScore;
        }
      }
      else {
        if (turn == -1) {
          int smallest = 100000;
          for (unsigned int i = 0; i < this->childNodes.size(); i++) {
            smallest = std::min(smallest, this->childNodes[i].score);
          }
          score = smallest;
        }
        else {
          int biggest = -100000;
          for (unsigned int i = 0; i < this->childNodes.size(); i++) {
            biggest = std::max(biggest, this->childNodes[i].score);
          }
        score = biggest;
        }
      }
    }
    
    int Node::determineMinimaxValue() {
      if (G.getWinner() == 0) {
        return 0;
      }
      else if (G.getWinner() == -1) {
        return -1;
      }
      else if (G.getWinner() == 1) {
        return 1;
      }
      else {
        return 0;
      }
    }
    

    AI Player Code

    Node AIPlayer::minimaxMove(int player, GameBoard GB) {
      G = GB;
      N = Node(G); 
      N.addToNodeList(player);
      return N;
    }
    
    int AIPlayer::makeTheMove(GameBoard *GB, Node ND) {
      if (GB->getWinner() == 1 || GB->getWinner() == -1) {
        return 1;
      }
    
      int smallest = 100000;
    
      for (unsigned int i = 0; i < ND.childNodes.size(); i++) {
        smallest = std::min(smallest, ND.childNodes[i].score);
      }
    
      for (unsigned int j = 0; j < ND.childNodes.size(); j++) {
        if (smallest == ND.childNodes[j].score) {
          for (int x = 0; x < 3; x++) {
            for (int y = 0; y < 3; y++) {
              for (int z = 0; z < 3; z++) {
                GB->playerBox[x][y][z] = ND.childNodes[j].G.playerBox[x][y][z];
                GB->winCondition[x][y][z] = ND.childNodes[j].G.winCondition[x][y][z];
              }
            }
          }
        }
      }
      return 1;
    }