Tic Tac Toe With AI — The ultimate beginner guide! (Part 5)

This is the last part of our series.I want once again to give all the credit for this tutorial to the mind that created the original →article← Because it helped me, I just wanted to create an bridge that can be walked by other beginners too, that’s how much i appreciated the article.

My final version will be a simplified version of his work. I really encourage you after you finish this guide, to go an read he’s tutorial, now that you have the skills to understand :)

In our last version →V0.4← we have created the algorithm, but it’s not finished yet.For now our miniMax() returns an console.log, let’s change that by making it return an number and let’s see what we can do with it.

The first thing to do is to understand the logic of our algorithm, so we return to our pseudo-code.

So, we create all possible states with our recursion function, and then whenever we find a terminal state we give it an value.Say for example we have 4 states, 2 states return 10, 1 state returns 0 and the last state return -10. When it’s the computer turn, from all 4 states, it will chose the one that returns -10. In this manner computer chooses where to put an “O” each time.

The only problem is that our algorithm will return multiple states that return -10, so we must find a way to chose the best possible move.We do so by adding another property to State().

Pro Tip: Encapsulation (each object is responsible for specific tasks).

It’s time to use some Encapsulation and create a function that increments .oMovesCount every time we create an state from the previous state.We put it under the globals variable.

we use state.turn just for readability, we could have said next.turn instead, it does not make any difference

Since now we are creating a new State() each time we call .applyTo we need a way of updating our GameManager .currentState() , so we can keep track of the current state we are in.

So now every time we call .advanceTo it will take whatever State() we have and assign it to this.currentState.

Let’s see that in action in our takeABlindMove()

Take you’re time and absorb the information.It’s crucial you understand this lines of code.

We go back to our recursion function and update the .map and base case.

Explanation: whenever we reach the terminal state of a board we will return a number.So now we have a function that creates all possible permutations of the board, and return an number that is indicative of the value of each state and it all depends on the .oMovesCount.

The next problem is that we may have multiple states with the same value, so we need a method to sort them, and then choosing the first state of the list.

IMPORTANT, IMPORTANT, IMPORTANT We are using forEach to compare the stateScore depending on turn

For the first time we have the finished version of the algorithm.The first thing we explain is why we have return stateScore inside our recursion function.Until now we saw that in our base case we return an number, so why is this return stateScore? The explanation is simple: we use our base case to create a number nextScore, that we use to compare to the initial stateScore,

and depending on wich turn is it, we update the stateScore, and finaly we return the stateScore. For me this was the first time that i have seen an recursion with another return inside of it, and was the most difficult part to understand.Chew on this until you get it and then we move to the last part.

Pro Tip: remember that miniMax creates all possible states of our board starting from an already filled board(state), so it’s state.turn determines the sorting between stateScore and nextScore.

Example: we call the miniMax(_state), on a state that it have only 1 more depth to go until it fills the board(2 states left).Our stateScore will be initiated at -1000, it will do it’s recursion and find out that one state return 10 — _state.oMovesCount = -20, and the other returns 20. If our initial miniMax was called on a state with turn “X” , it will return -20 (because it took 30 .oMovesCount to get to that state).

Now sorting, we create 2 static functions and attach them to the AIAction():

Now that we have all the pieces, it’s time to build our takeAMasterMove().

We use console.log for better understanding, and then call the function

If we look into the console we will see that the first time it return an array of 8 objects, each with an minMaxValue. Now, i ask you, to tell me, which state we will chose? If you truly understand the algorithm you will know the answer.

SPOILER ALERT

Now we update our MasterMove()

The last thing we want to do is create an switch in notify, and give the player to possibility to play against a novice computer or an expert.

We create 2 button elements(we are using bootstrap :)

We update our AI()

We update our start event too.

Now we just have to give the class difficultyLevel to the element that it is clicked.

Full code pen: →V0.5

Extra feature: the reason we are creating .DESCENDING and .ASCENDING is to bee able to make the computer play as “X” or “O”. I will not show how to make use of it, try and do it by you’re self, besides Free Code Camp ask you to bee able to play as X or O, take it as a final graduation exam!

Ok boys and girls, it’s over, now we finally have our finished game. Wow, what a journey it was, for me writing this and i imagine for you too that stuck with this tutorial until the end.I want to thank you, and please leave a comment if it was of any help for you.