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.