Hello,

I've recently, inadvertently, found incentive to write my own Tic-Tac-Toe game. I've been working on it for more-or-less 15 minutes and would like some help or a point in the right direction. It is almost finished but there is a function, namely the "_scan4Winner" function, that I must write an algorithm or come up with a formula to check for a winning condition.

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
 
using namespace std;
 
#define NWIN_CON 0
#define PONE_WIN 1
#define PTWO_WIN 2
#define GRST 2
 
const char playerOne = 'X';
const char playerTwo = 'O';
 
int _writeGrid( int y, int x, int flag );
int _scan4Winner(char (*grid)[3]);
int _playerWin(int winner);
 
int main()
{
    char setLocation;
    /* char hexc = 0x41; */
    int randomee, turn, ret;
    int px = 0, py = 0;
 
    srand(time(NULL)); // Seed for random
 
    // This will put a random number between 1 and 10 which will
    // determine what player initially goes first
    randomee = rand() % 10 + 1;
 
    // Which player goes first
    turn = (randomee >= 5) ? 1 : 0;
 
    // Indicate which user goes first
    cout << "Player " << turn << "is first" << endl;
 
     // This will print a fresh new grid
    _writeGrid(NULL, NULL, GRST);
 
    // Start the actual game
    while (1)
    {
        cout << "Player " << turn << " set: ";
        cin  >> setLocation;
 
        switch (setLocation)
        {
            case 0x41 :
                px = 0;
                py = 0;
                break;
            case 0x42 :
                px = 1;
                py = 0;
                break;
            case 0x43 :
                px = 2;
                py = 0;
                break;
            case 0x44 :
                px = 0;
                py = 1;
                break;
            case 0x45 :
                px = 1;
                py = 1;
                break;
            case 0x46 :
                px = 2;
                py = 1;
                break;
            case 0x47 :
                px = 0;
                py = 2;
                break;
            case 0x48 :
                px = 1;
                py = 2;
                break;
            case 0x49 :
                px = 2;
                py = 2;
                break;
        }
 
       /*************************************
        while (hexc != 0x49)
        {
            if (setLocation == hexc)
            {
               ret = _writeGrid(py, px, turn);
               hexc = 0x41;
               px = 0;
               py = 0;
               break;
            }
            hexc++;
            if (px < 2)
                px++;
            else
            {
                px = 0;
                py++;
            }
        }
       ****************************************/
 
        // Once the coords are determined pass them and which player is 
        // going, return and check if that player won.
        ret = _writeGrid(px, py, turn);
 
        if (ret == PONE_WIN)
        {
            turn = 0;
            _writeGrid(NULL, NULL, GRST);
        }
        else if (ret == PTWO_WIN)
        {
            turn = 1;
            _writeGrid(NULL, NULL, GRST);
        }
        else
            turn = (turn) ? 0 : 1; // If no one won yet switch players
    }
 
 return 0;
}
 
// This function writes a 3 x 3 tic-tac-toe grid and adds an X or a O 
// depending on the current user, and also takes a grid corrdinates 
// that'll determine the position or the X or O before it prints the grid
int _writeGrid(int y, int x, int flag)
{
    static char gridMap[3][3]; // Make static so grid is remembered
    int i, j, winner;
    char hexc = 0x41;
 
    // If the flag is a reset flag, reset the whole grid
    if (flag == GRST)
    {
        for (i=0;i<3;i++)
        {
            for (j=0;j<3;j++)
            {
                gridMap[i][j] = hexc;
                hexc++;
                cout << "|  " << gridMap[i][j];
            }
            cout << endl << endl;
        }
        return GRST;
    }
    else
    {
        // Condition flag and place either an X(playerOne) or O 
        // (playerTwo) into the coords passed as parameters
        gridMap[x][y] = (flag) ? playerOne : playerTwo;
        system("cls");
 
        // This section will actually write the grid using the values
        // stored in gridMap
        for (i=0;i<3;i++)
        {
            for (j=0;j<3;j++)
            {
                cout << "|  " << gridMap[i][j];
            }
            cout << endl << endl;
        }
 
        // The next step is to check for a winner so we pass gridMap 
        // containing the actual grid status winner = _scan4Winner   
        // (gridMap) If a winning condition is encountered notify the 
        // player who wins, add one point to his current score and reset   
        // the grid.
        if (winner)
            return _playerWin((flag) ? PONE_WIN : PTWO_WIN);
 
        return NWIN_CON;
    }
}
 
// This function will have a formula or algorition that'll check for
// winning conditions.
int _scan4Winner(char (*grid)[3])
{
    return 0;
}
 
// This function simply prints whose the winner of a tic-tac-toe game 
// and increments a counter variable for the corresponding user
int _playerWin(int winner)
{
    static int pcount1;
    static int pcount2;
    if (winner == PONE_WIN)
    {
        pcount1++;
        cout << "Player one wins -  score is" << pcount1 << endl;
        return PONE_WIN;
    }
    else
    {
        pcount2++;
        cout << "Player two wins -  score is" << pcount2 << endl;
        return PTWO_WIN;
    }
}

Thanks in advanced, LamaBot

Easiest is to pass in the position of the last move then check each direction from that location for 3 in a row.

Easiest is to pass in the position of the last move then check each direction from that location for 3 in a row.

Awesome Waltp,

I didn't do exactly what you'd said but you gave me an idea. I figured that'd I check on every move and just pass the whole array so that even if the player were to move in the middle of a given check it'll still work without writing extra code to detect where to scan relative to the most current position. Wicked cool! Thanks again Waltp. Also, since you're a Moderator, perhaps you can point me in the right direction where I can upload some tutorials or something? If not, its cool, I'll find it sooner or later. Last, in the previously posted code, I accidently rendered a statement as a comment, namely the "winner = _scan4Winner(gridMap)"; I was trying to make it look pretty and I messed it up I guess. :o Here is the code for the _scan4Winner function and all affiliated functions:

// This function writes a 3 x 3 tic-tac-toe grid and adds an X or a O 
// depending on the current user, and also takes a grid corrdinates 
// that'll determine the position or the X or O before it prints the grid
int _writeGrid(int y, int x, int flag)
{
    static char gridMap[3][3]; // Make static so grid is remembered
    int i, j, winner;
    char hexc = 0x41;
 
    // If the flag is a reset flag, reset the whole grid
    if (flag == GRST)
    {
        for (i=0;i<3;i++)
        {
            for (j=0;j<3;j++)
            {
                gridMap[i][j] = hexc;
                hexc++;
                cout << "   " << gridMap[i][j];
            }
            cout << endl << endl;
        }
        return SUCCESS;
    }
    else
    {
        system("cls");
 
        // Condition flag and place either an X(playerOne) or O
        //(playerTwo) into the coords passed as parameters
       if (gridMap[x][y] != 'X' && gridMap[x][y] != 'O')
            gridMap[x][y] = (flag) ? playerOne : playerTwo;
        else
            cout << "That position is already occupied" << endl << endl;
       if (_checkFull(gridMap))
            return GRST;
 
 
        // This section will actually write the grid using the values
        // stored in gridMap
        for (i=0;i<3;i++)
        {
            for (j=0;j<3;j++)
                cout << "   " << gridMap[i][j];
            cout << endl << endl;
        }
 
        // The next step is to check for a winner so we pass gridMap 
        // which contains the actually grid status. Note, this is the 
        // statement that got accidently included with the comments 
        // of the first posted code
         winner = _scan4Winner(gridMap); 
 
         // If a winning condition is encountered notify the player who 
         //wins, add one point to his current score and reset the grid
        if (winner)
            return _playerWin(winner);
 
        return NWIN_CON;
    }
}
 
// This function will have a formula or algorition that'll check for
// winning conditions.
int _scan4Winner(char (*grid)[3])
{
    if (_checkDiagnol(grid, 'X') || _checkAcross(grid, 'X') || _checkDown(grid, 'X'))
        return PONE_WIN;
    else if (_checkDiagnol(grid, 'O') || _checkAcross(grid, 'O') || _checkDown(grid, 'O'))
        return PTWO_WIN;
 
    return NWIN_CON;
}
 
// This function simply prints whose the winner of a tic-tac-toe game 
// and increments a counter variable for the corresponding user
int _playerWin(int winner)
{
    static int pcount1;
    static int pcount2;
 
    if (winner == PONE_WIN)
    {
        pcount1++;
        cout << "Player one wins - win score is " << pcount1 << endl;
        return PONE_WIN;
    }
    else
    {
        pcount2++;
        cout << "Player two wins - win score is " << pcount2 << endl;
        return PTWO_WIN;
    }
}
 
// Check both diagnol directions
bool _checkDiagnol(char (*grid)[3], char player)
{
    int i, j, counter1 , counter2;
    j = counter1 = counter2 = 0;
 
    for (i=0;i<3;i++)
    {
        if (grid[j][i] == player)
            counter1++;
        j++;
    }
    j=0;
    for (i=2;i>=0;i--)
    {
        if (grid[j][i] == player)
            counter2++;
        j++;
    }
    return (counter1 == 3 || counter2 == 3) ? true : false;
}
 
// Across down the Y axis
bool _checkAcross(char (*grid)[3], char player)
{
    int i, j, counter;
    counter = 0;
 
    for (i=0;i<3;i++)
    {
       for (j=0;j<3;j++)
       {
           if (grid[i][j] == player)
                counter++;
       }
       if (counter >= 3)
            return true;
       else
            counter = 0;
    }
    return false;
}
 
// Check down across the X axis
bool _checkDown(char (*grid)[3], char player)
{
    int i, j, counter;
    counter = j = 0;
    for (i=0;i<3;i++)
    {
        for (j=0;j<3;j++)
        {
            if (grid[j][i] == player)
                counter++;
        }
        if (counter == 3)
             return true;
        else
            counter = 0;
    }
    return false;
}
 
// Check for a full grid
static bool _checkFull(char (*grid)[3])
{
    int i, j;
 
    for (i=0;i<3;i++)
    {
        for (j=0;j<3;j++)
        {
            if (grid[i][j] != 'X' && grid[i][j] != 'O')
                return 0;
        }
    }
    return 1;
}

I'll probably need to modify the code, but it works fine for right now. Any suggestions will be much appreciated.

Thanks a lot, LamaBot

With this solution you are checking 3 colums, 3 rows, and 2 diagonals. IOW 8 tests.
If you pass in the last move, you can check 1 row, 1 col, and 0, 1, or 2 diagonals. For this, a maximum of 4 tests. To check to see if a diagonal has to be tested,

if both indexes are 1, both diagonals must be tested (center square)
else
if either index is 1 there are no diagonals (side square)
else
test one diagonal (corner square)

With this solution you are checking 3 colums, 3 rows, and 2 diagonals. IOW 8 tests.
If you pass in the last move, you can check 1 row, 1 col, and 0, 1, or 2 diagonals. For this, a maximum of 4 tests. To check to see if a diagonal has to be tested,

if both indexes are 1, both diagonals must be tested (center square)
else
if either index is 1 there are no diagonals (side square)
else
test one diagonal (corner square)

Hey Waltp,

Sorry I havn't replied sooner, but I've had work today :sad:
I understood what you suggestion, and it works. I kind of re-wrote the program, created classes for my nueral network (3 layer-forward model) and started to write some activations functions; as to see which one seems fit during training, and yes, I am going to incorporate an AI don't ask why:lol: Some of the programs original functionality doesn't work, in particular the "_endGame(void)" function will not end the game but simply allows you to view a winners score before resetting the grid. I'll change this laters as I develope my AI, which I might need some help, hopefully you, now-and-then???? Anyway, thanks for the suggestion; To think, I've got experienced with AI yet I can't deviate, figure and reckon that 8 tests would be better than 4!!!! Need to stop plumbing and start programming more again. Enough babbling, here is the code:

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
 
using namespace std;
 
#define PONE_WIN 0
#define PTWO_WIN 1
#define GREG 1
#define GRST 2
 
const char player[] = "XO";
char grid[3][3];
void _writeGrid(int flag);
void _playerWin(int winner);
 
inline bool _checkDiagnolDecline(char player) {
    return (grid[1][1]==player && grid[2][2]==player && grid[0][0]==player) ? true : false;
}
inline bool _checkDiagnolIncline(char player) {
    return (grid[2][0]==player && grid[0][2]==player && grid[1][1]==player) ? true : false;
}
inline bool _checkDown(int x, char player) {
        return (grid[0][x]==player && grid[1][x]==player && grid[2][x]==player) ? true : false;
}
inline bool _checkAcross(int y, char player) {
        return (grid[y][0]==player && grid[y][1]==player && grid[y][2]==player) ? true : false;
}
 
static inline int _endGame(void) {
    char endGame;
    cout << "Would you like to play another game? y / n ";
    cin >> endGame;
    if (endGame == 'y' || endGame == 'Y')
        return 0;
    return 1;
}
 
static bool _checkFull(void);
 
int main() {
    char setLocation, endGame;
    char hexc = 0x41;
    int randomee, turn, ret;
    int px = 0, py = 0;
 
    srand(time(NULL)); // Seed for random
    randomee = rand() % 10 + 1;
    turn = (randomee >= 5) ? 1 : 0;
    cout << "Player " << turn << " is first" << endl << endl;
    _writeGrid(GRST);
 
    while (1) {
        cout << "Player " << turn << " set: ";
        cin  >> setLocation;
        switch (setLocation) {
            case 0x41 :
                px = 0;
                py = 0;
                break;
            case 0x42 :
                px = 1;
                py = 0;
                break;
            case 0x43 :
                px = 2;
                py = 0;
                break;
            case 0x44 :
                px = 0;
                py = 1;
                break;
            case 0x45 :
                px = 1;
                py = 1;
                break;
            case 0x46 :
                px = 2;
                py = 1;
                break;
            case 0x47 :
                px = 0;
                py = 2;
                break;
            case 0x48 :
                px = 1;
                py = 2;
                break;
            case 0x49 :
                px = 2;
                py = 2;
                break;
        }
        if (grid[py][px] == player[0] || grid[py][px] == player[1])
            cout << "Position is occupied" << endl;
        else {
            grid[py][px] = player[turn];
 
            if (_checkFull()) {
            } else {
                if (py==1 && px==1) {
                    if (_checkDiagnolIncline(player[turn]) || _checkDiagnolDecline(player[turn]))
                        _playerWin(turn);
                } else if (!py && !px || py==2 && px==2) {
                    if (_checkDiagnolDecline(player[turn]))
                        _playerWin(turn);
                } else if (py==2 && px==0 || py==0 && px==2) {
                    if (_checkDiagnolIncline(player[turn]))
                        _playerWin(turn);
                }
 
                if (_checkAcross(py, player[turn]))
                    _playerWin(turn);
                if (_checkDown(px, player[turn]))
                    _playerWin(turn);
 
                _writeGrid(GREG);
                turn = (turn) ? 0 : 1;
            }
        }
    }
    return 0;
}
 
void _writeGrid(int flag) {
    int i, j;
    char hexc = 0x41;
 
    if (flag == GRST) {
        for (i=0;i<3;i++) {
            for (j=0;j<3;j++) {
                grid[i][j] = hexc;
                hexc++;
                cout << "   " << grid[i][j];
            }
            cout << endl << endl;
        }
    } else {
        system("cls");
            for (i=0;i<3;i++) {
            for (j=0;j<3;j++)
                cout << "   " << grid[i][j];
            cout << endl << endl;
        }
    }
}

Does that look right? I'm still open for suggestions and I've got some ideas of my own I'll post later on, but I'm freaking tired.

Thanks in advance, LamaBot

Sorry I havn't replied sooner, but I've had work today :sad:

No problem. It's not like I was waiting by the computer just for you :mrgreen:

Anyway, thanks for the suggestion; To think, I've got experienced with AI yet I can't deviate, figure and reckon that 8 tests would be better than 4!!!!

It all comes with more time and more experience. I remember one program I wrote with multiple nested for loops (15 I think) -- took over 3 pages and a couple bad practices. Less than a year later I rewrote the entire program to less than 1 page. Knowledge can come at you fast...

cin  >> setLocation;
        switch (setLocation) {
            case 0x41 :
                px = 0;
                py = 0;
                break;
            case 0x42 :
                px = 1;
                py = 0;
                break;
            case 0x43 :
                px = 2;
                py = 0;
                break;
            case 0x44 :
                px = 0;
                py = 1;
                break;
            case 0x45 :
                ...
                py = 2;
                break;
        }

This whole section can be reduced to 2 simple equations. Hint: Subract 41 from setLocation and look at division and modulus operators...

Does that look right? I'm still open for suggestions and I've got some ideas of my own I'll post later on, but I'm freaking tired.

If it works, it's (probably) right. I didn't look at the whole thing, but the above jumped out at me big time...

No problem. It's not like I was waiting by the computer just for you :mrgreen:

Ah you were'nt, darn, and all this time.... Lol - just playing :lol:

You'll have to forgive my spunky personality. I used the method(s) you suggested; the obviously one insinuated was:

px = (setLocation - 0x41) % 3; // Duh...;)

In a non-defensive manner of speaking, because I wrote the original program relatively fast I didn't put to much time in code efficiency - meaning I usually write the steps of a program and modify the code later for efficiency. Although you helped me from having to really think about it and I appreciate your expertise.

Thanks, LamaBot

This article has been dead for over six months. Start a new discussion instead.