diff --git a/.github/workflows/disabled/python-lint.yml b/.github/workflows/disabled/python-lint.yml new file mode 100644 index 00000000..308b56a8 --- /dev/null +++ b/.github/workflows/disabled/python-lint.yml @@ -0,0 +1,29 @@ +# Uses (Lintly): https://github.com/grantmcconnaughey/Lintly +# Submits code reviews based on flake8 output +name: Python (Lintly) + +on: pull_request + +jobs: + lint-python: + name: Lint Python with flake8 + runs-on: ubuntu-latest + + steps: + - name: Check out Git repository + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: '3.x' + + # Install flake8 and Lintly + - name: Install Python dependencies + run: pip install flake8 lintly + + # Run Lintly with flake8 + - name: Lint with flake8 + run: flake8 | lintly --commit-sha=${{ github.event.pull_request.head.sha }} + env: + LINTLY_API_KEY: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 92a2e387..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Python Style - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - check-python-style: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Lint with flake8 - run: | - pip install flake8 - flake8 . diff --git a/.github/workflows/python-format.yml b/.github/workflows/python-format.yml new file mode 100644 index 00000000..0eac2193 --- /dev/null +++ b/.github/workflows/python-format.yml @@ -0,0 +1,33 @@ +# Uses (Lint Action): https://github.com/marketplace/actions/lint-action#supported-tools +# Creates annotations from linting problems +# Autofixes problems if possible (it's a black formatter) +name: Python (Lint Action) + +on: pull_request + +jobs: + format-lint-python: + name: Format Python with black and lint with flake8 + runs-on: ubuntu-latest + + steps: + - name: Check out Git repository + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: Install Python dependencies + run: pip install black flake8 + + - name: Run black and flake8 + uses: wearerequired/lint-action@v1 + with: + black: true + flake8: true + black_args: "--line-length 79 --exclude='1_beginner/chapter1/examples/error.py'" # same max line length as flake8 + flake8_args: "--max-line-length=88 --ignore=E203,W503 --exclude=1_beginner/chapter1/examples/error.py" # prevent conflicts with black + auto_fix: true # auto commit style fixes + \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1d74e219..c233127c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ .vscode/ +*.xml +*.iml +venv/ \ No newline at end of file diff --git a/1_beginner/chapter1/examples/comments.py b/1_beginner/chapter1/examples/comments.py index ae3363a0..af16ba6e 100644 --- a/1_beginner/chapter1/examples/comments.py +++ b/1_beginner/chapter1/examples/comments.py @@ -2,11 +2,11 @@ # This is a single line comment -''' +""" This is a multi-line comment -''' +""" """ This is also a multi-line diff --git a/1_beginner/chapter1/examples/error.py b/1_beginner/chapter1/examples/error.py new file mode 100644 index 00000000..269c6720 --- /dev/null +++ b/1_beginner/chapter1/examples/error.py @@ -0,0 +1,8 @@ +# Syntax Errors +prnt("Hello") # this would be a syntax error +# someone misspelled 'print' as 'prnt' + + print("Hello") # this would also result in an error since there's an extra indent + +# Runtime Error +print(1 / 0) # this would result in a ZeroDivision error diff --git a/1_beginner/chapter1/examples/printing.py b/1_beginner/chapter1/examples/printing.py index 1a754a83..40f4ab82 100644 --- a/1_beginner/chapter1/examples/printing.py +++ b/1_beginner/chapter1/examples/printing.py @@ -1,13 +1,18 @@ # Printing +# You can print other types besides strings; we'll get to that later +print("Hello") +print(1) +print(99.99) + # Strings can be in single or double quotes -print('Message') +print("Message") print("Message") # You can print with multiple arguments # Arguments are separated by a space by default -print('Message', 'with', 'arguments') +print("Message", "with", "arguments") # You can use string concatenation ("adding") # to put strings together (just be careful about spacing!) -print('Message' + 'with' + 'concatenation') # no spaces between words +print("Message" + "with" + "concatenation") # no spaces between words diff --git a/1_beginner/chapter1/practice/style.py b/1_beginner/chapter1/practice/style.py index 17d24ce2..1385d773 100644 --- a/1_beginner/chapter1/practice/style.py +++ b/1_beginner/chapter1/practice/style.py @@ -3,7 +3,7 @@ # fix the style in this file so that it runs properly # and there are comments explaining the program -''' +""" print("Hello World!") print("This is a Python program") @@ -12,4 +12,4 @@ input("Enter your age: ") print("Your age is " + age) -''' +""" diff --git a/1_beginner/chapter1/solutions/hello_world_again.py b/1_beginner/chapter1/solutions/hello_world_again.py index c387c637..18cb59e0 100644 --- a/1_beginner/chapter1/solutions/hello_world_again.py +++ b/1_beginner/chapter1/solutions/hello_world_again.py @@ -1,3 +1,3 @@ # Hello World Again # Print "Hello World" to the console -print('Hello World') +print("Hello World") diff --git a/1_beginner/chapter1/solutions/my_first_chapter.py b/1_beginner/chapter1/solutions/my_first_chapter.py index 4774bf60..86413b93 100644 --- a/1_beginner/chapter1/solutions/my_first_chapter.py +++ b/1_beginner/chapter1/solutions/my_first_chapter.py @@ -1,8 +1,8 @@ # My First Chapter # Print "My first chapter" and "Good morning!" # Then print 3 other messages of your choice to the console -print('My first chapter') -print('Good morning!') -print('This is a message') -print('This is another message') -print('One more message!') +print("My first chapter") +print("Good morning!") +print("This is a message") +print("This is another message") +print("One more message!") diff --git a/1_beginner/chapter2/examples/convert.py b/1_beginner/chapter2/examples/convert.py index 588ce4a3..f41618c6 100644 --- a/1_beginner/chapter2/examples/convert.py +++ b/1_beginner/chapter2/examples/convert.py @@ -1,7 +1,7 @@ # Converting to Different Data Types -x = '5' -y = '6' +x = "5" +y = "6" sum = int(x) + int(y) # this is 11 because x and y were converted to integers print(sum) diff --git a/1_beginner/chapter2/examples/data.py b/1_beginner/chapter2/examples/data.py index e3579a6d..c194bc27 100644 --- a/1_beginner/chapter2/examples/data.py +++ b/1_beginner/chapter2/examples/data.py @@ -3,7 +3,7 @@ # strings are a series of characters # they are in single or double quotes print("Jane") -print('Doe') +print("Doe") # integers are whole numbers (positive, negative, and 0) print(25) diff --git a/1_beginner/chapter2/examples/string_or_number.py b/1_beginner/chapter2/examples/string_or_number.py index 71a7b04f..3d5b7f74 100644 --- a/1_beginner/chapter2/examples/string_or_number.py +++ b/1_beginner/chapter2/examples/string_or_number.py @@ -1,8 +1,8 @@ # String or Number? # strings -x = '5' -y = '6' +x = "5" +y = "6" print(x + y) # this 56 (concatenation) # integers diff --git a/1_beginner/chapter2/examples/variables.py b/1_beginner/chapter2/examples/variables.py index e27e936f..80758363 100644 --- a/1_beginner/chapter2/examples/variables.py +++ b/1_beginner/chapter2/examples/variables.py @@ -2,7 +2,7 @@ # use variables to store data first_name = "Jane" -last_name = 'Doe' +last_name = "Doe" age = 25 diff --git a/1_beginner/chapter2/practice/float.py b/1_beginner/chapter2/practice/float.py index dba9d16f..83edd678 100644 --- a/1_beginner/chapter2/practice/float.py +++ b/1_beginner/chapter2/practice/float.py @@ -1,8 +1,10 @@ # Float -# Write a program that takes a number from the user +# Write a program that takes a float from the user # and stores it in a variable. Cast the number to an # integer and store in another variable. -# Then print: (floating point number) = (integer). +# Then print: (floating point number) = (integer number). +# For example, if the user enters 5, the output would be: +# 5.0 = 5 # Also print the type of the floating point variable. # Remember! type(variable_name) will return the data type of a variable diff --git a/1_beginner/chapter2/solutions/favorite.py b/1_beginner/chapter2/solutions/favorite.py index dfa82425..0915dcb0 100644 --- a/1_beginner/chapter2/solutions/favorite.py +++ b/1_beginner/chapter2/solutions/favorite.py @@ -10,7 +10,10 @@ # Display output print( - favorite_person + " bought you " - + favorite_food + " and " - + favorite_drink + "." + favorite_person + + " bought you " + + favorite_food + + " and " + + favorite_drink + + "." ) diff --git a/1_beginner/chapter2/solutions/float.py b/1_beginner/chapter2/solutions/float.py index df33aa18..68df6728 100644 --- a/1_beginner/chapter2/solutions/float.py +++ b/1_beginner/chapter2/solutions/float.py @@ -1,8 +1,10 @@ # Float -# Write a program that takes a number from the user +# Write a program that takes a float from the user # and stores it in a variable. Cast the number to an # integer and store in another variable. -# Then print: (floating point number) = (integer). +# Then print: (floating point number) = (integer number). +# For example, if the user enters 5, the output would be: +# 5.0 = 5 # Also print the type of the floating point variable. # Remember! type(variable_name) will return the data type of a variable diff --git a/1_beginner/chapter2/solutions/print_data_types.py b/1_beginner/chapter2/solutions/print_data_types.py index 96fefe04..28848bb9 100644 --- a/1_beginner/chapter2/solutions/print_data_types.py +++ b/1_beginner/chapter2/solutions/print_data_types.py @@ -14,5 +14,5 @@ # strings print("Tahiti, it's a magical place") -print('May the Force be with you') +print("May the Force be with you") print("Hey guys") diff --git a/1_beginner/chapter3/examples/aug_assign_operators.py b/1_beginner/chapter3/examples/aug_assign_operators.py index 9f76b466..7e85834b 100644 --- a/1_beginner/chapter3/examples/aug_assign_operators.py +++ b/1_beginner/chapter3/examples/aug_assign_operators.py @@ -12,6 +12,9 @@ a *= b # a = a * b +a **= b +# a = a ** b + a /= b # a = a / b diff --git a/1_beginner/chapter3/examples/math_operators.py b/1_beginner/chapter3/examples/math_operators.py index 00648a58..8c54a408 100644 --- a/1_beginner/chapter3/examples/math_operators.py +++ b/1_beginner/chapter3/examples/math_operators.py @@ -29,4 +29,4 @@ # Exponent x = 2 y = 3 -print(x ** y) # prints 8 +print(x**y) # prints 8 diff --git a/1_beginner/chapter3/practice/change.py b/1_beginner/chapter3/practice/change.py new file mode 100644 index 00000000..443b95b6 --- /dev/null +++ b/1_beginner/chapter3/practice/change.py @@ -0,0 +1,26 @@ +""" +Write code that takes, as input, the number of dollars a person has +(a floating number), and outputs how much they have +in dollars, quarters, dimes, nickels and pennies. + +For example, if someone has $4.62, the program would print the following: + +4 dollars +2 quarters +1 dime +0 nickels +2 cents + +The starting code is given. + +Note: This is a challenge problem! Do not feel bad or disheartned if you can't +solve it. We will go over it next class. +""" + +CENTS_PER_DOLLAR = 100 + +num_cents = int( + float(input("How many dollars do you have: $")) * CENTS_PER_DOLLAR +) + +# What do you do next? Write code here diff --git a/1_beginner/chapter3/practice/compare_x.py b/1_beginner/chapter3/practice/compare_x.py index e701f2c1..d98a212f 100644 --- a/1_beginner/chapter3/practice/compare_x.py +++ b/1_beginner/chapter3/practice/compare_x.py @@ -5,5 +5,4 @@ # print x (comparison operator) (another number), # it prints False. -x = 10 # write code here diff --git a/1_beginner/chapter3/practice/cookies.py b/1_beginner/chapter3/practice/cookies.py new file mode 100644 index 00000000..d2116e0b --- /dev/null +++ b/1_beginner/chapter3/practice/cookies.py @@ -0,0 +1,14 @@ +""" +Cookies +Prompt the user with the following questions: + How many cookies did you make? + How many cookies did your friend make? + How many cookies are burnt? + How many friends do you have? + +Print the total number of unburnt cookies. +Then output the number of cookies that will be left over if you +throw out all the burnt cookies and all the friends get the same amount. +""" + +# Write code here diff --git a/1_beginner/chapter3/practice/cylinder_volume.py b/1_beginner/chapter3/practice/cylinder_volume.py index 6e9a2eab..50b776ce 100644 --- a/1_beginner/chapter3/practice/cylinder_volume.py +++ b/1_beginner/chapter3/practice/cylinder_volume.py @@ -1,5 +1,7 @@ # Cylinder Volume # Design a program that asks the user for the -# height and radius of a cylinder and returns the volume +# height and radius of a cylinder and prints the volume +# The formula for the volume of a cylinder is +# volume = pi * (radius ^ 2) * height # write code here diff --git a/1_beginner/chapter3/practice/decimal.py b/1_beginner/chapter3/practice/decimal.py index bb8a4ee2..bc381d1e 100644 --- a/1_beginner/chapter3/practice/decimal.py +++ b/1_beginner/chapter3/practice/decimal.py @@ -1,7 +1,10 @@ # Decimal # Write a program that asks the user for a floating point number as input. -# It returns the decimal part (the part to the right of the decimal point). +# It prints out the decimal part (the part to the right of the decimal point). # Don't worry about floating point errors! -# The output should round to the correct answer, though. +# Note: To display a more exact output, you can use +# rounded_decimal = round(decimal_variable, 10) +# to set rounded_decimal to your decimal_variable rounded to a precision +# of 10 decimal places. # Write code here diff --git a/1_beginner/chapter3/practice/decisions_1.py b/1_beginner/chapter3/practice/decisions_1.py index 635d8a11..bc7e87c8 100644 --- a/1_beginner/chapter3/practice/decisions_1.py +++ b/1_beginner/chapter3/practice/decisions_1.py @@ -1,5 +1,5 @@ # Decisions 1 # Write in Python syntax what you would say -# if you wanted either a 5 dollars OR (2 drinks AND 1 snack). +# if you wanted either 5 dollars OR (2 drinks AND 1 snack). # write your code here diff --git a/1_beginner/chapter3/practice/integer.py b/1_beginner/chapter3/practice/integer.py new file mode 100644 index 00000000..96693b18 --- /dev/null +++ b/1_beginner/chapter3/practice/integer.py @@ -0,0 +1,9 @@ +""" +Integer + +Write a program that takes any number +(decimals included) as input, and outputs +whether or not it's an integer. +""" + +# Insert code here. diff --git a/1_beginner/chapter3/practice/lunch_tables.py b/1_beginner/chapter3/practice/lunch_tables.py new file mode 100644 index 00000000..21c8c2c2 --- /dev/null +++ b/1_beginner/chapter3/practice/lunch_tables.py @@ -0,0 +1,8 @@ +""" +Lunch Tables +Ask the user to input how many people are in the lunchroom, +and how many people can sit at each table. +Output how many people will be left without a table. +""" + +# Write code here diff --git a/1_beginner/chapter3/practice/money.py b/1_beginner/chapter3/practice/money.py index 77ad1982..b99c8134 100644 --- a/1_beginner/chapter3/practice/money.py +++ b/1_beginner/chapter3/practice/money.py @@ -1,5 +1,8 @@ # Money -# In Python syntax, what would you write to -# say you have at least $4.50? +# Let's say you have a variable money which +# stores the amount of money you currently have. +# In Python syntax, write the boolean expression +# that evaluates to True if the amount of money +# you have is at least $4.50. # write code here diff --git a/1_beginner/chapter3/practice/names.py b/1_beginner/chapter3/practice/names.py new file mode 100644 index 00000000..dcda62a9 --- /dev/null +++ b/1_beginner/chapter3/practice/names.py @@ -0,0 +1,9 @@ +""" +Names + +Write a program that asks a user +for two names, and outputs True if +the names are NOT the same. +""" + +# Insert code here. diff --git a/1_beginner/chapter3/practice/no_greater_than.py b/1_beginner/chapter3/practice/no_greater_than.py new file mode 100644 index 00000000..fc4e31ed --- /dev/null +++ b/1_beginner/chapter3/practice/no_greater_than.py @@ -0,0 +1,12 @@ +""" +Create a program that takes a POSITIVE integer +as an input and checks if it is no greater than 100. +Print True if it is, and False if it isn't + +YOU MAY NOT USE THE GREATER THAN or +LESS THAN OPERATORS (>, <, >=, or <=). +Find a way to do this problem only +using only the == operator and any math operators you want. +""" + +# write code here diff --git a/1_beginner/chapter3/practice/wizard.py b/1_beginner/chapter3/practice/wizard.py new file mode 100644 index 00000000..49c8d555 --- /dev/null +++ b/1_beginner/chapter3/practice/wizard.py @@ -0,0 +1,39 @@ +""" +Wizard + +There are 3 criteria to determine whether +you’re a wizard or not. + +Define a variable called is_wizard and +use logic operators to set it to the correct +value based on the criteria. + +Here are some example variable values and outputs. +You'll need to figure out the order of logic operators +needed to turn these inputs into these outputs! :) + +Example variable values and output: + +If you can fly, you’ve not a battled a dragon, +and you’re alive, output “Wizard: True” + +If you can fly, you’ve battled a dragon, +and you’re not alive, output “Wizard: True” + +If you can’t fly, you’ve battled a dragon, +and you’re not alive, output “Wizard: False” + +If you can’t fly, you’ve battled a dragon, +and you’re alive, output “Wizard: True" + +The initial variables are given, but you'll have +to change the values to test your code. +""" + +can_fly = True +battled_dragon = False +is_alive = True + +# Insert your code here. + +# print("Wizard:", is_wizard) diff --git a/1_beginner/chapter3/solutions/change.py b/1_beginner/chapter3/solutions/change.py new file mode 100644 index 00000000..a8cd5ee9 --- /dev/null +++ b/1_beginner/chapter3/solutions/change.py @@ -0,0 +1,48 @@ +""" +Write code that takes, as input, the number of dollars a person has +(a floating number), and outputs how much they have +in dollars, quarters, dimes, nickels and pennies. + +For example, if someone has $4.62, the program would print the following: + +4 dollars +2 quarters +1 dime +0 nickels +2 cents + +The starting code is given. + +Note: This is a challenge problem! Do not feel bad or disheartned if you can't +solve it. We will go over it next class. +""" + +CENTS_PER_DOLLAR = 100 +CENTS_PER_QUARTER = 25 +CENTS_PER_DIME = 10 +CENTS_PER_NICKEL = 5 + +# prompt user for dollars and convert it to cents +num_cents = int( + float(input("How many dollars do you have: $")) * CENTS_PER_DOLLAR +) + +# calculate change and display it +dollars = num_cents // CENTS_PER_DOLLAR +remaining = num_cents % CENTS_PER_DOLLAR +print(dollars, "dollars") + +quarters = remaining // CENTS_PER_QUARTER +remiaining = remaining % CENTS_PER_QUARTER +print(quarters, "quarters") + +dimes = remaining // CENTS_PER_DIME +remiaining = remaining % CENTS_PER_DIME +print(dimes, "dimes") + +nickels = remaining // CENTS_PER_NICKEL +remiaining = remaining % CENTS_PER_NICKEL +print(nickels, "nickels") + +cents = remaining +print(cents, "cents") diff --git a/1_beginner/chapter3/solutions/circle.py b/1_beginner/chapter3/solutions/circle.py index 3afe613f..78463e52 100644 --- a/1_beginner/chapter3/solutions/circle.py +++ b/1_beginner/chapter3/solutions/circle.py @@ -10,7 +10,7 @@ radius = float(input("Enter a radius: ")) # Calculate the area and circumference -area = PI * radius ** 2 +area = PI * radius**2 circumference = 2 * PI * radius # Print the result diff --git a/1_beginner/chapter3/solutions/cookies.py b/1_beginner/chapter3/solutions/cookies.py new file mode 100644 index 00000000..1a228dcc --- /dev/null +++ b/1_beginner/chapter3/solutions/cookies.py @@ -0,0 +1,33 @@ +""" +Cookies +Prompt the user with the following questions: + How many cookies did you make? + How many cookies did your friend make? + How many cookies are burnt? + How many friends do you have? + +Print the total number of unburnt cookies. +Then output the number of cookies that will be left over if you +throw out all the burnt cookies and all the friends get the same amount. +""" + +# Get all the inputs and store as integers in variables +your_cookies = int(input("How many cookies did you make? ")) +friend_cookies = int(input("How many cookies did your friend make? ")) +burnt_cookies = int(input("How many of those cookies are burnt? ")) +friend_count = int(input("How many friends do you have? ")) +print() # This makes a new empty line (a line break) + +# Calculate the total number of unburnt cookies +total = your_cookies + friend_cookies - burnt_cookies + +# Display the number of unburnt cookies. +# Cast the integer variable "total" to a string +print("Number of unburnt cookies: " + str(total)) + +# Calculate the REMAINDER of cookies +# after dividing the total between your friends +remainder = total % friend_count + +# Display the number of cookies left over +print("Number of cookies left over:", remainder) diff --git a/1_beginner/chapter3/solutions/cylinder_volume.py b/1_beginner/chapter3/solutions/cylinder_volume.py index 9e324bee..07494915 100644 --- a/1_beginner/chapter3/solutions/cylinder_volume.py +++ b/1_beginner/chapter3/solutions/cylinder_volume.py @@ -1,8 +1,10 @@ # Cylinder Volume # Design a program that asks the user for the -# height and radius of a cylinder and returns the volume +# height and radius of a cylinder and prints the volume +# The formula for the volume of a cylinder is +# volume = pi * (radius ^ 2) * height PI = 3.14 -height = float(input('Height of cylinder: ')) -radius = float(input('Radius of cylinder: ')) -volume = 3.14 * radius ** 2 * height +height = float(input("Height of cylinder: ")) +radius = float(input("Radius of cylinder: ")) +volume = PI * radius**2 * height print("The volume of the cylinder is", volume) diff --git a/1_beginner/chapter3/solutions/decimal.py b/1_beginner/chapter3/solutions/decimal.py index 21ca8fde..f1917397 100644 --- a/1_beginner/chapter3/solutions/decimal.py +++ b/1_beginner/chapter3/solutions/decimal.py @@ -1,8 +1,11 @@ # Decimal # Write a program that asks the user for a floating point number as input. -# It returns the decimal part (the part to the right of the decimal point). +# It prints out the decimal part (the part to the right of the decimal point). # Don't worry about floating point errors! -# The output should round to the correct answer, though. +# Note: To display a more exact output, you can use +# rounded_decimal = round(decimal_variable, 10) +# to set rounded_decimal to your decimal_variable rounded to a precision +# of 10 decimal places. # Get the floating point number input num = float(input("Enter a floating point number: ")) diff --git a/1_beginner/chapter3/solutions/even.py b/1_beginner/chapter3/solutions/even.py index c05aba99..9f76d08a 100644 --- a/1_beginner/chapter3/solutions/even.py +++ b/1_beginner/chapter3/solutions/even.py @@ -7,5 +7,5 @@ i = int(input("Enter an integer: ")) # Display output -is_even = (i % 2 == 0) +is_even = i % 2 == 0 print("Is this number even? " + str(is_even)) diff --git a/1_beginner/chapter3/solutions/integer.py b/1_beginner/chapter3/solutions/integer.py new file mode 100644 index 00000000..b2f23268 --- /dev/null +++ b/1_beginner/chapter3/solutions/integer.py @@ -0,0 +1,15 @@ +""" +Integer + +Write a program that takes any number +(decimals included) as input, and outputs +whether or not it's an integer. +""" + +# Get input as a floating point +x = float(input("Enter a number: ")) + +# Compare x to the integer version of itself +is_integer = x == int(x) + +print("Is integer? " + str(is_integer)) diff --git a/1_beginner/chapter3/solutions/lunch_tables.py b/1_beginner/chapter3/solutions/lunch_tables.py new file mode 100644 index 00000000..c8c44b37 --- /dev/null +++ b/1_beginner/chapter3/solutions/lunch_tables.py @@ -0,0 +1,16 @@ +""" +Lunch Tables +Ask the user to input how many people are in the lunchroom, +and how many people can sit at each table. +Output the number of people that will be left without a table. +""" + +# Get user input +people = input("How many people are in the lunchroom? ") +table_limit = input("How many people can sit at a table? ") + +# Calculate the number of outcasts (the remainder) +outcasts = int(people) % int(table_limit) + +# Display output +print("Outcasts:", outcasts) diff --git a/1_beginner/chapter3/solutions/money.py b/1_beginner/chapter3/solutions/money.py index 49eb23f6..c03c578d 100644 --- a/1_beginner/chapter3/solutions/money.py +++ b/1_beginner/chapter3/solutions/money.py @@ -1,5 +1,8 @@ # Money -# In Python syntax, what would you write to -# say you have at least $4.50? +# Let's say you have a variable money which +# stores the amount of money you currently have. +# In Python syntax, write the boolean expression +# that evaluates to True if the amount of money +# you have is at least $4.50. -# dollars >= 4.50 +# money >= 4.50 diff --git a/1_beginner/chapter3/solutions/names.py b/1_beginner/chapter3/solutions/names.py new file mode 100644 index 00000000..c48cedb3 --- /dev/null +++ b/1_beginner/chapter3/solutions/names.py @@ -0,0 +1,15 @@ +""" +Names + +Write a program that asks a user +for two names, and outputs True if +the names are NOT the same. +""" + +name1 = input("Person 1: ") +name2 = input("Person 2: ") + +# "False" if name1 and name2 are equal +not_same = name1 != name2 + +print("Not the same?", not_same) diff --git a/1_beginner/chapter3/solutions/no_greater_than.py b/1_beginner/chapter3/solutions/no_greater_than.py new file mode 100644 index 00000000..09b5ce63 --- /dev/null +++ b/1_beginner/chapter3/solutions/no_greater_than.py @@ -0,0 +1,12 @@ +""" +Create a program that takes a POSITIVE integer +as an input and checks if it is no greater than 100. +Print True if it is, and False if it isn't + +YOU MAY NOT USE THE GREATER THAN or +LESS THAN OPERATORS (>, <, >=, or <=). +Find a way to do this problem only +using only the == operator and any math operators you want. +""" +x = int(input("Enter you number here. It must be positive: ")) +print(x // 100 == 0) diff --git a/1_beginner/chapter3/solutions/wizard.py b/1_beginner/chapter3/solutions/wizard.py new file mode 100644 index 00000000..10154aba --- /dev/null +++ b/1_beginner/chapter3/solutions/wizard.py @@ -0,0 +1,39 @@ +""" +Wizard + +There are 3 criteria to determine whether +you’re a wizard or not. + +Define a variable called is_wizard and +use logic operators to set it to the correct +value based on the criteria. + +Here are some example variable values and outputs. +You'll need to figure out the order of logic operators +needed to turn these inputs into these outputs! :) + +Example variable values and output: + +If you can fly, you’ve not a battled a dragon, +and you’re alive, output “Wizard: True” + +If you can fly, you’ve battled a dragon, +and you’re not alive, output “Wizard: True” + +If you can’t fly, you’ve battled a dragon, +and you’re not alive, output “Wizard: False” + +If you can’t fly, you’ve battled a dragon, +and you’re alive, output “Wizard: True" + +The initial variables are given, but you'll have +to change the values to test your code. +""" + +can_fly = True +battled_dragon = False +is_alive = True + +is_wizard = can_fly or (battled_dragon and is_alive) + +print("Wizard:", is_wizard) diff --git a/1_beginner/chapter4/practice/four_numbers.py b/1_beginner/chapter4/practice/four_numbers.py new file mode 100644 index 00000000..f9c724a8 --- /dev/null +++ b/1_beginner/chapter4/practice/four_numbers.py @@ -0,0 +1,15 @@ +""" +Ask the user for 4 numbers. +Use only 3 if else blocks to find +the largest number. You may not use elifs. +For example, this counts as an if/else block: + +if(2 > 3): + print("yay") +else: + print("nay") + +This question is really tricky, and requires some ingenuity. +""" + +# Write code here diff --git a/1_beginner/chapter4/practice/grade.py b/1_beginner/chapter4/practice/grade.py index be6eb034..992997ed 100644 --- a/1_beginner/chapter4/practice/grade.py +++ b/1_beginner/chapter4/practice/grade.py @@ -1,6 +1,23 @@ -# Grade -# Write a program that asks the user to enter -# the score for a student's test. -# Print the letter grade that the test score receives. +""" +Grade +Write a program that asks the user to enter +the score for a student's test. +The letter grades are as follows: + +A: >= 90 +B: >= 80 +c: >= 70 +D: >= 60 +F: < 60 + +Print the letter grade that the test score receives. +""" + +# write code here + +""" +See if you can write the same program, +but without using >= anywhere. +""" # write code here diff --git a/1_beginner/chapter4/practice/hours.py b/1_beginner/chapter4/practice/hours.py index 16b89a1d..53d87890 100644 --- a/1_beginner/chapter4/practice/hours.py +++ b/1_beginner/chapter4/practice/hours.py @@ -1,8 +1,15 @@ # Hours # Write a program that asks the user # how many hours they spend on the internet -# per day, and return if they’re addicted +# per day, and print if they’re addicted # or not based on the hours. (5 or more hours # is addicted, less is not). # write code here + +# See if you can write the same program, +# except that the user is addicted to the internet +# if the number of hours they spend on the internet +# is greater than 2 times the remainder of hours / 7 + +# write code here diff --git a/1_beginner/chapter4/practice/menu.py b/1_beginner/chapter4/practice/menu.py new file mode 100644 index 00000000..b528e351 --- /dev/null +++ b/1_beginner/chapter4/practice/menu.py @@ -0,0 +1,19 @@ +""" +A restaurant menu has food and drink sections, +each from which the customer must choose an order. +By default, any combination of food and drink orders +are $1,000,000,000. + +But if the customer enters 'french toast' +AND 'coffee', there is a discount of $1. + +Otherwise, if the customer enters 'chicken soup' OR 'apple juice', +the price increases by $1. + +Write a program that takes an order from a user +and prints out the appropriate price. +Assume that all inputs are in lowercase +and that it is always food first, and then drink. +""" + +# Your code goes here diff --git a/1_beginner/chapter4/practice/money_check.py b/1_beginner/chapter4/practice/money_check.py new file mode 100644 index 00000000..4a17169b --- /dev/null +++ b/1_beginner/chapter4/practice/money_check.py @@ -0,0 +1,9 @@ +# Money Check +# Write a program that asks for a person's +# amount of money (floating point). +# If the person's amount of money is 0, +# print "Bankrupt". If not, print "Not Bankrupt" +# If the person's amount of money is +# greater than 1000.0, then print "Rich". + +# Write your code here diff --git a/1_beginner/chapter4/practice/month_and_day.py b/1_beginner/chapter4/practice/month_and_day.py new file mode 100644 index 00000000..b8683db7 --- /dev/null +++ b/1_beginner/chapter4/practice/month_and_day.py @@ -0,0 +1,9 @@ +""" +Write a program that takes user input of the month and date, +and prints out "Boo!" if it is Halloween, and "April fools!" +if it is April fools' day. If it is any other month and date, +print "It's not Halloween or April Fools..." +Assume all inputs are integers (for example, month 1 is January). +""" + +# Your code goes here diff --git a/1_beginner/chapter4/practice/simplify.py b/1_beginner/chapter4/practice/simplify.py new file mode 100644 index 00000000..fac39e8f --- /dev/null +++ b/1_beginner/chapter4/practice/simplify.py @@ -0,0 +1,20 @@ +""" +Simplify +Here is a block of code where one tries to determine +if someone is a Nobel Prize winner. + +Rewrite the code to work in one statement. + +(Remove the multi-line comments when +you start working.) +""" + +""" +if does_significant_work: + if makes_breakthrough: + is_nobel_prize_candidate = True + else: + is_nobel_prize_candidate = False +elif not does_significant_work: + is_nobel_prize_candidate = False +""" diff --git a/1_beginner/chapter4/practice/square.py b/1_beginner/chapter4/practice/square.py new file mode 100644 index 00000000..6a86f2a0 --- /dev/null +++ b/1_beginner/chapter4/practice/square.py @@ -0,0 +1,7 @@ +""" +Write a program that takes an integer input, +and prints the square of that number if it is even, +and prints the number itself otherwise. +""" + +# Your code goes here diff --git a/1_beginner/chapter4/practice/temperature.py b/1_beginner/chapter4/practice/temperature.py new file mode 100644 index 00000000..576dc9d4 --- /dev/null +++ b/1_beginner/chapter4/practice/temperature.py @@ -0,0 +1,20 @@ +""" +Temperature +Hoppity the Rabbit wrote some code, +but it doesn't run correctly and he needs your help! +The following program is supposed to print a string +based on a numerical input of the temperature. +See if you can fix it for him! +""" + +# I added some comments to aid you. Good luck! - Hoppity + +# assume possible input range is 0-100 +temp = int(input("Enter temperature: ")) + +if temp < 100: # 60-100 is hot + print("hot") +if temp <= 60: # 30-59 is normal + print("normal") +if temp < 30: # 0-29 is cold + print("cold") diff --git a/1_beginner/chapter4/practice/walk.py b/1_beginner/chapter4/practice/walk.py new file mode 100644 index 00000000..c568ee63 --- /dev/null +++ b/1_beginner/chapter4/practice/walk.py @@ -0,0 +1,24 @@ +""" +Timmy wants to walk his pet dog twice a day. +He decides that the second walk has to be +6 hours after the first walk ends. +Each walk takes exactly 1 hour. + +Timmy is forgetful, so he decides to write +a program that tells him whether or not it +is time to walk his dog a second time. + +Write a program that takes the input of +the hour and the minute when the first walk +started and the current time, and prints "Late" +if it is past the scheduled time for the second walk, +"Now" if it is exactly the right time, and +"Early" if it is before the second walk time. + +Assume all inputs and outputs must be in 24-hour +style (e.g. 4 PM is 16:00), and that Timmy will schedule +his walks so that the second walk will not start later +than midnight (24:00). +""" + +# Your code goes here diff --git a/1_beginner/chapter4/solutions/difference.py b/1_beginner/chapter4/solutions/difference.py index 3f5de68e..ac84d4e3 100644 --- a/1_beginner/chapter4/solutions/difference.py +++ b/1_beginner/chapter4/solutions/difference.py @@ -4,9 +4,9 @@ # greater than 17 print "Negative". Otherwise, # print the result of 17 minus the given number. -x = float(input('Enter a number: ')) +x = float(input("Enter a number: ")) if x > 17: - print('Negative') + print("Negative") else: print(17 - x) diff --git a/1_beginner/chapter4/solutions/four_numbers.py b/1_beginner/chapter4/solutions/four_numbers.py new file mode 100644 index 00000000..45b0445d --- /dev/null +++ b/1_beginner/chapter4/solutions/four_numbers.py @@ -0,0 +1,39 @@ +""" +Ask the user for 4 numbers. +Use only 3 if else blocks to find +the largest number. You may not use elifs. +For example, this counts as an if/else block: + +if 2 > 3: + print("yay") +else: + print("nay") + +This question is really tricky, and requires some ingenuity. +""" + +a = float(input("Enter number 1: ")) +b = float(input("Enter number 2: ")) +c = float(input("Enter number 3: ")) +d = float(input("Enter number 4: ")) + +max_1 = -1 +max_2 = -1 +max_final = -1 + +if a > b: + max_1 = a +else: + max_1 = b + +if c > d: + max_2 = c +else: + max_2 = d + +if max_1 > max_2: + max_final = max_1 +else: + max_final = max_2 + +print("The max is", max_final) diff --git a/1_beginner/chapter4/solutions/grade.py b/1_beginner/chapter4/solutions/grade.py index 7b10f8b2..6ae3c9dc 100644 --- a/1_beginner/chapter4/solutions/grade.py +++ b/1_beginner/chapter4/solutions/grade.py @@ -1,7 +1,17 @@ -# Grade -# Write a program that asks the user to enter -# the score for a student's test. -# Print the letter grade that the test score receives. +""" +Grade +Write a program that asks the user to enter +the score for a student's test. +The letter grades are as follows: + +A: >= 90 +B: >= 80 +c: >= 70 +D: >= 60 +F: < 60 + +Print the letter grade that the test score receives. +""" score = float(input("Enter test score: ")) if score >= 90: @@ -14,3 +24,20 @@ print("D") else: print("F") + +""" +See if you can write the same program, +but without using >= anywhere. +""" + +score = float(input("Enter test score: ")) +if score < 60: + print("F") +elif score < 70: + print("D") +elif score < 80: + print("C") +elif score < 90: + print("B") +else: + print("A") diff --git a/1_beginner/chapter4/solutions/hours.py b/1_beginner/chapter4/solutions/hours.py index cfc1d478..87b964e1 100644 --- a/1_beginner/chapter4/solutions/hours.py +++ b/1_beginner/chapter4/solutions/hours.py @@ -1,7 +1,7 @@ # Hours # Write a program that asks the user # how many hours they spend on the internet -# per day, and return if they’re addicted +# per day, and print if they’re addicted # or not based on the hours. (5 or more hours # is addicted, less is not). @@ -13,3 +13,15 @@ print("You are addicted to the internet.") else: print("You aren't addicted to the internet.") + + +# See if you can write the same program, +# except that the user is addicted to the internet +# if the number of hours they spend on the internet +# is greater than 2 times the remainder of hours / 7 + +hours = int(input("How many hours/day do you spend on the internet? ")) +if hours > 2 * (hours % 7): + print("You are addicted to the internet.") +else: + print("You aren't addicted to the internet.") diff --git a/1_beginner/chapter4/solutions/menu.py b/1_beginner/chapter4/solutions/menu.py new file mode 100644 index 00000000..9412b483 --- /dev/null +++ b/1_beginner/chapter4/solutions/menu.py @@ -0,0 +1,34 @@ +""" +A restaurant menu has food and drink sections, +each from which the customer must choose an order. +By default, any combination of food and drink orders +are $1,000,000,000. + +But if the customer enters 'french toast' +AND 'coffee', there is a discount of $1. + +Otherwise, if the customer enters 'chicken soup' OR 'apple juice', +the price increases by $1. + +Write a program that takes an order from a user +and prints out the appropriate price. +Assume that all inputs are in lowercase +and that it is always food first, and then drink. +""" + +# all orders are $1 million by default +total_cost = 1_000_000_000 # underscores to increase readability + +# take the user's order +food = input("What food would you like? ") +drink = input("What drink would you like? ") + +# discount of $1 if the user orders french toast and coffee +if food == "french toast" and drink == "coffee": + total_cost -= 1 +# charge extra $1 if user orders chicken soup or apple juice +elif food == "chicken soup" or drink == "apple juice": + total_cost += 1 + +# display total +print("Total cost: $" + str(total_cost)) diff --git a/1_beginner/chapter4/solutions/money_check.py b/1_beginner/chapter4/solutions/money_check.py new file mode 100644 index 00000000..548bd2a7 --- /dev/null +++ b/1_beginner/chapter4/solutions/money_check.py @@ -0,0 +1,17 @@ +# Money Check +# Write a program that asks for a person's +# amount of money (floating point). +# If the person's amount of money is 0, +# print "Bankrupt". If not, print "Not Bankrupt" +# If the person's amount of money is +# greater than 1000.0, then print "Rich". + +money = float(input("Enter the amount of money you have: $")) + +if money == 0: + print("Bankrupt") +else: + print("Not Bankrupt") + +if money > 1000: + print("Rich") diff --git a/1_beginner/chapter4/solutions/month_and_day.py b/1_beginner/chapter4/solutions/month_and_day.py new file mode 100644 index 00000000..8d7ee2be --- /dev/null +++ b/1_beginner/chapter4/solutions/month_and_day.py @@ -0,0 +1,19 @@ +""" +Write a program that takes user input of the month and date, +and prints out "Boo!" if it is Halloween, and "April fools!" +if it is April fools' day. If it is any other month and date, +print "It's not Halloween or April Fools..." +Assume all inputs are integers (for example, month 1 is January). +""" + +# ask user for month and date +month = int(input("Enter a month: ")) +day = int(input("Enter a date: ")) + +# print a message based on the month and date +if month == 10 and day == 31: + print("Boo!") +elif month == 4 and day == 1: + print("April fools!") +else: + print("It's not Halloween or April Fools...") diff --git a/1_beginner/chapter4/solutions/simplify.py b/1_beginner/chapter4/solutions/simplify.py new file mode 100644 index 00000000..4b453bd0 --- /dev/null +++ b/1_beginner/chapter4/solutions/simplify.py @@ -0,0 +1,12 @@ +""" +Simplify +Here is a block of code where one tries to determine +if someone is a Nobel Prize winner. + +Rewrite the code to work in one statement. + +(Remove the multi-line comments when +you start working.) +""" + +# is_nobel_prize_candidate = does_significant_work and makes_breakthrough diff --git a/1_beginner/chapter4/solutions/square.py b/1_beginner/chapter4/solutions/square.py new file mode 100644 index 00000000..678cae8b --- /dev/null +++ b/1_beginner/chapter4/solutions/square.py @@ -0,0 +1,15 @@ +""" +Write a program that takes an integer input, +and prints the square of that number if it is even, +and prints the number itself otherwise. +""" + +# prompt user to enter an int +number = int(input("Enter an integer: ")) + +if number % 2 == 0: + # if number is even, print its square + print(number**2) +else: + # otherwise, print the number itself + print(number) diff --git a/1_beginner/chapter4/solutions/temperature.py b/1_beginner/chapter4/solutions/temperature.py new file mode 100644 index 00000000..28c5cb0c --- /dev/null +++ b/1_beginner/chapter4/solutions/temperature.py @@ -0,0 +1,26 @@ +""" +Temperature +Hoppity the Rabbit wrote some code, +but it doesn't run correctly and he needs your help! +The following program is supposed to print a string +based on a numerical input of the temperature. +See if you can fix it for him! +""" + +# assume possible input range is 0-100 +temp = int(input("Enter temperature: ")) + +if 60 <= temp <= 100: # 60-100 is hot + print("hot") +if 30 <= temp < 60: # 30-59 is normal + print("normal") +if 0 <= temp < 30: # 0-29 is cold + print("cold") + +# alternatively, you can also use elif +# if temp >= 60: +# print('hot') +# elif temp >= 30: +# print('normal') +# else: +# print('cold') diff --git a/1_beginner/chapter4/solutions/walk.py b/1_beginner/chapter4/solutions/walk.py new file mode 100644 index 00000000..f6dc7d72 --- /dev/null +++ b/1_beginner/chapter4/solutions/walk.py @@ -0,0 +1,51 @@ +""" +Timmy wants to walk his pet dog twice a day. +He decides that the second walk has to be +6 hours after the first walk ends. +Each walk takes exactly 1 hour. + +Timmy is forgetful, so he decides to write +a program that tells him whether or not it +is time to walk his dog a second time. + +Write a program that takes the input of +the hour and the minute when the first walk +started and the current time, and prints "Late" +if it is past the scheduled time for the second walk, +"Now" if it is exactly the right time, and +"Early" if it is before the second walk time. + +Assume all inputs and outputs must be in 24-hour +style (e.g. 4 PM is 16:00), and that Timmy will schedule +his walks so that the second walk will not start later +than midnight (24:00). +""" + +# prompt user for first walk start time +first_hour = int(input("Enter the hour of the time of the first walk: ")) +first_minute = int(input("Enter the minute of the time of the first walk: ")) + +# calculate second walk start time +# add first_hour + 1 is when the first walk ends, +# 6 hours later the second walk should start +second_hour = first_hour + 1 + 6 +second_minute = first_minute + +# prompt user for current time +current_hour = int(input("Enter the current hour: ")) +current_minute = int(input("Enter the current minute: ")) + +# print whether Timmy is late or early +# to the second walk +if current_hour > second_hour: + print("Late") +elif current_hour < second_hour: + print("Early") +else: + # compare minutes if hours are equal + if current_minute > second_minute: + print("Late") + elif current_minute < second_minute: + print("Early") + else: # current_minute == second_minute + print("Now") diff --git a/1_beginner/chapter5/practice/TV.py b/1_beginner/chapter5/practice/TV.py index 9a2e1622..842bc783 100644 --- a/1_beginner/chapter5/practice/TV.py +++ b/1_beginner/chapter5/practice/TV.py @@ -1,9 +1,9 @@ # TV -# Pretend you just got a 37 on your test, -# and you mom says you can’t watch TV until you get above an 84. -# (HINT: comparison operators). You increase -# your test score 6 points per day (iterations). +# Pretend you just got a 50 on your test, +# and you mom says you can’t watch TV until you get +# a score of at least 80. (HINT: comparison operators). +# You increase your test score by 10 points per day. # Write a program that tells you after -# how many days you'll be able to watch TV +# how many days you'll be able to watch TV. Use a loop. # write code here diff --git a/1_beginner/chapter5/practice/add_all_the_way.py b/1_beginner/chapter5/practice/add_all_the_way.py index fbd2f8a5..b91d4817 100644 --- a/1_beginner/chapter5/practice/add_all_the_way.py +++ b/1_beginner/chapter5/practice/add_all_the_way.py @@ -1,8 +1,10 @@ # Add All the Way -# Write a program that asks for and -# reads an input from the user -# Then add all the numbers up from 0 -# to that number up. You can use a -# for or while loop. Print out the sum. +# Take a number from the user and +# add every number up from 1 to that number. +# Print the result. +# You can use a for or while loop. # write code here + + +# Try using the other loop and do the same p[roblem again diff --git a/1_beginner/chapter5/practice/alternating.py b/1_beginner/chapter5/practice/alternating.py new file mode 100644 index 00000000..0fe44abc --- /dev/null +++ b/1_beginner/chapter5/practice/alternating.py @@ -0,0 +1,12 @@ +""" +Alternating +Ask the user for a positive integer. Then print the numbers from 1 +to that number, but alternating in sign. For example, if the input +was 5, what would be printed is 1, -1, 2, -2, 3, -3, 4, -4, 5. +(Note, DO NOT include the last negative number). +Do this with a for loop and then with a while loop. +""" + +# Write code here + +# Now try it with a while loop diff --git a/1_beginner/chapter5/practice/even.py b/1_beginner/chapter5/practice/even.py index 97c95033..b5b1b482 100644 --- a/1_beginner/chapter5/practice/even.py +++ b/1_beginner/chapter5/practice/even.py @@ -1,5 +1,5 @@ # Even # Print every even number greater than 10 -# and less than 101 +# and less than 101 (10 ia included) # write code here diff --git a/1_beginner/chapter5/practice/fibonnaci.py b/1_beginner/chapter5/practice/fibonnaci.py new file mode 100644 index 00000000..1aa27bde --- /dev/null +++ b/1_beginner/chapter5/practice/fibonnaci.py @@ -0,0 +1,20 @@ +""" CHALLENGE PROBLEM!! NOT FOR THE FAINT OF HEART! + +The Fibonacci numbers, discovered by Leonardo di Fibonacci, +is a sequence of numbers that often shows up in mathematics and, +interestingly, nature. The sequence goes as such: + +1,1,2,3,5,8,13,21,34,55,... + +where the sequence starts with 1 and 1, and then each number is the sum of the +previous 2. For example, 8 comes after 5 because 5+3 = 8, and 55 comes after 34 +because 34+21 = 55. + +The challenge is to use a for loop (not recursion, if you know what that is), +to find the 100th Fibonnaci number. +""" + +# write code here + + +# Can you do it with a while loop? diff --git a/1_beginner/chapter5/practice/fizzbuzz.py b/1_beginner/chapter5/practice/fizzbuzz.py new file mode 100644 index 00000000..894f212b --- /dev/null +++ b/1_beginner/chapter5/practice/fizzbuzz.py @@ -0,0 +1,22 @@ +# Fizz Buzz +""" +Credit to: https://www.youtube.com/watch?v=QPZ0pIK_wsc + +Fizz Buzz is a game played between 2 people +where they take turns counting up starting at 1. +However, if the number is divisible by 3, the person +should say "fizz" instead of the number. +If the number is divisible by 5, the person should +say "buzz" instead of the number. +If the number is divisible by both 3 and 5, +the person should say "fizzbuzz" instead of the number. +For example, this is how the first 5 results would look like: +1 +2 +fizz +4 +buzz +Write a program that outputs the first 100 results. +""" + +# Write your code here diff --git a/1_beginner/chapter5/practice/multiply.py b/1_beginner/chapter5/practice/multiply.py new file mode 100644 index 00000000..95f0b4aa --- /dev/null +++ b/1_beginner/chapter5/practice/multiply.py @@ -0,0 +1,12 @@ +""" +Multiply + +Write a program that asks the user +for 10 integers, multiplies them all +together, and displays the product at +the end. + +Use a for loop! +""" + +# Insert your code here. diff --git a/1_beginner/chapter5/practice/prime.py b/1_beginner/chapter5/practice/prime.py new file mode 100644 index 00000000..e631641e --- /dev/null +++ b/1_beginner/chapter5/practice/prime.py @@ -0,0 +1,28 @@ +""" + +Write a program to check if a number is prime or not. A prime +number is one that is not divisible by any number other than +1 and itself. For example, 11 is prime because it is not divisible +by 2,3,4,5,...10 (i.e. 11/10, for example, is not an integer). + +Write a for loop to check if a number is prime or not. + +""" + +# write code here + +numer = int(input("Enter number here: ")) + +""" +Given a number less than or equal to 10 billion, see if you can check if it is +prime in UNDER 2 SECONDS. The code you wrote above probably wont do that, so +you will have to figure out a clever solution. + +NOTE: THE ABOVE IS A CHALLENGE PROBLEM, AND IS EXTRREEMMLY HARD TO DO. +I COULDN'T DO IT OPTIMALY UNTIL I LEARNED HOW TO. If you can't +figure out the solution, don't feel discouraged, its seriously a really +hard problem (I have seen this asked to college students :O) + +""" + +# write code here diff --git a/1_beginner/chapter5/practice/series.py b/1_beginner/chapter5/practice/series.py new file mode 100644 index 00000000..c8ea06e8 --- /dev/null +++ b/1_beginner/chapter5/practice/series.py @@ -0,0 +1,16 @@ +""" + +Here are some interesting results from mathematics that we can +model with computer science. + +In class we added up 1 + 1/2 + 1/4 + 1/8 ... +See if you can add up 1 + 1/3 + 1/9 + 1/27 + 1/81 ... in a similar fashion. +What is the answer? What if, instead of 3, we use 5 (1 + 1/5 + 1/25 + 1/125...) +7? Do you see a patter? (Note: make sure not to end up with an infinite loop). + + + +Also, try adding up 1 -1/3 + 1/5 - 1/7 + 1/9 - 1/11 ... 10 million times. then +multiply this result by 4. What number is this close to? + +""" diff --git a/1_beginner/chapter5/practice/up_2_fifty.py b/1_beginner/chapter5/practice/up_2_fifty.py index 3d800176..d2b99f89 100644 --- a/1_beginner/chapter5/practice/up_2_fifty.py +++ b/1_beginner/chapter5/practice/up_2_fifty.py @@ -2,7 +2,7 @@ # Write a program that takes a number from the user # and adds 2 to it until it reaches 50 or more, # then prints out how many times 2 was added. -# If the number is already greater than 50, +# If the number is already 50 or greater, # then print out ('Already there!') # write code here diff --git a/1_beginner/chapter5/practice/virtual_pet.py b/1_beginner/chapter5/practice/virtual_pet.py new file mode 100644 index 00000000..cd843cf0 --- /dev/null +++ b/1_beginner/chapter5/practice/virtual_pet.py @@ -0,0 +1,23 @@ +""" +Virtual Pet +Code a virtual pet! +First, create three variables: name, hunger, and happiness. +Initialize hunger to 6 and happiness to 0, and set name +to whatever you like. (e.g. 'Otto') + +Your program should continuously take input from the user: + If the user enters 'feed', decrease hunger by 2. Increase by 1 otherwise. + If the user enters 'pet', increase happiness by 2. Decrease by 1 otherwise. + If the user enters 'quit', end the program. + +Your program should also print the status of your pet each time +it prompts the user to enter a command: + Print "[pet name] is hungry" when hunger is above 5, where [pet name] + is the name variable. + Print "[pet name] wants more attention" when happiness is below 5. + Print "[pet name] feels happy" when hunger is equal to or less than 5, + and happiness is equal to or greater than 5. + +Feel free to customize your virtual pet by changing the how much the hunger +and happiness variables increase and decrease, or add more actions! +""" diff --git a/1_beginner/chapter5/solutions/TV.py b/1_beginner/chapter5/solutions/TV.py index 510df61d..76f87a2e 100644 --- a/1_beginner/chapter5/solutions/TV.py +++ b/1_beginner/chapter5/solutions/TV.py @@ -1,14 +1,14 @@ # TV -# Pretend you just got a 37 on your test, -# and you mom says you can’t watch TV until you get above an 84. -# (HINT: comparison operators). You increase -# your test score 6 points per day (iterations). +# Pretend you just got a 50 on your test, +# and you mom says you can’t watch TV until you get +# a score of at least 80. (HINT: comparison operators). +# You increase your test score by 10 points per day. # Write a program that tells you after -# how many days you'll be able to watch TV +# how many days you'll be able to watch TV. Use a loop. -x = 37 -d = 0 -while x < 84: - x += 6 - d += 1 -print(d) +x = 50 +days = 0 +while x < 80: + x += 10 + days += 1 +print("You can watch TV after", days, "days") diff --git a/1_beginner/chapter5/solutions/add_all_the_way.py b/1_beginner/chapter5/solutions/add_all_the_way.py index be0db267..84dc010c 100644 --- a/1_beginner/chapter5/solutions/add_all_the_way.py +++ b/1_beginner/chapter5/solutions/add_all_the_way.py @@ -1,9 +1,8 @@ # Add All the Way -# Write a program that asks for and -# reads an input from the user -# Then add all the numbers up from 0 -# to that number up. You can use a -# for or while loop. Print out the sum. +# Take a number from the user and +# add every number up from 1 to that number. +# Print the result. +# You can use a for or while loop. # for loop solution sum = 0 diff --git a/1_beginner/chapter5/solutions/alternating.py b/1_beginner/chapter5/solutions/alternating.py new file mode 100644 index 00000000..de8f197d --- /dev/null +++ b/1_beginner/chapter5/solutions/alternating.py @@ -0,0 +1,27 @@ +""" +Alternating +Ask the user for a positive integer. Then print the numbers from 1 +to that number, but alternating in sign. For example, if the input +was 5, what would be printed is 1, -1, 2, -2, 3, -3, 4, -4, 5. +(Note, DO NOT include the last negative number). +Do this with a for loop and then with a while loop. +""" + +# for loop solution +number = int(input("Enter number here: ")) +for num in range(1, number + 1): + if num == number: + print(num) + else: + print(num) + print(-num) + + +# while loop solution +number = int(input("Enter number here: ")) +current_num = 1 +while current_num < number: + print(current_num) + print(-current_num) + current_num += 1 +print(current_num) diff --git a/1_beginner/chapter5/solutions/echo_enhanced.py b/1_beginner/chapter5/solutions/echo_enhanced.py index 7ae98a4a..9e68fbe6 100644 --- a/1_beginner/chapter5/solutions/echo_enhanced.py +++ b/1_beginner/chapter5/solutions/echo_enhanced.py @@ -10,9 +10,9 @@ print("Enter a message, 'c' to cancel an echo, or 'q' to quit.") while True: message = input("Message: ") - if message == 'q': + if message == "q": break # quit - elif message == 'c': + elif message == "c": continue # cancel echo else: print(message) # echo message diff --git a/1_beginner/chapter5/solutions/fizzbuzz.py b/1_beginner/chapter5/solutions/fizzbuzz.py new file mode 100644 index 00000000..b3c9dda9 --- /dev/null +++ b/1_beginner/chapter5/solutions/fizzbuzz.py @@ -0,0 +1,30 @@ +# Fizz Buzz +""" +Credit to: https://www.youtube.com/watch?v=QPZ0pIK_wsc + +Fizz Buzz is a game played between 2 people +where they take turns counting up starting at 1. +However, if the number is divisible by 3, the person +should say "fizz" instead of the number. +If the number is divisible by 5, the person should +say "buzz" instead of the number. +If the number is divisible by both 3 and 5, +the person should say "fizzbuzz" instead of the number. +For example, this is how the first 5 results would look like: +1 +2 +fizz +4 +buzz +Write a program that outputs the first 100 results. +""" + +for i in range(1, 101): + if i % 3 == 0 and i % 5 == 0: + print("fizzbuzz") + elif i % 3 == 0: + print("fizz") + elif i % 5 == 0: + print("buzz") + else: + print(i) diff --git a/1_beginner/chapter5/solutions/multiply.py b/1_beginner/chapter5/solutions/multiply.py new file mode 100644 index 00000000..618dcd3c --- /dev/null +++ b/1_beginner/chapter5/solutions/multiply.py @@ -0,0 +1,19 @@ +""" +Multiply + +Write a program that asks the user +for 10 integers, multiplies them all +together, and displays the product at +the end. + +Use a for loop! +""" + +# Initial value is 1 +product = 1 + +# Ask the user for 10 numbers and multiply. +for i in range(10): + product *= int(input("Enter a number: ")) + +print("The product is " + str(product)) diff --git a/1_beginner/chapter5/solutions/up_2_fifty.py b/1_beginner/chapter5/solutions/up_2_fifty.py index 8e0eccb1..1f68afe7 100644 --- a/1_beginner/chapter5/solutions/up_2_fifty.py +++ b/1_beginner/chapter5/solutions/up_2_fifty.py @@ -2,7 +2,7 @@ # Write a program that takes a number from the user # and adds 2 to it until it reaches 50 or more, # then prints out how many times 2 was added. -# If the number is already greater than 50, +# If the number is already 50 or greater, # then print out ('Already there!') # prompt user for a number @@ -23,4 +23,4 @@ # once the loop is done running print(iterations) else: - print('Already there!') + print("Already there!") diff --git a/1_beginner/chapter5/solutions/virtual_pet.py b/1_beginner/chapter5/solutions/virtual_pet.py new file mode 100644 index 00000000..ba357c08 --- /dev/null +++ b/1_beginner/chapter5/solutions/virtual_pet.py @@ -0,0 +1,47 @@ +""" +Virtual Pet +Code a virtual pet! +First, create three variables: name, hunger, and happiness. +Initialize hunger to 6 and happiness to 0, and set name +to whatever you like. (e.g. 'Otto') + +Your program should continuously take input from the user: + If the user enters 'feed', decrease hunger by 2. Increase by 1 otherwise. + If the user enters 'pet', increase happiness by 2. Decrease by 1 otherwise. + If the user enters 'quit', end the program. + +Your program should also print the status of your pet each time +it prompts the user to enter a command: + Print "[pet name] is hungry" when hunger is above 5, where [pet name] + is the name variable. + Print "[pet name] wants more attention" when happiness is below 5. + Print "[pet name] feels happy" when hunger is equal to or less than 5, + and happiness is equal to or greater than 5. + +Feel free to customize your virtual pet by changing the how much the hunger +and happiness variables increase and decrease, or add more actions! +""" + +name = "Otto" +hunger = 6 +happiness = 0 + +command = input("> ") +while command != "quit": # Exits loop when user quits + # change Otto's hunger or happiness based on user command + if command == "feed": + hunger -= 2 # Otto is fed, hunger decreases + happiness -= 1 # Otto is not pet, happiness decreases + elif command == "pet": + happiness += 2 # Otto is pet, happiness increases + hunger += 1 # Otto is not fed, hunger increases + + # display Otto's status + if hunger > 5: # Otto is not fed enough + print(name + " is hungry") + if happiness < 5: # Otto is not pet enough + print(name + " wants more attention") + elif hunger <= 5 and happiness >= 5: # Otto is satisfied! + print(name + " feels happy") + + command = input("> ") # Keep taking user input until user quits diff --git a/1_beginner/chapter6/examples/identity_operators.py b/1_beginner/chapter6/examples/identity_operators.py new file mode 100644 index 00000000..e939fa54 --- /dev/null +++ b/1_beginner/chapter6/examples/identity_operators.py @@ -0,0 +1,46 @@ +# Identity Operators + +message_1 = "hello" +message_2 = "hello" + +print(message_1 is message_2) # True +print(message_1 is not message_2) # False + +message_3 = "world" +message_4 = "tahiti" + +print(message_3 is message_4) # False +print(message_3 is not message_4) # True + +# Identity vs Equality + +# primitive values, if duplicated, +# are stored in the same location +x = 5 +y = 5 +if x is y: + print("x is y") +else: + print("x is not y") + +# lists are objects, +# so they are located in different +# locations in memory +a = [1, 2, 3] +b = [1, 2, 3] + +# use the identity operator +# to test if the 2 variables +# reference the same location +# in memory +if a is b: + print("a is b") +else: + print("a is not b") + +# use the equality operator +# to test if content is equivalent +if a == b: + print("a == b") +else: + print("a != b") diff --git a/1_beginner/chapter6/examples/lists.py b/1_beginner/chapter6/examples/lists.py index 034955b5..da827321 100644 --- a/1_beginner/chapter6/examples/lists.py +++ b/1_beginner/chapter6/examples/lists.py @@ -12,10 +12,10 @@ my_list = [] # empty list # append() adds elements to the end of the list -my_list.append('live') -my_list.append('long') -my_list.append('and') -my_list.append('prosper') +my_list.append("live") +my_list.append("long") +my_list.append("and") +my_list.append("prosper") print(my_list) # copy() returns a copy of the list @@ -27,15 +27,15 @@ print(my_list) # remove() removes the first item with the specified value -my_list.remove('live') +my_list.remove("live") print(my_list) # index() returns the index of the first element # with the specified value -print(my_list.index('prosper')) +print(my_list.index("prosper")) # insert() adds an element at the specified position -my_list.insert(0, 'live') +my_list.insert(0, "live") print(my_list) # reverse() reverses the order of the list diff --git a/1_beginner/chapter6/examples/membership_operators.py b/1_beginner/chapter6/examples/membership_operators.py new file mode 100644 index 00000000..b491948f --- /dev/null +++ b/1_beginner/chapter6/examples/membership_operators.py @@ -0,0 +1,13 @@ +# Membership Operators + +my_list = [1, 2, 3, "oh no"] + +if "oh no" in my_list: + print("oh no is an element in my_list") +else: + print("oh no is not an element in my_list") + +if 4 not in my_list: + print("4 is not an element in my_list") +else: + print("4 is an element in my_list") diff --git a/1_beginner/chapter6/practice/grades.py b/1_beginner/chapter6/practice/grades.py new file mode 100644 index 00000000..1bd0e054 --- /dev/null +++ b/1_beginner/chapter6/practice/grades.py @@ -0,0 +1,22 @@ +""" +Grades + +Create a list called names and a list called grades. +Ask the user to input a name, and then ask +them to input the person's grade. Add the inputs +to the corresponding lists. Use a for loop to ask +for these inputs 5 times. + +Display the info as "[name]: [grade]". + +Example lists AFTER user input: +names = ["John", "Belle", "Ria", "Steph", "Louis"] +grades = [93, 85, 100, 82, 70] + +Example output: +John: 93 +Belle: 85 +etc. +""" + +# Insert your code here. diff --git a/1_beginner/chapter6/practice/grocery_list.py b/1_beginner/chapter6/practice/grocery_list.py new file mode 100644 index 00000000..6b5ca07b --- /dev/null +++ b/1_beginner/chapter6/practice/grocery_list.py @@ -0,0 +1,24 @@ +""" +Grocery List + +Create a program that prompts the +user to continuously enter items for a grocery +list. Stop asking them for items +when the user enters 'quit'. + +Print the grocery list in a numbered format. + +Ask the user to enter prices for each +item in the grocery list in order. + +Finally, ask the user how many of +each item they bought. Based on their +input, calculate the total grocery bill +and display it. (Bonus points if you +can format the money so that it displays +2 decimals.) + +Demo: https://youtu.be/BmMj16Ox5iA +""" + +# write code here diff --git a/1_beginner/chapter6/practice/indexing.py b/1_beginner/chapter6/practice/indexing.py new file mode 100644 index 00000000..b288bc67 --- /dev/null +++ b/1_beginner/chapter6/practice/indexing.py @@ -0,0 +1,22 @@ +""" +Indexing + +1. Create a list with the following names: + Mark + Arya + Paz + Lulu + Jon + Robin +2. Print the first element of the list. +3. Print the 3rd-to last name ("Lulu") WITHOUT +using people[3]. +4. Print the second element of the list. + +Don't "hard-code" the answers. +For example, don't write print("Mark"). +Instead, use list indexing to get the values +from the list. +""" + +# Insert your code here. diff --git a/1_beginner/chapter6/practice/integer_info.py b/1_beginner/chapter6/practice/integer_info.py new file mode 100644 index 00000000..ea5ffbfb --- /dev/null +++ b/1_beginner/chapter6/practice/integer_info.py @@ -0,0 +1,13 @@ +""" +Integer Info +Create a program that takes an integer as input and +creates a list with the following elements: + The number of digits + The last digit + A 'True' boolean value if the number is even, 'False' if odd +Print the list. +Some examples are given to help check your work. +""" + +# Example 1: The input 123456 should print [6, 6, True] +# Example 2: The input 101202303 should print [9, 3, False] diff --git a/1_beginner/chapter6/practice/manipulation.py b/1_beginner/chapter6/practice/manipulation.py new file mode 100644 index 00000000..43ae7e42 --- /dev/null +++ b/1_beginner/chapter6/practice/manipulation.py @@ -0,0 +1,32 @@ +# Manipulation +# Modify the following code according to +# the instructions. + +nums = [] + +""" +The following code adds the numbers 0, 10, ... 100 +to the list nums, and displays this list. +First, make a copy of nums and store it in the varible more_nums. + +Then, clear nums, and add the numbers +2, 7, 12, ... 72 to nums instead. +""" + +for i in range(0, 101, 10): + nums.append(i) +print(nums) + +""" +Write the code for the following actions: +Change the 1st element of more_nums to -100. +Change the 2nd element of nums to 0. +Remove the last element from more_nums. +Remove every element divisible by 3 from more_nums. +Insert a 21 after the 20 in more_nums (assume that +you DON'T know the index of 20 ahead of time). +Insert 15 0's in the 3rd-to-last position. + Sample list after insertions: + [..., 0, 0, ..., 0, 0, element, element] +Display nums and more_nums. +""" diff --git a/1_beginner/chapter6/practice/monty_hall.py b/1_beginner/chapter6/practice/monty_hall.py new file mode 100644 index 00000000..ba8106ed --- /dev/null +++ b/1_beginner/chapter6/practice/monty_hall.py @@ -0,0 +1,44 @@ +""" +Monty Hall +Code the classic Monty Hall problem! + +The following is a description of the Monty Hall Problem: + There are three closed doors, 2 have goats behind them, + only one has a car behind it. + You don't know which door has what. + The goal is to pick the door that has the car behind it. + After you make a choice, Monty (the host) opens a door + that you did not choose, revealing a goat. + You are then asked whether you want to + change your choice to the other door. + The door you chose is more likely to have a car + if you switch to the other door, apparently! + +More step-by-step instructions (pseudocode) are commented below. + +Read more about the Monty Hall Problem here: +https://betterexplained.com/articles/understanding-the-monty-hall-problem/ + +Demo: https://youtu.be/lwqtZU1ZFgM +""" + +# Make a list that represents the three closed doors, +# 'G' for the doors that have a goat, 'C' for the door that has a car. +# This step has already been done for you. +import random + +doors = ["G", "G", "C"] + +# Make the Monty Hall game repeat 6 times. +# (All of the following actions should be in your loop.) + +# The shuffle function from the random module randomizes the doors every loop +random.shuffle(doors) + +# The user enters their 1st choice. + +# A door that has a goat is revealed, cannot be the one user chose + +# The user is prompted to choose again + +# The prize behind the user's ultimate choice is revealed! diff --git a/1_beginner/chapter6/practice/names.py b/1_beginner/chapter6/practice/names.py new file mode 100644 index 00000000..14e1f181 --- /dev/null +++ b/1_beginner/chapter6/practice/names.py @@ -0,0 +1,17 @@ +""" +Names + +Make a list called people and fill it with +at least 6 names. Make another list and use +list slicing to fill it with every other name +from the original list, starting with the 1st name. +Print both lists. +""" + +# Insert your code here. + +""" +Use a for loop to ask the user to add 4 names +to the list. After you ask for each name, print +out the last 5 names of the list. +""" diff --git a/1_beginner/chapter6/practice/restaurant.py b/1_beginner/chapter6/practice/restaurant.py new file mode 100644 index 00000000..d71be0d2 --- /dev/null +++ b/1_beginner/chapter6/practice/restaurant.py @@ -0,0 +1,94 @@ +""" +Restaurant +Write a program that asks someone +what they want to order and give them +the total cost of their meal. + +Each meal should have 3 categories +(ex. food, drinks, desserts), and the user +must order 1 item from each category. + +Make sure to include a 7% sales tax +(multiply the total by 1.07), and +round the answer to 2 decimal places + +The steps are outlined in the following code. +""" + +""" +1. Store the menu in lists. +Each of the 3 categories should have an +items menu and a costs menu. + +Example of the 6 lists you need to create: +foods, food_costs, drinks, drink_costs, +desserts, dessert_costs +""" + +# Insert your lists here. + +""" +2. Display the menu. +Iterate through the items and costs for each +category to do this. + +Example output: +Welcome to my restaurant! Here's the menu: + +Food: +Pancakes: $5 +Waffles: $3 +Toast: $100 + +Drinks: +Juice: $2 +Water: $50 +Tea: $1 + +Sugar: +Muffin: $4 +Lollipop: $20 +Brownie: $15 +""" + +# Insert the code for displaying the menu here. + +""" +3. Ask the user to order. +This code should be in a loop. After you display the +user's total at the end, ask them if they want to +order again. If they say "no", the program should end. +Otherwise, you should take their order and display the +new total again. + +As you take their order, check if what they ordered +is in the corresponding list for that category. +If it is, add the price of the item to the total. +Otherwise, display a warning message and add $1000 to the total. + +Example order (using menu above): +What food would you like? pancakes +What drink would you like? water +What sugar item would you like? candy +You didn't order a proper sugary item! Adding $1000 to tab. +""" + +# Insert the code that takes the user's order here. +# Make sure it's in a loop. + +""" +4. Finalize the total. +Add a 7% sales tax to your sum, and +round this value to 2 decimal places. +Display the total. +""" + +# Add the code to finalize and display the total here. + +""" +5. As mentioned in Step 3, ask the user if they want +to order again. If they say "no", then stop the program. +Otherwise, let them order again. +""" + +# Ask the user if they want to order again here. diff --git a/1_beginner/chapter6/practice/snookle_game.py b/1_beginner/chapter6/practice/snookle_game.py new file mode 100644 index 00000000..0b308e76 --- /dev/null +++ b/1_beginner/chapter6/practice/snookle_game.py @@ -0,0 +1,21 @@ +""" +Snookle Game +Snookle the sheep wants to play a game. +Given a list of positive integers and a main number, the player iterates +through each element in the list and chooses to either add it or +to subtract it from the current main number. + +This is done by having the user enter either 'add' or 'subtract' every turn. +The main number will be updated to the new value. + +A player wins if they make 12 to be the main number. +If the end of the list is reached, go back to the first element +in the list and keep going until the player wins. +Code the game for Snookle! + +Demo: https://youtu.be/JtQDsb9rsf4 +""" + +# example values to get you started +nums = [3, 1, 4, 2, 6, 5, 8, 10] +main = 7 diff --git a/1_beginner/chapter6/practice/sum_list.py b/1_beginner/chapter6/practice/sum_list.py new file mode 100644 index 00000000..a49e23de --- /dev/null +++ b/1_beginner/chapter6/practice/sum_list.py @@ -0,0 +1,8 @@ +# Sum List +# Create a program that prints the +# sum of numbers in a list. +# A test list has been given to you. + +numbers = [34, 12, 57, 0, 325, -2535, 12, 1] + +# write code here diff --git a/1_beginner/chapter6/practice/too_long.py b/1_beginner/chapter6/practice/too_long.py new file mode 100644 index 00000000..4a235299 --- /dev/null +++ b/1_beginner/chapter6/practice/too_long.py @@ -0,0 +1,7 @@ +""" +Too Long +Print and remove all elements with length +greater than 4 in a given list of strings. +""" +# list to help you test your code +the_list = ["dragon", "cab", "science", "dove", "lime", "river", "pop"] diff --git a/1_beginner/chapter6/solutions/grades.py b/1_beginner/chapter6/solutions/grades.py new file mode 100644 index 00000000..47c9c498 --- /dev/null +++ b/1_beginner/chapter6/solutions/grades.py @@ -0,0 +1,32 @@ +""" +Grades + +Create a list called names and a list called grades. +Ask the user to input a name, and then ask +them to input the person's grade. Add the inputs +to the corresponding lists. Use a for loop to ask +for these inputs 5 times. + +Display the info as "[name]: [grade]". + +Example lists AFTER user input: +names = ["John", "Belle", "Ria", "Steph", "Louis"] +grades = [93, 85, 100, 82, 70] + +Example output: +John: 93 +Belle: 85 +etc. +""" + +names = [] +grades = [] + +# Collect inputs. +for i in range(5): + names.append(input("Enter a name: ")) + grades.append(input("Enter their grade: ")) + +# Format output correctly. +for i in range(len(names)): + print(names[i] + ": " + grades[i]) diff --git a/1_beginner/chapter6/solutions/grocery_list.py b/1_beginner/chapter6/solutions/grocery_list.py new file mode 100644 index 00000000..1e251a2e --- /dev/null +++ b/1_beginner/chapter6/solutions/grocery_list.py @@ -0,0 +1,65 @@ +""" +Grocery List + +Create a program that prompts the +user to continuously enter items for a grocery +list. Stop asking them for items +when the user enters 'quit'. + +Print the grocery list in a numbered format. + +Ask the user to enter prices for each +item in the grocery list in order. + +Finally, ask the user how many of +each item they bought. Based on their +input, calculate the total grocery bill +and display it. (Bonus points if you +can format the money so that it displays +2 decimals.) + +Demo: https://youtu.be/BmMj16Ox5iA +""" + +item = "" +items = [] # stores grocery items + +# continuously ask user for grocery items +# and store them in a list +while item != "quit": + item = input("Enter a grocery item, or 'quit': ") + if item != "quit": + items.append(item) + +# print items in numbered format +for i in range(0, len(items)): + print(str(i + 1) + ". " + items[i]) + +print() + +# ask user to enter prices for +# each grocery item +prices = [] +for i in range(0, len(items)): + price = float(input("Enter price for " + items[i] + ": $")) + prices.append(price) + +print() + +# ask user for quantity of each +# item in the grocery list +quantities = [] +for i in range(0, len(items)): + quantity = int(input("Enter quantity bought for " + items[i] + ": ")) + quantities.append(quantity) + +print() + +# calculate total grocery bill +total = 0 +for i in range(0, len(items)): + total += prices[i] * quantities[i] + +# print total, formatted to 2 decimals +# because it's money +print("Total: $%.2f" % total) diff --git a/1_beginner/chapter6/solutions/indexing.py b/1_beginner/chapter6/solutions/indexing.py new file mode 100644 index 00000000..0b096ab2 --- /dev/null +++ b/1_beginner/chapter6/solutions/indexing.py @@ -0,0 +1,26 @@ +""" +Indexing + +1. Create a list with the following names: + Mark + Arya + Paz + Lulu + Jon + Robin +2. Print the first element of the list. +3. Print the 3rd-to last name ("Lulu") WITHOUT +using people[3]. +4. Print the second element of the list. + +Don't "hard-code" the answers. +For example, don't write print("Mark"). +Instead, use list indexing to get the values +from the list. +""" + +names = ["Mark", "Arya", "Paz", "Lulu", "Jon", "Robin"] + +print(names[0]) +print(names[-3]) +print(names[1]) diff --git a/1_beginner/chapter6/solutions/integer_info.py b/1_beginner/chapter6/solutions/integer_info.py new file mode 100644 index 00000000..0a9ee705 --- /dev/null +++ b/1_beginner/chapter6/solutions/integer_info.py @@ -0,0 +1,21 @@ +""" +Integer Info +Create a program that takes an integer as input and +creates a list with the following elements: + The number of digits + The last digit + A 'True' boolean value if the number is even, 'False' if odd +Print the list. +Some examples are given to help check your work. +""" + +# Example 1: The input 123456 should print [6, 6, True] +# Example 2: The input 101202303 should print [9, 3, False] + +num = int(input("Enter an integer: ")) # convert string input to int +info = [ + len(str(num)), + num % 10, # mod 10 of any number will return its last digit + num % 2 == 0, +] +print(info) diff --git a/1_beginner/chapter6/solutions/manipulation.py b/1_beginner/chapter6/solutions/manipulation.py new file mode 100644 index 00000000..77da4574 --- /dev/null +++ b/1_beginner/chapter6/solutions/manipulation.py @@ -0,0 +1,54 @@ +# Manipulation +# Modify the following code according to +# the instructions. + +nums = [] + +""" +The following code adds the numbers 0, 10, ... 100 +to the list nums, and displays this list. +First, make a copy of nums and store it in the varible more_nums. + +Then, clear nums, and add the numbers +2, 7, 12, ... 72 to nums instead. +""" + +for i in range(0, 101, 10): + nums.append(i) +print(nums) + +more_nums = nums.copy() + +nums.clear() +for i in range(2, 73, 5): + nums.append(i) + +""" +Write the code for the following actions: +Change the 1st element of more_nums to -100. +Change the 2nd element of nums to 0. +Remove the last element from more_nums. +Remove every element divisible by 3 from more_nums. +Insert a 21 after the 20 in more_nums (assume that +you DON'T know the index of 20 ahead of time). +Insert 15 0's in the 3rd-to-last position. + Sample list after insertions: + [..., 0, 0, ..., 0, 0, element, element] +Display nums and more_nums. +""" + +more_nums[0] = -100 +nums[1] = 0 +more_nums.pop(-1) + +for i in more_nums: + if i % 3 == 0: + more_nums.remove(i) + +more_nums.insert(more_nums.index(20) + 1, 21) + +for i in range(15): + more_nums.insert(-2, 0) + +print("nums:\n", nums) +print("more_nums:\n", more_nums) diff --git a/1_beginner/chapter6/solutions/monty_hall.py b/1_beginner/chapter6/solutions/monty_hall.py new file mode 100644 index 00000000..7d851c40 --- /dev/null +++ b/1_beginner/chapter6/solutions/monty_hall.py @@ -0,0 +1,71 @@ +""" +Monty Hall +Code the classic Monty Hall problem! + +The following is a description of the Monty Hall Problem: + There are three closed doors, 2 have goats behind them, + only one has a car behind it. + You don't know which door has what. + The goal is to pick the door that has the car behind it. + After you make a choice, Monty (the host) opens a door + that you did not choose, revealing a goat. + You are then asked whether you want to + change your choice to the other door. + The door you chose is more likely to have a car + if you switch to the other door, apparently! + +More step-by-step instructions (pseudocode) are commented below. + +Read more about the Monty Hall Problem here: +https://betterexplained.com/articles/understanding-the-monty-hall-problem/ + +Demo: https://youtu.be/lwqtZU1ZFgM +""" + +# Make a list that represents the three closed doors, +# 'G' for the doors that have a goat, 'C' for the door that has a car. +import random + +doors = ["G", "G", "C"] + +# Make the Monty Hall game repeat 6 times. +for i in range(6): + # Randomize the doors + random.shuffle(doors) + + # reset the doors left + doors_left = [1, 2, 3] + + # The user enters their 1st choice + print("A new Monty Hall game has begun!") + choice = int(input("Choose from doors 1, 2, or 3...\n> ")) + doors_left.remove(choice) + + # A door that has a goat is revealed, cannot be the one user chose + # makes a duplicate list to avoid messing up the original + reveal_doors = doors.copy() + + # removes user's choice so that it won't be opened, + # but keeps in the element to not mess up the indices + reveal_doors[choice - 1] = "-" + goat_door = reveal_doors.index("G") + 1 + doors_left.remove(goat_door) + print("Monty opens door", goat_door, "to reveal a goat!") + + # The user is prompted to choose again + print("With this new information, do you want to switch doors?") + print("Your first choice was Door", choice) + print("If you switch, you will be opening Door", doors_left[0]) + print("Enter 'y' to switch, or 'n' to keep your first choice.") + switch = input("> ") + + if switch == "y": + choice = doors_left[0] + + # The prize behind the user's ultimate choice is revealed! + if doors[choice - 1] == "C": + print("You got... a car! Congratulations!") + else: + print("You got... a goat! Better luck next time!") + + print() diff --git a/1_beginner/chapter6/solutions/names.py b/1_beginner/chapter6/solutions/names.py new file mode 100644 index 00000000..ef98419a --- /dev/null +++ b/1_beginner/chapter6/solutions/names.py @@ -0,0 +1,30 @@ +""" +Names + +Make a list called people and fill it with +at least 6 names. Make another list and use +list slicing to fill it with every other name +from the original list, starting with the 1st name. +Print both lists. +""" + +people = ["Mark", "Anya", "Wan", "Jewel", "Stef", "Hank"] + +# Answers may vary. +# Some other possible answers: +# group = people[0, 5, 2] or people[0, len(people), 2] + +group = people[0 : len(people) : 2] + +print(people) +print(group) + +""" +Use a for loop to ask the user to add 4 names +to the list. After you ask for each name, print +out the last 5 names of the list. +""" + +for i in range(4): + people.append(input("Enter a name: ")) + print("Last 5 names:", people[-5:]) diff --git a/1_beginner/chapter6/solutions/restaurant.py b/1_beginner/chapter6/solutions/restaurant.py new file mode 100644 index 00000000..1da48666 --- /dev/null +++ b/1_beginner/chapter6/solutions/restaurant.py @@ -0,0 +1,126 @@ +""" +Restaurant +Write a program that asks someone +what they want to order and give them +the total cost of their meal. + +Each meal should have 3 categories +(ex. food, drinks, desserts), and the user +must order 1 item from each category. + +Make sure to include a 7% sales tax +(multiply the total by 1.07), and +round the answer to 2 decimal places + +The steps are outlined in the following code. +""" + +""" +1. Store the menu in lists. +Each of the 3 categories should have an +items menu and a costs menu. + +Example of the 6 lists you need to create: +foods, food_costs, drinks, drink_costs, +desserts, dessert_costs +""" + +foods = ["pancakes", "waffles", "toast"] +food_costs = [5, 3, 100] + +drinks = ["juice", "water", "tea"] +drink_costs = [2, 50, 1] + +sugar_foods = ["muffin", "lollipop", "brownie"] +sugar_costs = [4, 20, 15] + +""" +2. Display the menu. +Iterate through the items and costs for each +category to do this. +""" + +print("Welcome to my restaurant! Here's the menu:") +print("Foods:") +for i in range(len(foods)): + print(foods[i] + ": $" + str(food_costs[i])) +print() + +print("Drinks:") +for i in range(len(drinks)): + print(drinks[i] + ": $" + str(drink_costs[i])) +print() + +print("Sugar:") +for i in range(len(sugar_foods)): + print(sugar_foods[i] + ": $" + str(sugar_costs[i])) +print() + +print("Order in all lowercase!\n") + +""" +3. Ask the user to order. +This code should be in a loop. After you display the +user's total at the end, ask them if they want to +order again. If they say "no", the program should end. +Otherwise, you should take their order and display the +new total again. + +As you take their order, check if what they ordered +is in the corresponding list for that category. +If it is, add the price of the item to the total. +Otherwise, display a warning message and add $1000 to the total. + +4. Finalize the total. +Add a 7% sales tax to your sum, and +round this value to 2 decimal places. +Display the total. + +5. As mentioned in Step 3, ask the user if they want +to order again. If they say "no", then stop the program. +Otherwise, let them order again. +""" + +# Take the user's order. +while True: + cost = 0 + + # Foods + food = input("What food would you like? ") + + if food in foods: + cost += food_costs[foods.index(food)] + else: + print("You didn't order a proper food... Adding $1000 to tab.") + cost += 1000 + + # Drinks + drink = input("What drink would you like? ") + + if drink in drinks: + cost += drink_costs[drinks.index(drink)] + else: + print("You didn't order a proper drink... Adding $1000 to tab.") + cost += 1000 + + # Sugar + sugar = input("What sugar item would you like? ") + + if sugar in sugar_foods: + cost += sugar_costs[sugar_foods.index(sugar)] + else: + print("You didn't order a proper sugary item! Adding $1000 to tab.") + cost += 1000 + + # Sales tax + cost *= 1.07 + + # Display output + print("Your total is $%.2f." % cost) + print() + + # Ask if the user wants to order again + ans = input('Do you want to order again ("yes" or "no")? ') + print() + if ans == "no": + break diff --git a/1_beginner/chapter6/solutions/snookle_game.py b/1_beginner/chapter6/solutions/snookle_game.py new file mode 100644 index 00000000..583f633e --- /dev/null +++ b/1_beginner/chapter6/solutions/snookle_game.py @@ -0,0 +1,42 @@ +""" +Snookle Game +Snookle the sheep wants to play a game. +Given a list of positive integers and a main number, the player iterates +through each element in the list and chooses to either add it or +to subtract it from the current main number. + +This is done by having the user enter either 'add' or 'subtract' every turn. +The main number will be updated to the new value. + +A player wins if they make 12 to be the main number. +If the end of the list is reached, go back to the first element +in the list and keep going until the player wins. +Code the game for Snookle! + +Demo: https://youtu.be/JtQDsb9rsf4 +""" + +# example values to get you started +nums = [3, 1, 4, 2, 6, 5, 8, 10] +main = 7 + +win = False # The game keeps going until this variable is set to True + +while not win: + for num in nums: + # prompt user to add or subtract current num + print("main number is currently " + str(main)) + choice = input("[add] or [subtract] " + str(num) + "?\n> ") + + # update main value based on choice + if choice == "add": + main += num + elif choice == "subtract": + main -= num + + # If the main number if 12, the user has won! + if main == 12: + win = True # Exit while loop and end game + break # Exit for loop + +print("Congrats you won the game!") diff --git a/1_beginner/chapter6/solutions/sum_list.py b/1_beginner/chapter6/solutions/sum_list.py new file mode 100644 index 00000000..e027baeb --- /dev/null +++ b/1_beginner/chapter6/solutions/sum_list.py @@ -0,0 +1,13 @@ +# Sum List +# Create a program that prints the +# sum of numbers in a list. +# A test list has been given to you. + +numbers = [34, 12, 57, 0, 325, -5, 12, 1] + +sum = 0 + +for num in numbers: + sum += num + +print("Sum of elements in list:", sum) diff --git a/1_beginner/chapter6/solutions/too_long.py b/1_beginner/chapter6/solutions/too_long.py new file mode 100644 index 00000000..3c17d7e3 --- /dev/null +++ b/1_beginner/chapter6/solutions/too_long.py @@ -0,0 +1,15 @@ +""" +Too Long +Print and remove all elements with length +greater than 4 in a given list of strings. +""" +the_list = ["dragon", "cab", "science", "dove", "lime", "river", "pop"] + +to_remove = [] +for x in the_list: # iterates through every element in the list + if len(x) > 4: # if the element length is greater than 4 + print(x) # prints the element + to_remove.append(x) # appends element to remove list + +for y in to_remove: # iterates through every element meant to be removed + the_list.remove(y) # removes element from list diff --git a/1_beginner/chapter7/examples/string_manipulation.py b/1_beginner/chapter7/examples/string_manipulation.py index efeab443..99c3a6e5 100644 --- a/1_beginner/chapter7/examples/string_manipulation.py +++ b/1_beginner/chapter7/examples/string_manipulation.py @@ -44,6 +44,9 @@ my_string = "hello" print(my_string[2]) # prints 'l' print(my_string[2:4]) # prints 'll' +# start: 2 (inclusive), stop: 4 (exclusive), default step of 1 +print(my_string[-1:-3:-1]) # prints 'ol' +# start: -1 (inclusive), stop: -3 (exclusive), step of -1 for char in my_string: print(char) # prints each character on its own line diff --git a/1_beginner/chapter7/practice/capital.py b/1_beginner/chapter7/practice/capital.py new file mode 100644 index 00000000..56372834 --- /dev/null +++ b/1_beginner/chapter7/practice/capital.py @@ -0,0 +1,14 @@ +""" +Capital + +Write a program that takes a string as input, +makes every other letter capital, sets the rest +of the characters to be lowercase, and displays +the new string. + +Hint: Make sure you don't count non-alphabetic +characters. + +Example input: I love Zee the Cat. +Example output: I lOvE zEe ThE cAt. +""" diff --git a/1_beginner/chapter7/practice/every_other_word.py b/1_beginner/chapter7/practice/every_other_word.py new file mode 100644 index 00000000..dd6fffa1 --- /dev/null +++ b/1_beginner/chapter7/practice/every_other_word.py @@ -0,0 +1,8 @@ +""" +Every Other Word + +Write a program that takes a sentence +as input and prints every other word. +""" + +# Insert your code here. diff --git a/1_beginner/chapter7/practice/first_three_words.py b/1_beginner/chapter7/practice/first_three_words.py new file mode 100644 index 00000000..53205f94 --- /dev/null +++ b/1_beginner/chapter7/practice/first_three_words.py @@ -0,0 +1,10 @@ +""" +First Three Words + +Write a program which asks +the user to enter a sentence. +Print the first three words in the sentence. +(Assume the user enters at least 3 words.) +""" + +# write code here diff --git a/1_beginner/chapter7/practice/ingly.py b/1_beginner/chapter7/practice/ingly.py new file mode 100644 index 00000000..6f8f9abe --- /dev/null +++ b/1_beginner/chapter7/practice/ingly.py @@ -0,0 +1,14 @@ +""" +Ingly + +Write a Python program to add 'ing' +at the end of a given string (length should be at least 3). +If the given string already ends with 'ing' then add 'ly' instead. +If the string length of the given string is less than 3, leave it unchanged. +Print the resulting string. + +Adapted from W3Resource, problem 6: +https://www.w3resource.com/python-exercises/string/ +""" + +# write code here diff --git a/1_beginner/chapter7/practice/phone_number.py b/1_beginner/chapter7/practice/phone_number.py new file mode 100644 index 00000000..425f458e --- /dev/null +++ b/1_beginner/chapter7/practice/phone_number.py @@ -0,0 +1,22 @@ +""" +Phone Number + +Write a program that asks the user to input a phone number. +Strip their input to get rid of any accidental spaces. + +If the input has anything besides digits or the input +isn’t exactly 10 digits long, The program should keep asking them +for a phone number until the input is formatted correctly. + +Display the phone number at the end. + +Example program run: +Enter a phone number (10 digits): 732-000-0000 +Enter a phone number (10 digits): abcdeabcde +Enter a phone number (10 digits): (908)9999999 +Enter a phone number (10 digits): 732000000 +Enter a phone number (10 digits): 1234567891 +Your number is 1234567891 +""" + +# Insert your code here. diff --git a/1_beginner/chapter7/practice/replace.py b/1_beginner/chapter7/practice/replace.py new file mode 100644 index 00000000..86338be9 --- /dev/null +++ b/1_beginner/chapter7/practice/replace.py @@ -0,0 +1,17 @@ +""" +Replace + +Write a Python program that asks the user for a string +and then prints a version of that string +where all occurrences of its first char +have been changed to '$', +except the first char itself. + +Sample Input: 'restart' +Expected Output: 'resta$t' + +Adapted from W3Resource, problem 4: +https://www.w3resource.com/python-exercises/string/ +""" + +# write code here diff --git a/1_beginner/chapter7/practice/reverse_alphabet.py b/1_beginner/chapter7/practice/reverse_alphabet.py new file mode 100644 index 00000000..3bb59eea --- /dev/null +++ b/1_beginner/chapter7/practice/reverse_alphabet.py @@ -0,0 +1,14 @@ +""" +Reverse Alphabet (Challenge Problem) + +Create a list of strings by asking the user +to provide 10 inputs. Sort these in reverse +alphabetical order, and display the result. + +Note: "Cat" will come before "dog" because of ASCII values. + +To sort while IGNORING case, you might want to create +another list, make the elements all uppercase or all lowercase, +sort this list, and figure out how to use this list to +sort the original list. +""" diff --git a/1_beginner/chapter7/practice/upper.py b/1_beginner/chapter7/practice/upper.py index 2894d162..18ea29b9 100644 --- a/1_beginner/chapter7/practice/upper.py +++ b/1_beginner/chapter7/practice/upper.py @@ -1,6 +1,7 @@ # Upper # Continuously ask a user to enter words. -# (Make sure that the input given is actually just 1 word.) +# You should remove leading/trailing whitespace, and then +# make sure that the word is only made up of letters. # Store the words in a list. # Stop asking the user for words if they enter an empty string # (the string has no characters or is completely whitespace). diff --git a/1_beginner/chapter7/practice/vowels.py b/1_beginner/chapter7/practice/vowels.py new file mode 100644 index 00000000..f98230f2 --- /dev/null +++ b/1_beginner/chapter7/practice/vowels.py @@ -0,0 +1,9 @@ +""" +Vowels + +Create a program which takes a string +from the user and prints the number +of vowels that are in the string. +""" + +# write code here diff --git a/1_beginner/chapter7/solutions/capital.py b/1_beginner/chapter7/solutions/capital.py new file mode 100644 index 00000000..6202367f --- /dev/null +++ b/1_beginner/chapter7/solutions/capital.py @@ -0,0 +1,31 @@ +""" +Capital + +Write a program that takes a string as input, +makes every other letter capital, sets the rest +of the characters to be lowercase, and displays +the new string. + +Hint: Make sure you don't count non-alphabetic +characters. + +Example input: I love Zee the Cat. +Example output: I lOvE zEe ThE cAt. +""" + +str = input("Enter a string: ") + +new_str = "" +letter_count = 0 # Used to avoid counting non-alpha characters. + +for c in str: + if c.isalpha(): + if letter_count % 2 == 0: + new_str += c.upper() + else: + new_str += c.lower() + letter_count += 1 + else: + new_str += c + +print(new_str) diff --git a/1_beginner/chapter7/solutions/every_other_word.py b/1_beginner/chapter7/solutions/every_other_word.py new file mode 100644 index 00000000..45e11a82 --- /dev/null +++ b/1_beginner/chapter7/solutions/every_other_word.py @@ -0,0 +1,17 @@ +""" +Every Other Word + +Write a program that takes a sentence +as input and prints every other word. + +Example input: I like cats, dogs, and pizza. +Example output: +I +cats, +and +""" + +sentence = input("Enter a sentence: ") +words = sentence.split() +for i in range(0, len(words), 2): + print(words[i]) diff --git a/1_beginner/chapter7/solutions/first_three_words.py b/1_beginner/chapter7/solutions/first_three_words.py new file mode 100644 index 00000000..d8454e30 --- /dev/null +++ b/1_beginner/chapter7/solutions/first_three_words.py @@ -0,0 +1,15 @@ +""" +First Three Words + +Write a program which asks +the user to enter a sentence. +Print the first three words in the sentence. +(Assume the user enters at least 3 words.) +""" + +sentence = input("Enter a sentence: ") + +words = sentence.split() + +for word in words[:3]: + print(word) diff --git a/1_beginner/chapter7/solutions/ingly.py b/1_beginner/chapter7/solutions/ingly.py new file mode 100644 index 00000000..2c018ea0 --- /dev/null +++ b/1_beginner/chapter7/solutions/ingly.py @@ -0,0 +1,22 @@ +""" +Ingly + +Write a Python program to add 'ing' +at the end of a given string (length should be at least 3). +If the given string already ends with 'ing' then add 'ly' instead. +If the string length of the given string is less than 3, leave it unchanged. +Print the resulting string. + +Adapted from W3Resource, problem 6: +https://www.w3resource.com/python-exercises/string/ +""" + +string = input("Enter a string: ") + +if len(string) >= 3: + if string[-3:] != "ing": + string += "ing" + else: + string += "ly" + +print(string) diff --git a/1_beginner/chapter7/solutions/phone_number.py b/1_beginner/chapter7/solutions/phone_number.py new file mode 100644 index 00000000..951e4c3e --- /dev/null +++ b/1_beginner/chapter7/solutions/phone_number.py @@ -0,0 +1,26 @@ +""" +Phone Number + +Write a program that asks the user to input a phone number. +Strip their input to get rid of any accidental spaces. + +If the input has anything besides digits or the input +isn’t exactly 10 digits long, The program should keep asking them +for a phone number until the input is formatted correctly. + +Display the phone number at the end. + +Example program run: +Enter a phone number (10 digits): 732-000-0000 +Enter a phone number (10 digits): abcdeabcde +Enter a phone number (10 digits): (908)9999999 +Enter a phone number (10 digits): 732000000 +Enter a phone number (10 digits): 1234567891 +Your number is 1234567891 +""" + +number = "" +while len(number) != 10 or not number.isdigit(): + number = input("Enter a phone number (10 digits): ") + +print("Your number is", number) diff --git a/1_beginner/chapter7/solutions/replace.py b/1_beginner/chapter7/solutions/replace.py new file mode 100644 index 00000000..68bf0b37 --- /dev/null +++ b/1_beginner/chapter7/solutions/replace.py @@ -0,0 +1,28 @@ +""" +Replace + +Write a Python program that asks the user for a string +and then prints a version of that string +where all occurrences of its first char +have been changed to '$', +except the first char itself. + +Sample Input: 'restart' +Expected Output: 'resta$t' + +Adapted from W3Resource, problem 4: +https://www.w3resource.com/python-exercises/string/ +""" + +string = input("Enter a string: ") +first_char = string[0] + +result = first_char + +for i in range(1, len(string)): + if string[i] == first_char: + result += "$" + else: + result += string[i] + +print(result) diff --git a/1_beginner/chapter7/solutions/reverse_alphabet.py b/1_beginner/chapter7/solutions/reverse_alphabet.py new file mode 100644 index 00000000..13b1170b --- /dev/null +++ b/1_beginner/chapter7/solutions/reverse_alphabet.py @@ -0,0 +1,38 @@ +""" +Reverse Alphabet (Challenge Problem) + +Create a list of strings by asking the user +to provide 10 inputs. Sort these in reverse +alphabetical order, and display the result. + +Note: "Cat" will come before "dog" because of ASCII values. + +To sort while IGNORING case, you might want to create +another list, make the elements all uppercase or all lowercase, +sort this list, and figure out how to use this list to +sort the original list. + +""" + +strings = [] + +# Get user input. +for i in range(10): + strings.append(input("Enter a string: ")) + +# Create a lowercase version of strings. +lower_strings = [] +for str in strings: + lower_strings.append(str.lower()) +lower_strings.sort() + +# Set the position of each string in strings to the +# position of the lowercase string in lower_strings. +sorted_strings = strings.copy() +for str in strings: + # i is the goal index of str. + i = lower_strings.index(str.lower()) + sorted_strings[i] = str + +# Display the sorted list. +print(sorted_strings) diff --git a/1_beginner/chapter7/solutions/upper.py b/1_beginner/chapter7/solutions/upper.py index c9e094e8..6594205f 100644 --- a/1_beginner/chapter7/solutions/upper.py +++ b/1_beginner/chapter7/solutions/upper.py @@ -1,6 +1,7 @@ # Upper # Continuously ask a user to enter words. -# (Make sure that the input given is actually just 1 word.) +# You should remove leading/trailing whitespace, and then +# make sure that the word is only made up of letters. # Store the words in a list. # Stop asking the user for words if they enter an empty string # (the string has no characters or is completely whitespace). @@ -15,12 +16,8 @@ if word == "": break if word.isalpha(): - words.append(word) + words.append(word.upper()) else: print("Error, please enter 1 word") -result = [] -for word in words: - result.append(word.upper()) - -print(result) +print(words) diff --git a/1_beginner/chapter7/solutions/vowels.py b/1_beginner/chapter7/solutions/vowels.py new file mode 100644 index 00000000..9ded1707 --- /dev/null +++ b/1_beginner/chapter7/solutions/vowels.py @@ -0,0 +1,20 @@ +""" +Vowels + +Create a program which takes a string +from the user and prints the number +of vowels that are in the string. +""" +VOWELS = "aeiou" + +# Ask user for a string +string = input("Enter a string: ") + +# Count the number of vowels in the string +number_of_vowels = 0 +for char in string: + if char in VOWELS: + number_of_vowels += 1 + +# Print number of vowels +print("Number of vowels:", number_of_vowels) diff --git a/2_intermediate/chapter10/examples/2d_lists.py b/2_intermediate/chapter10/examples/2d_lists.py index 51f68b34..8f53ef52 100644 --- a/2_intermediate/chapter10/examples/2d_lists.py +++ b/2_intermediate/chapter10/examples/2d_lists.py @@ -1,10 +1,10 @@ # 2D Lists my_list = [ - ['hello', 'world', 'code'], - ['docs', 'slides', 'sheets'], - ['google', 'amazon', 'facebook'], - ['this', 'is', 'python'] + ["hello", "world", "code"], + ["docs", "slides", "sheets"], + ["google", "amazon", "facebook"], + ["this", "is", "python"], ] print(my_list[1][2]) # prints 'sheets' diff --git a/2_intermediate/chapter10/examples/nested_loop.py b/2_intermediate/chapter10/examples/nested_loop.py index f4a5db5f..dd2a3d15 100644 --- a/2_intermediate/chapter10/examples/nested_loop.py +++ b/2_intermediate/chapter10/examples/nested_loop.py @@ -2,10 +2,10 @@ # Traversing 2D Lists my_list = [ - ['hello', 'world', 'code'], - ['docs', 'slides', 'sheets'], - ['google', 'amazon', 'facebook'], - ['this', 'is', 'python'] + ["hello", "world", "code"], + ["docs", "slides", "sheets"], + ["google", "amazon", "facebook"], + ["this", "is", "python"], ] # print each element in my_list diff --git a/2_intermediate/chapter10/practice/address.py b/2_intermediate/chapter10/practice/address.py new file mode 100644 index 00000000..3234b99e --- /dev/null +++ b/2_intermediate/chapter10/practice/address.py @@ -0,0 +1,17 @@ +""" +Address + +Create a 2D list where each row represents a +person and has 3 elements: their name, their age, +and their address. + +The entire list should have 4 such entries (4 people), +so it will be a 4x3 list. + +Display the name and address of the 2nd person in the list. + +Then, display the entire list with the format: +name (age): address +""" + +# Insert your code here. diff --git a/2_intermediate/chapter10/practice/img_avg.py b/2_intermediate/chapter10/practice/img_avg.py new file mode 100644 index 00000000..2b71c3b3 --- /dev/null +++ b/2_intermediate/chapter10/practice/img_avg.py @@ -0,0 +1,83 @@ +""" +Image Average + +Here is the challenge problem for nested loops: +Images are often represented as 3D lists. +The outer list is the entire image. +The 1st level inner list is a row of pixels. +The 2nd level inner list is the RGB values for that pixel. +RGB (red, green, blue) values determine the color of the pixel. + +The interesting thing is that we can iterate over images. +The challenge is: given an image, create a program that +will return a different image where each pixel is the average +of the pixels surrounding it in the original image. + +To find the average value of all of a pixels neighbors, you must +calculate the average of the red values, blue values, and green values. +For example, if the neighbors of a pixel with value [1, 2, 3] +were [20, 30, 40] and [10, 120, 30], the new pixel that would replace the +original one would be [15, 75, 35] (since the average of 20 and 10 is 15, +the average of 30 and 120 is 75, and the average of 40 and 30 is 35). + +EXAMPLE: An image with 9 pixels may look like: +[ + [ + [31, 41, 42], [51, 1, 101], [24, 141, 33] + ], + + [ + [50, 21, 28], [31, 49, 201], [90, 54, 33] + ], + + [ + [12, 81, 3], [22, 8, 91], [101, 141, 132] + ] +] + +HINT: Don't forget that a pixel may have varying amount of neighboring +pixels. A pixel at the edge, for example, has 3 neighboring pixels while +a pixel at the center of the image has 8 neighboring pixels (one on each +of its 4 sides, and then one at each of its 4 corners). +""" + +# Import libraries needed to run the program +# Before importing the libraries, you must have them installed. +# This problem requires the following libraries: +# pillow, requests, numpy, and matplotlib +# If you don't already have them installed, open your command prompt or terminal +# and please do +# this: pip install -U (library) (any other libraries, each separated by a space) +# ex: pip install -U numpy matplotlib requests pillow +# Note: on some windows machines, you may need to +# do: py -m pip install -U (library) (any other libraries, each separated by a space) + +from PIL import Image +import requests +import numpy +import matplotlib.pyplot as plt + +# Code that grabs the image from the internet and makes it into an array +IMAGE_URL = ( + "https://images.dog.ceo/breeds/waterdog-spanish/20180723_185544.jpg" +) +img = numpy.array( + Image.open(requests.get(IMAGE_URL, stream=True).raw) +).tolist() + +# create newimg as an empty list so that we'll know if something went wrong +# ie. if we try to display it and the function didn't run, we'd get an +# invalid shape error +newimg = [[[] for column in row] for row in img] + +# Code that displays the original image +print("now displaying the original image") +plt.imshow(img) +plt.show() + +# Write code to create newimg here + +# Code that displays the new image at the end +print("now displaying the new image") +plt.imshow(newimg) +plt.show() diff --git a/2_intermediate/chapter10/practice/odd_sum.py b/2_intermediate/chapter10/practice/odd_sum.py new file mode 100644 index 00000000..a07d215b --- /dev/null +++ b/2_intermediate/chapter10/practice/odd_sum.py @@ -0,0 +1,11 @@ +# Odd Sum +# Given a 2D list, find the sum of all elements at odd indexes for all +# the lists at odd indexes. Print this sum times the sum of all +# first element of all the 1D lists in the 2D list. +# +# Ex:[[1,2,3,6],[2,41,2,1]]should have print 42 after the program runs. +# +# Write the code below. + +two_d_list = [[1, 2, 3, 5, 2], [2, 3, 1, 4], [2, 3, 1, 2, 21], [21, 3, 1, 41]] +# two_d_list should print 51 after the program runs. diff --git a/2_intermediate/chapter10/practice/print_even.py b/2_intermediate/chapter10/practice/print_even.py index 425767a5..60e873c9 100644 --- a/2_intermediate/chapter10/practice/print_even.py +++ b/2_intermediate/chapter10/practice/print_even.py @@ -6,12 +6,12 @@ # if you did it right! my_list = [ - ['awesome', 'hello', 'job', 'world'], - ['you', 'words', 'got', 'books'], - ['it', 'python', 'right'], - ['keep', 'plant', 'learning'], - ['how', 'school', 'to'], - ['code'] + ["awesome", "hello", "job", "world"], + ["you", "words", "got", "books"], + ["it", "python", "right"], + ["keep", "plant", "learning"], + ["how", "school", "to"], + ["code"], ] # write code here diff --git a/2_intermediate/chapter10/practice/random_grid.py b/2_intermediate/chapter10/practice/random_grid.py new file mode 100644 index 00000000..d579d720 --- /dev/null +++ b/2_intermediate/chapter10/practice/random_grid.py @@ -0,0 +1,40 @@ +""" +Random Grid + +Create a 2D list with 4 rows and a randomly +determined number of columns. The column +number should be a random EVEN number between +2 and 16 (inclusive). + +All the even column numbers (including 0) should +be filled with asterisks (*). The odd numbered +columns should be filled with underscores (_). + +Display the grid at the end by printing out +elements individually: don't use print(list). +Assume that you don't know the size of the grid +beforehand. In other words, if you wanted to display +the 2D list without knowing the number of rows and +columns in it, how would you code this? + +For example, a 4x6 grid would display this: +*_*_*_ +*_*_*_ +*_*_*_ + +This might be useful: +print("a") +print("a") +would display the "a"s with newlines: +a +a + +print("a", end=" ") +print("a") +changes the end of the first "a" from a newline to a space. +The output is this: +a a + +""" + +# Insert your code here. diff --git a/2_intermediate/chapter10/practice/smooth_max.py b/2_intermediate/chapter10/practice/smooth_max.py new file mode 100644 index 00000000..6530e8ae --- /dev/null +++ b/2_intermediate/chapter10/practice/smooth_max.py @@ -0,0 +1,9 @@ +# Given a 2D list, let's call a element "smooth" if index of the +# element in its 1D list plus the element is even. For example, +# given the 2D list [[0,4][2,6]], the 1st element of each of the +# 1D list is considered "smooth" because 0 + 0 is 0 and 0 + 2 is 2 +# (both are even numbers). Find the maximum "smooth" element and +# print it. Using the example [[0,4][2,6]] again, the maximum +# "smooth" element is 2 because 2 is bigger than 0. + +two_d_list = [[425, 214, 412, 123], [312, 214, 123, 343]] diff --git a/2_intermediate/chapter10/solutions/address.py b/2_intermediate/chapter10/solutions/address.py new file mode 100644 index 00000000..50e69f13 --- /dev/null +++ b/2_intermediate/chapter10/solutions/address.py @@ -0,0 +1,30 @@ +""" +Address + +Create a 2D list where each row represents a +person and has 3 elements: their name, their age, +and their address. + +The entire list should have 4 such entries (4 people), +so it will be a 4x3 list. + +Display the name and address of the 2nd person in the list. + +Then, display the entire list with the format: +name (age): address +""" + +contacts = [ + ["Jeremy", 10, "45 Pancake Road"], + ["Nicey", 18, "111 Cupcake Street"], + ["Hawthorne", 15, "19 Sinister Avenue"], + ["Nilah", 14, "Banks of the Nile River"], +] + +# 2nd person +print(contacts[1][0] + ": " + contacts[1][2]) +print() + +# Display the entire list. +for contact in contacts: + print(contact[0] + " (%d): " % contact[1] + contact[2]) diff --git a/2_intermediate/chapter10/solutions/img_avg.py b/2_intermediate/chapter10/solutions/img_avg.py new file mode 100644 index 00000000..66077ed6 --- /dev/null +++ b/2_intermediate/chapter10/solutions/img_avg.py @@ -0,0 +1,162 @@ +""" +Image Average + +Here is the challenge problem for nested loops: +Images are often represented as 3D lists. +The outer list is the entire image. +The 1st level inner list is a row of pixels. +The 2nd level inner list is the RGB values for that pixel. +RGB (red, green, blue) values determine the color of the pixel. + +The interesting thing is that we can iterate over images. +The challenge is: given an image, create a program that +will return a different image where each pixel is the average +of the pixels surrounding it in the original image. + +To find the average value of all of a pixels neighbors, you must +calculate the average of the red values, blue values, and green values. +For example, if the neighbors of a pixel with value [1, 2, 3] +were [20, 30, 40] and [10, 120, 30], the new pixel that would replace the +original one would be [15, 75, 35] (since the average of 20 and 10 is 15, +the average of 30 and 120 is 75, and the average of 40 and 30 is 35). + +EXAMPLE: An image with 9 pixels may look like: +[ + [ + [31, 41, 42], [51, 1, 101], [24, 141, 33] + ], + + [ + [50, 21, 28], [31, 49, 201], [90, 54, 33] + ], + + [ + [12, 81, 3], [22, 8, 91], [101, 141, 132] + ] +] + +HINT: Don't forget that a pixel may have varying amount of neighboring +pixels. A pixel at the edge, for example, has 3 neighboring pixels while +a pixel at the center of the image has 8 neighboring pixels (one on each +of its 4 sides, and then one at each of its 4 corners). +""" + +# Import libraries needed to run the program +# Before importing the libraries, you must have them installed. +# This problem requires the following libraries: +# pillow, requests, numpy, and matplotlib +# If you don't already have them installed, open your command prompt or terminal +# and please do +# this: pip install -U (library) (any other libraries, each separated by a space) +# ex: pip install -U numpy matplotlib requests pillow +# Note: on some windows machines, you may need to +# do: py -m pip install -U (library) (any other libraries, each separated by a space) + + +from PIL import Image +import requests +import numpy +import matplotlib.pyplot as plt + +# Code that grabs the image from the internet and makes it into an array +IMAGE_URL = ( + "https://images.dog.ceo/breeds/waterdog-spanish/20180723_185544.jpg" +) +img = numpy.array( + Image.open(requests.get(IMAGE_URL, stream=True).raw) +).tolist() + +# create newimg as an empty list so that we'll know if something went wrong +# ie. if we try to display it and the function didn't run, we'd get an +# invalid shape error +newimg = [[[] for column in row] for row in img] + +# Code that displays the original image +print("now displaying the original image") +plt.imshow(img) +plt.show() + + +def distort(original_image, new_image): + """ + Modifies new_image so that each pixel in new_image + will be the average of the surrounding + DISTORTION_RADIUS pixels. + DISTORTION_RADIUS can be changed for more/less distortion. + Arguments: + original_image (list or tuple) - the reference image. + new_image (list) - the image to modify. + """ + DISTORTION_RADIUS = 1 # this should be a positive integer + # Note that each increase of DISTORTION_RADIUS increases + # run time amazingly. Slower PC's should stick to values like + # 1 or 2 for DISTORTION_RADIUS + + for row in range(len(original_image)): + for column in range(len(original_image[0])): + # we set these to empty lists because the for loops + # will iterate through all valid relative indexes + # (including 0) and append them to these lists. + x_relative_indexes = [] + y_relative_indexes = [] + + # handle y relative indexes + # +1 to DISTORTION_RADIUS because stop is exclusive + for relative_y in range(-DISTORTION_RADIUS, DISTORTION_RADIUS + 1): + if ( + row + relative_y < 0 + or row + relative_y > len(original_image) - 1 + ): + # ignore relative indexes that are out of range of the + # original image + continue + # if it isn't out of range, it's valid and should be appended + y_relative_indexes.append(relative_y) + + # handle x relative indexes + # +1 to DISTORTION_RADIUS because stop is exclusive + for relative_x in range(-DISTORTION_RADIUS, DISTORTION_RADIUS + 1): + if ( + column + relative_x < 0 + or column + relative_x > len(original_image[0]) - 1 + ): + # ignore relative indexes that are out of range of the + # original image + continue + # if it isn't out of range, it's valid and should be appended + x_relative_indexes.append(relative_x) + + # at this point, x_relative_indexes and y_relative_indexes are + # complete, so now we use them. + r_total = g_total = b_total = counter = 0 # initialize variables + for x in x_relative_indexes: + for y in y_relative_indexes: + # since images are 'rgb': + # red is the first val + r_total += original_image[row + y][column + x][0] + + # green is the second val + g_total += original_image[row + y][column + x][1] + + # blue is third val + b_total += original_image[row + y][column + x][2] + + counter += 1 + + # round because images don't deal w/ floats, only integers + r_avg = round(r_total / counter) + g_avg = round(g_total / counter) + b_avg = round(b_total / counter) + + # update the pixel in newimg to match the average of its + # surrounding pixels + new_image[row][column] = [r_avg, g_avg, b_avg] + + +print("now modifying file. Depending on your pc, this may take a while.") +distort(img, newimg) + +# Code that displays the new image at the end +print("now displaying the new image") +plt.imshow(newimg) +plt.show() diff --git a/2_intermediate/chapter10/solutions/odd_sum.py b/2_intermediate/chapter10/solutions/odd_sum.py new file mode 100644 index 00000000..eff7cc7e --- /dev/null +++ b/2_intermediate/chapter10/solutions/odd_sum.py @@ -0,0 +1,22 @@ +# Odd Sum +# Given a 2D list, find the sum of all elements at odd indexes for all +# the lists at odd indexes. Print this sum times the sum of all +# first element of all the 1D lists in the 2D list. +# +# Ex:[[1,2,3,6],[2,41,2,1]]should have print 42 after the program runs. +# +# Write the code below. + +two_d_list = [[1, 2, 3, 5, 2], [2, 3, 1, 4], [2, 3, 1, 2, 21], [21, 3, 1, 41]] +# two_d_list should print 51 after the program runs. + +odd_sum = 0 +for outer_idx in range(1, len(two_d_list), 2): + for inner_idx in range(1, len(two_d_list[outer_idx]), 2): + odd_sum += two_d_list[outer_idx][inner_idx] + +first_sum = 0 +for inner_list in range(len(two_d_list)): + first_sum += two_d_list[inner_list][0] + +print(odd_sum * first_sum) diff --git a/2_intermediate/chapter10/solutions/print_even.py b/2_intermediate/chapter10/solutions/print_even.py index 92ed7a4e..29a4969f 100644 --- a/2_intermediate/chapter10/solutions/print_even.py +++ b/2_intermediate/chapter10/solutions/print_even.py @@ -6,12 +6,12 @@ # if you did it right! my_list = [ - ['awesome', 'hello', 'job', 'world'], - ['you', 'words', 'got', 'books'], - ['it', 'python', 'right'], - ['keep', 'plant', 'learning'], - ['how', 'school', 'to'], - ['code'] + ["awesome", "hello", "job", "world"], + ["you", "words", "got", "books"], + ["it", "python", "right"], + ["keep", "plant", "learning"], + ["how", "school", "to"], + ["code"], ] for words in my_list: diff --git a/2_intermediate/chapter10/solutions/random_grid.py b/2_intermediate/chapter10/solutions/random_grid.py new file mode 100644 index 00000000..934122d9 --- /dev/null +++ b/2_intermediate/chapter10/solutions/random_grid.py @@ -0,0 +1,54 @@ +""" +Random Grid + +Create a 2D list with 4 rows and a randomly +determined number of columns. The column +number should be a random EVEN number between +2 and 16 (inclusive). + +All the even column numbers (including 0) should +be filled with asterisks (*). The odd numbered +columns should be filled with underscores (_). + +Display the grid at the end by printing out +elements individually: don't use print(list). +Assume that you don't know the size of the grid +beforehand. In other words, if you wanted to display +the 2D list without knowing the number of rows and +columns in it, how would you code this? + +For example, a 4x6 grid would display this: +*_*_*_ +*_*_*_ +*_*_*_ + +This might be useful: +print("a") +print("a") +would display the "a"s with newlines: +a +a + +print("a", end=" ") +print("a") +changes the end of the first "a" from a newline to a space. +The output is this: +a a + +""" +import random + +grid = [] +cols = random.randint(1, 8) * 2 + +# Fill the grid 2D list. +for row in range(4): + grid.append([]) + for col in range(cols): + grid[row].append("*" if (col % 2 == 0) else "_") + +# Display the grid without knowing the size beforehand. +for row in grid: + for col in row: + print(col, end="") + print() diff --git a/2_intermediate/chapter10/solutions/smooth_max.py b/2_intermediate/chapter10/solutions/smooth_max.py new file mode 100644 index 00000000..a61465f6 --- /dev/null +++ b/2_intermediate/chapter10/solutions/smooth_max.py @@ -0,0 +1,20 @@ +# Given a 2D list, let's call a element "smooth" if index of the +# element in its 1D list plus the element is even. For example, +# given the 2D list [[0,4][2,6]], the 1st element of each of the +# 1D list is considered "smooth" because 0 + 0 is 0 and 0 + 2 is 2 +# (both are even numbers). Find the maximum "smooth" element and +# print it. Using the example [[0,4][2,6]] again, the maximum +# "smooth" element is 2 because 2 is bigger than 0. + +two_d_list = [[425, 214, 412, 123], [312, 214, 123, 343]] +curr_max = None + +for outer_idx in range(len(two_d_list)): + for inner_idx in range(len(two_d_list[outer_idx])): + curr_elem = two_d_list[outer_idx][inner_idx] + to_check = curr_elem + inner_idx + if to_check % 2 == 0: + if curr_max is None or curr_elem > curr_max: + curr_max = curr_elem + +print(curr_max) diff --git a/2_intermediate/chapter11/examples/define_function.py b/2_intermediate/chapter11/examples/define_function.py new file mode 100644 index 00000000..552421cc --- /dev/null +++ b/2_intermediate/chapter11/examples/define_function.py @@ -0,0 +1,8 @@ +# This is how you define a new function +# "scoop_ice_cream" is the function name +# and "flavor" is a parameter + + +def scoop_ice_cream(flavor): + # write function code here + pass diff --git a/2_intermediate/chapter11/examples/parameters.py b/2_intermediate/chapter11/examples/parameters.py new file mode 100644 index 00000000..90539b69 --- /dev/null +++ b/2_intermediate/chapter11/examples/parameters.py @@ -0,0 +1,43 @@ +# when using regular parameters, remember that order matters +def scoop_ice_cream(param1, param2, param3): + pass + + +scoop_ice_cream("chocolate", "vanilla", "sprinkles") + + +# keyword arguments can be used to input parameter out of order +def func(p1, p2, p3): + print(p1) # prints 2 + print(p2) # prints 3 + print(p3) # prints 1 + + +func(p3=1, p1=2, p2=3) + + +# keyword arguments can also be used to make parameters optional +def car(speed=100): # if no speed is given, 100 is defaulted + print("Car speed:", speed) + + +car(speed=150) # prints "Car speed: 150" +car() # prints "Car speed: 100" + + +# *args can take in an unknown number of regular parameters +def function_name(param1, *args): + print(param1) # prints "p1" + print(args) # prints (1, 2, 3, 4) + + +function_name("p1", 1, 2, 3, 4) + + +# **kwargs can take in an unknown number of keyword parameters +def function_name(param1, **kwargs): + print(param1) # prints "p1" + print(kwargs) # prints {"a":1, "b":2, "c":3} + + +function_name("p1", a=1, b=2, c=3) diff --git a/2_intermediate/chapter11/examples/return.py b/2_intermediate/chapter11/examples/return.py new file mode 100644 index 00000000..2f50f2b1 --- /dev/null +++ b/2_intermediate/chapter11/examples/return.py @@ -0,0 +1,14 @@ +# The return statement "hands back" a value +# to where the function itself was called + + +def average(numbers): + # returns the average of a given list + return sum(numbers) / len(numbers) + + +numbers = [1, 2, 3] + +# assigns average of "numbers" to "avg" and prints it +avg = average(numbers) +print(avg) diff --git a/2_intermediate/chapter11/practice/case.py b/2_intermediate/chapter11/practice/case.py new file mode 100644 index 00000000..e5f9622d --- /dev/null +++ b/2_intermediate/chapter11/practice/case.py @@ -0,0 +1,24 @@ +""" +Case + +Display the string "Apple" in the following formats: +1) normally +2) all uppercase +3) all lowercase + +Display the string "mRoWiE" in the same 3 formats. + +Ask the user to input a sentence, and display this +input in the same 3 formats. + +Do this in AT MOST 8 lines of code. +By the end of the program, 9 lines should have been +displayed (3 formats for each of the 3 strings). + +Example of the 3 formats for one string: +Apple +APPLE +apple +""" + +# Insert your code here. diff --git a/2_intermediate/chapter11/practice/cashier_job.py b/2_intermediate/chapter11/practice/cashier_job.py new file mode 100644 index 00000000..c1e5dfa4 --- /dev/null +++ b/2_intermediate/chapter11/practice/cashier_job.py @@ -0,0 +1,12 @@ +# Cashier Job +# Write a function called calculate_total +# that will take the number of pennies, nickels, dimes, +# quarters, and discount rate (i.e. 15 for 15% discount). +# Return the total amount of money after discount. +# +# Print what is returned by the function after it is run with 97 pennies, +# 13 nickels, 18 dimes, 54 quarters, and 20% discount. +# Print what is returned by the function after it is run with 32 pennies, +# 19 nickels, 22 dimes, 71 quarters, and 51% discount. + +# write code here diff --git a/2_intermediate/chapter11/practice/count_magical.py b/2_intermediate/chapter11/practice/count_magical.py new file mode 100644 index 00000000..114698b2 --- /dev/null +++ b/2_intermediate/chapter11/practice/count_magical.py @@ -0,0 +1,13 @@ +# Write a function called count_magical +# that returns the number of even numbers +# in a given list. In the function, +# if the number of evens is greater than +# half of the length of the list, print "Magical" +# Else, print "Not Magical" +# +# Write a function called main which tests +# the count_magical function on at least +# 3 different lists of integers. Use the main +# function to test count_magical by calling main(). + +# write code here diff --git a/2_intermediate/chapter11/practice/number_mystery_1.py b/2_intermediate/chapter11/practice/number_mystery_1.py new file mode 100644 index 00000000..5b01f798 --- /dev/null +++ b/2_intermediate/chapter11/practice/number_mystery_1.py @@ -0,0 +1,12 @@ +# Number Mystery 1 +# Write a function called num_mystery that takes in 3 integers. +# The function should calculate the sum of the 3 integers and +# the difference between the largest integer and the smallest integer. +# The function should return the product of these two integers you calculated. +# +# Hint: You may find it useful to use the max() and min() functions. +# +# Use the num_mystery function on 1, 2, 3 and print the result. +# Use the num_mystery function on 5, 13, 7 and print the result. + +# write code here diff --git a/2_intermediate/chapter11/practice/nutrition_facts.py b/2_intermediate/chapter11/practice/nutrition_facts.py new file mode 100644 index 00000000..cfb4c4e8 --- /dev/null +++ b/2_intermediate/chapter11/practice/nutrition_facts.py @@ -0,0 +1,75 @@ +""" +Write a function that provides the nutrition facts of an item +within the provided dictionary 'nutrition_facts'. It should +provide the calories by default. It should accept the other +keys within the item's dictionary as keyword arguments. Use **. +If that keyword argument is True, then print out the value +stored by the key in addition to the default string that +says the number of calories. If the user entered in an +invalid specific, it should tell the user about this. If the +user entered in an invalid food, it should ignore the user +completely. + +---Example 1--- +parameters: "lays potato chips", allergens=True + +output: +"Lays potato chips have/has 220 calories" +"allergens" : ["processed on equipment that also processes peanuts", +"contains milk ingredients"] + +---Example 2--- +parameters: "lays potato chips" + +output: +"Lays potato chips have/has 220 calories" + +---Example 3--- +parameters: "lays potato chips", main_ingredients= True + +output: +"Lays potato chips have/has 220 calories" +"main_ingredients" : ["potato", "salt", "canola oil"] +""" + +nutrition_facts = { + "lays potato chips": { + "item": "Lays potato chips", + "calories": 220, + "all_ingredients": [ + "potato", + "salt", + "canola oil", + "msg", + "yeast extract", + "onion extract", + "milk protein concentrate", + "sour cream", + "xantham gum", + "maltodextrin", + "sunflower oil", + ], + "main_ingredients": ["potato", "salt", "canola oil"], + "description": "Sour Cream and Onion Flavor", + "allergens": [ + "processed on equipment that also processes peanuts", + "contains milk ingredients", + ], + }, + "nutella": { + "item": "Nutella", + "calories": 200, + "all_ingredients": [ + "sugar", + "palm oil", + "hazelnuts", + "skim milk", + "cocoa", + "lecithin", + "vanillin (artificial flavor)", + ], + "main_ingredients": ["sugar", "palm oil", "hazelnuts"], + "description": "Hazelnut spread with cocoa", + "allergens": ["Contains Tree Nuts", "Contains milk", "Contains soy"], + }, +} diff --git a/2_intermediate/chapter11/practice/product.py b/2_intermediate/chapter11/practice/product.py new file mode 100644 index 00000000..8e8ee4d7 --- /dev/null +++ b/2_intermediate/chapter11/practice/product.py @@ -0,0 +1,16 @@ +""" +Product + +Write a function that takes a list +of numbers as input and returns +the product of all the numbers in +the list. + +Use it to print the products of the +following sets of numbers: +-1, 5, 3, 2, 8 +2.5, 3, 0 +4, 3, 7, 10 +""" + +# Insert your code here. diff --git a/2_intermediate/chapter11/practice/rect.py b/2_intermediate/chapter11/practice/rect.py new file mode 100644 index 00000000..78461ada --- /dev/null +++ b/2_intermediate/chapter11/practice/rect.py @@ -0,0 +1,30 @@ +""" +Rect + +Write a function that takes in 2 integer +parameters: length, and width. + +The function should print out a rectangle of +asterisks (*) with that length and width. + +Example, if the length is 5 and the width is 3, +the function should print: +***** +***** +***** + +Useful information: +1) print("a", end="") + removes the newline from the end of the print statement. +2) print("a" * 5) displays "aaaaa". + +Use the function to display rectangles with +the following dimensions (with a linebreak +between each one): +2x6 +7x4 +3x5 + +""" + +# Insert your code here. diff --git a/2_intermediate/chapter11/practice/remove_duplicates.py b/2_intermediate/chapter11/practice/remove_duplicates.py new file mode 100644 index 00000000..54a7f5a3 --- /dev/null +++ b/2_intermediate/chapter11/practice/remove_duplicates.py @@ -0,0 +1,15 @@ +# Write a function called remove_duplicates +# The sole parameter of the function should be a list +# The function should look through a list, +# Find all duplicate elements, and remove them +# Sort the resulting list +# YOU MAY NOT USE THE set() function IN PYTHON. +# Hint: To sort a list, use sorted(list) +# Another hint: Use dict.removekeys(list) +# To take the elements from a list, +# and convert them to keys in a dictionary + +# Example: array = [1,1,2,5,4,6,12,3,4,6] +# Result should print [1,2,3,4,5,6,12] + +# Write code here diff --git a/2_intermediate/chapter11/practice/shopping.py b/2_intermediate/chapter11/practice/shopping.py new file mode 100644 index 00000000..9f3635e4 --- /dev/null +++ b/2_intermediate/chapter11/practice/shopping.py @@ -0,0 +1,22 @@ +""" +Code a function named shopping that will print the number +of items that a customer would like to buy. It will take in +an unknown number of parameters, each representing +one item. After that, the function will ask the customer, +"Are you sure you would like to buy" and stating the number +of items the customer wants to purchase.Your function does +not need to respond to a yes/no answer from the user; +it just needs to print the output (the question). + + +===Example 1=== +# Parameters: "soap", "brush", "comb" +# Output: "Are you sure you would like to buy 3 items?" + +===Example 2=== +# Parameters: "lotion", "shoes", "pencil", "crayon" +# Output: "Are you sure you would like to buy 4 items?" +""" + + +# Insert your code here diff --git a/2_intermediate/chapter11/solutions/case.py b/2_intermediate/chapter11/solutions/case.py new file mode 100644 index 00000000..0fe9c1f5 --- /dev/null +++ b/2_intermediate/chapter11/solutions/case.py @@ -0,0 +1,34 @@ +""" +Case + +Display the string "Apple" in the following formats: +1) normally +2) all uppercase +3) all lowercase + +Display the string "mRoWiE" in the same 3 formats. + +Ask the user to input a sentence, and display this +input in the same 3 formats. + +Do this in AT MOST 8 lines of code. +By the end of the program, 9 lines should have been +displayed (3 formats for each of the 3 strings). + +Example of the 3 formats for one string: +Apple +APPLE +apple +""" + + +# Define a function that prints the 3 formats. +def display(str): + print(str) + print(str.upper()) + print(str.lower()) + + +display("Apple") +display("mRoWiE") +display(input("Enter a sentence: ")) diff --git a/2_intermediate/chapter11/solutions/cashier_job.py b/2_intermediate/chapter11/solutions/cashier_job.py new file mode 100644 index 00000000..24cb95ab --- /dev/null +++ b/2_intermediate/chapter11/solutions/cashier_job.py @@ -0,0 +1,25 @@ +# Cashier Job +# Write a function called calculate_total +# that will take the number of pennies, nickels, dimes, +# quarters, and discount rate (i.e. 15 for 15% discount). +# Return the total amount of money after discount. +# +# Print what is returned by the function after it is run with 97 pennies, +# 13 nickels, 18 dimes, 54 quarters, and 20% discount. +# Print what is returned by the function after it is run with 32 pennies, +# 19 nickels, 22 dimes, 71 quarters, and 51% discount. + + +def calculate_total(penny, nickel, dime, quarter, discount): + before_discount = ( + 0.01 * penny + 0.05 * nickel + 0.1 * dime + 0.25 * quarter + ) + discount_multiplier = 1 - discount * 0.01 + + # Round to 2 decimals since it is money + return round(before_discount * discount_multiplier, 2) + + +print(calculate_total(97, 13, 18, 54, 20)) + +print(calculate_total(32, 19, 22, 71, 51)) diff --git a/2_intermediate/chapter11/solutions/count_magical.py b/2_intermediate/chapter11/solutions/count_magical.py new file mode 100644 index 00000000..b9989882 --- /dev/null +++ b/2_intermediate/chapter11/solutions/count_magical.py @@ -0,0 +1,38 @@ +# Write a function called count_magical +# that returns the number of even numbers +# in a given list. In the function, +# if the number of evens is greater than +# half of the length of the list, print "Magical" +# Else, print "Not Magical" +# +# Write a function called main which tests +# the count_magical function on at least +# 3 different lists of integers. Use the main +# function to test count_magical by calling main(). + + +def count_magical(my_list): + number_of_evens = 0 + for n in my_list: + if n % 2 == 0: + number_of_evens += 1 + + if number_of_evens > len(my_list) / 2: + print("Magical") + else: + print("Not Magical") + + return number_of_evens + + +def main(): + list_1 = [1, 2, 3, 4, 5, 6] # not magical, 3 evens + list_2 = [0, 35, 1, 35, 2, 4] # not magical, 3 evens + list_3 = [10, 20, 12, 3, -9] # magical, 3 evens + + print("Number of evens in list 1:", count_magical(list_1)) + print("Number of evens in list 2:", count_magical(list_2)) + print("Number of evens in list 3:", count_magical(list_3)) + + +main() diff --git a/2_intermediate/chapter11/solutions/number_mystery_1.py b/2_intermediate/chapter11/solutions/number_mystery_1.py new file mode 100644 index 00000000..5ec847dc --- /dev/null +++ b/2_intermediate/chapter11/solutions/number_mystery_1.py @@ -0,0 +1,27 @@ +# Number Mystery 1 +# Write a function called num_mystery that takes in 3 integers. +# The function should calculate the sum of the 3 integers and +# the difference between the largest integer and the smallest integer. +# The function should return the product of these two integers you calculated. +# +# Hint: You may find it useful to use the max() and min() functions. +# +# Use the num_mystery function on 1, 2, 3 and print the result. +# Use the num_mystery function on 5, 13, 7 and print the result. + + +def num_mystery(first_int, second_int, third_int): + # calculate the sum of the 3 numbers + sum_of_three = first_int + second_int + third_int + + # calculate the difference between the max and min + largest = max(first_int, second_int, third_int) + smallest = min(first_int, second_int, third_int) + difference = largest - smallest + + # return the product + return sum_of_three * difference + + +print(num_mystery(1, 2, 3)) # prints 12 +print(num_mystery(5, 13, 7)) # prints 200 diff --git a/2_intermediate/chapter11/solutions/nutrition_facts.py b/2_intermediate/chapter11/solutions/nutrition_facts.py new file mode 100644 index 00000000..b8b52605 --- /dev/null +++ b/2_intermediate/chapter11/solutions/nutrition_facts.py @@ -0,0 +1,97 @@ +""" +Write a function that provides the nutrition facts of an item +within the provided dictionary 'nutrition_facts'. It should +provide the calories by default. It should accept the other +keys within the item's dictionary as keyword arguments. Use **. +If that keyword argument is True, then print out the value +stored by the key in addition to the default string that +says the number of calories. If the user entered in an +invalid specific, it should tell the user about this. If the +user entered in an invalid food, it should ignore the user +completely. + +---Example 1--- +parameters: "lays potato chips", allergens=True + +output: +"Lays potato chips have/has 220 calories" +"allergens": ["processed on equipment that also processes peanuts", +"contains milk ingredients"] + +---Example 2--- +parameters: "lays potato chips" + +output: +"Lays potato chips have/has 220 calories" + +---Example 3--- +parameters: "lays potato chips", main_ingredients= True + +output: +"Lays potato chips have/has 220 calories" +"main_ingredients" : ["potato", "salt", "canola oil"] +""" + +nutrition_facts = { + "lays potato chips": { + "item": "Lays potato chips", + "calories": 220, + "all_ingredients": [ + "potato", + "salt", + "canola oil", + "msg", + "yeast extract", + "onion extract", + "milk protein concentrate", + "sour cream", + "xantham gum", + "maltodextrin", + "sunflower oil", + ], + "main_ingredients": ["potato", "salt", "canola oil"], + "description": "Sour Cream and Onion Flavor", + "allergens": [ + "processed on equipment that also processes peanuts", + "contains milk ingredients", + ], + }, + "nutella": { + "item": "Nutella", + "calories": 200, + "all_ingredients": [ + "sugar", + "palm oil", + "hazelnuts", + "skim milk", + "cocoa", + "lecithin", + "vanillin (artificial flavor)", + ], + "main_ingredients": ["sugar", "palm oil", "hazelnuts"], + "description": "Hazelnut spread with cocoa", + "allergens": ["Contains Tree Nuts", "Contains milk", "Contains soy"], + }, +} + + +def food_info(item, **specifics): + item = item.lower() + if item in nutrition_facts: + print( + nutrition_facts[item]["item"], + "have/has", + nutrition_facts[item]["calories"], + "calories", + ) + for specific in specifics: + if specific in nutrition_facts[item] and specifics[specific] is True: + print(f"{specific} : {nutrition_facts[item][specific]}") + else: + print( + f"{specific} isn't a valid specific about the nutrition facts" + ) + + +food_info("lays potato chips", allergens=True) +food_info("nutella", allergens=True) diff --git a/2_intermediate/chapter11/solutions/product.py b/2_intermediate/chapter11/solutions/product.py new file mode 100644 index 00000000..3318d52e --- /dev/null +++ b/2_intermediate/chapter11/solutions/product.py @@ -0,0 +1,29 @@ +""" +Product + +Write a function that takes a list +of numbers as input and returns +the product of all the numbers in +the list. + +Use it to print the products of the +following sets of numbers: +-1, 5, 3, 2, 8 +2.5, 3, 0 +4, 3, 7, 10 +""" + + +# Define a product() function with a list parameter. +def product(list): + product = 1 + for i in list: + product *= i + return product + + +# Use the function to display products, where +# each set of numbers is given as a list. +print(product([-1, 5, 3, 2, 8])) +print(product([2.5, 3, 0])) +print(product([4, 3, 7, 10])) diff --git a/2_intermediate/chapter11/solutions/rect.py b/2_intermediate/chapter11/solutions/rect.py new file mode 100644 index 00000000..41f48d5e --- /dev/null +++ b/2_intermediate/chapter11/solutions/rect.py @@ -0,0 +1,41 @@ +""" +Rect + +Write a function that takes in 2 integer +parameters: length, and width. + +The function should print out a rectangle of +asterisks (*) with that length and width. + +Example, if the length is 5 and the width is 3, +the function should print: +***** +***** +***** + +Useful information: +1) print("a", end="") + removes the newline from the end of the print statement. +2) print("a" * 5) displays "aaaaa". + +Use the function to display rectangles with +the following dimensions (with a linebreak +between each one): +2x6 +7x4 +3x5 + +""" + + +# Define a function with length and width parameters. +def draw_rect(length, width): + for row in range(width): + print("*" * length) + print() + + +# Use the function to draw rectangles of various sizes. +draw_rect(2, 6) +draw_rect(7, 4) +draw_rect(3, 5) diff --git a/2_intermediate/chapter11/solutions/remove_duplicates.py b/2_intermediate/chapter11/solutions/remove_duplicates.py new file mode 100644 index 00000000..3e382b27 --- /dev/null +++ b/2_intermediate/chapter11/solutions/remove_duplicates.py @@ -0,0 +1,32 @@ +# Write a function called remove_duplicates +# The sole parameter of the function should be a list +# The function should look through a list, +# Find all duplicate elements, and remove them +# Sort the resulting list +# YOU MAY NOT USE THE set() function IN PYTHON. +# Hint: To sort a list, use sorted(list) +# Another hint: Use dict.fromkeys(list) +# To take the elements from a list, +# and convert them to keys in a dictionary + +# Example: array = [1,1,2,5,4,6,12,3,4,6] +# Result should print [1,2,3,4,5,6,12] + +# Write code here + +list1 = [1, 1, 2, 5, 4, 6, 12, 3, 4, 6] # Define your list + + +# Define your Function +def remove_duplicates(array): + my_list = list(dict.fromkeys(array)) + # Converts the list into a dictionary. + # Fromkeys(array) turns each item into a key + # There cannot be multiple keys, + # So all the duplicate keys are removed + # Convert the keys back into a list + return sorted(my_list) + # Returns the sorted list of keys that are not duplicate. + + +print(remove_duplicates(list1)) # Call the function diff --git a/2_intermediate/chapter11/solutions/shopping.py b/2_intermediate/chapter11/solutions/shopping.py new file mode 100644 index 00000000..ca6526c5 --- /dev/null +++ b/2_intermediate/chapter11/solutions/shopping.py @@ -0,0 +1,27 @@ +""" +Code a function named shopping that will print the number +of items that a customer would like to buy. It will take in +an unknown number of parameters, each representing +one item. After that, the function will ask the customer, +"Are you sure you would like to buy" and stating the number +of items the customer wants to purchase. Your function does not +need to respond to a yes/no answer from the user; it just +needs to print the output (the question). + + +===Example 1=== +# Parameters: "soap", "brush", "comb" +# Output: "Are you sure you would like to buy 3 items?" + +===Example 2=== +# Parameters: "lotion", "shoes", "pencil", "crayon" +# Output: "Are you sure you would like to buy 4 items?" +""" + + +def shopping(*args): + item_number = len(args) + print("Are you sure you would like to buy", item_number, "items?") + + +shopping("soap", "brush", "comb") diff --git a/2_intermediate/chapter12/examples/class.py b/2_intermediate/chapter12/examples/class.py new file mode 100644 index 00000000..06cbdfcf --- /dev/null +++ b/2_intermediate/chapter12/examples/class.py @@ -0,0 +1,18 @@ +class dot_example: # use 'class' keyword followed by your class' name + # classes can store functions and data; we call functions "methods" + # we call data "attributes" + + # below are dot_example's attributes + fun = True + difficult = False + + +our_example = dot_example() # instantiate the class; make sure to use () + +# would print True +print(our_example.fun) # our_example is the object, fun is the attribute + +# would print False +print( + our_example.difficult +) # our_example is the object, difficult is the attribute diff --git a/2_intermediate/chapter12/examples/inheritance.py b/2_intermediate/chapter12/examples/inheritance.py new file mode 100644 index 00000000..8e1de605 --- /dev/null +++ b/2_intermediate/chapter12/examples/inheritance.py @@ -0,0 +1,22 @@ +# Inheritance in coding is when one "child" class receives +# all of the methods and attributes of another "parent" class + + +class Test: + def __init__(self): + self.x = 0 + + +# class Derived_Test inherits from class Test +class Derived_Test(Test): + def __init__(self): + Test.__init__(self) # do Test's __init__ method + # Test's __init__ gives Derived_Test the attribute 'x' + self.y = 1 + + +b = Derived_Test() + +# Derived_Test now has an attribute "x", even though +# it originally didn't +print(b.x, b.y) diff --git a/2_intermediate/chapter12/examples/init_function.py b/2_intermediate/chapter12/examples/init_function.py new file mode 100644 index 00000000..d98dc6bc --- /dev/null +++ b/2_intermediate/chapter12/examples/init_function.py @@ -0,0 +1,22 @@ +# The __init__ function is automatically called when +# a new object is created. It is good to use this method +# when there are certain values that are required beforehand +# for the object to work properly. + + +class Tesla: + def __init__(self, maxSpeed=120, color="red"): + # the init function always needs the self keyword + # if no maxSpeed is entered, maxSpeed will default to 120 + # if no color is entered, color will default to "red" + + # set the class' attribute maxSpeed to the provided maxSpeed + self.maxSpeed = maxSpeed + + # set the class' attribute color to the provided color + self.color = color + + +p1 = Tesla(140, "blue") +print(p1.maxSpeed) # will print 140 +print(p1.color) # will print "blue" diff --git a/2_intermediate/chapter12/examples/methods.py b/2_intermediate/chapter12/examples/methods.py new file mode 100644 index 00000000..5c570652 --- /dev/null +++ b/2_intermediate/chapter12/examples/methods.py @@ -0,0 +1,12 @@ +class Tesla: + def __init__(self, maxSpeed=120, color="red"): + self.maxSpeed = maxSpeed + self.color = color + + # a method: acts just like a function, but needs the self keyword + def drive(self): + print("The car is now driving") + + +p1 = Tesla(140, "blue") +p1.drive() # will execute the drive method from class Tesla diff --git a/2_intermediate/chapter12/examples/self_word.py b/2_intermediate/chapter12/examples/self_word.py new file mode 100644 index 00000000..f09e7d70 --- /dev/null +++ b/2_intermediate/chapter12/examples/self_word.py @@ -0,0 +1,28 @@ +# The self keyword is used when you want a method or +# attribute to be for a specific object. This means that, +# down below, each Tesla object can have different maxSpeed +# and colors from each other. + + +class Tesla: + def __init__(self, maxSpeed=120, color="red"): + self.maxSpeed = maxSpeed + self.color = color + + def change(self, c): + self.color = c + + +p1 = Tesla(140, "blue") +p2 = Tesla(100, "blue") + + +# Notice how, when we use the self keyword, each object can +# have different attributes even though they are from the +# same class. + +p1.change("green") +print(p1.color) # prints "green" + +p2.change("yellow") +print(p2.color) # prints "yellow" diff --git a/2_intermediate/chapter12/practice/buildings.py b/2_intermediate/chapter12/practice/buildings.py new file mode 100644 index 00000000..92169829 --- /dev/null +++ b/2_intermediate/chapter12/practice/buildings.py @@ -0,0 +1,21 @@ +# Create a class called 'building' +# It should have a build method that prints: +# "under construction..." +# "built" +# It should also have an __init__ method that runs the build method +# (basically, the __init__ method should call the build method) +# The __init__ method should also set an attribute 'built' to True + +# Create a class 'library' +# It should be a child class from 'building'. +# Its init method should run building's init method. It should also +# create an empty list called 'books' +# 'library' should also have a 'restock' method +# that asks the user for a book to buy and prints "bought %s" where +# %s is the bookname. The 'restock' method should also append the +# book's name to the library's list 'books' +# Lastly, the library class should have a method 'catalog' that prints +# all the books in the library on separate lines + +# Finally, instantiate the library class +# (you should see "under construction..." and "built" if you did it right diff --git a/2_intermediate/chapter12/practice/food_class.py b/2_intermediate/chapter12/practice/food_class.py new file mode 100644 index 00000000..868eeef3 --- /dev/null +++ b/2_intermediate/chapter12/practice/food_class.py @@ -0,0 +1,34 @@ +""" +Food + +Create a Food class with 4 instance +attributes: name. calories, grams of +protein, and grams of fat. + +It should have 2 methods: an eat method +that prints "You are eating " and the name +of the food, and a burn method that prints +"You burned [x] calories." where [x] is +the number of calories in the food. + +Create a JunkFood subclass and a Meal +subclass. Both subclasses should carry +over the attributes and methods of the +Food class. +The JunkFood subclass should have an +additional attribute for the grams of +sugar contained in the food, and the Meal +subclass should have an additional attribute +for the mg of sodium it contains. + +Create a list called snacks and fill it with at +least 3 junk foods, and create a list called +meals and fill it with at least 3 meals. +Then, use Python to show that you ate all the +foods in both lists, and burned off one meal +(pick this meal randomly). +Display the total number of calories, +grams of protein, grams of fat, grams of +sugar, and mg of sodium that you ate (the total +for all the foods in both lists). +""" diff --git a/2_intermediate/chapter12/practice/student_class.py b/2_intermediate/chapter12/practice/student_class.py new file mode 100644 index 00000000..30033517 --- /dev/null +++ b/2_intermediate/chapter12/practice/student_class.py @@ -0,0 +1,5 @@ +# Create a class called Student with instance attributes: name and age. +# The user can input the name and age. Add 2 methods to the class: +# 1. A raise_hand method which prints out the student's name followed +# by "is now raising their hand." +# 2. A grow_older method that makes the student older by 1 year. diff --git a/2_intermediate/chapter12/practice/teacher_class.py b/2_intermediate/chapter12/practice/teacher_class.py new file mode 100644 index 00000000..0c7e422c --- /dev/null +++ b/2_intermediate/chapter12/practice/teacher_class.py @@ -0,0 +1,20 @@ +# Create a class called Teacher. Add 3 instance variables to this +# class: name, age, and students (a list of Student objects). +# Add 2 Methods to the class: A display_students method that +# prints out the names of all the students, each on their own line, and +# a graduate_students method that increments the age of all of the +# teacher's Students by 1. Then it should print out all their ages. + +# Student class implemented below. Teacher class uses it. + + +class Student: + def __init__(self, name, age): + self.name = name + self.age = age + + def raise_hand(self): + print(self.name + " is now raising their hand.") + + def grow_older(self): + self.age += 1 diff --git a/2_intermediate/chapter12/solutions/buildings.py b/2_intermediate/chapter12/solutions/buildings.py new file mode 100644 index 00000000..3b345cce --- /dev/null +++ b/2_intermediate/chapter12/solutions/buildings.py @@ -0,0 +1,50 @@ +# Create a class called 'building' +# It should have a build method that prints: +# "under construction..." +# "built" +# It should also have an __init__ method that runs the build method +# (basically, the __init__ method should call the build method) +# The __init__ method should also set an attribute 'built' to True + +# Create a class 'library' +# It should be a child class from 'building'. +# Its init method should run building's init method. It should also +# create an empty list called 'books' +# 'library' should also have a 'restock' method +# that asks the user for a book to buy and prints "bought %s" where +# %s is the bookname. The 'restock' method should also append the +# book's name to the library's list 'books' +# Lastly, the library class should have a method 'catalog' that prints +# all the books in the library on separate lines + +# Finally, instantiate the library class +# (you should see "under construction..." and "built" if you did it right + + +class building: + def __init__(self): + self.build() + self.built = True + + def build(self): + print("under construction...") + print("built") + + +class library(building): + def __init__(self): + super().__init__() + self.books = [] + + def restock(self): + book = input("What book should we buy? ") + print("Bought %s" % book) + self.books.append(book) + + def catalog(self): + print("Here are our books:") + for book in self.books: + print(book) + + +oak_library = library() diff --git a/2_intermediate/chapter12/solutions/food_class.py b/2_intermediate/chapter12/solutions/food_class.py new file mode 100644 index 00000000..5500c123 --- /dev/null +++ b/2_intermediate/chapter12/solutions/food_class.py @@ -0,0 +1,101 @@ +""" +Food + +Create a Food class with 4 instance +attributes: name. calories, grams of +protein, and grams of fat. + +It should have 2 methods: an eat method +that prints "You are eating " and the name +of the food, and a burn method that prints +"You burned [x] calories." where [x] is +the number of calories in the food. + +Create a JunkFood subclass and a Meal +subclass. Both subclasses should carry +over the attributes and methods of the +Food class. +The JunkFood subclass should have an +additional attribute for the grams of +sugar contained in the food, and the Meal +subclass should have an additional attribute +for the mg of sodium it contains. + +Create a list called snacks and fill it with at +least 3 junk foods, and create a list called +meals and fill it with at least 3 meals. +Then, use Python to show that you ate all the +foods in both lists, and burned off one meal +(pick this meal randomly). +Display the total number of calories, +grams of protein, grams of fat, grams of +sugar, and mg of sodium that you ate (the total +for all the foods in both lists). +""" +import random + + +class Food: + def __init__(self, name, cals, protein, fat): + self.name = name + self.cals = cals + self.protein = protein + self.fat = fat + + def eat(self): + print("You are eating " + self.name + ".") + + def burn(self): + print("You burned %d calories." % self.cals) + + +class JunkFood(Food): + def __init__(self, name, cals, protein, fat, sugar): + super().__init__(name, cals, protein, fat) + self.sugar = sugar + + +class Meal(Food): + def __init__(self, name, cals, protein, fat, sodium): + super().__init__(name, cals, protein, fat) + self.sodium = sodium + + +snacks = [ + JunkFood("Oreo", 55, 0.5, 2.2, 4.1), + JunkFood("Brownie", 70, 2, 3, 2), + JunkFood("Chips", 160, 2, 10, 0), +] + +meals = [ + Meal("Rice and beans", 400, 10, 5, 15), + Meal("Burrito", 350, 8, 9, 100), + Meal("Pizza", 500, 20, 15, 150), +] + +cals, protein, fat, sugar, sodium = 0, 0, 0, 0, 0 + +for snack in snacks: + snack.eat() + cals += snack.cals + protein += snack.protein + fat += snack.fat + sugar += snack.sugar + +for meal in meals: + meal.eat() + cals += meal.cals + protein += meal.protein + fat += meal.fat + sodium += meal.sodium + +# Choose a random meal to burn off. +meals[random.randrange(len(meals))].burn() + +# Display totals +print("Totals eaten:") +print("Calories:", cals) +print("Protein (g):", protein) +print("Fat (g):", fat) +print("Sugar (g):", sugar) +print("Sodium (mg):", sodium) diff --git a/2_intermediate/chapter12/solutions/student_class.py b/2_intermediate/chapter12/solutions/student_class.py new file mode 100644 index 00000000..fee7f937 --- /dev/null +++ b/2_intermediate/chapter12/solutions/student_class.py @@ -0,0 +1,17 @@ +# Create a class called Student with instance attributes: name and age. +# The user can input the name and age. Add 2 methods to the class: +# 1. A raise_hand method which prints out the student's name followed +# by "is now raising their hand." +# 2. A grow_older method that makes the student older by 1 year. + + +class Student: + def __init__(self, name, age): + self.name = name + self.age = age + + def raise_hand(self): + print(self.name + " is now raising their hand.") + + def grow_older(self): + self.age += 1 diff --git a/2_intermediate/chapter12/solutions/teacher_class.py b/2_intermediate/chapter12/solutions/teacher_class.py new file mode 100644 index 00000000..b774f7e5 --- /dev/null +++ b/2_intermediate/chapter12/solutions/teacher_class.py @@ -0,0 +1,36 @@ +# Create a class called Teacher. Add 3 instance variables to this +# class: name, age, and students (a list of Student objects). +# Add 2 Methods to the class: A display_students method that +# prints out the names of all the students, each on their own line, and +# a graduate_students method that increments the age of all of the +# teacher's Students by 1. Then it should print out all their ages. + +# Student class implemented below. Teacher class uses it. + + +class Teacher: + def __init__(self, name, age, students): + self.name = name + self.age = age + self.students = students + + def display_students(self): + for student in self.students: + print(student.name) + + def graduate_students(self): + for student in self.students: + student.grow_older() + print(student.age) + + +class Student: + def __init__(self, name, age): + self.name = name + self.age = age + + def raise_hand(self): + print(self.name + " is now raising their hand.") + + def grow_older(self): + self.age += 1 diff --git a/2_intermediate/chapter13/examples/bankAccount.py b/2_intermediate/chapter13/examples/bankAccount.py new file mode 100644 index 00000000..5c6248ca --- /dev/null +++ b/2_intermediate/chapter13/examples/bankAccount.py @@ -0,0 +1,45 @@ +class bankAccount: + def __init__(self, owner: str, balance: float): + self.owner = owner + self.balance = balance + + def __getitem__(self, item: str): + if item == "owner": + return self.owner + elif item == "balance": + return self.balance + else: + # if the attribute isn't a valid attribute, you should + # raise an AttributeError + raise AttributeError + + def __setitem__(self, item: str, value): + if item == "owner": + self.owner = value + elif item == "balance": + self.balance = value + else: + # if the attribute isn't a valid attribute, you should + # raise an AttributeError + raise AttributeError + + def __bool__(self): + """ + If we wanted the bank account to return True if the person + is not bankrupt and False if they are bankrupt, we could do: + """ + return self.balance > 0 + + +account = bankAccount("John", 100) +print(account["owner"]) +print(account["balance"]) +account["balance"] = 200 +print(account["balance"]) +account["owner"] = "John Jr." +print(account["owner"]) + + +print(bool(account)) +if account: + print("not bankrupt") diff --git a/2_intermediate/chapter13/examples/coordinateGrid.py b/2_intermediate/chapter13/examples/coordinateGrid.py new file mode 100644 index 00000000..6f0d00b4 --- /dev/null +++ b/2_intermediate/chapter13/examples/coordinateGrid.py @@ -0,0 +1,52 @@ +class coordinateGrid: + def __init__( + self, + x_start: int = 0, + x_end: int = 10, + y_start: int = 0, + y_end: int = 10, + ): + """ + Creates a list of coordinates similar to a coordinate grid. + Each item in self.coordinates is a list representing one row in a + coordinate grid. + each item within that row is a point (tuple) of x, y + ex: coordinateGrid(0, 1, -1, 1)'s coordinates would be + [ + [(0, 1), (1, 1)], + [(0, 0), (1, 0)], + [(0, -1), (1, -1)] + ] + Arguments: + x_start, x_end, y_start, and y_end are all inclusive + """ + self.coordinates = [ + [(x, y) for x in range(x_start, x_end + 1)] + for y in range(y_end, y_start - 1, -1) + ] + + def __contains__(self, item: tuple) -> bool: + """ + Checks to see if the provided tuple (or list) + of length 2 (the tuple/list represents a point of x,y) + is in self.coordinates. + """ + return True in [item in row for row in self.coordinates] + + def __len__(self) -> bool: + """ + In this case, we're saying that the length of the coordinateGrid + is its area. Thus, we do height * width + height = len(self.coordinates) and + width = len(self.coordinates[0]) (or any row's length) + """ + return len(self.coordinates) * len(self.coordinates[0]) + + +grid1 = coordinateGrid(-1, 1, -1, 1) +grid2 = coordinateGrid(-10, 10, -10, 10) +point1 = (10, 10) +print(point1 in grid1) +print(point1 in grid2) +print(len(grid1)) +print(len(grid2)) diff --git a/2_intermediate/chapter13/examples/vector.py b/2_intermediate/chapter13/examples/vector.py new file mode 100644 index 00000000..6038aad3 --- /dev/null +++ b/2_intermediate/chapter13/examples/vector.py @@ -0,0 +1,26 @@ +class Vector: + """ + Constructor + + self: a reference to the object we are creating + vals: a list of integers which are the contents of our vector + """ + + def __init__(self, vals): + self.vals = ( + vals # We're using the keyword self to create a field/property + ) + print("Assigned values ", vals, " to vector.") + + """ + String Function + + Converts the object to a string in readable format for programmers + """ + + def __str__(self): + return str(self.vals) # Returns the contents of the vector + + +vec = Vector([2, 3, 2]) +print(str(vec)) # [2, 3, 2] diff --git a/2_intermediate/chapter13/examples/vector2.py b/2_intermediate/chapter13/examples/vector2.py new file mode 100644 index 00000000..224ccd01 --- /dev/null +++ b/2_intermediate/chapter13/examples/vector2.py @@ -0,0 +1,59 @@ +class Vector: + """ + Constructor + + self: a reference to the object we are creating + vals: a list of integers which are the contents of our vector + """ + + def __init__(self, vals): + self.vals = vals + # print("Assigned values ", vals, " to vector.") + + """ + String Function + + Converts the object to a string in readable format for programmers + """ + + def __str__(self): + return str(self.vals) + + """ + Elementwise power: raises each element in our vector to the given power + """ + + def __pow__(self, power): + return Vector([i**power for i in self.vals]) + + """ + Addition: adds each element to corresponding element in other vector + """ + + def __add__(self, vec): + return Vector( + [self.vals[i] + vec.vals[i] for i in range(len(self.vals))] + ) + + """ + Multiplies each element in the vector by a specified constant + """ + + def __mul__(self, constant): + return Vector([self.vals[i] * constant for i in range(len(self.vals))]) + + """ + Elementwise subtraction: does same as addition, just subtraction instead + """ + + def __sub__(self, vec): + return self + (vec * (-1)) + + +vec = Vector([2, 3, 2]) +otherVec = Vector([3, 4, 5]) +print(str(vec)) # [2, 3, 2] +print(vec**2) # [4, 9, 4] +print(vec - otherVec) # [-1, -1, -3] +print(vec + otherVec) # [5, 7, 7] +print(vec * 5) # [10, 15, 10] diff --git a/2_intermediate/chapter13/examples/vector3.py b/2_intermediate/chapter13/examples/vector3.py new file mode 100644 index 00000000..25307216 --- /dev/null +++ b/2_intermediate/chapter13/examples/vector3.py @@ -0,0 +1,68 @@ +class Vector: + """ + Constructor + + self: a reference to the object we are creating + vals: a list of integers which are the contents of our vector + """ + + def __init__(self, vals): + self.vals = vals + # print("Assigned values ", vals, " to vector.") + + """ + String Function + + Converts the object to a string in readable format for programmers + """ + + def __str__(self): + return str(self.vals) + + def __pow__(self, power): + return Vector([i**power for i in self.vals]) + + # Calculates Euclidean norm + def norm(self): + return sum((self**2).vals) ** 0.5 + + # __lt__: implements the less than operator (<) + def __lt__(self, other): + return self.norm() < other.norm() + + # __gt__: implements the greater than operator (>) + def __gt__(self, other): + return self.norm() > other.norm() + + # __le__: implements the less than equal to operator (<=) + def __le__(self, other): + return self.norm() <= other.norm() + + # __ge__: implements the greater than equal to operator (>=) + def __ge__(self, other): + return self.norm() >= other.norm() + + # __eq__: implements the equals operator (==) + def __eq__(self, other): + return self.norm() == other.norm() + + # __ne__:implements the not equals operator (!=) + def __ne__(self, other): + return self.norm() != other.norm() + + +vec = Vector([2, 3, 2]) +vec2 = Vector([3, 4, 5]) +print(vec < vec2) # True +print(vec > vec2) # False + +print(vec <= vec2) # True +print(vec >= vec2) # False +print(vec <= vec) # True +print(vec >= vec) # True + +print(vec == vec2) # False +print(vec == vec) # True + +print(vec != vec2) # True +print(vec != vec) # False diff --git a/2_intermediate/chapter13/practice/car.py b/2_intermediate/chapter13/practice/car.py new file mode 100644 index 00000000..dd972e70 --- /dev/null +++ b/2_intermediate/chapter13/practice/car.py @@ -0,0 +1,31 @@ +""" +A new car is said to devalue 20% in the first year. Assuming that +this trend continues and that mileage divided by 100 is all you +subtract from this adjusted price, make a class "car" that has at +least the attributes "year, original price (aka og price), and +mileage." Also, follow these guidelines. + +--When using str() on a car, it should return the year, original + price, mileage, and adjusted price. +--When adding, it should add the value to its mileage before + adjusting the adjusted price. +--When multiplying, it should multiply the mileage by the value + before adjusting the adjusted price +--(While subtracting or dividing mileage on a car to sell it is + totally unethical,) When subtracting or dividing, it should + subtract the value from its mileage or divide its mileage by + the value before adjusting the adjusted price. +--When checking gt(which means greater than), lt, ge, le, ne, + and eq, it should compare the price with the other value. +--You should be able to compare cars (prices) but not add cars + together + +If you need help with modeling the equation for the adjusted price, +this may help + +self.adjustedprice=self.ogprice * (0.8**(2020-self.year))) +self.adjustedprice=round((self.adjustedprice),2)-self.mileage/100 + +""" + +# write your code below diff --git a/2_intermediate/chapter13/practice/filler b/2_intermediate/chapter13/practice/filler new file mode 100644 index 00000000..17725cff --- /dev/null +++ b/2_intermediate/chapter13/practice/filler @@ -0,0 +1 @@ +#This is filler. Remove later diff --git a/2_intermediate/chapter13/practice/lexicographical_vector.py b/2_intermediate/chapter13/practice/lexicographical_vector.py new file mode 100644 index 00000000..43a8f202 --- /dev/null +++ b/2_intermediate/chapter13/practice/lexicographical_vector.py @@ -0,0 +1,12 @@ +""" +Reimplement the __lt__ and __gt__ in the given Vector +class(the one in this section) so that we are comparing +the vector's contents based on lexicographical ordering. +Think of lexicographical ordering as how you arrange words +in a dictionary. For instance, by lexicographical ordering, +'a' < 'ab', 'ab' < 'ad', 'bcd' > 'a'. It works analogously +for numbers, but instead, each character has been substituted +by a number. +""" + +# write your code below diff --git a/2_intermediate/chapter13/practice/line.py b/2_intermediate/chapter13/practice/line.py new file mode 100644 index 00000000..6bfca643 --- /dev/null +++ b/2_intermediate/chapter13/practice/line.py @@ -0,0 +1,9 @@ +""" +Write a class called Line which will take the arguments slope +and intercept in its constructor. When we print the class, +the __str__ method should return a string with the line expressed +in the form "y=mx+b" where m and b are the slope and intercept +respectively. +""" + +# write your code below diff --git a/2_intermediate/chapter13/practice/matrix.py b/2_intermediate/chapter13/practice/matrix.py new file mode 100644 index 00000000..91ba119a --- /dev/null +++ b/2_intermediate/chapter13/practice/matrix.py @@ -0,0 +1,10 @@ +""" +Build a class called Matrix which will take a list of lists +(containing integers) and store it as a field. Add an assertion +using the keyword assert to ensure that the list of lists is +rectangular (i.e. assert len(list_0) = len(list_i) for i in range(n)) +You should also implement a __str__ method so that we can print +the contents of the matrix using print without having to access its field. +""" + +# write your code below diff --git a/2_intermediate/chapter13/practice/matrix_add_subtract.py b/2_intermediate/chapter13/practice/matrix_add_subtract.py new file mode 100644 index 00000000..8aa83fe2 --- /dev/null +++ b/2_intermediate/chapter13/practice/matrix_add_subtract.py @@ -0,0 +1,25 @@ +""" +Write a modified version of the Matrix class(that was defined in +one of the example problems in this section) with an __add__ +operation as well as a __sub__ operation. It should add matrices, +assuming that they will be of the same length. Also, the unmodified +Matrix class code will be given. +""" + +""" +This is the unmodified Matrix class code. + +class Matrix: + def __init__(self,thelist: list): + self.thelist=thelist + for items in range(len(self.thelist)): + assert type(self.thelist[items])==list + assert len(self.thelist[0]) == len(self.thelist[items]) + for things in range(len(self.thelist[items])): + assert type(self.thelist[items][things])==int + + def __str__(self): + return str(self.thelist) +""" + +# write your code below diff --git a/2_intermediate/chapter13/practice/matrix_frobenius_norm.py b/2_intermediate/chapter13/practice/matrix_frobenius_norm.py new file mode 100644 index 00000000..f7689fd1 --- /dev/null +++ b/2_intermediate/chapter13/practice/matrix_frobenius_norm.py @@ -0,0 +1,26 @@ +""" +Write a modified version of the Matrix class(that was defined in +one of the example problems in this section) so that the __str__ +method instead returns a string containing a single number: the +matrix's Frobenius norm. The formula for the Frobenius norm will +be the square root of the sum of all the elements squared in the +matrix. Also, the unmodified Matrix class code will be given. +""" + +""" +This is the unmodified Matrix class code. + +class Matrix: + def __init__(self,thelist: list): + self.thelist=thelist + for items in range(len(self.thelist)): + assert type(self.thelist[items])==list + assert len(self.thelist[0]) == len(self.thelist[items]) + for things in range(len(self.thelist[items])): + assert type(self.thelist[items][things])==int + + def __str__(self): + return str(self.thelist) +""" + +# write your code below diff --git a/2_intermediate/chapter13/practice/matrix_less_greater.py b/2_intermediate/chapter13/practice/matrix_less_greater.py new file mode 100644 index 00000000..1d928dae --- /dev/null +++ b/2_intermediate/chapter13/practice/matrix_less_greater.py @@ -0,0 +1,25 @@ +""" +Implement the less than and greater than operators for +the Matrix class(from a previous example problem) so that +we compare them based on their Frobenius norms which we +have implemented in the earlier section as an exercise. +Also, the unmodified Matrix class code will be given. +""" + +""" +This is the unmodified Matrix class code. + +class Matrix: + def __init__(self,thelist: list): + self.thelist=thelist + for items in range(len(self.thelist)): + assert type(self.thelist[items])==list + assert len(self.thelist[0]) == len(self.thelist[items]) + for things in range(len(self.thelist[items])): + assert type(self.thelist[items][things])==int + + def __str__(self): + return str(self.thelist) +""" + +# write your code below diff --git a/2_intermediate/chapter13/practice/polar_coordinates.py b/2_intermediate/chapter13/practice/polar_coordinates.py new file mode 100644 index 00000000..98e23c30 --- /dev/null +++ b/2_intermediate/chapter13/practice/polar_coordinates.py @@ -0,0 +1,10 @@ +""" +Write a class called PolarCoordinates which will take a +value called radius and angle. When we print this class, +we want the coordinates in Cartesian coordinates, or we want +you to print two values: x and y. (If you don't know the +conversion formula, x = radius * cos(angle), y = radius * sin(angle). +Use Python's built-in math library for the cosine and sine operators) +""" + +# write your code below diff --git a/2_intermediate/chapter13/practice/triangle.py b/2_intermediate/chapter13/practice/triangle.py new file mode 100644 index 00000000..61ec4f28 --- /dev/null +++ b/2_intermediate/chapter13/practice/triangle.py @@ -0,0 +1,15 @@ +""" +Write a class called Triangle which will take three tuples +(each tuple contains two integers: the x and y coordinates +of a vertex). Then, define an __add__ operation that acts as +a translation operation. Its input argument will be a tuple +of two integers that will indicate the x and y translations +that will be applied to each coordinate. (basically, add the +tuple to each coordinate of the triangle). Also, define a +vertical and horizontal transformation tool in the form +of __mul__ which will also take a tuple of two integers that +will be multiplied to the x and y coordinates of each vertex +respectively. +""" + +# write your code below diff --git a/2_intermediate/chapter13/practice/vector.py b/2_intermediate/chapter13/practice/vector.py new file mode 100644 index 00000000..c3107c1f --- /dev/null +++ b/2_intermediate/chapter13/practice/vector.py @@ -0,0 +1,10 @@ +""" +Define a Vector class so that the multiply operation is with +another Vector instead. The multiply operation should be the +inner or dot product of the two vectors. That means that each +element in the vector should be multiplied with its +corresponding element in the other vector, and then summed. +A scalar (regular number) should be returned. +""" + +# write your code below diff --git a/2_intermediate/chapter13/solutions/car.py b/2_intermediate/chapter13/solutions/car.py new file mode 100644 index 00000000..5f5c6717 --- /dev/null +++ b/2_intermediate/chapter13/solutions/car.py @@ -0,0 +1,124 @@ +""" +A new car is said to devalue 20% in the first year. Assuming that +this trend continues and that mileage divided by 100 is all you +subtract from this adjusted price, make a class "car" that has at +least the attributes "year, original price (aka og price), and +mileage." Also, follow these guidelines. + +--When using str() on a car, it should return the year, original + price, mileage, and adjusted price. +--When adding, it should add the value to its mileage before + adjusting the adjusted price. +--When multiplying, it should multiply the mileage by the value + before adjusting the adjusted price +--(While subtracting or dividing mileage on a car to sell it is + totally unethical,) When subtracting or dividing, it should + subtract the value from its mileage or divide its mileage by + the value before adjusting the adjusted price. +--When checking gt(which means greater than), lt, ge, le, ne, + and eq, it should compare the price with the other value. +--You should be able to compare cars (prices) but not add cars + together + +If you need help with modeling the equation for the adjusted price, +this may help + +self.adjustedprice=self.ogprice * (0.8**(2020-self.year))) +self.adjustedprice=round((self.adjustedprice),2)-self.mileage/100 + +""" + +# write your code below + + +class car: + def __init__(self, year, brand, ogprice, mileage): + self.year = year + self.brand = brand + self.ogprice = ogprice + self.mileage = mileage + self.adjustedprice = self.adjustprice() + + def adjustprice(self): + self.adjustedprice = float(self.ogprice * (0.8 ** (2020 - self.year))) + self.adjustedprice = ( + round((self.adjustedprice), 2) - self.mileage / 100 + ) + return self.adjustedprice + + def __str__(self): + return "This car is a {} model from {}. It was originally worth ${} and \ + has driven {} miles. It is now worth {}".format( + self.year, + self.brand, + self.ogprice, + self.mileage, + self.adjustedprice, + ) + + def __lt__(self, value): + if type(value) == car: + return self.adjustedprice < value.adjustedprice + elif type(value) != object: + return self.adjustedprice < value + + def __gt__(self, value): + if type(value) == car: + return self.adjustedprice > value.adjustedprice + elif type(value) != object: + return self.adjustedprice > value + + def __eq__(self, value): + if type(value) == car: + return self.adjustedprice == value.adjustedprice + elif type(value) != object: + return self.adjustedprice == value + + def __ne__(self, value): + if type(value) == car: + return self.adjustedprice != value.adjustedprice + elif type(value) != object: + return self.adjustedprice != value + + def __le__(self, value): + if type(value) == car: + return self.adjustedprice <= value.adjustedprice + elif type(value) != object: + return self.adjustedprice <= value + + def __ge__(self, value): + if type(value) == car: + return self.adjustedprice >= value.adjustedprice + elif type(value) != object: + return self.adjustedprice >= value + + def __add__(self, value): + if type(value) == car: + return None + elif type(value) != object: + self.mileage += value + self.adjustedprice = self.adjustprice() + + def __sub__(self, value): + if type(value) == car: + return None + elif type(value) != object: + self.mileage -= value + self.adjustedprice = self.adjustprice() + + def __truediv__(self, value): + if type(value) == car: + return None + elif type(value) != object: + self.mileage = self.mileage / value + self.adjustedprice = self.adjustprice() + + def __mul__(self, value): + if type(value) == car: + return None + elif type(value) != object: + self.mileage = self.mileage * value + self.adjustedprice = self.adjustprice() + + +Maserati = car(2009, "porsche", 30000, 14000) diff --git a/2_intermediate/chapter13/solutions/filler b/2_intermediate/chapter13/solutions/filler new file mode 100644 index 00000000..17725cff --- /dev/null +++ b/2_intermediate/chapter13/solutions/filler @@ -0,0 +1 @@ +#This is filler. Remove later diff --git a/2_intermediate/chapter13/solutions/lexicographical_vector.py b/2_intermediate/chapter13/solutions/lexicographical_vector.py new file mode 100644 index 00000000..de8faafd --- /dev/null +++ b/2_intermediate/chapter13/solutions/lexicographical_vector.py @@ -0,0 +1,72 @@ +""" +Reimplement the __lt__ and __gt__ in the given Vector +class(the one in this section) so that we are comparing +the vector's contents based on lexicographical ordering. +Think of lexicographical ordering as how you arrange words +in a dictionary. For instance, by lexicographical ordering, +'a' < 'ab', 'ab' < 'ad', 'bcd' > 'a'. It works analogously +for numbers, but instead, each character has been substituted +by a number. +""" + +# write your code below + + +class Vector: + def __init__(self, vals): + self.vals = vals + self.length = len(self.vals) + self.scalar = 0 + + def __mul__(self, vec): + ... # see above example + + def morecheck(self, vec, shorter): + for i in range(shorter.length): + if self.vals[i] > vec.vals[i]: + return True + if self.vals[i] < vec.vals[i]: + return False + + def __gt__(self, vec): + assert type(vec) == Vector + if self.length > vec.length: + a = self.morecheck(vec, vec) + if a is not None: + return a + return True # if all other values ==, self = longer/greater + if self.length < vec.length: + a = self.morecheck(vec, self) + if a is not None: + return a + return False # if all other values ==, self = shorter/smaller + if self.length == vec.length: + a = self.morecheck(vec, self) + if a is not None: + return a + return False # if all other values ==, self = equal/not greater + + def lesscheck(self, vec, shorter): + for i in range(shorter.length): + if self.vals[i] < vec.vals[i]: + return True + if self.vals[i] > vec.vals[i]: + return False + + def __lt__(self, vec): + assert type(vec) == Vector + if self.length > vec.length: + a = self.lesscheck(vec, vec) + if a is not None: + return a + return False # if all other values ==, self = longer/greater + if self.length < vec.length: + a = self.lesscheck(vec, self) + if a is not None: + return a + return True # if all other values ==, self = shorter/smaller + if self.length == vec.length: + a = self.lesscheck(vec, self) + if a is not None: + return a + return False # if all other values ==, self = equal/not less diff --git a/2_intermediate/chapter13/solutions/line.py b/2_intermediate/chapter13/solutions/line.py new file mode 100644 index 00000000..a524ff83 --- /dev/null +++ b/2_intermediate/chapter13/solutions/line.py @@ -0,0 +1,23 @@ +""" +Write a class called Line which will take the arguments slope +and intercept in its constructor. When we print the class, +the __str__ method should return a string with the line expressed +in the form "y=mx+b" where m and b are the slope and intercept +respectively. +""" + +# write your code below + + +class Line: + def __init__(self, slope, intercept): + self.slope = slope + self.intercept = intercept + + def __str__(self): + self.equation = "y={}x+{}".format(self.slope, self.intercept) + return self.equation + + +myline = Line(3, 1) +print(str(myline)) diff --git a/2_intermediate/chapter13/solutions/matrix.py b/2_intermediate/chapter13/solutions/matrix.py new file mode 100644 index 00000000..fd1ba70f --- /dev/null +++ b/2_intermediate/chapter13/solutions/matrix.py @@ -0,0 +1,27 @@ +""" +Build a class called Matrix which will take a list of lists +(containing integers) and store it as a field. Add an assertion +using the keyword assert to ensure that the list of lists is +rectangular (i.e. assert len(list_0) = len(list_i) for i in range(n)) +You should also implement a __str__ method so that we can print +the contents of the matrix using print without having to access its field. +""" + +# write your code below + + +class Matrix: + def __init__(self, thelist: list): + self.thelist = thelist + for items in range(len(self.thelist)): + assert type(self.thelist[items]) == list + assert len(self.thelist[0]) == len(self.thelist[items]) + for things in range(len(self.thelist[items])): + assert type(self.thelist[items][things]) == int + + def __str__(self): + return str(self.thelist) + + +mymatrix = Matrix([[3, 4], [7, 8], [4, 8]]) +print(str(mymatrix)) diff --git a/2_intermediate/chapter13/solutions/matrix_add_subtract.py b/2_intermediate/chapter13/solutions/matrix_add_subtract.py new file mode 100644 index 00000000..d2e24973 --- /dev/null +++ b/2_intermediate/chapter13/solutions/matrix_add_subtract.py @@ -0,0 +1,57 @@ +""" +Write a modified version of the Matrix class(that was defined in +one of the example problems in this section) with an __add__ +operation as well as a __sub__ operation. It should add matrices, +assuming that they will be of the same length. Also, the unmodified +Matrix class code will be given. +""" + +# write your code below + + +""" +This is the unmodified Matrix class code. + +class Matrix: + def __init__(self,thelist: list): + self.thelist=thelist + for items in range(len(self.thelist)): + assert type(self.thelist[items]) == list + assert len(self.thelist[0]) == len(self.thelist[items]) + for things in range(len(self.thelist[items])): + assert type(self.thelist[items][things]) == int + + def __str__(self): + return str(self.thelist) +""" + + +class Matrix: + def __init__(self, thelist: list): + self.thelist = thelist + for items in range(len(self.thelist)): + assert type(self.thelist[items]) == list + assert len(self.thelist[0]) == len(self.thelist[items]) + for things in range(len(self.thelist[items])): + assert type(self.thelist[items][things]) == int + + def __str__(self): + return str(self.thelist) + + def __add__(self, other): + assert type(other) == Matrix + for items in range(len(self.thelist)): + for things in range(len(self.thelist[items])): + self.thelist[items][things] += other.thelist[items][things] + + def __sub__(self, other): + assert type(other) == Matrix + for items in range(len(self.thelist)): + for things in range(len(self.thelist[items])): + self.thelist[items][things] -= other.thelist[items][things] + + +mymatrix = Matrix([[3, 4], [7, 8]]) +othermatrix = Matrix([[5, 6], [7, 8]]) +mymatrix - othermatrix +print(mymatrix.thelist) diff --git a/2_intermediate/chapter13/solutions/matrix_frobenius_norm.py b/2_intermediate/chapter13/solutions/matrix_frobenius_norm.py new file mode 100644 index 00000000..09be9792 --- /dev/null +++ b/2_intermediate/chapter13/solutions/matrix_frobenius_norm.py @@ -0,0 +1,53 @@ +""" +Write a modified version of the Matrix class(that was defined in +one of the example problems in this section) so that the __str__ +method instead returns a string containing a single number: the +matrix's Frobenius norm. The formula for the Frobenius norm will +be the square root of the sum of all the elements squared in the +matrix. Also, the unmodified Matrix class code will be given. +""" + +# write your code below + +import math + +""" +This is the unmodified Matrix class code. + +class Matrix: + def __init__(self,thelist: list): + self.thelist=thelist + for items in range(len(self.thelist)): + assert type(self.thelist[items]) == list + assert len(self.thelist[0]) == len(self.thelist[items]) + for things in range(len(self.thelist[items])): + assert type(self.thelist[items][things]) == int + + def __str__(self): + return str(self.thelist) +""" + + +class Matrix: + def __init__(self, thelist: list): + self.thelist = thelist + for items in range(len(self.thelist)): + assert type(self.thelist[items]) == list + assert len(self.thelist[0]) == len(self.thelist[items]) + for things in range(len(self.thelist[items])): + assert type(self.thelist[items][things]) == int + self.froebiannorm() + + def froebiannorm(self): + self.squared = 0 + for items in range(len(self.thelist)): + for things in range(len(self.thelist[items])): + self.squared += self.thelist[items][things] ** 2 + self.norm = math.sqrt(self.squared) + + def __str__(self): + return str(self.norm) + + +mymatrix = Matrix([[3, 4], [7, 8]]) +print(str(mymatrix)) diff --git a/2_intermediate/chapter13/solutions/matrix_less_greater.py b/2_intermediate/chapter13/solutions/matrix_less_greater.py new file mode 100644 index 00000000..5521a3c6 --- /dev/null +++ b/2_intermediate/chapter13/solutions/matrix_less_greater.py @@ -0,0 +1,74 @@ +""" +Implement the less than and greater than operators for +the Matrix class(from a previous example problem) so that +we compare them based on their Frobenius norms which we +have implemented in the earlier section as an exercise. +Also, the unmodified Matrix class code will be given. +""" + +# write your code below + +import math + +""" +This is the unmodified Matrix class code. + +class Matrix: + def __init__(self,thelist: list): + self.thelist=thelist + for items in range(len(self.thelist)): + assert type(self.thelist[items]) == list + assert len(self.thelist[0]) == len(self.thelist[items]) + for things in range(len(self.thelist[items])): + assert type(self.thelist[items][things]) == int + + def __str__(self): + return str(self.thelist) +""" + + +class Matrix: + def __init__(self, thelist: list): + self.thelist = thelist + self.norm = 0 + for items in range(len(self.thelist)): + assert type(self.thelist[items]) == list + assert len(self.thelist[0]) == len(self.thelist[items]) + for things in range(len(self.thelist[items])): + assert type(self.thelist[items][things]) == int + self.froebiannorm() + + def froebiannorm(self): + self.squared = 0 + for items in range(len(self.thelist)): + for things in range(len(self.thelist[items])): + self.squared += self.thelist[items][things] ** 2 + self.norm = math.sqrt(self.squared) + + def __str__(self): + return str(self.norm) + + def __add__(self, other): + assert type(other) == Matrix + for items in range(len(self.thelist)): + for things in range(len(self.thelist[items])): + self.thelist[items][things] += other.thelist[items][things] + + def __sub__(self, other): + assert type(other) == Matrix + for items in range(len(self.thelist)): + for things in range(len(self.thelist[items])): + self.thelist[items][things] -= other.thelist[items][things] + + def __lt__(self, other): + assert type(other) == Matrix + return self.norm < other.norm + + def __gt__(self, other): + assert type(other) == Matrix + return self.norm > other.norm + + +mymatrix = Matrix([[3, 4], [7, 8]]) +othermatrix = Matrix([[5, 6], [7, 8]]) +print(mymatrix > othermatrix) diff --git a/2_intermediate/chapter13/solutions/polar_coordinates.py b/2_intermediate/chapter13/solutions/polar_coordinates.py new file mode 100644 index 00000000..ff743831 --- /dev/null +++ b/2_intermediate/chapter13/solutions/polar_coordinates.py @@ -0,0 +1,27 @@ +""" +Write a class called PolarCoordinates which will take a +value called radius and angle. When we print this class, +we want the coordinates in Cartesian coordinates, or we want +you to print two values: x and y. (If you don't know the +conversion formula, x = radius * cos(angle), y = radius * sin(angle). +Use Python's built-in math library for the cosine and sine operators) +""" + +# write your code below + +import math + + +class PolarCoordinates: + def __init__(self, radius, angle): + self.radius = radius + self.angle = angle + + def __str__(self): + self.x = self.radius * math.cos(self.angle) + self.y = self.radius * math.sin(self.angle) + return "{},{}".format(self.x, self.y) + + +group = PolarCoordinates(2, math.pi) +print(str(group)) diff --git a/2_intermediate/chapter13/solutions/triangle.py b/2_intermediate/chapter13/solutions/triangle.py new file mode 100644 index 00000000..31043ab8 --- /dev/null +++ b/2_intermediate/chapter13/solutions/triangle.py @@ -0,0 +1,45 @@ +""" +Write a class called Triangle which will take three tuples +(each tuple contains two integers: the x and y coordinates +of a vertex). Then, define an __add__ operation that acts as +a translation operation. Its input argument will be a tuple +of two integers that will indicate the x and y translations +that will be applied to each coordinate. (basically, add the +tuple to each coordinate of the triangle). Also, define a +vertical and horizontal transformation tool in the form +of __mul__ which will also take a tuple of two integers that +will be multiplied to the x and y coordinates of each vertex +respectively. +""" + +# write your code below + + +class Triangle: + def __init__(self, pair1, pair2, pair3): + self.coordinatelist = [pair1, pair2, pair3] + for i in range(len(self.coordinatelist)): + assert ( + type(self.coordinatelist[i]) == tuple + and len(self.coordinatelist[i]) == 2 + ) + self.coordinatelist[i] = list(self.coordinatelist[i]) + + def __add__(self, other): + assert type(other) == tuple and len(other) == 2 + for i in range(len(self.coordinatelist)): + self.coordinatelist[i][0] += other[0] + self.coordinatelist[i][1] += other[1] + return tuple(self.coordinatelist) + + def __mul__(self, other): + assert type(other) == tuple and len(other) == 2 + for i in range(len(self.coordinatelist)): + self.coordinatelist[i][0] *= other[0] + self.coordinatelist[i][1] *= other[1] + return tuple(self.coordinatelist) + + +mytriangle = Triangle((0, 0), (1, 0), (0, 1)) +print(mytriangle + (1, 1)) +print(mytriangle * (2, 2)) diff --git a/2_intermediate/chapter13/solutions/vector.py b/2_intermediate/chapter13/solutions/vector.py new file mode 100644 index 00000000..95c90664 --- /dev/null +++ b/2_intermediate/chapter13/solutions/vector.py @@ -0,0 +1,39 @@ +""" +Define a Vector class so that the multiply operation is with +another Vector instead. The multiply operation should be the +inner or dot product of the two vectors. That means that each +element in the vector should be multiplied with its +corresponding element in the other vector, and then summed. +A scalar (regular number) should be returned. +""" + +# write your code below + + +class Vector: + def __init__(self, vals): + self.vals = vals + self.length = len(self.vals) + self.scalar = 0 + + def __mul__(self, vec): + assert type(vec) == Vector + a = 0 + if self.length >= vec.length: + for i in range(vec.length): + self.scalar += self.vals[i] * vec.vals[i] + while a + vec.length < self.length: + self.scalar += self.vals[i] + a += 1 + if self.length < vec.length: + for i in range(self.length): + self.scalar += self.vals[i] * vec.vals[i] + while (a + self.length) < vec.length: + self.scalar += self.vals[i] + a += 1 + return self.scalar + + +vector1 = Vector([2, 3, 2]) +vector2 = Vector([3, 4, 5]) +print(vector1 * vector2) # should give 28 diff --git a/2_intermediate/chapter8/practice/food.py b/2_intermediate/chapter8/practice/food.py new file mode 100644 index 00000000..7530aa12 --- /dev/null +++ b/2_intermediate/chapter8/practice/food.py @@ -0,0 +1,11 @@ +""" +Food +Start with an empty list called food. +Ask the user to either enter a fruit, a vegetable, +or a junk food item. Do this 10 times, and each time, +the question you ask should be selected randomly. + +Add each of their answers to the list, sort the list +alphabetically, and then display the final list +by printing each item on a separate line. +""" diff --git a/2_intermediate/chapter8/practice/fortune.py b/2_intermediate/chapter8/practice/fortune.py new file mode 100644 index 00000000..4fc4e49d --- /dev/null +++ b/2_intermediate/chapter8/practice/fortune.py @@ -0,0 +1,10 @@ +""" +Fortune + +Store 6 fortunes cookie messages, and display a +random one each time the user runs the program. + +Assume that you DON'T know how many messages are in the list. + +(Hint: You’re randomly choosing the index to access.) +""" diff --git a/2_intermediate/chapter8/solutions/food.py b/2_intermediate/chapter8/solutions/food.py new file mode 100644 index 00000000..9416d970 --- /dev/null +++ b/2_intermediate/chapter8/solutions/food.py @@ -0,0 +1,30 @@ +""" +Food +Start with an empty list called food. +Ask the user to either enter a fruit, a vegetable, +or a junk food item. Do this 10 times, and each time, +the question you ask should be selected randomly. + +Add each of their answers to the list, sort the list +alphabetically, and then display the final list +by printing each item on a separate line. +""" +import random + +foods = [] +prompts = [ + "Enter a fruit: ", + "Enter a vegetable: ", + "Enter a junk food item: ", +] + +# Choose a random prompt and collect input. +for i in range(10): + i = random.randrange(len(prompts)) + item = input(prompts[i]) + foods.append(item) +print() + +foods.sort() +for food in foods: + print(food) diff --git a/2_intermediate/chapter8/solutions/fortune.py b/2_intermediate/chapter8/solutions/fortune.py new file mode 100644 index 00000000..417fd1c1 --- /dev/null +++ b/2_intermediate/chapter8/solutions/fortune.py @@ -0,0 +1,22 @@ +""" +Fortune + +Store 6 fortunes cookie messages, and display a +random one each time the user runs the program. + +Assume that you DON'T know how many messages are in the list. + +(Hint: You’re randomly choosing the index to access.) +""" +import random + +messages = [ + "You will meet the love of your life today.", + "You will turn into a fish.", + "It will rain diamonds today.", + "You will encounter a field of mangoes.", + "Look for the light. Underneath it, there will be a chess board.", + "You'll run a sub-6:00 mile today.", +] + +print(messages[random.randrange(len(messages))]) diff --git a/2_intermediate/chapter9/examples/dict_values.py b/2_intermediate/chapter9/examples/dict_values.py new file mode 100644 index 00000000..ac1a9d0a --- /dev/null +++ b/2_intermediate/chapter9/examples/dict_values.py @@ -0,0 +1,16 @@ +# Retrieving, Updating, and Adding Values + +contacts = { + "John Doe": "1234 Main St", + "Jane Smith": "5678 Market St", + "Daisy Johnson": "1357 Wall St", +} + +daisy_address = contacts["Daisy Johnson"] +print(daisy_address) # prints “1357 Wall St” + +contacts["Daisy Johnson"] = "2468 Park Ave" +print(contacts["Daisy Johnson"]) # prints “2468 Park Ave” + +contacts["Leo Fitz"] = "1258 Monkey Dr" +print(contacts) # prints the dictionary with the new entry diff --git a/2_intermediate/chapter9/examples/dictionaries.py b/2_intermediate/chapter9/examples/dictionaries.py deleted file mode 100644 index 2a479601..00000000 --- a/2_intermediate/chapter9/examples/dictionaries.py +++ /dev/null @@ -1,122 +0,0 @@ -# Dictionaries -# TODO split into multiple programs -print(">>>Why should you use dictionaries?") - -# implementation 1 using if statements -value = 10 -if value == 10: - print("Tom") -elif value == 20: - print("Daniel") -elif value == 30: - print("Elizabeth") - -# implementation 2 using dictionary -dict1 = {10: "Tom", 20: "Daniel", 30: "Elizabeth"} -print(dict1[value]) - -print() - -print(">>> Initializing dictionaries") -contacts = { - "John Doe": "1234 Main St", - "Jane Smith": "5678 Market St", - "Daisy Johnson": "1357 Wall St" -} -print(contacts) - -print() - -# retrieving, updating, and adding values -print(">>> Retrieving, updating, and adding values") - -daisy_address = contacts["Daisy Johnson"] -print(daisy_address) # prints “1357 Wall St” - -contacts["Daisy Johnson"] = "2468 Park Ave" -print(contacts["Daisy Johnson"]) # prints “2468 Park Ave” - -contacts["Leo Fitz"] = "1258 Monkey Dr" -print(contacts) # prints the dictionary with the new entry - -print() - -# manipulating dictionaries -print(">>> Manipulating dictionaries") - -# get - retrieves the value at a specified key -leo_address = contacts.get("Leo Fitz") -print(leo_address) - -# the following gives you an error -# because "Melinda May" is not a key: -# melinda_address = contacts["Melinda May"] - -# however, using the get(keyname, value) method, -# you can specify the value that is returned -# if the key doesn't exist in the dictionary -coulson_address = contacts.get("Phil Coulson", -1) -print(coulson_address) # prints -1 - -# pop - removes the value at a specified key and returns -# the removed value -removed_address = contacts.pop("John Doe") -print(removed_address) # prints "1234 Main St" -print(contacts) # John Doe has been removed - -# del - deletes a dictionary or key-value pair -del contacts["Jane Smith"] # deletes Jane Smith - -# this would delete the entire dictionary: -# del contacts - -# copy - returns a copy of the dictionary -copy_of_contacts = contacts.copy() -print(copy_of_contacts) - -# clear - removes all key-value pairs from the dictionary -contacts.clear() -print(contacts) - -print() - -# iterating through dictionaries -print(">>> Iterating through dictionaries") - -contacts = copy_of_contacts # undo the clear - -# iterate through each key -for name in contacts: - print(name) - -# or, use keys() method -for name in contacts.keys(): - print(name) - -# using each key to print each value -for name in contacts: - # prints each address associated with each name - print(contacts[name]) - -# iterate through each value using values() -for address in contacts.values(): - print(address) - -# iterate through keys and values using items() -for name, address in contacts.items(): - print(name + ", " + address) - -print() - -# checking if a key or value is in a dictionary -print(">>> Checking if a key or value is in a dictionary") - -if "Daisy Johnson" in contacts: - print("Daisy Johnson is a key in contacts") -else: - print("Daisy Johnson is not a key in contacts") - -if "1234 Main St" in contacts.values(): - print("1234 Main St is a value in contacts") -else: - print("1234 Main St is not a value in contacts") diff --git a/2_intermediate/chapter9/examples/initialize_dict.py b/2_intermediate/chapter9/examples/initialize_dict.py new file mode 100644 index 00000000..d29066f5 --- /dev/null +++ b/2_intermediate/chapter9/examples/initialize_dict.py @@ -0,0 +1,13 @@ +# Initializing Dictionaries + +contacts = { + "John Doe": "1234 Main St", + "Jane Smith": "5678 Market St", + "Daisy Johnson": "1357 Wall St", +} +print(contacts) + +# creating a dict from a list of lists +my_list = [["key1", "value1"], ["key2", "value2"], ["key3", "value3"]] +my_dict = dict(my_list) +print(my_dict) diff --git a/2_intermediate/chapter9/examples/iterate_dict.py b/2_intermediate/chapter9/examples/iterate_dict.py new file mode 100644 index 00000000..dd979854 --- /dev/null +++ b/2_intermediate/chapter9/examples/iterate_dict.py @@ -0,0 +1,24 @@ +# Iterating Through Dictionaries + +contacts = {"Daisy Johnson": "2468 Park Ave", "Leo Fitz": "1258 Monkey Dr"} + +# iterate through each key +for name in contacts: + print(name) + +# or, use keys() method +for name in contacts.keys(): + print(name) + +# using each key to print each value +for name in contacts: + # prints each address associated with each name + print(contacts[name]) + +# iterate through each value using values() +for address in contacts.values(): + print(address) + +# iterate through keys and values using items() +for name, address in contacts.items(): + print(name + ", " + address) diff --git a/2_intermediate/chapter9/examples/key_value_membership.py b/2_intermediate/chapter9/examples/key_value_membership.py new file mode 100644 index 00000000..4a95c9f3 --- /dev/null +++ b/2_intermediate/chapter9/examples/key_value_membership.py @@ -0,0 +1,13 @@ +# Check Key/Value Membership in a Dictionary + +contacts = {"Daisy Johnson": "2468 Park Ave", "Leo Fitz": "1258 Monkey Dr"} + +if "Daisy Johnson" in contacts: + print("Daisy Johnson is a key in contacts") +else: + print("Daisy Johnson is not a key in contacts") + +if "1234 Main St" in contacts.values(): + print("1234 Main St is a value in contacts") +else: + print("1234 Main St is not a value in contacts") diff --git a/2_intermediate/chapter9/examples/manipulate_dict.py b/2_intermediate/chapter9/examples/manipulate_dict.py new file mode 100644 index 00000000..cb3e205f --- /dev/null +++ b/2_intermediate/chapter9/examples/manipulate_dict.py @@ -0,0 +1,42 @@ +# Manipulating Dictionaries + +contacts = { + "John Doe": "1234 Main St", + "Jane Smith": "5678 Market St", + "Daisy Johnson": "2468 Park Ave", + "Leo Fitz": "1258 Monkey Dr", +} + +# get - retrieves the value at a specified key +leo_address = contacts.get("Leo Fitz") +print(leo_address) + +# the following gives you an error +# because "Melinda May" is not a key: +# melinda_address = contacts["Melinda May"] + +# however, using the get(keyname, value) method, +# you can specify the value that is returned +# if the key doesn't exist in the dictionary +coulson_address = contacts.get("Phil Coulson", -1) +print(coulson_address) # prints -1 + +# pop - removes the value at a specified key and returns +# the removed value +removed_address = contacts.pop("John Doe") +print(removed_address) # prints "1234 Main St" +print(contacts) # John Doe has been removed + +# del - deletes a dictionary or key-value pair +del contacts["Jane Smith"] # deletes Jane Smith + +# this would delete the entire dictionary: +# del contacts + +# copy - returns a copy of the dictionary +copy_of_contacts = contacts.copy() +print(copy_of_contacts) + +# clear - removes all key-value pairs from the dictionary +contacts.clear() +print(contacts) diff --git a/2_intermediate/chapter9/examples/why_dict.py b/2_intermediate/chapter9/examples/why_dict.py new file mode 100644 index 00000000..66268626 --- /dev/null +++ b/2_intermediate/chapter9/examples/why_dict.py @@ -0,0 +1,30 @@ +# Why Dictionaries + +# dictionaries as an alternative to parallel lists +names = [ + "Jane Doe", + "John Williams", +] +addresses = ["1234 Main St", "5678 Market Pl", "1357 Wall St"] + +# better solution: make a dictionary to +# explicitly associate a name with an address +# this is also called mapping a key to a value +contacts = { + "Jane Doe": "1234 Main St", + "John Williams": "5678 Market Pl", + "Alex Summers": "1357 Wall St", +} + +# implementation 1 using if statements +value = 10 +if value == 10: + print("Tom") +elif value == 20: + print("Daniel") +elif value == 30: + print("Elizabeth") + +# implementation 2 using dictionary +dict1 = {10: "Tom", 20: "Daniel", 30: "Elizabeth"} +print(dict1[value]) diff --git a/2_intermediate/chapter9/practice/catalog.py b/2_intermediate/chapter9/practice/catalog.py new file mode 100644 index 00000000..3b3938f3 --- /dev/null +++ b/2_intermediate/chapter9/practice/catalog.py @@ -0,0 +1,36 @@ +""" +Catalog + +Write a program that takes asks the +user whether they'd like to add, delete, +or clear the entries in a store catalog. + +After they perform some action, the program +should display the updated dictionary in the +format: + item1: price1 + item2: price2 + etc. + +Keep asking them if they'd like to add, delete, +or clear entries until they enter "q". + +Possible actions: +If they enter "add": + Ask them to enter an item. + Ask them to enter a price. + The item should be the key, and the price + should be the value in the dictionary. + +If they enter "delete": + Ask them to to enter which item + they'd like to delete. + +If they enter "clear": + Clear all the entries from the dictionary. + +If they enter "q": + Display the final dictionary and end the program. +""" + +# Insert your code here. diff --git a/2_intermediate/chapter9/practice/participation_grade.py b/2_intermediate/chapter9/practice/participation_grade.py new file mode 100644 index 00000000..8ecad6af --- /dev/null +++ b/2_intermediate/chapter9/practice/participation_grade.py @@ -0,0 +1,51 @@ +# Warning: can be challenging +# +# A teacher is given a list of students.The number of occurences of a student's +# name in the list is the number of times the student participated this week. +# If a student has 8 or more participations, they get an A. If a student has +# between 4 and 7 participations, they get a B. If a student has more than +# 0 but less than 4, the student gets a C. +# +# Make a dictionary with the keys as the students' name and the values as the +# corresponding student's letter grade. Print the dictionary +# +# Write your code below + + +participation_occurences = [ + "Sam", + "Dan", + "Bob", + "Dan", + "Sam", + "Sam", + "Bob", + "Dan", + "Dan", + "Ivan", + "Ivan", + "Ray", + "Sam", + "Sam", + "Dan", + "Ivan", + "Ivan", + "Ray", + "Dan", + "Ivan", + "Ivan", + "Sam", + "Dan", + "Ray", + "Sam", + "Dan", + "Bob", + "Dan", + "Sam", + "Sam", + "Dan", + "Bob", + "Dan", + "Sam", + "Sam", +] diff --git a/2_intermediate/chapter9/practice/word.py b/2_intermediate/chapter9/practice/word.py new file mode 100644 index 00000000..a11cec0e --- /dev/null +++ b/2_intermediate/chapter9/practice/word.py @@ -0,0 +1,19 @@ +""" +Word + +Store words (keys) and definitions (values) +in a dictionary in a random order. + +Then, ask the user to input a word. +If the word is in the dictionary, +use it to display the definition. +Otherwise, print out a message saying +that the word is not there. + +Display the entire dictionary at the end in the format: +word1: definition1 +word2: definition2 +etc. +""" + +# Insert your code here. diff --git a/2_intermediate/chapter9/solutions/catalog.py b/2_intermediate/chapter9/solutions/catalog.py new file mode 100644 index 00000000..abb304cd --- /dev/null +++ b/2_intermediate/chapter9/solutions/catalog.py @@ -0,0 +1,65 @@ +""" +Catalog + +Write a program that takes asks the +user whether they'd like to add, delete, +or clear the entries in a store catalog. + +After they perform some action, the program +should display the updated dictionary in the +format: + item1: price1 + item2: price2 + etc. + +Keep asking them if they'd like to add, delete, +or clear entries until they enter "q". + +Possible actions: +If they enter "add": + Ask them to enter an item. + Ask them to enter a price. + The item should be the key, and the price + should be the value in the dictionary. + +If they enter "delete": + Ask them to to enter which item + they'd like to delete. + +If they enter "clear": + Clear all the entries from the dictionary. + +If they enter "q": + Display the final dictionary and end the program. +""" + +catalog = {} + +while True: + # Display catalog + print("Current catalog:") + if len(catalog) != 0: + for item, price in catalog.items(): + print(item + ": $" + price) + else: + print("Your catalog is empty.") + print() + + ans = input( + "Would you like to add, delete, " + "or clear entries from your catalog? " + ) + + # Perform actions depending on input + if ans == "q": + break + elif ans == "add": + item = input("Enter an item to add: ") + price = input("Enter the price: $") + catalog[item] = price + elif ans == "delete": + item = input("Enter an item to delete: ") + del catalog[item] + elif ans == "clear": + catalog.clear() + print() diff --git a/2_intermediate/chapter9/solutions/participation_grade.py b/2_intermediate/chapter9/solutions/participation_grade.py new file mode 100644 index 00000000..dd4dde72 --- /dev/null +++ b/2_intermediate/chapter9/solutions/participation_grade.py @@ -0,0 +1,68 @@ +# Warning: can be challenging +# +# A teacher is given a list of students.The number of occurences of a student's +# name in the list is the number of times the student participated this week. +# If a student has 8 or more participations, they get an A. If a student has +# between 4 and 7 participations, they get a B. If a student has more than +# 0 but less than 4, the student gets a C. +# +# Make a dictionary with the keys as the students' name and the values as the +# corresponding student's letter grade. Print the dictionary +# +# Write your code below + + +participation_occurences = [ + "Sam", + "Dan", + "Bob", + "Dan", + "Sam", + "Sam", + "Bob", + "Dan", + "Dan", + "Ivan", + "Ivan", + "Ray", + "Sam", + "Sam", + "Dan", + "Ivan", + "Ivan", + "Ray", + "Dan", + "Ivan", + "Ivan", + "Sam", + "Dan", + "Ray", + "Sam", + "Dan", + "Bob", + "Dan", + "Sam", + "Sam", + "Dan", + "Bob", + "Dan", + "Sam", + "Sam", +] + +grade_dict = {} +for student in participation_occurences: + if student not in grade_dict: + grade_dict[student] = 1 + else: + grade_dict[student] += 1 + +for student in grade_dict.keys(): + if grade_dict[student] > 7: + grade_dict[student] = "A" + elif grade_dict[student] > 3: + grade_dict[student] = "B" + else: + grade_dict[student] = "C" + +print(grade_dict) diff --git a/2_intermediate/chapter9/solutions/word.py b/2_intermediate/chapter9/solutions/word.py new file mode 100644 index 00000000..14a61180 --- /dev/null +++ b/2_intermediate/chapter9/solutions/word.py @@ -0,0 +1,37 @@ +""" +Word + +Store words (keys) and definitions (values) +in a dictionary in a random order. + +Then, ask the user to input a word. +If the word is in the dictionary, +use it to display the definition. +Otherwise, print out a message saying +that the word is not there. + +Display the entire dictionary at the end in the format: +word1: definition1 +word2: definition2 +etc. +""" + +dictionary = { + "monkey": "a cute human", + "banana": "sustenance for cute humans", + "neha": "a really strange human", + "phone": "magic communication device", + "cookie": "heaven on Earth", +} + +# Show definition +word = input("Enter a word: ") +if word in dictionary: + print("Definition:", dictionary.get(word)) +else: + print("This word cannot be found.") +print() + +# Display formatted dictionary +for key, value in dictionary.items(): + print(key + ": " + value) diff --git a/3_advanced/chapter14/examples/enumerate.py b/3_advanced/chapter14/examples/enumerate.py new file mode 100644 index 00000000..53023364 --- /dev/null +++ b/3_advanced/chapter14/examples/enumerate.py @@ -0,0 +1,19 @@ +# The enumerate function assigns numbers to every element +# in an iterable, starting with zero. +# it returns an enumerate object, so you have to do list or tuple +# to access the enumerated values. + + +countries = ["Japan", "America", "South Korea", "China"] +numerated_list = list(enumerate(countries)) +print(numerated_list) +# prints [(0, ' Japan'), (1, 'America'), (2, 'South Korea'), (3, ' China')] + + +# This code gets all the countries with even indexes greater than 1 +answer_list = [] +for index, country in enumerate(countries): + if index % 2 == 0 and index > 1: + answer_list.append(country) +print(answer_list) +# prints ['South Korea'] diff --git a/3_advanced/chapter14/examples/list_comp.py b/3_advanced/chapter14/examples/list_comp.py new file mode 100644 index 00000000..1d066806 --- /dev/null +++ b/3_advanced/chapter14/examples/list_comp.py @@ -0,0 +1,32 @@ +# List comprehensions are a faster and more +# elegant way to create a new list +# based on an existing list + + +# squares each number from 0 to 9 and adds to 'listL' +listL = [] +for i in range(10): + listL.append(i * i) +print(listL) +# prints [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + + +# does the same thing as above, but in shorter, cleaner code +squares = [i * i for i in range(10)] +print(squares) +# also prints [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + + +# squares all numbers in list 'a' IF they are greater than 5 +a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +squares = [i * i for i in a if i > 5] +print(squares) +# prints [36, 49, 64, 81, 100] + + +# converts all letters in 'word' to uppercase and adds to list 'ask' +word = " Hey how are you" +asks = [i.upper() for i in word] +print(asks) +# prints [' ', 'H', 'E', 'Y', ' ', 'H', 'O', 'W', +# ' ', 'A', 'R', 'E', ' ', 'Y', 'O', 'U'] diff --git a/3_advanced/chapter14/examples/swapping_vars.py b/3_advanced/chapter14/examples/swapping_vars.py new file mode 100644 index 00000000..3ed76f03 --- /dev/null +++ b/3_advanced/chapter14/examples/swapping_vars.py @@ -0,0 +1,33 @@ +# Normally, when switching the values of two variables, +# you need a third, temporary variable. With python, +# you can ignore that step using tuple unpacking. + + +""" Switching two variables with a temporary variable """ +# Switching 2 and 3 +list1 = [1, 2, 3, 4, 5] + +temp = list1[1] +list1[1] = list1[2] +list1[2] = temp + +print(list1) +# prints [1, 3, 2, 4, 5] + + +""" Switching two variables without a temporary variable """ +list1 = [1, 2, 3, 4, 5] + +list1[1], list1[2] = list1[2], list1[1] + +print(list1) +# also prints [1, 3, 2, 4, 5] + + +""" Switching many variables without a temporary variable """ +list1 = [1, 2, 3, 4, 5] + +list1[0], list1[1], list1[2] = list1[1], list1[2], list1[0] + +print(list1) +# prints [2, 3, 1, 4, 5] diff --git a/3_advanced/chapter14/examples/tuple_unpack.py b/3_advanced/chapter14/examples/tuple_unpack.py new file mode 100644 index 00000000..2404891e --- /dev/null +++ b/3_advanced/chapter14/examples/tuple_unpack.py @@ -0,0 +1,27 @@ +# Extracting values from "countries" + + +""" Typical way to extract values """ +countries = ("china", "mexico", "brazil", "USA") + +a = countries[0] +b = countries[1] +c = countries[2] +d = countries[3] + +print(a, b, c, d) # prints "china mexico brazil USA" + + +""" Extracting values with tuple unpacking """ +a, b, c, d = countries + +print(a, b, c, d) # prints "china mexico brazil USA" + + +""" Special feature for tuple unpacking """ +# Using the * says that you want all values +# in the middle of the tuple to be put together +# in a list. +a, *b, c = countries + +print(a, b, c) # prints "china ["mexico", "brazil"] USA" diff --git a/3_advanced/chapter14/examples/zip.py b/3_advanced/chapter14/examples/zip.py new file mode 100644 index 00000000..34f51221 --- /dev/null +++ b/3_advanced/chapter14/examples/zip.py @@ -0,0 +1,27 @@ +# The zip function groups elements from different +# iterables into tuples by their index + + +a = [1, 2, 3] +b = ["a", "b", "c"] +c = ["!", "@", "#"] + +G = zip(a, b, c) + +print(list(G)) +# prints [(1, 'a', '!'), (2, 'b', '@'), (3, 'c', '#')] + + +list_one = [1, 2] +list_two = [41] + +for pair in zip(list_one, list_two): + print(pair) +# this would only print (1, 41) because list_two has only one element + + +countries = [" Japan", "America", "South Korea", " China"] +numbers = [1, 2, 3, 4] +dict1 = dict(zip(countries, numbers)) +print(dict1) +# prints {' China': 4, ' Japan': 1, 'America': 2, 'South Korea': 3} diff --git a/3_advanced/chapter14/practice/add_10.py b/3_advanced/chapter14/practice/add_10.py new file mode 100644 index 00000000..ad466151 --- /dev/null +++ b/3_advanced/chapter14/practice/add_10.py @@ -0,0 +1,13 @@ +# Problem name: add_10 +# A messy teacher named Bob would like to add 10 points to each +# student’s recent test score. +# There are four students, and going from highest score to lowest +# score, it is Mike, Dan, Stan, and Ban. +# Add 10 to each score and assign those values to the correct student. +# Solve this problem by adding no more than 2 lines of code. +# Hint: Use tuple unpacking and list comprehension. + +# the scores are given +scores = (100, 90, 80, 70) + +# write your code below diff --git a/3_advanced/chapter14/practice/bob_selection.py b/3_advanced/chapter14/practice/bob_selection.py new file mode 100644 index 00000000..24b0e826 --- /dev/null +++ b/3_advanced/chapter14/practice/bob_selection.py @@ -0,0 +1,22 @@ +""" +Problem Name: bob_selection +Bob is choosing a person to go to the moon with him. The way he +chooses is quite strange. He will choose the first person from a +list given to him whose age is divisible by 5 and whose index +within the list is divisible by 5. If he does find such a person, +print the person’s name. If he doesn’t, don’t print anything. The +list given to him contains lists which contain the person’s name +and age. Use enumerate to solve this problem. +""" + +# the list is given to him +people_list = [ + ("Ana", 22), + ("Mark", 41), + ("Dan", 10), + ("Jack", 14), + ("Ben", 51), + ("Jorge", 65), +] + +# write your code below diff --git a/3_advanced/chapter14/practice/darwin_raccoon.py b/3_advanced/chapter14/practice/darwin_raccoon.py new file mode 100644 index 00000000..4ca34dc3 --- /dev/null +++ b/3_advanced/chapter14/practice/darwin_raccoon.py @@ -0,0 +1,16 @@ +""" +Problem Name: darwin_raccoon +Darwin is observing raccoons’ growths on an unnamed island. He spends 7 days +in total on this island, and on every day, he would record the average growth +of raccoons in inches. He loses data on day 7, so he decides to make the data +on that day to be the maximum of the previous 6 days. He needs to make a +dictionary for use later where the key is the day number and the value is the +average growth of raccoons on that day. You help him make the dictionary. +Use zip to solve this problem. +""" + +# The lists are already given to you +days_list = ["Day 1", "Day 2", "Day 3", "Day 4", "Day 5", "Day 6", "Day 7"] +growths_list = [1.4, 2.1, 1.3, 0.1, 0.4, 1.9] + +# write your code below diff --git a/3_advanced/chapter14/practice/even_words.py b/3_advanced/chapter14/practice/even_words.py new file mode 100644 index 00000000..ea46e178 --- /dev/null +++ b/3_advanced/chapter14/practice/even_words.py @@ -0,0 +1,11 @@ +# Problem name: even_words +# Given a string that the user inputs create a list that contains the +# square of the lengths of words with an even amount of characters. +# +# For example, if the string is "I am cool", the list would be [4, 16]. +# Use a list comprehension to solve it. + +# The user inputs the string +string = input() + +# write your code below diff --git a/3_advanced/chapter14/practice/odd_squares.py b/3_advanced/chapter14/practice/odd_squares.py new file mode 100644 index 00000000..50d63c21 --- /dev/null +++ b/3_advanced/chapter14/practice/odd_squares.py @@ -0,0 +1,12 @@ +# Problem name: odd_squares +# Given a list of integers, create a list of all the squares of the odd +# integers within the list. +# Use a list comprehension to solve it. + +# the given code takes an input and makes it a list of numbers +# for example, entering “1 23 4” as the input will result in the list [1,23,4] +ex_list = input().split() +for idx in range(len(ex_list)): + ex_list[idx] = int(ex_list[idx]) + +# write your code below diff --git a/3_advanced/chapter14/practice/pokemon_presentation.py b/3_advanced/chapter14/practice/pokemon_presentation.py new file mode 100644 index 00000000..a471120b --- /dev/null +++ b/3_advanced/chapter14/practice/pokemon_presentation.py @@ -0,0 +1,17 @@ +""" +Problem Name: pokemon_presentation +You’re a teacher giving a presentation on the types +(https://pokemon.fandom.com/wiki/Types) of pokemons +(https://www.pokemon.com/us/pokedex/). You have a list pokemons_list, +and you have another list types_list(this has the types of the +pokemons in pokemons_lists in the same order). Since you are +presenting, print “[insert pokemon] is a [insert type] type” for all +the pokemons in pokemons_list with their corresponding types in +types_list. Use zip to solve this problem. +""" + +# the lists are already given to you +pokemons_list = ["Charmander", "Squirtle", "Bulbasaur", "Pikachu"] +types_list = ["Fire", "Water", "Grass and Poison", "Electric"] + +# write your code below diff --git a/3_advanced/chapter14/practice/square_root_list.py b/3_advanced/chapter14/practice/square_root_list.py new file mode 100644 index 00000000..353209ab --- /dev/null +++ b/3_advanced/chapter14/practice/square_root_list.py @@ -0,0 +1,16 @@ +""" +Problem Name: square_root_list +Take a user given list of numbers and make a list of all the +square roots of the numbers using list comprehension. Use a +list comprehension to solve it. +Hint: The square root of a number is the same as taking the one half +power of a number. +""" + +# The given code takes an input and makes it a list of numbers. +# For example, entering “1 23 4” as the input will result in the list [1,23,4] +ex_list = input().split() +for idx in range(len(ex_list)): + ex_list[idx] = int(ex_list[idx]) + +# write your code below diff --git a/3_advanced/chapter14/practice/update_score.py b/3_advanced/chapter14/practice/update_score.py new file mode 100644 index 00000000..4b666dde --- /dev/null +++ b/3_advanced/chapter14/practice/update_score.py @@ -0,0 +1,20 @@ +# Problem name: update_score +# A hacker named Dan wishes to hack into a competition where +# they judge participants in three categories on a scale of 10. +# Dan wants his friend Bob to win. +# Bob will only win if he has all 10s while the +# other competitors, Jo and Stan, don’t. + +# Judges will store the scoring within a tuple ([...], [...], [...]). +# The scores before Dan hacked are given. +# Bob will be located as the first person the judges scored and will +# have the lowest points out of any participant. + +# Create a program to help Dan help Bob win. +# Also, print Bob’s score at the end. +# Use tuple unpacking to solve this problem. + +# the scores are given +scores = ([1, 1, 1], [2, 2, 2], [3, 3, 3]) + +# write your code below diff --git a/3_advanced/chapter14/practice/worried_josh.py b/3_advanced/chapter14/practice/worried_josh.py new file mode 100644 index 00000000..75e477e7 --- /dev/null +++ b/3_advanced/chapter14/practice/worried_josh.py @@ -0,0 +1,18 @@ +# Problem Name: worried_josh + +""" +Josh is worried about his test score. He wants to score in the top n, +where n is a positive integer that the user inputs. Given a list of +student names where the student with the highest score is the 0th index +and the score goes down from there, print “YES!” if Josh scores in the top n, +and “NO :(“ if he doesn’t. Assume n will not be greater than the number of +students. Use enumerate to solve this problem. +""" + +# the list of student names is given and the n is a user input +# remember the +# leftmost student = highest score, rightmost student = lowest score +students = ["Dan", "Isaac", "Jo", "Josh", "Dennis", "Erwin", "Ivan", "Penny"] +n = int(input()) + +# write your code below diff --git a/3_advanced/chapter14/solutions/add_10.py b/3_advanced/chapter14/solutions/add_10.py new file mode 100644 index 00000000..3c7bf125 --- /dev/null +++ b/3_advanced/chapter14/solutions/add_10.py @@ -0,0 +1,15 @@ +# Problem name: add_10 +# A messy teacher named Bob would like to add 10 points to each +# student’s recent test score. +# There are four students, and going from highest score to lowest +# score, it is Mike, Dan, Stan, and Ban. +# Add 10 to each score and assign those values to the correct student. +# Solve this problem by adding no more than 2 lines of code. +# Hint: Use tuple unpacking and list comprehension. + +# the scores are given +scores = (100, 90, 80, 70) + +# write your code below +scores_added = [n + 10 for n in scores] +Mike, Dan, Stan, Ban = scores_added diff --git a/3_advanced/chapter14/solutions/bob_selection.py b/3_advanced/chapter14/solutions/bob_selection.py new file mode 100644 index 00000000..63714617 --- /dev/null +++ b/3_advanced/chapter14/solutions/bob_selection.py @@ -0,0 +1,26 @@ +""" +Problem Name: bob_selection +Bob is choosing a person to go to the moon with him. The way he +chooses is quite strange. He will choose the first person from a +list given to him whose age is divisible by 5 and whose index +within the list is divisible by 5. If he does find such a person, +print the person’s name. If he doesn’t, don’t print anything. The +list given to him contains lists which contain the person’s name +and age. Use enumerate to solve this problem. +""" + +# the list is given to him +people_list = [ + ("Ana", 22), + ("Mark", 41), + ("Dan", 10), + ("Jack", 14), + ("Ben", 51), + ("Jorge", 65), +] + +# write your code below +for index, person_data in enumerate(people_list): + if person_data[1] % 5 == 0 and index % 5 == 0: + print(person_data[0]) + break diff --git a/3_advanced/chapter14/solutions/darwin_raccoon.py b/3_advanced/chapter14/solutions/darwin_raccoon.py new file mode 100644 index 00000000..5083c40c --- /dev/null +++ b/3_advanced/chapter14/solutions/darwin_raccoon.py @@ -0,0 +1,17 @@ +""" +Problem Name: darwin_raccoon +Darwin is observing raccoons’ growths on an unnamed island. He spends 7 days +in total on this island, and on every day, he would record the average growth +of raccoons in inches. He loses data on day 7, so he decides to make the data +on that day to be the maximum of the previous 6 days. He needs to make a +dictionary for use later where the key is the day number and the value is the +average growth of raccoons on that day. You help him make the dictionary. +Use zip to solve this problem. +""" + +# The lists are already given to you +days_list = ["Day 1", "Day 2", "Day 3", "Day 4", "Day 5", "Day 6", "Day 7"] +growths_list = [1.4, 2.1, 1.3, 0.1, 0.4, 1.9] + +# write your code below +data_dictionary = dict(zip(days_list, growths_list + [max(growths_list)])) diff --git a/3_advanced/chapter14/solutions/even_words.py b/3_advanced/chapter14/solutions/even_words.py new file mode 100644 index 00000000..4cc6bd57 --- /dev/null +++ b/3_advanced/chapter14/solutions/even_words.py @@ -0,0 +1,12 @@ +# Problem name: even_words +# Given a string that the user inputs create a list that contains the +# square of the lengths of words with an even amount of characters. +# +# For example, if the string is "I am cool", the list would be [4, 16]. +# Use a list comprehension to solve it. + +# The user inputs the string +string = input() + +# write your code below +evenwords = [len(word) ** 2 for word in string.split() if len(word) % 2 == 0] diff --git a/3_advanced/chapter14/solutions/odd_squares.py b/3_advanced/chapter14/solutions/odd_squares.py new file mode 100644 index 00000000..4a4dfd74 --- /dev/null +++ b/3_advanced/chapter14/solutions/odd_squares.py @@ -0,0 +1,13 @@ +# Problem name: odd_squares +# Given a list of integers, create a list of all the squares of the odd +# integers within the list. +# Use a list comprehension to solve it. + +# the given code takes an input and makes it a list of numbers +# for example, entering “1 23 4” as the input will result in the list [1,23,4] +ex_list = input().split() +for idx in range(len(ex_list)): + ex_list[idx] = int(ex_list[idx]) + +# write your code below +odds_quares = [n**2 for n in list if n % 2 == 1] diff --git a/3_advanced/chapter14/solutions/pokemon_presentation.py b/3_advanced/chapter14/solutions/pokemon_presentation.py new file mode 100644 index 00000000..ff9cb3fc --- /dev/null +++ b/3_advanced/chapter14/solutions/pokemon_presentation.py @@ -0,0 +1,19 @@ +""" +Problem Name: pokemon_presentation +You’re a teacher giving a presentation on the types +(https://pokemon.fandom.com/wiki/Types) of pokemons +(https://www.pokemon.com/us/pokedex/). You have a list pokemons_list, +and you have another list types_list(this has the types of the +pokemons in pokemons_lists in the same order). Since you are +presenting, print “[insert pokemon] is a [insert type] type” for all +the pokemons in pokemons_list with their corresponding types in +types_list. Use zip to solve this problem. +""" + +# the lists are already given to you +pokemons_list = ["Charmander", "Squirtle", "Bulbasaur", "Pikachu"] +types_list = ["Fire", "Water", "Grass and Poison", "Electric"] + +# write your code below +for pokemon, tp in zip(pokemons_list, types_list): + print(pokemon + " is a " + tp + " type") diff --git a/3_advanced/chapter14/solutions/square_root_list.py b/3_advanced/chapter14/solutions/square_root_list.py new file mode 100644 index 00000000..862835c8 --- /dev/null +++ b/3_advanced/chapter14/solutions/square_root_list.py @@ -0,0 +1,17 @@ +""" +Problem Name: square_root_list +Take a user given list of numbers and make a list of all the +square roots of the numbers using list comprehension. Use a +list comprehension to solve it. +Hint: The square root of a number is the same as taking the one half +power of a number. +""" + +# The given code takes an input and makes it a list of numbers. +# For example, entering "1 23 4" as the input will result in the list [1,23,4] +ex_list = input().split() +for idx in range(len(ex_list)): + ex_list[idx] = int(ex_list[idx]) + +# write your code below +new_list = [number ** (1 / 2) for number in ex_list] diff --git a/3_advanced/chapter14/solutions/update_score.py b/3_advanced/chapter14/solutions/update_score.py new file mode 100644 index 00000000..76e6ade5 --- /dev/null +++ b/3_advanced/chapter14/solutions/update_score.py @@ -0,0 +1,25 @@ +# Problem name: update_score +# A hacker named Dan wishes to hack into a competition where +# they judge participants in three categories on a scale of 10. +# Dan wants his friend Bob to win. +# Bob will only win if he has all 10s while the +# other competitors, Jo and Stan, don’t. + +# Judges will store the scoring within a tuple ([...], [...], [...]). +# The scores before Dan hacked are given. +# Bob will be located as the first person the judges scored and will +# have the lowest points out of any participant. + +# Create a program to help Dan help Bob win. +# Also, print Bob’s score at the end. +# Use tuple unpacking to solve this problem. + +# the scores are given +scores = ([1, 1, 1], [2, 2, 2], [3, 3, 3]) + +# write your code below +scores[0][0] = 10 +scores[0][1] = 10 +scores[0][2] = 10 +(Bob, Jo, Stan) = scores +print(Bob) diff --git a/3_advanced/chapter14/solutions/worried_josh.py b/3_advanced/chapter14/solutions/worried_josh.py new file mode 100644 index 00000000..8af9207e --- /dev/null +++ b/3_advanced/chapter14/solutions/worried_josh.py @@ -0,0 +1,26 @@ +# Problem Name: worried_josh + +""" +Josh is worried about his test score. He wants to score in the top n, +where n is a positive integer that the user inputs. Given a list of +student names where the student with the highest score is the 0th index +and the score goes down from there, print “YES!” if Josh scores in the top n, +and “NO :(“ if he doesn’t. Assume n will not be greater than the number of +students. Use enumerate to solve this problem. +""" + +# the list of student names is given and the n is a user input +# remember the +# leftmost student = highest score, rightmost student = lowest score +students = ["Dan", "Isaac", "Jo", "Josh", "Dennis", "Erwin", "Ivan", "Penny"] +n = int(input()) + +# write your code below + +said_yes = False +for index, name in enumerate(students): + if name == "Josh" and index + 1 <= n: + print("YES!") + said_yes = True +if not said_yes: + print("NO :(") diff --git a/3_advanced/chapter15/examples/calculate_time.py b/3_advanced/chapter15/examples/calculate_time.py new file mode 100644 index 00000000..a2e01338 --- /dev/null +++ b/3_advanced/chapter15/examples/calculate_time.py @@ -0,0 +1,13 @@ +# This code will get all the odd birthdays and print it +birthdays = [12, 4, 21, 11, 24] # O(1) + +odd_birthdays = [] # O(1) +for birthday in birthdays: # O(n) + if birthday % 2 == 1: # O(1)*O(n) = O(n) + odd_birthdays.append(birthday) # O(1)*O(n) = O(n) + +print(odd_birthdays) # O(1) + +# Sum = O(1) + O(1) + O(n) + O(n) + O(n) + O(1) +# Sum = 3*O(1) + 3*O(n) +# Final Running Time = O(n) diff --git a/3_advanced/chapter15/examples/running_time.py b/3_advanced/chapter15/examples/running_time.py new file mode 100644 index 00000000..2f06c596 --- /dev/null +++ b/3_advanced/chapter15/examples/running_time.py @@ -0,0 +1,32 @@ +""" O(1) """ +# Any assignments +x = 1 # O(1) +x += 1 # O(1) + +# If statement structure +# Condition and code inside not always O(1) +if 1 == 1: # O(1) + print(1) # O(1) +else: # O(1) + print(2) # O(1) + +# Some list operations +x = [1, 2, 4, 213] +x.append(14) # O(1) +x[0] = 11 # O(1) + + +""" O(n) """ +# "Most" for loops are O(n) +for number in [123, 4, 21, 312, 41]: # O(n) + print(number) # O(1) + + +""" O(n^2), O(n^3), etc. """ +# "Most" of the time, every extra for loop +# increases running time by a factor of n + +example_list = [12, 3, 214, 5, 12] +for num1 in example_list: # O(n) + for num2 in example_list: # O(n) + print(num1, num2) # O(1) diff --git a/3_advanced/chapter15/examples/tricky.py b/3_advanced/chapter15/examples/tricky.py new file mode 100644 index 00000000..1406a8a6 --- /dev/null +++ b/3_advanced/chapter15/examples/tricky.py @@ -0,0 +1,17 @@ +# Tricky if statements +ex_list = [1, 23, 421, 32] +if 1 == 2: + for num in ex_list: + print(num) +else: + print(ex_list) + + +# Loops ending prematurely +ex_list = [1, 23, 421, 32] +counter = 0 +for num in ex_list: + counter += 1 + print(num) + if counter == 1: + break diff --git a/3_advanced/chapter15/practice/ch15_practice1.py b/3_advanced/chapter15/practice/ch15_practice1.py new file mode 100644 index 00000000..d605dbe0 --- /dev/null +++ b/3_advanced/chapter15/practice/ch15_practice1.py @@ -0,0 +1,17 @@ +""" +The following code is not meant to be run because +there's no input. Instead, analyze it's running time +in terms of Big-O. The first two lines are already +analyzed for you. Do the same for all the other lines. +The input of the problem is ex_list, and assume it has +n elements. At the end, put the total running time of +code. + + +ex_list = [?,?,?...] #Input,O(1) +num_even = 0 #O(1) +for num in ex_list: + if num % 2 == 0: + num_even += 1 +print(num_even) +""" diff --git a/3_advanced/chapter15/practice/ch15_practice2.py b/3_advanced/chapter15/practice/ch15_practice2.py new file mode 100644 index 00000000..9e886b2e --- /dev/null +++ b/3_advanced/chapter15/practice/ch15_practice2.py @@ -0,0 +1,19 @@ +""" +The following code is not meant to be run because +there's no input. Instead, analyze it's running time +in terms of Big-O. The first two lines are already +analyzed for you. Do the same for all the other lines. +The input of the problem is ex_list, and assume it has +n elements. At the end, put the total running time of +code. + + +# ex_list = [?,?,?,...]#Input,O(1) +for i in range(2): # O(1) + ex_list.insert(0, 1) + ex_list.append(1) +for number in ex_list: + for number in ex_list: + break + break +""" diff --git a/3_advanced/chapter15/practice/ch15_practice3.py b/3_advanced/chapter15/practice/ch15_practice3.py new file mode 100644 index 00000000..582bf665 --- /dev/null +++ b/3_advanced/chapter15/practice/ch15_practice3.py @@ -0,0 +1,20 @@ +""" +The following code is not meant to be run because +there's no input. Instead, analyze it's running time +in terms of Big-O. The first two lines are already +analyzed for you. Do the same for all the other lines. +The input of the problem is ex_list, and assume it +has n elements. At the end, put the total running +time of code. + + +ex_list = [?,?,?,...]#Input,O(1) +for i in range(len(ex_list)):#O(n) + if i%2 == 0: + print(1) + else: + print(2) + for j in range(len(ex_list)): + for k in range(len(ex_list)): + print(j,k) +""" diff --git a/3_advanced/chapter15/practice/ch15_practice4.py b/3_advanced/chapter15/practice/ch15_practice4.py new file mode 100644 index 00000000..ebf66abd --- /dev/null +++ b/3_advanced/chapter15/practice/ch15_practice4.py @@ -0,0 +1,18 @@ +""" +The following code is not meant to be run because +there's no input. Instead, analyze it's running time +in terms of Big-O. The first two lines are already +analyzed for you. Do the same for all the other lines. +The input of the problem is ex_list, and assume it +has n elements. At the end, put the total running +time of code. Note: You will be surprised! + + +ex_list = [?,?,?,...]#Input,O(1) +for i in range(len(ex_list)):#O(n) + print(i) + ex_list.append(i) +for i in ex_list: + print(i) + ex_list.append(i) +""" diff --git a/3_advanced/chapter15/practice/ch15_practice5.py b/3_advanced/chapter15/practice/ch15_practice5.py new file mode 100644 index 00000000..8e456b10 --- /dev/null +++ b/3_advanced/chapter15/practice/ch15_practice5.py @@ -0,0 +1,18 @@ +""" +The following code is not meant to be run because +there's no input. Instead, analyze it's running time +in terms of Big-O. The first two lines are already +analyzed for you. Do the same for all the other lines. +At the end, put the total running time of code. +The input of the problem is ex_2d_list, and assume +it has n numbers. This problem assumes you have the +knowledge of 2D Lists. + + +ex_2d_list = [[?,?,?],[?,?]...]#Input,O(1) +list_sum = 0#O(1) +for ex_1d_list in ex_2d_list: + for element in ex_1d_list: + list_sum += element +print(list_sum) +""" diff --git a/3_advanced/chapter15/solutions/ch15_practice1.py b/3_advanced/chapter15/solutions/ch15_practice1.py new file mode 100644 index 00000000..b324171b --- /dev/null +++ b/3_advanced/chapter15/solutions/ch15_practice1.py @@ -0,0 +1,18 @@ +""" +The following code is not meant to be run because +there's no input. Instead, analyze it's running time +in terms of Big-O. The first two lines are already +analyzed for you. Do the same for all the other lines. +The input of the problem is ex_list, and assume it has +n elements. At the end, put the total running time of +code. + + +ex_list = [?,?,?...] #Input,O(1) +num_even = 0 #O(1) +for num in ex_list: #O(n) + if num % 2 == 0: #O(1) + num_even += 1 #O(1) +print(num_even) #O(1) +#Total running time = O(n) +""" diff --git a/3_advanced/chapter15/solutions/ch15_practice2.py b/3_advanced/chapter15/solutions/ch15_practice2.py new file mode 100644 index 00000000..d193e4e5 --- /dev/null +++ b/3_advanced/chapter15/solutions/ch15_practice2.py @@ -0,0 +1,20 @@ +""" +The following code is not meant to be run because +there's no input. Instead, analyze it's running time +in terms of Big-O. The first two lines are already +analyzed for you. Do the same for all the other lines. +The input of the problem is ex_list, and assume it has +n elements. At the end, put the total running time of +code. + + +ex_list = [?,?,?,...]#Input,O(1) +for i in range(2):#O(1) + ex_list.insert(0,1)#O(n) + ex_list.append(1)#O(1) +for number in ex_list:#O(1) + for number in ex_list:#O(1) + break#O(1) + break#O(1) +#Total running time = O(n) +""" diff --git a/3_advanced/chapter15/solutions/ch15_practice3.py b/3_advanced/chapter15/solutions/ch15_practice3.py new file mode 100644 index 00000000..94f6739d --- /dev/null +++ b/3_advanced/chapter15/solutions/ch15_practice3.py @@ -0,0 +1,21 @@ +""" +The following code is not meant to be run because +there's no input. Instead, analyze it's running time +in terms of Big-O. The first two lines are already +analyzed for you. Do the same for all the other lines. +The input of the problem is ex_list, and assume it +has n elements. At the end, put the total running +time of code. + + +ex_list = [?,?,?,...]#Input,O(1) +for i in range(len(ex_list)):#O(n) + if i%2 == 0:#O(1) + print(1)#O(1) + else:#O(1) + print(2)#O(1) + for j in range(len(ex_list)):#O(n) + for k in range(len(ex_list)):#O(n) + print(j,k)#O(1) +#Total running time = O(n^3) +""" diff --git a/3_advanced/chapter15/solutions/ch15_practice4.py b/3_advanced/chapter15/solutions/ch15_practice4.py new file mode 100644 index 00000000..29e88c25 --- /dev/null +++ b/3_advanced/chapter15/solutions/ch15_practice4.py @@ -0,0 +1,28 @@ +""" +The following code is not meant to be run because +there's no input. Instead, analyze it's running time +in terms of Big-O. The first two lines are already +analyzed for you. Do the same for all the other lines. +The input of the problem is ex_list, and assume it +has n elements. At the end, put the total running +time of code. Note: You will be surprised! + + +ex_list = [?,?,?,...]#Input,O(1) +for i in range(len(ex_list)):#O(n) + print(i)#O(1) + ex_list.append(i)#O(1) +for i in ex_list:#No Big-O. Runs forever. + print(i)#O(1) + ex_list.append(i)#O(1) +#Total running time = There is no upper bound, so +#no Big-O. +# +#Explanation: The second for loop will keep looping +#since ex_list will keep increasing in size each time +#you loop. You may ask why doesn't the first for loop do +#the same? That is because the number of times the first +#for loop loops is set at the very start of the loop, +#whereas for the second for loop will keep looping until +#every element is checked. +""" diff --git a/3_advanced/chapter15/solutions/ch15_practice5.py b/3_advanced/chapter15/solutions/ch15_practice5.py new file mode 100644 index 00000000..a56efd4d --- /dev/null +++ b/3_advanced/chapter15/solutions/ch15_practice5.py @@ -0,0 +1,23 @@ +""" +The following code is not meant to be run because +there's no input. Instead, analyze it's running time +in terms of Big-O. The first two lines are already +analyzed for you. Do the same for all the other lines. +At the end, put the total running time of code. +The input of the problem is ex_2d_list, and assume +it has n numbers. This problem assumes you have the +knowledge of 2D Lists. + + +ex_2d_list = [[?,?,?],[?,?]...]#Input,O(1) +list_sum = 0#O(1) +for ex_1d_list in ex_2d_list:#This line and next line combined = O(n) + for element in ex_1d_list: + list_sum += element#O(1) +print(list_sum)#O(1) +#Total running time = O(n) +# +#Explanation: We are finding the running time in terms of the input. +#The whole 2d list has n elements so the double for loop will loop +#n times in total. +""" diff --git a/3_advanced/chapter16/examples/Selection Sort Code.py b/3_advanced/chapter16/examples/Selection Sort Code.py new file mode 100644 index 00000000..dc919a08 --- /dev/null +++ b/3_advanced/chapter16/examples/Selection Sort Code.py @@ -0,0 +1,9 @@ +arr = [1, 4, 2, 7, 7, 6] # change this array to the array you want to sort +for first_idx in range(len(arr)): + min_idx = first_idx + for second_idx in range(first_idx + 1, len(arr)): + if arr[second_idx] < arr[min_idx]: + min_idx = second_idx + arr[first_idx], arr[min_idx] = arr[min_idx], arr[first_idx] + +print(arr) diff --git a/3_advanced/chapter16/examples/Selection_Sort_analyzed.py b/3_advanced/chapter16/examples/Selection_Sort_analyzed.py new file mode 100644 index 00000000..a37ce68a --- /dev/null +++ b/3_advanced/chapter16/examples/Selection_Sort_analyzed.py @@ -0,0 +1,18 @@ +arr = [int, int, int] # this is the input, so we're not analyzing it + +for first_idx in range(len(arr)): # O(n) + min_idx = first_idx # O(1) * O(n) = O(n) + + for second_idx in range(first_idx + 1, len(arr)): # O(n) * O(n) = O(n^2) + if arr[second_idx] < arr[min_idx]: # O(1) * O(n) * O(n) = O(n^2) + min_idx = second_idx # O(1) * O(n) * O(n) = O(n^2) + + arr[first_idx], arr[min_idx] = ( + arr[min_idx], + arr[first_idx], + ) # O(1) * O(n) = O(n) + + +# Sum = O(n) + O(n) + O(n^2) + O(n^2) + O(n^2) + O(n) +# Sum = 3*O(n) + 3*O(n^2) +# Final Running Time = O(n^2) diff --git a/3_advanced/chapter16/practice/selection_sort_even.py b/3_advanced/chapter16/practice/selection_sort_even.py new file mode 100644 index 00000000..2be5473c --- /dev/null +++ b/3_advanced/chapter16/practice/selection_sort_even.py @@ -0,0 +1,18 @@ +""" +The Selection Sort code we saw sorts an array from least to greatest. +Modify this code so that the code sorts only the elements at the even +indexes, ignoring elements at odd indexes. + +Selection Sort Code: + +arr = [?,?,?]#change this array to the array you want to sort +for first_idx in range(len(arr)): + min_idx = first_idx + for second_idx in range(first_idx+1, len(arr)): + if arr[second_idx] < arr[min_idx]: + min_idx = second_idx + arr[first_idx], arr[min_idx] = arr[min_idx], arr[first_idx] + +""" + +# write your code below diff --git a/3_advanced/chapter16/practice/selection_sort_f3.py b/3_advanced/chapter16/practice/selection_sort_f3.py new file mode 100644 index 00000000..3fb8c4d5 --- /dev/null +++ b/3_advanced/chapter16/practice/selection_sort_f3.py @@ -0,0 +1,18 @@ +""" +The Selection Sort code we saw sorts an array from least to greatest. +Modify this code so that the code sorts only the first three elements +of an array. + +Selection Sort Code: + +arr = [?,?,?]#change this array to the array you want to sort +for first_idx in range(len(arr)): + min_idx = first_idx + for second_idx in range(first_idx+1, len(arr)): + if arr[second_idx] < arr[min_idx]: + min_idx = second_idx + arr[first_idx], arr[min_idx] = arr[min_idx], arr[first_idx] + +""" + +# write your code below diff --git a/3_advanced/chapter16/practice/selection_sort_gtl.py b/3_advanced/chapter16/practice/selection_sort_gtl.py new file mode 100644 index 00000000..52459438 --- /dev/null +++ b/3_advanced/chapter16/practice/selection_sort_gtl.py @@ -0,0 +1,17 @@ +""" +The Selection Sort code we saw sorts an array from least to greatest. +Modify the code so that the code sorts an array from greatest to least. + +Selection Sort Code: + +arr = [?,?,?]#change this array to the array you want to sort +for first_idx in range(len(arr)): + min_idx = first_idx + for second_idx in range(first_idx+1, len(arr)): + if arr[second_idx] < arr[min_idx]: + min_idx = second_idx + arr[first_idx], arr[min_idx] = arr[min_idx], arr[first_idx] + +""" + +# write your code below diff --git a/3_advanced/chapter16/solutions/selection_sort_even.py b/3_advanced/chapter16/solutions/selection_sort_even.py new file mode 100644 index 00000000..5e23944e --- /dev/null +++ b/3_advanced/chapter16/solutions/selection_sort_even.py @@ -0,0 +1,28 @@ +""" +The Selection Sort code we saw sorts an array from least to greatest. +Modify this code so that the code sorts only the elements at the even +indexes, ignoring elements at odd indexes. + +Selection Sort Code: + +arr = [?,?,?]#change this array to the array you want to sort +for first_idx in range(len(arr)): + min_idx = first_idx + for second_idx in range(first_idx+1, len(arr)): + if arr[second_idx] < arr[min_idx]: + min_idx = second_idx + arr[first_idx], arr[min_idx] = arr[min_idx], arr[first_idx] + +""" + +# write your code below + +arr = [4, 1, 2, 5, 123, 98, 23] +for first_idx in range(0, len(arr), 2): # range(start, stop, step) + min_idx = first_idx + for second_idx in range(first_idx, len(arr), 2): + if arr[second_idx] < arr[min_idx]: + min_idx = second_idx + arr[first_idx], arr[min_idx] = arr[min_idx], arr[first_idx] + +print(arr) diff --git a/3_advanced/chapter16/solutions/selection_sort_f3.py b/3_advanced/chapter16/solutions/selection_sort_f3.py new file mode 100644 index 00000000..8fabd885 --- /dev/null +++ b/3_advanced/chapter16/solutions/selection_sort_f3.py @@ -0,0 +1,30 @@ +""" +The Selection Sort code we saw sorts an array from least to greatest. +Modify this code so that the code sorts only the first three elements +of an array. + +Selection Sort Code: + +arr = [?,?,?]#change this array to the array you want to sort +for first_idx in range(len(arr)): + min_idx = first_idx + for second_idx in range(first_idx+1, len(arr)): + if arr[second_idx] < arr[min_idx]: + min_idx = second_idx + arr[first_idx], arr[min_idx] = arr[min_idx], arr[first_idx] + +""" + +# write your code below + +arr = [4, 1, 2, 5, 123, 98, 23] +f3_arr = arr[:3] # this will contains the elements before the 3rd index. +remaining_arr = arr[3:] # this will be [] if original arr <= 3 +for first_idx in range(len(f3_arr)): + min_idx = first_idx + for second_idx in range(first_idx + 1, len(f3_arr)): + if f3_arr[second_idx] < f3_arr[min_idx]: + min_idx = second_idx + f3_arr[first_idx], f3_arr[min_idx] = f3_arr[min_idx], f3_arr[first_idx] + +print(f3_arr + remaining_arr) # adding lists will combine the lists diff --git a/3_advanced/chapter16/solutions/selection_sort_gtl.py b/3_advanced/chapter16/solutions/selection_sort_gtl.py new file mode 100644 index 00000000..425e064b --- /dev/null +++ b/3_advanced/chapter16/solutions/selection_sort_gtl.py @@ -0,0 +1,26 @@ +""" +The Selection Sort code we saw sorts an array from least to greatest. +Modify the code so that the code sorts an array from greatest to least. + +Selection Sort Code: + +arr = [?,?,?]#change this array to the array you want to sort +for first_idx in range(len(arr)): + min_idx = first_idx + for second_idx in range(first_idx+1, len(arr)): + if arr[second_idx] < arr[min_idx]: + min_idx = second_idx + arr[first_idx], arr[min_idx] = arr[min_idx], arr[first_idx] + +""" + +# write your code below + +arr = [1, 27, 412, 3, 12, 4] +for first_idx in range(len(arr)): + min_idx = first_idx + for second_idx in range(first_idx + 1, len(arr)): + if arr[second_idx] > arr[min_idx]: + min_idx = second_idx + arr[first_idx], arr[min_idx] = arr[min_idx], arr[first_idx] +print(arr) diff --git a/3_advanced/chapter17/examples/Maybe Not a Set.py b/3_advanced/chapter17/examples/Maybe Not a Set.py new file mode 100644 index 00000000..04383cf9 --- /dev/null +++ b/3_advanced/chapter17/examples/Maybe Not a Set.py @@ -0,0 +1,2 @@ +maybe_not_a_set = {} +print(type(maybe_not_a_set)) diff --git a/3_advanced/chapter17/examples/sets.py b/3_advanced/chapter17/examples/sets.py new file mode 100644 index 00000000..7cb86a04 --- /dev/null +++ b/3_advanced/chapter17/examples/sets.py @@ -0,0 +1,54 @@ +# initialize a set with the following syntax +myset = {"item 1", "item 2", "any type besides list and dict", 5, 8, 3} +# or +myothset = set() # creates an empty set +notaset = {} # doesn't create a set; creates a dict; remember that + +# sets don't always print the same way +# try it +print(myset) +print(myset) +print(myset) +# for this reason, indexing won't work with sets +# try it +anotherset = {"this", "might", "not", "be", "in", "order"} +for i in anotherset: + print(i) + +# subsets and supersets +# a superset is a set that has all the items that a subset has +oursuperset = {1, 3, 5, 7, 9} +oursubset = {1, 5, 3} + +# should print True since oursuperset has 1,5, and 3 +print(oursuperset.issuperset(oursubset)) +# should print True since oursuperset has 1,5, and 3 +print(oursubset.issubset(oursuperset)) +# should print False since oursubset doesn't have 7 or 9 +print(oursubset.issuperset(oursuperset)) + +# set methods +# includes .intersection , .union , .difference , .symettricdifference +set1 = {1, 2, 3, 4, 5} +set2 = {3, 4, 5, 6, 7} + +# will print 3,4,5 which is the common items (the 'intersection') +print(set1.intersection(set2)) +# will print 1,2 which is the different items in set 1 (the 'difference') +print(set1.difference(set2)) +# will print 6,7 which is the difference items in set 2 (the 'difference') +print(set2.difference(set1)) +# will print 1,2,6,7 since those are the different items in both +print(set1.symmetric_difference(set2)) +# will print 1,2,3,4,5,6,7 since those are the unique items +print(set1.union(set2)) + +# set methods (cont.) +# also includes .add , .discard , .remove , .pop , .update +# note: .discard and .remove are similar; check the comments +a_set = set() +a_set.add(2) # can only add 1 element +a_set.update([8, 9, 7]) # is a union between a_set and {8,9,7} +a_set.remove(8) # removes 8; if 8 isn't there, raises a key error +a_set.discard(9) # removes 9; if 9 isn't there, do nothing (no error) +a_set.pop() # removes the first element; in this case 2 diff --git a/3_advanced/chapter17/examples/tuples.py b/3_advanced/chapter17/examples/tuples.py new file mode 100644 index 00000000..cc41a975 --- /dev/null +++ b/3_advanced/chapter17/examples/tuples.py @@ -0,0 +1,33 @@ +# initializing a tuple +mytuple = () # is an empty tuple +mytuple = tuple() # also an empty tuple +myothtuple = (1,) # tuples with just 1 item need a comma at the end +moretuple = (4, 6, 3, {5, 6}, [7]) # valid; tuples accept all types + + +# these all work and will run with no error +tup = tuple([2, 4, 6, 8]) # creates a tuple out of the list +tup = tuple("tuple") # creates a tuple out of the string +tup = tuple({"a": "A", "b": "B"}) # creates a tuple out of the dict +# note: it creates the tuple out of the dict's keys, not values +tup = tuple({2, 4, 6, 8}) # creates a tuple out of the set +tup = 2, 4, 6, 8 # you don't even need parentheses +# however, you need at least one element in the tuple to do this + + +# modifying a tuple +# you can't modify a tuple's main elements +anothtuple = (4, 56, 7, [4, 6, 8]) +# what won't work: anothtuple[2] = 3 +# will produce an error +# what will work: +anothtuple[3][0] = 6 +# this works since you you're modifying the list's elements, not the tuple's + + +# tuple methods +# includes .index and .count +lasttupexample = (4, 6, 8, 10, 4, 2) +print(lasttupexample.count(4)) # prints 2 since there are two 4's +print(lasttupexample.index(4)) +# ^works just like list indexes; prints 0 which = the first occurence diff --git a/3_advanced/chapter17/practice/challenge (hard math + python)/disjoint_set_union.py b/3_advanced/chapter17/practice/challenge (hard math + python)/disjoint_set_union.py new file mode 100644 index 00000000..ede8aff9 --- /dev/null +++ b/3_advanced/chapter17/practice/challenge (hard math + python)/disjoint_set_union.py @@ -0,0 +1,22 @@ +# Given n number of cities (essentially a list of cities numbered +# from 0 to n-1), and a list of “bridges” with some arbitrary cost +# (how expensives that bridge is to build) which can connect two +# cities (you can move both directions along it), finda set of bridges +# such that when built you can reach any city from any starting city +# and that the combined cost of building the bridges is the cheapest +# possible. (this problem is called the minimum spanning tree) It +# should return the minimum total cost of building such a set of +# bridges (don’t return which those bridges are) + + +# numberOfCities: number of cities that needs to be connected +# possibleBridges: a vector containing a vectors each of size 3 +# which denotes [cityA, cityB, cost] + + +def findMinimumCost(numberOfCities, possibleBridges): + # put your code here; remove pass + pass + + +edges = [[0, 1, 1], [0, 2, 1], [1, 2, 2]] diff --git a/3_advanced/chapter17/practice/challenge (hard math + python)/linear_independence_checker.py b/3_advanced/chapter17/practice/challenge (hard math + python)/linear_independence_checker.py new file mode 100644 index 00000000..9ae4cbee --- /dev/null +++ b/3_advanced/chapter17/practice/challenge (hard math + python)/linear_independence_checker.py @@ -0,0 +1,31 @@ +# In linear algebra, a vector (list) of n vectors (lists) each +# containing n integers are considered linearly independent if we +# solve the following equation +# a * x + a * x + a * x + a * x = 0 +# 1 1 2 2 3 3 n n +# for where they can be any real constant, and we find that all of +# them equal to 0 being the only possible solution for this system +# of equations (this is called the trivial solution). Write a method +# which takes a vector of n vectors each of size n and determine if +# they are linearly independent. Return true if linearly independent, +# and false otherwise. +# (for this question, assume n is less than equal to 4 and greater than 1) + +# Note: Another way we can determine if n vectors is linearly independent is if +# you find that the matrix formed by concatenating the vectors has a nonzero +# determinant. This might be somewhat easier +# (search up calculating “determinant using cofactor expansion”). + + +def calculateDeterminant(matrix): + # put your code here; remove "pass" + pass + + +def testLinearIndependence(mat): + return calculateDeterminant(mat) != 0 + + +# mat is an example of an acceptable list of 3 (n) lists each containing +# 3 (n) integers; hint: if you use mat, it should give you True +mat = [[1, 1, 4], [0, 0, 5], [0, 7, 8]] diff --git a/3_advanced/chapter17/practice/challenge (hard math + python)/powerset_generator.py b/3_advanced/chapter17/practice/challenge (hard math + python)/powerset_generator.py new file mode 100644 index 00000000..503d9dba --- /dev/null +++ b/3_advanced/chapter17/practice/challenge (hard math + python)/powerset_generator.py @@ -0,0 +1,10 @@ +# Given a set of distinct integers (between 1 and 15 inclusive) +# (ex: {3,6,2,1}), return an array of all possible subsets that can be +# formed using the elements in the set. This also includes the empty +# set and the entire set itself. +# The set of all such subsets is referred to as the powerset. + + +def generatePowerset(elements): + # put your code here; remove "pass" + pass diff --git a/3_advanced/chapter17/practice/compute_similarity.py b/3_advanced/chapter17/practice/compute_similarity.py new file mode 100644 index 00000000..197eefa2 --- /dev/null +++ b/3_advanced/chapter17/practice/compute_similarity.py @@ -0,0 +1,11 @@ +# Given two sets of integers A and B (each element in these sets are +# between 1 and 1000 inclusive), find the similarity of the two sets +# (the sets are guaranteed to be nonempty). The similarity is a number +# which is computed by dividing the size of the intersection of the +# two sets by their union size. +# Note: the intersection is the # of elements that both sets have in common. + + +def compute_similarity(set1, set2): + # put your code here; remove "pass" + pass diff --git a/3_advanced/chapter17/practice/countable_set.py b/3_advanced/chapter17/practice/countable_set.py new file mode 100644 index 00000000..5bf37d82 --- /dev/null +++ b/3_advanced/chapter17/practice/countable_set.py @@ -0,0 +1,27 @@ +# Create a class called CountableSet which stores the number of times each +# element has been inserted into the CountableSet object. +# (Basically, store it like element: #of times inserted into Countable Set) +# Implement the following class: + + +class CountableSet: + def __init__( + self, objs + ): # objs is the initial list of objects to be inserted + pass + + # insert x into the set one time, increment the count by one + # returns True if map didn't already contain the key + # x is the integer to be inserted + def insert(self, x): + pass + + # decrement the count by one, returns True if map contains key + # x is integer to be deleted + def delete(self, x): + pass + + # should return the number of times x has been inserted into the set + # x is the integer being queried + def get(self, x): + pass diff --git a/3_advanced/chapter17/practice/duplicate_detector.py b/3_advanced/chapter17/practice/duplicate_detector.py new file mode 100644 index 00000000..fbd2f9f7 --- /dev/null +++ b/3_advanced/chapter17/practice/duplicate_detector.py @@ -0,0 +1,8 @@ +# Create a program that finds whether a list contains duplicates +# should return True or False +# use sets in your code + + +def dup_detector(item): + # put your code here and remove "pass" + pass diff --git a/3_advanced/chapter17/practice/ice_cream_shop.py b/3_advanced/chapter17/practice/ice_cream_shop.py new file mode 100644 index 00000000..cd813e68 --- /dev/null +++ b/3_advanced/chapter17/practice/ice_cream_shop.py @@ -0,0 +1,19 @@ +""" +An ice cream shop keeps all its available flavours +in a set. Every month, it gets a list of all the +flavours that are no longer available. Return +a new set containing the available flavours this +month after all the no-longer-available flavours +are removed. It is possible to solve this problem +using either discard or remove. +""" + + +def remove_flavours(avail_flavs, no_longer_flavs): + pass + # Remove pass and write your code in here + + +avail_flavs = {"Strawberry", "Blueberry", "Vanilla"} +remove_flavours(avail_flavs, ["Strawberry", "Vanilla"]) +print(avail_flavs) diff --git a/3_advanced/chapter17/practice/magic_tuple_number.py b/3_advanced/chapter17/practice/magic_tuple_number.py new file mode 100644 index 00000000..f4faa35e --- /dev/null +++ b/3_advanced/chapter17/practice/magic_tuple_number.py @@ -0,0 +1,17 @@ +""" +A magic tuple number for a given element is the +index of the given element’s first occurrence in +the tuple TIMES the number of occurrences of +the given element in the tuple. Given an element +and a tuple, return the magic tuple number. If +the element does not exist in the tuple, return -1. +""" + + +def magic_tuple_number(given_tup, given_elem): + pass + # Remove pass and write your code in here + + +print(magic_tuple_number((1, 3, "Two", 3), 3)) # prints 2 +print(magic_tuple_number((1, 3, "Bob"), "Cat")) # prints -1 diff --git a/3_advanced/chapter17/practice/min_superset.py b/3_advanced/chapter17/practice/min_superset.py new file mode 100644 index 00000000..1a7be908 --- /dev/null +++ b/3_advanced/chapter17/practice/min_superset.py @@ -0,0 +1,11 @@ +# Given 3 sets of distinct integers, return the size of the superset +# of minimum size which is the superset of all the given sets. +# Implement the following method: + + +def findMinSupersetLength(sets): + # code here; remove "pass" + pass + + +sets = [{1, 2, 3}, {2, 3, 5}, {1, 3, 6}] diff --git a/3_advanced/chapter17/practice/modify_tuple.py b/3_advanced/chapter17/practice/modify_tuple.py new file mode 100644 index 00000000..b758bb83 --- /dev/null +++ b/3_advanced/chapter17/practice/modify_tuple.py @@ -0,0 +1,14 @@ +# Given a tuple whose first two elements are +# strings and the third element is a dictionary. +# Add the given key and value to the dictionary +# in the tuple. + + +def modify_tuple(given_tuple, given_key, given_val): + pass + # Remove pass and write your code in here + + +given_tuple = ("Ken", "Kaneki", {1: "Apple"}) +modify_tuple(given_tuple, 2, "Orange") +print(given_tuple) # Dictionary should now be updated diff --git a/3_advanced/chapter17/practice/odd_set_day.py b/3_advanced/chapter17/practice/odd_set_day.py new file mode 100644 index 00000000..8a09dcda --- /dev/null +++ b/3_advanced/chapter17/practice/odd_set_day.py @@ -0,0 +1,18 @@ +""" +Given a set, remove all the even numbers from +it, and for each even number removed, add +"Removed [insert the even number you removed]". +Example: {1,54, 2, 5} becomes {"Removed 54", 1, +5, "Removed 2"}. It is possible to solve this +problem using either discard or remove. +""" + + +def odd_set_day(given_set): + pass + # Remove pass and write your code in here + + +given_set = {1, 2, 4, 5} +odd_set_day(given_set) +print(given_set) diff --git a/3_advanced/chapter17/practice/only_fav_movies.py b/3_advanced/chapter17/practice/only_fav_movies.py new file mode 100644 index 00000000..4c9215d9 --- /dev/null +++ b/3_advanced/chapter17/practice/only_fav_movies.py @@ -0,0 +1,23 @@ +""" +Charles is going to the movie today and wants +to figure out if all the movies played today +are his favorite movies. Given a set containing +the movies that are going to be played today +and a set containing all his favorite movies, +return True if all the movies played today are +his favorite. Return False otherwise. +""" + + +def only_fav_movies(movies_today, favorite_movies): + pass + # Remove pass and write your code in here + + +favorite_movies = {"Home Alone", "Star Wars", "Pokemon"} +print( + only_fav_movies({"Home Alone", "Star Wars"}, favorite_movies) +) # Prints True +print( + only_fav_movies({"Spider Man", "Home Alone"}, favorite_movies) +) # Prints False diff --git a/3_advanced/chapter17/practice/open_mind.py b/3_advanced/chapter17/practice/open_mind.py new file mode 100644 index 00000000..cc602224 --- /dev/null +++ b/3_advanced/chapter17/practice/open_mind.py @@ -0,0 +1,40 @@ +""" +Two brothers come together to watch TV everyday. +On weekdays(monday through thursday), they are +not open minded and only watch what they like to +watch themselves. Due to this, it is possible +for nothing to be watched. On weekends(friday +through sunday), they are open minded and would +watch what they themselves like to watch and what +the others like to watch even if they themselves +don’t like to watch that specific TV show. +Assume, all the TV shows the brothers like will +be played everyday. Given a day(1-4 represents +weekday and 5-7 represents weekend) and two +sets(what the brothers each like to watch), +return the set of possible TV shows the brothers +would both watch on that day. +""" + + +def open_mind(first_bro_set, second_bro_set, day): + pass + # Remove pass and write your code in here + + +first_bro_set = { + "pokemon", + "regular show", + "ben 10", + "adventure time", + "mega man", +} +second_bro_set = { + "ben 10", + "powerpuff girls", + "curious george", + "arthur", + "mega man", +} +print(open_mind(first_bro_set, second_bro_set, 3)) +print(open_mind(first_bro_set, second_bro_set, 7)) diff --git a/3_advanced/chapter17/practice/set_creator.py b/3_advanced/chapter17/practice/set_creator.py new file mode 100644 index 00000000..3a5e35dd --- /dev/null +++ b/3_advanced/chapter17/practice/set_creator.py @@ -0,0 +1,12 @@ +# Create an empty set and print the type of it. Create a +# set from a given dictionary(do set(given_dict)) and print it. +# Note: The set created from the given dictionary contains +# only the keys of the dictionary. + + +def set_creator(given_dict): + pass + # Remove pass and write your code in here + + +set_creator({1: "Wall Street", 2: "Main Street", "Tower": 3}) diff --git a/3_advanced/chapter17/practice/tuple_bears.py b/3_advanced/chapter17/practice/tuple_bears.py new file mode 100644 index 00000000..7c6d4a8b --- /dev/null +++ b/3_advanced/chapter17/practice/tuple_bears.py @@ -0,0 +1,16 @@ +# Fred had lost his teddy bear, so his parents are going to the store +# to buy a replacement for him. You are given a list of length 2 +# (2 elements) tuples, where each tuple represents a teddy bear. where +# the first element contains a number showing how similar that that bear is +# to Fred's original teddy bear(the smaller, the better), and the second +# element is a string of the teddy bear's name. +# Find the teddy bear closest to the one Fred lost, and print its name +# (don’t worry about tuples w/ same #) + + +def tuple_bears(item): + # put your code here and remove pass + pass + + +teddy_bears = [(5, "Freddy"), (3, "Runaway"), (7, "Killer"), (2, "Luscious")] diff --git a/3_advanced/chapter17/practice/tuple_creator.py b/3_advanced/chapter17/practice/tuple_creator.py new file mode 100644 index 00000000..05792021 --- /dev/null +++ b/3_advanced/chapter17/practice/tuple_creator.py @@ -0,0 +1,13 @@ +# Create an empty tuple and print the type of it. +# Create a tuple from any one element and print the type of it. +# Create a tuple from a given dictionary and print it. +# Note: The tuple created from a dictionary will only contain +# the keys of the dictionary + + +def tuple_creator(given_dict): + pass + # Remove pass and write your code in here + + +tuple_creator({1: "Wall Street", 2: "Main Street", "Tower": 3}) diff --git a/3_advanced/chapter17/solutions/challenge (hard math + python)/disjoint_set_union b/3_advanced/chapter17/solutions/challenge (hard math + python)/disjoint_set_union new file mode 100644 index 00000000..1ae9a1c8 --- /dev/null +++ b/3_advanced/chapter17/solutions/challenge (hard math + python)/disjoint_set_union @@ -0,0 +1,67 @@ +# Given n number of cities (essentially a list of cities numbered +# from 0 to n-1), and a list of “bridges” with some arbitrary cost +# (how expensives that bridge is to build) which can connect two +# cities (you can move both directions along it), finda set of bridges +# such that when built you can reach any city from any starting city +# and that the combined cost of building the bridges is the cheapest +# possible. (this problem is called the minimum spanning tree) It +# should return the minimum total cost of building such a set of +# bridges (don’t return which those bridges are) + + +# numberOfCities: number of cities that needs to be connected +# possibleBridges: a vector containing a vectors each of size 3 +# which denotes [cityA, cityB, cost] + +class DSU: + def __init__(self, size): + self.parents = [i for i in range(size)] + + def parent(self, n): + if (not(self.parents[n] == n)): + self.parents[n] = self.parent(self.parents[n]) + return self.parents[n] + + def isMerged(self, n1, n2): + return self.parent(n1) == self.parent(n2) + + def merge(self, n1, n2): + self.parents[self.parent(n1)] = self.parent(n2) + +# numberOfCities: number of cities that needs to be connected +# possibleBridges: a vector containing a vectors each of size 3 which denotes [cityA, cityB, cost] +def findMinimumCost(numberOfCities, possibleBridges): + size = numberOfCities + edges = possibleBridges + table = {} + weights = [] + dsu = DSU(size) + for edge in edges: + if edge[2] in table: + table[edge[2]].append([edge[0], edge[1]]) + else: + table[edge[2]] = [[edge[0], edge[1]]] + weights.append(edge[2]) + weights.sort() + total = 0 + merged = 0 + for weight in weights: + for edge in table[weight]: + n1 = edge[0] + n2 = edge[1] + if dsu.isMerged(n1, n2): + continue + dsu.merge(n1, n2) + total += weight + merged += 1 + if merged == size - 1: + return total + return total + +edges = [ + [0, 1, 1], + [0, 2, 1], + [1, 2, 2] +] + +print(findMinimumCost(3, edges)) diff --git a/3_advanced/chapter17/solutions/challenge (hard math + python)/linear_independence_checker.py b/3_advanced/chapter17/solutions/challenge (hard math + python)/linear_independence_checker.py new file mode 100644 index 00000000..485ae7b1 --- /dev/null +++ b/3_advanced/chapter17/solutions/challenge (hard math + python)/linear_independence_checker.py @@ -0,0 +1,50 @@ +# In linear algebra, a vector (list) of n vectors (lists) each +# containing n integers are considered linearly independent if we +# solve the following equation +# a * x + a * x + a * x + a * x = 0 +# 1 1 2 2 3 3 n n +# for where they can be any real constant, and we find that all of +# them equal to 0 being the only possible solution for this system +# of equations (this is called the trivial solution). Write a method +# which takes a vector of n vectors each of size n and determine if +# they are linearly independent. Return true if linearly independent, +# and false otherwise. +# (for this question, assume n is less than equal to 4 and greater than 1) + +# Note: Another way we can determine if n vectors is linearly independent is if +# you find that the matrix formed by concatenating the vectors has a nonzero +# determinant. This might be somewhat easier +# (search up calculating “determinant using cofactor expansion”). + + +def calculateDeterminant(matrix): + length = len(matrix) + if length == 2: + return matrix[0][0] * matrix[1][1] - matrix[1][0] * matrix[0][1] + cofactorLength = length - 1 + total = 0 + negativeFactor = 1 + for i in range(length): + matrixMinor = [ + [0 for k in range(cofactorLength)] for j in range(cofactorLength) + ] + for j in range(cofactorLength): + for k in range(cofactorLength): + if k >= i: + matrixMinor[j][k] = matrix[j + 1][k + 1] + else: + matrixMinor[j][k] = matrix[j + 1][k] + total = total + negativeFactor * matrix[0][i] * calculateDeterminant( + matrixMinor + ) + negativeFactor *= -1 + return total + + +def testLinearIndependence(mat): + return calculateDeterminant(mat) != 0 + + +mat = [[1, 1, 4], [0, 0, 5], [0, 7, 8]] + +print(testLinearIndependence(mat)) # prints True (determinant is equal to -35) diff --git a/3_advanced/chapter17/solutions/challenge (hard math + python)/powerset_generator b/3_advanced/chapter17/solutions/challenge (hard math + python)/powerset_generator new file mode 100644 index 00000000..f3ddf09c --- /dev/null +++ b/3_advanced/chapter17/solutions/challenge (hard math + python)/powerset_generator @@ -0,0 +1,21 @@ +# Given a set of distinct integers (between 1 and 15 inclusive) +# (ex: {3,6,2,1}), return an array of all possible subsets that can be +# formed using the elements in the set. This also includes the empty +# set and the entire set itself. +# The set of all such subsets is referred to as the powerset. + + +def generatePowerset(elements): + setSize = len(elements) + vec = list(elements) + limit = 1 << setSize # << is same as 1 * 2**setSize + result = [None for i in range(limit)] + for i in range(limit): + currentSet = set() + for j in range(setSize): + if ((1 << j) & i) > 0: # bitwise operations + currentSet.add(vec[j]) + result[i] = currentSet + return result + +print(generatePowerset({1, 2, 3})) diff --git a/3_advanced/chapter17/solutions/compute_similarity.py b/3_advanced/chapter17/solutions/compute_similarity.py new file mode 100644 index 00000000..4a1e662a --- /dev/null +++ b/3_advanced/chapter17/solutions/compute_similarity.py @@ -0,0 +1,18 @@ +# Given two sets of integers A and B (each element in these sets are +# between 1 and 1000 inclusive), find the similarity of the two sets +# (the sets are guaranteed to be nonempty). The similarity is a number +# which is computed by dividing the size of the intersection of the +# two sets by their union size. +# Note: the intersection is the # of elements that both sets have in common. + + +def computeSimilarity(set1, set2): + intersectionSize = 0 + for elem in set1: + if elem in set2: + intersectionSize += 1 + unionSize = len(set1) + len(set2) - intersectionSize + return intersectionSize / float(unionSize) + + +print(computeSimilarity({1, 2}, {1, 3})) diff --git a/3_advanced/chapter17/solutions/countable_set.py b/3_advanced/chapter17/solutions/countable_set.py new file mode 100644 index 00000000..c6944ae2 --- /dev/null +++ b/3_advanced/chapter17/solutions/countable_set.py @@ -0,0 +1,52 @@ +# Create a class called CountableSet which stores the number of times each +# element has been inserted into the CountableSet object. +# (Basically, store it like element: #of times inserted into Countable Set) +# Implement the following class: + + +class CountableSet: + def __init__( + self, objs + ): # objs is the initial list of objects to be inserted + self.elems = {} + for obj in objs: + self.insert(obj) + + # insert x into the set one time, increment the count by one + # returns True if map didn't already contain the key + # x is the integer to be inserted + def insert(self, x): + if x in self.elems.keys(): + self.elems[x] += 1 + return False + else: + self.elems[x] = 1 + return True + + # decrement the count by one, returns True if map contains key + # x is integer to be deleted + def delete(self, x): + if x in self.elems.keys(): + self.elems[x] -= 1 + if self.elems[x] == 0: + self.elems.pop(x) + return True + return False + + # returns the number of times x has been inserted into the set + # x is the integer being queried + def get(self, x): + if x in self.elems.keys(): + return self.elems[x] + return 0 + + +x = CountableSet([1, 2, 3, 1]) +x.insert(3) +print(x.elems) +x.insert(2) +print(x.elems) +x.delete(1) +print(x.elems) +x.delete(1) +print(x.elems) diff --git a/3_advanced/chapter17/solutions/duplicate_detector.py b/3_advanced/chapter17/solutions/duplicate_detector.py new file mode 100644 index 00000000..adf9a8a5 --- /dev/null +++ b/3_advanced/chapter17/solutions/duplicate_detector.py @@ -0,0 +1,14 @@ +# Create a program that finds whether a list contains duplicates +# should return True or False +# use sets in your code + + +def dup_detector(item): + theset = set(item) + if len(theset) < len(item): + return True + else: + return False + + +print(dup_detector([5, 4, 3, 2, 2])) # should print True diff --git a/3_advanced/chapter17/solutions/ice_cream_shop.py b/3_advanced/chapter17/solutions/ice_cream_shop.py new file mode 100644 index 00000000..36255fb4 --- /dev/null +++ b/3_advanced/chapter17/solutions/ice_cream_shop.py @@ -0,0 +1,19 @@ +""" +An ice cream shop keeps all its available flavours +in a set. Every month, it gets a list of all the +flavours that are no longer available. Return +a new set containing the available flavours this +month after all the no-longer-available flavours +are removed. It is possible to solve this problem +using either discard or remove. +""" + + +def remove_flavours(avail_flavs, no_longer_flavs): + for flav in no_longer_flavs: + avail_flavs.remove(flav) + + +avail_flavs = {"Strawberry", "Blueberry", "Vanilla"} +remove_flavours(avail_flavs, ["Strawberry", "Vanilla"]) +print(avail_flavs) diff --git a/3_advanced/chapter17/solutions/magic_tuple_number.py b/3_advanced/chapter17/solutions/magic_tuple_number.py new file mode 100644 index 00000000..048028b2 --- /dev/null +++ b/3_advanced/chapter17/solutions/magic_tuple_number.py @@ -0,0 +1,19 @@ +""" +A magic tuple number for a given element is the +index of the given element’s first occurrence in +the tuple TIMES the number of occurrences of +the given element in the tuple. Given an element +and a tuple, return the magic tuple number. If +the element does not exist in the tuple, return -1. +""" + + +def magic_tuple_number(given_tup, given_elem): + elem_count = given_tup.count(given_elem) + if elem_count != 0: + return given_tup.index(given_elem) * elem_count + return -1 + + +print(magic_tuple_number((1, 3, "Two", 3), 3)) # prints 2 +print(magic_tuple_number((1, 3, "Bob"), "Cat")) # prints -1 diff --git a/3_advanced/chapter17/solutions/min_superset.py b/3_advanced/chapter17/solutions/min_superset.py new file mode 100644 index 00000000..e7b55ff7 --- /dev/null +++ b/3_advanced/chapter17/solutions/min_superset.py @@ -0,0 +1,31 @@ +# Given 3 sets of distinct integers, return the size of the superset +# of minimum size which is the superset of all the given sets. +# Implement the following method: + + +# superset calcuated using Principle of Inclusion and Exclusion +# sets: a vector containing 3 sets +def findMinSupersetLength(sets): + total = 0 + for i in range(3): + total += len(sets[i]) + for i in range(3): + prevIndex = ((i - 1) + 3) % 3 + nextIndex = (i + 1) % 3 + total -= len(sets[prevIndex].intersection(sets[nextIndex])) + total += len(sets[0].intersection(sets[1]).intersection(sets[2])) + return total + + +sets = [{1, 2, 3}, {2, 3, 5}, {1, 3, 6}] + +print(findMinSupersetLength(sets)) + + +""" +Alternate, more understandable solution: +def findMinSupersetLength(sets): + a = sets[0] + a = a.union(sets[1]).union(set[2]) + return len(a) +""" diff --git a/3_advanced/chapter17/solutions/modify_tuple.py b/3_advanced/chapter17/solutions/modify_tuple.py new file mode 100644 index 00000000..46027594 --- /dev/null +++ b/3_advanced/chapter17/solutions/modify_tuple.py @@ -0,0 +1,13 @@ +# Given a tuple whose first two elements are +# strings and the third element is a dictionary. +# Add the given key and value to the dictionary +# in the tuple. + + +def modify_tuple(given_tuple, given_key, given_val): + given_tuple[2][given_key] = given_val + + +given_tuple = ("Ken", "Kaneki", {1: "Apple"}) +modify_tuple(given_tuple, 2, "Orange") +print(given_tuple) # Dictionary should now be updated diff --git a/3_advanced/chapter17/solutions/odd_set_day.py b/3_advanced/chapter17/solutions/odd_set_day.py new file mode 100644 index 00000000..431dd929 --- /dev/null +++ b/3_advanced/chapter17/solutions/odd_set_day.py @@ -0,0 +1,23 @@ +""" +Given a set, remove all the even numbers from +it, and for each even number removed, add +"Removed [insert the even number you removed]". +Example: {1,54, 2, 5} becomes {"Removed 54", 1, +5, "Removed 2"}. It is possible to solve this +problem using either discard or remove. +""" + + +def odd_set_day(given_set): + add_remove = [] + for elem in given_set: + if elem % 2 == 0: + add_remove.append(elem) + for remove in add_remove: + given_set.remove(remove) + given_set.add("Removed " + str(remove)) + + +given_set = {1, 2, 4, 5} +odd_set_day(given_set) +print(given_set) diff --git a/3_advanced/chapter17/solutions/only_fav_movies.py b/3_advanced/chapter17/solutions/only_fav_movies.py new file mode 100644 index 00000000..be33cc6e --- /dev/null +++ b/3_advanced/chapter17/solutions/only_fav_movies.py @@ -0,0 +1,29 @@ +""" +Charles is going to the movie today and wants +to figure out if all the movies played today +are his favorite movies. Given a set containing +the movies that are going to be played today +and a set containing all his favorite movies, +return True if all the movies played today are +his favorite. Return False otherwise. +""" + + +def only_fav_movies(movies_today, favorite_movies): + return movies_today.issubset(favorite_movies) + + +favorite_movies = {"Home Alone", "Star Wars", "Pokemon"} +print( + only_fav_movies({"Home Alone", "Star Wars"}, favorite_movies) +) # Prints True +print( + only_fav_movies({"Spider Man", "Home Alone"}, favorite_movies) +) # Prints False + + +# An Alternative Solution that is equally efficient is to +# check favorite_movies.issuperset(movies_today) + +# Another Alternative Solution that is less efficient is to +# check if every element of movies_today is in favorite_movies. diff --git a/3_advanced/chapter17/solutions/open_mind.py b/3_advanced/chapter17/solutions/open_mind.py new file mode 100644 index 00000000..cccdfd6e --- /dev/null +++ b/3_advanced/chapter17/solutions/open_mind.py @@ -0,0 +1,42 @@ +""" +Two brothers come together to watch TV everyday. +On weekdays(monday through thursday), they are +not open minded and only watch what they like to +watch themselves. Due to this, it is possible +for nothing to be watched. On weekends(friday +through sunday), they are open minded and would +watch what they themselves like to watch and what +the others like to watch even if they themselves +don’t like to watch that specific TV show. +Assume, all the TV shows the brothers like will +be played everyday. Given a day(1-4 represents +weekday and 5-7 represents weekend) and two +sets(what the brothers each like to watch), +return the set of possible TV shows the brothers +would both watch on that day. +""" + + +def open_mind(first_bro_set, second_bro_set, day): + if day <= 4: + return first_bro_set.intersection(second_bro_set) + else: + return first_bro_set.union(second_bro_set) + + +first_bro_set = { + "pokemon", + "regular show", + "ben 10", + "adventure time", + "mega man", +} +second_bro_set = { + "ben 10", + "powerpuff girls", + "curious george", + "arthur", + "mega man", +} +print(open_mind(first_bro_set, second_bro_set, 3)) +print(open_mind(first_bro_set, second_bro_set, 7)) diff --git a/3_advanced/chapter17/solutions/set_creator.py b/3_advanced/chapter17/solutions/set_creator.py new file mode 100644 index 00000000..9d2493d3 --- /dev/null +++ b/3_advanced/chapter17/solutions/set_creator.py @@ -0,0 +1,14 @@ +# Create an empty set and print the type of it. Create a +# set from a given dictionary(do set(given_dict)) and print it. +# Note: The set created from the given dictionary contains +# only the keys of the dictionary. + + +def set_creator(given_dict): + empty_set = set() + print(type(empty_set)) + dict_set = set(given_dict) + print(dict_set) + + +set_creator({1: "Wall Street", 2: "Main Street", "Tower": 3}) diff --git a/3_advanced/chapter17/solutions/tuple_bears.py b/3_advanced/chapter17/solutions/tuple_bears.py new file mode 100644 index 00000000..9154cad0 --- /dev/null +++ b/3_advanced/chapter17/solutions/tuple_bears.py @@ -0,0 +1,23 @@ +# Fred had lost his teddy bear, so his parents are going to the store +# to buy a replacement for him. You are given a list of length 2 +# (2 elements) tuples, where each tuple represents a teddy bear. where +# the first element contains a number showing how similar that that bear is +# to Fred's original teddy bear(the smaller, the better), and the second +# element is a string of the teddy bear's name. +# Find the teddy bear closest to the one Fred lost, and print its name +# (don’t worry about tuples w/ same #) + + +def tuple_bears(item): + ourmin = [item[0][0], item[0][1]] + for i in range(len(item)): + if ourmin[0] > item[i][0]: + ourmin[0] = item[i][0] + ourmin[1] = item[i][1] + i = 0 + return ourmin[1] + + +tuplelist = [(5, "Freddy"), (3, "Runaway"), (7, "Killer"), (2, "Luscious")] + +print(tuple_bears(tuplelist)) diff --git a/3_advanced/chapter17/solutions/tuple_creator.py b/3_advanced/chapter17/solutions/tuple_creator.py new file mode 100644 index 00000000..b702ef21 --- /dev/null +++ b/3_advanced/chapter17/solutions/tuple_creator.py @@ -0,0 +1,17 @@ +# Create an empty tuple and print the type of it. +# Create a tuple from any one element and print the type of it. +# Create a tuple from a given dictionary and print it. +# Note: The tuple created from a dictionary will only contain +# the keys of the dictionary + + +def tuple_creator(given_dict): + empty_tuple = () + print(type(empty_tuple)) + one_element_tuple = ("Blueberry",) + print(type(one_element_tuple)) + dict_tuple = tuple(given_dict) + print(dict_tuple) + + +tuple_creator({1: "Wall Street", 2: "Main Street", "Tower": 3}) diff --git a/3_advanced/chapter18/examples/Number Of a Factor.py b/3_advanced/chapter18/examples/Number Of a Factor.py new file mode 100644 index 00000000..65182bbe --- /dev/null +++ b/3_advanced/chapter18/examples/Number Of a Factor.py @@ -0,0 +1,23 @@ +# Code to figure out how many of a factor a number has + + +def number_factor(number, factor, factor_counter=0): + """ + Parameters: + 1)number is the number in which we are finding the number of + factors of. EX: 24 + 2)factor is the factor in which we are finding the number of + in the parameter number. EX: 2 + + Output: The number of times the parameter number can be divisible + by the parameter factor. This number is also the parameter + factor_counter right before it is returned. EX: 3 + """ + + if number % factor != 0: # Base Case + return factor_counter + else: # Recursive Case + return number_factor(number / factor, factor, factor_counter + 1) + + +print(number_factor(24, 2)) diff --git a/3_advanced/chapter18/examples/binary_search.py b/3_advanced/chapter18/examples/binary_search.py new file mode 100644 index 00000000..c14336b5 --- /dev/null +++ b/3_advanced/chapter18/examples/binary_search.py @@ -0,0 +1,31 @@ +# Code for binary search + + +def binary_search(arr, low, high, key): + """ + Parameters: + 1) arr is the sorted array in which we will be finding the element + 2) low is the lower bound of the interval in which we will + be finding the element index + 3) high is the upper bound of the interval in which we will + be finding the element index + 4) key is the element we are trying to find the index of + + Output: the index of the element key in the array arr. + If the element x does not exist in array arr, -1 will + be returned. + """ + + if high >= low: + mid = (high + low) // 2 + if arr[mid] == key: # Base Case 1 + return mid + elif arr[mid] < key: # Recursive Case 1 + return binary_search(arr, mid + 1, high, key) + else: # Recursive Case 2 (arr[mid] > x) + return binary_search(arr, low, mid - 1, key) + else: # Base Case 2: element not found + return -1 + + +print(binary_search([1, 24, 28, 30, 40, 52], 0, 5, 28)) diff --git a/3_advanced/chapter18/examples/fibonacci.py b/3_advanced/chapter18/examples/fibonacci.py new file mode 100644 index 00000000..d881582b --- /dev/null +++ b/3_advanced/chapter18/examples/fibonacci.py @@ -0,0 +1,79 @@ +# Fibonacci +# The Fibonacci sequence starts with 0 and 1. +# The next number in the sequence is the sum of the previous 2 numbers. +# Thus, the first 5 Fibonacci numbers are: 0, 1, 1, 2, 3. + + +def recursive_fib(n): + """ + Returns the nth number in the Fibonacci sequence recursively + + Args: + n (int): the position of the number in the Fibonacci sequence you want + + Returns: + int: the nth number in the Fibonacci sequence + For example, recursive_fib(5) will return 3 + """ + if n <= 0: # Base Case 1: out of bounds + return None + elif n == 1: # Base Case 2 + return 0 + elif n == 2: # Base Case 3 + return 1 + else: # Recursive Case + return recursive_fib(n - 1) + recursive_fib(n - 2) + + +def iterative_fib(n): + """ + Returns the nth number in the Fibonacci sequence iteratively + + Args: + n (int): the position of the number in the Fibonacci sequence you want + + Returns: + int: the nth number in the Fibonacci sequence + For example, iterative_fib(5) will return 3 + """ + if n <= 0: + return None # base case; out of bounds + + current = 0 + next_term = 1 + + for i in range(n - 1): # this is equivalent to for i in range(1, n) + current, next_term = next_term, current + next_term + # this is just a slightly rewritten fib sequence; + # instead of looking at the past 2 cases, it looks at the + # current and next terms to determine the next next term + + return current # will be 0 if n is 1, 1 if n is 2, etc... + + +def fib_sequence(n): + """ + Returns the fibonacci sequence as a list up to the nth fibonacci number + + Args: + n (int): the position of the number in the Fibonacci + sequence you want to go up to + + Returns: + list: the nth number in the Fibonacci sequence + For example, fib_sequence(5) will return [0, 1, 1, 2, 3] + + Adapted from: + https://medium.com/@danfcorreia/fibonacci-iterative-28b042a3eec + """ + sequence = [0, 1] + + for i in range(2, n): + sequence.append(sequence[i - 2] + sequence[i - 1]) + + return sequence + + +print("Recursive fib:", recursive_fib(5)) +print("Iterative fib:", iterative_fib(5)) +print("Fib sequence:", fib_sequence(5)) diff --git a/3_advanced/chapter18/examples/infinite_recursion.py b/3_advanced/chapter18/examples/infinite_recursion.py new file mode 100644 index 00000000..067371ba --- /dev/null +++ b/3_advanced/chapter18/examples/infinite_recursion.py @@ -0,0 +1,24 @@ +# notice how there is no base +# case, meaning no way out + + +def recurse(i): + i = i + 1 + print(i) + recurse(i) + + +recurse(0) # this will result in the following message: +# RecursionError: maximum recursion depth exceeded while +# calling a Python object + +# RecursionError happens when you exceed your maximum +# recursion limit. By default, it is set to 1000 +# you can check the maximum recursion depth by doing +# import sys +# sys.getrecursionlimit() +# you can change the maximum recursion depth by doing +# import sys +# sys.setrecursionlimit() +# However, this can be dangerous, so only do it if you +# know what you're doing. diff --git a/3_advanced/chapter18/practice/factorial.py b/3_advanced/chapter18/practice/factorial.py new file mode 100644 index 00000000..5248b0bb --- /dev/null +++ b/3_advanced/chapter18/practice/factorial.py @@ -0,0 +1,11 @@ +# Create a recursive method that mirrors how a +# factorial would work in math. + + +def factorial(n): + if n == 0: + # add here and remove "pass" + pass + else: + # add here and remove "pass" + pass diff --git a/3_advanced/chapter18/practice/koch_curve.py b/3_advanced/chapter18/practice/koch_curve.py new file mode 100644 index 00000000..499449ae --- /dev/null +++ b/3_advanced/chapter18/practice/koch_curve.py @@ -0,0 +1,42 @@ +# This is a fun problem that uses the turtle module. +# If you’ve never used the turtle module, the first two +# lines below are how to set it up and lines after are basic instructions: + +# import turtle +# bob = turtle.Turtle() #doesn’t have to be bob +# bob.left(angle) #turns bob left to an angle like 90 +# bob.right(angle) #turns bob right to an angle like 90 +# bob.fd(distance) #moves bob forward “distance” amount +# Depending on what IDE you are using, you may have to use +# bob.mainloop() if a window with an arrow doesn’t pop up. + +# The goal of this problem is to create a koch curve. +# [search up what koch curve looks like] + +# A koch curve works as follows: +# -Draw a Koch curve with length x/3. +# -Turn left 60 degrees. +# -Draw a Koch curve with length x/3. +# -Turn right 120 degrees. +# -Draw a Koch curve with length x/3. +# -Turn left 60 degrees. +# -Draw a Koch curve with length x/3. +# However, if x<3, you will just move bob(the turtle) forward +# by length x + +# Credits to: +# http://greenteapress.com/thinkpython2/html/thinkpython2006.html + + +import turtle + +bob = turtle.Turtle() + + +def kochcurve(x): + if x < 3: + # add here and remove "pass" + pass + else: + # add here and remove "pass" + pass diff --git a/3_advanced/chapter18/practice/list_sum.py b/3_advanced/chapter18/practice/list_sum.py new file mode 100644 index 00000000..fb253aa8 --- /dev/null +++ b/3_advanced/chapter18/practice/list_sum.py @@ -0,0 +1,16 @@ +# Create a recursive sequence that finds the sum of +# a list that may contain another list within it. + + +def listsum(arr): + total = 0 + + for i in arr: + if isinstance(i, list): + # add here and remove "pass" + pass + else: + # add here and remove "pass" + pass + + return total diff --git a/3_advanced/chapter18/practice/logarithm.py b/3_advanced/chapter18/practice/logarithm.py new file mode 100644 index 00000000..22927fe9 --- /dev/null +++ b/3_advanced/chapter18/practice/logarithm.py @@ -0,0 +1,16 @@ +""" +Create a recursive method that mirrors how a logarithm works in math. +You can have the base by default by ten. You do not have to deal +with decimals, just worry about returning integers. + +Note: Logarithms return the power that you raise a base number to +in order to get a number. + +Ex: logarithm of 9 to base 3 = 2; In this example, since 3 to the +2nd power gives you 9, the logarithm of 9 to base 3 is equal to 2. +""" + + +def logarithm(): # add parameters + # add here and remove "pass" + pass diff --git a/3_advanced/chapter18/solutions/factorial.py b/3_advanced/chapter18/solutions/factorial.py new file mode 100644 index 00000000..f1447a09 --- /dev/null +++ b/3_advanced/chapter18/solutions/factorial.py @@ -0,0 +1,9 @@ +# Create a recursive method that mirrors how a +# factorial would work in math. + + +def factorial(n): + if n == 0: + return 1 + else: + return n * factorial(n - 1) diff --git a/3_advanced/chapter18/solutions/koch_curve.py b/3_advanced/chapter18/solutions/koch_curve.py new file mode 100644 index 00000000..e3d0802b --- /dev/null +++ b/3_advanced/chapter18/solutions/koch_curve.py @@ -0,0 +1,48 @@ +# This is a fun problem that uses the turtle module. +# If you’ve never used the turtle module, the first two +# lines below are how to set it up and lines after are basic instructions: + +# import turtle +# bob = turtle.Turtle() #doesn’t have to be bob +# bob.left(angle) #turns bob left to an angle like 90 +# bob.right(angle) #turns bob right to an angle like 90 +# bob.fd(distance) #moves bob forward “distance” amount +# Depending on what IDE you are using, you may have to use bob.mainloop() +# if a window with an arrow doesn’t pop up. + +# The goal of this problem is to create a koch curve. +# [search up what koch curve looks like] + +# A koch curve works as follows: +# -Draw a Koch curve with length x/3. +# -Turn left 60 degrees. +# -Draw a Koch curve with length x/3. +# -Turn right 120 degrees. +# -Draw a Koch curve with length x/3. +# -Turn left 60 degrees. +# -Draw a Koch curve with length x/3. +# However, if x<3, you will just move bob(the turtle) forward by length x + +# Credits to: +# http://greenteapress.com/thinkpython2/html/thinkpython2006.html + + +import turtle + +bob = turtle.Turtle() + + +def kochcurve(x): + if x < 3: + bob.fd(x) + else: + kochcurve(x / 3) + bob.left(60) + kochcurve(x / 3) + bob.right(120) + kochcurve(x / 3) + bob.left(60) + kochcurve(x / 3) + + +kochcurve(90) # doesn't to be 90, could be any number diff --git a/3_advanced/chapter18/solutions/list_sum.py b/3_advanced/chapter18/solutions/list_sum.py new file mode 100644 index 00000000..73e9c467 --- /dev/null +++ b/3_advanced/chapter18/solutions/list_sum.py @@ -0,0 +1,17 @@ +# Create a recursive sequence that finds the sum of +# a list that may contain another list within it. +# +# Solution Visualizer URL: https://www.w3resource.com/python-exercises/ +# data-structures-and-algorithms/python-recursion-exercise-3.php + + +def listsum(arr): + total = 0 + + for i in arr: + if isinstance(i, list): + total = total + listsum(i) + else: + total = total + i + + return total diff --git a/3_advanced/chapter18/solutions/logarithm.py b/3_advanced/chapter18/solutions/logarithm.py new file mode 100644 index 00000000..a625c78f --- /dev/null +++ b/3_advanced/chapter18/solutions/logarithm.py @@ -0,0 +1,24 @@ +""" +Create a recursive method that mirrors how a logarithm works in math. +You can have the base by default by ten. You do not have to deal +with decimals, just worry about returning integers. + +Note: Logarithms return the power that you raise a base number to +in order to get a number. + +Ex: logarithm of 9 to base 3 = 2; In this example, since 3 to the +2nd power gives you 9, the logarithm of 9 to base 3 is equal to 2. +""" + + +def logarithm(number, base=10, at=1, times=0): + if number < 1 or base == 1: + return None + if number == 1: + return 0 + if at > number: + return times - 1 + if at == number: + return times + newcurrent = at * base + return logarithm(number, base, newcurrent, times + 1) diff --git a/3_advanced/chapter19/examples/error_handle.py b/3_advanced/chapter19/examples/error_handle.py new file mode 100644 index 00000000..a7e070d0 --- /dev/null +++ b/3_advanced/chapter19/examples/error_handle.py @@ -0,0 +1,25 @@ +# Error handling with try clauses or the assert keyword +# can help coders debug programs. They are also useful +# if you want to ignore a certain error. + + +""" try clause """ +try: # this will try the following code + x = 1 + y = "hi" + x + y +except TypeError: # this will only run if there's a TypeError + print("incorrect types, try again") +except NameError: # this will only run if there's a NameError + print("maybe you forgot to create that") +else: # this will run if no error occurs + print("everything good here!") +finally: + # will run no matter what + print("x is", x, "\ny is", y) + + +""" assert keyword """ +string = "goodbye" +assert string == "hello", "string is not hello" +print(string) # this will not be run because assert raises an exception diff --git a/3_advanced/chapter19/practice/finally_use.py b/3_advanced/chapter19/practice/finally_use.py new file mode 100644 index 00000000..82ab209a --- /dev/null +++ b/3_advanced/chapter19/practice/finally_use.py @@ -0,0 +1,15 @@ +# Write a function that takes two parameters and tries to divide +# parameter 1 by parameter 2 to get a result and print that result. +# However, if something goes wrong, have an except print a message +# saying "something went wrong" (optional: have specific messages +# for different errors). Finally, it should, no matter what, print +# "Goodbye World" when it is done. + + +def finally_use(num1, num2): + pass # change this + + +finally_use(4, 0) +finally_use(5, "hi") +finally_use(8, 4) diff --git a/3_advanced/chapter19/practice/int_checker.py b/3_advanced/chapter19/practice/int_checker.py new file mode 100644 index 00000000..d794f1e0 --- /dev/null +++ b/3_advanced/chapter19/practice/int_checker.py @@ -0,0 +1,10 @@ +# Create a program that asks the user to input an integer. Try to +# convert it from a string to an integer. If it fails, send the +# user a message telling them to input a real integer next time. + + +def int_checker(): + pass # change this out for the real code + + +int_checker() diff --git a/3_advanced/chapter19/practice/list_practice.py b/3_advanced/chapter19/practice/list_practice.py new file mode 100644 index 00000000..df751a2a --- /dev/null +++ b/3_advanced/chapter19/practice/list_practice.py @@ -0,0 +1,19 @@ +# Write a function that accesses a global list. It should try to +# take the user’s input for how many times to repeat its process. +# Its process should be: 1. ask the user for a value (any type) +# 2. Append that value to the list. Once that is done, ask the user +# to press q to quit or to input a number to access that index of the +# list. There should be a different error message depending on the type +# of error raised. Whether or not there are errors, when the user is done +# (or there is an error), it should print the list and ask the user +# whether they would like to continue. If this input is 'y', call the +# function again. + +globlist = [] + + +def list_practice(): + pass # change this when you write your code + + +list_practice() diff --git a/3_advanced/chapter19/practice/type_checker.py b/3_advanced/chapter19/practice/type_checker.py new file mode 100644 index 00000000..da688d35 --- /dev/null +++ b/3_advanced/chapter19/practice/type_checker.py @@ -0,0 +1,15 @@ +# Domestic bees make their honeycombs in rings where the total cells is +# (n + 1) * (3n) + 1 where n is the number of rows in the honeycomb +# Create a function that takes one argument and prints how many total +# cells there are in the honeycomb. +# If the argument is not the correct type, print a message saying so. +# It should be able to run through the list provided. + + +def type_checker(x): + pass # remove this + + +arg_list = [4, "hi", "obviously NAN", 5.6, None, {3: 4}, [3, 3]] +for i in arg_list: + type_checker(i) diff --git a/3_advanced/chapter19/solutions/finally_use.py b/3_advanced/chapter19/solutions/finally_use.py new file mode 100644 index 00000000..63d0c88f --- /dev/null +++ b/3_advanced/chapter19/solutions/finally_use.py @@ -0,0 +1,23 @@ +# Write a function that takes two parameters and tries to divide +# parameter 1 by parameter 2 to get a result and print that result. +# However, if something goes wrong, have an except print a message +# saying "something went wrong" (optional: have specific messages +# for different errors). Finally, it should, no matter what, print +# "Goodbye World" when it is done. + + +def finally_use(num1, num2): + try: + result = num1 / num2 + print(result) + except ZeroDivisionError: + print("Cannot divide by Zero") + except TypeError: + print("Invalid type for division") + finally: + print("Goodbye World") + + +finally_use(4, 0) +finally_use(5, "hi") +finally_use(8, 4) diff --git a/3_advanced/chapter19/solutions/int_checker.py b/3_advanced/chapter19/solutions/int_checker.py new file mode 100644 index 00000000..f47f4858 --- /dev/null +++ b/3_advanced/chapter19/solutions/int_checker.py @@ -0,0 +1,14 @@ +# Create a program that asks the user to input an integer. Try to +# convert it from a string to an integer. If it fails, send the +# user a message telling them to input a real integer next time. + + +def int_checker(): + number = input("Please input an integer") + try: + number = int(number) + except ValueError: # You don't need to specify + print("Sorry, that wasn't a valid integer") + + +int_checker() diff --git a/3_advanced/chapter19/solutions/list_practice.py b/3_advanced/chapter19/solutions/list_practice.py new file mode 100644 index 00000000..a86e9dd6 --- /dev/null +++ b/3_advanced/chapter19/solutions/list_practice.py @@ -0,0 +1,41 @@ +# Write a function that accesses a global list. It should try to +# take the user’s input for how many times to repeat its process. +# Its process should be: 1. ask the user for a value (any type) +# 2. Append that value to the list. Once that is done, ask the user +# to press q to quit or to input a number to access that index of the +# list. There should be a different error message depending on the type +# of error raised. Whether or not there are errors, when the user is done +# (or there is an error), it should print the list and ask the user +# whether they would like to continue. If this input is 'y', call the +# function again. + +globlist = [] + + +def list_practice(): + global globlist + try: + times = int(input("How many times would you like to do this? ")) + for i in range(times): + globlist.append(input("What to append? ")) + myinput = input( + "press q to quit; input a number to access that value in the list" + ) + while myinput != "q": + print(globlist[int(myinput)]) + myinput = input( + "press q to quit, input a number to access that value of" + + " the list" + ) + except ValueError: + print("That's not a number") + except IndexError: + print("That's out of range") + finally: + print("This is the list you ended up with: ", globlist) + cont = input("try again? y/n ") + if cont == "y": + list_practice() + + +list_practice() diff --git a/3_advanced/chapter19/solutions/type_checker.py b/3_advanced/chapter19/solutions/type_checker.py new file mode 100644 index 00000000..a5456e81 --- /dev/null +++ b/3_advanced/chapter19/solutions/type_checker.py @@ -0,0 +1,21 @@ +# Domestic bees make their honeycombs in rings where the total cells is +# (n + 1) * (3n) + 1 where n is the number of rows in the honeycomb +# Create a function that takes one argument and prints how many total +# cells there are in the honeycomb. +# If the argument is not the correct type, print a message saying so. +# It should be able to run through the list provided. + + +def type_checker(x): + try: + print((x + 1) * (3 * x) + 1) + # note to students: print(x * any number) would not result in + # an error if x is a string; it would just print x that many + # times + except TypeError: + print("That's not a valid number") + + +arg_list = [4, "hi", "obviously NAN", 5.6, None, {3: 4}, [3, 3]] +for i in arg_list: + type_checker(i) diff --git a/3_advanced/chapter20/examples/json.py b/3_advanced/chapter20/examples/json.py new file mode 100644 index 00000000..098dc9f9 --- /dev/null +++ b/3_advanced/chapter20/examples/json.py @@ -0,0 +1,52 @@ +import json + + +""" Writing """ +x = open("filename.json", "w") # opens JSON file with write mode +topdict = {} + +chinese = {"hello": "ni hao", "bye": "zai jian", "how are you": "ni hao ma"} +frenchlist = [34, 1, 2, 6] + +topdict["chinese"] = chinese +topdict["frenchlist"] = frenchlist + +json.dump(topdict, x, indent=4) # writes value of topdict into JSON file +x.close() # closes the JSON file and saves the changes + + +""" Reading """ +x = open("./testit.json", "r") # opens JSON file with write read +y = json.load(x) # "grabs" JSON data from testit.json + +for key in y: + print(key, ", ", y[key]) # prints the top values of the JSON file + +x.close() + + +""" Editing a pre-existing JSON file """ +x = open("filename.json", "r") +y = json.load(x) # y becomes the equivalent of a Python dictionary +x.close() + +# the value can be all the normal types that dictionaries can hold +y["some_key"] = "some value" +x = open("filename.json", "w") +json.dump(y, x, indent=4) +x.close() + + +""" json.dumps() method """ +oldDict = {"fname": "john", "lname": "doe", "age": 20} +print("oldDict:", type(oldDict)) # prints data type of oldDict +newStr = json.dumps(oldDict) # converts oldDict to string format +print("newStr:", type(newStr)) # prints data type of newStr + + +""" json.loads() method """ + +oldStr = '{"fname": "john", "lname": "doe", "age": 20}' +print("oldStr:", type(oldStr)) # prints data type of oldStr +newDict = json.loads(oldStr) # converts oldStr to string format +print("newDict:", type(newDict)) # prints data type of newDict diff --git a/3_advanced/chapter20/examples/shelve.py b/3_advanced/chapter20/examples/shelve.py new file mode 100644 index 00000000..01c75a50 --- /dev/null +++ b/3_advanced/chapter20/examples/shelve.py @@ -0,0 +1,17 @@ +# shelve is a Python module that aids with storing data. +# It functions similar to a dictionary, although it +# only allows keys to be strings. + + +import shelve + +# this will open or create a database +myshelf = shelve.open("mydatabase") + +# remember, while the key must be a string, the value can be any type +myshelf["key1"] = 4 + +print(myshelf["key1"]) # prints 4 + +myshelf.close() +# always remember to close the shelve after writing to save the data diff --git a/3_advanced/chapter20/examples/text_files.py b/3_advanced/chapter20/examples/text_files.py new file mode 100644 index 00000000..4f57ea51 --- /dev/null +++ b/3_advanced/chapter20/examples/text_files.py @@ -0,0 +1,71 @@ +# The functions below are the basics of +# creating, editting, and reading text files. + + +# "a" stands for "append" +myfile = open("mytext.txt", "a") + + +# "w" stands for "write" +myfile = open("mytext.txt", "w") + + +# writes into a mytext.txt on different lines +# "hello world\nhi again\nhelloooo" +# You need to use the "\n" character if you want to write to a new line; if you +# don't use it, the next .write() will write to the same line as the previous .write() +myfile.write("hello world\n") # writes on line 1 +myfile.write("hi again\n") # writes on line 2 +myfile.write("helloooo") # writes on line 3 + + +# saves file +myfile.close() + + +# "r" stands for "read" +myfile = open("mytext.txt", "r") + + +# takes data from mytext.txt and prints it +mydata = myfile.read() +print(mydata) + + +# determines if myfile is readable +print(myfile.readable()) + + +print(myfile.readline()) # prints the first line +print(myfile.readline()) # prints the second line +print(myfile.readline()) # prints the third line + + +# determines if you can set your position in myfile +print(myfile.seekable()) + + +# sets your position to the 0th index +myfile.seek(0) + + +# prints a list of all the lines in the file +print(myfile.readlines()) + + +# prints your position in a file +print(myfile.tell()) + + +# determines if you can write into myfile +print(myfile.writable()) + + +myfile = open("mytext.txt", "w") + + +# writes lines from provided list into myfile +myfile.writelines(["line one\n", "line 2"]) + + +myfile.close() diff --git a/3_advanced/chapter20/practice/favorite_foods.json b/3_advanced/chapter20/practice/favorite_foods.json new file mode 100644 index 00000000..ba2df668 --- /dev/null +++ b/3_advanced/chapter20/practice/favorite_foods.json @@ -0,0 +1,8 @@ +{ + "favorite foods": { + "Jerry": "ice cream", + "Ben": "ice cream", + "Steven": "eggroll", + "Spongebob": "Krabby Patty" + } +} \ No newline at end of file diff --git a/3_advanced/chapter20/practice/hidden_message.py b/3_advanced/chapter20/practice/hidden_message.py new file mode 100644 index 00000000..e878863d --- /dev/null +++ b/3_advanced/chapter20/practice/hidden_message.py @@ -0,0 +1,5 @@ +# Create a program that reads textfile.txt and writes (appends) 2 +# newlines and then every 7th word followed by a space + +# ex: given “hi”, “ho”, “ha”, “hy”, “he”, “hu”, “we”, “everyone” +# it would print 2 newlines and then ‘hi everyone’ diff --git a/3_advanced/chapter20/practice/json_practice_1.py b/3_advanced/chapter20/practice/json_practice_1.py new file mode 100644 index 00000000..4961d68f --- /dev/null +++ b/3_advanced/chapter20/practice/json_practice_1.py @@ -0,0 +1,6 @@ +# use the "favorite_foods.json" +# in that json file, there will be a dictionary called "favorite_foods" +# print all the unique favorite foods, which will be the values. +# Save all the names into a list. Add that list to the dictionary +# ('names' should be the key and the names list should be the value) +# and write the dictionary into the json file diff --git a/3_advanced/chapter20/practice/json_practice_2.py b/3_advanced/chapter20/practice/json_practice_2.py new file mode 100644 index 00000000..8b6426dd --- /dev/null +++ b/3_advanced/chapter20/practice/json_practice_2.py @@ -0,0 +1,4 @@ +# use the file "wildlife.json" +# load the data in the JSON file +# add at least one habitat and corresponding animal(s) to the dictionary +# finally, write the updated dictionary to the json file. diff --git a/3_advanced/chapter20/practice/modify_random_text.py b/3_advanced/chapter20/practice/modify_random_text.py new file mode 100644 index 00000000..7e459c31 --- /dev/null +++ b/3_advanced/chapter20/practice/modify_random_text.py @@ -0,0 +1,5 @@ +# Create a program that creates a blank text file and writes a +# random number (in the form of a string) between 1 and 1000 on it. +# Next, close the file. Next, open the file again (this time read it) +# and read the text. Assign a variable to that data. +# print (not write) the variable, then print the int(variable) * 4. diff --git a/3_advanced/chapter20/practice/mydatabase.db b/3_advanced/chapter20/practice/mydatabase.db new file mode 100644 index 00000000..9c84d85b Binary files /dev/null and b/3_advanced/chapter20/practice/mydatabase.db differ diff --git a/3_advanced/chapter20/practice/mydb.db b/3_advanced/chapter20/practice/mydb.db new file mode 100644 index 00000000..56a11caf Binary files /dev/null and b/3_advanced/chapter20/practice/mydb.db differ diff --git a/3_advanced/chapter20/practice/shelve_practice_1.py b/3_advanced/chapter20/practice/shelve_practice_1.py new file mode 100644 index 00000000..ac470e74 --- /dev/null +++ b/3_advanced/chapter20/practice/shelve_practice_1.py @@ -0,0 +1,13 @@ +# Use mydatabase.db; Using it, first get all the keys and put them into a list. +# For help on this, see the hint below. Next, sort the list. Finally, print the +# corresponding values. +# To do that, do print((shelfname)[key]) where (shelfname) is the name of your +# shelf and key is the key. + +# Hint: to get a dictionary or shelf’s keys, all you have to do is this: +""" +for key in myshelf.keys(): + keylist.append(key) +""" +# Keep in mind that the “myshelf” is just a name for a dictionary or shelf and +# that the “keylist” is just a list holding the keys. diff --git a/3_advanced/chapter20/practice/shelve_practice_2.py b/3_advanced/chapter20/practice/shelve_practice_2.py new file mode 100644 index 00000000..89ea7dca --- /dev/null +++ b/3_advanced/chapter20/practice/shelve_practice_2.py @@ -0,0 +1,4 @@ +# Use mydb.db . Using it, first create a total variable. Then, add +# all of the shelve’s values to the total. Remember to check if +# the value is an integer before adding it to the total. +# After all, shelves can store all types. diff --git a/3_advanced/chapter20/practice/shelve_practice_3.py b/3_advanced/chapter20/practice/shelve_practice_3.py new file mode 100644 index 00000000..a87e6986 --- /dev/null +++ b/3_advanced/chapter20/practice/shelve_practice_3.py @@ -0,0 +1,12 @@ +# Create a database to store orders. Next, ask the customer for their +# name and store that as a variable. Next, ask the customer whether +# they want to view their previous order or make a new order. + +# If they want to make a new order, use the shelf to store the order +# as the value and the customer’s name as the key. + +# If they want to view a previous order, check if their name is in +# the shelf’s keys. If it is, print their previous order. +# If not, tell them that they haven’t ordered. +# Remember to close the shelf. +# Hint: to store their order, you can do: shelf[name] = order diff --git a/3_advanced/chapter20/practice/textfile.txt b/3_advanced/chapter20/practice/textfile.txt new file mode 100644 index 00000000..6056bb2a --- /dev/null +++ b/3_advanced/chapter20/practice/textfile.txt @@ -0,0 +1,6 @@ +hi ho he hu hy ha we everyone. wow pow kow some tome Biome How +are bar tsar czar ceasar tar do moo cow baa sheep pig big you too +blue eggs and ham spam do? sew. machine grow large. barge in hopefully +successfully totally completely absolutely did it! you bought that +old fish ten days, did it taste good? huh? what? ok. it is never +close till it is right! diff --git a/3_advanced/chapter20/practice/txt_write_practice.py b/3_advanced/chapter20/practice/txt_write_practice.py new file mode 100644 index 00000000..0d26d17c --- /dev/null +++ b/3_advanced/chapter20/practice/txt_write_practice.py @@ -0,0 +1,136 @@ +import requests +import shelve + + +class game: + def __init__(self, pageinfo, bsl: int, gns, gds, gps, gops, end): + """ + Arguments: + bsl is the beginning search location. It should be an integer + pageinfo is the html of a website converted to a string + gns is the 'game's name start'; it is what to look for directly + before a game's name + gds is the 'game's discount start'; it is what to look for directly + before a game's discount + gps is the 'game's price start'; it is what to look for directly + before a game's discounted price + gops is the 'game original price start'; it is what to look for + directly before a game's original price + + """ + self.string = pageinfo + self.isvalid = True + self.begin = bsl + self.endloc = 0 + + self.discount = self.find(self.string, gds, end, cb=True) + self.price = self.find(self.string, gps, end) + self.ogprice = self.find(self.string, gops, end) + self.name = self.find(self.string, gns, end, cwe=True) + + def find(self, string, start: str, end: str, cb=False, cwe=False): + """ + Arguments + string is the string where the substring you are looking for + is located + start is the substring directly before the substring you are + looking for + end is the substring directly after the substring you are + looking for + cb is whether or not to change the beginning point for the + searches to the endloc + cwe is whether to compare the endloc with self.begin and + check whether the difference is withing the acceptable range + """ + try: + startloc = string.index(start, self.begin) + endloc = string.index(end, startloc) + except Exception: + self.endloc = self.begin + 1 + self.isvalid = False + return + + if cb: + self.begin = endloc + if cwe: + # check if the end location is too far away from the + # beginning to be a valid name + if endloc - self.begin > 300: + self.isvalid = False + self.endloc = endloc + + return string[startloc:endloc].lstrip(start).rstrip(end) + + +class scansteampage: + def __init__(self, database="gameshelf"): + """ + See game's explanation for the abbreviations + """ + link = "https://store.steampowered.com/" + gns = '
' + gds = '
-' + gops = 'class="discount_original_price">' + gps = 'class="discount_final_price">' + end = "
" + + self.games = [] + self.database = database + info = requests.get(link).text + + self.gather_games(info, gns, gds, gps, gops, end) + self.write_info() + + def gather_games(self, info, gns, gds, gps, gops, end): + """ + This method adds game objects to the scansteampage + object's list self.games + """ + position = 0 + consecutive_fails = 0 + + while consecutive_fails < 2: + a_game = game(info, position, gns, gds, gps, gops, end) + position = a_game.endloc + if a_game.isvalid: + self.games.append(a_game) + consecutive_fails = 0 + else: + consecutive_fails += 1 + + def write_info(self): + """ + This method makes writes to the database. + + The database's keys will be games' titles + The database's values will be strings of the following + format: + (name) is on sale for (price), which is a (discount percent) + discount from its original price of (original price) + """ + gameshelf = shelve.open(self.database) + if len(gameshelf.keys()) > 0: + gameshelf.clear() + for game in self.games: + gameshelf[game.name] = ( + "%s is on sale for %s, which is a %s" + % (game.name, game.price, game.discount) + + " discount from its original price of %s" % game.ogprice + ) + gameshelf.close() + + +# comment out the below line after running it once +ourgamesshelf = scansteampage() + +# The above code creates a shelf called gameshelf +# First, create a list to store the values +# Next, write "Current Sales\n" on a blank text file. +# Finally, write the values followed by 2 newlines to The +# text file. +# Your end result should look like below: +# Current Sales +# Something is on sale for $1000.00, which is a 50% discount from +# its original price of 2000.00 + +# write your code here. diff --git a/3_advanced/chapter20/practice/wildlife.json b/3_advanced/chapter20/practice/wildlife.json new file mode 100644 index 00000000..468e9e76 --- /dev/null +++ b/3_advanced/chapter20/practice/wildlife.json @@ -0,0 +1,6 @@ +{ + "China": "pandas", + "Africa": "cheetas", + "North America": "bison", + "South America": "boa constrictor" +} \ No newline at end of file diff --git a/3_advanced/chapter20/solutions/favorite_foods.json b/3_advanced/chapter20/solutions/favorite_foods.json new file mode 100644 index 00000000..376b2db4 --- /dev/null +++ b/3_advanced/chapter20/solutions/favorite_foods.json @@ -0,0 +1,14 @@ +{ + "favorite foods": { + "Jerry": "ice cream", + "Ben": "ice cream", + "Steven": "eggroll", + "Spongebob": "Krabby Patty" + }, + "names": [ + "Jerry", + "Ben", + "Steven", + "Spongebob" + ] +} \ No newline at end of file diff --git a/3_advanced/chapter20/solutions/hidden_message.py b/3_advanced/chapter20/solutions/hidden_message.py new file mode 100644 index 00000000..2bc77063 --- /dev/null +++ b/3_advanced/chapter20/solutions/hidden_message.py @@ -0,0 +1,16 @@ +# Create a program that reads textfile.txt and writes (appends) 2 +# newlines and then every 7th word followed by a space + +# ex: given “hi”, “ho”, “ha”, “hy”, “he”, “hu”, “we”, “everyone” +# it would print 2 newlines and then ‘hi everyone’ + +myfile = open("./textfile.txt", "r") +text = myfile.read().split() +myfile.close() + +myfile = open("./textfile.txt", "a") +myfile.write("\n\n") +for i in range(len(text)): + if i % 7 == 0: + myfile.write(text[i] + " ") +myfile.close() diff --git a/3_advanced/chapter20/solutions/json_practice_1.py b/3_advanced/chapter20/solutions/json_practice_1.py new file mode 100644 index 00000000..ed6de397 --- /dev/null +++ b/3_advanced/chapter20/solutions/json_practice_1.py @@ -0,0 +1,23 @@ +# use the "favorite_foods.json" +# in that json file, there will be a dictionary called "favorite_foods" +# print all the unique favorite foods, which will be the values. +# Save all the names into a list. Add that list to the dictionary +# ('names' should be the key and the names list should be the value) +# and write the dictionary into the json file + +import json + +a = open("favorite_foods.json", "r") +x = json.load(a) +names = [] +foods = set() +for name, food in x["favorite foods"].items(): + foods.add(food) + names.append(name) +for food in foods: + print(food) +x["names"] = names # create an item within the dictionary that has the names +a.close() +n = open("favorite_foods.json", "w") +json.dump(x, n, indent=4) +n.close() diff --git a/3_advanced/chapter20/solutions/json_practice_2.py b/3_advanced/chapter20/solutions/json_practice_2.py new file mode 100644 index 00000000..5944b7ba --- /dev/null +++ b/3_advanced/chapter20/solutions/json_practice_2.py @@ -0,0 +1,14 @@ +# use the file "wildlife.json" +# load the data in the JSON file +# add at least one habitat and corresponding animal(s) to the dictionary +# finally, write the updated dictionary to the json file. + +import json + +a = open("wildlife.json", "r") +x = json.load(a) +a.close() +x["Deepest Peru"] = "Paddington" +n = open("wildlife.json", "w") +json.dump(x, n, indent=4) +n.close() diff --git a/3_advanced/chapter20/solutions/modify_random_text.py b/3_advanced/chapter20/solutions/modify_random_text.py new file mode 100644 index 00000000..2bdc2e67 --- /dev/null +++ b/3_advanced/chapter20/solutions/modify_random_text.py @@ -0,0 +1,16 @@ +# Create a program that creates a blank text file and writes a +# random number (in the form of a string) between 1 and 1000 on it. +# Next, close the file. Next, open the file again (this time read it) +# and read the text. Assign a variable to that data. +# print (not write) the variable, then print the int(variable) * 4. + +import random + +myfile = open("blank.txt", "w") +myfile.write(str(random.randint(0, 1000))) +myfile.close() + +refile = open("blank.txt", "r") +thetext = refile.read() +print(thetext) +print(int(thetext) * 4) diff --git a/3_advanced/chapter20/solutions/shelve_practice_1.py b/3_advanced/chapter20/solutions/shelve_practice_1.py new file mode 100644 index 00000000..53a3c2d2 --- /dev/null +++ b/3_advanced/chapter20/solutions/shelve_practice_1.py @@ -0,0 +1,24 @@ +# Use mydatabase.db; Using it, first get all the keys and put them into a list. +# For help on this, see the hint below. Next, sort the list. Finally, print the +# corresponding values. +# To do that, do print((shelfname)[key]) where (shelfname) is the name of your +# shelf and key is the key. + +# Hint: to get a dictionary or shelf’s keys, all you have to do is this: +""" +for key in myshelf.keys(): + keylist.append(key) +""" +# Keep in mind that the “myshelf” is just a name for a dictionary or shelf and +# that the “keylist” is just a list holding the keys. + +import shelve + +keylist = [] +myshelf = shelve.open("mydatabase") +for key in myshelf.keys(): + keylist.append(key) +keylist.sort() +for key in keylist: + print(myshelf[key]) +myshelf.close() diff --git a/3_advanced/chapter20/solutions/shelve_practice_2.py b/3_advanced/chapter20/solutions/shelve_practice_2.py new file mode 100644 index 00000000..1c6614ad --- /dev/null +++ b/3_advanced/chapter20/solutions/shelve_practice_2.py @@ -0,0 +1,14 @@ +# Use mydb.db . Using it, first create a total variable. Then, add +# all of the shelve’s values to the total. Remember to check if +# the value is an integer before adding it to the total. +# After all, shelves can store all types. + +import shelve + +total = 0 +ashelf = shelve.open("mydb") +for val in ashelf.values(): + if isinstance(val, int): + total += val +print(total) +ashelf.close() diff --git a/3_advanced/chapter20/solutions/shelve_practice_3.py b/3_advanced/chapter20/solutions/shelve_practice_3.py new file mode 100644 index 00000000..26a06d75 --- /dev/null +++ b/3_advanced/chapter20/solutions/shelve_practice_3.py @@ -0,0 +1,30 @@ +# Create a database to store orders. Next, ask the customer for their +# name and store that as a variable. Next, ask the customer whether +# they want to view their previous order or make a new order. + +# If they want to make a new order, use the shelf to store the order +# as the value and the customer’s name as the key. + +# If they want to view a previous order, check if their name is in +# the shelf’s keys. If it is, print their previous order. +# If not, tell them that they haven’t ordered. +# Remember to close the shelf. +# Hint: to store their order, you can do: shelf[name] = order + +import shelve + +shelf = shelve.open("orders") +name = input("What is your name? ") +instruction = input( + "Would you like to view a previous order or make" + + " a new order?\nAnswer with 'order' or 'view': " +) +if instruction == "order": + order = input("Type any order: ") + shelf[name] = order +elif instruction == "view" and name in shelf.keys(): + print("Here is your previous order: ") + print(shelf[name]) +elif instruction == "view" and name not in shelf.keys(): + print("Sorry, you don't seem to have ordered before.") +shelf.close() diff --git a/3_advanced/chapter20/solutions/textfile.txt b/3_advanced/chapter20/solutions/textfile.txt new file mode 100644 index 00000000..10499a72 --- /dev/null +++ b/3_advanced/chapter20/solutions/textfile.txt @@ -0,0 +1,8 @@ +hi ho he hu hy ha we everyone. wow pow kow some tome Biome How +are bar tsar czar ceasar tar do moo cow baa sheep pig big you too +blue eggs and ham spam do? sew. machine grow large. barge in hopefully +successfully totally completely absolutely did it! you bought that +old fish ten days, did it taste good? huh? what? ok. it is never +close till it is right! + +hi everyone. How do you do? hopefully you did it right! diff --git a/3_advanced/chapter20/solutions/txt_write_practice.py b/3_advanced/chapter20/solutions/txt_write_practice.py new file mode 100644 index 00000000..499cefff --- /dev/null +++ b/3_advanced/chapter20/solutions/txt_write_practice.py @@ -0,0 +1,141 @@ +import requests +import shelve + + +class game: + def __init__(self, pageinfo, bsl: int, gns, gds, gps, gops, end): + """ + Arguments: + bsl is the beginning search location. It should be an integer + pageinfo is the html of a website converted to a string + gns is the 'game's name start'; it is what to look for directly + before a game's name + gds is the 'game's discount start'; it is what to look for directly + before a game's discount + gps is the 'game's price start'; it is what to look for directly + before a game's discounted price + gops is the 'game original price start'; it is what to look for + directly before a game's original price + + """ + self.string = pageinfo + self.isvalid = True + self.begin = bsl + self.endloc = 0 + + self.discount = self.find(self.string, gds, end, cb=True) + self.price = self.find(self.string, gps, end) + self.ogprice = self.find(self.string, gops, end) + self.name = self.find(self.string, gns, end, cwe=True) + + def find(self, string, start: str, end: str, cb=False, cwe=False): + """ + Arguments + string is the string where the substring you are looking for + is located + start is the substring directly before the substring you are + looking for + end is the substring directly after the substring you are + looking for + cb is whether or not to change the beginning point for the + searches to the endloc + cwe is whether to compare the endloc with self.begin and + check whether the difference is withing the acceptable range + """ + try: + startloc = string.index(start, self.begin) + endloc = string.index(end, startloc) + except Exception: + self.endloc = self.begin + 1 + self.isvalid = False + return + + if cb: + self.begin = endloc + if cwe: + # check if the end location is too far away from the + # beginning to be a valid name + if endloc - self.begin > 300: + self.isvalid = False + self.endloc = endloc + + return string[startloc:endloc].lstrip(start).rstrip(end) + + +class scansteampage: + def __init__(self, database="gameshelf"): + """ + See game's explanation for the abbreviations + """ + link = "https://store.steampowered.com/" + gns = '
' + gds = '
-' + gops = 'class="discount_original_price">' + gps = 'class="discount_final_price">' + end = "
" + + self.games = [] + self.database = database + info = requests.get(link).text + + self.gather_games(info, gns, gds, gps, gops, end) + self.write_info() + + def gather_games(self, info, gns, gds, gps, gops, end): + """ + This method adds game objects to the scansteampage + object's list self.games + """ + position = 0 + consecutive_fails = 0 + + while consecutive_fails < 2: + a_game = game(info, position, gns, gds, gps, gops, end) + position = a_game.endloc + if a_game.isvalid: + self.games.append(a_game) + consecutive_fails = 0 + else: + consecutive_fails += 1 + + def write_info(self): + """ + This method makes writes to the database. + + The database's keys will be games' titles + The database's values will be strings of the following + format: + (name) is on sale for (price), which is a (discount percent) + discount from its original price of (original price) + """ + gameshelf = shelve.open(self.database) + if len(gameshelf.keys()) > 0: + gameshelf.clear() + for game in self.games: + gameshelf[game.name] = ( + "%s is on sale for %s, which is a %s" + % (game.name, game.price, game.discount) + + " discount from its original price of %s" % game.ogprice + ) + gameshelf.close() + + +# comment out the below line after running it once +ourgamesshelf = scansteampage() + +# The above code creates a shelf called gameshelf +# First, create a list to store the values +# Next, write "Current Sales\n" on a blank text file. +# Finally, write the values followed by 2 newlines to The +# text file. +# Your end result should look like below: +# Current Sales +# Something is on sale for $1000.00, which is a 50% discount from +# its original price of 2000.00 + +mygameshelf = shelve.open("gameshelf") +mytextfile = open("Sales!", "w") +mytextfile.write("Current Sales\n") +for value in mygameshelf.values(): + mytextfile.write(value + "\n\n") +mytextfile.close() diff --git a/3_advanced/chapter20/solutions/wildlife.json b/3_advanced/chapter20/solutions/wildlife.json new file mode 100644 index 00000000..c60a8d22 --- /dev/null +++ b/3_advanced/chapter20/solutions/wildlife.json @@ -0,0 +1,7 @@ +{ + "China": "pandas", + "Africa": "cheetas", + "North America": "bison", + "South America": "boa constrictor", + "Deepest Peru": "Paddington" +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..77b1cea0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Code 4 Tomorrow + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 0657fc49..e2dfa533 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,18 @@ # Python Source code from Code For Tomorrow's Python course +![Python (Lintly)](https://github.com/code-for-tomorrow/python/workflows/Python%20(Lintly)/badge.svg) +![Python (Lint Action)](https://github.com/code-for-tomorrow/python/workflows/Python%20(Lint%20Action)/badge.svg) + ## Difficulty Level -Source code is organized under 3 difficulty levels: +Source code is organized under 4 categories: 1. `1_beginner` 2. `2_intermediate` 3. `3_advanced` +4. `games` ## Chapters -Under each of the 3 packages, source code is further divided by chapter. +Under each of the 4 packages, source code is further divided by chapter. ### Beginner 1. `chapter1` Intro to Python 2. `chapter2` Data @@ -24,17 +28,24 @@ Under each of the 3 packages, source code is further divided by chapter. 10. `chapter10` 2D Lists 11. `chapter11` Functions 12. `chapter12` Classes +13. `chapter13` Special Methods ### Advanced -13. `chapter13` Special Methods -14. `chapter14` Selection Sort -15. `chapter15` Tuples & Sets -16. `chapter16` Pythonness -17. `chapter17` Exception Handling +14. `chapter14` Unique Python Features +15. `chapter15` Asymptotic Analysis +16. `chapter16` Selection Sort +17. `chapter17` Tuples & Sets 18. `chapter18` Recursion -19. `chapter19` Asymptotic Analysis +19. `chapter19` Exception Handling 20. `chapter20` File I/O +### Games +1. `chapter1` Console Games +2. `chapter2` Pygame Basics +3. `chapter3` Pygame Events +4. `chapter4` OOP + Pygame +5. `chapter5` Pygame Sounds + ## Category Under each chapter, source code is further divided by category: 1. `examples` - example code to demo certain programming concepts @@ -42,70 +53,3 @@ Under each chapter, source code is further divided by category: 3. `solutions` - solutions to practice exercises The practice template file has the same name as the corresponding solution file. - -# Practice Problem Guidelines -Please follow the following guidelines when creating or finding practice problems. We expect all problems submitted by teachers to maintain the highest quality! (You can also read these guidelines on every repository’s README.md.) - -If you have questions, send a message to #questions on the teachers Discord. - -## Follow good programming style. -* Indent properly. -* Make sure there are spaces between binary operators, parentheses, curly braces, etc. when applicable. (For method/function calls, there should not be a space between the method/function name and the parentheses.) -* Name files so that they are representative of what the program is about. - * Avoid naming files after concepts, like “Arrays.java” -* Name variables/methods/functions descriptively, and follow your language’s conventions. - * Java: camelCase for regular variables, snake case (and uppercase) for constants - * Python: Snake case for regular variables, snake case (and uppercase) for constants -* If a line of code is very long, separate it into 2+ lines (e.g. for long lists/arrays, long print statements, etc.) -* Leave whitespace where appropriate to increase readability. -* Add comments to explain parts of your code. - -## Follow the C4T practice and solution problem format. -* All code filed under `practice` should be templates. Templates may include things like an empty class and main method. They must include a multi-line comment with the full instructions that are also displayed on Thinkific at the top of the program. -* All code filed under `solutions` should be solutions. Solutions should include the full instructions at the top of the file and then a possible solution to the problem. -* The practice template and solution should have *the same file name.* -* For Java teachers: Make sure that you have the correct package statement as the first line of your code. - -## Thoroughly test your solution. Make sure it works as intended. -* Hundreds of students and ~50 teachers could potentially see and use your work. Please make sure that it works! - -## Make sure that the problem is doable for the skill level of your students. -* Check Thinkific to see if the concepts that you need to solve the problem have been covered already. -* Be aware of how long your own class takes on other practice problems. -* Practice problems shouldn’t take several hours/days. - -## Cite your sources. -* If you are using data or practice problems from somewhere other than yourself, you MUST cite your sources. -* Providing a link to the original is usually sufficient if you found the problem online. -* If you found it in a book, cite the full title and author. - -## Submit practice problem(s). -### Overall directions no matter which option you choose: -* Make sure you push to the correct folder and repository. -* Instructions should be formatted as follows: - -> **Practice:** [full instructions] -> -> **Practice template:** practice/[FileName].extension -> -> **Solution:** solutions/[FileName].extension - -To submit a problem, you have 3 options: - -### (1) Pushing to GitHub -1. Dm Rebecca Dang on the Teachers Discord with your GitHub username and course(s) you’re teaching to be invited to the C4T GitHub organization -2. Clone the repo. -3. Create your own branch. Name it appropriately. -4. Push your branch to remote. -5. Mention @Curriculum Development on the Teachers Discord that you have pushed your branch. Be sure to tell us what the name of your branch is! -6. If Curr-Dev approves, you have permission to merge your branch with master. - -### (2) Making a pull request -[See this help article](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) - -### (3) Email C4T Curriculum Development -1. Email [c4t.curriculum@gmail.com](mailto:c4t.curriculum@gmail.com) with all necessary info (e.g. instructions, practice template file, solution file). -2. Mention @Curriculum Development on the Teachers Discord that you have emailed us please! - - -Thank you teachers for following these guidelines and helping us build a problem base! diff --git a/dsa/chapter1/examples/recursion.py b/dsa/chapter1/examples/recursion.py new file mode 100644 index 00000000..15e6c106 --- /dev/null +++ b/dsa/chapter1/examples/recursion.py @@ -0,0 +1,35 @@ +# Code to figure out how many of a factor a number has + + +def number_factor(number, factor, factor_counter=0): + """ + Parameters: + 1) number is the number in which we are finding the number of + factors of. EX: 24 + 2) factor is the factor in which we are finding the number of + in the parameter number. EX: 2 + Output: The number of times the parameter number can be divisible + by the parameter factor. This number is also the parameter + factor_counter right before it is returned. EX: 3 + """ + + if number % factor != 0: # Base Case + return factor_counter + else: # Recursive Case + return number_factor(number / factor, factor, factor_counter + 1) + + +print(number_factor(24, 2)) + + +def countdown(n, arr=[]): + if n < 0: # base case 1 + return "out of bounds" + if n == 0: # base case 2 + return arr + # recursive case + arr.append(n) + return countdown(n - 1, arr) + + +print(countdown(5)) diff --git a/dsa/chapter1/practice/time_complexity.py b/dsa/chapter1/practice/time_complexity.py new file mode 100644 index 00000000..e3acacf8 --- /dev/null +++ b/dsa/chapter1/practice/time_complexity.py @@ -0,0 +1,25 @@ +""" +For each of the following time complexities, create +a function that has that time complexity. +""" + +# time complexity: O(1) +# your code here + + +# time complexity: O(n) +# your code here + + +# time complexity: O(n^2) +# your code here + + +# time complexity: O(log(n)) +# your code here + +# time complexity: O(n * log(n)) +# your code here + +# time complexity: O(2**n) +# your code here diff --git a/dsa/chapter1/practice/time_complexity_questions.py b/dsa/chapter1/practice/time_complexity_questions.py new file mode 100644 index 00000000..f272e328 --- /dev/null +++ b/dsa/chapter1/practice/time_complexity_questions.py @@ -0,0 +1,74 @@ +""" +Classify the following code examples with their +runtimes. Write your responses as comments. +""" + + +def do_something(): + # runtime for do_something() is O(1) + pass + + +# what is the runtime for example 1? +def example_one(n): + for i in range(n): + do_something() + + +# what is the runtime for example 2? +def example_two(n): + do_something() + + +# what is the runtime for example 3? +def example_three(n): + for i in range(n): + for x in range(i): + do_something() + + +# what is the runtime for example 4? +def example_four(n): + for i in range(n // 2): + do_something() + + +# what is the runtime for example 5? +def example_five(n): + i = 0 + while i < n: + do_something() + i *= 2 + + +# what is the runtime for example 6? +def example_six(n): + for i in range(10): + do_something() + + +# what is the runtime for example 7? +def example_seven(n): + for i in range(2**n): + do_something() + + +# what is the runtime for example 8? +def example_eight(n): + for i in range(n): + for x in range(7): + do_something() + + +# what is the runtime for example 9? +def example_nine(n): + for i in range(n): + example_one(n) + + +# what is the runtime for example 10? +def example_ten(n): + i = 0 + while i < n: + do_something() + i += 2 diff --git a/dsa/chapter1/solutions/time_complexity.py b/dsa/chapter1/solutions/time_complexity.py new file mode 100644 index 00000000..d006e0f8 --- /dev/null +++ b/dsa/chapter1/solutions/time_complexity.py @@ -0,0 +1,99 @@ +""" +For each of the following time complexities, create +a function that has that time complexity. The following solutions +are examples and not the only ways to have done this problem. +""" + + +# time complexity: O(1) +def double_my_number(number): + x = number + x *= 2 + return x + + +# time complexity: O(n) +def sum_till_n(n): + total = 0 + for i in range(n): + total += i + + return total + + +# time complexity: O(n^2) +def print_triangle(n): + for row in range(n): + for column in range(row): + print("* ", end="") + print() + + +# time complexity: O(log(n)) +def sum_powers_of_two(max_number): + power_of_two = 1 + total = 0 + + while power_of_two < max_number: + total += power_of_two + power_of_two *= 2 + + return total + + +# time complexity: O(n * log(n)) +def sum_many_powers_of_two(number_of_times): + total = 0 + for i in range(number_of_times): + # since sum_powers_of_two is O(log(n))and this for loop is O(n), + # the resulting time complexity is O(n * log(n)) + total += sum_powers_of_two(i) + + return total + + +# time complexity: O(2**n) +def get_binary_combinations(number_of_digits): + """ + Gets the combinations of binary numbers with number_of_digits digits + For example, get_binary_combinations(2) should give + ["00", "01", "10", "11"]. + + """ + cur_options = ["0", "1"] + next_options = [] + + operations = 0 + + # In total, this is O(2**n). It may be a bit confusing, but + # this is O(2**n) because of the fact that the current options + # doubles each time we go through the for loop, so it has to + # spend twice as long each time. + for i in range(number_of_digits - 1): + for option in cur_options: + next_options.append(option + "0") + next_options.append(option + "1") + operations += 1 + cur_options = next_options + next_options = [] + + print(f"took {operations} operations") + return cur_options + + +# for comparison, here's a very easy to understand +# function with O(2**n) runtime. +def regular_o_2_to_the_n(n): + operations = 0 + for i in range(2**n): + operations += 1 + print(f"took {operations} operations") + + +# if you actually don't believe that get_binary_combinations is O(2**n), +# try running the below. +# as you can see, they have similar operational cost, +# meaning that get_binary_combinations really is O(2**n) +# times = 20 +# get_binary_combinations(times) +# regular_o_2_to_the_n(times) diff --git a/dsa/chapter1/solutions/time_complexity_questions.py b/dsa/chapter1/solutions/time_complexity_questions.py new file mode 100644 index 00000000..96945dc9 --- /dev/null +++ b/dsa/chapter1/solutions/time_complexity_questions.py @@ -0,0 +1,84 @@ +""" +Classify the following code examples with their +runtimes. Write your responses as comments. +""" + + +def do_something(): + # runtime for do_something() is O(1) + pass + + +# what is the runtime for example 1? +# runtime is O(n) +def example_one(n): + for i in range(n): + do_something() + + +# what is the runtime for example 2? +# runtime is O(1) +def example_two(n): + do_something() + + +# what is the runtime for example 3? +# runtime is O(n^2) +def example_three(n): + for i in range(n): + for x in range(i): + do_something() + + +# what is the runtime for example 4? +# runtime is O(n) +def example_four(n): + for i in range(n // 2): + do_something() + + +# what is the runtime for example 5? +# runtime is O(log(n)) +def example_five(n): + i = 0 + while i < n: + do_something() + i *= 2 + + +# what is the runtime for example 6? +# runtime is O(1) +def example_six(n): + for i in range(10): + do_something() + + +# what is the runtime for example 7? +# runtime is O(2**n) +def example_seven(n): + for i in range(2**n): + do_something() + + +# what is the runtime for example 8? +# runtime is O(n) +def example_eight(n): + for i in range(n): + for x in range(7): + do_something() + + +# what is the runtime for example 9? +# runtime is O(n^2) +def example_nine(n): + for i in range(n): + example_one(n) + + +# what is the runtime for example 10? +# runtime is O(n) +def example_ten(n): + i = 0 + while i < n: + do_something() + i += 2 diff --git a/dsa/chapter2/examples/bst.py b/dsa/chapter2/examples/bst.py new file mode 100644 index 00000000..f3d8c4d5 --- /dev/null +++ b/dsa/chapter2/examples/bst.py @@ -0,0 +1,215 @@ +class Node: + def __init__(self, key, value) -> None: + # set key/value + self.key = key + self.value = value + + # set children to None + self.left: Node = None + self.right: Node = None + + def __str__(self) -> str: + return str(self.key) + ": " + str(self.value) + + def __repr__(self) -> str: + return str(self) + + +class BinaryTree: + def __init__(self) -> None: + self.root = None + + def search(self, key): + """ + Searches the binary tree for a node with the given key. + Takes advantage of the fact that, in a binary tree, + keys with lesser values go on the left and keys with greater + values go on the right + """ + current = self.root + while current is not None: + if key == current.key: + return current.value + elif key < current.key: + current = current.left + else: + current = current.right + raise Exception("KEY NOT FOUND") + + def get_height(self): + """ + Gets the height of the binary tree. + """ + if self.root is None: + return 0 + + height = 0 + next = [self.root] + while len(next) != 0: + temp_next = [] + height += 1 + for node in next: + if node.left is not None: + temp_next.append(node.left) + if node.right is not None: + temp_next.append(node.right) + next = temp_next + return height + + def insert_node(self, node: Node) -> None: + """ + Tries to insert a node into the tree. + If a node with the same key is already found, + sets the value of that node to the value of + the provided node + """ + # case where root is None + if self.root is None: + self.root = node + return + + # go through the nodes + current = self.root + while current is not None: + if node.key == current.key: + current.value = node.value + return + elif node.key < current.key: + if current.left is None: + current.left = node + return + else: + current = current.left + else: + if current.right is None: + current.right = node + return + else: + current = current.right + + def remove_node(self, parent, right_or_left="right"): + """ + Helper method to remove a node. + Notice how we have to set `parent.xxx` to something. + This is because, in order to remove a node from a binary + tree, what you are really doing is getting rid of all + references to that node. So, we make sure to change + the value stored in `parent.xxx` to a different node + (or `None`) so that we remove the node we're trying to get rid of + """ + if right_or_left == "right": + node = parent.right + else: + node = parent.left + + if node.right is not None: + temp = node.left + if right_or_left == "right": + parent.right = node.right + else: + parent.left = node.right + + if temp is not None: + self.insert_node(temp) + elif node.left is not None: + temp = node.right + if right_or_left == "right": + parent.right = node.left + else: + parent.left = node.left + if temp is not None: + self.insert_node(temp) + else: + if right_or_left == "right": + parent.right = None + else: + parent.left = None + + def __getitem__(self, key): + return self.search(key) + + def __setitem__(self, key, value): + self.insert_node(Node(key, value)) + + def __delitem__(self, key): + """ + Deletes an entry from the binary tree + """ + # case where key to delete is the root + if self.root is not None and self.root.key == key: + if self.root.right is not None: + temp = self.root.left + self.root = self.root.right + if temp is not None: + self.insert_node(temp) + elif self.root.left is not None: + temp = self.root.right + self.root = self.root.left + if temp is not None: + self.insert_node(temp) + else: + self.root = None + + # regular cases + current = self.root + while current is not None: + if current.left is not None and current.left.key == key: + self.remove_node(current, "left") + break + if current.right is not None and current.right.key == key: + self.remove_node(current, "right") + break + + if key < current.key: + current = current.left + if key > current.key: + current = current.right + + def print_structure(self) -> None: + """ + Prints out what the binary tree looks like + """ + if self.root is None: + print("{}") + return + + height = self.get_height() + spacing = 6 + total_width = spacing * (2**height) + + # print top divider + print("-" * total_width) + + current_generation = [self.root] + next_generation = [] + for i in range(1, height + 1): + margin_between = int( + (total_width - spacing * (2 ** (i - 1))) / ((2 ** (i - 1)) + 1) + ) + for node in current_generation: + print(" " * margin_between, end="") + if node is None: + print(" " * spacing, end="") + next_generation.extend([None] * 2) + else: + print(node, end="") + next_generation.extend([node.left, node.right]) + # print a newline + print() + + current_generation = next_generation + next_generation = [] + + # print bottom divider + print("-" * total_width) + + +myBinaryTree = BinaryTree() +myBinaryTree[33] = 22 +myBinaryTree[22] = 11 +myBinaryTree[44] = 33 +myBinaryTree[55] = 22 +del myBinaryTree[33] + +# to see how it internally arranges data +myBinaryTree.print_structure() diff --git a/dsa/chapter2/examples/graph.py b/dsa/chapter2/examples/graph.py new file mode 100644 index 00000000..189040aa --- /dev/null +++ b/dsa/chapter2/examples/graph.py @@ -0,0 +1,68 @@ +# simple graph +my_graph = { + "A": {"B", "C", "E"}, + "B": {"A", "D"}, + "C": {"A"}, + "D": {"B", "E"}, + "E": {"D", "A"}, +} + + +# graph data structure +class Node: + def __init__(self, val: str, neighbors: list = None): + self.val = val + if neighbors: + self.neighbors = neighbors + else: + self.neighbors = set() + + def addNeighbor(self, n): + self.neighbors.add(n) + + +class Graph: + def __init__(self, connections: list = None): + self.nodes = {} + if connections: + self.parse(connections) + + def createNode(self, values: list): + for value in values: + self.nodes[value] = Node(value) + + def createEdge(self, n1: str, n2: str): + self.nodes[n1].addNeighbor(self.nodes[n2]) + self.nodes[n2].addNeighbor(self.nodes[n1]) + + def parse(self, connections: list): + for connection in connections: + if connection[0] not in self.nodes: + self.createNode([connection[0]]) + if connection[1] not in self.nodes: + self.createNode([connection[1]]) + + self.createEdge(connection[0], connection[1]) + + def __repr__(self): + s = "{\n" + for value in self.nodes: + node = self.nodes[value] + s += f"\t{value}: {[n.val for n in node.neighbors]}\n" + s += "}" + return s + + +# Takes in a list of tuples representing connections +my_graph = Graph( + [ + ("A", "B"), + ("B", "E"), + ("E", "D"), + ("D", "F"), + ("D", "A"), + ("A", "C"), + ("C", "B"), + ] +) +print(my_graph) diff --git a/dsa/chapter2/examples/linked_list.py b/dsa/chapter2/examples/linked_list.py new file mode 100644 index 00000000..371c6131 --- /dev/null +++ b/dsa/chapter2/examples/linked_list.py @@ -0,0 +1,216 @@ +from datetime import datetime as d + + +class Node: + def __init__(self, value, prev=None, next=None) -> None: + self.value = value + self.prev = prev + self.next = next + + +class DoublyLinkedList: + def __init__(self) -> None: + self.head = Node(None) + self.head.next = Node(None, self.head) + self.tail = self.head.next + self.size = 0 + + def add_current(self, value, current) -> bool: + """ + Helper method + + O(1) operation + + Arguments: + @param value - the value to insert + + @param current - the node to insert the value in front of + + @returns bool - True on success + """ + if current.next is None: + current.next = Node(value, current) + else: + current.next = Node(value, current, current.next) + if current.next.next: + current.next.next.prev = current.next + self.size += 1 + return True + + def add_front(self, value) -> bool: + """ + O(1) operation + """ + return self.add_current(value, self.head) + + def add_back(self, value) -> bool: + """ + O(1) operation + """ + return self.add_current(value, self.tail.prev) + + def add(self, value, idx) -> bool: + """ + O(N) operation since it has to iterate to idx + """ + if idx > self.size: + return False + current = self.head + for i in range(idx): + current = current.next + self.add_current(value, current) + + def set(self, value, idx) -> bool: + """ + O(N) operation since it has to iterate to idx + """ + if idx >= self.size: + return False + current = self.head.next + for i in range(idx): + current = current.next + current.value = value + return True + + def set_front(self, value) -> bool: + """ + O(1) operation + """ + if self.size == 0: + return False + self.head.next.value = value + return True + + def set_back(self, value) -> bool: + """ + O(1) operation + """ + if self.size == 0: + return False + self.tail.prev.value = value + return True + + def remove_current(self, current) -> bool: + """ + Helper method (O(1) operation) + + Arguments: + @param current - the node to be removed + + @returns bool - True on success + """ + current.prev.next = current.next + if current.prev.next: + current.prev.next.prev = current.prev + self.size -= 1 + return True + + def remove_value(self, value) -> bool: + """ + Attempts to remove the first occurrence of value from the list. + + O(N) operation since it has to iterate through the list to find the value + + @returns bool - True on success (value found and removed), + False on failure to find the value + """ + current = self.head.next + + # advance the cursor until either we've reached the end + # of the list or we've reached the value + while current != self.tail and current.value != value: + current = current.next + + # found the item, time to remove + if current != self.tail and current.value == value: + self.remove_current(current) + return True + + # didn't find the item + return False + + def remove_front(self) -> bool: + if self.size == 0: + return True + return self.remove_current(self.head.next) + + def remove_back(self) -> bool: + if self.size == 0: + return True + return self.remove_current(self.tail.prev) + + def remove_idx(self, idx) -> bool: + """ + O(N) operation since it has to iterate to idx + """ + if idx >= self.size: + return False + current = self.head.next + for i in range(idx): + current = current.next + return self.remove_current(current) + + def clear(self) -> bool: + """ + O(1) operation + """ + self.head.next = self.tail + self.tail.prev = self.head + self.size = 0 + + def __str__(self) -> str: + ret = "[" + current = self.head.next + while current.next is not None: + ret += str(current.value) + current = current.next + if current.next is not None: + ret += ", " + ret += "]" + return ret + + def print_from_front(self) -> None: + print(self) + + def print_from_back(self) -> None: + current = self.tail.prev + print("[", end="") + while current.prev is not None: + print(current.value, end="") + current = current.prev + if current.prev is not None: + print(", ", end="") + print("]") + + +my_double = DoublyLinkedList() +my_regular = [] + +TEST_SIZE = 100000 + +start = d.now() +for i in range(TEST_SIZE): + my_regular.insert(0, i) +end = d.now() +print(f"it took {(end-start).total_seconds()} seconds to do that regularly") + +start = d.now() +for i in range(TEST_SIZE): + my_double.add_front(i) +end = d.now() +print(f"it took {(end-start).total_seconds()} seconds to do that doubly") + +start = d.now() +for i in range(TEST_SIZE - 1): + my_regular.pop(0) +end = d.now() +print(f"it took {(end-start).total_seconds()} seconds to do that regularly") + +start = d.now() +for i in range(TEST_SIZE - 1): + my_double.remove_front() +end = d.now() +print(f"it took {(end-start).total_seconds()} seconds to do that doubly") + +print(my_regular) +print(my_double) diff --git a/dsa/chapter2/examples/queue.py b/dsa/chapter2/examples/queue.py new file mode 100644 index 00000000..386fa5bd --- /dev/null +++ b/dsa/chapter2/examples/queue.py @@ -0,0 +1,32 @@ +class Queue: + def __init__(self): + # Make List + self.queue_list = [] + + def appending(self, item): + # Checks to see if there are any duplicates in list + if item in self.queue_list: + # If so it returns error + return "Value Already Exists" + else: + self.queue_list.append(item) + + def pops(self): + # Checks to see if list is empty + if len(self.queue_list) != 0: + # if it isn’t empty it removes first value + return self.queue_list.pop(0) + else: + return "List is Empty" + + +Check_Queue = Queue() + +# Should add value to list +Check_Queue.appending(100) +Check_Queue.appending(200) +Check_Queue.appending(300) + +# Should print 300 and then 200 +print(Check_Queue.pops()) +print(Check_Queue.pops()) diff --git a/dsa/chapter2/examples/stack.py b/dsa/chapter2/examples/stack.py new file mode 100644 index 00000000..bc2e00a3 --- /dev/null +++ b/dsa/chapter2/examples/stack.py @@ -0,0 +1,32 @@ +class Stack: + def __init__(self): + # Make List + self.stack_list = [] + + def appending(self, item): + # Checks to see if there are any duplicates in list + if item in self.stack_list: + # If so it returns error + return "Value Already Exists" + else: + self.stack_list.append(item) + + def pops(self): + # Checks to see if list is empty + if len(self.stack_list) != 0: + # if it isn’t empty it removes last value + return self.stack_list.pop() + else: + return "List is Empty" + + +Check_Stack = Stack() + +# Should add value to list +Check_Stack.appending(100) +Check_Stack.appending(200) +Check_Stack.appending(300) + +# Should print 300 and then 200 +print(Check_Stack.pops()) +print(Check_Stack.pops()) diff --git a/dsa/chapter2/practice/basic_bst.py b/dsa/chapter2/practice/basic_bst.py new file mode 100644 index 00000000..5f6469fa --- /dev/null +++ b/dsa/chapter2/practice/basic_bst.py @@ -0,0 +1,112 @@ +""" +Let's create a very basic version of a BST that only +has insertion capabilities. Your task will be to fill +in the node class and the BST class. The structure is somewhat +different from the BST that was given as an example, but +the logic is the same. +""" + + +class BSTNode: + def __init__(self, key, value): + """ + set self.key to key + set self.value to the value + create a left neighbor/child and a right neighbor/child, each of which + start as None + """ + + # your code here + pass + + def add_recursively(self, other_node): + """ + Adds the other node to this node or this node's children. + If other_node's key is equal to this node's key, do nothing. + If other_node's key is less than this node's key, then: + - if this node's left child is None, set this node's left child + to that other node + - if this node's left child is not None, then add this node + recursively to the left child + If other_node's key is greater than this node's key, then: + - if this node's right child is None, set this node's right child + to that other node + - if this node's right child is not None, then add this node + recursively to the right child + """ + + # your code here + pass + + def get_value(self, key): + """ + Tries to return the value of the node whose key matches `key`. + If this node's key matches `key`, return this node's value. + If the key is less than this node's key: + - if left child is None, return 0 + - else, get the value recursively + If the key is greater than this node's key: + - if right child is None, return 0 + - else, get the value recursively + """ + + # your code here + pass + + def __str__(self): + """ + Creates and returns a string that looks like: + left_child, self value, right_child + However, if left child or right_child is None, don't add them + to the string. + """ + + # your code here + pass + + +class BST: + def __init__(self): + # set a root node to None + + # your code here + pass + + def __setitem__(self, item, value): + """ + create a new node whose key is item and whose value is value + then, if root is None, set root to that node. + else, add that node recursively. + """ + + # your code here + pass + + def __getitem__(self, item): + """ + Try to find the node with key that matches item. + If no match is found, return 0 + """ + + # your code here + pass + + def __repr__(self): + """ + Returns a string representation of the root + """ + # your code here + pass + + +my_bst = BST() +my_bst[50] = 30 +my_bst[40] = 31 +my_bst[60] = 32 +my_bst[30] = 33 +my_bst[70] = 34 +my_bst[20] = 35 +my_bst[80] = 36 +my_bst[10] = 37 +my_bst[90] = 38 +print(my_bst) # 37, 35, 33, 31, 10, 32, 34, 36, 38 diff --git a/dsa/chapter2/practice/basic_linked_list.py b/dsa/chapter2/practice/basic_linked_list.py new file mode 100644 index 00000000..08ffd30d --- /dev/null +++ b/dsa/chapter2/practice/basic_linked_list.py @@ -0,0 +1,136 @@ +""" +In this problem, you will create a basic Doubly Linked List. +The goal is to be able to have nodes connected all the way +to 100. All you have to do is fill in the add_front and +add_back methods. +""" + + +class DoublyLinkedListNode: + def __init__(self, value) -> None: + self.value = value + + self.next = None + self.prev = None + + def __repr__(self): + return f"{self.value}" + + +class DoublyLinkedList: + def __init__(self) -> None: + """ + Creates a head and tail node. For convenience, + set the head to a node with the value `None` + and the tail to a node with the value `None`. + Sets head's next node as tail, and tail's previous + node as head. + Sets the size of the list to 0 as well. + + This way, the list will be "empty" when + the head's next node is the tail (and the tail's + previous node is the head). The purpose of these + nodes is to make insertion and deletion much faster. + They will not store any value besides `None` and can + be thought of as placeholders for the beginning and + end of the list + """ + # create the nodes + self.head = DoublyLinkedListNode(None) + self.tail = DoublyLinkedListNode(None) + + # set head's next to tail, and tail's prev to head + self.head.next, self.tail.prev = self.tail, self.head + + # set the size of the list to 0 + self.size = 0 + + def add_front(self, value): + """ + Adds a node with the provided value to the front + of the Doubly Linked List. Increases size by 1 as well. + + By "front of the Doubly Linked List," we mean that it + should be the node right after the placeholder head node. + + Ex: if you had nodes A, B, C, D, and you inserted node E, + then you would have A, E, B, C, D. A's next node would be + E, E's next node would be B, B's prev node would be E, + and E's prev node would be A. + """ + # create a node with the provided value + # add it to the front of the Doubly Linked List + # make sure to correctly set the prev/next nodes + # your code here + + # increase size by 1 + self.size += 1 + + def add_back(self, value): + """ + Adds a node with the provided value to the back + of the Doubly Linked List. Increases size by 1 as well. + + By "back of the Doubly Linked List," we mean that it should + be the node right before the placeholder tail node. + + Ex: if you had nodes A, B, C, D, and you inserted node E, + then you would have A, B, C, E, D. C's next node would be + E, E's next node would be D, D's prev node would be E, + and E's prev node would be C. + """ + # create a node with the provided value + # add it to the back of the Doubly Linked List + # make sure to correctly set the prev/next nodes + + # increase size by 1 + self.size += 1 + + def print_forward(self): + """ + Iterates through and prints all the nodes. + This should start at the head and end at the tail. + """ + # ignore self.head since self.head is a "placeholder" + cur_node = self.head.next + + while cur_node.next is not None: + print(cur_node, end=", ") + cur_node = cur_node.next + print() + + def print_backward(self): + """ + Iterates through and prints all the nodes. + This should start at the tail and end at the tail + """ + # ignore self.tail since self.tail is a "placeholder" + cur_node = self.tail.prev + + while cur_node.prev is not None: + print(cur_node, end=", ") + cur_node = cur_node.prev + print() + + def __len__(self): + """ + Returns the length of the list + """ + return self.size + + +our_doubly_linked_list = DoublyLinkedList() + +# add the numbers 50-99 +for i in range(50, 100): + our_doubly_linked_list.add_back(i) + +# add numbers 49 - 0 +for i in range(49, -1, -1): + our_doubly_linked_list.add_front(i) + +# print our list forward (0 -> 99) +our_doubly_linked_list.print_forward() + +# print our list backward (99 -> 9) +our_doubly_linked_list.print_backward() diff --git a/dsa/chapter2/practice/nodes_to_10.py b/dsa/chapter2/practice/nodes_to_10.py new file mode 100644 index 00000000..e8af4d1f --- /dev/null +++ b/dsa/chapter2/practice/nodes_to_10.py @@ -0,0 +1,35 @@ +""" +Nodes to 10 + +Fill in the node class, then create nodes so that +printing start_node will print the numbers from 0 to 10. +""" + + +class Node: + def __init__(self, value): + self.value = value + + # create a neighbors list + # your code here + + def __repr__(self) -> str: + # start with just the node's value + ret = f"{self.value}, " + + # add all of the neighbors' values (recursively) to the string + for node in self.neighbors: + ret += f"{node}" + + return ret + + +start_node = Node(0) + +# add code that creates nodes and adds them as neighbors so that +# start_node is connected to 1, 1 is connected to 2, 2 is connected to 3, +# 3 is connected to 4, etc. If done correctly, printing start_node will +# print 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, +# your code here + +print(start_node) diff --git a/dsa/chapter2/practice/restaurant_queue.py b/dsa/chapter2/practice/restaurant_queue.py new file mode 100644 index 00000000..1aea151a --- /dev/null +++ b/dsa/chapter2/practice/restaurant_queue.py @@ -0,0 +1,56 @@ +""" +Cook cook cook, orders all day + +As a chef in a restaurant, you cook a bunch of dishes +You can only take one order at a time, and you're tired of having +people complain at you when you don't do their order first. So you decide to +set up a system where you accumulate a "list" of orders and cook one order +-- the first order that was put into the "list" -- at a time. + +Your job is to implement this "list" as an OrderQueue. +You should be able to add new orders into your OrderQueue +and remove finished orders from your OrderQueue. +Starter code is provided. +""" + + +class OrderQueue: + def __init__(self) -> None: + self.orders = [] + + def dequeue(self) -> str: + """ + Removes the first order in the OrderQueue + and returns it + """ + # your code here + pass + + def enqueue(self, order: str) -> None: + """ + Inserts the order into the OrderQueue + + Args: + order: str - the order to be inserted into the OrderQueue + """ + # your code here + pass + + def __len__(self): + return len(self.orders) + + +# test code +uncooked_orders = OrderQueue() + +# 3 customers ordered at the same time +uncooked_orders.enqueue("a medium rare steak") +uncooked_orders.enqueue("six gyoza") +uncooked_orders.enqueue("two enchiladas") + +# now you cook them one by one +for order in range(len(uncooked_orders)): + # this should print + # the medium rare steak first, then the gyoza, and finally the enchiladas + print(f"finished cooking {uncooked_orders.dequeue()}") +print(f"done! (exactly {len(uncooked_orders)} orders left)") diff --git a/dsa/chapter2/practice/word_reversal.py b/dsa/chapter2/practice/word_reversal.py new file mode 100644 index 00000000..dfd89765 --- /dev/null +++ b/dsa/chapter2/practice/word_reversal.py @@ -0,0 +1,66 @@ +""" +Ever wanted to reverse a word a harder way? +Well, look no further than this problem that puts your +knowledge of Stacks to the test in order to solve a problem +that is already solveable by python builtins! + +Your job is to reverse a string by using a stack, +adding every letter in the string (starting from the beginning of the string) +into the stack, and then popping every letter from the stack. +If done correctly, this will result in a reversed version of the string. + +Starter code is given. +""" + + +class Stack: + def __init__(self) -> None: + """ + Initializes the stack. The back of the list will be + the top of the stack (so self.items[-1] is the first item + in the stack) + """ + self.items = [] + + def push(self, letter: str) -> None: + """ + Adds the letter to the stack. The letter should end up + on the *top* of the stack (the back of the list) + """ + # your code here + pass + + def pop(self) -> str: + """ + Removes the top letter from the stack. Returns this letter. + """ + # your code here + pass + + def __len__(self) -> int: + return len(self.items) + + +def reverse_word(word: str) -> str: + letter_stack = Stack() + + # push every letter in the word (starting from the beginning of the word) + # into the stack + for letter in word: + # your code here + pass + + reversed_word = "" + # pop every letter from the stack and add it to our reversed word + for i in range(len(letter_stack)): + # your code here + pass + + return reversed_word + + +# test code +print(reverse_word("boj doog")) +print(reverse_word("racecar")) +print(reverse_word("a man a plan a canal panama")) +print(reverse_word("read kool")) diff --git a/dsa/chapter2/solutions/basic_bst.py b/dsa/chapter2/solutions/basic_bst.py new file mode 100644 index 00000000..5186e4e3 --- /dev/null +++ b/dsa/chapter2/solutions/basic_bst.py @@ -0,0 +1,151 @@ +""" +Let's create a very basic version of a BST that only +has insertion capabilities. Your task will be to fill +in the node class and the BST class. The structure is somewhat +different from the BST that was given as an example, but +the logic is the same. +""" + + +class BSTNode: + def __init__(self, key, value): + """ + set self.key to key + set self.value to the value + create a left neighbor/child and a right neighbor/child, each of which + start as None + """ + + # set key, value + self.key, self.value = key, value + + # set left child, right child + self.left_child: BSTNode = None + self.right_child: BSTNode = None + + def add_recursively(self, other_node): + """ + Adds the other node to this node or this node's children. + If other_node's key is equal to this node's key, do nothing. + If other_node's key is less than this node's key, then: + - if this node's left child is None, set this node's left child + to that other node + - if this node's left child is not None, then add this node + recursively to the left child + If other_node's key is greater than this node's key, then: + - if this node's right child is None, set this node's right child + to that other node + - if this node's right child is not None, then add this node + recursively to the right child + """ + if other_node.key == self.key: + return # do nothing + + if other_node.key < self.key: + if self.left_child is None: + self.left_child = other_node + else: + self.left_child.add_recursively(other_node) + + if other_node.key > self.key: + if self.right_child is None: + self.right_child = other_node + else: + self.right_child.add_recursively(other_node) + + def get_value(self, key): + """ + Tries to return the value of the node whose key matches `key`. + If this node's key matches `key`, return this node's value. + If the key is less than this node's key: + - if left child is None, return 0 + - else, get the value recursively + If the key is greater than this node's key: + - if right child is None, return 0 + - else, get the value recursively + """ + if self.key == key: + return self.value + + if key < self.key: + if self.left_child is None: + return 0 + else: + return self.left_child.get_value(key) + + if key > self.key: + if self.right_child is None: + return 0 + else: + return self.right_child.get_value(key) + + def __str__(self): + """ + Creates and returns a string that looks like: + left_child, self value, right_child + However, if left child or right_child is None, don't add them + to the string. + """ + ret = "" + + # add left child to the string if it isn't None + if self.left_child is not None: + ret += str(self.left_child) + ", " + + # add self.value to the string + ret += str(self.value) + + # add right child to the string if it isn't None + if self.right_child is not None: + ret += ", " + str(self.right_child) + + return ret + + +class BST: + def __init__(self): + # set a root node to None + self.root = None + + def __setitem__(self, item, value): + """ + create a new node whose key is item and whose value is value + then, if root is None, set root to that node. + else, add that node recursively. + """ + # create the new node + new_node = BSTNode(item, value) + + # either add it recursively or set it as the root + if self.root is None: + self.root = new_node + else: + self.root.add_recursively(new_node) + + def __getitem__(self, item): + """ + Try to find the node with key that matches item. + If root is None or no match is found return 0 + """ + if self.root is None: + return 0 + return self.root.get_value(item) + + def __repr__(self): + """ + Returns a string representation of the root + """ + return str(self.root) + + +my_bst = BST() +my_bst[50] = 30 +my_bst[40] = 31 +my_bst[60] = 32 +my_bst[30] = 33 +my_bst[70] = 34 +my_bst[20] = 35 +my_bst[80] = 36 +my_bst[10] = 37 +my_bst[90] = 38 +print(my_bst) diff --git a/dsa/chapter2/solutions/basic_linked_list.py b/dsa/chapter2/solutions/basic_linked_list.py new file mode 100644 index 00000000..0719dea7 --- /dev/null +++ b/dsa/chapter2/solutions/basic_linked_list.py @@ -0,0 +1,147 @@ +""" +In this problem, you will create a basic Doubly Linked List. +The goal is to be able to have nodes connected all the way +to 100. All you have to do is fill in the add_front and +add_back methods. +""" + + +class DoublyLinkedListNode: + def __init__(self, value) -> None: + self.value = value + + self.next = None + self.prev = None + + def __repr__(self): + return f"{self.value}" + + +class DoublyLinkedList: + def __init__(self) -> None: + """ + Creates a head and tail node. For convenience, + set the head to a node with the value `None` + and the tail to a node with the value `None`. + Sets head's next node as tail, and tail's previous + node as head. + Sets the size of the list to 0 as well. + + This way, the list will be "empty" when + the head's next node is the tail (and the tail's + previous node is the head). The purpose of these + nodes is to make insertion and deletion much faster. + They will not store any value besides `None` and can + be thought of as placeholders for the beginning and + end of the list + """ + # create the nodes + self.head = DoublyLinkedListNode(None) + self.tail = DoublyLinkedListNode(None) + + # set head's next to tail, and tail's prev to head + self.head.next, self.tail.prev = self.tail, self.head + + # set the size of the list to 0 + self.size = 0 + + def add_front(self, value): + """ + Adds a node with the provided value to the front + of the Doubly Linked List. Increases size by 1 as well. + + By "front of the Doubly Linked List," we mean that it + should be the node right after the placeholder head node. + + Ex: if you had nodes A, B, C, D, and you inserted node E, + then you would have A, E, B, C, D. A's next node would be + E, E's next node would be B, B's prev node would be E, + and E's prev node would be A. + """ + # create a node with the provided value + new_node = DoublyLinkedListNode(value) + + # get the old second-to-front node + old_second_to_front_node = self.head.next + + # change the orders + old_second_to_front_node.prev, new_node.prev = new_node, self.head + self.head.next, new_node.next = new_node, old_second_to_front_node + + # increase size by 1 + self.size += 1 + + def add_back(self, value): + """ + Adds a node with the provided value to the back + of the Doubly Linked List. Increases size by 1 as well. + + By "back of the Doubly Linked List," we mean that it should + be the node right before the placeholder tail node. + + Ex: if you had nodes A, B, C, D, and you inserted node E, + then you would have A, B, C, E, D. C's next node would be + E, E's next node would be D, D's prev node would be E, + and E's prev node would be C. + """ + # create a node with the provided value + new_node = DoublyLinkedListNode(value) + + # get the old second-to-last node + old_second_to_last_node = self.tail.prev + + # change the orders + old_second_to_last_node.next, new_node.next = new_node, self.tail + self.tail.prev, new_node.prev = new_node, old_second_to_last_node + + # increase size by 1 + self.size += 1 + + def print_forward(self): + """ + Iterates through and prints all the nodes. + This should start at the head and end at the tail. + """ + # ignore self.head since self.head is a "placeholder" + cur_node = self.head.next + + while cur_node.next is not None: + print(cur_node, end=", ") + cur_node = cur_node.next + print() + + def print_backward(self): + """ + Iterates through and prints all the nodes. + This should start at the tail and end at the tail + """ + # ignore self.tail since self.tail is a "placeholder" + cur_node = self.tail.prev + + while cur_node.prev is not None: + print(cur_node, end=", ") + cur_node = cur_node.prev + print() + + def __len__(self): + """ + Returns the length of the list + """ + return self.size + + +our_doubly_linked_list = DoublyLinkedList() + +# add the numbers 50-99 +for i in range(50, 100): + our_doubly_linked_list.add_back(i) + +# add numbers 49 - 0 +for i in range(49, -1, -1): + our_doubly_linked_list.add_front(i) + +# print our list forward (0 -> 99) +our_doubly_linked_list.print_forward() + +# print our list backward (99 -> 9) +our_doubly_linked_list.print_backward() diff --git a/dsa/chapter2/solutions/nodes_to_10.py b/dsa/chapter2/solutions/nodes_to_10.py new file mode 100644 index 00000000..aef5a221 --- /dev/null +++ b/dsa/chapter2/solutions/nodes_to_10.py @@ -0,0 +1,39 @@ +""" +Nodes to 10 + +Fill in the node class, then create nodes so that +printing start_node will print the numbers from 0 to 10. +""" + + +class Node: + def __init__(self, value): + self.value = value + + # create a neighbors list + self.neighbors = [] + + def __repr__(self) -> str: + # start with just the node's value + ret = f"{self.value}, " + + # add all of the neighbors' values (recursively) to the string + for node in self.neighbors: + ret += f"{node}" + + return ret + + +start_node = Node(0) + +# add code that creates nodes and adds them as neighbors so that +# start_node is connected to 1, 1 is connected to 2, 2 is connected to 3, +# 3 is connected to 4, etc. If done correctly, printing start_node will +# print 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, +previous_node = start_node +for value in range(1, 11): + cur_node = Node(value) + previous_node.neighbors.append(cur_node) + previous_node = cur_node + +print(start_node) diff --git a/dsa/chapter2/solutions/restaurant_queue.py b/dsa/chapter2/solutions/restaurant_queue.py new file mode 100644 index 00000000..2eb55e07 --- /dev/null +++ b/dsa/chapter2/solutions/restaurant_queue.py @@ -0,0 +1,54 @@ +""" +Cook cook cook, orders all day + +As a chef in a restaurant, you cook a bunch of dishes +You can only take one order at a time, and you're tired of having +people complain at you when you don't do their order first. So you decide to +set up a system where you accumulate a "list" of orders and cook one order +-- the first order that was put into the "list" -- at a time. + +Your job is to implement this "list" as an OrderQueue. +You should be able to add new orders into your OrderQueue +and remove finished orders from your OrderQueue. +Starter code is provided. +""" + + +class OrderQueue: + def __init__(self) -> None: + self.orders = [] + + def dequeue(self) -> str: + """ + Removes the first order in the OrderQueue + and returns it + """ + return self.orders.pop(0) + + def enqueue(self, order: str) -> None: + """ + Inserts the order into the OrderQueue + + Args: + order: str - the order to be inserted into the OrderQueue + """ + self.orders.append(order) + + def __len__(self): + return len(self.orders) + + +# test code +uncooked_orders = OrderQueue() + +# 3 customers ordered at the same time +uncooked_orders.enqueue("a medium rare steak") +uncooked_orders.enqueue("six gyoza") +uncooked_orders.enqueue("two enchiladas") + +# now you cook them one by one +for order in range(len(uncooked_orders)): + # this should print + # the medium rare steak first, then the gyoza, and finally the enchiladas + print(f"finished cooking {uncooked_orders.dequeue()}") +print(f"done! (exactly {len(uncooked_orders)} orders left)") diff --git a/dsa/chapter2/solutions/word_reversal.py b/dsa/chapter2/solutions/word_reversal.py new file mode 100644 index 00000000..e8372fcb --- /dev/null +++ b/dsa/chapter2/solutions/word_reversal.py @@ -0,0 +1,62 @@ +""" +Ever wanted to reverse a word a harder way? +Well, look no further than this problem that puts your +knowledge of Stacks to the test in order to solve a problem +that is already solveable by python builtins! + +Your job is to reverse a string by using a stack, +adding every letter in the string (starting from the beginning of the string) +into the stack, and then popping every letter from the stack. +If done correctly, this will result in a reversed version of the string. + +Starter code is given. +""" + + +class Stack: + def __init__(self) -> None: + """ + Initializes the stack. The back of the list will be + the top of the stack (so self.items[-1] is the first item + in the stack) + """ + self.items = [] + + def push(self, letter: str) -> None: + """ + Adds the letter to the stack. The letter should end up + on the *top* of the stack (the back of the list) + """ + self.items.append(letter) + + def pop(self) -> str: + """ + Removes the top letter from the stack. Returns this letter. + """ + return self.items.pop() + + def __len__(self) -> int: + return len(self.items) + + +def reverse_word(word: str) -> str: + letter_stack = Stack() + + # push every letter in the word (starting from the beginning of the word) + # into the stack + for letter in word: + letter_stack.push(letter) + + reversed_word = "" + # pop every letter from the stack and add it to our reversed word + for i in range(len(letter_stack)): + reversed_word += letter_stack.pop() + + return reversed_word + + +# test code +print(reverse_word("boj doog")) +print(reverse_word("racecar")) +print(reverse_word("a man a plan a canal panama")) +print(reverse_word("read kool")) diff --git a/dsa/chapter3/examples/a_star.py b/dsa/chapter3/examples/a_star.py new file mode 100644 index 00000000..254b2337 --- /dev/null +++ b/dsa/chapter3/examples/a_star.py @@ -0,0 +1,84 @@ +from queue import PriorityQueue + + +def heuristic(cell, end): + # assuming (100, 100) is end + x1, y1 = cell + x2, y2 = end + return abs(x2 - x1) + abs(y2 - y1) + + +def reconstruct(path, end): + final = [] + curr = end + while curr in path: + final.append(curr) + curr = path[curr] + + return reversed(final) + + +def get_neighbors(cell, start=(0, 0), end=(100, 100)): + def between(a, b, c): + return (b <= a and a <= c) or (b >= a and a >= c) + + x1, y1 = cell + return [ + (x + x1, y + y1) + for x in range(-1, 2) + for y in range(-1, 2) + if ( + between(x + x1, start[0], end[0]) + and between(y + y1, start[1], end[1]) + ) + ] + + +def a_star(end: tuple = (100, 100), start: tuple = (0, 0)): + count = 0 + open = PriorityQueue() + open.put((0, count, start)) + path = {} + g_score = { + (x, y): float("inf") + for x in range(end[0] + 1) + for y in range(end[1] + 1) + } + g_score[start] = 0 + + f_score = { + (x, y): float("inf") for x in range(end[0]) for y in range(end[1]) + } + f_score[0] = heuristic(start, end) + + while not open.empty(): + curr = open.get()[2] + + temp_g = g_score[curr] + 1 + for n in get_neighbors(curr, start, end): + if n == end: + path[n] = curr + return reconstruct(path, end) + if temp_g < g_score[n]: + path[n] = curr + g_score[n] = temp_g + f_score[n] = temp_g + heuristic(n, end) + if not any(n == item[2] for item in open.queue): + count += 1 + open.put((f_score[n], count, n)) + + # path not found + return None + + +path = [coord for coord in a_star((50, 10))] +path.insert(0, (0, 0)) + +for y in range(0, 11): + lst = [] + for x in range(0, 51): + if (x, y) not in path: + lst.append("x") + else: + lst.append("o") + print(lst) diff --git a/dsa/chapter3/examples/bfs.py b/dsa/chapter3/examples/bfs.py new file mode 100644 index 00000000..5fe04c0a --- /dev/null +++ b/dsa/chapter3/examples/bfs.py @@ -0,0 +1,19 @@ +from queue import Queue + + +def BFS(start_node): + visited = set(start_node) + current_depth_nodes = Queue() + current_depth_nodes.put(start_node) + + while len(current_depth_nodes) != 0: # this depth is not empty + # returns and deletes the first element + current_node = current_depth_nodes.get() + + # add each neighbor to this depth + for neighbor in current_node.neighbors: + if neighbor not in visited: + # adds element to end + visited.add(neighbor) + # adds element to end + current_depth_nodes.put(neighbor) diff --git a/dsa/chapter3/examples/binary_search.py b/dsa/chapter3/examples/binary_search.py new file mode 100644 index 00000000..8869c353 --- /dev/null +++ b/dsa/chapter3/examples/binary_search.py @@ -0,0 +1,27 @@ +def binary_search(lst, item): + """ + Arguments: + lst - a list sorted in ascending order + item - the item that we want to find + Returns: + the idx of the item or -1 if not found + """ + + low_bound = 0 + upper_bound = len(lst) - 1 + + # take the average, but make sure it's an integer + cur_idx = (low_bound + upper_bound) // 2 + + while low_bound <= upper_bound: + if lst[cur_idx] == item: + return cur_idx + if lst[cur_idx] < item: + # it was an undershot, so set this as the new lower bound + low_bound = cur_idx + 1 + else: # lst[cur_idx] > item) + # it was an overshot, so set this as the new upper bound + upper_bound = cur_idx - 1 + # update cur_idx + cur_idx = (low_bound + upper_bound) // 2 + return -1 diff --git a/dsa/chapter3/examples/dfs.py b/dsa/chapter3/examples/dfs.py new file mode 100644 index 00000000..7de583cb --- /dev/null +++ b/dsa/chapter3/examples/dfs.py @@ -0,0 +1,20 @@ +def recursive_dfs(visited, graph, node): + if node not in visited: + print(node) + visited.add(node) + for neighbour in graph[node]: + recursive_dfs(visited, graph, neighbour) + + +def iterative_dfs(graph, start): + stack, path = [start], [] + + while stack: + node = stack.pop() + if node in path: + continue + path.append(node) + for neighbor in graph[node]: + stack.append(neighbor) + + return path diff --git a/dsa/chapter3/examples/linear_search.py b/dsa/chapter3/examples/linear_search.py new file mode 100644 index 00000000..e8821336 --- /dev/null +++ b/dsa/chapter3/examples/linear_search.py @@ -0,0 +1,14 @@ +def linear_search(arr, val) -> int: + """ + Search the provided array for the provided value + and get the index, if found + Arguments: + arr - the array to search in + val - the value to search for + Returns: + int - the index of the value if it was found, else -1 + """ + for i in range(len(arr)): + if arr[i] == val: + return i + return -1 diff --git a/dsa/chapter3/examples/mergesort.py b/dsa/chapter3/examples/mergesort.py new file mode 100644 index 00000000..235d46c9 --- /dev/null +++ b/dsa/chapter3/examples/mergesort.py @@ -0,0 +1,42 @@ +def mergelists(lst1, lst2): + idx1 = 0 + idx2 = 0 + ret = [] + + # while either one has unused items + while idx1 < len(lst1) or idx2 < len(lst2): + # both lists still have items + if idx1 < len(lst1) and idx2 < len(lst2): + if lst1[idx1] < lst2[idx2]: + ret.append(lst1[idx1]) # add the item from lst1 + idx1 += 1 # increment our idx in lst1 + else: # lst2[idx2] <= lst1[idx1] + ret.append(lst2[idx2]) # add the item from lst2 + idx2 += 1 # increment our idx in lst2 + + # only one list still has items + elif idx1 < len(lst1): # if only lst1 still has items + ret.extend(lst1[idx1:]) # add the rest of this list + idx1 = len(lst1) + elif idx2 < len(lst2): # if only lst2 still has items + ret.extend(lst2[idx2:]) # add the rest of this list + idx2 = len(lst2) + + return ret + + +def mergesort(lst): + # "base case" where the list is just 0 or 1 item(s). + # In this case, we can say it is already sorted and just return it. + if len(lst) <= 1: + return lst + + # if it's not just 1 or 0 item(s), then follow mergesort logic + middle_idx = len(lst) // 2 # we want an integer, so use // + first_half = mergesort(lst[:middle_idx]) # sort the first half + second_half = mergesort(lst[middle_idx:]) # sort the second half + return mergelists(first_half, second_half) # merge the two sorted halves + + +print(mergesort([5, 4, 3, 2, 1])) +print(mergesort([i for i in range(99, -1, -1)])) diff --git a/dsa/chapter3/examples/quicksort.py b/dsa/chapter3/examples/quicksort.py new file mode 100644 index 00000000..13c41aac --- /dev/null +++ b/dsa/chapter3/examples/quicksort.py @@ -0,0 +1,71 @@ +def partitionv1(arr, pi): + """ + partitionv1 takes some pivot index (pi), and puts all of the items + smaller than pivot to the left, and all of the items larger than + pivot to the right, + + in doing so we put pivot in the same spot as if the entire list was + sorted + + note: this is only one way of doing it + """ + # moves pivot to the end of the list so it doesn't get in the way + arr[-1], arr[pi] = arr[pi], arr[-1] + + i = 0 # i is initialized to be the left side of our list + + for j in range(len(arr)): + # if j is smaller than the pivot, arr[j] is smaller than the pivot, + # so we want to move it to the left + if arr[j] < arr[-1]: + # swaps arr[j] and arr[i], so arr[j] is at the left side of the list + arr[i], arr[j] = arr[j], arr[i] + i += 1 + # move the pivot from the end to the correct location + arr[i], arr[-1] = arr[-1], arr[i] + + +def partitionv2(arr, low, high): + """ + partitionv2 takes an array, a low, and a high and partitions + the section of the array between low and high (inclusive). + + partitionv2 always partitions with the last element in the + section as the pivot + """ + + i = low # i is initialized to be the left side of our list + + for j in range(low, high): + # if j is smaller than the pivot, arr[j] is smaller than the pivot, + # so we want to move it to the left + if arr[j] < arr[high]: + # swaps arr[j] and arr[i], so arr[j] is at the left side of the list + arr[i], arr[j] = arr[j], arr[i] + i += 1 + # move the pivot from the end to the correct location + arr[i], arr[high] = arr[high], arr[i] + + +def quicksort(arr, low, high): + """ + quicksort that recursively partitions the left nd right side + of the pivot + + This implementation always partitions with the last element as the pivot + """ + + i = low # i is initialized to be the left side of our list + + for j in range(low, high): + # if j is smaller than the pivot, arr[j] is smaller than the pivot, + # so we want to move it to the left + if arr[j] < arr[high]: + # swaps arr[j] and arr[i], so arr[j] is at the left side of the list + arr[i], arr[j] = arr[j], arr[i] + i += 1 + # move the pivot from the end to the correct location + arr[i], arr[high] = arr[high], arr[i] + + quicksort(arr, low, i - 1) # left side of the pivot + quicksort(arr, i + 1, high) # right side of the pivot diff --git a/dsa/chapter3/examples/selection_sort.py b/dsa/chapter3/examples/selection_sort.py new file mode 100644 index 00000000..b8731de5 --- /dev/null +++ b/dsa/chapter3/examples/selection_sort.py @@ -0,0 +1,27 @@ +def selection_sort(lst): + for i in range(len(lst)): + min_value = lst[i] + min_val_idx = i + + # find the new minimum value and its idx + for x in range(i, len(lst)): + if lst[x] < min_value: + min_value = lst[x] + min_val_idx = x + + # swap the minimum value with the value at the current idx + lst[i], lst[min_val_idx] = min_value, lst[i] + return lst + + +test1 = [3, 12, 7, 2, 0, 3] +test1 = selection_sort(test1) +print(test1) # [0, 2, 3, 3, 7, 12] + +test2 = [-23, 0, 72, -33, 11, 6, 2, -5, -9, 10, -1] +test2 = selection_sort(test2) +print(test2) # [-33, -23, -9, -5, -1, 0, 2, 6, 10, 11, 72] + +test3 = [i for i in range(1000, -1, -1)] +test3 = selection_sort(test3) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...] +print(test3) diff --git a/dsa/chapter3/practice/a_star.py b/dsa/chapter3/practice/a_star.py new file mode 100644 index 00000000..eb2b598a --- /dev/null +++ b/dsa/chapter3/practice/a_star.py @@ -0,0 +1,186 @@ +""" +A Star Practice + +In this practice problem, you get to fill in some a_star code +as well as see the effects of using different heuristics on +a_star's execution time. + +Heuristics and helper functions are given. Your job is to fill +in sections of the A* algorithm where it says 'your code here' +""" + +from queue import PriorityQueue +import math +import random + + +class Point: + def __init__(self, x: int, y: int) -> None: + self.x: int = x + self.y: int = y + + def get_neighbors(self, start, end): + """ + This function returns a list of neighboring points + using the fact that neighboring points will be the + following (p = neighboring, c = current) + + ``` + p p p + p c p + p p p + ``` + """ + + def between(a, b, c): + return (b <= a and a <= c) or (b >= a and a >= c) + + return [ + Point(x + self.x, y + self.y) + for x in range(-1, 2) + for y in range(-1, 2) + if ( + between(x + self.x, start.x, end.x) + and between(y + self.y, start.y, end.y) + ) + ] + + def __eq__(self, __o: object) -> bool: + return self.x == __o.x and self.y == __o.y + + def __hash__(self) -> int: + return hash((self.x, self.y)) + + def __str__(self): + return f"({self.x}, {self.y})" + + def __repr__(self) -> str: + return str(self) + + +def adding_heuristic(cur: Point, end: Point): + """ + this heuristic returns a value + that looks like the following: + abs(x-x1) + abs(y-y1). + """ + return abs(cur.x - end.x) + abs(cur.y - end.y) + + +def triangle_heuristic(cur: Point, end: Point): + """ + this heuristic will return a value + based off the pythagorean theorem + that looks like the following + sqrt((x-x1)^2 + (y-y1)^2) + """ + return math.sqrt((cur.x - end.x) ** 2 + (cur.y - end.y) ** 2) + + +def bad_heuristic(cur: Point, end: Point): + """ + This heuristic will return a value + that is the opposite of the distance, + meaning that the closer cur is to end, + the worse (higher) score this will give it + """ + return -(abs(cur.x - end.x) + abs(cur.y - end.y)) + + +def random_heuristic(cur: Point, end: Point): + """ + Returns a totally random number. + """ + return random.randint(cur.x, end.x) + random.randint(cur.y, end.y) + + +def reconstruct_path(path: dict, start, end): + backwards_path = [] + curr = end # we know that we start at the end + while curr in path: + # add the current node to the backwards_path + backwards_path.append(curr) + + # since path is a dictionary of node : how to get there, + # we get the previous node in the path by doing path[curr] + curr = path[curr] + + # this will be the first item after we reverse the list + backwards_path.append(start) + + return reversed(backwards_path) + + +def a_star(start: Point, end: Point, heuristic): + # min_x, max_x = min(start.x, end.x), max(start.x, end.x) # uncomment this + # min_y, max_y = min(start.y, end.y), max(start.y, end.y) # uncomment this + """ + initialize f_scores (final scores) to infinity for every + point between the [min_x, min_y] and [max_x, max_y] + + initialize g_scores (distance to get there) to infinity for every + point between [min_x, min_y] and [max_x, max_y] + + Since it takes 0 steps to get to the start, initialize that g score to 0 + """ + # your code here + + # initialize our unexplored queue and add + # insert the start node. + # format for inserting nodes: (f_score, count, node) + count = 0 + unexplored = PriorityQueue() + unexplored.put((0, count, start)) + + # this is a dictionary that stores entries in the format + # node: how to get there + # this means that path[(1, 1)] might equal (0, 0) + # since maybe the path goes from (0, 0) to (1, 1) + # we use this variable to help us reconstruct the path that + # a star found + path = {} + + # allows us to see how many executions it really took + num_executions = 0 + + while not unexplored.empty(): + current: Point = unexplored.get()[2] # just get the Point + + # it takes 1 more step to get to any neighbor, so their g_scores will + # be one more than the current g score + for node in current.get_neighbors(start, end): + if node == end: + # the way to get to the end is from the current node + path[node] = current + print(f"finished after {num_executions} executions") + return reconstruct_path(path, start, end) + else: + """ + if either we haven't explored this node yet + (meaning g_scores[node] = infinity) or this + is a shorter path to get to this node + (g_score[current] + 1 < g_scores[node]), then: + + * update our path + * update our f and g scores: + * remember, f score = g score + heuristic + * if it wasn't already in unexplored: + * update our count + * add the unexplored node w/ its score and count to + unexplored + """ + # your code here + + num_executions += 1 + print(f"no solution found after {num_executions} executions") + return None # no path found + + +# you can try changing the heuristic and seeing how that affects the path taken +# as well as the number of executions it took +path = a_star(Point(0, 0), Point(10, 15), adding_heuristic) +path_len = 0 +for i in path: + print(i) + path_len += 1 +print(f"path length was {path_len}") diff --git a/dsa/chapter3/practice/bfs_dfs.py b/dsa/chapter3/practice/bfs_dfs.py new file mode 100644 index 00000000..7480d097 --- /dev/null +++ b/dsa/chapter3/practice/bfs_dfs.py @@ -0,0 +1,101 @@ +""" +In this practice problem, we practice implementing +breadth first search and depth first search and see them +in action +""" + + +class Node: + def __init__(self, value: int) -> None: + self.value: int = value + self.children = [] + + def __str__(self) -> str: + return str(self.value) + + def __repr__(self) -> str: + return str(self) + + +def BFS(start_node: Node): + """ + Implement a breadth-first search algorithm, + that prints the nodes that you visit as you go. + + Remember, a breadth-first search algorithm works by + visiting all the children in a certain depth before + advancing to the next depth + + Note that your function will be slightly different from + the one in the example since the nodes in this file + have children and not neighbors + """ + # first, initialize an empty list of all visited nodes + # next, initialize a list of all the nodes at the current depth + # and have it contain start_node + # lastly, create an empty list of all the nodes at the next depth. + # your code here + + # iterate until the the list of nodes at the current depth is empty + # in each iteration, go through all the nodes at the current depth + # and, if it isn't in the list of visited nodes: + # print it + # add it to visited nodes + # add its children to the list of nodes at the next depth + # once done iterating through all the nodes at the current depth, + # set the list of nodes at the current depth equal to the + # list of nodes at the next depth + # and set the next depth to an empty list + # your code here + + +def DFS(start_node: Node, visited: list = []): + """ + Implement a recursive depth-first search algorithm, + that prints the nodes that you visit as you go. + + Remember, a depth-first search algorithm goes all the way + down to the last child before working its way up and visiting + neighbors + + Note that your function will be slightly different from + the one in the example since the nodes in this file + have children and not neighbors + """ + # check if the start node is in visited + # if it isn't, then print the node + # add it to the list of visited, + # and then use DFS on each of its children + # (make sure to pass the list of visited as an argument) + + +if __name__ == "__main__": + # make a graph that looks like the following + # / 5 + # 2 - 6 - 10 + # / + # 1 - 3 - 7 - 11 - 12 + # \ + # 4 - 8 + # \ 9 + start_node = Node(1) + for i in range(3): + start_node.children.append(Node(i + 2)) + start_node.children[0].children.append(Node(5)) + start_node.children[0].children.append(Node(6)) + start_node.children[0].children[1].children.append(Node(10)) + + start_node.children[1].children.append(Node(7)) + start_node.children[1].children[0].children.append(Node(11)) + start_node.children[1].children[0].children[0].children.append(Node(12)) + + start_node.children[2].children.append(Node(8)) + start_node.children[2].children.append(Node(9)) + + print("with BFS") + BFS(start_node) # 1 2 3 4 5 6 7 8 9 10 11 12 + print() + + print("with DFS") + DFS(start_node) # 1 2 5 6 10 3 7 11 12 4 8 9 + print() diff --git a/dsa/chapter3/practice/mergesort.py b/dsa/chapter3/practice/mergesort.py new file mode 100644 index 00000000..11d85596 --- /dev/null +++ b/dsa/chapter3/practice/mergesort.py @@ -0,0 +1,69 @@ +def mergesort(lst: list) -> list: + """ + Let's implement mergesort, + First let's create a base case where if the list is 0 or 1 elements long, + return it + """ + # your code here + + """ + Now that we have handled the base case, if the list is any longer, we can + go into typical mergesort logic, + + We need to split the list into 2 halves, so lets first find the middle + index value. Use // instead of / because we want an integer + """ + # your code here + + """ + Now we can run mergesort on the first and second halves of lst. Create a + variable first_half which is the result of calling mergesort on the first + half of lst. Repeat for the second half of the list, creating the variable + second_half. Use list splicing for this. + """ + # your code here + + """ + Now we need to merge the two sorted halves. In order to do this, we will + implement a mergelists helper function. Return the result of mergelists + with first_half and second_half as the two parameters. + """ + return # your code here + + +def mergelists(lst1: list, lst2: list) -> list: + idx1 = 0 + idx2 = 0 + ret = [] + + """ + Let's create a while loop that runs for as long as idx1 or idx2 is less + than the len of lst1 or lst2. + """ + while idx1 < len(lst1) or idx2 < len(lst2): + # If both lists have items, we need to compare the first items of the + # lst1 and lst2, and append whichever item is smaller to ret. Then, we + # increment the idx1 or idx2 variable respectively + if idx1 < len(lst1) and idx2 < len(lst2): + # your code here + pass + + elif idx1 < len(lst1): + # if only lst1 has items left, append the remaining items to the + # end of ret, and set idx1 to len(lst1) + + # your code here + pass + elif idx2 < len(lst2): + # if only lst2 has items left, append the remaining items to the + # end of ret, and set idx2 to len(lst2) + + # your code here + pass + return ret + + +if __name__ == "__main__": + lst = [-3, 5, -10, 18, 74, 22, 1, -40] + mergesort(lst) + print(lst) diff --git a/dsa/chapter3/practice/quicksort.py b/dsa/chapter3/practice/quicksort.py new file mode 100644 index 00000000..a5b76662 --- /dev/null +++ b/dsa/chapter3/practice/quicksort.py @@ -0,0 +1,89 @@ +lst = [-3, 5, -10, 18, 74, 22, 1, -40] + + +def quicksort(arr: list): + """ + quicksort_recursive takes in the list you are sorting, the first index of + the sublist you want to sort, and the last index of the sublist you want + to sort, in that order + + For the first call to quicksort_recursive, the first index and last index + should be 0 and the index to the last item of the list respectively + + Make a call to quicksort_recursive with the appropriate arguments below + """ + # your code here + + +def quicksort_recursive(arr, low, high): + """ + Arguments: + arr: list, the entire list we are sorting, + low: int, the first index of the sublist we are sorting + high: int, the last index of the sublist we are sorting + """ + # base case + if low >= high: + return + + # partition the sublist and return the pivot_index + pivot_index = partition(arr, low, high) # NOQA + + # recursive calls + """ + After the list has been partitioned around the pivot_index, we need to + call quicksort_recursive on the two sublists: the one to the left of the + pivot_index, and the one to the right + + We do this on the right side by setting the high index to one less than + pivot_index, and on the left side by setting the low index to one higher + than pivot_index + + Make calls to quicksort_recursive with the appropriate arguments below + """ + # your code here + + +def partition(arr, low, high): + """ + Partition takes a pivot (in our case, arr[high]), and accomplishes the + following: + All of the elements between low and high that are SMALLER than the + pivot are placed to the LEFT of the pivot. + Conversely, all elements between low and high that are LARGER than + the pivot are placed to the RIGHT of the pivot + This has the side effect that the location of pivot after the partition has + taken place is the same as if the list was sorted. Of course, the areas to + the left and right of the pivot are not yet sorted. + """ + i = low # initialize i to the left side of what we are sorting + + """ + Create a for loop that creates an index j, and loops through indexes low + (inclusive) to high (exclusive) + """ + # your code here + """ + Inside our loop, we are trying to find items (arr[j]) that are less than + our pivot (arr[high]). If we find one, we want to swap our item + (arr[j]) with arr[i], an item thats to the left side of our + sublist. Then, we will increment i by one, so we don't continuously + swap with same arr[i] over and over again. + + Create an if statement to do this below + """ + # your code here + + """ + Our pivot (arr[high]) is still on the right side of our sublist. Let's swap + it with arr[i] so it moves to the right spot. + """ + # your code here + + # return the pivot_index + return i + + +if __name__ == "__main__": + quicksort(lst, 0, len(lst) - 1) + print(lst) diff --git a/dsa/chapter3/practice/searching.py b/dsa/chapter3/practice/searching.py new file mode 100644 index 00000000..fb3bb591 --- /dev/null +++ b/dsa/chapter3/practice/searching.py @@ -0,0 +1,124 @@ +""" +Let's see the difference between linear and binary searches! +Some of the algorithm is already done for you, but you +will have to fill in some areas. + +Then, run the code and you can see the results +""" + +import random +from datetime import datetime as d + + +def linear_search(arr, val) -> int: + """ + Linear Search - iterates through all the items in the array and checks + equality with the provided value. If the value matches, returns + the index of that value. Else, returns -1 + Arguments: + arr - the array to search + val - the value to search for + Returns: + int - index of the value on success, -1 on failure + """ + for i in range(len(arr)): + # your code here + pass + return -1 + + +def binary_search(arr, val) -> int: + """ + Binary Search - checks the list for a value using a binary search. + Only works on sorted lists since it assumes that all the values + in indexes greater than i are greater and all the values in + indexes less than i are less. + Arguments: + arr - the array to search + val - the value to search for + Returns: + int - index of the value on success, -1 on failure + """ + low = 0 + high = len(arr) - 1 + while low <= high: + current = (low + high) // 2 + if val == arr[current]: + # your code here + pass + elif val < arr[current]: + # your code here + pass + else: # val > arr[current] + # your code here + pass + return -1 + + +# example 1 - sorted list +# the below demonstrates the binary search is faster than +# linear search on sorted lists +size = 100000 +lst_1 = [i for i in range(size)] + +tests = 3 +for i in range(tests): + print(f"sorted test #{i+1}:") + print("searching linearly") + target = random.randint(0, size) + linear_start = d.now() + linear_result = linear_search(lst_1, target) + linear_end = d.now() + print( + "finished searching linearly in " + + f"{(linear_end - linear_start).total_seconds()} seconds " + + f"and got the {'right' if linear_result == target else 'wrong'} result" + + f" ({linear_result})" + ) + + print("searching binarily") + binary_start = d.now() + binary_result = binary_search(lst_1, target) + binary_end = d.now() + print( + "finished searching binarily in " + + f"{(binary_end - binary_start).total_seconds()} seconds " + + f"and got the {'right' if binary_result == target else 'wrong'} result" + + f" ({binary_result})" + ) + print() + +# example 2 - unsorted list +# the below demonstrates that binary search doesn't work on unsorted +# lists, but linear search does +size = 100000 +lst_2 = [i for i in range(size)] +random.shuffle(lst_2) + +tests = 3 +for i in range(tests): + print(f"unsorted test #{i+1}:") + print("searching linearly") + idx = random.randint(0, size) + target = lst_2[idx] + linear_start = d.now() + linear_result = linear_search(lst_2, target) + linear_end = d.now() + print( + "finished searching linearly in " + + f"{(linear_end - linear_start).total_seconds()} seconds " + + f"and got the {'right' if linear_result == idx else 'wrong'} result" + + f" ({linear_result})" + ) + + print("searching binarily") + binary_start = d.now() + binary_result = binary_search(lst_2, target) + binary_end = d.now() + print( + "finished searching binarily in " + + f"{(binary_end - binary_start).total_seconds()} seconds " + + f"and got the {'right' if binary_result == idx else 'wrong'} result" + + f" ({binary_result})" + ) + print() diff --git a/dsa/chapter3/practice/selectionsort.py b/dsa/chapter3/practice/selectionsort.py new file mode 100644 index 00000000..a1ac9f51 --- /dev/null +++ b/dsa/chapter3/practice/selectionsort.py @@ -0,0 +1,31 @@ +def selectionsort(arr: list): + """ + Let's implement selection sort! There are 4 easy steps to follow in order + to implement it. + 1. Create a loop through iterate through the list. + 2. Create an inner loop that iterates from the outer index + 1 to the + end of the list. + 3. Compare the element at the outer index to the element at the inner + index. + 4. If the element at the outer index is larger than at the inner index, + swap the 2 elements. + """ + + # Step 1, create an outer loop that iterates through the whole list. Let's + # name the outer index "i" + + # Step 2, create an inner loop that iterates from i+1 to the end of the + # list, let's name inner index "j" + + # Step 3, check if the element at index i is larger than the element at + # index j + + # Step 4, swap the element at the outer index with the element at the + # inner index + pass + + +if __name__ == "__main__": + lst = [-3, 5, -10, 18, 74, 22, 1, -40] + selectionsort(lst) + print(lst) diff --git a/dsa/chapter3/solutions/a_star.py b/dsa/chapter3/solutions/a_star.py new file mode 100644 index 00000000..759f28a3 --- /dev/null +++ b/dsa/chapter3/solutions/a_star.py @@ -0,0 +1,195 @@ +""" +A Star Practice + +In this practice problem, you get to fill in some a_star code +as well as see the effects of using different heuristics on +a_star's execution time. + +Heuristics and helper functions are given. Your job is to fill +in sections of the A* algorithm where it says 'your code here' +""" + +from queue import PriorityQueue +import math +import random + + +class Point: + def __init__(self, x: int, y: int) -> None: + self.x: int = x + self.y: int = y + + def get_neighbors(self, start, end): + """ + This function returns a list of neighboring points + using the fact that neighboring points will be the + following (p = neighboring, c = current) + + ``` + p p p + p c p + p p p + ``` + """ + + def between(a, b, c): + return (b <= a and a <= c) or (b >= a and a >= c) + + return [ + Point(x + self.x, y + self.y) + for x in range(-1, 2) + for y in range(-1, 2) + if ( + between(x + self.x, start.x, end.x) + and between(y + self.y, start.y, end.y) + ) + ] + + def __eq__(self, __o: object) -> bool: + return self.x == __o.x and self.y == __o.y + + def __hash__(self) -> int: + return hash((self.x, self.y)) + + def __str__(self): + return f"({self.x}, {self.y})" + + def __repr__(self) -> str: + return str(self) + + +def adding_heuristic(cur: Point, end: Point): + """ + this heuristic returns a value + that looks like the following: + abs(x-x1) + abs(y-y1). + """ + return abs(cur.x - end.x) + abs(cur.y - end.y) + + +def triangle_heuristic(cur: Point, end: Point): + """ + this heuristic will return a value + based off the pythagorean theorem + that looks like the following + sqrt((x-x1)^2 + (y-y1)^2) + """ + return math.sqrt((cur.x - end.x) ** 2 + (cur.y - end.y) ** 2) + + +def bad_heuristic(cur: Point, end: Point): + """ + This heuristic will return a value + that is the opposite of the distance, + meaning that the closer cur is to end, + the worse (higher) score this will give it + """ + return -(abs(cur.x - end.x) + abs(cur.y - end.y)) + + +def random_heuristic(cur: Point, end: Point): + """ + Returns a totally random number. + """ + return random.randint(cur.x, end.x) + random.randint(cur.y, end.y) + + +def reconstruct_path(path: dict, start, end): + backwards_path = [] + curr = end # we know that we start at the end + while curr in path: + # add the current node to the backwards_path + backwards_path.append(curr) + + # since path is a dictionary of node : how to get there, + # we get the previous node in the path by doing path[curr] + curr = path[curr] + + # this will be the first item after we reverse the list + backwards_path.append(start) + + return reversed(backwards_path) + + +def a_star(start: Point, end: Point, heuristic): + min_x, max_x = min(start.x, end.x), max(start.x, end.x) + min_y, max_y = min(start.y, end.y), max(start.y, end.y) + + # initialize f scores (final scores) to infinity for every + # point between the (min_x, min_y) and (max_x, max_y) + f_scores = { + Point(x, y): float("inf") + for x in range(min_x, max_x + 1) + for y in range(min_y, max_y + 1) + } + + # initialize g scores (distance to get there) to infinity for every + # point between (min_x, min_y) and (max_x, max_y) + g_scores = { + Point(x, y): float("inf") + for x in range(min_x, max_x + 1) + for y in range(min_y, max_y + 1) + } + # it takes 0 steps to get to the start, so initialize that g score to 0 + g_scores[start] = 0 + + # this will be how many nodes we have added + # because priorityqueue sorts things, this is added as a backup measure + # when putting items into the queue to say that, if their f scores are + # the same, then just explore the one that we found first + count = 0 + unexplored = PriorityQueue() + unexplored.put((0, count, start)) + + # this is a dictionary that stores node: how to get there + # this means that path[(1, 1)] might equal (0, 0) + # we use this variable to help us reconstruct the path that + # a star found + path = {} + + # allows us to see how many executions it really took + num_executions = 0 + + while not unexplored.empty(): + current: Point = unexplored.get()[2] # just get the Point + + # it takes 1 more step to get to any neighbor, so their g_scores will be + # one more than the current g score + temp_g_score = g_scores[current] + 1 + for node in current.get_neighbors(start, end): + if node == end: + # the way to get to the end is from the current node + path[node] = current + print(f"finished after {num_executions} executions") + return reconstruct_path(path, start, end) + else: + # if either we haven't explored this node yet + # (meaning g_scores[node] = infinity) or this is a shorter path to + # get to this node, then + if temp_g_score < g_scores[node]: + # update our path that way now the shortest way to get to this node + # is through the current node + path[node] = current + + # update our f and g scores + g_scores[node] = temp_g_score + f_scores[node] = temp_g_score + heuristic(node, end) + + # add the node to unexplored if it wasn't already in unexplored + if not any(node == item[2] for item in unexplored.queue): + # update our count and add the unexplored node w/ its score + count += 1 + unexplored.put((f_scores[node], count, node)) + num_executions += 1 + print(f"no solution found after {num_executions} executions") + return None # no path found + + +# you can try changing the heuristic and seeing how that affects the path taken, +# as well as the number of executions it took +path = a_star(Point(0, 0), Point(15, 33), adding_heuristic) +path_len = 0 +for i in path: + print(i) + path_len += 1 +print(f"path length was {path_len}") diff --git a/dsa/chapter3/solutions/bfs_dfs.py b/dsa/chapter3/solutions/bfs_dfs.py new file mode 100644 index 00000000..acd6fe8a --- /dev/null +++ b/dsa/chapter3/solutions/bfs_dfs.py @@ -0,0 +1,87 @@ +class Node: + def __init__(self, value: int) -> None: + self.value: int = value + self.children = [] + + def __str__(self) -> str: + return str(self.value) + + def __repr__(self) -> str: + return str(self) + + +def BFS(start_node: Node): + """ + Implement a breadth-first search algorithm, + but print the nodes that you visit as you go. + + Remember, a breadth-first search algorithm works by + visiting all the children in a certain depth before + advancing to the next depth + + Note that your function will be slightly different from + the one in the example since the nodes in this file + have children and not neighbors + """ + + # initialize our lists + visited_nodes = [] + current_depth_nodes = [start_node] + next_depth_nodes = [] + + # iterate until there are no nodes at the current depth + while len(current_depth_nodes) != 0: + for node in current_depth_nodes: + if node not in visited_nodes: + print(node, end=" ") + + # add the node to visited + # and add its children to the list of nodes at the next depth + visited_nodes.append(node) + next_depth_nodes.extend(node.children) + + # "go to the next depth level" by setting + # current_depth_nodes = next_depth_nodes + current_depth_nodes = next_depth_nodes + next_depth_nodes = [] + + +def DFS(start_node: Node, visited: list = []): + if start_node not in visited: + print(start_node, end=" ") + + visited.append(start_node) + for node in start_node.children: + DFS(node, visited) + + +if __name__ == "__main__": + # make a graph that looks like the following + # / 5 + # 2 - 6 - 10 + # / + # 1 - 3 - 7 - 11 - 12 + # \ + # 4 - 8 + # \ 9 + start_node = Node(1) + for i in range(3): + start_node.children.append(Node(i + 2)) + start_node.children[0].children.append(Node(5)) + start_node.children[0].children.append(Node(6)) + start_node.children[0].children[1].children.append(Node(10)) + + start_node.children[1].children.append(Node(7)) + start_node.children[1].children[0].children.append(Node(11)) + start_node.children[1].children[0].children[0].children.append(Node(12)) + + start_node.children[2].children.append(Node(8)) + start_node.children[2].children.append(Node(9)) + + print("with BFS") + BFS(start_node) # 1 2 3 4 5 6 7 8 9 10 11 12 + print() + + print("with DFS") + DFS(start_node) # 1 2 5 6 10 3 7 11 12 4 8 9 + print() diff --git a/dsa/chapter3/solutions/mergesort.py b/dsa/chapter3/solutions/mergesort.py new file mode 100644 index 00000000..93deb682 --- /dev/null +++ b/dsa/chapter3/solutions/mergesort.py @@ -0,0 +1,74 @@ +def mergesort(lst: list) -> list: + """ + Let's implement mergesort, + First let's create a base case where if the list is 0 or 1 elements long, + return it + """ + if len(lst) <= 1: + return lst + + """ + Now that we have handled the base case, if the list is any longer, we can + go into typical mergesort logic, + + We need to split the list into 2 halves, so lets first find the middle + index value. Use // instead of / because we want an integer + """ + middle_idx = len(lst) // 2 + + """ + Now we can run mergesort on the first and second halves of lst. Create a + variable first_half which is the result of calling mergesort on the first + half of lst. Repeat for the second half of the list, creating the variable + second_half. Use list splicing for this. + """ + first_half = mergesort(lst[:middle_idx]) # sort the first half + second_half = mergesort(lst[middle_idx:]) # sort the second half + + """ + Now we need to merge the two sorted halves. In order to do this, we will + implement a mergelists helper function. Return the result of mergelists + with first_half and second_half as the two parameters. + """ + return mergelists(first_half, second_half) # merge the two sorted halves + + +def mergelists(lst1: list, lst2: list) -> list: + idx1 = 0 + idx2 = 0 + ret = [] + + """ + Let's create a while loop that runs for as long as idx1 or idx2 is less + than the len of lst1 or lst2. + """ + while idx1 < len(lst1) or idx2 < len(lst2): + # If both lists have items, we need to compare the first item of the + # lst1 and lst2, and append whichever item is smaller to ret. Then, we + # increment the idx1 or idx2 variable respectively + if idx1 < len(lst1) and idx2 < len(lst2): + if lst1[idx1] < lst2[idx2]: + ret.append(lst1[idx1]) # add the item from lst1 + idx1 += 1 # increment our idx in lst1 + else: # lst2[idx2] <= lst1[idx1] + ret.append(lst2[idx2]) # add the item from lst2 + idx2 += 1 # increment our idx in lst2 + + elif idx1 < len(lst1): + # if only lst1 has items left, append the remaining items to the + # end of ret, and set idx1 to len(lst1) + ret.extend(lst1[idx1:]) + idx1 = len(lst1) + elif idx2 < len(lst2): + # if only lst2 has items left, append the remaining items to the + # end of ret, and set idx2 to len(lst2) + ret.extend(lst2[idx2:]) + idx2 = len(lst2) + + return ret + + +if __name__ == "__main__": + lst = [-3, 5, -10, 18, 74, 22, 1, -40] + mergesort(lst) + print(lst) diff --git a/dsa/chapter3/solutions/quicksort.py b/dsa/chapter3/solutions/quicksort.py new file mode 100644 index 00000000..45d0bd90 --- /dev/null +++ b/dsa/chapter3/solutions/quicksort.py @@ -0,0 +1,89 @@ +def quicksort(arr: list): + """ + quicksort_recursive takes in the list you are sorting, the first index of + the sublist you want to sort, and the last index of the sublist you want + to sort, in that order + + For the first call to quicksort_recursive, the first index and last index + should be 0 and the index to the last item of the list respectively + + Make a call to quicksort_recursive with the appropriate arguments below + """ + quicksort_recursive(arr, 0, len(arr) - 1) + + +def quicksort_recursive(arr, low, high): + """ + Arguments: + arr: list, the entire list we are sorting, + low: int, the first index of the sublist we are sorting + high: int, the last index of the sublist we are sorting + """ + # base case + if low >= high: + return + + pivot_index = partition(arr, low, high) + + # recursive calls + """ + After the list has been partitioned around the pivot_index, we need to + call quicksort_recursive on the two sublists: the one to the left of the + pivot_index, and the one to the right + + We do this on the right side by setting the high index to one less than + pivot_index, and on the left side by setting the low index to one higher + than pivot_index + + Make calls to quicksort_recursive with the appropriate arguments below + """ + quicksort_recursive(arr, low, pivot_index - 1) # right side + quicksort_recursive(arr, pivot_index + 1, high) # left side + + +def partition(arr, low, high): + """ + Partition takes a pivot (in our case, arr[high]), and accomplishes the + following: + All of the elements between low and high that are SMALLER than the + pivot are placed to the LEFT of the pivot. + Conversely, all elements between low and high that are LARGER than + the pivot are placed to the RIGHT of the pivot + This has the side effect that the location of pivot after the partition has + taken place is the same as if the list was sorted. Of course, the areas to + the left and right of the pivot are not yet sorted. + """ + i = low # initialize i to the left side of what we are sorting + + """ + Create a for loop that creates an index j, and loops through indexes low + (inclusive) to high (exclusive) + """ + for j in range(low, high): # iterate through the list with arr[j] + """ + In our loop, we are trying to find items (arr[j]) that are less than + our pivot (arr[high]). If we find one, we want to swap our item + (arr[j]) with arr[i], an item thats to the left side of our + sublist. Then, we will increment i by one, so we don't continuously + swap with same arr[i] over and over again. + + Create an if statement to do this below + """ + if arr[j] < arr[high]: + # swap arr[j] with arr[i] so arr[j] is at the left side + arr[i], arr[j] = arr[j], arr[i] + i += 1 + """ + Our pivot (arr[high]) is still on the right side of our sublist. Let's swap + it with arr[i] so it moves to the right spot. + """ + arr[i], arr[high] = arr[high], arr[i] + + # return the pivot_index + return i + + +if __name__ == "__main__": + lst = [-3, 5, -10, 18, 74, 22, 1, -40] + quicksort(lst) + print(lst) diff --git a/dsa/chapter3/solutions/searching.py b/dsa/chapter3/solutions/searching.py new file mode 100644 index 00000000..8b6d332f --- /dev/null +++ b/dsa/chapter3/solutions/searching.py @@ -0,0 +1,121 @@ +""" +Let's see the difference between linear and binary searches! +Some of the algorithm is already done for you, but you +will have to fill in some areas. + +Then, run the code and you can see the results +""" + +import random +from datetime import datetime as d + + +def linear_search(arr, val) -> int: + """ + Linear Search - iterates through all the items in the array and checks + equality with the provided value. If the value matches, returns + the index of that value. Else, returns -1 + Arguments: + arr - the array to search + val - the value to search for + Returns: + int - index of the value on success, -1 on failure + """ + for i in range(len(arr)): + if arr[i] == val: + return i + return -1 + + +def binary_search(arr, val) -> int: + """ + Binary Search - checks the list for a value using a binary search. + Only works on sorted lists since it assumes that all the values + in indexes greater than i are greater and all the values in + indexes less than i are less. + Arguments: + arr - the array to search + val - the value to search for + Returns: + int - index of the value on success, -1 on failure + """ + low = 0 + high = len(arr) - 1 + while low <= high: + current = (low + high) // 2 + if val == arr[current]: + return current + elif val < arr[current]: + high = current - 1 + else: # val > arr[current] + low = current + 1 + return -1 + + +# example 1 - sorted list +# the below demonstrates the binary search is faster than +# linear search on sorted lists +size = 100000 +lst_1 = [i for i in range(size)] + +tests = 3 +for i in range(tests): + print(f"sorted test #{i+1}:") + print("searching linearly") + target = random.randint(0, size) + linear_start = d.now() + linear_result = linear_search(lst_1, target) + linear_end = d.now() + print( + "finished searching linearly in " + + f"{(linear_end - linear_start).total_seconds()} seconds " + + f"and got the {'right' if linear_result == target else 'wrong'} result" + + f" ({linear_result})" + ) + + print("searching binarily") + binary_start = d.now() + binary_result = binary_search(lst_1, target) + binary_end = d.now() + print( + "finished searching binarily in " + + f"{(binary_end - binary_start).total_seconds()} seconds " + + f"and got the {'right' if binary_result == target else 'wrong'} result" + + f" ({binary_result})" + ) + print() + +# example 2 - unsorted list +# the below demonstrates that binary search doesn't work on unsorted +# lists, but linear search does +size = 100000 +lst_2 = [i for i in range(size)] +random.shuffle(lst_2) + +tests = 3 +for i in range(tests): + print(f"unsorted test #{i+1}:") + print("searching linearly") + idx = random.randint(0, size) + target = lst_2[idx] + linear_start = d.now() + linear_result = linear_search(lst_2, target) + linear_end = d.now() + print( + "finished searching linearly in " + + f"{(linear_end - linear_start).total_seconds()} seconds " + + f"and got the {'right' if linear_result == idx else 'wrong'} result" + + f" ({linear_result})" + ) + + print("searching binarily") + binary_start = d.now() + binary_result = binary_search(lst_2, target) + binary_end = d.now() + print( + "finished searching binarily in " + + f"{(binary_end - binary_start).total_seconds()} seconds " + + f"and got the {'right' if binary_result == idx else 'wrong'} result" + + f" ({binary_result})" + ) + print() diff --git a/dsa/chapter3/solutions/selectionsort.py b/dsa/chapter3/solutions/selectionsort.py new file mode 100644 index 00000000..22cb686c --- /dev/null +++ b/dsa/chapter3/solutions/selectionsort.py @@ -0,0 +1,31 @@ +def selectionsort(arr: list): + """ + Let's implement selection sort! There are 4 easy steps to follow in order + to implement it. + 1. Create a loop through iterate through the list. + 2. Create an inner loop that iterates from the outer index + 1 to the + end of the list. + 3. Compare the element at the outer index to the element at the inner + index. + 4. If the element at the outer index is larger than at the inner index, + swap the 2 elements. + """ + + # Step 1, create an outer loop that iterates through the whole list. Let's + # name the outer index "i" + for i in range(len(arr)): + # Step 2, create an inner loop that iterates from i+1 to the end of the + # list, let's name inner index "j" + for j in range(i + 1, len(arr)): + # Step 3, check if the element at index i is larger than the + # element at index j + if arr[i] > arr[j]: + # Step 4, swap the element at the outer index with the element + # at the inner index + arr[i], arr[j] = arr[j], arr[i] + + +if __name__ == "__main__": + lst = [-3, 5, -10, 18, 74, 22, 1, -40] + selectionsort(lst) + print(lst) diff --git a/games/chapter1/examples/guessthepassword.py b/games/chapter1/examples/guessthepassword.py new file mode 100644 index 00000000..7961131b --- /dev/null +++ b/games/chapter1/examples/guessthepassword.py @@ -0,0 +1,20 @@ +# Directions: The goal of this exercise is to create +# a game where the user has to guess a certain password that +# you set and see how many tries it takes for that user to guess correctly + +# start with assigning the password to some variable +pas = "password" + +# set an input so it will appear in the console and ask the user +guess = input("Enter the password:") + +# set a counter to count the number of guesses +counter = 1 + +# set a while loop to check if the user guess correctly and count the number of guesses +while guess != pas: + guess = input("Incorrect Password. Try Again:") + counter += 1 + +# print the results +print(f"Nice Job. Unlocked. It took you {str(counter)} tries") diff --git a/games/chapter1/examples/hangman.py b/games/chapter1/examples/hangman.py new file mode 100644 index 00000000..380707ef --- /dev/null +++ b/games/chapter1/examples/hangman.py @@ -0,0 +1,60 @@ +# Directions: Lets Play Hangman. In the code, create a function that +# takes as a paramater the word that the user has to guess. +# The user should have 15 'lives'. +# Similar to the original game of hangman, if the user guesses an incorrect +# letter, then their lives goes down. If they guess a correct letter, they +# don't lose a life. + + +score = 0 + + +def hangman(endword: str): + global score + wordSet = set(endword) + print( + "Welcome to Hangman! You have 15 lives to " + + "figure out the correct word. Good Luck!" + ) + + lives = 15 + correctguesses = [] + + # mainloop + for i in range(15): + # take user input + guess = input(f"Guess a letter! You have {lives} lives left: ") + + # win condition + if guess == endword: + print(f"Nice, the word is '{endword}'") + score += 1 + break + + if guess in endword: + correctguesses.append(guess) + + # 'draw screen' phase + for i in range(len(endword)): + if endword[i] in correctguesses: + print(endword[i], end="") + else: + print("_ ", end="") + print() + + if guess not in wordSet: + lives -= 1 + + # update game state + # game over condition + if lives == 0: + print(f"You ran out of lives. The correct word is '{endword}'") + + # win condition + if set(correctguesses) == wordSet: + print(f"Nice, the word is '{endword}'") + score += 1 + break + + +hangman("hangman") diff --git a/games/chapter1/practice/blackjack.py b/games/chapter1/practice/blackjack.py new file mode 100644 index 00000000..af9644b1 --- /dev/null +++ b/games/chapter1/practice/blackjack.py @@ -0,0 +1,11 @@ +# Directions: The goal of blackjack is to be the first player +# to get to 21. Each player will draw randomly and the +# sum of the cards will add to 21. If the cards of a player go +# over 21, that person automatically loses. + +# Add your imports here + +print("Welcome to the game of BlackJack. ") +print("") + +# Add your code here diff --git a/games/chapter1/practice/poker.py b/games/chapter1/practice/poker.py new file mode 100644 index 00000000..931a9a9f --- /dev/null +++ b/games/chapter1/practice/poker.py @@ -0,0 +1,370 @@ +from random import choice, randint + +# how poker actually works: +# Every player is dealt two cards (face down) +# The number of cards in the middle (face up) is initially 3 and +# is increased one per round. Players decide if they want to bet on the round +# or fold before the next card is revealed. If a player bets, then all other +# players must 'call' (put in the same # of chips) +# once there are 5 cards in the middle, then the players see +# who can make the best match with their 2 cards and +# the 5 cards in the middle the player that makes the best match wins + +# check the code in the area that says "--CODE AREA--" +# THERE ARE 4 INSTRUCTIONS; if you fill them out, then the program should work. +# Note that, in our version of poker, the game ends once any player has less +# than 7 chips. + +suites = ["Clubs", "Diamonds", "Hearts", "Spades"] +face_cards = {11: "Jack", 12: "Queen", 13: "King", 14: "Ace"} +rankings = { + 0: "Royal Flush", + 1: "Straight Flush", + 2: "Four of a kind", + 3: "Full House", + 4: "Flush", + 5: "Straight", + 6: "Three of a Kind", + 7: "Two pairs", + 8: "Pair", + 9: "High Card", +} +deck = [] + + +# --- SUPPORTING CODE --- +class card: + def __init__(self, value: int, suite: str, name: str = None): + self.name = name if name else str(value) + self.value = value + self.suite = suite + + def __str__(self) -> str: + return f"A(n) {self.name} of {self.suite}" + + def __eq__(self, o) -> bool: + return str(self) == str(o) + + def __sub__(self, o) -> bool: + return self.value - o.value + + +class hand_results: + """ + A class for easy comparing of results of a hand + Note that a hand_result is considered "less than" another + hand_result if the hand_result's priority has a lower value + than the other hand_result's priority (meaning that + the first hand_result has a higher priority). Vice versa for + gt + """ + + def __init__(self, results: list): + self.results = results + self.priority = ( + results.index(True) if True in results else len(results) + ) + + def __lt__(self, o) -> bool: + return self.priority > o.priority + + def __le__(self, o) -> bool: + return self.priority >= o.priority + + def __gt__(self, o) -> bool: + return self.priority < o.priority + + def __ge__(self, o) -> bool: + return self.priority <= o.priority + + def __eq__(self, o) -> bool: + return self.priority == o.priority + + +class hand: + """ + This class represents one person's hand (or the river) + """ + + def __init__(self): + self.cards = [] + + def add_card(self, card: card) -> None: + self.cards.append(card) + + def __str__(self) -> str: + msg = "" + for card in self.cards: + msg += str(card) + ", " + return msg + + def __len__(self) -> int: + return len(self.cards) + + def union(self, o) -> None: + for card in o.cards: + self.cards.append(card) + + def does_val_card_exist( + self, val: int, cards_not_equal_to: list = [] + ) -> tuple: + for card in self.cards: + if card.value == val and card not in cards_not_equal_to: + return (True, card) + return (False, None) + + def find_matches( + self, num_matches: int, cards_to_exclude: list = [] + ) -> tuple: + for card in self.cards: + temp = [] + for oth in cards_to_exclude: + temp.append(oth) + if card not in temp: + temp.append(card) + + for i in range(num_matches - 1): + bool_val, potential_card = self.does_val_card_exist( + card.value, temp + ) + if not bool_val: + break + temp.append(potential_card) + else: + card_matches = temp + for oth_card in cards_to_exclude: + card_matches.remove(oth_card) + return (True, card_matches) + return (False, []) + + def check_straight_flush(self, card_start: card): + potential_card = card_start + for i in range(4): # there need to be 4 cards higher than it + bool_val, potential_card = self.does_val_card_exist( + potential_card.value + 1 + ) + if not bool_val or potential_card.suite != card_start.suite: + break + else: # for loop finished fine + return True + return False + + def check_straight(self, card_start: card) -> bool: + potential_card = card_start + for i in range(4): # there need to be 4 cards higher than it + bool_val, potential_card = self.does_val_card_exist( + potential_card.value + 1 + ) + if not bool_val: + break + else: # for loop finished fine + return True + return False + + def get_best_hand(self) -> hand_results: + # try to get a 5 card flush: + flush_possible = False + for card in self.cards: + same_suite = 0 + for other_card in self.cards: + if not card == other_card and card.suite == other_card.suite: + same_suite += 1 + if same_suite >= 5: + flush_possible = True + + # try to get a 5 card straight + straight_possible = False + for card in self.cards: + potential_card = card + for i in range(4): # there need to be 4 cards higher than it + bool_val, potential_card = self.does_val_card_exist( + potential_card.value + 1 + ) + if not bool_val: + break + else: # for loop finished fine + straight_possible = True + + # try to get a straight flush + straight_flush_possible = False + if straight_possible and flush_possible: + for card in self.cards: + if not straight_flush_possible: + straight_flush_possible = self.check_straight_flush(card) + + # royal flush possible + royal_flush_possible = False + if self.does_val_card_exist(10)[0]: + royal_flush_possible = self.check_straight( + self.does_val_card_exist(10)[1] + ) + + # try to get a pair (2 cards of same val) + pair_possible = self.find_matches(2)[0] + + # try to get a 3 of a kind + three_possible = self.find_matches(3)[0] + + four_possible = self.find_matches(4)[0] + + # try to get a full house + full_house_possible = False + for card in self.cards: + bool_val, cards = self.find_matches(3) + if ( + bool_val and not full_house_possible + ): # was able to find 3 of a kind (2 other cards of same value) + # use exclude and try to find a pair + full_house_possible = self.find_matches(2, cards)[0] + + two_pair_possible = False + for card in self.cards: + bool_val, cards = self.find_matches(2) # find a pair + if bool_val and not two_pair_possible: + two_pair_possible = self.find_matches(2, cards)[0] + + return hand_results( + [ + royal_flush_possible, + straight_flush_possible, + four_possible, + full_house_possible, + flush_possible, + straight_possible, + three_possible, + two_pair_possible, + pair_possible, + ] + ) + + +def initialize_deck(): + global deck + + deck = [ + card(value, suite, face_cards[value]) + if value >= 11 + else card(value, suite) + for value in range(2, 15) + for suite in suites + ] + + +def take_card() -> card: + global deck + c = choice(deck) + deck.remove(c) + return c + + +# -- CODE AREA -- +# -- Your code will go here -- + +dealer_chips = 20 +player_chips = 20 + + +def play_poker(): + global dealer_chips, player_chips, deck + + round_num = 0 + player_inp = "" + + # INSTRUCTION + # while both players have more than 7 chips + while """YOUR CONDITION HERE""": + initialize_deck() + + # inicialize hands to randomized ones each round + player = hand() + dealer = hand() + river = hand() + for i in range(2): # two cards initially + dealer.add_card(take_card()) + player.add_card(take_card()) + # initialize the pool in the middle + for i in range(3): + river.add_card(take_card()) + + chips_at_stake = 0 + winner = "" + + round_num += 1 + print(f"round number {round_num}") + # do one individual round + while len(river) < 5: + print(f"your hand right now is {player}") + print(f"the river is currently {river}") + # dealer bet + dealerbet = min( + randint(1, 5), dealer_chips + ) # that way the dealer doesn't go into negative chips + dealer_chips -= dealerbet + chips_at_stake += dealerbet + + # player either calls or folds + print(f"dealer bet {dealerbet}") + player_inp = input( + "call (bet that much) or fold (abandon this round) or STOP? " + ) + + # INSTRUCTION + # handle input + # if the input is 'STOP', then quit the program + # if the input is 'call', then the player bets the same number + # of chips that the dealer bet (player chips will decrease + # and chips_at_stake will increase) + # lastly, if the input is 'fold', then set winner to True + # and break out of the round (use the break keyword) + if player_inp == "STOP": + pass + if player_inp == "call": + pass + if player_inp == "fold": + pass + + # update the river + river.add_card(take_card()) + + print(f"currently, dealer has {dealer_chips} chips") + print(f"currently, you have {player_chips} chips") + + print() + + print(f"The river ended up as {river}") + print() + # no winner yet (meaning the round ended normally) + if winner == "": + # compare hands + dealer.union(river) + player.union(river) + + dealer_result = dealer.get_best_hand() + player_result = player.get_best_hand() + print( + "It was your", + rankings[player_result.priority], + "vs the dealer's", + rankings[dealer_result.priority], + ) + + # INSTRUCTION + # if player_result is greater than or equal to + # dealer_result, then the player won that round + # if not, then the dealer won that round. + # make sure to update the variable winner + + # INSTRUCTION + # if the dealer won, then + # print "The dealer won that round" + # The dealer then gets the chips that were in chips_at_stake + # if you won, then + # print "You won that round" + # the player gets the chips that were in chips_at_stake + # chips_at_stake will be 0 again no matter what + # Also, make sure to + # print how many chips each player has + print() # used to make it look prettier since adds extra line + + +play_poker() diff --git a/games/chapter1/solution/blackjack.py b/games/chapter1/solution/blackjack.py new file mode 100644 index 00000000..8cf43ec2 --- /dev/null +++ b/games/chapter1/solution/blackjack.py @@ -0,0 +1,86 @@ +# Directions: The goal of blackjack is to be the first player +# to get to 21. Each player will draw randomly and the +# sum of the cards will add to 21. If the cards of a player go +# over 21, that person automatically loses. + +import random + + +print("Welcome to the game of BlackJack. ") +print("") + +# Create the lists of the two players +# the dealer is the console and the player is the user +dealerList = [] +userList = [] + +# append two random cards to start the game +for i in range(2): + dealerList.append(random.randint(2, 11)) + userList.append(random.randint(2, 11)) + +# print the first two cards +print("Here is the dealer's cards:" + str(dealerList)) +print("Here is the user's cards:" + str(userList)) +if sum(userList) == 21: + print("User Won") + exit() +if sum(dealerList) == 21: + print("Dealer won") + exit() + +# ask hit or stay... write a functionn for hit and stay... +# conditional for the typed in key hit means to take another +# card and stay means to play with already drawn cards + +# print the instructions +ask = input("Type in H to hit and S to stay:") + + +# write a function for hit to use in multiple scenarios +def hit(cards): + cards.append(random.randint(2, 11)) + + +# while loop will keep checking the two players' cards to see if they reached 21 or not +while ask == "H" or ask == "h": + hit(userList) + print("Here is your hand:" + str(userList)) + + if sum(userList) > 21: + print("User is Busted") + exit() + if sum(userList) == 21: + print("User Won") + exit() + ask = input("Type in H to hit and S to stay:") + +# if statement for "stay" +if ask == "S" or ask == "s": + print("Here is your hand:" + str(userList)) + + +# when hit- append a anotehr random number into the list of the dealer/userList +# when stay- just go to next play and print out the list +# while dealer less than 17 append new cards to the list + +while sum(dealerList) < 17: + hit(dealerList) + print("Here is dealer's hand:" + str(dealerList)) + +# compare cards for win +if sum(dealerList) > 21: + print("Dealer is Busted") + + exit() +if sum(dealerList) == 21 and sum(userList) == 21: + print("It is a tie") + exit() +if sum(dealerList) == 21: + print("Dealer Won") + exit() + +if 21 - sum(dealerList) > 21 - sum(userList): + print("User is closer") +if 21 - sum(dealerList) < 21 - sum(userList): + print("Dealer is closer") diff --git a/games/chapter1/solution/poker.py b/games/chapter1/solution/poker.py new file mode 100644 index 00000000..21ea1f02 --- /dev/null +++ b/games/chapter1/solution/poker.py @@ -0,0 +1,362 @@ +from random import choice, randint + +# how poker actually works: +# Every player is dealt two cards (face down) +# The number of cards in the middle (face up) is initially 3 and +# is increased one per round. Players decide if they want to bet on the round +# or fold before the next card is revealed. If a player bets, then all other +# players must 'call' (put in the same # of chips) +# once there are 5 cards in the middle, then the players see +# who can make the best match with their 2 cards and +# the 5 cards in the middle the player that makes the best match wins + +# check the code in the area that says "--CODE AREA--" +suites = ["Clubs", "Diamonds", "Hearts", "Spades"] +face_cards = {11: "Jack", 12: "Queen", 13: "King", 14: "Ace"} +rankings = { + 0: "Royal Flush", + 1: "Straight Flush", + 2: "Four of a kind", + 3: "Full House", + 4: "Flush", + 5: "Straight", + 6: "Three of a Kind", + 7: "Two pairs", + 8: "Pair", + 9: "High Card", +} +deck = [] + + +# --- SUPPORTING CODE --- +class card: + def __init__(self, value: int, suite: str, name: str = None): + self.name = name if name else str(value) + self.value = value + self.suite = suite + + def __str__(self) -> str: + return f"A(n) {self.name} of {self.suite}" + + def __eq__(self, o) -> bool: + return str(self) == str(o) + + def __sub__(self, o) -> bool: + return self.value - o.value + + +class hand_results: + """ + A class for easy comparing of results of a hand + Note that a hand_result is considered "less than" another + hand_result if the hand_result's priority has a lower value + than the other hand_result's priority (meaning that + the first hand_result has a higher priority). Vice versa for + gt + """ + + def __init__(self, results: list): + self.results = results + self.priority = ( + results.index(True) if True in results else len(results) + ) + + def __lt__(self, o) -> bool: + return self.priority > o.priority + + def __le__(self, o) -> bool: + return self.priority >= o.priority + + def __gt__(self, o) -> bool: + return self.priority < o.priority + + def __ge__(self, o) -> bool: + return self.priority <= o.priority + + def __eq__(self, o) -> bool: + return self.priority == o.priority + + +class hand: + """ + This class represents one person's hand (or the river) + """ + + def __init__(self): + self.cards = [] + + def add_card(self, card: card) -> None: + self.cards.append(card) + + def __str__(self) -> str: + msg = "" + for card in self.cards: + msg += str(card) + ", " + return msg + + def __len__(self) -> int: + return len(self.cards) + + def union(self, o) -> None: + for card in o.cards: + self.cards.append(card) + + def does_val_card_exist( + self, val: int, cards_not_equal_to: list = [] + ) -> tuple: + for card in self.cards: + if card.value == val and card not in cards_not_equal_to: + return (True, card) + return (False, None) + + def find_matches( + self, num_matches: int, cards_to_exclude: list = [] + ) -> tuple: + for card in self.cards: + temp = [] + for oth in cards_to_exclude: + temp.append(oth) + if card not in temp: + temp.append(card) + + for i in range(num_matches - 1): + bool_val, potential_card = self.does_val_card_exist( + card.value, temp + ) + if not bool_val: + break + temp.append(potential_card) + else: + card_matches = temp + for oth_card in cards_to_exclude: + card_matches.remove(oth_card) + return (True, card_matches) + return (False, []) + + def check_straight_flush(self, card_start: card): + potential_card = card_start + for i in range(4): # there need to be 4 cards higher than it + bool_val, potential_card = self.does_val_card_exist( + potential_card.value + 1 + ) + if not bool_val or potential_card.suite != card_start.suite: + break + else: # for loop finished fine + return True + return False + + def check_straight(self, card_start: card) -> bool: + potential_card = card_start + for i in range(4): # there need to be 4 cards higher than it + bool_val, potential_card = self.does_val_card_exist( + potential_card.value + 1 + ) + if not bool_val: + break + else: # for loop finished fine + return True + return False + + def get_best_hand(self) -> hand_results: + # try to get a 5 card flush: + flush_possible = False + for card in self.cards: + same_suite = 0 + for other_card in self.cards: + if not card == other_card and card.suite == other_card.suite: + same_suite += 1 + if same_suite >= 5: + flush_possible = True + + # try to get a 5 card straight + straight_possible = False + for card in self.cards: + potential_card = card + for i in range(4): # there need to be 4 cards higher than it + bool_val, potential_card = self.does_val_card_exist( + potential_card.value + 1 + ) + if not bool_val: + break + else: # for loop finished fine + straight_possible = True + + # try to get a straight flush + straight_flush_possible = False + if straight_possible and flush_possible: + for card in self.cards: + if not straight_flush_possible: + straight_flush_possible = self.check_straight_flush(card) + + # royal flush possible + royal_flush_possible = False + if self.does_val_card_exist(10)[0]: + royal_flush_possible = self.check_straight( + self.does_val_card_exist(10)[1] + ) + + # try to get a pair (2 cards of same val) + pair_possible = self.find_matches(2)[0] + + # try to get a 3 of a kind + three_possible = self.find_matches(3)[0] + + four_possible = self.find_matches(4)[0] + + # try to get a full house + full_house_possible = False + for card in self.cards: + bool_val, cards = self.find_matches(3) + if ( + bool_val and not full_house_possible + ): # was able to find 3 of a kind (2 other cards of same value) + # use exclude and try to find a pair + full_house_possible = self.find_matches(2, cards)[0] + + two_pair_possible = False + for card in self.cards: + bool_val, cards = self.find_matches(2) # find a pair + if bool_val and not two_pair_possible: + two_pair_possible = self.find_matches(2, cards)[0] + + return hand_results( + [ + royal_flush_possible, + straight_flush_possible, + four_possible, + full_house_possible, + flush_possible, + straight_possible, + three_possible, + two_pair_possible, + pair_possible, + ] + ) + + +def initialize_deck(): + global deck + + deck = [ + card(value, suite, face_cards[value]) + if value >= 11 + else card(value, suite) + for value in range(2, 15) + for suite in suites + ] + + +def take_card() -> card: + global deck + c = choice(deck) + deck.remove(c) + return c + + +# -- CODE AREA -- +# -- Your code will go here -- + +# initialize two variables +# one will be the dealer's chips, the other will be the player's chips +dealer_chips = 20 +player_chips = 20 + + +def play_poker(): + global dealer_chips, player_chips, deck + + round_num = 0 + player_inp = "" + while player_inp != "STOP" and (dealer_chips > 7 and player_chips > 7): + initialize_deck() + + # inicialize hands to randomized ones each round + player = hand() + dealer = hand() + river = hand() + for i in range(2): # two cards initially + dealer.add_card(take_card()) + player.add_card(take_card()) + # initialize the pool in the middle + for i in range(3): + river.add_card(take_card()) + + chips_at_stake = 0 + winner = "" + + round_num += 1 + print(f"round number {round_num}") + # do one individual round + while len(river) < 5: + print(f"your hand right now is {player}") + print(f"the river is currently {river}") + # dealer bet + dealerbet = min( + randint(1, 5), dealer_chips + ) # that way the dealer doesn't go into negative chips + dealer_chips -= dealerbet + chips_at_stake += dealerbet + + # player either calls or folds + print(f"dealer bet {dealerbet}") + player_inp = input( + "call (bet that much) or fold (abandon this round) or STOP? " + ) + + # handle input + if player_inp == "STOP": + return # just get out of the function + if player_inp == "call": + chips_at_stake += dealerbet + player_chips -= dealerbet + # if betting dealerbet chips would put them in debt + if player_chips < 0: + print("Sorry, you lose") + return + if player_inp == "fold": + winner = "dealer" + break + + # update the river + river.add_card(take_card()) + + print(f"currently, dealer has {dealer_chips} chips") + print(f"currently, you have {player_chips} chips") + + print() + + print(f"The river ended up as {river}") + print() + # no winner yet + if winner == "": + # compare hands + dealer.union(river) + player.union(river) + + dealer_result = dealer.get_best_hand() + player_result = player.get_best_hand() + print( + "It was your", + rankings[player_result.priority], + "vs the dealer's", + rankings[dealer_result.priority], + ) + if player_result >= dealer_result: + winner = "player" + else: + winner = "dealer" + if winner == "dealer": + print("dealer won that round") + dealer_chips += chips_at_stake + chips_at_stake = 0 + else: # winner == "player" + print("you won that round") + player_chips += chips_at_stake + chips_at_stake = 0 + + print(f"currently, dealer has {dealer_chips} chips") + print(f"currently, you have {player_chips} chips") + print() + + +play_poker() diff --git a/games/chapter2/examples/basic_window.py b/games/chapter2/examples/basic_window.py new file mode 100644 index 00000000..f2cb1116 --- /dev/null +++ b/games/chapter2/examples/basic_window.py @@ -0,0 +1,25 @@ +import pygame # imports the module + +# RESIZABLE is only needed if you want a resizable window +from pygame.locals import RESIZABLE + +# initializes imported pygame modules +pygame.init() + +# creates resizable pygame window that is 500 pixels wide and 400 high +# sets the caption of the window to "My first pygame app!" +flag = RESIZABLE +window = pygame.display.set_mode((500, 400), flag) +pygame.display.set_caption("My first pygame app!") + +# this is where the game loop begins +run = True +while run: + for event in pygame.event.get(): + # checks if the close button is pressed + # if so, exit the game loop + if event.type == pygame.QUIT: + run = False + +# deactivates pygame modules, opposite of pygame.init() +pygame.quit() diff --git a/games/chapter2/examples/comprehensive_example.py b/games/chapter2/examples/comprehensive_example.py new file mode 100644 index 00000000..ed1942df --- /dev/null +++ b/games/chapter2/examples/comprehensive_example.py @@ -0,0 +1,37 @@ +import pygame + +RED = (255, 0, 0) +BLACK = (0, 0, 0) +x = y = 0 +width = 100 +height = 50 + +# initializes imported pygame modules +pygame.init() + +# creates pygame window that is 500 pixels wide and 400 high +# sets the caption of the window to "My first pygame app!" +window = pygame.display.set_mode((500, 400)) +pygame.display.set_caption("My first pygame app!") + +# this is where the game loop begins +run = True +while run: + # change the coordinates + x, y = x + 1, y + 1 + + # draw a black screen over the previous frame + window.fill(BLACK) + + # draw a new rectangle and update the screen + pygame.draw.rect(window, RED, (x, y, width, height)) + pygame.display.update() + + for event in pygame.event.get(): + # checks if the close button is pressed + # if so, exit the game loop + if event.type == pygame.QUIT: + run = False + +# deactivate pygame modules, opposite of pygame.init() +pygame.quit() diff --git a/games/chapter2/examples/draw_objects.py b/games/chapter2/examples/draw_objects.py new file mode 100644 index 00000000..fdc45209 --- /dev/null +++ b/games/chapter2/examples/draw_objects.py @@ -0,0 +1,54 @@ +import pygame + +pygame.init() + +window = pygame.display.set_mode((800, 800)) +pygame.display.set_caption("Drawing and Moving Objects") + +BLACK = (0, 0, 0) # background color +RED = (255, 0, 0) +GREEN = (0, 255, 0) + +# make a rectangle without the pygame.Rect class +x = 100 # top-left x value +y = 400 # top-left y value +width = 100 # width of the rectangle +height = 50 # height of the rectangle + +# make a pygame.Rect rectangle +# the syntax is `myvar = pygame.Rect(top-left x, top-left y, width, height)` +# with 0 as top-left x value, 100 as top-left y value, +# width = 50, height = 100 +green_rectangle = pygame.Rect(0, 100, 50, 100) + +run = True +while run: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + run = False + + # move a rectangle that isn't a pygame.Rect object + x += 1 # move to the right 1 px + y += 1 # move down 1 px + + # move a rectangle that is a pygame.Rect object + green_rectangle.move_ip(1, 2) # moves 1 to the right, 2 down + # this is equivalent to green_rectangle = green_rectangle.move(1, 2) + + # erase the previous frame + window.fill(BLACK) + + # draw a rectangle that isn't a pygame.Rect object + pygame.draw.rect(window, RED, (x, y, width, height)) + + # draw a rectangle that is a pygame.Rect object + pygame.draw.rect(window, GREEN, green_rectangle) + + # update the screen + pygame.display.update() + + # sometimes you need to limit frame rate or your objects + # will seem to move too fast + pygame.time.wait(30) # wait 30 milliseconds between frame + +pygame.quit() # close pygame after finishing diff --git a/games/chapter2/examples/text.py b/games/chapter2/examples/text.py new file mode 100644 index 00000000..9e19a3c7 --- /dev/null +++ b/games/chapter2/examples/text.py @@ -0,0 +1,40 @@ +import pygame +from pygame.locals import RESIZABLE + +pygame.init() +flag = RESIZABLE +window = pygame.display.set_mode((500, 400), flag) +pygame.display.set_caption("Text!") +WHITE = (255, 255, 255) + +run = True +while run: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + run = False + + # step 1 to writing: load the font with pygame.font.SysFont(font, size) + # For font - You can either use the default font + # (pygame.font.get_default_font()) or use a font name + # (like Comic Sans or Arial). + # For size - a positive integer representing the font size. + font = pygame.font.SysFont("Arial", 32) + + # step 2 - render the font with + # (font variable name).render( + # text: string, antialias: bool, color: tuple, background=None + # ) + # In this case, we render the text "Hello World!", pass True as antiaalias + # and have the color of the text be WHITE + text = font.render("Hello World!", True, WHITE) + + # step 3 - blit to the screen + # You can either blit the text to a rectangle on the screen or a specified + # coordinate + # In this case, we blit (draw) the text with a top-left value of (0, 0) + window.blit(text, (100, 100)) + + # update the screen; just like with moving/displaying rectangles + pygame.display.update() + +pygame.quit() diff --git a/games/chapter2/practice/add_text.py b/games/chapter2/practice/add_text.py new file mode 100644 index 00000000..d6b1f142 --- /dev/null +++ b/games/chapter2/practice/add_text.py @@ -0,0 +1,54 @@ +# Add some text to your game + +# This problem builds off of bouncingrect.py + +# Add some text to the screen. You can either: +# - draw the text to a specified coordinate OR +# - blit the text onto the bouncing rectangle. + +import pygame +import time # not necessary, but used for frame cap + +pygame.init() # initialize pygame module + +SCREEN_SIZE = (600, 400) +RECT_SIZE = (100, 100) +RED = (255, 0, 0) +BLACK = (0, 0, 0) +momentum = [1, 1] # (down and right) + +window = pygame.display.set_mode(SCREEN_SIZE) +running = True + +# start the rectangle in the middle of the screen +x = SCREEN_SIZE[0] // 2 +y = SCREEN_SIZE[1] // 2 + +while running: + # if the rectangle collided with the left or right side + # of the screen + if x + RECT_SIZE[0] >= SCREEN_SIZE[0] or x <= 0: + momentum[0] = -momentum[0] + # if the rectangle collided with the top or bottom + # of the screen + if y + RECT_SIZE[1] >= SCREEN_SIZE[1] or y <= 0: + momentum[1] = -momentum[1] + + # add the speed to the current x and y to get the + # new x and y + x += momentum[0] + y += momentum[1] + + window.fill(BLACK) # 'erase' the previous frame + pygame.draw.rect(window, RED, (x, y, RECT_SIZE[0], RECT_SIZE[1])) + + # Your code here. + + pygame.display.update() # update the display + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + time.sleep(0.01) # frame cap to make the rectangle more visible + +pygame.quit() # deactivate the pygame module diff --git a/games/chapter2/practice/bouncing_rect.py b/games/chapter2/practice/bouncing_rect.py new file mode 100644 index 00000000..e14e4f4d --- /dev/null +++ b/games/chapter2/practice/bouncing_rect.py @@ -0,0 +1,25 @@ +# Make a “bouncing rectangle!” + +# For this, please use the given screen and rectangle +# width and height. + +# The rectangle should start in (or close to) the middle of the +# screen. It should be moving down and right. If it collides +# with the screen’s lower or upper boundary, it should reverse +# its vertical direction. If it collides with the screen’s left +# or right boundary, it should reverse its horizontal direction. + +# Note: you can import the time module as well and use +# time.sleep(0.01) +# in your mainloop to act as a frame cap to make your rectangle +# more visible + +# put imports here + +SCREEN_SIZE = (600, 400) +RECT_SIZE = (100, 100) +RED = (255, 0, 0) +BLACK = (0, 0, 0) +momentum = [1, 1] # (down and right) + +# add code here diff --git a/games/chapter2/practice/moving_text.py b/games/chapter2/practice/moving_text.py new file mode 100644 index 00000000..27a67359 --- /dev/null +++ b/games/chapter2/practice/moving_text.py @@ -0,0 +1,23 @@ +# move some text! + +# The text should start in the upper corner and be moving +# down and to the right. You can move the text using coordinates +# or blitz the text on to a moving rectangle. Feel free to be +# creative with colors, fonts, and font sizes. However, if +# applicable, make the rectangle proportional to the text, and +# everything smaller than the screen + + +# Note: you can import a time module in your loop +# to make it more clear + + +# imports! + +SCREEN_SIZE = (800, 800) +BLACK = (0, 0, 0) +BLUE = (0, 0, 255) +WHITE = (255, 255, 255) +momentum = (2, 2) # down and right + +# add code here diff --git a/games/chapter2/practice/reset_position.py b/games/chapter2/practice/reset_position.py new file mode 100644 index 00000000..8599a1af --- /dev/null +++ b/games/chapter2/practice/reset_position.py @@ -0,0 +1,27 @@ +# Reset the moving rectangle's position if it leaves the screen! +# The rectangle that will be moving is already provided +# it is `red_rectangle`. Your job is to move it across the screen +# at a speed of 5px down and 5px right per frame. Then, if the +# bottom of the rectangle is greater than the screen height or the +# right of the rectangle is greater than the screen width, reset +# the rectangle's x and y to 0 and 0. + +import pygame + +pygame.init() + +SCREEN_HEIGHT = 600 +SCREEN_WIDTH = 600 + +window = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) +pygame.display.set_caption("Reset Position") + +# color constants +RED = (255, 0, 0) +BLACK = (0, 0, 0) + +# makes a pygame.Rect rectangle +# the syntax is `myvar = pygame.Rect(top-left x, top-left y, width, height)` +red_rectangle = pygame.Rect(0, 0, 100, 100) + +# add code here diff --git a/games/chapter2/solutions/add_text.py b/games/chapter2/solutions/add_text.py new file mode 100644 index 00000000..38838645 --- /dev/null +++ b/games/chapter2/solutions/add_text.py @@ -0,0 +1,61 @@ +# Add some text to your game + +# This problem builds off of bouncingrect.py + +# Add some text to the screen. You can either: +# - draw the text to a specified coordinate OR +# - blit the text onto the bouncing rectangle. + +import pygame +import time # not necessary, but used for frame cap + +pygame.init() # initialize pygame module + +SCREEN_SIZE = (600, 400) +RECT_SIZE = (100, 100) +RED = (255, 0, 0) +BLACK = (0, 0, 0) +WHITE = (255, 255, 255) +momentum = [1, 1] # (down and right) + +window = pygame.display.set_mode(SCREEN_SIZE) +running = True + +# start the rectangle in the middle of the screen +x = SCREEN_SIZE[0] // 2 +y = SCREEN_SIZE[1] // 2 + +while running: + # if the rectangle collided with the left or right side + # of the screen + if x + RECT_SIZE[0] >= SCREEN_SIZE[0] or x <= 0: + momentum[0] = -momentum[0] + # if the rectangle collided with the top or bottom + # of the screen + if y + RECT_SIZE[1] >= SCREEN_SIZE[1] or y <= 0: + momentum[1] = -momentum[1] + + # add the speed to the current x and y to get the + # new x and y + x += momentum[0] + y += momentum[1] + + window.fill(BLACK) # 'erase' the previous frame + pygame.draw.rect(window, RED, (x, y, RECT_SIZE[0], RECT_SIZE[1])) + + font = pygame.font.SysFont("Calibri", 16) + + bouncetext = font.render("This Bounces!", True, WHITE) + stationarytext = font.render("This doesn't bounce", True, WHITE) + + window.blit(bouncetext, (x, y, RECT_SIZE[0], RECT_SIZE[1])) + window.blit(stationarytext, (100, 100)) + + pygame.display.update() # update the display + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + time.sleep(0.01) # frame cap to make the rectangle more visible + +pygame.quit() # deactivate the pygame module diff --git a/games/chapter2/solutions/bouncing_rect.py b/games/chapter2/solutions/bouncing_rect.py new file mode 100644 index 00000000..36a9d81b --- /dev/null +++ b/games/chapter2/solutions/bouncing_rect.py @@ -0,0 +1,59 @@ +# Make a “bouncing rectangle!” + +# For this, please use the given screen and rectangle +# width and height. + +# The rectangle should start in (or close to) the middle of the +# screen. It should be moving down and right. If it collides +# with the screen’s lower or upper boundary, it should reverse +# its vertical direction. If it collides with the screen’s left +# or right boundary, it should reverse its horizontal direction. + +# Note: you can import the time module as well and use +# time.sleep(0.01) +# in your mainloop to act as a frame cap to make your rectangle +# more visible + +import pygame +import time # not necessary, but used for frame cap + +pygame.init() # initialize pygame module + +SCREEN_SIZE = (600, 400) +RECT_SIZE = (100, 100) +RED = (255, 0, 0) +BLACK = (0, 0, 0) +momentum = [1, 1] # (down and right) + +window = pygame.display.set_mode(SCREEN_SIZE) +running = True + +# start the rectangle in the middle of the screen +x = SCREEN_SIZE[0] // 2 +y = SCREEN_SIZE[1] // 2 + +while running: + # if the rectangle collided with the left or right side + # of the screen + if x + RECT_SIZE[0] >= SCREEN_SIZE[0] or x <= 0: + momentum[0] = -momentum[0] + # if the rectangle collided with the top or bottom + # of the screen + if y + RECT_SIZE[1] >= SCREEN_SIZE[1] or y <= 0: + momentum[1] = -momentum[1] + + # add the speed to the current x and y to get the + # new x and y + x += momentum[0] + y += momentum[1] + + window.fill(BLACK) # 'erase' the previous frame + pygame.draw.rect(window, RED, (x, y, RECT_SIZE[0], RECT_SIZE[1])) + pygame.display.update() # update the display + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + time.sleep(0.01) # frame cap to make the rectangle more visible + +pygame.quit() # deactivate the pygame module diff --git a/games/chapter2/solutions/moving_text.py b/games/chapter2/solutions/moving_text.py new file mode 100644 index 00000000..4c92e455 --- /dev/null +++ b/games/chapter2/solutions/moving_text.py @@ -0,0 +1,58 @@ +import pygame + +pygame.init() # initializes pygame module + +SCREEN_SIZE = (800, 800) +BLACK = (0, 0, 0) # background color +BLUE = (0, 0, 255) # color of font +WHITE = (255, 255, 255) # color of rectangle +momentum = (2, 2) + +window = pygame.display.set_mode(SCREEN_SIZE) +pygame.display.set_caption("Moving-Text") + + +# make a pygame.Rect rectangle +white_rectangle = pygame.Rect(0, 100, 130, 40) + +# move the text with coordinates instead of the rectangle +x = 0 # x-coordinate of the top-left pixel of text +y = 400 # y-coordinate of the top-left pixel of text + +# sets a font and font size +font = pygame.font.SysFont("Times New Roman", 40) + +# create a piece of text +text = font.render("HELLO", False, BLUE) + +run = True +while run: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + run = False + + # moves a pygame.Rect rectangle relative to its position + white_rectangle.move_ip(momentum) # moves 2 to the right, 2 down + + # move the text by coordinates + x += 1 # move right by one pixel + y += 1 # move down by one pixel + + # erase the previous frame + window.fill(BLACK) + + # draw a rectangle that is a pygame.Rect object + pygame.draw.rect(window, WHITE, white_rectangle) + + # draws text onto the rectangle + window.blit(text, white_rectangle) + # you can also use coordinates in the form of a tuple + # the coordinates would place the top left pixel + # Syntax: window.blit(text, (x,y)) + + # update the screen + pygame.display.update() + + pygame.time.wait(30) # adds a 30 millisecond delay + +pygame.quit() # close pygame after finishing diff --git a/games/chapter2/solutions/reset_position.py b/games/chapter2/solutions/reset_position.py new file mode 100644 index 00000000..fe5cbc5f --- /dev/null +++ b/games/chapter2/solutions/reset_position.py @@ -0,0 +1,55 @@ +# Reset the moving rectangle's position if it leaves the screen! +# The rectangle that will be moving is already provided +# it is `red_rectangle`. Your job is to move it across the screen +# at a speed of 5px down and 5px right per frame. Then, if the +# bottom of the rectangle is greater than the screen height or the +# right of the rectangle is greater than the screen width, reset +# the rectangle's x and y to 0 and 0. + +import pygame + +pygame.init() + +SCREEN_HEIGHT = 600 +SCREEN_WIDTH = 600 + +window = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) +pygame.display.set_caption("Reset Position") + +# color constants +RED = (255, 0, 0) +BLACK = (0, 0, 0) + +# makes a pygame.Rect rectangle +# the syntax is `myvar = pygame.Rect(top-left x, top-left y, width, height)` +red_rectangle = pygame.Rect(0, 0, 100, 100) + +run = True +while run: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + run = False + + # move the rectangle 5 units right and 5 units down each frame + red_rectangle.move_ip(5, 5) + + # erase the previous frame + window.fill(BLACK) + + # reset the rectangle if its right is past the screen width or + # its bottom is below the screen height + if ( + red_rectangle.right > SCREEN_WIDTH + or red_rectangle.bottom > SCREEN_HEIGHT + ): + red_rectangle.x, red_rectangle.y = 0, 0 + + # draw the rectangle in red + pygame.draw.rect(window, RED, red_rectangle) + + # update the screen + pygame.display.update() + + pygame.time.wait(50) # wait 50 milliseconds between frame + +pygame.quit() diff --git a/games/chapter3/examples/comprehensive_example.py b/games/chapter3/examples/comprehensive_example.py new file mode 100644 index 00000000..d978eff4 --- /dev/null +++ b/games/chapter3/examples/comprehensive_example.py @@ -0,0 +1,23 @@ +import pygame + +pygame.init() + +flag = pygame.locals.RESIZABLE +window = pygame.display.set_mode((500, 400), flag) + +pygame.event.set_blocked(pygame.KEYDOWN) + +run = True +while run: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + # if event is QUIT + run = False + if event.type == pygame.KEYUP: + # if event is KEYUP + print("Up up up!") + if event.type == pygame.KEYDOWN: + # will never happen because KEYDOWN is blocked + print("Down down down!") + +pygame.quit() diff --git a/games/chapter3/practice/MoveRectProblem.py b/games/chapter3/practice/MoveRectProblem.py new file mode 100644 index 00000000..22d180ad --- /dev/null +++ b/games/chapter3/practice/MoveRectProblem.py @@ -0,0 +1,11 @@ +# Build off of your previous code (the code from QuitPygameProblem.py) +# Draw the provided rectangle onto the screen. +# Move the object up when either the W or up arrow key is pressed; +# right when either the D or right arrow is pressed; etc. + +# rect[0] = rect color +# rect[1] = x-coord +# rect[2] = y-coord +# rect[3] = width +# rect[4] = height +rectangle = [(255, 0, 0), 20, 20, 20, 20] diff --git a/games/chapter3/practice/QuitPygameProblem.py b/games/chapter3/practice/QuitPygameProblem.py new file mode 100644 index 00000000..f18c99c8 --- /dev/null +++ b/games/chapter3/practice/QuitPygameProblem.py @@ -0,0 +1,8 @@ +# Create a pygame window. +# Close the pygame window when either the quit event occurs +# or the escape key is pressed. +# Use the provided width and height. Fill the screen with white + +width = 500 +height = 500 +white = (255, 255, 255) diff --git a/games/chapter3/practice/SpaceCounter.py b/games/chapter3/practice/SpaceCounter.py new file mode 100644 index 00000000..da33176d --- /dev/null +++ b/games/chapter3/practice/SpaceCounter.py @@ -0,0 +1,7 @@ +# Create a program that increments a counter every time the space bar is +# pressed. This counter should be displayed as text on the pygame window. + +import pygame + +pygame.init() +screen = pygame.display.set_mode((400, 400)) diff --git a/games/chapter3/practice/ticking_counter.py b/games/chapter3/practice/ticking_counter.py new file mode 100644 index 00000000..4befdd84 --- /dev/null +++ b/games/chapter3/practice/ticking_counter.py @@ -0,0 +1,22 @@ +# make a time bomb! + +# Create a counter that starts at a number, such +# 10 and goes down everytime the user presses +# the keyboard. However, this is a time bomb! +# create some text to warn the user, and when the +# number gets low, switch the message. Then, when +# the number hits zero, switch the message again +# to show that they've blown up, and exit the program. +# Wait a little before exiting so that the last message +# is readable. +# Make sure to use some of the methods featured in 3.4! + +import pygame # add more imports if needed + +screen = pygame.display.set_mode((400, 400)) + +# feel free to change these values +fonts = pygame.font.SysFont("arial", 20) +font = pygame.font.SysFont("arial", 70) +text = "DON'T PRESS A KEY" +counter = 10 diff --git a/games/chapter3/solutions/MoveRectProblem.py b/games/chapter3/solutions/MoveRectProblem.py new file mode 100644 index 00000000..14056966 --- /dev/null +++ b/games/chapter3/solutions/MoveRectProblem.py @@ -0,0 +1,57 @@ +# Build off of your previous code (the code from QuitPygameProblem.py) +# Draw the provided rectangle onto the screen. +# Move the object up when either the W or up arrow key is pressed; +# right when either the D or right arrow is pressed; etc. + +import pygame + +pygame.init() + +run = True +width = 500 +height = 500 +white = (255, 255, 255) +screen = pygame.display.set_mode((width, height)) +screen.fill(white) + +# rect[0] = rect color +# rect[1] = x-coord +# rect[2] = y-coord +# rect[3] = width +# rect[4] = height +rectangle = [(255, 0, 0), 20, 20, 20, 20] + +# pygame main loop +while run: + pygame.time.delay(50) + # clear the screen by filling it white + screen.fill(white) + # draw rect + pygame.draw.rect( + screen, + rectangle[0], + pygame.Rect(rectangle[1], rectangle[2], rectangle[3], rectangle[4]), + ) + # Check events + for event in pygame.event.get(): + if event.type == pygame.QUIT: + # there are a couple of ways of stopping the pygame + # loop. One way is to set run = false. Or you can + # import sys and use sys.exit() to stop your program. + run = False + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_ESCAPE: + run = False + # get states of keys + keysPressed = pygame.key.get_pressed() + # recall that y coord decreases as you go up the window + # the origin is at the top left corner + if keysPressed[pygame.K_UP] or keysPressed[pygame.K_w]: + rectangle[2] -= 5 if rectangle[2] >= 5 else 0 + if keysPressed[pygame.K_s] or keysPressed[pygame.K_DOWN]: + rectangle[2] += 5 if rectangle[2] <= 475 else 0 + if keysPressed[pygame.K_d] or keysPressed[pygame.K_RIGHT]: + rectangle[1] += 5 if rectangle[1] <= 475 else 0 + if keysPressed[pygame.K_a] or keysPressed[pygame.K_LEFT]: + rectangle[1] -= 5 if rectangle[1] >= 5 else 0 + pygame.display.update() diff --git a/games/chapter3/solutions/QuitPygameProblem.py b/games/chapter3/solutions/QuitPygameProblem.py new file mode 100644 index 00000000..2ab243e3 --- /dev/null +++ b/games/chapter3/solutions/QuitPygameProblem.py @@ -0,0 +1,29 @@ +# Create a pygame window. +# Close the pygame window when either the quit event occurs +# or the escape key is pressed. +# Use the provided width and height. Fill the screen with white + +import pygame + +pygame.init() + +run = True +width = 500 +height = 500 +white = (255, 255, 255) +screen = pygame.display.set_mode((width, height)) +screen.fill(white) + +# pygame main loop +while run: + pygame.time.delay(50) + for event in pygame.event.get(): + if event.type == pygame.QUIT: + # there are a couple of ways of stopping the pygame + # loop. One way is to set run = false. Or you can + # import sys and use sys.exit() to stop your program. + run = False + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_ESCAPE: + run = False + pygame.display.update() diff --git a/games/chapter3/solutions/SpaceCounter.py b/games/chapter3/solutions/SpaceCounter.py new file mode 100644 index 00000000..bb742f0f --- /dev/null +++ b/games/chapter3/solutions/SpaceCounter.py @@ -0,0 +1,39 @@ +# Create a program that increments a counter every time the space bar is +# pressed. This counter should be displayed as text on the pygame window. + +import pygame + +pygame.init() +screen = pygame.display.set_mode((400, 400)) + +font = pygame.font.SysFont("arial", 70) + +display_counter = 0 + +run = True + +while run: + # Render the "display_counter" to the screen + show_counter = font.render(str(display_counter), True, (255, 192, 203)) + + # Makes Screen Black + screen.fill((0, 0, 0)) + + # Prints Data on Screen + screen.blit(show_counter, (30, 30)) + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + run = False + + # Checks to see if key is pressed + if event.type == pygame.KEYDOWN: + # Checks to see if the space is pressed + if event.key == pygame.K_SPACE: + # Adds one to the counter + display_counter += 1 + + # Updates the data + pygame.display.update() + +pygame.quit() diff --git a/games/chapter3/solutions/ticking_counter.py b/games/chapter3/solutions/ticking_counter.py new file mode 100644 index 00000000..13e8478b --- /dev/null +++ b/games/chapter3/solutions/ticking_counter.py @@ -0,0 +1,38 @@ +import pygame +import time # use this to show text + +pygame.init() +screen = pygame.display.set_mode((400, 400)) # set frame + +fonts = pygame.font.SysFont("arial", 20) # size of message +font = pygame.font.SysFont("arial", 70) # size of counter +text = "DON'T PRESS A KEY" # text +counter = 10 # sets the counter + +run = True + +while run: + if pygame.event.peek(pygame.KEYDOWN): # checks queue for keydown + counter -= 1 # decreases counter by one + pygame.event.clear(pygame.KEYDOWN) # clears keydown from queue + if counter == 1: # changes text + text = "PLEASE YOU'll BLOW US UP!" + if counter == 0: # changes text and ends program + text = "YOU BLEW US UP D:" + run = False + # the if statements are before because when + # run is false, they will still run + # one last time, showing the last message + show_message = fonts.render( + text, True, (255, 102, 253) + ) # sets the message + show_counter = font.render( + str(counter), True, (255, 230, 102) + ) # sets the counter + screen.fill((0, 0, 0)) # refreshes every frame + screen.blit(show_counter, (200, 200)) # shows counter + screen.blit(show_message, (50, 100)) # shows message + pygame.display.update() # updates the frame + +time.sleep(1) # makes the last value of text readable +pygame.quit() diff --git a/games/chapter4/examples/OOP_game.py b/games/chapter4/examples/OOP_game.py new file mode 100644 index 00000000..ab3bf7c7 --- /dev/null +++ b/games/chapter4/examples/OOP_game.py @@ -0,0 +1,473 @@ +""" +This is a tank game made with pygame and original images. + +Brief description of classes within this file: + Game_obj - the abstract base class for all the objects that appear + on-screen, including the Tank class, the Bullet class, and + the Target class + Bullet - inherits from Game_obj. + Target - inherits from Game_obj. It is always stationary. + Tank - inherits from Game_obj. Takes keyboard input (W, A, S, and D) + to control the tank's movement. + App - the abstract base class for the actual Tank_game class. It's + purpose is to define a structure for the game. + Tank_game - the functional class that inherits from App. It creates + a bullet whenever the mouse is clicked. It handles the collisions + (if a bullet hits a target, both are deleted. If the tank runs into + the target, the target is deleted.) +""" + +import pygame + +from pygame.locals import ( + K_w, + K_s, + K_a, + K_d, + KEYDOWN, + KEYUP, + QUIT, + RESIZABLE, + MOUSEBUTTONDOWN, +) +import time +import math +import random + +BULLET_IMG_PATH = "./bullet.png" +TARGET_IMG_PATH = "./target.png" +TANK_IMG_PATH = "./completetank.png" + +BLACK = (255, 255, 255) +DIRTBROWN = (168, 95, 0) +SANDBROWN = (237, 201, 175) + +TANKSPEED = [2, 2] # speed x and speed y +BULLETSPEED = [8, 8] + + +class Game_obj: + def __init__(self, picture: str, **kwargs) -> None: + """ + A basic game object class. It handles collisions, + the basic drawing method, the move and moveto methods, + and the check_out_of_screen method. + + Arguments: + picture:str - the location of the picture that will be displayed on + the screen for this object + Valid keyword arguments: + "size":tuple(x,y) - a specific size that you want to have the object be. + The picture will be scaled to that size and the hitbox + will be updated accordingly. + "position":tuple(x,y) - the tuple at which the top left of the object + should be positioned at + "speed":tuple(x,y) - the tuple that represents the object's speed. + """ + self.name = "" + + # self.image will be a pygame.Surface class + self.image = pygame.image.load(picture) + self.image = ( + pygame.transform.scale( + self.image, (kwargs["size"][0], kwargs["size"][1]) + ) + if "size" in kwargs + else self.image + ) + + self.rect = ( + self.image.get_rect() + ) # self.rect will be of pygame.Rect class + self.size = self.rect.size # will be a tuple of (sizex, sizey) + + if "position" in kwargs: + self.moveto(kwargs["position"]) + + self.speed = ( + {"x": kwargs["speed"][0], "y": kwargs["speed"][1]} + if "speed" in kwargs + else {"x": 0, "y": 0} + ) + + def check_collision(self, other: object) -> bool: + if not isinstance(other, Game_obj): + raise TypeError( + "Invalid type; need a game_obj or a child class of game_obj" + ) + # the rect class's colliderect method returns 1 if there is + # a collision and 0 if there isn't a collision + return self.rect.colliderect(other.rect) == 1 + + def draw(self, screen: pygame.Surface, color: tuple) -> None: + pygame.draw.rect(screen, color, self.rect, 0) + screen.blit(self.image, self.rect) + + def move(self) -> None: + """ + Moves the object according to it's current speed. + """ + self.rect = self.rect.move(self.speed["x"], self.speed["y"]) + # self.draw(screen, color) + + def set_speed(self, new_speed: tuple) -> None: + """ + Sets the object's speed to the provided tuple + Arguments: + new_speed (tuple(x,y)) - a tuple containing the desired speed for + the object to have. + """ + self.speed["x"], self.speed["y"] = new_speed[0], new_speed[1] + + def moveto(self, position: tuple) -> None: + """ + A helper function that moves the rectangle to the desired position. + + Arguments: + position (tuple) - the x and y coordinates of where you want the rectangle's + top left to be moved to. + """ + self.rect = self.rect.move( + position[0] - self.rect.topleft[0], + position[1] - self.rect.topleft[1], + ) + + def check_out_of_screen(self, screen_size: tuple) -> bool: + """ + Checks whether or not the object is completely outside of the screen. + Returns True or False accordingly. + Arguments: + screen_size (tuple) - the size of the screen (x,y) + """ + if ( + self.rect.bottom > screen_size[1] + or self.rect.top < 0 + or self.rect.left < 0 + or self.rect.right > screen_size[0] + ): + return True + return False + + def __str__(self): + return ( + f"{self.name} object located at the position {self.rect.topleft}" + ) + + +class Bullet(Game_obj): + def __init__(self, **kwargs) -> None: + super().__init__(BULLET_IMG_PATH, **kwargs) + self.name = "Bullet" + + +class Target(Game_obj): + def __init__(self, **kwargs) -> None: + kwargs["size"] = 40, 40 + super().__init__(TARGET_IMG_PATH, **kwargs) + self.name = "Target" + + +class Tank(Game_obj): + def __init__(self, **kwargs) -> None: + super().__init__(TANK_IMG_PATH, **kwargs) + self.direction = [0, 0] + self.SPEED = kwargs["speed"] if "speed" in kwargs else [2, 2] + self.speed["x"], self.speed["y"] = 0, 0 + + def set_speed(self) -> None: + # use math stuff to calculate the speed given that the + # max speed is self.SPEED + self.speed["x"] = ( + self.direction[0] + / math.sqrt(sum(abs(num) for num in self.direction)) + * self.SPEED[0] + if (sum(abs(num) for num in self.direction)) != 0 + else self.direction[0] * self.SPEED[0] + ) + self.speed["y"] = ( + self.direction[1] + / math.sqrt(sum(abs(num) for num in self.direction)) + * self.SPEED[1] + if (sum(abs(num) for num in self.direction)) != 0 + else self.direction[1] * self.SPEED[1] + ) + + def set_path(self, direction: str) -> None: + if direction == "up": + self.direction[1] -= 1 + if direction == "down": + self.direction[1] += 1 + if direction == "left": + self.direction[0] -= 1 + if direction == "right": + self.direction[0] += 1 + + def unset_path(self, direction: str) -> None: + if direction == "up": + self.direction[1] += 1 + if direction == "down": + self.direction[1] -= 1 + if direction == "left": + self.direction[0] += 1 + if direction == "right": + self.direction[0] -= 1 + + +class App: + """ + The abstract base class for the actual Tank_game class. It's + main purpose is to define a structure for the game. + It's structure is as follows: + Upon initialization, it runs the create_objects method + It's mainloop is comprised of the following methods: + check_events + check_collisions + move_objects + update_display + """ + + def __init__( + self, flags=RESIZABLE, width=960, height=540, title="My Game" + ): + pygame.init() + self.size = [width, height] + self.screen = pygame.display.set_mode(self.size, flags) + pygame.display.set_caption(title, title) + + self.running = True + + self.create_objects() + + def create_objects(self): + """ + This should create the initial objects on the screen. + """ + pass + + def check_events(self, event): + """ + This should take user input and handle it appropriately. + """ + pass + + def update_display(self): + """ + This should utilize clear the screen and then draw + all current objects onto the screen. + """ + pass + + def move_objects(self): + """ + This should utilize the move method that the game objects have. + """ + pass + + def check_collisions(self): + """ + This should utilize the check_collision method that the game objects + have. + """ + pass + + def mainloop(self): + while self.running: + for event in pygame.event.get(): + if event.type == QUIT: + self.running = False + break + else: + self.check_events( + event + ) # this will handle checking for user input + # such as KEYUP and MOUSEBUTTONDOWN events needed to run the game + self.check_collisions() # checks collisions between bullet/tank and targets + self.move_objects() # moves each object on the screen + self.update_display() # redraws updated objects onto the screen + pygame.display.update() # pygame’s method to show the updated screen + time.sleep(0.01) # not necessary; it's a frame cap + pygame.quit() + + +class Tank_Game(App): + def __init__(self): + # this can be changed, it's the number of targets allowed at a time. + # we initialize this before super().__init__ because super().__init__ calls + # create_objects, which utilizes self.NUM_TARGETS + self.NUM_TARGETS = 3 + + super().__init__(title="Tanks") + + self.playerscore = 0 # the player's score + + # sets the display icon to the TankIcon.png provided + pygame.display.set_icon(pygame.image.load("./TankIcon.png")) + + def create_objects(self): + """ + This creates the initial objects seen when the game + first starts up. + """ + # tank + self.tank = Tank(speed=TANKSPEED) + self.tank.moveto( + ( + self.size[0] / 2 - self.tank.size[0], # move to middle x + self.size[1] - self.tank.size[1], # move to bottom y + ) + ) + + # targets + self.targets = [Target(speed=[0, 0]) for i in range(self.NUM_TARGETS)] + for target in self.targets: + target.moveto( + ( + random.randint( + 0, self.size[0] - target.size[0] + ), # random x + random.randint( + 0, self.size[1] - target.size[1] + ), # random y + ) + ) + + # bullets + self.bullets = [] + + # Score text + self.font = pygame.font.SysFont(pygame.font.get_default_font(), 32) + + def check_events(self, event): + """ + We imported all from pygame.locals, so that means + that we can check KEYDOWN and KEYUP and individual + keys such as K_w (w key), K_a (a key), etc. + """ + # change the path of the tank if w, a, s, or d was pressed + if event.type == KEYDOWN: + if event.key == K_w: + self.tank.set_path("up") + if event.key == K_s: + self.tank.set_path("down") + if event.key == K_a: + self.tank.set_path("left") + if event.key == K_d: + self.tank.set_path("right") + if event.type == KEYUP: + if event.key == K_w: + self.tank.unset_path("up") + if event.key == K_s: + self.tank.unset_path("down") + if event.key == K_a: + self.tank.unset_path("left") + if event.key == K_d: + self.tank.unset_path("right") + self.tank.set_speed() + + # create bullets if mouse button was pressed + if event.type == MOUSEBUTTONDOWN: + bul = Bullet(speed=BULLETSPEED) + bul.moveto( + (self.tank.rect.centerx, (self.tank.rect.top - bul.size[1])) + ) # move the bullet to the front of the tank + + # math stuff to calculate trajectory + mouse_pos = pygame.mouse.get_pos() + h = mouse_pos[1] - bul.rect.center[1] + w = mouse_pos[0] - bul.rect.center[0] + hyp = math.sqrt(h**2 + w**2) + vertical_speed = ( + BULLETSPEED[1] * (h / hyp) if hyp != 0 else BULLETSPEED[1] * h + ) + horizontal_speed = ( + BULLETSPEED[0] * (w / hyp) if hyp != 0 else BULLETSPEED[0] * w + ) + + bul.set_speed((horizontal_speed, vertical_speed)) + self.bullets.append(bul) + + def move_objects(self): + """ + This method moves the objects within the game. + If a bullet is outside of the screen, it is + not moved and is unreferenced. + """ + self.tank.move() + + self.bullets = [ + bullet + for bullet in self.bullets + if bullet.check_out_of_screen(self.size) is False + ] + + for bullet in self.bullets: + bullet.move() + + def check_collisions(self): + """ + This checks whether any of the objects within the game have collided + with each other. Specifically, we are looking for collisions between + bullets and targets or the tank and targets + """ + deletions = 0 # number of targets deleted + num_bullets = len(self.bullets) + + # check bullet-target collisions + for i in range(num_bullets): + for target in self.targets: + # if the bullet collided with the target + if self.bullets[i - deletions].check_collision(target) is True: + # pop both the bullet and target so that they will be + # effectively deleted + self.bullets.pop(i - deletions) + self.targets.pop(self.targets.index(target)) + + # give points for hitting the target + self.playerscore += 20 + deletions += 1 + break # stop the current iteration since the target and + # bullet are popped, so referencing them would error. + + # check tank-target collisions + for target in self.targets: + if self.tank.check_collision(target) is True: + self.targets.pop(self.targets.index(target)) + deletions += 1 + self.playerscore += 10 # only 10 for running over targets lol + + # create a new target for every deleted target + for i in range(deletions): + a = Target(speed=[0, 0]) + a.moveto( + ( + random.randint(0, self.size[0] - a.size[0]), + random.randint(0, self.size[1] - a.size[1]), + ) + ) + self.targets.append(a) + + def update_display(self): + self.screen.fill(SANDBROWN) + + # tank + self.tank.draw(self.screen, SANDBROWN) + + # targets + for target in self.targets: + target.draw(self.screen, BLACK) + + # bullets + for bullet in self.bullets: + bullet.draw(self.screen, BLACK) + + # score text + font_img = self.font.render( + "Score: %s" % str(self.playerscore), True, BLACK + ) + font_rect = font_img.get_rect() + pygame.draw.rect(self.screen, SANDBROWN, font_rect, 1) + self.screen.blit(font_img, font_rect) + + +game = Tank_Game() +game.mainloop() diff --git a/games/chapter4/examples/TankIcon.png b/games/chapter4/examples/TankIcon.png new file mode 100644 index 00000000..d9e00bbe Binary files /dev/null and b/games/chapter4/examples/TankIcon.png differ diff --git a/games/chapter4/examples/Target.png b/games/chapter4/examples/Target.png new file mode 100644 index 00000000..d48bc78e Binary files /dev/null and b/games/chapter4/examples/Target.png differ diff --git a/games/chapter4/examples/bullet.png b/games/chapter4/examples/bullet.png new file mode 100644 index 00000000..fce8c161 Binary files /dev/null and b/games/chapter4/examples/bullet.png differ diff --git a/games/chapter4/examples/completetank.png b/games/chapter4/examples/completetank.png new file mode 100644 index 00000000..036cd84b Binary files /dev/null and b/games/chapter4/examples/completetank.png differ diff --git a/games/chapter4/examples/tank_game.zip b/games/chapter4/examples/tank_game.zip new file mode 100644 index 00000000..c6900645 Binary files /dev/null and b/games/chapter4/examples/tank_game.zip differ diff --git a/games/chapter4/practice/flappy_bird/OOPflappybird.py b/games/chapter4/practice/flappy_bird/OOPflappybird.py new file mode 100644 index 00000000..08ca2aa2 --- /dev/null +++ b/games/chapter4/practice/flappy_bird/OOPflappybird.py @@ -0,0 +1,494 @@ +# TODO +# Create the FlappyBird game!! + +# You are provided with some starting code. +# The starting code, however, doesn't run by itself. +# What you need to do: +# define GameObj's draw method +# define GameObj's check_collision method. + +# Complete all the methods within Tubes class + +# Complete the draw_score and draw_buttons methods in the FlappyBird class + +import pygame +import random + +pygame.init() + +# screen +width = 800 +height = 600 +SIZE = (width, height) +screen = pygame.display.set_mode(SIZE) + +# colors +LGREEN = (62, 245, 59) +DGREEN = (40, 143, 39) +YELLOW = (250, 250, 37) +WHITE = (255, 255, 255) +BLACK = (0, 0, 0) +RED = (255, 0, 0) +LILAC = (175, 95, 237) +LBLUE = (80, 221, 242) +DBLUE = (80, 99, 242) +PINK = (245, 144, 188) +CYAN = (0, 150, 150) + +# images +BACKGROUNDIMG = pygame.image.load("./background.png") +BACKGROUNDIMG = pygame.transform.scale(BACKGROUNDIMG, (width, height)) +SPRITESHEET = pygame.image.load("./flyingbird.png") +COINPIC = pygame.image.load("./coin.png") + +# ---------- States of the Game ---------- +MENUSTATE = 0 # Menu Screen +GAMESTATE = 1 # Play Game +LOSESTATE = 2 # u loose >:) +QUITSTATE = 3 +NUMSTATES = 4 + + +class GameObj: + """ + An abstract class used as the base class for all the + game's objects + """ + + def __init__(self): + """ + This __init__ method provides no functionality. + It just enables the methods defined in this class. + Thus, calling super().__init__ is unnecessary. + """ + self.rect = pygame.Rect + + def draw( + self, + screen: pygame.Surface, + color: tuple, + specific_rect: pygame.Rect = None, + ): + """ + Draws a rectangle onto the screen in the specified color. + If specific_rect is not None, draw specific_rect onto the screen. + If specific_rect is None, draw self.rect onto the screen. + """ + pass + + def move(self, speed: dict = None, specific_rect: pygame.Rect = None): + """ + Moves a rectangle. + @param speed - The speed to move the rectangle at. It should be + a dictionary of form {'x': int, 'y': int}; for example, + {'x':33, 'y':-22}. If no speed is provided, uses self.speed + @param specific_rect - if specific_rect is None, then this method + will move self.rect. If specific_rect is not None, then this method will + move specific_rect + """ + if not speed and hasattr(self, "speed"): + if specific_rect: + return specific_rect.move(self.speed["x"], self.speed["y"]) + else: + self.rect = self.rect.move(self.speed["x"], self.speed["y"]) + if speed: + if specific_rect: + return specific_rect.move(speed["x"], speed["y"]) + else: + self.rect = self.rect.move(speed["x"], speed["y"]) + + def check_collision(self, other, specific_rect: pygame.Rect = None): + """ + Checks if rectangles have collided. If specific_rect is not None, + checks if specific_rect collides with other.rect. If specific_rect is None, + checks if self.rect collides with other.rect. + """ + pass + + +class Tubes(GameObj): + """ + Class to represent the two tubes. + + Ex: + The tubes will look sort of like the below drawing + (one on the top, one on the bottom) + (let - be top or bottom of school) + ------------ + | | + |_| + + _ + | | + | | + | | + ------------ + """ + + TUBEGAP = 230 # smaller TUBEGAP -> smaller dist between tubes + TUBEWIDTH = 100 + + def __init__(self, bottom_tube_height: int): + """ + Initializes two pygame.Rect objects: one for the + top tube (call it top_tube) and one for the bottom tube + (call it bottom_tube). Uses the TUBEGAP + and TUBEWIDTH variables as dimensions. + """ + pass + + def draw(self, screen: pygame.Surface): + """ + Uses the draw() method from the inherited + GameObj class to draw the top and bottom tubes. + Hint: this will use the specific_rect argument + """ + pass + + def move(self, speed: dict): + """ + Uses the move() method from the inherited + GameObj class to move the specific top and bottom tubes. + Hint: this will use the specific_rect argument + """ + pass + + def check_collision(self, other) -> bool: + """ + Uses the check_collision() method from the inherited + GameObj class to check for any collisions + between the given object and the tubes. + Hint: this will use the specific_rect argument + + Returns: + boolean - if either tube is collided with, return True + """ + pass + + +class Coin(GameObj): + """ + The coin that the bird will get + in-between tubes. + Doesn't need to do anything, so pretty short class. + """ + + def __init__(self, center_y: int): + """ + Makes a coin object. + The coin's x coordinate will be the width of the screen + The coin's y coordinate will be centered around `center_y` + @param center_y:int - the y coordinate to center the coin around + """ + temprect = COINPIC.get_rect() + self.rect = pygame.Rect( + width, + center_y - temprect.height // 2, + temprect.width, + temprect.height, + ) + + def draw(self, screen: pygame.Surface): + super().draw(screen, BLACK) + + def blit(self, screen: pygame.Surface): + screen.blit(COINPIC, self.rect) + + +class Bird(GameObj): + """ + The bird itself. It processes the sprites + and handles jumping. + """ + + start_center_pos = (width // 8, height // 2) + + def __init__(self): + self.process_spritesheet(SPRITESHEET, 3, 3) + self.rect = pygame.Rect( + self.start_center_pos[0] - self.sprite_frame_width // 2, + self.start_center_pos[1] - self.sprite_frame_height // 2, + self.sprite_frame_width, + self.sprite_frame_height, + ) + self.momentum = 0 # the bird's current vertical speed + self.jump_height = 15 + self.min_speed = -10 # the maximum speed the bird flies down at + self.cur_sprite_idx = 0 + + def process_spritesheet( + self, + spritesheet: pygame.Surface, + num_pics_x: int, + num_pics_y: int, + offset_x: int = 0, + offset_y: int = 0, + ): + """ + Creates sprites from the spritesheet. + @param spritesheet: pygame.Surface - the spritesheet. + @param num_pics_x: int - the number of sprites in each row on + the spritesheet + @param num_pics_y: int - the number of sprites in each column + on the spritesheet + @param offset_x: int - the x offset before the sprite rows start + @param offset_y: int - the y offset before the sprite columns start + """ + self.sprites = [] + self.sprite_frame_width = ( + spritesheet.get_width() - offset_x + ) // num_pics_x + self.sprite_frame_height = ( + spritesheet.get_height() - offset_y + ) // num_pics_y + for row in range(num_pics_x): + for column in range(num_pics_y): + temp = spritesheet.subsurface( + ( + row * self.sprite_frame_width + offset_x, + column * self.sprite_frame_height + offset_y, + self.sprite_frame_width, + self.sprite_frame_height, + ) + ) + # get the bounding box for the actual colored pixels + # (so that we won't be blit-ing extra empty pixels) + # (makes collisions more accurate) + temprect = temp.get_bounding_rect() + # then, append the shortened image to the sprites list + self.sprites.append(temp.subsurface(temprect)) + + def draw(self, screen: pygame.Surface, framecount: int): + curr_sprite_idx = framecount // 5 % len(self.sprites) + if curr_sprite_idx != self.cur_sprite_idx: + # if it is now a different sprite, adjust self.rect + # so that it won't be bigger or smaller than the new sprite + self.cur_sprite_idx = curr_sprite_idx + temp = self.sprites[self.cur_sprite_idx] + self.rect = temp.get_rect().move( + self.rect.topleft[0], self.rect.topleft[1] + ) + super().draw(screen, BLACK) + + def blit(self, screen: pygame.Surface): + screen.blit(self.sprites[self.cur_sprite_idx], self.rect) + + def process_movement(self, event): + if event.type == pygame.KEYDOWN and event.key == pygame.K_UP: + self.momentum = self.jump_height + + def move(self): + super().move({"x": 0, "y": -self.momentum}) + self.momentum -= 1 + + # if the bird would fly down faster than self.min_speed, + # cap self.momentum at self.min_speed + if self.momentum < self.min_speed: + self.momentum = self.min_speed + + +class Button(GameObj): + """ + A button with text. Used for the + 'Quit Game' 'Start Game' and 'Retry' buttons + """ + + def __init__( + self, + center_x: int, + center_y: int, + bgcolor: tuple, + textcolor: tuple, + text: str = "", + textsize: int = 32, + ): + """ + Creates a Button object. + @param center_x: int - the x coordinate of the button's center + @param center_y: int - the y coordinate of the button's center + @param bgcolor: tuple - the color for the button's background + @param textcolor: tuple - the color for the button's text + @param text: str - the text to put inside the button + @param textsize: int - the size of the button's text + """ + self.font = pygame.font.SysFont("arial", textsize) + self.font_img = self.font.render(text, True, textcolor) + self.rect = self.font_img.get_rect() + self.rect.center = (center_x, center_y) + self.bgcolor = bgcolor + self.active = True + + def draw(self, screen: pygame.Surface): + super().draw(screen, self.bgcolor) + screen.blit(self.font_img, self.rect) + + def is_clicked(self, event: pygame.event.Event): + if event.type == pygame.MOUSEBUTTONDOWN: + return event.pos in self + + def __contains__(self, coordinate): + return self.rect.contains((coordinate[0], coordinate[1], 0, 0)) + + +class FlappyBird: + """ + This is the game class. + """ + + def __init__(self): + self.running = True + self.gamestate = MENUSTATE + self.create_buttons() + self.framecount = 0 + self.clock = pygame.time.Clock() + + def create_buttons(self, button1text="Start Game", button2bg=RED): + self.buttons = { + "start": Button( + width // 2, height // 4, LGREEN, LILAC, button1text + ), + "quit": Button( + width // 2, height // 4 * 3, button2bg, LILAC, "Quit Game" + ), + } + + def mainloop(self): + while self.running: + events = pygame.event.get() + for event in events: + self.set_state(event) + if event.type == pygame.QUIT: + self.running = False + if self.gamestate == MENUSTATE: + screen.fill(LBLUE) + self.draw_buttons(screen) + + elif self.gamestate == GAMESTATE: + self.draw_all() + self.check_collisions() + + # update bird's speed + for event in events: + self.bird.process_movement(event) + self.move_objects() + + self.create_tubes() + + elif self.gamestate == LOSESTATE: + screen.fill(RED) + self.draw_buttons(screen) + + elif self.gamestate == QUITSTATE: + self.running = False + + self.framecount += 1 + pygame.display.update() + self.clock.tick(60) + pygame.quit() + + def set_state(self, event): + if event.type == pygame.MOUSEBUTTONDOWN: + if all([but.active for but in self.buttons.values()]): + if self.buttons["start"].is_clicked(event): + self.buttons["start"].active = False + self.buttons["quit"].active = False + self.start_game() + elif self.buttons["quit"].is_clicked(event): + self.gamestate = QUITSTATE + self.buttons["start"].active = False + self.buttons["quit"].active = False + + def draw_background(self): + screen.blit(BACKGROUNDIMG, (self.background_x, 0)) + screen.blit(BACKGROUNDIMG, (self.background_x + width, 0)) + self.background_x -= 2 + if self.background_x < -1 * width: + self.background_x = 0 + + def draw_score(self): + """ + Writes the player's score onto the screen in the top + right corner. + Hint: this uses pygame fonts + """ + pass + + def draw_buttons(self, screen: pygame.Surface): + """ + Draws the "start" and "quit" buttons onto the screen. + Hint: this uses `self.buttons` (which is already made) + """ + pass + + def create_tubes(self): + """ + Creates tubes and puts a coin in the middle of each tube. + """ + if ( + len(self.tubes) == 0 + or self.tubes[-1].bottom_tube.right < width - 200 + ): + bottom_tube_height = random.randint(0, height - Tubes.TUBEGAP) + self.tubes.append(Tubes(bottom_tube_height)) + self.coins.append( + Coin(height - bottom_tube_height - (Tubes.TUBEGAP // 2)) + ) + + def draw_all(self): + # draw bird and coin rectangles before background so that they won't + # show + self.bird.draw(screen, self.framecount) + for coin in self.coins: + coin.draw(screen) + + self.draw_background() + + for tube in self.tubes: + tube.draw(screen) + + # blit images/sprites onto the screen + self.bird.blit(screen) + for coin in self.coins: + coin.blit(screen) + + self.draw_score() + + def check_collisions(self): + for tube in self.tubes: + if tube.check_collision(self.bird): + self.gamestate = LOSESTATE + self.create_buttons("Retry?", LBLUE) + if tube.bottom_tube.right < 0: + self.tubes.remove(tube) + + for coin in self.coins: + if self.bird.check_collision(coin): + self.score += 1 + self.coins.remove(coin) + if coin.rect.right < 0: + self.coins.remove(coin) + + if self.bird.rect.bottom > height: # fell out of screen + self.gamestate = LOSESTATE + self.create_buttons("Retry?", LBLUE) + + def move_objects(self): + SPEED = 3 # x speed that objects move towards the bird at + for tube in self.tubes: + tube.move({"x": -SPEED, "y": 0}) + for coin in self.coins: + coin.move({"x": -SPEED, "y": 0}) + self.bird.move() + + def start_game(self): + self.gamestate = GAMESTATE + self.background_x = 0 + self.score = 0 + self.bird = Bird() + self.tubes = [] + self.coins = [] + self.create_tubes() + + +a = FlappyBird() +a.mainloop() diff --git a/games/chapter4/practice/flappy_bird/background.png b/games/chapter4/practice/flappy_bird/background.png new file mode 100644 index 00000000..0be8c233 Binary files /dev/null and b/games/chapter4/practice/flappy_bird/background.png differ diff --git a/games/chapter4/practice/flappy_bird/bird.png b/games/chapter4/practice/flappy_bird/bird.png new file mode 100644 index 00000000..2b8a5b32 Binary files /dev/null and b/games/chapter4/practice/flappy_bird/bird.png differ diff --git a/games/chapter4/practice/flappy_bird/coin.png b/games/chapter4/practice/flappy_bird/coin.png new file mode 100644 index 00000000..dc457bff Binary files /dev/null and b/games/chapter4/practice/flappy_bird/coin.png differ diff --git a/games/chapter4/practice/flappy_bird/explosion_transparent.png b/games/chapter4/practice/flappy_bird/explosion_transparent.png new file mode 100644 index 00000000..a92d7099 Binary files /dev/null and b/games/chapter4/practice/flappy_bird/explosion_transparent.png differ diff --git a/games/chapter4/practice/flappy_bird/flappy_bird.zip b/games/chapter4/practice/flappy_bird/flappy_bird.zip new file mode 100644 index 00000000..4a21a88a Binary files /dev/null and b/games/chapter4/practice/flappy_bird/flappy_bird.zip differ diff --git a/games/chapter4/practice/flappy_bird/flyingbird.png b/games/chapter4/practice/flappy_bird/flyingbird.png new file mode 100644 index 00000000..25bd824a Binary files /dev/null and b/games/chapter4/practice/flappy_bird/flyingbird.png differ diff --git a/games/chapter4/practice/hockey.py b/games/chapter4/practice/hockey.py new file mode 100644 index 00000000..78db3c80 --- /dev/null +++ b/games/chapter4/practice/hockey.py @@ -0,0 +1,625 @@ +# Make a two-player hockey game! The application will consist +# of two rectangular paddles, starting on each side of the screen, +# and one circular ball that players must bounce around. Players can +# move the paddles in any direction to hit the ball into the goal. +# If the ball makes contact with safe parts of the screen, it will +# bounce off at a random angle but in the same general direction +# (left or right). It will do the same if it makes contact with one +# of the paddles, but will head towards the opposite general direction +# instead. If the ball touches the goals on either side of the screen, +# the application will say “Game Over. Player _ Wins”. You must put +# your code in classes and have separate keys for each player to +# move their paddles. + +# please use the provided constants. + +# TODO - +# Fill in the Game_obj class (init is done) +# Fill in Player class's init, draw, and setpath methods +# Fill in Ball class's collide_line and get_obj_path methods +# Fill in BoundingLine's init method +# Fill in Hockey class's update_display, move_objects, check_events, +# and check_collisions methods + + +import pygame +from pygame.locals import ( + K_w, + K_s, + K_a, + K_d, + K_UP, + K_DOWN, + K_LEFT, + K_RIGHT, + KEYDOWN, + KEYUP, + QUIT, + RESIZABLE, +) +from pygame.rect import Rect + +import math +import time +import random + +# define the necessary color constants using rgb values +BLACK = (0, 0, 0) +GREEN = (0, 120, 0) +RED = (120, 0, 0) +WHITE = (255, 255, 255) + +# define player controls +PLAYER1CONTROLS = {"up": K_w, "down": K_s, "left": K_a, "right": K_d} +PLAYER2CONTROLS = { + "up": K_UP, + "down": K_DOWN, + "left": K_LEFT, + "right": K_RIGHT, +} + +# initial screensize +SCREENSIZE = [900, 600] + +# how big the ball's radius will be +BALL_RADIUS = 3 + + +class Game_obj: + def __init__(self): + """ + This should just declare the variables + later used in the other methods + """ + self.speed = {"x": 0, "y": 0} + self.rect = Rect + self.prev_rect = Rect + + def move(self): + """ + This should first set self.prev_rect equal to self.rect. + Then, it should move self.rect according to self.speed + by using self.rect's move method as demonstrated in OOP_game.py. + """ + pass # your code here + + def move_to(self, coordinate): + """ + This should first set self.prev_rect equal to self.rect. + Then, it should move self.rect so that its top left lies at + the provided coordinate. + """ + pass # your code here + + def check_collision(self, other): + """ + This should return either True or False based on whether + self.rect's collide_rect method returns 1 or 0 (respectively) + similar to how it is done in OOP_game.py + """ + pass # your code here + + +class Player(Game_obj): + PLAYERSPEED = (3, 3) + PADDLESIZE = (10, 50) # x width, y width + + def __init__(self, control_keys): + """ + Creates a player rectangle. It should have an attribute + self.control_keys from provided control keys. It should + create a rectangle at (0, 0) that has self.PADDLESIZE dimensions. + It should also initialize self.path to [0,0] (x_path, y_path) + Arguments: + control_keys - (dict) should be a dictionary of the following format: + { + "up": (KEY) (ex: K_w), + "down": (KEY) (ex: K_s), + "left": (KEY) (ex: K_a), + "right": (KEY) (ex: K_d) + } + """ + pass + + def draw(self, surface: pygame.Surface): + """ + This should draw self.rect onto the provided surface in + the color GREEN + Note: the surface acts just like 'window' in previous + lessons + """ + pass # your code here + + def set_path(self, event): + """ + This is the method that calls self.key_checker with + the provided event and 'up', 'down', 'left', and 'right' + + note: a call to self.key_checker will look like: + self.key_checker(event, 'direction string here') + """ + pass # your code here + + def key_checker(self, event, direction): + """ + Helper function to deal with event keys. Sets self.path + according to PATH_VALUES + Arguments: + event(pygame.event.Event) - the event + direction(str) - the direction to check KEYDOWN and KEYUP for. + """ + PATH_VALUES = {"up": 1, "down": 1, "left": 0, "right": 0} + DIRECTION_VALUES = {"up": -1, "down": 1, "left": -1, "right": 1} + + # if the event doesn't have a key attribute, just return + if not hasattr(event, "key"): + return + + # if it does, then check if it the right key + if event.key == self.control_keys[direction]: + if event.type == KEYUP: + self.path[PATH_VALUES[direction]] += -DIRECTION_VALUES[ + direction + ] + if event.type == KEYDOWN: + self.path[PATH_VALUES[direction]] += DIRECTION_VALUES[ + direction + ] + + def set_speed(self): + """ + Sets the speed according to the Player object's path. + This should be called after self.path has been set. + """ + # this is provided since it's math-intensive. + self.speed["x"] = ( + self.path[0] + / math.sqrt(sum(abs(num) for num in self.path)) + * self.PLAYERSPEED[0] + if (sum(abs(num) for num in self.path)) != 0 + else self.path[0] * self.PLAYERSPEED[0] + ) + self.speed["y"] = ( + self.path[1] + / math.sqrt(sum(abs(num) for num in self.path)) + * self.PLAYERSPEED[1] + if (sum(abs(num) for num in self.path)) != 0 + else self.path[1] * self.PLAYERSPEED[1] + ) + + +class Ball(Game_obj): + BALLSPEED = (6, 6) + + def __init__(self, radius): + super().__init__() + self.rect = Rect(0, 0, radius * 2, radius * 2) + self.radius = radius + + # set up initial speed + initial_ang = random.randint(1, int(math.pi / 2 * 100)) / 100 + self.speed["x"] = ( + math.cos(initial_ang) + * self.BALLSPEED[0] + * (-1 if random.randint(0, 1) == 0 else 1) + ) + self.speed["y"] = ( + math.sin(initial_ang) + * self.BALLSPEED[1] + * (-1 if random.randint(0, 1) == 0 else 1) + ) + + def draw(self, screen: pygame.Surface): + pygame.draw.circle( + screen, WHITE, center=self.rect.center, radius=self.radius + ) + + def collide_line(self, other): + """ + Checks if the ball has hit a line. + If it did, update the speed accordingly + + IE: + if it collided with top or bottom, set + self.speed['y'] to negative self.speed['y'] + if it collided with right or left, + set self.speed['x'] to negative self.speed['x'] + + Arguments: + other (BoundingLine or Goal) - the line to check for a collision with + Returns: + True - if the collision happened + False - if the collision didn't happen + """ + pass # your code here + + def get_obj_path(self, object: Game_obj) -> tuple: + """ + if the object's speed is greater than 0, x_path = 1 + elif the object's speed is less than 0, x_path = -1 + else, the x_path = 0. + Do the same for y speed for a variable y_path. + """ + # your code here + + return # (x_path, y_path) (uncomment when you write your code here) + + def get_paddle_collision_dir(self, paddle: Player) -> tuple: + """ + Gets the direction in which the ball will be headed + after a collision with a paddle. + Does not actually check if the collision happened + Provided since it's somewhat complicated + + Arguments: + paddle (Player) - the player that the ball 'collided with' + Returns: + tuple(int, int) - a tuple of length 2 with just +-1's + ex: (1, 1) or (1, -1) or (-1, 1), or (-1, -1) + It corresponds to the direction in which the ball + will be headed. The first item will be the x direction + and the second item will be the y direction. + """ + paddle_x_dir, paddle_y_dir = self.get_obj_path(paddle) + + ball_x_dir, ball_y_dir = self.get_obj_path(self) + + resulting_x_dir = None + resulting_y_dir = None + + if paddle.speed["x"] == 0 or paddle_x_dir == ball_x_dir: + if abs(paddle.speed["x"]) > abs(self.speed["x"]): + resulting_x_dir = paddle_x_dir + elif abs(paddle.speed["x"]) < abs(self.speed["x"]): + resulting_x_dir = -ball_x_dir + else: + resulting_x_dir = -ball_x_dir + + if paddle.speed["y"] == 0 or paddle_y_dir == ball_y_dir: + if abs(paddle.speed["y"]) > abs(self.speed["y"]): + resulting_y_dir = paddle_y_dir + elif abs(paddle.speed["y"]) < abs(self.speed["y"]): + resulting_y_dir = -ball_y_dir + else: + resulting_y_dir = -ball_y_dir + + return (resulting_x_dir, resulting_y_dir) + + def collide_paddle(self, paddle: Player, executions: int) -> None: + """ + Handles collisions with paddles. + + Checks if the ball hit the provided player. If it did, + it will adjust the ball's direction. + + Arguments: + paddle(Player) - the paddle to check for a collision with + executions(int) - the amount of executions of the game's mainloop + It's not important, but it prevents unwanted collisions during + the 0th execution when we first set up the game by moving + the objects to the right place + """ + PROPORTION = 0.25 # used when "escaping" a collision + MINIMUM_ANGLE = ( + 15 # this is in degrees; it's just a fine-tuning aspect + ) + # that makes the game more realistic + + resulting_x_dir = None + resulting_y_dir = None + + a = self.trace_collisions(paddle) + if a[0] and executions != 0: + resulting_x_dir, resulting_y_dir = a[1] + + if self.check_collision(paddle): + resulting_x_dir, resulting_y_dir = self.get_paddle_collision_dir( + paddle + ) + + # if resulting_x_dir and resulting_y_dir aren't None, then update ball speed + if resulting_x_dir and resulting_y_dir: + print(MINIMUM_ANGLE * math.pi / 180 * 100) + print(math.pi / 2 * 100) + angle = ( + random.randint( + 0, + int( + math.pi / 2 * 100 + - (MINIMUM_ANGLE * math.pi / 180 * 100) + ), + ) + / 100 + ) + + print("angle", angle) + + self.speed["x"] = ( + math.cos(angle) * self.BALLSPEED[0] * resulting_x_dir + ) + self.speed["y"] = ( + math.sin(angle) * self.BALLSPEED[1] * resulting_y_dir + ) + + # escape the collision so as to prevent the "same" collision from being + # handled when collide_paddle is called next time. + while self.check_collision(paddle): + self.move_to( + ( + self.rect.topleft[0] + PROPORTION * self.speed["x"], + self.rect.topleft[1] + PROPORTION * self.speed["y"], + ) + ) + + def trace_collisions(self, paddle): + COLLISIONS_TO_CHECK = 30 # the higher this is, the slower. + + # find how much the ball moved during the past execution + delta_x = self.rect.topleft[0] - self.prev_rect.topleft[0] + delta_y = self.rect.topleft[1] - self.prev_rect.topleft[1] + + # find how much the paddle moved during the past execution + paddle_delta_x = paddle.rect.topleft[0] - paddle.prev_rect.topleft[0] + paddle_delta_y = paddle.rect.topleft[1] - paddle.prev_rect.topleft[1] + + # check COLLISIONS_TO_CHECK times for a collision that occurred during + # the "update game" phase (when we moved the objects) + for i in range(COLLISIONS_TO_CHECK): + # move both the ball and the paddle/player to where they would've been if we + # subdivided the move phase into COLLISIONS_TO_CHECK individual frames + ball_past = Ball(BALL_RADIUS) + ball_past.move_to( + ( + self.prev_rect.topleft[0] + + (delta_x * i / COLLISIONS_TO_CHECK), + self.prev_rect.topleft[1] + + (delta_y * i / COLLISIONS_TO_CHECK), + ) + ) + paddle_past = Player({}) + paddle_past.move_to( + ( + paddle.prev_rect.topleft[0] + + (paddle_delta_x * i / COLLISIONS_TO_CHECK), + paddle.prev_rect.topleft[1] + + (paddle_delta_y * i / COLLISIONS_TO_CHECK), + ) + ) + + # now that we have a ball and a paddle, check if they collided + if ball_past.check_collision(paddle_past): + return (True, ball_past.get_paddle_collision_dir(paddle_past)) + # if the loop failed (didn't return), then + # return False and an empty tuple + return (False, tuple()) + + +class BoundingLine: + DEFAULT_SIZE = 3 + + def __init__(self, parameters): + """ + Replace 'parameters' with real parameters. + This should take: + the starting coordinate of the line + the ending coordinate of the line + the line's name (which should either be + 'top', 'bottom', 'left', or 'right') + (optional) default_size - the size of the line. If not provided, + use DEFAULT_SIZE + This should create the rectangle that stretches from the start + coordinate to the end coordinate with a width or height + (depending on its orientation) of default_size + (or DEFAULT_SIZE if default_size isn't provided) + + Make sure to: + if it is the bottom line: move it DEFAULT_SIZE units up + if it is the right line: move it DEFAULT_SIZE units left + *This is a workaround so that these will show on screen and not + be off-screen + """ + + def draw(self, screen: pygame.Surface, color): + pygame.draw.rect(screen, color, self.rect) + + +class Goal(BoundingLine): + def draw(self, screen: pygame.Surface): + super().draw( + screen, WHITE + ) # the goal should be in white so you can see it + + +class App: + def __init__( + self, flags=RESIZABLE, width=900, height=600, title="My game" + ): + pygame.init() + self.size = [width, height] + self.screen = pygame.display.set_mode(self.size, flags) + pygame.display.set_caption(title, title) + self.running = True + + self.GAMESTATE = 0 + self.WONSTATE = 1 + self.QUITSTATE = 2 + + self.currstate = self.GAMESTATE + self.winning_player = 0 # will be 1 or 2 when a player won + + self.executions = 0 # useful for debugging + + def mainloop(self): + while self.running: + # main game loop (for the game itself) + # because this is a while loop, the game will keep going until someone won + # so we don't need to worry about the post-game text being displayed + if self.currstate == self.GAMESTATE: + for event in pygame.event.get(): + if event.type == QUIT: + # set the variables that are keeping the game running + # to values that won't keep the game running + self.running = False + self.currstate = self.QUITSTATE + else: + self.check_events(event) + self.check_collisions() + self.move_objects() + self.update_display() + pygame.display.update() + time.sleep(0.01) + self.executions += 1 + + if self.currstate == self.WONSTATE: + # 'post-game' game loop (just shows winning text) + for event in pygame.event.get(): + if event.type == QUIT: + self.running = False + self.currstate = self.QUITSTATE + self.display_winning_text() + + if self.currstate == self.QUITSTATE: + pygame.quit() + + def check_events(self, event) -> None: + pass + + def check_collisions(self) -> None: + pass + + def move_objects(self) -> None: + pass + + def update_display(self) -> None: + pass + + def display_winning_text(self) -> None: + pass + + +class Hockey(App): + """ + This is the functional class whose mainloop will be called + to play hockey. + This should inherit from App (where mainloop is defined) + Its methods should utilize the methods within the + game object classes. + """ + + def __init__(self): + super().__init__(title="Hockey!") + + # initialize players + self.player_1 = Player(PLAYER1CONTROLS) + self.player_2 = Player(PLAYER2CONTROLS) + + # move players to starting positions + self.player_1.move_to( + ( + self.size[0] / 8 - self.player_1.rect.width, + self.size[1] / 2 - self.player_1.rect.height, + ) + ) + self.player_2.move_to( + ( + self.size[0] / 8 * 7 - self.player_2.rect.width, + self.size[1] / 2 - self.player_2.rect.height, + ) + ) + + # initialize ball and move it to starting position (center) + self.ball = Ball(BALL_RADIUS) + self.ball.move_to( + ( + self.size[0] / 2 - self.ball.rect.width, + self.size[1] / 2 - self.ball.rect.height, + ) + ) + + # initialize bounding lines - the edges of the screen off which the + # ball should bounce + self.top_line = BoundingLine((0, 0), (self.size[0], 0), "top") + self.bottom_line = BoundingLine( + (0, self.size[1]), (self.size[0], self.size[1]), "bottom" + ) + self.left_line = BoundingLine((0, 0), (0, self.size[1]), "left") + self.right_line = BoundingLine( + (self.size[0], 0), (self.size[0], self.size[1]), "right" + ) + self.bounding_lines = [ + self.top_line, + self.bottom_line, + self.left_line, + self.right_line, + ] + + # initialize Goals + self.goal_1 = Goal( + (0, (self.size[1] / 2) - (5 * self.size[1] / 16)), + (0, (self.size[1] / 2) + (self.size[1] / 16)), + "left", + 3, + ) + self.goal_2 = Goal( + (self.size[0], (self.size[1] / 2) - (5 * self.size[1] / 16)), + (self.size[0], (self.size[1] / 2) + (self.size[1] / 16)), + "right", + 3, + ) + + def update_display(self): + """ + This should fill the screen with BLACK and then + use the draw method of each of the objects to draw them onto + the screen. + """ + pass # your code here + + def move_objects(self): + """ + This should use the move method for the players and the ball + """ + pass # your code here + + def check_events(self): + """ + This should set the path for each of the players before setting + their speed. + """ + pass # your code here + + def check_collisions(self): + """ + This should check whether the ball collided with the goal, the + bounding lines, or a player's paddle + + If the ball collided with a goal, then set self.curstate to + self.WINSTATE, and set self.winner to player 1 if the ball + hit goal 2 or player 2 if the ball hit goal 1 + + Note: use the ball's methods (ie collide_paddle or collide_line) + """ + pass # your code here + + def display_winning_text(self): + self.screen.fill(BLACK) + + self.font = pygame.font.SysFont(pygame.font.get_default_font(), 32) + if self.winning_player != 0: + font_img = self.font.render( + "Game Over. Player %s won" % str(self.winning_player), + True, + WHITE, + ) + else: + # this won't actually be seen, but it prevents "Player 0 won" from showing + # up on the screen for a split-second if QUIT was pressed before someone won + font_img = self.font.render("Nobody won", True, WHITE) + font_rect = font_img.get_rect() + pygame.draw.rect(self.screen, BLACK, font_rect, 1) + self.screen.blit(font_img, font_rect) + pygame.display.update() # show the new text. + + +our_game = Hockey() +our_game.mainloop() diff --git a/games/chapter4/solutions/flappy_bird/.DS_Store b/games/chapter4/solutions/flappy_bird/.DS_Store new file mode 100644 index 00000000..5008ddfc Binary files /dev/null and b/games/chapter4/solutions/flappy_bird/.DS_Store differ diff --git a/games/chapter4/solutions/flappy_bird/OOPflappybird.py b/games/chapter4/solutions/flappy_bird/OOPflappybird.py new file mode 100644 index 00000000..3df29777 --- /dev/null +++ b/games/chapter4/solutions/flappy_bird/OOPflappybird.py @@ -0,0 +1,500 @@ +import pygame +import random + +pygame.init() + +# screen +width = 800 +height = 600 +SIZE = (width, height) +screen = pygame.display.set_mode(SIZE) + +# colors +LGREEN = (62, 245, 59) +DGREEN = (40, 143, 39) +YELLOW = (250, 250, 37) +WHITE = (255, 255, 255) +BLACK = (0, 0, 0) +RED = (255, 0, 0) +LILAC = (175, 95, 237) +LBLUE = (80, 221, 242) +DBLUE = (80, 99, 242) +PINK = (245, 144, 188) +CYAN = (0, 150, 150) + +# images +BACKGROUNDIMG = pygame.image.load("./background.png") +BACKGROUNDIMG = pygame.transform.scale(BACKGROUNDIMG, (width, height)) +SPRITESHEET = pygame.image.load("./flyingbird.png") +COINPIC = pygame.image.load("./coin.png") + +# ---------- States of the Game ---------- +MENUSTATE = 0 # Menu Screen +GAMESTATE = 1 # Play Game +LOSESTATE = 2 # u loose +QUITSTATE = 3 +NUMSTATES = 4 + + +class GameObj: + """ + An abstract class used as the base class for all the + game's objects + """ + + def __init__(self): + """ + This __init__ method provides no functionality. + It merely enables the methods defined in this class. + Thus, calling super().__init__ is unnecessary. + """ + self.rect = pygame.Rect + + def draw( + self, + screen: pygame.Surface, + color: tuple, + specific_rect: pygame.Rect = None, + ): + """ + Draws a rectangle onto the screen in the specified color. + If specific_rect is not None, draw specific_rect onto the screen. + If specific_rect is None, draw self.rect onto the screen. + """ + if not specific_rect: + pygame.draw.rect(screen, color, self.rect) + else: + pygame.draw.rect(screen, color, specific_rect) + + def move(self, speed: dict = None, specific_rect: pygame.Rect = None): + """ + Moves a rectangle. + @param speed - The speed to move the rectangle at. It should be + a dictionary of form {'x': int, 'y': int}; for example, + {'x':33, 'y':-22}. If no speed is provided, uses self.speed + @param specific_rect - if specific_rect is None, then this method + will move self.rect. If specific_rect is not None, then this method will + move specific_rect + """ + if not speed and hasattr(self, "speed"): + if specific_rect: + return specific_rect.move(self.speed["x"], self.speed["y"]) + else: + self.rect = self.rect.move(self.speed["x"], self.speed["y"]) + if speed: + if specific_rect: + return specific_rect.move(speed["x"], speed["y"]) + else: + self.rect = self.rect.move(speed["x"], speed["y"]) + + def check_collision(self, other, specific_rect: pygame.Rect = None): + if not specific_rect: + return self.rect.colliderect(other.rect) == 1 + else: + return specific_rect.colliderect(other.rect) == 1 + + +class Tubes(GameObj): + """ + Class to represent the two tubes. + + Ex: + The tubes will look sort of like the below drawing + (one on the top, one on the bottom) + (let - be top or bottom of school) + ------------ + | | + |_| + + _ + | | + | | + | | + ------------ + """ + + TUBEGAP = 230 # smaller TUBEGAP -> smaller dist between tubes + TUBEWIDTH = 100 + + def __init__(self, bottom_tube_height: int): + """ + Initializes two pygame.Rect objects: one for the + top tube (call it top_tube) and one for the bottom tube + (call it bottom_tube). Uses the TUBEGAP + and TUBEWIDTH variables as dimensions. + """ + self.bottom_tube = pygame.Rect( + width, + height - bottom_tube_height, + self.TUBEWIDTH, + bottom_tube_height, + ) + self.top_tube = pygame.Rect( + width, + 0, + self.TUBEWIDTH, + height - bottom_tube_height - self.TUBEGAP, + ) + + def draw(self, screen: pygame.Surface): + """ + Uses the draw() method from the inherited + GameObj class to draw the top and bottom tubes. + Hint: this will use the specific_rect argument + """ + super().draw(screen, DGREEN, self.bottom_tube) + super().draw(screen, DGREEN, self.top_tube) + + def move(self, speed: dict): + """ + Uses the move() method from the inherited + GameObj class to move the specific top and bottom tubes. + Hint: this will use the specific_rect argument + """ + self.bottom_tube = super().move(speed, self.bottom_tube) + self.top_tube = super().move(speed, self.top_tube) + + def check_collision(self, other) -> bool: + """ + Uses the check_collision() method from the inherited + GameObj class to check for any collisions + between the given object and the tubes. + Hint: this will use the specific_rect argument + + Returns: + boolean - if either tube is collided with, return True + """ + bottom = super().check_collision(other, self.bottom_tube) + top = super().check_collision(other, self.top_tube) + return bottom or top + + +class Coin(GameObj): + """ + The coin that the bird will get + in-between tubes. + Doesn't need to do anything, so pretty short class. + """ + + def __init__(self, center_y): + """ + Makes a coin object. + The coin's x coordinate will be the width of the screen + The coin's y coordinate will be centered around `center_y` + @param center_y:int - the y coordinate to center the coin around + """ + temprect = COINPIC.get_rect() + self.rect = pygame.Rect( + width, + center_y - temprect.height // 2, + temprect.width, + temprect.height, + ) + + def draw(self, screen: pygame.Surface): + super().draw(screen, BLACK) + + def blit(self, screen: pygame.Surface): + screen.blit(COINPIC, self.rect) + + +class Bird(GameObj): + """ + The bird itself. It processes the sprites + and handles jumping. + """ + + start_center_pos = (width // 8, height // 2) + + def __init__(self): + self.process_spritesheet(SPRITESHEET, 3, 3) + self.rect = pygame.Rect( + self.start_center_pos[0] - self.sprite_frame_width // 2, + self.start_center_pos[1] - self.sprite_frame_height // 2, + self.sprite_frame_width, + self.sprite_frame_height, + ) + self.momentum = 0 # the bird's current vertical speed + self.jump_height = 15 + self.min_speed = -10 # the maximum speed the bird flies down at + self.cur_sprite_idx = 0 + + def process_spritesheet( + self, + spritesheet: pygame.Surface, + num_pics_x: int, + num_pics_y: int, + offset_x: int = 0, + offset_y: int = 0, + ): + """ + Creates sprites from the spritesheet. + @param spritesheet: pygame.Surface - the spritesheet. + @param num_pics_x: int - the number of sprites in each row on + the spritesheet + @param num_pics_y: int - the number of sprites in each column + on the spritesheet + @param offset_x: int - the x offset before the sprite rows start + @param offset_y: int - the y offset before the sprite columns start + """ + self.sprites = [] + self.sprite_frame_width = ( + spritesheet.get_width() - offset_x + ) // num_pics_x + self.sprite_frame_height = ( + spritesheet.get_height() - offset_y + ) // num_pics_y + for row in range(num_pics_x): + for column in range(num_pics_y): + temp = spritesheet.subsurface( + ( + row * self.sprite_frame_width + offset_x, + column * self.sprite_frame_height + offset_y, + self.sprite_frame_width, + self.sprite_frame_height, + ) + ) + # get the bounding box for the actual colored pixels + # (so that we won't be blit-ing extra empty pixels) + # (makes collisions more accurate) + temprect = temp.get_bounding_rect() + # then, append the shortened image to the sprites list + self.sprites.append(temp.subsurface(temprect)) + + def draw(self, screen: pygame.Surface, framecount: int): + curr_sprite_idx = framecount // 5 % len(self.sprites) + if curr_sprite_idx != self.cur_sprite_idx: + # if it is now a different sprite, adjust self.rect + # so that it won't be bigger or smaller than the new sprite + self.cur_sprite_idx = curr_sprite_idx + temp = self.sprites[self.cur_sprite_idx] + self.rect = temp.get_rect().move( + self.rect.topleft[0], self.rect.topleft[1] + ) + super().draw(screen, BLACK) + + def blit(self, screen: pygame.Surface): + screen.blit(self.sprites[self.cur_sprite_idx], self.rect) + + def process_movement(self, event): + if event.type == pygame.KEYDOWN and event.key == pygame.K_UP: + self.momentum = self.jump_height + + def move(self): + super().move({"x": 0, "y": -self.momentum}) + self.momentum -= 1 + + # if the bird would fly down faster than self.min_speed, + # cap self.momentum at self.min_speed + if self.momentum < self.min_speed: + self.momentum = self.min_speed + + +class Button(GameObj): + """ + A Button with text. Used for the + 'Quit Game' 'Start Game' and 'Retry' buttons + """ + + def __init__( + self, + center_x: int, + center_y: int, + bgcolor: tuple, + textcolor: tuple, + text: str = "", + textsize: int = 32, + ): + """ + Creates a Button object. + @param center_x: int - the x coordinate of the button's center + @param center_y: int - the y coordinate of the button's center + @param bgcolor: tuple - the color for the button's background + @param textcolor: tuple - the color for the button's text + @param text: str - the text to put inside the button + @param textsize: int - the size of the button's text + """ + self.font = pygame.font.SysFont("arial", textsize) + self.font_img = self.font.render(text, True, textcolor) + self.rect = self.font_img.get_rect() + self.rect.center = (center_x, center_y) + self.bgcolor = bgcolor + self.active = True + + def draw(self, screen: pygame.Surface): + super().draw(screen, self.bgcolor) + screen.blit(self.font_img, self.rect) + + def is_clicked(self, event: pygame.event.Event): + if event.type == pygame.MOUSEBUTTONDOWN: + return event.pos in self + + def __contains__(self, coordinate): + return self.rect.contains((coordinate[0], coordinate[1], 0, 0)) + + +class FlappyBird: + """ + This is the game class. + """ + + def __init__(self): + self.running = True + self.gamestate = MENUSTATE + self.create_buttons() + self.framecount = 0 + self.clock = pygame.time.Clock() + + def create_buttons(self, button1text="Start Game", button2bg=RED): + self.buttons = { + "start": Button( + width // 2, height // 4, LGREEN, LILAC, button1text + ), + "quit": Button( + width // 2, height // 4 * 3, button2bg, LILAC, "Quit Game" + ), + } + + def mainloop(self): + while self.running: + events = pygame.event.get() + for event in events: + self.set_state(event) + if event.type == pygame.QUIT: + self.running = False + if self.gamestate == MENUSTATE: + screen.fill(LBLUE) + self.draw_buttons(screen) + + elif self.gamestate == GAMESTATE: + self.draw_all() + self.check_collisions() + + # update bird's speed + for event in events: + self.bird.process_movement(event) + self.moveobjects() + + self.create_tubes() + + elif self.gamestate == LOSESTATE: + screen.fill(RED) + self.draw_buttons(screen) + + elif self.gamestate == QUITSTATE: + self.running = False + + self.framecount += 1 + pygame.display.update() + self.clock.tick(60) + pygame.quit() + + def set_state(self, event): + if event.type == pygame.MOUSEBUTTONDOWN: + if all([but.active for but in self.buttons.values()]): + if self.buttons["start"].is_clicked(event): + self.buttons["start"].active = False + self.buttons["quit"].active = False + self.startgame() + elif self.buttons["quit"].is_clicked(event): + self.gamestate = QUITSTATE + self.buttons["start"].active = False + self.buttons["quit"].active = False + + def draw_background(self): + screen.blit(BACKGROUNDIMG, (self.background_x, 0)) + screen.blit(BACKGROUNDIMG, (self.background_x + width, 0)) + self.background_x -= 2 + if self.background_x < -1 * width: + self.background_x = 0 + + def draw_score(self): + """ + Writes the player's score onto the screen in the top + right corner. + Hint: this uses pygame fonts + """ + font = pygame.font.SysFont("arial", 32) + font_img = font.render(f"Score : {self.score}", True, WHITE) + screen.blit(font_img, font_img.get_rect().move(width - 200, 0)) + + def draw_buttons(self, screen): + """ + Draws the "start" and "quit" buttons onto the screen. + Hint: this uses `self.buttons` (which is already made) + """ + self.buttons["start"].draw(screen) + self.buttons["quit"].draw(screen) + + def create_tubes(self): + """ + Creates tubes and puts a coin in the middle of each tube. + """ + if ( + len(self.tubes) == 0 + or self.tubes[-1].bottom_tube.right < width - 200 + ): + bottom_tube_height = random.randint(0, height - Tubes.TUBEGAP) + self.tubes.append(Tubes(bottom_tube_height)) + self.coins.append( + Coin(height - bottom_tube_height - (Tubes.TUBEGAP // 2)) + ) + + def draw_all(self): + # draw bird and coin rectangles before background so that they won't + # show + self.bird.draw(screen, self.framecount) + for coin in self.coins: + coin.draw(screen) + + self.draw_background() + + for tube in self.tubes: + tube.draw(screen) + + # blit images/sprites onto the screen + self.bird.blit(screen) + for coin in self.coins: + coin.blit(screen) + + self.draw_score() + + def check_collisions(self): + for tube in self.tubes: + if tube.check_collision(self.bird): + self.gamestate = LOSESTATE + self.create_buttons("Retry?", LBLUE) + if tube.bottom_tube.right < 0: + self.tubes.remove(tube) + + for coin in self.coins: + if self.bird.check_collision(coin): + self.score += 1 + self.coins.remove(coin) + if coin.rect.right < 0: + self.coins.remove(coin) + + if self.bird.rect.bottom > height: # fell out of screen + self.gamestate = LOSESTATE + self.create_buttons("Retry?", LBLUE) + + def moveobjects(self): + SPEED = 3 # x speed that objects move towards the bird at + for tube in self.tubes: + tube.move({"x": -SPEED, "y": 0}) + for coin in self.coins: + coin.move({"x": -SPEED, "y": 0}) + self.bird.move() + + def startgame(self): + self.gamestate = GAMESTATE + self.background_x = 0 + self.score = 0 + self.bird = Bird() + self.tubes = [] + self.coins = [] + self.create_tubes() + + +a = FlappyBird() +a.mainloop() diff --git a/games/chapter4/solutions/flappy_bird/background.png b/games/chapter4/solutions/flappy_bird/background.png new file mode 100644 index 00000000..0be8c233 Binary files /dev/null and b/games/chapter4/solutions/flappy_bird/background.png differ diff --git a/games/chapter4/solutions/flappy_bird/bird.png b/games/chapter4/solutions/flappy_bird/bird.png new file mode 100644 index 00000000..2b8a5b32 Binary files /dev/null and b/games/chapter4/solutions/flappy_bird/bird.png differ diff --git a/games/chapter4/solutions/flappy_bird/coin.png b/games/chapter4/solutions/flappy_bird/coin.png new file mode 100644 index 00000000..dc457bff Binary files /dev/null and b/games/chapter4/solutions/flappy_bird/coin.png differ diff --git a/games/chapter4/solutions/flappy_bird/explosion_transparent.png b/games/chapter4/solutions/flappy_bird/explosion_transparent.png new file mode 100644 index 00000000..a92d7099 Binary files /dev/null and b/games/chapter4/solutions/flappy_bird/explosion_transparent.png differ diff --git a/games/chapter4/solutions/flappy_bird/flyingbird.png b/games/chapter4/solutions/flappy_bird/flyingbird.png new file mode 100644 index 00000000..25bd824a Binary files /dev/null and b/games/chapter4/solutions/flappy_bird/flyingbird.png differ diff --git a/games/chapter4/solutions/hockey.py b/games/chapter4/solutions/hockey.py new file mode 100644 index 00000000..ab4dd66f --- /dev/null +++ b/games/chapter4/solutions/hockey.py @@ -0,0 +1,606 @@ +# Make a two-player hockey game! The application will consist +# of two rectangular paddles, starting on each side of the screen, +# and one circular ball that players must bounce around. Players can +# move the paddles in any direction to hit the ball into the goal. +# If the ball makes contact with safe parts of the screen, it will +# bounce off at a random angle but in the same general direction +# (left or right). It will do the same if it makes contact with one +# of the paddles, but will head towards the opposite general direction +# instead. If the ball touches the goals on either side of the screen, +# the application will say “Game Over. Player _ Wins”. You must put +# your code in classes and have separate keys for each player to +# move their paddles. + + +import pygame +from pygame.locals import ( + K_w, + K_s, + K_a, + K_d, + K_UP, + K_DOWN, + K_LEFT, + K_RIGHT, + KEYDOWN, + KEYUP, + QUIT, + RESIZABLE, +) +from pygame.rect import Rect + +import math +import time +import random + +# define the necessary color constants using rgb values +BLACK = (0, 0, 0) +GREEN = (0, 120, 0) +RED = (120, 0, 0) +WHITE = (255, 255, 255) + +# define player controls +PLAYER1CONTROLS = {"up": K_w, "down": K_s, "left": K_a, "right": K_d} +PLAYER2CONTROLS = { + "up": K_UP, + "down": K_DOWN, + "left": K_LEFT, + "right": K_RIGHT, +} + +# initial screensize +SCREENSIZE = [900, 600] + +# how big the ball's radius will be +BALL_RADIUS = 3 + + +class Game_obj: + def __init__(self): + # we don't want to pass actual values to the Rect class since + # we want this class to be abstract + self.rect = Rect + self.prev_rect = Rect + self.speed = {"x": 0, "y": 0} + + def move(self): + self.prev_rect = self.rect + self.rect = self.rect.move(self.speed["x"], self.speed["y"]) + + def move_to(self, position: tuple): + self.prev_rect = self.rect + self.rect = self.rect.move( + position[0] - self.rect.topleft[0], + position[1] - self.rect.topleft[1], + ) + + def check_collision(self, other) -> bool: + return self.rect.colliderect(other.rect) == 1 + + +class Player(Game_obj): + PLAYERSPEED = (3, 3) + PADDLESIZE = (10, 50) # x width, y width + + def __init__(self, control_keys: dict) -> None: + """ + Creates a player rectangle with the provided control keys. + Arguments: + control_keys - (dict) should be a dictionary of the following format: + { + "up": (KEY) (ex: K_w), + "down": (KEY) (ex: K_s), + "left": (KEY) (ex: K_a), + "right": (KEY) (ex: K_d) + } + """ + super().__init__() + self.control_keys = control_keys + self.rect = Rect(0, 0, self.PADDLESIZE[0], self.PADDLESIZE[1]) + self.path = [0, 0] + + def draw(self, screen: pygame.Surface): + pygame.draw.rect(screen, GREEN, self.rect) + + def set_path(self, event): + self.key_checker(event, "up") + self.key_checker(event, "down") + self.key_checker(event, "left") + self.key_checker(event, "right") + + def key_checker(self, event: pygame.event.Event, direction: str) -> None: + """ + Helper function to deal with event keys. Sets self.path + according to PATH_VALUES + Arguments: + event(pygame.event.Event) - the event + direction(str) - the direction to check KEYDOWN and KEYUP for. + """ + PATH_VALUES = {"up": 1, "down": 1, "left": 0, "right": 0} + DIRECTION_VALUES = {"up": -1, "down": 1, "left": -1, "right": 1} + + # if the event doesn't have a key attribute, just return + if not hasattr(event, "key"): + return + + # if it does, then check if it the right key + if event.key == self.control_keys[direction]: + if event.type == KEYUP: + self.path[PATH_VALUES[direction]] += -DIRECTION_VALUES[ + direction + ] + if event.type == KEYDOWN: + self.path[PATH_VALUES[direction]] += DIRECTION_VALUES[ + direction + ] + + def set_speed(self) -> None: + """ + Sets the speed according to the Player object's path. + This should be called after self.path has been set. + """ + self.speed["x"] = ( + self.path[0] + / math.sqrt(sum(abs(num) for num in self.path)) + * self.PLAYERSPEED[0] + if (sum(abs(num) for num in self.path)) != 0 + else self.path[0] * self.PLAYERSPEED[0] + ) + self.speed["y"] = ( + self.path[1] + / math.sqrt(sum(abs(num) for num in self.path)) + * self.PLAYERSPEED[1] + if (sum(abs(num) for num in self.path)) != 0 + else self.path[1] * self.PLAYERSPEED[1] + ) + + +class Ball(Game_obj): + BALLSPEED = (6, 6) + + def __init__(self, radius: int) -> None: + super().__init__() + self.rect = Rect(0, 0, radius * 2, radius * 2) + self.radius = radius + + # set up initial speed + initial_ang = random.randint(1, int(math.pi / 2 * 100)) / 100 + self.speed["x"] = ( + math.cos(initial_ang) + * self.BALLSPEED[0] + * (-1 if random.randint(0, 1) == 0 else 1) + ) + self.speed["y"] = ( + math.sin(initial_ang) + * self.BALLSPEED[1] + * (-1 if random.randint(0, 1) == 0 else 1) + ) + + def draw(self, screen: pygame.Surface): + pygame.draw.circle( + screen, WHITE, center=self.rect.center, radius=self.radius + ) + + def collide_line(self, other) -> bool: + """ + Checks if the ball has hit a line. + If it did, update the speed accordingly + + Arguments: + other (BoundingLine or Goal) - the line to check for a collision with + Returns: + True - if the collision happened + False - if the collision didn't happen + """ + if self.check_collision(other): + if other.name == "top" or other.name == "bottom": + self.speed["y"] = -self.speed["y"] + return True + elif other.name == "left" or other.name == "right": + self.speed["x"] = -self.speed["x"] + return True + return False + + def get_obj_path(self, object: Game_obj) -> tuple: + if object.speed["x"] > 0: + x_path = 1 + elif object.speed["x"] < 0: + x_path = -1 + else: + x_path = 0 + + if object.speed["y"] > 0: + y_path = 1 + elif object.speed["y"] < 0: + y_path = -1 + else: + y_path = 0 + + return (x_path, y_path) + + def get_paddle_collision_dir(self, paddle: Player) -> tuple: + """ + Gets the direction in which the ball will be headed + after a collision with a paddle. + Does not actually check if the collision happened + + Arguments: + paddle (Player) - the player that the ball 'collided with' + Returns: + tuple(int, int) - a tuple of length 2 with just +-1's + ex: (1, 1) or (1, -1) or (-1, 1), or (-1, -1) + It corresponds to the direction in which the ball + will be headed. The first item will be the x direction + and the second item will be the y direction. + """ + paddle_x_dir, paddle_y_dir = self.get_obj_path(paddle) + + ball_x_dir, ball_y_dir = self.get_obj_path(self) + + resulting_x_dir = None + resulting_y_dir = None + + if paddle.speed["x"] == 0 or paddle_x_dir == ball_x_dir: + if abs(paddle.speed["x"]) > abs(self.speed["x"]): + resulting_x_dir = paddle_x_dir + elif abs(paddle.speed["x"]) < abs(self.speed["x"]): + resulting_x_dir = -ball_x_dir + else: + resulting_x_dir = -ball_x_dir + + if paddle.speed["y"] == 0 or paddle_y_dir == ball_y_dir: + if abs(paddle.speed["y"]) > abs(self.speed["y"]): + resulting_y_dir = paddle_y_dir + elif abs(paddle.speed["y"]) < abs(self.speed["y"]): + resulting_y_dir = -ball_y_dir + else: + resulting_y_dir = -ball_y_dir + + return (resulting_x_dir, resulting_y_dir) + + def collide_paddle(self, paddle: Player, executions: int) -> None: + """ + Handles collisions with paddles. + Arguments: + paddle(Player) - the paddle to check for a collision with + executions(int) - the amount of executions of the game's mainloop + It's not important, but it prevents unwanted collisions during + the 0th execution when we first set up the game by moving + the objects to the right place + """ + PROPORTION = 0.25 # used when "escaping" a collision + MINIMUM_ANGLE = ( + 30 # this is in degrees; it's just a fine-tuning aspect + ) + # that makes the game more realistic + + resulting_x_dir = None + resulting_y_dir = None + + a = self.trace_collisions(paddle) + if a[0] and executions != 0: + resulting_x_dir, resulting_y_dir = a[1] + + if self.check_collision(paddle): + resulting_x_dir, resulting_y_dir = self.get_paddle_collision_dir( + paddle + ) + + # if resulting_x_dir and resulting_y_dir aren't None, then update ball speed + if resulting_x_dir and resulting_y_dir: + print(MINIMUM_ANGLE * math.pi / 180 * 100) + print(math.pi / 2 * 100) + angle = ( + random.randint( + 0, + int( + math.pi / 2 * 100 + - (MINIMUM_ANGLE * math.pi / 180 * 100) + ), + ) + / 100 + ) + + print("angle", angle) + + self.speed["x"] = ( + math.cos(angle) * self.BALLSPEED[0] * resulting_x_dir + ) + self.speed["y"] = ( + math.sin(angle) * self.BALLSPEED[1] * resulting_y_dir + ) + + # escape the collision so as to prevent the "same" collision from being + # handled when collide_paddle is called next time. + while self.check_collision(paddle): + self.move_to( + ( + self.rect.topleft[0] + PROPORTION * self.speed["x"], + self.rect.topleft[1] + PROPORTION * self.speed["y"], + ) + ) + + def trace_collisions(self, paddle): + COLLISIONS_TO_CHECK = 30 # the higher this is, the slower. + + # find how much the ball moved during the past execution + delta_x = self.rect.topleft[0] - self.prev_rect.topleft[0] + delta_y = self.rect.topleft[1] - self.prev_rect.topleft[1] + + # find how much the paddle moved during the past execution + paddle_delta_x = paddle.rect.topleft[0] - paddle.prev_rect.topleft[0] + paddle_delta_y = paddle.rect.topleft[1] - paddle.prev_rect.topleft[1] + + # check COLLISIONS_TO_CHECK times for a collision that occurred during + # the "update game" phase (when we moved the objects) + for i in range(COLLISIONS_TO_CHECK): + # move both the ball and the paddle/player to where they would've been if we + # subdivided the move phase into COLLISIONS_TO_CHECK individual frames + ball_past = Ball(BALL_RADIUS) + ball_past.move_to( + ( + self.prev_rect.topleft[0] + + (delta_x * i / COLLISIONS_TO_CHECK), + self.prev_rect.topleft[1] + + (delta_y * i / COLLISIONS_TO_CHECK), + ) + ) + paddle_past = Player({}) + paddle_past.move_to( + ( + paddle.prev_rect.topleft[0] + + (paddle_delta_x * i / COLLISIONS_TO_CHECK), + paddle.prev_rect.topleft[1] + + (paddle_delta_y * i / COLLISIONS_TO_CHECK), + ) + ) + + # now that we have a ball and a paddle, check if they collided + if ball_past.check_collision(paddle_past): + return (True, ball_past.get_paddle_collision_dir(paddle_past)) + # if the loop failed (didn't return), then + # return False and an empty tuple + return (False, tuple()) + + +class BoundingLine: + DEFAULT_SIZE = 3 + + def __init__( + self, + start_coord: tuple, + end_coord: tuple, + name: str, + default_size=None, + ): + if default_size: # if a default size is provided + self.DEFAULT_SIZE = default_size + + self.start_coord = start_coord # starting coordinate, normally topleft + self.end_coord = end_coord # ending coordinate, normally bottomright + + self.rect = Rect( + start_coord[0], + start_coord[1], + end_coord[0] + if end_coord[0] - start_coord[0] != 0 + else self.DEFAULT_SIZE, + end_coord[1] + if end_coord[1] - start_coord[1] != 0 + else self.DEFAULT_SIZE, + ) + + self.name = name # this comes in handy in Ball's collide_line method + + # small fix for bottom and right bounding lines getting drawn outside of + # the screen + if self.name == "bottom" and self.rect.bottom > SCREENSIZE[1]: + self.rect = self.rect.move(0, -3) + elif self.name == "right" and self.rect.right > SCREENSIZE[0]: + self.rect = self.rect.move(-3, 0) + + def draw(self, screen: pygame.Surface, color=RED): + pygame.draw.rect(screen, color, self.rect) + + +class Goal(BoundingLine): + def draw(self, screen: pygame.Surface): + super().draw( + screen, WHITE + ) # the goal should be in white so you can see it + + +class App: + def __init__( + self, flags=RESIZABLE, width=900, height=600, title="My game" + ): + pygame.init() + self.size = [width, height] + self.screen = pygame.display.set_mode(self.size, flags) + pygame.display.set_caption(title, title) + self.running = True + + self.GAMESTATE = 0 + self.WONSTATE = 1 + self.QUITSTATE = 2 + + self.currstate = self.GAMESTATE + self.winning_player = 0 # will be 1 or 2 when a player won + + self.executions = 0 # useful for debugging + + def mainloop(self): + while self.running: + # main game loop (for the game itself) + # because this is a while loop, the game will keep going until someone won + # so we don't need to worry about the post-game text being displayed + if self.currstate == self.GAMESTATE: + for event in pygame.event.get(): + if event.type == QUIT: + # set the variables that are keeping the game running + # to values that won't keep the game running + self.running = False + self.currstate = self.QUITSTATE + else: + self.check_events(event) + self.check_collisions() + self.move_objects() + self.update_display() + pygame.display.update() + time.sleep(0.01) + self.executions += 1 + + if self.currstate == self.WONSTATE: + # 'post-game' game loop (just shows winning text) + for event in pygame.event.get(): + if event.type == QUIT: + self.running = False + self.currstate = self.QUITSTATE + self.display_winning_text() + + if self.currstate == self.QUITSTATE: + pygame.quit() + + def check_events(self, event) -> None: + pass + + def check_collisions(self) -> None: + pass + + def move_objects(self) -> None: + pass + + def update_display(self) -> None: + pass + + def display_winning_text(self) -> None: + pass + + +class Hockey(App): + def __init__(self): + super().__init__(title="Hockey!") + + # initialize players + self.player_1 = Player(PLAYER1CONTROLS) + self.player_2 = Player(PLAYER2CONTROLS) + + # move players to starting positions + self.player_1.move_to( + ( + self.size[0] / 8 - self.player_1.rect.width, + self.size[1] / 2 - self.player_1.rect.height, + ) + ) + self.player_2.move_to( + ( + self.size[0] / 8 * 7 - self.player_2.rect.width, + self.size[1] / 2 - self.player_2.rect.height, + ) + ) + + # initialize ball and move it to starting position (center) + self.ball = Ball(BALL_RADIUS) + self.ball.move_to( + ( + self.size[0] / 2 - self.ball.rect.width, + self.size[1] / 2 - self.ball.rect.height, + ) + ) + + # initialize bounding lines - the edges of the screen off which the + # ball should bounce + self.top_line = BoundingLine((0, 0), (self.size[0], 0), "top") + self.bottom_line = BoundingLine( + (0, self.size[1]), (self.size[0], self.size[1]), "bottom" + ) + self.left_line = BoundingLine((0, 0), (0, self.size[1]), "left") + self.right_line = BoundingLine( + (self.size[0], 0), (self.size[0], self.size[1]), "right" + ) + self.bounding_lines = [ + self.top_line, + self.bottom_line, + self.left_line, + self.right_line, + ] + + # initialize Goals + self.goal_1 = Goal( + (0, (self.size[1] / 2) - (5 * self.size[1] / 16)), + (0, (self.size[1] / 2) + (self.size[1] / 16)), + "left", + 3, + ) + self.goal_2 = Goal( + (self.size[0], (self.size[1] / 2) - (5 * self.size[1] / 16)), + (self.size[0], (self.size[1] / 2) + (self.size[1] / 16)), + "right", + 3, + ) + + def update_display(self) -> None: + self.screen.fill(BLACK) # fill the screen with black to clear it + + # draw main objects + self.player_1.draw(self.screen) + self.player_2.draw(self.screen) + self.ball.draw(self.screen) + + # draw bounding lines + self.top_line.draw(self.screen) + self.bottom_line.draw(self.screen) + self.left_line.draw(self.screen) + self.right_line.draw(self.screen) + + # draw goals + self.goal_1.draw(self.screen) + self.goal_2.draw(self.screen) + + def move_objects(self) -> None: + self.player_1.move() + self.player_2.move() + self.ball.move() + + def check_events(self, event) -> None: + self.player_1.set_path(event) + self.player_2.set_path(event) + + self.player_1.set_speed() + self.player_2.set_speed() + + def check_collisions(self) -> None: + for line in self.bounding_lines: + self.ball.collide_line(line) + self.ball.collide_paddle(self.player_1, self.executions) + self.ball.collide_paddle(self.player_2, self.executions) + + if self.ball.collide_line(self.goal_1): + self.currstate = self.WONSTATE + self.winning_player = 2 # player 2 (right player) scored + elif self.ball.collide_line(self.goal_2): + self.currstate = self.WONSTATE + self.winning_player = 1 # player 1 (left player) scored + + def display_winning_text(self) -> None: + self.screen.fill(BLACK) + + self.font = pygame.font.SysFont(pygame.font.get_default_font(), 32) + if self.winning_player != 0: + font_img = self.font.render( + "Game Over. Player %s won" % str(self.winning_player), + True, + WHITE, + ) + else: + # this won't actually be seen, but it prevents "Player 0 won" from showing + # up on the screen for a split-second if QUIT was pressed before someone won + font_img = self.font.render("Nobody won", True, WHITE) + font_rect = font_img.get_rect() + pygame.draw.rect(self.screen, BLACK, font_rect, 1) + self.screen.blit(font_img, font_rect) + pygame.display.update() # show the new text. + + +our_game = Hockey() +our_game.mainloop() diff --git a/games/chapter5/examples/bounce.wav b/games/chapter5/examples/bounce.wav new file mode 100644 index 00000000..44f62bce Binary files /dev/null and b/games/chapter5/examples/bounce.wav differ diff --git a/games/chapter5/examples/bouncing_rect.py b/games/chapter5/examples/bouncing_rect.py new file mode 100644 index 00000000..f838b0b5 --- /dev/null +++ b/games/chapter5/examples/bouncing_rect.py @@ -0,0 +1,47 @@ +import pygame +import time # not necessary, but used for frame cap + +pygame.init() # initialize pygame module + +bounce_sound = pygame.mixer.Sound("./bounce.wav") + +SCREEN_SIZE = (600, 400) +RECT_SIZE = (100, 100) +RED = (255, 0, 0) +BLACK = (0, 0, 0) +momentum = [1, 1] # (down and right) + +window = pygame.display.set_mode(SCREEN_SIZE) +running = True + +# start the rectangle in the middle of the screen +x = SCREEN_SIZE[0] // 2 +y = SCREEN_SIZE[1] // 2 + +while running: + # if the rectangle collided with the left or right side + # of the screen + if x + RECT_SIZE[0] >= SCREEN_SIZE[0] or x <= 0: + momentum[0] = -momentum[0] + bounce_sound.play() # add in the bounce sound for collisions + # if the rectangle collided with the top or bottom + # of the screen + if y + RECT_SIZE[1] >= SCREEN_SIZE[1] or y <= 0: + momentum[1] = -momentum[1] + bounce_sound.play() # add in the bounce sound for collisions + + # add the speed to the current x and y to get the + # new x and y + x += momentum[0] + y += momentum[1] + + window.fill(BLACK) # 'erase' the previous frame + pygame.draw.rect(window, RED, (x, y, RECT_SIZE[0], RECT_SIZE[1])) + pygame.display.update() # update the display + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + time.sleep(0.01) # frame cap to make the rectangle more visible + +pygame.quit() # deactivate the pygame module diff --git a/games/chapter5/practice/TankIcon.png b/games/chapter5/practice/TankIcon.png new file mode 100644 index 00000000..d9e00bbe Binary files /dev/null and b/games/chapter5/practice/TankIcon.png differ diff --git a/games/chapter5/practice/Target.png b/games/chapter5/practice/Target.png new file mode 100644 index 00000000..d48bc78e Binary files /dev/null and b/games/chapter5/practice/Target.png differ diff --git a/games/chapter5/practice/bullet.png b/games/chapter5/practice/bullet.png new file mode 100644 index 00000000..fce8c161 Binary files /dev/null and b/games/chapter5/practice/bullet.png differ diff --git a/games/chapter5/practice/completetank.png b/games/chapter5/practice/completetank.png new file mode 100644 index 00000000..036cd84b Binary files /dev/null and b/games/chapter5/practice/completetank.png differ diff --git a/games/chapter5/practice/explosion.wav b/games/chapter5/practice/explosion.wav new file mode 100644 index 00000000..c33f0412 Binary files /dev/null and b/games/chapter5/practice/explosion.wav differ diff --git a/games/chapter5/practice/fire.wav b/games/chapter5/practice/fire.wav new file mode 100644 index 00000000..17a60459 Binary files /dev/null and b/games/chapter5/practice/fire.wav differ diff --git a/games/chapter5/practice/simple_bg.wav b/games/chapter5/practice/simple_bg.wav new file mode 100644 index 00000000..b09f4e25 Binary files /dev/null and b/games/chapter5/practice/simple_bg.wav differ diff --git a/games/chapter5/practice/sound_tanks.py b/games/chapter5/practice/sound_tanks.py new file mode 100644 index 00000000..e33e7e6a --- /dev/null +++ b/games/chapter5/practice/sound_tanks.py @@ -0,0 +1,468 @@ +# Edit the code from the tank game so that it will: +# play an explosion sound when a target is hit or run over +# play a gunshot sound when a bullet is "fired" (created) +# play background music during the whole game. + +# you can either use the provided .wav files or find +# your own (responsibly, of course). + +# Note: +# because the pygame.init is inside App's init method, we don't put +# sounds here. instead, we make the sounds and the music inside Tankgame's +# init method. + + +import pygame + +from pygame.locals import ( + K_w, + K_s, + K_a, + K_d, + KEYDOWN, + KEYUP, + QUIT, + RESIZABLE, + MOUSEBUTTONDOWN, +) +import time +import math +import random + +BULLET_IMG_PATH = "./bullet.png" +TARGET_IMG_PATH = "./target.png" +TANK_IMG_PATH = "./completetank.png" + +BLACK = (255, 255, 255) +DIRTBROWN = (168, 95, 0) +SANDBROWN = (237, 201, 175) + +TANKSPEED = [2, 2] # speed x and speed y +BULLETSPEED = [8, 8] + + +class Game_obj: + def __init__(self, picture: str, **kwargs) -> None: + """ + A basic game object class. It handles collisions, + the basic drawing method, the move and moveto methods, + and the check_out_of_screen method. + + Arguments: + picture:str - the location of the picture that will be displayed on + the screen for this object + Valid keyword arguments: + "size":tuple(x,y) - a specific size that you want to have the object be. + The picture will be scaled to that size and the hitbox + will be updated accordingly. + "position":tuple(x,y) - the tuple at which the top left of the object + should be positioned at + "speed":tuple(x,y) - the tuple that represents the object's speed. + """ + self.name = "" + + # self.image will be a pygame.Surface class + self.image = pygame.image.load(picture) + self.image = ( + pygame.transform.scale( + self.image, (kwargs["size"][0], kwargs["size"][1]) + ) + if "size" in kwargs + else self.image + ) + + self.rect = ( + self.image.get_rect() + ) # self.rect will be of pygame.Rect class + self.size = self.rect.size # will be a tuple of (sizex, sizey) + + if "position" in kwargs: + self.moveto(kwargs["position"]) + + self.speed = ( + {"x": kwargs["speed"][0], "y": kwargs["speed"][1]} + if "speed" in kwargs + else {"x": 0, "y": 0} + ) + + def check_collision(self, other: object) -> bool: + if not isinstance(other, Game_obj): + raise TypeError( + "Invalid type; need a game_obj or a child class of game_obj" + ) + # the rect class's colliderect method returns 1 if there is + # a collision and 0 if there isn't a collision + return self.rect.colliderect(other.rect) == 1 + + def draw(self, screen: pygame.Surface, color: tuple) -> None: + pygame.draw.rect(screen, color, self.rect, 0) + screen.blit(self.image, self.rect) + + def move(self) -> None: + """ + Moves the object according to it's current speed. + """ + self.rect = self.rect.move(self.speed["x"], self.speed["y"]) + # self.draw(screen, color) + + def set_speed(self, new_speed: tuple) -> None: + """ + Sets the object's speed to the provided tuple + Arguments: + new_speed (tuple(x,y)) - a tuple containing the desired speed for + the object to have. + """ + self.speed["x"], self.speed["y"] = new_speed[0], new_speed[1] + + def moveto(self, position: tuple) -> None: + """ + A helper function that moves the rectangle to the desired position. + + Arguments: + position (tuple) - the x and y coordinates of where you want the rectangle's + top left to be moved to. + """ + self.rect = self.rect.move( + position[0] - self.rect.topleft[0], + position[1] - self.rect.topleft[1], + ) + + def check_out_of_screen(self, screen_size: tuple) -> bool: + """ + Checks whether or not the object is completely outside of the screen. + Returns True or False accordingly. + Arguments: + screen_size (tuple) - the size of the screen (x,y) + """ + if ( + self.rect.bottom > screen_size[1] + or self.rect.top < 0 + or self.rect.left < 0 + or self.rect.right > screen_size[0] + ): + return True + return False + + def __str__(self): + return ( + f"{self.name} object located at the position {self.rect.topleft}" + ) + + +class Bullet(Game_obj): + def __init__(self, **kwargs) -> None: + super().__init__(BULLET_IMG_PATH, **kwargs) + self.name = "Bullet" + + +class Target(Game_obj): + def __init__(self, **kwargs) -> None: + kwargs["size"] = 40, 40 + super().__init__(TARGET_IMG_PATH, **kwargs) + self.name = "Target" + + +class Tank(Game_obj): + def __init__(self, **kwargs) -> None: + super().__init__(TANK_IMG_PATH, **kwargs) + self.direction = [0, 0] + self.SPEED = kwargs["speed"] if "speed" in kwargs else [2, 2] + self.speed["x"], self.speed["y"] = 0, 0 + + def set_speed(self) -> None: + # use math stuff to calculate the speed given that the + # max speed is self.SPEED + self.speed["x"] = ( + self.direction[0] + / math.sqrt(sum(abs(num) for num in self.direction)) + * self.SPEED[0] + if (sum(abs(num) for num in self.direction)) != 0 + else self.direction[0] * self.SPEED[0] + ) + self.speed["y"] = ( + self.direction[1] + / math.sqrt(sum(abs(num) for num in self.direction)) + * self.SPEED[1] + if (sum(abs(num) for num in self.direction)) != 0 + else self.direction[1] * self.SPEED[1] + ) + + def set_path(self, direction: str) -> None: + if direction == "up": + self.direction[1] -= 1 + if direction == "down": + self.direction[1] += 1 + if direction == "left": + self.direction[0] -= 1 + if direction == "right": + self.direction[0] += 1 + + def unset_path(self, direction: str) -> None: + if direction == "up": + self.direction[1] += 1 + if direction == "down": + self.direction[1] -= 1 + if direction == "left": + self.direction[0] += 1 + if direction == "right": + self.direction[0] -= 1 + + +class App: + """ + The abstract base class for the actual Tank_game class. It's + main purpose is to define a structure for the game. + It's structure is as follows: + Upon initialization, it runs the create_objects method + It's mainloop is comprised of the following methods: + check_events + check_collisions + move_objects + update_display + """ + + def __init__( + self, flags=RESIZABLE, width=960, height=540, title="My Game" + ): + pygame.init() + self.size = [width, height] + self.screen = pygame.display.set_mode(self.size, flags) + pygame.display.set_caption(title, title) + + self.running = True + + self.create_objects() + + def create_objects(self): + """ + This should create the initial objects on the screen. + """ + pass + + def check_events(self, event): + """ + This should take user input and handle it appropriately. + """ + pass + + def update_display(self): + """ + This should utilize clear the screen and then draw + all current objects onto the screen. + """ + pass + + def move_objects(self): + """ + This should utilize the move method that the game objects have. + """ + pass + + def check_collisions(self): + """ + This should utilize the check_collision method that the game objects + have. + """ + pass + + def mainloop(self): + while self.running: + for event in pygame.event.get(): + if event.type == QUIT: + self.running = False + break + else: + self.check_events( + event + ) # this will handle checking for user input + # such as KEYUP and MOUSEBUTTONDOWN events needed to run the game + self.check_collisions() # checks collisions between bullet/tank and targets + self.move_objects() # moves each object on the screen + self.update_display() # redraws updated objects onto the screen + pygame.display.update() # pygame’s method to show the updated screen + time.sleep(0.01) # not necessary; it's a frame cap + pygame.quit() + + +class Tank_Game(App): + def __init__(self): + # this can be changed, it's the number of targets allowed at a time. + # we initialize this before super().__init__ because super().__init__ calls + # create_objects, which utilizes self.NUM_TARGETS + self.NUM_TARGETS = 3 + + super().__init__(title="Tanks") + + self.playerscore = 0 # the player's score + + # sets the display icon to the TankIcon.png provided + pygame.display.set_icon(pygame.image.load("./TankIcon.png")) + + def create_objects(self): + """ + This creates the initial objects seen when the game + first starts up. + """ + # tank + self.tank = Tank(speed=TANKSPEED) + self.tank.moveto( + ( + self.size[0] / 2 - self.tank.size[0], # move to middle x + self.size[1] - self.tank.size[1], # move to bottom y + ) + ) + + # targets + self.targets = [Target(speed=[0, 0]) for i in range(self.NUM_TARGETS)] + for target in self.targets: + target.moveto( + ( + random.randint( + 0, self.size[0] - target.size[0] + ), # random x + random.randint( + 0, self.size[1] - target.size[1] + ), # random y + ) + ) + + # bullets + self.bullets = [] + + # Score text + self.font = pygame.font.SysFont(pygame.font.get_default_font(), 32) + + def check_events(self, event): + """ + We imported all from pygame.locals, so that means + that we can check KEYDOWN and KEYUP and individual + keys such as K_w (w key), K_a (a key), etc. + """ + # change the path of the tank if w, a, s, or d was pressed + if event.type == KEYDOWN: + if event.key == K_w: + self.tank.set_path("up") + if event.key == K_s: + self.tank.set_path("down") + if event.key == K_a: + self.tank.set_path("left") + if event.key == K_d: + self.tank.set_path("right") + if event.type == KEYUP: + if event.key == K_w: + self.tank.unset_path("up") + if event.key == K_s: + self.tank.unset_path("down") + if event.key == K_a: + self.tank.unset_path("left") + if event.key == K_d: + self.tank.unset_path("right") + self.tank.set_speed() + + # create bullets if mouse button was pressed + if event.type == MOUSEBUTTONDOWN: + bul = Bullet(speed=BULLETSPEED) + bul.moveto( + (self.tank.rect.centerx, (self.tank.rect.top - bul.size[1])) + ) # move the bullet to the front of the tank + + # math stuff to calculate trajectory + mouse_pos = pygame.mouse.get_pos() + h = mouse_pos[1] - bul.rect.center[1] + w = mouse_pos[0] - bul.rect.center[0] + hyp = math.sqrt(h**2 + w**2) + vertical_speed = ( + BULLETSPEED[1] * (h / hyp) if hyp != 0 else BULLETSPEED[1] * h + ) + horizontal_speed = ( + BULLETSPEED[0] * (w / hyp) if hyp != 0 else BULLETSPEED[0] * w + ) + + bul.set_speed((horizontal_speed, vertical_speed)) + self.bullets.append(bul) + + def move_objects(self): + """ + This method moves the objects within the game. + If a bullet is outside of the screen, it is + not moved and is unreferenced. + """ + self.tank.move() + + self.bullets = [ + bullet + for bullet in self.bullets + if bullet.check_out_of_screen(self.size) is False + ] + + for bullet in self.bullets: + bullet.move() + + def check_collisions(self): + """ + This checks whether any of the objects within the game have collided + with each other. Specifically, we are looking for collisions between + bullets and targets or the tank and targets + """ + deletions = 0 # number of targets deleted + num_bullets = len(self.bullets) + + # check bullet-target collisions + for i in range(num_bullets): + for target in self.targets: + # if the bullet collided with the target + if self.bullets[i - deletions].check_collision(target) is True: + # pop both the bullet and target so that they will be + # effectively deleted + self.bullets.pop(i - deletions) + self.targets.pop(self.targets.index(target)) + + # give points for hitting the target + self.playerscore += 20 + deletions += 1 + break # stop the current iteration since the target and + # bullet are popped, so referencing them would error. + + # check tank-target collisions + for target in self.targets: + if self.tank.check_collision(target) is True: + self.targets.pop(self.targets.index(target)) + deletions += 1 + self.playerscore += 10 # only 10 for running over targets lol + + # create a new target for every deleted target + for i in range(deletions): + a = Target(speed=[0, 0]) + a.moveto( + ( + random.randint(0, self.size[0] - a.size[0]), + random.randint(0, self.size[1] - a.size[1]), + ) + ) + self.targets.append(a) + + def update_display(self): + self.screen.fill(SANDBROWN) + + # tank + self.tank.draw(self.screen, SANDBROWN) + + # targets + for target in self.targets: + target.draw(self.screen, BLACK) + + # bullets + for bullet in self.bullets: + bullet.draw(self.screen, BLACK) + + # score text + font_img = self.font.render( + "Score: %s" % str(self.playerscore), True, BLACK + ) + font_rect = font_img.get_rect() + pygame.draw.rect(self.screen, SANDBROWN, font_rect, 1) + self.screen.blit(font_img, font_rect) + + +game = Tank_Game() +game.mainloop() diff --git a/games/chapter5/solutions/TankIcon.png b/games/chapter5/solutions/TankIcon.png new file mode 100644 index 00000000..d9e00bbe Binary files /dev/null and b/games/chapter5/solutions/TankIcon.png differ diff --git a/games/chapter5/solutions/Target.png b/games/chapter5/solutions/Target.png new file mode 100644 index 00000000..d48bc78e Binary files /dev/null and b/games/chapter5/solutions/Target.png differ diff --git a/games/chapter5/solutions/bullet.png b/games/chapter5/solutions/bullet.png new file mode 100644 index 00000000..fce8c161 Binary files /dev/null and b/games/chapter5/solutions/bullet.png differ diff --git a/games/chapter5/solutions/completetank.png b/games/chapter5/solutions/completetank.png new file mode 100644 index 00000000..036cd84b Binary files /dev/null and b/games/chapter5/solutions/completetank.png differ diff --git a/games/chapter5/solutions/explosion.wav b/games/chapter5/solutions/explosion.wav new file mode 100644 index 00000000..c33f0412 Binary files /dev/null and b/games/chapter5/solutions/explosion.wav differ diff --git a/games/chapter5/solutions/fire.wav b/games/chapter5/solutions/fire.wav new file mode 100644 index 00000000..17a60459 Binary files /dev/null and b/games/chapter5/solutions/fire.wav differ diff --git a/games/chapter5/solutions/simple_bg.wav b/games/chapter5/solutions/simple_bg.wav new file mode 100644 index 00000000..b09f4e25 Binary files /dev/null and b/games/chapter5/solutions/simple_bg.wav differ diff --git a/games/chapter5/solutions/sound_tanks.py b/games/chapter5/solutions/sound_tanks.py new file mode 100644 index 00000000..051e181c --- /dev/null +++ b/games/chapter5/solutions/sound_tanks.py @@ -0,0 +1,483 @@ +# Edit the code from the tank game so that it will: +# play an explosion sound when a target is hit or run over +# play a gunshot sound when a bullet is "fired" (created) +# play background music during the whole game. + +# you can either use the provided .wav files or find +# your own (responsibly, of course). + +# Note: +# because the pygame.init is inside App's init method, we don't put +# sounds here. instead, we make the sounds and the music inside Tankgame's +# init method. + + +import pygame + +from pygame.locals import ( + K_w, + K_s, + K_a, + K_d, + KEYDOWN, + KEYUP, + QUIT, + RESIZABLE, + MOUSEBUTTONDOWN, +) +import time +import math +import random + +BULLET_IMG_PATH = "./bullet.png" +TARGET_IMG_PATH = "./target.png" +TANK_IMG_PATH = "./completetank.png" + +BLACK = (255, 255, 255) +DIRTBROWN = (168, 95, 0) +SANDBROWN = (237, 201, 175) + +TANKSPEED = [2, 2] # speed x and speed y +BULLETSPEED = [8, 8] + + +class Game_obj: + def __init__(self, picture: str, **kwargs) -> None: + """ + A basic game object class. It handles collisions, + the basic drawing method, the move and moveto methods, + and the check_out_of_screen method. + + Arguments: + picture:str - the location of the picture that will be displayed on + the screen for this object + Valid keyword arguments: + "size":tuple(x,y) - a specific size that you want to have the object be. + The picture will be scaled to that size and the hitbox + will be updated accordingly. + "position":tuple(x,y) - the tuple at which the top left of the object + should be positioned at + "speed":tuple(x,y) - the tuple that represents the object's speed. + """ + self.name = "" + + # self.image will be a pygame.Surface class + self.image = pygame.image.load(picture) + self.image = ( + pygame.transform.scale( + self.image, (kwargs["size"][0], kwargs["size"][1]) + ) + if "size" in kwargs + else self.image + ) + + self.rect = ( + self.image.get_rect() + ) # self.rect will be of pygame.Rect class + self.size = self.rect.size # will be a tuple of (sizex, sizey) + + if "position" in kwargs: + self.moveto(kwargs["position"]) + + self.speed = ( + {"x": kwargs["speed"][0], "y": kwargs["speed"][1]} + if "speed" in kwargs + else {"x": 0, "y": 0} + ) + + def check_collision(self, other: object) -> bool: + if not isinstance(other, Game_obj): + raise TypeError( + "Invalid type; need a game_obj or a child class of game_obj" + ) + # the rect class's colliderect method returns 1 if there is + # a collision and 0 if there isn't a collision + return self.rect.colliderect(other.rect) == 1 + + def draw(self, screen: pygame.Surface, color: tuple) -> None: + pygame.draw.rect(screen, color, self.rect, 0) + screen.blit(self.image, self.rect) + + def move(self) -> None: + """ + Moves the object according to it's current speed. + """ + self.rect = self.rect.move(self.speed["x"], self.speed["y"]) + # self.draw(screen, color) + + def set_speed(self, new_speed: tuple) -> None: + """ + Sets the object's speed to the provided tuple + Arguments: + new_speed (tuple(x,y)) - a tuple containing the desired speed for + the object to have. + """ + self.speed["x"], self.speed["y"] = new_speed[0], new_speed[1] + + def moveto(self, position: tuple) -> None: + """ + A helper function that moves the rectangle to the desired position. + + Arguments: + position (tuple) - the x and y coordinates of where you want the rectangle's + top left to be moved to. + """ + self.rect = self.rect.move( + position[0] - self.rect.topleft[0], + position[1] - self.rect.topleft[1], + ) + + def check_out_of_screen(self, screen_size: tuple) -> bool: + """ + Checks whether or not the object is completely outside of the screen. + Returns True or False accordingly. + Arguments: + screen_size (tuple) - the size of the screen (x,y) + """ + if ( + self.rect.bottom > screen_size[1] + or self.rect.top < 0 + or self.rect.left < 0 + or self.rect.right > screen_size[0] + ): + return True + return False + + def __str__(self): + return ( + f"{self.name} object located at the position {self.rect.topleft}" + ) + + +class Bullet(Game_obj): + def __init__(self, **kwargs) -> None: + super().__init__(BULLET_IMG_PATH, **kwargs) + self.name = "Bullet" + + +class Target(Game_obj): + def __init__(self, **kwargs) -> None: + kwargs["size"] = 40, 40 + super().__init__(TARGET_IMG_PATH, **kwargs) + self.name = "Target" + + +class Tank(Game_obj): + def __init__(self, **kwargs) -> None: + super().__init__(TANK_IMG_PATH, **kwargs) + self.direction = [0, 0] + self.SPEED = kwargs["speed"] if "speed" in kwargs else [2, 2] + self.speed["x"], self.speed["y"] = 0, 0 + + def set_speed(self) -> None: + # use math stuff to calculate the speed given that the + # max speed is self.SPEED + self.speed["x"] = ( + self.direction[0] + / math.sqrt(sum(abs(num) for num in self.direction)) + * self.SPEED[0] + if (sum(abs(num) for num in self.direction)) != 0 + else self.direction[0] * self.SPEED[0] + ) + self.speed["y"] = ( + self.direction[1] + / math.sqrt(sum(abs(num) for num in self.direction)) + * self.SPEED[1] + if (sum(abs(num) for num in self.direction)) != 0 + else self.direction[1] * self.SPEED[1] + ) + + def set_path(self, direction: str) -> None: + if direction == "up": + self.direction[1] -= 1 + if direction == "down": + self.direction[1] += 1 + if direction == "left": + self.direction[0] -= 1 + if direction == "right": + self.direction[0] += 1 + + def unset_path(self, direction: str) -> None: + if direction == "up": + self.direction[1] += 1 + if direction == "down": + self.direction[1] -= 1 + if direction == "left": + self.direction[0] += 1 + if direction == "right": + self.direction[0] -= 1 + + +class App: + """ + The abstract base class for the actual Tank_game class. It's + main purpose is to define a structure for the game. + It's structure is as follows: + Upon initialization, it runs the create_objects method + It's mainloop is comprised of the following methods: + check_events + check_collisions + move_objects + update_display + """ + + def __init__( + self, flags=RESIZABLE, width=960, height=540, title="My Game" + ): + pygame.init() + self.size = [width, height] + self.screen = pygame.display.set_mode(self.size, flags) + pygame.display.set_caption(title, title) + + self.running = True + + self.create_objects() + + def create_objects(self): + """ + This should create the initial objects on the screen. + """ + pass + + def check_events(self, event): + """ + This should take user input and handle it appropriately. + """ + pass + + def update_display(self): + """ + This should utilize clear the screen and then draw + all current objects onto the screen. + """ + pass + + def move_objects(self): + """ + This should utilize the move method that the game objects have. + """ + pass + + def check_collisions(self): + """ + This should utilize the check_collision method that the game objects + have. + """ + pass + + def mainloop(self): + while self.running: + for event in pygame.event.get(): + if event.type == QUIT: + self.running = False + break + else: + self.check_events( + event + ) # this will handle checking for user input + # such as KEYUP and MOUSEBUTTONDOWN events needed to run the game + self.check_collisions() # checks collisions between bullet/tank and targets + self.move_objects() # moves each object on the screen + self.update_display() # redraws updated objects onto the screen + pygame.display.update() # pygame’s method to show the updated screen + time.sleep(0.01) # not necessary; it's a frame cap + pygame.quit() + + +class Tank_Game(App): + def __init__(self): + # this can be changed, it's the number of targets allowed at a time. + # we initialize this before super().__init__ because super().__init__ calls + # create_objects, which utilizes self.NUM_TARGETS + self.NUM_TARGETS = 3 + + super().__init__(title="Tanks") + + self.playerscore = 0 # the player's score + + # sets the display icon to the TankIcon.png provided + pygame.display.set_icon(pygame.image.load("./TankIcon.png")) + + # set up the sounds + self.fire_sound = pygame.mixer.Sound("./fire.wav") + self.explosion_sound = pygame.mixer.Sound("./explosion.wav") + + # load the music and play it + pygame.mixer.music.load("./simple_bg.wav") + pygame.mixer.music.play(-1) + + def create_objects(self): + """ + This creates the initial objects seen when the game + first starts up. + """ + # tank + self.tank = Tank(speed=TANKSPEED) + self.tank.moveto( + ( + self.size[0] / 2 - self.tank.size[0], # move to middle x + self.size[1] - self.tank.size[1], # move to bottom y + ) + ) + + # targets + self.targets = [Target(speed=[0, 0]) for i in range(self.NUM_TARGETS)] + for target in self.targets: + target.moveto( + ( + random.randint( + 0, self.size[0] - target.size[0] + ), # random x + random.randint( + 0, self.size[1] - target.size[1] + ), # random y + ) + ) + + # bullets + self.bullets = [] + + # Score text + self.font = pygame.font.SysFont(pygame.font.get_default_font(), 32) + + def check_events(self, event): + """ + We imported all from pygame.locals, so that means + that we can check KEYDOWN and KEYUP and individual + keys such as K_w (w key), K_a (a key), etc. + """ + # change the path of the tank if w, a, s, or d was pressed + if event.type == KEYDOWN: + if event.key == K_w: + self.tank.set_path("up") + if event.key == K_s: + self.tank.set_path("down") + if event.key == K_a: + self.tank.set_path("left") + if event.key == K_d: + self.tank.set_path("right") + if event.type == KEYUP: + if event.key == K_w: + self.tank.unset_path("up") + if event.key == K_s: + self.tank.unset_path("down") + if event.key == K_a: + self.tank.unset_path("left") + if event.key == K_d: + self.tank.unset_path("right") + self.tank.set_speed() + + # create bullets if mouse button was pressed + if event.type == MOUSEBUTTONDOWN: + bul = Bullet(speed=BULLETSPEED) + bul.moveto( + (self.tank.rect.centerx, (self.tank.rect.top - bul.size[1])) + ) # move the bullet to the front of the tank + + # math stuff to calculate trajectory + mouse_pos = pygame.mouse.get_pos() + h = mouse_pos[1] - bul.rect.center[1] + w = mouse_pos[0] - bul.rect.center[0] + hyp = math.sqrt(h**2 + w**2) + vertical_speed = ( + BULLETSPEED[1] * (h / hyp) if hyp != 0 else BULLETSPEED[1] * h + ) + horizontal_speed = ( + BULLETSPEED[0] * (w / hyp) if hyp != 0 else BULLETSPEED[0] * w + ) + + bul.set_speed((horizontal_speed, vertical_speed)) + self.bullets.append(bul) + + # play the bullet fired sound since a new bullet was fired + self.fire_sound.play() + + def move_objects(self): + """ + This method moves the objects within the game. + If a bullet is outside of the screen, it is + not moved and is unreferenced. + """ + self.tank.move() + + self.bullets = [ + bullet + for bullet in self.bullets + if bullet.check_out_of_screen(self.size) is False + ] + + for bullet in self.bullets: + bullet.move() + + def check_collisions(self): + """ + This checks whether any of the objects within the game have collided + with each other. Specifically, we are looking for collisions between + bullets and targets or the tank and targets + """ + deletions = 0 # number of targets deleted + num_bullets = len(self.bullets) + + # check bullet-target collisions + for i in range(num_bullets): + for target in self.targets: + # if the bullet collided with the target + if self.bullets[i - deletions].check_collision(target) is True: + # pop both the bullet and target so that they will be + # effectively deleted + self.bullets.pop(i - deletions) + self.targets.pop(self.targets.index(target)) + + # give points for hitting the target + self.playerscore += 20 + deletions += 1 + + self.explosion_sound.play() # play the explosion sound + + break # stop the current iteration since the target and + # bullet are popped, so referencing them would error. + + # check tank-target collisions + for target in self.targets: + if self.tank.check_collision(target) is True: + self.targets.pop(self.targets.index(target)) + deletions += 1 + self.playerscore += 10 # only 10 for running over targets lol + self.explosion_sound.play() + + # create a new target for every deleted target + for i in range(deletions): + a = Target(speed=[0, 0]) + a.moveto( + ( + random.randint(0, self.size[0] - a.size[0]), + random.randint(0, self.size[1] - a.size[1]), + ) + ) + self.targets.append(a) + + def update_display(self): + self.screen.fill(SANDBROWN) + + # tank + self.tank.draw(self.screen, SANDBROWN) + + # targets + for target in self.targets: + target.draw(self.screen, BLACK) + + # bullets + for bullet in self.bullets: + bullet.draw(self.screen, BLACK) + + # score text + font_img = self.font.render( + "Score: %s" % str(self.playerscore), True, BLACK + ) + font_rect = font_img.get_rect() + pygame.draw.rect(self.screen, SANDBROWN, font_rect, 1) + self.screen.blit(font_img, font_rect) + + +game = Tank_Game() +game.mainloop()