Learning Diary: Python Tic Tac Toe
I am slowly working my way through the book Automating the Boring Stuff with Python by Al Sweigart. I'm mostly working through a lot of the same concepts I previously covered in earlier entries in the series. While that content is useful to reinforce concepts for someone swapping between learning a new language and coding in Powershell at work, it isn't really compelling blogging material. Recently though, I hit the chapter on dictionary data types and how to use them. It ends by having you make a basic game of Tic Tac Toe.
The book invites you to find the rest of the code from their website, and I decided it would be more fun to take the time to figure out how to make it and then come back to their code and see how close I got. Below is the code from the lesson verbatim. It asks you where you want to go and places your mark there. You'll need to know what the keys are to all of the spaces for it to work, and once someone wins, you'll keep going until all nine spaces are filled.
theBoard = {'top-L': ' ','top-M': ' ', 'top-R': ' ', 'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
def printBoard(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'])
turn = 'X'
for i in range(9):
print('Turn for ' + turn + '. Move on which space?')
move = input()
theBoard[move] = turn
if turn == 'X':
turn = 'O'
else:
turn = 'X'
printBoard(theBoard)
It works, in so much as it lets you make marks on the board and shows them to you. There is no check for who wins, or if you need to take all nine turns. So the first thing I did was add a block for checking each turn if someone had one. It's a long string of if/elif statements. Each way, it checks for equal statements across all the possible matches. Spot the really dumb condition I hadn't thought of until I tested it:
def checkWin(board):
if(board['top-L'] == board['top-M'] and board['top-M'] == board['top-R']):
printBoard(board)
print(board['top-R'] + " wins thanks for playing!")
return "win"
elif(board['top-L'] == board['mid-L'] and board['mid-L'] == board['low-L']):
printBoard(board)
print(board['low-L'] + " wins, thanks for playing!")
return "win"
elif(board['top-L'] == board['mid-M'] and board['mid-M'] == board['low-R']):
printBoard(board)
print(board['low-R'] + " wins, thanks for playing!")
return "win"
elif(board['mid-L'] == board['mid-M'] and board['mid-M'] == board['mid-R']):
printBoard(board)
print(board['mid-R'] + " wins, thanks for playing!")
return "win"
elif(board['low-L'] == board['low-M'] and board['low-M'] == board['low-R']):
printBoard(board)
print(board['low-R'] + " wins, thanks for playing!")
return "win"
elif(board['low-R'] == board['mid-M'] and board['mid-M'] == board['top-L']):
printBoard(board)
print(board['top-L'] + " wins, thanks for playing!")
return "win"
elif(board['low-L'] == board['mid-M'] and board['mid-M'] == board['top-R']):
printBoard(board)
print(board['top-R'] + " wins, thanks for playing!")
return "win"
elif(board['top-M'] == board['mid-M'] and board['mid-M'] == board['low-M']):
printBoard(board)
print(board['low-M'] + " wins, thanks for playing!")
return "win"
elif(board['top-R'] == board['mid-R'] and board['mid-R'] == board['low-R']):
printBoard(board)
print(board['low-R'] + " wins, thanks for playing!")
return "win"
else:
print("Next Turn")
Since the board starts as blanks, there's already a winning condition. I fixed this by swapping in numbers for all the existing blanks, overwriting them as the game goes. Also, the main function needed a step to run the board through the function, as well as adding an extra step to the loop that ends the game in a draw. Here is the updated definition:
theBoard = {'top-L': '1','top-M': '2', 'top-R': '3', 'mid-L': '4', 'mid-M': '5', 'mid-R': '6', 'low-L': '7', 'low-M': '8', 'low-R': '9'}
Here is the updated main section of the code:
for i in range(10):
print('Turn for ' + turn + '. Move on which space?')
move = input()
theBoard[move] = turn
result = checkWin(theBoard)
if result == 'win':
break
if i == 10:
print("It's a draw, thanks for playing!")
break
if turn == 'X':
turn = 'O'
else:
turn = 'X'
printBoard(theBoard)
This change gave me the idea on how to fix the interface so it was easier to play. Especially because if you neglect to enter the second coordinate as a capital letter, it doesn't do anything for your turn which is obviously not fair. Rather than work out some elaborate way to check all the possible typed permutations, it is probably easier to ask the user to enter a number for their position. Time for another function. This takes a variable and translates the numbers 1-9 into the keys for the dictionary. This prevents a player from having any issues entering their choice. The new function is below.
def translateInput(input):
if input == 1:
position = 'top-L'
return position
elif input == 2:
position = 'top-M'
return position
elif input == 3:
position = 'top-R'
return position
elif input == 4:
position = 'mid-L'
return position
elif input == 5:
position = 'mid-M'
return position
elif input == 6:
position = 'mid-R'
return position
elif input == 7:
position = 'low-L'
return position
elif input == 8:
position = 'low-M'
return position
elif input == 9:
position = 'low-R'
return position
There were also some changes needed to the main function. A new variable is created to accept the return value from this function, as well as ensuring that the input is translated to an integer. This version of the code works great and you can easily play a quick game with a friend, but there is still one problem. You can choose the same square as the other player. This requires that we refine it one step further to ensure that the move you make is legal. With this step, we have a playable game of Tic Tac Toe. I added one more step to validate that the input is 1-9. Then I wrapped the main game in its own function to call it at the end of the file.
The entire program is below with my changes. Anyone better versed in Python, please comment below with anything I may have overlooked. I'd be interested in revisiting this at some point to add some graphics and a computer opponent, but that's probably a very distant goal. I'll keep you posted with new projects as I make progress.
theBoard = {'top-L': '1','top-M': '2', 'top-R': '3', 'mid-L': '4', 'mid-M': '5', 'mid-R': '6', 'low-L': '7', 'low-M': '8', 'low-R': '9'}
def printBoard(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'])
def checkWin(board):
if(board['top-L'] == board['top-M'] and board['top-M'] == board['top-R']):
printBoard(board)
print(board['top-R'] + " wins thanks for playing!")
return "win"
elif(board['top-L'] == board['mid-L'] and board['mid-L'] == board['low-L']):
printBoard(board)
print(board['low-L'] + " wins, thanks for playing!")
return "win"
elif(board['top-L'] == board['mid-M'] and board['mid-M'] == board['low-R']):
printBoard(board)
print(board['low-R'] + " wins, thanks for playing!")
return "win"
elif(board['mid-L'] == board['mid-M'] and board['mid-M'] == board['mid-R']):
printBoard(board)
print(board['mid-R'] + " wins, thanks for playing!")
return "win"
elif(board['low-L'] == board['low-M'] and board['low-M'] == board['low-R']):
printBoard(board)
print(board['low-R'] + " wins, thanks for playing!")
return "win"
elif(board['low-R'] == board['mid-M'] and board['mid-M'] == board['top-L']):
printBoard(board)
print(board['top-L'] + " wins, thanks for playing!")
return "win"
elif(board['low-L'] == board['mid-M'] and board['mid-M'] == board['top-R']):
printBoard(board)
print(board['top-R'] + " wins, thanks for playing!")
return "win"
elif(board['top-M'] == board['mid-M'] and board['mid-M'] == board['low-M']):
printBoard(board)
print(board['low-M'] + " wins, thanks for playing!")
return "win"
elif(board['top-R'] == board['mid-R'] and board['mid-R'] == board['low-R']):
printBoard(board)
print(board['low-R'] + " wins, thanks for playing!")
return "win"
else:
print("Next Turn")
def translateInput(input):
if input == 1:
position = 'top-L'
return position
elif input == 2:
position = 'top-M'
return position
elif input == 3:
position = 'top-R'
return position
elif input == 4:
position = 'mid-L'
return position
elif input == 5:
position = 'mid-M'
return position
elif input == 6:
position = 'mid-R'
return position
elif input == 7:
position = 'low-L'
return position
elif input == 8:
position = 'low-M'
return position
elif input == 9:
position = 'low-R'
return position
def playagame():
turn = 'X'
printBoard(theBoard)
for i in range(10):
print('Turn for ' + turn + '. Move on which space? (1-9)')
move = input()
if not (move.isdigit()) or not (1 <= (int(move)) <= 9):
inputInvalid = 'y'
while inputInvalid == 'y':
print("Please enter an integer 1-9.")
move = input()
if not (1 <= (int(move)) <= 9):
inputInvalid = 'y'
else:
inputInvalid = 'n'
translatedInput = translateInput(int(move))
if theBoard[translatedInput] == 'X' or theBoard[translatedInput] == 'O':
inputInvalid = 'y'
while inputInvalid == 'y':
print('Sorry that space is already taken.')
move = input()
translatedInput = translateInput(int(move))
if theBoard[translatedInput] == 'X' or theBoard[translatedInput] == 'O':
inputInvalid = 'y'
else:
inputInvalid = 'n'
theBoard[translatedInput] = turn
result = checkWin(theBoard)
if result == 'win':
break
if i == 10:
print("It's a draw, thanks for playing!")
break
if turn == 'X':
turn = 'O'
else:
turn = 'X'
printBoard(theBoard)
playagame()
Rants and Reviews. Mostly just BS and Affiliate Links.
Follow on Mastodon