Simple Blackjack game using JavaScript and jQuery

Posted on

Problem

For my first JavaScript/jQuery project, I made a simple Blackjack game. However, I would like to have a more object oriented approach. I would truly appreciate it if someone can review my code and point out how I can make it more object oriented.

function reset(){

    // Reset
    $(".dealer-cards").html("<div class='card card1'></div><div class='card card2 flipped'></div><div class='new-cards'></div><div class='clear'></div><div id='dealerTotal' class='dealer-total'></div>");

    $(".player-cards").html("<div class='card card1'></div><div class='card card2'></div><div class='new-cards'></div><div class='clear'></div><div id='playerTotal' class='player-total'></div>");

    var reloadGame = "<div class='btn' id='hit'>Hit</div><div class='btn' id='stand'>Stand</div>";
    $(".buttons").html(reloadGame);

    $(".dealer-cards").css("width","");
    $(".player-cards").css("width","");

    $("#playerTotal").html('');
    $("#dealerTotal").html('');
    $("#message").html('');

}


function deal(){

    reset();

/// Store cards in array
    var cards = ["ace-of-clubs","two-of-clubs","three-of-clubs","four-of-clubs","five-of-clubs","six-of-clubs","seven-of-clubs","eight-of-clubs","nine-of-clubs","ten-of-clubs","jack-of-clubs","queen-of-clubs","king-of-clubs","ace-of-spades","two-of-spades","three-of-spades","four-of-spades","five-of-spades","six-of-spades","seven-of-spades","eight-of-spades","nine-of-spades","ten-of-spades","jack-of-spades","queen-of-spades","king-of-spades","ace-of-hearts","two-of-hearts","three-of-hearts","four-of-hearts","five-of-hearts","six-of-hearts","seven-of-hearts","eight-of-hearts","nine-of-hearts","ten-of-hearts","jack-of-hearts","queen-of-hearts","king-of-hearts","ace-of-diamonds","two-of-diamonds","three-of-diamonds","four-of-diamonds","five-of-diamonds","six-of-diamonds","seven-of-diamonds","eight-of-diamonds","nine-of-diamonds","ten-of-diamonds","jack-of-diamonds","queen-of-diamonds","king-of-diamonds"];

    /// Store correlating values in an array
    var values = [11,2,3,4,5,6,7,8,9,10,10,10,10,11,2,3,4,5,6,7,8,9,10,10,10,10,11,2,3,4,5,6,7,8,9,10,10,10,10,11,2,3,4,5,6,7,8,9,10,10,10,10];

    /// Zero out dealer total
    var dealerTotal = 0;
    $(".dealer-cards .card").each(function(){

        var num = Math.floor(Math.random()*cards.length);
        var cardClass = cards[num];

        $(this).addClass(cardClass);

        $(this).attr("data-value",values[num]);

        dealerTotal = parseInt(dealerTotal) + parseInt(values[num]);

        if(dealerTotal>21){
            $(".dealer-cards .card").each(function(){
                if($(this).attr("data-value")==11){
                    dealerTotal = parseInt(dealerTotal) - 10;
                    $(this).attr("data-value",1);
                }
            });
        }

        $("#dealerTotal").html(dealerTotal);

        cards.splice(num, 1);
        values.splice(num, 1);
    });

    /// Zero out player total
    var playerTotal = 0;
    $(".player-cards .card").each(function(){

        var num = Math.floor(Math.random()*cards.length);
        var cardClass = cards[num];

        $(this).addClass(cardClass);

        $(this).attr("data-value",values[num]);

        playerTotal = parseInt(playerTotal) + parseInt(values[num]);

        if(playerTotal>21){
            $(".player-cards .card").each(function(){
                if($(this).attr("data-value")==11){
                    playerTotal = parseInt(playerTotal) - 10;
                    $(this).attr("data-value",1);
                }
            });
        }

        $("#playerTotal").html(playerTotal);

        cards.splice(num, 1);
        values.splice(num, 1);

    });


    /// If player hits
    $("#hit").click(function(){
        var num = Math.floor(Math.random()*cards.length);
        var cardClass = cards[num];

        var newCard = "<div class='card " +  cardClass + "' data-value='" + values[num] + "'></div>";
        $(".player-cards .new-cards").append(newCard);

        playerTotal = parseInt(playerTotal) + parseInt(values[num]);

        if(playerTotal>21){
            $(".player-cards .card").each(function(){
                if($(this).attr("data-value")==11){
                    playerTotal = parseInt(playerTotal) - 10;
                    $(this).attr("data-value",1);
                }
            });
        }

        cards.splice(num, 1);
        values.splice(num, 1);

        $("#playerTotal").html(playerTotal);
        $(".player-cards").width($(".player-cards").width()+84);


        if(playerTotal>21){
            $("#message").html('Bust!');
            var reloadGame = "<div class='btn' id='deal'>Deal</div>";
            $(".buttons").html(reloadGame);
            /// Pay up
            $("#bet").html('0');    
            return false;
        }


    });

    /// If player stands
    $("#stand").click(function(){

        $("#dealerTotal").css("visibility","visible");
        $(".card2").removeClass("flipped");

        //// Keep adding a card until over 17 or dealer busts
        var keepDealing = setInterval(function(){

            var dealerTotal = $("#dealerTotal").html();
            var playerTotal = $("#playerTotal").html();

            /// If there are aces
            if(dealerTotal>21){
                $(".dealer-cards .card").each(function(){

                    //// and check if still over 21 in the loop
                    if($(this).attr("data-value")==11 && dealerTotal>21){
                        dealerTotal = parseInt(dealerTotal) - 10;
                        $(this).attr("data-value",1);
                    }
                });
            }

            if(dealerTotal>21){
                $("#message").html('Dealer Bust!'); 
                var reloadGame = "<div class='btn' id='deal'>Deal</div>";
                $(".buttons").html(reloadGame);
                clearInterval(keepDealing);
                /// Pay up
                var bet = $("#bet").html();
                var money = $("#money").html();
                var winnings = bet * 2;
                $("#bet").html('0');
                $("#money").html(parseInt(winnings) + parseInt(money));
                return false;
            }


            if(dealerTotal>=17){
                /// You Win
                if(playerTotal>dealerTotal){

                    $("#message").html('You Win!');

                    /// Pay up
                    var bet = $("#bet").html();
                    var money = $("#money").html();
                    var winnings = bet * 2;
                    $("#bet").html('0');
                    $("#money").html(parseInt(winnings) + parseInt(money));
                }

                /// You Lose
                if(playerTotal<dealerTotal){

                    $("#message").html('You Lose!');
                    /// Pay up
                    var bet = $("#bet").html();
                    var money = $("#money").html();
                    $("#bet").html('0');
                    $("#money").html(parseInt(money) - parseInt(bet));
                }
                if(playerTotal==dealerTotal){
                    $("#message").html('Push!');
                    var bet = $("#bet").html();
                    var money = $("#money").html();
                    $("#money").html(parseInt(bet) + parseInt(money));
                    $("#bet").html('0');
                }
                var reloadGame = "<div class='btn' id='deal'>Deal</div>";
                $(".buttons").html(reloadGame);
                clearInterval(keepDealing);
                return false;
            }

            var num = Math.floor(Math.random()*cards.length);
            var cardClass = cards[num];

            var newCard = "<div class='card " +  cardClass + "' data-value='" + values[num] + "'></div>";
            $(".dealer-cards .new-cards").append(newCard);

            dealerTotal = parseInt(dealerTotal) + parseInt(values[num]);

            $("#dealerTotal").html(dealerTotal);
            $(".dealer-cards").width($(".dealer-cards").width()+84)


            cards.splice(num, 1);
            values.splice(num, 1);

            }, 300);
    });         
}

$(document).ready(function(){

    deal();


    //// Bet
    $("#more").click(function(){

        var bet = 10;
        var currentBet = $("#bet").html();
        var money = $("#money").html();
        if(money==0) return false;
        if(currentBet){

            $("#bet").html(parseInt(currentBet) + bet);

            } else {

            $("#bet").html(bet);

        }

        $("#money").html(money-bet);

    });

    $("#less").click(function(){

        var bet = -10;
        var currentBet = $("#bet").html();
        if(currentBet==0) return false;

        var money = $("#money").html();

        if(currentBet){

            $("#bet").html(parseInt(currentBet) + bet);

            } else {

            $("#bet").html(bet);

        }

        $("#money").html(money-bet);

    });



    setInterval(function(){
        $("#deal").unbind('click');
        $("#deal").click(function(){
            /// location.reload(); 
            deal();
        });
    }, 200);
});

Solution

This is a partial review. I might have missed things.

Model and view

Split your model and your view. You might have heard from the design pattern “model-view-controller”. The model is the abstract data behind the scenes. The view is what the user sees, and all variables related to what the user sees, and interacts with.

You don’t have to fully implement it as such, but keeping this in mind, this should help you categorize which variables belong to which part of the code. It should also help figuring out what data should be exposed.

classes and ids

I notice that you use classes to find all containers for dealer-cards and player-cards. There is, however, just one such container. I think the code might break if you have more of such containers. I would suggest using an id for elements that you expect only to occur once.

Whitespace

Fix your whitespace. The indentation of certain code is not correct. Use whitespace consistently around operators, and whitespace consistently after commas.

Abstract equality vs strict equality

I would recommend that you use strict equality (===) instead of abstract equality (==). Abstract equality is not associative, which can cause weird bugs.

Buttons

Don’t use <div> for everything. We have a wide variety of elements for a wide variety of tasks. Buttons can be simulated using… <button> (or <input type="button">). The great thing is that these buttons work exactly as we expect them to work in every other application. We can select them with the keyboard for example, and press them with “enter”, or tab them on a touch device. Your <div> buttons do none of those things. They are restyled divs.

.data(..)

Use .data(..) to access data values instead of .attr('data- + …)or.attr(‘data-x’)`. That’s what that method is for… and any performance improvements the jQuery team thought up will be applied automatically.

cards and values

You are using two similarly sized Array’s to store name and worth of a particular card. Make that at least an Object:

var cards = {
  "ace-of-clubs": 11,
  "two-of-clubs": 2,
  ...
};

In the end you probably want to make a small class out of that.

Leave a Reply

Your email address will not be published. Required fields are marked *