A Coding Blog Bitcoin Story #3: Building the Dice Rolling Functionality in Yahtzee
The rust is peeling off and the obsession is coming back.
Last year, when the team and I were working on the “Bitcoin story-telling project,” I lived and breathed code. I saw it while sleeping, and thought about it constantly. As I carried out my day, advancing the project was always was on my mind. It became my obsession and it lasted for quite some time.
Well, that obsession is back. I can’t stop myself from conceptualizing and thinking about how to code the next feature in the Yahtzee app. It’s all I want to do.
I guess the reason why, is because I want to show that I can code a fully-functioning game from scratch. I think when I do, my coding confidence will finally be heightened — and it will end up being one of my proudest moments in life. Even moreso than the work I did while building the Bitcoin story-telling app (and there’s good reason for that).
Back while we coded that project, I relied too heavily on the coding genius I was working with. I was eager to get everything done as quickly as possible, so even though I wanted to learn the ins-and-outs of everything coded into the project, speed took precedence to learning. More specifically, I didn’t let myself struggle to come up with the answer on my own. I would do my best to find a quick solution, and if it wasn’t there, I would ask him for help — and he would deliver each and every time (I don’t call him a coding genius for nothing).
Unfortunately, learning like this isn’t the most effective way to increase your knowledge and improve your skills. When you’re just given the answer, your mind doesn’t process and absorb the information the same way it does when you go through the struggles of finding the answer yourself. It just doesn’t stick quite the same.
Well, this time is different. This time around, I’m letting myself struggle until I find the answers on my own. I’m trying a bunch of different things and doing research until it clicks and I figure it out myself. That same disservice from the past will not be repeated again. This time, building and learning the application from the ground-up is my standard operating procedure. Now, learning takes precedence to speed.
And what have I learned now that learning has taken the front seat? So much. So very much.
Building out the dice rolling functions by pseudo-coding and going through the trial-and-error process has expanded my ability to think through the process of building code immensely. Overall though, the main point I’ve taken from this project thus far, is that coding is step-by-step. You can envision a big elaborate application with many cool features, but without the foundation code set in stone, you’ll never be able to get to that point.
For this Yahtzee app, that foundation is the dice rolling function. Without being able to render the proper images and capture the correct values, the ability to keep score is a pipe dream. So, that’s where I had to start. Function mission #1 was randomizing 5 different values; capturing these values; and assigning those values as a property to each dice.
In the screenshot above, you’ll see the rolledNumber() function return a value using the standard Math-based functions that randomize numbers for us. Nothing special here.
We then use it in the obtainNumbers() function, where five different dice variables are declared from the get go (‘let’ is used here because they’ll be changing on the regular). Then, we take each value and pass it into the this.diceRollDetermination() function, which also possesses a second parameter in the form of a number representing the first dice, second dice, and so on. You can ignore the ‘if this.state.diceOneChecked === false’ statement for now, as I’ll get into that later.
I must confess, this diceRollDetermination() function really put me through the ringer. This functionality took multiple iterations before I finally got it right. While failing to conceptualize the entire step-by-step process; I learned that when you update state (setState) in React, you cannot use a variable name in place of the actual state label. For instance, say I had a diceImage1, diceImage2, etc. in my state constructor that rendered a particular dice image depending upon the state. Well, I couldn’t run a ‘for loop’ that allowed me to set the state using this manner: this.setState({ diceImage[index]: diceRollNumber)}.
I feel kind of dumb now as it seems pretty obvious as to why I couldn’t do that, but I’ll just chalk it up to a learning experience. I’ll also chalk up the next screenshot and function as a learning experience too, because I just realized the above function is not needed at all. The ‘this.determineWhichDice() function’ passes in the exact same parameters/values and does all the real magic there. Da-Doy…
Well, at least the entire dice rolling process does function properly with the above code — even with the soon-to-be-removed function added in. So, I won’t dwell on this imperfection. I’ll continue describing what it does and why I find it so elegant.
Based upon the diceRollNumber (the 1–6), diceNumber becomes the image of the dice. If the number is 6, it will become the image of a dice with 6 dots. Then, based upon the dicePosition (1, 2, 3, 4, or 5), we setState to change the image, as well as the value. That diceRollNumber becomes the value, and the image laying around in the diceNumber variable becomes the image displayed in the relevant dice slot. Elegant!
With this, I could change my dice numbers based upon the click of a button. Unfortunately, with just the above code, they ALWAYS change values, even if you wanted to hold 4 sixes for a nice-sized 4 of a kind. So, some work needed to be done there, and that was my next task at hand.
Part of my work was already complete after adding a checkbox input with a name property to each Dice component, so I just needed to know if the dice was checked or not. In order to determine this, I used the Boolean-based check property and an onChange() function to switch it back and forth.
And the onChange (called handleChange here) function itself.
With this function, we capture the event.target.name, which is one of the five names included in the switch statement. Then depending on which checkbox/dice they click, the “diceXChecked” state switches to true if it were false, or false if it were true. If the checked property returns true, the border color changes to green. If the checked property returns false, the border color remains red.
And that brings us back to the obtainNumbers() function that includes an if statement based upon this checked property.
Here, if the dice isn’t checked, it can be rolled again. If it is checked, the diceRollDetermination() function will NOT be triggered, and the dice will NOT be rolled. Now, the dice can be properly held, but much like before, they unfortunately can be rolled over and over again without anything else happening. This meant onwards to adding “number of rolls” to state, and automatically unchecking the dice after the 3rd turn is over (In case you weren’t aware, you get three rolls in Yahtzee before you have to take a score). You’ll find the “unchecking of the dice” function in the following screenshot.
Nothing amazing here, but it reminds me of a problem I came across while building the dice functionality. I discovered that it’s rather difficult to update state for a particular spot in an array. If you’re trying to update the state for the 3rd value in an array, you can’t call a setState function on diceChecked[2]: false. It doesn’t work. So, that’s why you’ll see separate states declared for each checkbox within the overall state constructor. But it doesn’t matter. This function does the trick; it just makes the code look a little extra WET. A little slipperiness doesn’t hurt anyone, does it?
With this particular function, we can uncheck all the dice, but we need a particular place to call the function. As a result, the turnNumber state was created. When the “Roll Dice” button is clicked to trigger the rollDice() function, the turnNumber state updates, and triggers the unCheckDice() function once it reaches turn number 3.
So now, the dice and turns are properly functioning. You can hold the dice, and after 3 rolls, they are unchecked and reset. Once I had this figured out, I thought it was time to move onto the score-keeping logic, but I realized you could check the dice even before you click the “Roll dice” button, and that is obviously no good. So, I needed to learn about disabling buttons, checkboxes, and radio buttons before I could proceed.
What I learned is that disabling is pretty simple. Once again, I could setState and pass in a True/False Boolean value that changed at the proper points of time. The game would disallow selection of the checkbox at the start (disabled === true), and change after the first roll was made. You can see that in the rollDice() function as well, when I update the disabledDice array to a bunch of falses. In the future, I’ll be using this disabling feature on the “Roll dice” button when I force a user to select a score on the 3rd roll.
So that’s where I’m at now. Or rather, I should say that’s where *we’re* at now. One of my study group guys tinkered around with the code, and made his mark by adding the “Turn number text display” to the application.
The cool part about displaying the roll number like that, is that he conditionally rendered the particular div that contains the display based upon whether the turn number is 0 or not. If it’s 0, the text says “Roll the dice to start!” If the roll number is something other than 0, the text says “Roll Number: X”. And since the turnNumber is passed in as a prop, it updates automatically. Pretty nifty.
I know I said I was going to do this all on my own, but I can’t pass up on some coding collaboration when it’s there. Meng is a passionate developer and great to work with, so if he wants to help, I’m all for it. It’ll also force me to code as cleanly and by-the-book as possible, as well as reinforcing what I already learned about git branches and pull requests. I see no downside here.
With that said, I will still complete the majority of the project myself. This is a learning experience so I won’t be pawning off the score-keeping part on him. The logic-based parts are mine; he’s welcome to help with anything else.
And now that this blog post is complete, I’m ready to get back to my obsession.
Until next time,
Sam