Bool functions and Tic-Tac-Toe

Hey guys, I am writing a code to give the result of a tic-tac-toe game. Specifically, every possible board combination is read in as a .txt file where each line is a game ( for example, xooxoxxxo would mean x wins with three in a row down the first column). I made two boolean functions to determine whether x or o wins (I deal with ties, incomplete and invalid games later). I read in the .txt file as a vector of strings, each with 9 objects. In the bool functions I index into each string to determine the game result, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  bool xwin(string gameline) {
    	if (gameline[0 && 3 && 6] == x || gameline[1 && 4 && 7] == x || gameline [2 && 5 && 8] ==x) {
    		return true;}
    	if (gameline[0 && 4 && 8] == x || gameline[2 && 4 && 6]==x) {
    		return true;}
    	if (gameline[0 && 1 && 2] == x || gameline[3 && 4 && 5]==x || gameline[6 && 7 && 8]==x) {
    		return true;}
    	else
    		{return false;}
        }
  bool owin(string gameline) 
  {
    	if (gameline[0 && 3 && 6] == o || gameline[1 && 4 && 7] == o || gameline [2 && 5 && 8] == o) {
    		return true;}
    	if (gameline[0 && 4 && 8] == o || gameline[2 && 4 && 6] == o) {
    		return true;}
    	if (gameline[0 && 1 && 2] == o || gameline[3 && 4 && 5] == o || gameline[6 && 7 && 8]==o) {
    		return true;}
    	else
    		{return false;}
    }


However, the result is not always correct. For instance, it will say there is a tie or invalid game when x has won. I think the error is in the boolean functions. Firstly, can I put the && operator, or instead a comma, into the index? And is there a more succinct way to code the function?

Thanks for the help.
I think you need to be explicit:

(gameline[0] == 'o' && gameline[3] == 'o' && gameline[6]=='o')

Yes, you can condense the logic. If the middle is marked, there are 4 patterns that win, each with 2 additional cells. If not, there are 4 other patterns that win. There might be other ways but that leaps out right away.


you can also do weird stuff like sum of 3 == 3*'o'

Last edited on
Thank you @jonnin! That makes sense; I was just hoping I wouldn't have to type everything out that way but it looks like I do. I ended up getting rid of the bool functions and putting the conditions directly into int main, and got rid of separate conditions for an x win vs. an o win to condense the code a bit. However, I am still getting many of the boards wrong when I run the code. This assignment is meant to practice reading in a file, writing to it, and using vectors. Here is the code now:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
using std::cout;
using std::endl;
using std::cin;
using std::string;
using std::vector;
using std::getline;

string gameline;
 char space = '#'; //empty space
 char x = 'x';
 char o = 'o';
 char winner;

int main () {
   std::ifstream myfile;  

   vector <std::string> gamevec;

   myfile.open ("smallttt.txt");
   if (myfile.is_open())
   {  while (std::getline (myfile, gameline))
      {  gamevec.push_back(gameline);}
    myfile.close();
   }

   for (string gameline : gamevec) {
   	int countspace = 0;
   	int countx = 0;
   	int counto = 0;
      for (int i = 0 ; i < 9; i++) {
   	     if (gameline[i] == space) {
             countspace++;}
         if (gameline[i] == 'x') {
         	 countx++;}
         if (gameline[i] == 'o') {
             counto++;
         }
     }  //invalid based on number of x's and o's (x plays first, so we can't have more o's than x's)
      if (counto > countx || countx > counto + 1) {
             std::cout << gameline << "  " << "i" << std::endl;}
      else
      { //Ways that x or o can win: here, a '#' denotes an empty space.
         //vertical wins
      	if (gameline[0] == gameline[3] == gameline[6] != '#') {
    	    winner = gameline[0];
      	}
    	if (gameline[1] == gameline[4] == gameline[7] != '#') {
    		winner = gameline [1];
    	}
    	if (gameline [2] == gameline[5] == gameline[8] != '#') {
    		winner = gameline [2];
    	}
    	//diagonal wins
    	if (gameline[0] == gameline[4] == gameline[8] != '#') {
    		winner = gameline [0];
    	}
    	if (gameline[2] == gameline[4] == gameline[6] != '#') {
    		winner = gameline [2];
    	}
    	//horizontal wins
    	if (gameline[0] == gameline[1] == gameline[2] != '#') {
    		winner = gameline [0];
    	}
        if (gameline[3] == gameline[4] == gameline[5] != '#') {
        	winner = gameline [3];
        }
        if (gameline[6] == gameline[7] == gameline[8] != '#') {
        	winner = gameline [6];
        }
        //other cases, if invalid or if nobody wins (i denotes invalid)
        if ((winner == 'o' and counto != countx) || (winner == 'x' and counto == countx)) {
            std::cout << gameline << "  " << "i" << std::endl;
        }
        else if (winner == 'o' || winner == 'x')
        {
           std::cout << gameline << "  " << winner << std::endl;
        }
        //tie games: if game is finished and nobody won (winner does not equal x or o)
        else if (countspace == 0 and winner != 'o' and winner != 'x') { 
      	     std::cout << gameline << "  " << "t" << std::endl;
      	 }
         //games in progress: if nobody won yet, game isnt finished, and is valid game so far
      	else if (countspace != 0 and winner != 'o' and winner != 'x') { 
         	 std::cout << gameline << "  " << "c" << std::endl;}
       }
}
return 0;
}

For instance, it said the board xooooxxxx (read left to right going down the tic tac toe board) is invalid (way too many boards say invalid actually), even though it should print 'x', since x wins. Also, from the thousands of boards, 't' for tie is never printed. Is there any glaring or simple fixes I seem to be missing? Any tips are much appreciated.
if (gameline[0] == gameline[3] == gameline[6] != '#') {
Unfortunately this doesn't do what you think it does. The program evaluates gameline[0] == gameline[3], which is a bool (true or false). Then it takes that bool and compares it to gameline[6]. Since you can't compare bool to char, it converts gameline[6] to bool by saying that ASCII character 0 is false and everything else is true. Now the comparison is way of the rails from what you wanted.

You want something like this:
if (gameline[0] != '#' && gameline[3] != '#' && gameline[6] != '#') {

Repeating all that code a zillion times is messy and error prone so why not create a function to do the work for you:

1
2
3
4
5
6
7
8
9
10
// given 3 indexes into gameline, check for a winner in those squares. If there is winner, return true and set "winner" to indicate who won ('x' or 'o').  Otherwise return false
bool haveWinner(idx1, idx2, idx3, char &winner) {
    if (gameline[idx1] == gameline[idx2] && gameline[idx2] == gameline[idx3]) {
        if (gameline[idx] != space) {
            winner = gameline[idx1];
            return true;
        }
    }
    return false;
}

Now lines 48-73 can be something like:
1
2
3
4
bool foundWinner = getWinner(0,3,6,winner) || getWinner(1,4,7,winner) ||
    getWinner(2,5,8,winner) || getWinner(0,4,8,winner) ||
    getWinner(2,4,6,winner) || getWinner(0,1,2,winner) ||
    getWinner(3,4,5,winner) || getWinner(6,7,8,winner);



Oooooh I see. I didn't really think about how the == comparison works. The index ints in a bool function is a great idea, thank you dhayden!

When calling the function, shouldn't it be haveWinner instead of getWinner? (why is the name different, is that a typo?); I changed the name, declared the integers and it only output i or c. So I changed it a bit more and not it only outputs i for every possible game. This is my code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//Copyright 2017 Hayley Walker hayleyjw@bu.edu
//C++ program w4tttanalyse.cpp that reads tic-tac-toe scenarios from a file and determines the status of each game.
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
using std::cout;
using std::endl;
using std::cin;
using std::string;
using std::vector;
using std::getline;

string gameline;
 char space = '#'; //empty space
 char x = 'x';
 char o = 'o';
 char winner;
 int wincount = 0;
 int idx1;
 int idx2;
 int idx3;

//check for a winner given 3 indices. If a winner exists, return true 
//and set "winner" to the winning char.  Otherwise return false.
bool getWinner(int idx1, int idx2, int idx3, char winner) {
    if (gameline[idx1] == gameline[idx2] && gameline[idx2] == gameline[idx3]) {
        if (gameline[idx1] != space) {
            winner = gameline[idx1];
            wincount++;
            return true;
        }
    }
    return false;
}

int main () {
   std::ifstream myfile;  

   vector <std::string> gamevec;

   myfile.open ("tictactoeboards.txt");
   if (myfile.is_open())
   {  while (std::getline (myfile, gameline))
      {  gamevec.push_back(gameline);}
    myfile.close();
   }
   for (string gameline : gamevec) {
   	int countspace = 0;
   	int countx = 0;
   	int counto = 0;
      for (int i = 0 ; i < 9; i++) {
   	     if (gameline[i] == space) {
             countspace++;}
         if (gameline[i] == 'x') {
         	 countx++;}
         if (gameline[i] == 'o') {
             counto++;
         }

     }  //invalid based on number of x's and o's (x plays first, so we can't have more o's than x's)
      if (counto > countx || countx > counto + 1) {
             std::cout << gameline << "  " << "i" << std::endl;}

      else
      { //Ways that x or o can win
        if (getWinner(0,3,6,winner) || getWinner(1,4,7,winner) ||
           getWinner(2,5,8,winner) || getWinner(0,4,8,winner) ||
           getWinner(2,4,6,winner) || getWinner(0,1,2,winner) ||
           getWinner(3,4,5,winner) || getWinner(6,7,8,winner)) {
           std::cout << gameline << "  " << winner << std::endl;
           }
      	

        //other cases, if invalid or if nobody wins (i denotes invalid)
        else if ((winner == 'o' and counto != countx) || (winner == 'x' and counto == countx)) {
            std::cout << gameline << "  " << "i" << std::endl;
        }
    
        //tie games: if game is finished and nobody won (winner does not equal x or o)
        else if (countspace == 0 and getWinner == false) { 
      	     std::cout << gameline << "  " << "t" << std::endl;
      	 }
         //games in progress: if nobody won yet and game isnt finished, and its a valid game so far
      	else if (countspace != 0 and getWinner == false) { 
         	 std::cout << gameline << "  " << "c" << std::endl;}
       }
}
return 0;
}

I'm not sure why it's stuck on i (invalid), as the if statements for i don't seem to encompass everything (unless the loop is exiting after that if statement?). Thanks for the help.
Line 76: If getWinner() returned false for all calls at lines 67-70 then winner was never set. Actually I think you want the logic of lines 76-78 to run when there is a winner.

Lines 81 & 85: getWinner == false doesn't do what you think. It checks whether the address of the getWinner() function is zero. It never is. It seems that you want to run this code when there is no winner.

I think this is what you want. Without the input file I can't be sure.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
        if (counto > countx || countx > counto + 1) {
            // invalid based on number of x's and o's (x plays first,
            // so we can't have more o's than x's)
            std::cout << gameline << "  " << "i" << std::endl;
        } else if (getWinner(0, 3, 6, winner) || getWinner(1, 4, 7, winner) ||
                   getWinner(2, 5, 8, winner) || getWinner(0, 4, 8, winner) ||
                   getWinner(2, 4, 6, winner) || getWinner(0, 1, 2, winner) ||
                   getWinner(3, 4, 5, winner) || getWinner(6, 7, 8, winner)) {
            // We have 3 in a row
            if ((winner == 'o' and counto != countx)
                || (winner == 'x' and counto == countx)) {
                // Wrong number of moves. It's invalid
                std::cout << gameline << "  " << "i" << std::endl;
            } else {
                // It's a winner!
                std::cout << gameline << "  " << winner << std::endl;
            }
        } else { // No 3 in a row
            if (countspace == 0) {
                // The board is full - tie game
                std::cout << gameline << "  " << "t" << std::endl;

            } else /* if (countspace != 0) */ {
                // The game is in progress
                std::cout << gameline << "  " << "c" << std::endl;
            }
        }

Topic archived. No new replies allowed.