Given an input where we have two players: "X" and "O", and empty cells marked with a "." like:

check_winner([ "X.O", "XX.", "XOO"])

the program outputs if there are three "X" or "O" in horizontal, vertical or diagonal. If there is a draw it outputs "D".

expected results

def check_winner(game_result: List[str]) -> str:

    for i, row in enumerate(game_result):
        if game_result[i][0] == game_result[i][1] == game_result[i][2]:
            if game_result[i][0] != ".":
                return game_result[i][0]
        if game_result[0][i] == game_result[1][i] == game_result[2][i]:
            if game_result[0][i] != ".":
                return game_result[0][i]

    if game_result[0][0] == game_result[1][1] == game_result[2][2]:
        if game_result[1][1] != ".":
            return game_result[1][1]

    if game_result[0][2] == game_result[1][1] == game_result[2][0]:
        if game_result[1][1] != ".":
            return game_result[1][1]

    return "D"

How could we improve this code?


1 Answer 1


To avoid repetitious code, look for commonality. Here, there's a repeated task of checking whether a group of 3 characters are all the same (and not .). This could be done more generally using a set. Sets don't carry duplicates, so if the set of a group has length 1, that means all characters were the same.

The example below shows this idea, by first extracting the different groups and then iterating over them all. This probably means it's less efficient than it could be - it would be faster to create as few groups as possible - but I think the code is somewhat legible at least.

def check_winner(lst):

    # collect "triplets": rows, columns and diagonals
    rows = lst[:]
    cols = list(zip(*lst))
    diag1 = [row[i] for i, row in enumerate(rows)]
    diag2 = [row[2 - i] for i, row in enumerate(rows)]
    triplets = rows + cols + [diag1, diag2]

    # if all characters in a triplet are the same, and that character
    # is not ".", then that character marks the winner
    for triplet in triplets:
        if len(set(triplet)) == 1 and triplet[0] != '.':
            return triplet[0]
    return 'D'

print(check_winner([ "X.O", "XX.", "XOO"]))
  • \$\begingroup\$ Hello @Sedsarq thanks for your feedback and thanks for your time answering. I wonder what means the line: cols = list(zip(*lst)) What is the * before the list used for? \$\endgroup\$
    – Yone
    Commented May 24, 2019 at 22:11
  • \$\begingroup\$ That's the unpacking operator. lst is a list, like [a, b, c]. The * removes the brackets, so zip receives the 3 items separately like zip(a, b, c) instead of inside a list. Zip can then pair up elements with the same index. Like a zipper, joining vertical strands horizontally. When the items are rows, this means making columns. @enoy \$\endgroup\$
    – Sedsarq
    Commented May 25, 2019 at 5:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.