hello, i'm maryn!

20 Jul 2020

Blackjack - a Weekend Project

A couple years ago, I got addicted to a mobile game called BitLife. It’s an extremely well-thought-out life simulator. You get to watch your character grow up, make decisions for them, settle down, emigrate, and you can visit the casino.

I’m not a gambler. I’ve probably played Blackjack only once before in real life, at a friend’s dorm in college, without bets. However, I constantly catch myself playing the BitLife casino for 30, 40, 120 minutes at a time every few weeks. The game logic is simple, easy to follow, and extremely addictive.

A Top-level description

Assignment: Use the Deck of Cards API that I randomly found last week to create a standalone Blackjack mini-game. Make it mobile-friendly, feel free to use libraries.

So, here’s what I came up with: https://github.io/revertdata/blackjack

When you visit the site, it asks you to click a button, which generates your in-game bank balance. Then, it shuffles six decks of cards, begins the round, and gives two cards to the dealer, and two to the player. One of the dealer cards is hidden, so as to keep the player in suspense about the dealers’ chances, compared to their own. The player can either “Hit” (ask for another card) or “Stand” (end their turn for the round). The goal is to get as close to 21 as possible without going over, competing against the dealer.

The Process

When I first opened up my IDE and powered on a Python server, I immediately felt anxious. It had been so long since I started a project from scratch. For the past few years, I had been working on other peoples’ code bases, or my own code bases that had been in the works for a while already. It was daunting, a blank canvas. Where do I even begin? Should I use a framework? What if I forgot how to initialize a project?

Then, I got started. Since I have another upcoming (larger-scale) project in Vue that I haven’t touched in a while, I thought I’d take the opportunity to refamiliarize myself with the syntax. I could have also written it in React, since that’s what I use at work, but I figured that the Blackjack project was a little too light-weight.

I decided to take all of Saturday to work on the back-end and game logic, and all of Sunday to work on the front-end. Of course, there was a little bit of bleeding on both days. I’d need to get some basic front-end stuff out of the way to make it easier on the back-end, or need to fix a flaw in my logic I discovered while fiddling with the design.

Things I learned

I thought I’d highlight some things that stuck out to me during the entire process. Maybe then, they’ll stick and I can use this information in the future. Or I can just refer to this blog post when something relevant comes up.

Vue Watchers

At my previous job, I was introduced to an open-sourced Javascript class called CountUp. I thought it would be perfect to use for a “slots-esque” display of the randomly generated bank balance. However, in the six months since I last used it, the class has had a bit of an upgrade to support module syntax.

I couldn’t figure out how to get Github pages to accept the script with type="module", so I had to work around it.

  1. Minimize the legacy (UMD) version of CountUp and refer to countUp.CountUp() in code
  2. Add a Vue computed function to return this.you.bank. This is because you is an object, and can’t be referred to explicitly by a Watcher.
  3. Add a Vue watch function that updates every time the computed bank balance is updated (just once per session in my code). This is where you return new countUp.CountUp(...)

And you’re done! This was my first time using a Vue watcher. I would recommend checking out the official documentation so I don’t end up putting my foot in my mouth, but Computed Properties and Watchers seem to kind of go hand-in-hand. The Vue documentation says it’s actually better to use Computed Properties when you can, but Watchers are great for more extensive operations (like this workaround).

Map → Sort → Reduce

I’ve used Javascript’s map, sort, and reduce functions in the past, but never chained. Maybe it sounds dumb, but I feel quite proud to have come up with this gem for calculating the cards’ points:

function calculateCardPoints(cards) {
	return cards.map(card => card.value).sort(function(a, b) {
		if (a === 'ACE') return 1;
		else if (b === 'ACE') return -1;
		return a[1] - b[1];
	}).reduce(function(a, b) {
		return cardValue(a, this) + cardValue(b, this);
	}, 0);

Essentially, it maps the cards values into an array of only values, sorts them by ascending (with the ACE card last, since it has two possible values), then the entire array is added together. Maybe it seems a little basic, and it certainly should. But I was proud of myself for recognizing what I needed and chaining all of it together as best I could.

Also, take note of my use of this. Javascript in its purest form!

Fake promises to let Javascript “sleep”

The game ultimately had a horrible user experience, because there were no pauses, no suspension for the player waiting to see if they’ve beat the dealer or not. My first programming language was actually Python, so I looked up if there was a Javascript equivalent to Python’s sleep.

Turns out, there is! I came across this article that explains the use of “fake” promises to add a pause to your web app. It looks like this:

function sleep(duration) {
	return new Promise(resolve => {
		setTimeout(() => {
		}, duration * 1000)

Then, I added a few await sleep() functions here and there to make the game play seem a little more realistic. Before the dealer deals his cards (not the player’s cards, since those have a natural pause waiting for you to Hit), after card points are calculated (to give the user time to read the new changes), and after the player’s Blackjack is calculated and the player is forced to Stand.

Friendly Feedback

The BitLife developers actually saw it! They said ‘That’s awesome, great work!’ as a reply to my Reddit post in r/BitLifeApp.

I also posted it to Twitter and Discord, where I got some really friendly rave reviews as well. I realized that I forgot to do testing in Chrome and Windows… Thanks for the support, everyone :)

If you spot something that could do better, feel free to slate it in the issues board and I’ll get to it whenever I feel like it. Or, fork it and make a pull request.

Why It Took Me So Long

I have been thinking about creating this clone for two years. The actual project took me around 8-10 hours in total. So why did it take me so long to get around to starting? Fear, maybe. Lack of general motivation. Burn-out from my day-job. All very valid procrastination excuses.

I used to be a grind machine. From kindergarten through college, I was always looking for ways to optimize my time, be the most productive, do as much as I could, be the best I could be. I don’t feel that need anymore. I feel like there doesn’t need to be as big of an emphasis on “doing the most”.

If you feel passionate about something, or you need to get something done, then being productive makes sense. But if you’re being productive just for the sake of being productive, then how is it at all productive? Mentally, that was where I was after I graduated, so I started trying to figure myself out.

Anyway, thanks for reading.

maryn at 09:36