Tic-Tac-Toe - Part 2

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

Part 2 - Creating a tic-tac-toe game with AI.

UPDATE UPDATE: there's actually a major bug in the plugin. See here for details and a substitution: Part 2.1

UPDATE: since part-2 was released, there have been two bugs found. I've had to rewrite this part of the tutorial to fix these issues. I'm now taking advantage of the built-in Function plugin which, if I had this in the part-1 tutorial, would have made the complete tutorial simpler to follow, as I now have to move things around.

This tutorial will take our playable manual game and add the few extra events needed to call the AI algorithm implemented in JavaScript for a player-against-computer game.

Start with the previous tutorial's CAPX file: Part 1

In the layout, insert new item BHT Tic-Tac-Toe AI.

In the layout, add two instance variables to the Tile sprite. Name them BoardX and BoardY. Also, change the font size on LogText to 6, as we will be added more logging.

Insert the General:Function object.

In the Event sheet, at event (2), set the new instance variables as follows:


At this point we need to add two functions as we need to call the code several times, and a function is the perfect mechanism for this.

At the end of the event sheet, Add Event: Function ->On function. Name it "LogArray". Now copy the logging code at events 10 & 11 to this function. You can't copy as a sub-event, so with the copied events still selected drag them right onto the function, and they will move under the function. Change event 10 from Set to Append: "--" & Function.Param(0) & "--" & newline


Now Add Event: Function->On function. Name it "CheckForWinner". Now, this is a bit cumbersome, but we want to move our check-for-winner logic at Event 12 to our function, as we need to call it once after the player moves, and then again after the computer moves. We need to juggle a bit here, so on "CheckForWinner", Add->Add blank sub-event. This gives us somewhere to add our local varibles. (C2 won't let us copy the current locals, so we need to redo this step). Remake the three "Current" local variables on the blank event: CurrentA, CurrentB, CurrentC.

Now we can actually copy the Event 12 code (select on the very left of the event to get the sub-events also), and paste on the blank event. Delete the blank event.
Open full size image

At event (7), Mark an X – Board: Is CurrentTurnIsX, we add the extra action: BHTTicTacToeAI:Store a move:Move Player X:index=Board.UID2LY(Tile.UID)*3 + Board.UID2LX(Tile.UID). Since the AI uses the 0-8 indexing, we convert our XY coordinates from the Board object in two-dimensions to one dimension, by multiplying Y by 3 to get the row offset and adding X to get the column offset.


At event 10, we want to use our new functions to call our code that we need to repeat for the player's move, and the computer's move.
Delete the action: LogText:Set text to "". Add action: Function:Call function:"LogArray", and add parameter "player". Add action: Function:Call function:"CheckForWinner". (We don't need parameters).

Select event 11, and Toggle disable. It isn't needed any more, but deleting it would move the event numbers around, and I'm trying to avoid this for the sake of the tutorial.

To automate Player O, we need a new event between events 11 and 12. Select event 11, right-click that event and do an Insert new event below. Select Board:Is boolean instance variable set. Choose CurrentTurnIsX, but immediately alter it with an Invert on the condition. We are checking that it is NOT X’s turn. Add a second condition: GameDone=0. This stop play if the game is over (X wins before).


Create a local variable on our new event: PlayerOMove. Add action: System:Set value:PlayerOMove=BHTTicTacToeAI.GetMoveO.

Create a new sub-event under (12) Board… System:Compare variable:PlayerOMove >= 0
Now we store the move. Add a new action, BHTTicTacToeAI:Store a move:Move Player O=PlayerOMove.

Create two local variable on this new event: tileX, tileY. Add action tileX=PlayerOMove%3, and tileY=int(PlayerOMove/3). This just converts our 0 to 8 index to two dimensions again. "%" is the modulus operator which gives the remainer of a division, that is, 7 % 3 is 1, since 7/3 is the same as (6 + 1)/3. We throw away the part that is a multiple of 3 and just care about the remainder, 1. This gives us our X index, and the integer value of the division by 3 is our Y, so from single to double to single we get:
(1,2) is row 2, column 1, converted to one dimension is 2*3 + 1 = 7. 7%3 is 1, and int(7/3) = 2, or (1,2) again. (Again, to get back to our human readable grid, add one to the PlayerOMove to get our 1-9 scheme, from the grid in Part 1).

Now that we have the correct coordinates for our Board object, we create a new chess piece. To keep our local CurrentPlay array up to date, we set its value to 2, the O player’s value. And we toggle our current player again.

Now select event 13, and Insert new event below:System:Pick All:Tile. Add a second condition: System:For each:Tile. On event 14, add a sub-event: Tile:Compare instance variable:BoardX=tileX. Add a second condition: Tile:Compare instance variable:BoardY=tileY. Add an action: Tile:Set visible:Invisible. This marks that tile as used, and can't be played.

We now add one more event to check if Os move wins the game. Select event 12, and do an Insert new event below. C2 doesn't let us add a blank peer event, so we just create any event for now:System:Every X seconds:1.0. Select the condition (not the event) and delete. Add action: Function:Call function:"LogArray", Add parameter: "computer". Add action: Function:Call function:"CheckForWinner".


Now select the check-for-winner event below and Toggle disable, as we don't need this anymore.

You can now play against the computer, using an AI algorithm. I'll discuss the algorithm next.

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 115.6k 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 115.6k 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 115.6k 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.7k 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.