I made this in April 2022 as my first graphical game, starting from a tutorial and then turning it into a complete Tic-Tac-Toe program of my own. I added a home screen, scorekeeping, local two-player play, three computer difficulties, forfeits, and custom end screens. The project was a fun way to learn Pygame before I used it for the much larger Blackjack game that followed.
From a tutorial to a complete game
This started as a small tutorial project and became an excuse to keep experimenting. I built a home screen where the player can choose who moves first, switch between playing a friend and the computer, cycle through difficulty levels, reset the scores, and start another round without restarting the program.
The scoreboard stays active for the whole session and records wins for X and O as well as ties. During a match, either player can forfeit, and the game draws a line through the winning row, column, or diagonal before showing the result screen.
A hand-built computer player
The hard opponent does not use minimax or search a full game tree. It first tries every empty square to find an immediate winning move, then repeats the scan to block an immediate win by the player. If neither applies, it scores each open square by counting how many still-possible winning routes pass through it and randomly chooses among the best-scoring options.
I also added a special defense for the classic opposite-corners fork: when the player owns two opposite corners and the computer owns the center, the computer chooses an edge rather than another corner. Medium difficulty still takes wins and blocks losses, but mixes heuristic and random moves during open play. Easy chooses randomly from the remaining squares.
Drawing the interface with Pygame
There are no image assets in the project. The grid is made from lines, X marks from pairs of diagonal strokes, O marks from outlined circles, and every button or panel from rectangles and rendered text. Their positions and sizes are calculated from the window width, height, and one derived line-width value, so the whole interface comes from the same small set of dimensions.
The board is a 3-by-3 NumPy array containing zero for an empty square, one for X, and two for O. Win detection returns both the winning player and the endpoints of the winning route, allowing the same result to drive the game logic and the line drawn across the board.


