Search code examples
c++language-features

Is there an easier way of representing conditions in C++?


I wrote a basic tic-tac-toe game based on multidimensional arrays. g[3][3]. In my program I have about 9 conditions like the one I am about to show you:

if((g[0][0] == X && g[0][1] == X && g[0][2] == X) || (g[0][0] == O && g[0][1] == O && g[0][2] == O))

This is quite insane. I am probably doing something wrong but this is why I am addressing this question. Is there an easier way of representing long and complicated conditions like this? For example couldn't I somehow do:

if(grid.hasXes)

Solution

  • You're probably going about it the wrong way. There are only 3^9, or 19683 possible combinations, so you can convert your grid to an int, even on a 16 bit machine:

    int
    asInt( char const (&grid)[3][3] )
    {
        int results = 0;
        for ( int i = 0; i != 3; ++ i ) {
            for ( int j = 0; j != 3; ++ j ) {
                results *= 3;
                switch ( grid[i][j] ) {
                case 'X':
                    results += 1;
                    break;
    
                case 'Y':
                    results += 2;
                    break;
    
                case ' ':
                    break;
    
                default:
                    assert(0);
                }
            }
        }
        return results;
    }
    

    Afterwards, you can use the int to index into a table indicating who won (if anyone). Alternatively, you can convert just one or the other player's position into a 9 bit int:

    int
    asInt( char const (&grid)[3][3], char who )
    {
        int results = 0;
        for ( int i = 0; i != 3; ++ i ) {
            for ( int j = 0; j != 3; ++ j ) {
                results *= 2;
                if ( grid[i][j] == who ) {
                    ++ results;
                }
            }
        }
        return results;
    }
    

    You can then use a simple linear search into a table, verifying that the necessary bits are set:

    static int const wins[] =
    {
        0007, 0070, 0700,       //  rows
        0111, 0222, 0444,       //  columns
        0124, 0421              //  diagonals
    };
    
    class Wins
    {
        int myToMatch;
    public:
        Wins( char const (&grid)[3][3], char who )
            : myToMatch( asInt( grid, who ) )
        {
        }
        bool operator()( int entry ) const
        {
            return (entry & myToMatch) == entry;
        }
    };
    

    Then:

    if ( std::find_if( begin( wins ), end( wins ), Wins( grid, 'X' ) )
                != end( wins ) {
        //  X wins
    else if ( std::find_if( begin( wins ), end( wins ), Wins( grid, 'O' ) )
                != end( wins ) {
        //  O wins
    else
        //  play another turn.
    

    You could even consider keeping the grid as two ints, one per player. The bit number for a position would be 3 * i + j, and to test if a move is legal:

    bool
    isLegal( int gridX, int gridY, int i, int j )
    {
        return ((gridX | gridY) & (1 << (3 * i + j))) == 0;
    }