As a hobbyist game developer working with Python and Pygame, I recently decided to take on the classic challenge of building a Tic Tac Toe game. To make it more interesting, I implemented an AI opponent using the Minimax algorithm.
Everything seemed to be working fine the grid was displaying, player clicks were registered, and the AI responded correctly. But then, I hit a wall. My terminal threw this error:
TypeError: cannot unpack non-iterable NoneType object
It was pointing directly to this line in my main game loop:
row, col = comp.evaluation(board)
And just like that, the game froze. So I dug into the code to figure out what went wrong.
Define Error
Here’s the issue in plain terms I was expecting the AI to always return a valid move a tuple like (1, 2)
but instead, under certain conditions, it was returning None
. And Python doesn’t know how to unpack None
into two variables, hence the TypeError.
Root Cause Analysis
The culprit turned out to be inside the MinMax
class, particularly this method:
def evaluation(self, main_board):
...
return move
The move
was supposed to be a tuple like (row, col)
, but sometimes, it ended up being None
. Why?
It turns out, the issue occurs when the board is full (i.e., the game is a draw), or when the AI logic can’t find a valid move. The minmax
method would return None
in such cases:
eval, move = self.minmax(main_board, False)
# If no move is possible
return move # move = None
So when the game tried to do this:
row, col = comp.evaluation(board)
Python essentially tried to unpack None
, resulting in that TypeError.
Fix for the Error
To fix this, I updated the evaluation()
method to never return None
directly. Instead, I return a default safe value when no moves are possible.
Update evaluation()
method:
def evaluation(self, main_board):
if self.level == 0:
move = self.random_choice(main_board)
eval = 'random'
else:
eval, move = self.minmax(main_board, False)
if move is None:
print("No move possible: Board is full or no valid moves left.")
return -1, -1 # Safe fallback
print(f'Minimax chose: {move} with evaluation {eval}')
return move
Safe unpacking in main()
:
move = comp.evaluation(board)
if move == (-1, -1):
tictac.run = False
continue
row, col = move
Now, even if the board is full or the AI can’t decide on a move, the game handles it gracefully and avoids crashing.
Additional Feature I Practice and Implement
After solving the error, I decided to expand the project to make it more robust and interesting. Here are some features I added:
Difficulty Levels
- Level 0 – AI plays randomly.
- Level 1 – Full Minimax algorithm.
- Level 2 (new) – Depth-limited Minimax with pruning.
This helped me explore performance optimization and gave players a smoother experience.
Score Tracking
I added a simple score tracker with:
self.scores = {'player': 0, 'computer': 0, 'draw': 0}
Every time a game ends, it checks who won and updates the corresponding score.
Visual Enhancement
I improved the visual feedback by:
- Highlighting the winning line
- Adding a short delay before the next game
- Displaying winner messages
These small changes made the game feel much more polished.
Human vs Human Mode
Previously, the game supported only user vs AI. I added support for two player mode by toggling the game mode:
if self.gamemode == 'user':
self.gamemode = 'computer'
else:
self.gamemode = 'user'
This was a great exercise in managing game state and conditional behavior.
AI Starts First
With a simple keypress (A
), I let the computer go first handy for testing and game variety.
Play Again Prompt
After the game ends, players are shown:
“Game Over – Press R to Restart or Q to Quit”
This made replaying quick and intuitive.
Final Thoughts
Building this Tic-Tac-Toe game with Pygame and the Minimax algorithm was a fantastic learning experience. The TypeError
was a good reminder that even a smart AI can break things when it has no valid choices and you have to plan for that.
Beyond fixing the bug, the project helped me practice:
- Handling edge cases
- Implementing clean game logic
- Improving user experience with visuals and interactivity
If you’re into Python or game development, I highly recommend trying this out. And when you hit your own TypeErrors just know it’s part of the process.