BlackJackScreenCap

Blackjack

For my final project in computer programming, I decided that I wanted to create a simulation of blackjack, the popular card game. I thought that it would be a fun project to work on, as I felt that there would be enough portions of it that would challenge me, while at the same time not being insurmountable.

BlackJackScreenCap

Before you get a chance to try out what I have, blackjack does have somewhat flexible rules, my version only uses a single deck of cards and the house will stand on a soft 17. Additionally, I had planned for there to be a way to bet in the game, but I unfortunately did not have time to properly implement that. Now that I’ve talked about it, here is the link to my game.

Dealing Cards

The way in which I selected cards to deal was in part taken from an earlier program that I wrote for BlueJ, which I used in GreenFoot. I have a value between 1 and 52 for each card in a standard 52 card deck, and I made this chart to help clarify for me which card had which value:

0606141217

The next thing that I had to deal with was ensuring that the same card was never dealt out twice (ex. 2 cases of 6 of hearts in the same hand). To do this I added have the following code ahead of where I add the value of the card to the ArrayLists in which I had the players’ hands:

while ((house.contains(tempCard) == true) || (player1.contains(tempCard) == true))
{
   tempCard = dealCard();
}

This essentially just looks at the ArrayLists that have the values of the hands ( house and player1 respectively) and sees if the new value is already present. If it is, the program simply finds a new value through the dealCard() method, and will continue this until it finds a value that is not already present. The rest of the code is mostly adding the card to the necessary ArrayLists and creating the Card object:

//Deals give the card to the players based on whose turn it is.
if (playerTurn1 == true)
{
   //Puts the value out of 52 into an ArrayList
   player1.add(tempCard);

   //finds the value the card would have in blackjack, nad then adds it to another ArrayList
   if ((tempCard % 13 == 0) || (tempCard % 13 == 12) || (tempCard % 13 == 11))
   {
      playerVal1.add(10);
   }
   else if (tempCard % 13 == 1)
   {
      playerVal1.add(11);
   }
   else
   {
      playerVal1.add(tempCard % 13);
   }

   //Creates a Card object in the world, increases the deal count, and has a slight delay.
   addObject(new Card(tempCard), 0 + xPlayerAdd, getHeight()/4 + yAdd);
   xPlayerAdd +=(getWidth()/8);
   deal ++;
   Greenfoot.delay(5);
}
//the code here is the same as directly above, just for the house instead of player1.
else if (houseTurn == true)
{ 
   house.add(tempCard);
   if ((tempCard % 13 == 0) || (tempCard % 13 == 12) || (tempCard % 13 == 11))
   {
      houseVal.add(10);
   }
   else if (tempCard % 13 == 1)
   {
      houseVal.add(11);
   }
   else
   {
      houseVal.add(tempCard % 13);
   }
   addObject(new Card(tempCard), 0 + xHouseAdd, getHeight()/4 );
   xHouseAdd +=(getWidth()/8);
   deal ++;
   Greenfoot.delay(5);
 }

 

Images of Cards

As I decided to do my project in GreenFoot,  I also needed to create a visual representation of the cards. I immediately ruled out creating 52 separate classes for the cards, as that would be excessively long. I eventually decided to create an ArrayList of GreenFoot Images, and get the image from the list based on the value each card has. This was still rather long and tedious, even if it was better than the alternatives it was essentially the following code repeated 52 times with the file name changed:

GreenfootImage g = new GreenfootImage("40px-Playing_card_spade_A.svg.png");
images.add(g);

I had gotten this ArrayList sorted out but I then had another issue, and this ended up halting my progress for several days. I needed to find a way to have the Card class have access to both the ArrayList of images as well as the value that each specific card had when it was created. I first attempted to pass both the value of the card and the ArrayList of GreenFoot images to the Card class, however I ran into an issue where I needed the specific instance of the World class. I searched for a way to do this, but they few results I managed to find were fairly long and I did not understand them at all. I eventually abandoned this approach in favor creating an entirely new class, Methods, and making my Card and Deck classes subclasses of Methods. I thought that by doing this Deck and Card would have the values of any variable I had assigned in methods, but I later learned that this was not how the relationship between subclasses and superclass worked, and I had to change my approach again. Eventually, I went back to my original plan of attempting to pass information, and after refining my original searches I was successful. The second was that I did eventually find way to get the specific instance of my CardWorld class in a fairly concise manner:

((CardWorld) getWorld()).genericMethodHere();

This was able to solve my primary issue with this, however another soon appeared, specifically when I dealt the initial four cards of the round (2 for each player) all of the images were the same, even though their values in the ArrayLists were different. I finally found that this was a result of how I was passing the cards their values. The value was stored in a variable that I was passing, but because the cards were being created in the same method they could not ask for the variable until after they were all created, and so they all received the same variable value. I resolved this issue fairly simply by adding a constructor to my Card class, which allowed me to give the Card its value as soon as it was created. The final code to assign the cards their images is below, and I find that it now works extremely well for what I need:

images = ((CardWorld) getWorld()).GiveImages();
setImage(images.get(CardVal - 1));

This may seem like an extremely long long section on an issue that seems fairly simple to resolve, and I agree. This is due to the fact that what was in reality a few very simple to solve issues holding me up for a quite a few days. I am certain that if had resolved this earlier I could have made much more progress, and I regret that I was not able to.

The Game

After I had ensured that dealing cards was fully functional, I then set out to add in the rules of the game. The first thing that I worked out was the turns for the players (ex. the player chooses to hit or stand, then the house). This was somewhat tricky to work out, however in the end it eventually just came down to some variables being added to determine which player to deal to. After this I had to come up with a way to add up the total value of the cards in a player’s hand, rather than using multiple for loops, I just made the following function, with a parameter for the ArrayList to be added:

public int handSums(ArrayList<Integer> hand)
{
   int sum = 0;
   //add up the cards for the given hand.
   for (int i = 0; i < hand.size(); i++)
   {
      sum += hand.get(i);
   }
   return sum;
}

This function ended up being invaluable to me, as I continued with my project I used this many times I then created buttons that would allow the player to choose whether they wanted to hit, stand, or double down. The latter was added as an option because of the betting system I hoped to add, this would have dealt one more card to the player, doubled their bet,  It currently only deals one card and ends the turn.

ButtonsScreenCap

The code for the house was even simpler however, essentially just drawing until their hand total is over 17:

public void houseDeal()
{
   while (handSums(houseVal) < 17)
   {
      nextCard();
   }
   roundOver = true;
}

 

Winning and Losing

After I had the turns working correctly and players’ hands being added properly, the next thing I needed to do was determine whether the player won or lost. For the most part this was simply a lot of if statements for various circumstances, then running a method to pop up a message informing the player if they won, as well as if they or the house had gotten a blackjack (Ace and King/Queen/Jack/10). There was one portion of this that was somewhat tricky however. In Blackjack, the value of an Ace is normally 11, but if the hand an Ace is in has a total value of over 21, that Ace’s value can be changed to a one. Because of this I had to design a method to do this:

public void checkAce(ArrayList<Integer> hand)
{
   if ((handSums(hand) > 21) && (hand.contains(11) == true))
   {
      hand.set(hand.indexOf(11),1);
   }
}

The method itself was fairly simple, what was harder was determining where and when I needed to implement it. There were a number of places it was needed, and finding the correct spot to check for the Ace was very difficult at times.

Rounds

The final thing I implemented in my program was the ability to transition from one round to another. Before this the program would start, the players would hit or stand, the winner would be determined, and then the program would end; if you wanted to play another round you would have to restart the program entirely. I mainly accomplished this by passing some variables in order to tell the different classes to prepare for a new round, but I also had one main method which primarily clears the ArrayLists for the hands, reset some starting variables, and delete all of the cards on the screen:

public void clearHands()
{
   for (int i = (house.size() - 1); i >= 0; i--)
   {
      house.remove(i);
   }
   for (int i = (houseVal.size() - 1); i >= 0; i--)
   {
      houseVal.remove(i);
   }
   for (int i = (player1.size() - 1); i >= 0; i--)
   {
      player1.remove(i);
   }
   for (int i = (playerVal1.size() - 1); i >= 0; i--)
   {
      playerVal1.remove(i);
   }
   List<Card> cards = getObjects(Card.class);
   removeObjects(cards);
   removeObjects(getObjectsAt(getWidth()/2, getHeight()/2,RoundButton.class));
   xPlayerAdd = (getWidth()/8);
   xHouseAdd = (getWidth()/8);
   startRound = true;
   roundOver = false;
}

At first I had the round changing on its own after a brief delay, but I experienced some issues with this, namely the last card for the player, and any additional cards the house was dealt would not be assigned their images, instead the winner was announced and delay would happen, then the next round would start without assigning the images. I determined that this was basically because the program was stuck in a loop until the next round could start, and so the cards never had the opportunity to assign their images. I solved this issue by creating a new button instead of ending the round, effectively eliminating the loop before the next round started. When the player clicks the button the button then calls the function to end the round,clear the hands, and finally delete the button itself.

RoundButtonScreenCap

This not only solves the problem with the loop, but also allows the player to take as much time as they need to bring up the terminal to see who won, as well as to look at what cards they and the house had. The latter effect was not intended, but I find that I actually like the way this currently works much more.

Conclusion

I feel that my project went fairly well for me, and while I wish I could have done much more (betting, nicer images, making the whole program cleaner, etc.), I am still pleased with what I have accomplished. I had several challenges along the way, some of which caught me up far longer than they should have (passing information between classes), but beyond those, I feel the project went very well, and I had an enjoyable time creating, and hope to someday return to it.

One thought on “Blackjack

Leave a Reply

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