After finishing my reinforcement learning based Tic Tac Toe agent (https://github.com/llinauer/tic-tac-toe-rl), I was really proud. I thought, how cool that you can train an AI to play the game perfectly with a mere 200 thousand rounds of training!
Some time later I read the book One Jump Ahead by Jonathan Schaeffer. In this book, Dr. Schaeffer describes in great detail the development of his checkers playing program CHINOOK, but it also touches some more general topics about game playing AI. The book is awesome by the way, and I would greatly recommend anybody interested in such tings to read it. Anyway, in one chapter, he does a comparison between machine learning and knowledge-based approaches and mentions there that learning everything from scratch, even if the knowledge is readily available, is at best, cumbersome.
Tic Tac Toe for example, is widely (universally?) known and everybody knows how to play. Even after only some games, people know how to react in certain situations and probably many of them develop a strategy to play perfectly. A quick look on wikipedia confirms: 8 rules is all you need to be unbeatable. 8 rules vs. 200 thousand games!
That really showed me how a little knowledge can go a long way in AI systems and that's also why I wanted to create a good old-fashioned Tic Tac Toe program. This is what this repository is about.
This project is derived from https://github.com/llinauer/tic-tac-toe-rl and uses python3 with numpy.
Let me state the rules for perfect play in Tic Tac Toe explicitly here:
The first rule of Tic Tac Toe is: you do not ... (jk)
If you have two in a row, complete it to win. Easy enough.
If the opponent has two in a row, block it to avert a loss. Also a no-brainer.
Place your symbol, so that you open up two winning lanes. In the example here, X plays the lower left corner and now has two rows with two symbols. O can only block one of them and will loose the turn after.
If your opponent can fork, prevent it. In the example here, X can open a fork by placing in the lower left corner. O knows this and can block. In this situation, it would be equally good for O to place in the middle and threaten with a win.
If free, place in the center. However, if it is the first round, you can increase your opponents chance of loosing, by placing in the corner. So that's probably a good thing to do.
When your opponent plays a corner, go for the opposite corner.
If a corner is empty, place there.
Play the middle square on any of the four side. This rule really only applies in the last move, when you are forced to draw.
To play, run the tic_tac_toe.py script via:
python tic_tac_toe.py -s {x, o}
You can choose which side you wann play with the -s argument.
Enjoy.