Next lesson playing in 5 seconds

Cancel
  • Overview
  • Transcript

5.3 Display the Game

In the last lesson, we set up all the framework, if you will, for making the rows that we will display to our users. So in this lesson, let's actually go ahead and convert that Forsyth-Edwards Notation into an actual visible chessboard. So the first thing we'll do is we will create the rows. And what we'll do is say board.split, and we will split it on the first space as we saw in the last lesson. And we wanna get the first piece of that notation which will be the board arrangement itself. And then we'll go ahead and split that by forward slash. And so now, we have an array of strings. Each string being the Forsyth-Edwards Notation for that row. Now, let's create a variable called data, which is going to be the value we'll eventually return, our array of arrays of cells. So we'll rows.map and let's map over each individual row. And, of course, we'll pass row as the parameter to this callback. We'll also take i as the second parameter, which will be the index of the row. Now this is going to start with the row at the top of the board, which is actually the eighth rank because we count up from the bottom when we are facing it from white's side of the board. So i here will be 0 for the eighth rank, 1 for the seventh rank, etc. So let's create a rank variable here which will just be 8- i. And I'll just put a note here and this will be row number. Then, let's create a counter for the file as well. And the file, you'll remember, is just what we call the column in chess. Now these are going to be by letter, but we'll start with a number here, we'll have 0 for a, and we'll move up to 8 for h and we'll convert this number to a letter later on. Okay, so now we wanna loop over each row. So now we wanna loop over each cell in each row. And this is where things are gonna get a little bit tricky. So we're gonna say row.split, and we're splitting on nothing, so that each character will be split itself. And then let's map on this. And each value passed to this callback will of course be an individual cell. So there are two things that we need to do here. Either this is a number, in which case, we need to create a number of empty cells, or it's a letter, in which case we need to create an object representing that piece in that cell. So let's create a variable n, and we'll do parseInt(cell). And then if (isNaN(n)), meaning if n is not a number. Then we know that what we actually need to do here is return the cell with the piece in the cell. Otherwise, we know that we need to return n empty cells. Okay, so let's first talk about returning the cell with the actual piece. Let's create another little helper function here. And you'll notice I'm breaking things up into a lot of helper functions here, which is generally a good practice. And we'll call this makeCell. And this function here is going to take a couple of parameters. It's gonna take the value that will go in that cell, which is a letter representing the piece. Remember capital letters for white, lowercase letters for black, K for king, Q for queen, etc. It's also going to take the rank and the file so that it knows what its coordinates are. And then we're just going to return an object here which will have a couple of properties. We'll have the piece itself, which will be the value that we pass in. We'll have the image, which I'm calling an image but it's actually a character. But will just go with image for now because maybe later on you want to change this to actual images or something. The image is going to be that pieces object that we created last time. So we'll say pieces, and then [val] and then we also need the cell name. And this is going to be the actual algebraic coordinates. And this is, of course, where we use the rank and file. So we'll say, String.fromCharCode, and we'll say 97 + file. Now this works because 97 is a, and when file is 0. And when file should be a, we know that file will be 0. So we'll say, 97 + 0 is a, 97 + 1 is 98, which will be b, etc. And so this will get us the right file letter first and then we'll just add the rank to that, so this is going to be something like E4 or B7. All right, so that is what we need for the cell and we'll use all three of these values in our actual template when we're rendering this cell. So back up here in our makeRows function. If n is not a number, that means it's a letter and we need to make a cell. So we'll say, makeCell and we'll pass in cell. We will pass in the current rank and will pass in a file and will increment file by one. And you might wonder, why are we incrementing file by one here instead of using another index from this map function? Well, it's because if cell is a number then we're gonna need to return multiple cells over multiple columns, right? So we need to be able to manipulate that file number separately from the index here. So actually, what we want to do here is return makeCell and if n is a number then we want to return another array. An array with n number of empty cells. We can actually use a little bit of a hacky technique here. Because if we just did Array(n) we would get an array with a length property of n, but it wouldn't actually have n values in it. So if we did Array(n).map, which is what we're going to do here for each individual empty cell, then we would never actually call this callback function because Array(n) doesn't actually have any items in it. However, we can get our way around this by doing Array.apply and we'll apply it to null, which will create a new array. And we pass Array(n) as the second parameter, which is like passing an array of parameters to the Array constructor right because we're using apply. So these are basically spread out within the constructor. This actually does give us an Array of n Items. They're all undefined, but that's fine because we actually have an array of n items. So now, from within here we can just return makeCell. And we'll pass it cell which will be undefined, we'll pass it the current rank and we'll pass it file++. So what will happen here is we will get an array of empty cells. Now, what happens when cell is undefined in our makeCell function? Well, the piece will be blank, which is fine. The problem is here where we're getting the image. So why don't we just say, or an empty string. Meaning that if there's no piece that matches that value in the pieces object, just return an empty string so of course we'll have an empty cell. And then, of course, the rank and the file will be accurate so the cells ID, if you will, will be just fine. All right, now that was a little tricky but I think we got through that okay. However, you might realize we're not quite finished yet. Because, think about it like this, if we have a row that looks something like this, maybe it's 4P3, right? Meaning four blank spaces, a white pawn, and then three more blank spaces make up that row. Well, then when we do row.split here, we'll have of course three items to map over. And, of course, since we're mapping we get an array back, but what will be the values in this array? Well, because the first item is a number, this line will not run. So instead, we'll get back an array with four empty cells. So meaning, the first value inside of our array will be another array. The second value is a capital P, which means we'll get a white pawn cell made and so that will be our object as we would expect. And then, the third value is a three so we'll get another array back. Now these two arrays here have the cell objects within them. So basically, what we need to do is we need to flatten this array so that instead of an array that has arrays and objects, we'll just have all of the objects within those arrays. Now, if I was using something like underscore in this project, we could just use the flatten function. However, since I'm not, we're not gonna grab underscore just for that, instead let's cobble together our own basic flatten. So we'll say an empty array [].concat.apply. Once again, we're going to apply an array function here. We are going to apply it to an empty array. And then, the array that we get back from row.split (").map will be all of our parameters to concat. And so, it's just going to concat all of the objects within that array together. And so by concatenating all the items in that array together, we're gonna get one array flattened. At least flattened by one level, which is all we need to do here. Okay, so let me not forget my closing parentheses, again, at the end of our map parentheses. And what we have right here is our one row that we need. So then right at the top here, let's just go ahead and return that. And now, back up here at our top level rows.map, we've successfully created all of our rows. Okay, there's one more step to take here. And that is because right now, the rows that we've created are from the perspective of the player who's playing white. And we need to reverse everything for the player who's playing black. And this is, of course, why we've passed the black player's ID as a second parameter to make row. So what we can do is say if b, meaning the ID of the player playing black, is equal to Meteor.userId. So we are the black player, then what do we want to do? Well, it's actually pretty simple, all we have to do is reverse everything within each row and then reverse the rows themselves. So we can just say data.reverse and this is an array function that acts in place. So now we've reversed our rows. And now we just have to reverse each individual row. So we'll say, data = data.map. We take each individual row, and we can just return row.reverse. And now data will be set for the black player's perspective. So then at the very end, we'll just go ahead and return data and that's that. So, we now have an array of rows and each of those rows is an array of this cell notation that we have chosen. So, now with all of that in place, we are ready to actually display our board. So let's come back to the game.html file here. And let's add a div with another class. And this class is going to be col-sm-4, so this is gonna be a third of the 12-column grid that Bootstrap gives us. And in here, let's render a sub template, we'll call it board. We'll hand it the rows that we have just created. Remember in the previous lesson, we actually made a call to makeRows to create the rows helper for this game template. So, we can pass the rows to board. So down here, let's create a template, we'll give it a name of board. This is, of course, going to be a table. And in here, let's have an each block and we will loop over each row in this and I've decided this time why don't we actually label each data context as row. So in here, we want to have a table row, of course. And then let's do each cell in the row. And in here we will create a table data element. Let's give it an id and this id is going to be the cell. You remember over here in makeCell, we set cell to be the coordinate using algebraic notation for that cell, so like E4 or C7. And then within here let's render the actual image, which is not actually an image as you'll recall. And then within this cell, we will render the character. Now, because we don't want it to escape the entity, we have to use }}} here, otherwise we'll just get the entity character string showing up on our board. So let's go ahead and save this. And now if we come back to the browser, you can see that things are starting to look okay. We have what appears to be all of the pieces showing up correctly. Notice that because we are playing black, we are from the black player's perspective. If instead, I were to create a new browser window here, and if I sign in as Paul and go to Paul's board, notice how he views these players from white's perspective, while I view them from the black perspective. Okay, now of course this does not look quite right yet, it appears that we need to do some styling to make this look like an actual chess board, and to give some height to our empty rows. So, we will look at styling our chess board in the next lesson.

Back to the top