July 12th, 2023: Blackjack

Today I challenged myself to write a simple console based game of Blackjack “In One Hour”. Several hours later and I’m done! It wasn’t so much that it was a tricky feat, rather I got hung up on a few areas of interest.

First, I discovered that the console (Windows in this case) supports UTF-8 by default, and all 52 standard cards actually do have their own printable character, neat! So I started playing with that for a while. First issue is the Unicode characters for cards look like this:

🂷🂬

So… quite unreadable. Ah well, at least I can just use the characters for suits (♠, ♥, ♦, ♣) and draw my own old school ASCII art to display them. Oh wait, the “10” card is the only one to use two characters for its rank. Hmm, is there another character I could use? I could use the “enclosed numbers” block so it would be ⑩, but that looks weird if I don’t do them all and they are pretty hard to read too. Then I looked into combining characters which is a standard way of using Unicode and went down a few rabbit holes before deciding, as I’m sure many frustrated fussy hobbyist programmers before me have, that “T” is good enough to mean ten and move on having wasted a bunch of time.

When playing with the Unicode cards, I also noted that I was never seeing kings come up and couldn’t figure out why. It looked like an Off By One type error, but the rest of the program worked as expected. Turns out, Unicode includes an “alternate” rank called a “knight” that was used in France and Spain in times of yore. Now, obnoxiously dropped in the middle of the Unicode ordering. Fine, we can just add an offset to ignore this issue.

I also spent some time refamiliarizing myself with methods of randomizing and distributing elements from a collection and looked through the documentation of the random package in Python. I ended up writing my class for creating a deck, shuffling, dealing, and storing card information as I can reuse parts for future projects, but it was still informative learning some existing options.

Lastly, I spent a while just playing with timing and “animating” the game, such that it can be in console. Just blanking the console and reprinting various states. I could have been a bit more clever and stored sections in some sort of display buffer, only then editing the changing parts, but instead just wrote a few multipurpose, hard coded display sections which work well enough for a silly task like this.

Overall a fun little short project. Pretty simple, but not trivially so. Upon completing the base game, I decided to add doubling down on your bet as an option, which was simple enough. Now it all functions as intended, looks “good”, and is fun. Yet to tackle is Splitting as it’s a whole other display rigmarole I’ll have to deal with. It’ll bother me if I leave it unfinished though, so surely I’ll go back to it.

See Source Code on Github: https://github.com/cswartzell/SpeedRound.git

Hooray! It works!

Code Snippet

       def print_hand(hand, hide_first):
            for _ in range(len(hand)):
                print("╭――╮", end="  ")
            print()
            
            for crd in hand:
                if hide_first:
                    print(f"│🜲\u2009│", end=" ")
                    hide_first = False
                else:
                    print(f"│{crd.unicode_suit}{crd.unicode_rank}│", end="  ")
            print()
            
            for _ in range(len(hand)):
                print("╰――╯", end="  ")
            print("\n")
            

        def print_state(player_cash, dealer_hand, player_hand, player_total, hide_dealer, won=0, msg="", d_total=0):
            system('cls')
            print(f"                                             Cash: ${player_cash}")
            if won > 0:
                print(f"                                                  +${won}")
            elif won < 0:
                print(f"                                                  -${won}")
            else:
                print()
            print("Dealer: ", end="")
            if d_total:
                print(d_total)
            else:
                print()
            print_hand(dealer_hand, hide_dealer)
            print(f"Player: {player_total}")
            print_hand(player_hand, False)
            if msg:
                print(msg)
            if won != 0:
                sleep(.5)
                print_state(player_cash + won, dealer_hand, player_hand, player_total, hide_dealer, d_total=d_total, won=0, msg=msg)

Leave a comment