Next lesson playing in 5 seconds

Cancel
  • Overview
  • Transcript

5.5 Making Moves: The Event Handler

Now that we're displaying the board, the obvious next step is to let the users move their pieces. In this lesson, we'll write an event handler that determines if a piece can be moved or not, and if so, where we can move it to.

5.5 Making Moves: The Event Handler

Now that we can display a chessboard to our users, the next step is to allow them to actually make moves on their turn. So in this lesson, that is what we are going to do. Now here in the game.js file, we already have a helper. So underneath this helper, let's add Template.game.events. And we're going to wire up a click event. So of course this click event will take place on our table data elements. Now this is going to be a little bit complex. So let's think about what exactly is going to happen here. When the user clicks on a cell, what exactly do we need to check for first? If this is the first cell that they're clicking on, then we want to check to see if that piece can move. If that piece can move, then we'll go ahead and select that cell. If the piece can't move, then we'll just ignore that click. All right, so then, however, if they click a cell, it's also possible that they have already selected a cell and they're clicking a second cell. So what happens if this is the second cell? Now first of all, I guess I should tell you that when we select a cell basically we select it and we remember it, right? So that we know there's already a cell that has been clicked. And that's when we know that we have a second cell. If we have a click but we already have a cell that was clicked, well then there are a couple of things to check. First we can check to see if this is actually the first cell being clicked a second time. And if it is, well, then we want to deselect that cell and basically forget that cell, right? If it's not that cell being selected the second time, then the question is can the piece on the first cell move to the second cell? If it can, then we'll go ahead and we'll make the move. Otherwise, if it can't, then we'll just ignore that click and assume that they made a mistake. All right. So this is basically the outline for the function we want to write here. And now that we know exactly what behavior we're looking for, this will be a little bit easier to write. Now, there's actually something that we have left out at the very beginning. And that is before we even start wondering is this the first cell or the second cell? We need to know is this user allowed to make a move right now. Is it their turn? So let's start in this function by getting the game data which we'll just call data. And we can use our getGame function for that to get our game record from the database. And then we need to see if it's currently this user's turn. Now, the color of the user's turn, w for white or b for black, is actually the second field in the Forsyth-Edwards Notation. It keeps track of whose turn is next. So we know that the Forsyth-Edwards Notation is data.board. And we also know that the fields are divided up by a space, so we'll split it on the space. And we know that the second value, or the value with an index of one, is the color of the user whose turn it is. So, now this value right here is either going to be w or b. So, then we can do data square bracket and we put all of that inside those square brackets. And that is going to get either the b or the w property from our data object. And that is going to be the ID of the player whose turn it is. So, we can say if that ID is equal to. Actually let's say if it's not equal to Meteor.userId, then let's just go ahead and return because it's not that user's turn so we can just ignore their clicks. Otherwise we need to start going through our tree of decisions which we have already written out here. So let's start by seeing if this is the first cell. We need to be able to keep track of the first cell so that when we actually select a second cell we'll remember what the first cell is. So let's create a new variable up here and we'll call it selectedCell and it'll start off by being null. Then we can say if there is a selected cell, we know that we're working with the second cell. However, if there's not, we know we're working with the first cell. So what do we need to do if we're working with the first cell? Well, we need to check to see if this piece can move. So we're gonna write a canMove function. So let's say if canMove this.cell. Now you might wonder where are we getting this.cell from? Well, if we look back at our template here, we know that this function is going to be called within this data context right here. Right? Because this is the table data element that's being clicked. So when we click on this table data element,it's going to have the same value of this for whatever our data context is here. So it's going to be the individual cell. So this.cell is actually the cell property on our cell object. Now we know from the makeCell function that, let's see where that is right down here. Cell is going to be the coordinates of that cell and so we can use the coordinates to figure out if any piece on that cell can be moved. So let's see how we might write this canMove function. Well, to figure out if a piece on this cell can actually move anywhere, we're going to need a chessboard. So let's go ahead and create a chessboard. We'll say var chess equals new Chess and we'll pass it data.board. Remember data.board is our Forsyth–Edwards Notation. And the Chess library we're using accepts Forsyth–Edwards Notation as a parameter to the constructor. By the way, I don't think I've mentioned this but I'll have a link to the documentation for this Chess library underneath this video. So you can learn a little bit more about all the functions it offers. Otherwise just pay attention as we go along and I'll tell you about the functions we need. The first one of those functions that we need, actually, is chess.moves. So because we need access to this chess object, we're gonna have to keep our helper function within this function here. But that's fine, we can do nested functions. So we'll say canMove and we have a from cell, right? So this is our starting point. So we can say, var moves equals chess.moves and we pass an object. And we have the square is from. Now remember from here is going to be the cell ID. So it's going to be something like e4 or it's going to be something like h8, something like that. And so because we've loaded up our chess data into this board, it knows where everything on the board is. It actually even knows whose turn it is and so if we click on someone else's piece, even if that piece were able to move for that player, we won't be able to move it because it's not our turn. So basically chess.moves will return an array of all the possible moves of the piece that we're starting with. All right, now we will extend this canMove function a little later on, but at this point, all we really need to know is are there any moves possible from this square? Because if there are, then we're going to allow them to select that square. So we can return moves.length is greater than 0. All right, so, if we are selecting the first cell and if that cell can move, then we want to go ahead and select the cell. And we need to remember two things here, actually. We need to remember the cell data, which is this, right? Of course, this is the data about this cell, but we also need to remember which HTML node is being clicked and I'll tell you why in just a second. So let's add another variable here. I'll just duplicate selectedCell and change this to selectedNode. And so then down here where we call select, let's pass it in the evt.target which of course is going to be the element that we clicked. And then we'll pass it this which is the data for the cell that was clicked. So this select function can go outside of our event handler here. We'll say function select, and it takes a node and the data. And let's do a couple things in here. We'll set selectedNode equal to the node and selectedData equal to the data. And finally, let's set selectedNode.classList.add and let's add the class selected. And this is why we need to remember the node so that eventually later we'll be able to deselect the node which just means remove the selected class. Now if we go back to our style.css file, let's add a record for this. We can say td.selected. And let's add a box shadow here and the property here will be box-shadow. We'll say inset 0 0 15 pixels and then let's add a color here, 03A9F4. All right. Now even though we've hardly written any code here for this event handler, we should be able to see this in action. If we come back to our board, and I try and select any piece, notice that because I am the black player, obviously, and white always goes first, I can't actually select anything. However if I move over to a different game where I can go first and I click on a piece. Notice that I do get our little shadow there which lets the user know that that cell has been selected. There we go and you can see any cell that could be moved actually will work. So you can see I've just selected all the pieces that can move. Now, obviously this isn't really the behavior we want, but it does show us that we are on the right track. Let's go back to our list here and we've dealt with the first cell. So now we need to worry about our rules here for what happens when a second cell is clicked. All right, so if there is a selected cell, then the first thing we need to do is check to see if this is the same cell that was just selected. So we can do is say if selected cell, which is remember our data, .cell is equal to this.cell, meaning the two cells have the very same algebraic notation address, then let's call our deselect function. And we have to go ahead and write that. So underneath select here, let's write deselect. And as you might imagine it's basically the exact opposite. So i'll just copy these lines. And we'll set selectedNode to null. We'll set selectedData to null. And actually, before we set selected node to null, let's go ahead and say classlist.remove the selected class. And this makes it very easy for us to deselect the cell. If we go back and give this a try, you can see that one click selects it. And if I click it again, you can see actually, nothing's happening. And this is because if we go back and look at our selected cells here. It looks like I’m not using the right value. Okay, somehow I said selectedData instead of selectedCell and you know what, actually selectedData is actually more accurate term. So let's go ahead and change this to selectedData, okay? So if the selectedData.cell is the same as the currently selected cell. Let's see what happens. If I click it once, it's selected. If I click it again, it's deselected. Excellent. Okay, so so far, things are looking good. We can deselect. Next, if it is not the same cell, we need to decide if this piece can move to that cell. What we really need is to decide can this piece move from a certain cell to a certain cell? So what we need to decide now is can the piece we first selected moved to the cell we selected second. So that goes in this else block here meaning if we did select a cell before, and it's not the same cell we selected now. All right. So, what we need to do is find a move, if such a move is possible. So, we're going to modify our canMove function so that it will work with a from and a to. Meaning, not just can this piece move anywhere but can it move from here to there? Now let's first see what exactly this list of moves looks like. If we do a console.log(moves), you'll be able to see exactly what this looks like. And right now, if I click this pawn, for example. You can see that it can move to e3 or to e4. And those are the two moves possible. And let me deselect that first and click on this knight. Then you can see that we have knight to f3 and knight to h3. And this immediately shows you what's going on here. The moves that are listed here are not just the cell IDs, if you will. They are actually the algebraic notation for that move. So we have knight to h3. Now the problem here is that if i click this knight first and then I click h3 here, for example. The ID that we have for h3 is h3. And so we can't just look in this array and say, okay does h3 match any of the items in that array? It does match one of the item but it's not exact. It's Nh3 which is the full syntax for the proper move. So what we need to do is say do any of these move strings have the address that we're looking for inside them. And we can do this pretty simply by doing moves.reduce. Now if you're not familiar with the array reduce function. And don't worry, we're going to organize this function properly in just a second. The way it works is you loop over an array taking the previous value and a new current value. And if you want, you can pass in an initial previous value which we will do is false. Right so, moves.reduce is going to basically reduce the entire array into one value. And we can do that by first receiving our starter value. And then the first value in the array and we return some value for that. And then whatever we return there will be our new previous value and we'll get the second item in the array. And so we just loop over each item in the array this way and whatever we return at the end will be the value of moves.reduce. So we're gonna use this to look through our moves array and find out if anything matches. So what we can do is if there was a previous value, then let's just return that previous value. Right, that's fine. Previous by default is going to be false and previous will be whatever is returned from this function. So if any point previously, we've returned something other than false, then we know that we've already determined that our piece can move to that cell. Now we need to take to as a second parameter to this canMove function. So, that's how we know what cell we're moving to. Previous is still false, meaning we haven't yet found the cell that we can move to. Instead, let's return curr, which is the current cell, .indexof, so we're gonna search within the string for the address of the cell that we're looking for. Right? So if this is going to be something like knight moves to h3, or even something like knight captures on h3 and puts the king in check. Our address could be as complex as something like this. Then basically what we're trying to do is find out is h4 here within this string. So if current, meaning whatever our move string will be, .index of to, meaning our address, if that is greater than minus 1, then we know that current is an acceptable value. So this is, of course, Boolean so that's going to be true. So we'll use a ternary expression here and say, if curr is the move that we're looking for and we'll return curr. Otherwise we'll return false. Okay this function call right here, moves.reduce, is going to either return false or it's going to return the full move to the address that we're asking for. Okay, now obviously this won't work right now because this is all underneath the return value here. So what we need to do is, within return here, let's use a ternary expression. And we can say if a to value was, and actually let's say, if a to value was not passed in, then we'll just return moves.length is greater than 0. So if there was no to value, we know we just have a from value. And we're asking canMove can the piece on the from cell move. And if moves.length is greater than 0, then yes it can. Otherwise, if there is a to cell, we're asking something specific. We're asking can the from piece move to the to cell. And so if that's the case, then we'll go ahead and return whatever value returns from moves.reduce. And that is our expanded canMove function. All right. So back up here, var move equals canMove from and to. So, this is either gonna return false or it's going to return the full algebraic notation for the move that we're trying to make. We can just say, if move, well, then let's go ahead and make the move. So we'll call Meteor.call makeMove. And this is a Meteor method that of course we have yet to write. And we'll write it in the next lesson. Data._id is going to be the game ID, right. We need a way for the function to find the game. And then move will be the move that we are going to make. And finally we can do deselect to deselect that cell and that way we're ready for the next move. So we don't have a makeMove function written yet but we should be able to see everything in action up to this point. So if we come back to our game here, since it's white's turn and you can see if I click on one of these cells that can't move right now, we get no response at all. If I click on a cell that can move, the cell is selected. Now if I click on that cell again, it's deselected and I can select a different cell. If I click on a cell that can move and then click on other cells where it cannot move to, notice that we have a problem from is not defined on line 31. Okay, and, yes of course, I've used from and to here but we actually need to refer to actual cells. So the question we're asking is can we move from a selectedData.cell to this.cell. Right, okay, so if we click here. And then we click on a second cell where we cannot move to. Notice that absolutely nothing happens on any of these. However, if I click on a cell that this knight could move to, we get an error. Error invoking method makeMove, method not found and this cell is deselected because deselect was called. Okay, so that means we have our front-end behavior working perfectly. In the next lesson we will write that makeMove method.

Back to the top