Modeling board state0:00
Well, that last episode was a long one, and I think we've earned ourselves a breather. So in this episode, all we're going to do is build a very basic local version of Tic-Tac-Toe in Vue. The first thing we're going to need is some way to represent the board state at any given period of time. So let's create a constant called boardState, and we want it to be dynamic, so we'll make it a ref. And seeing as there are nine separate squares on a board of Tic-Tac-Toe, I think it would make sense that we do this using an array with nine items inside it. There are three states a square can be in, right?
make sense that we do this using an array with nine items inside it. There are three states a square can be in, right? Blank, so it's not been selected or chosen yet, a cross, or a nought. So we could use a string representation for this. An empty string would be blank, a cross would be obviously a cross, and a nought would be a nought. However, we're going to have to perform mathematical calculations on this array in order to determine whose turn it is, in order to determine who's the winner, in order to determine if there's a stalemate. And whenever you're dealing with mathematical calculations, it makes much more sense to
a stalemate. And whenever you're dealing with mathematical calculations, it makes much more sense to work with numbers. So how about we represent crosses and noughts with a -1 or a 1 respectively? And if the space is empty, if it's blank, we'll use a 0. So what we'd have is an array with nine 0s to start, 0, 0, 0, 0, 0, 0, 0, 0, 0. That's quite difficult to type actually. Anyhow, so let's say player one, who's X, who's crosses, they go and they want to place an X in the top left corner. Well, we would change this first item to -1.
an X in the top left corner. Well, we would change this first item to minus one. And then player two goes, who's noughts, and they want to place their nought bang in the center of the square. Well, that would be position five, right? Because you've got square one, two, three, four, five. So one, two, three, four, five. And that would be a one. And this is how we would represent board state. So let's now transform that board state into something we can actually see on the front
Rendering the grid2:04
And this is how we would represent board state. So let's now transform that board state into something we can actually see on the front end. I think I'm going to use a MenuItem here seen as it's essentially a list of buttons that we're going to click. So we'll add our list item v-for, and we want to loop over each square in the current board state. And then inside here, well, if the square is empty, we'll want a button we can click. Otherwise, we're going to want a span that either says X or noughts or crosses. Let's first of all say v-if on the button.
Otherwise, we're going to want a span that either says X or nought, noughts or crosses. Let's first of all say VIF on the button. And we only want to show the button if the square is equal to zero. I can't even spell square. Let me try that again. If the square is equal to zero. And just for a second, I'm going to put a simple dash in the button so that we can see it on the front end. Then we'll add a VELTS to our span. Let's set VTEXT equal to a ternary operator.
Then we'll add a VELTS to our span. Let's set VTEXT equal to a ternary operator. Is the square equal to minus one? If it is, then we've said that minus one represents crosses and anything else, essentially one would represent noughts. Okay, let's see if we can see anything on the front end. Yes, we can. So as you'd expect, the board state has minus one in the first square. So the first thing we see is a cross. But if we go down to what we'd expect to be the center of the board, we see a nought.
Styling with CSS grid3:23
So the first thing we see is a cross. But if we go down to what we'd expect to be the center of the board, we see a nought. Of course, it's not really much of a square at the moment. So let's use CSS to pretty things up. It would be foolish not to use a CSS grid for this. We'll say grid, grid-cols 3. Let's add a little bit of a gap between each item. And I think if we did like width: 0 and then a min-width: fit, that should work quite nicely. Yeah, that's a very small grid, but it's a grid all the same.
quite nicely. Yeah, that's a very small grid, but it's a grid all the same. So then maybe on each list item, we could say class size 32. And what if we made all of these items grids as well? And then we use place-item-center. How would that look? Hey, it's already starting to look pretty good. Okay, let's set a background color. bg-gray-200 perhaps. Nice.
BG gray 200 perhaps. Nice. I want to center this entire thing. So mx-auto, maybe mt-12. Very good. And then, hmm, the text could do with being a little larger on these spans. Let's say text-4xl and font-bold. And I think on any square that's been filled out, I sort of want the background to be a little darker. So what if we said bg-gray-300?
little darker. So what if we said bg-gray-300? Well, it has made the background darker, but they don't fill up the whole square. Tell you what, seeing as actually we'd want to be able to click anywhere in this square, and at the moment, the button is only very small. Why don't we change this around? Why don't by default, we set the background to 300? And then we'll remove this background here. And then on the button, we could say class equals, is it place-self-stretch? Yeah, I think that's it.
And then on the button, we could say class equals, is it place-self-stretch? Yeah, I think that's it. place-self-stretch with a background gray-200. There we go. That's much better. So now we can see the squares that have already been filled out are dark gray. But we can click anywhere in these other squares to be able to activate them, to be able to make our selection. I think it also makes sense to have a little hover animation going on on the buttons themselves.
This is just a simple explanation with no code.
Handling square clicks5:57
Cool. So obviously, we now have a cool looking grid. It looks like a tic-tac-toe grid, but when I click the buttons, nothing happens. So let's update our view code to make sure that we can actually fill the grid out with northern crosses. We want to listen for a click on the button itself, so at click equals. And why don't we call the function fillSquare? Very good. And I'll need to pass the index of the current square I've clicked. So we'll accept that in the v-for.
And I'll need to pass the index of the current square I've clicked. So we'll accept that in the V4. Square, index, pass the index in. And then let's come down to our script here, and we'll create a const called fillSquare, which is where pretty much all of the logic for this particular thing is going to go. So fillSquare equals a closure, receives the index we've clicked. And to fill a square, as we've said, you literally take the boardState at the index you've been given, and you fill it with either 1 or -1. So just to test this, let's say boardState.value at the given index equals,
at the index you've been given, and you fill it with either 1 or -1. So just to test this, let's say boardState.value at the given index equals, and we'll set it to -1, which we've said represents a cross. If we come back to the browser now, I'm hoping that I can click these squares, and sure enough, I can fill the entire thing out with crosses. Not a very fair game of tic-tac-toe, but it is a game of tic-tac-toe. To make the game a little more fair, we're going to need to alternate between players. We could do that using a very simple boolean that we'd flip after you click each button in view. However, I think there's actually a better way to do this.
Alternating player turns7:26
after you click each button in view. However, I think there's actually a better way to do this using nothing more than the existing state that we already have. If you take, let's say, an empty board, and you sum up all of the values in this array, you end up with 0. Let's say that crosses always go first. So cross plays their first square, let's say here, and you sum up the array again. What do you end up with? Minus 1. OK, so minus 1 indicates that crosses have gone first.
Minus 1. OK, so minus 1 indicates that crosses have gone first. Then noughts go. They play here. Sum up the array again. What do you come to? Well, you're back to 0. So whenever the array is at 0, that means that its crosses turn again, and they take their go, you sum up the array, you end up at minus 1, back to noughts, and they place 1, it goes back to 0, and its crosses turn.
and they take their go, you sum up the array, you end up at minus 1, back to noughts, and they place 1, it goes back to 0, and its crosses turn. And you can use that exact same simple calculation to determine the current player. So let's get that added to our script. Let's calculate this in a computed property, const, and I'll call it crossTurn or xTurn. xTurn equals computed. I'm going to pass a closure. And the easiest way to sum up an array in JavaScript is using reduce.
I'm going to pass a closure. And the easiest way to sum up an array in JavaScript is using reduce. So we'll say boardState.value.reduce. We accept the carry and the given value of the item. We just need to add those two things together, carry plus value, and the initial value we pass to reduce is going to be 0. Of course, that will give us the sum of the array. We need to determine if that sum means that it's x's turn or not. So we'll say, is it equal to 0? If it is, then it's x's turn.
So we'll say, is it equal to 0? If it is, then it's xTurn's turn. So with that completed, we can come to fillSquare, and we can say, what's the current value of the xTurn boolean? If it is xTurn's turn, then I want to set this to -1, which represents an x or a cross. Otherwise, I want to set it to 1, which represents a 0. Let's see if that works. Given the current state of the board, we would expect that it would be a cross in the next square.
Given the current state of the board, we would expect that it would be a cross in the next square. If I click, yes, indeed. And here's the moment of truth. Will it add a 0 when I click here? Yes, it will. So if we go back to our little array here, and I'll go back and replace all of these items with a blank slate. There we go. We should now have an empty board to work with.
There we go. We should now have an empty board to work with where you can actually play a local game of noughts and crosses. X goes here, then nought, then crosses, nought, crosses, nought. And although we're not automatically calculating a win state, if you were playing with a friend next to you, you'd be able to say, ah, I just won. So there we go. We actually have a basic working version of tic-tac-toe in the browser. But of course, this is just the start.
Planning win detection10:13
We actually have a basic working version of tic-tac-toe in the browser. But of course, this is just the start. And as I already said, we're not automatically calculating any win condition here. Let's think about how we can do that in our next episode.
