Search code examples
c++tic-tac-toe

C++ - Shortest win-checking function for tic-tac-toe?


This is my "getWin" function in my C++ console tic-tac-toe game. It works just fine in the scope of this simple program, but just out of curiosity, what is a shorter/more efficient way of writing a function that returns the winner (if there is one) to a tic tac toe game?

notes - winArg is either 'X' or 'O', depending on which player you're testing for. top_left, middle_center, etc. are enumerated types. the final return statement, return 'n';, is for when there isn't a winner yet.

char getWin(char winArg)
{
    if (board[top_left] == winArg)
    {
        if (board[top_center] == winArg)
        {
            if (board[top_right] == winArg)
                return winArg;
        }
        if (board[middle_center] == winArg)
        {
            if (board[bottom_right] == winArg)
                return winArg;
        }
        if (board[middle_left] == winArg)
        {
            if (board[bottom_left] == winArg)
                return winArg;
        }
    }
    if (board[top_right] == winArg)
    {
        if (board[middle_center] == winArg)
        {
            if (board[bottom_left] == winArg)
                return winArg;
        }
        if (board[middle_right] == winArg)
        {
            if (board[bottom_right] == winArg)
                return winArg;
        }
    }
    if (board[bottom_right] == winArg)
    {
        if (board[bottom_center] == winArg)
        {
            if (board[bottom_left] == winArg)
                return winArg;
        }
    }
    //middle vertical and horizontal lines
    if (board[top_center] == winArg && board[middle_center] == winArg && board[bottom_center] == winArg)
    {
        return winArg;
    }
    else if (board[middle_right] == winArg && board[middle_center] == winArg && board[middle_left] == winArg)
    {
        return winArg;
    }

    return 'n';
}

Solution

  • You could, in my opinion, make it more readable by factoring out the detection of a line check, something like:

    char lineWin(int a, int b, int c) {
        if (board[a] == ' ')      return 'n';
        if (board[a] != board[b]) return 'n';
        if (board[b] != board[c]) return 'n';
        return board[a];
    }
    

    Then you can tidy up your full checking code with something like:

    char getWinner(void) {
        char wnr;
    
        // Horizontal lines.
    
        if ((wnr = lineWin(top_left,    top_center,    top_right   )) != 'n') return wnr;
        if ((wnr = lineWin(middle_left, middle_center, middle_right)) != 'n') return wnr;
        if ((wnr = lineWin(bottom_left, bottom_center, bottom_right)) != 'n') return wnr;
    
        // Vertical lines.
    
        if ((wnr = lineWin(top_left,   middle_left,   bottom_left  )) != 'n') return wnr;
        if ((wnr = lineWin(top_center, middle_center, bottom_center)) != 'n') return wnr;
        if ((wnr = lineWin(top_right,  middle_right,  bottom_right )) != 'n') return wnr;
    
        // Diagonal lines.
    
        if ((wnr = lineWin(top_left,  middle_center, bottom_right  )) != 'n') return wnr;
        if ((wnr = lineWin(top_right, middle_center, bottom_left   )) != 'n') return wnr;
    
        return 'n';
    }
    

    Now keep in mind this solution is for a 3x3 Tic-Tac-Toe game. If that's all you're doing then it's ideal and much more suitable than an algorithmic solution that, for each cell, checks the eight different directions for a win while restricting checking off the edge of the board.

    If you're going to design something for Connect-Four/Four-in-a-row or any other game that has a non-trivial number of ways to win, then your approach would have to be different. Although, even for that, you still don't have to go overboard with checking every direction from every cell, you can still use a relatively simple algorithm.