diff --git a/.gitignore b/.gitignore index 28c8795a..93c1b7ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ - *.class Level1-Module3/_01_gui_app_bindings/_c_PigLatinTranslator/__pycache__/PigLatinConverter.cpython-38.pyc *.xml @@ -11,8 +10,6 @@ Level1-Module3/_01_gui_app_bindings/_c_PigLatinTranslator/__pycache__/PigLatinCo .idea .idea/workspace.xml - - # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -56,4 +53,4 @@ coverage.xml saved/ .jtl/*completion* -.vscode +.vscode \ No newline at end of file diff --git a/.lib/auto_turtle.py b/.lib/auto_turtle.py new file mode 100644 index 00000000..faebcbf0 --- /dev/null +++ b/.lib/auto_turtle.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python +""" +auto_turtle.py + +This script automatically sets up the ipyturtle3 environment +for use in a Jupyter Notebook. + +When used with %run, it will: +1. Import all necessary modules (turtle, Canvas, TurtleScreen, Turtle, display). +2. Create and display a default Canvas named 'myCanvas'. +3. Define a custom Turtle class that remaps the coordinate system so that + the student's (0, 0) is at (start_x, start_y) on the canvas. +4. Display the Canvas in the notebook. +5. Wait a moment to ensure the canvas is rendered. +6. Create a TurtleScreen for the canvas named 'myTS'. +""" + +import time # Might not be necessary, but used for sleep + +# --- Configuration --- +canvas_width = 750 # Width of the turtle canvas +canvas_height = 250 # Height of the turtle canvas +start_x = -300 # Starting X position of the canvas +start_y = -50 # Starting Y position of the canvas +time_sleep = 0.1 # Time to wait for canvas rendering +# --------------------- + +try: + # 1. Import the main library and necessary classes + import ipyturtle3 as turtle + import sys, types + from ipyturtle3 import Canvas, TurtleScreen, Turtle + from IPython.display import display + + turtle = types.ModuleType("turtle") # Make a fake 'turtle' module + # 2. Define a NEW Turtle class that overrides the original for custom behavior + class turtle(Turtle): + """ + Custom Turtle class that remaps the coordinate system. + The student's (0, 0) is now our (START_X, START_Y). + + This class overrides all position-related methods to + translate between the student's "relative" coordinates + and the canvas's "absolute" coordinates. + """ + def __init__(self, screen=None, *args, **kwargs): + """ + Initializes the turtle and moves it instantly + to the new (0, 0) origin point. + """ + # Store the new origin offset + self._origin_x = start_x + self._origin_y = start_y + + # Call the original __init__ to create the turtle + super().__init__(screen, *args, **kwargs) + + # --- Set the default starting position (instantly) --- + original_speed = self.speed() # Save original speed + self.speed(0) # Set to instant + self.penup() + super().goto(self._origin_x, self._origin_y) # Move to *absolute* start + self.pendown() + self.speed(original_speed) # Restore original speed + + # --- Private Helper Methods --- + + def _to_absolute(self, x, y): + """Converts student's relative (x, y) to absolute canvas coordinates.""" + return (x + self._origin_x, y + self._origin_y) + + def _to_relative(self, x, y): + """Converts absolute canvas (x, y) to student's relative coordinates.""" + return (x - self._origin_x, y - self._origin_y) + + # --- Overridden Position & Movement Methods --- + + def goto(self, x, y): + """Moves the turtle to a (student) coordinate.""" + abs_x, abs_y = self._to_absolute(x, y) + super().goto(abs_x, abs_y) + + def setposition(self, x, y=None): + """Alias for goto.""" + # This handles the case where setposition is called with one arg (a tuple) + # or two args (x, y). Standard turtle behavior. + if y is None: + try: + x, y = x + except TypeError: + # Handle error or just pass to goto to raise it + pass + self.goto(x, y) + + def setx(self, x): + """Sets the turtle's (student) x coordinate.""" + abs_x = x + self._origin_x + super().setx(abs_x) + + def sety(self, y): + """Sets the turtle's (student) y coordinate.""" + abs_y = y + self._origin_y + super().sety(abs_y) + + def position(self): + """Returns the turtle's (student) position.""" + abs_x, abs_y = super().position() + return self._to_relative(abs_x, abs_y) + + def pos(self): + """Alias for position.""" + return self.position() + + def xcor(self): + """Returns the turtle's (student) x coordinate.""" + abs_x = super().xcor() + return abs_x - self._origin_x + + def ycor(self): + """Returns the turtle's (student) y coordinate.""" + abs_y = super().ycor() + return abs_y - self._origin_y + + turtle.Turtle = Turtle # This puts the class in the fame module + sys.modules["turtle"] = turtle # This allows "import turtle" to work. + + # 3. Create a Canvas for the turtle to draw on + myCanvas = Canvas(width=canvas_width, height=canvas_height) + + # 4. Display the Canvas in the Jupyter notebook + display(myCanvas) + + # 5. Wait a moment to ensure the canvas is fully rendered + time.sleep(time_sleep) + + # 6. Create a TurtleScreen + myTS = TurtleScreen(myCanvas) + myTS.clear() # Clear the screen to start fresh + +# Handle import errors gracefully +except ImportError: + print("❌ Error: 'ipyturtle3' or 'IPython' not found.") + print("Please make sure you are in a Jupyter environment and have") + print("ipyturtle3 installed (e.g., `pip install ipyturtle3`).") +# Handle any other exceptions +except Exception as e: + print(f"An error occurred during auto_turtle setup: {e}") \ No newline at end of file diff --git a/.lib/auto_turtle_concise.py b/.lib/auto_turtle_concise.py new file mode 100644 index 00000000..4b4f1d9a --- /dev/null +++ b/.lib/auto_turtle_concise.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +""" +auto_turtle_concise.py +Inherits setup from auto_turtle.py and initializes 'tina'. + +This just inherits the setup from auto_turtle.py but creates 'tina' the turtle for you. +It's a more concise way to get started with Tina the Turtle without having to write the setup code again. +My intent was to use this for the later examples in this course since they already will know how to use Tina at that point. +""" + +try: + # Attempting to open and execute auto_turtle.py to inherit its setup + # Use utf-8 to prevent a UnicodeDecodeError on some systems + with open(".lib/auto_turtle.py", encoding="utf-8") as f: + exec(f.read()) + + # Setup Tina (using the variables inherited from auto_turtle.py) + # These will only run if the 'exec' above was successful + tina = turtle(myTS) # type: ignore[name-defined] + tina.shape('turtle') + tina.speed(5) + +except FileNotFoundError: + print("Error: Could not find and inherit from '.lib/auto_turtle.py'.") + print("Please ensure auto_turtle.py exists in the .lib directory.") + +except Exception as e: + # Any other exceptions that may occur + print(f"Error: {e}") \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index b4e65206..0d816c9e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,9 +4,7 @@ "**/.svn": true, "**/.hg": true, "**/Thumbs.db": true, - "**/*.crswap": true, "**/__pycache__": true, - "**/.cache": true, "**/.coverage": true, "**/.coverage.*": true, "**/.hypothesis": true, @@ -14,7 +12,9 @@ "**/.nox": true, "**/.pytest_cache": true, "**/.ruff_cache": true, - "**/.tox": true + "**/.tox": true, + "**/*.crswap": true, + "**/.cache": true } } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index eb7fd539..988e023e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,4 +1,4 @@ -{ +league{ "version": "2.0.0", "tasks": [] } \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..67e55e5f --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,99 @@ +# Curriculum Review Guide + +This repository is the League's Python Apprentice curriculum, aligned with the Python Institute's [PCEP](https://pythoninstitute.org/pcep) (entry-level) exam. Students are typically middle school and early high school age. Lessons live in `lessons/` as Jupyter notebooks organized into numbered category directories. + +When asked to review a lesson or set of lessons, use the criteria below. + +## Voice + +Aim for a fun adult tone — a good teacher who respects the reader, not a kids-TV host or a cheerleader. Silly topic choices (badgers, mushrooms, snakes) are fine; breathless enthusiasm is not. + +**Do:** +- Write like you'd explain something to a smart 12-year-old. +- Use "you" — the student is the reader. +- Allow at most one exclamation point per lesson, and only when the emphasis is genuinely earned. + +**Don't:** +- Use "super", "awesome", "amazing", "fantastic", "incredible" as intensifiers. +- Write "We're so excited", "Don't worry!", "Let's go!", "You've got this!", "Ready to have some fun?". +- Use ALL-CAPS warnings or stacked exclamation points. +- Use emojis as substitutes for words (e.g. "turn left 🡐", "move forward ⬆️"). Decorative emojis in small doses are fine. +- Adopt kid-speak or corporate-training-speak. + +**Example:** +- Okay: *"The turtle draws a line behind it as it moves."* +- Not okay: *"The turtle draws a SUPER cool line as it zooms around! 🚀"* + +## Content flow + +Within each lesson and across lessons in a category, check: + +- **Logical progression** — each step follows from the last. +- **No prerequisites from later lessons** — every concept used in code or exercises has been introduced already, or is flagged with a brief "we'll cover this later". +- **No abrupt jumps** — difficulty increases smoothly. +- **Callbacks where warranted** — reference earlier lessons when it reinforces the current one, but don't force it. +- **Consistent terminology** — once we call it a "function", don't switch to "method" or "routine" without reason. Flag terminology drift across lessons. +- **No holes** — if a lesson uses a skill the student hasn't seen, that's a hole worth flagging. + +**Motivation vs. mechanics:** Kids often do fine being handed a mechanic and getting the reason after they've tried it, as long as the mechanic itself is sensible. Don't flag missing motivation unless the lesson genuinely doesn't make sense without it. + +**One concept per lesson:** The general preference is focused lessons, but existing structure takes priority. Note serious violations in the review, but **do not rewrite lessons to split concepts without explicit approval**. + +## Technical quality + +- Code examples must run as written. +- Examples should be realistic — not `foo()` / `bar()` placeholders — but not so elaborate they distract from the concept being taught. +- Follow PEP 8: 4-space indents, `snake_case` for variables and functions, `PascalCase` for classes, blank lines between top-level definitions, no trailing whitespace, line lengths ~79–100 chars, imports at the top. +- Accurate technical claims. Simplifications are fine ("a variable is a box for data"); outright-wrong statements are not. +- When we teach a topic, teach it thoroughly: a worked example, an exercise, and at least one edge case or common mistake called out. + +### Exercise stubs are intentional + +Many lessons have exercise code where key lines or whole function bodies are missing, commented out, or replaced with `...` / `# Your code here`. The student is supposed to type those in. Do **not** flag these as bugs, syntax errors, or missing implementations. Common shapes: + +- `def foo():` with just a `# Your code here` comment as the body +- A bare `x = ` with no value (student will fill in the expression) +- `... # Your code here` placeholders in the middle of otherwise-complete code +- `.py` files that contain only a docstring — the rest is for the student + +Only flag these if something adjacent is actually wrong (e.g. the surrounding hint text is misleading, or the stub references a concept the student hasn't learned). + +## Scope + +Curriculum targets PCEP. Intentionally **out of scope**: + +- List / dict / set comprehensions +- Generators and `yield` +- Decorators +- Context managers beyond `with open(...)` +- Metaclasses, descriptors, `__slots__` +- `async` / `await` +- Type hints beyond incidental use + +Do not suggest adding these. Teach what we teach thoroughly rather than expanding the surface area. + +## Review workflow + +When asked to review one or more lessons: + +1. Read the lesson(s) in full, plus enough surrounding lessons in the same category (and the lesson immediately before and after) to judge flow and prerequisites. +2. Produce a markdown review with one `## ` heading per file, and findings as bulleted items underneath. +3. The user will delete findings they don't want applied and hand the edited review back. +4. Apply the remaining findings. + +### Finding format + +Each bullet must be **standalone and deletable** — removing it shouldn't break any other finding. Include: category tag, location, the problem, and a concrete suggestion. + +Group findings under each file heading in this order: **Voice**, **Flow**, **Technical**, **Scope**. Order within a category follows the location in the file. + +``` +## lessons/10_Turtles/10_Welcome/10_Welcome.ipynb + +- **Voice** (cell 0, opening line): "Hello future coders, programmers, and engineers!" — drop the greeting, open with "Welcome to your first Python lesson." +- **Voice** (cell 0, intro paragraph): "We're super excited to help you..." — rewrite as "This course will help you take your first steps into programming." +- **Flow** (cell 2, exercise): exercise uses `random.randint()` but `random` hasn't been introduced — either introduce it earlier or swap the exercise for one using only what's been taught. +- **Technical** (cell 4, code block): mixes `Turtle()` and `turtle.Turtle()` — standardize on one convention and use it throughout the lesson. +``` + +Keep findings specific enough that applying one doesn't require re-reading the whole lesson. If a single change touches multiple places in a lesson (e.g. "rename `x` to `count` everywhere"), that's one finding — say so in the bullet. diff --git a/lessons/.jtl/Quiz_Data/Module_Four_Quiz.json b/lessons/.jtl/Quiz_Data/Module_Four_Quiz.json new file mode 100644 index 00000000..956459af --- /dev/null +++ b/lessons/.jtl/Quiz_Data/Module_Four_Quiz.json @@ -0,0 +1,202 @@ +[ + { + "question": "What keyword defines a function?", + "type": "multiple_choice", + "answers": [ + { "answer": "function", "correct": false, "feedback": "Python uses 'def' to define functions." }, + { "answer": "def", "correct": true, "feedback": "Correct! 'def' creates a function." }, + { "answer": "define", "correct": false, "feedback": "Python uses 'def' to define functions." }, + { "answer": "func", "correct": false, "feedback": "Python uses 'def' to define functions." } + ] + }, + { + "question": "What is a function for?", + "type": "multiple_choice", + "answers": [ + { "answer": "To repeat code without rewriting it", "correct": true, "feedback": "Correct! Functions let you reuse code." }, + { "answer": "To make programs slower", "correct": false, "feedback": "Functions help organize and reuse code efficiently." }, + { "answer": "To delete variables", "correct": false, "feedback": "Functions are for organizing and reusing code." }, + { "answer": "To create errors", "correct": false, "feedback": "Functions help organize and reuse code." } + ] + }, + { + "question": "What is a dictionary?", + "type": "multiple_choice", + "answers": [ + { "answer": "A book of definitions", "correct": false, "feedback": "In Python, a dictionary stores key-value pairs." }, + { "answer": "A data structure with keys and values", "correct": true, "feedback": "Correct! Dictionaries store key-value pairs like {'name': 'Alice'}." }, + { "answer": "A list of numbers", "correct": false, "feedback": "Dictionaries store key-value pairs, not just lists." }, + { "answer": "A type of loop", "correct": false, "feedback": "Dictionaries are data structures, not loops." } + ] + }, + { + "question": "What is a set?", + "type": "multiple_choice", + "answers": [ + { "answer": "A collection with no duplicates", "correct": true, "feedback": "Correct! Sets automatically remove duplicates." }, + { "answer": "An ordered list of items", "correct": false, "feedback": "Sets are unordered and have no duplicates." }, + { "answer": "A type of string", "correct": false, "feedback": "Sets are collections, not strings." }, + { "answer": "A dictionary", "correct": false, "feedback": "Sets and dictionaries are different data structures." } + ] + }, + { + "question": "Which can be changed after creation?", + "type": "many_choice", + "answers": [ + { "answer": "Lists", "correct": true, "feedback": "Correct! Lists are mutable." }, + { "answer": "Sets", "correct": true, "feedback": "Correct! Sets are mutable." }, + { "answer": "Tuples", "correct": false, "feedback": "Tuples cannot be changed." }, + { "answer": "Dictionaries", "correct": true, "feedback": "Correct! Dictionaries are mutable." } + ] + }, + { + "question": "How do sets differ from lists?", + "type": "many_choice", + "answers": [ + { "answer": "No duplicate items in sets", "correct": true, "feedback": "Correct! Sets remove duplicates." }, + { "answer": "Sets are unordered", "correct": true, "feedback": "Correct! Set order is not guaranteed." }, + { "answer": "Sets use curly braces {}", "correct": true, "feedback": "Correct! Sets look like {1, 2, 3}." }, + { "answer": "Sets can use numbers like s[0]", "correct": false, "feedback": "Sets cannot be indexed." } + ] + }, + { + "question": "What prints: set(['a','a','b'])?", + "type": "multiple_choice", + "answers": [ + { "answer": "{'a', 'a', 'b'}", "correct": false, "feedback": "Sets remove duplicates." }, + { "answer": "{'a', 'b'}", "correct": true, "feedback": "Correct! Sets keep only unique items." }, + { "answer": "['a', 'b']", "correct": false, "feedback": "Sets use {}, not []." }, + { "answer": "{}", "correct": false, "feedback": "The set has 2 items: 'a' and 'b'." } + ] + }, + { + "question": "How to create an empty dictionary?", + "type": "many_choice", + "answers": [ + { "code": "d = dict()", "correct": true, "feedback": "Correct! This creates an empty dictionary." }, + { "code": "d = {}", "correct": true, "feedback": "Correct! {} makes an empty dictionary." }, + { "code": "d = []", "correct": false, "feedback": "[] creates a list." }, + { "code": "d = set()", "correct": false, "feedback": "This creates a set." } + ] + }, + { + "question": "What does d.get('key') return if 'key' doesn't exist?", + "type": "multiple_choice", + "answers": [ + { "answer": "An error", "correct": false, "feedback": "get() returns None, not an error." }, + { "answer": "None", "correct": true, "feedback": "Correct! get() returns None for missing keys." }, + { "answer": "0", "correct": false, "feedback": "get() returns None for missing keys." }, + { "answer": "''", "correct": false, "feedback": "get() returns None for missing keys." } + ] + }, + { + "question": "Which method returns key-value pairs?", + "type": "multiple_choice", + "answers": [ + { "answer": "d.keys()", "correct": false, "feedback": "keys() returns only keys." }, + { "answer": "d.values()", "correct": false, "feedback": "values() returns only values." }, + { "answer": "d.items()", "correct": true, "feedback": "Correct! items() returns (key, value) tuples." }, + { "answer": "d.pairs()", "correct": false, "feedback": "No pairs() method exists. Use items()." } + ] + }, + { + "question": "What prints: d={'a':1, 'b':2}; print(d['b'])?", + "type": "multiple_choice", + "answers": [ + { "answer": "1", "correct": false, "feedback": "d['b'] is 2." }, + { "answer": "2", "correct": true, "feedback": "Correct! d['b'] equals 2." }, + { "answer": "b", "correct": false, "feedback": "It prints the value, not the key." }, + { "answer": "{'b': 2}", "correct": false, "feedback": "It prints just the value: 2." } + ] + }, + { + "question": "How to remove a dictionary item?", + "type": "many_choice", + "answers": [ + { "code": "d.remove('key')", "correct": false, "feedback": "No remove() method exists." }, + { "code": "del d['key']", "correct": true, "feedback": "Correct! del removes dictionary items." }, + { "code": "d.delete('key')", "correct": false, "feedback": "No delete() method exists." }, + { "code": "d.pop('key')", "correct": true, "feedback": "Correct! pop() removes and returns the value." } + ] + }, + { + "question": "What does 'x' in d check?", + "type": "multiple_choice", + "answers": [ + { "answer": "If 'x' is a value", "correct": false, "feedback": "'in' checks keys, not values." }, + { "answer": "If 'x' is a key", "correct": true, "feedback": "Correct! 'in' checks if 'x' is a key." }, + { "answer": "If 'x' is a key or value", "correct": false, "feedback": "'in' only checks keys." }, + { "answer": "If d is empty", "correct": false, "feedback": "'in' checks for a specific key." } + ] + }, + { + "question": "What is list comprehension syntax?", + "type": "multiple_choice", + "answers": [ + { "code": "[x for x in list]", "correct": true, "feedback": "Correct! This is list comprehension." }, + { "code": "for x in list: [x]", "correct": false, "feedback": "List comprehension uses [x for x in list]." }, + { "code": "list(x)", "correct": false, "feedback": "This converts to a list, not comprehension." }, + { "code": "{x for x in list}", "correct": false, "feedback": "This creates a set, not a list." } + ] + }, + { + "question": "What prints: [x*2 for x in [1,2,3]]?", + "type": "multiple_choice", + "answers": [ + { "answer": "[1, 2, 3]", "correct": false, "feedback": "Each number is multiplied by 2." }, + { "answer": "[2, 4, 6]", "correct": true, "feedback": "Correct! Each number is doubled." }, + { "answer": "[1, 4, 9]", "correct": false, "feedback": "This would be x*x (squaring)." }, + { "answer": "[3, 6, 9]", "correct": false, "feedback": "Each value is multiplied by 2, not 3." } + ] + }, + { + "question": "What does * do in function(*args)?", + "type": "multiple_choice", + "answers": [ + { "answer": "Multiplies all arguments", "correct": false, "feedback": "* unpacks arguments, doesn't multiply." }, + { "answer": "Unpacks items as separate arguments", "correct": true, "feedback": "Correct! * unpacks a list/tuple into arguments." }, + { "answer": "Creates a copy", "correct": false, "feedback": "* unpacks items, doesn't copy." }, + { "answer": "Reverses the order", "correct": false, "feedback": "* unpacks items, doesn't reverse." } + ] + }, + { + "question": "What does zip() do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Compresses files", "correct": false, "feedback": "zip() combines lists, not files." }, + { "answer": "Pairs items from multiple lists", "correct": true, "feedback": "Correct! zip() pairs corresponding elements." }, + { "answer": "Sorts lists", "correct": false, "feedback": "zip() combines lists, doesn't sort." }, + { "answer": "Reverses lists", "correct": false, "feedback": "zip() combines lists, doesn't reverse." } + ] + }, + { + "question": "What prints: list(zip([1,2], [3,4]))?", + "type": "multiple_choice", + "answers": [ + { "answer": "[(1, 2), (3, 4)]", "correct": false, "feedback": "zip() pairs 1st items, then 2nd items." }, + { "answer": "[(1, 3), (2, 4)]", "correct": true, "feedback": "Correct! zip() pairs corresponding items." }, + { "answer": "[1, 2, 3, 4]", "correct": false, "feedback": "zip() creates tuples, not a flat list." }, + { "answer": "[1, 3, 2, 4]", "correct": false, "feedback": "zip() creates tuples of pairs." } + ] + }, + { + "question": "When do you use a dictionary?", + "type": "multiple_choice", + "answers": [ + { "answer": "To store key-value pairs", "correct": true, "feedback": "Correct! Dictionaries map keys to values." }, + { "answer": "To store only numbers", "correct": false, "feedback": "Dictionaries can store any data types." }, + { "answer": "To remove duplicates", "correct": false, "feedback": "Sets remove duplicates, not dictionaries." }, + { "answer": "To sort items", "correct": false, "feedback": "Dictionaries are for key-value storage." } + ] + }, + { + "question": "What error: int('hello')?", + "type": "multiple_choice", + "answers": [ + { "answer": "TypeError", "correct": false, "feedback": "This raises ValueError for invalid conversion." }, + { "answer": "ValueError", "correct": true, "feedback": "Correct! ValueError for invalid string to int." }, + { "answer": "IndexError", "correct": false, "feedback": "IndexError is for invalid indices." }, + { "answer": "KeyError", "correct": false, "feedback": "KeyError is for missing dictionary keys." } + ] + } +] diff --git a/lessons/.jtl/Quiz_Data/Module_One_Quiz.json b/lessons/.jtl/Quiz_Data/Module_One_Quiz.json new file mode 100644 index 00000000..61d5a686 --- /dev/null +++ b/lessons/.jtl/Quiz_Data/Module_One_Quiz.json @@ -0,0 +1,302 @@ +[ + { + "question": "What does forward() do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Moves the turtle forward", "correct": true, "feedback": "Correct! forward() moves the turtle in the direction it's facing." }, + { "answer": "Turns the turtle", "correct": false, "feedback": "Incorrect. forward() moves, not turns." }, + { "answer": "Changes color", "correct": false, "feedback": "Incorrect. forward() is for movement." }, + { "answer": "Draws a circle", "correct": false, "feedback": "Incorrect. Use circle() to draw circles." } + ] + }, + { + "question": "Which command turns the turtle left?", + "type": "multiple_choice", + "answers": [ + { "answer": "left()", "correct": true, "feedback": "Correct! left() turns the turtle to the left." }, + { "answer": "turn()", "correct": false, "feedback": "Incorrect. Use left() or right()." }, + { "answer": "rotate()", "correct": false, "feedback": "Incorrect. Use left() to turn." }, + { "answer": "spin()", "correct": false, "feedback": "Incorrect. Use left() to turn left." } + ] + }, + { + "question": "How many degrees is a square corner?", + "type": "multiple_choice", + "answers": [ + { "answer": "90 degrees", "correct": true, "feedback": "Correct! Square corners are 90 degrees." }, + { "answer": "45 degrees", "correct": false, "feedback": "Incorrect. Squares have 90-degree corners." }, + { "answer": "180 degrees", "correct": false, "feedback": "Incorrect. That's a straight line." }, + { "answer": "360 degrees", "correct": false, "feedback": "Incorrect. That's a full circle." } + ] + }, + { + "question": "What does penup() do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Stops drawing when the turtle moves", "correct": true, "feedback": "Correct! penup() lifts the pen so the turtle doesn't draw." }, + { "answer": "Stops the turtle", "correct": false, "feedback": "Incorrect. The turtle can still move." }, + { "answer": "Moves the turtle up", "correct": false, "feedback": "Incorrect. It controls drawing, not direction." }, + { "answer": "Deletes the drawing", "correct": false, "feedback": "Incorrect. It just lifts the pen." } + ] + }, + { + "question": "When would you use penup()?", + "type": "multiple_choice", + "answers": [ + { "answer": "When you want to move without drawing", "correct": true, "feedback": "Correct! Use penup() to move the turtle without leaving a line." }, + { "answer": "When you want to draw faster", "correct": false, "feedback": "Incorrect. Use speed() to change drawing speed." }, + { "answer": "When you want to change colors", "correct": false, "feedback": "Incorrect. Use pencolor() to change colors." }, + { "answer": "When you want to stop the program", "correct": false, "feedback": "Incorrect. penup() just stops drawing." } + ] + }, + { + "question": "Which command changes the pen color?", + "type": "multiple_choice", + "answers": [ + { "answer": "pencolor()", "correct": true, "feedback": "Correct! pencolor() sets the drawing color." }, + { "answer": "color()", "correct": false, "feedback": "Close, but pencolor() is more specific for the pen." }, + { "answer": "setcolor()", "correct": false, "feedback": "Incorrect. Use pencolor()." }, + { "answer": "changecolor()", "correct": false, "feedback": "Incorrect. Use pencolor()." } + ] + }, + { + "question": "What does goto(100, 100) do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Moves turtle to position (100, 100)", "correct": true, "feedback": "Correct! goto() moves to specific coordinates." }, + { "answer": "Moves forward 100 steps", "correct": false, "feedback": "Incorrect. goto() moves to coordinates." }, + { "answer": "Turns 100 degrees", "correct": false, "feedback": "Incorrect. goto() is for position, not turning." }, + { "answer": "Draws 100 circles", "correct": false, "feedback": "Incorrect. goto() moves the turtle." } + ] + }, + { + "question": "What is a loop?", + "type": "multiple_choice", + "answers": [ + { "answer": "Code that repeats multiple times", "correct": true, "feedback": "Correct! Loops repeat code automatically." }, + { "answer": "Code that runs once", "correct": false, "feedback": "Incorrect. Loops repeat code." }, + { "answer": "A type of variable", "correct": false, "feedback": "Incorrect. Loops are for repetition." }, + { "answer": "A drawing command", "correct": false, "feedback": "Incorrect. Loops control how many times code runs." } + ] + }, + { + "question": "How many times does range(5) repeat?", + "type": "multiple_choice", + "answers": [ + { "answer": "5 times", "correct": true, "feedback": "Correct! range(5) creates 0, 1, 2, 3, 4 - that's 5 numbers." }, + { "answer": "4 times", "correct": false, "feedback": "Incorrect. range(5) gives you 5 numbers." }, + { "answer": "6 times", "correct": false, "feedback": "Incorrect. range(5) stops at 4." }, + { "answer": "Forever", "correct": false, "feedback": "Incorrect. range(5) repeats exactly 5 times." } + ] + }, + { + "question": "What is the correct syntax for a for loop?", + "type": "multiple_choice", + "answers": [ + { "code": "for i in range(5):\n tina.forward(50)", "correct": true, "feedback": "Correct! Use 'for', 'in', 'range()', and a colon." }, + { "code": "for i range(5)\n tina.forward(50)", "correct": false, "feedback": "Incorrect. Missing 'in' and the colon." }, + { "code": "loop 5 times:\n tina.forward(50)", "correct": false, "feedback": "Incorrect. Python uses 'for i in range()'." }, + { "code": "repeat(5):\n tina.forward(50)", "correct": false, "feedback": "Incorrect. Use 'for i in range(5):'." } + ] + }, + { + "question": "Why use loops instead of writing the same code many times?", + "type": "multiple_choice", + "answers": [ + { "answer": "Loops are shorter and easier to change", "correct": true, "feedback": "Correct! Loops save time and reduce mistakes." }, + { "answer": "Loops make programs slower", "correct": false, "feedback": "Incorrect. Loops are efficient." }, + { "answer": "Loops are harder to read", "correct": false, "feedback": "Incorrect. Loops make code clearer." }, + { "answer": "You can't use loops in Python", "correct": false, "feedback": "Incorrect. Loops are essential in Python." } + ] + }, + { + "question": "What is a variable?", + "type": "multiple_choice", + "answers": [ + { "answer": "A container that stores a value", "correct": true, "feedback": "Correct! Variables hold information you can use later." }, + { "answer": "A type of loop", "correct": false, "feedback": "Incorrect. Variables store data." }, + { "answer": "A drawing command", "correct": false, "feedback": "Incorrect. Variables store values." }, + { "answer": "A color", "correct": false, "feedback": "Incorrect. Variables can store many types of data." } + ] + }, + { + "question": "Which creates a variable named 'size' with value 100?", + "type": "multiple_choice", + "answers": [ + { "code": "size = 100", "correct": true, "feedback": "Correct! Use = to assign a value to a variable." }, + { "code": "size == 100", "correct": false, "feedback": "Incorrect. == checks equality. Use = to assign." }, + { "code": "100 = size", "correct": false, "feedback": "Incorrect. Variable name goes on the left." }, + { "code": "var size = 100", "correct": false, "feedback": "Incorrect. Python doesn't use 'var'." } + ] + }, + { + "question": "What happens when you run this code: x = 5; x = x + 1?", + "type": "multiple_choice", + "answers": [ + { "answer": "x becomes 6", "correct": true, "feedback": "Correct! x is updated to its old value plus 1." }, + { "answer": "x stays 5", "correct": false, "feedback": "Incorrect. x gets a new value." }, + { "answer": "x becomes 0", "correct": false, "feedback": "Incorrect. x increases by 1." }, + { "answer": "Error", "correct": false, "feedback": "Incorrect. This is valid Python code." } + ] + }, + { + "question": "What is a function?", + "type": "multiple_choice", + "answers": [ + { "answer": "Reusable code with a name", "correct": true, "feedback": "Correct! Functions are named blocks of code you can use repeatedly." }, + { "answer": "A variable", "correct": false, "feedback": "Incorrect. Functions contain code, not just values." }, + { "answer": "A loop", "correct": false, "feedback": "Incorrect. Functions and loops are different." }, + { "answer": "A color", "correct": false, "feedback": "Incorrect. Functions are code blocks." } + ] + }, + { + "question": "Which keyword starts a function definition?", + "type": "multiple_choice", + "answers": [ + { "answer": "def", "correct": true, "feedback": "Correct! Use 'def' to define a function." }, + { "answer": "function", "correct": false, "feedback": "Incorrect. Python uses 'def'." }, + { "answer": "func", "correct": false, "feedback": "Incorrect. Use 'def'." }, + { "answer": "define", "correct": false, "feedback": "Incorrect. The keyword is 'def'." } + ] + }, + { + "question": "What is the correct way to define a function?", + "type": "multiple_choice", + "answers": [ + { "code": "def my_function():\n tina.forward(50)", "correct": true, "feedback": "Correct! Use def, name, parentheses, colon, and indented code." }, + { "code": "def my_function()\n tina.forward(50)", "correct": false, "feedback": "Incorrect. Missing the colon after ()." }, + { "code": "function my_function():\n tina.forward(50)", "correct": false, "feedback": "Incorrect. Use 'def', not 'function'." }, + { "code": "my_function():\n tina.forward(50)", "correct": false, "feedback": "Incorrect. Missing 'def' keyword." } + ] + }, + { + "question": "When would you use a function?", + "type": "multiple_choice", + "answers": [ + { "answer": "When you want to reuse the same code", "correct": true, "feedback": "Correct! Functions let you use code multiple times." }, + { "answer": "When you want to store a number", "correct": false, "feedback": "Incorrect. Use variables to store values." }, + { "answer": "When you want to repeat code a specific number of times", "correct": false, "feedback": "Incorrect. That's what loops are for." }, + { "answer": "When you want to change colors", "correct": false, "feedback": "Incorrect. Functions are for organizing reusable code." } + ] + }, + { + "question": "What does return do in a function?", + "type": "multiple_choice", + "answers": [ + { "answer": "Sends a value back from the function", "correct": true, "feedback": "Correct! return gives back a result." }, + { "answer": "Prints to the screen", "correct": false, "feedback": "Incorrect. That's what print() does." }, + { "answer": "Stops the entire program", "correct": false, "feedback": "Incorrect. return only exits the function." }, + { "answer": "Creates a variable", "correct": false, "feedback": "Incorrect. return sends values back." } + ] + }, + { + "question": "What is a list?", + "type": "multiple_choice", + "answers": [ + { "answer": "A collection of items in order", "correct": true, "feedback": "Correct! Lists hold multiple items like [1, 2, 3]." }, + { "answer": "A single number", "correct": false, "feedback": "Incorrect. Lists hold multiple items." }, + { "answer": "A function", "correct": false, "feedback": "Incorrect. Lists are data containers." }, + { "answer": "A loop", "correct": false, "feedback": "Incorrect. Lists store data." } + ] + }, + { + "question": "Which is the correct way to create a list?", + "type": "multiple_choice", + "answers": [ + { "code": "colors = ['red', 'blue', 'green']", "correct": true, "feedback": "Correct! Use square brackets with items separated by commas." }, + { "code": "colors = ('red', 'blue', 'green')", "correct": false, "feedback": "Incorrect. Parentheses create tuples. Use []." }, + { "code": "colors = {'red', 'blue', 'green'}", "correct": false, "feedback": "Incorrect. Curly braces create sets. Use []." }, + { "code": "colors = 'red', 'blue', 'green'", "correct": false, "feedback": "Incorrect. Use square brackets []." } + ] + }, + { + "question": "How do you get the first item from a list?", + "type": "multiple_choice", + "answers": [ + { "answer": "list[0]", "correct": true, "feedback": "Correct! Python lists start at index 0." }, + { "answer": "list[1]", "correct": false, "feedback": "Incorrect. That's the second item. Use [0]." }, + { "answer": "list.first()", "correct": false, "feedback": "Incorrect. Use [0] to get the first item." }, + { "answer": "first(list)", "correct": false, "feedback": "Incorrect. Use list[0]." } + ] + }, + { + "question": "What does append() do to a list?", + "type": "multiple_choice", + "answers": [ + { "answer": "Adds an item to the end", "correct": true, "feedback": "Correct! append() adds items to the end of the list." }, + { "answer": "Removes an item", "correct": false, "feedback": "Incorrect. Use remove() to delete items." }, + { "answer": "Sorts the list", "correct": false, "feedback": "Incorrect. Use sort() to order items." }, + { "answer": "Counts items", "correct": false, "feedback": "Incorrect. Use len() to count items." } + ] + }, + { + "question": "How do you loop through each item in a list?", + "type": "multiple_choice", + "answers": [ + { "code": "for item in my_list:\n print(item)", "correct": true, "feedback": "Correct! This loops through each item in the list." }, + { "code": "for my_list in item:\n print(item)", "correct": false, "feedback": "Incorrect. It should be 'for item in my_list'." }, + { "code": "loop my_list:\n print(item)", "correct": false, "feedback": "Incorrect. Use 'for item in my_list:'." }, + { "code": "for item:\n print(my_list)", "correct": false, "feedback": "Incorrect. Missing 'in my_list'." } + ] + }, + { + "question": "What does circle(50) draw?", + "type": "multiple_choice", + "answers": [ + { "answer": "A circle with radius 50", "correct": true, "feedback": "Correct! The number is the radius." }, + { "answer": "50 circles", "correct": false, "feedback": "Incorrect. It draws one circle." }, + { "answer": "A square", "correct": false, "feedback": "Incorrect. circle() draws circles." }, + { "answer": "A line 50 units long", "correct": false, "feedback": "Incorrect. Use forward() for lines." } + ] + }, + { + "question": "Where is position (0, 0) on the turtle screen?", + "type": "multiple_choice", + "answers": [ + { "answer": "Center of the screen", "correct": true, "feedback": "Correct! (0, 0) is in the middle." }, + { "answer": "Top left corner", "correct": false, "feedback": "Incorrect. (0, 0) is the center." }, + { "answer": "Bottom right corner", "correct": false, "feedback": "Incorrect. (0, 0) is the center." }, + { "answer": "Top right corner", "correct": false, "feedback": "Incorrect. (0, 0) is in the middle." } + ] + }, + { + "question": "What does speed(0) do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Makes turtle draw fastest", "correct": true, "feedback": "Correct! speed(0) is the fastest setting." }, + { "answer": "Stops the turtle", "correct": false, "feedback": "Incorrect. 0 means fastest." }, + { "answer": "Makes turtle slowest", "correct": false, "feedback": "Incorrect. 0 is fastest." }, + { "answer": "Makes turtle invisible", "correct": false, "feedback": "Incorrect. Use hideturtle() for that." } + ] + }, + { + "question": "What does # at the start of a line mean?", + "type": "multiple_choice", + "answers": [ + { "answer": "It's a comment (Python ignores it)", "correct": true, "feedback": "Correct! Comments explain code to humans." }, + { "answer": "It's a command", "correct": false, "feedback": "Incorrect. # creates comments." }, + { "answer": "It causes an error", "correct": false, "feedback": "Incorrect. Comments are ignored." }, + { "answer": "It makes code run faster", "correct": false, "feedback": "Incorrect. Comments don't affect speed." } + ] + }, + { + "question": "Which code correctly draws a triangle?", + "type": "multiple_choice", + "answers": [ + { "code": "for i in range(3):\n tina.forward(100)\n tina.left(120)", "correct": true, "feedback": "Correct! Triangle has 3 sides and 120-degree turns." }, + { "code": "for i in range(4):\n tina.forward(100)\n tina.left(120)", "correct": false, "feedback": "Incorrect. Triangle has 3 sides, not 4." }, + { "code": "for i in range(3):\n tina.forward(100)\n tina.left(90)", "correct": false, "feedback": "Incorrect. Triangle needs 120-degree turns." }, + { "code": "tina.triangle(100)", "correct": false, "feedback": "Incorrect. There's no triangle() method." } + ] + }, + { + "question": "What does exitonclick() do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Keeps window open until you click", "correct": true, "feedback": "Correct! The window waits for a click before closing." }, + { "answer": "Closes window immediately", "correct": false, "feedback": "Incorrect. It waits for a click." }, + { "answer": "Opens a new window", "correct": false, "feedback": "Incorrect. It controls the current window." }, + { "answer": "Saves your drawing", "correct": false, "feedback": "Incorrect. It manages window closing." } + ] + } +] diff --git a/lessons/.jtl/Quiz_Data/Module_Three_Quiz.json b/lessons/.jtl/Quiz_Data/Module_Three_Quiz.json new file mode 100644 index 00000000..f0fd30ff --- /dev/null +++ b/lessons/.jtl/Quiz_Data/Module_Three_Quiz.json @@ -0,0 +1,203 @@ +[ + { + "question": "What does `range(5)` print?", + "type": "multiple_choice", + "answers": [ + { "answer": "0, 1, 2, 3, 4", "correct": true, "feedback": "Correct! range(5) starts at 0 and stops before 5." }, + { "answer": "1, 2, 3, 4, 5", "correct": false, "feedback": "Incorrect. range() starts at 0 by default." }, + { "answer": "5, 4, 3, 2, 1", "correct": false, "feedback": "Incorrect. range() counts forward, not backward." }, + { "answer": "1, 2, 3, 4", "correct": false, "feedback": "Incorrect. range() starts at 0, not 1." } + ] + }, + { + "question": "Which can you loop through?", + "type": "many_choice", + "answers": [ + { "answer": "Lists", "correct": true, "feedback": "Correct! Lists are iterable." }, + { "answer": "Strings", "correct": true, "feedback": "Correct! You can loop through each character." }, + { "answer": "Tuples", "correct": true, "feedback": "Correct! Tuples are iterable." }, + { "answer": "Ranges", "correct": true, "feedback": "Correct! Ranges are iterable." }, + { "answer": "Integers", "correct": false, "feedback": "Incorrect. You cannot loop through a single integer." } + ] + }, + { + "question": "What is `list(range(2, 10, 2))`?", + "type": "multiple_choice", + "answers": [ + { "answer": "[2, 4, 6, 8]", "correct": true, "feedback": "Correct! Start at 2, stop before 10, step by 2." }, + { "answer": "[2, 4, 6, 8, 10]", "correct": false, "feedback": "Incorrect. range() stops before the stop value." }, + { "answer": "[0, 2, 4, 6, 8]", "correct": false, "feedback": "Incorrect. It starts at 2, not 0." }, + { "answer": "[2, 3, 4, 5, 6, 7, 8, 9]", "correct": false, "feedback": "Incorrect. The step is 2, not 1." } + ] + }, + { + "question": "What is a tuple?", + "type": "multiple_choice", + "answers": [ + { "answer": "An immutable list", "correct": true, "feedback": "Correct! Tuples cannot be changed after creation." }, + { "answer": "A list you can change", "correct": false, "feedback": "Incorrect. Tuples are immutable." }, + { "answer": "A dictionary", "correct": false, "feedback": "Incorrect. Tuples use () not {}." }, + { "answer": "A function", "correct": false, "feedback": "Incorrect. Tuples are a data structure." } + ] + }, + { + "question": "How do you create a one-item tuple?", + "type": "multiple_choice", + "answers": [ + { "answer": "(1,)", "correct": true, "feedback": "Correct! The comma is required." }, + { "answer": "(1)", "correct": false, "feedback": "Incorrect. This is just 1 in parentheses." }, + { "answer": "[1]", "correct": false, "feedback": "Incorrect. This creates a list." }, + { "answer": "{1}", "correct": false, "feedback": "Incorrect. This creates a set." } + ] + }, + { + "question": "What does `a, b = (5, 10)` do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Sets a=5 and b=10", "correct": true, "feedback": "Correct! This is tuple unpacking." }, + { "answer": "Creates an error", "correct": false, "feedback": "Incorrect. This is valid Python syntax." }, + { "answer": "Sets a=10 and b=5", "correct": false, "feedback": "Incorrect. Order matters in unpacking." }, + { "answer": "Sets both a and b to (5, 10)", "correct": false, "feedback": "Incorrect. The tuple is split between variables." } + ] + }, + { + "question": "What is slicing?", + "type": "multiple_choice", + "answers": [ + { "answer": "Getting a portion of a list", "correct": true, "feedback": "Correct! Slicing extracts part of a sequence." }, + { "answer": "Removing all items from a list", "correct": false, "feedback": "Incorrect. That's clear() or del." }, + { "answer": "Sorting a list", "correct": false, "feedback": "Incorrect. That's sort() or sorted()." }, + { "answer": "Adding to a list", "correct": false, "feedback": "Incorrect. That's append() or insert()." } + ] + }, + { + "question": "Can tuples and lists both be sliced?", + "type": "many_choice", + "answers": [ + { "answer": "Yes, both support slicing", "correct": true, "feedback": "Correct! Both can use [start:stop]." }, + { "answer": "Yes, both support indexing", "correct": true, "feedback": "Correct! Both can use [index]." }, + { "answer": "Yes, both support looping", "correct": true, "feedback": "Correct! Both are iterable." }, + { "answer": "Yes, both can be modified", "correct": false, "feedback": "Incorrect. Tuples are immutable." } + ] + }, + { + "question": "What does `[::-1]` do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Reverses a list", "correct": true, "feedback": "Correct! Negative step reverses order." }, + { "answer": "Sorts a list", "correct": false, "feedback": "Incorrect. That's sort() or sorted()." }, + { "answer": "Removes the first item", "correct": false, "feedback": "Incorrect. That's [1:]." }, + { "answer": "Creates an error", "correct": false, "feedback": "Incorrect. This is valid slice syntax." } + ] + }, + { + "question": "What does enumerate() do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Adds index numbers to items", "correct": true, "feedback": "Correct! enumerate() returns (index, item) pairs." }, + { "answer": "Counts items", "correct": false, "feedback": "Incorrect. That's len()." }, + { "answer": "Sorts items", "correct": false, "feedback": "Incorrect. That's sorted()." }, + { "answer": "Removes duplicates", "correct": false, "feedback": "Incorrect. That's set()." } + ] + }, + { + "question": "What does zip() do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Combines two lists into pairs", "correct": true, "feedback": "Correct! zip() pairs items from each list." }, + { "answer": "Compresses a file", "correct": false, "feedback": "Incorrect. That's file compression, not Python's zip()." }, + { "answer": "Joins two lists end-to-end", "correct": false, "feedback": "Incorrect. That's concatenation with +." }, + { "answer": "Sorts two lists", "correct": false, "feedback": "Incorrect. That's sorted()." } + ] + }, + { + "question": "What does split() do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Breaks a string into a list", "correct": true, "feedback": "Correct! split() divides text into pieces." }, + { "answer": "Joins a list into a string", "correct": false, "feedback": "Incorrect. That's join()." }, + { "answer": "Removes spaces", "correct": false, "feedback": "Incorrect. That's strip()." }, + { "answer": "Makes text lowercase", "correct": false, "feedback": "Incorrect. That's lower()." } + ] + }, + { + "question": "What is true about slicing?", + "type": "many_choice", + "answers": [ + { "answer": "Stop index is not included", "correct": true, "feedback": "Correct! [1:3] gives index 1 and 2 only." }, + { "answer": "Can use negative numbers", "correct": true, "feedback": "Correct! [-1] gets the last item." }, + { "answer": "Step can be negative", "correct": true, "feedback": "Correct! [::-1] reverses." }, + { "answer": "Causes error if out of range", "correct": false, "feedback": "Incorrect. Slicing is forgiving." } + ] + }, + { + "question": "What does append() do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Adds item to end of list", "correct": true, "feedback": "Correct! append() adds to the end." }, + { "answer": "Adds item to start of list", "correct": false, "feedback": "Incorrect. That's insert(0, item)." }, + { "answer": "Removes last item", "correct": false, "feedback": "Incorrect. That's pop()." }, + { "answer": "Sorts the list", "correct": false, "feedback": "Incorrect. That's sort()." } + ] + }, + { + "question": "What's the difference: sorted() vs sort()?", + "type": "multiple_choice", + "answers": [ + { "answer": "sorted() returns new list; sort() changes original", "correct": true, "feedback": "Correct! sorted() creates a copy." }, + { "answer": "No difference", "correct": false, "feedback": "Incorrect. They handle the original list differently." }, + { "answer": "sort() returns new list; sorted() changes original", "correct": false, "feedback": "Incorrect. It's the opposite." }, + { "answer": "sorted() only works on numbers", "correct": false, "feedback": "Incorrect. Both work on any comparable items." } + ] + }, + { + "question": "What does enumerate() start at?", + "type": "multiple_choice", + "answers": [ + { "answer": "0", "correct": true, "feedback": "Correct! enumerate() starts counting at 0." }, + { "answer": "1", "correct": false, "feedback": "Incorrect. Python uses 0-based indexing." }, + { "answer": "It depends on the list", "correct": false, "feedback": "Incorrect. It always starts at 0 by default." }, + { "answer": "-1", "correct": false, "feedback": "Incorrect. enumerate() counts forward from 0." } + ] + }, + { + "question": "What does join() do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Combines list items into a string", "correct": true, "feedback": "Correct! join() makes a string from a list." }, + { "answer": "Splits a string into a list", "correct": false, "feedback": "Incorrect. That's split()." }, + { "answer": "Adds two lists together", "correct": false, "feedback": "Incorrect. That's concatenation with +." }, + { "answer": "Removes items from a list", "correct": false, "feedback": "Incorrect. That's remove() or pop()." } + ] + }, + { + "question": "How do you count backward with range()?", + "type": "multiple_choice", + "answers": [ + { "answer": "Use negative step: range(10, 0, -1)", "correct": true, "feedback": "Correct! Negative step counts backward." }, + { "answer": "range(0, 10)", "correct": false, "feedback": "Incorrect. This counts forward." }, + { "answer": "range(10)", "correct": false, "feedback": "Incorrect. This counts forward from 0 to 9." }, + { "answer": "range(-10, 0)", "correct": false, "feedback": "Incorrect. This counts from -10 to -1." } + ] + }, + { + "question": "What does break do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Exits the loop immediately", "correct": true, "feedback": "Correct! break stops the loop." }, + { "answer": "Skips to next iteration", "correct": false, "feedback": "Incorrect. That's continue." }, + { "answer": "Pauses the loop", "correct": false, "feedback": "Incorrect. break ends the loop." }, + { "answer": "Restarts the loop", "correct": false, "feedback": "Incorrect. break exits completely." } + ] + }, + { + "question": "What does continue do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Skips to next loop iteration", "correct": true, "feedback": "Correct! continue skips remaining code in current iteration." }, + { "answer": "Exits the loop", "correct": false, "feedback": "Incorrect. That's break." }, + { "answer": "Pauses the loop", "correct": false, "feedback": "Incorrect. continue moves to next iteration." }, + { "answer": "Restarts from beginning", "correct": false, "feedback": "Incorrect. continue goes to next iteration." } + ] + } +] diff --git a/lessons/.jtl/Quiz_Data/Module_Two_Quiz.json b/lessons/.jtl/Quiz_Data/Module_Two_Quiz.json new file mode 100644 index 00000000..87a86bb2 --- /dev/null +++ b/lessons/.jtl/Quiz_Data/Module_Two_Quiz.json @@ -0,0 +1,202 @@ +[ + { + "question": "What is an int in Python?", + "type": "multiple_choice", + "answers": [ + { "answer": "A whole number", "correct": true, "feedback": "Correct! int stands for integer and represents whole numbers like 5, -10, or 0." }, + { "answer": "A decimal number", "correct": false, "feedback": "Incorrect. Decimal numbers use the float type." }, + { "answer": "A text value", "correct": false, "feedback": "Incorrect. Text values use the str type." }, + { "answer": "A True/False value", "correct": false, "feedback": "Incorrect. True/False values use the bool type." } + ] + }, + { + "question": "What is a boolean?", + "type": "multiple_choice", + "answers": [ + { "answer": "True or False value", "correct": true, "feedback": "Correct! A boolean (bool) can only be True or False." }, + { "answer": "A number", "correct": false, "feedback": "Incorrect. Numbers are int or float types." }, + { "answer": "A word or sentence", "correct": false, "feedback": "Incorrect. Words and sentences are strings (str)." }, + { "answer": "A list of items", "correct": false, "feedback": "Incorrect. Lists are a different data type." } + ] + }, + { + "question": "What does the % operator do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Returns the remainder", "correct": true, "feedback": "Correct! 7 % 3 gives 1 because 7 divided by 3 leaves a remainder of 1." }, + { "answer": "Divides numbers", "correct": false, "feedback": "Incorrect. Use / or // for division." }, + { "answer": "Multiplies numbers", "correct": false, "feedback": "Incorrect. Use * for multiplication." }, + { "answer": "Calculates percentage", "correct": false, "feedback": "Incorrect. Despite the symbol, % finds the remainder, not percentages." } + ] + }, + { + "question": "What is // used for?", + "type": "multiple_choice", + "answers": [ + { "answer": "Division without decimals", "correct": true, "feedback": "Correct! 11 // 4 gives 2, dropping the decimal part." }, + { "answer": "Regular division", "correct": false, "feedback": "Incorrect. Regular division uses /." }, + { "answer": "Finding remainders", "correct": false, "feedback": "Incorrect. Use % to find remainders." }, + { "answer": "Adding comments", "correct": false, "feedback": "Incorrect. Comments use #." } + ] + }, + { + "question": "What does str() do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Converts to text", "correct": true, "feedback": "Correct! str(42) converts the number 42 to the text '42'." }, + { "answer": "Converts to a number", "correct": false, "feedback": "Incorrect. Use int() or float() for numbers." }, + { "answer": "Checks the data type", "correct": false, "feedback": "Incorrect. Use type() to check data types." }, + { "answer": "Makes text uppercase", "correct": false, "feedback": "Incorrect. Use upper() for uppercase." } + ] + }, + { + "question": "Which is correct Python syntax?", + "type": "multiple_choice", + "answers": [ + { "code": "if x == 5:", "correct": true, "feedback": "Correct! Use == for comparison and : to start the if block." }, + { "code": "if x = 5:", "correct": false, "feedback": "Incorrect. Use = for assignment, but == for comparison." }, + { "code": "if x == 5", "correct": false, "feedback": "Incorrect. Missing the colon (:) at the end." }, + { "code": "if (x == 5):", "correct": false, "feedback": "Incorrect. Parentheses aren't needed in Python if statements." } + ] + }, + { + "question": "What does upper() do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Makes text UPPERCASE", "correct": true, "feedback": "Correct! 'hello'.upper() returns 'HELLO'." }, + { "answer": "Makes text lowercase", "correct": false, "feedback": "Incorrect. Use lower() for lowercase." }, + { "answer": "Capitalizes first letter", "correct": false, "feedback": "Incorrect. Use title() or capitalize() for that." }, + { "answer": "Removes spaces", "correct": false, "feedback": "Incorrect. Use strip() to remove spaces." } + ] + }, + { + "question": "What is 10 % 3?", + "type": "multiple_choice", + "answers": [ + { "answer": "1", "correct": true, "feedback": "Correct! 10 divided by 3 is 3 with remainder 1." }, + { "answer": "3", "correct": false, "feedback": "Incorrect. That's 10 // 3 (quotient without remainder)." }, + { "answer": "3.33", "correct": false, "feedback": "Incorrect. That's 10 / 3 (regular division)." }, + { "answer": "0", "correct": false, "feedback": "Incorrect. There is a remainder when dividing 10 by 3." } + ] + }, + { + "question": "Which creates a string?", + "type": "multiple_choice", + "answers": [ + { "code": "name = \"Alice\"", "correct": true, "feedback": "Correct! Quotes create strings in Python." }, + { "code": "name = Alice", "correct": false, "feedback": "Incorrect. Without quotes, Python looks for a variable named Alice." }, + { "code": "name = str", "correct": false, "feedback": "Incorrect. This assigns the str type itself, not a string value." }, + { "code": "name = 'Alice", "correct": false, "feedback": "Incorrect. Missing closing quote." } + ] + }, + { + "question": "What does type() tell you?", + "type": "multiple_choice", + "answers": [ + { "answer": "The data type", "correct": true, "feedback": "Correct! type(5) returns int, type('hi') returns str." }, + { "answer": "The value", "correct": false, "feedback": "Incorrect. type() shows the type, not the value itself." }, + { "answer": "The length", "correct": false, "feedback": "Incorrect. Use len() for length." }, + { "answer": "If it's valid code", "correct": false, "feedback": "Incorrect. type() doesn't validate code syntax." } + ] + }, + { + "question": "What does == check?", + "type": "multiple_choice", + "answers": [ + { "answer": "If values are equal", "correct": true, "feedback": "Correct! 5 == 5 returns True because they're equal." }, + { "answer": "Assigns a value", "correct": false, "feedback": "Incorrect. Use = for assignment, not ==." }, + { "answer": "If values are not equal", "correct": false, "feedback": "Incorrect. Use != for not equal." }, + { "answer": "If one is greater", "correct": false, "feedback": "Incorrect. Use > for greater than." } + ] + }, + { + "question": "What is 15 // 4?", + "type": "multiple_choice", + "answers": [ + { "answer": "3", "correct": true, "feedback": "Correct! Floor division gives 3, ignoring the remainder." }, + { "answer": "3.75", "correct": false, "feedback": "Incorrect. That's regular division (15 / 4)." }, + { "answer": "4", "correct": false, "feedback": "Incorrect. 4 goes into 15 three times, not four." }, + { "answer": "3.0", "correct": false, "feedback": "Incorrect. With integers, // returns an integer (3), not a float." } + ] + }, + { + "question": "What does strip() do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Removes spaces from ends", "correct": true, "feedback": "Correct! ' hi '.strip() returns 'hi'." }, + { "answer": "Removes all spaces", "correct": false, "feedback": "Incorrect. strip() only removes spaces from the start and end." }, + { "answer": "Splits text into parts", "correct": false, "feedback": "Incorrect. Use split() to divide text." }, + { "answer": "Makes text lowercase", "correct": false, "feedback": "Incorrect. Use lower() for lowercase." } + ] + }, + { + "question": "What causes a TypeError?", + "type": "multiple_choice", + "answers": [ + { "code": "5 + \"10\"", "correct": true, "feedback": "Correct! Can't add a number and string directly. Convert first." }, + { "code": "5 + 10", "correct": false, "feedback": "Incorrect. This is valid and equals 15." }, + { "code": "\"5\" + \"10\"", "correct": false, "feedback": "Incorrect. This is valid string concatenation ('510')." }, + { "code": "int(\"10\") + 5", "correct": false, "feedback": "Incorrect. This is valid after conversion (equals 15)." } + ] + }, + { + "question": "What does 'and' require?", + "type": "multiple_choice", + "answers": [ + { "answer": "Both conditions True", "correct": true, "feedback": "Correct! True and True is True, but anything else is False." }, + { "answer": "At least one condition True", "correct": false, "feedback": "Incorrect. That's what 'or' requires." }, + { "answer": "Both conditions False", "correct": false, "feedback": "Incorrect. Then the result would be False." }, + { "answer": "One True, one False", "correct": false, "feedback": "Incorrect. Then 'and' returns False." } + ] + }, + { + "question": "What is float used for?", + "type": "multiple_choice", + "answers": [ + { "answer": "Decimal numbers", "correct": true, "feedback": "Correct! float represents numbers with decimals like 3.14 or 2.5." }, + { "answer": "Whole numbers only", "correct": false, "feedback": "Incorrect. Use int for whole numbers." }, + { "answer": "Text values", "correct": false, "feedback": "Incorrect. Use str for text." }, + { "answer": "True/False values", "correct": false, "feedback": "Incorrect. Use bool for True/False." } + ] + }, + { + "question": "Which starts an if statement?", + "type": "multiple_choice", + "answers": [ + { "code": "if x > 5:", "correct": true, "feedback": "Correct! Use if, a condition, then a colon." }, + { "code": "if x > 5", "correct": false, "feedback": "Incorrect. Missing the colon (:)." }, + { "code": "if: x > 5", "correct": false, "feedback": "Incorrect. The colon comes after the condition." }, + { "code": "x > 5 if:", "correct": false, "feedback": "Incorrect. Wrong order; start with 'if'." } + ] + }, + { + "question": "What does lower() do?", + "type": "multiple_choice", + "answers": [ + { "answer": "Makes text lowercase", "correct": true, "feedback": "Correct! 'HELLO'.lower() returns 'hello'." }, + { "answer": "Makes text uppercase", "correct": false, "feedback": "Incorrect. Use upper() for uppercase." }, + { "answer": "Removes spaces", "correct": false, "feedback": "Incorrect. Use strip() to remove spaces." }, + { "answer": "Converts to number", "correct": false, "feedback": "Incorrect. Use int() or float() for numbers." } + ] + }, + { + "question": "What is 8 % 4?", + "type": "multiple_choice", + "answers": [ + { "answer": "0", "correct": true, "feedback": "Correct! 8 divided by 4 is 2 with no remainder." }, + { "answer": "2", "correct": false, "feedback": "Incorrect. That's 8 // 4 (quotient). % gives remainder (0)." }, + { "answer": "4", "correct": false, "feedback": "Incorrect. The remainder is 0, not 4." }, + { "answer": "8", "correct": false, "feedback": "Incorrect. The remainder of 8 % 4 is 0." } + ] + }, + { + "question": "What does != mean?", + "type": "multiple_choice", + "answers": [ + { "answer": "Not equal", "correct": true, "feedback": "Correct! 5 != 3 is True because they're not equal." }, + { "answer": "Equal", "correct": false, "feedback": "Incorrect. Use == for equal." }, + { "answer": "Greater than", "correct": false, "feedback": "Incorrect. Use > for greater than." }, + { "answer": "Less than", "correct": false, "feedback": "Incorrect. Use < for less than." } + ] + } +] \ No newline at end of file diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index 82a3655a..3252c3ee 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -2,7 +2,7 @@ name: Python Apprentice module_dir: .. uid: 283c41e4-8abe-49bd-9335-e19ba693277b modules: -- name: Turtles +- name: Introduction to Python with Turtle Graphics uid: Io0hFJiW lessons: - name: Welcome @@ -54,28 +54,33 @@ modules: - name: Loops uid: abX8sNwB exercise: 10_Turtles/40_Loops/10_Loops.ipynb - - name: Loop with Turtle + - name: Loop With Turtle + uid: 85lNu5qj exercise: 10_Turtles/40_Loops/20_Loop_with_Turtle.py display: true - - name: Loop with Turtle + - name: Loop With Turtle + uid: BpGnQq64 exercise: 10_Turtles/40_Loops/30_Loop_with_Turtle.py - name: Variables and Functions uid: O1qsljMz lessons: - - name: Variables and Functions + - name: Variables uid: HOBo0wvj - exercise: 10_Turtles/50_Variables_and_Functions/10_Variables_and_Functions.ipynb + exercise: 10_Turtles/50_Variables_and_Functions/10_Variables.ipynb - name: Efficient Turtle exercise: 10_Turtles/50_Variables_and_Functions/20_Efficient_Turtle.py display: true + - name: Functions + uid: 0CwXIYSb + exercise: 10_Turtles/50_Variables_and_Functions/20_Functions.ipynb - name: More Turtle Programs uid: I4bCPbWz lessons: - name: More Turtle Programs uid: IloYptI2 exercise: 10_Turtles/60_More_Turtle_Programs/10_More_Turtle_Programs.ipynb - - name: More Turtle programs - exercise: 10_Turtles/60_More_Turtle_Programs/20_More_Turtle_programs.py + - name: More Turtle Programs + exercise: 10_Turtles/60_More_Turtle_Programs/20_More_Turtle_Programs.py - name: More Turtle Programs exercise: 10_Turtles/60_More_Turtle_Programs/30_More_Turtle_Programs.py - name: More Turtle Programs @@ -92,113 +97,132 @@ modules: exercise: 10_Turtles/70_Projects/30_Tash_Me_Click.py - name: Tash Me Twirl exercise: 10_Turtles/70_Projects/40_Tash_Me_Twirl.py - - name: Introducting Lists + - name: Introducing Lists uid: g4kLhJ2U lessons: - name: Lists uid: 0KEhJUGe - exercise: 10_Turtles/80_Introducting_Lists/10_Lists.ipynb + exercise: 10_Turtles/80_Introducing_Lists/10_Lists.ipynb - name: Color Lines - exercise: 10_Turtles/80_Introducting_Lists/20_Color_Lines.py + exercise: 10_Turtles/80_Introducing_Lists/20_Color_Lines.py display: true - name: Graphics Projects uid: 3shr4ruR lessons: - name: Flaming Ninja Star + uid: ejUIkGvk exercise: 10_Turtles/90_Graphics_Projects/10_Flaming_Ninja_Star.py display: true - name: Crazy Spiral + uid: zfzMbyH7 exercise: 10_Turtles/90_Graphics_Projects/20_Crazy_Spiral.py - name: Pentagon Crazy + uid: QG1OFNKY exercise: 10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py display: true - name: Turtle Spiral + uid: rkftzcAi exercise: 10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py display: true -- name: Types and Logic +- name: PCEP Alignment uid: ryHvW6vk lessons: - - name: Numbers and Strings + - name: Operators and Types uid: HXQZ0Iui - exercise: 20_Types_and_Logic/10_Numbers_and_Strings.ipynb + exercise: 20_Types_and_Logic/10_Operators_and_Types.ipynb + - name: String Operations + uid: K1jP2RdA + exercise: 20_Types_and_Logic/20_String_Operations.ipynb - name: Control Flow uid: g6JPkFUs - exercise: 20_Types_and_Logic/20_Control_Flow.ipynb + exercise: 20_Types_and_Logic/30_Control_Flow.ipynb + - name: Working With Numbers + uid: b1Q9Y1j1 + exercise: 20_Types_and_Logic/40_Working_With_Numbers.ipynb - name: My Ages - exercise: 20_Types_and_Logic/30_My_Ages.py + exercise: 20_Types_and_Logic/50_My_Ages.py display: true - name: Simple Adder - exercise: 20_Types_and_Logic/40_Simple_Adder.py + exercise: 20_Types_and_Logic/60_Simple_Adder.py - name: Infuriating Calculator - exercise: 20_Types_and_Logic/50_Infuriating_Calculator.py + exercise: 20_Types_and_Logic/70_Infuriating_Calculator.py - name: Code Challenges uid: jJI6aomB - exercise: 20_Types_and_Logic/60_Code_Challenges.ipynb -- name: Loops + exercise: 20_Types_and_Logic/80_Code_Challenges.ipynb +- name: PCEP Alignment uid: TzgRqJlw lessons: - name: Iteration uid: ITpqcvxv - exercise: 30_Loops/010_Iteration.ipynb - - name: Loops with Range - uid: WcGpR3Xg - exercise: 30_Loops/020_Loops_with_Range.ipynb - - name: Lists - uid: 7zsKa84X - exercise: 30_Loops/030_Lists.ipynb - - name: Crazy Tina - exercise: 30_Loops/040_Crazy_Tina.py - display: true - - name: Tuples - uid: 9zTQza2e - exercise: 30_Loops/050_Tuples.ipynb - - name: Indexing and Slicing - uid: P27f2L8k - exercise: 30_Loops/060_Indexing_and_Slicing.ipynb - - name: List Story - exercise: 30_Loops/070_List_Story.py - - name: Strings - uid: Tmg4QRhJ - exercise: 30_Loops/080_Strings.ipynb - - name: Fizz Buzz Badgers - exercise: 30_Loops/090_Fizz_Buzz_Badgers.py + exercise: 30_Loops/10_Iteration.ipynb - name: For Loop Gauntlet uid: 8yGSkBgV exercise: 30_Loops/100_For_Loop_Gauntlet.ipynb - - name: FizzBuzz Gui Grid + - name: Fizzbuzz Gui Grid + uid: cKjBvzzU exercise: 30_Loops/110_FizzBuzz_Gui_Grid.py display: true - name: More Iterables uid: PrKwTywv exercise: 30_Loops/120_More_Iterables.ipynb - name: Iterable Turtle - uid: vTyp2WhX - exercise: 30_Loops/130_Iterable_Turtle.ipynb + exercise: 30_Loops/130_Iterable_Turtle.py - name: More Loops uid: RMSFNtMb exercise: 30_Loops/140_More_Loops.ipynb - name: Number Guess + uid: rZO9PHOt exercise: 30_Loops/150_Number_Guess.py - name: Extras uid: VJSgvOr5 exercise: 30_Loops/160_Extras.ipynb -- name: Data Structures Func + - name: Loops With Range + uid: WcGpR3Xg + exercise: 30_Loops/20_Loops_with_Range.ipynb + - name: Looping Through Lists + uid: 7zsKa84X + exercise: 30_Loops/30_Looping_Through_Lists.ipynb + - name: Crazy Tina + uid: JBzJ2nx1 + exercise: 30_Loops/40_Crazy_Tina.py + display: true + - name: Tuples + uid: 9zTQza2e + exercise: 30_Loops/50_Tuples.ipynb + - name: Indexing And Slicing + uid: P27f2L8k + exercise: 30_Loops/60_Indexing_and_Slicing.ipynb + - name: List Story + uid: G8DoR8LV + exercise: 30_Loops/70_List_Story.py + - name: Strings + uid: Tmg4QRhJ + exercise: 30_Loops/80_Strings.ipynb + - name: Fizz Buzz Badgers + uid: U1uGzJ1r + exercise: 30_Loops/90_Fizz_Buzz_Badgers.py +- name: PCEP Alignment uid: fDPxSid0 lessons: - name: Functions uid: 4LyScnS5 exercise: 40_Data_Structures_Func/10_Functions.ipynb + - name: Exceptions + uid: 87Ie2JGb + exercise: 40_Data_Structures_Func/20_Exceptions.ipynb - name: Dicts Sets uid: VXQdZcqg - exercise: 40_Data_Structures_Func/20_Dicts_Sets.ipynb + exercise: 40_Data_Structures_Func/30_Dicts_Sets.ipynb - name: Funny Words Db - exercise: 40_Data_Structures_Func/30_Funny_Words_Db.py + uid: VenVwSQz + exercise: 40_Data_Structures_Func/40_Funny_Words_Db.py display: true - name: Splat Comprehension uid: mU94qia6 - exercise: 40_Data_Structures_Func/40_Splat_Comprehension.ipynb + exercise: 40_Data_Structures_Func/50_Splat_Comprehension.ipynb - name: Tic Tac Toe - exercise: 40_Data_Structures_Func/50_Tic_Tac_Toe.py + uid: mNMfKPiT + exercise: 40_Data_Structures_Func/60_Tic_Tac_Toe.py display: true - name: Projects uid: rLq5eVeW @@ -206,5 +230,6 @@ modules: - name: Hotel Management lesson: 50_Projects/10_Hotel_Management.md - name: Random Walk + uid: eefj8Ioy exercise: 50_Projects/20_Random_Walk.py display: true diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index e733bb09..76b718a2 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -1,61 +1,39 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Getting Started with Python\n", - "\n", - "This is the first lesson of your first Python class with the League of Amazing\n", - "Programmers. To follow these lessons, you should be reading this file in Visual\n", - "Studio Code if you are on your own computer, or if you are using a website, it\n", - "should be Github Codespaces, or the League Code Server. \n", - "\n", - "We will be writing programs in Python, and starting with turtle programming,\n", - "which is a fun way to learn programming because you will be drawing pictures\n", - "like this:\n", - "\n", - "
\n", - " \"spiral\"\n", - "
\n", - "\n", - "\n", - "It's called turtle programming because you are controlling a turtle that moves\n", - "around the screen drawing lines. You can make the turtle move forward, turn left\n", - "or right, and change the color of the lines it draws.\n", - "\n", - "However, before we start, we need to setup a few things, so let's move to the\n", - "next lesson to get started. \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.11" - }, - "syllabus": { - "uid": "RRTPqCQu" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": "# **Getting Started with Python**\n\nWelcome to your first **Python** lesson with the *League of Amazing Programmers*. This course will help you take your first steps into programming.\n\nTo follow along, make sure you're reading this lesson in one of these spots:\n\n* [**Visual Studio Code**](https://code.visualstudio.com/) (if you're on your own computer)\n* [**Github Codespaces**](https://github.com/features/codespaces) (if you're working in a web browser)\n* **The League Code Server** (if you're using the League's online coding platform) \n\nWe'll start by learning **turtle programming**, which lets you give commands to a virtual turtle, telling it where to move on the screen. It's like having a small robot artist you can control with code. To do this, we'll use a pre-installed Python tool called the **turtle module**.\n\nYou can tell your turtle to move forward, turn left or right, and change the color of the lines it draws. With these commands, you can create pictures like the one below.\n\n
\n\n
\n \"A\n
\n\n
\n

Getting Set Up

\n

Before you can start drawing, you'll need to set up your coding environment. If anything seems confusing, ask your instructor for help.

\n

When you're ready, click the blue Next Lesson button at the bottom-left of your screen to continue.

\n\n
\n

Course Symbols

\n This course uses a few symbols to flag different kinds of information. Take a moment to look them over before you continue.\n
    \n
  • Tooltip — Small pop-up boxes that appear when you hover over specific text or icons. They provide quick definitions and explanations for key terms.
  • \n
  • Important — Highlights key concepts, rules, or warnings. These often contain information you'll need for later lessons.
  • \n
  • Bookmarks — Provide extra details, background, or interesting facts about a topic. They're optional, but reading them can give you a fuller picture of how things work.
  • \n
\n
It's worth reading through each of these, since the lessons refer back to them.
\n
\n
\n
" }, - "nbformat": 4, - "nbformat_minor": 4 -} + { + "cell_type": "markdown", + "metadata": {}, + "source": ">**Note:** Take your time with each lesson. To get the most out of this course, study the code examples, experiment with them, and ask questions when you're stuck. Rushing through without practicing tends to make things harder to follow later on." + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Welcome", + "uid": "RRTPqCQu" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb b/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb index 676bdaf5..d71a2085 100644 --- a/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb +++ b/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb @@ -1,48 +1,45 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## Open a Virtual Screen on the Web\n", - "\n", - "If you started your editor as a Codespace on Github, that is, you clicked\n", - "on a button like \n", - "to start your editor, then you'll need to open a virtual screen. Most of the time this will happen automatically, but if it doesn't click on the monitor icon in the upper right of the screen. \n", - "\n", - "Now you have a virtual screen running. When your program writes to the screen, it will show up in this window. \n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.11" - }, - "syllabus": { - "uid": "KmgIQbhr" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Let's Open Your Virtual Screen**\n", + "\n", + "To get started, you probably clicked a button like this Create codespace on master to open your Codespace. Now, to see your turtle's drawings, you need to open a virtual screen.\n", + "\n", + "Most of the time, this screen will open automatically. If it doesn't appear, look for the little monitor icon in the top right corner of your editor and click on it.\n", + "\n", + "You should now be looking at something like the image below. Click the *connect* button.\n", + "
\n", + " \"Virtual\n", + "
" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python-Apprentice (3.13.3)", + "language": "python", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 4 + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.3" + }, + "syllabus": { + "name": "Open The Screen", + "uid": "KmgIQbhr" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index 7e27a6c7..a7619aea 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -1,212 +1,205 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Running Programs\n", - "\n", - "We will be running Python program in two different ways: in Python files, and in\n", - "code Notebooks. \n", - "\n", - "* Notebook files end with `.ipynb`. These files are very special, because they\n", - " are both documents you can read and have programs that you can run. \n", - "* Python files end with `.py`. You can run them as normal programs, by pressing \n", - " the ▶️ play bytton. \n", - "\n", - "## Run Notebook Code Cells\n", - "\n", - "Notice that this file is a Notebook file. If you are reading the file in Visual\n", - "Studio Code, you will see \"⏩ Run All\" at the top of the screen, and some\n", - "paragraphs that have code will have a \"▶️\" on the left side. \n", - "\n", - "Below is a code cell you can try to run. If you hover your pointer over the\n", - "cell, you will see a \"▶️\" button. Click it to run the code." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello 👋 World 🌎 ! Today is 2025-06-14\n" - ] - } - ], - "source": [ - "import datetime\n", - "\n", - "date = datetime.date.today() # Get the Date\n", - "\n", - "s = F\"Hello 👋 World 🌎 ! Today is\" # Make a string with a message and the date\n", - "\n", - "print(s, date) # Print the string. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "Here is what that code cell looks like with the run button. Try running it!\n", - "\n", - "
\n", - "\n", - "The first time you press the \"▶️\" it may not do anything. Well, it did do\n", - " something, but you might not have seen it. After you click the button, look at\n", - " the top of the Visual Studio window for a box that looks like: \n", - "\n", - "
\n", - "\n", - "If you see this, you will want to select an entry for a Python interpreter. It\n", - "might look like \" .venv (Python 3.11)\" or maybe \"★ Python 3.12\" or something\n", - "like that. Pick the first one in the list, or the one with the \"★ \". Select an interpreter and then re-run the code. \n", - "\n", - "\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You will have to select an interpreter every time you open a new notebook." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The block of code is called a \"Cell\", which is what we'll call it in the future.\n", - "In addition to running it with the ▶️ button, you can also press ⇧ (shift key) +\n", - "Enter to run it.\n", - "\n", - "Here are some other tips for cells: \n", - "\n", - "* Click in the cell to edit it. When the cell is editable, you will see a blue\n", - " bar on the side and a blue outline around the cell. \n", - "* Some operations on a cell, like moving it up or down, require it to be in\n", - " \"Command Mode\". Hit the Esc key to enter command mode. The blue side bar will\n", - " stay, but the blue outline will disappear. \n", - "* When a cell is active, there is a small menu in the upper right with more\n", - " options. \n", - " \n", - "## Assignment\n", - "\n", - "Now, you try it: \n", - "\n", - "1. Copy the Hello World code in the cell above to the cell below. \n", - "2. Change the code to also print out your name\n", - "3. Run the code. " - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# Write your Hello World code below\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Run `.py` programs\n", - "\n", - "The files that end in `.py` are normal Python programs, and the type of program you will \n", - "run most often. Now let's run a Python program, which is in the file named ``02_Meet_Tina.py``. \n", - "\n", - "There are a few ways to do this, but the easiest is to look in the lower left of\n", - "the screen for the Lesson Browser actions: \n", - "\n", - "
\n", - "\n", - "Just click on the Run and Stop buttons. You can also click on \"Next Lesson\" to\n", - "go to the next lesson. \n", - "\n", - "### Use the ▶️ button\n", - "\n", - "Here is another way to run a program. \n", - "\n", - "1. Click on the file name to open the file\n", - "2. Look in the upper right for these icons: \n", - " click on the ▶️ run button to run the program. \n", - "3. Click on the window to close it. \n", - "\n", - "### Hit the F5 Key\n", - "\n", - "You can also run the program by hitting the F5 key. On a Mac, you will have to\n", - "hold down the fn key and then hit F5. This is a bit different than using the run\n", - "button, because F5 will open the debugger. \n", - "\n", - "The first time you hit F5, look at the top of the IDE window. You should see a\n", - "selection window that reads \"Select Debugger\". Select the first option, \"Python\n", - "Debugger\". Then, on the next window, select \"Python File. Debug currently active\n", - "Python file\". After that, you will see the debug bar: \n", - "\n", - "
\n", - "\n", - "We will learn all of the features for the debugger later, but for now you just\n", - "need to know that you press the red square to exit your program, and the gree\n", - "circle to re-run it. \n", - "\n", - "\n", - "
\n", - "\n", - "You can't run a program again until you end the currently running program. If\n", - "you see a turtle window open, then you should either click on the window to\n", - "close it ( if the program ends with `turtle.exitonclick()` or click on the X in\n", - "the upper right of the turtle window. )\n", - "\n", - "Or, if you are using the debugger, click the red square in the debugger bar. \n", - "\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, open the next file, ``02_Meet_Tina.py`` and run it." - ] - }, + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Running Programs**\n", + "\n", + "You can run Python in two main ways: using Python files or Notebooks.\n", + "\n", + "* **Notebook files** end with `.ipynb`. These are special because you can read them and also run code inside them.\n", + "* **Python files** end with `.py`. You can run these by clicking the ▶️ play button.\n", + "\n", + "## **How to Run Code Cells**\n", + "\n", + "This file is a Notebook. If you're using Visual Studio Code, you'll see a ⏩ **Run All** button at the top, and some code blocks will have a ▶️ button on the left.\n", + "\n", + "Try running the code below. Move your mouse over the cell and click the ▶️ button." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - }, - "syllabus": { - "uid": "cNLK6qtR" + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello 👋 World 🌎! Today is 2026-04-18\n" + ] } + ], + "source": [ + "import datetime\n", + "\n", + "# Get the current date\n", + "date = datetime.date.today()\n", + "\n", + "# Make a string with a message and the date\n", + "s = \"Hello 👋 World 🌎! Today is\"\n", + "\n", + "# Print the string and the date\n", + "print(s, date)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here's what the code cell looks like with the run button.\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Setting Up The Interpreter**\n", + "\n", + "> **Note:** If your code ran fine after pressing the play button you can safely skip this section and move on to *What is a Code Cell?*\n", + "\n", + "The first time you press the ▶️ button you might notice that nothing happens. Something did happen, but you couldn't see it. This is because you still need to pick which interpreter (or version) of Python to use.\n", + "\n", + "Look at the top of Visual Studio Code for a box that looks like this , and click on it. \n", + "\n", + "This should have made a dropdown appear in the top-middle of your editor, as shown below:\n", + "\n", + "\n", + "\n", + "Now click on a Python option from the list (look for one with a ★ or .venv in the name) and try running the code again.\n", + "\n", + "\n", + "
\n", + "\n", + "> **Note:** You'll need to pick a Python interpreter every time you open a new notebook." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **What is a Code Cell?**\n", + "In a notebook, a *code cell* is simply a box where you can write and run code. You can try running a code cell by clicking the ▶️ button or by pressing ⇧ Shift + ⏎ Enter on your keyboard.\n", + "\n", + "Here are some tips for using cells:\n", + "\n", + "* Click inside a cell to change or add code. When you do, you'll see a blue bar and outline.\n", + "* To move a cell up or down, press the esc key first to enter *Command Mode*. \n", + "* When a cell is active, look for a small menu in the top right for more options." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Your First Assignment**\n", + "\n", + "1. Copy the Hello World code from above into the cell below, or type `print(\"Hello World!\")`
\n", + "2. Change the string inside the quotation marks so that it also prints your name (e.g., change \"Hello World!\" to \"Your Name\").
\n", + "3. Press the play button to run the code.
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Write your Hello World code below\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **How to Run `.py` Programs**\n", + "\n", + "In the next lesson you'll run a Python program called `Meet_Tina.py`. There are a few ways to run a `.py` file — use whichever you prefer.\n", + "\n", + "### **Using the Lesson Browser**\n", + "\n", + "The Lesson Browser is a simple way to run `.py` programs in this course.\n", + "\n", + "In the bottom-left, you'll see buttons like this:\n", + "\n", + "\n", + "\n", + "Here's how it works, step by step:\n", + "- Click ▶ Run to start your program\n", + "\n", + "- Click ⏹ Stop to stop it\n", + "\n", + "- When you're ready for the next lesson, just click Next Lesson\n", + "\n", + "### **Using the Play Button**\n", + "\n", + "You can also run a `.py` program by clicking the ▶️ button in your editor.\n", + "\n", + "1. Click the file name to open it.\n", + "2. In the top right, look for these icons and click the ▶️ run button.\n", + "3. When you're done, just close the window.\n", + "\n", + "### **Using the Debugger**\n", + "\n", + "If you want to try out the debugger, you can press F5 on Windows 🪟, or fn + F5 on Mac 🍎. The first time, you'll be asked to select a debugger—just pick the Python Debugger and then choose to debug the currently active Python file.\n", + "\n", + "You'll see a debug bar like this:\n", + "\n", + "\n", + "\n", + "You don't need to know all the buttons yet. For now: red square stops the program, green circle runs it again.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "If a program is already running, close it before starting another.\n", + "\n", + "You have two options to close it:\n", + "
\n", + "__1)__ Click the in the top-right corner of the window, or, if the program's code ends with turtle.exitonclick(), simply click anywhere inside the window to close it.\n", + "
\n", + "__2)__ If you are using the debugger, you can also stop the program by clicking the red square icon in the debugger toolbar, like this: \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, click the Next Lesson button, open the file `Meet_Tina.py`, and try running it." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python-Apprentice (3.13.3)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.3" }, - "nbformat": 4, - "nbformat_minor": 4 + "syllabus": { + "name": "Run Programs", + "uid": "cNLK6qtR" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/lessons/10_Turtles/20_Introducing_Tina/10_Meet_Tina/Meet_Tina.py b/lessons/10_Turtles/20_Introducing_Tina/10_Meet_Tina/Meet_Tina.py index 50dbaa2d..492a18f8 100644 --- a/lessons/10_Turtles/20_Introducing_Tina/10_Meet_Tina/Meet_Tina.py +++ b/lessons/10_Turtles/20_Introducing_Tina/10_Meet_Tina/Meet_Tina.py @@ -1,20 +1,19 @@ """ -Run this program to meet Tina the Turtle. Tina is a -hexagon with legs and a head. +# Meet_Tina.py -You can run this program by: +This program draws Tina: a turtle with a hexagon-shaped green shell, four brown legs, a head, and a tail. -1) clicking on ▶️ icon at the top of the editor -window -2) Hit the F5 function key +There are two ways to run this program: +1. Click the 'Run' (▶) button at the top of your editor window OR in the bottom left corner. +2. Press the F5 key (in editors like VS Code, Thonny, or GitHub Codespaces). -You won't understand what this program is doing just -yet, but don't worry, that's what you will be -learning in the next few lessons. +You don't need to understand all of this yet. Later lessons will walk through it piece by piece. """ import turtle # Tell Python we want to work with the turtle -turtle.setup(600,600,0,0) # Set the size of the window +from math import radians, tan + +turtle.setup(600, 600, 0, 0) # Set the size of the window tina = turtle.Turtle() # Create a turtle named tina @@ -28,14 +27,13 @@ def head_pos(l=200): """ Position of tina's head, relative to the center of the screen""" - from math import radians, tan - return (l/2) / tan(radians(30)) + return (l/2) / tan(radians(30)) -def draw_body(t, l = 200): +def draw_body(t, l=200): """Draw the body of the turtle""" t.pencolor('green') # Set the pen color to green t.fillcolor('green') # Set the fill color to green - t.penup() + t.penup() t.goto(0,0) # Move tina to the center of the screen t.setheading(-90) # Set the heading of tina to -90 degrees t.forward(head_pos(l)) # Move tina forward by the head position @@ -70,25 +68,25 @@ def draw_leg(t, a, r=170, w=40, l=50): t.forward(w/2) # Move tina forward by half the width t.end_fill() -def draw_head(): +def draw_head(t): """Draw a brown head at the head position""" - tina.penup() - tina.goto(0, head_pos()-20) # Move tina to the head position - tina.pendown() - tina.pencolor('brown') # Set the pen color to brown - tina.fillcolor('brown') # Set the fill color to brown - tina.begin_fill() - tina.circle(50) # Draw a circle with radius 50 - tina.end_fill() - -def say_hello(): + t.penup() + t.goto(0, head_pos()-20) # Move tina to the head position + t.pendown() + t.pencolor('brown') # Set the pen color to brown + t.fillcolor('brown') # Set the fill color to brown + t.begin_fill() + t.circle(50) # Draw a circle with radius 50 + t.end_fill() + +def say_hello(t): """Make tina say hello, with text to the right of her head""" - tina.penup() - tina.goto(75, head_pos()+75) # Move tina to the position for the text - tina.pendown() - tina.write("Hello! I'm Tina!", font=("Arial", 20, "normal")) # Write the text + t.penup() + t.goto(75, head_pos()+75) # Move tina to the position for the text + t.pendown() + t.write("Hello! I'm Tina!", font=("Arial", 20, "normal")) # Write the text -draw_head() +draw_head(tina) for lp in (30, -30, -150, 150): draw_leg(tina, lp) # Draw the legs at the specified angles @@ -97,6 +95,6 @@ def say_hello(): draw_body(tina) # Draw the body of the turtle -say_hello() # Make tina say hello +say_hello(tina) # Make tina say hello -turtle.exitonclick() # Close the window when we click on it \ No newline at end of file +turtle.exitonclick() # Close the window when we click on it diff --git a/lessons/10_Turtles/20_Introducing_Tina/10_Meet_Tina/README.md b/lessons/10_Turtles/20_Introducing_Tina/10_Meet_Tina/README.md index 854f60fb..a3ef012f 100644 --- a/lessons/10_Turtles/20_Introducing_Tina/10_Meet_Tina/README.md +++ b/lessons/10_Turtles/20_Introducing_Tina/10_Meet_Tina/README.md @@ -4,8 +4,8 @@ uid: tvO1dlwm # Meet Tina -Run this program to meet Tina. You can run the program by +It's time to meet Tina the Turtle, you can run this program by using any of the following options: -1. (Best) Click the green  ▶️ run  button in the bottom left of the screen. -2. Hit the F5 key. -3. Click the ▶️ icon in the editor title bar ( upper right) \ No newline at end of file +1. (Best) Click the green Run button in the bottom-left corner of your screen. +2. Press the F5 key on Windows, or fn + F5 on Mac. +3. Click the **▷** play icon in the upper right corner of your editor window (in the title bar). \ No newline at end of file diff --git a/lessons/10_Turtles/20_Introducing_Tina/20_What_Can_Tina_Do.ipynb b/lessons/10_Turtles/20_Introducing_Tina/20_What_Can_Tina_Do.ipynb index 9681fa3b..fe034598 100644 --- a/lessons/10_Turtles/20_Introducing_Tina/20_What_Can_Tina_Do.ipynb +++ b/lessons/10_Turtles/20_Introducing_Tina/20_What_Can_Tina_Do.ipynb @@ -1,66 +1,101 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# What Can Tina Do?\n", - "\n", - "The first program where you met Tina was pretty complicated! But it was made of a lot of simple parts. The next program is simpler, and has commands that you can figure out for yourself. In the next program, `02c_Squares_and_Circles`, Tina will draw a square and a circle. Your task will be to read the program, figure out what it does, and change it. \n", - "\n", - "Here is what some of the program will look like: \n", - "\n", - "```python\n", - "tina = turtle.Turtle() # Create a turtle named tina\n", - "\n", - "tina.pencolor('blue') # Set the pen color to blue\n", - "tina.forward(200) # Move tina forward by the forward distance\n", - "tina.right(90) # Turn tina left by the left turn\n", - "\n", - "```\n", - "\n", - "The program will have a lot of lines like this. Each line is a command for Tina to do something. The commands are in English, and they are pretty easy to understand. What you will do is: \n", - "\n", - "Run the program, then read through the program and figure out what each command does. The part of the program at the start of the line: \n", - "\n", - "```python\n", - "tina.forward(200) \n", - "```\n", - "\n", - "is the command. What could this line do? Maybe ... move Tina forward by 200 steps?\n", - "\n", - "After that will be a comment, which looks like this: \n", - "\n", - "```python\n", - "# Move tina forward by the forward distance\n", - "```\n", - "\n", - "Comments are only for people to read, and they start with a `#`. They are there to help you understand the program.\n", - "\n", - "## Assignment\n", - "\n", - "1. Run the program `02c_Squares_and_Circles` and see what it does.\n", - "2. Read through the program and figure out what each command does.\n", - "3. Change the program so that Tina draws a square and a circle of different sizes and colors.\n", - "4. Run the program again and see what it does now.\n", - "\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "name": "python", - "version": "3.12.11" - }, - "syllabus": { - "uid": "7tUP3zAZ" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **What Can Tina Do?**\n", + "\n", + "In the last lesson, you met Tina the Turtle in a program that might have looked a little complicated. Tina's behavior can be broken down into simple steps.\n", + "\n", + "In the next program, `Squares_and_Circles.py`, you'll see a much simpler example. Tina will only draw a square and a circle, your task will be to read the program, figure out what it does, and make some fun changes.\n", + "\n", + "Before moving on, here's how it works.\n", + "\n", + "### **How Tina Follows Commands**\n", + "\n", + "Here is what some of the program will look like with comments to explain what each line does:\n", + "\n", + "```python\n", + "tina = turtle.Turtle() # Make a turtle named tina\n", + "tina.pencolor('blue') # Change tina's pen color to blue\n", + "tina.forward(200) # Move tina forward by 200 steps\n", + "tina.right(90) # Turn tina right by 90 degrees\n", + "```\n", + "\n", + "- The code *before* the `#` is a **command** — it's an instruction that tells Tina exactly what to do, such as moving forward or changing color.\n", + "- The text *after* the `#` is a **comment** — it's a note for humans that explains what the command does. \n", + "\n", + "> **Tip:** Comments are ignored by Python and don't affect how the program runs." + ] }, - "nbformat": 4, - "nbformat_minor": 2 + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Additional Help**\n", + "\n", + "Reading documentation is one of the most useful skills a programmer can build. It helps you find commands, learn new features, and debug on your own.\n", + "\n", + "### **How to Find Help**\n", + "* **Search Online:** Use Google or Bing to search for things like `python turtle commands` or `how to draw a circle with python turtle`.\n", + "* **Check Official Docs:** Look through the [Python Turtle documentation](https://docs.python.org/3/library/turtle.html). It lists every command you can use.\n", + "* **Look for Examples:** Websites like Stack Overflow or GeeksforGeeks often have code examples you can try.\n", + "\n", + "### **Why Use Documentation?**\n", + "\n", + "* **Learn Correct Syntax:** See exactly how to write a command so it works.\n", + "* **Discover Features:** find commands you haven't used yet.\n", + "* **Fix Errors:** Find out why your code might not be working and how to fix it.\n", + "\n", + "> **Tip:** Copy example code and change things to see what happens. Experimentation is how most of this gets learned.\n", + "\n", + "
Official Turtle Documentation\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## **Assignment**\n", + "\n", + "Read the steps below, then move on to the Next Lesson.\n", + "\n", + "1. **Run the program** called `Squares_and_Circles.py` to see what Tina draws.\n", + "2. **Read the code** for each line, and try to guess what Tina will do (The comments will help!).\n", + "3. **Change the program** to draw a square and a circle of different sizes and colors, experiment with the commands, and run it again to see the results.\n", + "4. **Run the program again** and see how Tina's drawing changes." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python-Apprentice (3.13.3)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.3" + }, + "syllabus": { + "name": "What Can Tina Do", + "uid": "7tUP3zAZ" + } + }, + "nbformat": 4, + "nbformat_minor": 2 } diff --git a/lessons/10_Turtles/20_Introducing_Tina/30_Squares_and_Circles/README.md b/lessons/10_Turtles/20_Introducing_Tina/30_Squares_and_Circles/README.md index 239cf3a0..d3dc6c8e 100644 --- a/lessons/10_Turtles/20_Introducing_Tina/30_Squares_and_Circles/README.md +++ b/lessons/10_Turtles/20_Introducing_Tina/30_Squares_and_Circles/README.md @@ -4,7 +4,7 @@ uid: E7KlecQ3 # Squares and Circles -1. Run the program `02b_Squares_and_Circles` and see what it does. -2. Read through the program and figure out what each command does. -3. Change the program so that Tina draws a square and a circle of different sizes and colors. -4. Run the program again and see what it does now. \ No newline at end of file +1. Run the program `Squares_and_Circles.py` to see what it does. +2. Read through the program, using the comments to help you, and try to understand what each command does. +3. Try modifying the program's values so that Tina draws a square and a circle with different sizes and colors. +4. Run the program again to see how your changes affect the drawing. \ No newline at end of file diff --git a/lessons/10_Turtles/20_Introducing_Tina/30_Squares_and_Circles/Squares_and_Circles.py b/lessons/10_Turtles/20_Introducing_Tina/30_Squares_and_Circles/Squares_and_Circles.py index 7e22ab03..a26747f8 100644 --- a/lessons/10_Turtles/20_Introducing_Tina/30_Squares_and_Circles/Squares_and_Circles.py +++ b/lessons/10_Turtles/20_Introducing_Tina/30_Squares_and_Circles/Squares_and_Circles.py @@ -1,22 +1,25 @@ """ -This is a simple Turtle program that draws a square and writes a message. The -lines that start with a # are comments. They are not executed by Python. The -lines inside the three quotes are also comments, but of a different sort ( -called "doc comments" ) Comments are used to explain what the code does. Read -the program and try to understand what each line does. +# Squares_and_Circles.py + +This Turtle program demonstrates basic drawing and text output in Python. +It draws a colored square, places a filled circle inside it, and writes a message on the screen. +Lines starting with # are single-line comments, used to describe what each part of the code does. +Text enclosed in triple quotes (like this) is a docstring, which provides a multi-line explanation at the top of the file. + +Review each section to see how the turtle is moved, how shapes are drawn, and how text is displayed. """ import turtle # Tell Python we want to work with the turtle -turtle.setup(600,600,0,0) # Set the size of the window +turtle.setup(600, 600, 0, 0) # Set the size of the window tina = turtle.Turtle() # Create a turtle named tina tina.shape('turtle') # Set the shape of the turtle to a turtle -tina.speed(2) # Make the turtle move as fast, but not too fast. +tina.speed(2) # Move at a moderate speed, not too fast. ## ## Move Tina to the Starting Position -# +## tina.penup() # Lift the pen up so we can move tina without drawing tina.goto(-100, 100) # Move tina to the starting position @@ -24,15 +27,16 @@ ## ## Draw a Square +## Repeat forward + right three more times to complete the square. ## tina.pencolor('blue') # Set the pen color to blue tina.forward(200) # Move tina forward by the forward distance -tina.right(90) # Turn tina right a quarter turn +tina.right(90) # Turn tina right by 90 degrees tina.pencolor('red') # Set the pen color to red -tina.forward(200) # Continue the last two steps three more times -tina.right(90) # to draw a square +tina.forward(200) +tina.right(90) tina.pencolor('green') # Set the pen color to green tina.forward(200) @@ -46,11 +50,10 @@ ## Draw a Circle ## -tina.penup() +tina.penup() tina.goto(0, -75) -tina.pendown() - tina.pendown() + tina.color('red') # Set the color of tina to red tina.begin_fill() tina.circle(75) @@ -61,15 +64,9 @@ ## tina.penup() # Lift the pen up so we can move tina without drawing -tina.goto(-50, -150) -tina.forward(20) # Move tina forward by 20 -tina.left(90) # Turn tina left by 90 degrees -tina.forward(20) # Move tina forward by 20 +tina.goto(-30, -130) # Move to where the text should appear tina.write("Why, hello there!") # Write the message "Why, hello there!" -tina.backward(20) # Move tina backward by 20 -turtle.exitonclick() # Close the window when we click on it +turtle.exitonclick() # Close the window when we click on it -# You're on your way, soon you'll be writing your own programs! -# But first, let's save your progress. Continue with -# the next file, 03_Check_In_Your_Code.ipynb \ No newline at end of file +# Nice work. Save your progress in the next lesson, 40_Check_In_Your_Code.ipynb diff --git a/lessons/10_Turtles/20_Introducing_Tina/40_Check_In_Your_Code.ipynb b/lessons/10_Turtles/20_Introducing_Tina/40_Check_In_Your_Code.ipynb index 02882c66..23358877 100644 --- a/lessons/10_Turtles/20_Introducing_Tina/40_Check_In_Your_Code.ipynb +++ b/lessons/10_Turtles/20_Introducing_Tina/40_Check_In_Your_Code.ipynb @@ -1,147 +1,189 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Check In Your Code\n", - "\n", - "Now that you have written a few programs, you have made changes to the source code in\n", - "your Codespace. If you want to save those changes for later – and you almost\n", - "certainly do – you should \"check it in\" to Source Control. If you don't,\n", - "eventually your GitHub codespace will be stopped automatically, then deleted\n", - "after a few weeks, and you will lose your changes. So check your code in!\n", - "\n", - "## Make a Change\n", - "\n", - "Let's make a change to `02a_Meet_Tina` so we have something to check in. You can\n", - "change anything you want! Maybe make the circle a different size \n", - "( change `tina.circle(100, steps=50)` ) or change what tina says ( change\n", - "`tina.write(\"Why, hello there!\")` ) or anything else, then save your file. \n", - "\n", - "## Commit and sync your code\n", - "\n", - "Once you have made the change, save your file. Then, on the left side of the\n", - "Codespace window, look for this icon. The number may be different for you (it\n", - "will probably be 1). \n", - "\n", - "
\n", - "\n", - "This is the Source Control icon; click on it to get access to the Source Control tool for\n", - "checking in your code. Now the Source Control pane should look like this: \n", - "\n", - "
\n", - "\n", - "To check in your code enter a message in the Message box to describe what you\n", - "changed, then click on the ✓ Commit button. That will check in your changes,\n", - "but they aren't really stored permanently yet.\n", - "\n", - "After you commit, the blue button will change to:\n", - "\n", - "
\n", - "\n", - "Click the Sync button, and *now* your changes are safely in your GitHub account's repository. \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "One problem you might have is if you click the ✓ Commit button and it doesn't\n", - "seem to do anything, but the edit pane ( where you edit files ) changes to look\n", - "like this:\n", - "\n", - "
\n", - "\n", - "This happens if you click the ✓ Commit button without entering a commit message first. There are two ways to proceed:\n", - "\n", - "1. Close the edit message file by clicking on the x in the files tab. then go\n", - " back to the Source Control window and enter a message before clicking on the\n", - " ✓ Commit button. \n", - "2. Enter a commit message at the top of the edit message file, save the file,\n", - " then close it, and the commit process will finish. \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Remember to check in your code frequently! The best thing to do is check in\n", - "after every lesson, but you should be especially diligent about checking code in\n", - "before you finish your lessons for the day. \n", - "\n", - "If you want to force your Codespace to stop, click on the blue area in the lower\n", - "left corner. That will pop up a menu ( at the top of the screen ) with options\n", - "to stop the codespace. \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Continue your Lessons" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When you want to resume your lessons, you will probably find that your Codespace has been automatically stopped. You should have already checked in your code, but if you didn't, your changes will still be in the Codespace. If you left the browser window open, it will look like this:\n", - "\n", - "
\n", - "\n", - "If you see that, just click on the green \"Restart Codespace\" to pick up where you left off. \n", - "\n", - "If you don't still have that window open, it is still easy to restart your Codespace. \n", - "\n", - "First, go your GitHub account and look for the repository called \"Python-Apprentice.\" \n", - "\n", - "Next, click on the \n", - "button, and in the popup window, select the \"Codespaces\" tab. You should *not*\n", - "see the green \"Create Codespace\" button, but rather, it should look something\n", - "like this: \n", - "\n", - "
\n", - "\n", - "The codespaces have crazy names, like this one, \"expert broccoli\".\n", - "\n", - "If your Codespace isn't actually stopped, it is still running, you will see \"Active\" along with a green dot: \n", - "\n", - "
\n", - "\n", - " Either way, you can click on the crazy name link to open the Codespace and continue your lessons. \n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - }, - "syllabus": { - "uid": "doD6P7fk" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Check In Your Code**\n", + "\n", + "Now that you've created a few programs, it's important to save your progress so you don't lose your work. The best way to do this is to **check in** your code using a **Source Control System** like **Git** . \n", + "\n", + "> **Note:** If you skip this step, your GitHub Codespace may eventually stop automatically, and after a few weeks, it will be deleted—along with any unsaved changes. Check in your code often so you don't lose work.\n", + "\n", + "### **How To Make Changes**\n", + "\n", + "Let's make a change to `Meet_Tina.py` so you have something to check in. \n", + "\n", + "You can change anything — try a different circle size, or change what Tina says.\n", + "\n", + "**For Example**\n", + "```python\n", + " tina.circle(100, steps=50) # Change the circle's size\n", + " tina.write(\"Why, hello there!\") # Modify what Tina says\n", + "```\n", + "\n", + "> **Tip:** After making your change, save the file. You can do this quickly by pressing Ctrl + S (Windows), or Cmd + S (Mac)." + ] }, - "nbformat": 4, - "nbformat_minor": 4 + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Commit and Sync Your Code**\n", + "\n", + "### **Step 1: Open Source Control**\n", + "\n", + "After saving your changes, find the Source Control icon (which looks like a branch or Y-shape) in the Activity Bar on the far left side of the Codespace window. If you have made changes, this icon may display a number badge showing how many files have been updated since your last commit.\n", + "\n", + "**Source Control Icon**\n", + "
\n", + "
\n", + "
\n", + "\n", + "> **Note:** The number may be different for you based on how many changes you have made. \n", + "\n", + "Once you find the Source Control icon, click on it to open the Source Control Panel shown below.\n", + "\n", + "**Source Control Panel**\n", + "\n", + "
\n", + "\n", + "In this panel, you'll see a list of all files that have been modified, added, or deleted. You can click on any file in the list to review the changes you made, compare them to previous versions, and confirm that your updates are correct before committing.\n", + "\n", + "### **Step 2: Commit Your Changes**\n", + "\n", + "**To check in your code:**\n", + "\n", + "1. Enter a message in the Message box describing what you changed.\n", + "2. Click the ✓ Commit button\n", + " (This saves your changes locally, but remember, they haven't been pushed to the repository yet).\n", + "\n", + "After you commit, the blue button will change and look something like this:\n", + "\n", + "\n", + " 🔄︎ Sync Changes 1 🡑\n", + "\n", + "\n", + "*Click the Sync button* to safely upload your changes to your GitHub repository.\n", + "\n", + "Your work is now stored in your GitHub account.\n", + "\n", + "> **Tip:** Commit your code often—after every lesson, and always before you finish for the day.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## **Troubleshooting Commit Problems**\n", + "\n", + "Sometimes, when you click the ✓ Commit button, nothing seems to happen and you will see a screen like this:\n", + "\n", + "\n", + "\n", + "This just means you tried to commit your changes, and forgot to enter a commit message.\n", + "\n", + "There are two ways to fix it:\n", + "\n", + "1. **By closing the edit message file**\n", + " - Click the (on the file tab), return to Source Control, enter your commit message, and click the ✓ Commit button again.\n", + "\n", + "2. **By entering a commit message directly**\n", + " - At the top of the edit message file, type your commit message. Save the file, close it, and your commit will finish.\n", + "\n", + "### **How to Manually Stop Your Codespace**\n", + "\n", + "If you want to stop your Codespace manually (e.g., when you’re done working for the day), look for the blue status area in the lower left corner of the window. Click on this area, and a menu will appear at the top of the screen. From this menu, select the option to stop your Codespace, and the next time you start it, you can pick up right where you left off.\n", + "\n", + "> **Tip:** Stopping your Codespace helps save resources and ensures your work is safely paused. Just remember to save/commit any changes!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **How to Continue Your Lessons After a Break**\n", + "\n", + "When you return to your lessons, you may find that your Codespace has automatically stopped. Ideally, you should have already checked in your code, but if you haven't, your changes will still be saved in the Codespace. \n", + "\n", + "If you left the browser window open, you should see the same screen open as before:\n", + "\n", + "
\n", + "\n", + "If you see this screen, simply click the green Restart Codespace button to continue where you left off.\n", + "\n", + "### **Restarting Your Codespace from GitHub**\n", + "\n", + "If you don't have that window open anymore, it's still easy to restart your Codespace.\n", + "\n", + "**For Example**\n", + "1. Go to your GitHub account and find the repository named Python-Apprentice.\n", + "2. Click on the <> Code ⯆ button\n", + "3. In the popup window, select the \"Codespaces\" tab. \n", + "\n", + "> **Note:** You should *not* see the green Create Codespace button. \n", + "\n", + "**Example**\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "> **Note:** Codespaces often have fun, random names, like 'expert broccoli' 🥦 or 'organic spoon' 🥄 to help you identify them.\n", + "\n", + "### **Checking Codespace Status**\n", + "\n", + "If your Codespace is still running, you should see ' Active' next to it.\n", + "\n", + "**Example**\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "> **Note:** In either case, just click the Codespace name to open it and continue your lessons" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Check In Your Code", + "uid": "doD6P7fk" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/lessons/10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py b/lessons/10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py index 8167867e..0cd83f93 100644 --- a/lessons/10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py +++ b/lessons/10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py @@ -1,14 +1,17 @@ """ -For this program, you will tell Tina the Turtle to draw a triangle. +# 10_Turtle_Tricks.py -You should look at the previous programs to see how to use the turtle commands. -Copy lines of code from those programs to this one to draw a triangle. +In this exercise, you will use Tina the Turtle to draw an equilateral triangle. + +- Use the commands: tina.forward() and tina.left() to draw the three sides of the triangle. +- Change the pen color before drawing each side using tina.pencolor(), so each side is a different color. + +Refer to previous turtle programs for examples of how to use these commands. """ # These lines are needed in most turtle programs import turtle # Tell Python we want to work with the turtle -turtle.setup(600,600,0,0) # Set the size of the window - +turtle.setup(600, 600, 0, 0) # Set the size of the window tina = turtle.Turtle() # Create a turtle named tina # Use tina.forward() and tina.left() to draw a triangle diff --git a/lessons/10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py b/lessons/10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py index 5598ef1f..29f39316 100644 --- a/lessons/10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py +++ b/lessons/10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py @@ -1,14 +1,17 @@ """ -For this program, you will tell Tina the Turtle to draw -a pentagon. +# 20_Turtle_Tricks.py -You should look at the previous program, 02_Meet_Tina.py -to see how to use the turtle commands. +In this assignment, you will use Tina the Turtle to draw a pentagon. + +- Each side of the pentagon should be a different color. +- Use the turtle commands: tina.forward(), tina.left(), and tina.pencolor() to accomplish this. + +Refer to the previous program, Meet_Tina.py, for examples of how to use turtle commands. """ # These lines are needed in most turtle programs import turtle # Tell Python we want to work with the turtle -turtle.setup(600,600,0,0) # Set the size of the window +turtle.setup(600, 600, 0, 0) # Set the size of the window tina = turtle.Turtle() # Create a turtle named tina # Use tina.forward() and tina.left() to draw a pentagon diff --git a/lessons/10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py b/lessons/10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py index cecf2e9f..0d4ae1b2 100644 --- a/lessons/10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py +++ b/lessons/10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py @@ -1,17 +1,19 @@ """ -For this program, you will tell Tina the Turtle to draw -multiple shapes. +# 30_Turtle_Tricks.py -Draw two circles, filled with different colors, -and in different places on the screen. +In this assignment, you will use Tina the Turtle to draw multiple shapes on the screen. -You should look at the previous program, 02_Meet_TIna.py -to see how to use the turtle commands. +- Draw two circles, each filled with a different color. +- Position the circles in different locations on the screen (they should not overlap). +- Use the turtle commands: begin_fill(), end_fill(), fillcolor(), circle(), and goto() to complete the task. +- Try different colors and positions. + +Refer to the previous program, Meet_Tina.py for examples of how to use these turtle commands. """ # These lines are needed in most turtle programs import turtle # Tell Python we want to work with the turtle -turtle.setup(600,600,0,0) # Set the size of the window +turtle.setup(600, 600, 0, 0) # Set the size of the window tina = turtle.Turtle() # Create a turtle named tina # Use tina.circle() to draw a circle, and tina.goto() to move tina to a new location @@ -21,4 +23,4 @@ turtle.exitonclick() # Close the window when we click on it -# Dont forget to check in your code! \ No newline at end of file +# Save your progress by checking in your code. \ No newline at end of file diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 676b87f9..7ebbe6d8 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -1,118 +1,279 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Loops and Variables\n", - "\n", - "In our first Tina the Turtle program, we had Tina draw a square, but it was \n", - "very tedious, and involved a lot of repetition: \n", - "\n", - "```python \n", - "import turtle # Tell Python we want to work with the turtle\n", - "turtle.setup(600,600,0,0) # Set the size of the window\n", - "\n", - "tina = turtle.Turtle() # Create a turtle named tina\n", - "\n", - "tina.shape('turtle') # Set the shape of the turtle to a turtle\n", - "tina.speed(2) # Make the turtle move as fast, but not too fast. \n", - "\n", - "tina.forward(150) # Move tina forward by the forward distance\n", - "tina.left(90) # Turn tina left by the left turn\n", - "\n", - "tina.forward(150) # Continue the last two steps three more times\n", - "tina.left(90) # to draw a square\n", - "\n", - "tina.forward(150)\n", - "tina.left(90)\n", - "\n", - "tina.forward(150)\n", - "tina.left(90)\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, - "source": [ - "The code \n", - "\n", - "```python \n", - "tina.forward(150)\n", - "tina.left(90)\n", - "\n", - "```\n", - "\n", - "gets repeated 4 times. That doesn't seem right!\n", - "\n", - "We can improve this code with a loop. Here is an example of a loop:\n", - "\n", - "( Remember that you can run it; click the \"▶️\" on the left. \n", - " ) " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# For loop; try changing the number of iterations to 6\n", - "for i in range(4):\n", - " print('Loop Iteration', i)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that the number of iterations is the number inside of the parentheses in\n", - "`range()`. Also notice that the `print` statement is indented below the `for` loop\n", - "line. Indenting is how you tell python what lines of code should be repeated. \n", - "\n", - "The `i` in the line `for i in range(4)` is a variable; it will hold the number of the \n", - "iteration, starting at 0. For most of our loops in this module we won't use the \n", - "variable, but we will study it more in a later module. \n", - "\n", - "\n", - "Now try updating the next program ``05_Loop_with_Turtle.py`` with a loop. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - }, - "syllabus": { - "uid": "abX8sNwB" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": "# **Loops**\n\nLoops let you run the same code many times without repeating yourself. In this lesson you'll see how `for` loops work and how they make turtle programs shorter and easier to change.\n\n> **Tip:** Loops show up in every programming language, so it's worth taking time to get comfortable with them." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "### **Why Use Loops?**\n\nInstead of repeating the same instructions over and over again. A loop will let you tell Tina to do something multiple times by using a simple command.\n\nLoops are useful because they help us:\n\n- **Save time** by enabling us to write less code and let the computer handle repetition.\n- **Reduce mistakes** since fewer repeated lines means fewer errors.\n- **Make updates easier** because when you change one part of your code, the loop applies it everywhere.\n\n### **How Do Loops Work?**\n\nLet's look at this simple example of a `for` loop:" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# A simple for loop example\n", + "for i in range(4): # Loop will run 4 times from 0 to 3\n", + " print('Loop Iteration', i) # This is because range(4) generates numbers 0, 1, 2, 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `for` loop tells Tina to repeat the lines that are indented under the `for` statement a specific number of times, while the `i` variable simply keeps track of the current iteration, and the `range(4)` function generates a sequence of numbers from 0 to 3 (so four iterations in total). Each time the loop runs, the variable `i` takes on the next value in that sequence (e.g., 0, then 1, then 2, then 3)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Manual vs. Loop-Based Drawing with Tina**\n", + "\n", + "### **Drawing Shapes Without Loops**\n", + "Now let's take a look at a repetitive Tina the Turtle program that draws a square and a triangle without using loops:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "%run .lib/auto_turtle.py # This just handles the general imports for you\n", + "\n", + "# Set up Tina the Turtle\n", + "tina = turtle(myTS) # type: ignore[name-defined] - Creates a turtle named tina\n", + "tina.shape('turtle') # Set the shape of the turtle to a turtle\n", + "tina.speed(5) # Make the turtle move as fast, but not too fast.\n", + "\n", + "# Draw a square without using a loop\n", + "tina.forward(100) # Move tina forward 100 units\n", + "tina.left(90) # Turn tina left 90 degrees\n", + "tina.forward(100) # Move tina forward 100 units\n", + "tina.left(90) # Turn tina left 90 degrees\n", + "tina.forward(100) # Move tina forward 100 units\n", + "tina.left(90) # Turn tina left 90 degrees\n", + "tina.forward(100) # Move tina forward 100 units\n", + "tina.left(90) # Turn tina left 90 degrees\n", + "\n", + "tina.penup() # Lift the pen to move without drawing\n", + "tina.goto(150, 0) # Move tina to the center of the canvas\n", + "tina.pendown() # Put the pen down to start drawing\n", + "\n", + "# Draw a triangle without using a loop\n", + "tina.forward(120) # Move forward 120 units\n", + "tina.left(120) # Turn left 120 degrees\n", + "tina.forward(120) # Move forward 120 units\n", + "tina.left(120) # Turn left 120 degrees\n", + "tina.forward(120) # Move forward 120 units\n", + "tina.left(120) # Turn left 120 degrees" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Did you notice how many times `tina.forward()` and `tina.left()` are repeated? This makes the code longer, harder to read, and more likely to have mistakes if you need to change something. Imagine how confusing it would be if we wanted to move the turtle 36 times to draw a star — that's a lot of repetitive code.\n\nLet's see how we can use loops to make this code cleaner and easier to manage. We can even add more shapes without making the code much longer." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Using Loops To Draw Shapes**\n", + "\n", + "Instead of repeating the same lines, we can use a loop to make Tina draw a square, triangle, and add in that star, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Run Me!\n\n%run .lib/auto_turtle.py # This just handles the general imports for you\n\n# Set up Tina the Turtle\ntina = turtle(myTS) # type: ignore[name-defined] - Creates a turtle named tina\ntina.shape('turtle') # Set the shape of the turtle to a turtle\ntina.speed(5) # Make the turtle move fast, but not too fast \n\n# Draw a square using a loop\nfor i in range(4): # Loop 4 times\n tina.forward(100) # Move forward 100 units\n tina.left(90) # Turn left 90 degrees\n\ntina.penup() # Lift the pen to move without drawing\ntina.goto(125, 0) # Move tina to over to the right of the canvas\ntina.pendown() # Put the pen down to start drawing\n\n# Draw a triangle using a loop\nfor i in range(3): # Loop 3 times\n tina.forward(120) # Move forward 120 units\n tina.left(120) # Turn left 120 degrees\n\n# Move tina to the center of the canvas\ntina.penup() # Lift the pen to move without drawing\ntina.goto(260, 50) # Move tina further to the right of the canvas\ntina.pendown() # Put the pen down to start drawing\n\ntina.speed('fastest') # Set speed to fastest for a faster star drawing\n\n# Draw a star pattern using a loop\nfor i in range(36): # Loop 36 times\n tina.forward(110) # Move forward 110 units\n tina.left(170) # Turn left 170 degrees" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Now, Tina will move forward and turn left four times, drawing a square, 3 times, for a triangle, and 36 times to draw a star. Using loops makes your code cleaner and easier to understand.\n\nHere's what's happening:\n- **Square** repeats `tina.forward(100)` and `tina.left(90)` *4* times.\n- **Triangle** repeats `tina.forward(120)` and `tina.left(120)` *3* times.\n- **Star** repeats `tina.forward(100)` and `tina.left(170)` *36* times.\n\nFor example, if you wanted Tina to draw a hexagon (they have 6 sides), you would only need to change the `range(4)` to `range(6)` and the angle from `90` to `60`.\n\n```python\nfor i in range(6): # Repeat 6 times for a hexagon\n tina.forward(150) # Move forward 150 units\n tina.left(60) # Turn left 60 degrees\n```" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## **Practicing with Loops**\n\nTry running these examples to see how loops can be used in different ways.\n\n### **Drawing an Orange Square**\n\nThis is a simple example that uses a loop to draw an orange square." + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Size of the square\n", + "size = 10 \n", + "# Loop will run 'size' times \n", + "for i in range(size):\n", + " # Print a row of 'size' orange squares\n", + " print('🟧' * size) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Here's what's happening:\n- `for i in range(size):` tells Python to repeat the indented code `size` times.\n- The variable `i` starts at 0 and goes up to `size - 1` (so, for `size = 20`, it goes from 0 to 19).\n- Each time the loop runs, it prints a row of orange squares using `'🟧' * size`.\n\n> **Note:** This loop creates a square grid by printing `size` rows, each containing `size` orange squares. Try changing the value of `size` to see how the output changes." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Drawing a Checkerboard Pattern**\n", + "\n", + "This is a slightly more complex example that uses nested loops to create a checkerboard pattern:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Define the size of the checkerboard\n", + "size = 10\n", + "\n", + "# Loop through each row\n", + "for row in range(size):\n", + " # Loop through each column in the current row\n", + " for col in range(size):\n", + " # If the sum of row and col is even, print a black circle\n", + " if (row + col) % 2 == 0:\n", + " print('⚫️', end='')\n", + " # Otherwise, print a white circle\n", + " else:\n", + " print('⚪️', end='')\n", + " # Go to the next line\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here’s what’s happening:\n", + "- `for row in range(size):` loops through each row of the checkerboard, repeating `size` times.\n", + "- Inside that, `for col in range(size):` loops through each column in the current row.\n", + "- The condition `(row + col) % 2 == 0` checks if the sum of the row and column indices is even. If it is, it prints a black circle (`⚫️`); otherwise, it prints a white circle (`⚪️`).\n", + "- `print()` at the end of the inner loop moves to the next line, creating a grid.\n", + "\n", + "> **Tip:** Nested loops are great for creating patterns like checkerboards. The outer loop handles the rows, while the inner loop handles the columns, allowing you to control each cell in the grid." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Drawing an American Flag**\n", + "\n", + "This is a more complex example that uses loops to draw an American flag with stars and stripes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Total number of rows in the flag\n", + "flag_height = 15\n", + "# Total number of columns in the flag \n", + "flag_width = 32 \n", + "\n", + "# Number of rows in the blue canton (top-left corner)\n", + "canton_height = 9 \n", + "# Number of columns in the blue canton \n", + "canton_width = 11 \n", + "\n", + "# Loop through each row of the flag\n", + "for row in range(flag_height): \n", + " # Loop through each column in the current row \n", + " for col in range(flag_width): \n", + " # Check if current cell is inside the canton\n", + " if row < canton_height and col < canton_width: \n", + " # Alternate stars and blue squares in the canton \n", + " if (row + col) % 2 == 0: \n", + " # Print a star\n", + " print('⭐', end='') \n", + " else:\n", + " # Print a blue square\n", + " print('🟦', end='') \n", + " else:\n", + " # Alternate red and white stripes\n", + " if row % 2 == 0:\n", + " # Print a red stripe \n", + " print('🟥', end='') \n", + " else:\n", + " # Print a white stripe\n", + " print('⬜️', end='')\n", + " # Move to the next line after each row \n", + " print() " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here’s what’s happening:\n", + "- `for row in range(flag_height):` loops through each row of the flag.\n", + "- Inside that, `for col in range(flag_width):` loops through each column in the current row.\n", + "- The first `if` statement checks if the current position is within the blue canton (top-left corner). If it is, it prints a blue square (`🟦`).\n", + "- `else` checks if the current row is even or odd to determine whether to print a red stripe (`🟥`) or a white stripe (`⬜️`).\n", + "- Modulo (`%`) is used to alternate the stripes to create the correct pattern of even and odd rows.\n", + "- Our inner `print()` statement ensures that each symbol is printed on the same line for the current row.\n", + "- `print()` at the end of the inner loop moves to the next line after completing a row, creating the full flag pattern.\n", + "\n", + "> **Tip:** You can use if-elif-else statements inside loops to create complex patterns and designs, like the American flag. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Now, click the Next Lesson button, open the file `20_Loop_with_Turtle.py`, and try running it." + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Loops", + "uid": "abX8sNwB" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py b/lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py index 49d6dda9..e4651fcb 100644 --- a/lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py +++ b/lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py @@ -1,23 +1,31 @@ """ -Turtles with a loop. +# 20_Loop_with_Turtle.py -This program has four identical lines of code to draw a square, -but you know you can use a loop to make the program simpler. +This program currently uses four pairs of lines to move and turn the turtle, +drawing each side of a square individually. + +In this exercise, you will modify the program to use a loop to draw the square instead. + +- Replace the repeated movement and turning lines with a for loop that runs four times. + +uid: 85lNu5qj +name: Loop With Turtle """ import turtle # Tell Python we want to work with the turtle -turtle.setup(600,600,0,0) # Set the size of the window +turtle.setup(600, 600, 0, 0) # Set the size of the window tina = turtle.Turtle() # Create a turtle named tina tina.shape('turtle') # Set the shape of the turtle to a turtle -tina.speed(2) # Make the turtle move as fast, but not too fast. +tina.speed(2) # Move at a moderate speed, not too fast. +# Repeat forward + left three more times to finish the square. tina.forward(150) # Move tina forward by the forward distance -tina.left(90) # Turn tina left by the left turn +tina.left(90) # Turn tina left by 90 degrees -tina.forward(150) # Continue the last two steps three more times -tina.left(90) # to draw a square +tina.forward(150) +tina.left(90) tina.forward(150) tina.left(90) @@ -25,4 +33,4 @@ tina.forward(150) tina.left(90) -turtle.exitonclick() # Close the window when we click on it \ No newline at end of file +turtle.exitonclick() # Close the window when we click on it diff --git a/lessons/10_Turtles/40_Loops/30_Loop_with_Turtle.py b/lessons/10_Turtles/40_Loops/30_Loop_with_Turtle.py index 324da245..1909d7d5 100644 --- a/lessons/10_Turtles/40_Loops/30_Loop_with_Turtle.py +++ b/lessons/10_Turtles/40_Loops/30_Loop_with_Turtle.py @@ -1,9 +1,16 @@ """ -Turtles with a loop. +# 30_Loop_with_Turtle.py -Study the previous program, 05a_Loop_with_Turtle.py, and then -write a new program that uses a loop to draw a pentagon. -( You can cut and past most of it! ) +In this program, use a loop to draw a regular pentagon (5-sided shape) with Tina the Turtle. + +- Review your previous program, 20_Loop_with_Turtle.py, which uses a loop to draw a shape with the turtle module. +- Make sure your code is clear and well-commented. +- Run your program to verify that Tina the Turtle draws a pentagon. + +(Hint: You can copy and modify your previous code!) + +uid: BpGnQq64 +name: Loop With Turtle """ ... # Your code here \ No newline at end of file diff --git a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb new file mode 100644 index 00000000..a15f0fae --- /dev/null +++ b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb @@ -0,0 +1,226 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Variables**\n", + "\n", + "In this lesson, you'll learn how to use **variables** to store information, perform calculations, and make your programs flexible. We'll explore different types of variables, see how they work in code, and practice using them to solve problems.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In case you got stuck on the previous lesson, here is one way you could have solved it:\n", + "\n", + "```python\n", + "for i in range(4): # Loop 4 times (once for each side of the square)\n", + " tina.forward(150) # Move Tina forward by 150 units to draw one side\n", + " tina.left(90) # Turn Tina left by 90 degrees to make a square corner\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **What is a Variable?**\n", + "\n", + "A variable is like a labeled box that stores information—numbers, words, or anything you want. You can change what's inside anytime, and your program uses the value in the box when it runs. This makes your code easier to update and experiment with, since you only need to change the value in one place. Variables help you organize, remember, and use information in your programs.\n", + "\n", + "For example, let's use a variable to store the number of sides for a shape:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# Store the number of sides for the shape in a variable\n", + "sides = 6\n", + "\n", + "# Calculate the angle needed to turn at each corner using the variable 'sides'\n", + "# The formula 360 / sides gives the angle for regular polygons\n", + "angle = 360 / sides\n", + "\n", + "# Print out the number of sides and the calculated angle\n", + "print('Angle for', sides, 'sides is', angle) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "In this code, `sides` and `angle` are variables:\n- `sides` is a whole number (integer) with a value of `6`.\n- `angle` is a decimal number (float) with a value of `60.0`.\n\nIf you change the value of `sides`, the value of `angle` updates automatically because `angle` is calculated as `360 / sides`. This makes your code flexible and easy to modify. Try changing `sides` to different numbers and watch how `angle` changes." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "### **Exploring Variables in Loops**\n\nYou may not have noticed, but you have been working with variables all along.\n\nLet's use a loop to calculate the angle for shapes with 1 to 9 sides:" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Calculate angles for shapes with 1 to 9 sides\n", + "for sides in range(1, 10): # Loop through numbers 1 to 9\n", + " angle = 360 / sides # Calculate the angle for the current number of sides\n", + " print(\"Angle for\", sides, \"sides is\", angle) # Print the result" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Notice how the angle changes for each number of sides. This shows the power of variables — they let you write code that adapts automatically.\n\nFor example, in your turtle programs, you can use a variable like `sides` to set how many sides your shape will have. The angle for each corner is calculated with the formula `angle = 360 / sides`. If you change the value of `sides`, the value of `angle` updates right away.\n\nWith variables, Tina the turtle can draw any polygon — triangle, square, hexagon, or even a 9-sided shape — just by changing one number. This makes your code flexible and easy to experiment with." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Different Types of Variables**\n", + "\n", + "Variables can store different types of data, such as **integers**, **floats**, **strings**, **booleans**, **lists**, **tuples**, **dictionaries**, **sets**, and **NoneType**. \n", + "\n", + "You don’t need to memorize all these types right now—let’s just look at some examples to see how they work!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Run Me!\n\n# Variables can store different types of data\n\n# Numbers (integers & floats)\nmy_age = 12 # This is an integer (a whole number)\ntemperature = 98.6 # This is a float (a fractional number)\n\n# Text (strings)\nmy_name = \"Alice\" \nfavorite_color = \"blue\"\n\n# Boolean values (True/False)\nis_sunny = True\nis_raining = False\n\nprint(\"Name:\", my_name)\nprint(\"Age:\", my_age)\nprint(\"Temperature:\", temperature)\nprint(\"Favorite color:\", favorite_color)\nprint(\"Is it sunny?\", is_sunny)\n\n# Try changing the numbers, text, or boolean values and see what happens." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Variables and Mathematical Operations**\n", + "\n", + "Variables are powerful because they let you do calculations and create messages that update automatically. Change a variable's value, and every calculation or message that uses it will change too! You can also combine variables with text (called concatenation) to display results or create custom messages in your programs." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Run Me!\n\n# Let's plan a pizza party using variables\n\n# First we'll want to declare them\npizzas = 5 # Number of pizzas ordered for the party\nslices_per_pizza = 8 # Number of slices in each pizza\npeople = 12 # Number of people sharing the pizzas\n\n# Now it's time for a little bit of math\ntotal_slices = pizzas * slices_per_pizza # Multiply to get total slices\nslices_per_person = total_slices / people # Divide to find slices each person gets\n\n# Finally, we can print our results\nprint(\"We have\", pizzas, \"pizzas\") # Show how many pizzas there are\nprint(\"Each pizza has\", slices_per_pizza, \"slices\") # Show slices per pizza\nprint(\"Total slices:\", total_slices) # Show total number of slices\nprint(\"Slices per person:\", slices_per_person) # Show how many slices each person gets\n\n# Try changing the number of pizzas, slices_per_pizza, or people, and see what happens." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Using Variables with Tina**\n", + "\n", + "Let's see how variables make Tina's drawing programs much more flexible and powerful. Instead of hard-coding numbers, we can use variables to control Tina's behavior:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "%run .lib/auto_turtle.py # This just handles the general imports for you\n", + "\n", + "tina = turtle(myTS) # type: ignore[name-defined] - Creates a turtle named tina\n", + "tina.shape('turtle') # Set the shape of the turtle to a turtle\n", + "tina.speed(2) # Make the turtle move as fast, but not too fast\n", + "\n", + "# Variables to control the shape\n", + "sides = 32 # Number of sides for the shape\n", + "size = 10 # Size of each side\n", + "color = \"blue\" # Color of the shape\n", + "\n", + "# Set Tina's color using our variable\n", + "tina.color(color) # Set the turtle's color\n", + "\n", + "# Calculate the angle using our sides variable\n", + "angle = 360 / sides\n", + "\n", + "# Draw the shape using our variables\n", + "for i in range(sides):\n", + " tina.forward(size) # Move forward by the size variable\n", + " tina.left(angle) # Turn by the calculated angle" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Variable Excercises**\n", + "\n", + "Try these exercises to practice working with variables:\n", + "\n", + "1. Create variables for your height and your friend's height, then calculate the difference.\n", + "2. Make variables for the length and width of a rectangle, then calculate the area.\n", + "3. Store your favorite number in a variable and calculate what it would be if doubled, tripled, and squared." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Your turn! Complete these variable exercises:\n", + "\n", + "# Exercise 1: Heights\n", + "my_height = 65 # My height in inches\n", + "friend_height = 68 # Friend's height in inches\n", + "height_difference = # Complete this calculation using variables\n", + "print(\"Height difference:\", height_difference, \"inches\")\n", + "\n", + "# Exercise 2: Rectangle area\n", + "length = 10 # Length of the rectangle\n", + "width = 5 # Width of the rectangle\n", + "area = # Complete this calculation using variables\n", + "print(\"Rectangle area:\", area, \"square units\")\n", + "\n", + "# Exercise 3: Favorite number calculations \n", + "favorite_number = 7 # Store your favorite number here\n", + "doubled = # Calculate double the favorite number using variables\n", + "tripled = # Calculate triple the favorite number using variables \n", + "squared = # Calculate the square of the favorite number using variables\n", + "print(\"Your number doubled:\", doubled)\n", + "print(\"Your number tripled:\", tripled)\n", + "print(\"Your number squared:\", squared)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Variables", + "uid": "HOBo0wvj" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables_and_Functions.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables_and_Functions.ipynb deleted file mode 100644 index 3274a706..00000000 --- a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables_and_Functions.ipynb +++ /dev/null @@ -1,175 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Variables and Functions\n", - "\n", - "
\n", - " A few things to remember:\n", - "\n", - " 1) Commit your code! Review `03_Check_In_You_Code` for a reminder \n", - " on how to commit and sync your changes. \n", - " 2) Remember that these `.ipynb` files have runnable cells. Review `01_Get_Started`, \n", - " in the section \"Running Programs\" for a reminder on how to run code cells in this file. \n", - " \n", - "
\n", - "\n", - "\n", - "Here is one of the ways to solve the last assignment, making shapes with a\n", - "loop:\n", - "\n", - "```python \n", - "\n", - "import turtle # Tell Python we want to work with the turtle\n", - "turtle.setup(600,600,0,0) # Set the size of the window\n", - "\n", - "tina = turtle.Turtle() # Create a turtle named tina\n", - "\n", - "for i in range(4):\n", - " tina.forward(150) # Move tina forward by the forward distance\n", - " tina.left(90) # Turn tina left by the left turn\n", - "\n", - "\n", - "```\n", - "\n", - "Let's see if we can make this code even simpler. Notice that the code that draws\n", - "the square has a range of `4` and left turn of `90` degrees, and $4*90 = 360$,\n", - "and there are 360 degrees in a circle. The pentagram has a range of `5` and a\n", - "left turn of `72` degrees, and $5*72=360$. Hmm... could we come up with a way to\n", - "compute the angle for any number of sides? To do that, we going to use\n", - "variables and math. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run me\n", - "# Compute the angle for a number of sides\n", - "\n", - "sides = 6\n", - "angle = 360/sides\n", - "\n", - "angle" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we want 6 sides the angle is 60 degrees, and sure enough, $60*6 = 360$.\n", - "\n", - "The words `sides` and `angle` are __variables__, which are like the names of boxes \n", - "that can hold values. We can __assign__ values to variables, then use those variables \n", - "in other places. \n", - "\n", - "```python \n", - "\n", - "sides = 4\n", - "angle = 360/sides # Calculate the angle from the number of sides. \n", - "\n", - "for i in range(sides):\n", - " tina.forward(150) # Move tina forward by the forward distance\n", - " tina.left(angle) # Turn tina left by the left turn\n", - "\n", - "```\n", - "\n", - "In this program, if you change the number that we assign to `sides`, the angle will change automatically!\n", - "\n", - "Let's see if the angles created by this equation seem correct. Fill in the\n", - "'...' in the program in the cell below to calculate angles from sides: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run me!\n", - "\n", - "for sides in range(1, 10):\n", - " angle = ...\n", - " print(\"Angle for \", sides, \" sides is \", angle)\n", - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Functions\n", - "\n", - "We can make another improvement in our program; by grouping \n", - "the commands to create a shape into a __function__. Here is an example of a function:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def say_hello(name, times): # define the function, and give it a name. 'name' and 'times' are parameters\n", - "\n", - " for i in range(times): # This is the body of the function\n", - " print(i, \"Hello \", name)\n", - "\n", - "say_hello(\"John\", 5) # Call the function, and pass it two arguments" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The line that starts with 'def' is the start of the function definition, and the\n", - "last line is where we call the function -- we actually run it. Try calling the\n", - "function again with different parameters." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "say_hello('bob',3)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - }, - "syllabus": { - "uid": "HOBo0wvj" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/lessons/10_Turtles/50_Variables_and_Functions/20_Efficient_Turtle.py b/lessons/10_Turtles/50_Variables_and_Functions/20_Efficient_Turtle.py index 2060ccb8..bcf33089 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/20_Efficient_Turtle.py +++ b/lessons/10_Turtles/50_Variables_and_Functions/20_Efficient_Turtle.py @@ -1,22 +1,24 @@ - """ -More Efficient Turtles +# 30_Efficient_Turtle.py + +In this program, use what you've learned about functions and variables to make a program that can draw a square, pentagon, and hexagon with a single function. -Use what you've learned about functions and variables to make a program that -can draw a square, pentagon, and hexagon with a single function +- Create a function that draws a polygon based on the number of sides passed to it as an argument. +- Use variables to calculate the angle needed to turn the turtle based on the number of sides. +- Call the function multiple times with different arguments to draw a square, pentagon, and hexagon. """ -import turtle # Tell Python we want to work with the turtle -turtle.setup(600,600,0,0) # Set the size of the window +import turtle # Tell Python we want to work with the turtle +turtle.setup(600, 600, 0, 0) # Set the size of the window -tina = turtle.Turtle() # Create a turtle named tina +tina = turtle.Turtle() # Create a turtle named tina -tina.shape('turtle') # Set the shape of the turtle to a turtle -tina.speed(2) # Make the turtle move as fast, but not too fast. +tina.shape('turtle') # Set the shape of the turtle to a turtle +tina.speed(2) # Move at a moderate speed, not too fast. def draw_polygon(sides): - angle = ... # Calculate angle from number of sides + angle = ... # Calculate angle from number of sides for i in range(...): # Loop through the number of sides ... # Move tina forward by the forward distance diff --git a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb new file mode 100644 index 00000000..081fed9c --- /dev/null +++ b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb @@ -0,0 +1,226 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": "# **Functions**\n\nIn this lesson, you'll learn how to create and use **functions** in Python. Functions help you organize your code, avoid repetition, and solve problems more efficiently. You'll discover how to define functions, use **parameters** to pass information, and return results. By the end, you'll be able to write your own reusable code blocks to perform calculations, make decisions, and more.\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **What Are Functions and Why Use Them?**\n", + "\n", + "A function is a named block of code that does a specific job. You can **call** a function whenever you need it, instead of writing the same code over and over. \n", + "\n", + "Functions help programmers:\n", + "- **Solve problems step-by-step** through breaking big problems into smaller ones\n", + "- **Organize your code** so it becomes easier to read and fix \n", + "- **Reuse code** and avoid having to copy and paste \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Defining Functions and Performing Calculations**\n", + "\n", + "Let's see how we can turn our angle calculation from the previous lesson into a function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Run Me!\n\n# Define the function\ndef calculate_angle(sides): # Define the function with a parameter called 'sides'\n angle = 360 / sides # Calculate the turning angle for a regular polygon\n return angle # Send the calculated angle back to whoever called this function\n\n# Now we can use the function with different values\ntriangle_angle = calculate_angle(3) # Call the function with 3 sides (triangle)\nsquare_angle = calculate_angle(4) # Call the function with 4 sides (square) \nhexagon_angle = calculate_angle(6) # Call the function with 6 sides (hexagon)\n\n# Print the results\nprint(\"Triangle angle:\", triangle_angle)\nprint(\"Square angle:\", square_angle)\nprint(\"Hexagon angle:\", hexagon_angle) \n\n# Try changing the parameters to see how the angle changes, or create new shapes." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When you define a function, you use the word `def` followed by your function's name. Inside the parentheses, you list any **parameters**, and when you call the function, you provide **arguments** that fill in those placeholders and let the function do its job.\n", + "\n", + "For example, calling `calculate_angle(4)` sets `sides = 4` and gives you the angle for a square. Calling `calculate_angle(6)` sets `sides = 6` for a hexagon.\n", + "\n", + "You can also use a function in a loop to quickly calculate angles for many shapes, which saves time and keeps your code neat.\n", + "\n", + "Let's take a look:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Define the function\n", + "def calculate_angle(sides): # Define a function that takes one parameter\n", + " angle = 360 / sides # Formula: 360 degrees divided by number of sides\n", + " return angle # Return the calculated angle to the caller\n", + "\n", + "# Now let's test it with different shapes using a loop\n", + "for sides in range(3, 10): # Loop through 3 to 9 sides (3=triangle, 4=square, etc.)\n", + " angle = calculate_angle(sides) # Call our function with the current number of sides\n", + " print(\"Angle for\", sides, \"sides is\", angle, \"degrees\") # Display the result" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Using Parameters and Arguments**\n", + "\n", + "Functions use parameters to accept input values, and you provide arguments when calling the function. This lets you reuse the same function with different data.\n", + "\n", + "Here's a simple example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# A function that uses parameters and arguments\n", + "def say_hello(name, times): # Define function with two parameters: name and times\n", + " for i in range(times): # Repeat the greeting for the specified number of times\n", + " print(i + 1, \"Hello\", name) # Print greeting number (i+1) and the person's name\n", + "\n", + "say_hello(\"John\", 5) # Call the function: \"John\" is the name, 5 is how many times" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Remember, the `def` line starts the function definition and the last line calls and then runs the function.\n", + "\n", + "Try calling the function with different arguments:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "say_hello('Alice', 3) # Greet Alice 3 times\n", + "say_hello('Bob', 2) # Greet Bob 2 times \n", + "say_hello('Charlie', 4) # Greet Charlie 4 times\n", + "\n", + "# Try calling the function with your own names and different numbers!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Returning Values from Functions**\n", + "\n", + "Many functions calculate and return values you can use elsewhere in your program. The `return` statement sends a value back to whoever called the function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Run Me!\n\n# Functions can perform calculations and store their results in variables\n\ndef add_numbers(x, y): # Adds two numbers and returns their sum\n result = x + y # Calculate the sum\n return result # Return the sum\n\ndef multiply_numbers(a, b): # Multiplies two numbers and returns the product\n return a * b # Return the product\n\ndef calculate_area(length, width): # Calculates the area of a rectangle\n area = length * width # Area = length × width\n return area # Return the area\n\n# Call the functions and store their results in variables\n\nsum_result = add_numbers(10, 5) # Store the sum of 10 and 5\nproduct = multiply_numbers(4, 7) # Store the product of 4 and 7\nroom_area = calculate_area(12, 10) # Store the area of a 12×10 room\n\n# Print the results returned from the functions\n\nprint(\"10 + 5 =\", sum_result)\nprint(\"4 × 7 =\", product)\nprint(\"Room area:\", room_area, \"square feet\")\n\n# Try changing the numbers to see different results." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Advanced Function Examples**\n", + "\n", + "Functions can make decisions, validate input, and return multiple results:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Run Me!\n\nfrom math import ceil\n\n# Decision function\ndef check_temperature(temp): # Function takes temperature as parameter\n if temp > 80: # Check if it's hot (above 80 degrees)\n return \"It's hot outside!\" # Return message for hot weather\n elif temp > 60: # Check if it's nice (between 60-80 degrees)\n return \"Nice weather!\" # Return message for nice weather \n else: # Everything else (60 degrees or below)\n return \"It's cold outside!\" # Return message for cold weather\n\n# Age validation\ndef is_valid_age(age): # Function to check if an age makes sense\n if age >= 0 and age <= 100: # Reasonable age range (0 to 100 years)\n return True # Return True if age is valid\n else: # If age is negative or over 100\n return False # Return False if age is invalid\n\n# Pizza calculator\ndef pizza_calculator(people, slices_per_person=3): # Default: 3 slices per person if not specified\n total_slices_needed = people * slices_per_person # Total slices = people × slices each person gets\n pizzas_needed = ceil(total_slices_needed / 8) # Round up to the next whole pizza (8 slices per pizza)\n return total_slices_needed, pizzas_needed # A function can return more than one value\n\n# Test functions and print results\nweather_report = check_temperature(75) # Test temperature function with 75 degrees\nprint(weather_report)\n\nage_valid = is_valid_age(25) # Test age validation with 25 years old\nprint(\"Is age 25 valid?\", age_valid)\n\n# When a function returns more than one value, we can catch them with a\n# comma-separated assignment like this:\nslices, pizzas = pizza_calculator(10) # Calculate pizza needs for 10 people\nprint(\"For 10 people: need\", slices, \"slices and\", pizzas, \"pizzas\") \n\n# Try changing the inputs to see different results." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## **Using Functions with Tina**\n\nNow let's put our function knowledge to work with Tina." + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Run Me!\n\n%run .lib/auto_turtle.py # This just handles the general imports for you\n\ntina = turtle(myTS) # type: ignore[name-defined] - Creates a turtle named tina\ntina.shape('turtle') # Set the shape of the turtle to a turtle\ntina.speed(2) # Make the turtle move as fast, but not too fast\n\n# Function to draw any polygon\ndef draw_polygon(sides, size, color=\"black\"):\n \"\"\"Draws a polygon with the specified number of sides and size\"\"\"\n tina.color(color)\n angle = 360 / sides # Calculate the turning angle\n for i in range(sides):\n tina.forward(size)\n tina.left(angle)\n\n# Function to move Tina without drawing\ndef move_tina(x, y):\n \"\"\"Moves Tina to a new position without drawing a line\"\"\"\n tina.penup()\n tina.goto(x, y)\n tina.pendown()\n\n# Now let's use our functions\ndraw_polygon(3, 50, \"red\") # Draw a red triangle\nmove_tina(100, 0) # Move to a new spot\ndraw_polygon(4, 60, \"blue\") # Draw a blue square\nmove_tina(225, 0) # Move again\ndraw_polygon(6, 40, \"green\") # Draw a green hexagon\n\n# Try calling the functions with different parameters to see how they change the drawing." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## **Function Challenges**\n\nWrite these functions using parameters, return values, and logic:" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Exercise 1: Write a function that converts Celsius to Fahrenheit\n", + "# Formula: F = (C × 9/5) + 32\n", + "def celsius_to_fahrenheit(celsius):\n", + " # Your code here\n", + "\n", + "# Exercise 2: Write a function that finds the larger of two numbers\n", + "def find_larger(num1, num2):\n", + " # Your code here\n", + "\n", + "# Exercise 3: Write a function that calculates the perimeter of a rectangle\n", + "def rectangle_perimeter(length, width):\n", + " # Your code here\n", + "\n", + "# Exercise 4: Write a function that counts down from a number\n", + "def countdown(start_number):\n", + " # Your code here - use a loop to print numbers from start_number down to 1\n", + "\n", + "# Use print statements to see the results:\n", + "# print(celsius_to_fahrenheit(0)) # Should print 32.0\n", + "# print(find_larger(10, 5)) # Should print 10\n", + "# print(rectangle_perimeter(5, 3)) # Should print 16\n", + "# print(countdown(5)) # Should print 5, 4, 3, 2, 1" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Functions", + "uid": "0CwXIYSb" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/10_Turtles/60_More_Turtle_Programs/10_More_Turtle_Programs.ipynb b/lessons/10_Turtles/60_More_Turtle_Programs/10_More_Turtle_Programs.ipynb index 7ebc70f4..56f02736 100644 --- a/lessons/10_Turtles/60_More_Turtle_Programs/10_More_Turtle_Programs.ipynb +++ b/lessons/10_Turtles/60_More_Turtle_Programs/10_More_Turtle_Programs.ipynb @@ -1,337 +1,188 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Learn more about the Turtle\n", - "\n", - "( Have you checked in your code? ) \n", - "\n", - "We've seen a lot of Turtle programs so far, but there is a lot more to the\n", - "Python turtle. Fortunately, there is a list of everything that the Turtle is\n", - "capable of: the Python documentation for the turtle module. Visit this site to see\n", - "the full documentation: \n", - "\n", - "https://docs.python.org/3.10/library/turtle.html\n", - "\n", - "There are a lot of interesting new things in this documentation! For instance: \n", - "\n", - "* You can make the turtle go to a position with `setx()` and `sety()`\n", - "* You can set the turtle shape from any image file with `.addshape` and `turtle.shape()`" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, - "source": [ - "# Change the Turtle Image\n", - "\n", - "Here is an example of how you can change the look of the turtle to an image. \n", - "You can copy the ``set_turtle_image()`` function into your own programs. \n", - "\n", - "Important:\n", - "* The image file must be a GIF. ( The file name ends in `.gif` )\n", - "* The image file must be in the `image` directory, and the `image` directory\n", - " must be in the same directory as your program. \n", - "\n", - "```python\n", - "\n", - "import turtle\n", - "\n", - "def set_turtle_image(turtle, image_name):\n", - " \"\"\"Set the turtle's shape to a custom image.\"\"\"\n", - "\n", - " from pathlib import Path\n", - " image_dir = Path(__file__).parent.parent / \"images\"\n", - " image_path = str(image_dir / image_name)\n", - "\n", - " screen = turtle.getscreen()\n", - " screen.addshape(image_path)\n", - " turtle.shape(image_path)\n", - "\n", - "# Set up the screen\n", - "screen = turtle.Screen()\n", - "screen.setup(width=600, height=600)\n", - "\n", - "# Create a turtle and set its shape to the custom GIF\n", - "t = turtle.Turtle()\n", - "\n", - "set_turtle_image(t, \"pikachu.gif\")\n", - "\n", - "t.penup()\n", - "t.speed(3)\n", - "\n", - "for i in range(4):\n", - " t.goto(200, 200)\n", - " t.goto(-200, -200)\n", - "\n", - "turtle.exitonclick() \n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Change the Background Image\n", - "\n", - "Important:\n", - "* The image file can be a GIF or a PNG\n", - "* The image file must be in the `image` directory, and the `image` directory\n", - " must be in the same directory as your program. \n", - "\n", - "```python\n", - "\n", - "import turtle\n", - "\n", - "def set_background_image(window, image_name):\n", - " \"\"\"Set the background image of the turtle window to the image with the given name.\"\"\"\n", - "\n", - " from pathlib import Path\n", - " from PIL import Image\n", - "\n", - " image_dir = Path(__file__).parent.parent / \"images\"\n", - " image_path = str(image_dir / image_name)\n", - "\n", - " image = Image.open(image_path)\n", - " \n", - " window.setup(image.width, image.height, startx=0, starty=0)\n", - " window.bgpic(image_path)\n", - "\n", - "# Set up the screen\n", - "import turtle # Tell Python we want to work with the turtle\n", - "turtle.setup(width=600, height=600) # Set the size of the window\n", - "\n", - "tina = turtle.Turtle() # Create a turtle named tina\n", - "\n", - "screen = turtle.Screen() # Get the screen that tina is on\n", - "set_background_image(screen, \"emoji.png\") # Set the background image of the screen\n", - "\n", - "turtle.exitonclick() \n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# More than One Turtle\n", - "\n", - "You can have more than one turtle on the screen. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```python\n", - "# Double click on this cell to copy the code. \n", - "\n", - "import turtle as turtle\n", - "\n", - "screen = turtle.Screen()\n", - "screen.setup(width=600, height=600)\n", - "screen.bgcolor('white')\n", - "\n", - "t1 = turtle.Turtle()\n", - "t1.penup()\n", - "t1.shape(\"turtle\")\n", - "\n", - "t2 = turtle.Turtle()\n", - "t2.penup()\n", - "t2.shape(\"arrow\")\n", - "\n", - "for i in range(-200, 200):\n", - " t1.goto(i,i)\n", - " t2.goto(i,-i)\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Click on the Screen\n", - "\n", - "You can make the turtle do things when you click on the screen. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```python\n", - "# Double click on this cell to copy the code \n", - "\n", - "import turtle as turtle\n", - "\n", - "screen = turtle.Screen()\n", - "screen.setup(width=600, height=600)\n", - "screen.bgcolor('white')\n", - "\n", - "t = turtle.Turtle()\n", - "t.penup()\n", - "t.shape(\"turtle\")\n", - "\n", - "# This is the function that gets called when you click on the screen\n", - "def screen_clicked(x, y):\n", - " \"\"\"Print the x and y coordinates of the screen when clicked.\n", - " and make the turtle move to the clicked location.\"\"\"\n", - "\n", - " print('You pressed: x=' + str(x) + ', y=' + str(y))\n", - "\n", - " t.goto(x, y)\n", - " \n", - "screen.onclick(screen_clicked) # Important! Tell Python which function to use when the screen is clicked\n", - "\n", - "turtle.done() # Important! Use `done` not `exitonclick` to keep the window open\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Click on the Turtle\n", - "\n", - "You can execute a function when the user clicks on the turtle. \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```python \n", - "# Double click on this cell to copy the code\n", - "\n", - "import turtle as turtle\n", - "\n", - "turtle.setup(width=600, height=600)\n", - "\n", - "t = turtle.Turtle()\n", - "\n", - "t.shape(\"turtle\")\n", - "t.turtlesize(stretch_wid=10, stretch_len=10, outline=4) # Make the turtle really big\n", - "\n", - "def turtle_clicked(t, x, y):\n", - " \"\"\"Function that gets called when the user clicks on the turtle\n", - "\n", - " This function will make the turtle tilt 20 degrees 18 times, making a full\n", - " circle. It is called by the turtle when the user clicks on it.\n", - "\n", - " Args:\n", - " t (Turtle): The turtle object that was clicked\n", - " x (int): The x coordinate of the click\n", - " y (int): The y coordinate of the click\n", - " \"\"\"\n", - "\n", - " print('turtle clicked!')\n", - " \n", - " for i in range(0,360, 20): # Full circle, 20 degrees at a time\n", - " t.tilt(20) # Tilt the turtle 20 degrees\n", - "\n", - "# Connect the turtle to the turtle_clicked function\n", - "t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y))\n", - "\n", - "turtle.done() # Important! Use `done` not `exitonclick` to keep the window open\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "In the line `t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y))` the\n", - "lambda keyword is used to create an anonymous function, a function that does not\n", - "have a name. Lambda functions are advanced, we'll explain them in a future\n", - "lesson, but for now, just know that this is a way to pass arguments to a\n", - "function when using the `onclick` method. You can just copy this line to make\n", - "the turtle respond to clicks.\n", - "\n", - "\n", - "## But Which Turtle?\n", - "Unfortunately, if you have multiple turtles, you wont be able to tell which\n", - "turtle was clicked on in the ``turtle_clicked`` function. Here is one way to\n", - "solve that problem, using some advanced Python that we have not learned yet. \n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "```python\n", - "# Double click on this cell to copy the code\n", - "\n", - "import turtle as turtle\n", - "\n", - "screen = turtle.Screen()\n", - "screen.setup(width=600, height=600)\n", - "screen.bgcolor('white')\n", - "\n", - "t = turtle.Turtle()\n", - "t.penup()\n", - "t.shape(\"turtle\")\n", - "t.turtlesize(stretch_wid=10, stretch_len=10, outline=4)\n", - "\n", - "def turtle_clicked(t, x, y):\n", - "\n", - " print('turtle clicked!')\n", - " \n", - " for i in range(0,360, 20):\n", - " t.tilt(20)\n", - "\n", - "t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y))\n", - "\n", - "turtle.done() # Important! Use `done` not `exitonclick` to keep the window open\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Check In Your Code\n", - "\n", - "Now would be a great time to ensure all of your changes are checked in. You can review how to check in your\n", - "code, and how to restart your Codespace, in our [Code Check In How To Guide](https://curriculum.jointheleague.org/howto/checkin_restart.html).\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - }, - "syllabus": { - "uid": "IloYptI2" + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": "# **More Turtle Programs**\n\nAlthough we've used a lot of Turtle examples so far, the turtle module has many more\nfeatures. The official Python documentation lists everything the turtle can do.\n\nA few useful extras you'll find in the docs:\n\n* Move the turtle directly to a coordinate with `setx()` and `sety()`\n* Use your own image files as turtle shapes with `screen.addshape()` and `turtle.shape()`\n\nCheck out this link, or take a look below!\n\n
\n
\n Click Here To View The Official Python Turtle Documentation\n \n
" + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "plaintext" } + }, + "source": [ + "## **Change the Turtle's Image**\n", + "\n", + "You can change how the turtle looks by setting its shape to a GIF image. Copy the\n", + "`set_turtle_image()` function into your programs to reuse this behavior." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "```python\n# Double-click to copy!\n\nimport turtle\n\ndef set_turtle_image(turtle, image_name):\n \"\"\"Set the turtle's shape to a custom image.\"\"\"\n\n from pathlib import Path # Import Path from pathlib module\n image_dir = Path(__file__).parent / \"images\" # Define the directory containing images\n image_path = str(image_dir / image_name) # Create the full path to the image file\n\n screen = turtle.getscreen() # Get the turtle's screen\n screen.addshape(image_path) # Register the image as a shape\n turtle.shape(image_path) # Set the turtle's shape to the image\n\n# Set up the screen\nscreen = turtle.Screen()\nscreen.setup(width=600, height=600)\n\n# Create a turtle and set its shape to the custom GIF\nt = turtle.Turtle()\n\nset_turtle_image(t, \"pikachu.gif\")\n\nt.penup() # Prevent drawing when moving\nt.speed(3) # Set a moderate speed\n\n# Move the turtle to each corner of the screen in a square pattern\nfor x, y in [(200, 200), (200, -200), (-200, -200), (-200, 200)]:\n t.goto(x, y)\n\nturtle.exitonclick() \n```" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> **Note:** The image must be a GIF, and you can put the image inside of an *images* folder next to your program file (e.g., `pikachu.gif`)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Set a Background Picture**\n", + "\n", + "You can use a GIF or PNG as the turtle window background. Put the file in the\n", + "*images* folder next to your program.\n", + "\n", + "#### Double-click this code to copy it!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```python\n", + "# Double-click to copy!\n", + "\n", + "import turtle\n", + "\n", + "def set_background_image(window, image_name):\n", + " \"\"\"Set the background image of the turtle window to the image with the given name.\"\"\"\n", + " from pathlib import Path # Import Path from pathlib module\n", + " from PIL import Image # Import Image from PIL (Pillow) library\n", + "\n", + " image_dir = Path(__file__).parent / \"images\" # Define the directory containing images\n", + " image_path = str(image_dir / image_name) # Create the full path to the image file\n", + "\n", + " image = Image.open(image_path) # Open the image to get its dimensions\n", + " \n", + " window.setup(image.width, image.height, startx=0, starty=0) # Set window size to image size\n", + " window.bgpic(image_path) # Set the background picture of the window\n", + "\n", + "turtle.setup(width=600, height=600) # Set the size of the window\n", + "\n", + "tina = turtle.Turtle() # Create a turtle named tina\n", + "\n", + "screen = turtle.Screen() # Get the screen that tina is on\n", + "set_background_image(screen, \"emoji.png\") # Set the background image of the screen\n", + "\n", + "turtle.exitonclick() \n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **More Than One Turtle**\n", + "\n", + "You can put multiple turtles on the same screen and control each independently.\n", + "\n", + "#### Double-click this code to copy it!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "```python\n# Double-click to copy!\n\nimport turtle # Import the turtle module\n\nscreen = turtle.Screen() # Set up the screen\nscreen.setup(width=600, height=600) # Set the size of the window\nscreen.bgcolor('white') # Set the background color\n\nt1 = turtle.Turtle() # Create the first turtle\nt1.penup() # Lift the pen to move without drawing\nt1.shape(\"turtle\") # Set the shape of the turtle\n\nt2 = turtle.Turtle() # Create the second turtle\nt2.penup() # Lift the pen to move without drawing\nt2.shape(\"arrow\") # Set the shape of the turtle\n\n# Move both turtles in a loop\nfor i in range(-200, 200):\n t1.goto(i, i)\n t2.goto(i, -i)\n```" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Respond to Screen Clicks**\n", + "\n", + "You can run a function when the user clicks anywhere on the turtle screen." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "```python\n# Double-click to copy!\n\nimport turtle\n\nscreen = turtle.Screen() # Set up the screen\nscreen.setup(width=600, height=600) # Set the size of the window\nscreen.bgcolor('white') # Set the background color\n\nt = turtle.Turtle() # Create a turtle\nt.penup() # Prevent drawing when moving\nt.shape(\"turtle\") # Set the shape of the turtle\n\n# This is the function that gets called when you click on the screen\ndef screen_clicked(x, y):\n \"\"\"Print the x and y coordinates of the screen when clicked.\n and make the turtle move to the clicked location.\"\"\"\n\n print('You pressed: x=' + str(x) + ', y=' + str(y))\n\n t.goto(x, y) # Move the turtle to the clicked location\n \nscreen.onclick(screen_clicked) # Important! Tell Python which function to use when the screen is clicked\n\nturtle.done() # Important! Use `done` not `exitonclick` to keep the window open\n```" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Clicking The Turtle Directly**\n", + "\n", + "You can also run a function when the user clicks a turtle. This allows the turtle library to call your\n", + "function with the click coordinates and the turtle object.\n", + "\n", + "#### Double-click this code to copy it!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "```python\n# Double-click to copy!\n\nimport turtle # Import the turtle module\n\nscreen = turtle.Screen() # Set up the screen\nscreen.setup(width=600, height=600) # Set the size of the window\nscreen.bgcolor('white') # Set the background color\n\nt = turtle.Turtle() # Create a turtle\nt.shape(\"turtle\") # Set the shape of the turtle\nt.turtlesize(stretch_wid=10, stretch_len=10, outline=4) # Make the turtle really big\n\ndef turtle_clicked(t, x, y):\n \"\"\"Function that gets called when the user clicks on the turtle\n\n This function will make the turtle tilt 20 degrees 18 times, making a full\n circle. It is called by the turtle when the user clicks on it.\n\n Args:\n t (Turtle): The turtle object that was clicked\n x (int): The x coordinate of the click\n y (int): The y coordinate of the click\n \"\"\"\n\n print('turtle clicked!')\n \n for i in range(0,360, 20): # Full circle, 20 degrees at a time\n t.tilt(20) # Tilt the turtle 20 degrees\n\n# Connect the turtle to the turtle_clicked function\nt.onclick(lambda x, y, t=t: turtle_clicked(t, x, y))\n\nturtle.done() # Important! Use `done` not `exitonclick` to keep the window open\n```" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + " Click Here For Further Explanation\n", + "\n", + "When you have more than one turtle, the click handler must know which turtle was clicked, not just the **x** and **y** coordinates.\n", + "\n", + "```python\n", + "# Lambda function\n", + "t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y))\n", + "```\n", + "\n", + "The `t=t` part essentially just locks in, or remembers, the current turtle so the handler won't mix them up later.\n", + "\n", + "```python\n", + "# Named function\n", + "def make_handler(t):\n", + " def handler(x, y):\n", + " turtle_clicked(t, x, y)\n", + " return handler\n", + "\n", + "t.onclick(make_handler(t))\n", + "```\n", + "\n", + "Both do the same thing and we will go over how to use the **lambda** function later on, so just pick whichever option you find easier.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "> **Tip:** If you haven't checked in your code, now's a good time. For a refresher, see the Check in Code and Restart Codespaces page." + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" }, - "nbformat": 4, - "nbformat_minor": 2 -} + "syllabus": { + "uid": "IloYptI2", + "name": "More Turtle Programs" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/10_Turtles/60_More_Turtle_Programs/20_More_Turtle_programs.py b/lessons/10_Turtles/60_More_Turtle_Programs/20_More_Turtle_Programs.py similarity index 54% rename from lessons/10_Turtles/60_More_Turtle_Programs/20_More_Turtle_programs.py rename to lessons/10_Turtles/60_More_Turtle_Programs/20_More_Turtle_Programs.py index 8a27e9bf..35406796 100644 --- a/lessons/10_Turtles/60_More_Turtle_Programs/20_More_Turtle_programs.py +++ b/lessons/10_Turtles/60_More_Turtle_Programs/20_More_Turtle_Programs.py @@ -1,7 +1,8 @@ """ -Copy the code from the previous lesson, 08a_More_Turtle_programs.ipynb, -from the section "Change the Turtle Image" +Copy the code from the previous lesson, 10_More_Turtle_Programs.ipynb, +from the section "Change the Turtle's Image" Then change the code so that the turtle has a different image ( look in the 'images' -directory ) and moves to the corners of the screen in a square pattern. -""" \ No newline at end of file +directory ) and moves to the corners of the screen in a square pattern. +""" + diff --git a/lessons/10_Turtles/60_More_Turtle_Programs/30_More_Turtle_Programs.py b/lessons/10_Turtles/60_More_Turtle_Programs/30_More_Turtle_Programs.py index 088e6989..4ede6d59 100644 --- a/lessons/10_Turtles/60_More_Turtle_Programs/30_More_Turtle_Programs.py +++ b/lessons/10_Turtles/60_More_Turtle_Programs/30_More_Turtle_Programs.py @@ -1,7 +1,8 @@ """ -Copy the code from the previous lesson, 08a_More_Turtle_programs.ipynb, -from the section "Change the Background Image" +Copy the code from the previous lesson, 10_More_Turtle_Programs.ipynb, +from the section "Set a Background Picture" + +Then change the code so that the turtle uses a different background image +(look in the 'images' directory) and draws a shape on top of it with your turtle. +""" -Then change the code so that the turtle has a different image ( look in the 'images' -directory ) and moves to the corners of the screen in a square pattern. -""" \ No newline at end of file diff --git a/lessons/10_Turtles/60_More_Turtle_Programs/40_More_Turtle_Programs.py b/lessons/10_Turtles/60_More_Turtle_Programs/40_More_Turtle_Programs.py index 3aecd860..b1217364 100644 --- a/lessons/10_Turtles/60_More_Turtle_Programs/40_More_Turtle_Programs.py +++ b/lessons/10_Turtles/60_More_Turtle_Programs/40_More_Turtle_Programs.py @@ -1,6 +1,6 @@ """ -Copy the code from the previous lesson, 08a_More_Turtle_programs.ipynb, -from the section " Click on the Turtle" +Copy the code from the previous lesson, 10_More_Turtle_Programs.ipynb, +from the section "Clicking the Turtle Directly" Then change the code so that the turtle has a different image ( look in the 'images' directory ) and when you click on it, it moves to a random location on the screen. @@ -10,4 +10,5 @@ import random x = random.randint(-300, 300) y = random.randint(-300, 300) -""" \ No newline at end of file +""" + diff --git a/lessons/10_Turtles/70_Projects/10_LeagueBot.py b/lessons/10_Turtles/70_Projects/10_LeagueBot.py index 64076070..5bea5a3b 100644 --- a/lessons/10_Turtles/70_Projects/10_LeagueBot.py +++ b/lessons/10_Turtles/70_Projects/10_LeagueBot.py @@ -1,4 +1,4 @@ -""" +Leagu""" LeagueBot Write your own turtle program! Here is what your program should do @@ -6,7 +6,7 @@ 1) Change the turtle image to 'leaguebot_bot.gif' 2) Change the turtle size to 10x10 3) Change the turtle line color to 'blue' -4) Draw a hexagon using a loop and variables. +4) Draw a hexagon using a loop and variables. """ import turtle as turtle diff --git a/lessons/10_Turtles/70_Projects/20_Tash_Me.py b/lessons/10_Turtles/70_Projects/20_Tash_Me.py index f65cebf5..9eec93b9 100644 --- a/lessons/10_Turtles/70_Projects/20_Tash_Me.py +++ b/lessons/10_Turtles/70_Projects/20_Tash_Me.py @@ -1,4 +1,4 @@ -""" +""" Tash Me Write a program that: @@ -6,8 +6,7 @@ 2) Make the turtle shape a moustache 3) Move the moustache to the right spot on the emoji -Hint: See 08a_More Turtle Programs, section 'Change the Background Image' and -'Change the Turtle Shape' +Hint: See the `10_More_Turtle_Programs` section labeled 'Set a Background Picture'. """ ... # Your code here \ No newline at end of file diff --git a/lessons/10_Turtles/70_Projects/30_Tash_Me_Click.py b/lessons/10_Turtles/70_Projects/30_Tash_Me_Click.py index 9a953a9a..5a7cde90 100644 --- a/lessons/10_Turtles/70_Projects/30_Tash_Me_Click.py +++ b/lessons/10_Turtles/70_Projects/30_Tash_Me_Click.py @@ -1,8 +1,9 @@ -# Tash Me with a Click -# -# Update your Tash Me program ( copy your old program here ) to put -# the moustache where you click on the screen. -# -# Hint: See 08a_More Turtle Programs, section 'Click on the Screen' +""" +# 30_Tash_Me_Click.py + +Copy your old 20_Tash_Me.py code here and update the program to put the moustache where you click on the screen. + +Hint: See 10_More_Turtle_Programs, section 'Respond to Screen Clicks' +""" ... # Your code here \ No newline at end of file diff --git a/lessons/10_Turtles/70_Projects/40_Tash_Me_Twirl.py b/lessons/10_Turtles/70_Projects/40_Tash_Me_Twirl.py index c86ca34f..8e648600 100644 --- a/lessons/10_Turtles/70_Projects/40_Tash_Me_Twirl.py +++ b/lessons/10_Turtles/70_Projects/40_Tash_Me_Twirl.py @@ -1,10 +1,9 @@ -""" -Tash Me with a Twirl +""" +# 40_Tash_Me_Twirl.py -Update your Tash Me Click program ( copy your old program here ) -so the moustache will twirl when you click on it. +Copy your old 30_Tash_Me_Click.py code here and update the program so that the moustache will twirl when you click on it. -Hint: See 08a_More Turtle Programs, section 'Click on the Turtle' +Hint: See 10_More_Turtle_Programs, section 'Clicking The Turtle Directly' """ ... # Your code here \ No newline at end of file diff --git a/lessons/10_Turtles/80_Introducing_Lists/10_Lists.ipynb b/lessons/10_Turtles/80_Introducing_Lists/10_Lists.ipynb new file mode 100644 index 00000000..58115acc --- /dev/null +++ b/lessons/10_Turtles/80_Introducing_Lists/10_Lists.ipynb @@ -0,0 +1,242 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Lists**\n", + "\n", + "One of the most useful data structures in Python is a **list**. Grocery lists are a good way to think about them, as they keep an ordered collection of items you can look up or change, like this:\n", + "\n", + "Things To Buy:\n", + "- `Apples`\n", + "- `Oranges`\n", + "- `Bread`\n", + "- `Milk`\n", + "\n", + " But in Python we write lists using square brackets, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "things_to_buy = ['apples', 'oranges', 'bread', 'milk']\n", + "things_to_buy # This just displays the list so you can see it" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Indexing**\n", + "\n", + "You can use `[` and `]` with a number to get a single item. Since numbers in Python start at 0, the first index item is `things_to_buy[0]`.\n", + "\n", + "Try the example below to see indexing in action." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "things_to_buy = ['apples', 'oranges', 'bread', 'milk']\n", + "things_to_buy[1] # This gets the second item (index 1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Iterating**\n", + "\n", + "You can loop over a list to do something with each item. This is\n", + "called iteration and is very common in programs. The example below prints each\n", + "item in the list." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "things_to_buy = ['apples', 'oranges', 'bread', 'milk']\n", + "for item in things_to_buy:\n", + " print(item)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Helpful list operations**\n", + "\n", + "Here are a few examples of common things you can do with lists:\n", + "\n", + "| Operation | Description |\n", + "| :--- | :--- |\n", + "| `append(x)` | adds `x` to the end |\n", + "| `insert(i, x)` | inserts `x` at index `i` |\n", + "| `remove(x)` | removes the first occurrence of `x` |\n", + "| `len(list)` | gets the number of items |\n", + "| `list[start:stop]` | returns a sub-list (slicing) |" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "items = ['apple', 'banana']\n", + "print('Start:', items)\n", + "\n", + "items.append('cherry')\n", + "print('After append:', items)\n", + "\n", + "items.insert(1, 'orange')\n", + "print('After insert:', items)\n", + "\n", + "items.remove('banana')\n", + "print('After remove:', items)\n", + "print('Length:', len(items))\n", + "print('Slice (0:2):', items[0:2])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "There is a lot more you can do with lists (sorting, nested lists, and more),\nbut this covers the basics you need for our lessons." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Lists and Tina**\n", + "\n", + "Lists are useful in graphics programs too. For example, you can store colors in a list and loop over them to draw each side in a different color:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "%run .lib/auto_turtle.py # This just handles the general imports for you\n", + "\n", + "tina = turtle(myTS) # type: ignore[name-defined] - Creates a turtle named tina\n", + "tina.shape('turtle') # Set the shape of the turtle to a turtle\n", + "tina.speed(2) # Make the turtle move as fast, but not too fast\n", + "\n", + "forward = 100\n", + "left = 90\n", + "colors = [ 'red', 'blue', 'black', 'orange']\n", + "\n", + "for color in colors:\n", + " tina.color(color)\n", + " tina.forward(forward)\n", + " tina.left(left)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or, we could use a list to change the angle that tina turns: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "%run .lib/auto_turtle.py # This just handles the general imports for you\n\ntina = turtle(myTS) # type: ignore[name-defined] - Creates a turtle named tina\ntina.shape('turtle') # Set the shape of the turtle to a turtle\ntina.speed(2) # Make the turtle move as fast, but not too fast\n\nforward = 100\n\nfor angle in [90, 90, 90, 90]:\n tina.forward(forward)\n tina.left(angle)" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## **Exercises (Optional)**\n\nIf you are looking for more practice, try to complete the following exercises:" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Exercise 1 - Favorite Foods\n", + "\n", + "# Create a list called `favorite_foods` with 5 items\n", + "... # your code here\n", + "\n", + "# Now use a loop to print each food\n", + "... # your code here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Exercise 2 - Modify Numbers\n", + "\n", + "# Using the list below, append 4, insert 0 at the start, and remove 2.\n", + "numbers = [1, 2, 3]\n", + "... # your code here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Excercise 3 - Tina's Angles\n", + "\n", + "# Given the list of angles, print Tina's steps to turn and move forward\n", + "angles = [45, 90, 135]\n", + "... # your code here" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Lists", + "uid": "0KEhJUGe" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/10_Turtles/80_Introducting_Lists/20_Color_Lines.py b/lessons/10_Turtles/80_Introducing_Lists/20_Color_Lines.py similarity index 70% rename from lessons/10_Turtles/80_Introducting_Lists/20_Color_Lines.py rename to lessons/10_Turtles/80_Introducing_Lists/20_Color_Lines.py index 790be08f..07af8e75 100644 --- a/lessons/10_Turtles/80_Introducting_Lists/20_Color_Lines.py +++ b/lessons/10_Turtles/80_Introducing_Lists/20_Color_Lines.py @@ -1,16 +1,16 @@ """ -Color Lines +# 20_Color_Lines.py -1) Finish the program to make Tina draw a square with each side being a different color. +Finish the program to make Tina draw a square with each side being a different color. """ import turtle # Tell Python we want to work with the turtle -turtle.setup(600,600,0,0) # Set the size of the window +turtle.setup(600, 600, 0, 0) # Set the size of the window tina = turtle.Turtle() # Create a turtle named tina tina.shape('turtle') # Set the shape of the turtle to a turtle -tina.speed(2) # Make the turtle move as fast, but not too fast. +tina.speed(2) # Move at a moderate speed, not too fast. colors = [ 'red', 'blue', 'black', 'orange'] # define a list of colors diff --git a/lessons/10_Turtles/80_Introducting_Lists/README.md b/lessons/10_Turtles/80_Introducing_Lists/README.md similarity index 56% rename from lessons/10_Turtles/80_Introducting_Lists/README.md rename to lessons/10_Turtles/80_Introducing_Lists/README.md index 4e809e0b..1028c78a 100644 --- a/lessons/10_Turtles/80_Introducting_Lists/README.md +++ b/lessons/10_Turtles/80_Introducing_Lists/README.md @@ -5,5 +5,5 @@ uid: g4kLhJ2U --- -# Introducting Lists +# Introducing Lists diff --git a/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb b/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb deleted file mode 100644 index 7b52c860..00000000 --- a/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb +++ /dev/null @@ -1,159 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# First Look at Lists\n", - "\n", - "One of the most important data structures in Python is the list. A list in Python is\n", - "like a grocery list: \n", - "\n", - "```\n", - "Things To Buy\n", - " - apples\n", - " - oranges\n", - " - bread \n", - " - milk\n", - "```\n", - "\n", - "But in Python we would write it like this: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "things_to_buy = [ 'apples','oranges','bread','milk']\n", - "things_to_buy" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The brackets, `[` and `]` are most often used to mean that something is a list. \n", - "\n", - "There are a lot of neat things we can do with a list.\n", - "\n", - "First, you can get a specific item from a list, using the `[]` with a number inside. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "things_to_buy[1]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Getting values out of a list like this is called \"indexing\".\n", - "\n", - "Like most programming languages, the first item in a list is 0, not 1, so if\n", - "you wanted to get `apples` from the list, you would write `things_to_buy[0]`\n", - "\n", - "Another important thing about lists is you can _iterate_ them, which means 'do\n", - "something repeatedly'. Here is how we would print out all of the items in the\n", - "list: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "things_to_buy = [ 'apples','oranges','bread','milk']\n", - "\n", - "for item in things_to_buy:\n", - " print(item)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Loops and lists could be very useful for our turtle programs. For instance, we could make a square with \n", - "a different color on each side: \n", - "\n", - "```python\n", - "import turtle\n", - "tina = turtle.Turtle()\n", - "tina.shape(\"turtle\")\n", - "\n", - "forward = 50\n", - "left = 90\n", - "colors = [ 'red', 'blue', 'black', 'orange']\n", - "\n", - "for color in colors:\n", - " tina.color(color)\n", - " tina.forward(forward)\n", - " tina.left(left)\n", - "```\n", - "\n", - "Or, we could change the angle that tina turns: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "# Note we are going to print out what Tina does, instead of actually run Tina in a window. \n", - "\n", - "forward = 50\n", - "\n", - "for left in [ 45, 60, 90, 45, -90, 60, 22 , -45, 90]:\n", - " print(f\"tina.forward({forward})\")\n", - " print(f\"tina.left({left})\")\n", - " print(\" \")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There is a lot more that a list can do, but this is all we currently need to know. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - }, - "syllabus": { - "uid": "0KEhJUGe" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/lessons/10_Turtles/90_Graphics_Projects/10_Flaming_Ninja_Star.py b/lessons/10_Turtles/90_Graphics_Projects/10_Flaming_Ninja_Star.py index a657bff5..ce479e3a 100644 --- a/lessons/10_Turtles/90_Graphics_Projects/10_Flaming_Ninja_Star.py +++ b/lessons/10_Turtles/90_Graphics_Projects/10_Flaming_Ninja_Star.py @@ -1,7 +1,11 @@ -"""Flaming Ninja Star +""" +# 10_Flaming_Ninja_Star.py + +This program already works; run it to see what it does. +Then change it to make it draw a different pattern. -This program already works; run it to see what it does. -Then change it to make it draw a different pattern. +uid: ejUIkGvk +name: Flaming Ninja Star """ import random @@ -9,53 +13,40 @@ # Returns a random color! -def getRandomColor(): +def get_random_color(): return "#%06X" % (random.randint(0, 0xFFFFFF)) colors = ["red", "blue", "green", "yellow", "orange"] -def getNextColor(i): +def get_next_color(i): return colors[i % len(colors)] -turtle.setup(600,600,0,0) # Set the size of the window +turtle.setup(600, 600, 0, 0) # Set the size of the window window = turtle.Screen() -baseSize = 200 # the size of the black part of the star -flameSize = 130 # the length of the flaming arms - -t = turtle.Turtle() - -t.shape("turtle") +base_size = 200 # the size of the black part of the star +flame_size = 130 # the length of the flaming arms -t.width(2) - -t.speed(0) +t = turtle.Turtle() +t.shape("turtle") +t.width(2) +t.speed(0) for i in range(25): - t.pencolor(getRandomColor()) - - t.fillcolor(getRandomColor()) - + t.pencolor(get_random_color()) + t.fillcolor(get_random_color()) t.begin_fill() - - t.forward(64) - - t.left(40) - - t.forward(flameSize) - - t.right(170) - - t.forward(flameSize) - - t.right(62) - - t.forward(baseSize) - + t.forward(64) + t.left(40) + t.forward(flame_size) + t.right(170) + t.forward(flame_size) + t.right(62) + t.forward(base_size) t.end_fill() -t.hideturtle() +t.hideturtle() -turtle.done() \ No newline at end of file +turtle.done() diff --git a/lessons/10_Turtles/90_Graphics_Projects/20_Crazy_Spiral.py b/lessons/10_Turtles/90_Graphics_Projects/20_Crazy_Spiral.py index dcf86c36..cbae6cd0 100644 --- a/lessons/10_Turtles/90_Graphics_Projects/20_Crazy_Spiral.py +++ b/lessons/10_Turtles/90_Graphics_Projects/20_Crazy_Spiral.py @@ -1,8 +1,11 @@ """ -Crazy Spiral +# 20_Crazy_Spiral.py Make your own crazy spiral with a pattern like -in 14_FLaming_Ninja_Star.py, but use what you've learned about loops +in 10_Flaming_Ninja_Star.py, but use what you've learned about loops + +uid: zfzMbyH7 +name: Crazy Spiral """ ... # Copy code to make a turtle and set up the window @@ -19,8 +22,8 @@ def make_a_shape(t): # 2) Call make_a_shape() in a loop to make the turtle draw a spiral. # For instance, you can call make_a_shape() 100 times to make a spiral with 100 shapes. -# The second ... in the for loop should be the number of shapes you want to make, -# for example 100, or it could use islice(), cycle(), or a list of numbers. +# The second ... in the for loop should be the number of shapes you want to make, +# for example 100, or a list of numbers. num_shapes = ... diff --git a/lessons/10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py b/lessons/10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py index 280bf6a3..2b3e8753 100644 --- a/lessons/10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py +++ b/lessons/10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py @@ -1,41 +1,44 @@ """ -Pentagon Crazy +# 30_Pentagon_Crazy.py This program already works. Run it, then change it to make it draw a different pattern. + +uid: QG1OFNKY +name: Pentagon Crazy """ import random import turtle -def getRandomColor(): +colors = ("red", "blue", "green", "yellow", "orange") + +def get_random_color(): return "#%06X" % (random.randint(0, 0xFFFFFF)) -def getNextColor(i): +def get_next_color(i): return colors[i % len(colors)] window = turtle.Screen() window.bgcolor("black") window.setup(width=600, height=600, startx=0, starty=0) -colors = ("red", "blue", "green", "yellow", "orange") - -myTurtle = turtle.Turtle() -myTurtle.shape("turtle") -myTurtle.speed(0) -myTurtle.width(1) +my_turtle = turtle.Turtle() +my_turtle.shape("turtle") +my_turtle.speed(0) +my_turtle.width(1) sides = 5 angle = 360 / sides for i in range(360): if i == 100: - myTurtle.width(2) + my_turtle.width(2) if i == 200: - myTurtle.width(3) - myTurtle.pencolor(getNextColor(i)) - myTurtle.forward(i) - myTurtle.right(angle + 1) + my_turtle.width(3) + my_turtle.pencolor(get_next_color(i)) + my_turtle.forward(i) + my_turtle.right(angle + 1) -myTurtle.hideturtle() +my_turtle.hideturtle() -turtle.done() \ No newline at end of file +turtle.done() diff --git a/lessons/10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py b/lessons/10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py index 82c50051..246d1902 100644 --- a/lessons/10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py +++ b/lessons/10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py @@ -1,47 +1,51 @@ -"""Penta Spiral +""" +# 40_Turtle_Spiral.py This program already works. See if you can change it to make it draw a different pattern. + +uid: rkftzcAi +name: Turtle Spiral """ import random import turtle # Returns a random color! -def getRandomColor(): +def get_random_color(): return "#%06X" % (random.randint(0, 0xFFFFFF)) window = turtle.Screen() window.bgcolor("white") # Make a new turtle -myTurtle = turtle.Turtle() +my_turtle = turtle.Turtle() # This code sets our shape to a turtle -myTurtle.shape("turtle") +my_turtle.shape("turtle") # Set your turtle's speed -myTurtle.speed(0) +my_turtle.speed(0) # Set your turtle's color -myTurtle.color("green") +my_turtle.color("green") # Use a loop to repeat the code below 50 times for i in range(50): # Set the turtle color to a random color - myTurtle.pencolor(getRandomColor()) + my_turtle.pencolor(get_random_color()) # Move the turtle (5*i) pixels. 'i' is the loop variable - myTurtle.forward(5 * i) + my_turtle.forward(9 * i) # Turn the turtle (360/7) degrees to the right - myTurtle.right(360 / 7 + i*5) + my_turtle.right(360 / 7 + i*5) # Change the turtle width to 'i' (the loop variable) - myTurtle.width(i) + my_turtle.width(i) - # Check the pattern against the picture in the recipe. If it matches, you are done! + # Check the pattern against the picture in the recipe. If it matches, you are done. turtle.done() -# Now check in your code! \ No newline at end of file +# Now check in your code. diff --git a/lessons/10_Turtles/Module_One_Quiz.ipynb b/lessons/10_Turtles/Module_One_Quiz.ipynb new file mode 100644 index 00000000..9f5bcbd3 --- /dev/null +++ b/lessons/10_Turtles/Module_One_Quiz.ipynb @@ -0,0 +1,41 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e55e7d8e", + "metadata": {}, + "source": [ + "# **Module One Quiz**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "495aaca3", + "metadata": {}, + "outputs": [], + "source": "from jupyterquiz import display_quiz\n\ndisplay_quiz(\"lessons/.jtl/Quiz_Data/Module_One_Quiz.json\")" + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb b/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb deleted file mode 100644 index 921b8bba..00000000 --- a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb +++ /dev/null @@ -1,577 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Reminder about Notebooks\n", - "\n", - "Remember that this document is an executable notebook, and each \"paragraph\" is called a \"cell\". Here are some tips for cells: \n", - "\n", - "* Click in the cell to edit it. When the cell is editable, you will see a blue bar on the side and a blue outline around the cell.\n", - "* Click on the \"▶️\" in the upper left of a cell to run it. If it doesn't do anything, look at the top of the screen to select a kernel. \n", - "* or Hit SHIFT-ENTER to run a cell. You can also run a cell by hitting SHIFT-ENTER\n", - "* Some operations on a cell, like moving it up or down, require it to be in \"Command Mode\". Hit the Esc key to enter command mode. The blue side \n", - " bar will stay, but the blue outline will disappear. \n", - "* When a cell is active, there is a small menu in the upper right with more options. \n", - "\n", - "If you run a cell and it doesn't look like it is doing anything, look at the top of the Visual Studio window for a box that looks like: \n", - "\n", - "
\n", - "\n", - "If you see this, you will want to click on the \"Python Environments\" and then\n", - "select an entry for a Python interpreter. It might look like \" .venv (Python\n", - "3.11)\" or maybe \"★ Python 3.12\" or something like that. Select an interpreter\n", - "and then re-run the code. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Variables and Datatypes\n", - "\n", - "In the lessons with Tina the Turtle you learned about variables. In this section\n", - "we are going to explore variables in more detail. You've seen several \n", - "different ways for declaring and assigning to a variable, such as: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Assign to some variables. \n", - "age = 14\n", - "bank_account = 159.99\n", - "name = \"John\"\n", - "colors = [\"red\", \"blue\", \"green\"]\n", - "\n", - "print(f\"Name: {name}, Age: {age}, Bank Account: {bank_account}, Colors: {colors}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Types\n", - "\n", - "While all of these variable assignments have the same form, the *type* of the variables are all different. We can find out \n", - "what the type of the variable is with the ``type()`` function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "print(\"Age : \", type(age))\n", - "print(\"Bank : \", type(bank_account))\n", - "print(\"Name : \", type(name))\n", - "print(\"Colors: \", type(colors))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "These types look a bit cryptic. Here is an explanation: \n", - "\n", - "* `int` is an integer, a number with no fraction part, like '3' \n", - "* `float` is a \"floating point number\" which does have a fraction part, like '3.5'\n", - "* `str` is a string, like a name. \n", - "* `list` is a list. \n", - "\n", - "There are many other types in Python. Each variable has a type, and the type determines what the variable can do and how you use it. For instance, you can add two variables, but what it means to add variables depends on what the variable is. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# add type integers\n", - "\n", - "a = 10\n", - "b = 20\n", - "\n", - "print(\"a + b = \", a + b)\n", - "\n", - "# add '10' to '20 as strings\n", - "\n", - "a = \"10\"\n", - "b = \"20\"\n", - "\n", - "print(\"a + b = \", a + b) # Wait, what????" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When we add `10` and `20` as integers, we get `30`, as you'd expect. But if you add them as strings it just sticks them together! What if you add an integer to a string?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a = 10\n", - "b = \"20\"\n", - "print(\"a + b = \", a + b)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Oh, that did not work, you can't add an integer to a string. But ... you can convert one to the other and then add them. To convert the variables, we will use `int()` and `str()`. ( Hmmm, the conversion functions have the same name as the type they convert to!)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a = 10\n", - "b = \"20\"\n", - "\n", - "# Convert b to an integer, then add\n", - "\n", - "print( a + int(b))\n", - "\n", - "# Convert a to a string, then add\n", - "\n", - "print( str(a) + b)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Numbers\n", - "\n", - "\n", - "Numbers seem really easy in Python; you just write numbers as you'd expect ... but there\n", - "are two kinds: integers and floating point numbers. Floating point numbers have a decimal\n", - "and integers don't.\n", - "\n", - "And, there are actually many ways to write these numbers. We can write integers in different bases. Here are\n", - "four different ways to write the number `37`:\n", - "\n", - "* Decimal, base 10: 37\n", - "* Octal, base 8: 0o45\n", - "* Hexadecimal, base 16: 0x25\n", - "* Binary, base 2: 0b100101\n", - "\n", - "When we count, we normally use decimal, base 10, and you may remember that when we write \n", - "the number `237` that actually means:\n", - "\n", - "* 2 * 100 + ( where 100 is 10*10 or 10^2 )\n", - "* 3 * 10 +\n", - "* 7\n", - "\n", - "Octal, Hexadecimal and Binary work the same way, but using bases of 8, 16 or 2. So, \n", - "we can decompose these numbers as:\n", - "\n", - "* Decimal 37 = 37 = (3*10 + 7)\n", - "* Octal 37 = 0o45 = (4*8 + 5)\n", - "* Hexadecimal 37 = 0x25 = (2*16 + 5)\n", - "* Binary 37 = 0b100101 = (1*32 + 0*16 + 0*8 + 1)\n", - "\n", - "Different number bases can be a difficult topic, so if you would like some more\n", - "explanation, Khan Academy has a few videos about number systems. \n", - "\n", - "* [Decimal and Binary](https://youtu.be/ku4KOFQ-bB4?si=PC9lZA_ZdXBilgsY)\n", - "* [Hexadecimal](https://youtu.be/4EJay-6Bioo?si=vFOama4qPED81GZA)\n", - "\n", - "As a programmer, you will probably never use octal, but you'll occasionally use\n", - "hexadecimal and binary. \n", - "\n", - "If you want to create these numbers, you can use the conversion functions ``oct()``, ``hex()`` and ``bin()``: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Conversion functions\n", - "\n", - "print(oct(37))\n", - "print(hex(37))\n", - "print(bin(37))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But what if you want to convert a string to a number? In that case you can use ``int()`` and ``float()``.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# String to integer and float\n", - "\n", - "print('int', int('1305'))\n", - "print('int',float('1305.32'))\n", - "\n", - "# The int() function can also take a second argument, which is the base of the number to be converted.\n", - "# so we can convert all of the other bases\n", - "\n", - "print('octal',int('45', 8))\n", - "print('octal',int('0o45', 8))\n", - "\n", - "print('hex',int('25', 16))\n", - "print('hex',int('0x25', 16))\n", - "\n", - "print('binary', int('100101', 2))\n", - "print('binary', int('0b100101', 2))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For really big numbers you can use scientific notation or '_' to separate\n", - "digits, like you would normally use commas. So, here are three ways to write 1\n", - "million:\n", - "\n", - "* 1000000 \n", - "* 1_000_000 \n", - "* 1e6\n", - "\n", - "In the last one, scientific notation, the 'e' basically means the number of zeros, so ``1e6`` means \n", - "`1 followed by 6 zeros` ( more correctly it means 1 * 10^6. ) \n", - "\n", - "\n", - "### Operators\n", - "\n", - "Here are a few of the math operators that you can use on numbers. You've\n", - "probably seen most of them, but there may be one or two surprises. \n", - "\n", - "| operator | description | example | result |\n", - "|----------|-------------|---------|--------|\n", - "| + | addition | 2 + 3 | 5 |\n", - "| - | subtraction | 5 - 2 | 3 |\n", - "| * | multiplication | 4 * 6 | 24 |\n", - "| / | division | 11 / 4 | 2.75 |\n", - "| // | floor division (**aka integer division**) | 11 // 4 | 2 |\n", - "| % | modulo (**remainder**) | 11 % 4 | 3 |\n", - "| ** | exponentiation (**power**) | 2 ** 3 | 8 |\n", - "\n", - "\n", - "The floor division operator, `//` divides the two numbers and then \n", - "cuts off the decimal part, so you always get an integer. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Floor division\n", - "\n", - "# Regular division will result in a float\n", - "\n", - "print(10 / 3)\n", - "\n", - "# But floor division is always an integer\n", - "\n", - "print(10 // 3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "The modulo operator returns the remainder after the division. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Modulo Operator\n", - "\n", - "print(\"9 % 3 == \" , 9 % 3 )\n", - "print(\"10 % 3 == \" , 10 % 3 )\n", - "print(\"11 % 3 == \" , 11 % 3 )\n", - "print(\"12 % 3 == \" , 12 % 3 )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "An interesting thing about `//` and `%` is that they are related to each other;\n", - "the two operators can \"take apart\" a number that you can put back with a simple\n", - "equation. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "a = 11\n", - "b = 3\n", - "\n", - "m = a % b # Modulo\n", - "fd = a // b # Floor division\n", - "\n", - "print(f\"{a} % {b} == m == \", m)\n", - "print(f\"{a} // {b} == fd == \", a // b)\n", - "\n", - "print(\"fd * b + m == a == \", fd * b + m)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Example of the Modulo Operator\n", - "\n", - "for i in range(12):\n", - " print(f\"{i}:i // 3 == {i // 3} %3 == {i % 3} | ({i} // 3 * 3) + ({i} % 3) == {i // 3 * 3 + i % 3}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that the modulo operator works like a clock: the result goes around to\n", - "the maximum number, then goes back to 0. \n", - "\n", - "The most important thing you will do with the modulo operator is check if a\n", - "number is evenly divisible by another number, in which case the modulo value is\n", - "0. So, 6 is evenly divisible by 3 because ``6 % 3 == 0``." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Test yourself\n", - "\n", - "Write a program that computes how old you are by subtracting the current year from your birthday, but you have to start\n", - "with those numbers as strings. Then print your age in decimal, hexadecimal, octal and binary. \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test yourself\n", - "\n", - "current_year = '2024' # Change to the current year. \n", - "birth_year = '1999' # Change to your birth year\n", - "\n", - "age = ... # Calculate the age\n", - "\n", - "print(\"You are \", age, \" years old in decimal\")\n", - "\n", - "print(\"You are \", ..., \" years old in hexadecimal\")\n", - "... # Print the age in octal\n", - "... # Print the age in binary\n", - "... # Print the age modulo 3" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Write a program that starts with two variables:\n", - "\n", - "* `kids` the number of kids \n", - "* `candy_bars` the number of candy bars\n", - "\n", - "You can set those variable to anything you want. You program should calculate how\n", - "to evenly distribute candy bars to the kids. Print out how many candy bars each\n", - "kid gets, and how many are left over. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test yourself\n", - "\n", - "kids = ...\n", - "candy_bars = ...\n", - "\n", - "candy_per_kid = ... # Calculate the number of candy bars each kid gets\n", - "print(\"Each kid gets \", candy_per_kid, \" candy bars\")\n", - "\n", - "candy_left_over = ... # Calculate the number of candy bars left over\n", - "print(\"There are \", candy_left_over, \" candy bars left over\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Things to do with Strings\n", - "\n", - "a = 'Hello' # Define with single quotes\n", - "b = \"World\" # Define with double quotes\n", - "\n", - "print(a + \" \" +b + '!') # Concatenate with + \n", - "\n", - "print(a * 3) # Repeat with *\n", - "\n", - "print(a[0]) # Indexing, get the first letter\n", - "print(a[-1]) # Indexing, get the last letter\n", - "\n", - "num = 1234\n", - "\n", - "print(str(num)+ \" \" + str(num)) # Convert to a string\n", - "\n", - "print(f\"Embed a variable |{num}| in a string\") # Interpolation" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There are also many string methods, such as upper(), lower(), replace(), and split(). You should \n", - "see the [Python Documentation](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str) to see all of them; here are just a few. \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "s = \"Hello World!\"\n", - "\n", - "print(s.lower()) # Lowercase\n", - "print(s.upper()) # Uppercase\n", - "print(s.title()) # Titlecase, capitalize the first letter of each word\n", - "\n", - "print(s.replace('World', 'Python')) # Replace\n", - "print(s.split()) # Split string at spaces\n", - "\n", - "print(s.startswith('Hello')) # Startswith, returns True\n", - "print(s.startswith('Bogon')) # Startswith, returns False\n", - "print(s.endswith('World!')) # Endswith\n", - "\n", - "s = \" Hello World! \"\n", - "print(s.strip()) # Remove leading and trailing spaces" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Test Yourself\n", - "\n", - "Write a program that has three variables, for \"hello\", your name, and a greeting\n", - "like \"how are you?\". Combine the variables with spaces between them into one\n", - "string, then capitalize each word and print out the result." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test Yourself\n", - "\n", - "hello = ... # Define a string for hello\n", - "name = ... # Your name\n", - "greet = ... # \n", - "\n", - "hello3 = ... # make your hello string repeat three times\n", - "s = ... # Concatenate hello3, name and greet\n", - "titled = ... # Make it title case\n", - "\n", - "print(titled) # Print the string" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Check In Your Code\n", - "\n", - "Don't forget to check in your code. You can review how to check in your code,\n", - "and how to restart your Codespace, in our [Code Check In Howto Guide.](https://curriculum.jointheleague.org/howto/checkin_restart.html)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - }, - "syllabus": { - "uid": "HXQZ0Iui" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb b/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb new file mode 100644 index 00000000..21546620 --- /dev/null +++ b/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb @@ -0,0 +1,368 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Operators and Types**\n", + "\n", + "In this lesson you'll further explore how Python represents numbers, text, how to convert between them, and some useful operators and methods.\n", + "\n", + "\n", + "### **Before You Begin**\n", + "
\n", + "\n", + "**REMINDER:** This file is an executable Jupyter-style notebook. It's made of *cells* — pieces of text or code you can run one at a time.\n", + "\n", + "**Quick run & edit helpers**\n", + "\n", + "* Click a cell to edit it, then press ⇧ Shift + ⏎ Enter to run the cell.\n", + "* You can also use the ▶️ button in the cell toolbar to run a cell too.\n", + "* When a cell is active you'll see a small menu in the upper-right with extra options.\n", + "* Press esc to enter Command Mode and Alt + ↑ Up or ↓ Down to move, c to copy, or d to delete cells. **NOTE:** *The cell's blue outline disappears when you are in Command Mode.*\n", + "* To navigate between cells, use the ↑ Up and ↓ Down arrow keys.\n", + "* You can also use the A and B keys to add new cells above or below the current cell.\n", + "\n", + "**What to do if a cell doesn't run**\n", + "\n", + "__1)__ Check the Python interpreter at the top of Visual Studio Code or your Codespace\n", + "
\n", + "__2)__ Click the kernel selector (if it says *Select Kernel*)\n", + "
\n", + "__3)__ Choose the .venv or a system interpreter (e.g., like .venv (Python 3.13.5), Python 3.13.5, or a similar option.).\n", + "\n", + "\n", + "\n", + "After selecting the interpreter, re-run the cell, or ask your instructor if you are still confused and need help connecting the kernel.\n", + "\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "### **Getting Started**\n\nAlthough we have already worked with variables before in the past [lesson](../10_Turtles/50_Variables_and_Functions/10_Variables.ipynb) (click the link for a refresher), we will now dive deeper into how they work and examine the various data types available in Python.\n\nWhen you're ready to begin, run the code block below." + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Create some variables with different types and assign them values\n", + "age = 14\n", + "bank_account = 159.99\n", + "name = \"John\"\n", + "colors = [\"red\", \"blue\", \"green\"]\n", + "\n", + "# Now print the values using \\n to print the next value on a new line\n", + "print(f\"Name: {name}\\nAge: {age}\\nBank Account: {bank_account}\\nColors: {colors}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Data Types**\n", + "\n", + "Although variable assignments look similar, each can hold different **data types**, like integers, strings, lists, and so on. \n", + "\n", + "The `type()` function lets you inspect a variable's type to determine what operations are allowed and how values behave when combined." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "print(\"Age : \", type(age))\n", + "print(\"Bank : \", type(bank_account))\n", + "print(\"Name : \", type(name))\n", + "print(\"Colors : \", type(colors))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Some common data types you'll see:\n\n* Integers: A whole number without a decimal part, like `3`\n* Floats: Real numbers that can have a decimal part, like `3.5`\n* Strings: Text data that is **immutable**, like `\"Alice\"`\n* Lists: are ordered sequences that are **mutable**, like `['red', 'blue']`\n\n> **Note:** There are many other types in Python, like bool (for `True`/`False` values) or dict (for key-value pairs), but the four above are some of the most common. \n\n### **Addition vs Concatenation**\n\nDifferent types support different operations — for example, `+` performs arithmetic on numbers but concatenation on strings.\n\nLet's see what happens when we add two integers and when we add two strings:" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Run Me!\n\n# Integers\nx = 10\ny = 20\n\n# Add x and y together\nprint(\"x + y =\", x + y)\n\n# Strings\nx = \"10\"\ny = \"20\"\n\n# Add x and y together\nprint(\"x + y =\", x + y) # Watch what happens." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When we add `10` and `20` as integers, we get `30`. But when we add them as strings, they are **concatenated** together as `\"1020\"`.\n", + "\n", + "But what happens if you try to add an integer to a string?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Assign x and y to different types\n", + "x = 10\n", + "y = \"20\"\n", + "\n", + "# Now lets see what happens when we try to add them\n", + "print(\"x + y =\", x + y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Oh, that caused a TypeError because you can't add an integer to a string. You *can*, however, convert one to the other type first and then add them.\n", + "\n", + "### **Type Conversion**\n", + "You can use `int()` to turn a string into a number, or `str()` to turn a number into a string. This lets you combine them in the way you want." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Converting between types\n", + "\n", + "# Assign x and y to different types\n", + "x = 10\n", + "y = \"20\"\n", + "\n", + "# Convert y to an integer, then add x and y together\n", + "print(\"Integer:\", x + int(y))\n", + "\n", + "# Convert x to a string, then add x and y together\n", + "print(\"String:\", str(x) + y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "> **Tip:** The conversion function has the same name as the type you want to convert to. For example, `int()` makes an integer, `str()` makes a string.\n", + "\n", + "Here are some common math operators for numbers. Notice how `//` and `%` work together:\n", + "\n", + "| Operator | Description | Example | Result |\n", + "|----------|-------------|---------|--------|\n", + "| $+$ | Addition | $2$ $+$ $3$ | $5$ |\n", + "| $-$ | Subtraction | $5$ $-$ $2$ | $3$ |\n", + "| $*$ | Multiplication | $4$ $*$ $6$ | $24$ |\n", + "| $/$ | Division | $11$ $/$ $4$ | $2.75$ |\n", + "| $//$ | Floor division (*Integer Division*) | $11$ $//$ $4$ | $2$ |\n", + "| $\\%$ | Modulo (*Remainder*) | $11$ $\\%$ $4$ | $3$ |\n", + "| $**$ | Exponentiation (*Power*) | $2$ ** $3$ | $8$ |" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Floor Division**\n", + "\n", + "Floor division uses the `//` operator. It divides and then drops anything after the decimal point. This means it returns an integer if you use integers, or a float if you use floats.\n", + "\n", + "For example, `11 // 4` gives `2` (because 4 goes into 11 two times), and `11.0 // 4.0` gives `2.0`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Regular division will result in a float even with integers\n", + "print(22 / 7)\n", + "\n", + "# Floor division with floats will also result in a float\n", + "print(22.0 // 7.0)\n", + "\n", + "# Floor division with integers will result in an integer\n", + "print(22 // 7)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What if you want to find the **remainder** after dividing? That's where the modulo operator (`%`) comes in!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Modulo**\n", + "\n", + "The **Modulo** operator (`%`) gives you the remainder after dividing one number by another. Think of it like a clock: when you count up and reach the divisor, you start back at zero.\n", + "\n", + "A common use for modulo is checking if a number is even or odd:\n", + "- `4 % 2 == 0` means 4 is even (no remainder)\n", + "- `5 % 2 == 1` means 5 is odd (remainder is 1)\n", + "\n", + "In general, if `n % divisor == 0`, then `n` is evenly divisible by that divisor." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Modulo Operator - Check if numbers are even or odd\n", + "\n", + "print(\"3 % 2 ==\", 3 % 2)\n", + "print(\"4 % 2 ==\", 4 % 2)\n", + "print(\"5 % 2 ==\", 5 % 2)\n", + "print(\"6 % 2 ==\", 6 % 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Connecting Division and Modulo**\n", + "\n", + "The floor division (`//`) and modulo (`%`) operators are closely related! Together, they let you break a number into two parts: the quotient and the remainder.\n", + "\n", + "You can always get back the original number using this formula:\n", + "$dividend = (quotient * divisor) + remainder$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Define numbers\n", + "dividend = 11\n", + "divisor = 4\n", + "\n", + "# Calculate Quotient and Remainder\n", + "quotient = dividend // divisor # Whole number result\n", + "remainder = dividend % divisor # Leftover amount\n", + "\n", + "# Print results\n", + "print(f\"If you divide {dividend} by {divisor}, the quotient is {quotient}, and the remainder is {remainder}.\")\n", + "\n", + "# Verification: (Quotient * Divisor) + Remainder = Dividend\n", + "print(f\"\\nVerification: ({quotient} * {divisor}) + {remainder} = {dividend}\")\n", + "print(f\"\\nResult: {quotient * divisor + remainder}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As shown above, when you divide `11` by `4`, the quotient is `2` and the remainder is `3`.\n", + "\n", + "Let's see how this works for a range of numbers:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Example of the Modulo Operator\n", + "\n", + "for i in range(10):\n", + " print(f\"Floor Division: {i} // 3 = {i // 3} | Modulo: {i} % 3 = {i % 3} | Rebuild = {i // 3} * 3 + {i % 3} = {i // 3 * 3 + i % 3}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Challenge**\n", + "\n", + "Imagine you have some candy bars and you have to share them evenly amongst a group of kids. Adjust the variables and calculations below to determine how many candy bars each kid gets and how many are left over.\n", + "\n", + "**Objectives:** \n", + "- Set `kids` to the number of kids\n", + "- Set `candy_bars` to the number of candy bars you have\n", + "\n", + "**Hints:** \n", + "- Use `//` to find how many each kid gets\n", + "- Use `%` to find the remainder of candy bars" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Test yourself\n", + "\n", + "kids = ... # Set the number of kids\n", + "candy_bars = ... # Set the number of candy bars\n", + "\n", + "candy_per_kid = ... # Calculate the number of candy bars each kid gets\n", + "print(\"Each kid gets\", candy_per_kid, \"candy bars\")\n", + "\n", + "candy_left_over = ... # Calculate the number of candy bars left over\n", + "print(\"There are\", candy_left_over, \"candy bars left over\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "
\nReminder: Check in your code. If you need a refresher, review the Check in Code and Restart Codespaces guide or circle back to the previous Check In Your Code lesson.\n
" + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Operators and Types", + "uid": "HXQZ0Iui" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb deleted file mode 100644 index a9f38e5b..00000000 --- a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb +++ /dev/null @@ -1,332 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Control Flow\n", - "\n", - "Programs don't work very well if they can't make decisions, and we can make decisions with `if` instructions. \n", - "You will see these types of instructions called \"conditionals\" or \"control flow\" or \"branching.\" Here is an example: \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Conditionals \n", - "\n", - "a = 10\n", - "\n", - "if a == 11:\n", - " print(\"a is 11\")\n", - "\n", - "if a == 10:\n", - " print(\"a is 10\")\n", - "\n", - "if a < 20:\n", - " print(\"a is less than 20\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the ``if`` statements above, it is important to understand what comes after the ``if``: an expression that evaluates to a boolean. \n", - "\n", - "* An 'expression' is a bit of code that when run is reduced to a value, like a string or an integer. \n", - "* 'Evaluates' means that the code is being run to determine its value. \n", - "* A 'Boolean' is a type of value that is either True or False. \n", - "\n", - "For instance, the code `5+3` is an expression that evaluates to `8`. Let's evaluate the conditional expressions in the code above: \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Evaluating boolean expressions. \n", - "print(\"a == 11:\", a == 11)\n", - "print(\"a == 10:\", a == 10)\n", - "print(\"a < 20:\", a < 20)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First thing to notice is that we use two equal signs for conditional\n", - "expressions! You use one to assign a variable, and two to check if things are\n", - "equal. So:\n", - "\n", - "* `a = 3` means \"Assign the value of 3 to a\"\n", - "* `a == 3` means \"evaluate to `True` is a equal to 3\"\n", - "\n", - "Don't forget! This is a very common error. \n", - "\n", - "\n", - "## Test Yourself\n", - "\n", - "Add code to determine if the statements in the comments are True or False" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test Yourself\n", - "\n", - "a = 10\n", - "b = 15\n", - "c = '30'\n", - "s = 'hello world'\n", - "\n", - "# Is a equal to 10?\n", - "\n", - "print(a == 10)\n", - "\n", - "# Is b less than 20?\n", - "...\n", - "\n", - "# Is c equal to the *integer* 30?\n", - "\n", - "...\n", - "\n", - "# Is c equal to the *string* '30'?\n", - "\n", - "...\n", - "\n", - "# Is a equal to b minus 5?\n", - "\n", - "..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## And and Or\n", - "\n", - "What if you want to do something only if both `a` was equal to 10 and `b` was equal to 15? In that case, you can use the logical operators `and` & `or`. The `and` operator returns `True` only when both sides of the operator are `True`, whereas `or` returns `True` if either side is `True`. For example:\n", - "\n", - "```python \n", - "True and True == True\n", - "True and False == False\n", - "False and False == False\n", - "\n", - "True or True == True\n", - "True or False == True\n", - "False or False == False\n", - "```\n", - "\n", - "You'll probably never write code like the above (and you probably shouldn't anyway) ... but you might write:\n", - "\n", - "```python \n", - "if a == 5 and s.startswith('hello'):\n", - " print(\"yeah!\")\n", - "\n", - "# or \n", - "\n", - "if time > \"11:30\" and time < \"12:30\":\n", - " print(\"Lunch time!\")\n", - "\n", - "# or\n", - "\n", - "if time < \"07:00\" or time > \"22:00\":\n", - " print(\"I am asleep\")\n", - "```\n", - "\n", - "## Test Yourself" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test Yourself\n", - "\n", - "a = 10\n", - "b = 15\n", - "c = '30'\n", - "s = 'hello world'\n", - "\n", - "\n", - "# set a_is_10 to be true if a is equal to 10\n", - "\n", - "a_is_10 = (a == 10)\n", - "\n", - "# Set last_is_world to be true if the last world in s is 'world'\n", - "\n", - "last_is_world = ...\n", - "\n", - "# set last_is_hello to be true if the last world in s is 'hello'\n", - "\n", - "last_is_hello = ...\n", - "\n", - "# if a is 10 and the last word in s is 'world' print 'success'\n", - "\n", - "if ...:\n", - " print(...)\n", - "\n", - "\n", - "# If the integer value of c is 30 or b is evenly divisible by 5, print 'success'\n", - "\n", - "if ...:\n", - " print(...)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Else and Else if\n", - "\n", - "There is more that `If` statements can do. You can add additional clauses with `elif` and execute specific code if\n", - "none of the `if` or `elif` blocks are ``True``. For instance: \n", - "\n", - "```python \n", - "\n", - "# Example of if-else\n", - "\n", - "if maybe_its_true:\n", - " # do this if maybe_its_true == True\n", - " ...\n", - "else:\n", - " # do this if maybe_its_true == False\n", - " ...\n", - "\n", - "# Example of if-elif-else\n", - "\n", - "maybe_this = ... \n", - "maybe_that = ...\n", - "\n", - "if maybe_this:\n", - " # do this if maybe_this_ == True\n", - " ...\n", - "elif maybe_that:\n", - " # do this if maybe_this == False and maybe_that == True\n", - " ...\n", - "else:\n", - " # do this if maybe_this == False and maybe_that == False\n", - " \n", - "```\n", - "\n", - "## Test Yourself\n", - "\n", - "Write a program that sets a variable `fb` to a number, then write a conditional that\n", - "\n", - "* If the number is evenly divisible by 5, print 'fizz'\n", - "* If the number is evenly divisible by 3, print 'buzz'\n", - "* If it is divisible by neither, print the number\n", - "\n", - "Test your program with different values of `fb`. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test yourself\n", - "\n", - "fb = ...\n", - "\n", - "if ...:\n", - " print(...)\n", - "\n", - "elif ...:\n", - " ...\n", - " \n", - "else:\n", - " ..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is a common way of using `if`/`elif`/`else` blocks, to break a continuous\n", - "value ( like the amount of water in a cup ) into categories. In this case, each\n", - "category is larger than the one before it, so the program will only return the\n", - "smallest category that is just larger than the limit. \n", - "\n", - "You might also use this method for converting an age into an age range. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def convert_ml_to_imperial(ml):\n", - " # Conversion values\n", - " teaspoon_ml = 4.92892\n", - " tablespoon_ml = 3 * teaspoon_ml\n", - " cup_ml = 16 * tablespoon_ml\n", - " pint_ml = 2 * cup_ml\n", - " quart_ml = 2 * pint_ml\n", - " gallon_ml = 4 * quart_ml\n", - "\n", - " if ml <= teaspoon_ml:\n", - " print(f\"The next larger measure is: Teaspoon ({teaspoon_ml} ml)\")\n", - " elif ml <= tablespoon_ml:\n", - " print(f\"The next larger measure is: Tablespoon ({tablespoon_ml:.4f} ml)\")\n", - " elif ml <= cup_ml:\n", - " print(f\"The next larger measure is: Cup ({cup_ml:.3f} ml)\")\n", - " elif ml <= pint_ml:\n", - " print(f\"The next larger measure is: Pint ({pint_ml:.3f} ml)\")\n", - " elif ml <= quart_ml:\n", - " print(f\"The next larger measure is: Quart ({quart_ml:.3f} ml)\")\n", - " elif ml <= gallon_ml:\n", - " print(f\"The next larger measure is: Gallon ({gallon_ml:.2f} ml)\")\n", - " else:\n", - " print(\"The amount exceeds a gallon!\")\n", - "\n", - "# Ask the user for the amount of water in milliliters\n", - "ml = float(input(\"Enter the amount of water in the cup (in ml): \"))\n", - "\n", - "# Call the function to get the Imperial measure\n", - "convert_ml_to_imperial(ml)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - }, - "syllabus": { - "uid": "g6JPkFUs" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/lessons/20_Types_and_Logic/20_String_Operations.ipynb b/lessons/20_Types_and_Logic/20_String_Operations.ipynb new file mode 100644 index 00000000..c9b3ab1a --- /dev/null +++ b/lessons/20_Types_and_Logic/20_String_Operations.ipynb @@ -0,0 +1,305 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3f053ace", + "metadata": {}, + "source": [ + "# **String Operations**\n", + "\n", + "Strings are sequences of characters that are used in Python to represent text or sentences. You can create them by enclosing text in single `'...'` or double `\"...\"` quotes.\n", + "\n", + "In this lesson, we'll explore how to manipulate strings, use common string methods, and format text.\n", + "\n", + "> **Note:** Strings are immutable, meaning once they are created, they cannot be changed, or used in mathematical operations without conversion." + ] + }, + { + "cell_type": "markdown", + "id": "c56aabc5", + "metadata": {}, + "source": [ + "### **Basic String Operations**\n", + "\n", + "Even though strings are text-based, you can still use them to perform operations that look like math! \n", + "\n", + "Let's take a look at the operators below:\n", + "\n", + "| Operation | Symbol | Description |\n", + "| :--- | :---: | :--- |\n", + "| Concatenation | $+$ | Joins two strings together. |\n", + "| Repetition | $*$ | Repeats a string a certain number of times. |\n", + "| Indexing | $[$ $]$ | Accesses a specific character in the string (starting at 0). |" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9fecabaa", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Things to do with Strings\n", + "\n", + "a = 'Hello' # Define with single quotes\n", + "b = \"World\" # Define with double quotes\n", + "\n", + "print(a + \" \" + b + '!') # Concatenate with + \n", + "\n", + "print(a * 3) # Repeat with *\n", + "\n", + "print(a[0]) # Indexing, get the first letter\n", + "print(a[-1]) # Indexing, get the last letter\n", + "\n", + "num = 1234\n", + "\n", + "print(str(num)+ \" \" + str(num)) # Convert to a string\n", + "\n", + "print(f\"Embed a variable |{num}| in a string\") # Interpolation" + ] + }, + { + "cell_type": "markdown", + "id": "0c80360d", + "metadata": {}, + "source": [ + "### **Slicing Strings**\n", + "\n", + "You can extract a part of a string (a *substring*) using slicing by using the syntax `string[start:end]`.\n", + "\n", + "| Parameter | Description | Included? |\n", + "| :--- | :--- | :--- |\n", + "| `start` | The index where the slice begins. | **Yes** |\n", + "| `end` | The index where the slice ends. | **No** (stops before) |\n", + "\n", + "##### **Visualing Indices**\n", + "\n", + "Think of indices as pointing *between* the characters rather than at the characters themselves. \n", + "\n", + "For example, consider the string below (`\"Python\"`):\n", + "\n", + "```text\n", + "┌───┬───┬───┬───┬───┬───┐\n", + "│ P │ y │ t │ h │ o │ n │\n", + "└───┴───┴───┴───┴───┴───┘\n", + "0 1 2 3 4 5 6\n", + "```\n", + "\n", + "If you slice `[0:2]`, you get everything between `0` and `2` (which would result in `\"Py\"`), but if you slice `[2:5]`, you get everything between `2` and `5` (which would result in `\"tho\"`)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73321b8d", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "text = \"Python Programming\"\n", + "\n", + "# Get the first 6 characters\n", + "print(text[0:6])\n", + "\n", + "# Get characters from index 7 to the end\n", + "print(text[7:])\n", + "\n", + "# Get the last 11 characters\n", + "print(text[-11:])" + ] + }, + { + "cell_type": "markdown", + "id": "6e10b6f9", + "metadata": {}, + "source": [ + "### **Length and Membership**\n", + "\n", + "Here are three useful operations for checking the properties of a string:\n", + "\n", + "| Syntax | Description |\n", + "| :--- | :--- |\n", + "| `len()` | Returns the number of characters in a string. |\n", + "| `in` | Checks if a substring exists within another string (returns `True` or `False`). |\n", + "| `not in` | Checks if a substring does not exist within another string (returns `True` or `False`). |\n", + "\n", + "These are commonly used to determine the size of a string or to check for a specific substring's presence." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f31172c3", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "password = \"supersecretpassword123\"\n", + "\n", + "# Check the length\n", + "print(\"Length:\", len(password))\n", + "\n", + "# Check if \"secret\" is in the password\n", + "print(\"Contains 'secret':\", \"secret\" in password)\n", + "\n", + "# Check if \"123\" is not in the password\n", + "print(\"Contains '123':\", \"123\" not in password)" + ] + }, + { + "cell_type": "markdown", + "id": "02c576d2", + "metadata": {}, + "source": [ + "### **String Methods**\n", + "\n", + "Python strings have many built-in methods that let you modify or check the content of the string. Since strings are *immutable* and cannot be changed, these methods return a *new* string rather than modifying the original one.\n", + "\n", + "There are many string methods, but here are few: `.upper()`, `.lower()`, `.replace()`, and `.split()`. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "89cec9c0", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "s = \"Hello World!\"\n", + "\n", + "print(s.lower()) # Lowercase\n", + "print(s.upper()) # Uppercase\n", + "print(s.title()) # Titlecase, capitalize the first letter of each word\n", + "\n", + "print(s.replace('World', 'Python')) # Replace\n", + "print(s.split()) # Split string at spaces\n", + "\n", + "print(s.startswith('Hello')) # Startswith, returns True\n", + "print(s.startswith('Bogon')) # Startswith, returns False\n", + "print(s.endswith('World!')) # Endswith\n", + "\n", + "s = \" Hello World! \"\n", + "print(s.strip()) # Remove leading and trailing spaces" + ] + }, + { + "cell_type": "markdown", + "id": "7a6ba428", + "metadata": {}, + "source": "> **Tip:** Check out the [Python Documentation](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str) for a full list of string methods and their descriptions." + }, + { + "cell_type": "markdown", + "id": "029bb659", + "metadata": {}, + "source": "### **Escape Characters**\n\nSometimes you need to include special characters in a string, like a newline or a quote. You can use the backslash `\\` to \"escape\" them.\n\n| Escape Sequence | Description |\n| :--- | :--- |\n| `\\n` | Newline (moves to the next line) |\n| `\\t` | Tab (adds indentation) |\n| `\\\"` or `\\'` | Quotes (useful if you need to print quotes inside a string) |\n| `\\\\` | Backslash (prints a backslash character) |" + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3517ae40", + "metadata": {}, + "outputs": [], + "source": "# Run Me!\n\nprint(\"Line 1\\nLine 2\") # Newline\nprint(\"Name:\\tAlice\") # Tab\nprint(\"She said, \\\"Hello!\\\"\") # Quotes\nprint(\"This is a backslash: \\\\\") # Backslash" + }, + { + "cell_type": "markdown", + "id": "6c1d110c", + "metadata": {}, + "source": [ + "> **Tip:** You can also use raw strings by prefixing the string with an `r`, if you want Python to ignore escape sequences (e.g., `r\"C:\\new_folder\"`)." + ] + }, + { + "cell_type": "markdown", + "id": "3cb9dbaf", + "metadata": {}, + "source": [ + "### **String Formatting (f-strings)**\n", + "\n", + "f-strings (formatted string literals) are a powerful way to embed variables and expressions directly into strings. Just put an `f` before the opening quote and wrap variables in curly braces `{}`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "155dfb90", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "name = \"Alice\"\n", + "score = 95\n", + "\n", + "# Using an f-string\n", + "message = f\"Player {name} scored {score} points.\"\n", + "print(message)\n", + "\n", + "# You can even do math inside the braces!\n", + "print(f\"Next level requires {score + 5} points.\")" + ] + }, + { + "cell_type": "markdown", + "id": "a3b8b6ad", + "metadata": {}, + "source": [ + "### **Test Yourself**\n", + "\n", + "Create three variables: one for a greeting like `\"hello\"`, one for your name, and one for a follow-up like `\"how are you?\"`. Combine them into a single string (with spaces), then convert the result to title case and print it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4738d8f7", + "metadata": {}, + "outputs": [], + "source": [ + "# Test Yourself\n", + "\n", + "hello = ... # Define a string for hello\n", + "name = ... # Your name\n", + "greet = ... # \n", + "\n", + "hello3 = ... # make your hello string repeat three times\n", + "s = ... # Concatenate hello3, name and greet\n", + "titled = ... # Make it title case\n", + "\n", + "print(titled) # Print the string" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "String Operations", + "uid": "K1jP2RdA" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/lessons/20_Types_and_Logic/30_Control_Flow.ipynb b/lessons/20_Types_and_Logic/30_Control_Flow.ipynb new file mode 100644 index 00000000..23eba322 --- /dev/null +++ b/lessons/20_Types_and_Logic/30_Control_Flow.ipynb @@ -0,0 +1,437 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Control Flow**\n", + "\n", + "Programs become significantly more useful when they can make decisions, and in Python, we can use `if` statements to handle this decision-making process. These types of instructions are often interchangeably referred to as conditionals, control flow, or branching.\n", + "\n", + "Let's look at this example below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Conditionals \n", + "\n", + "a = 10\n", + "\n", + "if a == 11: # Check if `a` is identical to 11\n", + " print(\"a is 11\") \n", + "\n", + "if a == 10: # Check if `a` is identical to 10\n", + " print(\"a is 10\")\n", + "\n", + "if a < 20: # Check if `a` is less than 20\n", + " print(\"a is less than 20\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the statements above, the code following the `if` keyword is an expression that evaluates to a Boolean. This simply means the program checks *if the expression* `a == 11` *evaluates to* `True`. If it does, the kernel will run the indented code below the statement.\n", + "\n", + "Let's evaluate the conditional expressions in the code above:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Evaluating boolean expressions\n", + "\n", + "print(\"a == 11:\", a == 11) # Check if `a` is identical to 11 and print the result\n", + "print(\"a == 10:\", a == 10) # Check if `a` is identical to 10 and print the result\n", + "print(\"a < 20:\", a < 20) # Check if `a` is less than 20 and print the result" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Notice that we use *two* equal signs (`==`) for conditional expressions!\n\n| Operator | Purpose |\n| :------- | :------: | \n| $=$ | Assigns a value to a variable. | \n| $==$ | Checks if two things are equal. | \n\n**For Example**\n* `a = 3` assigns the value of `3` to variable `a`.\n* `a == 3` checks if `a` is equal to `3` and evaluates to either `True` or `False`.\n\n>**Tip:** Confusing `=` and `==` is one of the most common beginner errors, so try to memorize the difference." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### **Challenge**\n", + "\n", + "Write code to determine if the commented-statements are `True` or `False`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Test Yourself\n", + "\n", + "a = 10\n", + "b = 15\n", + "c = '30'\n", + "s = 'hello world'\n", + "\n", + "# Is a equal to 10?\n", + "\n", + "print(a == 10)\n", + "\n", + "# Is b less than 20?\n", + "...\n", + "\n", + "# Is c equal to the *integer* 30?\n", + "\n", + "...\n", + "\n", + "# Is c equal to the *string* '30'?\n", + "\n", + "...\n", + "\n", + "# Is a equal to b minus 5?\n", + "\n", + "..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Working with Conditional Expressions**\n", + "\n", + "Conditional expressions evaluate to `True` or `False` and control program flow within `if` statements. We use various operators to create them, each with a unique purpose.\n", + "\n", + "### **The `And` Operator**\n", + "\n", + "A common option for combining multiple conditional expressions is using the `and` operator:\n", + "\n", + "| Operator | Purpose |\n", + "| :------- | :------: |\n", + "| `and` | Returns `True` only if *both* sides are `True`. |" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "General Representation:\n", + "\n", + "```python\n", + "True and True == True # Both sides are True\n", + "True and False == False # One side is False\n", + "False and False == False # Both sides are False\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "> **Note:** You probably never want to, nor should you ever need to write code like this. This is just to illustrate how the `and` operator works.\n", + "\n", + "Here is a more practical example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "a = 5\n", + "s = \"hello there\"\n", + "time = \"11:31\"\n", + "\n", + "# Using `and` to check if both conditions are true\n", + "if a == 5 and s.startswith('hello'):\n", + " print(\"Hooray!\")\n", + "\n", + "# Using `and` to check if both conditions are true\n", + "if time > \"11:30\" and time < \"12:30\":\n", + " print(\"It's lunch time!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "### **The `Or` Operator**\n\nWe can also use the `or` operator:\n\n| Operator | Purpose |\n| :------- | :------: |\n| `or` | Returns `True` if *either* side is `True`. |\n\nGeneral Representation:\n\n```python\nTrue or True == True # Both sides are True\nTrue or False == True # One side is True\nFalse or False == False # Both sides are False\n```\n\n> **Note:** You also probably never need to illustrate `or` like this in your code.\n\nHere is a more practical example:" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "time = \"23:15\"\n", + "day = \"Saturday\"\n", + "\n", + "# Using `or` to check if either condition is true\n", + "if time < \"07:00\" or time > \"22:00\":\n", + " print(\"Don't disturb me, I am asleep.\")\n", + "\n", + "# Using `or` to check if either condition is true\n", + "if day == \"Saturday\" or day == \"Sunday\":\n", + " print(\"It's the weekend!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "> **Note:** There are so many useful methods in Python like `.startswith()` or `.endswith()` for checking parts of strings, and the `in` operator for checking if a substring exists within a string. Explore the [String Methods](https://docs.python.org/3/library/stdtypes.html#string-methods) section of Python's documentation to learn more!" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Challenge**\n", + "\n", + "Determine if the commented statements are `True` or `False`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Test Yourself\n", + "\n", + "a = 10\n", + "b = 15\n", + "c = '30'\n", + "s = 'hello world'\n", + "\n", + "# set `a_is_10` to be `true` if `a` is equal to `10`\n", + "\n", + "a_is_10 = (a == 10)\n", + "\n", + "# Set `last_is_world` to be `true` if the last world in `s` is \"world\"\n", + "\n", + "last_is_world = ...\n", + "\n", + "# set `last_is_hello` to be `true` if the last world in `s` is \"hello\"\n", + "\n", + "last_is_hello = ...\n", + "\n", + "# if `a` is `10` and the last word in `s` is \"world\" print \"success\"\n", + "\n", + "if ...:\n", + " print(...)\n", + "\n", + "# If the integer value of `c` is `30` or `b` is evenly divisible by `5`, print \"success\"\n", + "\n", + "if ...:\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## **Using `if`, `elif`, and `else` Blocks**\n\nThere are more to `if` statements than meets the eye. You can also turn them into blocks by combining them with additional clauses, like `elif` or `else`. Pairing all three together can allow you to execute specific code if neither your initial `if` or subsequent `elif` condition is met. If this is the case, `else` will catch all other cases.\n\n> **Note:** Most other programming languages refer to this as `else if`, but in Python, we use the shortened form `elif`." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `if`**-**`elif` **Example**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "maybe_its_true = False\n", + "\n", + "# Check if `maybe_its_true` is True\n", + "if maybe_its_true == True:\n", + " # If it is true, run this code block\n", + " print(\"It's true!\")\n", + "\n", + "# Check if `maybe_its_true` is False\n", + "elif maybe_its_true == False:\n", + " # If it is false, run this code block instead\n", + " print(\"It's false!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `if`**-**`elif`**-**`else` **Example**\n", + "\n", + "Now, let's add an `else` clause to catch all other cases:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "maybe_this = 2\n", + "maybe_that = \"b\"\n", + "\n", + "if maybe_this > 7:\n", + " # do this if maybe_this_ == True\n", + " print(\"maybe_this is less than 7\")\n", + "\n", + "elif maybe_that == \"a\":\n", + " # do this if maybe_this == False and maybe_that == True\n", + " print(\"maybe_this is the string 'a'\")\n", + "\n", + "else:\n", + " # do this if maybe_this == False and maybe_that == False\n", + " print(\"Neither condition was met.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": ">**Tip:** `if-elif-else` blocks are useful when paired with `try` and `except` blocks to handle errors in your programs. However, we can worry about that later. This will be explained with more detail in a later lesson." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Challenge**\n", + "\n", + "Write a program that sets a variable `fb` to a number, then uses conditional statements to check the following:\n", + "- If the number is evenly divisible by **5**, print `fizz`.\n", + "- If the number is evenly divisible by **3**, print `buzz`.\n", + "- If the number is divisible by *neither*, print the number itself.\n", + "\n", + "**Instructions**\n", + "* Use `if`, `elif`, and `else` to structure your logic.\n", + "* Test your program by changing the value of `fb` to different numbers to ensure all conditions work as expected." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Test yourself\n", + "\n", + "fb = ...\n", + "\n", + "if ...:\n", + " print(...)\n", + "\n", + "elif ...:\n", + " ...\n", + " \n", + "else:\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Categorizing Values**\n", + "\n", + "A common use case for `if`-`elif`-`else` blocks is to categorize a continuous value, such as the volume of liquid in a container.\n", + "\n", + "In this pattern, conditions are checked in a specific order. The program executes the code block for the **first** condition that evaluates to `True`. This ensures that the value is matched to the most appropriate category (e.g., the smallest container that fits).\n", + "\n", + "**Common Applications:**\n", + "* **Unit Conversion:** Finding the appropriate unit for a measurement (like the example below).\n", + "* **Grading Systems:** Converting numerical scores into letter grades (A, B, C, etc.).\n", + "* **Demographics:** Grouping ages into categories (Child, Teen, Adult, Senior)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "def convert_ml_to_imperial(ml):\n", + " # Conversion values\n", + " teaspoon_ml = 4.92892\n", + " tablespoon_ml = 3 * teaspoon_ml\n", + " cup_ml = 16 * tablespoon_ml\n", + " pint_ml = 2 * cup_ml\n", + " quart_ml = 2 * pint_ml\n", + " gallon_ml = 4 * quart_ml\n", + "\n", + " if ml <= teaspoon_ml:\n", + " print(f\"The next larger measure is: Teaspoon ({teaspoon_ml} ml)\")\n", + " elif ml <= tablespoon_ml:\n", + " print(f\"The next larger measure is: Tablespoon ({tablespoon_ml:.4f} ml)\")\n", + " elif ml <= cup_ml:\n", + " print(f\"The next larger measure is: Cup ({cup_ml:.3f} ml)\")\n", + " elif ml <= pint_ml:\n", + " print(f\"The next larger measure is: Pint ({pint_ml:.3f} ml)\")\n", + " elif ml <= quart_ml:\n", + " print(f\"The next larger measure is: Quart ({quart_ml:.3f} ml)\")\n", + " elif ml <= gallon_ml:\n", + " print(f\"The next larger measure is: Gallon ({gallon_ml:.2f} ml)\")\n", + " else:\n", + " print(\"The amount exceeds a gallon!\")\n", + "\n", + "# Ask the user for the amount of water in milliliters\n", + "ml = float(input(\"Enter the amount of water in the cup (in ml): \"))\n", + "\n", + "# Call the function to get the Imperial measure\n", + "convert_ml_to_imperial(ml)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": ">**Tip:** The `input` function can be useful for getting user input to categorize values dynamically. You will use this a lot if you ever build interactive programs in the future." + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Control Flow", + "uid": "g6JPkFUs" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb b/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb new file mode 100644 index 00000000..91aa1f75 --- /dev/null +++ b/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb @@ -0,0 +1,141 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5ca9192c", + "metadata": {}, + "source": "## **Working With Numbers**\n\nPython has two main numeric types, integers (`int`) for whole numbers (e.g., $1$) and floats (`float`) for values with fractional parts (e.g., $3.14$).\n\nIntegers can be written in four different bases:\n\n* **Binary (Base 2):** $0b11111111$\n* **Octal (Base 8):** $0o377$\n* **Decimal (Base 10):** $255$\n* **Hexadecimal (Base 16):** $0xff$\n\n> **Note:** The prefixes `0b`, `0o`, and `0x` are just used to indicate binary, octal, and hexadecimal numbers, respectively.\n\nThis diagram below shows us how these bases relate to each other:\n\n| System | Base | Digits |\n| :--- | :---: | :--- |\n| **Binary** | $2$ | $0, 1$ |\n| **Octal** | $8$ | $0, 1, 2, 3, 4, 5, 6, 7$ |\n| **Decimal** | $10$ | $0, 1, 2, 3, 4, 5, 6, 7, 8, 9$ |\n| **Hexadecimal** | $16$ | $0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f$ |\n\nFor example, a decimal number $255$ can be broken down by its place values into $(2 \\times 100) + (5 \\times 10) + (5 \\times 1)$. If you look at this carefully, you'll see that $100$, $10$, and $1$ are powers of $10$ ($10^2$, $10^1$, and $10^0$). Each of the different number bases pretty much work the same, but their place values are different. \n\nIf you'd like more explanation on number systems, these *Khan Academy* videos are helpful:\n\n* [Decimal and Binary](https://youtu.be/ku4KOFQ-bB4?si=PC9lZA_ZdXBilgsY)\n* [Hexadecimal](https://youtu.be/4EJay-6Bioo?si=vFOama4qPED81GZA)\n\nOr expand the sections below for more details on each number system:\n\n
\n Binary\n\nBinary is a base-2 number system that uses a combination of bits and bytes (often with a `0b` prefix) to represent values. \n\nLet's look at the binary number `10110101` in the table below:\n\n| Place Value | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |\n|----------------------|-------|-------|-------|-------|-------|-------|-------|-------\n| Binary Digit | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 1 |\n\nTo convert a binary number to decimal, multiply each digit by its place value and add the results.\n\n| Sequential Step | Mathematical Calculation |\n|------|-------------|\n| Multiply by place value | $(1 \\times 128), (0 \\times 64), (1 \\times 32), (1 \\times 16), (0 \\times 8), (1 \\times 4), (0 \\times 2), (1 \\times 1)$ |\n| Add the results | $128 + 0 + 32 + 16 + 0 + 4 + 0 + 1 = 181$ |\n\n
\n\n> **Tip:** For binary, you can also just add the place values where there is a 1, like this: $128 + 32 + 16 + 4 + 1 = 181$.\n\n
\n\n
\n Octal\n\nOctal is a base-8 number system that uses octal digits to represent values.\n\nLet's look at the octal number `0o377` in the table below:\n\n| Place Value | 64 | 8 | 1 |\n|----------------------|-------|-------|-------\n| Octal Digit | 3 | 7 | 7 |\n\nTo convert an octal number to decimal, multiply each digit by its place value and add the results.\n\n| Sequential Step | Mathematical Calculation |\n|------|-------------|\n| Multiply by place value | $(3 \\times 64), (7 \\times 8), (7 \\times 1)$ |\n| Add the results | $192 + 56 + 7 = 255$ |\n\n
\n\n> **Note:** Octal is often used in computing as a shorthand for binary, since each octal digit represents exactly three binary digits.\n\n
\n\n
\n Hexadecimal\n\nHexadecimal is a base-16 number system that uses hex digits to represent values.\n\nLet's look at the hexadecimal number `0x2f3` in the table below:\n\n| Place Value | 256 | 16 | 1 |\n|----------------------|-------|-------|-------|\n| Hex Digit | 2 | F (15) | 3 |\n\nTo convert a hexadecimal number to decimal, multiply each digit by its place value and add the results.\n\n| Sequential Step | Mathematical Calculation |\n|------|-------------|\n| Multiply by place value | $(2 \\times 256), (15 \\times 16), (3 \\times 1)$ |\n| Add the results | $512 + 240 + 3 = 755$ |\n\n
\n\n> **Note:** Hexadecimal is widely used in computing because one hex digit represents exactly four binary digits (bits), making it easy to convert between binary and hex.\n\n
" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Using Conversion Functions**\n", + "\n", + "You can convert numbers to these representations with `oct()`, `hex()`, and `bin()` — examples below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Conversion functions\n", + "\n", + "number = 11 # You can change this number to test with other values\n", + "\n", + "# Convert and print the number in different bases using the conversion functions\n", + "print(f\"In base 2, {number} is \" + bin(number))\n", + "print(f\"In base 8, {number} is \" + oct(number))\n", + "print(f\"In base 10, {number} is \" + str(number))\n", + "print(f\"In base 16, {number} is \" + hex(number))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But what if you want to convert a string to a number? In that case you can use the `int()` and `float()` functions:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# String to integer and float\n", + "\n", + "# Integer (Base 10)\n", + "print('int', int('1305'))\n", + "\n", + "# Float (Base 10)\n", + "print('float', float('1305.32'))\n", + "\n", + "# The int() function can also take a second argument (e.g., the base of the number to be converted).\n", + "\n", + "# Binary (Base 2)\n", + "print('binary', int('100101', 2)) # Without prefix\n", + "print('binary', int('0b100101', 2)) # With prefix\n", + "\n", + "# Octal (Base 8)\n", + "print('octal', int('45', 8)) # Without prefix\n", + "print('octal', int('0o45', 8)) # With prefix\n", + "\n", + "# Hexadecimal (Base 16)\n", + "print('hex', int('25', 16)) # Without prefix\n", + "print('hex', int('0x25', 16)) # With prefix" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For large numbers you can use underscores like commas to group digits together or scientific notation. \n", + "\n", + "Here are three ways to write one million:\n", + "* **Decimal:** $1000000$\n", + "* **Underscores:** $1\\_000\\_000$\n", + "* **Scientific Notation:** $1e6$\n", + "\n", + "In $1e6$, the $e$ stands for *times 10 to the power of*, so $1e6$ stands for $1 * 10^6 = 1,000,000$. Or in simple terms, it just means one followed by six zeros!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Test Yourself**\n", + "\n", + "Create a program that starts with the current year and your birth year as *strings*. Convert them to numbers, compute your age, and then print that age in decimal, hexadecimal, octal, and binary.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Test yourself\n", + "\n", + "current_year = '...' # Change to the current year \n", + "birth_year = '...' # Change to your birth year\n", + "\n", + "age = ... # Calculate the age\n", + "\n", + "print(\"You are\", age, \"years old in decimal.\") # Print the age in decimal\n", + "\n", + "print(\"You are\", ..., \"years old in hexadecimal.\") # Print the age in hexadecimal\n", + "... # Print the age in octal\n", + "... # Print the age in binary\n", + "... # Print the age modulo 3" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Working With Numbers", + "uid": "b1Q9Y1j1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/lessons/20_Types_and_Logic/30_My_Ages.py b/lessons/20_Types_and_Logic/50_My_Ages.py similarity index 98% rename from lessons/20_Types_and_Logic/30_My_Ages.py rename to lessons/20_Types_and_Logic/50_My_Ages.py index 59128593..996ed1ec 100644 --- a/lessons/20_Types_and_Logic/30_My_Ages.py +++ b/lessons/20_Types_and_Logic/50_My_Ages.py @@ -1,4 +1,6 @@ """ +# 50_My_Ages.py + Am I Big Yet? Ask the user's age then use an if-elif-else statement to diff --git a/lessons/20_Types_and_Logic/60_Code_Challenges.ipynb b/lessons/20_Types_and_Logic/60_Code_Challenges.ipynb deleted file mode 100644 index 54f6dec9..00000000 --- a/lessons/20_Types_and_Logic/60_Code_Challenges.ipynb +++ /dev/null @@ -1,134 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": { - "editable": true, - "slideshow": { - "slide_type": "" - }, - "tags": [] - }, - "source": [ - "# Code Challenges\n", - "\n", - "Code challenge apps are a great way to continue to improve your skills. But\n", - "first ... go check in your code. You can review how to check in your code, and\n", - "how to restart your Codespace, in our \n", - "[Code Check In How To Guide.](https://curriculum.jointheleague.org/howto/checkin_restart.html)\n", - "\n", - "\n", - "## Hacker Rank\n", - "\n", - "[Hacker Rank](https://www.hackerrank.com/) is a website that provides a series\n", - "of programming challenges. The challenges are organized by skills and difficulty\n", - "level. The website provides a platform for users to practice their programming\n", - "skills and to compete with others. Hacker Rank also provides a series of\n", - "tutorials and practice problems to help users improve their programming skills.\n", - "\n", - "In the \"Hacker Rank\" notebooks like this, we will give you challenges from\n", - "Hacker rank. Each section will have one challenge, with a link to the Hacker\n", - "Rank challenge. To use that link, you will have to create a Hacker Rank Account.\n", - "If you do, you will be able to keep track of your progress with the challenges. \n", - "\n", - "*Important* When you create your Hacker Rank account *use your Github account*.\n", - "When you go to the sign up page, look for a button like this: \n", - "\n", - "\n", - "\n", - "Using Github will make it easier to sign up and login later. \n", - "\n", - "## Code Wars\n", - "\n", - "Another practice site we really like is [Code Wars](https://www.codewars.com/).\n", - "Where Hacker Rank is fairly professional looking, Code Wars is more like Rocket\n", - "League for coding. \n", - "\n", - "When you sign up for Code Wars, also be sure to sign up with Github. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Challenges\n", - "\n", - "## If Else\n", - "\n", - "[Solve this Challenge on Hacker Rank](https://www.hackerrank.com/challenges/py-if-else/problem?isFullScreen=true)\n", - "\n", - "\n", - "Given an integer $n$, perform the following conditional actions:\n", - "\n", - "- if $n$ is odd, print `weird`\n", - "- if $n$ is even and in the inclusive range of 2 to 5, print `not weird`\n", - "- if $n$ is even and in the inclusive range of 6 to 20, print `weird`\n", - "- if $n$ is even and greater than 20, print `not weird`\n", - "\n", - "Or solve the problem here:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Your solution here\n", - "\n", - "for n in [3, 5, 7, 11, 13, 17, 19, 23]:\n", - " ..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Hello Johnny\n", - "\n", - "[Solve this Challenge on Code Wars](https://www.codewars.com/kata/55225023e1be1ec8bc000390/train/python)\n", - "\n", - "Jenny has written a function that returns a greeting for a user. However, she's\n", - "in love with Johnny, and would like to greet him slightly different. She added a\n", - "special case to her function, but she made a mistake.\n", - "\n", - "Can you help her?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - }, - "syllabus": { - "uid": "jJI6aomB" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/lessons/20_Types_and_Logic/40_Simple_Adder.py b/lessons/20_Types_and_Logic/60_Simple_Adder.py similarity index 89% rename from lessons/20_Types_and_Logic/40_Simple_Adder.py rename to lessons/20_Types_and_Logic/60_Simple_Adder.py index 62cc665c..c35d3107 100644 --- a/lessons/20_Types_and_Logic/40_Simple_Adder.py +++ b/lessons/20_Types_and_Logic/60_Simple_Adder.py @@ -4,7 +4,7 @@ In this program we will just give you the comments for what you need to do. Look at the comments and the code snippets in the previous lessons, like -03_My_Ages.py, to figure out how to complete the program. +50_My_Ages.py, to figure out how to complete the program. """ # Import the required modules diff --git a/lessons/20_Types_and_Logic/50_Infuriating_Calculator.py b/lessons/20_Types_and_Logic/70_Infuriating_Calculator.py similarity index 76% rename from lessons/20_Types_and_Logic/50_Infuriating_Calculator.py rename to lessons/20_Types_and_Logic/70_Infuriating_Calculator.py index 0a25a999..cf7fbe5b 100644 --- a/lessons/20_Types_and_Logic/50_Infuriating_Calculator.py +++ b/lessons/20_Types_and_Logic/70_Infuriating_Calculator.py @@ -4,16 +4,17 @@ Let's write a calculator that's really hard to use, not because we want it to be hard, but just because we haven't learned how to make it easy yet. -Ask the user for three things: +Ask the user for three things: 1. A number 2. A second number 3. A math operation (add, subtract, multiply, divide) -4. Use if-elif-else statements to provide the desired math operation on the - numbers and display the result. -If the user enters an unknown operation, display an error message. ( use -messagebox.showerror() +Then use if-elif-else statements to perform the requested math operation on the +numbers and display the result. + +If the user enters an unknown operation, display an error message +(use `messagebox.showerror()`). For the number, you can ask for a float or an integer with simpledialog.askfloat() or simpledialog.askinteger(), and for the math operation @@ -26,7 +27,7 @@ # Hide the window, hint: use the withdraw method -# Ask the user for the first number +# Ask the user for the first number # Ask the user for the second number @@ -34,6 +35,6 @@ # Use if-elif-else statements to provide the desired math operation on the numbers and display the result. -# If the user enters an unknown operation, display an error message. ( use messagebox.showerror() +# If the user enters an unknown operation, display an error message (use `messagebox.showerror()`). -# Keep the window open \ No newline at end of file +# Keep the window open diff --git a/lessons/20_Types_and_Logic/80_Code_Challenges.ipynb b/lessons/20_Types_and_Logic/80_Code_Challenges.ipynb new file mode 100644 index 00000000..4e8d71a4 --- /dev/null +++ b/lessons/20_Types_and_Logic/80_Code_Challenges.ipynb @@ -0,0 +1,98 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "# **Code Challenges**\n", + "\n", + "Code challenges are a great way to continue improving your programming skills. Before you begin, make sure to check in your code. You can review the process for checking in your code and restarting your Codespace in our [Code Check In How To Guide.](https://curriculum.jointheleague.org/howto/checkin_restart.html)\n", + "\n", + "\n", + "## **Hacker Rank**\n", + "\n", + "[Hacker Rank](https://www.hackerrank.com/) is a platform for practicing programming through challenges organized by skill and difficulty level. You can solve problems, learn from tutorials, and track your progress as you compete with other programmers.\n", + "\n", + "In these notebooks, we provide you with Hacker Rank challenges. Each section includes one challenge with a link to solve it on the platform. You'll need to create a Hacker Rank account to track your progress.\n", + "\n", + "Look for a button like this on the sign-up page:\n", + "\n", + "\n", + "
\n", + "
\n", + "\n", + ">**Tip:** When you create your Hacker Rank account, use your GitHub account. Using GitHub makes signing up and logging in easier.\n", + "\n", + "## **Code Wars**\n", + "\n", + "[Code Wars](https://www.codewars.com/) is another excellent platform for practicing coding through challenges. While Hacker Rank maintains a professional interface and structure, Code Wars has a more competitive and gamified experience—think of it like Rocket League for coding. You solve problems to advance through ranks and earn achievement badges as you progress.\n", + "\n", + "> **Tip:** When you sign up for Code Wars, use your GitHub account as well to streamline your login and easily share your solutions." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Challenges**\n", + "\n", + "## If Else\n", + "\n", + "[Solve this challenge on Hacker Rank](https://www.hackerrank.com/challenges/py-if-else/problem?isFullScreen=true)\n", + "\n", + "Given an integer $n$, perform the following conditional actions:\n", + "\n", + "- If $n$ is odd, print `weird`\n", + "- If $n$ is even and in the range 2 to 5 (inclusive), print `not weird`\n", + "- If $n$ is even and in the range 6 to 20 (inclusive), print `weird`\n", + "- If $n$ is even and greater than 20, print `not weird`\n", + "\n", + "Solve the problem below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Your solution here\n\nfor n in [2, 3, 4, 5, 6, 11, 18, 20, 22, 24]:\n ..." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "# **Hello Johnny**\n\n[Solve this challenge on Code Wars](https://www.codewars.com/kata/55225023e1be1ec8bc000390/train/python)\n\nJenny has written a function that returns a greeting for a user. However, she's in love with Johnny, and would like to greet him slightly different. She added a special case to her function, but she made a mistake.\n\nCan you help her?\n\n> **Note:** Solve this one on Code Wars directly — the site provides the starter code and will check your answer for you." + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Code Challenges", + "uid": "jJI6aomB" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb b/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb new file mode 100644 index 00000000..3152cfd2 --- /dev/null +++ b/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb @@ -0,0 +1,41 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "04a3f84d", + "metadata": {}, + "source": [ + "# **Module Two Quiz**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bcd265b8", + "metadata": {}, + "outputs": [], + "source": "from jupyterquiz import display_quiz\n\ndisplay_quiz(\"../.jtl/Quiz_Data/Module_Two_Quiz.json\")" + } + ], + "metadata": { + "kernelspec": { + "display_name": "python-apprentice-jay (3.13.3)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/lessons/20_Types_and_Logic/lib/badgers.py b/lessons/20_Types_and_Logic/lib/badgers.py index e60cf3ae..9f400298 100644 --- a/lessons/20_Types_and_Logic/lib/badgers.py +++ b/lessons/20_Types_and_Logic/lib/badgers.py @@ -2,15 +2,10 @@ from tkinter import PhotoImage from pathlib import Path -class FizzBuzzer(): +class FizzBuzzer: """Run a user defined function on a sequence of numbers and display the result in a Tkinter window""" - def __init__(self): - self.current_number = 0 - - - # Function to update the display based on the current number def update_display(self): @@ -36,8 +31,8 @@ def isnumber(x): self.display_label.config(text=result, image='', fg='red') else: self.display_label.config(text=result, image='', fg='black') - - self.current_number+= 1 + + self.current_number += 1 def __init__(self, cb=None): @@ -72,4 +67,4 @@ def __init__(self, cb=None): self.current_number = 1 def run(self): - self.root.mainloop() \ No newline at end of file + self.root.mainloop() diff --git a/lessons/30_Loops/010_Iteration.ipynb b/lessons/30_Loops/010_Iteration.ipynb deleted file mode 100644 index b14d9644..00000000 --- a/lessons/30_Loops/010_Iteration.ipynb +++ /dev/null @@ -1,112 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Loops and Iteration\n", - "\n", - "We've already used loops a lot. They look like this: \n", - "\n", - "```python \n", - "for i in range(10):\n", - " print(i)\n", - "```\n", - "\n", - "Like ``if`` statements, loops have parts that we can understand individually.\n", - "All of the loops start with ``for``, then have a variable name, then have\n", - "``in``. The last part is called an \"iterable\". \n", - "\n", - "So, the form of the loop line is always:\n", - "\n", - "```\n", - " for in :\n", - "```\n", - "\n", - "We call it an \"iterable\" because ... we can iterate it, and \"iterate\" just\n", - "means \"take one thing after another\".\n", - "\n", - "Here are some things that are iterable:\n", - "\n", - "* a list: ['a','b','c','d']\n", - "* a tuple: (1,2,3,4,5)\n", - "* a string: \"hello world\"\n", - "* range: range(10)\n", - "* many others!\n", - "\n", - "You can put any of those things into a loop and get one part of them at a time. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "# Iterate over range()\n", - "\n", - "for i in range(5):\n", - " print(i)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Iterate a list\n", - "for i in ['a','b','c','d']:\n", - " print(i)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Iterate over a string\n", - "for c in \"Hello World!\":\n", - " print(c)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the next lessons we'll learn about each of these iterable types. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - }, - "syllabus": { - "uid": "ITpqcvxv" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/lessons/30_Loops/020_Loops_with_Range.ipynb b/lessons/30_Loops/020_Loops_with_Range.ipynb deleted file mode 100644 index b72e68ed..00000000 --- a/lessons/30_Loops/020_Loops_with_Range.ipynb +++ /dev/null @@ -1,173 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# The Range Object\n", - "\n", - "We've used `range()` a lot so far, so let's finally learn all of the details about it. \n", - "\n", - "As we've seen, `range()` is a way of creating a series of numbers:\n", - "\n", - "```python\n", - "for i in range(10):\n", - " print(i)\n", - "```\n", - "\n", - "But, `range()` doesn't actually *have* the number inside of it. It just creates them. For instance, \n", - "see what happens if we print it, or ask for its type:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "a = range(10)\n", - "\n", - "print(type(a))\n", - "print(a)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Range doesn't store numbers because if you had a really big range, like `range(1_000_000_000_000)` you'd run out of memory. \n", - "\n", - "However, there is a way to get the numbers inside of it: convert it to a list: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(list(range(10)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We will explain more about `list()` later, for now we will just use it to find out what numbers `range()` is producing. \n", - "\n", - "The `range()` actually can have one, two or three arguments. Here are the options: \n", - "\n", - "* `range(end)`: produces numbers from 0 to `end`\n", - "* `range(start, end)`: produces numbers from `start` to `end`\n", - "* `range(start, end, skip)`: produces numbers from `start` to `end`, skipping by `skip`\n", - "\n", - "So, here is how we can create odd numbers from 101 to 120:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "list(range(101, 120, 2))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "What `range(101, 120, 2)` means is \"Start at 101 and skip numbers by 2 until 120`\n", - "\n", - "Notice that ``range()`` does not include the number you put at the end; it stops one before it. There is a good reason for this, but we'll have to explain later. \n", - "\n", - "## Test Yourself\n", - "\n", - "Print out all of the odd years between the year you were born and today." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test yourself\n", - "\n", - "..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Badgers!\n", - "\n", - "[These Badgers](https://youtu.be/pzagBTcYsYQ?si=xr4QQ7ZkZBGow2j1) really like a program called Fizz Buzz, but they want their own version\n", - "\n", - "The normal rules for FizzBuzz are: \n", - "\n", - " Write a function to list the integers from 1 to 30, but for every multiple of\n", - " 3, write “Fizz”, and for every multiple of 5, write “Buzz”. For numbers which\n", - " are multiples of both 3 and 5, it should write “FizzBuzz”; for every other\n", - " number, it should print the number unchanged.\n", - "\n", - "Instead, here are the Badger rules: \n", - "\n", - "For the numbers from 1 to 30:\n", - "\n", - "* If the number is evenly divisible by 5, print '🦡 badger'\n", - "* If the number is evenly divisible by 3, print '🍄 mushroom'\n", - "* If the number is evenly divisible by both 3 and 5, print '🐍 snake!'\n", - "* If it is divisible by neither, print the number.\n", - "\n", - "Bonus: write the program without using the ``or`` operator." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Badgers!\n", - "\n", - "for i in range(1, 31):\n", - " ..." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - }, - "syllabus": { - "uid": "WcGpR3Xg" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/lessons/30_Loops/030_Lists.ipynb b/lessons/30_Loops/030_Lists.ipynb deleted file mode 100644 index 62244960..00000000 --- a/lessons/30_Loops/030_Lists.ipynb +++ /dev/null @@ -1,428 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "5a4acb70", - "metadata": {}, - "source": [ - "# First Look at Lists\n", - "\n", - "A list in Python is a lot like the lists that you already know about, like a grocery list:\n", - "\n", - "```\n", - "Things To Buy\n", - " - apples\n", - " - oranges\n", - " - bread \n", - " - milk\n", - "```\n", - "\n", - "But in Python we would write it like this: \n", - "\n", - "```python \n", - "things_to_buy = [ 'apples','oranges','bread','milk']\n", - "```\n", - "\n", - "The brackets, `[` and `]` are most often used to mean that something is a list. \n", - "\n", - "There are a lot of neat things we can do with a list.\n", - "\n", - "First, you can get a specific item from a list, using the `[]` with a number inside. \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9280847b", - "metadata": { - "lines_to_next_cell": 0 - }, - "outputs": [], - "source": [ - "# Indexing a list\n", - "\n", - "things_to_buy = [ 'apples','oranges','bread','milk']\n", - "\n", - "a = things_to_buy[1]\n", - "print(a)\n", - "\n", - "# Try changing the number in the [] and see what happens. " - ] - }, - { - "cell_type": "markdown", - "id": "c07ad6f5", - "metadata": {}, - "source": [ - "\n", - "Getting values out of a list like this is called \"indexing\".\n", - "\n", - "\n", - "Like most programming languages, the first item in a list is 0, not 1, so if\n", - "you wanted to get `apples` from the list, you would write `things_to_get[0]`\n", - "\n", - "Another important thing about lists is you can _iterate_ them, which means 'do\n", - "something repeatedly'. Here is how we would print out all of the items in the\n", - "list: \n" - ] - }, - { - "cell_type": "markdown", - "id": "2b5a515a", - "metadata": { - "lines_to_next_cell": 2 - }, - "source": [ - "## Using Lists\n", - "\n", - "Loops and lists could be very useful for our turtle programs. For instance, we could make a square with \n", - "a different color on each side: \n", - "\n", - "```python\n", - "import turtle\n", - "tina = turtle.Turtle()\n", - "tina.shape(\"turtle\")\n", - "\n", - "forward = 50\n", - "left = 90\n", - "colors = [ 'red', 'blue', 'black', 'orange']\n", - "\n", - "for color in colors:\n", - " tina.color(color)\n", - " tina.forward(forward)\n", - " tina.left(left)\n", - "```\n", - "\n", - "Or, we could change the angle that tina turns: \n", - "\n", - "```python\n", - "import turtle\n", - "tina = turtle.Turtle()\n", - "tina.shape(\"turtle\")\n", - "\n", - "forward = 50\n", - "\n", - "for left in [ 45, 60, 90, 45, -90, 60, 22 , -45, 90]:\n", - " tina.forward(forward)\n", - " tina.left(left)\n", - "```\n", - "\n", - "Here is a way that we could change two variables at once, using array indexes:\n", - "\n", - "```python\n", - "import turtle\n", - "tina = turtle.Turtle()\n", - "tina.shape(\"turtle\")\n", - "\n", - "forward = 50\n", - "lefts = [ 45, -60, 90, 45, -90, 60, 22 , -45 ]\n", - "colors = [ 'red', 'blue', 'black', 'orange', 'red', 'blue', 'black', 'orange']\n", - "\n", - "for i in range(8):\n", - " left = lefts[i]\n", - " color = colors[i]\n", - "\n", - " tina.color(color)\n", - " tina.forward(forward)\n", - " tina.left(left)\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "183089f2", - "metadata": { - "lines_to_next_cell": 2, - "title": "[python]" - }, - "outputs": [], - "source": [ - "things_to_buy = [ 'apples','oranges','bread','milk']\n", - " \n", - "for item in things_to_buy:\n", - " print(item)" - ] - }, - { - "cell_type": "markdown", - "id": "bc85d21a", - "metadata": { - "lines_to_next_cell": 0 - }, - "source": [ - "# Iterating over Iterables\n", - "\n", - "Here is the first simple list that you learned about earlier. \n", - "\n", - "```python \n", - "things_to_buy = [ 'apples','oranges','bread','milk']\n", - "```\n", - "\n", - "This variable, `things_to_buy` is interesting, because it is a list of\n", - "strings, but the strings are also a list, a list of letters. And in Python, \n", - "lists and strings are a lot a like. So, let's learn more about them both. \n", - "\n", - "Both lists and strings are \"iterables\". Iteration means taking things one at a\n", - "time, and \"iterating\" a list means that we will get the first thing in the\n", - "list, then the second, and on, until there is nothing left in the list. We have\n", - "seen iteration before, with loops. Here are two loops, \n", - "one iterating over a list, and another iterating over a string. \n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "15a2f38b", - "metadata": { - "title": "[python]" - }, - "outputs": [], - "source": [ - "things_to_buy = [ 'apples','oranges','bread','milk']\n", - " \n", - "print(\"Things to buy:\")\n", - "\n", - "for things in things_to_buy:\n", - " print(things)\n", - " \n", - "print()\n", - "for i in 'Hello World':\n", - " print(i)" - ] - }, - { - "cell_type": "markdown", - "id": "65625cd0", - "metadata": { - "lines_to_next_cell": 0 - }, - "source": [ - "## Iterables\n", - "\n", - "The for loop, which looks like `for in ` works by taking\n", - "each one of the things in the iterable, assigning it to the variable, then\n", - "running the code in the body of the loop. \n", - "\n", - "But, then you wonder, what does the code we first used for loops do? The one\n", - "with `range()` in it?\n", - "\n", - "Well, `range()` is an iterable! But it isn't a string or a list. It doesn't\n", - "have anything in it. It just gives you the next number. And as we learned\n", - "earlier, we can turn it into a list that does have things in it. Here is how: \n", - "\n", - "```python\n", - "# Turn a range() into a list:\n", - "\n", - "l = list(range(5, 10))\n", - "```\n", - "\n", - "When you put something inside `list()`, list will try to iterate the thing, and\n", - "then take each item and put it into a list. A string, like 'Hello World' is not\n", - "a list, but we can turn it into a list. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d63d5efe", - "metadata": {}, - "outputs": [], - "source": [ - "# Turn a string into a list:\n", - "\n", - "l = list(\"Hello World!\")\n", - "print(l)\n", - "\n", - "# That code above works the same as this code below:\n", - "l = list()\n", - "for c in \"Hello World!\":\n", - " l.append(c) # Adding to a list, more on this later\n", - "\n", - "print(l)" - ] - }, - { - "cell_type": "markdown", - "id": "d450eafd", - "metadata": {}, - "source": [ - "There is another way to turn a string into a list, by breaking the string at a\n", - "specific character using the `.split()` method." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9f82ddae", - "metadata": {}, - "outputs": [], - "source": [ - "# split a string at spaces, the default\n", - "s = 'One Two Three Four'\n", - "l = s.split()\n", - "print(l)\n", - "\n", - "# split a string at the comma character\n", - "s = 'One,Two,Three,Four'\n", - "l = s.split(',')\n", - "print(l)" - ] - }, - { - "cell_type": "markdown", - "id": "9f66003c", - "metadata": {}, - "source": [ - "We'll learn more about how to work with lists later; right now we just want to\n", - "show that it is an easy way to make a list. \n", - "\n", - "## Sorting\n", - "\n", - "Sorting, putting a list of items into a predictable order, is one of the most common\n", - "tasks in a program, and like most other things, there is more than one way to do\n", - "it. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7bd259e4", - "metadata": {}, - "outputs": [], - "source": [ - "# Run me!\n", - "l = list('adefibhgc')\n", - "l.sort() # This sorts the list in place\n", - "print(l)\n", - "print()\n", - "\n", - "l = list('adefibhgc')\n", - "sl = sorted(l) # This creates a new list & doesn't change the original\n", - "print(sl)" - ] - }, - { - "cell_type": "markdown", - "id": "0578ffed", - "metadata": {}, - "source": [ - "\n", - "## Adding To Lists\n", - "\n", - "You can add items to lists with `.append()`, and concatenate lists ( put them\n", - "together) with `+`:\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a6550d5f", - "metadata": { - "title": "[python]" - }, - "outputs": [], - "source": [ - "l = []\n", - "\n", - "# Add to the list using append\n", - "l.append('item 1')\n", - "l.append('item 2')\n", - "l.append('item 3')\n", - "\n", - "# You can also use = with an empty list to create a new list\n", - "l = l + ['item 4', 'item 5']\n", - "\n", - "print(l)" - ] - }, - { - "cell_type": "markdown", - "id": "ee522118", - "metadata": { - "lines_to_next_cell": 0 - }, - "source": [ - "\n", - "Try adding more items to the list!\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "17a599ca", - "metadata": { - "title": "[python]" - }, - "outputs": [], - "source": [ - "# Try adding more items to the list l\n" - ] - }, - { - "cell_type": "markdown", - "id": "c8f4a193", - "metadata": {}, - "source": [ - "## Test Yourself \n", - "\n", - "Show Us Your Lists!\n", - "\n", - "Now, you can write a program. Here is what your program should do. \n", - "\n", - "* Start with a string that has friend names, with spaces between the friend names, like\n", - "this, but with real names: `'foo bar baz'`. Split the list into a string.\n", - "* Ask the user for new friend names three times, and add those names to the list, using the `input()` function.\n", - "* Sort the list\n", - "* Print out each name on a seperate line. \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6c51bbf8", - "metadata": { - "title": "[python]" - }, - "outputs": [], - "source": [ - "# Test yourself\n", - "\n", - "# Here is how to get a name from the user.\n", - "# name = input(\"What is your friend's name? \")\n", - "# Look at the ^^^^ top of the window ^^^ for the prompt\n", - "\n" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "title,-all", - "main_language": "python", - "notebook_metadata_filter": "-all" - }, - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - }, - "syllabus": { - "uid": "7zsKa84X" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/lessons/30_Loops/050_Tuples.ipynb b/lessons/30_Loops/050_Tuples.ipynb deleted file mode 100644 index d3888cd1..00000000 --- a/lessons/30_Loops/050_Tuples.ipynb +++ /dev/null @@ -1,110 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Tuples — Lists You Can't Change\n", - "\n", - "Tuples are a kind of sequence and collection, like a list, with one really important difference: they can't be changed, they are \"immutable.\" What's the point of not being able to change them? Well, they are actually very useful, so let's learn about them. \n", - "\n", - "You can create a tuple in a similar way to creating a list, but using `()` instead of `[]`\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Tuples and Lists\n", - "\n", - "l = [1, 2, 3, 4, 5] # List\n", - "\n", - "print(l)\n", - "\n", - "t = (1, 2, 3, 4, 5) # Tuple\n", - "\n", - "print(t)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "One difference from a list is that when creating a tuple, there must be a comma\n", - "inside the parentheses, so if you have only one item, you have to follow it with\n", - "a comma, or it won't be a tuple." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l = [1] # List\n", - "t = (1,) # Tuple\n", - "nt = (1) # Not a tuple\n", - "\n", - "print('l',l,type(l))\n", - "print('t',t,type(t))\n", - "print('nt',nt,type(nt))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There are a lot of cases where you want to make a sequence that you can iterate over, but you don't need to add or remove items from the collection. Because tuples can't be changed, they can be optimized to be fast and small in ways that lists cannot be optimized. \n", - "\n", - "You can do a lot of the same things with tuples that you can do with lists, using the same syntax. For instance, indexing and slicing works the same way. \n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "t = (1, 2, 3, 4, 5) \n", - "\n", - "print(t[0]) # Accessing elements in a tuple\n", - "print(t[2:4]) # Slicing a tuple" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - }, - "syllabus": { - "uid": "9zTQza2e" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/lessons/30_Loops/060_Indexing_and_Slicing.ipynb b/lessons/30_Loops/060_Indexing_and_Slicing.ipynb deleted file mode 100644 index 253f052d..00000000 --- a/lessons/30_Loops/060_Indexing_and_Slicing.ipynb +++ /dev/null @@ -1,177 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Indexing and Slicing\n", - "\n", - "Because lists and strings are a sequence of things, you can do some interesting\n", - "things with them: you can get a specific item from the list, or you can get\n", - "a range of items. \n", - "\n", - "# Indexing\n", - "\n", - "Indexing is when you get a single item from a list or string by its position (index)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Indexing ( Run Me! )\n", - "\n", - "colors = [ 'red', 'blue', 'black', 'orange'] # define a list of colors\n", - "\n", - "print(colors[0]) # print the first item\n", - "print(colors[1]) # print the second item \n", - "\n", - "print(colors[-1]) # print the last item. Negative numbers are counted from the end" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Slicing\n", - "\n", - "A \"slice\" is a portion of a list, starting at one list item and ending with another. A slice is\n", - "defined by numbers in side brackets after a list. There are many forms, but they all follow the \n", - "pattern of `[start:stop:skip]` where any of those can be left out. \n", - "\n", - "Here are some examples of slicing:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Slicing lists (Run Me!)\n", - "\n", - "colors = [ 'red', 'blue', 'black', 'orange'] # define a list of colors\n", - "\n", - "print('[1:3]', colors[1:3]) # print the second to third items, but not including the third item\n", - "print('[:2]', colors[:2]) # print the first two items. Nothing before the ':' means 'from the start'\n", - "print('[2:]', colors[2:]) # print the third and subsequent items. Nothing after the ':' means 'to the end'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So, some of patterns you can use are: \n", - "\n", - "| Syntax | Description |\n", - "|--------------------|-----------------------------------------------|\n", - "| `l[start:stop]` | from `start` to `stop` |\n", - "| `l[start:stop:skip]` | from `start` to `stop`, skipping every `skip` |\n", - "| `l[start:]` | from `start` to the last |\n", - "| `l[:end]` | from the first to `end` |\n", - "| `l[::skip]` | from the first to the last, skipping by `skip` |\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also use negative numbers in slicing, which means \"from the end\"." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Slicing with negative ( Run me!)\n", - "\n", - "nums = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n", - "\n", - "print('[-3:]', nums[-3:]) # print the last three items\n", - "print('[:-3]', nums[:-3]) # print all but the last three items\n", - "print('[-6:-3]', nums[-6:-3]) # print the fourth to sixth items, but not including the sixth item\n", - "\n", - "# And we can mix negative and positives\n", - "\n", - "print('[2:-2]', nums[2:-2]) # print the third to the third last items, but not including the third last item\n", - "print('[-6:8]', nums[-6:8]) # print the fourth to the eighth items, but not including the eighth item\n", - "\n", - "# However, the most important use of negative indexes is to get the last item,\n", - "# or the last few items\n", - "\n", - "print('[-1]', nums[-1]) # print the last item\n", - "print('[-3:]', nums[-3:]) # print the last three items" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Skipping can also be positive or negative. A negative skip means to reverse the list." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Skipping items\n", - "\n", - "nums = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n", - "\n", - "print('[::2]', nums[::2]) # print every second item\n", - "print('[1::2]', nums[1::2]) # print every second item starting from the second item\n", - "\n", - "# This third argument can also be negative, which means to go backwards\n", - "# This is a common way to reverse a list\n", - "\n", - "print('[::-1]', nums[::-1]) # print the list in reverse order. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The most frequent use of skip is to reverse the list, using:\n", - "\n", - "```python \n", - "l[::-1]\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.6" - }, - "syllabus": { - "uid": "P27f2L8k" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/lessons/30_Loops/080_Strings.ipynb b/lessons/30_Loops/080_Strings.ipynb deleted file mode 100644 index 9eea876d..00000000 --- a/lessons/30_Loops/080_Strings.ipynb +++ /dev/null @@ -1,487 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Strings\n", - "\n", - "We've already seen strings, so let's take some time to understand them better. Here is a really basic string\n", - "assignment:\n", - "\n", - "```python \n", - "message = \"Hello World!\"\n", - "```\n", - "\n", - "You can create strings with different kinds of quotes. These are all basically the same, but each has a slightly different purpose: \n", - "\n", - "```python \n", - "message = \"Hello World!\"\n", - "message = 'Hello World!'\n", - "message = \"\"\"Hello World!\"\"\"\n", - "```\n", - "\n", - "The reason for single and double quotes is so you can \n", - "put the other kind of quote inside. \n", - "\n", - "```python \n", - "message = \" You're allowed to put single quotes inside double quotes\"\n", - "message = ' He said \"You can use double quotes inside single quotes.\" '\n", - "```\n", - "\n", - "You've seen the triple quote form at the top of your assignments, \n", - "where it gives you the instructions about how to complete the lesson. The \n", - "really important part of triple quotes is that it can span multiple lines, \n", - "and you can also have single and double quotes inside. \n", - "\n", - "```python \n", - "\n", - "message = \"\"\"\n", - "“Hope” is the thing with feathers -\n", - "That perches in the soul -\n", - "And sings the tune without the words -\n", - "And never stops - at all -\n", - "\"\"\"\n", - "```\n", - "\n", - "## Escaping\n", - "\n", - "There are some special characters that are hard to put into strings; they require a\n", - "special prefix character, and adding that character is called \"escaping\" or \"quoting\". \n", - "\n", - "For instance, you can put a double quote inside of a double quote string, but\n", - "you have put a slash in front of it. \n", - "\n", - "```python\n", - "message = ' You\\'re allowed to put single quotes inside single quotes, if you escape it '\n", - "message = \" He said \\\"You can use double quotes inside single quotes, if you escape\\\" \"\n", - "```\n", - "\n", - "Notice the `\\'` and `\\\"` characters in the strings. There are a lot of other characters\n", - "that you might escape, but there are only two that you will use most of the time:\n", - "\n", - "* `\\n` ends a line and starts a new one. \n", - "* `\\t` is a tab. It moves the next character to the next tab stop (usually every 4 or 8 characters).\n", - "\n", - "Here is an example of using `\\n` and `\\t` in a string." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Tabs and Newlines\n", - "\n", - "s = \"a\\tb?\\tc!!\\t\\n1\\t2\\t3\\t\\n\"\n", - "print(s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that the 1,2,3 line up with the a,b?,c!!, even though the strings are\n", - "different lengths. Using the `\\t` the first time makes the next character always\n", - "start at column 8, and the second one makes it always start at column 16. You\n", - "can use this for basic formatting.\n", - "\n", - "\n", - "# Test Yourself\n", - "\n", - "Create two strings and print them out. \n", - "The first string will have multiple line and have a 'Roses are Red' poem. Use triple quotes\n", - "\n", - "The second string will use double quotes and have a list of your friends first names and their ages, \n", - "with their ages lined up in the second column. \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test Yourself\n", - "\n", - "poem = \"\"\"\n", - "\"\"\"\n", - "\n", - "print(poem)\n", - "\n", - "friends = \"\"\n", - "\n", - "print(friends)\n", - "\n", - "# Hint: you will need \\t and \\n in the friends string, but not in the poem." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Fancy Formatting\n", - "\n", - "Python has several different ways to format a string; we are going to practice\n", - "the newest, and most important one. These are called 'f strings' and here is what\n", - "they look like: \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# f-strings\n", - "\n", - "age = 14\n", - "name = \"John\"\n", - "\n", - "print(f\"{name} is {age} years old.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The important parts of the `f` string is \n", - "\n", - "* It starts with 'f'\n", - "* You can insert variables inside it by surrounding them with curly braces, `{}`\n", - "\n", - "Here are some more important things you can do with f-strings. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# More F-Strings\n", - "\n", - "# You can put lots of expressions inside the curly braces, not just variable names\n", - "\n", - "print(f\"{name.upper()} is {age} years old. In 5 years, he will be {age + 5} years old.\")\n", - "\n", - "# You can add a colon after the variable name to format the output\n", - "# For instance, to make a number appear with commas as thousands separators, you can write {number:,}\n", - "# or to make it have only 2 decimal places, you can write {number:.2f}\n", - "\n", - "number = 1234567.890123\n", - "\n", - "print(f\"With a separator: {number:,}\")\n", - "print(f\"Rounded to 2 places: {number:.2f}\")\n", - "\n", - "# You can make a string appear in a certain number of spaces by writing {string:10}\n", - "# This will make the string appear in a space of 10 characters, padding it with spaces if necessary\n", - "\n", - "print(f\"{'Hello':10}{'World':10}!\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can combine f-strings and triple quotes for a very powerful method of formatting. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me\n", - "\n", - "name = \"John\"\n", - "age = 14\n", - "prize = \"a unicorn\"\n", - "\n", - "letter = f\"\"\"\n", - "Dear {name},\n", - "\n", - "Congratulations! You have won {prize} in our prize draw. \n", - "You are only {age} years old, so you must be very lucky!\n", - "\n", - "Yours sincerely,\n", - "The Prize Draw Team\n", - "\"\"\"\n", - "\n", - "print(letter)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There is a lot more you can do with f-strings, but this will get you started. \n", - "\n", - "## Test Yourself\n", - "\n", - "In the loop, print out:\n", - "\n", - "* start each line with \"!\"\n", - "* The line number, formatted to 3 spaces\n", - "* The string \"equations\" followed by a math equation showing the number times 2\n", - "* the last number is formatted to 5 spaces\n", - "\n", - "For instance, one of the lines should look like\n", - "\n", - "```python \n", - "! 6 equation: 6 * 2 = 12\n", - "```\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test Yourself\n", - "\n", - "for i in range(1, 9):\n", - " ..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Turning Strings to List\n", - "\n", - "Let's review how to strings into lists. There are two important ways. \n", - "\n", - "First, you can turn it directly into a list with `list()`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(list(\"Hello World\"))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Second, you can break it up on a character with `split()`:\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Split on spaces\n", - "m = \"Python is fun to learn\"\n", - "l = m.split()\n", - "print(l)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `.split()` method splits on spaces by default, but you can specify a\n", - "different character to split on by passing it as an argument to the method." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# split on commas\n", - "\n", - "m = 'a,b,c,d,e'\n", - "l = m.split(',')\n", - "print(l)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After you have split a string, you can rejoin it. There is a funny way to do it,\n", - "which we will have to explain later; for now just use this method." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "# Join with spaces\n", - "l = ['Python', 'is', 'fun', 'to', 'learn']\n", - "s = ' '.join(l) # Doesn't that look weird?\n", - "print(s)\n", - "\n", - "# Join with commas\n", - "l = ['a', 'b', 'c', 'd', 'e']\n", - "s = ','.join(l)\n", - "print(s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So, if you wanted to change the third word of a sentence, you might do:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "m = \"Python is fun to learn\"\n", - "l = m.split() # Turn it into a list of words\n", - "l[2] = 'amazing' # Change the third word\n", - "s = ' '.join(l) # Join the words back together\n", - "\n", - "print(s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you wanted to add a word in the middle you might do:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "m = \"Python is fun to learn\"\n", - "l = m.split() # Turn it into a list of words\n", - "\n", - "first_half = l[:2] # Get the first two words\n", - "second_half = l[2:] # Get the rest of the words\n", - "\n", - "l = first_half + ['really'] + second_half # Add 'really' in the middle, using + to combine lists\n", - "\n", - "s = ' '.join(l) # Join the words back together\n", - "\n", - "s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Test Yourself\n", - "\n", - "Use the strings and lists, along with `+`, `.split()`, slicing and indexing, to assemble and print out phrases.\n", - "\n", - "Hints:\n", - "\n", - "* You might need to use the string methods `.lower()` or `.capitalize()`\n", - "* You can extract words using either a slice on a string, or convert the string to a list first. \n", - "\n", - "First, here is an example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Example\n", - "\n", - "s1 = \"Pythons are constrictors!\"\n", - "s2 = \"John is funny\"\n", - "\n", - "# Print \"Python is fun\" \n", - "\n", - "space = s1[7]\n", - "s = s1[:6] + space + s2[5:-2] + s1[-1]\n", - "print(s)\n", - "\n", - "# or\n", - "\n", - "words1 = s1.split()\n", - "pythons = words1[0]\n", - "python = pythons[:-1]\n", - "words2 = s2.split()\n", - "is_word = words2[1] # 'is' is a reserved word in Python, so we can't use it as a variable name\n", - "funny = words2[2]\n", - "fun = funny[:-2]\n", - "exclaim = s1[-1]\n", - "\n", - "print(python + space + is_word + space + fun + exclaim)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now you try it:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test yourself \n", - "\n", - "s1 = \"The train in Spain.\"\n", - "s2 = \"makes passengers complain\"\n", - "s3 = [\"flowers\", \"grow\", \"again\" ]\n", - "s4 = \"to\"\n", - "s5 = \" \" \n", - "\n", - "# Print \"The train in Spain makes passengers complain.\"\n", - "\n", - "# Print \"The rain in Spain makes flowers grow again\"\n", - "\n", - "# Print \"The passengers grow flowers in Spain\n", - "\n", - "# Print \"The flowers in Spain train passengers to complain.\"\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - }, - "syllabus": { - "uid": "Tmg4QRhJ" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/lessons/30_Loops/100_For_Loop_Gauntlet.ipynb b/lessons/30_Loops/100_For_Loop_Gauntlet.ipynb index 19ee75eb..db632fe5 100644 --- a/lessons/30_Loops/100_For_Loop_Gauntlet.ipynb +++ b/lessons/30_Loops/100_For_Loop_Gauntlet.ipynb @@ -1,363 +1,395 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "539e9340", - "metadata": {}, - "source": [ - "# For Loop Gauntlet\n", - "\n", - "The goal of this assignment is to make you a master of utilizing for loops.\n", - "Complete all the for loop challenges on the paper. Please read each challenge\n", - "carefully and make sure that your for loops display exactly what is asked. If\n", - "you finish them all, try the bonus challenge.\n", - "\n", - "## Single for-loops\n", - "\n", - "Write a for loop to do each of the following\n" - ] - }, - { - "cell_type": "markdown", - "id": "d0b568dc", - "metadata": { - "lines_to_next_cell": 0 - }, - "source": [ - "Display all numbers from 0 to 100 " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d0752dd4", - "metadata": { - "title": "[python]" - }, - "outputs": [], - "source": [ - "# Display all numbers from 0 to 100 \n" - ] - }, - { - "cell_type": "markdown", - "id": "8b92e611", - "metadata": { - "lines_to_next_cell": 0 - }, - "source": [ - "Display all numbers from 100 to 0 " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aec8cf23", - "metadata": { - "title": "[python]" - }, - "outputs": [], - "source": [ - "# Display all numbers from 100 to 0 \n" - ] - }, - { - "cell_type": "markdown", - "id": "8942605e", - "metadata": { - "lines_to_next_cell": 0 - }, - "source": [ - "Display all even numbers from 2 to 100 " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ed3d8616", - "metadata": { - "lines_to_next_cell": 0, - "title": "[python]" - }, - "outputs": [], - "source": [ - "# Display all even numbers from 2 to 100 \n" - ] - }, - { - "cell_type": "markdown", - "id": "c3501380", - "metadata": { - "lines_to_next_cell": 0 - }, - "source": [ - "Display all odd numbers from 1 to 99 " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "038f48bf", - "metadata": { - "lines_to_next_cell": 0, - "title": "[python]" - }, - "outputs": [], - "source": [ - "# Display all odd numbers from 1 to 99\n" - ] - }, - { - "cell_type": "markdown", - "id": "f750c8e2", - "metadata": { - "lines_to_next_cell": 0 - }, - "source": [ - "Display all numbers from 1 to 40. if the number is odd, print \"odd\" next to the number. if the number is even, print \"even\" next to the number (see example below). \n", - "\n", - " 1 is odd \n", - " 2 is even \n", - " 3 is odd \n", - " 4 is even \n", - " 5 is odd...etc. \n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "174bdcd9", - "metadata": {}, - "outputs": [], - "source": [ - "# Display all numbers from 1 to 500.\n" - ] - }, - { - "cell_type": "markdown", - "id": "969e00f1", - "metadata": {}, - "source": [ - "Display all multiples of 7 from 0 to 70" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "620b3886", - "metadata": { - "lines_to_next_cell": 0, - "title": "[python]" - }, - "outputs": [], - "source": [ - "# Display all multiples of 7 from 0 to 777\n" - ] - }, - { - "cell_type": "markdown", - "id": "b468e701", - "metadata": { - "lines_to_next_cell": 0 - }, - "source": [ - "Print all the years you were alive and how old you were in each. e.g. \"in 1979, i was 2 years old.\" (for a really old person) " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6db862fd", - "metadata": { - "lines_to_next_cell": 0, - "title": "[python]" - }, - "outputs": [], - "source": [ - "# Your Code Here\n" - ] - }, - { - "cell_type": "markdown", - "id": "571b7e8d", - "metadata": { - "lines_to_next_cell": 0 - }, - "source": [ - "## Nested for-loops\n", - "\n", - "Write nested for loops (a for loop inside another for loop). Here is an example of nested loops. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "beb062f6", - "metadata": { - "lines_to_next_cell": 0, - "title": "[python]" - }, - "outputs": [], - "source": [ - "# Examples of Nested Loops\n", - "\n", - "for i in range(8):\n", - " for j in range(8):\n", - " print(f\"({i},{j})\", end= ' ')\n", - " print()" - ] - }, - { - "cell_type": "markdown", - "id": "3605a476", - "metadata": { - "lines_to_next_cell": 0 - }, - "source": [ - "For your first nested loop, display this output: \n", - "\n", - "```\n", - "0 0 \n", - "0 1 \n", - "0 2 \n", - "1 0 \n", - "1 1 \n", - "1 2 \n", - "2 0 \n", - "2 1 \n", - "2 2\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8c847ed3", - "metadata": { - "lines_to_next_cell": 0, - "title": "[python]" - }, - "outputs": [], - "source": [ - "# Display the output shown above\n" - ] - }, - { - "cell_type": "markdown", - "id": "c6a9c592", - "metadata": { - "lines_to_next_cell": 0 - }, - "source": [ - "Display the numbers 1 through 9 in a 3x3 square grid like this: \n", - "\n", - "```\n", - "1 2 3 \n", - "4 5 6 \n", - "7 8 9 \n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bb0a4a5b", - "metadata": { - "lines_to_next_cell": 0, - "title": "[python]" - }, - "outputs": [], - "source": [ - "# Display the numbers 1 through 9 in a 3x3 square grid\n" - ] - }, - { - "cell_type": "markdown", - "id": "3265fddb", - "metadata": { - "lines_to_next_cell": 0 - }, - "source": [ - "Display the numbers 1 through 100 in a 10x10 square grid. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ced82638", - "metadata": { - "lines_to_next_cell": 0, - "title": "[python]" - }, - "outputs": [], - "source": [ - "# Display the numbers 1 through 100 in a 10x10 square grid. \n" - ] - }, - { - "cell_type": "markdown", - "id": "9434b500", - "metadata": { - "lines_to_next_cell": 2 - }, - "source": [ - "Display the following output: \n", - "\n", - "```\n", - "* \n", - "* * \n", - "* * * \n", - "* * * * \n", - "* * * * * \n", - "* * * * * *\n", - "``` " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3b81fd1e", - "metadata": {}, - "outputs": [], - "source": [ - "# Display the asterisk pattern shown above\n" - ] - }, - { - "cell_type": "markdown", - "id": "5a40192d", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "title,-all", - "main_language": "python", - "notebook_metadata_filter": "-all" - }, - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - }, - "syllabus": { - "uid": "8yGSkBgV" - } + "cells": [ + { + "cell_type": "markdown", + "id": "539e9340", + "metadata": {}, + "source": "# **For Loop Gauntlet**\n\nWelcome to the For Loop Gauntlet. This assignment gives you practice with `for` loops.\n\nCarefully read and complete each challenge below. Make sure your code produces exactly the output described. If you finish all the main challenges, try the bonus at the end.\n\n## **Single For-Loops**\n\nWrite a `for` loop to accomplish each of the following tasks." }, - "nbformat": 4, - "nbformat_minor": 5 -} + { + "cell_type": "markdown", + "id": "d0b568dc", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "#### **Display all numbers from 0 to 100**\n", + "\n", + "Write a loop that prints every number from 0 up to and including 100." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0752dd4", + "metadata": { + "title": "[python]" + }, + "outputs": [], + "source": [ + "# Display all numbers from 0 to 100 \n" + ] + }, + { + "cell_type": "markdown", + "id": "8b92e611", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "#### **Display all numbers from 100 to 0**\n", + "\n", + "Write a loop that prints every number from 100 down to 0 (counting backwards)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aec8cf23", + "metadata": { + "title": "[python]" + }, + "outputs": [], + "source": [ + "# Display all numbers from 100 to 0 \n" + ] + }, + { + "cell_type": "markdown", + "id": "8942605e", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "#### **Display all even numbers from 2 to 100**\n", + "\n", + "Write a loop that prints every even number starting at 2 and ending at 100." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ed3d8616", + "metadata": { + "lines_to_next_cell": 0, + "title": "[python]" + }, + "outputs": [], + "source": [ + "# Display all even numbers from 2 to 100 \n" + ] + }, + { + "cell_type": "markdown", + "id": "c3501380", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "#### **Display all odd numbers from 1 to 99**\n", + "\n", + "Write a loop that prints every odd number from 1 up to and including 99." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "038f48bf", + "metadata": { + "lines_to_next_cell": 0, + "title": "[python]" + }, + "outputs": [], + "source": [ + "# Display all odd numbers from 1 to 99\n" + ] + }, + { + "cell_type": "markdown", + "id": "f750c8e2", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "#### **Odd or Even?**\n", + "\n", + "Write a loop that prints every number from 1 to 40. For each number, print whether it is *odd* or *even* next to the number, like this:\n", + "\n", + "```text\n", + "1 is odd\n", + "2 is even\n", + "3 is odd\n", + "4 is even\n", + "... and so on up to 40.\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "174bdcd9", + "metadata": {}, + "outputs": [], + "source": "# Print every number from 1 to 40 with whether it is odd or even.\n" + }, + { + "cell_type": "markdown", + "id": "969e00f1", + "metadata": {}, + "source": [ + "#### **Display all multiples of 7 from 0 to 70**\n", + "\n", + "Write a loop that prints every multiple of 7, starting at 0 and ending at 70." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "620b3886", + "metadata": { + "lines_to_next_cell": 0, + "title": "[python]" + }, + "outputs": [], + "source": [ + "# Display all multiples of 7 from 0 to 70\n", + "# Write a loop that prints every multiple of 7 from 0 to 70." + ] + }, + { + "cell_type": "markdown", + "id": "b468e701", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "#### **How Old Were You?**\n", + "\n", + "Print all the years you have been alive, and for each year, print how old you were. \n", + "\n", + "For example:\n", + "\n", + "```text\n", + "in 2010, I was 5 years old.\n", + "in 2011, I was 6 years old.\n", + "... and so on up to the current year.\n", + "```\n", + "\n", + "(Use your own birth year and adjust the range as needed!)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6db862fd", + "metadata": { + "lines_to_next_cell": 0, + "title": "[python]" + }, + "outputs": [], + "source": [ + "# Your Code Here\n" + ] + }, + { + "cell_type": "markdown", + "id": "571b7e8d", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "## **Nested For-Loops**\n", + "\n", + "Now let's practice writing loops inside of loops! These are called *nested* `for`*-loops*. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "beb062f6", + "metadata": { + "lines_to_next_cell": 0, + "title": "[python]" + }, + "outputs": [], + "source": [ + "# Examples of Nested Loops\n", + "\n", + "for i in range(8):\n", + " for j in range(8):\n", + " print(f\"({i},{j})\", end= ' ')\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "id": "3605a476", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "#### **Pair of Numbers**\n", + "\n", + "Write a nested loop to display the following output:\n", + "\n", + "```text\n", + "0 0\n", + "0 1\n", + "0 2\n", + "1 0\n", + "1 1\n", + "1 2\n", + "2 0\n", + "2 1\n", + "2 2\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c847ed3", + "metadata": { + "lines_to_next_cell": 0, + "title": "[python]" + }, + "outputs": [], + "source": [ + "# Display the output shown above\n" + ] + }, + { + "cell_type": "markdown", + "id": "c6a9c592", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "#### **3 x 3 Grid**\n", + "\n", + "Display the numbers 1 through 9 in a 3x3 grid, like this:\n", + "\n", + "```text\n", + "1 2 3\n", + "4 5 6\n", + "7 8 9\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb0a4a5b", + "metadata": { + "lines_to_next_cell": 0, + "title": "[python]" + }, + "outputs": [], + "source": [ + "# Display the numbers 1 through 9 in a 3x3 square grid\n" + ] + }, + { + "cell_type": "markdown", + "id": "3265fddb", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "#### **10 x 10 Grid**\n", + "\n", + "Display the numbers 1 through 100 in a 10x10 grid. Each row should have 10 numbers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ced82638", + "metadata": { + "lines_to_next_cell": 0, + "title": "[python]" + }, + "outputs": [], + "source": [ + "# Display the numbers 1 through 100 in a 10x10 square grid. \n" + ] + }, + { + "cell_type": "markdown", + "id": "9434b500", + "metadata": { + "lines_to_next_cell": 2 + }, + "source": [ + "#### **Triangle Pattern**\n", + "\n", + "Write a nested loop to display the following triangle pattern using asterisks:\n", + "\n", + "```text\n", + "*\n", + "* *\n", + "* * *\n", + "* * * *\n", + "* * * * *\n", + "* * * * * *\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3b81fd1e", + "metadata": {}, + "outputs": [], + "source": [ + "# Display the asterisk pattern shown above\n" + ] + }, + { + "cell_type": "markdown", + "id": "dee7d3d2", + "metadata": {}, + "source": "#### **Bonus Challenge**\n\nIf you are feeling adventurous, create a 10 x 10 multiplication table.\n\nThe output should look like this:\n\n```text\n1 2 3 4 5 6 7 8 9 10\n2 4 6 8 10 12 14 16 18 20\n3 6 9 12 15 18 21 24 27 30\n4 8 12 16 20 24 28 32 36 40\n5 10 15 20 25 30 35 40 45 50\n6 12 18 24 30 36 42 48 54 60\n7 14 21 28 35 42 49 56 63 70\n8 16 24 32 40 48 56 64 72 80\n9 18 27 36 45 54 63 72 81 90\n10 20 30 40 50 60 70 80 90 100\n```" + }, + { + "cell_type": "code", + "execution_count": null, + "id": "768d6414", + "metadata": {}, + "outputs": [], + "source": [ + "# Write a nested loop to display the multiplication table from 1 to 10.\n" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "title,-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + }, + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "For Loop Gauntlet", + "uid": "8yGSkBgV" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/lessons/30_Loops/10_Iteration.ipynb b/lessons/30_Loops/10_Iteration.ipynb new file mode 100644 index 00000000..5af8ca48 --- /dev/null +++ b/lessons/30_Loops/10_Iteration.ipynb @@ -0,0 +1,183 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Iteration**\n", + "\n", + "In programming, we often need to use iteration and loops to repeat a block of code multiple times. \n", + "\n", + "While you previously used loops to guide *Tina the Turtle* in previous [lessons](../10_Turtles/40_Loops/10_Loops.ipynb), these structures are equally vital for iterating through lists, processing data batches, and automating repetitive calculations.\n", + "\n", + "A basic `for` loop in Python looks like this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# This loop repeats 10 times, printing numbers 0 through 9\n", + "for i in range(10):\n", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "**What happens in the code above?**\n- The loop runs 10 times.\n- Each time, `i` takes the next value from 0 to 9.\n- `print(i)` displays the current value of `i`.\n\nTry running the cell to see the output." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Much like `if` statements, `for` loops follow a strict structural pattern. They always start with the `for` keyword, followed by a variable name, then the `in` keyword, and finally an iterable that can be looped over.\n", + "\n", + "The general syntax is:\n", + "\n", + "```python\n", + "for in :\n", + " # Code to repeat\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## **What is an Iterable?**\nAn iterable is any collection of items that can be stepped through one by one, and *iterate* just means to repetitively go through each individual item within a collection.\n\n| Iterable Type | Example | Why/When to Use |\n|-------------|-------------------------------|-------------------------------|\n| List | `['apple', 'banana']` | If you have a collection that needs to be updated or changed later. |\n| Tuple | `(1, 2, 3)` | The collection is fixed, immutable, and it shouldn't ever change. |\n| String | `\"Hello World\"` | You need to iterate through individual letters. |\n| Range | `range(10)` | When an action needs to be repeated a specific number of times. |\n\n
\n\n> **Note:** Iterables can be any collection, they aren't limited to the above examples. Curious? Refer to the [Official Python Documentation on Iterables](https://docs.python.org/3/glossary.html#term-iterable).\n\n#### **Common Iterable Patterns**\n\nA `for` loop automates a simple cycle: Python assigns each item from the iterable to your variable, runs the code block, and then moves on to the next item, repeating the process until a termination condition is met (e.g., when all iterable items have been processed).\n\n> **Warning:** If there is no termination condition or the iterable is infinite, the loop will run forever—this is called an infinite loop.\n\nLet's look at some examples.\n\n##### **List Iterations**" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# This loop repeats 4 times, printing the letters a through d\n", + "for i in ['a','b','c','d']:\n", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wait a minute! Did you notice that `i` is not declared before the loop begins?\n", + "\n", + "In Python, `for` loops automatically initialize the loop variable, assigning it each value from the collection after each iteration. This removes the need for manual setup and keeps your code clean. \n", + "\n", + "> **Tip:** You can use any variable name in a `for` loop, but it's common to use `i` for indices or `item` for items, which helps programmers understand the context at a glance.\n", + "\n", + "##### **Range Iterations**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# This loop repeats 5 times, printing numbers 0 through 4\n", + "for i in range(5):\n", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Did you know that you can also use `range()` with two or three arguments to control the start, stop, and step size during each iteration?\n", + "\n", + "For example, `range(2, 10, 2)` gives even numbers from 2 to 8. \n", + "\n", + "You can also convert a range to a list using `list(range(5))`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### **String Iterations**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# This loop repeats 12 times, printing each character in the string \"Hello World!\"\n", + "for char in \"Hello World!\":\n", + " print(char)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When looping through a string, you can also check for vowels (e.g., `if char.lower() in 'aeiou'`), count specific letters using counters (e.g., `counter += 1`), or build new strings (e.g., `new_string += char`). \n", + "\n", + "There are also other useful methods, like `.lower()` or `.upper()` for case-insensitive matching, `.replace()` for substitutions, and `enumerate()` to get both the index and character at the same time." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "### **Choosing Iterable Names**\n\nThe variable name you choose (like `i` or `char` in the examples above) is always up to you. However, it's usually a good practice to pick one that is descriptive and clearly explains what each item represents. \n\nTake a look at these examples and think about which names make the most sense:\n\n Usage Scenario | Example Name(s) | Example Code |\n|----------------|--------------------------|--------------|\n| Names | *name*, *student* | `for name in names:` |\n| Numbers | *number*, *num*, *i* | `for number in numbers:` |\n| Fruits | *fruit* | `for fruit in fruits:` |\n| Characters | *char*, *letter* | `for char in string:` |\n| Words | *word* | `for word in string.split():` |\n| Sentences | *sentence* | `for sentence in sentences:` |\n| Coordinates | *x*, *y*, *z* | `for x, y, z in coordinates:` |\n| Colors | *red*, *green*, *blue* | `for red, green, blue in colors:` |\n| Sizes | *width*, *height*, *depth*| `for width, height, depth in sizes:` |\n| Indices | *i*, *index* | `for i in range(len(list)):` |\n| Numbers | *num*, *i* | `for num in range(10):` |\n| Steps | *step* | `for step in range(0, 100, 10):` |\n\nBelow we can see an example of a `for` loop iterating over a list of `fruits`:" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Using descriptive variable names\n", + "fruits = ['apple', 'banana', 'cherry']\n", + "\n", + "for fruit in fruits:\n", + " print(\"I like to eat\", fruit)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Choosing clear names helps others (and your future self) quickly understand what your loop is doing." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Iteration", + "uid": "ITpqcvxv" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/30_Loops/110_FizzBuzz_Gui_Grid.py b/lessons/30_Loops/110_FizzBuzz_Gui_Grid.py index 20d1fb85..6b2f208a 100644 --- a/lessons/30_Loops/110_FizzBuzz_Gui_Grid.py +++ b/lessons/30_Loops/110_FizzBuzz_Gui_Grid.py @@ -10,22 +10,30 @@ * If the number is evenly divisible by 15, print '🐍' * If it is divisible by neither, print the number. -Additionally, If you are displaying a number color the numbers as follows: +Because any multiple of 15 is also a multiple of 5 and 3, check for 15 first. + +If you are displaying a number, color it as follows: * If the sum of the digits of the number is even, color the number blue * If the sum of the digits of the number is odd, color the number red +This lesson uses `guizero`, a small GUI library. Don't worry about how it works — +copy the examples and change the `Text(...)` call to show what you want. + Here is how you can display a number in your grid. Call this function in your loop to display the number in the grid cell at the row and column you specify. Text(app, text=str(number), grid=[col, row], color=color) -Or to display a badger: - +Or to display a badger: + Text(app, text='🦡', grid=[col, row], color=color) -HINT: You can use % and // to get the first and last digit of a number, or you can convert the number to a string and iterate over the digits + +HINT: You can use % and // to get the first and last digit of a number, +uid: cKjBvzzU +name: Fizzbuzz Gui Grid """ from guizero import App, Box, Text @@ -41,6 +49,6 @@ # If you are displaying a number, calculate the sum of the digits and determine the color -# Call Text(app, text='...', grid=[col, row], color=...) to display something. +# Call Text(app, text='...', grid=[col, row], color=...) to display something. -app.display() \ No newline at end of file +app.display() diff --git a/lessons/30_Loops/120_More_Iterables.ipynb b/lessons/30_Loops/120_More_Iterables.ipynb index a8deec7c..59254a80 100644 --- a/lessons/30_Loops/120_More_Iterables.ipynb +++ b/lessons/30_Loops/120_More_Iterables.ipynb @@ -1,332 +1,660 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Iteration Functions\n", - "\n", - "Python has some very important iteration functions that you can use in your programs. \n", - "\n", - "* ``enumerate()`` iterates over an iterable and returns it with an index. \n", - "* ``zip()`` combines two iterables, iterating through them side-by-side. \n", - "* ``cycle()`` goes through an iterable, then starts over and keeps going. \n", - "* ``islice()`` only takes a few of an iterable. \n", - "\n", - "Let's try some of these out. ( We'll work on ``enumerate()`` and ``zip()`` and leave\n", - "the others for later. )\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Enumerate\n", - "\n", - "colors = [ 'red', 'blue', 'black', 'orange']\n", - "\n", - "for i in enumerate(colors):\n", - " print(i)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that each iteration, ``enumerate()`` returns a _tuple_. The first item in\n", - "the tuple is its number in the list, and the second is the item in the list.\n", - "Usually, we will _unpack_ the tuple. In Python you can write code like this: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "t = (1,2,3)\n", - "a,b,c = t\n", - "print(t)\n", - "print(a,b,c)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Do you see what happened? When we wrote:\n", - "\n", - "```python \n", - "a,b,c = t\n", - "```\n", - "\n", - "The first item in ``t`` was assigned to ``a``, the second to ``b``, etc. That means when we use ``enumerate()`` we can write this instead:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Enumerate with Unpacking\n", - "\n", - "colors = [ 'red', 'blue', 'black', 'orange']\n", - "\n", - "for index, color in enumerate(colors): # Unpacking the tuple from enumerate()\n", - " print(\"#\", index, \"color is\", color)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another thing you should notice about our `enumerate()` example is that there\n", - "is more than one variable in the `for` loop. This is called \"unpacking\" and it\n", - "also works in assignment, where you can write:\n", - "\n", - "```python \n", - "a,b = 1,2\n", - "```\n", - "\n", - "That code will be equivalent to:\n", - "\n", - "```python\n", - "a = 1\n", - "b = 1\n", - "```\n", - "\n", - "What is really going on is that on the left side of the assignment is a tuple,\n", - "and on the right is an iterable, so you can put any iterable on the right. Most\n", - "of the time, you should have the same number of variables on the left as on the\n", - "right, but you can also use `*` to indicate that one variable should \"suck up\"\n", - "every thing left in the list. \n", - "\n", - "One more thing; you will sometimes use parentheses in the unpacking, such as when we\n", - "write:\n", - "\n", - "```python\n", - "pairs = [\n", - " ('a', 1),\n", - " ('b', 2),\n", - " ('c', 3)\n", - "],\n", - "\n", - "for color, (item1, item2) in enumerate(colors, pairs):\n", - " ...\n", - "```\n", - "\n", - "This one is more complex. It means that `enumerate()` is returning a tuple\n", - "for its second element, and that tuple should also be unpacked. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Unpack a range\n", - "a,b,c = range(3)\n", - "print(a,b,c)\n", - "\n", - "# use *rest to capture all the rest of the values\n", - "a,b,*rest = range(5)\n", - "print(a,b,rest)\n", - "\n", - "# the * doesn't have to go at the end\n", - "a,*b,c = range(5)\n", - "print(a,b,c)\n", - "\n", - "# Unpacking multiple levels\n", - "l1 = [1,2,3]\n", - "t1 = ('x','y','z')\n", - "\n", - "t2 = [ l1, t1 ] # two levels deep!\n", - "\n", - "# Unpack all of the levels\n", - "(a,b,c), (d,e,f) = t2\n", - "\n", - "print(a,b,c,d,e,f)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We will study unpacking in a lot more detail later for now you mostly need to understand how it works with iterator functions." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Zip \n", - "\n", - "Zip is another really important iteration tool. It lets you iterate over two lists at the same time. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Zip \n", - "\n", - "list1 = ['a','b','c','d']\n", - "list2 = ['1','2','3','4']\n", - "\n", - "for l1, l2 in zip(list1, list2): # <- Ok, look, unpacking!\n", - " print(l1, l2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice what ``zip()`` did: For each iteration of the loop, it took the first item from both lists, then the second item from both lists, then the third, etc. How could we use this in a turtle program? What if we had instructions about where the turtle should go and what colors it should draw lines?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Use zip to iterate over two lists at once\n", - "\n", - "colors = [ 'red', 'blue', 'black', 'orange']\n", - "\n", - "# Each tuple in the directions is first, how far to turn, then how far to go\n", - "directions = [\n", - " (0, 10),\n", - " (90, 20),\n", - " (0, 40),\n", - " (270, 10)\n", - "]\n", - "\n", - "for color, (angle, distance) in zip(colors, directions):\n", - " print(f\"Move {distance} units in direction {angle} degrees and color {color}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# islice\n", - "\n", - "The ``islice()`` function works like the slice notation on a list: it lets you\n", - "decide where to start and stop iteration. For instance, if you wanted to only\n", - "take the first 10 items of a list, you could use ``islice(l, 10)``\n", - "\n", - "For a normal list, you could just do that with `l[:10]`, so why would you need `islice()`?\n", - "The reason is that a list has a finite number of items, but an iterator can go on forever, \n", - "and like `range()` the iterator doesn't need to store the data ( it can generate it ) so \n", - "you need something more flexible. \n", - "\n", - "For example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from itertools import islice # Important! \n", - "\n", - "N = 1_000_000 \n", - "r = range(N)\n", - "\n", - "# range( iterator, stop ) or\n", - "# range( iterator, start, stop, step)\n", - "l = list( islice( r,N-5, N) )\n", - "\n", - "l" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Demonstrate islice\n", - "\n", - "from itertools import islice # Important! \n", - "\n", - "l = [0,1,2,3,4,5,6,7,8,9]\n", - "\n", - "for i in islice(l, 5): # Stop at 5\n", - " print(i)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# cycle\n", - "\n", - "The `cycle()` iterator function repeats its input iterator over and over, infinitely, \n", - "so it is exactly the kind of thing you want to use `islice()` for\n", - "\n", - "What if you had four colors, in a list: \n", - "\n", - "```python \n", - "colors = [ 'red', 'blue', 'black', 'orange']\n", - "```\n", - "\n", - "But you wanted to use the colors for a hexagon? You'd run out of colors. The ``cycle()`` iterator makes a list repeat infinitely. But, we don't want it \n", - "to be infinite, we want it to got six times. We can use ``islice()`` and ``cycle()`` to solve that problem:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Use cycle and islice\n", - "\n", - "from itertools import cycle, islice\n", - "\n", - "colors = [ 'red', 'blue', 'black', 'orange']\n", - "\n", - "for color in islice(cycle(colors), 25):\n", - " print(color, end=' ')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - }, - "syllabus": { - "uid": "PrKwTywv" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Iteration Functions**\n", + "\n", + "Python has some very important iteration functions that you can use in your programs: \n", + "\n", + "| Function | Description |\n", + "|----------|-------------|\n", + "| `enumerate()` | Iterates over an iterable and returns it with an index |\n", + "| `zip()` | Combines two iterables, iterating through them side-by-side |\n", + "| `cycle()` | Goes through an iterable, then starts over and keeps going |\n", + "| `islice()` | Takes only a portion of an iterable |\n", + "\n", + "Let's try some of these out. \n", + "\n", + "We'll work on `enumerate()` and `zip()` first, and leave the others for later." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Enumerate with Unpacking\n", + "\n", + "colors = ['red', 'blue', 'black', 'orange']\n", + "\n", + "for i in enumerate(colors):\n", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice that on each iteration, `enumerate()` returns a tuple. The first item in the tuple is the item's position in the list, and the second is the item itself.\n", + "\n", + "Usually, we'll unpack the tuple. \n", + "\n", + "In Python, you can write code like this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Unpacking Examples\n", + "\n", + "my_tuple = (1, 2, 3)\n", + "a, b, c = my_tuple\n", + "\n", + "print(my_tuple)\n", + "print(a, b, c)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Do you see what happened? When we wrote:\n", + "\n", + "```python\n", + "a, b, c = my_tuple\n", + "```\n", + "The first item in `my_tuple` was assigned to `a`, the second to `b`, and third to `c`. \n", + "\n", + "This means that when we use `enumerate()`, we can write code like this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Enumerate with Unpacking\n", + "\n", + "colors = ['red', 'blue', 'black', 'orange']\n", + "\n", + "for index, color in enumerate(colors): # Unpacking the tuple from enumerate()\n", + " print(\"#\", index, \"color is\", color)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Starting at a Different Index**\n", + "\n", + "By default, `enumerate()` starts counting at 0. But what if you want to start at a different number? \n", + "\n", + "You can pass a second argument to `enumerate()` to set the starting index!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# enumerate() with a custom start value\n", + "\n", + "colors = ['red', 'blue', 'black', 'orange']\n", + "\n", + "for index, color in enumerate(colors, start=1): # Start counting at 1 instead of 0\n", + " print(f\"Color #{index}: {color}\")\n", + " \n", + "print(\"\\n--- Starting at 100 ---\")\n", + "for index, color in enumerate(colors, start=100):\n", + " print(f\"Item {index} is {color}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **More About Unpacking**\n", + "\n", + "Another thing to notice about our `enumerate()` example is that there's more than one variable in the `for` loop. This is called unpacking, and it also works in assignment statements. \n", + "\n", + "For example, you can write:\n", + "\n", + "```python \n", + "a, b = 1, 2\n", + "```\n", + "\n", + "This code is equivalent to:\n", + "\n", + "```python\n", + "a = 1\n", + "b = 2\n", + "```\n", + "\n", + "What's really happening here is that the left side of the assignment is a tuple, and the right side is an iterable, so you can put any iterable on the right. Most of the time, you should have the same number of variables on the left as items on the right. However, you can also use `*` to indicate that one variable should *capture* all remaining items in the sequence. \n", + "\n", + "### **Nested Unpacking**\n", + "\n", + "You'll sometimes use parentheses in unpacking when working with nested structures, such as:\n", + "\n", + "```python\n", + "pairs = [\n", + " ('a', 1),\n", + " ('b', 2),\n", + " ('c', 3)\n", + "]\n", + "\n", + "for color, (item1, item2) in enumerate(colors, pairs):\n", + " # Do something with color, item1, and item2\n", + "```\n", + "\n", + "This is more complex—it means that `enumerate()` is returning a tuple as its second element, and that tuple should also be unpacked." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Unpack a range\n", + "a, b, c = range(3)\n", + "print(a, b, c)\n", + "\n", + "# Use *rest to capture all the rest of the values\n", + "a, b, *rest = range(5)\n", + "print(a, b, rest)\n", + "\n", + "# The * doesn't have to go at the end\n", + "a, *b, c = range(5)\n", + "print(a, b, c)\n", + "\n", + "# Unpacking multiple levels\n", + "list_one = [1, 2, 3]\n", + "tuple_one = ('x', 'y', 'z')\n", + "\n", + "tuple_two = [list_one, tuple_one] # Two levels deep!\n", + "\n", + "# Unpack all of the levels\n", + "(a, b, c), (d, e, f) = tuple_two\n", + "\n", + "print(a, b, c, d, e, f)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We'll study unpacking in much more detail later. For now, you just need to understand how it works with iterator functions." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **zip()**\n", + "\n", + "`zip()` is another really important iteration tool. It lets you iterate over two (or more) lists at the same time." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# zip() Example\n", + "\n", + "list_one = ['a', 'b', 'c', 'd']\n", + "list_two = ['1', '2', '3', '4']\n", + "\n", + "for list1, list2 in zip(list_one, list_two): # <- Ok, look, unpacking!\n", + " print(list1, list2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice what `zip()` does: it pairs up elements from each list, iterating through them together. On each loop iteration, it takes the next item from the first list and the next item from the second list, combining them so you can use both values at the same time.\n", + "\n", + "How could we use this in a turtle program? \n", + "\n", + "What if we had instructions about where the turtle should go and what colors it should use to draw lines?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Use `zip()` to iterate over two lists at once\n", + "\n", + "colors = ['red', 'blue', 'black', 'orange']\n", + "\n", + "# Each tuple in the directions is: (angle to turn, distance to go)\n", + "directions = [\n", + " (0, 10),\n", + " (90, 20),\n", + " (0, 40),\n", + " (270, 10)\n", + "]\n", + "\n", + "for color, (angle, distance) in zip(colors, directions):\n", + " print(f\"Move {distance} units in direction {angle} degrees and color {color}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **What if the Lists are Different Lengths?**\n", + "\n", + "An important thing to know about `zip()` is that it stops when the *shortest* list runs out of items. Let's see what happens:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# zip() with different length lists\n", + "\n", + "short_list = ['a', 'b', 'c']\n", + "long_list = [1, 2, 3, 4, 5, 6, 7]\n", + "\n", + "for letter, number in zip(short_list, long_list):\n", + " print(f\"{letter} -> {number}\")\n", + " \n", + "print(\"\\nNotice that we only got 3 pairs, even though long_list has 7 items!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **zip_longest() for when you need all items**\n", + "\n", + "If you want to keep going until the *longest* list is exhausted, use `zip_longest()` from the `itertools` module. For lists that run out early, it fills in with `None` (or a value you specify)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# zip_longest() Example\n", + "\n", + "from itertools import zip_longest\n", + "\n", + "short_list = ['a', 'b', 'c']\n", + "long_list = [1, 2, 3, 4, 5, 6, 7]\n", + "\n", + "print(\"Using zip_longest with default fillvalue (None):\")\n", + "for letter, number in zip_longest(short_list, long_list):\n", + " print(f\"{letter} -> {number}\")\n", + "\n", + "print(\"\\nUsing zip_longest with a custom fillvalue:\")\n", + "for letter, number in zip_longest(short_list, long_list, fillvalue='???'):\n", + " print(f\"{letter} -> {number}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Creating Dictionaries with zip()**\n", + "\n", + "One really common use of `zip()` is to create dictionaries by pairing keys with values:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "# Run Me!\n\n# Create a dictionary with zip()\n\nnames = ['Alice', 'Bob', 'Charlie', 'Diana']\nages = [25, 30, 35, 28]\ncities = ['New York', 'London', 'Tokyo', 'Paris']\n\n# Create a dictionary mapping names to ages\nage_dict = dict(zip(names, ages))\nprint(\"Age Dictionary:\", age_dict)\n\n# Create a dictionary mapping names to cities\ncity_dict = dict(zip(names, cities))\nprint(\"City Dictionary:\", city_dict)\n\n# You can even zip three lists to create a more complex structure\npeople = list(zip(names, ages, cities))\nprint(\"\\nPeople list (tuples):\", people)\n\n# Or build a dictionary with tuple values by looping\npeople_dict = {}\nfor name, age, city in zip(names, ages, cities):\n people_dict[name] = (age, city)\nprint(\"\\nPeople dictionary:\", people_dict)" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **islice()**\n", + "\n", + "The `islice()` function works like slice notation on a list—it lets you decide where to start and stop iteration. For instance, if you want to take only the first 10 items of an iterable, you could use `islice(l, 10)`.\n", + "\n", + "For a normal list, you could just do this with `l[:10]`, so why would you need `islice()`?\n", + "\n", + "The reason is that a list has a finite number of items, but an iterator can go on forever. Like `range()`, an iterator doesn't need to store all the data (it can generate it on-the-fly), so you need something more flexible than simple list slicing." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# islice() Example\n", + "\n", + "from itertools import islice # We'll need this!\n", + "\n", + "my_num = 1_000_000\n", + "my_range = range(my_num)\n", + "\n", + "# islice(iterator, stop) or\n", + "# islice(iterator, start, stop, step)\n", + "my_list = list(islice(my_range, my_num-5, my_num))\n", + "print(my_list)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **A Slice of Infinite Possibilities**\n", + "\n", + "Now that we understand how iterators work, let's explore how to control them more precisely. What if you don't want to iterate through an entire sequence, but only take a specific portion of it? The `islice()` function gives you that power—it's like slice notation (`[start:stop:step]`) but designed specifically for iterators." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Demonstrate islice()\n", + "\n", + "from itertools import islice # Important, but you knew that!\n", + "\n", + "my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n", + "\n", + "for i in islice(my_list, 5): # Stop at 5\n", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **cycle()**\n", + "\n", + "The `cycle()` iterator function repeats its input iterator over and over, infinitely. This is exactly the kind of thing you'll want to use `islice()` for.\n", + "\n", + "For example, what if you had four colors in a list: \n", + "\n", + "```python \n", + "colors = ['red', 'blue', 'black', 'orange']\n", + "```\n", + "\n", + "But you wanted to use these colors for a hexagon? You'd run out of colors after four sides! \n", + "\n", + "The `cycle()` iterator makes a list repeat infinitely. However, we don't want it to run forever—we want it to cycle through enough times for our hexagon. We can combine `islice()` and `cycle()` to solve this problem:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Use cycle and islice\n", + "\n", + "from itertools import cycle, islice # You will need both for this!\n", + "\n", + "colors = ['red', 'blue', 'black', 'orange']\n", + "\n", + "for color in islice(cycle(colors), 25):\n", + " print(color, end=' ')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **More Useful Iterator Functions**\n", + "\n", + "Python's `itertools` module has many more helpful functions. Let's look at a few more that you might find useful!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **chain(): Combine Multiple Iterables**\n", + "\n", + "`chain()` lets you iterate through multiple lists (or other iterables) one after another, as if they were one big list." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# chain() Example\n", + "\n", + "from itertools import chain\n", + "\n", + "list1 = [1, 2, 3]\n", + "list2 = ['a', 'b', 'c']\n", + "list3 = [100, 200]\n", + "\n", + "# Iterate through all lists as if they were one\n", + "for item in chain(list1, list2, list3):\n", + " print(item, end=' ')\n", + " \n", + "print(\"\\n\\nThis is much cleaner than writing:\")\n", + "print(\"for item in list1 + list2 + list3:\")\n", + "print(\" (which creates a whole new combined list in memory!)\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **repeat(): Repeat a Value**\n", + "\n", + "`repeat()` creates an iterator that returns the same value over and over. You can specify how many times, or let it go forever (use with `islice()` in that case!)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# repeat() Example\n", + "\n", + "from itertools import repeat\n", + "\n", + "# Repeat a value 5 times\n", + "for x in repeat('Hello', 5):\n", + " print(x, end=' ')\n", + "\n", + "print(\"\\n\\n--- Practical use: Create default values ---\")\n", + "# Create a list with 10 zeros\n", + "zeros = list(repeat(0, 10))\n", + "print(\"List of zeros:\", zeros)\n", + "\n", + "# Create a list with 5 empty lists\n", + "lists = list(repeat([], 5))\n", + "print(\"List of lists:\", lists)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Combining Multiple Iterator Functions**\n", + "\n", + "The real power comes from combining these functions together! Let's look at some practical examples:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Combining iterator functions\n", + "\n", + "from itertools import cycle, islice, chain\n", + "\n", + "# Example 1: Create a pattern by cycling through colors\n", + "colors = ['red', 'blue']\n", + "for i, color in enumerate(islice(cycle(colors), 10), start=1):\n", + " print(f\"Item {i}: {color}\")\n", + "\n", + "print(\"\\n--- Example 2: Alternate between two lists ---\")\n", + "letters = ['A', 'B', 'C']\n", + "numbers = [1, 2, 3]\n", + "\n", + "# Alternate between letters and numbers\n", + "for item in islice(chain.from_iterable(zip(letters, numbers)), 6):\n", + " print(item, end=' ')\n", + "\n", + "print(\"\\n\\n--- Example 3: Create a checkerboard pattern ---\")\n", + "# Using enumerate with cycle to create alternating rows\n", + "for row_num in range(5):\n", + " pattern = cycle(['■', '□']) if row_num % 2 == 0 else cycle(['□', '■'])\n", + " row = ''.join(islice(pattern, 8))\n", + " print(row)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Tips and Best Practices**\n", + "\n", + "Here are some important things to remember when using iterator functions:\n", + "\n", + "### **When to Use Each Function**\n", + "\n", + "| Function | Best Used For |\n", + "|----------|---------------|\n", + "| `enumerate()` | When you need both the index and the value while iterating |\n", + "| `zip()` | Pairing up items from multiple lists, creating dictionaries from separate key/value lists |\n", + "| `zip_longest()` | When lists have different lengths and you need all items |\n", + "| `cycle()` | Repeating a pattern infinitely (always use with `islice()` or a break condition!) |\n", + "| `islice()` | Taking a portion of an iterator, especially infinite ones |\n", + "| `chain()` | Iterating through multiple lists sequentially without creating a new combined list |\n", + "| `repeat()` | Creating default values or repeating the same value multiple times |\n", + "\n", + "### **Common Mistakes to Avoid**\n", + "\n", + "1. **Forgetting to import from itertools**: Functions like `cycle()`, `islice()`, `chain()`, and `zip_longest()` need to be imported!\n", + "2. **Using `cycle()` without a stop condition**: It will loop forever!\n", + "3. **Assuming `zip()` includes all items**: It stops at the shortest list.\n", + "4. **Creating huge lists in memory**: Use iterators instead when working with large datasets." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Challenge**\n", + "\n", + "Create a roster that assigns 10 players to 4 different teams (Red, Blue, Black, Orange). Use iteration functions to pair each player with a team, cycling through the teams since there are more players than teams.\n", + "\n", + "### **Requirements**\n", + "- Use `cycle()` to repeat through the teams since you have more players than teams\n", + "- Use `enumerate()` to number each player assignment\n", + "- Use `zip()` to pair each player with a team\n", + "- Print the output in a nice format showing each player's number, name, and team\n", + "\n", + "> **Hint:** Remember to import the necessary functions from the `itertools` module, and use f-strings to format your output nicely if you want to." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Challenge!\n", + "\n", + "# You are probably going to want to import some things...\n", + "from itertools import cycle\n", + "\n", + "# Data for our teams and players\n", + "teams = ['Red Team', 'Blue Team', 'Black Team', 'Orange Team']\n", + "players = ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve', 'Frank', 'Grace', 'Henry', 'Iris', 'Jack']\n", + "\n", + "# Your code here!\n", + "# Hint: Use enumerate() for numbering, zip() for pairing, and cycle() for repeating teams\n", + "\n", + "\n", + "# Bonus Challenges (Try these after completing the main challenge!)\n", + "# 1. Create a dictionary that maps each player to their assigned team.\n", + "# 2. Assign skill levels to each player (e.g., Beginner, Intermediate, Advanced) and include that in the output.\n", + "# 3. Create a tournament schedule that ensures each team plays against every other team at least once." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "More Iterables", + "uid": "PrKwTywv" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/30_Loops/130_Iterable_Turtle.ipynb b/lessons/30_Loops/130_Iterable_Turtle.ipynb deleted file mode 100644 index e3ac97e7..00000000 --- a/lessons/30_Loops/130_Iterable_Turtle.ipynb +++ /dev/null @@ -1,46 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "Programmable turtle graphics\n", - "\n", - "Use what you've learned about lists, loop, cycle, slice and zip to draw a pattern\n", - "\"\"\"\n", - "\n", - "t = ... # Create a turtle like in previous programs, like 04_Crazy_Tina.py\n", - "\n", - "colors = ... # Make a list of colors\n", - "\n", - "directions = [ # Create a list of directions and angles\n", - " ( , ),\n", - " ( , ), ... # Add more\n", - "]\n", - "\n", - "# Zip the colors and directions together, then unpack them. THere is a good example of this\n", - "# in 10_More_iterables.ipynb in the discussion of zip()\n", - "\n", - "for ... in zip( ... , ...):\n", - " t.color( ... )\n", - " t.forward( ... )\n", - " t.left( ... )\n", - "\n", - "# Don't forget the special way to end a turtle program. " - ] - } - ], - "metadata": { - "language_info": { - "name": "python" - }, - "syllabus": { - "uid": "vTyp2WhX" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/lessons/30_Loops/130_Iterable_Turtle.py b/lessons/30_Loops/130_Iterable_Turtle.py new file mode 100644 index 00000000..aede29a4 --- /dev/null +++ b/lessons/30_Loops/130_Iterable_Turtle.py @@ -0,0 +1,24 @@ +""" +Programmable turtle graphics + +Use what you've learned about lists, loop, cycle, slice and zip to draw a pattern +""" + +t = ... # Create a turtle like in previous programs, like 40_Crazy_Tina.py + +colors = ... # Make a list of colors + +directions = [ # Create a list of directions and angles + ( ), # Add an (angle, distance) tuple for each line + ( ), +] + +# Zip the colors and directions together, then unpack them. There is a good example of this +# in 120_More_Iterables.ipynb in the discussion of zip() + +for something in zip( ... , ...): + t.color( ... ) + t.forward( ... ) + t.left( ... ) + +# Don't forget the special way to end a turtle program. \ No newline at end of file diff --git a/lessons/30_Loops/140_More_Loops.ipynb b/lessons/30_Loops/140_More_Loops.ipynb index adfc5526..df3c9674 100644 --- a/lessons/30_Loops/140_More_Loops.ipynb +++ b/lessons/30_Loops/140_More_Loops.ipynb @@ -1,240 +1,250 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# More Loops\n", - "\n", - "## While Loops\n", - "\n", - "While loops are similar to for loops. Both repeat the code inside the loop.\n", - "The for loop repeats code for a specified number of times.\n", - "The while loop repeats code for as long as a condition is True.\n", - "\n", - "A while loop looks like this:\n", - "\n", - "```python\n", - "while :\n", - " Code to repeat\n", - "```\n", - "\n", - "Where `condition` is a boolean variable or something that evaluates to a boolean\n", - "(True or False). This is similar to an if or elif statement. Here is an example\n", - "of a while loop:\n", - "\n", - "```python \n", - "s = 0\n", - "while s < 55:\n", - " s = s +1\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Break and Continue\n", - "\n", - "To recap: \n", - "\n", - "* A `for` loop runs until the iterator runs out of items\n", - "* A `while` loop runs until the condition is false. \n", - "\n", - "But there is another way to cause a loop to exit: the `break` key word will\n", - "cause a loop to exit immediately." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# count() is an infinite iterator that returns numbers sequentially, 0,1,2,3 ...\n", - "\n", - "from itertools import count\n", - "\n", - "for i in count():\n", - "\n", - " print(i, end = ' ')\n", - "\n", - " if i >= 5: # break if i is greater than or equal to 5\n", - " print() # print a newline\n", - " break\n", - "\n", - " # Another way to do the same thing\n", - "\n", - "i = 0\n", - "while True:\n", - " \n", - " print(i, end = ' ')\n", - " \n", - " if i >= 5:\n", - " break\n", - "\n", - " i = i + 1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Sometimes you want to skip the rest of the loop and go back to the start, which you can do with `continue`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from itertools import count\n", - "\n", - "for i in count():\n", - "\n", - " if i % 3 == 0: # skip printing the number if it is divisible by 3\n", - " continue \n", - "\n", - " print(i, end = ' ')\n", - "\n", - " if i >= 10: # break if i is greater than or equal to 5\n", - " print() # print a newline\n", - " break" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Python has another loop feature that most languages do not have, the else clause\n", - "in a loop. The else clause is executed when the loop terminates normally (the\n", - "condition is false). It is not executed if the loop is terminated by a break\n", - "statement.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Complete the loop and run the else block\n", - "\n", - "for i in range(10):\n", - " print(i, end = ' ')\n", - "else:\n", - " print('else block') \n", - "\n", - "# Break and do not execute the else block\n", - "for i in range(10):\n", - " print(i, end = ' ')\n", - "\n", - " if i > 5:\n", - " print('breaking, skip the else')\n", - " break\n", - "\n", - "else:\n", - " print('else block') " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is a method of finding prime numbers, from the [Python documentation](https://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops). " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for n in range(2, 15):\n", - " for x in range(2, n):\n", - " if n % x == 0:\n", - " print(n, 'equals', x, '*', n//x)\n", - " break\n", - " else:\n", - " # loop fell through without finding a factor\n", - " print(n, 'is a prime number')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For a `while` block, the else block is executed when then condition is met. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Exit the loop when the condition is met, and run the else block\n", - "i = 0\n", - "while i < 5:\n", - " \n", - " print(i, end = ' ')\n", - "\n", - " i = i + 1\n", - "\n", - "else:\n", - " print('else block')\n", - "\n", - "# Exit with break, and do not execute the else block\n", - "i = 0\n", - "while True:\n", - " \n", - " print(i, end = ' ')\n", - " if i >= 5:\n", - " print('breaking, skip the else')\n", - " break\n", - "\n", - " i = i + 1\n", - "\n", - "else:\n", - " print('else block')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It's actually very rare to need an `else` block on a `for` or `while` loop, but you will use `break` and `continue` a lot. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - }, - "syllabus": { - "uid": "RMSFNtMb" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **More Loops**\n", + "\n", + "### **While Loops**\n", + "\n", + "While loops are similar to `for` loops. Both repeat the code inside the loop.\n", + "\n", + "* The `for` loop repeats code for a specified number of times.\n", + "* The `while` loop repeats code for as long as a condition is `True`.\n", + "\n", + "A `while` loop looks like this:\n", + "\n", + "```python\n", + "while :\n", + " # Code to repeat\n", + "```\n", + "\n", + "Where `` is a boolean variable or something that evaluates to a boolean (`True` or `False`). This is similar to an `if` or `elif` statement. \n", + "\n", + "Here is an example of a `while` loop:\n", + "\n", + "```python \n", + "s = 0\n", + "while s < 55:\n", + " s = s + 1\n", + "```" + ] }, - "nbformat": 4, - "nbformat_minor": 2 -} + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Break and Continue**\n", + "\n", + "To recap: \n", + "\n", + "* A `for` loop runs until the iterator runs out of items\n", + "* A `while` loop runs until the condition is `False`\n", + "\n", + "But there is another way to cause a loop to exit: the `break` keyword will cause a loop to exit immediately." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# count() is an infinite iterator that returns numbers sequentially: 0, 1, 2, 3...\n", + "\n", + "from itertools import count\n", + "\n", + "for i in count():\n", + " print(i, end=' ')\n", + "\n", + " if i >= 5: # Break if i is greater than or equal to 5\n", + " print() # Print a newline\n", + " break\n", + "\n", + "# Another way to do the same thing\n", + "\n", + "i = 0\n", + "while True:\n", + " print(i, end=' ')\n", + " \n", + " if i >= 5:\n", + " break\n", + "\n", + " i = i + 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **The Continue Statement**\n", + "\n", + "Sometimes you want to skip the rest of the loop and go back to the start, which you can do with `continue`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "from itertools import count\n", + "\n", + "for i in count():\n", + "\n", + " if i % 3 == 0: # Skip printing the number if it is divisible by 3\n", + " continue \n", + "\n", + " print(i, end=' ')\n", + "\n", + " if i >= 10: # Break if i is greater than or equal to 10\n", + " print() # Print a newline\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **The Else Clause in Loops**\n", + "\n", + "Python has another loop feature that most languages do not have: the `else` clause in a loop. \n", + "\n", + "The `else` clause is executed when the loop terminates normally (the condition is `False`). It is **not** executed if the loop is terminated by a `break` statement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Complete the loop and run the else block\n", + "for i in range(10):\n", + " print(i, end=' ')\n", + "else:\n", + " print('else block') \n", + "\n", + "# Break and do not execute the else block\n", + "for i in range(10):\n", + " print(i, end=' ')\n", + "\n", + " if i > 5:\n", + " print('breaking, skip the else')\n", + " break\n", + "else:\n", + " print('else block')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Finding Prime Numbers**\n", + "\n", + "Here is a method of finding prime numbers:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "for n in range(2, 15):\n", + " for x in range(2, n):\n", + " if n % x == 0:\n", + " print(n, 'equals', x, '*', n//x)\n", + " break\n", + " else:\n", + " # Loop fell through without finding a factor\n", + " print(n, 'is a prime number')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ">**Note:** If you want to learn more about using prime numbers in Python, refer to the [Python documentation](https://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Else Clause with While Loops**\n", + "\n", + "For a `while` block, the `else` block is executed when the condition becomes `False`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Exit the loop when the condition is met, and run the else block\n", + "i = 0\n", + "while i < 5:\n", + " print(i, end=' ')\n", + " i = i + 1\n", + "else:\n", + " print('else block')\n", + "\n", + "# Exit with break, and do not execute the else block\n", + "i = 0\n", + "while True:\n", + " print(i, end=' ')\n", + " \n", + " if i >= 5:\n", + " print('breaking, skip the else')\n", + " break\n", + "\n", + " i = i + 1\n", + "else:\n", + " print('else block')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "> **Note:** It's actually very rare to need an `else` block on a `for` or `while` loop, but you will use `break` and `continue` frequently." + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "More Loops", + "uid": "RMSFNtMb" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/30_Loops/150_Number_Guess.py b/lessons/30_Loops/150_Number_Guess.py index 93fdd3ca..07050694 100644 --- a/lessons/30_Loops/150_Number_Guess.py +++ b/lessons/30_Loops/150_Number_Guess.py @@ -1,4 +1,4 @@ -""" +""" Number Guess Game Pick a random number between 1 and 100. If the random number is divisible by 7, @@ -26,8 +26,12 @@ Use the ask_integer function to get the user's guess, like this: guess = ask_integer("Guess a number between 1 and 100: ") -Note: The prompts and output for your program will be in the teminal -at the bottom of the editor screen; this program does not use the GUI. +The prompts will appear in the terminal at the bottom of the editor window; +this program does not use a GUI. + +Note: The prompts and output for your program will be in the terminal. +uid: rZO9PHOt +name: Number Guess """ import random diff --git a/lessons/30_Loops/160_Extras.ipynb b/lessons/30_Loops/160_Extras.ipynb index 0c0ea2f0..f9f81f3c 100644 --- a/lessons/30_Loops/160_Extras.ipynb +++ b/lessons/30_Loops/160_Extras.ipynb @@ -1,91 +1,120 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Extras\n", - "\n", - "Here are some interesting things related to the lessons, very advanced and completely optional. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Yet More FizzBuzz\n", - "\n", - "It is possible to write a version of FizzBuzz that is even shorter than the one we wrote in the previous lesson. This version uses a list comprehension to generate the list of strings. Here is the code:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fb = ['fizz'*(i%3==0) + 'buzz'*(i%5==0) or str(i) for i in range(1,101)]\n", - "print(fb)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This version is very peculiar because it uses $n^4 \\mod 15$. See this [page](http://philcrissman.net/posts/eulers-fizzbuzz/) for an explanation." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fb = [(lambda n: { 1: n, 6: \"Fizz\", 10: \"Buzz\", 0: \"FizzBuzz\" }[n**4%15])(n) for n in range(1,101)]\n", - "print(fb)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(list(set(\"Once upon a time a 👦 with a 🐕 met a 👧 who had a 🐈 and they went to the park to play with a ⚽.\".split())))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(list()))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - }, - "syllabus": { - "uid": "VJSgvOr5" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Extras**\n", + "\n", + "Here are some interesting concepts related to the loop lessons. They are advanced, completely optional, and great for exploration after you finish the core loop material." + ] }, - "nbformat": 4, - "nbformat_minor": 2 + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Yet More FizzBuzz**\n", + "\n", + "Here is a shorter take on FizzBuzz using a single list comprehension system.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# List comprehension version of FizzBuzz\n", + "fb = ['fizz'*(i % 3 == 0) + 'buzz'*(i % 5 == 0) or str(i) for i in range(1, 101)]\n", + "print(fb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Euler's FizzBuzz**\n", + "\n", + "This version relies on modular arithmetic and uses $n^4$ $\\%$ $15$ to choose what to print.\n", + "\n", + "The remainder then feeds a small lookup table as shown below:\n", + "\n", + "- `1` → the number itself\n", + "- `6` → `Fizz`\n", + "- `10` → `Buzz`\n", + "- `0` → `FizzBuzz`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Euler-style mapping from remainder (n^4 % 15) to the correct label\n", + "fb = [(lambda n: {1: n, 6: \"Fizz\", 10: \"Buzz\", 0: \"FizzBuzz\"}[n ** 4 % 15])(n) for n in range(1, 101)]\n", + "print(fb)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Sets eliminate duplicates; emojis stay intact\n", + "print(list(set(\"Once upon a time a 👦 with a 🐕 met a 👧 who had a 🐈 and they went to the park to play with a ⚽.\".split())))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# An empty list literal\n", + "print(list())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> **Note:** Curious how that remainder trick works? Check out the Euler's FizzBuzz explanation [here](http://philcrissman.net/posts/eulers-fizzbuzz/)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Extras", + "uid": "VJSgvOr5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 } diff --git a/lessons/30_Loops/20_Loops_with_Range.ipynb b/lessons/30_Loops/20_Loops_with_Range.ipynb new file mode 100644 index 00000000..70073175 --- /dev/null +++ b/lessons/30_Loops/20_Loops_with_Range.ipynb @@ -0,0 +1,200 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Mastering the Range Object**\n", + "\n", + "We have used `range()` frequently, but now it is time to explore how it works in more detail.\n", + "\n", + "The `range()` function generates a sequence of numbers. It is an iterable, similar to a list or a string, which means you can loop through it.\n", + "\n", + "```python\n", + "for i in range(10):\n", + " print(i)\n", + "```\n", + "\n", + "However, unlike a list, `range()` is *memory efficient*. It does not store every number in memory, and instead, it calculates them on the fly as needed. \n", + "\n", + "Let's see what happens when we inspect a range object directly:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "range(0, 10)\n" + ] + } + ], + "source": [ + "# Run Me!\n", + "\n", + "a = range(10)\n", + "\n", + "print(type(a))\n", + "print(a)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Efficiency of Range**\n", + "\n", + "Although `range()` is designed for efficiency, attempting to store every number in a massive range like `range(1_000_000_000_000)` might crash your computer's memory.\n", + "\n", + "By generating numbers only when requested, `range()` stays lightweight regardless of size.\n", + "\n", + "To visualize the numbers a range produces (recommended for small ranges only), you can convert it to a list:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" + ] + } + ], + "source": [ + "# Run Me!\n", + "\n", + "print(list(range(10)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will cover `list()` in detail later. For now, use it as a tool to peek inside our ranges.\n", + "\n", + "### **Customizing Ranges**\n", + "\n", + "The `range()` function is versatile and accepts up to three arguments:\n", + "\n", + "| Syntax | Description |\n", + "| :--- | :--- |\n", + "| `range(stop)` | Generates numbers from `0` up to (but excluding) `stop`. |\n", + "| `range(start, stop)` | Generates numbers from `start` up to (but excluding) `stop`. |\n", + "| `range(start, stop, step)` | Generates numbers from `start` up to `stop`, incrementing by `step`. |\n", + "\n", + "For example, to generate odd numbers from 101 to 120:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[101, 103, 105, 107, 109, 111, 113, 115, 117, 119]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Run Me!\n", + "\n", + "list(range(101, 120, 2)) # Start at 101, add 2 at each step, and stop before reaching 120." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> **Note:** `range()` never includes the stop value itself. It always stops *before* reaching that number." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Challenge**\n", + "\n", + "Let's print all the *odd years* between your birth year and the current year." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Ellipsis" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Test yourself\n", + "\n", + "..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "\n### **Challenge**\n\nThese [Badgers](https://youtu.be/pzagBTcYsYQ?si=xr4QQ7ZkZBGow2j1) are fans of the classic FizzBuzz Programming Challenge, but instead, they have their own rules.\n\n\"Watch\n\n#### **Badger Badger Mushroom Rules:**\n\nIn this game, you will loop through the numbers from 1 to 30.\n\n| Condition | Print |\n| :------- | :----- |\n| Divisible by both 3 and 5 | 🐍 snake! |\n| Divisible by 5 (but not 3) | 🦡 badger |\n| Divisible by 3 (but not 5) | 🍄 mushroom |\n| Not divisible by 3 or 5 | The number itself |\n\n
\n\n**Bonus Challenge:** Can you solve this without using the `or` operator?" + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Badgers!\n", + "\n", + "for i in range(1, 31):\n", + " ..." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python-Apprentice (3.13.3)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.3" + }, + "syllabus": { + "name": "Loops With Range", + "uid": "WcGpR3Xg" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/30_Loops/30_Looping_Through_Lists.ipynb b/lessons/30_Loops/30_Looping_Through_Lists.ipynb new file mode 100644 index 00000000..58985469 --- /dev/null +++ b/lessons/30_Loops/30_Looping_Through_Lists.ipynb @@ -0,0 +1,418 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5a4acb70", + "metadata": {}, + "source": "# **Looping Through Lists**\n\nWe've introduced lists in a [previous lesson](../10_Turtles/80_Introducing_Lists/10_Lists.ipynb), but now, let's explore their power when combined with loops.\n\nAs a quick refresher, remember that a list is simply an ordered collection of items, and you can think of them like a backpack that holds various items.\n\nFor example, consider a list representing the contents of a backpack:\n\nBackpack Contents:\n- `map`\n- `flashlight`\n- `water bottle`\n- `snack`\n\nIn Python, we can represent this backpack as a list:" + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0e2837fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "backpack = ['map', 'flashlight', 'water bottle', 'snack']\n", + "backpack # Display the contents of the backpack" + ] + }, + { + "cell_type": "markdown", + "id": "31dcc11b", + "metadata": {}, + "source": [ + "### **Exploring Lists**\n", + "\n", + "There are a lot of neat things we can do with a list, like accessing a specific item using `[ ]` (with an index number)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9280847b", + "metadata": { + "lines_to_next_cell": 0 + }, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Indexing a list\n", + "backpack = ['map', 'flashlight', 'water bottle', 'snack']\n", + "\n", + "item = backpack[0] # Change the number in the [] to get different items.\n", + "print(item)" + ] + }, + { + "cell_type": "markdown", + "id": "c07ad6f5", + "metadata": {}, + "source": [ + "Getting a specific value from a list is called indexing.\n", + "\n", + "Since indexes start at `0` instead of `1`, to get the first item (`'map'`) from our list, you use `backpack[0]` rather than `backpack[1]`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6deea034", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "backpack = ['map', 'flashlight', 'water bottle', 'snack']\n", + "\n", + "print(\"This backpack contains a \" + backpack[0] + \", \" + backpack[1] + \", \" + backpack[2] + \", and \" + backpack[3] + \".\")" + ] + }, + { + "cell_type": "markdown", + "id": "a84799ac", + "metadata": {}, + "source": [ + "Lists are iterable, meaning we can loop through them item by item.\n", + "\n", + "To print all items in the list we can use a `for` loop:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "183089f2", + "metadata": { + "lines_to_next_cell": 2, + "title": "[python]" + }, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "backpack = ['map', 'flashlight', 'water', 'snack']\n", + " \n", + "for item in backpack:\n", + " print(item)" + ] + }, + { + "cell_type": "markdown", + "id": "bc85d21a", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "### **Iterating over Iterables**\n", + "\n", + "Lists and strings share many similarities in Python. Both are **iterables**.\n", + "\n", + "Iteration means processing items one by one. Below, we iterate over a list and then a string:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15a2f38b", + "metadata": { + "title": "[python]" + }, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "backpack = ['map', 'flashlight', 'water', 'snack']\n", + " \n", + "print(\"Backpack contents:\")\n", + "\n", + "for item in backpack:\n", + " print(item)\n", + " \n", + "print()\n", + "for i in 'Hello World':\n", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "id": "65625cd0", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "### **Iterables**\n", + "\n", + "A `for` loop assigns each item in an iterable to a variable and runs the loop body.\n", + "\n", + "`range()` is also an iterable, but it generates numbers on the fly instead of holding data. We can convert a range into a list:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9e90bab0", + "metadata": {}, + "outputs": [], + "source": [ + "# Turn a range() into a list:\n", + "\n", + "my_list = list(range(5, 10))" + ] + }, + { + "cell_type": "markdown", + "id": "46f3d3a6", + "metadata": {}, + "source": [ + "`list()` collects items from an iterable into a new list. For example, converting a string into a list of characters:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d63d5efe", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Turn a string into a list:\n", + "\n", + "my_list = list(\"Hello World!\")\n", + "print(my_list)\n", + "\n", + "# That code above works the same as this code below:\n", + "my_list = list()\n", + "for c in \"Hello World!\":\n", + " my_list.append(c) # Adding to a list, more on this later\n", + "\n", + "print(my_list)" + ] + }, + { + "cell_type": "markdown", + "id": "d450eafd", + "metadata": {}, + "source": [ + "Alternatively, use `.split()` to break a string into a list at a specific character (default is space)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f82ddae", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# split a string at spaces, the default\n", + "my_split = 'One Two Three Four'\n", + "my_list = my_split.split()\n", + "print(my_list)\n", + "\n", + "# split a string at the comma character\n", + "my_split = 'One,Two,Three,Four'\n", + "my_list = my_split.split(',')\n", + "print(my_list)" + ] + }, + { + "cell_type": "markdown", + "id": "8c4699cb", + "metadata": {}, + "source": [ + "### **Sorting**\n", + "\n", + "Sorting puts items in order. Python offers two ways to sort:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7bd259e4", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "my_list = list('adefibhgc')\n", + "my_list.sort() # This sorts the list in place\n", + "print(my_list)\n", + "print()\n", + "\n", + "my_list = list('adefibhgc')\n", + "sorted_list = sorted(my_list) # This creates a new list & doesn't change the original\n", + "print(sorted_list)" + ] + }, + { + "cell_type": "markdown", + "id": "0578ffed", + "metadata": {}, + "source": [ + "### **Adding To Lists**\n", + "\n", + "Use `.append()` to add items (modifies the list) or `+` to concatenate lists (creates a new list)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6550d5f", + "metadata": { + "title": "[python]" + }, + "outputs": [], + "source": "# Run Me!\n\nmy_list = []\n\n# Add to the list using append\nmy_list.append('item 1')\nmy_list.append('item 2')\nmy_list.append('item 3')\n\n# You can also use = with an empty list to create a new list\nmy_list = my_list + ['item 4', 'item 5']\n\nprint(my_list)" + }, + { + "cell_type": "markdown", + "id": "ee522118", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": "### **Try It**\nAdd more items to the list." + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17a599ca", + "metadata": { + "title": "[python]" + }, + "outputs": [], + "source": "# Run Me!\n\n# Try adding more items to the list (my_list) and printing it out again." + }, + { + "cell_type": "markdown", + "id": "37120164", + "metadata": {}, + "source": [ + "### **Using Lists with Turtle**\n", + "\n", + "We can use loops and lists to control Turtle. For example, changing the color for each side of a square:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2a608ff", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "%run .lib/auto_turtle_concise.py # This just handles the general imports for you\n", + "\n", + "forward = 100\n", + "left = 90\n", + "colors = [ 'red', 'blue', 'black', 'orange']\n", + "\n", + "for color in colors:\n", + " tina.color(color) # type: ignore[name-defined] - defined by auto_turtle_concise.py\n", + " tina.forward(forward) # type: ignore[name-defined] - defined by auto_turtle_concise.py\n", + " tina.left(left) # type: ignore[name-defined] - defined by auto_turtle_concise.py" + ] + }, + { + "cell_type": "markdown", + "id": "21f3bce9", + "metadata": {}, + "source": [ + "Or changing the turn angle:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31189d07", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "%run .lib/auto_turtle_concise.py # This just handles the general imports for you\n", + "\n", + "forward = 100\n", + "\n", + "for left in [ 90, 90, 90, 90 ]:\n", + " tina.forward(forward) # type: ignore[name-defined] - defined by auto_turtle_concise.py\n", + " tina.left(left) # type: ignore[name-defined] - defined by auto_turtle_concise.py" + ] + }, + { + "cell_type": "markdown", + "id": "301f3495", + "metadata": {}, + "source": [ + "#### **Parallel Iteration**\n", + "\n", + "We can use list indexes to update multiple variables in a single loop. This is called parallel iteration, where a shared index `i` accesses corresponding items from different lists." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "085f3175", + "metadata": {}, + "outputs": [], + "source": "# Run Me!\n\n%run .lib/auto_turtle_concise.py # This just handles the general imports for you\n\nforward = 100\nlefts = [ 90, 90, 90, 90, 90, 90, 90, 90 ]\ncolors = [ 'red', 'blue', 'black', 'orange', 'purple', 'pink', 'cyan', 'green']\n\nfor i in range(8):\n left = lefts[i]\n color = colors[i]\n\n tina.color(color) # type: ignore[name-defined] - defined by auto_turtle_concise.py\n tina.forward(forward) # type: ignore[name-defined] - defined by auto_turtle_concise.py\n tina.left(left) # type: ignore[name-defined] - defined by auto_turtle_concise.py" + }, + { + "cell_type": "markdown", + "id": "c8f4a193", + "metadata": {}, + "source": "### **Challenge**\n\nYou can complete the challenge below by following these simple steps:\n- Start with a string of friend names separated by spaces (e.g., like `'Sarah Alice Michael'`), and `.split()` it into a list.\n- Ask the user for a new friend's name *3* times and add each name to the list.\n- `.sort()` the list alphabetically to organize them.\n- Print each name onto a new line (`\\n`) using a loop statement." + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c51bbf8", + "metadata": { + "title": "[python]" + }, + "outputs": [], + "source": [ + "# Test yourself\n", + "\n", + "# Here is how to get a name (or input) from the user.\n", + "# name = input(\"What is your friend's name?\")\n", + "# Look at the top of the window for the prompt." + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "title,-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + }, + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Looping Through Lists", + "uid": "7zsKa84X" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/lessons/30_Loops/040_Crazy_Tina.py b/lessons/30_Loops/40_Crazy_Tina.py similarity index 80% rename from lessons/30_Loops/040_Crazy_Tina.py rename to lessons/30_Loops/40_Crazy_Tina.py index 0ca78c00..505ab475 100644 --- a/lessons/30_Loops/040_Crazy_Tina.py +++ b/lessons/30_Loops/40_Crazy_Tina.py @@ -5,24 +5,27 @@ she will turn, and the colors she will use. The access those lists to draw the pattern. -hint: all of your lists should have the same number of elements. Review the ' Using Lists' section of the previous lesson if you need more help + +hint: all of your lists should have the same number of elements. +uid: JBzJ2nx1 +name: Crazy Tina """ import turtle # Tell Python we want to work with the turtle -turtle.setup(600,600,0,0) # Set the size of the window +turtle.setup(600, 600, 0, 0) # Set the size of the window tina = turtle.Turtle() # Create a turtle named tina tina.shape('turtle') # Set the shape of the turtle to a turtle -tina.speed(2) # Make the turtle move as fast, but not too fast. +tina.speed(2) # Move at a moderate speed, not too fast. forwards = [ ... ] lefts = [ ... ] colors = [ ... ] -for i in range(8): +for i in range(8): forward = ... left = ... diff --git a/lessons/30_Loops/50_Tuples.ipynb b/lessons/30_Loops/50_Tuples.ipynb new file mode 100644 index 00000000..a6a31771 --- /dev/null +++ b/lessons/30_Loops/50_Tuples.ipynb @@ -0,0 +1,190 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": "# **Tuples — Lists You Can't Change**\n\nTuples are a type of sequence and collection, much like lists, but with one key difference: once created, they cannot be modified. This property is known as being **immutable**. While it might seem restrictive, immutability makes tuples efficient and useful in many scenarios.\n\nYou can create a tuple just like a list, but you use parentheses `()` instead of square brackets `[]`." + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "my_list = [1, 2, 3, 4, 5] # List\n", + "print(my_list)\n", + "\n", + "my_tuple = (1, 2, 3, 4, 5) # Tuple\n", + "print(my_tuple)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **The Comma Rule**\n", + "\n", + "One key difference between a list and a tuple is that when creating a tuple with a *single item*, you must include a comma inside the parentheses. Without this comma, Python will not recognize it as a tuple.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "my_list = [1] # List\n", + "my_tuple = (1,) # Tuple\n", + "not_a_tuple = (1) # Not a Tuple\n", + "\n", + "print('List', my_list, type(my_list))\n", + "print('Tuple', my_tuple, type(my_tuple))\n", + "print('Not a Tuple', not_a_tuple, type(not_a_tuple))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> **Note:** This is because parentheses are also used for grouping mathematical expressions and Python needs the comma to distinguish between a tuple and a value inside parentheses." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Immutability**\n", + "\n", + "As stated before, once a tuple is created, its contents cannot be altered. Run the cell below and see what happens when you try to change an item in a tuple." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me \n", + "\n", + "my_tuple = (1, 2, 3) # Tuple\n", + "my_tuple[0] = 10 # This will raise a TypeError" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code produced a TypeError because tuples are immutable, meaning their contents cannot be changed after creation.\n", + "\n", + ">**Note:** Immutability is a key difference between tuples and lists. Lists are mutable, so their elements can be changed. If you need to modify a tuple, you must first convert it to a list." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Accessing Tuple Elements**\n", + "\n", + "Many of the same operations can be performed on tuples and lists, as both support indexing, slicing, and share the same syntax. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "my_tuple = (1, 2, 3, 4, 5) # Tuple\n", + "\n", + "print(my_tuple[0]) # Accessing elements in a tuple\n", + "print(my_tuple[2:4]) # Slicing a tuple" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Tuple Unpacking**\n", + "A useful feature of tuples is tuple unpacking, which allows you to assign each value in a tuple to a separate variable in a single line of code. This can make your code more concise and readable." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run me!\n", + "\n", + "# Here, we unpack the tuple into three variables\n", + "a, b, c = (10, 20, 30)\n", + "\n", + "# Now if we print using variables, we can unpack the values\n", + "print(\"a:\", a)\n", + "print(\"b:\", b)\n", + "print(\"c:\", c)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Use Cases and Conversions**\n", + "\n", + "So, when should you use a tuple instead of a list? \n", + "\n", + "Tuples are great for data that you know shouldn't change, like the days of the week or coordinates on a map. They can also be used as keys in a dictionary, whereas lists cannot.\n", + "You can easily convert between lists and tuples using the `list()` and `tuple()` functions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run me!\n", + "\n", + "my_list = [1, 2, 3] # List\n", + "print(\"Original List:\", my_list)\n", + "\n", + "my_tuple = tuple(my_list) # Convert list to tuple\n", + "print(\"Converted to Tuple:\", my_tuple)\n", + "\n", + "converted_list = list(my_tuple) # Convert tuple back to list\n", + "print(\"Converted back to List:\", converted_list)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Tuples", + "uid": "9zTQza2e" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/30_Loops/60_Indexing_and_Slicing.ipynb b/lessons/30_Loops/60_Indexing_and_Slicing.ipynb new file mode 100644 index 00000000..7af11dfb --- /dev/null +++ b/lessons/30_Loops/60_Indexing_and_Slicing.ipynb @@ -0,0 +1,277 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Indexing and Slicing**\n", + "\n", + "Lists and strings are sequences of items, which means you can retrieve individual items by their position (indexing) or extract ranges of items (slicing).\n", + "\n", + "### **Indexing**\n", + "\n", + "Indexing is how we grab a single item from a list or string by referring to its position, or index.\n", + "\n", + "In Python, counting starts at `0`, so the first item is at index `0`, the second is at index `1`, and so on. You can also count backwards from the end using negative numbers, where `-1` is the last item." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Indexing\n", + "\n", + "colors = [ 'red', 'blue', 'black', 'orange'] # define a list of colors\n", + "\n", + "print(colors[0]) # print the first item\n", + "print(colors[1]) # print the second item \n", + "print(colors[-1]) # print the last item. Negative numbers are counted from the end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Slicing**\n", + "\n", + "A slice lets you grab a whole section of items at once using square brackets with colons to define a range: `[start:stop:step]`.\n", + "\n", + "* **start**: The index where the slice begins (inclusive).\n", + "* **stop**: The index where the slice ends (exclusive—it stops *before* this item).\n", + "* **step**: (Optional) How many items to skip. Defaults to `1`.\n", + "\n", + "You can leave out any of these parts, and Python will use defaults (start from the beginning, go to the end, step by 1)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Visualizing Positive Indices**\n", + "\n", + "Think of indices as pointing *between* items rather than at them, which helps explain why slices include the start but stop right before the end.\n", + "\n", + "For example, consider the list below:\n", + "\n", + "```text\n", + "┌────────┬────────┬─────────┬────────┐\n", + "│ red │ blue │ black │ orange │\n", + "└────────┴────────┴─────────┴────────┘\n", + "0 1 2 3 4\n", + "```\n", + "\n", + "A slice like `[0:2]` returns `['red', 'blue']`, and `[1:3]` returns `['blue', 'black']`.\n", + "\n", + "Let's look at some examples of slicing:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Slicing lists\n", + "\n", + "colors = [ 'red', 'blue', 'black', 'orange'] # define a list of colors\n", + "\n", + "print('[1:3]', colors[1:3]) # print the second to third items, but not including the third item\n", + "print('[:2]', colors[:2]) # print the first two items. Nothing before the ':' means 'from the start'\n", + "print('[2:]', colors[2:]) # print the third and subsequent items. Nothing after the ':' means 'to the end'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here are the common slice patterns:\n", + "\n", + "| Syntax | Description |\n", + "|--------------------|-----------------------------------------------|\n", + "| `my_list[start:stop]` | from `start` to `stop` |\n", + "| `my_list[start:stop:skip]` | from `start` to `stop`, skipping every `skip` |\n", + "| `my_list[start:]` | from `start` to the last |\n", + "| `my_list[:end]` | from the first to `end` |\n", + "| `my_list[::skip]` | from the first to the last, skipping by `skip` |" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "### **Negative Indices**\n\nWe can also use negative indices to count from the end of the list, which is handy when you want the last few items but don't know how long the list is.\n\n#### **Visualizing Negative Indices**\n\nThink of it as counting backwards:\n\n```text\n ┌────────┬────────┬─────────┬────────┐\n │ red │ blue │ black │ orange │\n └────────┴────────┴─────────┴────────┘\n-4 -3 -2 -1\n```\n\nFor example, `[-1]` gives you `'orange'`, and a slice like `[-3:-1]` gives you `['blue', 'black']`." + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Slicing with negative indices\n", + "\n", + "nums = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n", + "\n", + "print('[-3:]', nums[-3:]) # print the last three items\n", + "print('[:-3]', nums[:-3]) # print all but the last three items\n", + "print('[-6:-3]', nums[-6:-3]) # print the fourth to sixth items, but not including the sixth item\n", + "\n", + "# And we can mix negative and positive indices\n", + "\n", + "print('[2:-2]', nums[2:-2]) # print the third to the third last items, but not including the third last item\n", + "print('[-6:8]', nums[-6:8]) # print the fourth to the eighth items, but not including the eighth item" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "### **Skipping and Reversing**\n\nThe third number in a slice is the step, which tells Python how many items to jump over, allowing for skipping items in a sequence (e.g., `[::2]` takes every second item).\n\nWe can also use a negative step for reversing the order; `[::-1]` is the standard way to walk backwards through a list in Python." + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Skipping items\n", + "\n", + "nums = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n", + "\n", + "print('[::2]', nums[::2]) # print every second item\n", + "print('[1::2]', nums[1::2]) # print every second item starting from the second item\n", + "\n", + "# This third argument can also be negative, which means to go backwards\n", + "# This is a common way to reverse a list\n", + "\n", + "print('[::-1]', nums[::-1]) # print the list in reverse order. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Slicing Strings**\n", + "\n", + "Just like lists, strings are sequences of characters, which means you can slice them too! This is incredibly useful for text processing." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "word = \"Python\"\n", + "\n", + "print(word[0:2]) # Get the first two characters\n", + "print(word[2:]) # Get everything from index 2 to the end\n", + "print(word[::-1]) # Reverse the string" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Watch Out For Index Errors**\n", + "\n", + "Indexing is strict and if you try to access an index that doesn't exist, like `my_list[99]`, Python will crash with an IndexError." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "my_list = [1, 2, 3]\n", + "\n", + "print(my_list[10])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> **Note:** Unlike indexing, which raises an error for invalid positions, slicing is forgiving and simply returns any available items or an empty list without crashing." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Challenge**\n", + "\n", + "Fill in the indexing and slicing expressions to produce the requested outputs.\n", + "\n", + "- Indexing: Use a single index to get one item.\n", + "- Slicing: Use `[start:stop]` or `[start:stop:step]` (remember: `stop` is exclusive, default `step` is `1`)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Test Yourself\n", + "\n", + "nums = [10, 20, 30, 40, 50, 60]\n", + "\n", + "# Get the third item (indexing)\n", + "print(...)\n", + "\n", + "# First three items (slicing)\n", + "print(...)\n", + "\n", + "# Last two items\n", + "print(...)\n", + "\n", + "# From the second to the second-to-last item\n", + "print(...)\n", + "\n", + "# Every other item\n", + "print(...)\n", + "\n", + "# Reverse the list\n", + "print(...)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Indexing And Slicing", + "uid": "P27f2L8k" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/30_Loops/070_List_Story.py b/lessons/30_Loops/70_List_Story.py similarity index 92% rename from lessons/30_Loops/070_List_Story.py rename to lessons/30_Loops/70_List_Story.py index 85d9ece2..8e514a98 100644 --- a/lessons/30_Loops/070_List_Story.py +++ b/lessons/30_Loops/70_List_Story.py @@ -3,6 +3,9 @@ Use indexing to get words from the list, then append them to the story + +uid: G8DoR8LV +name: List Story """ words = ['Once', '👦', 'upon', '🐕', 'park', 'met', 'with', 'a', 'the', diff --git a/lessons/30_Loops/80_Strings.ipynb b/lessons/30_Loops/80_Strings.ipynb new file mode 100644 index 00000000..7797ddf7 --- /dev/null +++ b/lessons/30_Loops/80_Strings.ipynb @@ -0,0 +1,567 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Strings**\n", + "\n", + "We've used strings before, but let's look more closely at how you can define them using various quotes, starting with the most common type, double quotes:\n", + "\n", + "```python\n", + "message = \"Hello, World!\"\n", + "```\n", + "\n", + "You can also define strings using either single (`'`), double (`\"`), or triple (`\"\"\"`) quotes, each with their own purpose." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# You can use single quotes if you need to include double quotes inside your string\n", + "single = 'I need a \"Double Quote\" inside my string!'\n", + "\n", + "# Or double quotes if you need to include single quotes inside your string\n", + "double = \"I need a 'Single Quote' inside my string!\" \n", + "\n", + "# And triple quotes if you need a string that spans multiple lines\n", + "triple = \"\"\"“Hope” is the thing with feathers -\n", + "That perches in the soul -\n", + "And sings the tune without the words -\n", + "And never stops - at all -\n", + "\"\"\" \n", + "\n", + "print(single + \"\\n\") # The \\n just prints with a newline for better readability\n", + "print(double + \"\\n\") # You can ignore it for now, we will cover it later\n", + "print(triple)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> **Note:** Using single or double quotes can be mostly interchangeable, but triple quotes are specifically for multi-line strings." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Escaping Characters**\n", + "\n", + "Sometimes you need special characters in your strings, such as newlines `\\n`, tabs `\\t`, or quote marks, like `\"` and `'`, but you don't want Python to misinterpret them as syntax. If you include these directly, the kernel may get confused and think you’re ending the string or making a mistake.\n", + "\n", + "To properly include special characters without confusing the kernel, you must include a backslash `\\` before the character, which is called escaping or quoting." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "message = \"They said \\\"You can use double quotes inside double quotes, if you use a backslash '\\\\' to escape them.\\\"\"\n", + "print(message)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Common Escape Characters**\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\\nNewlineStarts a new line
\\tTabIndents text to the next tab stop
\\\" or \\'Literal QuoteAllows you to use the quote character itself
\n", + "\n", + "Here is an example of using `\\n` and `\\t` to format text." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Tabs and Newlines\n", + "\n", + "my_string = \"Ox\\tCat\\tFrog\\t\\n1\\t2\\t3\\t\"\n", + "print(my_string)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Did you notice how the numbers `1, 2, 3` align perfectly with `Ox, Cat, Frog`, even though the text lengths differ? This is because the tab character `\\t` creates uniform spacing (like columns), and `\\n` starts a new line. Together they help format the output neatly, making them very useful for organizing simple tables or aligned lists." + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Test Yourself\n", + "\n", + "# Fill in the poem string with a short poem of your choice\n", + "poem = \"\"\"\n", + "\"\"\"\n", + "print(poem)\n", + "\n", + "# Fill in the friends string to print your friends' names using \\t and \\n\n", + "friends = \"\"\n", + "print(friends)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Fancy Formatting (f-strings)**\n", + "\n", + "Python offers a modern and powerful way to format strings called f-strings, which are easy to read and very useful.\n", + "\n", + "Here is what they look like:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# f-strings \n", + "\n", + "age = 14\n", + "name = \"John\"\n", + "\n", + "print(f\"{name} is {age} years old.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> **Tip:** These are useful for including variables directly into strings without needing to use concatenation or other formatting methods." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Key Features of f-strings:**\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PrefixThe string must start with the letter f (before the opening quote).
PlaceholdersYou can insert variables or expressions directly into the string by wrapping them in curly braces {}.
\n", + "\n", + "Let's look at some advanced things you can do with f-strings." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# More f-strings\n", + "\n", + "name = \"John\"\n", + "age = 14\n", + "\n", + "# You can put lots of expressions inside the curly braces, not just variable names\n", + "# while name.upper() makes the name uppercase, age + 5 does math inside the string!\n", + "print(f\"{name.upper()} is {age} years old. In 5 years, he will be {age + 5} years old.\") \n", + "\n", + "# You can also control how numbers and text look using a colon : after the variable name.\n", + "# For example, to add commas to large numbers, or round to a certain number of decimal places:\n", + "number = 1234567.890123\n", + "\n", + "print(f\"With a separator: {number:,}\") # Adds commas: 1,234,567.890123\n", + "print(f\"Rounded to 2 places: {number:.2f}\") # Rounds to 2 decimal places: 1234567.89\n", + "\n", + "# You can also set the width of text or numbers for nice alignment.\n", + "# This will make the string appear in a space of 10 characters, padding it with spaces if necessary\n", + "print(f\"{'Hello':10}{'World':10}!\") # 'Hello' and 'World' will each take up 10 spaces" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can combine f-strings and triple quotes for a very powerful method of formatting. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me\n", + "\n", + "# Combining f-strings and Multiline Strings\n", + "\n", + "name = \"John\"\n", + "age = 14\n", + "prize = \"unicorn\"\n", + "team = \"The Prize Draw Team\"\n", + "letter = f\"\"\"Dear {name},\n", + "\n", + "Congratulations! You have won a {prize} in our prize draw. \n", + "You are only {age} years old, so you must be very lucky!\n", + "\n", + "Yours sincerely,\n", + "{team}\n", + "\"\"\"\n", + "\n", + "print(letter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "There is a lot more you can do with f-strings, but this covers the basics.\n\n> **Note:** For more advanced usage, you can refer to the [Official Python Documentation on f-strings](https://docs.python.org/3/tutorial/inputoutput.html#formatted-string-literals)." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Challenge**\n", + "\n", + "Update the loop below to print a formatted line for each number. \n", + "\n", + "You will print the following for each number from 1 to 8:\n", + "\n", + "- An exclamation mark `!` at the start of each line.\n", + "- The loop number, taking up 3 spaces.\n", + "- The string `\"equations\"` followed by a math equation showing the number multiplied by `2`.\n", + "- The last number should take up 5 spaces.\n", + "\n", + "For instance, one of the lines should look like\n", + "\n", + "```python \n", + "! 6 equation: 6 * 2 = 12\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [], + "source": [ + "# Test Yourself\n", + "\n", + "for i in range(1, 9):\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Turning Strings into Lists**\n", + "\n", + "Let's review how to convert strings into lists. If you can remember, there are two main ways to do this.\n", + "\n", + "### **Using `list()`**\n", + "You can convert a string directly into a list of individual characters using the `list()` function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(list(\"Hello World\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Using `.split()`**\n", + "You can split a string into a list of words (or other chunks) using the `.split()` method." + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Python', 'is', 'fun', 'to', 'learn']\n" + ] + } + ], + "source": [ + "# Run Me!\n", + "\n", + "# Split on spaces\n", + "my_string = \"Python is fun to learn\"\n", + "my_list = my_string.split()\n", + "\n", + "print(my_list)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By default, `.split()` separates text wherever it finds *spaces*. \n", + "\n", + "However, you can split by any character (like commas) by passing it inside the parentheses." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Split on commas\n", + "my_string = 'a,b,c,d,e'\n", + "my_list = my_string.split(',')\n", + "\n", + "print(my_list)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Joining Lists back into Strings**\n", + "\n", + "Once you have a list of strings, you can join them back together into a single string using the `.join()` method.\n", + "\n", + "The syntax might look a bit unique: you start with the *separator* string, then call `.join(list)`." + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python is fun to learn\n", + "a,b,c,d,e\n" + ] + } + ], + "source": [ + "# Run Me!\n", + "\n", + "# Join with spaces\n", + "my_list = ['Python', 'is', 'fun', 'to', 'learn']\n", + "my_string = ' '.join(my_list) # Doesn't that look weird?\n", + "\n", + "print(my_string)\n", + "\n", + "# Join with commas\n", + "my_list = ['a', 'b', 'c', 'd', 'e']\n", + "my_string = ','.join(my_list)\n", + "\n", + "print(my_string)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So, if you wanted to change the third word of a sentence, you might do something like this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "my_string = \"Python is fun to learn\"\n", + "my_list = my_string.split() # Turn it into a list of words\n", + "my_list[2] = 'amazing' # Change the third word\n", + "my_string = ' '.join(my_list) # Join the words back together\n", + "\n", + "print(my_string)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you wanted to add a word in the middle you might do:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "my_string = \"Python is fun to learn\"\n", + "my_list = my_string.split() # Turn it into a list of words\n", + "\n", + "first_half = my_list[:2] # Get the first two words\n", + "second_half = my_list[2:] # Get the rest of the words\n", + "\n", + "my_list = first_half + ['really'] + second_half # Add 'really' in the middle, using + to combine lists\n", + "\n", + "my_string = ' '.join(my_list) # Join the words back together\n", + "\n", + "print(my_string)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> **Note:** There are so many string methods available in Python! You can check out the [Official Python Documentation on String Methods](https://docs.python.org/3/library/stdtypes.html#string-methods) for a full list and explanations." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Challenge**\n", + "\n", + "Use your knowledge of strings and lists (including `+`, `.split()`, slicing, and indexing) to assemble and print out the following phrases.\n", + "\n", + "**First, here's an example:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Example\n", + "\n", + "string_one = \"Pythons are constrictors!\"\n", + "string_two = \"John is funny\"\n", + "\n", + "# Print \"Python is fun\" \n", + "\n", + "space = string_one[7]\n", + "message = string_one[:6] + space + string_two[5:-2] + string_one[-1]\n", + "print(message)\n", + "\n", + "# or\n", + "\n", + "words_one = string_one.split()\n", + "pythons = words_one[0]\n", + "python = pythons[:-1]\n", + "words_two = string_two.split()\n", + "is_word = words_two[1] # 'is' is a reserved word in Python, so we can't use it as a variable name\n", + "funny = words_two[2]\n", + "fun = funny[:-2]\n", + "exclaim = string_one[-1]\n", + "\n", + "print(python + space + is_word + space + fun + exclaim)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Now you try it!**\n", + "\n", + "**Hints:**\n", + "* You might need `.lower()` or `.capitalize()` to fix casing.\n", + "* You can extract words by slicing the string or by splitting it into a list first." + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": {}, + "outputs": [], + "source": [ + "# Test yourself \n", + "\n", + "s1 = \"The train in Spain.\"\n", + "s2 = \"makes passengers complain\"\n", + "s3 = [\"flowers\", \"grow\", \"again\" ]\n", + "s4 = \"to\"\n", + "s5 = \" \" \n", + "\n", + "# Print \"The train in Spain makes passengers complain.\"\n", + "\n", + "# Print \"The rain in Spain makes flowers grow again\"\n", + "\n", + "# Print \"The passengers grow flowers in Spain\n", + "\n", + "# Print \"The flowers in Spain train passengers to complain.\"\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Strings", + "uid": "Tmg4QRhJ" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/30_Loops/090_Fizz_Buzz_Badgers.py b/lessons/30_Loops/90_Fizz_Buzz_Badgers.py similarity index 85% rename from lessons/30_Loops/090_Fizz_Buzz_Badgers.py rename to lessons/30_Loops/90_Fizz_Buzz_Badgers.py index 243c6400..c79ff4ba 100644 --- a/lessons/30_Loops/090_Fizz_Buzz_Badgers.py +++ b/lessons/30_Loops/90_Fizz_Buzz_Badgers.py @@ -12,7 +12,11 @@ Your job is to modify only one line -- the one with range() -- so that the program only prints '🦡 badger' -Your program should print 4 badgers. +Your program should print 4 badgers. + +hint: run the program once and look at the numbers that are printed. +uid: U1uGzJ1r +name: Fizz Buzz Badgers """ for i in range(1, 31): # Change only this line diff --git a/lessons/30_Loops/Module_Three_Quiz.ipynb b/lessons/30_Loops/Module_Three_Quiz.ipynb new file mode 100644 index 00000000..c1f94b40 --- /dev/null +++ b/lessons/30_Loops/Module_Three_Quiz.ipynb @@ -0,0 +1,304 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b5757e58", + "metadata": { + "tags": [ + "parameters" + ] + }, + "source": [ + "# **Module Three Quiz**" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e88338fc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "var questionsMwNdqhtnsvOD=[\n {\n \"question\": \"What does `range(5)` print?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"0, 1, 2, 3, 4\", \"correct\": true, \"feedback\": \"Correct! range(5) starts at 0 and stops before 5.\" },\n { \"answer\": \"1, 2, 3, 4, 5\", \"correct\": false, \"feedback\": \"Incorrect. range() starts at 0 by default.\" },\n { \"answer\": \"5, 4, 3, 2, 1\", \"correct\": false, \"feedback\": \"Incorrect. range() counts forward, not backward.\" },\n { \"answer\": \"1, 2, 3, 4\", \"correct\": false, \"feedback\": \"Incorrect. range() starts at 0, not 1.\" }\n ]\n },\n {\n \"question\": \"Which can you loop through?\",\n \"type\": \"many_choice\",\n \"answers\": [\n { \"answer\": \"Lists\", \"correct\": true, \"feedback\": \"Correct! Lists are iterable.\" },\n { \"answer\": \"Strings\", \"correct\": true, \"feedback\": \"Correct! You can loop through each character.\" },\n { \"answer\": \"Tuples\", \"correct\": true, \"feedback\": \"Correct! Tuples are iterable.\" },\n { \"answer\": \"Ranges\", \"correct\": true, \"feedback\": \"Correct! Ranges are iterable.\" },\n { \"answer\": \"Integers\", \"correct\": false, \"feedback\": \"Incorrect. You cannot loop through a single integer.\" }\n ]\n },\n {\n \"question\": \"What is `list(range(2, 10, 2))`?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"[2, 4, 6, 8]\", \"correct\": true, \"feedback\": \"Correct! Start at 2, stop before 10, step by 2.\" },\n { \"answer\": \"[2, 4, 6, 8, 10]\", \"correct\": false, \"feedback\": \"Incorrect. range() stops before the stop value.\" },\n { \"answer\": \"[0, 2, 4, 6, 8]\", \"correct\": false, \"feedback\": \"Incorrect. It starts at 2, not 0.\" },\n { \"answer\": \"[2, 3, 4, 5, 6, 7, 8, 9]\", \"correct\": false, \"feedback\": \"Incorrect. The step is 2, not 1.\" }\n ]\n },\n {\n \"question\": \"What is a tuple?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"An immutable list\", \"correct\": true, \"feedback\": \"Correct! Tuples cannot be changed after creation.\" },\n { \"answer\": \"A list you can change\", \"correct\": false, \"feedback\": \"Incorrect. Tuples are immutable.\" },\n { \"answer\": \"A dictionary\", \"correct\": false, \"feedback\": \"Incorrect. Tuples use () not {}.\" },\n { \"answer\": \"A function\", \"correct\": false, \"feedback\": \"Incorrect. Tuples are a data structure.\" }\n ]\n },\n {\n \"question\": \"How do you create a one-item tuple?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"(1,)\", \"correct\": true, \"feedback\": \"Correct! The comma is required.\" },\n { \"answer\": \"(1)\", \"correct\": false, \"feedback\": \"Incorrect. This is just 1 in parentheses.\" },\n { \"answer\": \"[1]\", \"correct\": false, \"feedback\": \"Incorrect. This creates a list.\" },\n { \"answer\": \"{1}\", \"correct\": false, \"feedback\": \"Incorrect. This creates a set.\" }\n ]\n },\n {\n \"question\": \"What does `a, b = (5, 10)` do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Sets a=5 and b=10\", \"correct\": true, \"feedback\": \"Correct! This is tuple unpacking.\" },\n { \"answer\": \"Creates an error\", \"correct\": false, \"feedback\": \"Incorrect. This is valid Python syntax.\" },\n { \"answer\": \"Sets a=10 and b=5\", \"correct\": false, \"feedback\": \"Incorrect. Order matters in unpacking.\" },\n { \"answer\": \"Sets both a and b to (5, 10)\", \"correct\": false, \"feedback\": \"Incorrect. The tuple is split between variables.\" }\n ]\n },\n {\n \"question\": \"What is slicing?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Getting a portion of a list\", \"correct\": true, \"feedback\": \"Correct! Slicing extracts part of a sequence.\" },\n { \"answer\": \"Removing all items from a list\", \"correct\": false, \"feedback\": \"Incorrect. That's clear() or del.\" },\n { \"answer\": \"Sorting a list\", \"correct\": false, \"feedback\": \"Incorrect. That's sort() or sorted().\" },\n { \"answer\": \"Adding to a list\", \"correct\": false, \"feedback\": \"Incorrect. That's append() or insert().\" }\n ]\n },\n {\n \"question\": \"Can tuples and lists both be sliced?\",\n \"type\": \"many_choice\",\n \"answers\": [\n { \"answer\": \"Yes, both support slicing\", \"correct\": true, \"feedback\": \"Correct! Both can use [start:stop].\" },\n { \"answer\": \"Yes, both support indexing\", \"correct\": true, \"feedback\": \"Correct! Both can use [index].\" },\n { \"answer\": \"Yes, both support looping\", \"correct\": true, \"feedback\": \"Correct! Both are iterable.\" },\n { \"answer\": \"Yes, both can be modified\", \"correct\": false, \"feedback\": \"Incorrect. Tuples are immutable.\" }\n ]\n },\n {\n \"question\": \"What does `[::-1]` do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Reverses a list\", \"correct\": true, \"feedback\": \"Correct! Negative step reverses order.\" },\n { \"answer\": \"Sorts a list\", \"correct\": false, \"feedback\": \"Incorrect. That's sort() or sorted().\" },\n { \"answer\": \"Removes the first item\", \"correct\": false, \"feedback\": \"Incorrect. That's [1:].\" },\n { \"answer\": \"Creates an error\", \"correct\": false, \"feedback\": \"Incorrect. This is valid slice syntax.\" }\n ]\n },\n {\n \"question\": \"What does enumerate() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Adds index numbers to items\", \"correct\": true, \"feedback\": \"Correct! enumerate() returns (index, item) pairs.\" },\n { \"answer\": \"Counts items\", \"correct\": false, \"feedback\": \"Incorrect. That's len().\" },\n { \"answer\": \"Sorts items\", \"correct\": false, \"feedback\": \"Incorrect. That's sorted().\" },\n { \"answer\": \"Removes duplicates\", \"correct\": false, \"feedback\": \"Incorrect. That's set().\" }\n ]\n },\n {\n \"question\": \"What does zip() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Combines two lists into pairs\", \"correct\": true, \"feedback\": \"Correct! zip() pairs items from each list.\" },\n { \"answer\": \"Compresses a file\", \"correct\": false, \"feedback\": \"Incorrect. That's file compression, not Python's zip().\" },\n { \"answer\": \"Joins two lists end-to-end\", \"correct\": false, \"feedback\": \"Incorrect. That's concatenation with +.\" },\n { \"answer\": \"Sorts two lists\", \"correct\": false, \"feedback\": \"Incorrect. That's sorted().\" }\n ]\n },\n {\n \"question\": \"What does split() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Breaks a string into a list\", \"correct\": true, \"feedback\": \"Correct! split() divides text into pieces.\" },\n { \"answer\": \"Joins a list into a string\", \"correct\": false, \"feedback\": \"Incorrect. That's join().\" },\n { \"answer\": \"Removes spaces\", \"correct\": false, \"feedback\": \"Incorrect. That's strip().\" },\n { \"answer\": \"Makes text lowercase\", \"correct\": false, \"feedback\": \"Incorrect. That's lower().\" }\n ]\n },\n {\n \"question\": \"What is true about slicing?\",\n \"type\": \"many_choice\",\n \"answers\": [\n { \"answer\": \"Stop index is not included\", \"correct\": true, \"feedback\": \"Correct! [1:3] gives index 1 and 2 only.\" },\n { \"answer\": \"Can use negative numbers\", \"correct\": true, \"feedback\": \"Correct! [-1] gets the last item.\" },\n { \"answer\": \"Step can be negative\", \"correct\": true, \"feedback\": \"Correct! [::-1] reverses.\" },\n { \"answer\": \"Causes error if out of range\", \"correct\": false, \"feedback\": \"Incorrect. Slicing is forgiving.\" }\n ]\n },\n {\n \"question\": \"What does append() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Adds item to end of list\", \"correct\": true, \"feedback\": \"Correct! append() adds to the end.\" },\n { \"answer\": \"Adds item to start of list\", \"correct\": false, \"feedback\": \"Incorrect. That's insert(0, item).\" },\n { \"answer\": \"Removes last item\", \"correct\": false, \"feedback\": \"Incorrect. That's pop().\" },\n { \"answer\": \"Sorts the list\", \"correct\": false, \"feedback\": \"Incorrect. That's sort().\" }\n ]\n },\n {\n \"question\": \"What's the difference: sorted() vs sort()?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"sorted() returns new list; sort() changes original\", \"correct\": true, \"feedback\": \"Correct! sorted() creates a copy.\" },\n { \"answer\": \"No difference\", \"correct\": false, \"feedback\": \"Incorrect. They handle the original list differently.\" },\n { \"answer\": \"sort() returns new list; sorted() changes original\", \"correct\": false, \"feedback\": \"Incorrect. It's the opposite.\" },\n { \"answer\": \"sorted() only works on numbers\", \"correct\": false, \"feedback\": \"Incorrect. Both work on any comparable items.\" }\n ]\n },\n {\n \"question\": \"What does enumerate() start at?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"0\", \"correct\": true, \"feedback\": \"Correct! enumerate() starts counting at 0.\" },\n { \"answer\": \"1\", \"correct\": false, \"feedback\": \"Incorrect. Python uses 0-based indexing.\" },\n { \"answer\": \"It depends on the list\", \"correct\": false, \"feedback\": \"Incorrect. It always starts at 0 by default.\" },\n { \"answer\": \"-1\", \"correct\": false, \"feedback\": \"Incorrect. enumerate() counts forward from 0.\" }\n ]\n },\n {\n \"question\": \"What does join() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Combines list items into a string\", \"correct\": true, \"feedback\": \"Correct! join() makes a string from a list.\" },\n { \"answer\": \"Splits a string into a list\", \"correct\": false, \"feedback\": \"Incorrect. That's split().\" },\n { \"answer\": \"Adds two lists together\", \"correct\": false, \"feedback\": \"Incorrect. That's concatenation with +.\" },\n { \"answer\": \"Removes items from a list\", \"correct\": false, \"feedback\": \"Incorrect. That's remove() or pop().\" }\n ]\n },\n {\n \"question\": \"How do you count backward with range()?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Use negative step: range(10, 0, -1)\", \"correct\": true, \"feedback\": \"Correct! Negative step counts backward.\" },\n { \"answer\": \"range(0, 10)\", \"correct\": false, \"feedback\": \"Incorrect. This counts forward.\" },\n { \"answer\": \"range(10)\", \"correct\": false, \"feedback\": \"Incorrect. This counts forward from 0 to 9.\" },\n { \"answer\": \"range(-10, 0)\", \"correct\": false, \"feedback\": \"Incorrect. This counts from -10 to -1.\" }\n ]\n },\n {\n \"question\": \"What does break do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Exits the loop immediately\", \"correct\": true, \"feedback\": \"Correct! break stops the loop.\" },\n { \"answer\": \"Skips to next iteration\", \"correct\": false, \"feedback\": \"Incorrect. That's continue.\" },\n { \"answer\": \"Pauses the loop\", \"correct\": false, \"feedback\": \"Incorrect. break ends the loop.\" },\n { \"answer\": \"Restarts the loop\", \"correct\": false, \"feedback\": \"Incorrect. break exits completely.\" }\n ]\n },\n {\n \"question\": \"What does continue do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Skips to next loop iteration\", \"correct\": true, \"feedback\": \"Correct! continue skips remaining code in current iteration.\" },\n { \"answer\": \"Exits the loop\", \"correct\": false, \"feedback\": \"Incorrect. That's break.\" },\n { \"answer\": \"Pauses the loop\", \"correct\": false, \"feedback\": \"Incorrect. continue moves to next iteration.\" },\n { \"answer\": \"Restarts from beginning\", \"correct\": false, \"feedback\": \"Incorrect. continue goes to next iteration.\" }\n ]\n }\n]\n;\n\nif (typeof Question === 'undefined') {\n// Make a random ID\nfunction makeid(length) {\n var result = [];\n var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\n var charactersLength = characters.length;\n for (var i = 0; i < length; i++) {\n result.push(characters.charAt(Math.floor(Math.random() * charactersLength)));\n }\n return result.join('');\n}\n// Convert LaTeX delimiters and markdown links to HTML\nfunction jaxify(string) {\n let mystring = string;\n let count = 0, count2 = 0;\n let loc = mystring.search(/([^\\\\]|^)(\\$)/);\n let loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n while (loc >= 0 || loc2 >= 0) {\n if (loc2 >= 0) {\n mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, count2 % 2 ? '$1\\\\]' : '$1\\\\[');\n count2++;\n } else {\n mystring = mystring.replace(/([^\\\\]|^)(\\$)/, count % 2 ? '$1\\\\)' : '$1\\\\(');\n count++;\n }\n loc = mystring.search(/([^\\\\]|^)(\\$)/);\n loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n }\n // Replace markdown links\n mystring = mystring.replace(//g, 'http$1');\n mystring = mystring.replace(/\\[(.*?)\\]\\((.*?)\\)/g, '$1');\n return mystring;\n}\n\n// Base class for question types\nclass Question {\n static registry = {};\n static register(type, cls) {\n Question.registry[type] = cls;\n }\n static create(qa, id, index, options, rootDiv) {\n const Cls = Question.registry[qa.type];\n if (!Cls) {\n console.error(`No question class registered for type \"${qa.type}\"`);\n return;\n }\n const q = new Cls(qa, id, index, options, rootDiv);\n q.render();\n }\n\n constructor(qa, id, index, options, rootDiv) {\n this.qa = qa;\n this.id = id;\n this.index = index;\n this.options = options;\n this.rootDiv = rootDiv;\n // wrapper\n this.wrapper = document.createElement('div');\n this.wrapper.id = `quizWrap${id}`;\n this.wrapper.className = 'Quiz';\n this.wrapper.dataset.qnum = index;\n this.wrapper.style.maxWidth = `${options.maxWidth}px`;\n rootDiv.appendChild(this.wrapper);\n // question container\n this.outerqDiv = document.createElement('div');\n this.outerqDiv.id = `OuterquizQn${id}${index}`;\n this.wrapper.appendChild(this.outerqDiv);\n // question text\n this.qDiv = document.createElement('div');\n this.qDiv.id = `quizQn${id}${index}`;\n if (qa.question) {\n this.qDiv.innerHTML = jaxify(qa.question);\n this.outerqDiv.appendChild(this.qDiv);\n }\n // code block\n if (qa.code) {\n const codeDiv = document.createElement('div');\n codeDiv.id = `code${id}${index}`;\n codeDiv.className = 'QuizCode';\n const pre = document.createElement('pre');\n const codeEl = document.createElement('code');\n codeEl.innerHTML = qa.code;\n pre.appendChild(codeEl);\n codeDiv.appendChild(pre);\n this.outerqDiv.appendChild(codeDiv);\n }\n // answer container\n this.aDiv = document.createElement('div');\n this.aDiv.id = `quizAns${id}${index}`;\n this.aDiv.className = 'Answer';\n this.wrapper.appendChild(this.aDiv);\n // feedback container (append after answers)\n this.fbDiv = document.createElement('div');\n this.fbDiv.id = `fb${id}`;\n this.fbDiv.className = 'Feedback';\n this.fbDiv.dataset.answeredcorrect = 0;\n }\n\n render() {\n throw new Error('render() not implemented');\n }\n\n preserveResponse(val) {\n if (!this.options.preserveResponses) return;\n const resp = document.getElementById(`responses${this.rootDiv.id}`);\n if (!resp) return;\n const arr = JSON.parse(resp.dataset.responses);\n arr[this.index] = val;\n resp.dataset.responses = JSON.stringify(arr);\n printResponses(resp);\n }\n\n typeset(container) {\n if (typeof MathJax !== 'undefined') {\n const v = MathJax.version;\n if (v[0] === '2') {\n MathJax.Hub.Queue(['Typeset', MathJax.Hub]);\n } else {\n MathJax.typeset([container]);\n }\n }\n }\n}\n\n// Choose a random subset of an array. Can also be used to shuffle the array\nfunction getRandomSubarray(arr, size) {\n var shuffled = arr.slice(0), i = arr.length, temp, index;\n while (i--) {\n index = Math.floor((i + 1) * Math.random());\n temp = shuffled[index];\n shuffled[index] = shuffled[i];\n shuffled[i] = temp;\n }\n return shuffled.slice(0, size);\n}\n\nfunction printResponses(responsesContainer) {\n var responses=JSON.parse(responsesContainer.dataset.responses);\n var stringResponses='IMPORTANT!To preserve this answer sequence for submission, when you have finalized your answers:
  1. Copy the text in this cell below \"Answer String\"
  2. Double click on the cell directly below the Answer String, labeled \"Replace Me\"
  3. Select the whole \"Replace Me\" text
  4. Paste in your answer string and press shift-Enter.
  5. Save the notebook using the save icon or File->Save Notebook menu item



  6. Answer String:
    ';\n console.log(responses);\n responses.forEach((response, index) => {\n if (response) {\n console.log(index + ': ' + response);\n stringResponses+= index + ': ' + response +\"
    \";\n }\n });\n responsesContainer.innerHTML=stringResponses;\n}\n/* Callback function to determine whether a selected multiple-choice\n button corresponded to a correct answer and to provide feedback\n based on the answer */\nfunction check_mc() {\n var id = this.id.split('-')[0];\n //var response = this.id.split('-')[1];\n //console.log(response);\n //console.log(\"In check_mc(), id=\"+id);\n //console.log(event.srcElement.id) \n //console.log(event.srcElement.dataset.correct) \n //console.log(event.srcElement.dataset.feedback)\n\n var label = event.srcElement;\n //console.log(label, label.nodeName);\n var depth = 0;\n while ((label.nodeName != \"LABEL\") && (depth < 20)) {\n label = label.parentElement;\n console.log(depth, label);\n depth++;\n }\n\n\n\n var answers = label.parentElement.children;\n //console.log(answers);\n\n // Split behavior based on multiple choice vs many choice:\n var fb = document.getElementById(\"fb\" + id);\n\n\n\n /* Multiple choice (1 answer). Allow for 0 correct\n answers as an edge case */\n if (fb.dataset.numcorrect <= 1) {\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n //console.log(responsesContainer);\n var response = label.firstChild.innerText;\n if (label.querySelector(\".QuizCode\")){\n response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n }\n console.log(response);\n //console.log(document.getElementById(\"quizWrap\"+id));\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n console.log(responses);\n responses[qnum]= response;\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End code to preserve responses\n\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n //console.log(child);\n child.className = \"MCButton\";\n }\n\n\n\n if (label.dataset.correct == \"true\") {\n // console.log(\"Correct action\");\n if (\"feedback\" in label.dataset) {\n fb.innerHTML = jaxify(label.dataset.feedback);\n } else {\n fb.innerHTML = \"Correct!\";\n }\n label.classList.add(\"correctButton\");\n\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n\n } else {\n if (\"feedback\" in label.dataset) {\n fb.innerHTML = jaxify(label.dataset.feedback);\n } else {\n fb.innerHTML = \"Incorrect -- try again.\";\n }\n //console.log(\"Error action\");\n label.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n }\n else { /* Many choice (more than 1 correct answer) */\n var reset = false;\n var feedback;\n if (label.dataset.correct == \"true\") {\n if (\"feedback\" in label.dataset) {\n feedback = jaxify(label.dataset.feedback);\n } else {\n feedback = \"Correct!\";\n }\n if (label.dataset.answered <= 0) {\n if (fb.dataset.answeredcorrect < 0) {\n fb.dataset.answeredcorrect = 1;\n reset = true;\n } else {\n fb.dataset.answeredcorrect++;\n }\n if (reset) {\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n child.className = \"MCButton\";\n child.dataset.answered = 0;\n }\n }\n label.classList.add(\"correctButton\");\n label.dataset.answered = 1;\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n\n }\n } else {\n if (\"feedback\" in label.dataset) {\n feedback = jaxify(label.dataset.feedback);\n } else {\n feedback = \"Incorrect -- try again.\";\n }\n if (fb.dataset.answeredcorrect > 0) {\n fb.dataset.answeredcorrect = -1;\n reset = true;\n } else {\n fb.dataset.answeredcorrect--;\n }\n\n if (reset) {\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n child.className = \"MCButton\";\n child.dataset.answered = 0;\n }\n }\n label.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n //console.log(responsesContainer);\n var response = label.firstChild.innerText;\n if (label.querySelector(\".QuizCode\")){\n response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n }\n console.log(response);\n //console.log(document.getElementById(\"quizWrap\"+id));\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n if (label.dataset.correct == \"true\") {\n if (typeof(responses[qnum]) == \"object\"){\n if (!responses[qnum].includes(response))\n responses[qnum].push(response);\n } else{\n responses[qnum]= [ response ];\n }\n } else {\n responses[qnum]= response;\n }\n console.log(responses);\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End save responses stuff\n\n\n\n var numcorrect = fb.dataset.numcorrect;\n var answeredcorrect = fb.dataset.answeredcorrect;\n if (answeredcorrect >= 0) {\n fb.innerHTML = feedback + \" [\" + answeredcorrect + \"/\" + numcorrect + \"]\";\n } else {\n fb.innerHTML = feedback + \" [\" + 0 + \"/\" + numcorrect + \"]\";\n }\n\n\n }\n\n if (typeof MathJax != 'undefined') {\n var version = MathJax.version;\n console.log('MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([fb]);\n }\n } else {\n console.log('MathJax not detected');\n }\n\n}\n\n\n/* Function to produce the HTML buttons for a multiple choice/\n many choice question and to update the CSS tags based on\n the question type */\nfunction make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id) {\n\n var shuffled;\n if (shuffle_answers == true) {\n //console.log(shuffle_answers+\" read as true\");\n shuffled = getRandomSubarray(qa.answers, qa.answers.length);\n } else {\n //console.log(shuffle_answers+\" read as false\");\n shuffled = qa.answers;\n }\n\n\n var num_correct = 0;\n\n shuffled.forEach((item, index, ans_array) => {\n //console.log(answer);\n\n // Make label for input element\n var lab = document.createElement(\"label\");\n lab.className = \"MCButton\";\n lab.id = id + '-' + index;\n lab.onclick = check_mc;\n\n // Make input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.name = \"mcgroup-\" + id; // for grouping radios\n inp.className = \"sr-only\"; // or \"visually-hidden\" or whatever you call it\n\n\n lab.append(inp); // input is now inside the label\n\n var aSpan = document.createElement('span');\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n if (\"code\" in item) {\n var codeSpan = document.createElement('span');\n codeSpan.id = \"code\" + id + index;\n codeSpan.className = \"QuizCode\";\n var codePre = document.createElement('pre');\n codeSpan.append(codePre);\n var codeCode = document.createElement('code');\n codePre.append(codeCode);\n codeCode.innerHTML = item.code;\n lab.append(codeSpan);\n }\n\n // Set the data attributes for the answer\n lab.setAttribute('data-correct', item.correct);\n if (item.correct) {\n num_correct++;\n }\n if (\"feedback\" in item) {\n lab.setAttribute('data-feedback', item.feedback);\n }\n lab.setAttribute('data-answered', 0);\n\n // Only append the label (input is inside)\n aDiv.append(lab);\n\n\n });\n\n if (num_correct > 1) {\n outerqDiv.className = \"ManyChoiceQn\";\n } else {\n outerqDiv.className = \"MultipleChoiceQn\";\n }\n\n return num_correct;\n\n}\n// Object-oriented wrapper for MC/MANY choice\nclass MCQuestion extends Question {\n constructor(qa, id, idx, opts, rootDiv) { super(qa, id, idx, opts, rootDiv); }\n render() {\n //console.log(\"options.shuffleAnswers \" + this.options.shuffleAnswers);\n const numCorrect = make_mc(\n this.qa,\n this.options.shuffleAnswers,\n this.outerqDiv,\n this.qDiv,\n this.aDiv,\n this.id\n );\n if ('answer_cols' in this.qa) {\n this.aDiv.style.gridTemplateColumns =\n 'repeat(' + this.qa.answer_cols + ', 1fr)';\n }\n this.fbDiv.dataset.numcorrect = numCorrect;\n this.wrapper.appendChild(this.fbDiv);\n }\n}\nQuestion.register('multiple_choice', MCQuestion);\nQuestion.register('many_choice', MCQuestion);\nfunction check_numeric(ths, event) {\n\n if (event.keyCode === 13) {\n ths.blur();\n\n var id = ths.id.split('-')[0];\n\n var submission = ths.value;\n if (submission.indexOf('/') != -1) {\n var sub_parts = submission.split('/');\n //console.log(sub_parts);\n submission = sub_parts[0] / sub_parts[1];\n }\n //console.log(\"Reader entered\", submission);\n\n if ((\"precision\" in ths.dataset) && (ths.dataset.precision > 0)) {\n var precision = ths.dataset.precision;\n submission = Number(Number(submission).toPrecision(precision));\n }\n\n\n //console.log(\"In check_numeric(), id=\"+id);\n //console.log(event.srcElement.id) \n //console.log(event.srcElement.dataset.feedback)\n\n var fb = document.getElementById(\"fb\" + id);\n fb.style.display = \"none\";\n fb.innerHTML = \"Incorrect -- try again.\";\n\n var answers = JSON.parse(ths.dataset.answers);\n //console.log(answers);\n\n var defaultFB = \"Incorrect. Try again.\";\n var correct;\n var done = false;\n answers.every(answer => {\n //console.log(answer.type);\n\n correct = false;\n // if (answer.type==\"value\"){\n if ('value' in answer) {\n var value;\n if (\"precision\" in ths.dataset) {\n value = answer.value.toPrecision(ths.dataset.precision);\n } else {\n value = answer.value;\n }\n if (submission == value) {\n if (\"feedback\" in answer) {\n fb.innerHTML = jaxify(answer.feedback);\n } else {\n fb.innerHTML = jaxify(\"Correct\");\n }\n correct = answer.correct;\n //console.log(answer.correct);\n done = true;\n }\n\n // } else if (answer.type==\"range\") {\n } else if ('range' in answer) {\n console.log(answer.range);\n console.log(submission, submission >=answer.range[0], submission < answer.range[1])\n if ((submission >= answer.range[0]) && (submission < answer.range[1])) {\n fb.innerHTML = jaxify(answer.feedback);\n correct = answer.correct;\n console.log(answer.correct);\n done = true;\n }\n } else if (answer.type == \"default\") {\n if (\"feedback\" in answer) {\n defaultFB = answer.feedback;\n } \n }\n if (done) {\n return false; // Break out of loop if this has been marked correct\n } else {\n return true; // Keep looking for case that includes this as a correct answer\n }\n });\n console.log(\"done:\", done);\n\n if ((!done) && (defaultFB != \"\")) {\n fb.innerHTML = jaxify(defaultFB);\n //console.log(\"Default feedback\", defaultFB);\n }\n\n fb.style.display = \"block\";\n if (correct) {\n ths.className = \"Input-text\";\n ths.classList.add(\"correctButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n } else {\n ths.className = \"Input-text\";\n ths.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n console.log(submission);\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n //console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n console.log(responses);\n if (submission == ths.value){\n responses[qnum]= submission;\n } else {\n responses[qnum]= ths.value + \"(\" + submission +\")\";\n }\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End code to preserve responses\n\n if (typeof MathJax != 'undefined') {\n var version = MathJax.version;\n console.log('MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([fb]);\n }\n } else {\n console.log('MathJax not detected');\n }\n // After correct answer, if next JupyterQuiz question exists and has a text input, scroll by current question height\n if (correct) {\n // find the current question wrapper\n var wrapper = ths.closest('.Quiz');\n if (wrapper) {\n var nextWrapper = wrapper.nextElementSibling;\n if (nextWrapper && nextWrapper.classList.contains('Quiz')) {\n var nextInput = nextWrapper.querySelector('input.Input-text');\n if (nextInput) {\n var height = wrapper.getBoundingClientRect().height;\n console.log(height);\n nextInput.focus();\n }\n }\n }\n }\n return false;\n }\n\n}\n// Object-oriented wrapper for numeric questions\nclass NumericQuestion extends Question {\n constructor(qa, id, idx, opts, rootDiv) {\n super(qa, id, idx, opts, rootDiv);\n }\n render() {\n make_numeric(this.qa, this.outerqDiv, this.qDiv, this.aDiv, this.id);\n this.wrapper.appendChild(this.fbDiv);\n }\n}\nQuestion.register('numeric', NumericQuestion);\n\nfunction isValid(el, charC) {\n //console.log(\"Input char: \", charC);\n if (charC == 46) {\n if (el.value.indexOf('.') === -1) {\n return true;\n } else if (el.value.indexOf('/') != -1) {\n var parts = el.value.split('/');\n if (parts[1].indexOf('.') === -1) {\n return true;\n }\n }\n else {\n return false;\n }\n } else if (charC == 47) {\n if (el.value.indexOf('/') === -1) {\n if ((el.value != \"\") && (el.value != \".\")) {\n return true;\n } else {\n return false;\n }\n } else {\n return false;\n }\n } else if (charC == 45) {\n var edex = el.value.indexOf('e');\n if (edex == -1) {\n edex = el.value.indexOf('E');\n }\n\n if (el.value == \"\") {\n return true;\n } else if (edex == (el.value.length - 1)) { // If just after e or E\n return true;\n } else {\n return false;\n }\n } else if (charC == 101) { // \"e\"\n if ((el.value.indexOf('e') === -1) && (el.value.indexOf('E') === -1) && (el.value.indexOf('/') == -1)) {\n // Prev symbol must be digit or decimal point:\n if (el.value.slice(-1).search(/\\d/) >= 0) {\n return true;\n } else if (el.value.slice(-1).search(/\\./) >= 0) {\n return true;\n } else {\n return false;\n }\n } else {\n return false;\n }\n } else {\n if (charC > 31 && (charC < 48 || charC > 57))\n return false;\n }\n return true;\n}\n\nfunction numeric_keypress(evnt) {\n var charC = (evnt.which) ? evnt.which : evnt.keyCode;\n\n if (charC == 13) {\n check_numeric(this, evnt);\n } else {\n return isValid(this, charC);\n }\n}\n\n\n\n\n\nfunction make_numeric(qa, outerqDiv, qDiv, aDiv, id) {\n\n\n\n //console.log(answer);\n\n\n outerqDiv.className = \"NumericQn\";\n aDiv.style.display = 'block';\n\n var lab = document.createElement(\"label\");\n lab.className = \"InpLabel\";\n lab.innerHTML = \"Type numeric answer here:\";\n aDiv.append(lab);\n\n var inp = document.createElement(\"input\");\n inp.type = \"text\";\n //inp.id=\"input-\"+id;\n inp.id = id + \"-0\";\n inp.className = \"Input-text\";\n inp.setAttribute('data-answers', JSON.stringify(qa.answers));\n if (\"precision\" in qa) {\n inp.setAttribute('data-precision', qa.precision);\n }\n aDiv.append(inp);\n //console.log(inp);\n\n //inp.addEventListener(\"keypress\", check_numeric);\n //inp.addEventListener(\"keypress\", numeric_keypress);\n /*\n inp.addEventListener(\"keypress\", function(event) {\n return numeric_keypress(this, event);\n }\n );\n */\n //inp.onkeypress=\"return numeric_keypress(this, event)\";\n inp.onkeypress = numeric_keypress;\n inp.onpaste = event => false;\n\n inp.addEventListener(\"focus\", function (event) {\n this.value = \"\";\n return false;\n }\n );\n\n\n}\n// Override show_questions to use object-oriented Question API\nfunction show_questions(json, container) {\n // Accept container element or element ID\n if (typeof container === 'string') {\n container = document.getElementById(container);\n }\n if (!container) {\n console.error('show_questions: invalid container', container);\n return;\n }\n\n const shuffleQuestions = container.dataset.shufflequestions === 'True';\n const shuffleAnswers = container.dataset.shuffleanswers === 'True';\n const preserveResponses = container.dataset.preserveresponses === 'true';\n const maxWidth = parseInt(container.dataset.maxwidth, 10) || 0;\n let numQuestions = parseInt(container.dataset.numquestions, 10) || json.length;\n if (numQuestions > json.length) numQuestions = json.length;\n\n let questions = json;\n if (shuffleQuestions || numQuestions < json.length) {\n questions = getRandomSubarray(json, numQuestions);\n }\n\n questions.forEach((qa, index) => {\n const id = makeid(8);\n const options = {\n shuffleAnswers: shuffleAnswers,\n preserveResponses: preserveResponses,\n maxWidth: maxWidth\n };\n Question.create(qa, id, index, options, container);\n });\n\n if (preserveResponses) {\n const respDiv = document.createElement('div');\n respDiv.id = 'responses' + container.id;\n respDiv.className = 'JCResponses';\n respDiv.dataset.responses = JSON.stringify([]);\n respDiv.innerHTML = 'Select your answers and then follow the directions that will appear here.';\n container.appendChild(respDiv);\n }\n\n // Trigger MathJax typesetting if available\n if (typeof MathJax != 'undefined') {\n console.log(\"MathJax version\", MathJax.version);\n var version = MathJax.version;\n setTimeout(function(){\n var version = MathJax.version;\n console.log('After sleep, MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n if (MathJax.hasOwnProperty('typeset') ) {\n MathJax.typeset([container]);\n } else {\n console.log('WARNING: Trying to force load MathJax 3');\n window.MathJax = {\n tex: {\n inlineMath: [['$', '$'], ['\\\\(', '\\\\)']]\n },\n svg: {\n fontCache: 'global'\n }\n };\n\n (function () {\n var script = document.createElement('script');\n script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';\n script.async = true;\n document.head.appendChild(script);\n })();\n }\n }\n }, 500);\nif (typeof version == 'undefined') {\n } else\n {\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n if (MathJax.hasOwnProperty('typeset') ) {\n MathJax.typeset([container]);\n } else {\n console.log('WARNING: Trying to force load MathJax 3');\n window.MathJax = {\n tex: {\n inlineMath: [['$', '$'], ['\\\\(', '\\\\)']]\n },\n svg: {\n fontCache: 'global'\n }\n };\n\n (function () {\n var script = document.createElement('script');\n script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';\n script.async = true;\n document.head.appendChild(script);\n })();\n }\n } else {\n console.log(\"MathJax not found\");\n }\n }\n }\n // if (typeof MathJax !== 'undefined') {\n // const v = MathJax.version;\n // if (v[0] === '2') {\n // MathJax.Hub.Queue(['Typeset', MathJax.Hub]);\n // } else if (v[0] === '3') {\n // MathJax.typeset([container]);\n // }\n // }\n\n // Prevent link clicks from bubbling up\n Array.from(container.getElementsByClassName('Link')).forEach(link => {\n link.addEventListener('click', e => e.stopPropagation());\n });\n}\nfunction levenshteinDistance(a, b) {\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n const matrix = Array(b.length + 1).fill(null).map(() => Array(a.length + 1).fill(null));\n\n for (let i = 0; i <= a.length; i++) {\n matrix[0][i] = i;\n }\n\n for (let j = 0; j <= b.length; j++) {\n matrix[j][0] = j;\n }\n\n for (let j = 1; j <= b.length; j++) {\n for (let i = 1; i <= a.length; i++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n matrix[j][i] = Math.min(\n matrix[j - 1][i] + 1, // Deletion\n matrix[j][i - 1] + 1, // Insertion\n matrix[j - 1][i - 1] + cost // Substitution\n );\n }\n }\n return matrix[b.length][a.length];\n}\n// Object-oriented wrapper for string input questions\nclass StringQuestion extends Question {\n constructor(qa, id, idx, opts, rootDiv) {\n super(qa, id, idx, opts, rootDiv);\n }\n render() {\n make_string(this.qa, this.outerqDiv, this.qDiv, this.aDiv, this.id);\n this.wrapper.appendChild(this.fbDiv);\n }\n}\nQuestion.register('string', StringQuestion);\n\nfunction check_string(ths, event) {\n if (event.keyCode === 13) {\n ths.blur();\n\n var id = ths.id.split('-')[0];\n var submission = ths.value.trim();\n var fb = document.getElementById(\"fb\" + id);\n fb.style.display = \"none\";\n fb.innerHTML = \"Incorrect -- try again.\";\n\n var answers = JSON.parse(ths.dataset.answers);\n var defaultFB = \"Incorrect. Try again.\";\n var correct;\n var done = false;\n\n // Handle default answer pattern: filter out and capture default feedback\n var filteredAnswers = [];\n answers.forEach(answer => {\n if (answer.type === \"default\") {\n defaultFB = answer.feedback;\n } else {\n filteredAnswers.push(answer);\n }\n });\n answers = filteredAnswers;\n\n answers.every(answer => {\n correct = false;\n\n let match = false;\n if (answer.match_case) {\n match = submission === answer.answer;\n } else {\n match = submission.toLowerCase() === answer.answer.toLowerCase();\n }\n console.log(submission);\n console.log(answer.answer);\n console.log(match);\n\n if (match) {\n if (\"feedback\" in answer) {\n fb.innerHTML = jaxify(answer.feedback);\n } else {\n fb.innerHTML = jaxify(\"Correct\");\n }\n correct = answer.correct;\n done = true;\n } else if (answer.fuzzy_threshold) {\n var max_length = Math.max(submission.length, answer.answer.length);\n var ratio;\n if (answer.match_case) {\n ratio = 1- (levenshteinDistance(submission, answer.answer) / max_length);\n } else {\n ratio = 1- (levenshteinDistance(submission.toLowerCase(),\n answer.answer.toLowerCase()) / max_length);\n }\n if (ratio >= answer.fuzzy_threshold) {\n if (\"feedback\" in answer) {\n fb.innerHTML = jaxify(\"(Fuzzy) \" + answer.feedback);\n } else {\n fb.innerHTML = jaxify(\"Correct\");\n }\n correct = answer.correct;\n done = true;\n }\n\n }\n\n if (done) {\n return false;\n } else {\n return true;\n }\n });\n\n if ((!done) && (defaultFB != \"\")) {\n fb.innerHTML = jaxify(defaultFB);\n }\n\n fb.style.display = \"block\";\n if (correct) {\n ths.className = \"Input-text\";\n ths.classList.add(\"correctButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n } else {\n ths.className = \"Input-text\";\n ths.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n var qnum = document.getElementById(\"quizWrap\" + id).dataset.qnum;\n var responses = JSON.parse(responsesContainer.dataset.responses);\n responses[qnum] = submission;\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n\n if (typeof MathJax != 'undefined') {\n var version = MathJax.version;\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([fb]);\n }\n } else {\n console.log('MathJax not detected');\n }\n // After correct answer, if next JupyterQuiz question exists and has a text input, scroll by current question height\n if (correct) {\n var wrapper = ths.closest('.Quiz');\n if (wrapper) {\n var nextWrapper = wrapper.nextElementSibling;\n if (nextWrapper && nextWrapper.classList.contains('Quiz')) {\n var nextInput = nextWrapper.querySelector('input.Input-text');\n if (nextInput) {\n var height = wrapper.getBoundingClientRect().height;\n nextInput.focus();\n }\n }\n }\n }\n return false;\n }\n}\n\nfunction string_keypress(evnt) {\n var charC = (evnt.which) ? evnt.which : evnt.keyCode;\n\n if (charC == 13) {\n check_string(this, evnt);\n } \n}\n\n\nfunction make_string(qa, outerqDiv, qDiv, aDiv, id) {\n outerqDiv.className = \"StringQn\";\n aDiv.style.display = 'block';\n\n var lab = document.createElement(\"label\");\n lab.className = \"InpLabel\";\n lab.innerHTML = \"Type your answer here:\";\n aDiv.append(lab);\n\n var inp = document.createElement(\"input\");\n inp.type = \"text\";\n inp.id = id + \"-0\";\n inp.className = \"Input-text\";\n inp.setAttribute('data-answers', JSON.stringify(qa.answers));\n // Apply optional input width (approx. number of characters, in em units)\n if (qa.input_width != null) {\n inp.style['min-width'] = qa.input_width + 'em';\n }\n aDiv.append(inp);\n\n inp.onkeypress = string_keypress;\n inp.onpaste = event => false;\n\n inp.addEventListener(\"focus\", function (event) {\n this.value = \"\";\n return false;\n });\n}\n/*\n * Handle asynchrony issues when re-running quizzes in Jupyter notebooks.\n * Ensures show_questions is called after the container div is in the DOM.\n */\nfunction try_show() {\n if (document.getElementById(\"MwNdqhtnsvOD\")) {\n show_questions(questionsMwNdqhtnsvOD, MwNdqhtnsvOD);\n } else {\n setTimeout(try_show, 200);\n }\n};\n// Invoke immediately\n{\n try_show();\n}\n}\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from jupyterquiz import display_quiz\n", + "\n", + "display_quiz(\"../.jtl/Quiz_Data/Module_Three_Quiz.json\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c832450", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python-Apprentice (3.13.3)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/30_Loops/images/badger_video_thumbnail.jpg b/lessons/30_Loops/images/badger_video_thumbnail.jpg new file mode 100644 index 00000000..a8d32817 Binary files /dev/null and b/lessons/30_Loops/images/badger_video_thumbnail.jpg differ diff --git a/lessons/40_Data_Structures_Func/10_Functions.ipynb b/lessons/40_Data_Structures_Func/10_Functions.ipynb index cb8aee1c..1e59dbbc 100644 --- a/lessons/40_Data_Structures_Func/10_Functions.ipynb +++ b/lessons/40_Data_Structures_Func/10_Functions.ipynb @@ -1,509 +1,444 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Functions and Data Structures\n", - "\n", - "## Functions\n", - "\n", - "We've seen functions a few times, but haven't explained how they work in detail. Let's explore functions. \n", - "\n", - "A function looks like this:\n", - "\n", - "```python \n", - "def add_ten(x):\n", - " return x + 10\n", - "```\n", - "\n", - "Then we can use the function like this:\n", - "\n", - "```python \n", - "y = add_ten(5)\n", - "```\n", - "\n", - "The important parts of a function are: \n", - "\n", - "* It has a name, which comes right after \"def\"\n", - "* It has argument, or input values, that come right after the name. \n", - "* The body of the function is an indented block\n", - "* The function returns a value, which py default is `None`\n", - "\n", - "The simplest function you can write is:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def do_nothing():\n", - " pass\n", - "\n", - "y = do_nothing()\n", - "print(y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `pass` key word means \"do nothing\"; it just takes up space so the function\n", - "will have a body that is properly indented. This function does not have a\n", - "`return` statement, so it returns the empty value `None`.\n", - "\n", - "The important uses of functions is that they make parts of your program\n", - "reusable, and properly breaking a program into functions makes the program\n", - "easier to understand, use and test, especially if the function and it's\n", - "arguments have names that indicate what the function and arguments do or\n", - "what data they hold. \n", - "\n", - "By the way, have you checked in your code? Take a look at this [documentation](https://curriculum.jointheleague.org/howto/checkin_restart.html) if you forgot how it's done!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Function Arguments\n", - "\n", - "Function argument are the values you pass into the function so it can compute the value it returns. \n", - "You name the arguments on the argument list, and there are a few ways to assign values to arguments when you\n", - "call the function: you can specify the arguments by position, or by name. \n", - "\n", - "For instance:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Demonstrate function arguments\n", - "\n", - "def greet_user(name, greeting):\n", - " print(f\"{greeting}, {name}!\")\n", - "\n", - "# Call function with \"positional\" arguments. He first argument\n", - "# is assigned `name`, the second is assigned `greeting`.\n", - "\n", - "greet_user(\"Alice\", \"Bounjour\")\n", - "\n", - "# We can also call the function with \"keyword\" arguments. This\n", - "# allows us to specify the arguments in any order.\n", - "\n", - "greet_user(greeting=\"Hello\", name=\"Alice\") # greeting and name are in opposite order!\n", - "\n", - "# You can mix positional and keyword arguments, but all positional\n", - "# arguments must come first.\n", - "\n", - "greet_user(\"Bob\", greeting=\"Hello\")\n", - "\n", - "\n", - "# You can't have a positional argument after a keyword argument.\n", - "# This will cause an error ( uncomment and run to see )\n", - "# greet_user(name = \"Bob\", greeting)\n", - "\n", - "# You also can't specify an argument more than once. This will also\n", - "# cause an error ( uncomment and run to see )\n", - "# greet_user(\"Bob\", name=\"Bob\")\n", - "\n", - "\n", - "# And, you can't skip an argument ( if it doesn't have a default value.)\n", - "# This will also cause an error ( uncomment and run to see )\n", - "# greet_user(\"Bob\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also specify default values for arguments. If an argument has a default value, it is optional, \n", - "but the default arguments must go at the end of the list. \n", - " " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def greet_user(name, greeting='Hello', punct=\"!\"):\n", - " print(f\"{greeting}, {name}{punct}\")\n", - "\n", - "# Use the default value for punct and hello\n", - "greet_user(\"Alice\")\n", - "\n", - "# Or, override the default value\n", - "greet_user(\"Alice\", \"Hello\", \".\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another important point is that the variables you use in your function have to be declared, \n", - "such as by including the variable in the argument list. So this will throw an error:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def greet_user(name, greeting='Hello'):\n", - " print(f\"{greeting}, {name}{punct}\")\n", - "\n", - "greet_user(\"Alice\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Python didn't know what the variable `punct` is, so it could not run the function. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Test Yourself\n", - "\n", - "Write a function that will take two strings. The first is a character, and the second\n", - "is a longer string. The function will iterate over the second string and return the\n", - "position that the character has in the string.\n", - "\n", - "If the chracter is not in the string, return -1\n", - "\n", - "For instance: \n", - "\n", - "```python \n", - "find_char('x', 'My fox likes bricks') == 5\n", - "find char('z', 'There is a zebra in the garden') == 11\n", - "find char('w', \"I've lost my shoes\") == -1\n", - "```\n", - "\n", - "We will use `assert` to check your function. Assert will raise an error if the expression \n", - "is not True. \n", - "\n", - "Hint: you can `enumerate()` your string to get all of the characters and their positions. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test yourself\n", - "\n", - "# Write your own find_char function that takes a character and a string\n", - "\n", - "assert find_char('x', 'My fox likes bricks') == 5\n", - "assert find_char('z', 'There is a zebra in the garden') == 11\n", - "assert find_char('w', \"I've lost my shoes\") == -1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Composition\n", - "\n", - "You aren't limited to putting numbers and strings into function arguments; you\n", - "get put in anything that has a value ( which we call an `expression` )\n", - "\n", - "For instance, suppose we have the functions\n", - "\n", - "```python \n", - "def f(a,b):\n", - " pass\n", - "\n", - "def g(c,d):\n", - " pass\n", - "```\n", - "\n", - "The w can pass the output of f() directly into g()\n", - "\n", - "```python \n", - "g( f(1,2), f(3,4))\n", - "```\n", - "\n", - "Or put expressions in the argument list: \n", - "\n", - "```python \n", - "g( f(1,2)*f(3,4), f(5,6)-f(7,8))\n", - "```\n", - "\n", - "# Test Yourself\n", - "\n", - "Test yourself by writing functions. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test Yourself\n", - "\n", - "# Write a function to add two numbers and return the result\n", - "\n", - "# Write a function to subtract two numbers and return the result \n", - "\n", - "# Write a function to print \"Same\" if two numbers are the same, otherwise print \"Different\"\n", - "\n", - "# Use your functions to show that 2 + 2 = 4\n", - "\n", - "# Use your functions to show that ( 5 + 3) - 2 = 6\n", - "\n", - "# Use your functions to show that 2 + 2 != 3 + 3 = 6\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Oh No Closures\n", - "\n", - "Here is a warning, and also an exciting new feature, about functions. This function will work, but maybe not\n", - "the way you expect:\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "name = 'Bob'\n", - "\n", - "def greet_user(greeting):\n", - " print(f\"{greeting}, {name}\")\n", - "\n", - "greet_user(\"Hello\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Are you surprised that worked? If not, go read it again ...\n", - "\n", - "The `name` variable is not in the argument list of the function, so the function should not have been able to run. But it did ... because it\n", - "got the variable from outside the function. This behavior is called a `closure`, and it is very, very useful. But it will also cause problems if \n", - "you aren't careful. \n", - "\n", - "The lessons is: always include the variables you use in your function in your argument list, unless you know why you want a closure. ( And you don't yet! ) " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exceptions\n", - "\n", - "Sometimes, things go wrong. You can call these problems \"errors\" but they are\n", - "often known as \"exceptional conditions\" because they aren't the usual thing that happens. \n", - "For instance, what if we try to make an iteger of something that can't be an integer?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "int(\"This is not an integer\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The 'ValueError' part of the message is called an exception. It isn't just a message, its a thing we can use in our program to handle errors. For instance, suppose we have a list of things to convert to integers: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for e in [0,1, 65, 'Bob', 23,'larry']:\n", - " i = int(e)\n", - " print(f\"Converting {e} to an integer {i}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Well, we didn't get very far through the list. But, we can \"catch\" the exception and do something with it. It works like this:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for e in [0,1, 65, 'Bob', 23,'larry']:\n", - "\n", - " try:\n", - " i = int(e)\n", - " print(f\"Converting {e} to an integer {i}\")\n", - " except ValueError:\n", - " print(f\"Could not convert {e} to an integer\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Using the `try/except` structure, we can do something different if an error is\n", - "raised. This is a very advanced feature, and there is a lot you can do with it,\n", - "but for now, remember this structure, and remember that the part that goes after\n", - "the `except` is the name of the exception that you see in the error. Other types\n", - "of errors have other types of exceptions. \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run me! Uncomment one of the code lines to see the error it produces\n", - "# Put the comment back to see another error\n", - "# Index a list with a string\n", - "\n", - "my_list = [1,2,3,4,5]\n", - "\n", - "#print(my_list['Bob']) # TypeError: list indices must be integers or slices, not str\n", - "\n", - "#print(my_list[20]) # IndexError: list index out of range\n", - "\n", - "# assert False # AssertionError ( Although you don't usually catch assertions ) \n", - "\n", - "# x = 10 / 0 # ZeroDivisionError: division by zero\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you want to catch multiple exceptions ways to do it, and if you do, you will usually also want to name the exception. We've provided an exception name in the example below; each exceptino is named `e`.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# You can list the different types of different `except` clauses:\n", - "\n", - "try:\n", - " pass# do something\n", - "except ValueError as e:\n", - " print(f\"Got a value error {e}\")\n", - "except TypeError as e:\n", - " print(f\"Got a type error {e}\")\n", - "except ZeroDivisionError as e:\n", - " print(f\"Got a zero division error {e}\")\n", - "\n", - "# Or, you can use the \"superclass\" Exception to catch all exceptions\n", - "# But there are a lot of reasons to *not* do this\n", - "\n", - "try:\n", - " pass# do something\n", - "except Exception as e:\n", - " # Get all types of exceptions. \n", - " print(f\"Got an exception {e}\")" - ] - }, + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Functions and Data Structures**\n", + "\n", + "We've seen functions a few times, but haven't explained how they work in detail, so let's explore them further." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# First, we define the function:\n", + "def add_ten(x):\n", + " return x + 10\n", + "\n", + "# Then we can call the function like this:\n", + "result = add_ten(5)\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are several components that make up a function, you can refer to the table below for a quick overview.\n", + "\n", + "| Component | Example | Description |\n", + "|-----------|---------|-------------|\n", + "| **Name** | `add_ten` | Comes right after `def` |\n", + "| **Arguments** | `(x)` | Input values that come after the name |\n", + "| **Body** | Indented block | The code that runs when the function is called |\n", + "| **Return value** | `return x + 10` | The output of the function (defaults to `None` if not specified) |\n", + "\n", + "However, not all functions have these components. Let's look at a function that does not have any arguments or a return value." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "def do_nothing():\n", + " pass # 'pass' is just a placeholder that does nothing\n", + "\n", + "result = do_nothing()\n", + "print(\"Return value:\", result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Since `do_nothing()` has no `return` statement, it returns the empty value `None`. The `pass` keyword tells Python not to do anything at all. It's just a placeholder that reserves space and maintains a properly indented body.\n\n### **Why Use Functions?**\n\nFunctions make code more organized and easier to maintain by letting you reuse the same logic multiple times with different inputs, avoid duplication, break down complex problems into smaller pieces, and give meaningful names to code blocks. Using descriptive function names helps you and others understand what the code does when you revisit it later.\n\n> **Reminder:** By the way, when was the last time you checked in your code? Take a look at this documentation if you forgot how it's done.\n\n### **Function Arguments**\n\nArguments are values you pass into a function. You first name the arguments in the function definition (the argument list), then when calling the function, you can pass values to those named arguments *positionally* (in order) or by *name* using keywords (in any order). \n\nLet's see how this works:" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "def greet_user(name, greeting):\n", + " print(f\"{greeting}, {name}!\")\n", + "\n", + "# Method 1: Positional arguments (pass values in the order they're defined)\n", + "greet_user(\"Alice\", \"Bonjour\") # 'Alice' → name, 'Bonjour' → greeting\n", + "\n", + "# Method 2: Keyword arguments (pass values by name, order doesn't matter)\n", + "greet_user(greeting=\"Hello\", name=\"Alice\") # name and greeting specified by name\n", + "\n", + "# Method 3: Mix positional and keyword (positional MUST come first)\n", + "greet_user(\"Bob\", greeting=\"Hello\") # 'Bob' is positional, greeting is keyword" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, you can't have a positional argument after a keyword argument when calling a function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "greet_user(name = \"Bob\", greeting)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You also can't specify an argument more than once." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "greet_user(\"Bob\", name=\"Bob\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or skip an argument that doesn't have a default value." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "greet_user(\"Bob\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A good practice is to always put positional arguments first, followed by keyword arguments, and avoid mixing them unless necessary, otherwise you may frequently end up with a TypeError or SyntaxError when you try to run the code. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Setting Default Arguments**\n", + "\n", + "You can specify *default values* for arguments. Arguments with defaults are optional, but *all default arguments must come at the end* of the parameter list." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "def greet_user(name, greeting='Hello', punct=\"!\"):\n", + " print(f\"{greeting}, {name}{punct}\")\n", + "\n", + "# Use default values for greeting and punct\n", + "greet_user(\"Alice\")\n", + "\n", + "# Override just the punct default\n", + "greet_user(\"Alice\", \"Hello\", \".\")\n", + "\n", + "# Override greeting but use default punct\n", + "greet_user(\"Bob\", \"Hey\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> **Note:** When calling the function, if you don't provide a value for a default argument, the default value will be used." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Declaring Variables in Functions**\n", + "\n", + "Variables used in a function must also be *declared* — either as arguments or within the function — before they can be used. \n", + "\n", + "Let's look at an example that demonstrates this concept." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "def greet_user(name, greeting='Hello'):\n", + " print(f\"{greeting}, {name}{punct}\")\n", + "\n", + "greet_user(\"Alice\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Oops! Python didn't know what the variable `punct` was, so it couldn't run the function and raised a NameError.\n\n> **Tip:** Always declare all variables you use in your function, either in the argument list or within the function body." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Test Yourself**\n", + "\n", + "Write a function that searches for a character in a string and returns its position (index). If the character is found, print a message and return the index where it first appears. If it's not found, print a message and return `-1`.\n", + "\n", + "**Examples:**\n", + "\n", + "```python\n", + "find_char('x', 'My fox likes bricks') == 5\n", + "find_char('z', 'There is a zebra in the garden') == 11\n", + "find_char('w', \"I've lost my shoes\") == -1\n", + "```\n", + "\n", + "**Hint:** Use `enumerate()` to loop through the string and get both the index and character at each position." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Test yourself!\n", + "\n", + "# Write your own find_char function that takes a character and a string\n", + "def find_char(character, string):\n", + " ...\n", + "\n", + "# Test cases\n", + "assert find_char('x', 'My fox likes bricks') == 5\n", + "assert find_char('z', 'There is a zebra in the garden') == 11\n", + "assert find_char('w', \"I've lost my shoes\") == -1\n", + "\n", + "print(\"\\nAll tests passed!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Composition**\n", + "\n", + "You aren't limited to passing simple values into functions — you can pass any **expression** that evaluates to a value, including the output of other functions! This is called **function composition**, which allows you to write cleaner and more modular code.\n", + "\n", + "For instance, suppose you have these two functions that both return values:" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "def f(a, b):\n", + " return a + b\n", + "\n", + "def g(c, d):\n", + " return c + d" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can actually pass the output of `f()` directly into `g()`:" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Test Yourself\n", - "\n", - "Write a program that runs in a endless loop. Get a string from the user using\n", - "`input()` and convert it to an integer. If the user's input cannot be converted\n", - "to an integer ( and the user didn't enter 'q' ) check to see if the number is\n", - "in the list. If it is, print \"You got one!\". Exit the loop if the user enters\n", - "'q'. Report an error if the user's string cannot be converted to a number. \n", - "\n", - "If the user's number is not in the list, tell the user what the n'th number is\n", - "in the list is. That is, if the user enters `7`, and `7` is not in the list,\n", - "then tell the user '7 is not in the list, but the 7th number is the list is X',\n", - "where X is the 7th number. Be sure to handle the case where the list is shorter\n", - "than 7 elements, using an exception. \n", - "\n", - "You can check if something is in a list with `in`, like this:\n", - "\n", - "```python \n", - "if 5 in [1,2,3,4,5]:\n", - " print(\"It's In!\")\n", - "```" + "data": { + "text/plain": [ + "10" ] - }, + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Run Me!\n", + "\n", + "# Passing output of f() into g()\n", + "g(f(1, 2), f(3, 4)) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This calls `f(1, 2)`, which returns `3`, and then calls `g(3, 4)`, which returns `7`, and then adds them together to get `10`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Another way you can apply composition is to nest the expressions inside of the argument list:" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test Yourself\n", - "\n", - "# Check if the user's numbers are in this list:\n", - "\n", - "l = [5,10,45, 56]\n", - "\n", - "# Forever loop until the user enters a number in the list\n", - "\n", - " # Get a number from the user. \n", - "\n", - " # If the user enteres 'q', exit the loop\n", - "\n", - " # If the user's number is in the list, print \"Found it!\" and contine the loop\n", - "\n", - " # If the user's number is not in the list, find the n'th number in the list. Print it and continue the loop\n" + "data": { + "text/plain": [ + "17" ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - }, - "syllabus": { - "uid": "4LyScnS5" - } + ], + "source": [ + "# Run Me!\n", + "\n", + "# Nesting functions with arithmetic expressions:\n", + "g(f(1, 2) * f(3, 4), f(5, 6) - f(7, 8)) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Python evaluates this expression from the inside out. First, it calls each `f()` function and gets back: $3$, $7$, $11$, and $15$. \n", + "\n", + "Now it does the math: $3 * 7 = 21$ and $11 - 15 = -4$, finally passing these results to `g(21, -4)`, giving us $17$.\n", + "\n", + "> **Tip:** Function composition can sometimes make code harder to read if overused or nested too deeply. Use it judiciously to maintain clarity." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Test Yourself**\n", + "\n", + "Write three functions to handle each of the following operations: adding two numbers and returning the sum, subtracting the second number from the first and returning the result, and comparing two numbers and printing `\"Same\"` or `\"Different\"` depending on whether they match.\n", + "\n", + "Once you have your functions, use them to demonstrate that $2 + 2 = 4$, $(5 + 3) - 2 = 6$, and that $2 + 2$ differs from $3 + 3$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Test Yourself\n", + "\n", + "# Write three functions here:\n", + "...\n", + "\n", + "# Use your functions to demonstrate the following:\n", + "# a) 2 + 2 = 4\n", + "# b) (5 + 3) - 2 = 6\n", + "# c) 2 + 2 != 3 + 3 = 6\n", + "\n", + "# Demonstrate everything here:\n", + "..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Closures**\n", + "\n", + "Here's something surprising — this function works even though `name` is not in the argument list:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "name = 'Bob'\n", + "\n", + "def greet_user(greeting):\n", + " print(f\"{greeting}, {name}\")\n", + "\n", + "greet_user(\"Hello\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Even though the `name` variable was not in the argument list, outside of the function's scope and it shouldn't have been able to run, it did because Python was able to grab the variable from outside the function. This behavior is called a **closure**, and while it's very useful, it can also cause problems if you're not careful.\n\n> **Tip:** Always include the variables you use in your function in your argument list, unless you specifically want a closure. Closures are advanced, and you should avoid them until you understand why you need one." + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" }, - "nbformat": 4, - "nbformat_minor": 2 -} + "syllabus": { + "name": "Functions", + "uid": "4LyScnS5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb b/lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb deleted file mode 100644 index 9c36766a..00000000 --- a/lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb +++ /dev/null @@ -1,563 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Data Structures\n", - "\n", - "We've seen a lot of data structures so far, but we haven't put them all together. So far we've seen: \n", - "\n", - "* Lists. A list of items. \n", - "* Tuples. Like a list, but can't be changed after it is created. ( it's \"immutable\" )\n", - "* Strings. Like a list, but always made of characters, and is also \"immutable\"\n", - "\n", - "Lets introduce a new one: the `set`. A set is important because it works a bit\n", - "like a list, except:\n", - "\n", - "* You create a set with \"{}\" instead of \"[]\"\n", - "* Each item can only be in a set once. \n", - "* The items in a set are not ordered. \n", - "\n", - "If you put multiple items into a set, it will only store one of each, and if you\n", - "iterate over the items in a list, they aren't guaranteed to be in the same order\n", - "you put them in. Let's compare a set and a list. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l = ['a','a','b','b','c','c']\n", - "print(l)\n", - "\n", - "s = { 'a','a','b','b','c','c'}\n", - "print(s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that the list kept all of the items we put into it, and they are in the\n", - "same order, but the set removed the duplicates, and they are in a different\n", - "order. \n", - "\n", - "The formal name of these objects, string, set, list and tuple, are \"Collections\".\n", - "\n", - "# Creating Sets, Lists, Tuples\n", - "\n", - "So far we've seen one way for creating collections, using the braces, parentheses and quotes: \n", - "\n", - "```python\n", - "c = \"123\"\n", - "t = (1,2,3)\n", - "l = [1,2,3]\n", - "s = {1,2,3}\n", - "```\n", - "\n", - "But, there is another way! You can also use the 'constructor' function. Here is how we can create empty collections :\n", - "\n", - "```python \n", - "c = str()\n", - "t = tuple()\n", - "l = list()\n", - "s = set()\n", - "```\n", - "\n", - "These functions all can take one argument, an iterator, and that iterator can be\n", - "another set, list tuple. This means that you can easily convert between them. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "c = \"Hello\"\n", - "l = ['a','a','b','b','c','c']\n", - "\n", - "# Make a tuple from a list\n", - "t = tuple(l)\n", - "print(t)\n", - "\n", - "# Make a set from a list\n", - "s = set(l)\n", - "print(s)\n", - "\n", - "# Make a list from a string\n", - "a = list(c)\n", - "print(a)\n", - "\n", - "# Get the unique items from a list, by converting it to a set\n", - "# and then back to a list\n", - "print(list(set(l)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When you create an empty collection, you can sometimes add items to the\n", - "collection. Lists and sets are \"mutable\", which means they can be changed.\n", - "Tuples and strings are immutable, they cannot be changed after they are created.\n", - "However, you can \"concatenate\" to immutable objects to create new ones. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Adding to mutable collections\n", - "\n", - "l = list()\n", - "l.append('a')\n", - "l.append('b')\n", - "l.append('c')\n", - "print(l)\n", - "\n", - "s = set()\n", - "s.add('a')\n", - "s.add('b')\n", - "s.add('c')\n", - "print(s)\n", - "\n", - "# Concatenating immutable collections to create new objects\n", - "\n", - "t = tuple()\n", - "t = t + ('a',) # Note the comma, this is a tuple with one element\n", - "t = t + ('b',)\n", - "t = t + ('c',)\n", - "print(t)\n", - "\n", - "s = \"\"\n", - "s = s + \"a\"\n", - "s = s + \"b\"\n", - "s = s + \"c\"\n", - "print(s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When we write `s = s + 'a'` this means:\n", - "\n", - "1. Combine `s` and \"a\" to get a new string\n", - "2. Assign that new string back to s\n", - "\n", - "This operation will destroy the old `s` and create a new one with the same name.\n", - "This is very different that using `list.append()` or `set.add()` because those\n", - "methods keep the same list and set and just add to it. \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dictionaries\n", - "\n", - "You know what a dictionary is, right? It has words and definitions, and you look\n", - "up the words to find the definitions. Here is how we create a dictionary in\n", - "Python: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Dictionary example\n", - "\n", - "# A dictionary of words for superior people\n", - "\n", - "superior_words = {\n", - " \"abecedarian\": \"a person who is learning the alphabet\",\n", - " \"blandishment\": \"flattering speech or actions designed to persuade\",\n", - " \"cacophony\": \"a harsh, discordant mixture of sounds\",\n", - " \"defenestration\": \"the act of throwing someone out of a window\",\n", - " \"egregious\": \"outstandingly bad; shocking\",\n", - " \"flagitious\": \"criminal; villainous\",\n", - " \"grandiloquent\": \"pompous or extravagant in language, style, or manner\",\n", - " \"hirsute\": \"hairy\",\n", - " \"ignominious\": \"deserving or causing public disgrace or shame\",\n", - " \"juxtapose\": \"to place side by side for contrast or comparison\",\n", - " \"sesquipedalian\": \"given to using long words\",\n", - " \"xerebrose\": \"dry, uninteresting\"\n", - "}\n", - "\n", - "# one way to look up a word, using the key and \"[]\"\n", - "word = \"cacophony\"\n", - "definition = superior_words[word]\n", - "print(f\"{word}: {definition}\")\n", - "\n", - "# another way to look up a word, using `.get()`\n", - "word = \"xerebrose\"\n", - "definition = superior_words.get(word)\n", - "print(f\"{word}: {definition}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The \"{}\" curly braces are used to create both `dict` and `set` objects. The difference is that the `dict` definition has pairs separated by `:`, which the set does not. So this is a set: \n", - "\n", - "```python\n", - "s = { 1, 2, 3, 4}\n", - "```\n", - "\n", - "but this is a dict:\n", - "\n", - "```python \n", - "d = { \n", - " 'a': 1, \n", - " 'b': 2,\n", - " 'c': 3\n", - " }\n", - "```\n", - "\n", - "And like the other containers, you can also use a constructor function and then add items:\n", - "\n", - "```python \n", - "d = dict()\n", - "d['a'] = 1\n", - "d['b'] = 2\n", - "d['c'] = 3\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The dictionary has a feature like the set: its keys ( the word part of the word/definition pair ) is also unique. So if you add\n", - "a key twice, it will only be stored once:\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "d = { \n", - " \"one\": 1, \n", - " \"one\": 10, \n", - " \"two\": 2,\n", - " \"two\": 20,\n", - " \"three\": 3,\n", - " \"three\": 30\n", - " }\n", - "\n", - "print(d) # Only stores the last value for the key" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Hmmm ... we could use constructor functions to convert between other containers ... what happens if you try to convert a dict to another\n", - "container type?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "d = { \n", - " 'a': 1, \n", - " 'b': 2,\n", - " 'c': 3\n", - " }\n", - "\n", - "l = list(d)\n", - "print(l)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Hmmm ... it just used the keys? What happened to the values ( the \"definitions\" ) ? Well, you\n", - "need to use other methods to get those. Here are some of the access methods: \n", - "\n", - "* `dict.keys()`: Get only the keys.\n", - "* `dict.values()`: Get only the values. \n", - "* `dict.items()`: Get the keys and values as a collection of tuples. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Accessing dict keys and values\n", - "\n", - "d = { \n", - " 'a': 1, \n", - " 'b': 2,\n", - " 'c': 3\n", - "}\n", - "\n", - "print(d.keys())\n", - "print(d.values())\n", - "print(d.items())\n", - "\n", - "print()\n", - "\n", - "# Iterate over items in a dictionary\n", - "\n", - "for key, value in d.items():\n", - " print(f\"{key} = {value}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Pay attention to this idiom: \n", - "\n", - "```python \n", - "for key, value in d.items():\n", - " print(f\"{key} = {value}\")\n", - "```\n", - "\n", - "This is one of the very common operations with a dict, iterating over keys and\n", - "values. You should also know how to get an index for the iteration, using\n", - "`enumerate()`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Enumerate keys and values\n", - "\n", - "for index, (key, value) in enumerate(d.items()):\n", - " print(f\"#{index} {key} = {value}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Removing items\n", - "\n", - "You can also remove items from collections:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l = list(\"abcd\") # Make a list of characters from a string\n", - "\n", - "# Remove 'c' from the list\n", - "l.remove('c')\n", - "print(l)\n", - "\n", - "s = set(\"abcd\")\n", - "# Remove 'c' from the set\n", - "s.remove('c')\n", - "print(s)\n", - "\n", - "d = {\n", - " 'a': 1,\n", - " 'b': 2,\n", - " 'c': 3\n", - "}\n", - "\n", - "# To remove from a dict, use `del`\n", - "del d['c']\n", - "print(d)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Is it in there?\n", - "\n", - "You can use `in` to see if an item is in a collection. Use `not in` to check if it is not in the collection. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Check and see if something is in a collection\n", - "\n", - "l = list(\"abcd\")\n", - "print('a' in l, 'f' in l, 'g' not in l) # 'a' is in, but 'f' is not\n", - "\n", - "s = set(l)\n", - "print('a' in s, 'f' in s, 'g' not in s)\n", - "\n", - "d = {\n", - " 'a': 1,\n", - " 'b': 2,\n", - " 'c': 3\n", - "}\n", - "\n", - "# For dicts, 'in' checks for the existence of a key\n", - "print('a' in d, 'f' in d, 'g' not in d)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Test Yourself\n", - "\n", - "Write a function, `check_funny_words(sentence)` to check if any of these words are in a sentence:\n", - "\n", - "* snollygoster\n", - "* skedaddle\n", - "* lollygag\n", - "* collywobble\n", - "\n", - "If the sentence has funny words, return \"Funny\" and a list of the funny word.\n", - "If not, return \"not funny\"\n", - "\n", - "Write a loop to call your function on each of the sentences in `funny_sentences`\n", - "and print the return value of the function " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "funny_sentences = [\n", - " \"The snollygoster tried to skedaddle before anyone noticed his mischief.\",\n", - " \"After a day of lollygagging, the children suddenly got the collywobbles from all the candy.\",\n", - " \"A kerfuffle broke out when the gobbledygook in the instructions confused everyone.\",\n", - " \"The politician was such a snollygoster that he could bamboozle anyone without breaking a sweat.\",\n", - " \"The nincompoop tried to bamboozle everyone with his ridiculous story.\",\n", - " \"We decided to skedaddle from the park when we saw the kids starting to lollygag near the mud puddles.\",\n", - " \"The sudden collywobbles made him want to skedaddle from the roller coaster line.\",\n", - " \"The teacher was flummoxed by the students' whippersnapper antics during the lesson.\"\n", - "]\n", - "\n", - "def check_funny_words(sentence):\n", - " \"\"\"\n", - " Checks if any funny words are present in the given sentence.\n", - "\n", - " Args:\n", - " sentence (str): The sentence to check for funny words.\n", - "\n", - " Returns:\n", - " str: If funny words are found, returns a string with the funny words separated by commas.\n", - " If no funny words are found, returns \"Not funny\".\n", - " \"\"\"\n", - " \n", - " # IMPLEMENT ME!\n", - "\n", - "for s in funny_sentences:\n", - " print(check_funny_words(s))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# How big is it?\n", - "\n", - "Use `len()` to see how many items are in a collection.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "c = \"Hello\"\n", - "l = list(c) # Make a list of characters from a string\n", - "\n", - "print(len(c), len(l))\n", - "\n", - "d = { \n", - " 'a': 1, \n", - " 'b': 2,\n", - " 'c': 3\n", - " }\n", - "\n", - "print(len(d))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Sorting collections\n", - "\n", - "Sorting puts the items in the collection into order. For numbers, that means\n", - "numerical order, and for string, alphabetic order. You can sort lists, but for\n", - "immutable collections you will produce a new collection that is sorted, while\n", - "the original collection will remain unsorted. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l = list(\"gqycprc\")\n", - "\n", - "# Sort the list\n", - "l.sort()\n", - "print(l)\n", - "\n", - "# Use the sorted function to return a new sorted list\n", - "l = list(\"gqycprc\")\n", - "sorted_l = sorted(l)\n", - "print(l)\n", - "print(sorted_l)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - }, - "syllabus": { - "uid": "VXQdZcqg" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/lessons/40_Data_Structures_Func/20_Exceptions.ipynb b/lessons/40_Data_Structures_Func/20_Exceptions.ipynb new file mode 100644 index 00000000..a93be699 --- /dev/null +++ b/lessons/40_Data_Structures_Func/20_Exceptions.ipynb @@ -0,0 +1,294 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3626c1c1", + "metadata": {}, + "source": [ + "## **Exceptions**\n", + "\n", + "Sometimes things go wrong in your code. When that happens, Python raises an **exception**, which is a special kind of error that your program can handle.\n", + "\n", + "For example, what if we try to convert something to an integer that can't be converted?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39e40772", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "int(\"This is not an integer\")" + ] + }, + { + "cell_type": "markdown", + "id": "69ecbb3f", + "metadata": {}, + "source": [ + "The ValueError in the message is the exception type, but it's not just a message, we can actually use the type in our program to *handle errors* gracefully. \n", + "\n", + "Now instead, suppose we have a list of parameters, but we somehow forgot to check if they can actually be converted before using them:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f981b50c", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "for e in [0, 1, 65, 'Bob', 23, 'larry']:\n", + " i = int(e)\n", + " print(f\"Converting {e} to an integer: {i}\")" + ] + }, + { + "cell_type": "markdown", + "id": "5fe3fb51", + "metadata": {}, + "source": "That didn't work. The program tried to convert `Bob` to an integer and that caused an exception to be thrown, which led to a crash before the loop could finish.\n\nWhat if we could catch that exception using a try/except block and provide instructions on what to do when an error occurs?\n\nLet's take a look at how that works:" + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1b5e6046", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Using try/except to handle errors gracefully\n", + "for e in [0, 1, 65, 'Bob', 23, 'larry']:\n", + " try: # First attempt to convert e to an integer\n", + " i = int(e)\n", + " print(f\"Converting {e} to an integer: {i}\")\n", + " except ValueError: # If a ValueError occurs, handle it with this block\n", + " print(f\"Could not convert {e} to an integer\")" + ] + }, + { + "cell_type": "markdown", + "id": "78a4621e", + "metadata": {}, + "source": "Notice how the program continued running even after encountering an invalid parameter. The `try` block contains the code that might raise an exception, whereas the `except` block contains code that runs if an exception occurs. In this case, we successfully caught the ValueError, printed a message, and stopped the program from crashing.\n\n> **Note:** Handling exceptions is almost like having programs debug themselves or provide valuable debugging information, allowing programmers to analyze errors at runtime. This technique is essential for programs that handle user input or external data that may not always be in a predictable format." + }, + { + "cell_type": "markdown", + "id": "35c0af0e", + "metadata": {}, + "source": [ + "### **Exception Types**\n", + "\n", + "Python has many exception types for different errors, but here are some of the most common ones: TypeError, IndexError, AssertionError, ZeroDivisionError, and ValueError.\n", + "\n", + "Let's try running the examples below to see what happens:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "210c965a", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "my_list = [1, 2, 3, 4, 5]\n", + "\n", + "print(my_list['Bob'])" + ] + }, + { + "cell_type": "markdown", + "id": "cceceb83", + "metadata": {}, + "source": [ + "This raised a TypeError because we attempted to ask for a string rather than an integer." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81bf2ecc", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "my_tuple = (1, 2, 3, 4, 5)\n", + "\n", + "print(my_tuple[20]) # IndexError: Index out of range" + ] + }, + { + "cell_type": "markdown", + "id": "68228f19", + "metadata": {}, + "source": [ + "If you look closely at the range of this tuple, it only goes from 1 to 5 (technically 0 to 4). Therefore, we got an IndexError because we tried to access an index that doesn't exist (e.g., `20`)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "096746ee", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "assert False # AssertionError: Assertion failed" + ] + }, + { + "cell_type": "markdown", + "id": "079ad81e", + "metadata": {}, + "source": [ + "The `assert` statement is used to test if a condition is true. If the condition is false, it raises an AssertionError." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e07b6b", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "x = 10 / 0 # ZeroDivisionError: Division by zero" + ] + }, + { + "cell_type": "markdown", + "id": "b9616b71", + "metadata": {}, + "source": [ + "Since dividing by zero is mathematically undefined, Python raises a ZeroDivisionError." + ] + }, + { + "cell_type": "markdown", + "id": "67384d43", + "metadata": {}, + "source": [ + "### **Catching Multiple Exception Types**\n", + "\n", + "You can handle different exception types differently, or catch all exceptions at once:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2f5ad99d", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Handling multiple exception types\n", + "\n", + "# Approach 1: Catch different exceptions separately\n", + "try:\n", + " pass # do something\n", + "except ValueError as e:\n", + " print(f\"Got a value error: {e}\")\n", + "except TypeError as e:\n", + " print(f\"Got a type error: {e}\")\n", + "except ZeroDivisionError as e:\n", + " print(f\"Got a zero division error: {e}\")\n", + "\n", + "# Approach 2: Catch ALL exceptions at once (not recommended, but sometimes useful)\n", + "try:\n", + " pass # do something\n", + "except Exception as e:\n", + " # Get all types of exceptions\n", + " print(f\"Got an exception: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "8ad6ad17", + "metadata": {}, + "source": [ + "> **Note:** You can technically use a general Exception clause to catch *all* exceptions, but it's usually better to be specific and instead use it to catch ones you are not aware of. This helps prevent you from accidentally hiding bugs." + ] + }, + { + "cell_type": "markdown", + "id": "0398ac9d", + "metadata": {}, + "source": [ + "## **Test Yourself**\n", + "\n", + "Write a program that:\n", + "\n", + "1. Runs in an endless loop until the user enters `'q'`.\n", + "2. Prompts the user for input with `input()`.\n", + "3. Trys to convert the input to an integer; if it fails (and it is not `'q'`), show an error.\n", + "4. If the conversion succeeds, checks whether the number is in the list `[5, 10, 45, 56]`.\n", + " - If it *is* in the list, prints \"Found it!\".\n", + " - If it is *not* in the list, tells the user what the n-th number *in the list* is (e.g., `\"7 is not in the list, but the 7th number in the list is x\"`).\n", + " - Use an `except` block to handle the case where the list is too short to have an n-th element.\n", + "5. Keep looping until the user enters `'q'`.\n", + "\n", + "**Hint:**\n", + "```python\n", + "if 5 in [1, 2, 3, 4, 5]:\n", + " print(\"It's in!\")\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "befe162a", + "metadata": {}, + "outputs": [], + "source": [ + "# Test Yourself\n", + "\n", + "# Check if the user's numbers are in this list:\n", + "my_list = [5, 10, 45, 56]\n", + "\n", + "# Forever loop until the user enters a number in the list\n", + "# TODO: Get a number from the user\n", + "# TODO: If the user enters `q`, exit the loop\n", + "# TODO: If the user's number is in the list, print \"Found it!\" and continue the loop\n", + "# TODO: If the user's number is not in the list, print the n'th number and continue the loop" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Exceptions", + "uid": "87Ie2JGb" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/lessons/40_Data_Structures_Func/30_Dicts_Sets.ipynb b/lessons/40_Data_Structures_Func/30_Dicts_Sets.ipynb new file mode 100644 index 00000000..915d65c9 --- /dev/null +++ b/lessons/40_Data_Structures_Func/30_Dicts_Sets.ipynb @@ -0,0 +1,587 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Data Structures**\n", + "\n", + "We've seen a lot of data structures so far, but we haven't put them all together. \n", + "\n", + "Here's a quick summary of the main ones we've covered:\n", + "| Data Structure | Description |\n", + "| :--- | :--- |\n", + "| **Lists** | An ordered, **mutable** collection that can hold any type of items and be modified after creation. |\n", + "| **Tuples** | An ordered collection like a list, but they are **immutable**, meaning they can't be changed after being created. |\n", + "| **Strings** | An ordered sequence of text characters that is **immutable** and cannot be modified after creation. |\n", + "\n", + "\n", + "Now let's introduce **sets**. \n", + "\n", + "A `set` is important because it works like a list with key differences:\n", + "\n", + "* You create a set with `{}` instead of `[]`\n", + "* *Each item can only appear once* — duplicates are automatically removed\n", + "* The items in a set are *not ordered* — iteration order is not guaranteed\n", + "\n", + "Let's compare a set and a list to see this in action:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "my_list = ['a','a','b','b','c','c']\n", + "print(\"List: \", my_list)\n", + "\n", + "my_set = { 'a','a','b','b','c','c'}\n", + "print(\"Set: \", my_set)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **What Happened?**\n", + "\n", + "Notice the differences:\n", + "- The *list* kept all items we put into it, including duplicates, in the same order\n", + "- The *set* automatically removed the duplicates and stored items in a different order (sets don't guarantee order)\n", + "\n", + ">**Note:** The formal name for these objects (string, set, list, and tuple) is **collections**.\n", + "\n", + "## **Creating Sets, Lists, Tuples**\n", + "\n", + "So far we've seen one way to create collections using braces, parentheses, and quotes:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Creating collections with values\n", + "my_string = \"123\"\n", + "my_tuple = (1,2,3)\n", + "my_list = [1,2,3]\n", + "my_set = {1,2,3}\n", + "\n", + "print(f\"{type(my_string)}: {my_string}\\n{type(my_tuple)}: {my_tuple}\\n{type(my_list)}: {my_list}\\n{type(my_set)}: {my_set}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But there's another way! You can also use the **constructor** functions to create empty collections:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Creating empty collections using constructors\n", + "my_string = str()\n", + "my_tuple = tuple()\n", + "my_list = list()\n", + "my_set = set()\n", + "\n", + "print(f\"{type(my_string)}: {my_string}\\n{type(my_tuple)}: {my_tuple}\\n{type(my_list)}: {my_list}\\n{type(my_set)}: {my_set}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "These constructor functions can take one argument—an **iterable**—making it easy to convert between collection types." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "my_string = \"Hello\" + \"World\"\n", + "my_list = ['a','a','b','b','c','c']\n", + "\n", + "# Make a tuple from a list\n", + "my_tuple = tuple(my_list)\n", + "print(\"Tuple from list:\", my_tuple)\n", + "\n", + "# Make a set from a list\n", + "my_set = set(my_list)\n", + "print(\"Set from list:\", my_set)\n", + "\n", + "# Make a list from a string\n", + "my_list_from_string = list(my_string)\n", + "print(\"List from string:\", my_list_from_string)\n", + "\n", + "# Get the unique items from a list by converting to set then back to list\n", + "print(\"Unique items:\", list(set(my_list)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "
    \n\n## **Reminder: Mutable vs. Immutable Collections**\n\nCollections are either mutable (can be modified) or immutable (cannot be modified):\n\n| Type | Collections | How to Modify |\n| :--- | :--- | :--- |\n| **Mutable** | Lists, Sets | Use `.append()`, `.add()`, `.remove()` |\n| **Immutable** | Tuples, Strings | Use **concatenation** (`+`) to create new objects |\n\n
    " + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Adding to MUTABLE collections\n", + "my_list = list()\n", + "my_list.append('a')\n", + "my_list.append('b')\n", + "my_list.append('c')\n", + "print(\"List:\", my_list)\n", + "\n", + "my_set = set()\n", + "my_set.add('a')\n", + "my_set.add('b')\n", + "my_set.add('c')\n", + "print(\"Set: \", my_set)\n", + "\n", + "# Concatenating IMMUTABLE collections to create new objects\n", + "my_tuple = tuple()\n", + "my_tuple = my_tuple + ('a',) # Note the comma — this creates a 1-element tuple\n", + "my_tuple = my_tuple + ('b',)\n", + "my_tuple = my_tuple + ('c',)\n", + "print(\"Tuple:\", my_tuple)\n", + "\n", + "my_string = \"\"\n", + "my_string = my_string + \"a\"\n", + "my_string = my_string + \"b\"\n", + "my_string = my_string + \"c\"\n", + "print(\"String:\", my_string)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Dictionaries**\n", + "\n", + "You know what a real dictionary is, right? A book that has *words* (**keys**) and *definitions* (**values**) that can be used to look up what they mean. In Python dictionaries work the same way but work with **key-value pairs**!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Dictionary example - a collection of superior words and their definitions\n", + "superior_words = {\n", + " \"abecedarian\": \"a person who is learning the alphabet\",\n", + " \"blandishment\": \"flattering speech or actions designed to persuade\",\n", + " \"cacophony\": \"a harsh, discordant mixture of sounds\",\n", + " \"defenestration\": \"the act of throwing someone out of a window\",\n", + " \"egregious\": \"outstandingly bad; shocking\",\n", + " \"flagitious\": \"criminal; villainous\",\n", + " \"grandiloquent\": \"pompous or extravagant in language, style, or manner\",\n", + " \"hirsute\": \"hairy\",\n", + " \"ignominious\": \"deserving or causing public disgrace or shame\",\n", + " \"juxtapose\": \"to place side by side for contrast or comparison\",\n", + " \"sesquipedalian\": \"given to using long words\",\n", + " \"xerebrose\": \"dry, uninteresting\"\n", + "}\n", + "\n", + "# Method 1: Look up a word using square brackets []\n", + "word = \"cacophony\"\n", + "definition = superior_words[word]\n", + "print(f\"{word}: {definition}\")\n", + "\n", + "# Method 2: Look up a word using .get()\n", + "word = \"xerebrose\"\n", + "definition = superior_words.get(word)\n", + "print(f\"{word}: {definition}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Dictionary vs Set Syntax**\n", + "\n", + "Both dictionaries and sets use curly braces `{}`, but they're created differently:\n", + "\n", + "| Collection | Syntax | Example | Description |\n", + "|-----------|--------|---------|-------------|\n", + "| **Set** | Single values | `my_set = {1, 2, 3, 4}` | Items only, no keys |\n", + "| **Dict** | Key-value pairs | `my_dict = {'a': 1, 'b': 2}` | Uses colons to separate keys from values |" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Dictionary Keys are Unique**\n", + "\n", + "Just like sets, dictionaries have unique **keys**. If you add a key twice, only the last value is stored:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "my_dict = { \n", + " \"one\": 1, \n", + " \"one\": 10, # This overwrites the previous \"one\" entry\n", + " \"two\": 2,\n", + " \"two\": 20, # This overwrites the previous \"two\" entry\n", + " \"three\": 3,\n", + " \"three\": 30 # This overwrites the previous \"three\" entry\n", + "}\n", + "\n", + "print(my_dict) # Only the LAST value for each key is stored" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Converting Dictionaries to Other Types**\n", + "\n", + "What happens when you convert a dictionary to another collection type?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "my_dict = { \n", + " 'a': 1, \n", + " 'b': 2,\n", + " 'c': 3\n", + "}\n", + "\n", + "my_list = list(my_dict)\n", + "print(my_list) # What happened to the values?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When you convert a dictionary to a list they only include *keys* but not *values*! \n", + "\n", + "To access both keys and values, use these dictionary methods:\n", + "\n", + "| Method | Returns | Example |\n", + "|--------|---------|---------|\n", + "| `dict.keys()` | Only the keys | `d.keys()` → `dict_keys(['a', 'b', 'c'])` |\n", + "| `dict.values()` | Only the values | `d.values()` → `dict_values([1, 2, 3])` |\n", + "| `dict.items()` | Key-value pairs as tuples | `d.items()` → `dict_items([('a', 1), ('b', 2), ('c', 3)])` |" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Accessing dict keys and values\n", + "my_dict = { \n", + " 'a': 1, \n", + " 'b': 2,\n", + " 'c': 3\n", + "}\n", + "\n", + "print(\"Keys: \", my_dict.keys())\n", + "print(\"Values:\", my_dict.values())\n", + "print(\"Items: \", my_dict.items())\n", + "\n", + "print()\n", + "\n", + "# A common pattern: Iterate over both key AND value in a dictionary\n", + "for key, value in my_dict.items():\n", + " print(f\"{key} = {value}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Unpacking Key-Value Pairs**\n", + "\n", + "Pay close attention to this idiom:\n", + "\n", + "```python \n", + "for key, value in d.items():\n", + " print(f\"{key} = {value}\")\n", + "```\n", + "\n", + "This is one of the *most common operations* with dictionaries — iterating over both keys and values at the same time. This pattern is called *unpacking*, where you extract both parts of each key-value pair.\n", + "\n", + "You can also get an index for the iteration using `enumerate()`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Enumerate keys and values\n", + "my_dict = { \n", + " 'a': 1, \n", + " 'b': 2,\n", + " 'c': 3\n", + "}\n", + "\n", + "# Enumerate gives us an index along with each item\n", + "for index, (key, value) in enumerate(my_dict.items()):\n", + " print(f\"#{index} {key} = {value}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Removing Items**\n", + "\n", + "You can remove items from most collections:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Remove from a list\n", + "my_list = list(\"abcd\")\n", + "my_list.remove('c')\n", + "print(\"List after remove:\", my_list)\n", + "\n", + "# Remove from a set\n", + "my_set = set(\"abcd\")\n", + "my_set.remove('c')\n", + "print(\"Set after remove: \", my_set)\n", + "\n", + "# Remove from a dict (use 'del' keyword)\n", + "my_dict = {\n", + " 'a': 1,\n", + " 'b': 2,\n", + " 'c': 3\n", + "}\n", + "\n", + "del my_dict['c']\n", + "print(\"Dict after del: \", my_dict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Checking if Items Exist**\n", + "\n", + "You will want to use the `in` operator to check if an item is inside of a collection, and `not in` to check if it's absent:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Check if something is `in` a collection\n", + "my_list = list(\"abcd\")\n", + "print(\"List:\", 'a' in my_list, 'f' in my_list, 'g' not in my_list)\n", + "\n", + "my_set = set(my_list)\n", + "print(\"Set: \", 'a' in my_set, 'f' in my_set, 'g' not in my_set)\n", + "\n", + "my_dict = {\n", + " 'a': 1,\n", + " 'b': 2,\n", + " 'c': 3\n", + "}\n", + "\n", + "# For dicts, 'in' checks for the existence of a KEY, not a value\n", + "print(\"Dict:\", 'a' in my_dict, 'f' in my_dict, 'g' not in my_dict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **How Big Is It?**\n", + "\n", + "Use `len()` to find how many items are in a collection:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "my_string = \"Hello\"\n", + "my_list = list(my_string)\n", + "\n", + "print(\"String length:\", len(my_string))\n", + "print(\"List length: \", len(my_list))\n", + "\n", + "my_dict = { \n", + " 'a': 1, \n", + " 'b': 2,\n", + " 'c': 3\n", + "}\n", + "\n", + "print(\"Dict length: \", len(my_dict))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Sorting Collections**\n", + "\n", + "Sorting puts items in a collection into order (numerical for numbers, alphabetical for strings). \n", + "\n", + "* *Lists* can be sorted in-place using `.sort()`\n", + "* *Immutable collections* must use `sorted()` to create a new sorted collection" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Modifying a list in-place with .sort()\n", + "l = list(\"gqycprc\")\n", + "l.sort()\n", + "print(\"Sorted list:\", l)\n", + "\n", + "# Using sorted() to create a new sorted list (original unchanged)\n", + "l = list(\"gqycprc\")\n", + "sorted_l = sorted(l)\n", + "print(\"Original: \", l)\n", + "print(\"Sorted copy:\", sorted_l)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Test Yourself**\n", + "\n", + "Write a function, `check_funny_words(sentence)` that checks if any of these words appear in a sentence:\n", + "\n", + "* `snollygoster`\n", + "* `skedaddle`\n", + "* `lollygag`\n", + "* `collywobble`\n", + "\n", + "**Return value:**\n", + "- If the sentence contains funny words: return `\"Funny\"` and a list of the funny words found\n", + "- If not: return `\"Not funny\"`\n", + "\n", + "**Then:** Write a loop to call your function on each sentence in `funny_sentences` and print the result." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "funny_sentences = [\n", + " \"The snollygoster tried to skedaddle before anyone noticed his mischief.\",\n", + " \"After a day of lollygagging, the children suddenly got the collywobbles from all the candy.\",\n", + " \"A kerfuffle broke out when the gobbledygook in the instructions confused everyone.\",\n", + " \"The politician was such a snollygoster that he could bamboozle anyone without breaking a sweat.\",\n", + " \"The nincompoop tried to bamboozle everyone with his ridiculous story.\",\n", + " \"We decided to skedaddle from the park when we saw the kids starting to lollygag near the mud puddles.\",\n", + " \"The sudden collywobbles made him want to skedaddle from the roller coaster line.\",\n", + " \"The teacher was flummoxed by the students' whippersnapper antics during the lesson.\"\n", + "]\n", + "\n", + "def check_funny_words(sentence):\n", + " \"\"\"\n", + " Checks if any funny words are present in the given sentence.\n", + "\n", + " Args:\n", + " sentence (str): The sentence to check for funny words.\n", + "\n", + " Returns:\n", + " str: If funny words are found, returns a string with the funny words separated by commas.\n", + " If no funny words are found, returns \"Not funny\".\n", + " \"\"\"\n", + " \n", + " # IMPLEMENT ME!\n", + "\n", + "for my_set in funny_sentences:\n", + " print(check_funny_words(my_set))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "name": "Dicts Sets", + "uid": "VXQdZcqg" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/40_Data_Structures_Func/30_Funny_Words_Db.py b/lessons/40_Data_Structures_Func/40_Funny_Words_Db.py similarity index 98% rename from lessons/40_Data_Structures_Func/30_Funny_Words_Db.py rename to lessons/40_Data_Structures_Func/40_Funny_Words_Db.py index d7b57f86..ed0b829e 100644 --- a/lessons/40_Data_Structures_Func/30_Funny_Words_Db.py +++ b/lessons/40_Data_Structures_Func/40_Funny_Words_Db.py @@ -1,11 +1,12 @@ -from guizero import App, Box, Text, TextBox, PushButton, ListBox, error - """ Funny Words Dictionary +uid: VenVwSQz +name: Funny Words Db + This program provides a graphical user interface (GUI) for managing a dictionary of funny words and their definitions. Users can add new definitions, delete -existing definitions, and view the list of definitions in a listbox. +existing definitions, and view the list of definitions in a listbox. The module uses the guizero library to create the GUI components and handle user interactions. It defines several functions for adding and deleting definitions, @@ -14,12 +15,14 @@ window will appear with input fields for entering a word and its definition. Clicking the 'Add' button will add the definition to the dictionary and update the listbox. Selecting a definition from the listbox and clicking the 'Delete -Selected' button will remove the definition from the dictionary. +Selected' button will remove the definition from the dictionary. The module has a limit of storing up to 5 definitions. If the limit is reached, an error message will be displayed and new definitions will not be added. """ +from guizero import App, Box, Text, TextBox, PushButton, ListBox, error + # Implement the functions below def add_definition(db, key, value): @@ -73,7 +76,7 @@ def is_funny(definition): Returns: bool: True if the definition contains any of the funny words, False otherwise. """ - + # Return True if the definition contains any of the funny words, False otherwise return False @@ -91,7 +94,7 @@ def update_listbox(db): l = [ "Item 1: Fake Definition 1", "Item 2: Fake Definition 2", - "Item 3: fake Definition 3" + "Item 3: Fake Definition 3" ] # Add each definition to a string @@ -107,7 +110,7 @@ def update_listbox(db): def _add_definition(): word = word_entry.value.strip() definition = definition_entry.value.strip() - + if word and definition: if is_funny(definition): @@ -164,4 +167,4 @@ def handle_enter(event): _update_listbox(db) # Initial update of listbox -app.display() \ No newline at end of file +app.display() diff --git a/lessons/40_Data_Structures_Func/40_Splat_Comprehension.ipynb b/lessons/40_Data_Structures_Func/40_Splat_Comprehension.ipynb deleted file mode 100644 index 06f40ad2..00000000 --- a/lessons/40_Data_Structures_Func/40_Splat_Comprehension.ipynb +++ /dev/null @@ -1,632 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Tic Tac Toe\n", - "\n", - "In the nex project, you will implemement a Tic Tac Toe game, but to write a Tic Tac Toe game, we will need to learn about multi-dimensional\n", - "arrays. A multidimensional array in Python is really just a list of lists:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# A 2 dimensional array is an array of arrays. Here is an example:\n", - "\n", - "row1 = [1, 2, 3]\n", - "row2 = [4, 5, 6]\n", - "row3 = [7, 8, 9]\n", - "\n", - "two_dimensional_array = [\n", - " row1, \n", - " row2, \n", - " row3\n", - " ]\n", - "\n", - "print(two_dimensional_array)\n", - "\n", - "# Or , The more compact usual way: \n", - "\n", - "two_dimensional_array = [\n", - " [1, 2, 3],\n", - " [4, 5, 6],\n", - " [7, 8, 9]\n", - " ]\n", - "\n", - "# Now we can use '[][]' to access the elements of the 2D array.\n", - "\n", - "print(two_dimensional_array[0][0]) # 1\n", - "print(two_dimensional_array[1][2]) # 6\n", - "print(two_dimensional_array[2][0]) # 7" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "THe `[][]` construct is for indexing in two dimensions. To understand the two\n", - "dimensional access, think about what the first and second access return. \n", - "\n", - "Can you guess what the first index on the array will return? What do you think this will print out?\n", - "\n", - "```python \n", - "\n", - "print( two_dimensional_array[1] )\n", - "\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Try it! \n", - "# See what print( two_dimensional_array[1] ) produces\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When you have a multi-dimensional array, the first index operation returns a list, which is the row. Then, the second index operations works on the row to return a column, so `[][]` expands to :\n", - "\n", - "```python\n", - "l = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]\n", - "row_num = 2\n", - "col_num = 1\n", - "\n", - "# The typical way:\n", - "v = l[row_num][col_num]\n", - "\n", - "# Expand it out\n", - "row = l[row_num]\n", - "v = row[col_num]\n", - "\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Who Won ?\n", - "\n", - "When implementing Tic Tac Toe the important thing we have to do is figure out who won. Who is the winner? It's the player who has 3 of the players tokens in any tow, column or diagonal. How do we get a row? Well, that's easy, it is just a single index on the board:\n", - "\n", - "```python \n", - "board = [\n", - " [1, 2, 3],\n", - " [4, 5, 6],\n", - " [7, 8, 9]\n", - " ]\n", - "\n", - "first_row = board[0]\n", - "\n", - "```\n", - "\n", - "That was easy .. but how do we get the first column, with all three values? You\n", - "can probably come up with a manual way, but we'll show you the most Pythonic\n", - "way: you transpose the board and take a row. Transposing a matrix means swapping\n", - "rows an columns, so if you transpose, accessing a row will actually get you a\n", - "column, and in Python we can do this with `zip()`\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def pretty_print_2d(a):\n", - " \"\" \"Prints a 2D array in a pretty way\" \"\"\n", - " for row in a:\n", - " print(row)\n", - "\n", - "board = [\n", - " [1, 2, 3],\n", - " [4, 5, 6],\n", - " [7, 8, 9]\n", - " ]\n", - "\n", - "pretty_print_2d(board)\n", - "print()\n", - "\n", - "transposed = list(zip(*board)) # <--- HERE IS THE MAGIC\n", - "pretty_print_2d(transposed)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "See, the zip operation turned rows into columns, and columns into rows: in the\n", - "original, \"[1,2,3]\" is across the top in in the bottom \"(1,2,3)\" is down the\n", - "side. But ... one difference, the top has \"[]\", so it is an array of lists, but\n", - "the bottom has '()', so it is an array of tuples. We'll fix that later, for now\n", - "let's learn how `zip()` transposes. \n", - "\n", - "Here is the line of code: \n", - "\n", - "```python \n", - "list(zip(*board))\n", - "```\n", - "\n", - "The first thing to talk about is the `*`. In this usage, it is known as the\n", - "'splat operator', and it is unpacking `board` into the argument list of the zip\n", - "function. The `board` variable is iterable, and the splat takes each item of \n", - "`board` and makes a seperate argument for it. So, this line of code is equavalent to:\n", - "\n", - "```python \n", - "list(zip(board[0],board[1],board[2])))\n", - "```\n", - "\n", - "And that is equivalent to:\n", - "\n", - "```python \n", - "list(zip([1, 2, 3],[4, 5, 6],[7, 8, 9])))\n", - "```\n", - "\n", - "Now what does `zip()` do? It takes all of the first items of it's aguments, then all of the second, then all of the third, etc. \n", - "Let's try that: \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for e in zip([1, 2, 3],[4, 5, 6],[7, 8, 9]):\n", - " print(e)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " See, the first tuple we get is the first item of each of the lists in the `zip()` arguments, the second item is the second, etc. \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If that is hard to see, lets color the numbers: \n", - "\n", - "code:\n", - "
    \n",
    -        "for e in zip([1, 2, 3], [4, 5, 6],[7, 8, 9]):\n",
    -        "    print(e)\n",
    -        "
    \n", - "\n", - "output:\n", - "
    \n",
    -        "(1, 4, 7)\n",
    -        "(2, 5, 8)\n",
    -        "(3, 6, 9)\n",
    -        "
    " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "But, we don't want a tuple of tuples, we want a list of lists, so we have to do some converstion. \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# First transpose:\n", - "t = zip(*board)\n", - "\n", - "l = []\n", - "# convert each row from tuple to list\n", - "for e in t:\n", - " l.append(list(e))\n", - "\n", - "pretty_print_2d(l)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Introducing Comprehensions\n", - "\n", - "That conversion code is kinda ugly, but we can make it much prettier and more Pythonic with a list comprehension. Here is what that looks like: \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l = [list(e) for e in zip(*board)]\n", - "pretty_print_2d(l)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You certainly recognize \"[]\" for making a list, but what's the code inside???? The basic syntax here is just a for loop, with an extra expression: \n", - "\n", - "```python \n", - "[ for item in list ]\n", - "```\n", - "\n", - "We know what the `for item in list` is, so we are really just adding the\n", - "`` part, and all that is doing is being added to a list. So these\n", - "two bits of code are the same: \n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# The old way to square every number:\n", - "\n", - "nums = [1, 2, 3, 4, 5]\n", - "\n", - "squared = []\n", - "for n in nums:\n", - " expression = n * n\n", - " squared.append(expression)\n", - "\n", - "print(squared)\n", - "\n", - "# The comprehension way:\n", - "\n", - "squared = [n * n for n in nums]\n", - "print(squared)\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So, the comprehension is really just a nicer syntax for a certain kind of `for` loop that appends items to a list. Thre is a lot more to comprehensions, of course, but this is enough to get started. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Yes, but Who Won?\n", - "\n", - "So now we know an easy way to find the columns to check who won: you write a\n", - "function to figure out who won by row, then you use that function first to chech\n", - "all of the rows, then transpose the board to check all of the columns. \n", - "\n", - "But, we still have to check the two diagonals. Let's look at the coordinates of the diagonals.\n", - "if your board is:\n", - "\n", - "```\n", - "[1, 4, 7]\n", - "[2, 5, 8]\n", - "[3, 6, 9]\n", - "```\n", - "\n", - "Then the two diagonals are: `[1,5,9]', and '[7,5,1]'. Let's try to use a comprehension to \n", - "find the diagonals: \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Text Yourself\n", - "# In the comprehensions below, replace the ...\n", - "# with code that will produce the expected output.\n", - "\n", - "board = [\n", - " [1, 2, 3],\n", - " [4, 5, 6],\n", - " [7, 8, 9]\n", - " ]\n", - "\n", - "# Uncomment below and replace the ... with code that will produce the expected output.\n", - "#d1 = [ board[...][...] for i in range(3) ]\n", - "#assert d1 == [1, 5, 9]\n", - "\n", - "#d2 = [ board[...][...] for i in range(3) ]\n", - "#assert d2 == [3, 5, 7]\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Hint: three of the `...` are all replaced by the same thing. It will help if\n", - "you list out the coordinates of the diagonals, which are (0,0), (1,1) .... If\n", - "you can't get the right comprehension, you can also construct a new 3 item\n", - "list from getting each dialgonal cell from the board with 2d indexing, \n", - "like ` l = [ board[0][0],board[1][1], ...]`\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Ok, now we have explained: \n", - "\n", - "* How to get each row as a list\n", - "* How top get each column as a list\n", - "* How to get both diagonals. \n", - "\n", - "Now let's look as some Pythonic ways to figure out who won. What things are true\n", - "about a row where 'X' won? Let's look as some example code that will maybe given you some ideas. \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x_won = ['x','x','x']\n", - "o_won = ['o','o','o']\n", - "r1 = ['x','x','o']\n", - "r2 = ['x','o','o'] \n", - "r3 = ['x','o','x'] \n", - "r4 = ['o','x','x'] \n", - "\n", - "# The all() function returns True if all elements of the iterable are true.\n", - "print('\\n1 ===')\n", - "print( all([e == 'x' for e in x_won]) ) \n", - "print( all([e == 'o' for e in x_won]) ) \n", - "print( all([e == 'x' for e in r1]) ) \n", - "\n", - "print('\\n2 ===')\n", - "print(set(x_won))\n", - "print(set(o_won))\n", - "print(set(r1))\n", - "print(set(r2))\n", - "\n", - "print('\\n3 ===')\n", - "print(len(set(x_won)))\n", - "print(len(set(o_won)))\n", - "print(len(set(r1)))\n", - "print(len(set(r2)))\n", - "\n", - "print('\\n4 ===')\n", - "print(set(x_won) == {'x'})\n", - "print(set(o_won) == {'o'})\n", - "print(set(r1) == {'x'})\n", - "print(set(r2) == {'x'})\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Does that give you any ideas? Read the code carefully and then select a way to figure out which play won for a row. \n", - "\n", - "# Test Yourself\n", - "\n", - "Write the functions described below, then test your functions on the provided test code. \n", - "\n", - "Hint: The logic value of `None` is `False`, but the logic value of 'x' and 'o' are both `True`\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x_wins_boards = [\n", - " # x wins in row 1\n", - " [\n", - " ['o','' ,'o'],\n", - " ['x','x','x'],\n", - " ['o','' ,''],\n", - " ],\n", - " # x wins in col 2\n", - " [\n", - " ['o','' ,'x'],\n", - " ['' ,'o','x'],\n", - " ['o','' ,'x'],\n", - " ],\n", - " # x wins in the first diagonal\n", - " [\n", - " ['x','' ,'o'],\n", - " ['' ,'x','o'],\n", - " ['o','' ,'x'],\n", - " ]\n", - "]\n", - "\n", - "o_wins_boards = [\n", - " # o wins in row 0\n", - " [\n", - " ['o','o','o'],\n", - " ['' ,'x',''],\n", - " ['x','' ,'x'],\n", - " ],\n", - " # o wins in col 1\n", - " [\n", - " ['x','o','x'],\n", - " ['' ,'o',''],\n", - " ['' ,'o','x'],\n", - " ],\n", - " # o wins in the second diagonal\n", - " [\n", - " ['x','' ,'o'],\n", - " ['' ,'o',''],\n", - " ['o','x','x'],\n", - " ]\n", - "]\n", - "\n", - "no_winner_boards = [\n", - " # No winner example 1\n", - " [\n", - " ['x','o','x'],\n", - " ['o','x','o'],\n", - " ['o','x','o'],\n", - " ],\n", - " # No winner example 2\n", - " [\n", - " ['x','o','x'],\n", - " ['x','o','o'],\n", - " ['o','x','x'],\n", - " ],\n", - " # No winner example 3\n", - " [\n", - " ['x','o','x'],\n", - " ['x','x','o'],\n", - " ['o','x','o'],\n", - " ]\n", - "]\n", - "\n", - "\n", - "# First, write a function to check if a player has won on a row, column, or diagonal.\n", - "# Ths function takes a 3 element iterable and returns the winner. \n", - "\n", - "\n", - "def check_row(l):\n", - " \"\"\"Check if a player won on a row\n", - " Args:\n", - " l: a 3 element iterable\n", - " \n", - " Returns:\n", - " The winner's token ( x or o ) if there is one, otherwise None\n", - " \"\"\"\n", - " \n", - " return None\n", - "\n", - "\n", - "# Now, write a function that takes a 2D array and checks if there is a winner.\n", - "# This function should call the check_winner function for each row, column, and diagonal.\n", - "\n", - "def check_board(board):\n", - " \"\"\"Check if a player has won on a board\n", - " Args:\n", - " board: a 3x3 2D array\n", - " \n", - " Returns:\n", - " The winner's token ( x or o ) if there is one, otherwise None\n", - " \"\"\"\n", - "\n", - " return None\n", - "\n", - "\n", - "# Next, write some test code to test your functions. You should start by testing rows, like this\n", - "# \n", - "\n", - "# board = x_wins_boards[0]\n", - "# check_row( board[0]) # Should be None\n", - "# check_row( board[1]) # Should be 'x'\n", - "#\n", - "# Test the rows to determine if your check_rows() function works. \n", - "# \n", - "# Then, do the same checks with check_board()\n", - "#\n", - "# Finally,write a loop to check that the winner of all of the x_wins_boards is 'x', \n", - "# the winner of all of the o_wins_boards is 'o', and the winner of all of the no_winner_boards is None.\n", - "# You might use a comprehension, like [ check_board(board) for board in x_wins_boards ] \n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Final Test\n", - "# If all of your functions are working this code should pass:\n", - "\n", - "assert all([ check_board(board) == 'x' for board in x_wins_boards ] )\n", - "assert all([ check_board(board) == 'o' for board in o_wins_boards ] )\n", - "assert all([ check_board(board) is None for board in no_winner_boards ] )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Make It Better!\n", - "\n", - "Once you get the `check_board(board)` function working, lets try to make it better. See if you can run just one loop through all of the rows, columns and diagonal and have just one return. Here is some code that will give you a hint: \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l = [\n", - " [1, 2, 3],\n", - " [4, 5, 6],\n", - " [7, 8, 9]\n", - " ]\n", - "\n", - "def transpose(a):\n", - " return list(zip(*a))\n", - "\n", - "m = l[:] # Copy the whole list \n", - "m.extend(transpose(l)) # Add all of the items from transpose to m, a bit like m += transpose(l)\n", - "\n", - "for e in m:\n", - " print(e)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You've done a lot of work! So it is definitely time to [check in your code.](https://curriculum.jointheleague.org/howto/checkin_restart.html)\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - }, - "syllabus": { - "uid": "mU94qia6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} \ No newline at end of file diff --git a/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb b/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb new file mode 100644 index 00000000..aa70c48d --- /dev/null +++ b/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb @@ -0,0 +1,650 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": "# **Splat, Zip, and Comprehensions**\n\nIn the next project, you'll build a Tic Tac Toe game! But first, we need to learn how to work with **multi-dimensional data**. Think of it as working with grids—like the 3 × 3 grid of a Tic Tac Toe board." + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# A 2-dimensional array is an array of arrays:\n", + "row_one = [1, 2, 3]\n", + "row_two = [4, 5, 6]\n", + "row_three = [7, 8, 9]\n", + "\n", + "two_dimensional_array = [\n", + " row_one, \n", + " row_two, \n", + " row_three\n", + "]\n", + "\n", + "print(two_dimensional_array)\n", + "\n", + "# Or, we can define it all at once: \n", + "two_dimensional_array = [\n", + " [1, 2, 3],\n", + " [4, 5, 6],\n", + " [7, 8, 9]\n", + " ]\n", + "\n", + "# Now we can access elements by specifying the row and column:\n", + "print(two_dimensional_array[0][0]) # 1\n", + "print(two_dimensional_array[1][2]) # 6\n", + "print(two_dimensional_array[2][0]) # 7" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `[][]` construct lets you access elements in a grid. Think of it as giving directions: first the row, then the column.\n", + "\n", + "To understand how this works, let's break it down step by step. What do you think the first index alone will give us?\n", + "\n", + "```python \n", + "print( two_dimensional_array[1] )\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Try it! \n", + "# See what print( two_dimensional_array[1] ) produces" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here's the key insight: when you use the first index, you get back an entire row (which is a list). Then the second index works on that row to pick out a specific column.\n", + "\n", + "Think of it like getting directions to a seat in a theater:\n", + "\n", + "```python\n", + "my_list = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]\n", + "row_num = 2\n", + "col_num = 1\n", + "\n", + "# The shortcut way:\n", + "v = my_list[row_num][col_num]\n", + "\n", + "# What's really happening step by step:\n", + "row = my_list[row_num] # First, find the row\n", + "v = row[col_num] # Then, find the seat in that row\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Who Won?**\n", + "\n", + "When implementing Tic Tac Toe, the important thing we have to do is figure out who the winner is. Well who is that? It's the player who has three tokens in any row, column, or diagonal. \n", + "\n", + "How do we get a row? Well, that's easy—it's just a single index on the board:\n", + "\n", + "```python \n", + "board = [\n", + " [1, 2, 3],\n", + " [4, 5, 6],\n", + " [7, 8, 9]\n", + " ]\n", + "\n", + "first_row = board[0]\n", + "```\n", + "\n", + "Now how do we get the first column and all three values? \n", + "\n", + "You can probably do this manually, but we'll show you how to **transpose** the board and take a row. \n", + "\n", + "Transposing a matrix means swapping rows and columns, so if you transpose, accessing a row will actually get you a column. \n", + "\n", + "In Python we can do this with `zip()`!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "def pretty_print_2d(a):\n", + " \"\"\"Prints a 2D array in a pretty way\"\"\"\n", + " for row in a:\n", + " print(row)\n", + "\n", + "board = [\n", + " [1, 2, 3],\n", + " [4, 5, 6],\n", + " [7, 8, 9]\n", + " ]\n", + "\n", + "pretty_print_2d(board)\n", + "print()\n", + "\n", + "transposed = list(zip(*board)) # <--- HERE IS THE MAGIC\n", + "pretty_print_2d(transposed)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Look at what happened. The `zip()` function flipped our grid:\n- In the original, `[1, 2, 3]` goes across the top row\n- After transposing, `(1, 2, 3)` goes down the first column!\n\nDid you see how the brackets changed from `[]` to `()`? \n\nThat's because `zip()` automatically creates tuples instead of lists. We'll fix this in a moment.\n\nFirst, let's understand how `zip()` performs this magic trick.\n\nHere is the syntax:" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "list(zip(*board)) # Basic usage of zip with splat operator" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "The key part is the `*` symbol. This is called the **splat operator**, and it \"unpacks\" the board.\n", + "\n", + "Imagine the `*` as saying \"take apart this container and spread out its contents as separate pieces.\" \n", + "\n", + "So instead of passing one big board to `zip()`, we pass each row separately, like this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "list(zip(board[0], board[1], board[2])) # Without splat operator" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And that is also equivalent to:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "list(zip([1, 2, 3], [4, 5, 6], [7, 8, 9])) # Zipping three lists together" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, what does `zip()` do? It's like a zipper that connects matching positions from different lists:\n", + "- It takes the 1st item from each list and groups them together\n", + "- Then the 2nd item from each list and groups them together \n", + "- And so on...\n", + "\n", + "Let's see this in action:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "for e in zip([1, 2, 3], [4, 5, 6], [7, 8, 9]):\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Now you can see exactly what `zip()` does.\n\nThe first tuple contains the first item from each list, the second tuple contains the second item from each list, and so on. \n\nTo make this pattern even clearer, let's look at this color-coded version:\n\n**Input:**\n
    \nfor e in zip([1, 2, 3], [4, 5, 6],[7, 8, 9]):\n    print(e)\n
    \n\n**Output:**\n
    \n(1, 4, 7)\n(2, 5, 8)\n(3, 6, 9)\n
    \n\nBut we don't want tuples—we want lists! \n\nSo we need to convert them:" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# First transpose:\n", + "my_tuple = zip(*board)\n", + "\n", + "my_list = []\n", + "# Convert each row from tuple to list\n", + "for e in my_tuple:\n", + " my_list.append(list(e))\n", + "\n", + "pretty_print_2d(my_list)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## **Introducing Comprehensions**\n\nThat conversion code is kind of **verbose**, but we can make it much cleaner with a **list comprehension**.\n\nHere's how:" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "my_list = [list(e) for e in zip(*board)]\n", + "pretty_print_2d(my_list)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You know that `[ ]` creates a list, but what's that code inside? \n", + "\n", + "A **list comprehension** is basically a compact for loop with a recipe:\n", + "\n", + "```python \n", + "[ for item in list ]\n", + "```\n", + "\n", + "The `` part is what gets added to the new list for each iteration. \n", + "\n", + "These two code snippets do the exact same thing:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# The old way to square every number:\n", + "nums = [1, 2, 3, 4, 5]\n", + "\n", + "squared = []\n", + "for n in nums:\n", + " expression = n * n\n", + " squared.append(expression)\n", + "\n", + "print(squared)\n", + "\n", + "# The simpler, comprehension way:\n", + "squared = [n * n for n in nums]\n", + "print(squared)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "So, a comprehension is really just a nicer syntax for a certain kind of `for` loop that appends items to a list. There's a lot more to comprehensions, of course, but this is enough to get started. \n\n> **Tip:** Comprehensions are popular in Python because they make code more concise and readable." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## **Yes, but Who Won?**\n\nNow we know an easy way to find the columns. Well that's easy! We just need to write a function to check who won by row, then we can use that same function to check all the rows. Then we can transpose the board and check all the columns (which are now rows!). \n\n> **Tip:** This might seem a bit confusing, rows becoming columns and columns becoming rows, but just remember that transposing flips the grid along its diagonal. Think of it like turning a square piece of paper 90 degrees. It's still the same piece of paper, just rotated.\n\nNow remember, we still need to check the two diagonals. Let's look at their coordinates.\n\nIf this is your board:\n\n```python\n[1, 2, 3]\n[4, 5, 6]\n[7, 8, 9]\n```\n\nThen the two diagonals are: `[1, 5, 9]` and `[3, 5, 7]`. \n\nLet's use what we've learned about comprehension to find these diagonals:" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Test Yourself!\n", + "# In the comprehensions below, replace the ...\n", + "# with code that will produce the expected output.\n", + "\n", + "board = [\n", + " [1, 2, 3],\n", + " [4, 5, 6],\n", + " [7, 8, 9]\n", + " ]\n", + "\n", + "# Uncomment below and replace the ... with code that will produce the expected output.\n", + "# diag1 = [ board[...][...] for i in range(3) ]\n", + "# assert diag1 == [1, 5, 9]\n", + "\n", + "# diag2 = [ board[...][...] for i in range(3) ]\n", + "# assert diag2 == [3, 5, 7]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "> **Hint:** Notice the pattern in the coordinates!\n\nFor the first diagonal (top-left to bottom-right):\n- Position `(0, 0)` → value `1`\n- Position `(1, 1)` → value `5` \n- Position `(2, 2)` → value `9`\n- Pattern: row and column numbers are the same!\n\nFor the second diagonal (top-right to bottom-left):\n- Position `(0, 2)` → value `3`\n- Position `(1, 1)` → value `5`\n- Position `(2, 0)` → value `7`\n- Pattern: row + column always equals 2!\n\n> **Tip:** If comprehensions feel tricky, that's okay. Start simple with something like: `l = [board[0][0], board[1][1], board[2][2]]`" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Checking for a Winner**\n", + "\n", + "Ok, so far we know how to get: \n", + "\n", + "* Each *row* as a list\n", + "* Each *column* as a list (by transposing)\n", + "* Both *diagonals*\n", + "\n", + "Now let's look at some more ways to figure out who won. \n", + "\n", + "If you think about it logically, what must be true about a row for 'X' to win? \n", + "\n", + "Let's look at some example code that will give you some ideas:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "x_won = ['x', 'x', 'x']\n", + "o_won = ['o', 'o', 'o']\n", + "row1 = ['x', 'x', 'o']\n", + "row2 = ['x', 'o', 'o'] \n", + "row3 = ['x', 'o', 'x'] \n", + "row4 = ['o', 'x', 'x'] \n", + "\n", + "# The all() function returns True if all elements match our condition\n", + "print('\\n1 === Using all() with a comprehension ===')\n", + "print( all([e == 'x' for e in x_won]) ) # True - all are 'x'\n", + "print( all([e == 'o' for e in x_won]) ) # False - not all are 'o'\n", + "print( all([e == 'x' for e in row1]) ) # False - not all are 'x'\n", + "\n", + "print('\\n2 === Using set() to find unique values ===')\n", + "print(set(x_won)) # {'x'} - only one unique value\n", + "print(set(o_won)) # {'o'} - only one unique value\n", + "print(set(row1)) # {'x', 'o'} - two unique values\n", + "print(set(row2)) # {'x', 'o'} - two unique values\n", + "\n", + "print('\\n3 === Checking the length of the set ===')\n", + "print(len(set(x_won))) # 1 - winner has only 1 unique value\n", + "print(len(set(o_won))) # 1 - winner has only 1 unique value\n", + "print(len(set(row1))) # 2 - mixed values, no winner\n", + "print(len(set(row2))) # 2 - mixed values, no winner\n", + "\n", + "print('\\n4 === Comparing sets directly ===')\n", + "print(set(x_won) == {'x'}) # True - only contains 'x'\n", + "print(set(o_won) == {'o'}) # True - only contains 'o') \n", + "print(set(row1) == {'x'}) # False - contains both 'x' and 'o'\n", + "print(set(row2) == {'x'}) # False - contains both 'x' and 'o'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each approach above shows a different way to detect a winner. So, just pick whatever strategy you like best!\n", + "- **Approach 1**: Check if all elements equal 'x' (or 'o')\n", + "- **Approach 2 & 3**: A winning row has only one unique value\n", + "- **Approach 4**: Compare the set directly to `{'x'}` or `{'o'}`\n", + "\n", + "## Test Yourself\n", + "\n", + "Write the functions described below, then test your functions on the provided test code. \n", + "\n", + "> **Hint:** The logic value of `None` and empty string `''` is `False`, but the logic value of `'x'` and `'o'` are both `True`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x_wins_boards = [\n", + " # x wins in row 1\n", + " [\n", + " ['o','' ,'o'],\n", + " ['x','x','x'],\n", + " ['o','' ,''],\n", + " ],\n", + " # x wins in col 2\n", + " [\n", + " ['o','' ,'x'],\n", + " ['' ,'o','x'],\n", + " ['o','' ,'x'],\n", + " ],\n", + " # x wins in the first diagonal\n", + " [\n", + " ['x','' ,'o'],\n", + " ['' ,'x','o'],\n", + " ['o','' ,'x'],\n", + " ]\n", + "]\n", + "\n", + "o_wins_boards = [\n", + " # o wins in row 0\n", + " [\n", + " ['o','o','o'],\n", + " ['' ,'x',''],\n", + " ['x','' ,'x'],\n", + " ],\n", + " # o wins in col 1\n", + " [\n", + " ['x','o','x'],\n", + " ['' ,'o',''],\n", + " ['' ,'o','x'],\n", + " ],\n", + " # o wins in the second diagonal\n", + " [\n", + " ['x','' ,'o'],\n", + " ['' ,'o',''],\n", + " ['o','x','x'],\n", + " ]\n", + "]\n", + "\n", + "no_winner_boards = [\n", + " # No winner example 1\n", + " [\n", + " ['x','o','x'],\n", + " ['o','x','o'],\n", + " ['o','x','o'],\n", + " ],\n", + " # No winner example 2\n", + " [\n", + " ['x','o','x'],\n", + " ['x','o','o'],\n", + " ['o','x','x'],\n", + " ],\n", + " # No winner example 3\n", + " [\n", + " ['x','o','x'],\n", + " ['x','x','o'],\n", + " ['o','x','o'],\n", + " ]\n", + "]\n", + "\n", + "\n", + "# First, write a function to check if a player has won on a row, column, or diagonal.\n", + "# Ths function takes a 3 element iterable and returns the winner. \n", + "\n", + "\n", + "def check_row(l):\n", + " \"\"\"Check if a player won on a row\n", + " Args:\n", + " l: a 3 element iterable\n", + " \n", + " Returns:\n", + " The winner's token ( x or o ) if there is one, otherwise None\n", + " \"\"\"\n", + " \n", + " return None\n", + "\n", + "\n", + "# Now, write a function that takes a 2D array and checks if there is a winner.\n", + "# This function should call the check_winner function for each row, column, and diagonal.\n", + "\n", + "def check_board(board):\n", + " \"\"\"Check if a player has won on a board\n", + " Args:\n", + " board: a 3x3 2D array\n", + " \n", + " Returns:\n", + " The winner's token ( x or o ) if there is one, otherwise None\n", + " \"\"\"\n", + "\n", + " return None\n", + "\n", + "\n", + "# Next, write some test code to test your functions. You should start by testing rows, like this\n", + "# \n", + "\n", + "# board = x_wins_boards[0]\n", + "# check_row( board[0]) # Should be None\n", + "# check_row( board[1]) # Should be 'x'\n", + "#\n", + "# Test the rows to determine if your check_rows() function works. \n", + "# \n", + "# Then, do the same checks with check_board()\n", + "#\n", + "# Finally,write a loop to check that the winner of all of the x_wins_boards is 'x', \n", + "# the winner of all of the o_wins_boards is 'o', and the winner of all of the no_winner_boards is None.\n", + "# You might use a comprehension, like [ check_board(board) for board in x_wins_boards ] " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Final Test\n", + "# If all of your functions are working this code should pass:\n", + "\n", + "assert all([ check_board(board) == 'x' for board in x_wins_boards ] )\n", + "assert all([ check_board(board) == 'o' for board in o_wins_boards ] )\n", + "assert all([ check_board(board) is None for board in no_winner_boards ] )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Challenge: Make It Better!**\n", + "\n", + "Once your `check_board(board)` function works, let's **optimize** it!\n", + "\n", + "Instead of individually checking each row, column, and diagonal, can you check them all in one go?\n", + "\n", + "Here's an example of how to combine everything into a single list:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "board = [\n", + " [1, 2, 3],\n", + " [4, 5, 6],\n", + " [7, 8, 9]\n", + " ]\n", + "\n", + "def transpose(a):\n", + " \"\"\"Convert rows to columns and columns to rows\"\"\"\n", + " return [list(row) for row in zip(*a)]\n", + "\n", + "# Start with all rows\n", + "all_lines = board[:] # Copy the original rows \n", + "all_lines.extend(transpose(board)) # Add the transposed columns\n", + "\n", + "print(\"Original rows + transposed columns:\")\n", + "for line in all_lines:\n", + " print(line)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now you try it yourself!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# TODO: Optimize your check_board() function!\n", + "# \n", + "# GOAL: Instead of checking rows, columns, and diagonals separately,\n", + "# combine them all into ONE list and check them all at once.\n", + "#\n", + "# STEPS TO COMPLETE:\n", + "# 1. Copy your working check_board() function from the previous cell\n", + "# 2. Create a list that contains ALL lines to check:\n", + "# - Start with all the rows (hint: use board[:] to copy)\n", + "# - Add all the columns (hint: use transpose())\n", + "# - Add both diagonals (hint: use list comprehensions from earlier)\n", + "# 3. Loop through your combined list and check each line\n", + "# 4. Return the winner as soon as you find one\n", + "# 5. If no winner is found after checking all lines, return None\n", + "#\n", + "# Test your optimized function with the same test boards!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "
    \nReminder: Check in your code. If you need a refresher, review the Check in Code and Restart Codespaces guide or circle back to the previous Check In Your Code lesson.\n
    \n" + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + }, + "syllabus": { + "uid": "mU94qia6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/40_Data_Structures_Func/50_Tic_Tac_Toe.py b/lessons/40_Data_Structures_Func/60_Tic_Tac_Toe.py similarity index 86% rename from lessons/40_Data_Structures_Func/50_Tic_Tac_Toe.py rename to lessons/40_Data_Structures_Func/60_Tic_Tac_Toe.py index 9c90264b..2788a502 100644 --- a/lessons/40_Data_Structures_Func/50_Tic_Tac_Toe.py +++ b/lessons/40_Data_Structures_Func/60_Tic_Tac_Toe.py @@ -1,3 +1,10 @@ +""" +# 60_Tic_Tac_Toe.py + +uid: mNMfKPiT +name: Tic Tac Toe +""" + #imports from guizero import App, Box, PushButton, Text, info @@ -11,7 +18,7 @@ def check_row(l): """Check if a player won on a row Args: l: a 3 element iterable - + Returns: The winner's token ( x or o ) if there is one, otherwise None """ @@ -22,7 +29,7 @@ def check_win(board): """Check if a player has won on a board Args: board: a 3x3 2D array - + Returns: The winner's token ( x or o ) if there is one, otherwise None """ @@ -31,24 +38,18 @@ def check_win(board): # The following code is the main part of the program. It creates a GUI for the # game and handles the game logic. Implement the functions above first, then -# after your program is working you can try chaning the code below. +# after your program is working you can try changing the code below. class TicTacToe: """A Simple Tic Tac Toe game""" - app = None - board = None # The storage for user's markers - buttons = None # Holds UI elements for the board - board_pane = None # - message = None - turn_n = 0 - turn = X_MARK - def __init__(self, win_func=check_win): - self.board = None # The stoage for user's markers - + self.turn_n = 0 + self.turn = X_MARK + self.board = None # The storage for user's markers + self.app = App('Tic Tac Toe Game', bg='burlywood') - self.board_pane = Box(self.app, layout='grid') # Holds UI elements for the board + self.board_pane = Box(self.app, layout='grid') # Holds UI elements for the board self.message = Text(self.app, text="It is your turn, " + self.current_turn) self.reset_button = PushButton(self.app, text='Reset', command=self.reset) @@ -64,7 +65,7 @@ def reset(self): self.turn_n = 0 self.turn = X_MARK self.message.value = "It's your turn, " + self.current_turn - + self.board = [[None for _ in range(3)] for _ in range(3)] self.buttons = [[None for _ in range(3)] for _ in range(3)] diff --git a/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb b/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb new file mode 100644 index 00000000..e93159d2 --- /dev/null +++ b/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb @@ -0,0 +1,45 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3f7ce0c0", + "metadata": {}, + "source": [ + "# **Module Four Quiz**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c4ce2ab", + "metadata": {}, + "outputs": [], + "source": "from jupyterquiz import display_quiz\n\ndisplay_quiz(\"../.jtl/Quiz_Data/Module_Four_Quiz.json\")" + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "syllabus": { + "name": "Module Four Quiz", + "uid": "bX9eUdqT" + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/lessons/40_Data_Structures_Func/README.md b/lessons/40_Data_Structures_Func/README.md index b9668e38..424bbd5f 100644 --- a/lessons/40_Data_Structures_Func/README.md +++ b/lessons/40_Data_Structures_Func/README.md @@ -4,8 +4,8 @@ uid: fDPxSid0 # PCEP Alignment - * PCEP-30-02 4.3 – Collect and Process Data Using Dictionaries - * Dictionaries: Building, Indexing, Adding and Removing Keys - * Iterating Through Dictionaries and Their Keys and Values - * Checking the Existence of Keys - * Methods: `keys()`, `items()`, and `values()` \ No newline at end of file +* PCEP-30-02 4.3 – Collect and Process Data Using Dictionaries + * Dictionaries: Building, Indexing, Adding and Removing Keys + * Iterating Through Dictionaries and Their Keys and Values + * Checking the Existence of Keys + * Methods: `keys()`, `items()`, and `values()` diff --git a/lessons/50_Projects/10_Hotel_Management.md b/lessons/50_Projects/10_Hotel_Management.md index e32106c2..89304f91 100644 --- a/lessons/50_Projects/10_Hotel_Management.md +++ b/lessons/50_Projects/10_Hotel_Management.md @@ -1,13 +1,13 @@ # Hotel Management The purpose of this program is to make sure you have an understanding of the -topics you learned in Python Apprentice. This is going to be your 1st program -you write all by yourself, imagine someone hired you to create a hotel -management program. +topics you learned in Python Apprentice. This is the first program you'll +write on your own — imagine someone hired you to create a hotel management +program. ## Getting Started: * Under projects create a new file and name it hotel_management.py -* Create a docstring (it's the triple quotes “”” stuff goes inside “””) +* Create a docstring (it's the triple quotes `""" stuff goes inside """`) * Take some time to organize your thoughts on how the program will be laid out and what you will need (functions, imports, loops, etc.) * Write down your thoughts as a list or instructions to yourself inside the docstring @@ -43,7 +43,7 @@ management program. * Ex. Upgrade room, room service, spa package, etc. -Have Fun!!! +Have fun. diff --git a/lessons/50_Projects/20_Random_Walk.py b/lessons/50_Projects/20_Random_Walk.py index 51c445e7..820b0b04 100644 --- a/lessons/50_Projects/20_Random_Walk.py +++ b/lessons/50_Projects/20_Random_Walk.py @@ -1,9 +1,12 @@ -""" A simple turtle program that moves the turtle randomly in the grid +""" +A simple turtle program that moves the turtle randomly in the grid This program will perform a "random walk" by moving the turtle randomly in the grid. Implement the random_walk function that will move the turtle randomly in the grid. +uid: eefj8Ioy +name: Random Walk """ import turtle @@ -29,8 +32,8 @@ def random_walk(walker, steps): The turtle will move on a grid, taking a random step in one of the four directions For each of the steps, the turtle will move in a random direction (N, E, S, W) - a fixed number of steps. Fur instance, the turtle should move 10 pixels each time, but - in a random, direction. + a fixed number of steps. For instance, the turtle should move 10 pixels each time, but + in a random direction. Args: walker (turtle.Turtle): The turtle object @@ -38,7 +41,7 @@ def random_walk(walker, steps): """ - # You can make the turle move randomly in either of two ways: randomly choosing a direction + # You can make the turtle move randomly in either of two ways: randomly choosing a direction # or randomly choosing a angle to turn. You can use random.choice() to select a random element # from a list, like this: # direction = random.choice(["N", "E", "S", "W"]) @@ -46,7 +49,7 @@ def random_walk(walker, steps): # angle = random.choice([0, 90, 180, 270]) # # Or you can use random.randint() to generate a random integer between two values, like this: - # angle_index = random.randint(0, 4) + # angle_index = random.randint(0, 3) # directions = ["N", "E", "S", "W"] # or directions = [0, 90, 180, 270] # direction = directions[angle_index] # @@ -67,4 +70,4 @@ def random_walk(walker, steps): random_walk(walker, 200) # Close the turtle window on click -screen.exitonclick() +screen.exitonclick() \ No newline at end of file diff --git a/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb b/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb new file mode 100644 index 00000000..e675e591 --- /dev/null +++ b/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb @@ -0,0 +1,95 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8e43baa", + "metadata": {}, + "source": "# **So you want to be an ASCII Artist?**\n\nYou will use loops and other techniques that you've learned so far to generate patterns and designs using ASCII characters.\n\nThese can range from simple programs that print repeated characters to the console, to more complex designs that use nested loops to create intricate patterns. \n\n### **What is ASCII Art?**\n\nASCII art is a graphic design technique that uses printable characters from the ASCII standard to create images and designs. It originated in the early days of computers when graphical capabilities were limited, and artists used text characters to create visual representations.\n\nExamples of ASCII art can include simple designs like smiley faces to more complex images like landscapes, portraits, and intricate patterns.\n\nFirst we'll start with basics examples ([ASCII Art Archive](https://www.asciiart.eu/)):\n\n
     \n      _.-\"\"\"\"\"-._  \n/  \\.-\"\"\"-./  \\    =/\\                 /\\=      (__) \n\\    -   -    /    / \\'._   (\\_/)   _.'/ \\      (XX)_____ \n |   o   o   |    / .''._'--(o.o)--'_.''. \\     (oo)    /|\\  \n \\  .-'''-.  /   /.' _/ |`'=/ \" \\='`| \\_ `.\\      | |--/ | *\n  '-\\__Y__/-'   /` .' `\\;-,'\\___/',-;/` '. '\\     w w w  w \n     `---`     /.-' jgs   `\\(-V-)/`       `-.\\ \n               `            \"   \"            `\n
    \n\nThere's more where that came from.\n\nCheck out these different ASCII animations to get a feel for what can be done:\n\n\n \n \n \n \n \n
    \n\nOkay, let's explore them further with some advanced examples:\n\n\n \n \n \n \n \n \n
    \n\nNow you might be thinking, man, that looks complicated. How would I even begin to create something like that?\n\nThe answer is loops! \n\nBy using loops, you can repeat patterns of characters to create complex designs without having to type out every single character manually. The animation part is a bit more advanced and typically requires additional programming techniques beyond basic loops, such as manipulating the console output or using libraries designed for creating animations.\n\n> **Note:** Don't worry about the animation part for now unless you really want to try, then by all means, but the only requirement here is having fun." + }, + { + "cell_type": "markdown", + "id": "2553aff8", + "metadata": {}, + "source": "## **Before You Begin**\n\nIt may be a good idea to get acquainted with some common ASCII characters or alt-key codes that are often used in ASCII art. \n\nTake a look at this table below:\n\n| Characters | Fun Designs | Unique Symbols | Emojis |\n|------------|-------------|----------------------|--------|\n| `@` `#` `$` `%` `&` `*` | `:)` `:(` `:D` `:P` | `☺` `☻` `♥` `♦` `♣` `♠` | 😀 😂 😍 🤔 😎 |\n| `!` `?` `.` `,` `;` `:` | `^_^` `>_<` `-_-` | `★` `☆` `☀` `☁` `☂` | 👍 👎 🙏 💪 |\n| `/` `\\` `-` `+` `=` | `O_o` `>.` `<( )` | `♫` `♪` `☼` `☾` `☽` | 💖 💔 🔥 🌟 🌈 |\n| `( ) [ ] { }` | `:3` `:O` `:/` `:-)` | `☯` `☮` `☢` `☣` `☠` | 🌍 🌙 🌞 🌛 🌜 |\n| `<` `>` `~` `^` | `;)` `;D` `;P` | `☘` `☎` `☏` `✈` `✉` | 🎉 🎊 🎈 🎂 🎁 |\n\nOf course, feel free to explore and find other characters that you like.\n\n### **Additional Resources**\n\nYou can find more ASCII characters and symbols online, like here:\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
    SiteUse-Case Scenario
    ascii-codeComprehensive list of ASCII characters with codes and descriptions.
    alt-codesFind special characters and symbols with their corresponding alt codes.
    coolsymbolWide variety of symbols, emojis, and special characters for various uses.
    \n\nIt's not cheating to use an ASCII-art generator online — there are plenty of free ones that can create good designs.\n\nIn fact, you can find them by searching the web or by looking at these sites:\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
    SiteUse-Case Scenario
    patorjkGenerates text-based ASCII art banners and fonts using text input.
    text-imageFor converting images into ASCII art representations.
    ascii-art-generatorUsed to create various ASCII art designs from text or images.
    " + }, + { + "cell_type": "markdown", + "id": "4dbd6bcc", + "metadata": {}, + "source": [ + "### **Boilerplate Code**\n", + "\n", + "You can use the example below as a reference for your project, it can give you a basic idea on what you are expected to do." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c9ef4207", + "metadata": {}, + "outputs": [], + "source": [ + "def print_ascii(text):\n", + " \"\"\"\n", + " This function prints the given text in ASCII art style using loops, then prints it line by line, adding spaces between letters and forms a complete phrase.\n", + " If you notice, everything is broken down into smaller parts and then reassembled to create the final output.\n", + " \"\"\"\n", + " # Define each character using a collection of strings \n", + " ascii_chars = {\n", + " 'A': [r\" /$$$$$$ \", r\" /$$__ $$\", r\"| $$ \\ $$\", r\"| $$$$$$$$\", r\"| $$__ $$\", r\"| $$ | $$\", r\"| $$ | $$\", r\"|__/ |__/\"],\n", + " 'S': [r\" /$$$$$$ \", r\"/$$__ $$\", r\"| $$ \\__/\", r\"| $$$$$$ \", r\" \\____ $$\", r\" /$$ \\ $$\", r\"| $$$$$$/\", r\" \\______/ \"],\n", + " 'C': [r\" /$$$$$$ \", r\" /$$__ $$\", r\"| $$ \\__/\", r\"| $$ \", r\"| $$ \", r\"| $$ $$\", r\"| $$$$$$/\", r\" \\______/ \"],\n", + " 'I': [r\" /$$$$$$ \", r\"|_ $$_/ \", r\" | $$ \", r\" | $$ \", r\" | $$ \", r\" | $$ \", r\" /$$$$$$ \", r\"|______/ \"],\n", + " 'R': [r\" /$$$$$$$ \", r\"| $$__ $$\", r\"| $$ \\ $$\", r\"| $$$$$$$/\", r\"| $$__ $$\", r\"| $$ \\ $$\", r\"| $$ | $$\", r\"|__/ |__/\"],\n", + " 'T': [r\" /$$$$$$$$\", r\"|__ $$__/\", r\" | $$ \", r\" | $$ \", r\" | $$ \", r\" | $$ \", r\" | $$ \", r\" |__/ \"],\n", + " 'O': [r\" /$$$$$$ \", r\" /$$__ $$\", r\"| $$ \\ $$\", r\"| $$ | $$\", r\"| $$ | $$\", r\"| $$ | $$\", r\"| $$$$$$/\", r\" \\______/ \"],\n", + " 'L': [r\" /$$ \", r\"| $$ \", r\"| $$ \", r\"| $$ \", r\"| $$ \", r\"| $$ \", r\"| $$$$$$$$\", r\"|________/\"],\n", + " ' ': [r\" \", r\" \", r\" \", r\" \", r\" \", r\" \", r\" \", r\" \"]\n", + " }\n", + " \n", + " # Convert the input text to uppercase to match the keys in ascii_chars\n", + " phrase = text.upper() \n", + " \n", + " # Set the font height, width, and spacing\n", + " for line_num in range(8):\n", + " current_line = \"\" \n", + " for char in phrase:\n", + " if char in ascii_chars:\n", + " current_line += ascii_chars[char][line_num] + \" \" \n", + " else:\n", + " current_line += \" \" \n", + " print(current_line)\n", + "\n", + "# Output your specific phrase\n", + "print_ascii(\"ASCII ART IS COOL\")" + ] + }, + { + "cell_type": "markdown", + "id": "e63a0b03", + "metadata": {}, + "source": "## **General Project Requirements**\n\n### **Requirements**\n- **Functions:** Create at least two custom functions to structure your code (e.g., one for rendering, one for logic).\n- **Data Structures:** Employ lists or dictionaries to hold patterns or configuration data.\n- **Have Fun:** Build something you enjoy. It can be humorous, impressive, or strange—it's your choice.\n\n### **Exclusions**\n- **No Boilerplate Code:** Write your own unique implementation instead of copying the provided example.\n- **Hardcoding:** Use loops rather than manually typing out many `print()` statements for large patterns.\n- **Copy-Pasting:** Encapsulate repeated logic in functions instead of duplicating code.\n- **Infinite Loops:** Make sure your loops have a termination condition to prevent crashes.\n\n### **Additional Information**\n- You are free to use external libraries like [art](https://pypi.org/project/art/) or [pyfiglet](https://pypi.org/project/pyfiglet/), though they are not mandatory.\n- The objective is to practice loops, conditionals, and functions, so prioritize effective usage while being creative.\n- The art can be hand-drawn or generated, provided you write the program that displays it.\n
    \n\n### **Step-by-Step Breakdown**\n1. **Plan Your Design:** Choose the ASCII art you wish to make. Sketch it or use an online generator for ideas.\n2. **Choose Characters:** Pick the ASCII characters for your design and break them down into manageable parts to work with.\n3. **Define Functions:** Set up at least two functions: one for drawing and another for logic/settings.\n4. **Use Data Structures:** Organize your patterns or settings using lists or dictionaries.\n5. **Implement Loops:** Code repeated patterns or designs with loops to avoid hardcoding.\n6. **Test and Refine:** Execute your code to check the output. Improve the design or add features as needed.\n7. **Document Your Code:** Include comments explaining your logic and design decisions for future reference.\n8. **Create Project File:** Save your code in a `.py` file, ensuring it includes comments explaining your logic." + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb b/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb new file mode 100644 index 00000000..303ae206 --- /dev/null +++ b/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb @@ -0,0 +1,44 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ad97bb84", + "metadata": {}, + "source": "## **Spin The Wheel Game**\n\nDo you know someone who likes to watch game shows on TV? Have you ever wanted to create your own? Instead of a physical wheel, let's build a digital version you can play on your computer.\n\n**Example:**\n
    \n\nTypically, a *Spin The Wheel* game consists of a wheel divided into sections, each representing a different prize. Players take turns spinning the wheel, and wherever the wheel stops determines the prize they win. However, feel free to add your own twist to the game. This is your chance to get creative and have fun.\n\nSoftware development is all about problem-solving and creativity. So, think about how you can make your game engaging and enjoyable for players." + }, + { + "cell_type": "markdown", + "id": "7177e4ee", + "metadata": {}, + "source": [ + "### **Assignment Requirements**\n", + "- Create a list of prizes that players can win.\n", + "- Use the `random` module to simulate the spinning of the wheel.\n", + "- Allow players to spin the wheel multiple times.\n", + "- Keep track of the prizes won by each player.\n", + "- Display the total prizes won at the end of the game." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/lessons/50_Projects/Temp_Project_Ideas/Terminal_Game.ipynb b/lessons/50_Projects/Temp_Project_Ideas/Terminal_Game.ipynb new file mode 100644 index 00000000..daec03f8 --- /dev/null +++ b/lessons/50_Projects/Temp_Project_Ideas/Terminal_Game.ipynb @@ -0,0 +1,54 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f61f2931", + "metadata": {}, + "source": [ + "## **Creating A Terminal-Based Game**\n", + "Have you ever wanted to create your own text-based adventure game that runs in the terminal? In this project, you'll learn how to build a simple terminal-based game using Python. You'll create a storyline, implement user choices, and manage game states.\n", + "\n", + "**Example:**\n", + "
    \"Warsim:
    \n" + ] + }, + { + "cell_type": "markdown", + "id": "325bfeb9", + "metadata": {}, + "source": [ + "Typically, a terminal-based game involves:\n", + "- Displaying text to the player to describe the game world and events.\n", + "- Accepting user input to make choices or perform actions.\n", + "- Using control flow like *if* statements and *loops* to manage game logic and progression.\n", + "\n", + "You can start by defining a simple storyline and creating functions to handle different parts of the game. As you progress, you can add more complexity, such as inventory management, character stats, and random events." + ] + }, + { + "cell_type": "markdown", + "id": "9d173d54", + "metadata": {}, + "source": [ + "### **Assignment Requirements:**\n", + "- Use functions to organize your code.\n", + "- Implement user input to allow players to make choices.\n", + "- Use control flow to manage game progression.\n", + "- Add comments to explain your code." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..c9e00e13 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "python-apprentice-jay" +version = "0.1.0" +requires-python = ">=3.13" +dependencies = [ + "guizero>=1.6.0", + "invoke>=3.0.3", + "ipykernel>=7.2.0", + "ipython>=9.12.0", + "ipyturtle3>=0.1.4", + "jupyterquiz>=2.9.6.4", + "pgzero>=1.2.1", + "pillow>=12.2.0", + "pygame>=2.6.1", +] diff --git a/requirements.txt b/requirements.txt index 6f681837..9b510188 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,6 @@ guizero pillow IPython ipykernel -invoke \ No newline at end of file +ipyturtle3 +invoke +jupyterquiz \ No newline at end of file diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..2537deaa --- /dev/null +++ b/uv.lock @@ -0,0 +1,728 @@ +version = 1 +revision = 2 +requires-python = ">=3.13" + +[[package]] +name = "appnope" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, +] + +[[package]] +name = "asttokens" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "comm" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, +] + +[[package]] +name = "debugpy" +version = "1.8.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/b7/cd8080344452e4874aae67c40d8940e2b4d47b01601a8fd9f44786c757c7/debugpy-1.8.20.tar.gz", hash = "sha256:55bc8701714969f1ab89a6d5f2f3d40c36f91b2cbe2f65d98bf8196f6a6a2c33", size = 1645207, upload-time = "2026-01-29T23:03:28.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/e2/fc500524cc6f104a9d049abc85a0a8b3f0d14c0a39b9c140511c61e5b40b/debugpy-1.8.20-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:5dff4bb27027821fdfcc9e8f87309a28988231165147c31730128b1c983e282a", size = 2539560, upload-time = "2026-01-29T23:03:48.738Z" }, + { url = "https://files.pythonhosted.org/packages/90/83/fb33dcea789ed6018f8da20c5a9bc9d82adc65c0c990faed43f7c955da46/debugpy-1.8.20-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:84562982dd7cf5ebebfdea667ca20a064e096099997b175fe204e86817f64eaf", size = 4293272, upload-time = "2026-01-29T23:03:50.169Z" }, + { url = "https://files.pythonhosted.org/packages/a6/25/b1e4a01bfb824d79a6af24b99ef291e24189080c93576dfd9b1a2815cd0f/debugpy-1.8.20-cp313-cp313-win32.whl", hash = "sha256:da11dea6447b2cadbf8ce2bec59ecea87cc18d2c574980f643f2d2dfe4862393", size = 5331208, upload-time = "2026-01-29T23:03:51.547Z" }, + { url = "https://files.pythonhosted.org/packages/13/f7/a0b368ce54ffff9e9028c098bd2d28cfc5b54f9f6c186929083d4c60ba58/debugpy-1.8.20-cp313-cp313-win_amd64.whl", hash = "sha256:eb506e45943cab2efb7c6eafdd65b842f3ae779f020c82221f55aca9de135ed7", size = 5372930, upload-time = "2026-01-29T23:03:53.585Z" }, + { url = "https://files.pythonhosted.org/packages/33/2e/f6cb9a8a13f5058f0a20fe09711a7b726232cd5a78c6a7c05b2ec726cff9/debugpy-1.8.20-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9c74df62fc064cd5e5eaca1353a3ef5a5d50da5eb8058fcef63106f7bebe6173", size = 2538066, upload-time = "2026-01-29T23:03:54.999Z" }, + { url = "https://files.pythonhosted.org/packages/c5/56/6ddca50b53624e1ca3ce1d1e49ff22db46c47ea5fb4c0cc5c9b90a616364/debugpy-1.8.20-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:077a7447589ee9bc1ff0cdf443566d0ecf540ac8aa7333b775ebcb8ce9f4ecad", size = 4269425, upload-time = "2026-01-29T23:03:56.518Z" }, + { url = "https://files.pythonhosted.org/packages/c5/d9/d64199c14a0d4c476df46c82470a3ce45c8d183a6796cfb5e66533b3663c/debugpy-1.8.20-cp314-cp314-win32.whl", hash = "sha256:352036a99dd35053b37b7803f748efc456076f929c6a895556932eaf2d23b07f", size = 5331407, upload-time = "2026-01-29T23:03:58.481Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d9/1f07395b54413432624d61524dfd98c1a7c7827d2abfdb8829ac92638205/debugpy-1.8.20-cp314-cp314-win_amd64.whl", hash = "sha256:a98eec61135465b062846112e5ecf2eebb855305acc1dfbae43b72903b8ab5be", size = 5372521, upload-time = "2026-01-29T23:03:59.864Z" }, + { url = "https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl", hash = "sha256:5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7", size = 5337658, upload-time = "2026-01-29T23:04:17.404Z" }, +] + +[[package]] +name = "decorator" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, +] + +[[package]] +name = "executing" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, +] + +[[package]] +name = "guizero" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/dc/2157b632f3e57c2c46ad690072abe498899d3bad55a2200fae0b5eac5102/guizero-1.6.0.tar.gz", hash = "sha256:0cf3000a30a4cabcd5bee95c5decad20616b2dba5bb958b4f2286c2819ee3a9f", size = 4327325, upload-time = "2025-01-10T09:39:03.387Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/ea/bcdc9988604adc48d016ff6cdc0669b70bbb61525c1cbbc4f19801147d67/guizero-1.6.0-py3-none-any.whl", hash = "sha256:241e54f8a11c8faff53b1f6a2abdd486b8338b6f49674daad8c33dd58c910e09", size = 45128, upload-time = "2025-01-10T09:38:56.166Z" }, +] + +[[package]] +name = "invoke" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/f6/227c48c5fe47fa178ccf1fda8f047d16c97ba926567b661e9ce2045c600c/invoke-3.0.3.tar.gz", hash = "sha256:437b6a622223824380bfb4e64f612711a6b648c795f565efc8625af66fb57f0c", size = 343419, upload-time = "2026-04-07T15:17:48.307Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/de/bbc12563bbf979618d17625a4e753ff7a078523e28d870d3626daa97261a/invoke-3.0.3-py3-none-any.whl", hash = "sha256:f11327165e5cbb89b2ad1d88d3292b5113332c43b8553b494da435d6ec6f5053", size = 160958, upload-time = "2026-04-07T15:17:46.875Z" }, +] + +[[package]] +name = "ipycanvas" +version = "0.14.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ipywidgets" }, + { name = "numpy" }, + { name = "pillow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/56/484c8979bbcaa3e3f2da4eac6a1eb41e998e353e4c6ef89e9612889813c8/ipycanvas-0.14.3.tar.gz", hash = "sha256:c6a53a22eebf4d611b168b8f4434145883f27a7575509bd99a4bfc48c5385a39", size = 4150499, upload-time = "2025-12-11T09:12:59.916Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/37/c6880bd16093793dcb4c005011cf968f45fd815b7b5094fa8374524add26/ipycanvas-0.14.3-py2.py3-none-any.whl", hash = "sha256:8a2f48e1e079355d3e7d5683e5c6e7684a87c15c3750c8d8cd2289c95383ee3e", size = 142962, upload-time = "2025-12-11T09:12:50.5Z" }, +] + +[[package]] +name = "ipykernel" +version = "7.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appnope", marker = "sys_platform == 'darwin'" }, + { name = "comm" }, + { name = "debugpy" }, + { name = "ipython" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "matplotlib-inline" }, + { name = "nest-asyncio" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/8d/b68b728e2d06b9e0051019640a40a9eb7a88fcd82c2e1b5ce70bef5ff044/ipykernel-7.2.0.tar.gz", hash = "sha256:18ed160b6dee2cbb16e5f3575858bc19d8f1fe6046a9a680c708494ce31d909e", size = 176046, upload-time = "2026-02-06T16:43:27.403Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl", hash = "sha256:3bbd4420d2b3cc105cbdf3756bfc04500b1e52f090a90716851f3916c62e1661", size = 118788, upload-time = "2026-02-06T16:43:25.149Z" }, +] + +[[package]] +name = "ipython" +version = "9.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "ipython-pygments-lexers" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/73/7114f80a8f9cabdb13c27732dce24af945b2923dcab80723602f7c8bc2d8/ipython-9.12.0.tar.gz", hash = "sha256:01daa83f504b693ba523b5a407246cabde4eb4513285a3c6acaff11a66735ee4", size = 4428879, upload-time = "2026-03-27T09:42:45.312Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/22/906c8108974c673ebef6356c506cebb6870d48cedea3c41e949e2dd556bb/ipython-9.12.0-py3-none-any.whl", hash = "sha256:0f2701e8ee86e117e37f50563205d36feaa259d2e08d4a6bc6b6d74b18ce128d", size = 625661, upload-time = "2026-03-27T09:42:42.831Z" }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, +] + +[[package]] +name = "ipyturtle3" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ipycanvas" }, + { name = "webcolors" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/92/b0f716742ea285e63d9ea6d31a3003ec805e488890dba3e1fb849d5767df/ipyturtle3-0.1.4.tar.gz", hash = "sha256:82a180ffad757e8671a6e0d119d06167388b9d163a33a037ffcfb7771cdf65f4", size = 6154, upload-time = "2022-06-04T14:07:09.176Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/01/7165219ab45c09317dcfa924f5971fd664febe44f87b0cddffef048606f8/ipyturtle3-0.1.4-py3-none-any.whl", hash = "sha256:ac02d1fa065c24ec99555ef00ce602114f1ae0f0cb6dc8e5094a73ba035d5d7c", size = 6765, upload-time = "2022-06-04T14:07:05.427Z" }, +] + +[[package]] +name = "ipywidgets" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "comm" }, + { name = "ipython" }, + { name = "jupyterlab-widgets" }, + { name = "traitlets" }, + { name = "widgetsnbextension" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739, upload-time = "2025-11-01T21:18:12.393Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808, upload-time = "2025-11-01T21:18:10.956Z" }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, +] + +[[package]] +name = "jupyter-client" +version = "8.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-core" }, + { name = "python-dateutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/e4/ba649102a3bc3fbca54e7239fb924fd434c766f855693d86de0b1f2bec81/jupyter_client-8.8.0.tar.gz", hash = "sha256:d556811419a4f2d96c869af34e854e3f059b7cc2d6d01a9cd9c85c267691be3e", size = 348020, upload-time = "2026-01-08T13:55:47.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371, upload-time = "2026-01-08T13:55:45.562Z" }, +] + +[[package]] +name = "jupyter-core" +version = "5.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, +] + +[[package]] +name = "jupyterlab-widgets" +version = "3.0.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423, upload-time = "2025-11-01T21:11:29.724Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926, upload-time = "2025-11-01T21:11:28.008Z" }, +] + +[[package]] +name = "jupyterquiz" +version = "2.9.6.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/c1/125473952e47ec0768e290ad237129453250ddbc021582ed7cf1ad1dc433/jupyterquiz-2.9.6.4.tar.gz", hash = "sha256:43a959c4ac3786fc7ddaf0a11700c471bfb086d263b417c2d16a0494fd5066e7", size = 1406724, upload-time = "2026-03-05T20:37:07.809Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/52/bc858b1665d0dec3a2511f4e6f5c18ea85c0977563d624d597c95d6d0fd7/jupyterquiz-2.9.6.4-py2.py3-none-any.whl", hash = "sha256:f8c4418f6c827454523fc882a30d744b585cb58ac1ae277769c3059d04fc272b", size = 25916, upload-time = "2026-03-05T20:37:06.596Z" }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, +] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" }, + { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" }, + { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" }, + { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" }, + { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" }, + { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" }, + { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" }, + { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" }, + { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" }, + { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" }, + { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" }, + { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" }, + { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" }, + { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" }, + { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" }, + { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" }, + { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" }, + { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" }, + { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" }, + { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" }, + { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" }, + { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" }, + { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" }, + { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" }, + { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" }, + { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" }, + { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" }, + { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" }, + { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" }, + { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" }, + { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" }, + { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" }, + { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" }, + { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" }, +] + +[[package]] +name = "packaging" +version = "26.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" }, +] + +[[package]] +name = "parso" +version = "0.8.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621, upload-time = "2026-02-09T15:45:24.425Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894, upload-time = "2026-02-09T15:45:21.391Z" }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, +] + +[[package]] +name = "pgzero" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "pygame" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/76/972af9c4ad453ecdb22115fcfaa9fca7147207aa73a93caab8a7a23c5b6a/pgzero-1.2.1.tar.gz", hash = "sha256:8cadc020f028cbac3e0cbd3bb9311a1c045f1deedac7917ff433f986c38e6106", size = 2250608, upload-time = "2021-03-02T23:34:48.918Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/66/bc46c203802d47fa30a6caa92d13392274bcbebbb9ffcd0c5ed8030b3611/pgzero-1.2.1-py3-none-any.whl", hash = "sha256:734e1de1a99804c2610f90aa419411fc2b31200b9d683b6c9fc710c7a8e36606", size = 71293, upload-time = "2021-03-02T23:34:47.485Z" }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, + { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, + { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, + { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, + { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, + { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, + { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, + { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, + { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, + { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, + { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, + { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, + { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, + { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, + { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, + { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, + { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, + { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, + { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, + { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, + { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, + { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, + { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, + { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, + { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, + { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, + { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, + { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, + { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.52" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pygame" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/cc/08bba60f00541f62aaa252ce0cfbd60aebd04616c0b9574f755b583e45ae/pygame-2.6.1.tar.gz", hash = "sha256:56fb02ead529cee00d415c3e007f75e0780c655909aaa8e8bf616ee09c9feb1f", size = 14808125, upload-time = "2024-09-29T13:41:34.698Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/91/718acf3e2a9d08a6ddcc96bd02a6f63c99ee7ba14afeaff2a51c987df0b9/pygame-2.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae6039f3a55d800db80e8010f387557b528d34d534435e0871326804df2a62f2", size = 13090765, upload-time = "2024-09-29T14:27:02.377Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c6/9cb315de851a7682d9c7568a41ea042ee98d668cb8deadc1dafcab6116f0/pygame-2.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2a3a1288e2e9b1e5834e425bedd5ba01a3cd4902b5c2bff8ed4a740ccfe98171", size = 12381704, upload-time = "2024-09-29T14:27:10.228Z" }, + { url = "https://files.pythonhosted.org/packages/9f/8f/617a1196e31ae3b46be6949fbaa95b8c93ce15e0544266198c2266cc1b4d/pygame-2.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27eb17e3dc9640e4b4683074f1890e2e879827447770470c2aba9f125f74510b", size = 13581091, upload-time = "2024-09-29T11:30:27.653Z" }, + { url = "https://files.pythonhosted.org/packages/3b/87/2851a564e40a2dad353f1c6e143465d445dab18a95281f9ea458b94f3608/pygame-2.6.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c1623180e70a03c4a734deb9bac50fc9c82942ae84a3a220779062128e75f3b", size = 14273844, upload-time = "2024-09-29T11:40:04.138Z" }, + { url = "https://files.pythonhosted.org/packages/85/b5/aa23aa2e70bcba42c989c02e7228273c30f3b44b9b264abb93eaeff43ad7/pygame-2.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef07c0103d79492c21fced9ad68c11c32efa6801ca1920ebfd0f15fb46c78b1c", size = 13951197, upload-time = "2024-09-29T11:40:06.785Z" }, + { url = "https://files.pythonhosted.org/packages/a6/06/29e939b34d3f1354738c7d201c51c250ad7abefefaf6f8332d962ff67c4b/pygame-2.6.1-cp313-cp313-win32.whl", hash = "sha256:3acd8c009317190c2bfd81db681ecef47d5eb108c2151d09596d9c7ea9df5c0e", size = 10249309, upload-time = "2024-09-29T11:10:23.329Z" }, + { url = "https://files.pythonhosted.org/packages/7e/11/17f7f319ca91824b86557e9303e3b7a71991ef17fd45286bf47d7f0a38e6/pygame-2.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:813af4fba5d0b2cb8e58f5d95f7910295c34067dcc290d34f1be59c48bd1ea6a", size = 10620084, upload-time = "2024-09-29T11:48:51.587Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "python-apprentice-jay" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "guizero" }, + { name = "invoke" }, + { name = "ipykernel" }, + { name = "ipython" }, + { name = "ipyturtle3" }, + { name = "jupyterquiz" }, + { name = "pgzero" }, + { name = "pillow" }, + { name = "pygame" }, +] + +[package.metadata] +requires-dist = [ + { name = "guizero", specifier = ">=1.6.0" }, + { name = "invoke", specifier = ">=3.0.3" }, + { name = "ipykernel", specifier = ">=7.2.0" }, + { name = "ipython", specifier = ">=9.12.0" }, + { name = "ipyturtle3", specifier = ">=0.1.4" }, + { name = "jupyterquiz", specifier = ">=2.9.6.4" }, + { name = "pgzero", specifier = ">=1.2.1" }, + { name = "pillow", specifier = ">=12.2.0" }, + { name = "pygame", specifier = ">=2.6.1" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "pyzmq" +version = "27.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "implementation_name == 'pypy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" }, + { url = "https://files.pythonhosted.org/packages/60/cb/84a13459c51da6cec1b7b1dc1a47e6db6da50b77ad7fd9c145842750a011/pyzmq-27.1.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5", size = 1122436, upload-time = "2025-09-08T23:08:20.801Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b6/94414759a69a26c3dd674570a81813c46a078767d931a6c70ad29fc585cb/pyzmq-27.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6", size = 1156301, upload-time = "2025-09-08T23:08:22.47Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ad/15906493fd40c316377fd8a8f6b1f93104f97a752667763c9b9c1b71d42d/pyzmq-27.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7", size = 1341197, upload-time = "2025-09-08T23:08:24.286Z" }, + { url = "https://files.pythonhosted.org/packages/14/1d/d343f3ce13db53a54cb8946594e567410b2125394dafcc0268d8dda027e0/pyzmq-27.1.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05", size = 897275, upload-time = "2025-09-08T23:08:26.063Z" }, + { url = "https://files.pythonhosted.org/packages/69/2d/d83dd6d7ca929a2fc67d2c3005415cdf322af7751d773524809f9e585129/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9", size = 660469, upload-time = "2025-09-08T23:08:27.623Z" }, + { url = "https://files.pythonhosted.org/packages/3e/cd/9822a7af117f4bc0f1952dbe9ef8358eb50a24928efd5edf54210b850259/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128", size = 847961, upload-time = "2025-09-08T23:08:29.672Z" }, + { url = "https://files.pythonhosted.org/packages/9a/12/f003e824a19ed73be15542f172fd0ec4ad0b60cf37436652c93b9df7c585/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39", size = 1650282, upload-time = "2025-09-08T23:08:31.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4a/e82d788ed58e9a23995cee70dbc20c9aded3d13a92d30d57ec2291f1e8a3/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97", size = 2024468, upload-time = "2025-09-08T23:08:33.543Z" }, + { url = "https://files.pythonhosted.org/packages/d9/94/2da0a60841f757481e402b34bf4c8bf57fa54a5466b965de791b1e6f747d/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db", size = 1885394, upload-time = "2025-09-08T23:08:35.51Z" }, + { url = "https://files.pythonhosted.org/packages/4f/6f/55c10e2e49ad52d080dc24e37adb215e5b0d64990b57598abc2e3f01725b/pyzmq-27.1.0-cp313-cp313t-win32.whl", hash = "sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c", size = 574964, upload-time = "2025-09-08T23:08:37.178Z" }, + { url = "https://files.pythonhosted.org/packages/87/4d/2534970ba63dd7c522d8ca80fb92777f362c0f321900667c615e2067cb29/pyzmq-27.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2", size = 641029, upload-time = "2025-09-08T23:08:40.595Z" }, + { url = "https://files.pythonhosted.org/packages/f6/fa/f8aea7a28b0641f31d40dea42d7ef003fded31e184ef47db696bc74cd610/pyzmq-27.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e", size = 561541, upload-time = "2025-09-08T23:08:42.668Z" }, + { url = "https://files.pythonhosted.org/packages/87/45/19efbb3000956e82d0331bafca5d9ac19ea2857722fa2caacefb6042f39d/pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a", size = 1341197, upload-time = "2025-09-08T23:08:44.973Z" }, + { url = "https://files.pythonhosted.org/packages/48/43/d72ccdbf0d73d1343936296665826350cb1e825f92f2db9db3e61c2162a2/pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea", size = 897175, upload-time = "2025-09-08T23:08:46.601Z" }, + { url = "https://files.pythonhosted.org/packages/2f/2e/a483f73a10b65a9ef0161e817321d39a770b2acf8bcf3004a28d90d14a94/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96", size = 660427, upload-time = "2025-09-08T23:08:48.187Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d2/5f36552c2d3e5685abe60dfa56f91169f7a2d99bbaf67c5271022ab40863/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d", size = 847929, upload-time = "2025-09-08T23:08:49.76Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2a/404b331f2b7bf3198e9945f75c4c521f0c6a3a23b51f7a4a401b94a13833/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146", size = 1650193, upload-time = "2025-09-08T23:08:51.7Z" }, + { url = "https://files.pythonhosted.org/packages/1c/0b/f4107e33f62a5acf60e3ded67ed33d79b4ce18de432625ce2fc5093d6388/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd", size = 2024388, upload-time = "2025-09-08T23:08:53.393Z" }, + { url = "https://files.pythonhosted.org/packages/0d/01/add31fe76512642fd6e40e3a3bd21f4b47e242c8ba33efb6809e37076d9b/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a", size = 1885316, upload-time = "2025-09-08T23:08:55.702Z" }, + { url = "https://files.pythonhosted.org/packages/c4/59/a5f38970f9bf07cee96128de79590bb354917914a9be11272cfc7ff26af0/pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92", size = 587472, upload-time = "2025-09-08T23:08:58.18Z" }, + { url = "https://files.pythonhosted.org/packages/70/d8/78b1bad170f93fcf5e3536e70e8fadac55030002275c9a29e8f5719185de/pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0", size = 661401, upload-time = "2025-09-08T23:08:59.802Z" }, + { url = "https://files.pythonhosted.org/packages/81/d6/4bfbb40c9a0b42fc53c7cf442f6385db70b40f74a783130c5d0a5aa62228/pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7", size = 575170, upload-time = "2025-09-08T23:09:01.418Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, +] + +[[package]] +name = "tornado" +version = "6.5.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/f1/3173dfa4a18db4a9b03e5d55325559dab51ee653763bb8745a75af491286/tornado-6.5.5.tar.gz", hash = "sha256:192b8f3ea91bd7f1f50c06955416ed76c6b72f96779b962f07f911b91e8d30e9", size = 516006, upload-time = "2026-03-10T21:31:02.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:487dc9cc380e29f58c7ab88f9e27cdeef04b2140862e5076a66fb6bb68bb1bfa", size = 445983, upload-time = "2026-03-10T21:30:44.28Z" }, + { url = "https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:65a7f1d46d4bb41df1ac99f5fcb685fb25c7e61613742d5108b010975a9a6521", size = 444246, upload-time = "2026-03-10T21:30:46.571Z" }, + { url = "https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e74c92e8e65086b338fd56333fb9a68b9f6f2fe7ad532645a290a464bcf46be5", size = 447229, upload-time = "2026-03-10T21:30:48.273Z" }, + { url = "https://files.pythonhosted.org/packages/34/01/74e034a30ef59afb4097ef8659515e96a39d910b712a89af76f5e4e1f93c/tornado-6.5.5-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:435319e9e340276428bbdb4e7fa732c2d399386d1de5686cb331ec8eee754f07", size = 448192, upload-time = "2026-03-10T21:30:51.22Z" }, + { url = "https://files.pythonhosted.org/packages/be/00/fe9e02c5a96429fce1a1d15a517f5d8444f9c412e0bb9eadfbe3b0fc55bf/tornado-6.5.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3f54aa540bdbfee7b9eb268ead60e7d199de5021facd276819c193c0fb28ea4e", size = 448039, upload-time = "2026-03-10T21:30:53.52Z" }, + { url = "https://files.pythonhosted.org/packages/82/9e/656ee4cec0398b1d18d0f1eb6372c41c6b889722641d84948351ae19556d/tornado-6.5.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36abed1754faeb80fbd6e64db2758091e1320f6bba74a4cf8c09cd18ccce8aca", size = 447445, upload-time = "2026-03-10T21:30:55.541Z" }, + { url = "https://files.pythonhosted.org/packages/5a/76/4921c00511f88af86a33de770d64141170f1cfd9c00311aea689949e274e/tornado-6.5.5-cp39-abi3-win32.whl", hash = "sha256:dd3eafaaeec1c7f2f8fdcd5f964e8907ad788fe8a5a32c4426fbbdda621223b7", size = 448582, upload-time = "2026-03-10T21:30:57.142Z" }, + { url = "https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl", hash = "sha256:6443a794ba961a9f619b1ae926a2e900ac20c34483eea67be4ed8f1e58d3ef7b", size = 448990, upload-time = "2026-03-10T21:30:58.857Z" }, + { url = "https://files.pythonhosted.org/packages/b7/c8/876602cbc96469911f0939f703453c1157b0c826ecb05bdd32e023397d4e/tornado-6.5.5-cp39-abi3-win_arm64.whl", hash = "sha256:2c9a876e094109333f888539ddb2de4361743e5d21eece20688e3e351e4990a6", size = 448016, upload-time = "2026-03-10T21:31:00.43Z" }, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, +] + +[[package]] +name = "wcwidth" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, +] + +[[package]] +name = "webcolors" +version = "25.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491, upload-time = "2025-10-31T07:51:03.977Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905, upload-time = "2025-10-31T07:51:01.778Z" }, +] + +[[package]] +name = "widgetsnbextension" +version = "4.0.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402, upload-time = "2025-11-01T21:15:55.178Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503, upload-time = "2025-11-01T21:15:53.565Z" }, +]