# Python Crash Course
## Introduction
The following practical has been produced as a piece of work which you can work through in your own time. This practical is used to test your knowledge of fundamental Python syntax and programming practices.
This practical is not assessed and is intended to act as an extra resource for you to use to practice common programming data structures and techniques in Python. The core practical shouldn't be too challenging however, we will provide you with some extension work so you can practice your skills and try to be adventurous with your current knowledge.
This is intended for you to have free reign, so do things the way you want to but keep in mind that you should try and make your code as efficient as possible, following good practices as you go along.
**Note:** For Python programming and commenting conventions, see the following: https://www.python.org/dev/peps/pep-0008/.
To summarise:
- The following practical is purely for your own benefit.
- It is not assessed and there is no submission.
- It is deliberately quite vague in places regarding how to accomplish tasks. This is to encourage you to think of your own solutions.
- You are not expected to finish the practical in the session; feel free to continue working on it in your own time.
- A "sample" base example will be released shortly after the practical. Note that your solution does not have to look like the released sample, it is purely only intended to be used as an example.
## **The Task - Simple Blackjack**
For this practical we are going to use the classic card game Blackjack as the basis for a program. We don't, however, promote the use of this software as a means of gambling for obvious reasons.
As with most practicals, we will provide you with some support and point you in generally the right direction, but we will expect you to write the code by yourself as we are trying to test your abilities.
If at any point however you feel you don't really understand something fully, put your hand up and ask a demonstrator. This is your chance to get a face-to-face explanation about anything concerning programming.
---
### **1) Creating a Deck**
Before we start to develop the actual game, we will need to generate a deck of cards which can be used to play simple Blackjack. The code below shows you a simple example of how to create a deck of cards using a list which contains a string for every single variation of card.
```python
['A-Diamonds', '2-Diamonds', '3-Diamonds', '4-Diamonds', '5-Diamonds', '6-Diamonds', '7-Diamonds', '8-Diamonds', '9-Diamonds', '10-Diamonds', 'J-Diamonds', 'Q-Diamonds', 'K-Diamonds', 'A-Clubs', '2-Clubs', '3-Clubs', '4-Clubs', '5-Clubs', '6-Clubs', '7-Clubs', '8-Clubs', '9-Clubs', '10-Clubs', 'J-Clubs', 'Q-Clubs', 'K-Clubs', 'A-Spades', '2-Spades', '3-Spades', '4-Spades', '5-Spades', '6-Spades', '7-Spades', '8-Spades', '9-Spades', '10-Spades', 'J-Spades', 'Q-Spades', 'K-Spades', 'A-Hearts', '2-Hearts', '3-Hearts', '4-Hearts', '5-Hearts', '6-Hearts', '7-Hearts', '8-Hearts', '9-Hearts', '10-Hearts', 'J-Hearts', 'Q-Hearts', 'K-Hearts']
```
This could, however, be deemed as a **"poor quality"** deck for a few reasons, the main being+:
- Since picture cards (Jack, Queen, King, Ace) are represented with a single character (J, Q, K, A), one would have to implement and repeatedly call a function that would first check if the card is a picture card and, if so, then convert the character to an integer. Not ideal.
- All cards are stored as strings, meaning that it would be tedious to separate a card into two fields, its number and its suit.
This deck could lead to poor code like the following:
```python
# Example bad deck
deck = ['A-Diamonds', '2-Diamonds', '3-Diamonds', ...]
# Take the card at the top of the deck
card = deck[0]
# Get the characters in the card string from position 0 (first character, inclusive)
# up to position 1 (the second character, exclusive)
# split_card_num should now equal 'A'
split_card_num = card[0:1]
# Get the characters in the card string from position 2 (third character, inclusive)
# up to position 10 (the position after the last character, exclusive)
# split_card_num should now equal 'Diamonds'
split_card_suit = card[2:10]
print(split_card_num) # 'A'
print(split_card_suit) # "Diamonds"
```
---
Why is this code bad? Well, the first reason is that the position of the splices is hard coded. The code currently works with the Diamonds suit but what would happen if the card's suit was Clubs? The code would try and get characters from positions 2 through to 10 however, since the size of the string is smaller now an exception would be thrown - you can't get characters that don't exist.
Another thing that would go wrong is the first splice, to get the cards value. The code is going by the solution that the card's value is always 1 character long, which is not the case purely because of the value "10" which is clearly 2 characters long.
The above code would only work if the strings were always the same length and the desired information (the card value and suit string) were also always the same length.
This deck could be converted into a **"good quality"** deck if:
- Instead of a string, a data type that could store both a card's number and its suit (separately) was used.
- A card's number is stored in the deck directly as an integer and the card's suit is stored as a string.
One data type that could solve both the above issues would be a **tuple** that has 2 fields:
- An integer for the card's number.
- A string for the card's suit.
Another reason why tuples would be good here is because they are immutable. This prevents you from accidentally modifying the deck as you can't update tuples during runtime.
Of course, there are many ways to create a "good quality" deck: creating a card *object* could be one option (Jordan Barnes will explain object-orientated programming to you soon) or maybe rather than storing a card's suit as a string, an [ENUM](https://docs.python.org/3.7/library/enum.html) would be more appropriate.
That being said, tuples should be good enough for now. So, the ideal, "good quality", deck would look something like the following:
```python
# The reason why there are multiple '10 cards' is due to the fact that picture cards
# all hold a value of 10 in Blackjack.
# So, including the actual 10 card, each suit should have a
# total of 4 cards that hold a value of 10.
[(1, 'Diamonds'), (2, 'Diamonds'), (3, 'Diamonds'), ..., (10, 'Diamonds'), (10, 'Diamonds'), (10, 'Diamonds'), (10, 'Diamonds'), ...]
```
### Task:
- [ ] Given a bad quality deck (a list of strings), return a new deck that is of good quality, how you do this is down to you.
- [ ] Extension Task: Try and create a function that creates a good quality deck from the get go.
---
### Hints:
<details>
<summary>Click to expand...</summary>
The above bad code only requires a few modifications to work, [maybe look at the find string method](https://docs.python.org/3.7/library/stdtypes.html#str.find) and think about how it could be used in this situation.
Another option [could be the split string method](https://docs.python.org/3.7/library/stdtypes.html#str.split).
</details>
---
### **2) Shuffling a Deck**
Now that we have created a good quality deck, we need to create a function that will shuffle the deck so we don't have the same output every time. There are many ways to achieve this within Python, with the simplest being Python's ```random.shuffle``` function. Feel free to use this function but to truly test your knowledge you should try and create your own that will shuffle the deck (a list of tuples) for you.
The idea of this task is for you to practice using and manipulating common data types, so try to create your own function rather than using Python's own.
You may want your shuffle function to return a new list of elements in a different order or try something a little more complicated such as swapping the position of elements in a list ([in-place](https://en.wikipedia.org/wiki/In-place_algorithm)).
### Task:
- [ ] Implement a function that will shuffle a deck.
### Test your shuffle!
There are a few different ways in which you can test to see whether your shuffle function works correctly or not. One suggestion would be to use print statements to compare a deck before and after shuffling. You might also want to ensure that there are always 52 cards after shuffling - think about how you could compare decks to check this.
Something else to consider: *is it sufficient enough to just shuffle a deck once?*
---
### **3) Programming Simple Blackjack**
At this point you should now have a simple program which creates a deck and can shuffle said deck. From here on we want to start developing the fundamental components of the game. The following is a list of requirements for the program:
### Goal
- The objective of Blackjack is to beat the dealer by getting as close to 21 as possible, without going over (busting).
### Players
- At least two players - a dealer who should be automated and a player who should be able to provide input (their actions).
- At the start of each game, the player(s) should be dealt two cards and the dealer should be dealt one.
- Players must be able to hit (draw a new card) or stand (end your turn).
- **Note:** simple Blackjack does not have double down, split or surrender options.
- The players should be able to choose to hit until:
- The player chooses to stand.
- The player busts.
- Continue to deal cards until the player either chooses to stand or goes bust. After the player has chosen to stand or has gone bust.
- The dealer should draw cards until their total is greater than 16 points. That is, once the dealer reaches a total of 17 or above, they stop drawing cards and stand.
### Scoring
- Aces are worth 1 point, cards 2 through 9 are based on their card value, 10s and face cards (Jacks, Queens and Kings) are worth 10 points each.
- Player wins = +1 to player score counter.
- Dealer wins = +1 to dealers score counter.
- Both bust = Draw, no points awarded.
### Task: Implement the rules above and create a functioning game that a user can playing via the terminal
- [ ] Program the game logic.
- [ ] Implement player choice input via the terminal.
- [ ] Create appropriate console print statements to display the flow of the game.
---
### Hints:
<details>
<summary>Click to expand...</summary>
Think about the steps, the functions, the data types and data structures that are needed to program each requirement. It might help to run through the rules out loud and think about what each one requires.
- **Setting up player interaction**.
- How does a user input their actions? [See the ```input()``` function](https://docs.python.org/3.7/library/functions.html#input).
- **Dealing cards to players**.
- Could be in one big loop? What kind of loop? Think about the conditions needed for the loop.
- Initially dealing the player 2 cards could be something as simple as 2 lines that "pop" off either the first or the last element of the deck and hands the card to a player. [See *list.pop([i])*](https://docs.python.org/3.7/tutorial/datastructures.html)
- **Player can choose to hit or to stand. Loop until player either busts or decides to stand**.
- Maybe a while loop that accepts [user input](https://docs.python.org/3/library/functions.html#input) using the console ([effectively a TUI](https://en.wikipedia.org/wiki/Text-based_user_interface)).
- Dealer then draws cards until they either bust or total a value of 17-21.
- Maybe another while loop runs and deals while ... (or a *do while* if you're feeling a bit adventurous).
- Individual with higher total wins.
- Need to save the values of the player(s) as well as the dealer and then...
- All these steps incorporate fundamental programming concepts that you have come across in either lectures or practicals.
</details>
---
### 4) Try something yourself
- [ ] [Implement the standard Blackjack rules](https://en.wikipedia.org/wiki/Blackjack#Rules_of_play_at_casinos) (split, double down, Ace can be either 1 or 11 etc.).
- [ ] Implement multiple players. *Hint: you could use a list to store each players total.*
- [ ] Create a nice looking [TUI](https://en.wikipedia.org/wiki/Text-based_user_interface) that draws cards as ASCII art, or maybe even a GUI ([TkInter](https://wiki.python.org/moin/TkInter) could be a good option for this).
- [ ] Find out which card the player and dealer bust on the most (hint: use a [dictionary](https://docs.python.org/3.7/tutorial/datastructures.html#dictionaries)).
- [ ] Add a rewards functionality where you decide how many "gems" to bet.
- [ ] Keep a top 5 highscore (no. of wins in a row?) and store it in a file.