# Two player tic tac toe Python

Used classes to make this simple tic tac toe game.

Any improvements?

from itertools import chain

class Field:
def __init__(self, sequence):
self.sequence = sequence
self.rows = [[sequence[i] for i in range(j, j + 3)] for j in range(0, 9, 3)]
self.columns = [[row[i] for row in self.rows] for i in range(3)]
self.first_diagonal = self.rows[0][0] + self.rows[1][1] + self.rows[2][2]
self.second_diagonal = self.rows[0][2] + self.rows[1][1] + self.rows[2][0]
self.diagonals = [self.first_diagonal, self.second_diagonal]

def __str__(self):
return '''\
---------
| {} {} {} |
| {} {} {} |
| {} {} {} |
---------\
'''.format(*self.sequence)

def get_value(self, x, y):
return self.rows[x][y]

def set_value(self, x, y, value):
self.rows[x][y] = value
sequence = ''.join(list(chain.from_iterable(self.rows)))

self.__init__(sequence)  # Reinitializing attributes.

def get_state(self):

num_x = self.sequence.lower().count('x')
num_o = self.sequence.lower().count('o')

if abs(num_x - num_o) not in {0, 1}:
return 'Impossible'

winners = []
for sequence in chain(self.rows, self.columns, self.diagonals):
if len(set(sequence)) == 1:
if sequence[0] not in winners and sequence[0].lower() in ['x', 'o']:
winners.append(sequence[0])

if len(winners) > 1:
return 'Impossible'
elif winners:

return f'{winners[0]} wins'

# No winner
else:
if '_' in self.sequence or ' ' in self.sequence:
return 'Game not finished'
else:
return 'Draw'

def convert_index(x, y):
x, y = abs(y - 3), x - 1

return x, y

def make_move(field, move, value):
try:
x, y = move.split()
except ValueError:
print('You should enter numbers!')
return False

if x.isnumeric() and y.isnumeric():
x, y = int(x), int(y)
if x in {1, 2, 3} and y in {1, 2, 3}:
x, y = convert_index(x, y)
if field.get_value(x, y).lower() not in ['x', 'o']:
field.set_value(x, y, value)
return True
else:
print('This cell is occupied! Choose another one!')
else:
print('Coordinates should be from 1 to 3!')
else:
print('You should enter numbers!')

return False

field = Field('_' * 9)  # Empty field.

i = 1

while True:
print(field)

player = 'X' if i % 2 == 1 else 'O'

ordinates = input()

if make_move(field, ordinates, player):  # Only change player if a move was successful
i += 1

if field.get_state() not in ['Game not finished', 'Impossible']:
print(field)
print(field.get_state())
break

$$$$


## Input

One thing I would suggest is provide a hint to the user about the format of their coordinates input. It's not immediately obvious that the format should be x y. I had to look at the code to figure that out. Intuitively, one might expect to type in x and y on separate lines or maybe separate them with a comma.

One way to deal with this is to add a string in the input function, so rather than:

ordinates = input()

you could do something like:

ordinates = input("Please enter your coordinates (example: 1 2): ")

Better yet, it would be easier to deal with only individual x and y coordinates throughout the program, rather than mixing them with strings combining the two. For this, you would simply ask for the two coordinates with separate input() statements (e.g. input("Enter your x: ", input("Enter your y: ")`). Then you don't have to worry about splitting strings or unpacking tuples or anything or the sort.

## Coordinates

Another point is that the vertical coordinates seem to be backwards, in that the 1st row is at the bottom and the 3rd row at the top. Again it would make the game more user friendly to either specify this to the user or rearrange the grid array so that [1, 1] is top left and [3, 3] is bottom right.