# my first Tic-Tac-Toe game

This is my first post here, as I just started learning a bit of code pretty recently through "Automate Boring Stuff".

In one exercise, to learn about dictionaries, it was shown how to build a tic tac toe board where players could place their marks. I decided to try to use the exercise as a basis and create the rest of game mechanics, like winning conditions, allowing only the right inputs, no placing over already placed marks, etc.

I would like to have someone look at the code and see what are some bad practices that I might have commited. I would also like to know if there is a easier and more concise way of defining the winning conditions as I do not see my method scaling well for a game with a bigger board or something like that.

Bellow is my code:

theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': ' '}

def printBoard(board): #function to print board
print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])
print('-+-+-')
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])
print('-+-+-')
print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])

print('''
Hi! This is my tic-tac-toe text based game.
To make your move, write where you want to play:

top-L   top-M   top-R
mid-L   mid-M   mid-R
low-L   low-M   low-R

''')

turn= 'X' #starting
while True:
printBoard(theBoard)
print(f'Turn for {turn}. Where will you play?')
try:
move = input()
if theBoard[move] == ' ':
theBoard[move] = turn
if turn == 'X':
turn = 'O'
else:
turn = 'X'
else:
print('That move is not possible!')
continue
except KeyError:
print('''Wrong input.
To make your move, write where you want to play:

top-L   top-M   top-R
mid-L   mid-M   mid-R
low-L   low-M   low-R

''')
continue
if ((theBoard['top-L']==theBoard['top-M']==theBoard['top-R']) and theBoard['top-L']!=' ')\
or ((theBoard['mid-L']==theBoard['mid-M']==theBoard['mid-R']) and theBoard['mid-R']!=' ')\
or ((theBoard['low-L']==theBoard['low-M']==theBoard['low-R']) and theBoard['low-L']!=' ')\
or ((theBoard['top-L']==theBoard['mid-M']==theBoard['low-R']) and theBoard['top-L']!=' ')\
or ((theBoard['low-L']==theBoard['mid-M']==theBoard['top-R']) and theBoard['low-L']!=' ')\
or ((theBoard['top-L']==theBoard['mid-L']==theBoard['low-L']) and theBoard['top-L']!=' ')\
or ((theBoard['top-M']==theBoard['mid-M']==theBoard['low-M']) and theBoard['top-M']!=' ')\
or ((theBoard['top-R']==theBoard['mid-R']==theBoard['low-R']) and theBoard['top-R']!=' '):
break

if turn == 'X':
turn = 'O'
else:
turn = 'X'
print(f'Good Job {turn}, you won!')


The comment behind printBoard(): is pretty useless and can be removed. Comments that explain the obvious are usually clutter. That said, as a beginner it usually helps to write comments for functions and code blocks, but I'd then look if the comment says something different than the function and change the function name accordingly. If the comment is in front of a code block, that block can usually be extracted into its own function.
There are a lot of magic strings in there. Imagine the following case: You mistakenly write 'x' instead of 'X' (or o instead of O). While scanning the code it may not be obvious where the bug is. That's why we like to extract magic strings or numbers into constants. Little more overhead, but compile time errors instead of runtime bugs - and usually even more readability. E.g. 'X' could be PLAYER_1 and 'O' be PLAYER_2. Now we can even change the symbols and the code won't care.
You have a pretty long break condition. The entire condition of the if can be extracted into its own function and then the function can be called instead of while True:: while not SomeoneWon():