Project 2: Blackjack
Overview
You will implement a fully functioning game of Blackjack. No prior knowledge of the game is needed. However, you will need to know what cards appear in a standard deck of playing cards. If you have any questions about how the game itself works you can ask your instructor or a classmate.
Blackjack (also called “21”) is basically a game between an individual player and a dealer. There’s a gambling side to the game that this project does not address. The project simply plays the game with the user as “Player” and program as “Dealer”, and determines the winner and loser.
You will implement three successively more complicated programs, first to shuffle
, then deal
, and finally play
a game of Blackjack. And if you want, you might also implement a little extra
too!
As usual, while the topic of the project is a card game, this project is really about functions, program design, and arrays. Be sure to refer to what you learned in Unit 5, Unit 6, and the corresponding labs and homeworks.
Tips for success
Make sure you follow the SI204 Style Guide. This means proper indentation, use of whitespace within lines of code, logical organization of chunks of code,
Make sure you do a good job of putting things in functions. One monolithic main
function is not OK, even if it works!
Re-submit frequently, and definitely when you finish each part.
Start early! Remember “Hofstadter’s Law”:
It always takes longer than you expect, even when you take into account Hofstadter’s Law.
Not only is that true, it’s also a recrusive joke!
A great quote from Brian Kernighan, co-author of “the book” on C programming:
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are –– by definition –– not smart enough to debug it.
Grading
Make sure you follow the SI204 Style Guide. This means proper indentation, use of whitespace within lines of code, logical organization of chunks of code,
Make sure you do a good job of putting things in functions. One monolithic main
function is not OK, even if it works!
Re-submit frequently, and definitely when you finish each part.
Start early! Remember “Hofstadter’s Law”:
It always takes longer than you expect, even when you take into account Hofstadter’s Law.
Not only is that true, it’s also a recrusive joke!
A great quote from Brian Kernighan, co-author of “the book” on C programming:
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are –– by definition –– not smart enough to debug it.
70% of your grade will be based on functionality (does your program work perfectly for each part). That includes the test cases that run automatically when you submit, and may include other testing as well. The other 30% will be based on coding style, which includes readability, documentation, and design/organization.
All of this is factored into your maximum score based on which part you have completed. Partial credit for partially-working parts will be quite stingy; you have time, so we expect your code to work!
Honor Policy Reminder
Be sure to review the course policy on collaboration for programming projects, which are more about the work you can do on your own and have different rules compared to homeworks and labs. In particular:
- The only help you may receive on a project is from your instructor MGSP leaders for this class, and that help must be clearly cited. In turn, you cannot provide help to any other students on this project.
- In no circumstance - project, homework or lab - may you copy code and submit it as your own work. That is the definition of plagiarism.
- For projects, you may not look at other people’s project code nor may you show your own code to others.
- You can look at online resources for general purpose C programming, but not for help writing financial simulations or anything else that is specific to the functionality required of your project.
If you have questions about any of these rules, email your instructor and ask about it. We know you want to do what’s right and we are here to help you!
Part 1: Shuffle [up to 40 points]
Write a program called shuffle.c
that creates, shuffles, and prints out a deck of cards. A correct solution must store the integer representations of the 52 cards in an array, and only after that print the output. Otherwise its use in working towards the remaining steps of the project is limited.
Your program will start by asking the user for a seed value to use for the random number generator. (Look back at the Craps Lab for a refresher on how to use srand
and rand
from the <stdlib.h>
library). The only exception is that if the user enters 0 as a seed value, do not call srand at all.
Your program them creates an array with 52 cards. For us, each card will be represented by a single integer in the following way:
Represent suits by number according to these rules: 1 = ♣, 2 = ♦, 3 = ♥, 4 = ♠
Represent face values by number according to these rules: 2 = 2, 3 = 3, …, 10 = 10, 11 = J(jack), 12 = Q(queen), 13 = K(king), 14 = A(ace)
The number to represent a card should be
cardvalue = 100 * suitnumber + facevalue
so for example the jack of diamonds is number 211. You can reverse this and get the suit and face value from a card’s number according to
suitnumber = cardvalue / 100; // integer division rounds down! facevalue = cardvalue % 100;
Initially, the card numbers in your deck should be ordered numerically, which means it will start with the 2 of clubs up to the ace of clubs, then all the diamonds (in the same order), all the hearts, all the spades, and ending with the ace of spades.
You must use exactly the following method for your shuffling, remembering again not to shuffle at all if the user enters a seed value of 0:
for i from 0 up to 51 do
set j to rand() % 52
swap the index i element of the deck with the index j element
(Note, this is not a good enough shuffling algorithm for “real” applications because it introduces a small amount of bias in the ordering, but this is what we’re going to do to keep in simple.)
Our Unix terminal program understands “UTF-8” character encodings, which is how we will print card suits to the screen. Each suit symbol is represented by a string. Here are the suits and the corresponding string for printing them in unicode. ♣ ← "\u2663"
, ♦ ← "\u2666"
, ♥ ← "\u2665"
, ♠ ← "\u2660"
.
Also notice that the face values of all cards are one character long, except for 10 which is two characters. So you should always print the card’s face value right-justified to two spaces (meaning, add an extra space in front of every face value except for 10).
Here are some example runs for this part:
roche@ubuntu$
./shuffle
Seed:
0
2♣
3♣
4♣
5♣
6♣
7♣
8♣
⋮
J♠
Q♠
K♠
A♠
(Remember, seed 0 means no shuffling should occur, so the above cards are ordered from 102, the 2 of clubs, up to 414, the ace of spades.)
roche@ubuntu$
./shuffle
Seed:
100
9♦
8♣
J♥
8♦
9♠
7♣
9♥
J♦
⋮
7♥
A♣
K♦
4♥
Q♥
roche@ubuntu$
./shuffle
Seed:
2020
8♣
2♣
J♣
10♣
A♠
Q♣
5♣
3♥
4♦
8♦
⋮
10♥
6♥
4♥
7♥
Part 2 - Deal [up to 70 points]
Start by copying your shuffle.c
(which should work perfectly!) to a new file deal.c
. You’ll start in the same way, asking for a seed value and shuffling the deck, but then instead of printing out the deck you will deal cards to a Player and a Dealer as follows:
- First each player gets two cards. Deal these cards in this order: first player, then dealer, then player’s second card, then dealer’s second card.
- Next, repeatedly ask the player whether they would like to hit (
h
) or stand (s
). As long as they ask to hit, deal the next card into the player’s hand. - Next, deal two cards into the dealer’s hand, one at a time. (As in, assume for now that the dealer “hits” two times and then “stands”.)
- You must print out the player’s and dealer’s hands exactly as shown in the examples below, before each “decision” to hit or stand.
- Pause for dramatic effect after each time the dealer makes a decision to “hit” by calling
sleep(2)
to pause the program for two seconds. This will require#include <unistd.h>
at the top of your program.
Always deal cards from the top of the deck - which in our case means the end of the array. In other words, the last card that would have been printed by your shuffle
program should be the first one that goes into the Player’s hand.
You will probably want to store an array for the player’s hand and for the dealer’s hand, and write some nice functions so your main
stays nice and simple…
Here are some example runs:
roche@ubuntu$
./deal
Seed:
0
Player Dealer
| A♠ | K♠ |
| Q♠ | J♠ |
Hit or stand? [h/s]
s
Player Dealer
| A♠ | K♠ |
| Q♠ | J♠ |
Dealer hits.
Player Dealer
| A♠ | K♠ |
| Q♠ | J♠ |
| | 10♠ |
Dealer hits.
Player Dealer
| A♠ | K♠ |
| Q♠ | J♠ |
| | 10♠ |
| | 9♠ |
Dealer stands.
roche@ubuntu$
./deal
Seed:
100
Player Dealer
| Q♥ | 4♥ |
| K♦ | A♣ |
Hit or stand? [h/s]
h
Player Dealer
| Q♥ | 4♥ |
| K♦ | A♣ |
| 7♥ | |
Hit or stand? [h/s]
h
Player Dealer
| Q♥ | 4♥ |
| K♦ | A♣ |
| 7♥ | |
| 5♦ | |
Hit or stand? [h/s]
h
Player Dealer
| Q♥ | 4♥ |
| K♦ | A♣ |
| 7♥ | |
| 5♦ | |
| 10♣ | |
Hit or stand? [h/s]
s
Player Dealer
| Q♥ | 4♥ |
| K♦ | A♣ |
| 7♥ | |
| 5♦ | |
| 10♣ | |
Dealer hits.
Player Dealer
| Q♥ | 4♥ |
| K♦ | A♣ |
| 7♥ | 3♣ |
| 5♦ | |
| 10♣ | |
Dealer hits.
Player Dealer
| Q♥ | 4♥ |
| K♦ | A♣ |
| 7♥ | 3♣ |
| 5♦ | A♥ |
| 10♣ | |
Dealer stands.
roche@ubuntu$
./deal
Seed:
2020
Player Dealer
| 7♥ | 4♥ |
| 6♥ | 10♥ |
Hit or stand? [h/s]
h
Player Dealer
| 7♥ | 4♥ |
| 6♥ | 10♥ |
| 4♠ | |
Hit or stand? [h/s]
s
Player Dealer
| 7♥ | 4♥ |
| 6♥ | 10♥ |
| 4♠ | |
Dealer hits.
Player Dealer
| 7♥ | 4♥ |
| 6♥ | 10♥ |
| 4♠ | 3♦ |
Dealer hits.
Player Dealer
| 7♥ | 4♥ |
| 6♥ | 10♥ |
| 4♠ | 3♦ |
| | 10♠ |
Dealer stands.
Part 3: Play [up to 100 points]
Start by copying your deal.c
program (which should work perfectly at this point!) to a new file play.c
. You will now complete what’s necessary to play an actual game of Blackjack!
The scoring for Blackjack mostly involves adding up the points in each player’s hand. Whoever gets closer to 21 without going over wins. Going over 21 is called “going bust” and it means you lose. The points per card are as follows:
- 2 through 10 are worth the number of points as their face value says they are.
- Jack, Queen, and King are always worth exactly 10 points.
- Ace can be either “high” for 11 points, or “low” for 1 point. When calculating a hand’s score, you first try making the first Ace count as 11, unless that would cause the total to go over 21 and “bust”. If so, count the Aces as 1.
So for example the points for the hand Q♠ 9♥
would be 19, pretty good. The points for 7♥ A♥
would be 18, since counting the Ace as 11 doesn’t make it go over 21. But the points for 8♥ A♥ J♠ A♣
is 20, counting both aces as 1’s.
Now that you understand the scoring rules, here’s how your game should work:
- Start by getting the seed value, shuffling, and dealing the first two cards to the player and the dealer just like before. Except, don’t print out the dealer’s second card, to keep the player guessing, until after the player’s turn is over. You should still add it to the dealer’s hand, but print it out as
**
until the player’s turn is over. - Before asking the player whether they want to hit or stand, first calculate the total points in their hand. If it’s over 21, then print the message
Player busts!
and move on to the dealer. Otherwise let them keep hitting or standing until they go bust or decide to stand. - The dealer isn’t allowed to make their own decisions. Instead, they follow three rules:
- If the player busted, the dealer should just stand.
- If the dealer has 17 or more points, the dealer stands.
- Otherwise, the dealer will take a hit.
- Continue with the dealer’s rules until the dealer busts or stands, each time pausing for dramatic effect with
sleep(2)
as before. - When both turns are over, print out the player’s and the dealer’s final score, and then print out who won. Remember, winning means that your opponent busted, or you got closer to 21 than they did.
- In case of a tie game with neither player nor dealer busting, print the message
"Push! Play again."
and start a new game, continuing until either Player or Dealer wins.
Here are some example runs of this version:
roche@ubuntu$
./play
Seed:
100
Player Dealer
| Q♥ | 4♥ |
| K♦ | ** |
Hit or stand? [h/s]
s
Player Dealer
| Q♥ | 4♥ |
| K♦ | A♣ |
Dealer hits.
Player Dealer
| Q♥ | 4♥ |
| K♦ | A♣ |
| | 7♥ |
Dealer hits.
Player Dealer
| Q♥ | 4♥ |
| K♦ | A♣ |
| | 7♥ |
| | 5♦ |
Dealer stands.
Final scores: Player 20, Dealer 17.
Player wins!
roche@ubuntu$
./play
Seed:
107
Player Dealer
| K♠ | 6♥ |
| 6♣ | ** |
Hit or stand? [h/s]
h
Player Dealer
| K♠ | 6♥ |
| 6♣ | ** |
| 10♥ | |
Player busts!
Player Dealer
| K♠ | 6♥ |
| 6♣ | 9♣ |
| 10♥ | |
Dealer stands.
Final scores: Player 26, Dealer 15.
Dealer wins!
roche@ubuntu$
./play
Seed:
110
Player Dealer
| K♥ | 3♦ |
| J♣ | ** |
Hit or stand? [h/s]
s
Player Dealer
| K♥ | 3♦ |
| J♣ | A♠ |
Dealer hits.
Player Dealer
| K♥ | 3♦ |
| J♣ | A♠ |
| | 2♥ |
Dealer hits.
Player Dealer
| K♥ | 3♦ |
| J♣ | A♠ |
| | 2♥ |
| | 4♣ |
Dealer stands.
Final scores: Player 20, Dealer 20.
Push! Play again.
Player Dealer
| K♠ | K♥ |
| 9♠ | ** |
Hit or stand? [h/s]
s
Player Dealer
| K♠ | K♥ |
| 9♠ | 7♥ |
Dealer stands.
Final scores: Player 19, Dealer 17.
Player wins!
roche@ubuntu$
./play
Seed:
600
Player Dealer
| A♦ | 9♥ |
| 8♥ | ** |
Hit or stand? [h/s]
s
Player Dealer
| A♦ | 9♥ |
| 8♥ | 2♦ |
Dealer hits.
Player Dealer
| A♦ | 9♥ |
| 8♥ | 2♦ |
| | 3♠ |
Dealer hits.
Player Dealer
| A♦ | 9♥ |
| 8♥ | 2♦ |
| | 3♠ |
| | 5♣ |
Dealer stands.
Final scores: Player 19, Dealer 19.
Push! Play again.
Player Dealer
| 9♥ | 8♣ |
| 8♦ | ** |
Hit or stand? [h/s]
s
Player Dealer
| 9♥ | 8♣ |
| 8♦ | 9♦ |
Dealer stands.
Final scores: Player 17, Dealer 17.
Push! Play again.
Player Dealer
| K♣ | 5♠ |
| 4♥ | ** |
Hit or stand? [h/s]
h
Player Dealer
| K♣ | 5♠ |
| 4♥ | ** |
| K♥ | |
Player busts!
Player Dealer
| K♣ | 5♠ |
| 4♥ | J♠ |
| K♥ | |
Dealer stands.
Final scores: Player 24, Dealer 15.
Dealer wins!
Part 4: Extra [up to 110 points]
No credit will be given for this part until the rest works perfectly. Copy your program to a new file extra.c
.
Believe it or not, there are still a number of aspects of the game of Blackjack as it’s truly played, that we have failed to incorporate, such as:
- Casinos usually combine 7 decks to deal from, to thwart card counters.
- Each game should involve some betting. First, you have to ante a certain amount, say $5. After seeing thier two cards and the dealer’s 1, the player can fold and lose their $5, or play at the cost of another $5. After playing normally, the player either loses all $10 to the dealer, or wins back their $10 plus an additional $10 if they win that hand. (In the case of a push, you get your money back.)
- You might keep playing multiple games in a row, keeping track of your total winnings or losings.
- Perhaps there could be multiple players?
- Or some other feature I haven’t thought of?
This part is open-ended! Improve your game by adding some of the functionality described above, or something else that you think of. (If it’s not listed above, you probably want to run the idea by your instructor first.)
If you complete this part, be sure to document clearly in comments at the top of your extra.c
program what your extra functionality is and how it works!