Ok, its Tic Tac Toe - calm down, but it sure is fun to talk like that. :)
As you know, if you’re enrolled at Flatiron, part of the Object Oriented Ruby Final Projects curriculum is creating a Tic Tac Toe game that allows for a 1 player game (in which you you play against the computer), a 2 player game (in which you play a friend - not to say that your computer isn’t your friend, but I programmed this AI to be kind of a jerk - read on to find out more about that), and also a mode where the AI will play itself. You also have the option to create a mode called “Wargames” in which the AI will play itself 100 times in a row, and report the results back to you.
This project really ties together alot of the concepts of Objects that you learn from going through the OO Ruby curriculum, primarily Object Relationships. You build out a Board Class, a Game Class, and a Player Class, that has 2 subclasses; a Human Player Class and a Computer Player Class. The Board Class determines the look of the board, and also keeps track of the positions on the board that have been played, displaying it to you after each turn. The Game Class initializes the game and constantly checks to see if the game is over, by win or draw, and continues the game if not. The Player Class is split into the Human and Computer subclasses. The Human class basically just asks for input and through the Game and Board Classes, applies that input to the board. The Computer Class is where you determine the logic for how the AI will play Tic Tac Toe. This is where you get to be creative and where I had alot of fun.
(Please excuse the vagueness of the following - I wanted to give my experience with the project and how I worked through the logic, without giving away too much so as to ruin it for someone who is new at Flatiron and may not have completed this project yet)
I began by just getting the AI to work. I wrote a line of code using the collect.with_index method that returned an array of the indexes all of the “open positions” on the board. Then just called the sample method on that array to return an index that was open, and converted that to the proper input for it to make the move on the board. This worked and got me a functioning game. However, the instructions state that you must design the AI to play with some logic, and this approach only returns basically a randomly generated number. The instructions also state (regarding the computer playing itself) “A perfect computer AI should never be able to win, like in the case of thermonuclear war.”
So it seemed my goal was to make the AI unbeatable (or the way I saw it, if I ran the Wargames mode, there should be 100/100 draws and no wins. So I began researching Tic Tac Toe logic in general, thinking of whats the best way to play if I were playing. I ended up coming across the Minimax algorithm (as Tic Tac Toe is a very common project for programming). (WARNING: please excuse this poor explanation of the Minimax method, keep in mind just over 1 month ago, I couldn’t even “puts” anything with Ruby lol) In a watered-down -caveman-style nutshell, the Minimax algorithm (as applied to TicTacToe) would search the board, evaluating and scoring each position on the board for its “win-potential”. It also looks ahead in the game (typically up to 2 moves ahead) trying to determine the worst case and best case scenarios for each spot on the board. It then returns a position it feels will maximize the winning odds for itself or block the potential winning odds of the opponent. This seemed great, so I started researching it, and saw it EVERYWHERE, and is a staple of AI programming as I understand. However I wanted to see if I could come up with something purely from my own logic.
So I worked, and I worked, and I finally got it to where, in Wargames mode, I was getting 100 draws! Success! Looking back the logic seems so simple now, it almost feels like cheating. Which technically, I think it kind of is. Remember when I said I programmed it to be kind of a jerk. Well the reason for that is the AI doesn’t actually try to win. Instead it plays just to block the opponent from ever winning. The first few moves are planned out (ie. prioritizing corner slots and the center slot). But once there are a few pieces on the board, the AI simply checks the board for a possible opponent win (that would be 2 opponent pieces in a row - any row) and if there is, it takes the would-be third slot. I know, this isn’t the exact way the computer should play, it should actually play to win, but per the instructions, and general TicTacToe logic, a properly played game should always be a draw. Obviously the Minimax method would have created a much more sophisticated and elegant AI solution, but this was all mine (even if it is quite simple in logic), and I was proud and honestly so excited to actually build something I could interact with.
Overall this was alot of fun to build, and I am having a great time learning all I can about this amazingly empowering thing called program. My only wish is that I discovered this YEARS ago!!!