Current section: Tic Tac Toe 29 exercises
solution

Add Game History Feature

Loading solution

Transcript

00:00 Let's start by bringing these things in from MartyTheMoneyBag. Thanks, Marty. So we're going to have this is valid game state for validating what was in local storage, and the game state type, which is how we're going to be storing our state now. So rather than our default state being just the array of squares,

00:18 this is now going to be a game state object, which will have a history, which is an array. So this is our first entry in history. It's an array of these squares, and then a current step, which will start at index zero.

00:36 So with that then, we're going to have a bunch of stuff that's going to be messed up. So probably makes sense to change this from squares to tic-tac-toe. This is part of the migration strategy, I suppose. So we don't have old clients that have local storage, mucking around with what we're doing here.

00:56 Then right here, we're going to change it from squares to state. This will be set state because it's not just squares now, it's like all of the state of our app. This is no longer squares, it's going to be game state. Now, the local storage value, that all should be fine.

01:15 Here's our local storage key, everything. Something's wrong with local storage, we're not going to bother with it. But we need to do more than just checking, is it an array? We've got this is valid game state utility. Feel free to dive into that if you want to see how that works. But really, we just say, if it's valid game state, then return it, otherwise return the default state.

01:34 That makes our types happy right here. So now we are reading the state properly, which is good. So next, we have a bunch of variables that disappeared because we've renamed squares to state. We're actually going to do our squares, they're going to come from state.history.

01:51 So all of our histories live right here in this array. Then we're going to pluck out of those histories, the squares that are at our current step. So this gets us our squares. I'm going to rename this to current squares, just to be a little bit more specific because squares is more general term now.

02:11 All of those references got updated automatically, thank you VS Code. We're serializing the entire state, not just the squares. So instead of serializing the current squares, we're going to serialize the state, and we'll stick the state in here. So whenever the state changes,

02:29 let's put it in local storage. Great, we're making our way down, this is all good. So now instead of set squares, this is going to be set state, and instead of previous squares, this is going to be previous state. With that, we need to do things a little bit differently.

02:48 We still want to make a copy of the squares and make the change that we had before, but this time we also need to add the new squares to the history and then set that state and also increment the current step. So what we're going to do is we're going to grab

03:07 the current step and the history from the previous state, and then we're going to make a new history, which is going to be a copy of the current history from zero to what the current step is plus one. So wherever you are, even if you're backward in time,

03:26 if you make a move, you select a square, you were going to delete all the future that we had before and we're now on a different timeline. So we're going to go with whatever the current step is, plus one, and then we got to set the squares for this to be history at the current step. And we're going to do our with thing

03:50 with the index being the next value. So here are the new squares for this new history that we've got there. So then finally, we can return our new state object, which will have our current step being set to the current step plus one, or you could also use history.length or new history.length.

04:10 So either one of those should be the same number. And then our history should be the new history plus our additional squares. So you could do it this way. Our AI assistant wanted to do it this way. I don't want to. We're going to do it new history

04:27 plus the new squares that we just created for our new history. There are various ways that you could do this and none of them are wrong as long as it functions. But yeah, this is JavaScript gymnastics going on right now, which is always kind of fun. Okay, so instead of setting squares,

04:45 now we're going to set state with our default state. So that's what happens when we restart. And then finally, we have our moves. So that's the stuff that happens over here where you can select the different move that you're at. Looks like we already took care of that. So let's take care of our moves.

05:04 This, we're going to map over our history in our state. So we'll say state.history.map. And this is going to give us our step squares as well as our index. I'm going to prefix this with an underscore to say, we're not using this and I know we're not using it, but we don't need the actual squares.

05:23 We just actually need the index so we can iterate through each one of these. So then we can return an LI. And yes, this is an array that we're going to be rendering. So we want to specify a key. And in this situation, the key actually can be the index

05:41 because these are indexed or they're identified by where they appear in our history object. So this is why I said when we talked about keys before, why I've said that using the index isn't always wrong. You just want to understand when it's right. I'm going to rename this from index to step

05:59 to maybe make that a little bit more clear. So then we're going to make our button here and it's going to be disabled if the step that we're looking at is the current step. So let's do is current step right there. That will be useful and stick that right there, is current step disabled.

06:18 And then also like this message, I want to make a variable for that. So we'll call this the description. And if the step is truthy, then we'll go to move whatever, otherwise go to game start. So if it's the zeroth step, then that's the game start.

06:35 And so with that, then we can say description right here. And if it is the current step, we can also add current to that message that we're displaying for people. Finally, if we click on one of these items, we need to update the state

06:55 to have all the same state it did before, but just override the current step. So here's our previous state. Oops, we're going to merge the previous state with a current step being that of the step that they clicked. And that should take care of all of this.

07:17 And there we go. We've got our application hopefully working. Let's give this a shot. Boom, boom, go back, go forward, go forward, back, back. We've got our tic-tac-toe. We've got whatever this log level thing is. It's probably some other tool that I've got running in another browser.

07:36 But yeah, we've got our history right there. And as we continue, that history continues to grow until we finally get a winner. And then we can go back and be like, no, actually, I didn't want to go there. So that does make the game kind of interesting. And then of course, our restart should restart the game and update our local storage.

07:55 Hopefully that was a really interesting exploration into the things that you can do with set state in the callback form. And we're doing that twice here. Also what you can do with the callback form of use state and even verifying things, validating things so that they are the proper types and all of that stuff.

08:15 Feel free to dive into some of those utilities if you're interested. Keeping things up to date in local storage, also pretty interesting there. Initializing things from local storage, deriving state, so many cool things all represented in this one exercise. So I hope you had a good time with that one.