📱

Get Our Mobile App

Take your business learning on the go!

Download on the App StoreGet it on Google Play

Python for Beginners Tutorial – Learn Programming by Coding a Blackjack Game

Beau Carnes1:12:55

Transcription

This is a beginner's Python programming course. You will learn how to code in Python interactively by creating a blackjack game line by line. I'll walk you through every step of the way. I originally created this for another channel but wanted to share it with you here.

You can follow along using a local code editor such as Visual Studio Code, but I'll be demonstrating with an online code editor called Replit. Once you are logged into Replit, you can start by creating a new Python project. I'm going to close this tab here and zoom in a bit.

For this tutorial, I'm going to say what I'm about to do, and then I want you to see if you can do it on your own before I show you how to do it. Trying things out yourself will help you learn more than passively watching.

We'll first learn about variables. A variable is a reference to a piece of information that can change. We'll start by creating three variables. You can make up almost any name for a variable and set it equal to a string, integer, or other data type.

So let's create a variable called `suit` and set it equal to "hearts." Then a variable called `rank` and set it equal to "K" for King. Finally, a variable called `value` and set it equal to 10.

Okay, you'll notice the strings are surrounded by quotation marks, but the number or int is just by itself. Now I'll add a print statement to display information to the user. It prints the text "Your card is:" with a colon at the end.

I didn't tell you beforehand what to do this time, but from now on, I will. On your own, try to create another print statement that prints the rank. This time we can just add the variable right in the print statement without quotation marks.

We are going to be doing a lot of refactoring as we create this program. Let's refactor this so it's just one print statement that's going to print "Your card is: " and then the rank. So we are going to be doing string concatenation.

Just like that! You can concatenate as many strings and variables as you want. So let's update the code so that the print function prints "Your card is K of hearts." We need to add "of" and make sure we put spaces on each side of the word "of" and then suit.

Let me just adjust this here. Okay, as you know, you can use a list in Python to store multiple values or items at a time. So above the suit variable, create a `suits` variable and assign it to a list of suits: in this case, Spades, Clubs, Hearts, Diamonds.

We learned about how you can use the bracket operator to access a specific element in a list. The number inside the bracket specifies the index of the list to access. Remember, the indexes start at zero. So you update the suit variable so that the value of hearts comes from the suits list.

Now I'll practice a for loop. So add a for loop to the end of the code that prints each suit, and then we'll just test this out. I really hope you are actually following along and trying it out right before I show it to you. That's how you're going to learn the best here.

So Spades, Clubs, Hearts, Diamonds. Now this next thing is just to see if we can do it, so it's not going to be part of our final code. But right before the loop we just added, see if you can add another item to the suits list: that's the string "snakes."

There are a few different ways to do it, but we will use `append("snakes")`. This is just going to append the word "snakes" at the end of the list. So if I run this, we can now see "snakes" at the bottom.

Okay, now we're going to start the process of representing a full deck of cards with Python code. So we're going to actually get rid of a lot of this. We're going to get rid of all of this. We're just going to have the suits, and then we're going to have this for loop at the bottom.

We're going to do a lot of refactoring as we go, mainly for educational purposes, but also so we can create a really good blackjack game. So we have a list of suits. After that, we're going to create a list of ranks: that's 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K.

Now, before the suits list, create a new variable called `cards` and assign an empty list to the variable. You can create an empty list just with two brackets and nothing inside.

Now in the cards list, there should be an item for each card in the deck. Each item in the suits list should be combined with each item in the ranks list for a total of 52 items or cards. Let's work our way up to that.

So first, we'll update the print statement in the for loop so that it prints a list with two elements. The first element should be suit, and the second should be the first element of the ranks list. This should print an Ace in every suit.

So I'm going to update this so it's going to be a list with suit and ranks. The first item is going to be at index 0. Now let's print that out. So we got these four right here.

Now instead of just printing an Ace in every suit, let's print every rank in every suit. This can be done easily with a for loop nested within another for loop. So inside the for loop, add another for loop that loops through the ranks.

Then update the print statement so that it's not just printing the first element in the ranks list, but it's printing the rank from the other for loop. Let me show you what I mean.

We're going to do `for rank in ranks`, and then we have to make sure to indent this print statement so it's inside this other for loop. This is now just going to be rank, so it's going to print the suit and rank.

I'll just run that, and now we have, with this nested for loop, every card at every rank in every suit. All 52 cards are printed as two-item lists. An element in a list can be another list, so instead of printing 52 two-item lists, let's append those 52 cards to the cards list.

We already have the cards list here; it's empty, but I'm going to do `cards.append(...)`. So we're appending all these items to the cards list. Let's check what the cards list looks like by printing it out at the bottom.

Remember, make sure this is not indented at all, and we'll do `print(cards)`. I'll run that, and here it is. This is the list; it's not one. There's just a comma between each item in the list here.

You may notice that all the cards are in order in the cards list. For a game like this, though, the cards must be shuffled. To help with this, import the random module at the top of your code. So we just do `import random`.

Now we'll be able to use the random module. This is going to import the random module, which contains a variety of things related to random number generation. As you probably remember, when you import a Python module, it allows you to use additional commands in your code.

Specifically, we're going to be using the `random.shuffle` function. So right before it says `print(cards)`, we're going to call `random.shuffle` and pass in the cards list to that function.

If I run the program, we can see that these are not in order anymore. See, Ace of Spades, Three of Spades, King of Diamonds, Jack of Hearts. So these are no longer in order because they've been shuffled.

Now let's remove a single element from the cards list. This is similar to dealing a card from a deck, and this can be done with the `pop` method. So after the cards are shuffled, let's create another variable called `card` and just pop off a card from the cards list and put it into that variable called `card`, and just print that card.

I'll do `card = cards.pop()`, and then instead of printing all the cards, I'm just going to print a single card. I'll run the program. See, every time I run the program, you can see we're getting a different card. We're dealing a different card because it's been shuffled.

So we've already learned all about functions, and now we're going to create a function. So create a function called `shuffle` that just has the single line that shuffles the cards. So it's just `def shuffle():`, and then I just have to make sure this is indented.

Now when we call the shuffle function, it will shuffle the cards. So right before the print statement, call the shuffle function, and instead of just printing the single card, print the cards. So do `shuffle()`, and then I will print all the cards.

Let's try out the program, and we can see there was a problem. It's because we didn't put the colon here, so that's an important part of creating a function: putting the colon there.

Now we'll create another function called `deal`, and we'll put this line inside the deal function. So we're going to define `deal`, and I'll put the colon this time and make sure to indent that.

We can see this has an orange squiggly line underneath it because variables can only be accessed in the context that they were created. So the card variable will not be available outside of the deal function.

You can get a value out of a function by returning a result using the return statement. So at the end, we're going to return the card. Okay, now we've taken care of that squiggly line there.

So after the shuffle function is called, we'll call the deal function and assign the return value to a variable named card. Then we'll update the print function to print card instead of cards. So `card = deal()`, and then we'll just print the card.

Again, we see a different card every time we run the program. What if you want the deal function to deal more than one card? Well, let's refactor the deal function to accept an argument.

Any number of arguments can appear inside the parentheses when a function is created, separated by commas. Inside the function, the arguments are assigned to variables called parameters. So start by making it so the deal function takes an argument named `number`.

Then we'll make sure when we call the function, we use the new parameter by making it so we're going to deal two. So I'm just going to put `number` here. It's going to deal a number of cards.

I just didn't say this before, but now instead, this is not one card anymore. So we're going to update this to be `cards_dealt`. But there's a special shortcut you can use: it's going to be Command or Control D.

Now I'm actually selecting the card two different times. See, I now have multiple cursors here. So basically, I selected the word, I double-clicked to select the word, then did Command or Control D. Now it's selecting two words, and now I can type in `cards_dealt`.

So now I can type in two places at one time, so that's a cool thing that you can do in Replit, and you can do it in many other code editors. I'll run the program, but it should still only deal one card because even though we're passing this parameter into here, we're not doing anything with it yet.

So we want to update the deal function so it's going to return a list of cards instead of a single card. In the first line of the function, create an empty list named `cards_dealt`. Then update the last line of the function to return `cards_dealt` instead of `return card`.

So let's do that really quick. We're going to do `cards_dealt = []`, and I'll just copy that and paste it right here. Now, do you remember how to use the range function with a for loop? We talked about it earlier in the course; we just briefly touched on it.

But let's create a for loop that's going to add a card from the deck for each card dealt. We can do that by creating a for loop: `for x in range(number)`. Now this is a common thing you're going to be doing in Python: creating a for loop that's going to be in range number because now it's going to loop this many times, which is the number we passed in here.

We're going to do a few things in this for loop. First, we are going to actually do this. We already have `card = cards.pop()`, and then we'll do `cards_dealt.append(card)`.

So now just this card that we popped off the deck, we are appending it to the cards dealt, and then we're returning the cards dealt here. So down here in the code, let's separate out a single card from the two cards dealt.

Let's create a variable called `card` and set it equal to the first item in the cards dealt list, and then we'll just print that card instead of cards dealt. So we are going to do `card = cards_dealt[0]`, and then we'll just print a card.

Now I'm just going to test out the program. We're still just seeing a single card here, but it's doing a lot more behind the scenes now. So now let's separate out the rank part of a single card.

So after we create the card there, let's create a variable named `rank` and assign it the rank from the card. So we'll do `rank = card[1]`, and I have to get index 1 because the rank, that's the nine here, the second item in this card is the rank.

So each rank has a different value in blackjack. The value of an Ace or an A in this program is 11, or sometimes it can actually be 1. It's going to be 11 or 1, but we'll get to the one part later.

So Jack, J, Q, and K, which is Jack, Queen, and King, have the value of ten, and then the numbers have the value of the number. So we need to check what the rank is and set the value depending on the rank.

So this is the perfect time for a conditional statement, specifically an if statement. Before the final print statement of the program, we're going to add an if statement to check if the rank equals "A," and if so, we'll assign 11 to a variable named `value`.

So we'll do `if rank == "A":`, and I hope you remember, if you're following along, I hope you remember to use two equal signs instead of one equal sign here. So if rank equals "A," then value is going to equal 11.

Now if rank does not equal "A," we'll want to check if it equals "J," "Q," or "K." That can be done with an elif statement. For now, we'll just create an elif statement to check if the rank equals "J," and then if so, we will set the value to 10.

So we talked about the three logical operators: and, or, and not. You can use these three operators in conditional statements to check multiple conditions at once. So we want to check if rank is "J" or rank is "Q" or rank is "K."

So update the code with the "or" statements. Now there can be any number of elif statements after an if statement, but at the end, there can only be a single else statement.

As we discussed, the else is just going to be if none of the other ones are true. So let's add an else statement, and inside, we'll just assign rank to value because we've already gotten all the letters out of the way. The rest are numbers, and we can assign it directly to the value.

At the end, let's print the rank and the value. So I can just type in `rank, value`, and when multiple values in a print statement are listed with a comma separating them, both values are printed with a space in between.

So let's test this out a few times: Q, 10, 5, 6, 6. We can see every time we press, it's going to be a random rank and value.

Now we already talked about dictionaries in Python. It's like a list but more general. You can think of a dictionary as a mapping between a set of indices, which are called keys, and values, so key-value pairs. Each key maps to a value.

So above the print statement, let's create a variable called `rank_dict` for dictionary and create a dictionary with two items: a key-value pair for the rank and a key-value pair for the value.

So we have the string "rank" here and then the actual rank variable, string "value," and the actual value variable. Before we were printing the rank variable and the value variable, but let's update this code so we're actually getting the rank and value from the rank dictionary right here.

So I'm going to copy that, and then I just pasted that, but now I'm going to use bracket notation. So I'll put two brackets, but then I also have to surround this in quotation marks.

Then I'm going to put the rank dictionary, the brackets, and then the quotation marks because we're accessing that key there. Now I can just run the program, and it's still doing the same thing as before, just a lot more complicated as far as the code goes.

But it's going to be good to have more complicated code as our program is going to become more complicated as we go. So when writing a program, there are many ways to do almost everything.

Now we're going to refactor the code to get the value of each rank without using an if statement. Instead, we'll store both the rank name and value in the ranks list using dictionaries.

So let's delete all the lines of code after where it says shuffle. So here, I know we typed in a lot of stuff there, but it was just kind of to practice, and now we're going to practice a different method of doing this.

So now let's create a new variable called `card` at the end, and let's assign to the card variable a single card that will deal from the deck. But we'll make sure that card is not in a list.

So this is a little tricky. I'm going to do `deal(1)[0]`, but now I have to get the first item. So this is going to deal one card, but the one card is going to be in a list, so I want to get the first item in the list, which is going to be the only item in the list.

Now we're going to update the ranks list. So here's the ranks list. Each element of the list should now be a dictionary. When list or list elements are long, it's common to put each element on its own line.

So we're going to put each element on its own line, and each element is going to have the rank and the value. For instance, it'll be a rank "A," value 11; rank "2," value 2. So it's going to look like this.

Now I'm actually going to zoom out just a little bit, and we have all these ranks, and each one in this list is a dictionary. Each element in the list is a dictionary.

Okay, now that this is updated, let's go down and just print a card so we can see now that we've updated that ranks list. So print card.

Okay, so this is what it's going to look like coming from our list. So we got the suit, and then we have the rank that's also going to have the value here, the rank and the value.

We can see every time I click it, we get a random item. Now let's update the code so instead of printing the whole card, we just print the value.

So in this example, the value is 2. So we just want to print this 2, just that value. So how can we update this? Try to see if you can figure out how to update this line so it only prints just the value number there.

So first of all, we have to see that we're in a list, and we need to get the second element of the list, which is index 1. Then we have an object here, or a dictionary, and we need to get the value at that key.

So to get the value of that key, we are going to put more brackets, and I'm going to put "value," the key of value. So now that should work. Let's try it.

Okay, 9, 7. See, every time it's going to just give us the value of the card. Now we'll start defining classes that will be used in order to separate out different aspects of the game.

So classes, you may remember, provide a way of bundling data and functionality together. Creating a new class creates a new type of object, allowing new instances of that type to be made. An object can contain a number of functions, which we call methods, as well as data that is used by those functions, called attributes.

So we're going to use classes to model three parts of the game: a card, a deck, and a hand. So far, we've mainly worked on the elements of the deck class.

So right after this import statement at the top, we're going to make a class called `Deck`, and we're going to put everything that we've written so far in that class. So we're just going to do `class Deck:`.

Okay, now we just highlight everything here, and then I'm going to press Tab to put everything in the class of Deck because everything's indented a little bit. Then these last few lines of code we don't need, so I'll just delete those. Those are just for testing.

A class is like a template. You can use that class to create an instance of the class called an object. Then you can use the instance. Each instance keeps track of its own state, so you can update an instance created from a class, and it won't impact other objects created from the same class.

Assume you'll see an example of all this to make it easier to understand, but first, let's prepare the class to create an instance from it. When you create an instance of a class, Python automatically calls a function, also called a method, in the class named `__init__`.

Remember, we already discussed this earlier in the course. So the contents of this `__init__` method should be code that is run one time to initialize the instance.

So at the beginning of our class, let's create this `__init__` function. So we'll do `def __init__(self):`. If you remember from before, we always have to pass in `self` to all of these functions in a class because then it gets `self` is referring to the instance of the class that we've developed.

Now we're going to indent all the code that's not part of the shuffle or deal function so the code will be part of this new function. So I'm just going to highlight all of this here, including the suits here, and then just press Tab.

So like I said, we just added self in here. You should always have all the methods in a class or all the functions should have self. Anything inside the parentheses, remember it's called an argument; there are variables passed from the caller to the functions.

As I've said, all functions in a class should receive self as an argument, and self represents the instance of the class. By using the self keyword, the function can access the attributes and methods of the class.

So let's make sure to add self as the first item in the parentheses of the other functions. So we are going to add self here, and then see how we already have number here, but we're going to add self at the beginning.

We can still call this function with just a single number, but it's going to also get a reference to the instance here. Now I want you to notice that the cards here are underlined in red.

So before, it wasn't when we were before we made this into a class; we could just access this cards variable, but now we cannot. So let's fix that. Inside a class, in order to access a variable in multiple functions, also called methods, the variable has to start with `self.`.

So we're going to change all instances of cards in every function to `self.cards`, starting with this. So `self.cards`. Now this is going to make it so we can access it in other places, and then we can access it in all of these other methods.

Okay, we can now create an instance, also called an object, of the deck class. So at the very end of the code, let's create a variable called `deck1` and make it an instance of the deck class.

So to make sure I'm not indented at all, I'll do `deck1 = Deck()`. There we go. Now since we created card with `self.cards`, we can access that. We can access cards from the instance of the class.

So let's just print out the cards from our deck1. So do `print(deck1.cards)`, and we can try that out. Now you can see the list of all of these cards has the suit and the rank and the value for each card.

So underneath where we created deck1, let's create deck2. We'll create another instance of another deck.

So now we can call methods on these instances, and you see some of the methods we have: we have shuffle and deal. So on deck2, right after we create the deck2, let's shuffle the deck.

So `deck2.shuffle()`, and then I have to make sure to put the parentheses at the end here. Right? If we print deck1, let's print deck2 or the cards of deck2.

So I'm going to copy that, and then we'll print `deck2.cards`. So now we should see that the deck1 cards are not shuffled, and the deck2 cards are shuffled.

So let me move this over here. I'm going to run the program, and let's see if we can see that. Where's deck1? So here's deck1, and we can see how it's all diamonds, diamonds, diamonds, diamonds, diamonds. All the diamonds are in a row because unshuffled.

But then if we go into deck2, we can see we have diamonds, clubs, spades, diamonds, hearts. So these are shuffled, and deck2, they are shuffled.

Okay, the deck works. Now let's add safeguards to prevent errors. Every time the deal function is called, a card is removed from the cards list. You can only remove a card if there are cards to remove.

So before the program tries to pop a card off, check if the length of `self.cards` is greater than zero. Remember, you can get the number of items in a list with `len()`.

So see if you can figure that out on your own, and then I'm about to show you how it's done. So when it's going to deal here, right as we're dealing, we're going to add an if statement here.

So if the length of `self.cards` is greater than zero, and we don't need this parentheses here, so if the length of `self.cards` is greater than zero, then we will do this: we'll pop a card and add it to the cards dealt.

If not, we just won't do anything, and then we'll return cards dealt, which could be an empty array if there were no cards on the deck.

Now let's add something to the shuffle function. A deck with only one card does not need to be shuffled, so let's add the appropriate if statement to the shuffle function.

So we'll do `if len(self.cards) > 1:` then we will shuffle. Then make sure I'll make sure to put the colon there.

Okay, since a card is a separate concept than a deck, next we'll make a card class. So let's create a card class with an init function, and in that init function, we'll set `self.suit` to equal "hearts."

So hopefully you already tried this. I'm going to do `class Card:` and then I will do `def __init__(self, suit, rank):`. Instead of "hearts," we'll set it to suit and rank instead of "A."

So currently, anytime a card is created, it will be an Ace of hearts. Let's refactor the code so the suit and rank are specified when a card object is constructed.

So the init method can take additional parameters besides self that are passed into it as objects are constructed. So we'll update it to take suit and rank.

Now we'll create a special method that's `__str__`. When a class has this specific method, it's called when print is invoked on an object from the class.

So we want to make it so when we print an object from the card class, it will print something like "10 of hearts" or "3 of clubs" or something like that. So we don't do print here; we do return.

It's going to return `self.rank`, and then we have to get the rank, and we do plus and then " of " plus `self.suit`. So now it's going to return the rank, which is like 2 or A, of and then the suit, which is one of these.

So let's just try it out real quick. I'm going to go to the bottom. We don't need any of these to test because we're testing something completely different.

Now let's do `card1 = Card("hearts", "J")`. I'm going to create a card, and I have to pass in, and I have to first pass in the suit, so how about "hearts," and then I have to pass in the rank.

But we want to make it look like these ranks, so I'm just going to copy one of these here. And then after we create the card, I can just print `card1`. Let me clear this, and then I'll just run that.

J of hearts. I see I got the J of hearts. Feel free to add a few more cards like this and test out a few more if you want.

Okay, now we're going to refactor this slightly. You remember way toward the beginning of this course, we talked about f-strings. So f-strings allow us to put variables right within a string.

Do you remember how to do that? Let's see if you can update this to use an f-string. So first, we're going to create a new string, but we're going to start with the letter "F," and then inside this string, we put curly braces around the Python code.

We don't need these other strings here, so now we put another curly brace and then an ending string here. Okay, it's still showing these red squiggly lines because if I have a double quote around the strings and anytime other quotes are in the middle, I have to put a different type of quote.

So we're going to use single quotes. Okay, so now we can make this a whole string, but we use the brackets to put the variables right within the string.

So now we've updated that to use an f-string. So currently, in the deck class, the last line of this init method appends a list as an item to the cards list instead of appending suit, rank.

We'll create and append an instance of the card class. Then afterwards, when a deck is created, it's filled with cards. So it's just like this. We're just going to delete that.

I'll put `card = Card(suit, rank)`, and then I'll pass in suit and rank. So now we're passing in card instances.

So we're done with the deck and card classes, and we created them in such a way that they could basically be used for any card game. Now let's make a hand class. This will represent the hand in the game of blackjack.

So create a hand class and add an init method and initialize a variable called `self.cards` that is set to an empty list. So let's go down here, and we can also get rid of all this test code here.

So the new class is called `Hand`, and we'll also make the hand keep track of the value of the hand. So `self.value` will start at 0. In this blackjack game, there will be a human-controlled player and a program-controlled dealer.

So let's add a dealer parameter in the init constructor method of the hand class, and then when the hand class is created, the dealer should be set to true or false to keep track of what type of hand it is.

So I'll pass in the parameter dealer, and then we just have to create a variable called `dealer` and set it to dealer. So `self.dealer = dealer`.

If you remember from before, function parameters can have default values. So we want to make it so the default value of dealer is false. So then if we create a hand and we don't set the dealer value, it will automatically be false.

I'm just going to take out these spaces here to make it smaller. So now a hand can be created. Let's give it some functionality. We'll add an add card method, and the method should take a card list as a parameter.

Then we need to add that card list to the cards. So we can use the extend function, the extend method to append each item in card list onto the cards list.

So it's just going to look like this: `self.cards.extend(card_list)`. Now let's just add some code to test out what we have so far.

So let's create a deck, and then we will shuffle the deck: `deck.shuffle()`. Now we'll create a hand.

Now we can add cards to the hand. So `hand.add_card(deck.deal(2))`, and then we'll just print `hand.cards`.

Okay, so this is how it printed out. I was expecting this to look a little different because of this function. It should print like that, but I think the reason is because this is a list, so it's printing a list, not an individual card.

So let's change this to print an individual card. I'll print the first card, so put `[0]` in there, and I'll try it again: nine of diamonds.

And then we can also print the next card: three of hearts. And then we can also print both cards if we just copy that and do `hand.cards[0]` and `hand.cards[1]`.

Okay, Ace of hearts and nine of spades. So those are the two cards that were dealt to the hand. Now we'll go back to the hand class, and we'll add the ability to calculate the value of a hand.

So let's add a method called `calculate_value`, and inside the method, we'll set `self.value` to 0. Now we'll take this one step at a time. First, let's make a for loop that's going to go through every single card, and inside the for loop, we'll just set the value of the card to a variable called `card_value`.

So I'll do `for card in self.cards:`. Okay, so we're not doing anything with that yet, but we're going to in a second here.

Now we want to make sure that this is an integer, so let's convert that to an integer. If you remember, you just use `int()` and then put it in print.

Int and then inside the parentheses, we put this value, not just getting the card value for each card is not enough. Something must be done with the variable, so let's add that value to `self.value`.

So we'll do `self.value += card_value`.

As you may know, in blackjack, an Ace can have the value of either 11 or 1, depending on what is better for the player. So there's a few ways to implement that in code.

So we're going to do one way that's relatively simple. First, we'll check if the hand has an Ace. So let's first create a variable that will store whether the hand has an Ace.

So just call it `has_ace`, and we'll set it to false. We'll put it right under here. So I'll do `has_ace = False`, and since we're only going to be using `has_ace` within this method, we don't need to use `self.has_ace` because we're only using it here.

Now when we're going through the list of cards, let's check if the rank of a card is an Ace and then set `has_ace` equals true. So I'll do `if card[1] == "A":`, and then `has_ace = True`.

After this entire for loop, we're going to check if the card has an Ace, and if the value is over 21. If so, then we'll just subtract 10 from the value because that will be the same as setting the Ace to equal 1 instead of 11.

So we'll just do `if has_ace and self.value > 21:`, then `self.value -= 10`.

Okay, and look at this. This is something I don't think I've discussed yet. You could say `if has_ace == True and self.value > 21:`, but you can also, it's like a shorthand.

You don't have to say `if has_ace == True`. If `has_ace`, so that's just the same as saying if true or if false. You can just say `if has_ace`.

So we're seeing if both of these evaluate to true, then we will subtract 10 from the value.

Okay, now we'll just add another method to get the value of a hand called `get_value`, and the function will just return `self.value`.

So we're going to make sure that we're indented correctly and do `def get_value(self): return self.value`.

And then I have to make sure I put the parentheses here, and then I have to remember to put self since this is a self.value.

We could call down here, like we could call `hand.value` to get the value, but it's generally better to make a function to return the value. So I can do `hand.get_value()` that way.

There may be some extra code you want to run in there. Depending on different conditions, you may want to modify the value before you return it. So it's best practice to create a method that will get a value like this for you.

So currently, this value that's returned could be incorrect because if someone's going to get the value, the value has to be calculated correctly first, like checking for Aces and other things.

So let's call, let's calculate the value before we return the value. So I'm going to do `self.calculate_value()`.

So this is something that I think is new, where to call `calculate_value()` from within this. We're going to have to call `self.calculate_value()`, and self will refer to the instance that we're working with.

So we're calling the calculate value on the instance that is the hand instance, and we're getting the value, and then we're returning the value.

Okay, let's create another method called `is_blackjack`, and it'll return true if there's a blackjack and false otherwise. So it's a blackjack if the value is 21.

So I'm going to do `def is_blackjack(self): return self.value == 21`.

So this is going to evaluate to either true or false and return true or false depending on whether there's a blackjack.

Now we'll create the final method in the hand class that will display information about the hand. So let's create a method called `display`.

To start with, we'll just print "Your hand." Okay, now let's do a quick refactor. Instead of saying "Your hand," it should either say "Dealer's hand" or "Your hand," depending on whether `self.dealer` is true or not.

So we're going to do this all in one line. We're going to use a few things that we learned about earlier, including ternary operators, f-strings, and going between double quotes and single quotes.

And then one other new thing we are going to make this into an f-string, and then we are going to be using actually single quotes and double quotes within this f-string.

So if you want to use single quotes and double quotes within a string, then you can surround it with triple single quotes. So I'm going to delete this quote, just do three single quotes, and then delete this quote and do three single quotes.

So we got the double quote, single quote, and now this is a triple quote. So now we can use the double quote and single quotes within this string.

So I'm just going to delete "Your hand" right here, and we're going to put a ternary operator to see if it's going to say "Dealer's" or "Your."

So to do some code, I'm going to put these curly braces here, and then to do this ternary operator, we're going to put `self.dealer`, and now here it says here's the double quote, and here's a single quote.

So "Dealer's" will return "Dealer's" if `self.dealer` is true, so return "Dealer's" if `self.dealer`, else will return "Your."

Okay, that's the line. So it's going to be the Dealer's hand or your hand.

Next, we will add a for loop that will print out each of the cards. So `for card in self.cards: print(card)`.

Then finally, if the player is not the dealer, it should print "Value: " and then print the value of the cards.

So to do this, we can actually use the not operator. So if not `self.dealer`, then we will print "Value: " and then I can just put a comma.

It's a print of two different items, so the string, and it will print `self.get_value()`.

When you put a comma and two different things, it's going to put a space in between. Finally, we'll just add an empty print statement that will print a blank line.

Okay, let's test this out by instead of printing this, we are going to print `hand.display()`. See if this all works how we thought it was going to work.

So "Your hand." Okay, of Spades, two of Spades, a value is 12. So it's actually calculating that correctly because that's 10 plus 2 is 12, and then it's going to print none, which indicates that we did something wrong, which is that we did not need to print this because `hand.display()` already prints.

So now I'll just call `hand.display()`. Okay, so now it doesn't put none or doesn't, yeah, it doesn't put none at the end, so that looks right.

Okay, when you're playing blackjack, you don't get to see everyone else's cards. So we're going to update this so when the dealer's cards are printed during the game, only the second one should display. The first card should display as hidden.

So in this for loop when we're displaying the cards, we're going to need to get access to the card index since that will determine which to display since we're only going to display the second card.

So let's start by updating this for loop so we can get access to both the card and the card index. We briefly touched on this earlier in the course.

We're going to be using the `enumerate` function. So when I see `for index, card in enumerate(self.cards):`. This is going to return the index and the card for each card.

So I'm going to type `index, card` in, and we're getting the index and the card for all the items in `self.cards`.

Now we just have to update what's in the for loop to print "hidden" if it's the first card and it's a dealer.

So we'll do `if index == 0 and self.dealer:` then we will print "hidden."

Then we can use an else, and the other time, and let's make sure this lines up correctly. Any other time, we will print the card.

So what we did wrong here is this would be a double equal sign. I almost did the main mistake. You always have to watch out. Never use a single equal sign when you're checking equality because that's the single equal sign is the assignment operator.

So if `index == 0 and self.dealer`, then we'll print "hidden."

So in our version of the game, at the end of the game, all the dealer's cards will be shown, so you can see what the dealer had.

So to do that, we're going to create a new parameter in this display method, and it's going to be called `show_all_dealer_cards` with underscores for spaces, and we're going to set the default value to false.

So `show_all_dealer_cards = False`. Now we'll add it to this if statement, so we'll add another `and not show_all_dealer_cards`.

So it's going to be hidden if we're not showing all the dealer cards, but if we are showing all the dealer cards, then this whole if statement will be false, so we'll just print the card.

And there's going to be one other scenario where we're not going to print hidden if there's a blackjack, then the game is over. The person with the blackjack is just going to win, and then we'll just print all the cards.

So we're going to add that to this long if statement here. So we'll say `and not self.is_blackjack()`.

It should be `self.is_blackjack()` to be able to call this method here. Since this is such a long line, it's always going to go to this next line.

We can do this special thing: we can add a backslash here and then just go to the next line. So this backslash will indicate that the line continues on the following line.

Okay, we're done creating the hand class, so we'll delete everything that we were using for testing before.

Okay, it's time to code the final and longest class that runs the game. So what I want you to do is create a class called `Game`, and inside the class, create a method called `play`.

Inside the method, create a variable called `game_number` with the underscore for the space and set that to 0. So `class Game:` and then we'll create another variable `games_to_play` and set that to 0.

Now we're going to set `games_to_play` to be whatever the user inputs after they're asked, "How many games do you want to play?"

So you may remember how to do input from before, so we just do `input()`. Now we want to make sure the `games_to_play` is an int, so we just need to convert this to an int.

Okay, now let's test things so far. So at the end, I will put `g = Game()`. I'm going to create a new game, and then `g.play()`.

Okay, let's test this. How many games do you want to play? Five. Okay, well, it's not going to play the games yet. We still have to create that.

So there is a potential for an error here. If I do this again and I just put how many games I put you or some letter, we're going to get an error.

So basically, anytime someone puts something that's not a number, it's going to be an error. So let's create a try-except block to handle the exception, and if they put something that's not a number, we'll print "You must enter a number."

So let me arrange this, and we've already learned a little bit about try-except blocks. I'm going to put try, and it's going to try this, and then if that doesn't work, if there's an exception, it will print "You must enter a number."

So currently, the user gets only one chance to input a correct value. Let's make the program keep asking the user for a value until the user enters a number.

This can be done with a while loop. The while loop just keeps looping while something is true. So keep looping until the user enters a number by putting the entire try-except block into a while loop that keeps looping while `games_to_play <= 0`.

Oh, and I have to make sure I spell while correctly. Okay, now let's create the main game loop. This is a new loop that will loop one time per game played.

It should loop while `game_number < games_to_play`, and the first line of the loop should increment the game number by one. Inside the loop, we'll create a deck object in a deck variable and shuffle the deck.

Now we'll create a variable called `player_hand` and set it to a hand object, and then we'll create a variable called `dealer_hand` and set it to a hand object, but this time we'll make sure to specify that `dealer=True`.

Okay, this next part will be a little more complicated. We'll create a for loop that loops two times, and each iteration should add a card to the player's hand that is dealt from the deck and add a card to the dealer's hand that is also dealt from the deck.

Okay, we've just dealt two cards to each player. Now information is going to be printed to the console for each game. So let's start by printing an empty line.

Now we'll print an asterisk 30 times to make a divider. There's a trick to printing something a lot of times, so I can put an asterisk in quotation marks and then just do `* 30`.

So it's going to print it 30 times. Now we'll print the current game number out of the total number of games, so it'll be something like "Game 4 of 10."

We'll use an f-string, and then we'll just print 30 more asterisks. Now we'll display the player's hand and then the dealer's hand.

At this point in the game, someone could already have won if they got a blackjack. The code should check if there's a winner. Let's put the code to check if there's a winner in a separate method of the game class.

So create a method called `check_winner`. For now, the method should just return false, and just make sure everything's indented correctly. This should be less indented than the previous line here.

The `check_winner` function should take the player hand and dealer hand as arguments. Now before this return statement, we're going to check if `player_hand.get_value() > 21`.

If so, we'll print "You busted, dealer wins," and then return true. Remember, once the program gets to a return statement, none of the following statements in the block are run.

Now we'll use a few elif statements to check for various other conditions. So we'll add an elif statement to see if the dealer got over 21, and then we'll print "Dealer busted, you win," and then return true.

Oh, and I just copied all this, but this should be an elif, not if. Then we'll add an elif statement to check if both players have a blackjack, and then we'll print "Both players have a blackjack, tie," and then return true.

Then we'll add an elif statement to check if the player hand is a blackjack, and then we'll print "You have blackjack, you win," and then return true.

Then we'll check if the dealer hand has a blackjack and then say "Dealer has blackjack, dealer wins," and return true.

Okay, we're done with all the hand win conditions, but the game can also end if both players choose not to get more cards.

So we're going to add a new argument to the `check_winner` method with a default value. It's going to be `game_over=False`. So we'll add `game_over=False`.

If it's true, that means both players have chosen not to get more cards. Now we'll use the new argument. The string of if and elif statements should only be run if it's not a game over, and we'll make sure the line `return False` is not in the if statement.

So here we'll say `if not game_over:` and then I'll just select all these and put them in here. So if `game_over` is true, we'll check if the player hand's value is more than the dealer hand's value, and if so, we'll print "You win."

So we can do this with an else here. Else if `player_hand.get_value() < dealer_hand.get_value()`, then we'll print "Dealer wins."

And then we'll do an elif for if it's a tie. So this is an elif, and we'll say if these are equal to each other, and we'll print "Tie."

Then make sure we have the correct emoji for a tie, and then else the dealer has won. So we'll just do else, and then at the exact same level of indentation as the else we just added, we'll add `return True`.

This will make the method return true if `game_over` equals true. Now let's go back to the play method inside the while loop, and then we'll do an if statement.

We'll do `if self.check_winner(player_hand, dealer_hand):`. So let's go back up here. If this is true, that means we should go on to the next game.

To do that, we do `continue`. So `continue` is going to just go to the next iteration of the loop, and the loop we're on is this loop.

So when we go to the next iteration, we start a new game. At this point in the game, the player will be able to choose hit or stand.

So inside the while loop, but not inside the if statement we just added, we'll create a variable called `choice` and set it to be an empty string.

The player should be able to keep choosing until the value of their hand is over 21. So right under the choice variable, we'll add a while loop that loops while `player_hand.get_value() < 21`.

Inside the loop, we'll add a line to get the choice that's either going to be hit or stand, and then we'll just add this to convert whatever the answer is, whatever the user put in, we are going to convert it to lowercase.

The while loop we just added should also stop if the user's choice is "stand" or "S." So we'll update the line that starts the while loop to also stop if the choice isn't "S" or "stand."

So I'll just do `and choice not in ["S", "stand"]`. So we are checking if choice is not in this list, and inside the list, we have two elements: "S" or "stand."

So if choice is not in that, if the choice is not "S" or "stand," then we'll continue the loop.

And then after the input, we'll print an empty line. Also, we want the program to keep asking the user for choice until the user enters a valid choice.

The valid choices are "H" for hit and "S" for stand. So right after the last print statement at the same indentation, we'll add a while loop that will keep looping until the user enters a valid choice.

Inside that while loop, we'll ask for input again, but we'll specify it can be "H" or "S" as well. So this is going to look very similar to this line, but it's going to clarify things just a little bit.

And then we'll print another empty line. The last while loop we checked if choice was not in a list outside of the recently added while loop, but inside the loop we just added before that one, we'll add an if statement to check if choice is in the list "hit" or "H."

If so, we'll add a card to the player's hand that is dealt from the deck, and then right below that, we'll display the player's hand.

Outside all the while loops about the player making a choice, we'll check for a winner. We'll use the same if statement and continue statement that we used last time we checked for a winner.

So I'll just copy this, and then we have to make sure it's lined up correctly. Okay, so this is outside of this while loop.

So after this, all is done, we check for a winner. Let's just add an empty line there to make it more clear that the while loop is over.

Now we'll store the value of the player's hand in a variable named `player_hand_value` with underscores for spaces.

And we'll do the same thing with the dealer's hand. Remember, I could use the command D or Control D to select two words at once and change them both at the same time.

Okay, the dealer should keep drawing cards until the dealer hand value is more than 17. So we'll make this happen with a while loop, and inside the loop, we'll make sure the dealer is dealt a card from the deck and that dealer hand value is updated.

So you can try that yourself, but I'm just going to show you right now. While `dealer_hand.get_value() < 17:`, then we will do `dealer_hand.add_card(deck.deal(1))`.

Okay, and after this while loop, we'll display the dealer's hand, and when we call the display method, we'll make sure to set `show_all_dealer_cards=True`.

And since it's the end of the game, that's why we're just showing all the cards. Now we'll check for a winner just like before.

Then we'll print final results. Then we'll print "Your hand: " and then the player hand value.

And then the dealer's hand. Now we'll call the check winner function one final time, but this time it should not be in an if statement.

We'll pass in the hands like before, but this time we'll add a third argument of true to indicate that the game is over.

At this point in the code, the game is over. So outside the outer while loop and in the play method, we'll add the final line of saying "Thanks for playing."

So it's going to be outside that while loop, and we'll put `print("\nThanks for playing!")`.

And when I line this up with the while loop, I realize that this entire function should not be lined up with the while loop. Sometimes it gets tricky with figuring out the exact right indentation.

So if I kind of go up straight up here, I should see that this should be lined up with this play function. So I'm going to come back down to this function.

I'm going to copy this all, and I'm just going to do Shift + Tab to indent it all one less. This happens sometimes when writing Python code. Sometimes the indentation can get all mixed up, but that should be correct now.

And I think the red squiggly lines here on the return true are not a mistake in the code but a mistake in the error checking because it comes after that emoji, and it doesn't know how to handle the emoji.

But it's perfectly fine for code to have emojis. Okay, let's run the program and try it out. So I'll press play.

How many games do you want to play? I'll do three. So game one of three. So I can see I have 17. I don't know what the dealer has, but I'm going to "S" for stand.

Okay, it's always good to test. So it says "deal" is missing one required positional argument. So let's go up to it. It says line 139, so this can kind of help us know where to go.

So let's go up to 139, and yeah, I want to deal a single card, so I'm going to deal one card here.

And were there any other times I did use deal? I went to deal one card here, and yeah, I got the deal one up here.

So I just think I just forgot the deal one in those places. So thanks to these error messages, one of you have a problem. Make sure to read the error messages, and it can often give you a very good idea of what you need to do wrong because it even says "deal" is missing one required positional argument, the number.

So that can really help figure out what's wrong with your code. So let's try that again. We'll do three games, and then this time I will hit, and I'm going to stand.

Okay, so now we have another error. So it says 173, and oh, this I can already see this is spelled wrong. So let's go to 173 and make sure I spell that correctly.

Okay, let's try again. How many games do you want to play? Three. I'm going to hit and hit.

Okay, so the first game, you busted, dealer wins. And now we're on game number two. I'll hit, and this time I will stand.

Okay, dealer busted, you win. Now we're on game three of three, and I will hit, and I will stand.

Final results: your hand 20, dealer's hand 19. You win! Thanks for playing.

We just completed this whole game, and that's the end of this tutorial. You now know the basics of Python.