Tic-Tac-Toe - Part 2

Favourite 14 favourites
Tutorial written by blackhornetOriginally published on 17th, June 2012 - 14 revisions

Play()


Create a blank play-board that will contain the weight values we calculate by trying possible moves (line 111). Determine the valid winner subset for the current player (line 113), then determine the valid winner subset for the opponent (line 114). Calculate the weights for the current player (line 116), then add on the weights of the opponent (that is blocking the opponent) (line 117).

At this point I have to point out a flaw in the current algorithm. There is one particular case where the algorithm fails to pick an appropriate move, and that is if player X starts with any of the outer middle squares.

By default, the weight algorithm will give the highest weight to the center, because this position appears in the most winner combinations than any other position (4 out of 8), so it gets calculated more than any other, artificially making it a higher weight than we actually want. If we ignore this, you would see by experimenting that the following combination would have X allowed a win!

To avoid this I add some code to detect this special case and hard code a preferable move that deals with this. We'll see this in a minute.

Now we setup some variables to hold various state, including a array of the highest weight values, as there may be more than one with the same value.

For each element in our play-board (line 126), if the current element is higher than our current highest value (line 128), then we reset the highest array, push a new index, and store the new highest value (line 131). Otherwise if the value is equal to our current highest value (line 134), then we add that to the highest array (line 136). Add the current value to a running total (line 138). This is just an easy way to confirm that there are any possible moves. If not, then the game must be over.

Now, since we are already looping through the board positions, we’ll look for our special case, and flag it’s state. If the current board’s position has a player (line 140), increment our total-moves counter (line 142) and see if it is one of the four special locations we care about (line 143). If so, increment a special case counter (line 145), and store its position (line 146).
At this point we have an array of the highest weight indices, and our special case state.


Make sure we actually have a possible move (we aren’t finished) (line 153). If we only have one move so far, and our special case counter has only one case, and the highest position we calculated is in the center of the board (line 155), default to index zero (line 157) and, for indices 1 and 3, set the highest index to position 0 (line 162), or for 5 and 7, set it to position 8 (line 166). Otherwise if we have more than one index to choose from (line 170), we randomly pick an index in our highList array and store that (line 173). (There’s some controversy over how the random function works, so just to be doubly sure, I make sure that the index does not extend beyond a valid range (lines 175-177)). Otherwise, if there is only one highest value, default to index zero (line 181).

Now we just check that we have an index to choose from (line 184), and get that value (line 186). We return that move position (189).


Now skip down to line 209 and we set up storage for our winner array and our current-board array.


Down at line 232 now, in the onCreate() function, we build our winner array again, just as in the GUI. We also default our current-play array to zeros. Note our indexing starts at zero always, in JavaScript.


Pop down to line 282. This is our exposed function SetMoveX(), which just stores the given index to the Player X values. SetMoveO() does the same for player O.
Down to 295, GetMoveO() is the main function that calls the whole AI mechanism and gets the index of the next best move.
GetDataAt just gets the value at a given index.

Finished Game with AI

This is by no means the best implementation of Tic-Tac-Toe, but hopefully there's something useful here in having a whole game detailed from various levels of implementation.

Continue to the next page for an addendum with some bug fixes since this tutorial's initial release.

Comments

0
Kain The Supreme 2,902 rep

Hi,
Beautifull tutorial...
but there is a little bug....
I have tried the on line version that U made... and if I Click over the already assigned "O" It turn from "O" to a "X" and I can win in a easy way!

I fallow your tutorial and re-create leaving the debug visible... and the AI set the position of his move with a delay of one move.

This bug is present only on the AI version, without the AI all works correctly.

In to simple word
The game start
I choose the upper left corner
100
000
000

the Ai choose the midle point

100
020
000

But in the real state the array is
100
000
000
I try to upload an image to show
http://imageshack.us/photo/my-images/818/errorrmp.jpg/

Sunday, December 09, 2012 at 4:53:37 PM
0
Kain The Supreme 2,902 rep

I forgot to said that I was forced to edit this line

from this
"rotatable": true
to this
"rotatable": false

on the edittime.js of your plugin to works on C2!

Sunday, December 09, 2012 at 4:57:05 PM
0
blackhornet 138.1k rep

I've added an addendum with the bug fixes. Thanks @Kain_The_Supreme!

Thursday, December 13, 2012 at 4:12:47 AM
0
Kain The Supreme 2,902 rep

@blackhornet Thanks to You for this bug fix and to help other people to learn C2!!

Thursday, December 13, 2012 at 2:41:59 PM
0
blackhornet 138.1k rep

@Kain_The_Supreme found yet another bug. This was bigger, so I've had to rewrite chunks of page 1. The fix wasn't that hard, but it really mucked up my tutorial. Hopefully this isn't too complicated to follow anymore.

Friday, December 21, 2012 at 5:10:01 AM
0
Kain The Supreme 2,902 rep

Thanks Blackhornet once Again to fix, correct and to share with us °_°

Monday, December 31, 2012 at 1:53:13 PM
0
Kain The Supreme 2,902 rep

U got a msg!
^^'''

Monday, December 31, 2012 at 3:35:36 PM
0
blackhornet 138.1k rep

Yet another bug fixed, introduced with the previous fix. Added totalMoves counter on line 124 of the plugin to make sure the special-case logic is only performed after the very first move. Thanks again to @Kain_The_Supreme.

Sunday, January 20, 2013 at 11:33:00 PM
0
Kain The Supreme 2,902 rep

@blackhornet Was an Honor, good tuts and plugin!

Monday, January 21, 2013 at 2:21:24 PM
0
AbelaNET 14.8k rep

Thanks for sharing

Friday, March 25, 2016 at 9:33:45 PM

Leave a comment

Everyone is welcome to leave their thoughts! Register a new account or login.