diff --git a/.github/disabled-workflows/formatter.yml b/.github/disabled-workflows/formatter.yml deleted file mode 100644 index 724ce731..00000000 --- a/.github/disabled-workflows/formatter.yml +++ /dev/null @@ -1,25 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: Black Code Formatter -# Controls when the action will run. Triggers the workflow on push or pull request -# events but only for the master branch -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "build" - black-format: - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - name: Black Code Formatter - uses: lgeiger/black-action@v1.0.1 - - 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/pull_request.yml b/.github/workflows/pull_request.yml deleted file mode 100644 index cf4467bd..00000000 --- a/.github/workflows/pull_request.yml +++ /dev/null @@ -1,22 +0,0 @@ -# Uses https://github.com/marketplace/actions/lintly-flake8?version=v1.0 -name: Python Style (PR) - -on: - pull_request: - branches: [ master ] - -jobs: - check-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: Lintly-Flake8 - uses: grantmcconnaughey/lintly-flake8-github-action@v1.0 - with: - # The GitHub API token to create reviews with - token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml deleted file mode 100644 index 0e15d291..00000000 --- a/.github/workflows/push.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Python Style (Push) - -on: - push: - branches: [ master ] - -jobs: - check-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 . \ No newline at end of file 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/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/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/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 index e8b172e6..443b95b6 100644 --- a/1_beginner/chapter3/practice/change.py +++ b/1_beginner/chapter3/practice/change.py @@ -1,4 +1,4 @@ -''' +""" 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. @@ -15,13 +15,12 @@ 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 + 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/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/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 index 686c1ae1..fc4e31ed 100644 --- a/1_beginner/chapter3/practice/no_greater_than.py +++ b/1_beginner/chapter3/practice/no_greater_than.py @@ -1,4 +1,4 @@ -''' +""" 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 @@ -7,6 +7,6 @@ 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 index 6372d84f..a8cd5ee9 100644 --- a/1_beginner/chapter3/solutions/change.py +++ b/1_beginner/chapter3/solutions/change.py @@ -1,4 +1,4 @@ -''' +""" 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. @@ -15,7 +15,7 @@ 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 @@ -24,26 +24,25 @@ # prompt user for dollars and convert it to cents num_cents = int( - float(input("How many dollars do you have: $")) - * CENTS_PER_DOLLAR + 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") +print(dollars, "dollars") quarters = remaining // CENTS_PER_QUARTER remiaining = remaining % CENTS_PER_QUARTER -print(quarters, " quarters") +print(quarters, "quarters") dimes = remaining // CENTS_PER_DIME remiaining = remaining % CENTS_PER_DIME -print(dimes, " dimes") +print(dimes, "dimes") nickels = remaining // CENTS_PER_NICKEL remiaining = remaining % CENTS_PER_NICKEL -print(nickels, " nickels") +print(nickels, "nickels") cents = remaining -print(cents, " cents") +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/cylinder_volume.py b/1_beginner/chapter3/solutions/cylinder_volume.py index 6c04f715..07494915 100644 --- a/1_beginner/chapter3/solutions/cylinder_volume.py +++ b/1_beginner/chapter3/solutions/cylinder_volume.py @@ -4,7 +4,7 @@ # 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 = PI * 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/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 index f0134080..09b5ce63 100644 --- a/1_beginner/chapter3/solutions/no_greater_than.py +++ b/1_beginner/chapter3/solutions/no_greater_than.py @@ -1,4 +1,4 @@ -''' +""" 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 @@ -7,6 +7,6 @@ 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 index 1baca338..f9c724a8 100644 --- a/1_beginner/chapter4/practice/four_numbers.py +++ b/1_beginner/chapter4/practice/four_numbers.py @@ -1,4 +1,4 @@ -''' +""" Ask the user for 4 numbers. Use only 3 if else blocks to find the largest number. You may not use elifs. @@ -10,6 +10,6 @@ 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 22274604..992997ed 100644 --- a/1_beginner/chapter4/practice/grade.py +++ b/1_beginner/chapter4/practice/grade.py @@ -1,4 +1,4 @@ -''' +""" Grade Write a program that asks the user to enter the score for a student's test. @@ -11,13 +11,13 @@ 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 fa9b8e15..53d87890 100644 --- a/1_beginner/chapter4/practice/hours.py +++ b/1_beginner/chapter4/practice/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). diff --git a/1_beginner/chapter4/practice/menu.py b/1_beginner/chapter4/practice/menu.py index a615a953..b528e351 100644 --- a/1_beginner/chapter4/practice/menu.py +++ b/1_beginner/chapter4/practice/menu.py @@ -7,7 +7,7 @@ But if the customer enters 'french toast' AND 'coffee', there is a discount of $1. -And if the customer enters 'chicken soup' OR 'apple juice', +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 diff --git a/1_beginner/chapter4/practice/simplify.py b/1_beginner/chapter4/practice/simplify.py index 35f9e1fe..fac39e8f 100644 --- a/1_beginner/chapter4/practice/simplify.py +++ b/1_beginner/chapter4/practice/simplify.py @@ -1,6 +1,7 @@ """ -Here is a block of code where one tries do determine -if somone is a nobel prize winner. +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. @@ -8,7 +9,7 @@ you start working.) """ -''' +""" if does_significant_work: if makes_breakthrough: is_nobel_prize_candidate = True @@ -16,4 +17,4 @@ is_nobel_prize_candidate = False elif not does_significant_work: is_nobel_prize_candidate = False -''' +""" diff --git a/1_beginner/chapter4/practice/temperature.py b/1_beginner/chapter4/practice/temperature.py index 5aa44841..576dc9d4 100644 --- a/1_beginner/chapter4/practice/temperature.py +++ b/1_beginner/chapter4/practice/temperature.py @@ -13,8 +13,8 @@ temp = int(input("Enter temperature: ")) if temp < 100: # 60-100 is hot - print('hot') + print("hot") if temp <= 60: # 30-59 is normal - print('normal') + print("normal") if temp < 30: # 0-29 is cold - print('cold') + print("cold") 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 index e333dc33..45b0445d 100644 --- a/1_beginner/chapter4/solutions/four_numbers.py +++ b/1_beginner/chapter4/solutions/four_numbers.py @@ -1,4 +1,4 @@ -''' +""" Ask the user for 4 numbers. Use only 3 if else blocks to find the largest number. You may not use elifs. @@ -10,7 +10,7 @@ print("nay") This question is really tricky, and requires some ingenuity. -''' +""" a = float(input("Enter number 1: ")) b = float(input("Enter number 2: ")) diff --git a/1_beginner/chapter4/solutions/grade.py b/1_beginner/chapter4/solutions/grade.py index 07f0f553..6ae3c9dc 100644 --- a/1_beginner/chapter4/solutions/grade.py +++ b/1_beginner/chapter4/solutions/grade.py @@ -1,4 +1,4 @@ -''' +""" Grade Write a program that asks the user to enter the score for a student's test. @@ -11,7 +11,7 @@ F: < 60 Print the letter grade that the test score receives. -''' +""" score = float(input("Enter test score: ")) if score >= 90: @@ -25,10 +25,10 @@ else: print("F") -''' +""" See if you can write the same program, but without using >= anywhere. -''' +""" score = float(input("Enter test score: ")) if score < 60: diff --git a/1_beginner/chapter4/solutions/hours.py b/1_beginner/chapter4/solutions/hours.py index 8e5a97d8..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). diff --git a/1_beginner/chapter4/solutions/menu.py b/1_beginner/chapter4/solutions/menu.py index 4369b529..9412b483 100644 --- a/1_beginner/chapter4/solutions/menu.py +++ b/1_beginner/chapter4/solutions/menu.py @@ -7,7 +7,7 @@ But if the customer enters 'french toast' AND 'coffee', there is a discount of $1. -And if the customer enters 'chicken soup' OR 'apple juice', +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 @@ -24,11 +24,10 @@ 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': +if food == "french toast" and drink == "coffee": total_cost -= 1 - # charge extra $1 if user orders chicken soup or apple juice -if food == 'chicken soup' or drink == 'apple juice': +elif food == "chicken soup" or drink == "apple juice": total_cost += 1 # display total diff --git a/1_beginner/chapter4/solutions/simplify.py b/1_beginner/chapter4/solutions/simplify.py index be28f1c3..4b453bd0 100644 --- a/1_beginner/chapter4/solutions/simplify.py +++ b/1_beginner/chapter4/solutions/simplify.py @@ -1,6 +1,7 @@ """ -Here is a block of code where one tries do determine -if somone is a nobel prize winner. +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. diff --git a/1_beginner/chapter4/solutions/square.py b/1_beginner/chapter4/solutions/square.py index 2dbde5cf..678cae8b 100644 --- a/1_beginner/chapter4/solutions/square.py +++ b/1_beginner/chapter4/solutions/square.py @@ -9,7 +9,7 @@ if number % 2 == 0: # if number is even, print its square - print(number ** 2) + 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 index 03f54670..28c5cb0c 100644 --- a/1_beginner/chapter4/solutions/temperature.py +++ b/1_beginner/chapter4/solutions/temperature.py @@ -11,11 +11,11 @@ temp = int(input("Enter temperature: ")) if 60 <= temp <= 100: # 60-100 is hot - print('hot') + print("hot") if 30 <= temp < 60: # 30-59 is normal - print('normal') + print("normal") if 0 <= temp < 30: # 0-29 is cold - print('cold') + print("cold") # alternatively, you can also use elif # if temp >= 60: diff --git a/1_beginner/chapter4/solutions/walk.py b/1_beginner/chapter4/solutions/walk.py index c6cc4b45..f6dc7d72 100644 --- a/1_beginner/chapter4/solutions/walk.py +++ b/1_beginner/chapter4/solutions/walk.py @@ -22,8 +22,8 @@ """ # 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: ')) +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, @@ -32,20 +32,20 @@ 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: ')) +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') + print("Late") elif current_hour < second_hour: - print('Early') + print("Early") else: # compare minutes if hours are equal if current_minute > second_minute: - print('Late') + print("Late") elif current_minute < second_minute: - print('Early') + print("Early") else: # current_minute == second_minute - print('Now') + 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/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 index a08048a2..ba357c08 100644 --- a/1_beginner/chapter5/solutions/virtual_pet.py +++ b/1_beginner/chapter5/solutions/virtual_pet.py @@ -22,26 +22,26 @@ and happiness variables increase and decrease, or add more actions! """ -name = 'Otto' +name = "Otto" hunger = 6 happiness = 0 -command = input('> ') -while command != 'quit': # Exits loop when user quits +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 + 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') + 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') + print(name + " feels happy") - command = input('> ') # Keep taking user input until user quits + 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 index 8258d111..e939fa54 100644 --- a/1_beginner/chapter6/examples/identity_operators.py +++ b/1_beginner/chapter6/examples/identity_operators.py @@ -1,13 +1,13 @@ # Identity Operators -message_1 = 'hello' -message_2 = 'hello' +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' +message_3 = "world" +message_4 = "tahiti" print(message_3 is message_4) # False print(message_3 is not message_4) # True @@ -19,9 +19,9 @@ x = 5 y = 5 if x is y: - print('x is y') + print("x is y") else: - print('x is not y') + print("x is not y") # lists are objects, # so they are located in different @@ -34,13 +34,13 @@ # reference the same location # in memory if a is b: - print('a is b') + print("a is b") else: - print('a is not b') + print("a is not b") # use the equality operator # to test if content is equivalent if a == b: - print('a == b') + print("a == b") else: - print('a != b') + 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 index 3f8b2270..b491948f 100644 --- a/1_beginner/chapter6/examples/membership_operators.py +++ b/1_beginner/chapter6/examples/membership_operators.py @@ -8,6 +8,6 @@ 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') + print("4 is not an element in my_list") else: - print('4 is an element in my_list') + 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 index 9bcff2d9..6b5ca07b 100644 --- a/1_beginner/chapter6/practice/grocery_list.py +++ b/1_beginner/chapter6/practice/grocery_list.py @@ -1,4 +1,4 @@ -''' +""" Grocery List Create a program that prompts the @@ -19,6 +19,6 @@ 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/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 index 7b564abb..ba8106ed 100644 --- a/1_beginner/chapter6/practice/monty_hall.py +++ b/1_beginner/chapter6/practice/monty_hall.py @@ -26,7 +26,8 @@ # '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'] + +doors = ["G", "G", "C"] # Make the Monty Hall game repeat 6 times. # (All of the following actions should be in your loop.) 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/too_long.py b/1_beginner/chapter6/practice/too_long.py index af29a0ec..4a235299 100644 --- a/1_beginner/chapter6/practice/too_long.py +++ b/1_beginner/chapter6/practice/too_long.py @@ -4,4 +4,4 @@ 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'] +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 index f680c94c..1e251a2e 100644 --- a/1_beginner/chapter6/solutions/grocery_list.py +++ b/1_beginner/chapter6/solutions/grocery_list.py @@ -1,4 +1,4 @@ -''' +""" Grocery List Create a program that prompts the @@ -19,21 +19,21 @@ 2 decimals.) Demo: https://youtu.be/BmMj16Ox5iA -''' +""" -item = '' +item = "" items = [] # stores grocery items # continuously ask user for grocery items # and store them in a list -while item != 'quit': +while item != "quit": item = input("Enter a grocery item, or 'quit': ") - if item != '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(str(i + 1) + ". " + items[i]) print() @@ -41,7 +41,7 @@ # each grocery item prices = [] for i in range(0, len(items)): - price = float(input('Enter price for ' + items[i] + ': $')) + price = float(input("Enter price for " + items[i] + ": $")) prices.append(price) print() @@ -50,7 +50,7 @@ # item in the grocery list quantities = [] for i in range(0, len(items)): - quantity = int(input('Enter quantity bought for ' + items[i] + ': ')) + quantity = int(input("Enter quantity bought for " + items[i] + ": ")) quantities.append(quantity) print() @@ -62,4 +62,4 @@ # print total, formatted to 2 decimals # because it's money -print('Total: $%.2f' % total) +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 index 9468f58c..0a9ee705 100644 --- a/1_beginner/chapter6/solutions/integer_info.py +++ b/1_beginner/chapter6/solutions/integer_info.py @@ -16,6 +16,6 @@ info = [ len(str(num)), num % 10, # mod 10 of any number will return its last digit - num % 2 == 0 + 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 index cee71b8f..7d851c40 100644 --- a/1_beginner/chapter6/solutions/monty_hall.py +++ b/1_beginner/chapter6/solutions/monty_hall.py @@ -25,7 +25,8 @@ # 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'] + +doors = ["G", "G", "C"] # Make the Monty Hall game repeat 6 times. for i in range(6): @@ -46,8 +47,8 @@ # 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 + 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!") @@ -58,11 +59,11 @@ print("Enter 'y' to switch, or 'n' to keep your first choice.") switch = input("> ") - if switch == 'y': + if switch == "y": choice = doors_left[0] # The prize behind the user's ultimate choice is revealed! - if doors[choice - 1] == 'C': + if doors[choice - 1] == "C": print("You got... a car! Congratulations!") else: print("You got... a goat! Better luck next time!") 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 index aeea2135..583f633e 100644 --- a/1_beginner/chapter6/solutions/snookle_game.py +++ b/1_beginner/chapter6/solutions/snookle_game.py @@ -25,13 +25,13 @@ while not win: for num in nums: # prompt user to add or subtract current num - print('main number is currently ' + str(main)) + print("main number is currently " + str(main)) choice = input("[add] or [subtract] " + str(num) + "?\n> ") # update main value based on choice - if choice == 'add': + if choice == "add": main += num - elif choice == 'subtract': + elif choice == "subtract": main -= num # If the main number if 12, the user has won! @@ -39,4 +39,4 @@ win = True # Exit while loop and end game break # Exit for loop -print('Congrats you won the game!') +print("Congrats you won the game!") diff --git a/1_beginner/chapter6/solutions/sum_list.py b/1_beginner/chapter6/solutions/sum_list.py index 3a5ae513..e027baeb 100644 --- a/1_beginner/chapter6/solutions/sum_list.py +++ b/1_beginner/chapter6/solutions/sum_list.py @@ -10,4 +10,4 @@ for num in numbers: sum += num -print('Sum of elements in list:', sum) +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 index a32b2097..3c17d7e3 100644 --- a/1_beginner/chapter6/solutions/too_long.py +++ b/1_beginner/chapter6/solutions/too_long.py @@ -3,9 +3,13 @@ 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'] +the_list = ["dragon", "cab", "science", "dove", "lime", "river", "pop"] -for x in the_list: # iterates through every element in the_list - if len(x) > 4: # if element length is greater than 4 - print(x) # prints element - the_list.remove(x) # removes element +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 index d8c7e4b1..53205f94 100644 --- a/1_beginner/chapter7/practice/first_three_words.py +++ b/1_beginner/chapter7/practice/first_three_words.py @@ -1,10 +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 index aed54ccc..6f8f9abe 100644 --- a/1_beginner/chapter7/practice/ingly.py +++ b/1_beginner/chapter7/practice/ingly.py @@ -1,4 +1,4 @@ -''' +""" Ingly Write a Python program to add 'ing' @@ -9,6 +9,6 @@ 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 index a76ebc8a..86338be9 100644 --- a/1_beginner/chapter7/practice/replace.py +++ b/1_beginner/chapter7/practice/replace.py @@ -1,15 +1,17 @@ -''' +""" Replace -Write a Python program to print a string -from a given string where all occurrences -of its first char have been changed to '$', +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 String : 'restart' -Expected Result : 'resta$t' + +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 index 7bb9d437..f98230f2 100644 --- a/1_beginner/chapter7/practice/vowels.py +++ b/1_beginner/chapter7/practice/vowels.py @@ -1,9 +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 index f9c20694..d8454e30 100644 --- a/1_beginner/chapter7/solutions/first_three_words.py +++ b/1_beginner/chapter7/solutions/first_three_words.py @@ -1,13 +1,13 @@ -''' +""" 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: ') +sentence = input("Enter a sentence: ") words = sentence.split() diff --git a/1_beginner/chapter7/solutions/ingly.py b/1_beginner/chapter7/solutions/ingly.py index 32991574..2c018ea0 100644 --- a/1_beginner/chapter7/solutions/ingly.py +++ b/1_beginner/chapter7/solutions/ingly.py @@ -1,4 +1,4 @@ -''' +""" Ingly Write a Python program to add 'ing' @@ -9,14 +9,14 @@ Adapted from W3Resource, problem 6: https://www.w3resource.com/python-exercises/string/ -''' +""" -string = input('Enter a string: ') +string = input("Enter a string: ") if len(string) >= 3: - if string[-3:] != 'ing': - string += 'ing' + if string[-3:] != "ing": + string += "ing" else: - string += 'ly' + 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 index 3058adf6..68bf0b37 100644 --- a/1_beginner/chapter7/solutions/replace.py +++ b/1_beginner/chapter7/solutions/replace.py @@ -1,26 +1,27 @@ -''' +""" Replace -Write a Python program to print a string -from a given string where all occurrences -of its first char have been changed to '$', +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 String: 'restart' -Expected Result: 'resta$t' +Sample Input: 'restart' +Expected Output: 'resta$t' Adapted from W3Resource, problem 4: https://www.w3resource.com/python-exercises/string/ -''' +""" -string = input('Enter a 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 += '$' + result += "$" else: result += string[i] 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 1d10b83b..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). diff --git a/1_beginner/chapter7/solutions/vowels.py b/1_beginner/chapter7/solutions/vowels.py index 10b413ed..9ded1707 100644 --- a/1_beginner/chapter7/solutions/vowels.py +++ b/1_beginner/chapter7/solutions/vowels.py @@ -1,14 +1,14 @@ -''' +""" Vowels Create a program which takes a string from the user and prints the number of vowels that are in the string. -''' -VOWELS = 'aeiou' +""" +VOWELS = "aeiou" # Ask user for a string -string = input('Enter a string: ') +string = input("Enter a string: ") # Count the number of vowels in the string number_of_vowels = 0 @@ -17,4 +17,4 @@ number_of_vowels += 1 # Print number of vowels -print('Number of vowels:', 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 index 195abdca..ac1a9d0a 100644 --- a/2_intermediate/chapter9/examples/dict_values.py +++ b/2_intermediate/chapter9/examples/dict_values.py @@ -3,7 +3,7 @@ contacts = { "John Doe": "1234 Main St", "Jane Smith": "5678 Market St", - "Daisy Johnson": "1357 Wall St" + "Daisy Johnson": "1357 Wall St", } daisy_address = contacts["Daisy Johnson"] diff --git a/2_intermediate/chapter9/examples/initialize_dict.py b/2_intermediate/chapter9/examples/initialize_dict.py index 2411e087..d29066f5 100644 --- a/2_intermediate/chapter9/examples/initialize_dict.py +++ b/2_intermediate/chapter9/examples/initialize_dict.py @@ -3,7 +3,7 @@ contacts = { "John Doe": "1234 Main St", "Jane Smith": "5678 Market St", - "Daisy Johnson": "1357 Wall St" + "Daisy Johnson": "1357 Wall St", } print(contacts) diff --git a/2_intermediate/chapter9/examples/iterate_dict.py b/2_intermediate/chapter9/examples/iterate_dict.py index 80728a5d..dd979854 100644 --- a/2_intermediate/chapter9/examples/iterate_dict.py +++ b/2_intermediate/chapter9/examples/iterate_dict.py @@ -1,6 +1,6 @@ # Iterating Through Dictionaries -contacts = {'Daisy Johnson': '2468 Park Ave', 'Leo Fitz': '1258 Monkey Dr'} +contacts = {"Daisy Johnson": "2468 Park Ave", "Leo Fitz": "1258 Monkey Dr"} # iterate through each key for name in contacts: diff --git a/2_intermediate/chapter9/examples/key_value_membership.py b/2_intermediate/chapter9/examples/key_value_membership.py index bec05c8c..4a95c9f3 100644 --- a/2_intermediate/chapter9/examples/key_value_membership.py +++ b/2_intermediate/chapter9/examples/key_value_membership.py @@ -1,6 +1,6 @@ # Check Key/Value Membership in a Dictionary -contacts = {'Daisy Johnson': '2468 Park Ave', 'Leo Fitz': '1258 Monkey Dr'} +contacts = {"Daisy Johnson": "2468 Park Ave", "Leo Fitz": "1258 Monkey Dr"} if "Daisy Johnson" in contacts: print("Daisy Johnson is a key in contacts") diff --git a/2_intermediate/chapter9/examples/manipulate_dict.py b/2_intermediate/chapter9/examples/manipulate_dict.py index 9baef544..cb3e205f 100644 --- a/2_intermediate/chapter9/examples/manipulate_dict.py +++ b/2_intermediate/chapter9/examples/manipulate_dict.py @@ -4,7 +4,7 @@ "John Doe": "1234 Main St", "Jane Smith": "5678 Market St", "Daisy Johnson": "2468 Park Ave", - "Leo Fitz": "1258 Monkey Dr" + "Leo Fitz": "1258 Monkey Dr", } # get - retrieves the value at a specified key diff --git a/2_intermediate/chapter9/examples/why_dict.py b/2_intermediate/chapter9/examples/why_dict.py index 8b8fd210..66268626 100644 --- a/2_intermediate/chapter9/examples/why_dict.py +++ b/2_intermediate/chapter9/examples/why_dict.py @@ -1,16 +1,19 @@ # Why Dictionaries # dictionaries as an alternative to parallel lists -names = ['Jane Doe', 'John Williams', ] -addresses = ['1234 Main St', '5678 Market Pl', '1357 Wall St'] +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' + "Jane Doe": "1234 Main St", + "John Williams": "5678 Market Pl", + "Alex Summers": "1357 Wall St", } # implementation 1 using if statements 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 66135f71..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 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()