Tictactoe 100

Hello!

I have a question relating to my tictactoe game: I want to make a game where player plays against computer. Here is my issue:



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
void al_move()           // Definition of al_move        
{
x = rand() % 3;
y = rand() % 3;

make_move(x,y)
}

int play()
{

....  Lots of code

enum turn { Player=0,Al=1 }; 

for  (int turns=0; turns<9; ++turns)

{

int turn (int Player, int Al)

{ return (turn=0)? 1:0; }

}

if (turn=1) { return void al_move }

else { ...Ask player for making a move, read move }




Does this work?

Note: make_move is defined somewhere else but that doesn`t relate to my question
Last edited on
Does this work?

Erm... have you actually tried it?

lines 22 and 26 you're using '=' instead of '=='.

The general idea looks fine. Implement and see for your self it it works.
Damn, I have worked on this ***** almost for a half week 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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

#include <iostream>
#include <bitset>

namespace tictactoe
{

   void board()

   {

    constexpr size_t N = 3 ;
    using board = bitset<N*N> ;

   }
   
   
   
   
   
   const board wins[] = {
                             board("111000000"), board("000111000"), board("000000111"), // rows
                             board("100100100"), board("010010010"), board("001001001"), // cols
                             board("100010001"), board("001010100") // diagonals
                         } ;

    board noughts ; // bit board for first player
    board crosses ; // bit board for second player
    
    
    constexpr char NOUGHT = 'O' ;
    constexpr char CROSS = 'X' ;
    
    
    
    bool curr_move_noughts = true ;

    bool free= false ;                  

    board combined() { return noughts | crosses ; } // combined bit board for both players
    
    bool valid( size_t row, size_t col ) { return row<N && col<N ; }
    size_t pos( size_t row, size_t col ) { return row*N + col ; } // map row, col to bit position

    bool occupied( size_t row, size_t col ) { return valid(row,col) && combined()[ pos(row,col) ] ; }
    bool free( size_t row, size_t col ) { return valid(row,col) && !occupied(row,col) ; }

    void make_move( size_t row, size_t col, board& b ) { if( free(row,col) ) b[ pos(row,col) ] = true ; }
    void make_move( size_t row, size_t col )
    { if(curr_move_noughts) make_move(row,col,noughts) ; else make_move(row,col,crosses) ; }

    bool is_win( board b )
    {
        for( board w : wins ) if( (w&b) == w ) return true ;
        return false ;
    }
    bool is_win() { return is_win( curr_move_noughts ? noughts : crosses ) ; }

   


  void play()
  {
 
player=(player%2)?1:2;                  // Al can be X or O
 
int row=-1;                             // For player input
int col=-1;
  
enum turn { Player=0, Al=1 }; 

for  (int turns=0; turns<9; ++turns)

{

int turn (int Player, int Al)

{ return (turn==0)? 1:0; }

}

if (turn==1) { return  al_move();  print_board();}

else { 

            cout << "Player  " << player << " Choose a move..." <<endl;
	    cout << "Enter row and col : ";
	    cin  >> row >> col;
	    
	    if (!valid)
		{
		cout << "Illegal move!  Try again...\n" << endl;
	        }
		   else
		        {
			   make_move(row,col);

			   print_board();

                         }
           
       }
			

  
  
  
   if ( is_win( board b ) | is_win() ) { cout<< "Player " << player << " has won!" <<

 endl } 

  else if (!free) { cout<< "Game ended in a tie!" << endl; } 
 
 else { print_board; }

 
 }                                                
  


    ostream& print_board( ostream& stm = cout, board b = combined() )
    {
        stm << "------\n" ;
        for( std::size_t i = 0 ; i < N ; ++i )
        {
            for( std::size_t j = 0 ; j < N ; ++j )
            {
                const size_t k = pos(i,j) ;
                if( b[k] ) stm << ( noughts[k] ? NOUGHT : CROSS ) << ' ' ;
                else stm << ". " ;
            }
            stm << '\n' ;
        }
        return stm << "------\n" ;
    }
  
}


 al_move()                    // Al move doesn`t take argument??

{

x =  rand() % 3;
y =  rand() % 3;

make_move(x,y);

}

int main()
{
    
    TicTacToe ttt;

    ttt.board();
    ttt.play();
    

}





Any help would be appreciated, thanks
Last edited on
Hey that's a pretty cool way to do it. I especially like the way you're using bitmap to check the winning boards. It's full of syntax errors and stuff but you've mostly got the logic correct.

The one logic problem I see is in a1_move(). What if the square chosen is already occupied?

I'm away from my dev computer so I can't offer much help right now. If this can wait until Saturday I can walk you through the rest of it then. It won't take long. if you want to do that, send me a private message so I don't forget about it.
David- Are you still there?

It would be cool to get this working, so that I can move on improving the Al. First implement

the negamax algorithm, and then move to alpha-beta pruning.

I agree that the current code itself is a bit messy, though I have an idea how to improve on it. Maybe use a function to return game status:

1 game is over with a result

-1 game is in progress

0 game is over and no result.

Well, the code itself does not compile. I haven`t had much time lately. No souurces were published yet.
Here is a huge gift: the code with syntax errors fixed. I'm using an older compiler so some I had to change 2 or 3 things also. In the future, compile the code often as you're writing it. The syntax errors will often point out errors in your logic.

This code still doesn't work. I've deliberately left in some of your bugs to fix. For example:
- What happens when the user enters invalid row/column numbers?
- What happens when someone wins the game?
- What happens when the computer selects an occupied square?
- The users have to enter numbers from 0-2. It would be more user friendly if they entered numbers 1-3.

You may want to combine a bunch of the functions and data into a Board class.


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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#include <iostream>
#include <bitset>
#include <cstdlib>

using std::cin;
using std::cout;
using std::ostream;
using std::endl;
using std::bitset;

const size_t N = 3;
typedef bitset <N*N> board;


const board wins[] = {
    board(0b111000000), board(0b000111000), board(0b000000111),	// rows
    board(0b100100100), board(0b010010010), board(0b001001001),	// cols
    board(0b100010001), board(0b001010100)	// diagonals
};

board noughts;			// bit board for first player
board crosses;			// bit board for second player


const char NOUGHT = 'O';
const char CROSS = 'X';

void al_move();
board combined();
ostream & print_board(ostream & stm = cout, board b = combined());

// This indicates the current player. Computer plays noughts.
bool curr_move_noughts = true;

board
combined()
{
    return noughts | crosses;
}				// combined bit board for both players

bool
valid(size_t row, size_t col)
{
    return row < N && col < N;
}

size_t
pos(size_t row, size_t col)
{
    return row * N + col;
}				// map row, col to bit position

bool
occupied(size_t row, size_t col)
{
    return valid(row, col) && combined()[pos(row, col)];
}

bool
free(size_t row, size_t col)
{
    return valid(row, col) && !occupied(row, col);
}

void
make_move(size_t row, size_t col, board & b)
{
    if (free(row, col))
	b[pos(row, col)] = true;
}

void
make_move(size_t row, size_t col)
{
    if (curr_move_noughts)
	make_move(row, col, noughts);
    else
	make_move(row, col, crosses);
}

bool
is_win(board b)
{
    for (int i = 0; i < sizeof(wins) / sizeof(wins[0]); ++i) {
	if ((wins[i] & b) == wins[i])
	    return true;
    }
    return false;
}

bool
is_win()
{
    return is_win(curr_move_noughts ? noughts : crosses);
}

// is the board full?
bool is_full(board b)
{
    static board fullboard(0b111111111);
    return b == fullboard;
}

bool is_full()
{
    return is_full(curr_move_noughts ? noughts : crosses);
}



void
play()
{

    int row = -1;		// For player input
    int col = -1;

    for (int turns = 0; turns < 9; ++turns) {
	curr_move_noughts = !curr_move_noughts;

	if (curr_move_noughts) {
	    al_move(); 		// computer plays noughts
	} else {
	    cout << "Choose a move..." << endl;
	    cout << "Enter row and col : ";
	    cin >> row >> col;
	    // dmh - note that valid rows/col are 0-2, not 1-3. If you
	    // want the user to enter 1-3 then decrement the value
	    // they enter right after they enter it
	    if (!valid(row,col)) {
		cout << "Illegal move!  Try again...\n" << endl;
	    } else {
		make_move(row, col);
	    }
	}
	print_board();
	if (is_win()) {
	    cout << "Player " << (curr_move_noughts ? NOUGHT : CROSS) << " has won!" << endl;
	} else if (is_full()) {
	    cout << "Game ended in a tie!" << endl;
	}
    }
}



ostream & print_board(ostream & stm, board b)
{
    stm << "------\n";
    for (std::size_t i = 0; i < N; ++i) {
	for (std::size_t j = 0; j < N; ++j) {
	    const size_t
		k = pos(i, j);
	    if (b[k])
		stm << (noughts[k] ? NOUGHT : CROSS) << ' ';
	    else
		stm << ". ";
	}
	stm << '\n';
    }
    return stm << "------\n";
}


void
al_move()			// Al move doesn`t take argument??
{

    int x = rand() % 3;
    int y = rand() % 3;

    make_move(x, y);

}

int
main()
{
    play();
    return 0;
}

Awesome!

Just a little note. It is not actually worth of defining full board because we have already defined what free() does.

Here is my (hopefully) improved version

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

#include <iostream>
#include <bitset>
#include <cstdlib>

namespace tictactoe

{
	
class board {


constexpr size_t N = 3 ;
using board = bitset<N*N> ;


const board wins[] = {
    board(0b111000000), board(0b000111000), board(0b000000111),	// rows
    board(0b100100100), board(0b010010010), board(0b001001001),	// cols
    board(0b100010001), board(0b001010100)	// diagonals
};

board noughts;			// bit board for first player
board crosses;			// bit board for second player


const char NOUGHT = 'O';
const char CROSS = 'X';


ostream & print_board(ostream & stm = cout, board b = combined());

// This indicates the current player. Computer plays noughts.
bool curr_move_noughts = true;

board
combined()
{
    return noughts | crosses;
}				// combined bit board for both players

bool
valid(size_t row, size_t col)
{
    return row < N && col < N;
}

size_t
pos(size_t row, size_t col)
{
    return row * N + col;
}				// map row, col to bit position

bool
occupied(size_t row, size_t col)
{
    return valid(row, col) && combined()[pos(row, col)];
}

bool
free(size_t row, size_t col)
{
    return valid(row, col) && !occupied(row, col);
}

void
make_move(size_t row, size_t col, board & b)
{
    if (free(row, col))
	b[pos(row, col)] = true;
}

void
make_move(size_t row, size_t col)
{
    if (curr_move_noughts)
	make_move(row, col, noughts);
    else
	make_move(row, col, crosses);
}

bool
is_win(board b)
{
    for (int i = 0; i < sizeof(wins) / sizeof(wins[0]); ++i) {
	if ((wins[i] & b) == wins[i])
	    return true;
    }
    return false;
}

bool
is_win()
{
    return is_win(curr_move_noughts ? noughts : crosses);
}


};                                                                    // End of class


void
play()
{

    int row = -1;		// For player input
    int col = -1;

    for (int turns = 0; turns < 9; ++turns) {
	curr_move_noughts = !curr_move_noughts;

	if (curr_move_noughts) {
	    al_move(); 		// computer plays noughts
	} else {
	    cout << "Choose a move..." << endl;
	    cout << "Enter row and col : ";
	    cin >>row-->>col--;
	   
	    if (!valid(row,col)) {
		cout << "Illegal move!  Try again...\n" << endl;
	    } else {
		make_move(row, col);
	    }
	}
	print_board();
	if (is_win()) {
	    cout << "Player " << (curr_move_noughts ? NOUGHT : CROSS) << " has won!" << endl; return 0;
	} else if (!free) {
	    cout << "Game ended in a tie!" << endl;
	}
    }
}



ostream & print_board(ostream & stm, board b)
{
    stm << "------\n";
    for (std::size_t i = 0; i < N; ++i) {
	for (std::size_t j = 0; j < N; ++j) {
	    const size_t
		k = pos(i, j);
	    if (b[k])
		stm << (noughts[k] ? NOUGHT : CROSS) << ' ';
	    else
		stm << ". ";
	}
	stm << '\n';
    }
    return stm << "------\n";
}


void
al_move()			
{

    if (curr_move_noughts)           // Al turn
{

  bool found=false;
 
  while(found=false)

{

  x= rand()%3
  y= rand()%3

if (board&b (x,y) != occupied

{  make_move (x,y)

  found = true;
  !curr_move_noughts;                   // Player turn

 }

}


}

int
main()
{
    play();
    return 0;
}

	


	



Last edited on
Topic archived. No new replies allowed.