1

I'm just learning Python and am working on a Tic Tac Toe problem. When I'm trying to see if all the items in a row are the same, I originally did it via this:

if board[1] == board[4] == board[7]

This worked fine, but I quickly realized that I forgot to compare them to being an X or O. So I tried to change it to work like this:

if board[1] == board[4] == board[7] == ('X' or 'O')

This works if the line contains X's, but not if it contains O's. If I remove the X part, and just leave the O, then it works for O's. Is there a different way to do this and still keep it on one line?

I'm sure there's probably a much better way to do it in the first place, but I'm just wondering why this particular syntax doesn't work.

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Mike
  • 107
  • 1
  • 2
  • 7

6 Answers6

3

It doesn't work because or doesn't give you a set that you can compare multiple items against, it just returns a single value based on the truthiness of the first operand.

One option would be putting the three values into a set and then comparing that set against a tuple of other sets:

if {board[1], board[4], board[7]} in ({'X'}, {'O'}):

If the three values are the same, the set of those values will contain a single element; a common way to test if an arbitrary number of values is identical is to put them into a set and then check that the len of the set is 1.

If the three values are the same AND the value is 'X' or 'O', then the set must be specifically equal to either {'X'} or {'O'}, and a common way to test whether one thing is the same as any of a bunch of other things is to use the in operator.

A similar option would be to combine the three spaces into a string (rather than a set):

if board[1] + board[4] + board[7] in ("XXX", "OOO"):
Samwise
  • 68,105
  • 3
  • 30
  • 44
  • 2
    I misread your code, it does work. Although it's not obvious. – Barmar Mar 28 '23 at 20:34
  • The `in` operator only returns true if the left operand is in the right operand, so the left operand (the set made out of the three board spaces) needs to be either `{'X'}` or `{'O'}` (the two elements of the tuple that forms the right operand) for the `in` operator to return true. – Samwise Mar 28 '23 at 20:42
  • Got to say this is a pretty unique solution, though a bit confusing initially. – B Remmelzwaal Mar 28 '23 at 22:08
2

You need to use in to test if something is in a list, not ==.

if board[1] in ('X', 'O') and board[1] == board[4] == board[7]:
Barmar
  • 741,623
  • 53
  • 500
  • 612
1

You could write

if board[1] == board[4] == board[7] != NOT_X_OR_O:

(where NOT_X_OR_O is whatever string you use for an empty square) but that's not the most readable expression. I'd recommend one of the other suggestions and explicitly check one of the three equal values against a container.

chepner
  • 497,756
  • 71
  • 530
  • 681
1

I would just check if one of the elements is in the subset:

if board[1] == board[4] == board[7] and board[1] in {'X', 'O'}:
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
1

If you are using Python 3.10 or newer, you can use structural pattern matching:

match (board[1], board[4], board[7]):
    case ('X', 'X', 'X') | ('O', 'O', 'O'):
        result = True
    case _:
        result = False

On the other hand, you can achieve a similar effect in terms of readability and obviousness with a simple expression by assigning the three values to a variable first:

row = (board[1], board[4], board[7])
result = (row == ('X', 'X', 'X') or row == ('O', 'O', 'O'))

(outer parentheses added for clarity)

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
1

This also works, and in some ways is closest to what you first had in mind:

if board[1] == board[4] == board[7] in {'X', 'O'}:
    ...

This relies on the fact in is a relational operator and supports chaining, just like ==, < and so on do.

slothrop
  • 3,218
  • 1
  • 18
  • 11