From No Stack To Some Stack (Project 1: Slots)
Hey guys! I know the introduction made it sound like I started this series before I started my program with General Assembly, but that was a total lie of omission. We’re already in week 3, and have covered huge amounts of information. From Flexbox to Big O, we’ve played around with lots of things in CS, HTML, CSS, and Javascript. For example, I’ve learned to flout on my resume that I know jQuery, but to never actually use it for new projects — vanilla Javascript and other frameworks seem to be preferred now.
This week, our goal is to make a browser based game (no jQuery allowed). Since I’m not creative nor particularly clever, I chose to make a slot machine game. As of writing this sentence, my game looks kind of like this.
You probably noticed that the tab was producing audio, but the video didn’t contain whatever was playing. That’s because my game is ridiculously obnoxious. Last week, we covered adding audio to our HTML files, so naturally, I made it so “What’s New Pussycat” by Tom Jones started every time the user pressed a button to stop a slot. The only time you could get out of “What’s New Pussycat” was if you got a winning combination. But you’d only get momentary reprieve — it starts playing again once you press a “stahp” button. Your only salvation from Tom Jones is death.
That was only until this morning though. Since I have this week (the week of November 8) to create our game, I decided to add a shop where you could exchange coins for sounds, or a permanent reprieve from Tom Jones.
But aside from my insanity, you’re probably here to read how I’m doing with my code. Let’s talk about some of the main logic that I’m using to make this game happen.
Recursion Lets Slots Rotate and Rotate and Rotate
One of my primary challenges in making my game work was getting my slots to rotate. I also needed to make sure that when the user pressed the “stahp” button, the rotation would stop. So, in this function (co-authored by Stack Overflow), I set up a recursive function.
The general idea for this function was that I needed to loop through an array of objects which held my images to be displayed and the coin values they were associated with. My HTML file has a grid which represents the different slots, and those would get updated with the good ol’ DOM. I also had a “mySlots” array that would hold the values that were ultimately selected when the “stahp” button was pressed. The base case (showing off my recursion lingo) came through when the “stahp” button was pressed, and “mySlots” would be populated with the values that were used in that iteration of the function.
Here is where we ran into a problem. This function only updated the first column of slots (“mySlots” represented the grid where it ran left to right, top to bottom, from 0 to 8. You’ll see a graphic representation below). These slots were looking at an array of objects called “IMAGES”. Let’s pretend that I wanted to update my second and third column using the same function and same array of objects. The slots would run fine, but if a match was made, each row would contain the same images! To remedy this, we just recreated the array of objects in different orders, and made parallel functions that performed the same logic.
The Amy Method of Calculating Winners
Our cohort is 5 people strong, so we are all great friends. We like to get beers together at the end of the week and get drunk off two beers. Since I live in UTC/La Jolla (I’m not rich yet), and my campus is in Downtown San Diego, I always have to sober up before I can drive home. Turns out debugging code while intoxicated creates more errors than you began with. It’s fun though.
Anyways, one of my classmates is called Amy, and she came up with the basic logic underlying how to check if a set of slots produced a winner. Here is what it looks like.
WINNING_SLOTS is a constant (which is why it’s all caps) array of arrays. Each array within the array of arrays is the location of winning indexes. Visually, our game board looks like this.
Our array of arrays would then contain the total winning combinations:
WINNING_SLOTS = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 4, 8],
[6, 4, 2]
];
“mySlots” is an array that would contain the values like this:
[4, 10, 2, 5, 2, 10, 2, 5, 8]
Our function can check to see if the elements of “mySlots” match each other in any of the set of indices in WINNING_SLOTS. If you look closely, you’ll see that the indices [6, 4, 2] of “mySlots” all match each other with a value of 2. This combination would therefore produce a winner!
Javascript Can Make HTML and That’s Important to Know
A very important aspect of Excel is making sure your formulas rely on other cells instead of hard coded data, like the number 5. This way, if you were to change any of your inputs, your outputs would change accordingly. As I am learning about HTML and Javascript, I am seeing similar patterns. At this time, my shop has a set number of items and prices. But what if I wanted to add more items a user could buy. If I hard-coded my HTML to be the shop items, I would have to update my HTML file and my Javascript file to account for the changes. But, as the below example shows, we can dynamically create our HTML shop using Javascript.
The function above relies on a constant array of objects that I called “SOUNDS”. As you can see, the DOM can update our HTML document by appending children to parent tags, and can set the type of HTML to input by using the innerHTML method. Because I am using the forEach method on the “SOUNDS” array, the shop will easily update the HTML document with any changes I make on “SOUNDS”.
In both the generateShopItems function and the checkWinner function, I am finding the value of creating constant arrays of objects that the application heavily relies on. Creating code in this structure seems key to keeping my code DRY and less error prone.
evt.target is Powerful and Mysterious — I Also Struggle With Naming
Event delegation/bubbling is an integral aspect of any user interactive application. Let’s say you have a shop (like we do), and a user can buy items by clicking on buttons. One way you could handle this is by attaching event listeners to each of the buttons, and running your associated function on each of those events. Alternatively, you can add the event listener to the parent tag, and have the event listener identify the actual button that got clicked, so that you run the appropriate logic. Here is how I did it.
The first “if” statement is required because we want to make sure that a button got clicked. With the way that event delegation works, a user could click on empty space, and that would still register a click event — on the empty space. However, because the event target also allows you to look at what got clicked, we can make it so that only buttons are clicked. Next, even if we know what button got clicked, unless we give the button IDs to reference, we don’t actually know what item a user is actually purchasing. This is where we can rely on parent/sibling relationships. Let’s take a quick peek at the HTML for the table this event is tied to.
As you can see, the button is the child of the <th> tag. We want to access the <td> that contains the ${sound.sound} property. We’d usually think that this element is the direct sibling of the <th>, but the direct sibling is actually [object Text]. I don’t really know what this is, but the ${sound.price} <td> is the next sibling after [object Text].
While we’re here, let’s talk about the find array iterator:
The array.find method finds the first element in an array that matches the callback function requirement, and returns that element. This works great for my shop where each product name is unique, but it should not be used when there are duplicate entries in our array.
Conclusion
Thanks for taking the time to read this milestone in our journey towards being a full stack developer. Please let me know what you thought about everything, and hit me up if you are in San Diego and want to talk about developing! I’m always happy to hear additional insights.
Humble plug of my Github here
Let’s keep in mind I’m still in week 3.