I am trying to make a reverse 4x3 tic tac toe game in c++ using a depth first search and minimax. I keep getting a segmentation fault whenever the tree is trying to get the minimax value and I'm not sure why. I've looked at my logic a bunch of times and I just can't seem to see the problem. I don't think it is creating the tree correctly somehow.
My idea for depth first was that it would look at the parent node, if it didn't have all its children it would create the next child node, copy the parent board to the child board, then for each child in childNum array it would skip as many blank spaces as the number child it is, then put an X or an O accordingly on the board. Then if it was not an end state it would send that childnode to getMove() Once it got to an end state, it would store that value in the parent node and send the parent node to getMove() and work its way back up the tree.
Can anyone see what is causing this segmentation fault or any other errors I have in my code? Here it is:
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <vector>
#include <ctype.h>
#include <iostream>
using namespace std;
struct node_t{
node_t* parent;
int minimax;
int childrenVals[12];
bool isMax;
char board[4][3];
int level;
bool xmove;
node_t* child;
};
void getMove(node_t *root);
void printBoard(char board[4][3]);
int checkStatus(char board[4][3]);
int getChildNum( int minimax[12]);
int status = -2;
char myBoard[4][3];
int main(){
//create root node
node_t n;
node_t* root;
root = &n;
root->board[0][0] = '1';
root->board[0][1] = '2';
root->board[0][2] = '3';
root->board[1][0] = '4';
root->board[1][1] = '5';
root->board[1][2] = '6';
root->board[2][0] = '7';
root->board[2][1] = '8';
root->board[2][2] = '9';
root->board[3][0] = '/';
root->board[3][1] = '*';
root->board[3][2] = '-';
root->parent = NULL;
root->child = NULL;
root->isMax = true;
root->xmove = true;
root-> level = 0;
for(int i = 0; i < 12; i++){
root->childrenVals[i] = -2;
}
for(int i = 0; i < 4; i++){
for(int j = 0; j < 3; j++){
myBoard[i][j] = root->board[i][j];
}
}
cout<<"Welcome to Reverse Tic Tac Toe! Here is the board:"<<endl;
printBoard(root->board);
cout<<"To make a move, you will type in the number or symbol "<<endl;
cout<<"of the box you would like to place your piece in. "<<endl;
cout<<endl;
cout<<"Would you like to go first or second? (type 1 or 2):"<<endl;
char order;
char Omove;
bool isValid = false;
bool gameOver = false;
int gameValue;
//Get order
cin >> order;
while(order !='1' && order != '2'){
if(order != '1' && order != '2'){
cout<<"Please type a 1 if you would like to go first and a 2 if you
would like to go second:";
cin >> order;
}
else{
break;
}
}
if(order == '2'){
root->xmove = true;
getMove(root);
//root->board = myBoard;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 3; j++){
root->board[i][j] = myBoard[i][j];
}
}
root->level++;
printBoard(root->board);
}
while(gameOver == false){
cout<< "Your move:";
while(isValid == false){
cin >> Omove;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 3; j++){
if(root->board[i][j] == Omove && Omove != 'X' && Omove != 'O
'){
root->board[i][j] = 'O';
isValid = true;
}
}
}
if(isValid == false){
cout<<"Invalid move, please look at the board and try again:";
}
}
root->level++;
printBoard(root->board);
gameValue = checkStatus(root->board);
if(gameValue != -2){
gameOver = true;
}
else{
getMove(root);
for(int i = 0; i < 4; i++){
for(int j = 0; j < 3; j++){
root->board[i][j] = myBoard[i][j];
}
}
root->level++;
root->isMax = true;
printBoard(root->board);
if(gameValue != -2){
gameOver = true;
}
}
}
if(gameOver == 1){
cout<<"You lose!"<<endl;
}
else if(gameOver == -1){
cout<<"You win!"<<endl;
}
else if(gameOver == 0){
cout<<"It's a tie!"<<endl;
}
return 0;
}
void getMove(node_t *node){
status = checkStatus(node->board);
//if node is not an end state
if(status == -2){
int childNum = 0;
for(int i = 0; i < 12 - node->level; i++){
if(node->childrenVals[i] == 0 || node->childrenVals[i] == 1 || node-
>childrenVals[i] == -1){
childNum++;
}
}
//if node does not have all its children
if(childNum != 12 - node->level){
if(childNum > 0){
//delete current child pointer
delete node->child;
}
//create child Node
node_t* child = new node_t;
child->parent = node;
node->child = child;
child->level = node->level + 1;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 3; j++){
child->board[i][j] = node->board[i][j];
}
}
for(int i = 0; i < 12; i++){
child->childrenVals[i]= -2;
}
if(node->isMax == true){
child->isMax = false;
}
else{
child->isMax = true;
}
//xmove means the children of that node move for X
if(node->xmove == true){
child->xmove = false;
}
else{
child->xmove = true;
}
int openSpace = 0;
bool shouldbreak = false;
for(int i=0; i < 4; i++){
for(int j=0; j< 3; j++){
if(child->board[i][j] != 'X' && child->board[i][j] != 'O'){
if(openSpace == childNum){
if(node->xmove){
child->board[i][j] = 'X';
shouldbreak = true;
break;
}
else{
child->board[i][j] = 'O';
shouldbreak = true;
break;
}
}
else{
openSpace++;
}
}
if(shouldbreak == true){
break;
}
}
if(openSpace == childNum){
break;
}
}
// cout<<"Level: "<<node->level<<endl;
// printBoard(child->board);
// cout<<endl;
getMove(child);
}
//if node does have all its children
else{
//cout<<"Level: "<<node->level<<endl;
// printBoard(node->board);
//n is used for the case that it is back to the root node
int n;
if(node->isMax == true){
for(int i = 0; i < 12 - node->level; i++){
if(node->childrenVals[i] > node->minimax){
node->minimax = node->childrenVals[i];
n = i;
}
}
}
else{
for(int i = 0; i < 12 - node->level; i++){
if(node->childrenVals[i] < node->minimax){
node->minimax = node->childrenVals[i];
n = i;
}
}
}
//if not at root node
if(node->parent != NULL){
int next = getChildNum(node->parent->childrenVals);
node->parent->childrenVals[next] = node->minimax;
node_t* temp = node->parent;
node->parent = NULL;
getMove(temp);
}
//if at root node
else{
//get move from root by recreating nth child
int openSpace = 0;
for(int i=0; i < 4; i++){
for(int j=0; j< 3; j++){
if(node->board[i][j] == 'X' || node->board[i][j] == 'O'){
//go to next space
}
else{
if(openSpace == n){
node->board[i][j] = 'X';
break;
}
else{
openSpace++;
}
}
//may have to do a conditional break here
}
}
//set myBoard = this board
for(int i = 0; i < 4; i++){
for(int j = 0; j < 3; j++){
myBoard[i][j] = node->board[i][j];
}
}
return;
}
}
}
//if node is an end state
else{
if(node->parent != NULL){
int next = getChildNum(node->parent->childrenVals);
node->parent->childrenVals[next] = status;
getMove(node->parent);
}
}
}
int checkStatus(char board[4][3]){
//returns -2 for no win loss or draw
//returns 1 for win
//returns -1 for loss
//returns 0 for draw
if( (board[0][0] == 'X' && board[1][1] == 'X' && board[2][2] == 'X')||
(board[0][0] == 'X' && board[0][1] == 'X' && board[0][2] == 'X')||
(board[0][0] == 'X' && board[1][0] == 'X' && board[2][0] == 'X')||
(board[0][1] == 'X' && board[1][1] == 'X' && board[2][1] == 'X')||
(board[0][2] == 'X' && board[1][2] == 'X' && board[2][2] == 'X')||
(board[0][2] == 'X' && board[1][1] == 'X' && board[2][0] == 'X')||
(board[1][0] == 'X' && board[2][1] == 'X' && board[3][2] == 'X')||
(board[1][0] == 'X' && board[1][1] == 'X' && board[1][2] == 'X')||
(board[1][0] == 'X' && board[2][0] == 'X' && board[3][0] == 'X')||
(board[1][1] == 'X' && board[2][1] == 'X' && board[3][1] == 'X')||
(board[1][2] == 'X' && board[2][2] == 'X' && board[3][2] == 'X')||
(board[1][2] == 'X' && board[2][1] == 'X' && board[3][0] == 'X')||
(board[2][0] == 'X' && board[2][1] == 'X' && board[2][2] == 'X')||
(board[3][0] == 'X' && board[3][1] == 'X' && board[3][2] == 'X')){
return -1;
}
if( (board[0][0] == 'O' && board[1][1] == 'O' && board[2][2] == 'O')||
(board[0][0] == 'O' && board[0][1] == 'O' && board[0][2] == 'O')||
(board[0][0] == 'O' && board[1][0] == 'O' && board[2][0] == 'O')||
(board[0][1] == 'O' && board[1][1] == 'O' && board[2][1] == 'O')||
(board[0][2] == 'O' && board[1][2] == 'O' && board[2][2] == 'O')||
(board[0][2] == 'O' && board[1][1] == 'O' && board[2][0] == 'O')||
(board[1][0] == 'O' && board[2][1] == 'O' && board[3][2] == 'O')||
(board[1][0] == 'O' && board[1][1] == 'O' && board[1][2] == 'O')||
(board[1][0] == 'O' && board[2][0] == 'O' && board[3][0] == 'O')||
(board[1][1] == 'O' && board[2][1] == 'O' && board[3][1] == 'O')||
(board[1][2] == 'O' && board[2][2] == 'O' && board[3][2] == 'O')||
(board[1][2] == 'O' && board[2][1] == 'O' && board[3][0] == 'O')||
(board[2][0] == 'O' && board[2][1] == 'O' && board[2][2] == 'O')||
(board[3][0] == 'O' && board[3][1] == 'O' && board[3][2] == 'O')){
return 1;
}
//if it has not returned a win or a loss and the board is full return draw
bool isFull = true;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 3; j++){
if(board[i][j] != 'X' && board[i][j] != 'O'){
isFull = false;
}
}
}
if(isFull == true){
return 0;
}
else{
return -2;
}
}
int getChildNum( int childrenVals[12]){
//return index to know which child needs to be created next
for(int i = 0; i < 12; i++){
if(childrenVals[i] == -2){
return i;
}
}
return -1;
}
void printBoard(char board[4][3]){
for(int i = 0; i < 4; i++){
cout<<"|"<<board[i][0]<<"|";
cout<<"|"<<board[i][1]<<"|";
cout<<board[i][2]<<"|"<<endl;
}
cout<<endl;
}