[WHISTLE BLOWING]
Hello, and welcome to a coding challenge--
Tic-Tac-Toe.
I'm going to make Tic-Tac-Toe.
I'm hoping, when you look at how long this video is,
it's very short, because I don't have a lot of time right now.
So I'm going to try and make a very beginner-friendly example
of the game Tic-Tac-Toe without any bells and whistles,
without a lot of fancy code.
I'm not going to overengineer it.
I will come back and--
[MUSIC PLAYING]
(SINGING) Oh, you will refactor this later.
You know I will--
I will refactor later, because the reason why I am making this
is I eventually want to show you some different algorithms
for an AI or at least a bot to play the game Tic-Tac-Toe.
But that's not happening in this video.
We're going to make the most basic, simple, friendly version
of Tic-Tac-Toe right here, using JavaScript, the p5.js library,
and the p5 web editor.
Follow along, if you wish, and see what happens.
I have not practiced or planned for this at all.
All right, I need a board.
So I definitely need a board.
I'm going to say, let board-- and it's going to be an array.
And maybe, it'll be an array of arrays.
And let's use strings.
This is probably a terrible idea.
So this will be the top row.
This will be the middle row.
Tic-Tac-Toe is 3-by-3-by-3.
And then I need two players.
So player one is an X. And player 2 is an 0.
So now, I need to render the board.
Let's put some stuff in it.
Let's just pretend it has some stuff.
So I want to render the board.
I could use this with dom-- there's
so many ways I could do this.
I'm just going to do this with Canvas and in the Draw Loop.
So I'm going to say, for let i equals 0, i is less than 3,
i plus plus.
For let j equals 0, j is less than 3, j plus, plus.
So this is pretty tricky.
If you're a beginner programmer, this is a nested loop.
And I've basically made this a grid.
So every spot in this grid-- and let me actually fill it out
just so we see something here.
I'm going to pretend this has just been played.
So the idea is that this two-dimensional array
structure--
this list of lists--
is storing all the information for what
the current state of each cell in the Tic-Tac-Toe grid is.
At the beginning, they're all blank.
And then, as the players play, X's and O's get filled in.
So now I'm going to nest a loop through--
check every column, check every row, and render something.
So I could just use the Text function.
I could say, let spot equal the board index i index j.
And then I could say, text that spot at x, y.
So where is x and y?
So I need a width.
I'm going to fill the whole canvas.
So width equals the width of the canvas divided by 3.
And height equals the height of the canvas divided by 3.
It would make sense for me to have players in an array.
And maybe I could have 3.
It doesn't have to be a hardcoded number,
because I could make, like, a 5-by-5 tic-tac-toe board.
But I'm doing this in the simplest way possible.
So x equals width times i.
And y equals height times i.
And let's run this.
What's going to happen?
Do you see anything?-- some X's and O's?
They're sort of in there.
And then I'm going to say, text size 32, to make it bigger.
Why are they all on top of each other like that?
Oh!
I forgot j here.
There we go.
Look!
There's my tic-tac-toe board.
But things are kind of off.
Oh, because of the way--
you know what?
I should just draw it as a circle.
Let's do this.
If spot equals player 1, then draw an ellipse at x, y width,
width.
And then otherwise, if spot equals player 2,
then draw a line from x, y to x plus w, x plus h,
and another line from x plus w y to x, y plus h.
So that's me drawing an x.
Whoa!
That looks totally wrong.
So first, let me say, no fill.
And the offset should be plus w divided by 2.
So we need to offset all of those spot.
[TRILLS TONGUE]
Ugh!
Oh, and then this would be--
oh, no, no, no, no.
No, no, no, no.
Oh, my god!
Help!
Let's diagram this.
I have a 3-by-3 board.
This is x equals 0.
This is x equals 1.
This is x equals 2.
This is y equals 0.
This is y equals 1.
This is y equals 2.
So an x should be drawn from here--
from x, y, x, y to x plus h, y plus h.
So the x I did correctly.
Let's go back to here and comment out the ellipse.
And let's make the board all full of X's.
Let's just make the top row all full of X's.
Oh, my goodness.
[BUZZING NOISE]
Everybody, I just lost, like, two or--
like, 45 minutes-- not really that long--
by accident, because I had an x here.
This needs to be y.
OK, so now we can see the X's.
I could see the X's.
Let's draw the grid.
We could see the X's.
Oh, those are really giant X's.
Now, let's put the circles back in.
You can see, there's-- ah!
The circles need to be--
I need to say, ellipse mode corner.
There we go.
Oh, boy!
So the X's are all kind of connected
in a way that looks weird.
So actually, wouldn't it make sense
to not draw everything relative to the corners,
but to draw everything relative to the centers?
And I could have just used text-to-line and gone back
with the letters.
But I want to draw it.
I want to draw it.
So I'm going to draw everything relative to the center.
You'll see.
This is going to improve it.
Boy, this is really going super well so far.
So what I'm going to do is say, each X
is it's index into its column and row times the width
plus width divided by 2 plus height divided by 2.
That offsets everything by 1/2.
And then I'm actually going to say, the size--
I'll call this the X size.
I'm going to have this equal to w divided 2--
half of that.
And then I'm going to say, x minus x size, y minus x size
to x plus x size, --let's just call
this xr, kind of like the X's radius.
And then this one will be plus xr to y minus xr
and then to x minus xr to y plus xr.
And then, width divided by 4--
there we go!
There's my X's.
And let's make the stroke weight 4.
There's my X's.
And now the O's--
no more ellipse mode.
And let's make this w divided by 4 or divided by 2.
There we go!
Look!
So this is what the tic-tac-toe board looks like.
Yeah!
I like the way it looks better now.
That was painful.
So now, let's set it up as blank.
Oh, and by the way, I've reversed my x's and y's.
So actually, the x's should be j.
And the y's should be i.
Actually, let's just do it the other way around.
Let's keep x's i and y's j, but do
the j loop as the outer loop, and the i
loop as the inner loop.
There we go.
OK-- whoo!
So now, what I want to do next is play the game.
So let's have a variable called currentPlayer.
And currentPlayer is equal to player 1.
Let's randomly pick between player 1 and player 2.
So every time we start the game, the currentPlayer--
and let's actually-- let's make this an array.
I like the idea of making this an array for some reason.
And let's have the currentPlayer be
player's index 0 or the currentPlayer
is players index 1.
And then this-- we can make--
0 is a circle.
0 is the x.
I did it right.
0 is the x.
Index 1 is the circle.
What's going on here?
Where's my error?-- line 44--
too many brackets, huh?
Oh, I need a closing bracket for setup.
OK, come back, board.
And let's make the background a 255.
Oh, this is very silly, because I can say-- the whole reason
for me doing that is, in p5, I can say,
currentPlayer equals Random Players.
So this will pick a random player.
I really need to see those lines.
So let's draw the lines.
Let's draw a line from w0 to w height--
oh, no, no, no.
So to w-- the whole height.
Then we'll draw a line from w times 2.
So I just want to draw that criss-cross.
And we'll do the same thing for 0 to width.
And this will be h to h and then h times 2 to h times 2.
Now, really what I should do is make this now interactive,
so that you could click and add the x or the y.
I think I might leave that as a little challenge to you.
I'm just going to have the computer play the game
Tic-Tac-Toe and see if somebody wins.
So either the board will be full and it's
a tie or somebody will win.
And I won't use any intelligent algorithm.
I'm just going to have each player pick a random spot.
So what I'm going to do is I'm also
going to make an array called Available.
So each available spot--
in the beginning, I'll just say--
I'm going to make a nested loop.
So much for making me be super beginner friendly.
So I'm going to say, available.push--
an i and a j.
So right now, every little pair of index values for that grid
is available.
So each time through Draw, let's make a function
that's called nextTurn.
And we'll say, spot that I'm picking is a random-- oh,
I'm going to have to get an index--
index-- because I want to remove it.
Index is a random number that's between 0
and the length of how many things are available.
And then the Spot is going to take that array, Available,
and remove that index value.
The splice function will remove it and put it in Spot.
And then I'm going to say, board spot 0 spot 1--
oh, that's so awkward.
But spot is a little array with two values in it, 0 and 1.
And so this is not a comma.
It's another little bracket.
Ooh, look how horrible that looks.
Let's do, let i equal spot index 0.
Let j equal spot index 1.
And then, in the board ij, I'm going to say--
[TRILLING NOISE]
--the player-- currentPlayer And then currentPlayer should
equal--
let's just pick a random player.
This is not the right way to do it.
Let's just see if this works--
mousePressed, nextTurn.
So every time I click the mouse-- uhhh!--
currentPlayer is not a thing?
When I splice it out, does it come in an array?
Oh, how awful!-- it comes in an array.
So I need to do that.
There we go.
I'm filling up the board.
OK, I've done this in such a super awkward way.
I might want to rethink this.
But it does work.
You watching this will make a nicer version of this.
But I want to go back and forth between the players.
So currentPlayer should actually be an index into that array.
So I actually want to say random players.length,
because I want that to be an index.
Because then, when I am adding the things to the board,
I want it to be players index currentPlayer,
because the next player should be currentPlayer plus 1 modulus
players.length.
So I've built this in a way that you could
have more than two players.
So it should be O-X-O-X-O-X-O-X-O. Hey!
O won that one.
All right-- so this works with my wacky implementation.
Now, I don't need mousePressed to call nextTurn.
I just want draw to call nextTurn.
So it fills up.
But I also want to check for a winner.
Check for a winner.
So what I'm going to do--
I'm going to write a function called checkWinner.
And what I'm going to do in checkWinner
is see, first of all, if available.length equals 0,
then console.log toe.
So it was a tie.
So if the board fills up, it was a tie.
But first, I need to check-- is there a winner?
So let winner equal a null.
So I'm going to say, there's a winner.
Now, what I'm going to do is check all of the--
first, I'm going to check all of the ways across.
So let's first check horizontal.
So i is now the row.
So if board index i--
well, is that the row?
I don't remember-- 0 equals board index i 1 equals board
index i 2, then winner equals board index--
whichever one it is.
OK, so if all three of those are equal,
then the winner is whoever you picked.
I could also check now the columns.
I don't remember which is which, but whatever.
I'm doing one and the other.
Then if they're all equal, then I've got a winner.
So this would be vertical--
except it might be the other way around.
And now, I need to check diagonal.
So that's easy.
I just want to say, if board 0 0 is equal to board 1 1
is equal to board 2 2, then the winner is board 0 0.
And then I can also check, if board 2 0 is equal to board 1 1
is equal to board 0 2-- that would be the other diagonal--
then the winner is board 2 0.
And now, if winner is still null and available
is length, console.log tie.
Otherwise, console.log winner.
All right-- X is the winner.
But if there's a winner--
so let result equal checkWinner.
If result is not equal to null, then no loop--
stop the looping--
console.log result.
OK-- why am I getting errors?
Oh, OK.
Is nextTurn happening?
Ah-- nextTurn has to happen after this.
No-- oh!
[BELL DINGS]
I did something so nuts.
Look at this!
I was like, that doesn't work.
If this equals this equals that, that doesn't work.
This is the concept.
I'm going to write a function called equals3 a, b, c.
And I'm going to return if a equals b and b
equals c and a equals c--
so this is really the only way--
there's other ways.
But this will actually check--
if a is equal to b and b is equal to c and a
is equal to c-- if all of them are equal,
then all three of them are equal.
So I'm sure people have been screaming at their--
so now I can say, equals3--
and like this.
And then same thing here--
so this is checking all the horizontal.
This is checking all the vertical,
even though I have this mixed up.
This is checking the two diagonals.
If the winner is null and available, length is 0.
We have a tie.
Is that redundant?
Yes, this is redundant.
Yes-- whoops!
Where am I-- am I console logging somewhere else?
Oh, I forgot them.
Oh, I'm not returning the winner.
[BELL DINGS]
And I'm console logging a lot of other nonsense here.
I'm doing a horrible job at this.
This should be return.
I'm rushing.
You should never code and run.
Take a deep breath and relax while you're coding.
Return tie.
Otherwise, return winner.
Now-- oh, they could be equal!
They can't be blank.
They can't all be--
then somebody wins.
They have to be full.
So OK-- and a is not equal to blank.
There we go!
X is the winner.
Let's run it again.
X is the winner.
O is the winner.
X is the winner.
So let's at least make a createP winner style color FFF.
Oh-- result 32 point--
there we go.
That's what I'm looking for.
OK, now every time I run it--
tie, O, X, O wins.
And let's changed the frame rate.
We're going to make this super dramatic.
Frame rate-- 1.
OK, here we go, everybody.
O, X, O-- where's X going to go?
Oh!
X, O, X-- oh!
It was a tie.
Oh, no-- O won.
O won.
[CELEBRATORY MUSIC]
Congratulations, O. Let's play this one more time.
[RAGTIME PIANO MUSIC]
Here we go--
X, O. What will happen?
Place your bets.
X won.
Congratulations, X.
[RAGTIME PIANO MUSIC]
X, O, X-- come on, X. Oh, you can do it.
Oh, good work, X. Wow!
You really won that one.
All right-- thanks for watching this coding challenge,
where I made an AI play Tic-Tac-Toe against itself.
You can see how well it's learning.
X just keeps winning over and over again.
I will come back in a future second part to this.
I know I say this for so many coding challenges.
You think I'm never coming back.
It will be sometime in the next several years.
I will come back and fix this up a little bit,
as well as implement something called the mini-max algorithm,
to actually make thoughtful, smart decisions for how
to place your X's and O's for an AI
to learn how to beat this game and to always win Tic-Tac-Toe.
Ah-- and as a challenge to you, the viewer,
take this version of my Tic-Tac-Toe coding challenge
and, when X or O wins, draw a nice little line through it,
to indicate the winning.
I don't have time for that right now.
I should really add this to this.
But please add it to this.
Go to the Coding Train website, where
you will find this challenge on the website itself
and a place to add your community contribution as well
as--
a video tutorial about how to add your community contribution
is out now as well.
So I hope to see lots of Tic-Tac-Toe games
and computers playing them, people playing them.
And have a lot of fun making those.
And I'll see you in a future coding challenge.
Goodbye.
[BLOWS WHISTLE]
[MUSIC PLAYING]