Tic-Tac-Toe Evaluation – Solution

The insidious part of this month’s Exercise is writing a function that doesn’t count a stalemate as a win. It’s what happened to me for my first draft of a solution.

The code you must write for the is_winner() function evaluates rows, columns, and diagonals for matching characters. Specifically, either 'x' or 'o'. But the space character, ' ', can also match up in a row, column, or (less likely) diagonally. If you don’t code the function properly, it could return that “space” won the game. That’s just not fair!

For my solution, I checked diagonals first, then columns and rows. Here’s the first if test, which checks for the NE-SE diagonal:

if( *(g+0)==*(g+4) && *(g+4)==*(g+8) )
{
    r = *(g+0);
    if( r=='x' || r=='o' )
        return(r);
}

The if test clusters the upper-left/center and the center/lower-right squares or offsets in the grid. This is because you cannot stack logical expressions in a if condition. The following expression is invalid:

if( *(g+0)==*(g+4)==*(g+8) )

Tests must be done two at a time, which is how the first diagonal is evaluated. If all the characters match, variable r is assigned to the upper right square (because all the characters match it). A second if test confirms that the character is either 'x' or 'o'. When true, the character is returned. Otherwise, the matching character is the space, which isn’t a winning condition.

My solution next checks the SW-NE diagonal, using the same technique but different offsets in the array.

Rows and columns are checked by using a for loop and various offsets and such. Here is my solution for the is_winner() function.

2020_05-Exercise.c

/* determine whether the game was won */
char is_winner( char *g )
{
    char r;
    int x;

    /* check diagonals first */
        /* NW-SE */
    if( *(g+0)==*(g+4) && *(g+4)==*(g+8) )
    {
        r = *(g+0);
        if( r=='x' || r=='o' )
            return(r);
    }
        /* SW-NE */
    if( *(g+6)==*(g+4) && *(g+4)==*(g+2) )
    {
        r = *(g+6);
        if( r=='x' || r=='o' )
            return(r);
    }

    /* check columns */
    for( x=0; x<3; x++ )
    {
        if( *(g+0+x)==*(g+3+x) && *(g+3+x)==*(g+6+x) )    
        {
            r = *(g+0+x);
            if( r=='x' || r=='o' )
                return(r);
        }
    }

    /* check rows */
    for( x=0; x<3; x++ )
    {
        if( *(g+(3*x))==*(g+(3*x)+1) && *(g+(3*x)+1)==*(g+(3*x)+2) )
        {
            r = *(g+(3*x));
            if( r=='x' || r=='o' )
                return(r);
        }
    }

    return(' ');
}

Click here to view my full solution on GitHub.

The solution for game 5 in the array (element 4) is the true test. It’s layout can trip up the column test because three spaces appear in the first column, as you can see in the solution’s output here:

 x x o
 o o x
 x o x
Game 1: No winner

 o   x
   x o
 x    
Game 2: X wins!

 x x  
 o x x
 o o o
Game 3: O wins!

 o   x
 o o o
 x   x
Game 4: O wins!

   x o
   o x
     o
Game 5: No winner

 x   o
 x o  
 x o x
Game 6: X wins!

If you’re not careful with your is_winner() function, Game 5 comes back with the space character as the winner.

I hope you enjoyed this Exercise and that your solution rendered proper answers.

Leave a Reply