Solving a problem "Find Winner on a Tic Tac Toe Game"

Working through this problem Find Winner on a Tic Tac Toe Game I came up with

class Solution {
    func tictactoe(_ moves: [[Int]]) -> String {
        var playerAMoves = [[Int]]()
        var playerBMoves = [[Int]]()
        
        for (index, move) in moves.enumerated() {
            if index % 2 == 0 {
                playerAMoves.append(move)
            } else {
                playerBMoves.append(move)
            }
        }
        
        if checkWon(from: playerAMoves) == true {
            return "A"
        }
        
        if checkWon(from: playerBMoves) == true {
            return "B"
        }
        
        return "Draw"
    }
    
    func checkWon(from playerMoves: [[Int]]) -> Bool {
        for index in 0..<2 {
            // Player won horizontally in a row.
            if playerMoves[index][0] == playerMoves[index][1] && playerMoves[index][0] == playerMoves[index][2] {
                return true
            }
            
            // Player won vertically in a column.
            if playerMoves[0][index] == playerMoves[1][index] && playerMoves[0][index] == playerMoves[2][index] {
                return true
            }
        }
        
        // Player won diagonally between the top left and bottom right.
        if playerMoves[0][0] == playerMoves[1][1] && playerMoves[0][0] == playerMoves[2][2] {
            return true
        }
        
        // Player won diagonally between the top right and bottom left.
        if playerMoves[0][2] == playerMoves[1][1] && playerMoves[0][2] == playerMoves[2][0] {
            return true
        }
        
        return false
    }
}

(Scroll in code snippet to see it entirely.)

For some reason, running this in the web editor (Swift 5.5.2) outputs a runtime error process exited with signal SIGILL. Why might this be? Running it in an Xcode playground shows no compiler errors.

Also if anyone has any critiques or suggestions for the code I'd be happy to hear them as well, I'm trying to get better at these types of exercises.

I think it means there was some kind of runtime error, probably an array index out of bounds. Have you tested your solution against different tic tac toe inputs in Xcode?

1 Like

Your code has a fundamental logic flaw. When you .append something to an Array it just gets added at the end; it's index is the length of the Array before it's added.

But your checkWon routine indexes into the array as if the index is the row of tic tac toe game. That is not going to produce the right result and could easily fail if you read off the end of the Array.

To fix it: set up your 3x3 array beforehand: you only need one. Then when you read the input unpack each move, using its row and col to decide where to put the move in your array, putting different values for each player. Then you can check for the winner in the way you do in checkWon, though you only need to do it once.

1 Like

Thanks, I got it working for supplied test cases:

class Solution {
    func tictactoe(_ moves: [[Int]]) -> String {
        
        let populatedBoard = populatedBoard(with: moves)
        
        if let winner = checkWon(board: populatedBoard) {
            return winner
        }
        
        if moves.count < 9 {
            return "Pending"
        } else {
            return "Draw"
        }
    }
    
    func populatedBoard(with moves: [[Int]]) -> [[Character]] {
        var playerAMoves = [[Int]]()
        var playerBMoves = [[Int]]()
        
        var board: [[Character]] = [
            [" ", " ", " "],
            [" ", " ", " "],
            [" ", " ", " "]
        ]
        
        for (index, move) in moves.enumerated() where index % 2 == 0 {
            playerAMoves.append(move)
        }
        
        for (index, move) in moves.enumerated() where index % 2 == 1 {
            playerBMoves.append(move)
        }
        
        for move in playerAMoves {
            board[move[0]][move[1]] = "X"
        }
        
        for move in playerBMoves {
            board[move[0]][move[1]] = "O"
        }
        
        return board
    }
    
    func checkWon(board: [[Character]]) -> String? {
        for index in 0...2 {
            // Player won horizontally in a row.
            if board[index][0] == board[index][1] && board[index][0] == board[index][2] {
                if board[index][0] == "X" {
                    return "A"
                } else if board[index][0] == "O" {
                    return "B"
                }
            }
            
            // Player won vertically in a column.
            if board[0][index] == board[1][index] && board[0][index] == board[2][index] {
                if board[0][index] == "X" {
                    return "A"
                } else if board[0][index] == "O" {
                    return "B"
                }
            }
        }
        
        // Player won diagonally between the top left and bottom right.
        if board[0][0] == board[1][1] && board[0][0] == board[2][2] {
            if board[0][0] == "X" {
                return "A"
            } else if board[0][0] == "O" {
                return "B"
            }
        }
        
        // Player won diagonally between the top right and bottom left.
        if board[0][2] == board[1][1] && board[0][2] == board[2][0] {
            if board[0][2] == "X" {
                return "A"
            } else if board[0][2] == "O" {
                return "B"
            }
        }
        
        return nil
    }
}

However, it took me hours longer than LeetCode's artificial time limit of 45 minutes for mock interview purposes, which leads me to suspect my solution was needlessly complex or unclear. Would be happy to hear feedback if anyone has any.

I suspect you just need experience to get faster. Your approach there looks fine to me, though there are things you could do more cleanly. E.g. to generate the board

var isPlayerA = true
for move in moves {
    board[move[0], move[1]] = isPlayerA ? "X" : "O"
    isPlayerA.toggle()
}

When I find myself using .enumerated()I check if I actually need the index or if I can deduce whatever I'm using it for some other way. If I can the resulting code is generally cleaner and clearer.