Skip to content

A pixel-themed Blackjack game built with JavaScript, featuring interactive gameplay and persistent score tracking.

Notifications You must be signed in to change notification settings

shaepy/pixeljack

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

♠️ Pixeljack

Description

Pixeljack is a pixel-style Blackjack game where you try to beat the dealer by getting as close to 21 as possible without going over. Both you and the dealer start with two cards and take turns choosing to 'hit' (get another card) or 'stand' (keep your hand). Whoever gets closer to 21 without going over wins.

Pixeljack Presentation PDF

♦️ Tech Stack

  • HTML
  • CSS
  • JavaScript

♦️ Requirements

MVP User Stories

As a user,

  • I should be able to place a bet from my wallet to start the game.
  • I can see what cards are dealt to me and only one card of the dealer.
  • I can hit to get another card to add to my hand.
  • I can stand to end my turn.
  • I should see the result of the round (win, lose, push).
  • I can see my new wallet total after each result.
  • I should have an option to play again.
  • I want to reset my game status when I run out of currency.
  • I should be able to see instructions on how to play the game.

♦️ Planning

game flowchart

Planning Process

  1. Created a Project Spec to map out MVP requirements for the game
  2. Game flowchart (image above) to understand order of operations and visualize flow in code
  3. Mapped out Data Structure needed for card deck
  4. Pseudo-code
  5. Translate into actionable tasks on Kanban Board

Design Process

Initial wireframes

game screen result screen mobile screen

Mockups with initial design

bet screen game screen result screen old theme sketch

Choosing a Pixel theme

pixeljack sketch pixel theme with gradient background
mobile-pj desktop-pj

♦️ Code Process

Early Code

  1. Built out data structure for cards and player/dealer hands
    • cardDeck holds 52 card objects
    • I started with a single object for the table to hold both dealer and player cards. ie let table = {dealer: [], player: []}, which is later changed to separate objects of dealer = {cards: [], total: [], hitCardIdx: 0} for player and splitHand as well
starting concept html scaffold data-structure
  1. Scaffolding HTML for a dealer, player, and 2 buttons for hit and stand
  2. Using JavaScript to deal the cards to each hand, then display it
    • Created getCard() that grabs a random card from the cardDeck array. It filters through only the cards where hasBeenPlayed: false to avoid dealing duplicates.
    • dealCards() adds 2 cards to each hand (player and dealer)
    • addCardTotal() calculates the total of each hand using reduce with a check for whether an Ace can be changed if hand has busted
    • Display those cards using displayCards()
    • Created a checkForBlackjack() (ace and 10) which leads to an auto win unless Dealer has 21, which auto stands
  3. Attached event listeners and functions to hit and stand buttons
    • Created functions for hit and stand (as they are reused later)
      • hit draws another card, calls addCardTotal displayCards, and checksForBust
      • stand ends turn, calls revealHiddenCard, dealerHit, and compareResult
  4. Dealer's turn logic is to stand at 17 and above and always happens after the player turn
    • while (dealer.total <= 16) dealerHit()
  5. Win, lose, or tie logic implemented
    • compareResult will check the hand totals between player versus dealer (see Refactoring Code #4) while adjusting the bet/wallet totals, and showResultScreen will display or remove the visual elements
  6. Built out a resetGame() function that clears results and displays to prep the player to play again after a game ends
    • In the early builds, the play again button led you back to the bet screen rather than loading up another game. You had to pass through this screen and press 'play' again. Future improvements made play again and change bet into separate options for the user.
blackjack win bet and wallet on game screen
  1. Bet mechanic was added last after the main gameplay was functional and complete.
    • Started as a single bet option then added bet selectors to choose the amount
      • Then evolved to more options using data-bet = "10" for values since the bets are images
    • Added reset funds button that appears when player runs out of currency
  2. Once the MVP requirements were functional and tested, I had a basic Blackjack skin for the game theme and used CSS to make it responsive for mobile and web (before deciding on a pixel theme).
desktop bet screen desktop game screen desktop blackjack
mobile home screen mobile game screen

Refactoring Code

  1. Refactored from using .innerHTML declarations ie. display.innerHTML = <img src="${player.cards[0]}"> and instead now createCardImg(card) takes a card object and will construct the entire element and I can just append it to the necessary div element.
  2. Originally, the temporary user-facing messages ex. "Your Ace value has been changed", "Choose a bet amount to start a game", "Your wallet is too low", etc. were coded into the HTML and hidden. Since the messages all looked the same and appeared in similar spots, I refactored this to be two dynamic pieces so I could reuse it for any string that needed a temporary user-facing message.
    • handleFadeEffect(element) will take an element and apply or remove the opacity fading CSS classes
    • createTempMsg(string) will take a string and make a p element with that string, append it to the #temp-msg div then calls the handleFadeEffect to add the effect.
  3. Since the code uses a lot of setting display: none and display: flex for showing or hiding elements on the page, I use two functions that will take an array and set each element in the array to either display: flex or display: none
    • turnDisplayToFlex(arr) and turnDisplayToNone(arr)
    • This is also the method used to setAttribute of disabled or removeAttribute, for player action buttons.
    • Future improvement would be to change this to utility classes for displays and applying/removing a separate class but might need to review the current usage of IDs
  4. Updated compareResult() and compareSplitResult() to take all the result cases, including when a player busts. This way, checkForBust() only handles the checking of it, passing any visual elements and changing to isBust: true, and is directed towards the compare functions.
    • Doing this keeps the functions separate. Previously, the checkForBust function would branch a way to display a bust result for when a player or dealer busts. Now, all result outputs are in either compareResult or compareSplitResult. The only exception is when getting a natural blackjack (a case that does not result in the normal flow of operations).

♦️ Key Takeaways

Challenges

  • Using a relative path versus direct path for images in JavaScript for local host versus remote live servers was causing issues. A solution found was to reference the direct path but use a ternary operator to pass in a ${srcUrl} of either the local host IP or /pixeljack.
  • The Split feature required a lot of areas to refactor since the feature was implemented post-MVP. This led to a lot of things breaking during the process. My takeaway is to keep track of unit testing and listing out existing technical areas the new feature will touch, rather than focusing only on the implementation logic.
  • The ability for Aces to be flexible of either 1 or 11 value made Split hands difficult. It opened up a lot of edge cases since having a pair of aces can lead to the total of '22' unless accounting for switches to the value of 1. A large majority of time spent in implementing the Split feature was fixing cases and issues derived from this flexibility.
edge case two aces

Wins

  • Tracking my tasks in a Kanban board helped me in prioritizing features, bugs, and edge cases as they were discovered and built.
    • This also helped when I needed to create the post-MVP features: Split and Double Down. I was able to use the Kanban Board tasks similar to JIRA style tickets for myself.
    • On each 'ticket', I would do a mini-planning for each feature. This included user stories, psuedo-code, technical challenges to consider, etc. Even some scratch code to think things through.
user stories for split sketch code for split
  • By building in a modular design and cleaning up redundant code as I went, building the Double Down feature took only a few hours due to the reusability of previous functions. I also found debugging to go quicker when I understood the flow of operations really well thanks to the ease of readability.
    • Double Down is comprised of existing actions and behaviors:
      • createTempMsg creates a temporary message that displays with a fade-in/fade-out effect to inform users when they do not have enough coins
      • displayFunds will show the updated wallet and bet
      • hit will getCard and call to display the card, addCardTotal, and checkForBust
      • If player did not bust, call stand
      • Return bet to normal
double down code

♦️ Future Improvements

  • Card counting mode (currently, the deck resets every game)
  • Adjust cards to be stacked in a peek on top of each other rather than displayed fully
  • Potentially, allow player to forfeit game and exit to home screen before game has ended

♦️ Resource Credits

Special thank you to,

About

A pixel-themed Blackjack game built with JavaScript, featuring interactive gameplay and persistent score tracking.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published