From e60200cc15e9248f47a7820836e7f7fa88bb4bf4 Mon Sep 17 00:00:00 2001 From: Voidoid1977 Date: Wed, 13 Aug 2025 19:28:09 -0400 Subject: [PATCH 001/159] Adjusted 10_Welcome.ipynb page for the Python-Apprentice repository. Specifically to make it more appealing to K-12 students (e.g., added emojis, color, reworded a couple of things, etc.). **Suggestion has not been made on GitHub because I do not have access to the correct repo. This was saved in a markdown document locally, for now.** --- .../10_Turtles/10_Welcome/10_Welcome.ipynb | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index 298e30c8..e4340810 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -6,26 +6,36 @@ "source": [ "# Getting Started with Python\n", "\n", - "This is the first lesson of your first Python class with the Leage 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", + "Hello future coders, programmers, and engineers! Welcome to your first *Python* lesson with the **League of Amazing Programmers**. We're super excited to help you take your first steps into becoming an amazing programmer! \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", + "To follow along, make sure you're reading this lesson in one of these spots:\n", "\n", - "
\n", - " \"spiral\"\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", + "\n", + "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist 🤖 that you can control with code. To do this, we'll use a pre-installed Python tool called the `turtle` module. \n", + "\n", + "You can tell your turtle 🐢 to move forward ↑, turn left ← or ➝ right, and change the color 🎨 of the lines it draws 🖍️—Soon, you'll be able to create awesome pictures like this one!\n", + "\n", + "
\n", + "\n", + "
\n", + " \"A\n", "
\n", "\n", + "
\n", + "

Let's Get Set Up!

\n", + "

Before we can start drawing, we need to set up your coding environment. Don't worry if anything seems confusing—that's what your instructors are here for! Feel free to ask for help if you get stuck.

\n", + "

Ready to go? Click the blue Next Lesson button at the bottom-left of your screen to continue! 🚀

\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", + "- Notes & Key Terms:\n", + " - `Python` — A popular programming language known for its simplicity and readability.\n", + " - `Turtle Module` — A built-in Python library that allows for drawing shapes and patterns using a virtual turtle.\n", + " - `Turtle Programming` — A style of programming that uses the turtle module to create graphics and animations.\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" + "
" ] }, { From 074baabdb9389edbbecc11405df44427276bda71 Mon Sep 17 00:00:00 2001 From: Voidoid1977 Date: Wed, 20 Aug 2025 17:19:05 -0400 Subject: [PATCH 002/159] changed Open_The_Screen --- .vscode/settings.json | 20 -- .../10_Welcome/20_Open_The_Screen.ipynb | 15 +- .../10_Welcome/30_Run_Programs.ipynb | 212 ------------------ 3 files changed, 7 insertions(+), 240 deletions(-) delete mode 100644 .vscode/settings.json delete mode 100644 lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index b4e65206..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "files.exclude": { - "**/.git": true, - "**/.svn": true, - "**/.hg": true, - "**/Thumbs.db": true, - "**/*.crswap": true, - "**/__pycache__": true, - "**/.cache": true, - "**/.coverage": true, - "**/.coverage.*": true, - "**/.hypothesis": true, - "**/.mypy_cache": true, - "**/.nox": true, - "**/.pytest_cache": true, - "**/.ruff_cache": true, - "**/.tox": true - } - -} \ 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..eb13ed0f 100644 --- a/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb +++ b/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb @@ -5,14 +5,13 @@ "metadata": {}, "source": [ "\n", - "## Open a Virtual Screen on the Web\n", + "## 🖥️ Let's Open Your Virtual Screen!\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", + "Okay great! So now, you started your editor as a Codespace on GitHub. If you did this correctly, you clicked a button like this: , but now you need to open a **virtual screen** to interact with the turtle.\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" + "Usually this screen pops up by itself, but if you don't see it don't worry! Just look for the monitor icon in the top right corner of your screen and give it a click!\n", + "\n", + "Now that your virtual screen is ready, gt ready to create something awesome! When you run your code, your turtle's 🐢 drawings 🎨 will appear in this window. \n" ] }, { @@ -23,7 +22,7 @@ ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -37,7 +36,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.11" + "version": "3.12.11" }, "syllabus": { "uid": "KmgIQbhr" diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb deleted file mode 100644 index 0f866dcb..00000000 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ /dev/null @@ -1,212 +0,0 @@ -{ - "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." - ] - }, - { - "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.3" - }, - "syllabus": { - "uid": "cNLK6qtR" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} From 5cfb3dd630b7228ee9f11a76cb454f113c3291f5 Mon Sep 17 00:00:00 2001 From: Voidoid1977 Date: Wed, 20 Aug 2025 17:20:29 -0400 Subject: [PATCH 003/159] added back Run Programs --- .../10_Welcome/30_Run_Programs.ipynb | 212 ++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb new file mode 100644 index 00000000..0f866dcb --- /dev/null +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -0,0 +1,212 @@ +{ + "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." + ] + }, + { + "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.3" + }, + "syllabus": { + "uid": "cNLK6qtR" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 6c4a68d78d123c371ed12a6e5f01d63473e1d65f Mon Sep 17 00:00:00 2001 From: Voidoid1977 Date: Wed, 20 Aug 2025 17:25:50 -0400 Subject: [PATCH 004/159] made changes to 30_Run_Prgrams --- lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb | 2 +- lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 eb13ed0f..ef8ae28e 100644 --- a/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb +++ b/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "\n", - "## 🖥️ Let's Open Your Virtual Screen!\n", + "# 🖥️ Let's Open Your Virtual Screen!\n", "\n", "Okay great! So now, you started your editor as a Codespace on GitHub. If you did this correctly, you clicked a button like this: , but now you need to open a **virtual screen** to interact with the turtle.\n", "\n", diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index 0f866dcb..7e27a6c7 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -187,7 +187,7 @@ ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -201,7 +201,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.3" + "version": "3.12.11" }, "syllabus": { "uid": "cNLK6qtR" From cc0ac241b201cd019c0e8809df71ec007808e92d Mon Sep 17 00:00:00 2001 From: Voidoid1977 Date: Wed, 20 Aug 2025 20:02:04 -0400 Subject: [PATCH 005/159] Adjusted 30_Run_Programs.ipynb -- this is still a work in progress. --- .../10_Welcome/20_Open_The_Screen.ipynb | 89 ++-- .../10_Welcome/30_Run_Programs.ipynb | 384 ++++++++---------- 2 files changed, 222 insertions(+), 251 deletions(-) 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 ef8ae28e..0686c7d2 100644 --- a/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb +++ b/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb @@ -1,47 +1,48 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# 🖥️ Let's Open Your Virtual Screen!\n", - "\n", - "Okay great! So now, you started your editor as a Codespace on GitHub. If you did this correctly, you clicked a button like this: , but now you need to open a **virtual screen** to interact with the turtle.\n", - "\n", - "Usually this screen pops up by itself, but if you don't see it don't worry! Just look for the monitor icon in the top right corner of your screen and give it a click!\n", - "\n", - "Now that your virtual screen is ready, gt ready to create something awesome! When you run your code, your turtle's 🐢 drawings 🎨 will appear in this window. \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": "KmgIQbhr" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Let's Open Your Virtual Screen!\n", + "\n", + "Welcome back, Code Explorer! 🌟 Are you ready to see your code come to life? Of course you are!\n", + "\n", + "To get started, you probably clicked a button like this 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. But if it doesn't appear, don't worry! Just look for the monitor icon in the top right corner of your screen and click it.\n", + "\n", + "Once your virtual screen is open, you're ready to create something awesome! When you run your code, your turtle's 🐢 drawings 🎨 will appear in this window." + ] }, - "nbformat": 4, - "nbformat_minor": 4 + { + "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": "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..53dea427 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -1,212 +1,182 @@ { - "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", + "## Run Notebook 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! Just move your mouse over the code cell and click the ▶️ button." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "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 2025-08-20\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": [ + "Here’s what the code cell looks like with the run button. Try it out!\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Setting Up The Interpreter (If that does not work)\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 Cell?
\n", + "\n", + "The first time you press the ▶️ button you might notice that nothing happens. Don’t worry! Technically something did, you just were not able to see it. This is because you still need to pick which interpreter (or version) for 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 all you need to do is 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", + "
Note: You’ll need to pick a Python interpreter every time you open a new notebook.
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## What is a Cell?\n", + "A \"Cell\" is just a box where you can write code. You can run 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. This puts the cell in \"Command Mode.\"\n", + "* When a cell is active, look for a small menu in the top right for more options. \n", + "\n", + "## Try it yourself!\n", + "\n", + "Step 1. Copy the Hello World code from above into the cell below, or type `print(\"Hello World!\")`
\n", + "Step 2. Change the string inside the quotation marks so that it also prints your name (e.g., change \"Hello World!\" to \"Your Name\").
\n", + "Step 3. Now all you have to do is press the play button and run the code!
" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Write your Hello World code below\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tips on running `.py` programs\n", + "\n", + "Files that end with `.py` are regular Python programs. You’ll use these a lot! In the next lesson, we will try running a program called `02_Meet_Tina.py`.\n", + "\n", + "There are a few ways to run it. The easiest is to look in the lower left for the Lesson Browser, it looks like this:\n", + "\n", + "\n", + "\n", + "Then all you need to do is click the ▶ Run and ⏹ Stop buttons. You can also click \"Next Lesson\" to move on.\n", + "\n", + "### Use the ▶️ button\n", + "\n", + "Another way:\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, close the window.\n", + "\n", + "### Hit the F5 Key\n", + "\n", + "You can also run the program by pressing F5. (On a Mac, hold the fn key and press F5.) This opens the debugger, which helps you find problems in your code.\n", + "\n", + "The first time you use F5, you’ll see a window that says \"Select Debugger.\" Pick \"Python Debugger,\" then \"Python File. Debug currently active Python file.\" You’ll see the debug bar:\n", + "\n", + "
\n", + "\n", + "We’ll learn more about the debugger later. For now, just know: press the red square to stop your program, and the green circle to run it again.\n", + "\n", + "
\n", + "You can’t run a program again until you stop the one that’s running. If you see a turtle window open, close it (or click the X in the corner).\n", + "
\n", + "If you’re using the debugger, click the red square in the debug bar.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, open the file `02_Meet_Tina.py` and try running it!" + ] + }, + { + "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" }, - "nbformat": 4, - "nbformat_minor": 4 + "syllabus": { + "uid": "cNLK6qtR" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } From b7b742993d97e6c07b8e5be814aaa7040c170f2a Mon Sep 17 00:00:00 2001 From: Voidoid1977 Date: Wed, 20 Aug 2025 20:04:04 -0400 Subject: [PATCH 006/159] fixed 'Run All' example--looks better now --- lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index 53dea427..bfbff5ee 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -13,7 +13,7 @@ "\n", "## Run Notebook 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", + "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! Just move your mouse over the code cell and click the ▶️ button." ] From d4918ed14189f37013ede42afe7bdb5e38f9348e Mon Sep 17 00:00:00 2001 From: Voidoid1977 Date: Fri, 22 Aug 2025 22:50:47 -0400 Subject: [PATCH 007/159] Modified the '10_Welcome.ipynb' file to make it more consistent with other changes made to '20_Open_The_Screen.ipynb' & '30_Run_Programs.ipynb'. Many areas were reworked in the Run Programs notebook (e.g., I separated areas into headings for less confusion, rephrased the note at the bottom that confused me, and added tags for representing keyboard buttons). There are other changes made, feel free to look. I am trying to figure out how to keep my pull requests to the League repo separate from the pull requests to my forked repo. --- .../10_Turtles/10_Welcome/10_Welcome.ipynb | 25 ++-- .../10_Welcome/20_Open_The_Screen.ipynb | 21 +++- .../10_Welcome/30_Run_Programs.ipynb | 116 +++++++++++------- 3 files changed, 98 insertions(+), 64 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index e4340810..d1d07959 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -4,9 +4,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Getting Started with Python\n", + "# **Getting Started with Python**\n", "\n", - "Hello future coders, programmers, and engineers! Welcome to your first *Python* lesson with the **League of Amazing Programmers**. We're super excited to help you take your first steps into becoming an amazing programmer! \n", + "Hello future coders, programmers, and engineers! Welcome to your first **Python** lesson with the **League of Amazing Programmers**. We're super excited to help you take your first steps into becoming an amazing programmer! \n", "\n", "To follow along, make sure you're reading this lesson in one of these spots:\n", "\n", @@ -14,9 +14,9 @@ "* [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", "\n", - "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist 🤖 that you can control with code. To do this, we'll use a pre-installed Python tool called the `turtle` module. \n", + "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist 🤖 that you can control with code. To do this, we'll use a pre-installed Python tool called the **turtle module**. \n", "\n", - "You can tell your turtle 🐢 to move forward ↑, turn left ← or ➝ right, and change the color 🎨 of the lines it draws 🖍️—Soon, you'll be able to create awesome pictures like this one!\n", + "You can tell your turtle 🐢 to move forward ↑, turn left ← or ➝ right, and change the color 🎨 of the lines it draws 🖍️— Soon, you'll be able to create awesome pictures like this one!\n", "\n", "
\n", "\n", @@ -28,20 +28,23 @@ "

Let's Get Set Up!

\n", "

Before we can start drawing, we need to set up your coding environment. Don't worry if anything seems confusing—that's what your instructors are here for! Feel free to ask for help if you get stuck.

\n", "

Ready to go? Click the blue Next Lesson button at the bottom-left of your screen to continue! 🚀

\n", - "
\n", + "
\n", "\n", "- Notes & Key Terms:\n", - " - `Python` — A popular programming language known for its simplicity and readability.\n", - " - `Turtle Module` — A built-in Python library that allows for drawing shapes and patterns using a virtual turtle.\n", - " - `Turtle Programming` — A style of programming that uses the turtle module to create graphics and animations.\n", - "\n", + " - Python — A popular programming language known for its simplicity and readability.\n", + " - Turtle Module — A built-in Python library that allows for drawing shapes and patterns using a virtual turtle.\n", + " - Turtle Programming — A style of programming that uses the turtle module to create graphics and animations.\n", "
" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, - "source": [] + "outputs": [], + "source": [ + "# These are called code cells, we will explore them in more detail later. Feel free to practice inside them throughout this course!" + ] } ], "metadata": { 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 0686c7d2..5f56fc9d 100644 --- a/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb +++ b/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb @@ -4,21 +4,30 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Let's Open Your Virtual Screen!\n", + "# **Let's Open Your Virtual Screen!**\n", "\n", "Welcome back, Code Explorer! 🌟 Are you ready to see your code come to life? Of course you are!\n", "\n", - "To get started, you probably clicked a button like this to open your Codespace: . Now, to see your turtle's drawings, you need to open a **virtual screen**.\n", + "To get started, you probably clicked a button like this to open your Codespace. Now, to see your turtle's drawings, you just need to open a virtual screen. Don't worry, this part is easy.\n", "\n", - "Most of the time, this screen will open automatically. But if it doesn't appear, don't worry! Just look for the monitor icon in the top right corner of your screen and click it.\n", + "Most of the time, this screen will open automatically. If for some reason it doesn't appear, just look for the little monitor icon in the top right corner of your editor and click on it.\n", "\n", - "Once your virtual screen is open, you're ready to create something awesome! When you run your code, your turtle's 🐢 drawings 🎨 will appear in this window." + "You should now be looking at something like the image below, just click on the connect button!\n", + "
\n", + " \"Virtual\n", + "
\n", + "\n", + "With your virtual screen open, you are now one step closer to interacting with your turtle!" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, - "source": [] + "outputs": [], + "source": [ + "# These are called code cells, we will explore them in more detail later. Feel free to practice inside them throughout this course!\n" + ] } ], "metadata": { diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index bfbff5ee..64ea3441 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Running Programs\n", + "# **Running Programs**\n", "\n", "You can run Python in two main ways: using Python files or Notebooks.\n", "\n", @@ -20,17 +20,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello 👋 World 🌎 ! Today is 2025-08-20\n" - ] - } - ], + "outputs": [], "source": [ "import datetime\n", "\n", @@ -54,9 +46,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Setting Up The Interpreter (If that does not work)\n", + "### 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 Cell?
\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 Cell?
\n", "\n", "The first time you press the ▶️ button you might notice that nothing happens. Don’t worry! Technically something did, you just were not able to see it. This is because you still need to pick which interpreter (or version) for Python to use.\n", "\n", @@ -68,91 +60,121 @@ "\n", "Now all you need to do is 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.
" + "
Note: You’ll need to pick a Python interpreter every time you open a new notebook.
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## What is a Cell?\n", - "A \"Cell\" is just a box where you can write code. You can run a code cell by clicking the ▶️ button or by pressing Shift + Enter on your keyboard.\n", + "### What is a Notebook Code Cell?\n", + "A *cell* is just a box where you can write code. You can run 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. This puts the cell in \"Command Mode.\"\n", - "* When a cell is active, look for a small menu in the top right for more options. \n", - "\n", - "## Try it yourself!\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", + "**Try it yourself!**\n", "\n", - "Step 1. Copy the Hello World code from above into the cell below, or type `print(\"Hello World!\")`
\n", - "Step 2. Change the string inside the quotation marks so that it also prints your name (e.g., change \"Hello World!\" to \"Your Name\").
\n", - "Step 3. Now all you have to do is press the play button and run the code!
" + "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. Now all you have to do is press the play button and run the code!
" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "# Write your Hello World code below\n", - "\n" + "# Write your Hello World code below\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Tips on running `.py` programs\n", + "## How to Run `.py` Programs\n", + "\n", + "Running `.py` files might seem new or even a little overwhelming at first, but don’t worry—you’ll get the hang of it quickly! In the next lesson, you’ll try running a Python program called `02_Meet_Tina.py`. There are a few different ways to do this, and you can use whichever feels easiest for you. Let’s walk through each option together!\n", + "\n", + "### Using the Lesson Browser\n", "\n", - "Files that end with `.py` are regular Python programs. You’ll use these a lot! In the next lesson, we will try running a program called `02_Meet_Tina.py`.\n", + "The Lesson Browser is a simple way to run `.py` programs in this course.\n", "\n", - "There are a few ways to run it. The easiest is to look in the lower left for the Lesson Browser, it looks like this:\n", + "In the bottom-left, you’ll see buttons like this:\n", "\n", - "\n", + "\n", "\n", - "Then all you need to do is click the ▶ Run and ⏹ Stop buttons. You can also click \"Next Lesson\" to move on.\n", + "Here’s how it works, step by step:\n", + "- Click Run to start your program\n", "\n", - "### Use the ▶️ button\n", + "- Click Stop to stop it\n", "\n", - "Another way:\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, close the window.\n", + "3. When you’re done, just close the window.\n", "\n", - "### Hit the F5 Key\n", + "### Using the Debugger\n", "\n", - "You can also run the program by pressing F5. (On a Mac, hold the fn key and press F5.) This opens the debugger, which helps you find problems in your code.\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", - "The first time you use F5, you’ll see a window that says \"Select Debugger.\" Pick \"Python Debugger,\" then \"Python File. Debug currently active Python file.\" You’ll see the debug bar:\n", + "You’ll see a debug bar like this:\n", "\n", - "
\n", + "\n", "\n", - "We’ll learn more about the debugger later. For now, just know: press the red square to stop your program, and the green circle to run it again.\n", + "Don’t worry about all the buttons for now! You just need to know that you can press the red square to stop your program, and the green circle to run it again.\n", "\n", - "
\n", - "You can’t run a program again until you stop the one that’s running. If you see a turtle window open, close it (or click the X in the corner).\n", - "
\n", - "If you’re using the debugger, click the red square in the debug bar.\n", - "
" + "Remember, it’s totally normal to feel unsure at first. Take your time, try out each method, and soon running Python programs will feel easy!\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now, open the file `02_Meet_Tina.py` and try running it!" + "
\n", + "\n", + "**If a program is already running, you must close the program before starting another one.**\n", + "\n", + "**You have two options to close it:**\n", + "- **Click the ❌ in the top-right corner of the window.** \n", + " - **If the program's code ends with `turtle.exitonclick()`, you can simply click anywhere inside the window to close it.**\n", + "- **If you are using the debugger, you can also stop the program by clicking the red square icon in the debugger toolbar.**\n", + "\n", + "
" ] }, { "cell_type": "markdown", "metadata": {}, - "source": [] + "source": [ + "Now, click the Next Lesson button, open the file `02_Meet_Tina.py`, and try running it!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Practice Code Cell\n" + ] } ], "metadata": { @@ -171,7 +193,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.11" + "version": "3.13.5" }, "syllabus": { "uid": "cNLK6qtR" From d424a449ac4729bdc667f237d13b43170fab965f Mon Sep 17 00:00:00 2001 From: Voidoid1977 Date: Fri, 22 Aug 2025 23:18:33 -0400 Subject: [PATCH 008/159] Slight changes to IDE options on the 10_Welcome.ipynb notebook + changed 'Notes & Key Terms' section + added a blue next lesson icon. --- lessons/10_Turtles/10_Welcome/10_Welcome.ipynb | 18 +++++++++--------- .../10_Welcome/20_Open_The_Screen.ipynb | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index d1d07959..81a1078f 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -10,9 +10,9 @@ "\n", "To 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", + "* [**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", "\n", "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist 🤖 that you can control with code. To do this, we'll use a pre-installed Python tool called the **turtle module**. \n", "\n", @@ -27,13 +27,13 @@ "
\n", "

Let's Get Set Up!

\n", "

Before we can start drawing, we need to set up your coding environment. Don't worry if anything seems confusing—that's what your instructors are here for! Feel free to ask for help if you get stuck.

\n", - "

Ready to go? Click the blue Next Lesson button at the bottom-left of your screen to continue! 🚀

\n", - "
\n", + "

Ready to go? Click the blue Next Lesson button at the bottom-left of your screen to continue! 🚀

\n", + "
\n", "\n", - "- Notes & Key Terms:\n", - " - Python — A popular programming language known for its simplicity and readability.\n", - " - Turtle Module — A built-in Python library that allows for drawing shapes and patterns using a virtual turtle.\n", - " - Turtle Programming — A style of programming that uses the turtle module to create graphics and animations.\n", + "- **Notes & Key Terms:**\n", + " - **Python** — A popular programming language known for its simplicity and readability.\n", + " - **Turtle Module** — A built-in Python library that allows for drawing shapes and patterns using a virtual turtle.\n", + " - **Turtle Programming** — A style of programming that uses the turtle module to create graphics and animations.\n", "
" ] }, 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 5f56fc9d..c865ff16 100644 --- a/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb +++ b/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb @@ -46,7 +46,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.11" + "version": "3.13.5" }, "syllabus": { "uid": "KmgIQbhr" From b2aabdf4bd943a105cfa3f4e258d74d7c9f9d799 Mon Sep 17 00:00:00 2001 From: Voidoid1977 Date: Fri, 22 Aug 2025 23:32:39 -0400 Subject: [PATCH 009/159] added back the \.vscode\settings.json\ file that I accidentally removed somehow --- .vscode/settings.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..b4e65206 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,20 @@ +{ + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/Thumbs.db": true, + "**/*.crswap": true, + "**/__pycache__": true, + "**/.cache": true, + "**/.coverage": true, + "**/.coverage.*": true, + "**/.hypothesis": true, + "**/.mypy_cache": true, + "**/.nox": true, + "**/.pytest_cache": true, + "**/.ruff_cache": true, + "**/.tox": true + } + +} \ No newline at end of file From 1e9f28c4d56f0a3b18a8fb3dbdb7a7738f168cf9 Mon Sep 17 00:00:00 2001 From: Voidoid1977 Date: Fri, 22 Aug 2025 23:58:59 -0400 Subject: [PATCH 010/159] added back a tooltip to cursor -- check copilot review --- lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index 64ea3441..9e693dea 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -133,7 +133,7 @@ "\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", + "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", From a95c22747ea58487522c02de1094d3a5942d6d63 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 13 Sep 2025 03:30:43 -0400 Subject: [PATCH 011/159] Made slight improvements to sections that enhance code readability + preventing keyboard icons from wrappin and (e.g., arrow icon was not staying alongside Shift but is fixed now) --- .../10_Turtles/10_Welcome/10_Welcome.ipynb | 9 ----- .../10_Welcome/20_Open_The_Screen.ipynb | 9 ----- .../10_Welcome/30_Run_Programs.ipynb | 38 ++++++++----------- .../10_Meet_Tina/Meet_Tina.py | 15 +++----- 4 files changed, 22 insertions(+), 49 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index 81a1078f..41e11321 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -36,15 +36,6 @@ " - **Turtle Programming** — A style of programming that uses the turtle module to create graphics and animations.\n", "
" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# These are called code cells, we will explore them in more detail later. Feel free to practice inside them throughout this course!" - ] } ], "metadata": { 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 c865ff16..84716653 100644 --- a/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb +++ b/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb @@ -19,15 +19,6 @@ "\n", "With your virtual screen open, you are now one step closer to interacting with your turtle!" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# These are called code cells, we will explore them in more detail later. Feel free to practice inside them throughout this course!\n" - ] } ], "metadata": { diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index 9e693dea..5c119da8 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -8,12 +8,12 @@ "\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", + "* **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", - "## Run Notebook Code Cells\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", + "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! Just move your mouse over the code cell and click the ▶️ button." ] @@ -26,11 +26,14 @@ "source": [ "import datetime\n", "\n", - "date = datetime.date.today() # Get the Date\n", + "# Get the current date\n", + "date = datetime.date.today()\n", "\n", - "s = F\"Hello 👋 World 🌎 ! Today is\" # Make a string with a message and the date\n", + "# Make a string with a message and the date\n", + "s = f\"Hello 👋 World 🌎 ! Today is\"\n", "\n", - "print(s, date) # Print the string. " + "# Print the string and the date\n", + "print(s, date)" ] }, { @@ -48,7 +51,7 @@ "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 Cell?
\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 Cell\"
\n", "\n", "The first time you press the ▶️ button you might notice that nothing happens. Don’t worry! Technically something did, you just were not able to see it. This is because you still need to pick which interpreter (or version) for Python to use.\n", "\n", @@ -69,13 +72,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### What is a Notebook Code Cell?\n", - "A *cell* is just a box where you can write code. You can run a code cell by clicking the ▶️ button or by pressing ⇧ shift + ⏎ enter on your keyboard.\n", + "### 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. This puts the cell in \"Command Mode.\"\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. " ] }, @@ -83,7 +86,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Your First Assignment\n", + "### Your First Assignment\n", "**Try it yourself!**\n", "\n", "1. Copy the Hello World code from above into the cell below, or type `print(\"Hello World!\")`
\n", @@ -104,7 +107,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## How to Run `.py` Programs\n", + "## **How to Run `.py` Programs**\n", "\n", "Running `.py` files might seem new or even a little overwhelming at first, but don’t worry—you’ll get the hang of it quickly! In the next lesson, you’ll try running a Python program called `02_Meet_Tina.py`. There are a few different ways to do this, and you can use whichever feels easiest for you. Let’s walk through each option together!\n", "\n", @@ -166,15 +169,6 @@ "source": [ "Now, click the Next Lesson button, open the file `02_Meet_Tina.py`, and try running it!" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Practice Code Cell\n" - ] } ], "metadata": { 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..290d8e15 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,16 +1,13 @@ """ -Run this program to meet Tina the Turtle. Tina is a -hexagon with legs and a head. +Meet Tina the Turtle! -You can run this program by: +This program draws Tina, a turtle with a hexagon-shaped body, legs, and a head. -1) clicking on ▶️ icon at the top of the editor -window -2) Hit the F5 function key +How to run this program (2 options): +1. Click the 'Run' (▶️) button at the top of your editor window OR the bottom left of this window. +2. Press the F5 key (in editors like VS Code or 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. +Don't worry if you don't understand all the code yet—future lessons will explain everything! """ import turtle # Tell Python we want to work with the turtle From eac2cf77860750a0b9e481a3ceb2a1fc257c5df6 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 13 Sep 2025 03:35:59 -0400 Subject: [PATCH 012/159] removed image of 'Create codespace on master' and replaced it with a style so it remains consistent and resizable --- lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 84716653..8fe418e7 100644 --- a/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb +++ b/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb @@ -8,7 +8,7 @@ "\n", "Welcome back, Code Explorer! 🌟 Are you ready to see your code come to life? Of course you are!\n", "\n", - "To get started, you probably clicked a button like this to open your Codespace. Now, to see your turtle's drawings, you just need to open a virtual screen. Don't worry, this part is easy.\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 just need to open a virtual screen. Don't worry, this part is easy.\n", "\n", "Most of the time, this screen will open automatically. If for some reason it doesn't appear, just look for the little monitor icon in the top right corner of your editor and click on it.\n", "\n", From 1ee39a0b0384f3829892b43a7ae24ad1901469cb Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 13 Sep 2025 04:21:50 -0400 Subject: [PATCH 013/159] Made some stylistic changes to make this notebook more readable and consistent with the previous ones (e.g., Welcome, Open The Screen, Run Programs). Everything is now broken down into sections --- .../20_What_Can_Tina_Do.ipynb | 119 +++++++++--------- 1 file changed, 56 insertions(+), 63 deletions(-) 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..8c70e73f 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,59 @@ { - "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. Don’t get intimidated—Tina’s magic 🧙‍♂️ is actually very simple and can be broken down into basic steps!\n", + "\n", + "In the next program, `02c_Squares_and_Circles`, 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", + "But before we move ahead, let’s break down how this magic ✨ 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", + "```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 tells Tina exactly what action to perform.\n", + "- The text after the `#` is a *comment* — it explains what the command does, but does not affect the program.\n", + "\n", + "## **Assignment**\n", + "\n", + "Take a look at the steps below, and when you’re ready, move on to the Next Lesson and keep exploring what Tina can do!\n", + "\n", + "1. **Run the program** called `02c_Squares_and_Circles` to see what Tina draws.\n", + "2. **Read the code.** For each line, try to guess what Tina will do. (The comments will help!)\n", + "3. **Change the program:**\n", + " - Make Tina draw a square and a circle of different sizes and colors.\n", + " - Try changing the numbers and colors in the commands to see what happens!\n", + "4. **Run the program again** and see how Tina’s drawing changes.\n", + "\n", + "> **Tip:** If you’re not sure what a command does, try changing it and running the program again. Playing and experimenting is a great way to learn!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 2 + "language_info": { + "name": "python", + "version": "3.12.11" + }, + "syllabus": { + "uid": "7tUP3zAZ" + } + }, + "nbformat": 4, + "nbformat_minor": 2 } From bde61a2a523ec30cab5fb9b74940344723b4994e Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 13 Sep 2025 04:23:04 -0400 Subject: [PATCH 014/159] removed unnecessary bolding --- .../20_Introducing_Tina/20_What_Can_Tina_Do.ipynb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 8c70e73f..cab99bb6 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 @@ -29,12 +29,12 @@ "\n", "Take a look at the steps below, and when you’re ready, move on to the Next Lesson and keep exploring what Tina can do!\n", "\n", - "1. **Run the program** called `02c_Squares_and_Circles` to see what Tina draws.\n", - "2. **Read the code.** For each line, try to guess what Tina will do. (The comments will help!)\n", - "3. **Change the program:**\n", + "1. Run the program called `02c_Squares_and_Circles` to see what Tina draws.\n", + "2. Read the code. For each line, try to guess what Tina will do. (The comments will help!)\n", + "3. Change the program:\n", " - Make Tina draw a square and a circle of different sizes and colors.\n", " - Try changing the numbers and colors in the commands to see what happens!\n", - "4. **Run the program again** and see how Tina’s drawing changes.\n", + "4. Run the program again and see how Tina’s drawing changes.\n", "\n", "> **Tip:** If you’re not sure what a command does, try changing it and running the program again. Playing and experimenting is a great way to learn!" ] From 4bb762d039d58ee950705e9b306e8cad2bd8b50e Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 13 Sep 2025 04:27:05 -0400 Subject: [PATCH 015/159] adjusted how I previously set up notes and tips -- this is clearer and stands out better --- lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index 5c119da8..8b5a6114 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -51,7 +51,7 @@ "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 Cell\"
\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 Cell\"\n", "\n", "The first time you press the ▶️ button you might notice that nothing happens. Don’t worry! Technically something did, you just were not able to see it. This is because you still need to pick which interpreter (or version) for Python to use.\n", "\n", @@ -65,7 +65,8 @@ "\n", "\n", "
\n", - "
Note: You’ll need to pick a Python interpreter every time you open a new notebook.
" + "\n", + "> **Note:** You’ll need to pick a Python interpreter every time you open a new notebook." ] }, { @@ -144,7 +145,7 @@ "\n", "Don’t worry about all the buttons for now! You just need to know that you can press the red square to stop your program, and the green circle to run it again.\n", "\n", - "Remember, it’s totally normal to feel unsure at first. Take your time, try out each method, and soon running Python programs will feel easy!\n" + "> **Tip:** Remember, it’s totally normal to feel unsure at first. Take your time, try out each method, and soon running Python programs will feel easy!\n" ] }, { From a3e513e6add9ff4400b62035f6679a7d746b178a Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 13 Sep 2025 04:29:41 -0400 Subject: [PATCH 016/159] added a hover cursor for span items (e.g., they act as tooltips) --- lessons/10_Turtles/10_Welcome/10_Welcome.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index 41e11321..862b022a 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -14,7 +14,7 @@ "* [**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", "\n", - "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist 🤖 that you can control with code. To do this, we'll use a pre-installed Python tool called the **turtle module**. \n", + "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist 🤖 that you can control with code. To do this, we'll use a pre-installed Python tool called the **turtle module**. \n", "\n", "You can tell your turtle 🐢 to move forward ↑, turn left ← or ➝ right, and change the color 🎨 of the lines it draws 🖍️— Soon, you'll be able to create awesome pictures like this one!\n", "\n", From a6148af8be1118c1297ad175840d53dba8e05eab Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 18 Sep 2025 00:53:48 -0400 Subject: [PATCH 017/159] Added missing '#' to make comment more consistent --- .../30_Squares_and_Circles/Squares_and_Circles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e9004997..12d5a8ef 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 @@ -16,7 +16,7 @@ ## ## 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 From 4a283f021b7e159fc4dac55d84ecb13dda9d5962 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 18 Sep 2025 01:23:36 -0400 Subject: [PATCH 018/159] fixed bolding for 'Let's Get Set Up!' --- lessons/10_Turtles/10_Welcome/10_Welcome.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index 862b022a..cfa84837 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -40,7 +40,7 @@ ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -54,7 +54,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.11" + "version": "3.13.5" }, "syllabus": { "uid": "RRTPqCQu" From f72c409b26b2a3abc46615217ab862556d208ac7 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 18 Sep 2025 17:18:45 -0400 Subject: [PATCH 019/159] Added headings to break up content, changed icons to improve appearance, style, and clarity, and reworded several sentences, added bullet points, and added notes/reminders/tips. --- .../40_Check_In_Your_Code.ipynb | 186 +++++++++++------- 1 file changed, 111 insertions(+), 75 deletions(-) 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..2cc88f71 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 @@ -4,120 +4,156 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Check In Your Code\n", + "# **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", + "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", - "## Make a Change\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. So, don’t risk losing your hard work and always make sure you frequently check in your code! \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", + "### **How To Make Changes**\n", "\n", - "## Commit and sync your code\n", + "Let’s make a change to `Meet_Tina.py` so you have something to check in. \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", + "You can change anything you like, whether you make the circle a different size, or change what Tina says!\n", "\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", - "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" + "> **Tip:** After making your change, remember to save your file! You can do this quickly by pressing Ctrl + S (Windows), or Cmd + S (Mac)." ] }, { "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", + "## **Commit and Sync Your Code**\n", "\n", - "
\n", + "### **Step 1: Open Source Control**\n", "\n", - "This happens if you click the ✓ Commit button without entering a commit message first. There are two ways to proceed:\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", - "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", + "**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", - "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" + "Now your work is safely 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": [ - "## Continue your Lessons" + "\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 'X' (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": [ - "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", + "## **How to Continue Your Lessons After a Break**\n", "\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 see that, just click on the green \"Restart Codespace\" to pick up where you left off. \n", + "If you left the browser window open, you should see the same screen open as before:\n", "\n", - "If you don't still have that window open, it is still easy to restart your Codespace. \n", + "\n", "\n", - "First, go your GitHub account and look for the repository called \"Python-Apprentice.\" \n", + "If you see this screen, simply click the green Restart Codespace button to continue where you left off.\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", + "### **Restarting Your Codespace from GitHub**\n", "\n", - "
\n", + "If you don’t have that window open anymore, it’s still easy to restart your Codespace.\n", "\n", - "The codespaces have crazy names, like this one, \"expert broccoli\".\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", - "If your Codespace isn't actually stopped, it is still running, you will see \"Active\" along with a green dot: \n", + "> **Note:** You should *not* see the green Create Codespace button. \n", "\n", - "
\n", + "**Example**\n", "\n", - " Either way, you can click on the crazy name link to open the Codespace and continue your lessons. \n", - "\n" + "\n", + "
\n", + "
\n", + "\n", + "> **Note:** Codespaces often have fun, random names, like 'expert broccoli' 🥦 or 'organic spoon' 🥄 to you help 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": { @@ -136,7 +172,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.11" + "version": "3.13.5" }, "syllabus": { "uid": "doD6P7fk" From 551e795fbc0b40e9752678dddf9f5cae7a183cee Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Wed, 24 Sep 2025 17:45:09 -0400 Subject: [PATCH 020/159] Added cursor hover icon to the Python tooltip --- .../10_Turtles/20_Introducing_Tina/20_What_Can_Tina_Do.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 cab99bb6..10a49b8d 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 @@ -48,7 +48,7 @@ }, "language_info": { "name": "python", - "version": "3.12.11" + "version": "3.13.5" }, "syllabus": { "uid": "7tUP3zAZ" From b6dd53b8a649aaa46ba670e24b513f9d8ba10453 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Wed, 24 Sep 2025 17:45:51 -0400 Subject: [PATCH 021/159] Removed unnecessary bolding --- lessons/10_Turtles/10_Welcome/10_Welcome.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index cfa84837..11ae73a6 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -6,7 +6,7 @@ "source": [ "# **Getting Started with Python**\n", "\n", - "Hello future coders, programmers, and engineers! Welcome to your first **Python** lesson with the **League of Amazing Programmers**. We're super excited to help you take your first steps into becoming an amazing programmer! \n", + "Hello future coders, programmers, and engineers! Welcome to your first **Python** lesson with the **League of Amazing Programmers**. We're super excited to help you take your first steps into becoming an amazing programmer! \n", "\n", "To follow along, make sure you're reading this lesson in one of these spots:\n", "\n", From 2ada36a594ce27309a3af96e69e9d6ccaf4d0833 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Wed, 24 Sep 2025 17:47:13 -0400 Subject: [PATCH 022/159] Removed unnecessary bolding --- lessons/10_Turtles/10_Welcome/10_Welcome.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index 11ae73a6..87173948 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -27,7 +27,7 @@ "
\n", "

Let's Get Set Up!

\n", "

Before we can start drawing, we need to set up your coding environment. Don't worry if anything seems confusing—that's what your instructors are here for! Feel free to ask for help if you get stuck.

\n", - "

Ready to go? Click the blue Next Lesson button at the bottom-left of your screen to continue! 🚀

\n", + "

Ready to go? Click the blue Next Lesson button at the bottom-left of your screen to continue! 🚀

\n", "
\n", "\n", "- **Notes & Key Terms:**\n", From 727fb0d53831e87dab188459326731d9b55d57ff Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Wed, 24 Sep 2025 17:47:51 -0400 Subject: [PATCH 023/159] Added cursor hover icon to the Notebook files & Python files tooltip --- lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index 8b5a6114..d791a3a8 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -8,8 +8,8 @@ "\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", + "* **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", From 82953bdabc715ad1f02ba5933073d385a820a6e5 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Wed, 24 Sep 2025 17:49:37 -0400 Subject: [PATCH 024/159] Typos: changed '02_Meet_Tina.py' to 'Meet_Tina.py' --- lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index d791a3a8..a85c10c5 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -110,7 +110,7 @@ "source": [ "## **How to Run `.py` Programs**\n", "\n", - "Running `.py` files might seem new or even a little overwhelming at first, but don’t worry—you’ll get the hang of it quickly! In the next lesson, you’ll try running a Python program called `02_Meet_Tina.py`. There are a few different ways to do this, and you can use whichever feels easiest for you. Let’s walk through each option together!\n", + "Running `.py` files might seem new or even a little overwhelming at first, but don’t worry—you’ll get the hang of it quickly! In the next lesson, you’ll try running a Python program called `Meet_Tina.py`. There are a few different ways to do this, and you can use whichever feels easiest for you. Let’s walk through each option together!\n", "\n", "### Using the Lesson Browser\n", "\n", From 5057161b4bafa3c4afc943a8cdc36b5ea811d996 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Wed, 24 Sep 2025 17:50:25 -0400 Subject: [PATCH 025/159] Typos: changed '02_Meet_Tina.py' to 'Meet_Tina.py' --- lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index a85c10c5..1430afb4 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -168,7 +168,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, click the Next Lesson button, open the file `02_Meet_Tina.py`, and try running it!" + "Now, click the Next Lesson button, open the file `Meet_Tina.py`, and try running it!" ] } ], From afe8c1a67b623bdd21a67b8fa619c56f8c5c6ae0 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Wed, 24 Sep 2025 17:53:26 -0400 Subject: [PATCH 026/159] Typos: changed '02c_Squares_and_Circles.py' to 'Squares_and_Circles.py' --- .../10_Turtles/20_Introducing_Tina/20_What_Can_Tina_Do.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 10a49b8d..a9125176 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 @@ -8,7 +8,7 @@ "\n", "In the last lesson, you met Tina the Turtle in a program that might have looked a little complicated. Don’t get intimidated—Tina’s magic 🧙‍♂️ is actually very simple and can be broken down into basic steps!\n", "\n", - "In the next program, `02c_Squares_and_Circles`, 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", + "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", "But before we move ahead, let’s break down how this magic ✨ works!\n", "\n", @@ -29,7 +29,7 @@ "\n", "Take a look at the steps below, and when you’re ready, move on to the Next Lesson and keep exploring what Tina can do!\n", "\n", - "1. Run the program called `02c_Squares_and_Circles` to see what Tina draws.\n", + "1. Run the program called `Squares_and_Circles.py` to see what Tina draws.\n", "2. Read the code. For each line, try to guess what Tina will do. (The comments will help!)\n", "3. Change the program:\n", " - Make Tina draw a square and a circle of different sizes and colors.\n", From 82ba853c7e38f2350ae024bbc293c5937454e740 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Wed, 24 Sep 2025 18:02:32 -0400 Subject: [PATCH 027/159] Updated the multi-line comment in 'Squares_and_Circles.py' to be easier to understand --- .../30_Squares_and_Circles/Squares_and_Circles.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 12d5a8ef..dc20c3bc 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,9 +1,10 @@ """ -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. +This is a simple Turtle program that draws a circle inside of a square, and then it writes a message. +The lines that start with a # are called comments, they are not executed by Python. +Comments are used by developers to explain exactly what each line of code does. +The lines found inside three quotation marks (like these ones) are also comments, +but they are formally known as docstrings, or multi-line comments. +Read this program's code and try to understand what each line does. """ import turtle # Tell Python we want to work with the turtle From a4c6a19e19d75704a911c7829344a5bb7b94cf2f Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Wed, 24 Sep 2025 18:03:05 -0400 Subject: [PATCH 028/159] Updated the multi-line comment in 'Squares_and_Circles.py' to be easier to understand --- .../30_Squares_and_Circles/Squares_and_Circles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 dc20c3bc..d8b43d59 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,5 +1,5 @@ """ -This is a simple Turtle program that draws a circle inside of a square, and then it writes a message. +This is a simple Turtle program that draws a circle inside of a square, and writes a message. The lines that start with a # are called comments, they are not executed by Python. Comments are used by developers to explain exactly what each line of code does. The lines found inside three quotation marks (like these ones) are also comments, From 455d67d7413e9387d0647f324b43ac45cb6d8fbb Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Wed, 24 Sep 2025 18:10:43 -0400 Subject: [PATCH 029/159] Updated readme to be clearer; changed '02b_Squares_and_Circles.py' to 'Squares_and_Circles.py' --- .../20_Introducing_Tina/30_Squares_and_Circles/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 From a72f9972e31a1b7da16a7cf96d7f656dd31981d6 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Wed, 24 Sep 2025 18:37:20 -0400 Subject: [PATCH 030/159] Updated readme to be clearer --- .../10_Turtles/20_Introducing_Tina/10_Meet_Tina/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 From 33bda164f8a67136476e5c33a9d3be89e64e546d Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Wed, 24 Sep 2025 18:38:43 -0400 Subject: [PATCH 031/159] Updated comments to become clearer --- .../20_Introducing_Tina/10_Meet_Tina/Meet_Tina.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 290d8e15..571a79f2 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,13 +1,13 @@ """ Meet Tina the Turtle! -This program draws Tina, a turtle with a hexagon-shaped body, legs, and a head. +This program draws Tina: a turtle with a hexagon-shaped green shell, four brown legs, a head, and a tail. -How to run this program (2 options): -1. Click the 'Run' (▶️) button at the top of your editor window OR the bottom left of this window. -2. Press the F5 key (in editors like VS Code or Thonny; or GitHub Codespaces). +How to run this program: +1. Click the 'Run' (▶) button at the top of your editor window or in the bottom left corner. +2. Or, press the F5 key (in editors like VS Code, Thonny, or GitHub Codespaces). -Don't worry if you don't understand all the code yet—future lessons will explain everything! +Don't worry if you don't understand all the code yet—future lessons will explain everything step by step! """ import turtle # Tell Python we want to work with the turtle From 7241b8f1dea5296c64915d487e11d49fe95953e3 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Wed, 24 Sep 2025 18:40:56 -0400 Subject: [PATCH 032/159] Updated comments to become clearer --- .../20_Introducing_Tina/10_Meet_Tina/Meet_Tina.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 571a79f2..a73d6494 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 @@ -3,9 +3,9 @@ This program draws Tina: a turtle with a hexagon-shaped green shell, four brown legs, a head, and a tail. -How to run this program: -1. Click the 'Run' (▶) button at the top of your editor window or in the bottom left corner. -2. Or, press the F5 key (in editors like VS Code, Thonny, or GitHub Codespaces). +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). Don't worry if you don't understand all the code yet—future lessons will explain everything step by step! """ From bcd8adf96667b4055a3f2e641ccc756e8fad144d Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 26 Sep 2025 17:21:33 -0400 Subject: [PATCH 033/159] slight change to bottom tooltip --- .../10_Turtles/20_Introducing_Tina/20_What_Can_Tina_Do.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a9125176..2371d525 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 @@ -36,7 +36,7 @@ " - Try changing the numbers and colors in the commands to see what happens!\n", "4. Run the program again and see how Tina’s drawing changes.\n", "\n", - "> **Tip:** If you’re not sure what a command does, try changing it and running the program again. Playing and experimenting is a great way to learn!" + "> **Tip:** If you’re not sure what a command does, try changing it and running the program again. Experimentation is a great way to learn!" ] } ], From 0d311eeb4f60a0af24b8a720d858e06552f46b2c Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 26 Sep 2025 20:29:27 -0400 Subject: [PATCH 034/159] Restructured the Loops page to be more informative, comprehensively cover the concept, and include practice examples --- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 107 +++++++++++++-------- 1 file changed, 69 insertions(+), 38 deletions(-) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 676b87f9..980b64a1 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -4,12 +4,22 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Loops and Variables\n", + "# Loops\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", + "Welcome to the world of loops! In this lesson, you'll discover how loops make programming easier and more powerful. Loops let you repeat automate repetitive tasks, so you can save time, write cleaner code, and solve problems more efficiently. By pairing loops with *Tina the Turtle*, you'll learn how to simplify your programs, reduce mistakes, and create amazing things with less effort!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## What is a Loop?\n", + "\n", + "A **loop** is a programming structure that allows you repeatedly execute a set of instructions until a certain condition is met. Instead of writing the same code over and over, you can use a loop to do the work for you. They can help you by making your code shorter and easier to read, making your code less likely to have mistakes.\n", "\n", - "```python \n", + "For example, in our first Tina the Turtle program, we had Tina draw a square. The code looked like this:\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", @@ -21,39 +31,36 @@ "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", + "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) # Move tina forward by the forward distance\n", + "tina.left(90) # Turn tina left by the left turn\n", "\n", - "tina.forward(150)\n", - "tina.left(90)\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", - "tina.forward(150)\n", - "tina.left(90)\n", - "```" + "Notice how the last two lines (`tina.forward(150)` and `tina.left(90)`) are repeated four times. This makes the code longer, harder to read, and more likely to have mistakes if you need to change something. " ] }, { "cell_type": "markdown", - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, + "metadata": {}, "source": [ - "The code \n", + "### Why Use Loops?\n", "\n", - "```python \n", - "tina.forward(150)\n", - "tina.left(90)\n", + "Wouldn't it be better if we could tell Tina to repeat the previous steps automatically? That's exactly what loops are for!\n", "\n", - "```\n", + "Loops are useful because they:\n", "\n", - "gets repeated 4 times. That doesn't seem right!\n", + "- **Save time:** You don’t have to write the same code over and over.\n", + "- **Reduce errors:** Less repeated code means fewer chances to make mistakes.\n", + "- **Make changes easier:** If you want to change something, you only have to change it in one place.\n", "\n", - "We can improve this code with a loop. Here is an example of a loop:\n", + "### How Do Loops Work?\n", "\n", - "( Remember that you can run it; click the \"▶️\" on the left. \n", - " ) " + "Let’s look at this simple example of a `for` loop:" ] }, { @@ -62,25 +69,47 @@ "metadata": {}, "outputs": [], "source": [ - "# For loop; try changing the number of iterations to 6\n", - "for i in range(4):\n", - " print('Loop Iteration', i)" + "# Run Me!\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": [ - "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", + "Here’s what’s happening:\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", + "- `for i in range(4):` tells Python to repeat the indented code 4 times.\n", + "- The variable `i` starts at 0 and goes up to 3 (so, 0, 1, 2, 3). \n", + "- Each time the loop runs, it prints the current value of `i`.\n", "\n", + "> **Note:** Indentation is important! The code that is indented under the `for` statement is what gets repeated." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Using Loops with Tina the Turtle\n", "\n", - "Now try updating the next program ``05_Loop_with_Turtle.py`` with a loop. " + "Instead of repeating the same lines, we can use a loop to make Tina draw a square:\n", + "\n", + "```python\n", + "import turtle\n", + "turtle.setup(600,600,0,0)\n", + "tina = turtle.Turtle()\n", + "tina.shape('turtle')\n", + "tina.speed(2)\n", + "\n", + "for i in range(4):\n", + " tina.forward(150)\n", + " tina.left(90)\n", + "```\n", + "\n", + "Now, Tina will move forward and turn left four times, drawing a square. The loop makes the code much cleaner and easier to understand!\n", + "\n", + "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them—understanding loops will help you solve problems more efficiently and write better code!" ] }, { @@ -88,12 +117,14 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "# Practice: Use a loop to count from 1 to 10\n" + ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": ".venv", "language": "python", "name": "python3" }, @@ -107,7 +138,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.11" + "version": "3.13.5" }, "syllabus": { "uid": "abX8sNwB" From 68c6895c1c870710cbba73d8a54b525f1f1202a1 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 27 Sep 2025 01:33:52 -0400 Subject: [PATCH 035/159] Restructured Loops lesson for clarity; Reorganized content into clear, structured sections: 'What is a Loop?', 'Why Use Loops?', and 'How Do Loops Work?'; Added a bulleted list to explicitly state the benefits of using loops; Improved code examples by showing a direct 'before' (repetitive) and 'after' (looped) comparison; Added a concluding summary tip and a practice exercise cell to reinforce learning. --- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 980b64a1..1cc49227 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -6,7 +6,7 @@ "source": [ "# Loops\n", "\n", - "Welcome to the world of loops! In this lesson, you'll discover how loops make programming easier and more powerful. Loops let you repeat automate repetitive tasks, so you can save time, write cleaner code, and solve problems more efficiently. By pairing loops with *Tina the Turtle*, you'll learn how to simplify your programs, reduce mistakes, and create amazing things with less effort!" + "Welcome to the world of loops! In this lesson, you'll discover how loops make programming easier and more powerful by allowing you to automate repetitive tasks, save time, write cleaner code, and solve problems more efficiently. By pairing loops with *Tina the Turtle*, you'll learn how to simplify your programs, reduce mistakes, and create amazing things with less effort!" ] }, { @@ -50,13 +50,13 @@ "source": [ "### Why Use Loops?\n", "\n", - "Wouldn't it be better if we could tell Tina to repeat the previous steps automatically? That's exactly what loops are for!\n", + "Instead of repeating the same instructions, loops let you tell Tina to do something multiple times automatically!\n", "\n", - "Loops are useful because they:\n", + "Loops are powerful seeing that they:\n", "\n", - "- **Save time:** You don’t have to write the same code over and over.\n", - "- **Reduce errors:** Less repeated code means fewer chances to make mistakes.\n", - "- **Make changes easier:** If you want to change something, you only have to change it in one place.\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", "\n", From b9d48f5f18c7200fecd7d65650eb710ec7326db8 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 27 Sep 2025 02:43:06 -0400 Subject: [PATCH 036/159] Restructured .py files (Turtle Tricks + Meet Tina + Squares and Circles + Loop With Turtle) to have clearer directions; Corrects minor stylistic inconsistencies in the welcome lesson. Changed 'League of Amazing Programmers' from bold to italics for consistent emphasis; Wrapped the 'Let's Get Set Up!' heading (Welcome.ipynb) in a tag. --- .../10_Turtles/10_Welcome/10_Welcome.ipynb | 6 +- .../10_Welcome/30_Run_Programs.ipynb | 14 +-- .../10_Meet_Tina/Meet_Tina.py | 2 +- .../20_What_Can_Tina_Do.ipynb | 108 ++++++++++++++++-- .../Squares_and_Circles.py | 16 +-- .../30_Turtle_Tricks/10_Turtle_Tricks.py | 12 +- .../30_Turtle_Tricks/20_Turtle_Tricks.py | 12 +- .../30_Turtle_Tricks/30_Turtle_Tricks.py | 15 ++- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 39 ++++--- .../40_Loops/20_Loop_with_Turtle.py | 15 ++- .../40_Loops/30_Loop_with_Turtle.py | 13 ++- 11 files changed, 184 insertions(+), 68 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index 87173948..d9d03986 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -6,7 +6,7 @@ "source": [ "# **Getting Started with Python**\n", "\n", - "Hello future coders, programmers, and engineers! Welcome to your first **Python** lesson with the **League of Amazing Programmers**. We're super excited to help you take your first steps into becoming an amazing programmer! \n", + "Hello future coders, programmers, and engineers! Welcome to your first **Python** lesson with the *League of Amazing Programmers*. We're super excited to help you take your first steps into becoming an amazing programmer! \n", "\n", "To follow along, make sure you're reading this lesson in one of these spots:\n", "\n", @@ -25,10 +25,10 @@ "
\n", "\n", "
\n", - "

Let's Get Set Up!

\n", + "

Let's Get Set Up!

\n", "

Before we can start drawing, we need to set up your coding environment. Don't worry if anything seems confusing—that's what your instructors are here for! Feel free to ask for help if you get stuck.

\n", "

Ready to go? Click the blue Next Lesson button at the bottom-left of your screen to continue! 🚀

\n", - "
\n", + "
\n", "\n", "- **Notes & Key Terms:**\n", " - **Python** — A popular programming language known for its simplicity and readability.\n", diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index 1430afb4..a3f76bdc 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -49,7 +49,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Setting Up The Interpreter\n", + "### **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 Cell\"\n", "\n", @@ -73,7 +73,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### What is a Code Cell?\n", + "### **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", @@ -87,7 +87,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Your First Assignment\n", + "### **Your First Assignment**\n", "**Try it yourself!**\n", "\n", "1. Copy the Hello World code from above into the cell below, or type `print(\"Hello World!\")`
\n", @@ -121,11 +121,11 @@ "\n", "\n", "Here’s how it works, step by step:\n", - "- Click Run to start your program\n", + "- Click ▶ Run to start your program\n", "\n", - "- Click Stop to stop it\n", + "- Click ⏹ Stop to stop it\n", "\n", - "- When you’re ready for the next lesson, just click Next Lesson\n", + "- When you’re ready for the next lesson, just click Next Lesson\n", "\n", "### Using the Play Button\n", "\n", @@ -168,7 +168,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, click the Next Lesson button, open the file `Meet_Tina.py`, and try running it!" + "Now, click the Next Lesson button, open the file `Meet_Tina.py`, and try running it!" ] } ], 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 a73d6494..1648e708 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,5 +1,5 @@ """ -Meet Tina the Turtle! +# Meet_Tina.py This program draws Tina: a turtle with a hexagon-shaped green shell, four brown legs, a head, and a tail. 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 2371d525..03188b8c 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 @@ -10,31 +10,115 @@ "\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", - "But before we move ahead, let’s break down how this magic ✨ works!\n", + "But before we move ahead, let’s break down how this magic ✨ works!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\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", + "Here is what some of the program will look like with comments to explain what each line does:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "```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", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- The code *before* the `#` is a *command* — it tells Tina exactly what action to perform.\n", + "- The text *after* the `#` is a *comment* — it explains what the command does, but does not affect the program." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## **Python Command Cheatsheet**\n", "\n", - "- The code before the `#` is a *command* — it tells Tina exactly what action to perform.\n", - "- The text after the `#` is a *comment* — it explains what the command does, but does not affect the program.\n", + "This section is a handy cheat sheet listing the most important commands and functions used in the `Meet_Tina.py` program. It gives you quick explanations for each command, so you can easily look up what they do while reading or editing the code. Use this reference to help you understand Tina's actions and experiment confidently with your own changes." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **General Python Keywords & Functions**\n", + "```python\n", + "import # Brings an entire library of code into your program.\n", + "from ... import # Brings specific functions from a library into your program.\n", + "def # Defines a new function that you can call later.\n", + "return # Sends a value back from a function.\n", + "for ... in # Creates a loop to repeat a block of code for each item in a sequence.\n", + "range() # Generates a sequence of numbers, often used in loops.\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Math Library Functions**\n", + "```python\n", + "radians() # Converts an angle from degrees to radians (for trigonometric functions).\n", + "tan() # Calculates the tangent of an angle (which must be in radians).\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Turtle Graphics Commands**\n", + "```python\n", + "turtle.setup() # Sets the size and position of the turtle's drawing window.\n", + "turtle.Turtle() # Creates a new turtle object to draw with.\n", + "turtle.exitonclick() # Pauses the program, waiting for a mouse click on the window to close it.\n", + "speed() # Sets the turtle's animation speed (e.g., 'fastest', 'slow', or 1-10).\n", + "penup() # Lifts the pen, so the turtle moves without drawing.\n", + "goto() # Moves the turtle to an absolute (x, y) coordinate.\n", + "pendown() # Puts the pen down, so the turtle draws when it moves.\n", + "begin_fill() # Marks the starting point of a shape to be filled with color.\n", + "end_fill() # Fills the shape drawn since begin_fill() was called.\n", + "pencolor() # Sets the color of the turtle's pen (the outline of shapes).\n", + "fillcolor() # Sets the color that will be used to fill a shape.\n", + "setheading() # Sets the turtle's orientation to a specific angle (0 is east).\n", + "forward() # Moves the turtle forward by a specific distance.\n", + "backward() # Moves the turtle backward by a specific distance.\n", + "right() # Turns the turtle to its right by a specific angle.\n", + "left() # Turns the turtle to its left by a specific angle.\n", + "circle() # Draws a circle with a given radius.\n", + "write() # Writes text on the screen at the turtle's current position.\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\n", "## **Assignment**\n", "\n", - "Take a look at the steps below, and when you’re ready, move on to the Next Lesson and keep exploring what Tina can do!\n", + "Take a look at the steps below, and when you’re ready, move on to the Next Lesson and keep exploring what Tina can do!\n", "\n", - "1. Run the program called `Squares_and_Circles.py` to see what Tina draws.\n", - "2. Read the code. For each line, try to guess what Tina will do. (The comments will help!)\n", - "3. Change the program:\n", - " - Make Tina draw a square and a circle of different sizes and colors.\n", - " - Try changing the numbers and colors in the commands to see what happens!\n", - "4. Run the program again and see how Tina’s drawing changes.\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.\n", "\n", "> **Tip:** If you’re not sure what a command does, try changing it and running the program again. Experimentation is a great way to learn!" ] 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 d8b43d59..2a225df2 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,10 +1,12 @@ """ -This is a simple Turtle program that draws a circle inside of a square, and writes a message. -The lines that start with a # are called comments, they are not executed by Python. -Comments are used by developers to explain exactly what each line of code does. -The lines found inside three quotation marks (like these ones) are also comments, -but they are formally known as docstrings, or multi-line comments. -Read this program's code 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 @@ -73,4 +75,4 @@ # 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 +# the next file, 40_Check_In_Your_Code.ipynb \ No newline at end of file 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..1e61b20c 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,18 @@ """ -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. + +Objectives: +- 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 - 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..da1c38c7 100644 --- a/lessons/10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py +++ b/lessons/10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py @@ -1,9 +1,13 @@ """ -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. + +Objectives: +- 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 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..d0b4c7a4 100644 --- a/lessons/10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py +++ b/lessons/10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py @@ -1,12 +1,15 @@ """ -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. +Objectives: +- 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. +- Challenge yourself to experiment with 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 diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 1cc49227..cff25b1d 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Loops\n", + "# **Loops**\n", "\n", "Welcome to the world of loops! In this lesson, you'll discover how loops make programming easier and more powerful by allowing you to automate repetitive tasks, save time, write cleaner code, and solve problems more efficiently. By pairing loops with *Tina the Turtle*, you'll learn how to simplify your programs, reduce mistakes, and create amazing things with less effort!" ] @@ -13,11 +13,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## What is a Loop?\n", + "## **What is a Loop?**\n", "\n", "A **loop** is a programming structure that allows you repeatedly execute a set of instructions until a certain condition is met. Instead of writing the same code over and over, you can use a loop to do the work for you. They can help you by making your code shorter and easier to read, making your code less likely to have mistakes.\n", "\n", - "For example, in our first Tina the Turtle program, we had Tina draw a square. The code looked like this:\n", + "For example, in our first Tina the Turtle program, we had Tina draw a square. The code looked like this:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\n", "```python\n", "import turtle # Tell Python we want to work with the turtle\n", @@ -39,7 +45,13 @@ "\n", "tina.forward(150) # Move tina forward by the forward distance\n", "tina.left(90) # Turn tina left by the left turn\n", - "```\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\n", "Notice how the last two lines (`tina.forward(150)` and `tina.left(90)`) are repeated four times. This makes the code longer, harder to read, and more likely to have mistakes if you need to change something. " ] @@ -48,7 +60,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Why Use Loops?\n", + "### **Why Use Loops?**\n", "\n", "Instead of repeating the same instructions, loops let you tell Tina to do something multiple times automatically!\n", "\n", @@ -58,7 +70,7 @@ "- **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", + "### **How Do Loops Work?**\n", "\n", "Let’s look at this simple example of a `for` loop:" ] @@ -91,7 +103,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Using Loops with Tina the Turtle\n", + "## **Using Loops with Tina the Turtle**\n", "\n", "Instead of repeating the same lines, we can use a loop to make Tina draw a square:\n", "\n", @@ -109,16 +121,9 @@ "\n", "Now, Tina will move forward and turn left four times, drawing a square. The loop makes the code much cleaner and easier to understand!\n", "\n", - "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them—understanding loops will help you solve problems more efficiently and write better code!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Practice: Use a loop to count from 1 to 10\n" + "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them—understanding loops will help you solve problems more efficiently and write better code!\n", + "\n", + "Now, click the Next Lesson button, open the file `20_Loop_with_Turtle.py`, and try running it!" ] } ], 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..1aa11e55 100644 --- a/lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py +++ b/lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py @@ -1,8 +1,17 @@ """ -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. + +Objectives: +- Replace the repeated movement and turning lines with a loop that runs four times. +- The loop should move the turtle forward and then turn it left to draw each side of the square. +- This will make the code shorter and easier to read. + +Hint: Use a for loop to repeat the actions for each side of the square. """ import turtle # Tell Python we want to work with the turtle 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..98166d97 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,14 @@ """ -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. + +Objectives: +- 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!) """ ... # Your code here \ No newline at end of file From 2a0dd5984ca8c783d78a72d96bb9f1b6d8f6a22e Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sun, 28 Sep 2025 16:46:41 -0400 Subject: [PATCH 037/159] Slight change to the multi-line comment for brevity --- lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 1aa11e55..fc5a5abe 100644 --- a/lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py +++ b/lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py @@ -7,11 +7,7 @@ In this exercise, you will modify the program to use a loop to draw the square instead. Objectives: -- Replace the repeated movement and turning lines with a loop that runs four times. -- The loop should move the turtle forward and then turn it left to draw each side of the square. -- This will make the code shorter and easier to read. - -Hint: Use a for loop to repeat the actions for each side of the square. +- Replace the repeated movement and turning lines with a for loop that runs four times. """ import turtle # Tell Python we want to work with the turtle From 63eec319baaa930faae6fa65147314fdaa4e07f7 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 2 Oct 2025 12:16:53 -0400 Subject: [PATCH 038/159] Updated & Removed Python Cheatsheet and instead added a section about researching and finding coding documentation + an embedded Turtle Module Documentation Page --- .../20_What_Can_Tina_Do.ipynb | 124 +++++++----------- 1 file changed, 51 insertions(+), 73 deletions(-) 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 03188b8c..42396e82 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 @@ -10,100 +10,78 @@ "\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", - "But before we move ahead, let’s break down how this magic ✨ works!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ + "But before we move ahead, let’s break down how this magic ✨ works!\n", "\n", - "## **How Tina Follows Commands**\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", - "Here is what some of the program will look like with comments to explain what each line does:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ "```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", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "- The code *before* the `#` is a *command* — it tells Tina exactly what action to perform.\n", - "- The text *after* the `#` is a *comment* — it explains what the command does, but does not affect the program." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ + "```\n", "\n", - "## **Python Command Cheatsheet**\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", - "This section is a handy cheat sheet listing the most important commands and functions used in the `Meet_Tina.py` program. It gives you quick explanations for each command, so you can easily look up what they do while reading or editing the code. Use this reference to help you understand Tina's actions and experiment confidently with your own changes." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### **General Python Keywords & Functions**\n", - "```python\n", - "import # Brings an entire library of code into your program.\n", - "from ... import # Brings specific functions from a library into your program.\n", - "def # Defines a new function that you can call later.\n", - "return # Sends a value back from a function.\n", - "for ... in # Creates a loop to repeat a block of code for each item in a sequence.\n", - "range() # Generates a sequence of numbers, often used in loops.\n", - "```" + "> **Tip:** Comments are ignored by Python and don’t affect how the program runs." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### **Math Library Functions**\n", - "```python\n", - "radians() # Converts an angle from degrees to radians (for trigonometric functions).\n", - "tan() # Calculates the tangent of an angle (which must be in radians).\n", - "```" + "## **Seeking Additional Information**\n", + "\n", + "Learning to program with Python's Turtle graphics is fun and creative! One of the best habits you can develop is regularly consulting official documentation and tutorials. This helps you understand commands, discover new features, and solve problems more efficiently. Here’s a detailed guide on how to find and use Python Turtle documentation.\n", + "\n", + "### **How to Find Python Turtle Documentation**\n", + "\n", + "1. **Use a Search Engine** by opening your preferred search engine (Google, Bing, DuckDuckGo, etc.).\n", + "\n", + "2. **Enter Your Query** by typing keywords such as `python turtle documentation`, `python turtle commands`, or specific questions like `how to draw a circle with turtle in python`.\n", + "\n", + "3. **Visit Official Sources** and look for links to the [Python Turtle documentation](https://docs.python.org/3/library/turtle.html) or other reputable sites. The official Python docs are usually among the top results and provide comprehensive information.\n", + "\n", + "4. **Explore Tutorials and Examples** on websites and forums (such as Real Python, GeeksforGeeks, Stack Overflow, and YouTube) that offer step-by-step guides, video tutorials, and sample code. These resources can help you understand how commands work in real programs.\n", + "\n", + "5. **Bookmark Useful Pages** so you can save helpful documentation, tutorials, or example projects for quick reference as you continue learning.\n", + "\n", + "6. **Read API References** in the official documentation, which includes an API reference page that lists all the available Turtle methods (like `forward()`, `right()`, `pencolor()`, etc.), their parameters, and example usage.\n", + "\n", + "7. **Check Community Q&A** if you encounter an error or want to achieve a specific effect, as searching forums like Stack Overflow can provide solutions and explanations from other programmers.\n", + "\n", + "### **Why Consulting Documentation Is Essential**\n", + "\n", + "- **Learn Correct Syntax** because documentation shows the exact way to use commands and functions, helping you avoid mistakes and understand how each function works.\n", + "\n", + "- **Discover New Features** since you might find commands or options you didn’t know existed, allowing you to create more interesting and complex drawings.\n", + "\n", + "- **Troubleshoot Problems** if your code isn’t working as expected, as documentation and community forums often have solutions, explanations, and troubleshooting tips.\n", + "\n", + "- **Build Independence** by regularly searching for answers, which helps you become a more confident and resourceful programmer, able to solve problems on your own.\n", + "\n", + "- **Stay Updated** because documentation is updated as Python evolves, so checking it helps you learn about new features and best practices.\n", + "\n", + "### **Extra Ways To Use Documentation Effectively**\n", + "\n", + "- **Try Out Examples** by copying and running example code from the documentation to see how it works. Experiment by changing parameters and observing the results.\n", + "\n", + "- **Use the Search Feature** on most documentation sites, which have a search bar you can use to quickly find information about specific commands or topics.\n", + "\n", + "- **Read the Introduction** at the beginning of the documentation, which often explains how to set up Turtle graphics and provides an overview of its capabilities.\n", + "\n", + "> **Tip:** Whenever you’re unsure about a command or want to try something new, searching for documentation or examples is one of the best ways to learn and grow as a programmer. The more you explore, the more creative and confident you’ll become!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### **Turtle Graphics Commands**\n", - "```python\n", - "turtle.setup() # Sets the size and position of the turtle's drawing window.\n", - "turtle.Turtle() # Creates a new turtle object to draw with.\n", - "turtle.exitonclick() # Pauses the program, waiting for a mouse click on the window to close it.\n", - "speed() # Sets the turtle's animation speed (e.g., 'fastest', 'slow', or 1-10).\n", - "penup() # Lifts the pen, so the turtle moves without drawing.\n", - "goto() # Moves the turtle to an absolute (x, y) coordinate.\n", - "pendown() # Puts the pen down, so the turtle draws when it moves.\n", - "begin_fill() # Marks the starting point of a shape to be filled with color.\n", - "end_fill() # Fills the shape drawn since begin_fill() was called.\n", - "pencolor() # Sets the color of the turtle's pen (the outline of shapes).\n", - "fillcolor() # Sets the color that will be used to fill a shape.\n", - "setheading() # Sets the turtle's orientation to a specific angle (0 is east).\n", - "forward() # Moves the turtle forward by a specific distance.\n", - "backward() # Moves the turtle backward by a specific distance.\n", - "right() # Turns the turtle to its right by a specific angle.\n", - "left() # Turns the turtle to its left by a specific angle.\n", - "circle() # Draws a circle with a given radius.\n", - "write() # Writes text on the screen at the turtle's current position.\n", - "```" + "### **Official Python Turtle Documentation**\n", + "" ] }, { From 30d8a7e4da03754d43f69e8d3aa97fea414e175e Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 2 Oct 2025 20:27:07 -0400 Subject: [PATCH 039/159] created loop examples that are runnable --- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 178 ++++++++++++++++++--- 1 file changed, 155 insertions(+), 23 deletions(-) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index cff25b1d..594c3505 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -21,31 +21,28 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ + "# This code will not run inside the notebook, it's just for you to see how it works.\n", + "# You can copy and paste it into your own Python environment to see it in action!\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) # Move tina forward by the forward distance\n", "tina.left(90) # Turn tina left by the left turn\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) # Move tina forward by the forward distance\n", - "tina.left(90) # Turn tina left by the left turn\n", - "```" + "tina.left(90) # Turn tina left by the left turn" ] }, { @@ -56,6 +53,44 @@ "Notice how the last two lines (`tina.forward(150)` and `tina.left(90)`) are repeated four times. This makes the code longer, harder to read, and more likely to have mistakes if you need to change something. " ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Using Loops with Tina the Turtle**\n", + "\n", + "Instead of repeating the same lines, we can use a loop to make Tina draw a square:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This code will not run inside the notebook, it's just for you to see how it works.\n", + "# You can copy and paste it into your own Python environment to see it in action!\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", + "tina = turtle.Turtle() # Create 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", + "for i in range(4): # Loop 4 times\n", + " tina.forward(150) # Move forward 150 units\n", + " tina.left(90) # Turn left 90 degrees" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, Tina will move forward and turn left four times, drawing a square. The loop makes the code much cleaner and easier to understand!\n", + "\n", + "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them—understanding loops will help you solve problems more efficiently and write better code!" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -82,6 +117,8 @@ "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" ] @@ -103,26 +140,121 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## **Using Loops with Tina the Turtle**\n", + "## **Practicing with Loops**\n", + "\n", + "Loops are a powerful tool for automating repetitive tasks. Try running these examples to see how loops can be used in different ways!\n", + "\n", + "### **Drawing an Orange Square**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", "\n", - "Instead of repeating the same lines, we can use a loop to make Tina draw a square:\n", + "# Size of the square\n", + "size = 20 \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": [ + "### **Drawing a Checkerboard Pattern**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", "\n", - "```python\n", - "import turtle\n", - "turtle.setup(600,600,0,0)\n", - "tina = turtle.Turtle()\n", - "tina.shape('turtle')\n", - "tina.speed(2)\n", + "# Define the size of the checkerboard\n", + "size = 20\n", "\n", - "for i in range(4):\n", - " tina.forward(150)\n", - " tina.left(90)\n", - "```\n", + "# Loop through each row\n", + "for row in range(size):\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": [ + "### **Drawing an American Flag**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", "\n", - "Now, Tina will move forward and turn left four times, drawing a square. The loop makes the code much cleaner and easier to understand!\n", + "# Total number of rows in the flag\n", + "flag_height = 13\n", + "# Total number of columns in the flag \n", + "flag_width = 32 \n", "\n", - "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them—understanding loops will help you solve problems more efficiently and write better code!\n", + "# Number of rows in the blue canton (top-left corner)\n", + "canton_height = 7 \n", + "# Number of columns in the blue canton \n", + "canton_width = 12 \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": [ + "Try modifying these loops or creating your own to practice!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "Now, click the Next Lesson button, open the file `20_Loop_with_Turtle.py`, and try running it!" ] } From b6259d9b0f5d2a45614211347d5e3c7b695a44a0 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 3 Oct 2025 20:20:05 -0400 Subject: [PATCH 040/159] improved loop examples that are runnable --- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 594c3505..53d19e0c 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -163,6 +163,19 @@ " print('🟧' * size) " ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here’s what’s happening:\n", + "\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": {}, @@ -194,6 +207,21 @@ " print()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here’s what’s happening:\n", + "Here’s what’s happening:\n", + "\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 useful for working with grids or tables, like drawing patterns or processing 2D data. In this example, the outer loop handles rows and the inner loop handles columns, letting you control each cell in the grid individually." + ] + }, { "cell_type": "markdown", "metadata": {}, From 436327a9660bbeb4ce22821cb1e4e43513ca294b Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 6 Oct 2025 11:55:51 -0400 Subject: [PATCH 041/159] Updated lesson intro and summary for clarity; Replaced the old introductory markdown with a clearer, more engaging/deeper explanation of variables and functions; Improved formatting and wording for readability; Added a concise summary emphasizing the importance of variables and functions, and encouraged creative practice with loop and Tina the Turtle; WORK IN PROGRESS! --- .../10_Variables_and_Functions.ipynb | 139 ++++++++++++++---- 1 file changed, 109 insertions(+), 30 deletions(-) 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 index 1b3ede78..25e8a223 100644 --- 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 @@ -4,23 +4,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Variables and Functions\n", + "# **Variables and Functions**\n", "\n", - "
\n", - " A few things to remember:\n", + "Welcome to the world of **variables** and **functions**! In this lesson, you'll learn how variables help you store and reuse information, and how functions let you organize your code into reusable blocks. These concepts make your programs easier to read, update, and expand.\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", + "Here is one of the ways to solve the last assignment, making shapes with a loop:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This code will not run inside the notebook, it's just for you to see how it works.\n", + "# You can copy and paste it into your own Python environment to see it in action!\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", @@ -29,17 +27,80 @@ "\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", + " tina.left(90) # Turn tina left" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **What is a Variable?**\n", + "A variable is a name that stores a value. You can think of it as a labeled box that holds information for your program. Variables let you reuse values and make your code easier to change.\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": [ + "# Store the number of sides in a variable\n", + "sides = 6\n", + "# Calculate the angle using the variable\n", + "angle = 360 / sides\n", "\n", - "```\n", + "print('Angle for', sides, 'sides is', angle)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here, `sides` and `angle` are variables. Changing the value of `sides` automatically updates `angle`. This makes your code flexible and easy to modify.\n", + "\n", + "Try changing `sides` to different numbers and see how the angle changes!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Practice: Calculating Angles for Different Shapes**\n", + "Let'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", - "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. " + "# 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 is the power of variables—they let you write code that adapts automatically!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **What is a Function?**\n", + "\n", + "A function is a named block of code that you can run whenever you need it. Functions help you organize your code, avoid repetition, and make your programs easier to read and update.\n", + "\n", + "Here's an example of a simple function:" ] }, { @@ -48,13 +109,31 @@ "metadata": {}, "outputs": [], "source": [ - "# Run me\n", - "# Compute the angle for a number of sides\n", + "# Run Me!\n", "\n", - "sides = 6\n", - "angle = 360/sides\n", + "# Define a function to say hello multiple times\n", + "def say_hello(name, times): # Define the function with two parameters\n", + " for i in range(times): # Loop 'times' times\n", + " print(i, \"Hello\", name) # Print a greeting with the current count\n", + "say_hello(\"John\", 5) # Call the function with arguments" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The line starting with `def` defines the function. The indented lines are the function's body. You can call the function with different arguments to reuse the code.\n", "\n", - "angle" + "Try calling the function with different names and numbers:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "say_hello('Bob', 3)" ] }, { @@ -150,7 +229,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": ".venv", "language": "python", "name": "python3" }, @@ -164,7 +243,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.11" + "version": "3.13.5" }, "syllabus": { "uid": "HOBo0wvj" From 9b411c241756daa0f7699d3776aa14b916f7ef8d Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 9 Oct 2025 03:11:56 -0400 Subject: [PATCH 042/159] Separated '10_Variables_and_Functions.ipynb and 20_Variables_and_Functions.ipynb into 10_Variables.ipynb and 10_Functions.ipynb + extensively reworked both sections to comprehensively cover both topics --- .../10_Variables.ipynb | 307 +++++++++++++++ .../10_Variables_and_Functions.ipynb | 254 ------------- .../20_Functions.ipynb | 357 ++++++++++++++++++ .../20_Variables_and_Functions.ipynb | 160 -------- 4 files changed, 664 insertions(+), 414 deletions(-) create mode 100644 lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb delete mode 100644 lessons/10_Turtles/50_Variables_and_Functions/10_Variables_and_Functions.ipynb create mode 100644 lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb delete mode 100644 lessons/10_Turtles/50_Variables_and_Functions/20_Variables_and_Functions.ipynb 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..30fabf32 --- /dev/null +++ b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb @@ -0,0 +1,307 @@ +{ + "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:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# PROCEED WITH CAUTION:\n", + "# This code is for demonstration and may not run perfectly inside the notebook cell.\n", + "# It's just here to show you an example of how the turtle drawing works.\n", + "# If you want to see Tina in action, copy and paste this code into your own Python environment and run it!\n", + "\n", + "import turtle\n", + "turtle.setup(600, 600, 0, 0)\n", + "tina = turtle.Turtle()\n", + "\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" + ] + }, + { + "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": [ + "# 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", + "\n", + "If 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", + "\n", + "You may not have noticed, but you have been working with variables all along!\n", + "\n", + "Let'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", + "\n", + "For 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", + "\n", + "With 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)\n", + "my_age = 12 # This is an integer (a whole number)\n", + "temperature = 98.6 # This is a float (a fractional number)\n", + "\n", + "# Text (strings)\n", + "my_name = \"Alice\" \n", + "favorite_color = \"blue\"\n", + "\n", + "# Boolean values (True/False)\n", + "is_sunny = True\n", + "is_raining = False\n", + "\n", + "print(\"Name:\", my_name)\n", + "print(\"Age:\", my_age)\n", + "print(\"Temperature:\", temperature)\n", + "print(\"Favorite color:\", favorite_color)\n", + "print(\"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\n", + "pizzas = 5 # Number of pizzas ordered for the party\n", + "slices_per_pizza = 8 # Number of slices in each pizza\n", + "people = 12 # Number of people sharing the pizzas\n", + "\n", + "# Now it's time for a little bit of math\n", + "total_slices = pizzas * slices_per_pizza # Multiply to get total slices\n", + "slices_per_person = total_slices / people # Divide to find slices each person gets\n", + "\n", + "# Finally, we can print our results\n", + "print(\"We have\", pizzas, \"pizzas\") # Show how many pizzas there are\n", + "print(\"Each pizza has\", slices_per_pizza, \"slices\") # Show slices per pizza\n", + "print(\"Total slices:\", total_slices) # Show total number of slices\n", + "print(\"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": [ + "# PROCEED WITH CAUTION:\n", + "# This code is for demonstration and may not run perfectly inside the notebook cell.\n", + "# It's just here to show you an example of how the turtle drawing works.\n", + "# If you want to see Tina in action, copy and paste this code into your own Python environment and run it!\n", + "\n", + "# Let's use variables to make Tina draw different shapes!\n", + "import turtle\n", + "turtle.setup(600, 600, 0, 0)\n", + "tina = turtle.Turtle()\n", + "\n", + "# Variables to control the shape\n", + "sides = 6 # Number of sides for the shape\n", + "size = 100 # Size of each side\n", + "color = \"blue\" # Color of the shape\n", + "\n", + "# Set Tina's color using our variable\n", + "tina.color(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\n", + "\n", + "# Try changing the variables above and run again!\n", + "# What happens if you change sides to 3? Or 8?\n", + "# What if you change size to 50? Or color to \"red\"?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Variable Excercises (Optional)**\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": { + "uid": "HOBo0wvj" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} 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 25e8a223..00000000 --- a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables_and_Functions.ipynb +++ /dev/null @@ -1,254 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Variables and Functions**\n", - "\n", - "Welcome to the world of **variables** and **functions**! In this lesson, you'll learn how variables help you store and reuse information, and how functions let you organize your code into reusable blocks. These concepts make your programs easier to read, update, and expand.\n", - "\n", - "Here is one of the ways to solve the last assignment, making shapes with a loop:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# This code will not run inside the notebook, it's just for you to see how it works.\n", - "# You can copy and paste it into your own Python environment to see it in action!\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" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **What is a Variable?**\n", - "A variable is a name that stores a value. You can think of it as a labeled box that holds information for your program. Variables let you reuse values and make your code easier to change.\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": [ - "# Store the number of sides in a variable\n", - "sides = 6\n", - "# Calculate the angle using the variable\n", - "angle = 360 / sides\n", - "\n", - "print('Angle for', sides, 'sides is', angle)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, `sides` and `angle` are variables. Changing the value of `sides` automatically updates `angle`. This makes your code flexible and easy to modify.\n", - "\n", - "Try changing `sides` to different numbers and see how the angle changes!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Practice: Calculating Angles for Different Shapes**\n", - "Let'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 is the power of variables—they let you write code that adapts automatically!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **What is a Function?**\n", - "\n", - "A function is a named block of code that you can run whenever you need it. Functions help you organize your code, avoid repetition, and make your programs easier to read and update.\n", - "\n", - "Here's an example of a simple function:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Define a function to say hello multiple times\n", - "def say_hello(name, times): # Define the function with two parameters\n", - " for i in range(times): # Loop 'times' times\n", - " print(i, \"Hello\", name) # Print a greeting with the current count\n", - "say_hello(\"John\", 5) # Call the function with arguments" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The line starting with `def` defines the function. The indented lines are the function's body. You can call the function with different arguments to reuse the code.\n", - "\n", - "Try calling the function with different names and numbers:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "say_hello('Bob', 3)" - ] - }, - { - "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 more 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": ".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": "HOBo0wvj" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} 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..3db1b9bc --- /dev/null +++ b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb @@ -0,0 +1,357 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Functions**\n", + "\n", + "In this lesson, you'll learn how to create and use **functions** in Python. **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!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Functions** and Why Use Them?\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" + ] + }, + { + "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\n", + "\n", + "def 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\n", + "\n", + "triangle_angle = calculate_angle(3) # Call the function with 3 sides (triangle)\n", + "square_angle = calculate_angle(4) # Call the function with 4 sides (square) \n", + "hexagon_angle = calculate_angle(6) # Call the function with 6 sides (hexagon)\n", + "\n", + "# Print the results\n", + "\n", + "print(\"Triangle angle:\", triangle_angle)\n", + "print(\"Square angle:\", square_angle)\n", + "print(\"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", + "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", + "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", + "\n", + "def 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", + "\n", + "def multiply_numbers(a, b): # Multiplies two numbers and returns the product\n", + " return a * b # Return the product\n", + "\n", + "def 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", + "\n", + "sum_result = add_numbers(10, 5) # Store the sum of 10 and 5\n", + "product = multiply_numbers(4, 7) # Store the product of 4 and 7\n", + "room_area = calculate_area(12, 10) # Store the area of a 12×10 room\n", + "\n", + "# Print the results returned from the functions\n", + "\n", + "print(\"10 + 5 =\", sum_result)\n", + "print(\"4 × 7 =\", product)\n", + "print(\"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", + "\n", + "# Decision function\n", + "def 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\n", + "def 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\n", + "def 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 = -(-total_slices_needed // 8) # Round up to nearest whole pizza (8 slices per pizza)\n", + " return total_slices_needed, pizzas_needed # Return both values at once!\n", + "\n", + "# Test functions and print results\n", + "weather_report = check_temperature(75) # Test temperature function with 75 degrees\n", + "print(weather_report)\n", + "\n", + "age_valid = is_valid_age(25) # Test age validation with 25 years old\n", + "print(\"Is age 25 valid?\", age_valid)\n", + "\n", + "slices, pizzas = pizza_calculator(10) # Calculate pizza needs for 10 people (uses default 3 slices each)\n", + "print(f\"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", + "\n", + "Now let's put our function knowledge to work with Tina!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# PROCEED WITH CAUTION:\n", + "# This code is for demonstration and may not run perfectly inside the notebook cell.\n", + "# It's just here to show you an example of how the turtle drawing works.\n", + "# If you want to see Tina in action, copy and paste this code into your own Python environment and run it!\n", + "\n", + "import turtle\n", + "turtle.setup(600, 600, 0, 0)\n", + "tina = turtle.Turtle()\n", + "\n", + "# Function to draw any polygon\n", + "def 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", + " \n", + " for i in range(sides):\n", + " tina.forward(size)\n", + " tina.left(angle)\n", + "\n", + "# Function to move Tina without drawing\n", + "def 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!\n", + "draw_polygon(3, 50, \"red\") # Draw a red triangle\n", + "move_tina(100, 0) # Move to a new spot\n", + "draw_polygon(4, 60, \"blue\") # Draw a blue square\n", + "move_tina(-100, 100) # Move again\n", + "draw_polygon(6, 40, \"green\") # Draw a green hexagon\n", + "\n", + "# See how functions make our code cleaner and easier to reuse?\n", + "# Try calling the functions with different parameters!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Function Challenges (Optional)**\n", + "\n", + "Now it's your turn! Write 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": "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": { + "uid": "0CwXIYSb" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/lessons/10_Turtles/50_Variables_and_Functions/20_Variables_and_Functions.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/20_Variables_and_Functions.ipynb deleted file mode 100644 index 51815efc..00000000 --- a/lessons/10_Turtles/50_Variables_and_Functions/20_Variables_and_Functions.ipynb +++ /dev/null @@ -1,160 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Variables and Functions\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", - "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", - "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)" - ] - }, - { - "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": "0CwXIYSb" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} From 7ac313c6d39bdc3586f82c23adbffa7de95578a6 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 9 Oct 2025 03:12:30 -0400 Subject: [PATCH 043/159] Separated '10_Variables_and_Functions.ipynb and 20_Variables_and_Functions.ipynb into 10_Variables.ipynb and 10_Functions.ipynb + extensively reworked both sections to comprehensively cover both topics -- forgot to save --- .../20_Functions.ipynb | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb index 3db1b9bc..cf226d63 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb @@ -4,31 +4,32 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# **Functions**\n", + "# **Functions**\n", "\n", - "In this lesson, you'll learn how to create and use **functions** in Python. **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!" + "In 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": [ - "## **Functions** and Why Use Them?\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", + "## **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" + "- **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", + "### **Defining Functions and Performing Calculations**\n", "\n", - "Let's see how we can turn our angle calculation from the previous lesson into a function:" + "Let's see how we can turn our angle calculation from the previous lesson into a function:" ] }, { @@ -64,10 +65,12 @@ "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", + "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", - "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", + "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:" ] }, @@ -94,9 +97,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Using parameters and arguments\n", + "### **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", + "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:" ] @@ -121,9 +124,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Remember, the `def` line starts the function definition and the last line calls and then runs the function.\n", + "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:" + "Try calling the function with different arguments:" ] }, { @@ -145,9 +148,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Returning Values from functions\n", + "### **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:" + "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:" ] }, { @@ -190,9 +193,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Advanced Function Examples\n", + "## **Advanced Function Examples**\n", "\n", - "Functions can make decisions, validate input, and return multiple results:" + "Functions can make decisions, validate input, and return multiple results:" ] }, { From 32474ef7f5b7cb9dac215da61fc93b9637bd5904 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 13 Oct 2025 01:14:21 -0400 Subject: [PATCH 044/159] Updated 30_Efficient_Turtle.py to have clearer directions --- .../30_Efficient_Turtle.py | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py b/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py index 2060ccb8..40dcd72f 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py +++ b/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py @@ -1,22 +1,26 @@ """ -More Efficient Turtles +# 30_Efficient_Turtle.py -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 +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. + +Objectives: +- 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) # Make the turtle move as fast, but 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 From dc5b17422401f4d0ac5d127ff4f02970ccab556c Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 13 Oct 2025 01:17:24 -0400 Subject: [PATCH 045/159] Updated 30_Efficient_Turtle.py to have clearer directions --- .../30_Efficient_Turtle.py | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py b/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py index 2060ccb8..40dcd72f 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py +++ b/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py @@ -1,22 +1,26 @@ """ -More Efficient Turtles +# 30_Efficient_Turtle.py -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 +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. + +Objectives: +- 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) # Make the turtle move as fast, but 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 From 7bcfc3deeac9879ed77b94c6edf92d8e658b3a30 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 13 Oct 2025 01:21:01 -0400 Subject: [PATCH 046/159] removed unnecessary space --- .../20_Functions.ipynb | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb index cf226d63..5c06a50b 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb @@ -41,19 +41,16 @@ "# Run Me!\n", "\n", "# Define the function\n", - "\n", "def 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\n", - "\n", "triangle_angle = calculate_angle(3) # Call the function with 3 sides (triangle)\n", "square_angle = calculate_angle(4) # Call the function with 4 sides (square) \n", "hexagon_angle = calculate_angle(6) # Call the function with 6 sides (hexagon)\n", "\n", "# Print the results\n", - "\n", "print(\"Triangle angle:\", triangle_angle)\n", "print(\"Square angle:\", square_angle)\n", "print(\"Hexagon angle:\", hexagon_angle) \n", @@ -200,9 +197,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Nice weather!\n", + "Is age 25 valid? True\n", + "For 10 people: need 30 slices and 4 pizzas\n" + ] + } + ], "source": [ "# Run Me!\n", "\n", From b207ab4c69a66d17fa76b45f38ea547e298ed374 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 13 Oct 2025 01:21:45 -0400 Subject: [PATCH 047/159] accidentally pushed with a cell block open -- reverting it --- .../50_Variables_and_Functions/20_Functions.ipynb | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb index 5c06a50b..3e41a5b8 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb @@ -197,19 +197,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Nice weather!\n", - "Is age 25 valid? True\n", - "For 10 people: need 30 slices and 4 pizzas\n" - ] - } - ], + "outputs": [], "source": [ "# Run Me!\n", "\n", From cc88445fc5208168252f888f8833bc477ce8d92e Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 16 Oct 2025 17:19:30 -0400 Subject: [PATCH 048/159] switched out py image with animated gif --- .../10_Turtles/10_Welcome/10_Welcome.ipynb | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index d9d03986..732f0bc6 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -11,29 +11,38 @@ "To 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", + "* [**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", "\n", - "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist 🤖 that you can control with code. To do this, we'll use a pre-installed Python tool called the **turtle module**. \n", + "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist 🤖 that you can control with code. To do this, we'll use a pre-installed Python tool called the **turtle module**.\n", "\n", - "You can tell your turtle 🐢 to move forward ↑, turn left ← or ➝ right, and change the color 🎨 of the lines it draws 🖍️— Soon, you'll be able to create awesome pictures like this one!\n", + "You can tell your turtle 🐢 to move forward ↑, turn left ← or ➝ right, and change the color 🎨 of the lines it draws 🖍️— soon you'll be able to create animated drawings like the one below!\n", "\n", - "
\n", + "
\n", "\n", - "
\n", - " \"A\n", + "
\n", + " \"A\n", "
\n", "\n", - "
\n", - "

Let's Get Set Up!

\n", - "

Before we can start drawing, we need to set up your coding environment. Don't worry if anything seems confusing—that's what your instructors are here for! Feel free to ask for help if you get stuck.

\n", - "

Ready to go? Click the blue Next Lesson button at the bottom-left of your screen to continue! 🚀

\n", - "
\n", - "\n", - "- **Notes & Key Terms:**\n", - " - **Python** — A popular programming language known for its simplicity and readability.\n", - " - **Turtle Module** — A built-in Python library that allows for drawing shapes and patterns using a virtual turtle.\n", - " - **Turtle Programming** — A style of programming that uses the turtle module to create graphics and animations.\n", + "
\n", + "

Let's Get Set Up

\n", + "

Follow the quick checklist below so you'll be ready to run the turtle examples in this lesson.

\n", + "
    \n", + "
  1. Install Python 3.7+ — confirm by running python --version locally.
  2. \n", + "
  3. Open this folder in Visual Studio Code, Codespaces, or the League Code Server.
  4. \n", + "
  5. Run the examples from the notebook (or run the .py script if you prefer).
  6. \n", + "
  7. If the turtle window doesn't appear, try running the notebook locally (desktop Python) or run the example as a .py file — some remote/headless environments block GUI windows.
  8. \n", + "
\n", + "

If you get stuck, ask an instructor or open a new issue with the exact error or environment details (OS, Python version).

\n", + "
\n", + " Notes & Key Terms\n", + "
    \n", + "
  • Python — the language we'll use to write programs.
  • \n", + "
  • turtle — a built-in Python module for simple graphics and drawing.
  • \n", + "
  • GUI window — turtle opens a graphical window; it may not display in headless/remotely hosted sessions.
  • \n", + "
\n", + "
\n", + "
\n", "
" ] } From 8816d84f3f1b7947be05915a045dcd768ea5f863 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 17 Oct 2025 00:02:34 -0400 Subject: [PATCH 049/159] minor update: slowed down gif on welcome + slightly altered open the screen --- .../10_Turtles/10_Welcome/10_Welcome.ipynb | 41 ++++++++----------- .../10_Welcome/20_Open_The_Screen.ipynb | 4 +- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index 732f0bc6..b5365ae8 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -11,38 +11,31 @@ "To 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", + "* [**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", "\n", - "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist 🤖 that you can control with code. To do this, we'll use a pre-installed Python tool called the **turtle module**.\n", + "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist 🤖 that you can control with code. To do this, we'll use a pre-installed Python tool called the **turtle module**. \n", "\n", - "You can tell your turtle 🐢 to move forward ↑, turn left ← or ➝ right, and change the color 🎨 of the lines it draws 🖍️— soon you'll be able to create animated drawings like the one below!\n", + "You can tell your turtle 🐢 to move forward ↑, turn left ← or ➝ right, and change the color 🎨 of the lines it draws 🖍️— Soon, you'll be able to create awesome pictures like this one!\n", "\n", - "
\n", + "
\n", "\n", - "
\n", - " \"A\n", + "
\n", + " \"A\n", "
\n", "\n", - "
\n", - "

Let's Get Set Up

\n", - "

Follow the quick checklist below so you'll be ready to run the turtle examples in this lesson.

\n", - "
    \n", - "
  1. Install Python 3.7+ — confirm by running python --version locally.
  2. \n", - "
  3. Open this folder in Visual Studio Code, Codespaces, or the League Code Server.
  4. \n", - "
  5. Run the examples from the notebook (or run the .py script if you prefer).
  6. \n", - "
  7. If the turtle window doesn't appear, try running the notebook locally (desktop Python) or run the example as a .py file — some remote/headless environments block GUI windows.
  8. \n", - "
\n", - "

If you get stuck, ask an instructor or open a new issue with the exact error or environment details (OS, Python version).

\n", - "
\n", - " Notes & Key Terms\n", + "
\n", + "

Let's Get Set Up!

\n", + "

Before we can start drawing, we need to set up your coding environment. Don't worry if anything seems confusing—that's what your instructors are here for! Feel free to ask for help if you get stuck.

\n", + "

Ready to go? Click the blue Next Lesson button at the bottom-left of your screen to continue! 🚀

\n", + "\n", + "
\n", + " Notes & Key Terms\n", "
    \n", - "
  • Python — the language we'll use to write programs.
  • \n", - "
  • turtle — a built-in Python module for simple graphics and drawing.
  • \n", - "
  • GUI window — turtle opens a graphical window; it may not display in headless/remotely hosted sessions.
  • \n", + "
  • Python — A popular programming language known for its simplicity and readability.
  • \n", + "
  • Turtle Module — A built-in Python library that allows for drawing shapes and patterns using a virtual turtle.
  • \n", + "
  • Turtle Programming — A style of programming that uses the turtle module to create graphics and animations.
  • \n", "
\n", - "
\n", - "
\n", "
" ] } 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 8fe418e7..cac65775 100644 --- a/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb +++ b/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb @@ -6,11 +6,11 @@ "source": [ "# **Let's Open Your Virtual Screen!**\n", "\n", - "Welcome back, Code Explorer! 🌟 Are you ready to see your code come to life? Of course you are!\n", + "Ready to run your code and see what it does?\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 just need to open a virtual screen. Don't worry, this part is easy.\n", "\n", - "Most of the time, this screen will open automatically. If for some reason it doesn't appear, just look for the little monitor icon in the top right corner of your editor and click on it.\n", + "Most of the time, this screen will open automatically. If for some reason it doesn't appear, just 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, just click on the connect button!\n", "
\n", From b45e15ed0b9f4f9e370fc23d3855821528bae685 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 17 Oct 2025 00:43:55 -0400 Subject: [PATCH 050/159] slight modification to Run Programs --- lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index a3f76bdc..cba5db1b 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -112,7 +112,7 @@ "\n", "Running `.py` files might seem new or even a little overwhelming at first, but don’t worry—you’ll get the hang of it quickly! In the next lesson, you’ll try running a Python program called `Meet_Tina.py`. There are a few different ways to do this, and you can use whichever feels easiest for you. Let’s walk through each option together!\n", "\n", - "### Using the Lesson Browser\n", + "### **Using the Lesson Browser**\n", "\n", "The Lesson Browser is a simple way to run `.py` programs in this course.\n", "\n", @@ -127,7 +127,7 @@ "\n", "- When you’re ready for the next lesson, just click Next Lesson\n", "\n", - "### Using the Play Button\n", + "### **Using the Play Button**\n", "\n", "You can also run a `.py` program by clicking the ▶️ button in your editor.\n", "\n", @@ -135,7 +135,7 @@ "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", + "### **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", @@ -152,13 +152,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "
\n", + "
\n", "\n", "**If a program is already running, you must close the program before starting another one.**\n", "\n", "**You have two options to close it:**\n", "- **Click the ❌ in the top-right corner of the window.** \n", - " - **If the program's code ends with `turtle.exitonclick()`, you can simply click anywhere inside the window to close it.**\n", + " - **If the program's code ends with turtle.exitonclick(), you can simply click anywhere inside the window to close it.**\n", "- **If you are using the debugger, you can also stop the program by clicking the red square icon in the debugger toolbar.**\n", "\n", "
" From 7f5c74121346831cff3d070ee12c28e9619222cf Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 17 Oct 2025 01:50:46 -0400 Subject: [PATCH 051/159] Reworked 10_More_Turtle_Programs.ipynb to be clearer and consistent with the rest of the notebooks; updated 10_Variables.ipynb Tina code so that its inline markdown rather than python (running it frequently crashes the kernel) --- .../10_Variables.ipynb | 51 +- .../10_More_Turtle_Programs.ipynb | 601 ++++++++---------- 2 files changed, 284 insertions(+), 368 deletions(-) diff --git a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb index 30fabf32..e3be8a06 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb @@ -13,27 +13,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In case you got stuck on the previous lesson, here is one way you could have solved it:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# PROCEED WITH CAUTION:\n", - "# This code is for demonstration and may not run perfectly inside the notebook cell.\n", - "# It's just here to show you an example of how the turtle drawing works.\n", - "# If you want to see Tina in action, copy and paste this code into your own Python environment and run it!\n", + "In case you got stuck on the previous lesson, here is one way you could have solved it:\n", "\n", - "import turtle\n", - "turtle.setup(600, 600, 0, 0)\n", - "tina = turtle.Turtle()\n", + "```python\n", + "#30_Loop_with_Turtle.py\n", "\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" + "import turtle # Import the turtle graphics module to draw shapes\n", + "turtle.setup(600, 600, 0, 0) # Setup and size the drawing window: width=600, height=600, position=(0,0)\n", + "tina = turtle.Turtle() # Create a Turtle object named tina\n", + "\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", + "```" ] }, { @@ -196,20 +188,8 @@ "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": [ - "# PROCEED WITH CAUTION:\n", - "# This code is for demonstration and may not run perfectly inside the notebook cell.\n", - "# It's just here to show you an example of how the turtle drawing works.\n", - "# If you want to see Tina in action, copy and paste this code into your own Python environment and run it!\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:\n", + "```python\n", "# Let's use variables to make Tina draw different shapes!\n", "import turtle\n", "turtle.setup(600, 600, 0, 0)\n", @@ -231,9 +211,10 @@ " tina.forward(size) # Move forward by the size variable\n", " tina.left(angle) # Turn by the calculated angle\n", "\n", - "# Try changing the variables above and run again!\n", "# What happens if you change sides to 3? Or 8?\n", - "# What if you change size to 50? Or color to \"red\"?" + "# What if you change size to 50? Or color to \"red\"?\n", + "# Try changing the variables above and run it in your own python environment!\n", + "```" ] }, { 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 5ff0e6e7..58f3f7db 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,272 @@ { - "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 / \"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 / \"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": [ + "# **Turtle Tricks and Tips**\n", + "\n", + "Although we've used a lot of Turtle examples so far, the turtle module has many more\n", + "features. The official Python documentation lists everything the turtle can do.\n", + "\n", + "A 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", + "\n", + "Take a look below!\n", + "\n", + "### **Official Python Turtle Documentation**\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.\n", + "\n", + "> **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`).\n", + "\n", + "```python\n", + "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", + "\n", + "import turtle # For turtle graphics\n", + "from pathlib import Path # For handling file paths\n", + "\n", + "# Function to set the turtle's image\n", + "def set_turtle_image(t, image_name):\n", + " \"\"\"Set the turtle's shape to a custom image.\"\"\"\n", + " image_dir = Path(__file__).parent / 'images' # Directory with images\n", + " image_path = str(image_dir / image_name) # Full path to image file\n", + "\n", + " screen = turtle.getscreen() # Get the turtle screen\n", + " screen.addshape(image_path) # Add the image as a shape\n", + " t.shape(image_path) # Set the turtle's shape to the image\n", + "\n", + "# Set up the screen\n", + "screen = turtle.Screen() # Get the screen\n", + "screen.setup(width=600, height=600) # Set the screen size\n", + "\n", + "# Create a turtle and set its shape to the custom GIF\n", + "t = turtle.Turtle() # Create a turtle\n", + "set_turtle_image(t, 'pikachu.gif') # Set turtle to Pikachu image\n", + "\n", + "# Move the turtle around\n", + "t.penup() # Don't draw when moving\n", + "t.speed(3) # Set a moderate speed\n", + "for i in range(4): # Move in a square\n", + " t.goto(200, 200) # Move to top-right corner\n", + " t.goto(-200, -200) # Move to bottom-left corner\n", + "\n", + "turtle.exitonclick() # Wait for a click to close the window\n", + "```" + ] + }, + { + "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", + "```python\n", + "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", + "\n", + "import turtle # For turtle graphics\n", + "from pathlib import Path # For handling file paths\n", + "from PIL import Image # For image processing\n", + "\n", + "# Function to set the background image\n", + "def set_background_image(window, image_name): \n", + " \"\"\"Set the turtle window background to the named image.\"\"\"\n", + " image_dir = Path(__file__).parent / 'images' # Get the images directory\n", + " image_path = str(image_dir / image_name) # Build the full image path\n", + " image = Image.open(image_path) # Open the image\n", + "\n", + " # Resize the window to match the image and set the background\n", + " window.setup(image.width, image.height, startx=0, starty=0) # Resize window\n", + " window.bgpic(image_path) # Set the background picture\n", + "\n", + "# Set up the screen\n", + "turtle.setup(width=600, height=600) # Set initial window size\n", + "tina = turtle.Turtle() # Create a turtle\n", + "screen = turtle.Screen() # Create the screen\n", + "set_background_image(screen, 'emoji.png') # Set the background image\n", + "\n", + "turtle.exitonclick() # Wait for a click to close the window\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", + "```python\n", + "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", + "\n", + "import turtle # For turtle graphics\n", + "\n", + "screen = turtle.Screen() # Set up the screen\n", + "screen.setup(width=600, height=600) # Set the screen size\n", + "screen.bgcolor('white') # Set the background color\n", + "\n", + "t1 = turtle.Turtle() # Create the first turtle\n", + "t1.penup() # Lift the pen\n", + "t1.shape('turtle') # Set the shape to 'turtle'\n", + "\n", + "t2 = turtle.Turtle() # Create the second turtle\n", + "t2.penup() # Lift the pen\n", + "t2.shape('arrow') # Set the shape to 'arrow'\n", + "\n", + "for i in range(-200, 200): # Move both turtles in a loop\n", + " t1.goto(i, i) # Move the first turtle\n", + " t2.goto(i, -i) # Move the second turtle\n", + "```\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.\n", + "\n", + "```python\n", + "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", + "\n", + "import turtle # For turtle graphics\n", + "\n", + "screen = turtle.Screen() # Set up the screen\n", + "screen.setup(width=600, height=600) # Set the screen size\n", + "screen.bgcolor('white') # Set the background color\n", + "\n", + "t = turtle.Turtle() # Create a turtle\n", + "t.penup() # Lift the pen\n", + "t.shape('turtle') # Set the shape to 'turtle'\n", + "\n", + "# Function to handle screen clicks\n", + "def screen_clicked(x, y):\n", + " \"\"\"Called when the screen is clicked. Prints the coordinates and moves the turtle.\"\"\"\n", + " print(f'You pressed: x={x}, y={y}') # Print the coordinates\n", + " t.goto(x, y) # Move the turtle to the clicked position\n", + "\n", + "screen.onclick(screen_clicked) # Tell Python which function to call when the screen is clicked\n", + "turtle.done() # Use `done()` (keeps the window open)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Clicking The Turtle Directly**\n", + "\n", + "You can run a function when the user clicks a turtle. The turtle library calls your\n", + "function with the click coordinates and the turtle object.\n", + "\n", + "```python\n", + "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", + "\n", + "import turtle # For turtle graphics\n", + "\n", + "turtle.setup(width=600, height=600) # Set up the screen\n", + "t = turtle.Turtle() # Create a turtle\n", + "\n", + "t.shape('turtle')\n", + "t.turtlesize(stretch_wid=10, stretch_len=10, outline=4) # Make the turtle large\n", + "\n", + "# Function to handle turtle clicks\n", + "def turtle_clicked(t, x, y):\n", + " \"\"\"Called when the user clicks the turtle. Tilts the turtle in a circle.\"\"\"\n", + " print('turtle clicked!')\n", + " for _ in range(18): # Tilt the turtle in a circle\n", + " t.tilt(20) # 18 * 20 = 360 degrees\n", + "\n", + "# Connect the turtle to the turtle_clicked function (use a lambda to pass `t`)\n", + "t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y)) # Connect the click event\n", + "turtle.done() # Use `done()` (keeps the window open)\n", + "```\n", + "\n", + "In the line `t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y))`, the `lambda`\n", + "keyword creates a small anonymous function. We'll cover lambdas later, but\n", + "for now you can copy this pattern to pass the turtle object into the handler." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **How to Tell Which Turtle Was Clicked**\n", + "\n", + "When you have multiple turtles, you may want to know exactly which turtle was\n", + "clicked. The lambda trick above lets each turtle pass itself into the handler.\n", + "\n", + "```python\n", + "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", + "\n", + "import turtle # For turtle graphics\n", + "\n", + "screen = turtle.Screen() # Set up the screen\n", + "screen.setup(width=600, height=600) # Set the screen size\n", + "screen.bgcolor('white') # Set the background color\n", + "\n", + "t = turtle.Turtle() # Create a turtle\n", + "t.penup() # Lift the pen\n", + "t.shape('turtle') # Set the shape to 'turtle'\n", + "t.turtlesize(stretch_wid=10, stretch_len=10, outline=4) # Make the turtle large\n", + "\n", + "# Function to handle turtle clicks\n", + "def turtle_clicked(t, x, y):\n", + " print('turtle clicked!') # Print a message\n", + " for _ in range(18): # Tilt the turtle in a circle\n", + " t.tilt(20) # 18 * 20 = 360 degrees\n", + "\n", + "# What this line does is create a small anonymous function (a lambda) that\n", + "# captures the current turtle `t` and passes it to the `turtle_clicked` function\n", + "t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y)) # Connect the click event\n", + "turtle.done() # Use `done()` (keeps the window open)\n", + "``` \n", + "\n", + "> **Tip:** If you haven't checked in your code now would be a great time to do so! For a refresher, see the Check in Code and Restart Codespaces page." + ] + } + ], + "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": { + "uid": "IloYptI2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 } From c3f8e1559dda3bf07acfa9a4c410f366bd78048b Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 17 Oct 2025 01:53:34 -0400 Subject: [PATCH 052/159] updated section titles to match my changes to 10_More_Turtle_Programs.ipynb --- .../60_More_Turtle_Programs/20_More_Turtle_programs.py | 4 ++-- .../60_More_Turtle_Programs/30_More_Turtle_Programs.py | 4 ++-- .../60_More_Turtle_Programs/40_More_Turtle_Programs.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) 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 index 8a27e9bf..d58db138 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,6 +1,6 @@ """ -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. 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..0fd19961 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,6 +1,6 @@ """ -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 has a different image ( look in the 'images' directory ) and moves to the corners of the screen in a square pattern. 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..aad360d0 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. From 808bbe7f2b33cd2b623d730d69094e450ff655d8 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sun, 19 Oct 2025 01:29:52 -0400 Subject: [PATCH 053/159] attempting to fix code bugs and make them runnable in cells for turtle module --- .../10_More_Turtle_Programs.ipynb | 74 ++++++++++++++++--- 1 file changed, 63 insertions(+), 11 deletions(-) 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 58f3f7db..ad75e5bf 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 @@ -33,9 +33,28 @@ "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.\n", "\n", - "> **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`).\n", - "\n", - "```python\n", + "> **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": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name '__file__' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 22\u001b[39m\n\u001b[32m 20\u001b[39m \u001b[38;5;66;03m# Create a turtle and set its shape to the custom GIF\u001b[39;00m\n\u001b[32m 21\u001b[39m t = turtle.Turtle() \u001b[38;5;66;03m# Create a turtle\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m22\u001b[39m \u001b[43mset_turtle_image\u001b[49m\u001b[43m(\u001b[49m\u001b[43mt\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mpikachu.gif\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# Set turtle to Pikachu image\u001b[39;00m\n\u001b[32m 24\u001b[39m \u001b[38;5;66;03m# Move the turtle around\u001b[39;00m\n\u001b[32m 25\u001b[39m t.penup() \u001b[38;5;66;03m# Don't draw when moving\u001b[39;00m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 9\u001b[39m, in \u001b[36mset_turtle_image\u001b[39m\u001b[34m(t, image_name)\u001b[39m\n\u001b[32m 7\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mset_turtle_image\u001b[39m(t, image_name):\n\u001b[32m 8\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Set the turtle's shape to a custom image.\"\"\"\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m9\u001b[39m image_dir = Path(\u001b[34;43m__file__\u001b[39;49m).parent / \u001b[33m'\u001b[39m\u001b[33mimages\u001b[39m\u001b[33m'\u001b[39m \u001b[38;5;66;03m# Directory with images\u001b[39;00m\n\u001b[32m 10\u001b[39m image_path = \u001b[38;5;28mstr\u001b[39m(image_dir / image_name) \u001b[38;5;66;03m# Full path to image file\u001b[39;00m\n\u001b[32m 12\u001b[39m screen = turtle.getscreen() \u001b[38;5;66;03m# Get the turtle screen\u001b[39;00m\n", + "\u001b[31mNameError\u001b[39m: name '__file__' is not defined" + ] + } + ], + "source": [ "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "\n", "import turtle # For turtle graphics\n", @@ -44,12 +63,41 @@ "# Function to set the turtle's image\n", "def set_turtle_image(t, image_name):\n", " \"\"\"Set the turtle's shape to a custom image.\"\"\"\n", - " image_dir = Path(__file__).parent / 'images' # Directory with images\n", - " image_path = str(image_dir / image_name) # Full path to image file\n", - "\n", - " screen = turtle.getscreen() # Get the turtle screen\n", - " screen.addshape(image_path) # Add the image as a shape\n", - " t.shape(image_path) # Set the turtle's shape to the image\n", + " candidates = []\n", + "\n", + " # Prefer images next to the script when possible\n", + " try:\n", + " script_dir = Path(__file__).parent\n", + " candidates.append(script_dir / 'images')\n", + " except NameError:\n", + " script_dir = None\n", + "\n", + " # Walk up from cwd to root looking for an 'images' folder (useful in notebooks)\n", + " p = Path.cwd()\n", + " for _ in range(10): # limit how far we search\n", + " candidates.append(p / 'images')\n", + " if p.parent == p:\n", + " break\n", + " p = p.parent\n", + "\n", + " # Choose the first existing images file that matches the requested name\n", + " found = None\n", + " for img_dir in candidates:\n", + " img_path = img_dir / image_name\n", + " if img_path.exists():\n", + " found = img_path\n", + " break\n", + "\n", + " if found is None:\n", + " looked = '\\n'.join(str(p) for p in candidates)\n", + " raise FileNotFoundError(f\"Could not find '{image_name}' in any of these image directories:\\n{looked}\")\n", + "\n", + " resolved = str(found.resolve()) # Convert to string for turtle/tkinter\n", + " print(f\"Using image: {resolved}\") # Helpful debug output\n", + "\n", + " screen = turtle.getscreen() # Get the turtle screen\n", + " screen.addshape(resolved) # Add the image as a shape\n", + " t.shape(resolved) # Set the turtle's shape to the image\n", "\n", "# Set up the screen\n", "screen = turtle.Screen() # Get the screen\n", @@ -66,8 +114,12 @@ " t.goto(200, 200) # Move to top-right corner\n", " t.goto(-200, -200) # Move to bottom-left corner\n", "\n", - "turtle.exitonclick() # Wait for a click to close the window\n", - "```" + "# Wait for a click to close the window\n", + "try:\n", + " turtle.exitonclick()\n", + "except Exception:\n", + " # In some notebook environments exitonclick blocks; try done() as a fallback\n", + " turtle.done()\n" ] }, { From af86805adb539a6da03c47a51fe206a479f6534d Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sun, 19 Oct 2025 01:31:07 -0400 Subject: [PATCH 054/159] removing ipyturtle from code, cant figure out how to get extension to work --- .../10_More_Turtle_Programs.ipynb | 167 +++++++++--------- 1 file changed, 81 insertions(+), 86 deletions(-) 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 ad75e5bf..ba67d65c 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 @@ -14,7 +14,7 @@ "* 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", "\n", - "Take a look below!\n", + "Check out this link, or take a look below!\n", "\n", "### **Official Python Turtle Documentation**\n", "" @@ -40,64 +40,22 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name '__file__' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 22\u001b[39m\n\u001b[32m 20\u001b[39m \u001b[38;5;66;03m# Create a turtle and set its shape to the custom GIF\u001b[39;00m\n\u001b[32m 21\u001b[39m t = turtle.Turtle() \u001b[38;5;66;03m# Create a turtle\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m22\u001b[39m \u001b[43mset_turtle_image\u001b[49m\u001b[43m(\u001b[49m\u001b[43mt\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43mpikachu.gif\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# Set turtle to Pikachu image\u001b[39;00m\n\u001b[32m 24\u001b[39m \u001b[38;5;66;03m# Move the turtle around\u001b[39;00m\n\u001b[32m 25\u001b[39m t.penup() \u001b[38;5;66;03m# Don't draw when moving\u001b[39;00m\n", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 9\u001b[39m, in \u001b[36mset_turtle_image\u001b[39m\u001b[34m(t, image_name)\u001b[39m\n\u001b[32m 7\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mset_turtle_image\u001b[39m(t, image_name):\n\u001b[32m 8\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Set the turtle's shape to a custom image.\"\"\"\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m9\u001b[39m image_dir = Path(\u001b[34;43m__file__\u001b[39;49m).parent / \u001b[33m'\u001b[39m\u001b[33mimages\u001b[39m\u001b[33m'\u001b[39m \u001b[38;5;66;03m# Directory with images\u001b[39;00m\n\u001b[32m 10\u001b[39m image_path = \u001b[38;5;28mstr\u001b[39m(image_dir / image_name) \u001b[38;5;66;03m# Full path to image file\u001b[39;00m\n\u001b[32m 12\u001b[39m screen = turtle.getscreen() \u001b[38;5;66;03m# Get the turtle screen\u001b[39;00m\n", - "\u001b[31mNameError\u001b[39m: name '__file__' is not defined" - ] - } - ], + "outputs": [], "source": [ "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", - "\n", - "import turtle # For turtle graphics\n", + "import os # For operating system interactions\n", + "import turtle # For turtle graphics\n", "from pathlib import Path # For handling file paths\n", "\n", "# Function to set the turtle's image\n", "def set_turtle_image(t, image_name):\n", " \"\"\"Set the turtle's shape to a custom image.\"\"\"\n", - " candidates = []\n", - "\n", - " # Prefer images next to the script when possible\n", - " try:\n", - " script_dir = Path(__file__).parent\n", - " candidates.append(script_dir / 'images')\n", - " except NameError:\n", - " script_dir = None\n", - "\n", - " # Walk up from cwd to root looking for an 'images' folder (useful in notebooks)\n", - " p = Path.cwd()\n", - " for _ in range(10): # limit how far we search\n", - " candidates.append(p / 'images')\n", - " if p.parent == p:\n", - " break\n", - " p = p.parent\n", - "\n", - " # Choose the first existing images file that matches the requested name\n", - " found = None\n", - " for img_dir in candidates:\n", - " img_path = img_dir / image_name\n", - " if img_path.exists():\n", - " found = img_path\n", - " break\n", - "\n", - " if found is None:\n", - " looked = '\\n'.join(str(p) for p in candidates)\n", - " raise FileNotFoundError(f\"Could not find '{image_name}' in any of these image directories:\\n{looked}\")\n", - "\n", - " resolved = str(found.resolve()) # Convert to string for turtle/tkinter\n", - " print(f\"Using image: {resolved}\") # Helpful debug output\n", - "\n", - " screen = turtle.getscreen() # Get the turtle screen\n", - " screen.addshape(resolved) # Add the image as a shape\n", - " t.shape(resolved) # Set the turtle's shape to the image\n", + " image_dir = Path(os.getcwd()).parent / 'images' # Directory with images\n", + " image_path = str(image_dir / image_name) # Full path to image file\n", + "\n", + " screen = turtle.getscreen() # Get the turtle screen\n", + " screen.addshape(image_path) # Add the image as a shape\n", + " t.shape(image_path) # Set the turtle's shape to the image\n", "\n", "# Set up the screen\n", "screen = turtle.Screen() # Get the screen\n", @@ -110,16 +68,14 @@ "# Move the turtle around\n", "t.penup() # Don't draw when moving\n", "t.speed(3) # Set a moderate speed\n", - "for i in range(4): # Move in a square\n", + "for i in range(4): # Move in a figure eight\n", " t.goto(200, 200) # Move to top-right corner\n", " t.goto(-200, -200) # Move to bottom-left corner\n", + " t.goto(200, -200) # Move to bottom-right corner\n", + " t.goto(-200, 200) # Move to top-left corner\n", + "t.goto(200, 200) # Return to top-right corner\n", "\n", - "# Wait for a click to close the window\n", - "try:\n", - " turtle.exitonclick()\n", - "except Exception:\n", - " # In some notebook environments exitonclick blocks; try done() as a fallback\n", - " turtle.done()\n" + "turtle.done() # Wait for a click to close the window" ] }, { @@ -129,19 +85,25 @@ "## **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", - "```python\n", + "*images* folder next to your program." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "\n", - "import turtle # For turtle graphics\n", + "import os # For operating system interactions\n", + "import turtle # For turtle graphics\n", "from pathlib import Path # For handling file paths\n", - "from PIL import Image # For image processing\n", "\n", "# Function to set the background image\n", "def set_background_image(window, image_name): \n", " \"\"\"Set the turtle window background to the named image.\"\"\"\n", - " image_dir = Path(__file__).parent / 'images' # Get the images directory\n", + " image_dir = Path(os.getcwd()).parent / 'images' # Get the images directory\n", " image_path = str(image_dir / image_name) # Build the full image path\n", " image = Image.open(image_path) # Open the image\n", "\n", @@ -155,8 +117,7 @@ "screen = turtle.Screen() # Create the screen\n", "set_background_image(screen, 'emoji.png') # Set the background image\n", "\n", - "turtle.exitonclick() # Wait for a click to close the window\n", - "```" + "turtle.exitonclick() # Wait for a click to close the window" ] }, { @@ -165,8 +126,15 @@ "source": [ "## **More Than One Turtle**\n", "\n", - "You can put multiple turtles on the same screen and control each independently.\n", - "```python\n", + "You can put multiple turtles on the same screen and control each independently." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "\n", "import turtle # For turtle graphics\n", @@ -186,7 +154,8 @@ "for i in range(-200, 200): # Move both turtles in a loop\n", " t1.goto(i, i) # Move the first turtle\n", " t2.goto(i, -i) # Move the second turtle\n", - "```\n" + "\n", + "turtle.exitonclick() # Wait for a click to close the window" ] }, { @@ -195,9 +164,15 @@ "source": [ "## **Respond to Screen Clicks**\n", "\n", - "You can run a function when the user clicks anywhere on the turtle screen.\n", - "\n", - "```python\n", + "You can run a function when the user clicks anywhere on the turtle screen." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "\n", "import turtle # For turtle graphics\n", @@ -217,8 +192,7 @@ " t.goto(x, y) # Move the turtle to the clicked position\n", "\n", "screen.onclick(screen_clicked) # Tell Python which function to call when the screen is clicked\n", - "turtle.done() # Use `done()` (keeps the window open)\n", - "```" + "turtle.done() # Use `done()` (keeps the window open)" ] }, { @@ -228,9 +202,15 @@ "## **Clicking The Turtle Directly**\n", "\n", "You can run a function when the user clicks a turtle. The turtle library calls your\n", - "function with the click coordinates and the turtle object.\n", - "\n", - "```python\n", + "function with the click coordinates and the turtle object." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "\n", "import turtle # For turtle graphics\n", @@ -250,9 +230,14 @@ "\n", "# Connect the turtle to the turtle_clicked function (use a lambda to pass `t`)\n", "t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y)) # Connect the click event\n", - "turtle.done() # Use `done()` (keeps the window open)\n", - "```\n", "\n", + "turtle.done() # Use `done()` (keeps the window open)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "In the line `t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y))`, the `lambda`\n", "keyword creates a small anonymous function. We'll cover lambdas later, but\n", "for now you can copy this pattern to pass the turtle object into the handler." @@ -265,9 +250,15 @@ "## **How to Tell Which Turtle Was Clicked**\n", "\n", "When you have multiple turtles, you may want to know exactly which turtle was\n", - "clicked. The lambda trick above lets each turtle pass itself into the handler.\n", - "\n", - "```python\n", + "clicked. The lambda trick above lets each turtle pass itself into the handler." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "\n", "import turtle # For turtle graphics\n", @@ -290,9 +281,13 @@ "# What this line does is create a small anonymous function (a lambda) that\n", "# captures the current turtle `t` and passes it to the `turtle_clicked` function\n", "t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y)) # Connect the click event\n", - "turtle.done() # Use `done()` (keeps the window open)\n", - "``` \n", - "\n", + "turtle.done() # Use `done()` (keeps the window open)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "> **Tip:** If you haven't checked in your code now would be a great time to do so! For a refresher, see the Check in Code and Restart Codespaces page." ] } From 468fe31f18fd9fbed1a94401da229e7a78d53409 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sun, 19 Oct 2025 01:39:15 -0400 Subject: [PATCH 055/159] removed unnecessary space --- .../60_More_Turtle_Programs/10_More_Turtle_Programs.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 ba67d65c..368499b2 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 @@ -44,7 +44,7 @@ "source": [ "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "import os # For operating system interactions\n", - "import turtle # For turtle graphics\n", + "import turtle # For turtle graphics\n", "from pathlib import Path # For handling file paths\n", "\n", "# Function to set the turtle's image\n", @@ -97,7 +97,7 @@ "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "\n", "import os # For operating system interactions\n", - "import turtle # For turtle graphics\n", + "import turtle # For turtle graphics\n", "from pathlib import Path # For handling file paths\n", "\n", "# Function to set the background image\n", From 1835e1beae1de1bfb923152b8dd824b5c4b3386a Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Tue, 21 Oct 2025 13:39:22 -0400 Subject: [PATCH 056/159] Added a test notebook for trying out how to present ipyturtle3 --- .../10_Turtles/10_Welcome/TESTNOTEBOOK.ipynb | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 lessons/10_Turtles/10_Welcome/TESTNOTEBOOK.ipynb diff --git a/lessons/10_Turtles/10_Welcome/TESTNOTEBOOK.ipynb b/lessons/10_Turtles/10_Welcome/TESTNOTEBOOK.ipynb new file mode 100644 index 00000000..39728cb4 --- /dev/null +++ b/lessons/10_Turtles/10_Welcome/TESTNOTEBOOK.ipynb @@ -0,0 +1,153 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1e42fe22", + "metadata": {}, + "source": [ + "# **Working with ipyturtle3**\n", + "\n", + "The **ipyturtle3** library is a Jupyter Notebook extension that allows you to use the turtle graphics module within Jupyter Notebooks. It provides an interactive turtle graphics environment that works seamlessly in the notebook interface.\n", + "\n", + "To install ipyturtle3, you can use pip." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df15c8d0", + "metadata": {}, + "outputs": [], + "source": [ + "# Run this command in a Jupyter Notebook cell to install ipyturtle3\n", + "pip install ipyturtle3" + ] + }, + { + "cell_type": "markdown", + "id": "6b3fcfcd", + "metadata": {}, + "source": [ + "## **Let's Test it Out!**\n", + "\n", + "Now that we have ipyturtle3 installed, we can test it out by creating a simple turtle graphics program. In the next cell, let's import the necessary libraries and create a turtle object." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e262c5d", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "import ipyturtle3 as turtle\n", + "from ipyturtle3 import hold_canvas, Canvas, TurtleScreen, Turtle\n", + "from IPython.display import display\n", + "\n", + "# Create a Canvas\n", + "myCanvas = Canvas(width=800, height=250)\n", + "display(myCanvas)\n", + "\n", + "# Create a TurtleScreen\n", + "myTS = TurtleScreen(myCanvas)\n", + "myTS.clear()\n", + "\n", + "# Create a Turtle\n", + "eric = Turtle(myTS)\n", + "eric.shape(\"turtle\")\n", + "eric.color(\"green\")\n", + "eric.speed(5)\n", + "\n", + "# Functions to draw each letter\n", + "def draw_L(t):\n", + " t.setheading(-90)\n", + " t.forward(125)\n", + " t.setheading(0)\n", + " t.forward(50)\n", + "\n", + "def draw_E(t):\n", + " t.backward(50)\n", + " t.setheading(-90)\n", + " t.forward(60)\n", + " t.setheading(0)\n", + " t.forward(50)\n", + " t.backward(50)\n", + " t.setheading(-90)\n", + " t.forward(65)\n", + " t.setheading(0)\n", + " t.forward(50)\n", + "\n", + "def draw_A(t):\n", + " t.setheading(-75)\n", + " t.forward(130)\n", + " t.backward(130)\n", + " t.setheading(75)\n", + " t.backward(130)\n", + " t.forward(62.5)\n", + " t.setheading(0)\n", + " t.forward(35)\n", + "\n", + "def draw_G(t):\n", + " t.backward(50)\n", + " t.setheading(-90)\n", + " t.forward(125)\n", + " t.setheading(0)\n", + " t.forward(50)\n", + " t.setheading(90)\n", + " t.forward(62.5)\n", + " t.setheading(180)\n", + " t.forward(25)\n", + " t.setheading(0)\n", + "\n", + "def draw_U(t):\n", + " t.setheading(-90)\n", + " t.forward(125)\n", + " t.setheading(0)\n", + " t.forward(50)\n", + " t.setheading(90)\n", + " t.forward(125)\n", + " t.setheading(0)\n", + "\n", + "# Positions and corresponding drawing functions to reproduce 'LEAGUE'\n", + "letters = [\n", + " (-350, 75, draw_L),\n", + " (-225, 75, draw_E),\n", + " (-160, 75, draw_A),\n", + " (-50, 75, draw_G),\n", + " (-25, 75, draw_U),\n", + " (100, 75, draw_E),\n", + "]\n", + "\n", + "# Loop through positions and draw each letter (pen up to move, pen down to draw)\n", + "for x, y, fn in letters:\n", + " eric.penup()\n", + " eric.goto(x, y)\n", + " eric.pendown()\n", + " fn(eric)" + ] + } + ], + "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 +} From 347e12f34e17c5338e5a40412225c76587dedd74 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Tue, 21 Oct 2025 15:34:39 -0400 Subject: [PATCH 057/159] Updated a few cells to work with ipyturtle3 --- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 674 +++++++++++---------- 1 file changed, 368 insertions(+), 306 deletions(-) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 53d19e0c..f8d03019 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -1,314 +1,376 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Loops**\n", - "\n", - "Welcome to the world of loops! In this lesson, you'll discover how loops make programming easier and more powerful by allowing you to automate repetitive tasks, save time, write cleaner code, and solve problems more efficiently. By pairing loops with *Tina the Turtle*, you'll learn how to simplify your programs, reduce mistakes, and create amazing things with less effort!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **What is a Loop?**\n", - "\n", - "A **loop** is a programming structure that allows you repeatedly execute a set of instructions until a certain condition is met. Instead of writing the same code over and over, you can use a loop to do the work for you. They can help you by making your code shorter and easier to read, making your code less likely to have mistakes.\n", - "\n", - "For example, in our first Tina the Turtle program, we had Tina draw a square. The code looked like this:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# This code will not run inside the notebook, it's just for you to see how it works.\n", - "# You can copy and paste it into your own Python environment to see it in action!\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", - "tina = turtle.Turtle() # Create 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", - "tina.forward(150) # Move tina forward by the forward distance\n", - "tina.left(90) # Turn tina left by the left turn\n", - "tina.forward(150) # Move tina forward by the forward distance\n", - "tina.left(90) # Turn tina left by the left turn\n", - "tina.forward(150) # Move tina forward by the forward distance\n", - "tina.left(90) # Turn tina left by the left turn\n", - "tina.forward(150) # Move tina forward by the forward distance\n", - "tina.left(90) # Turn tina left by the left turn" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "Notice how the last two lines (`tina.forward(150)` and `tina.left(90)`) are repeated four times. This makes the code longer, harder to read, and more likely to have mistakes if you need to change something. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Using Loops with Tina the Turtle**\n", - "\n", - "Instead of repeating the same lines, we can use a loop to make Tina draw a square:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# This code will not run inside the notebook, it's just for you to see how it works.\n", - "# You can copy and paste it into your own Python environment to see it in action!\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", - "tina = turtle.Turtle() # Create 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", - "for i in range(4): # Loop 4 times\n", - " tina.forward(150) # Move forward 150 units\n", - " tina.left(90) # Turn left 90 degrees" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, Tina will move forward and turn left four times, drawing a square. The loop makes the code much cleaner and easier to understand!\n", - "\n", - "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them—understanding loops will help you solve problems more efficiently and write better code!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Why Use Loops?**\n", - "\n", - "Instead of repeating the same instructions, loops let you tell Tina to do something multiple times automatically!\n", - "\n", - "Loops are powerful seeing that they:\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", - "\n", - "Let’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": [ - "Here’s what’s happening:\n", - "\n", - "- `for i in range(4):` tells Python to repeat the indented code 4 times.\n", - "- The variable `i` starts at 0 and goes up to 3 (so, 0, 1, 2, 3). \n", - "- Each time the loop runs, it prints the current value of `i`.\n", - "\n", - "> **Note:** Indentation is important! The code that is indented under the `for` statement is what gets repeated." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **Practicing with Loops**\n", - "\n", - "Loops are a powerful tool for automating repetitive tasks. Try running these examples to see how loops can be used in different ways!\n", - "\n", - "### **Drawing an Orange Square**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Size of the square\n", - "size = 20 \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", - "\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**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Define the size of the checkerboard\n", - "size = 20\n", - "\n", - "# Loop through each row\n", - "for row in range(size):\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", - "Here’s what’s happening:\n", - "\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 useful for working with grids or tables, like drawing patterns or processing 2D data. In this example, the outer loop handles rows and the inner loop handles columns, letting you control each cell in the grid individually." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Drawing an American Flag**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Total number of rows in the flag\n", - "flag_height = 13\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 = 7 \n", - "# Number of columns in the blue canton \n", - "canton_width = 12 \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": [ - "Try modifying these loops or creating your own to practice!" - ] - }, + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Loops**\n", + "\n", + "Welcome to the world of loops! In this lesson, you'll discover how loops make programming easier and more powerful by allowing you to automate repetitive tasks, save time, write cleaner code, and solve problems more efficiently. By pairing loops with *Tina the Turtle*, you'll learn how to simplify your programs, reduce mistakes, and create amazing things with less effort!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **What is a Loop?**\n", + "\n", + "A **loop** is a programming structure that allows you repeatedly execute a set of instructions until a certain condition is met. Instead of writing the same code over and over, you can use a loop to do the work for you. They can help you by making your code shorter and easier to read, making your code less likely to have mistakes.\n", + "\n", + "For example, in our first Tina the Turtle program, we had Tina draw a square. The code looked similar to this `ipyturtle3` version:" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, click the Next Lesson button, open the file `20_Loop_with_Turtle.py`, and try running it!" + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8082ba476acc49e9bd547a5404c7c299", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Canvas(height=175, width=750)" ] + }, + "metadata": {}, + "output_type": "display_data" } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 + ], + "source": [ + "# Run Me!\n", + "\n", + "# Use `ipyturtle3` in place of the standard turtle\n", + "import ipyturtle3 as turtle\n", + "# Import necessary classes from `ipyturtle3` \n", + "from ipyturtle3 import Canvas, TurtleScreen, Turtle \n", + "\n", + "# Create a Canvas for the `ipyturtle3` turtle to draw on\n", + "myCanvas = Canvas(width=750, height=175)\n", + "display(myCanvas)\n", + "\n", + "# Create a TurtleScreen similar to setting up the screen in standard turtle\n", + "myTS = TurtleScreen(myCanvas) # This is like turtle.Screen()\n", + "myTS.clear() # Clear the screen to start fresh\n", + "\n", + "tina = Turtle(myTS) # Create 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", + "tina.hideturtle() # Hide the turtle while moving to start position\n", + "tina.penup() # Lift the pen to move without drawing\n", + "tina.goto(-300, -50) # Move tina to the left of the canvas\n", + "tina.pendown() # Put the pen down to start drawing\n", + "tina.showturtle() # Show the turtle again\n", + "\n", + "tina.forward(100) # Move tina forward by the forward distance\n", + "tina.left(90) # Turn tina left by the left turn\n", + "tina.forward(100) # Move tina forward by the forward distance\n", + "tina.left(90) # Turn tina left by the left turn\n", + "tina.forward(100) # Move tina forward by the forward distance\n", + "tina.left(90) # Turn tina left by the left turn\n", + "tina.forward(100) # Move tina forward by the forward distance\n", + "tina.left(90) # Turn tina left by the left turn" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "Notice how the last two lines (`tina.forward(150)` and `tina.left(90)`) are repeated four times. This makes the code longer, harder to read, and more likely to have mistakes if you need to change something. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Using Loops with Tina the Turtle**\n", + "\n", + "Instead of repeating the same lines, we can use a loop to make Tina draw a square:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "03908e0460a24d56bcc1c3a736072cd4", + "version_major": 2, + "version_minor": 0 }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - }, - "syllabus": { - "uid": "abX8sNwB" + "text/plain": [ + "Canvas(height=175, width=750)" + ] + }, + "metadata": {}, + "output_type": "display_data" } + ], + "source": [ + "# Run Me!\n", + "\n", + "# Use `ipyturtle3` in place of the standard turtle\n", + "import ipyturtle3 as turtle\n", + "# Import necessary classes from `ipyturtle3` \n", + "from ipyturtle3 import Canvas, TurtleScreen, Turtle \n", + "\n", + "# Create a Canvas for the `ipyturtle3` turtle to draw on\n", + "myCanvas = Canvas(width=750, height=175)\n", + "display(myCanvas)\n", + "\n", + "# Create a TurtleScreen similar to setting up the screen in standard turtle\n", + "myTS = TurtleScreen(myCanvas) # This is like turtle.Screen()\n", + "myTS.clear() # Clear the screen to start fresh\n", + "\n", + "tina = Turtle(myTS) # Create 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", + "tina.hideturtle() # Hide the turtle while moving to start position\n", + "tina.penup() # Lift the pen to move without drawing\n", + "tina.goto(-300, -50) # Move tina to the left of the canvas\n", + "tina.pendown() # Put the pen down to start drawing\n", + "tina.showturtle() # Show the turtle again\n", + "\n", + "for i in range(4): # Loop 4 times\n", + " tina.forward(100) # Move forward 150 units\n", + " tina.left(90) # Turn left 90 degrees" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, Tina will move forward and turn left four times, drawing a square. The loop makes the code much cleaner and easier to understand!\n", + "\n", + "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them—understanding loops will help you solve problems more efficiently and write better code!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Why Use Loops?**\n", + "\n", + "Instead of repeating the same instructions, loops let you tell Tina to do something multiple times automatically!\n", + "\n", + "Loops are powerful seeing that they:\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", + "\n", + "Let’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": [ + "Here’s what’s happening:\n", + "\n", + "- `for i in range(4):` tells Python to repeat the indented code 4 times.\n", + "- The variable `i` starts at 0 and goes up to 3 (so, 0, 1, 2, 3). \n", + "- Each time the loop runs, it prints the current value of `i`.\n", + "\n", + "> **Note:** Indentation is important! The code that is indented under the `for` statement is what gets repeated." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Practicing with Loops**\n", + "\n", + "Loops are a powerful tool for automating repetitive tasks. Try running these examples to see how loops can be used in different ways!\n", + "\n", + "### **Drawing an Orange Square**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Size of the square\n", + "size = 20 \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", + "\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**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Define the size of the checkerboard\n", + "size = 20\n", + "\n", + "# Loop through each row\n", + "for row in range(size):\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", + "Here’s what’s happening:\n", + "\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 useful for working with grids or tables, like drawing patterns or processing 2D data. In this example, the outer loop handles rows and the inner loop handles columns, letting you control each cell in the grid individually." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Drawing an American Flag**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Total number of rows in the flag\n", + "flag_height = 13\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 = 7 \n", + "# Number of columns in the blue canton \n", + "canton_width = 12 \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": [ + "Try modifying these loops or creating your own to practice!" + ] + }, + { + "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" }, - "nbformat": 4, - "nbformat_minor": 2 + "syllabus": { + "uid": "abX8sNwB" + } + }, + "nbformat": 4, + "nbformat_minor": 2 } From 7f8c4274e1ee2af03a2fae10b5abbed614bbb8b6 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Tue, 21 Oct 2025 15:35:24 -0400 Subject: [PATCH 058/159] Removed TESTNOTEBOOK.ipynb --- .../10_Turtles/10_Welcome/TESTNOTEBOOK.ipynb | 153 ------------------ 1 file changed, 153 deletions(-) delete mode 100644 lessons/10_Turtles/10_Welcome/TESTNOTEBOOK.ipynb diff --git a/lessons/10_Turtles/10_Welcome/TESTNOTEBOOK.ipynb b/lessons/10_Turtles/10_Welcome/TESTNOTEBOOK.ipynb deleted file mode 100644 index 39728cb4..00000000 --- a/lessons/10_Turtles/10_Welcome/TESTNOTEBOOK.ipynb +++ /dev/null @@ -1,153 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "1e42fe22", - "metadata": {}, - "source": [ - "# **Working with ipyturtle3**\n", - "\n", - "The **ipyturtle3** library is a Jupyter Notebook extension that allows you to use the turtle graphics module within Jupyter Notebooks. It provides an interactive turtle graphics environment that works seamlessly in the notebook interface.\n", - "\n", - "To install ipyturtle3, you can use pip." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "df15c8d0", - "metadata": {}, - "outputs": [], - "source": [ - "# Run this command in a Jupyter Notebook cell to install ipyturtle3\n", - "pip install ipyturtle3" - ] - }, - { - "cell_type": "markdown", - "id": "6b3fcfcd", - "metadata": {}, - "source": [ - "## **Let's Test it Out!**\n", - "\n", - "Now that we have ipyturtle3 installed, we can test it out by creating a simple turtle graphics program. In the next cell, let's import the necessary libraries and create a turtle object." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4e262c5d", - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "import ipyturtle3 as turtle\n", - "from ipyturtle3 import hold_canvas, Canvas, TurtleScreen, Turtle\n", - "from IPython.display import display\n", - "\n", - "# Create a Canvas\n", - "myCanvas = Canvas(width=800, height=250)\n", - "display(myCanvas)\n", - "\n", - "# Create a TurtleScreen\n", - "myTS = TurtleScreen(myCanvas)\n", - "myTS.clear()\n", - "\n", - "# Create a Turtle\n", - "eric = Turtle(myTS)\n", - "eric.shape(\"turtle\")\n", - "eric.color(\"green\")\n", - "eric.speed(5)\n", - "\n", - "# Functions to draw each letter\n", - "def draw_L(t):\n", - " t.setheading(-90)\n", - " t.forward(125)\n", - " t.setheading(0)\n", - " t.forward(50)\n", - "\n", - "def draw_E(t):\n", - " t.backward(50)\n", - " t.setheading(-90)\n", - " t.forward(60)\n", - " t.setheading(0)\n", - " t.forward(50)\n", - " t.backward(50)\n", - " t.setheading(-90)\n", - " t.forward(65)\n", - " t.setheading(0)\n", - " t.forward(50)\n", - "\n", - "def draw_A(t):\n", - " t.setheading(-75)\n", - " t.forward(130)\n", - " t.backward(130)\n", - " t.setheading(75)\n", - " t.backward(130)\n", - " t.forward(62.5)\n", - " t.setheading(0)\n", - " t.forward(35)\n", - "\n", - "def draw_G(t):\n", - " t.backward(50)\n", - " t.setheading(-90)\n", - " t.forward(125)\n", - " t.setheading(0)\n", - " t.forward(50)\n", - " t.setheading(90)\n", - " t.forward(62.5)\n", - " t.setheading(180)\n", - " t.forward(25)\n", - " t.setheading(0)\n", - "\n", - "def draw_U(t):\n", - " t.setheading(-90)\n", - " t.forward(125)\n", - " t.setheading(0)\n", - " t.forward(50)\n", - " t.setheading(90)\n", - " t.forward(125)\n", - " t.setheading(0)\n", - "\n", - "# Positions and corresponding drawing functions to reproduce 'LEAGUE'\n", - "letters = [\n", - " (-350, 75, draw_L),\n", - " (-225, 75, draw_E),\n", - " (-160, 75, draw_A),\n", - " (-50, 75, draw_G),\n", - " (-25, 75, draw_U),\n", - " (100, 75, draw_E),\n", - "]\n", - "\n", - "# Loop through positions and draw each letter (pen up to move, pen down to draw)\n", - "for x, y, fn in letters:\n", - " eric.penup()\n", - " eric.goto(x, y)\n", - " eric.pendown()\n", - " fn(eric)" - ] - } - ], - "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 -} From 5bfd947c8bc0c677749d98b38414f903d0e87605 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Tue, 21 Oct 2025 15:39:26 -0400 Subject: [PATCH 059/159] re-add .gitignore --- .gitignore | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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 From e3bbb0ff8d2f20642204fa69b82ce63d001c730d Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Tue, 21 Oct 2025 15:57:56 -0400 Subject: [PATCH 060/159] Added document hider to official turtle docs --- .../20_What_Can_Tina_Do.ipynb | 17 ++++----- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 38 ++----------------- .../10_More_Turtle_Programs.ipynb | 8 +++- 3 files changed, 18 insertions(+), 45 deletions(-) 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 42396e82..dd2bc652 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 @@ -73,15 +73,14 @@ "\n", "- **Read the Introduction** at the beginning of the documentation, which often explains how to set up Turtle graphics and provides an overview of its capabilities.\n", "\n", - "> **Tip:** Whenever you’re unsure about a command or want to try something new, searching for documentation or examples is one of the best ways to learn and grow as a programmer. The more you explore, the more creative and confident you’ll become!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Official Python Turtle Documentation**\n", - "" + "> **Tip:** Whenever you’re unsure about a command or want to try something new, searching for documentation or examples is one of the best ways to learn and grow as a programmer. The more you explore, the more creative and confident you’ll become!\n", + "\n", + "
\n", + "
\n", + " Click Here To View The Official Python Turtle Documentation\n", + " \n", + "
" ] }, { diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index f8d03019..250c4bb7 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -22,24 +22,9 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "8082ba476acc49e9bd547a5404c7c299", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Canvas(height=175, width=750)" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Run Me!\n", "\n", @@ -95,24 +80,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "03908e0460a24d56bcc1c3a736072cd4", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Canvas(height=175, width=750)" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Run Me!\n", "\n", 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 368499b2..2a09c879 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 @@ -16,8 +16,12 @@ "\n", "Check out this link, or take a look below!\n", "\n", - "### **Official Python Turtle Documentation**\n", - "" + "
\n", + "
\n", + " Click Here To View The Official Python Turtle Documentation\n", + " \n", + "
" ] }, { From 837314baf11026b1bc0807ad09f4e195bcb7f418 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 23 Oct 2025 23:54:21 -0400 Subject: [PATCH 061/159] Improved the Loops.ipynb to have ipyturtle3 --- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 261 ++++++++++++++------- 1 file changed, 175 insertions(+), 86 deletions(-) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 250c4bb7..62abb087 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -6,18 +6,28 @@ "source": [ "# **Loops**\n", "\n", - "Welcome to the world of loops! In this lesson, you'll discover how loops make programming easier and more powerful by allowing you to automate repetitive tasks, save time, write cleaner code, and solve problems more efficiently. By pairing loops with *Tina the Turtle*, you'll learn how to simplify your programs, reduce mistakes, and create amazing things with less effort!" + "Welcome to the world of **loops**! In this lesson, you'll discover how loops make programming easier and more powerful by allowing you to automate repetitive tasks, save time, write cleaner code, and solve problems more efficiently. By pairing loops with *Tina the Turtle*, you'll be able to simplify your programs, reduce mistakes, and create amazing things with less effort!\n", + "\n", + "> **Tip:** Take your time to practice using loops. They are a core concept in every programming language, and mastering them will greatly enhance your coding skills." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## **What is a Loop?**\n", + "### **Why Use Loops?**\n", + "\n", + "Instead 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", "\n", - "A **loop** is a programming structure that allows you repeatedly execute a set of instructions until a certain condition is met. Instead of writing the same code over and over, you can use a loop to do the work for you. They can help you by making your code shorter and easier to read, making your code less likely to have mistakes.\n", + "Loops are super useful because they help us:\n", "\n", - "For example, in our first Tina the Turtle program, we had Tina draw a square. The code looked similar to this `ipyturtle3` version:" + "- **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", + "\n", + "Let’s look at this simple example of a `for` loop:" ] }, { @@ -28,54 +38,26 @@ "source": [ "# Run Me!\n", "\n", - "# Use `ipyturtle3` in place of the standard turtle\n", - "import ipyturtle3 as turtle\n", - "# Import necessary classes from `ipyturtle3` \n", - "from ipyturtle3 import Canvas, TurtleScreen, Turtle \n", - "\n", - "# Create a Canvas for the `ipyturtle3` turtle to draw on\n", - "myCanvas = Canvas(width=750, height=175)\n", - "display(myCanvas)\n", - "\n", - "# Create a TurtleScreen similar to setting up the screen in standard turtle\n", - "myTS = TurtleScreen(myCanvas) # This is like turtle.Screen()\n", - "myTS.clear() # Clear the screen to start fresh\n", - "\n", - "tina = Turtle(myTS) # Create 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", - "tina.hideturtle() # Hide the turtle while moving to start position\n", - "tina.penup() # Lift the pen to move without drawing\n", - "tina.goto(-300, -50) # Move tina to the left of the canvas\n", - "tina.pendown() # Put the pen down to start drawing\n", - "tina.showturtle() # Show the turtle again\n", - "\n", - "tina.forward(100) # Move tina forward by the forward distance\n", - "tina.left(90) # Turn tina left by the left turn\n", - "tina.forward(100) # Move tina forward by the forward distance\n", - "tina.left(90) # Turn tina left by the left turn\n", - "tina.forward(100) # Move tina forward by the forward distance\n", - "tina.left(90) # Turn tina left by the left turn\n", - "tina.forward(100) # Move tina forward by the forward distance\n", - "tina.left(90) # Turn tina left by the left turn" + "# 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": [ - "\n", - "Notice how the last two lines (`tina.forward(150)` and `tina.left(90)`) are repeated four times. This makes the code longer, harder to read, and more likely to have mistakes if you need to change something. " + "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": [ - "### **Using Loops with Tina the Turtle**\n", + "## **Manual vs. Loop-Based Drawing with Tina**\n", "\n", - "Instead of repeating the same lines, we can use a loop to make Tina draw a square:" + "### **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:" ] }, { @@ -101,71 +83,158 @@ "\n", "tina = Turtle(myTS) # Create 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", - "tina.hideturtle() # Hide the turtle while moving to start position\n", - "tina.penup() # Lift the pen to move without drawing\n", - "tina.goto(-300, -50) # Move tina to the left of the canvas\n", - "tina.pendown() # Put the pen down to start drawing\n", - "tina.showturtle() # Show the turtle again\n", - "\n", - "for i in range(4): # Loop 4 times\n", - " tina.forward(100) # Move forward 150 units\n", - " tina.left(90) # Turn left 90 degrees" + "tina.speed(5) # Make the turtle move as fast, but not too fast.\n", + "\n", + "# Define a function to move the turtle to a specific start position\n", + "def movetostart(x, y):\n", + " tina.hideturtle() # Hide the turtle while moving to start position\n", + " tina.penup() # Lift the pen to move without drawing\n", + " tina.goto(x, y) # Move tina to the specified position\n", + " tina.pendown() # Put the pen down to start drawing\n", + " tina.showturtle() # Show the turtle again\n", + "\n", + "# Uses the movetostart function\n", + "movetostart(-300, -50) # Move tina to the left of the canvas\n", + "\n", + "# Drawing 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", + "movetostart(-150, -50) # Move tina to the center-left of the canvas\n", + "\n", + "# Drawing 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\n", + "\n", + "# Now imagine how confusing it would be if we had `ipyturtle3` move the turtle 36 times to draw a star!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now, Tina will move forward and turn left four times, drawing a square. The loop makes the code much cleaner and easier to understand!\n", + "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.\n", "\n", - "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them—understanding loops will help you solve problems more efficiently and write better code!" + "Let’s see how we can use loops to make this code cleaner and easier to manage. Maybe we can even add more shapes without making the code super long!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### **Why Use Loops?**\n", - "\n", - "Instead of repeating the same instructions, loops let you tell Tina to do something multiple times automatically!\n", - "\n", - "Loops are powerful seeing that they:\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", + "### **Using Loops To Draw Shapes**\n", "\n", - "### **How Do Loops Work?**\n", - "\n", - "Let’s look at this simple example of a `for` loop:" + "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, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "04aa1c62452344efbd00d0e084582308", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Canvas(height=175, width=750)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Run Me!\n", + " \n", + "# Use `ipyturtle3` in place of the standard turtle\n", + "import ipyturtle3 as turtle\n", + "# Import necessary classes from `ipyturtle3` \n", + "from ipyturtle3 import Canvas, TurtleScreen, Turtle \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" + "# Create a Canvas for the `ipyturtle3` turtle to draw on\n", + "myCanvas = Canvas(width=750, height=175)\n", + "display(myCanvas)\n", + "\n", + "# Create a TurtleScreen similar to setting up the screen in standard turtle\n", + "myTS = TurtleScreen(myCanvas) # This is like turtle.Screen()\n", + "myTS.clear() # Clear the screen to start fresh\n", + "\n", + "# Set up Tina the Turtle\n", + "tina = Turtle(myTS) # Create 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 fast, but not too fast\n", + "\n", + "# Define a function to move the turtle to a specific start position\n", + "def movetostart(x, y):\n", + " tina.hideturtle() # Hide the turtle while moving to start position\n", + " tina.penup() # Lift the pen to move without drawing\n", + " tina.goto(x, y) # Move tina to the specified position\n", + " tina.pendown() # Put the pen down to start drawing\n", + " tina.showturtle() # Show the turtle again\n", + "\n", + "movetostart(-300, -50) # Uses coordinates to move tina to the left of the canvas\n", + "\n", + "# Draw a square using a loop\n", + "for i in range(4): # Loop 4 times\n", + " tina.forward(100) # Move forward 100 units\n", + " tina.left(90) # Turn left 90 degrees\n", + "\n", + "movetostart(-150, -50) # Uses coordinates to move tina to the center-left of the canvas\n", + "\n", + "# Draw a triangle using a loop\n", + "for 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", + "movetostart(0, 0) # Uses coordinates to move tina to the center-right of the canvas\n", + "\n", + "tina.speed(9) # Set speed to 9 for a faster star drawing\n", + "\n", + "# Draw a star pattern using a loop\n", + "for 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": [ - "Here’s what’s happening:\n", + "Now, Tina will move forward and turn left four times, drawing a square. The loop makes the code much cleaner and easier to understand!\n", + "\n", + "
\n", + " Click here for a detailed explanation\n", "\n", - "- `for i in range(4):` tells Python to repeat the indented code 4 times.\n", - "- The variable `i` starts at 0 and goes up to 3 (so, 0, 1, 2, 3). \n", - "- Each time the loop runs, it prints the current value of `i`.\n", + "Here'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", "\n", - "> **Note:** Indentation is important! The code that is indented under the `for` statement is what gets repeated." + "For 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\n", + "for 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", + "```\n", + "
\n", + "
\n", + "\n", + "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them—understanding loops will help you solve problems more efficiently and write better code!" ] }, { @@ -174,7 +243,7 @@ "source": [ "## **Practicing with Loops**\n", "\n", - "Loops are a powerful tool for automating repetitive tasks. Try running these examples to see how loops can be used in different ways!\n", + "Try running these examples to see how loops can be used in different ways!\n", "\n", "### **Drawing an Orange Square**" ] @@ -188,7 +257,7 @@ "# Run Me!\n", "\n", "# Size of the square\n", - "size = 20 \n", + "size = 10 \n", "# Loop will run 'size' times \n", "for i in range(size):\n", " # Print a row of 'size' orange squares\n", @@ -199,11 +268,15 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Here’s what’s happening:\n", + "
\n", + " Click here for a detailed explanation\n", "\n", + "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", + "
\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!" ] @@ -224,10 +297,11 @@ "# Run Me!\n", "\n", "# Define the size of the checkerboard\n", - "size = 20\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", @@ -243,15 +317,18 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Here’s what’s happening:\n", - "Here’s what’s happening:\n", + "
\n", + " Click here for a detailed explanation\n", "\n", + "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", + "
\n", "\n", - "> **Tip:** Nested loops are useful for working with grids or tables, like drawing patterns or processing 2D data. In this example, the outer loop handles rows and the inner loop handles columns, letting you control each cell in the grid individually." + "> **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." ] }, { @@ -270,14 +347,14 @@ "# Run Me!\n", "\n", "# Total number of rows in the flag\n", - "flag_height = 13\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 = 7 \n", + "canton_height = 9 \n", "# Number of columns in the blue canton \n", - "canton_width = 12 \n", + "canton_width = 11 \n", "\n", "# Loop through each row of the flag\n", "for row in range(flag_height): \n", @@ -299,7 +376,7 @@ " print('🟥', end='') \n", " else:\n", " # Print a white stripe\n", - " print('⬜️', end='') \n", + " print('⬜️', end='')\n", " # Move to the next line after each row \n", " print() " ] @@ -308,7 +385,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Try modifying these loops or creating your own to practice!" + "
\n", + " Click here for a detailed explanation\n", + "\n", + "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", + "- The `elif` statement checks if the current row is even or odd to alternate between red (`🟥`) and white (`⬜️`) stripes.\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", + "
\n", + "\n", + "> **Tip:** You can use if-elif-else statements inside loops to create complex patterns and designs, like the American flag. " ] }, { From 475e85014f3ea014702abd62afe2e1a7173f21ab Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 23 Oct 2025 23:54:51 -0400 Subject: [PATCH 062/159] Improved the Loops.ipynb to have ipyturtle3 --- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 62abb087..228a2a60 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -139,24 +139,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "04aa1c62452344efbd00d0e084582308", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Canvas(height=175, width=750)" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Run Me!\n", " \n", @@ -164,6 +149,7 @@ "import ipyturtle3 as turtle\n", "# Import necessary classes from `ipyturtle3` \n", "from ipyturtle3 import Canvas, TurtleScreen, Turtle \n", + "from IPython.display import display\n", "\n", "# Create a Canvas for the `ipyturtle3` turtle to draw on\n", "myCanvas = Canvas(width=750, height=175)\n", From 775033aeb558f2be0b054a2e0db26f9d03497e3e Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 1 Nov 2025 23:28:33 -0400 Subject: [PATCH 063/159] Removed complicated functions from the loops notebook --- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 95 ++++++++++++---------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 228a2a60..f6f40dd8 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -70,6 +70,7 @@ "\n", "# Use `ipyturtle3` in place of the standard turtle\n", "import ipyturtle3 as turtle\n", + "\n", "# Import necessary classes from `ipyturtle3` \n", "from ipyturtle3 import Canvas, TurtleScreen, Turtle \n", "\n", @@ -85,18 +86,11 @@ "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", - "# Define a function to move the turtle to a specific start position\n", - "def movetostart(x, y):\n", - " tina.hideturtle() # Hide the turtle while moving to start position\n", - " tina.penup() # Lift the pen to move without drawing\n", - " tina.goto(x, y) # Move tina to the specified position\n", - " tina.pendown() # Put the pen down to start drawing\n", - " tina.showturtle() # Show the turtle again\n", - "\n", - "# Uses the movetostart function\n", - "movetostart(-300, -50) # Move tina to the left of the canvas\n", + "tina.penup() # Lift the pen to move without drawing\n", + "tina.goto(-300, -50) # Move tina to the left of the canvas\n", + "tina.pendown() # Put the pen down to start drawing\n", "\n", - "# Drawing a square without using a loop\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", @@ -106,9 +100,11 @@ "tina.forward(100) # Move tina forward 100 units\n", "tina.left(90) # Turn tina left 90 degrees\n", "\n", - "movetostart(-150, -50) # Move tina to the center-left of the canvas\n", + "tina.penup() # Lift the pen to move without drawing\n", + "tina.goto(-150, -50) # Move tina to the center of the canvas\n", + "tina.pendown() # Put the pen down to start drawing\n", "\n", - "# Drawing a triangle without using a loop\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", @@ -141,59 +137,74 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a1d58178df774f62ba10689c3a563909", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Canvas(height=175, width=750)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Run Me!\n", " \n", "# Use `ipyturtle3` in place of the standard turtle\n", "import ipyturtle3 as turtle\n", + "\n", "# Import necessary classes from `ipyturtle3` \n", "from ipyturtle3 import Canvas, TurtleScreen, Turtle \n", - "from IPython.display import display\n", "\n", "# Create a Canvas for the `ipyturtle3` turtle to draw on\n", "myCanvas = Canvas(width=750, height=175)\n", "display(myCanvas)\n", "\n", "# Create a TurtleScreen similar to setting up the screen in standard turtle\n", - "myTS = TurtleScreen(myCanvas) # This is like turtle.Screen()\n", - "myTS.clear() # Clear the screen to start fresh\n", + "myTS = TurtleScreen(myCanvas) # This is like turtle.Screen()\n", + "myTS.clear() # Clear the screen to start fresh\n", "\n", "# Set up Tina the Turtle\n", - "tina = Turtle(myTS) # Create 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 fast, but not too fast\n", - "\n", - "# Define a function to move the turtle to a specific start position\n", - "def movetostart(x, y):\n", - " tina.hideturtle() # Hide the turtle while moving to start position\n", - " tina.penup() # Lift the pen to move without drawing\n", - " tina.goto(x, y) # Move tina to the specified position\n", - " tina.pendown() # Put the pen down to start drawing\n", - " tina.showturtle() # Show the turtle again\n", + "tina = Turtle(myTS) # Create 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 fast, but not too fast\n", "\n", - "movetostart(-300, -50) # Uses coordinates to move tina to the left of the canvas\n", + "# Move tina to the left of the canvas\n", + "tina.penup() \n", + "tina.goto(-300, -50) \n", + "tina.pendown() \n", "\n", "# Draw a square using a loop\n", - "for i in range(4): # Loop 4 times\n", - " tina.forward(100) # Move forward 100 units\n", - " tina.left(90) # Turn left 90 degrees\n", + "for i in range(4): # Loop 4 times\n", + " tina.forward(100) # Move forward 100 units\n", + " tina.left(90) # Turn left 90 degrees\n", "\n", - "movetostart(-150, -50) # Uses coordinates to move tina to the center-left of the canvas\n", + "tina.penup() # Lift the pen to move without drawing\n", + "tina.goto(-150, -50) # Move tina to the center-left of the canvas\n", + "tina.pendown() # Put the pen down to start drawing\n", "\n", "# Draw a triangle using a loop\n", - "for i in range(3): # Loop 3 times\n", - " tina.forward(120) # Move forward 120 units\n", - " tina.left(120) # Turn left 120 degrees\n", + "for 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", - "movetostart(0, 0) # Uses coordinates to move tina to the center-right of the canvas\n", + "# Move tina to the center of the canvas\n", + "tina.penup() # Lift the pen to move without drawing\n", + "tina.goto(0, 0) # Move tina to the center of the canvas\n", + "tina.pendown() # Put the pen down to start drawing\n", "\n", - "tina.speed(9) # Set speed to 9 for a faster star drawing\n", + "tina.speed(9) # Set speed to 9 for a faster star drawing\n", "\n", "# Draw a star pattern using a loop\n", - "for i in range(36): # Loop 36 times\n", - " tina.forward(110) # Move forward 110 units\n", - " tina.left(170) # Turn left 170 degrees" + "for i in range(36): # Loop 36 times\n", + " tina.forward(110) # Move forward 110 units\n", + " tina.left(170) # Turn left 170 degrees" ] }, { @@ -396,7 +407,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": ".venv", "language": "python", "name": "python3" }, From b652e5f3a47f2ab113160ff48ae5b712131a1321 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 3 Nov 2025 11:55:35 -0500 Subject: [PATCH 064/159] Fixed some code cells --- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 18 +----------------- .../20_Functions.ipynb | 2 +- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index f6f40dd8..da0e04d2 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -70,7 +70,6 @@ "\n", "# Use `ipyturtle3` in place of the standard turtle\n", "import ipyturtle3 as turtle\n", - "\n", "# Import necessary classes from `ipyturtle3` \n", "from ipyturtle3 import Canvas, TurtleScreen, Turtle \n", "\n", @@ -137,22 +136,7 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "a1d58178df774f62ba10689c3a563909", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Canvas(height=175, width=750)" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Run Me!\n", " \n", diff --git a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb index 3e41a5b8..c92793dd 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb @@ -332,7 +332,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": ".venv", "language": "python", "name": "python3" }, From a2d6fd86459e7ca74fc51ab2a3dd4ad930b3e5d7 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 7 Nov 2025 12:30:56 -0500 Subject: [PATCH 065/159] Added a script that handles ipyturtle3 import statements --- .lib/auto_turtle.py | 49 ++++++++++++++++++++++ lessons/10_Turtles/40_Loops/10_Loops.ipynb | 30 ++----------- 2 files changed, 53 insertions(+), 26 deletions(-) create mode 100644 .lib/auto_turtle.py diff --git a/.lib/auto_turtle.py b/.lib/auto_turtle.py new file mode 100644 index 00000000..6ca84927 --- /dev/null +++ b/.lib/auto_turtle.py @@ -0,0 +1,49 @@ +#!/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. Display the Canvas in the notebook. +4. Wait a moment to ensure the canvas is rendered. +5. Create a TurtleScreen for the canvas named 'myTS'. +""" + +# --- Configuration --- +canvas_width = 750 # Width of the turtle canvas +canvas_height = 250 # Height of the turtle canvas +# --------------------- + +import time + +try: + # 1. Import the main library and necessary classes + import ipyturtle3 as turtle + from ipyturtle3 import Canvas, TurtleScreen, Turtle + from IPython.display import display + + # 2. Create a Canvas for the turtle to draw on + myCanvas = Canvas(width=canvas_width, height=canvas_height) + + # 3. Display the Canvas in the Jupyter notebook + display(myCanvas) + + # 4. Wait a moment to ensure the canvas is fully rendered + time.sleep(1) + + # 5. 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/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index da0e04d2..f0d61afb 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -68,20 +68,10 @@ "source": [ "# Run Me!\n", "\n", - "# Use `ipyturtle3` in place of the standard turtle\n", - "import ipyturtle3 as turtle\n", - "# Import necessary classes from `ipyturtle3` \n", - "from ipyturtle3 import Canvas, TurtleScreen, Turtle \n", + "%run .lib/auto_turtle.py # This just handles the general imports for you\n", "\n", - "# Create a Canvas for the `ipyturtle3` turtle to draw on\n", - "myCanvas = Canvas(width=750, height=175)\n", - "display(myCanvas)\n", - "\n", - "# Create a TurtleScreen similar to setting up the screen in standard turtle\n", - "myTS = TurtleScreen(myCanvas) # This is like turtle.Screen()\n", - "myTS.clear() # Clear the screen to start fresh\n", - "\n", - "tina = Turtle(myTS) # Create a turtle named tina\n", + "# Set up Tina the Turtle\n", + "tina = Turtle(myTS) # pylint: disable=undefined-variable\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", @@ -139,20 +129,8 @@ "outputs": [], "source": [ "# Run Me!\n", - " \n", - "# Use `ipyturtle3` in place of the standard turtle\n", - "import ipyturtle3 as turtle\n", - "\n", - "# Import necessary classes from `ipyturtle3` \n", - "from ipyturtle3 import Canvas, TurtleScreen, Turtle \n", - "\n", - "# Create a Canvas for the `ipyturtle3` turtle to draw on\n", - "myCanvas = Canvas(width=750, height=175)\n", - "display(myCanvas)\n", "\n", - "# Create a TurtleScreen similar to setting up the screen in standard turtle\n", - "myTS = TurtleScreen(myCanvas) # This is like turtle.Screen()\n", - "myTS.clear() # Clear the screen to start fresh\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) # Create a turtle named tina\n", From fcc65de03ba2965641cda6ff1299a301cc8e449a Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 7 Nov 2025 13:57:58 -0500 Subject: [PATCH 066/159] Added a class called 'turtle' that overrides the 'Turtle' class and allows custom behavior to be added in auto_turtle.py; updated Loops.ipynb cells to match some coordinate behavior that was added --- .lib/auto_turtle.py | 91 +++++++++++++++++++--- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 26 +++++-- 2 files changed, 98 insertions(+), 19 deletions(-) diff --git a/.lib/auto_turtle.py b/.lib/auto_turtle.py index 6ca84927..223b87f0 100644 --- a/.lib/auto_turtle.py +++ b/.lib/auto_turtle.py @@ -8,17 +8,21 @@ 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. Display the Canvas in the notebook. -4. Wait a moment to ensure the canvas is rendered. -5. Create a TurtleScreen for the canvas named 'myTS'. +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'. """ # --- Configuration --- -canvas_width = 750 # Width of the turtle canvas -canvas_height = 250 # Height of the turtle canvas +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 # --------------------- -import time +import time # Might not be necessary, but used for sleep try: # 1. Import the main library and necessary classes @@ -26,19 +30,84 @@ from ipyturtle3 import Canvas, TurtleScreen, Turtle from IPython.display import display - # 2. Create a Canvas for the turtle to draw on + # 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). + """ + def __init__(self, screen=None, *args, **kwargs): + # Store the new "origin" + 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 --- + # We use super().goto() to move to the *absolute* start + self.penup() + super().goto(self._origin_x, self._origin_y) + self.pendown() + + ### --- OVERRIDDEN METHODS (Coordinate Remapping) --- ### + + def goto(self, x, y): + """Moves the turtle to a (student) coordinate.""" + # Translate student's (x, y) to absolute (real_x, real_y) + real_x = x + self._origin_x + real_y = y + self._origin_y + # Call the original goto + super().goto(real_x, real_y) + + # setposition is an alias for goto + def setposition(self, x, y=None): + self.goto(x, y) + + def setx(self, x): + """Sets the turtle's (student) x coordinate.""" + real_x = x + self._origin_x + super().setx(real_x) + + def sety(self, y): + """Sets the turtle's (student) y coordinate.""" + real_y = y + self._origin_y + super().sety(real_y) + + def position(self): + """Returns the turtle's (student) position.""" + # Get the *absolute* position + abs_x, abs_y = super().position() + # Translate to student's *relative* position + rel_x = abs_x - self._origin_x + rel_y = abs_y - self._origin_y + return (rel_x, rel_y) + + # pos is an alias for position + def pos(self): + return self.position() + + def xcor(self): + """Returns the turtle's (student) x coordinate.""" + return self.position()[0] + + def ycor(self): + """Returns the turtle's (student) y coordinate.""" + return self.position()[1] + + # 3. Create a Canvas for the turtle to draw on myCanvas = Canvas(width=canvas_width, height=canvas_height) - # 3. Display the Canvas in the Jupyter notebook + # 4. Display the Canvas in the Jupyter notebook display(myCanvas) - # 4. Wait a moment to ensure the canvas is fully rendered + # 5. Wait a moment to ensure the canvas is fully rendered time.sleep(1) - # 5. Create a TurtleScreen + # 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.") diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index f0d61afb..85666fcc 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -126,21 +126,31 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e2459976ac8742aabcfaef4f34fae7ec", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Canvas(height=250, width=1000)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "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) # Create a turtle named tina\n", + "tina = turtlsde(myTS) # Create a turtle object named tina\n", "tina.shape('turtle') # Set the shape of the turtle to a turtle\n", - "tina.speed(5) # Make the turtle move fast, but not too fast\n", - "\n", - "# Move tina to the left of the canvas\n", - "tina.penup() \n", - "tina.goto(-300, -50) \n", - "tina.pendown() \n", + "tina.speed(5) # Make the turtle move fast, but not too fast \n", "\n", "# Draw a square using a loop\n", "for i in range(4): # Loop 4 times\n", From 903c1bf188db59890b44291c8b57f206f833c688 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 7 Nov 2025 14:10:09 -0500 Subject: [PATCH 067/159] Cleaned up auto_turtle.py Turtle class override code to be easier to read; made minor adjustments to Loops.ipynb code cells --- .lib/auto_turtle.py | 141 ++++++++++++--------- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 22 ++-- 2 files changed, 92 insertions(+), 71 deletions(-) diff --git a/.lib/auto_turtle.py b/.lib/auto_turtle.py index 223b87f0..2e1d86f9 100644 --- a/.lib/auto_turtle.py +++ b/.lib/auto_turtle.py @@ -20,6 +20,7 @@ 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 # --------------------- import time # Might not be necessary, but used for sleep @@ -32,68 +33,92 @@ # 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): """ - Custom Turtle class that remaps the coordinate system. - The student's (0, 0) is now our (start_x, start_y). + Initializes the turtle and moves it instantly + to the new (0, 0) origin point. """ - def __init__(self, screen=None, *args, **kwargs): - # Store the new "origin" - 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 --- - # We use super().goto() to move to the *absolute* start - self.penup() - super().goto(self._origin_x, self._origin_y) - self.pendown() - - ### --- OVERRIDDEN METHODS (Coordinate Remapping) --- ### - - def goto(self, x, y): - """Moves the turtle to a (student) coordinate.""" - # Translate student's (x, y) to absolute (real_x, real_y) - real_x = x + self._origin_x - real_y = y + self._origin_y - # Call the original goto - super().goto(real_x, real_y) - - # setposition is an alias for goto - def setposition(self, x, y=None): - self.goto(x, y) - - def setx(self, x): - """Sets the turtle's (student) x coordinate.""" - real_x = x + self._origin_x - super().setx(real_x) - - def sety(self, y): - """Sets the turtle's (student) y coordinate.""" - real_y = y + self._origin_y - super().sety(real_y) - - def position(self): - """Returns the turtle's (student) position.""" - # Get the *absolute* position - abs_x, abs_y = super().position() - # Translate to student's *relative* position - rel_x = abs_x - self._origin_x - rel_y = abs_y - self._origin_y - return (rel_x, rel_y) + # 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) - # pos is an alias for position - def pos(self): - return self.position() + # --- 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) - def xcor(self): - """Returns the turtle's (student) x coordinate.""" - return self.position()[0] + # --- Overridden Position & Movement Methods --- - def ycor(self): - """Returns the turtle's (student) y coordinate.""" - return self.position()[1] + 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 # 3. Create a Canvas for the turtle to draw on myCanvas = Canvas(width=canvas_width, height=canvas_height) @@ -102,7 +127,7 @@ def ycor(self): display(myCanvas) # 5. Wait a moment to ensure the canvas is fully rendered - time.sleep(1) + time.sleep(time_sleep) # 6. Create a TurtleScreen myTS = TurtleScreen(myCanvas) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 85666fcc..5600bbaa 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -71,14 +71,10 @@ "%run .lib/auto_turtle.py # This just handles the general imports for you\n", "\n", "# Set up Tina the Turtle\n", - "tina = Turtle(myTS) # pylint: disable=undefined-variable\n", + "tina = turtle(myTS) # pylint: disable=undefined-variable\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", - "tina.penup() # Lift the pen to move without drawing\n", - "tina.goto(-300, -50) # Move tina to the left of the canvas\n", - "tina.pendown() # Put the pen down to start drawing\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", @@ -90,7 +86,7 @@ "tina.left(90) # Turn tina left 90 degrees\n", "\n", "tina.penup() # Lift the pen to move without drawing\n", - "tina.goto(-150, -50) # Move tina to the center of the canvas\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", @@ -124,18 +120,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e2459976ac8742aabcfaef4f34fae7ec", + "model_id": "1cb2e9d76dde4c7ebb0be539bd11bfaa", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Canvas(height=250, width=1000)" + "Canvas(height=250, width=750)" ] }, "metadata": {}, @@ -148,7 +144,7 @@ "%run .lib/auto_turtle.py # This just handles the general imports for you\n", "\n", "# Set up Tina the Turtle\n", - "tina = turtlsde(myTS) # Create a turtle object named tina\n", + "tina = turtle(myTS) # Create a turtle object named tina\n", "tina.shape('turtle') # Set the shape of the turtle to a turtle\n", "tina.speed(5) # Make the turtle move fast, but not too fast \n", "\n", @@ -158,7 +154,7 @@ " tina.left(90) # Turn left 90 degrees\n", "\n", "tina.penup() # Lift the pen to move without drawing\n", - "tina.goto(-150, -50) # Move tina to the center-left of the canvas\n", + "tina.goto(125, 0) # Move tina to over to the right of the canvas\n", "tina.pendown() # Put the pen down to start drawing\n", "\n", "# Draw a triangle using a loop\n", @@ -168,10 +164,10 @@ "\n", "# Move tina to the center of the canvas\n", "tina.penup() # Lift the pen to move without drawing\n", - "tina.goto(0, 0) # Move tina to the center of the canvas\n", + "tina.goto(260, 50) # Move tina further to the right of the canvas\n", "tina.pendown() # Put the pen down to start drawing\n", "\n", - "tina.speed(9) # Set speed to 9 for a faster star drawing\n", + "tina.speed('fastest') # Set speed to fastest for a faster star drawing\n", "\n", "# Draw a star pattern using a loop\n", "for i in range(36): # Loop 36 times\n", From 135d9ffed3591e2b824cd268d8ae6d36d0e94149 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 7 Nov 2025 15:51:51 -0500 Subject: [PATCH 068/159] Updated a few cells to work with ipyturtle3 --- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 19 +----- .../10_Variables.ipynb | 59 ++++++++++++------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 5600bbaa..3da4ccd2 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -120,24 +120,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "1cb2e9d76dde4c7ebb0be539bd11bfaa", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Canvas(height=250, width=750)" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Run Me!\n", "\n", diff --git a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb index e3be8a06..04f8c15f 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb @@ -16,12 +16,6 @@ "In case you got stuck on the previous lesson, here is one way you could have solved it:\n", "\n", "```python\n", - "#30_Loop_with_Turtle.py\n", - "\n", - "import turtle # Import the turtle graphics module to draw shapes\n", - "turtle.setup(600, 600, 0, 0) # Setup and size the drawing window: width=600, height=600, position=(0,0)\n", - "tina = turtle.Turtle() # Create a Turtle object named tina\n", - "\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", @@ -45,6 +39,7 @@ "metadata": {}, "outputs": [], "source": [ + "\n", "# Store the number of sides for the shape in a variable\n", "sides = 6\n", "\n", @@ -188,17 +183,42 @@ "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:\n", - "```python\n", - "# Let's use variables to make Tina draw different shapes!\n", - "import turtle\n", - "turtle.setup(600, 600, 0, 0)\n", - "tina = turtle.Turtle()\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": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e3e5c905c1ca49fc913ea59747854397", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Canvas(height=250, width=750)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Run Me!\n", + "\n", + "%run .lib/auto_turtle.py # This just handles the general imports for you\n", + "\n", + "tina = turtle(myTS)\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 = 6 # Number of sides for the shape\n", - "size = 100 # Size of each side\n", - "color = \"blue\" # Color of the shape\n", + "sides = 32 # Number of sides for the shape\n", + "size = 60 # Size of each side\n", + "color = \"blue\" # Color of the shape\n", "\n", "# Set Tina's color using our variable\n", "tina.color(color)\n", @@ -208,13 +228,8 @@ "\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\n", - "\n", - "# What happens if you change sides to 3? Or 8?\n", - "# What if you change size to 50? Or color to \"red\"?\n", - "# Try changing the variables above and run it in your own python environment!\n", - "```" + " tina.forward(size) # Move forward by the size variable\n", + " tina.left(angle) # Turn by the calculated angle" ] }, { From f746817c728f2b4b7379539b2b6461c38ac144fb Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 13 Nov 2025 18:00:15 -0500 Subject: [PATCH 069/159] Minor fixes: used ype: ignore[name-defined] to hide intellisense error; altered auto_turtle.py to account for adding sys types --- .lib/auto_turtle.py | 6 +++++- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.lib/auto_turtle.py b/.lib/auto_turtle.py index 2e1d86f9..e40be751 100644 --- a/.lib/auto_turtle.py +++ b/.lib/auto_turtle.py @@ -28,9 +28,11 @@ 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): """ @@ -41,7 +43,6 @@ class turtle(Turtle): 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 @@ -120,6 +121,9 @@ def ycor(self): 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) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 3da4ccd2..c90151df 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -71,7 +71,7 @@ "%run .lib/auto_turtle.py # This just handles the general imports for you\n", "\n", "# Set up Tina the Turtle\n", - "tina = turtle(myTS) # pylint: disable=undefined-variable\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", @@ -129,7 +129,7 @@ "%run .lib/auto_turtle.py # This just handles the general imports for you\n", "\n", "# Set up Tina the Turtle\n", - "tina = turtle(myTS) # Create a turtle object named tina\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 fast, but not too fast \n", "\n", From 6f1236162cc2da7e4672c2df9a947b7c8e4f0d41 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 14 Nov 2025 15:19:14 -0500 Subject: [PATCH 070/159] converted 20_Functions.ipynb to use ipyturtle3 examples via auto_turtle.py --- .../20_Functions.ipynb | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb index c92793dd..d9769e40 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb @@ -253,21 +253,19 @@ "metadata": {}, "outputs": [], "source": [ - "# PROCEED WITH CAUTION:\n", - "# This code is for demonstration and may not run perfectly inside the notebook cell.\n", - "# It's just here to show you an example of how the turtle drawing works.\n", - "# If you want to see Tina in action, copy and paste this code into your own Python environment and run it!\n", + "# Run Me!\n", + "\n", + "%run .lib/auto_turtle.py # This just handles the general imports for you\n", "\n", - "import turtle\n", - "turtle.setup(600, 600, 0, 0)\n", - "tina = turtle.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(2) # Make the turtle move as fast, but not too fast\n", "\n", "# Function to draw any polygon\n", "def 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", - " \n", + " angle = 360 / sides # Calculate the turning angle\n", " for i in range(sides):\n", " tina.forward(size)\n", " tina.left(angle)\n", @@ -283,7 +281,7 @@ "draw_polygon(3, 50, \"red\") # Draw a red triangle\n", "move_tina(100, 0) # Move to a new spot\n", "draw_polygon(4, 60, \"blue\") # Draw a blue square\n", - "move_tina(-100, 100) # Move again\n", + "move_tina(225, 0) # Move again\n", "draw_polygon(6, 40, \"green\") # Draw a green hexagon\n", "\n", "# See how functions make our code cleaner and easier to reuse?\n", From caf46192e0f07dc89f0cc787adc569c4699ad8aa Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 14 Nov 2025 15:43:30 -0500 Subject: [PATCH 071/159] Switched code cells within 10_More_Turtle_Programs.ipynb to markdown cells (I'm not sure if I can get this working with ipyturtle3 without confusing students) --- .lib/auto_turtle.py | 4 +- .../10_More_Turtle_Programs.ipynb | 86 ++++++++++++------- 2 files changed, 57 insertions(+), 33 deletions(-) diff --git a/.lib/auto_turtle.py b/.lib/auto_turtle.py index e40be751..faebcbf0 100644 --- a/.lib/auto_turtle.py +++ b/.lib/auto_turtle.py @@ -15,6 +15,8 @@ 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 @@ -23,8 +25,6 @@ time_sleep = 0.1 # Time to wait for canvas rendering # --------------------- -import time # Might not be necessary, but used for sleep - try: # 1. Import the main library and necessary classes import ipyturtle3 as turtle 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 2a09c879..898ecb4f 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 @@ -47,39 +47,63 @@ "outputs": [], "source": [ "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", - "import os # For operating system interactions\n", - "import turtle # For turtle graphics\n", - "from pathlib import Path # For handling file paths\n", + "# Converted to use `ipyturtle3` widget-style syntax (best-effort).\n", + "from ipyturtle import Turtle # ipyturtle3 widget\n", + "from IPython.display import display, HTML # Display utilities for Jupyter\n", + "import os # For operating system interactions\n", + "from pathlib import Path # For handling file paths\n", "\n", - "# Function to set the turtle's image\n", "def set_turtle_image(t, image_name):\n", - " \"\"\"Set the turtle's shape to a custom image.\"\"\"\n", - " image_dir = Path(os.getcwd()).parent / 'images' # Directory with images\n", - " image_path = str(image_dir / image_name) # Full path to image file\n", - "\n", - " screen = turtle.getscreen() # Get the turtle screen\n", - " screen.addshape(image_path) # Add the image as a shape\n", - " t.shape(image_path) # Set the turtle's shape to the image\n", - "\n", - "# Set up the screen\n", - "screen = turtle.Screen() # Get the screen\n", - "screen.setup(width=600, height=600) # Set the screen size\n", - "\n", - "# Create a turtle and set its shape to the custom GIF\n", - "t = turtle.Turtle() # Create a turtle\n", - "set_turtle_image(t, 'pikachu.gif') # Set turtle to Pikachu image\n", - "\n", - "# Move the turtle around\n", - "t.penup() # Don't draw when moving\n", - "t.speed(3) # Set a moderate speed\n", - "for i in range(4): # Move in a figure eight\n", - " t.goto(200, 200) # Move to top-right corner\n", - " t.goto(-200, -200) # Move to bottom-left corner\n", - " t.goto(200, -200) # Move to bottom-right corner\n", - " t.goto(-200, 200) # Move to top-left corner\n", - "t.goto(200, 200) # Return to top-right corner\n", - "\n", - "turtle.done() # Wait for a click to close the window" + " \"\"\"Try to set a custom image for the turtle (best-effort).\"\"\"\n", + " image_dir = Path(os.getcwd()).parent / 'images' # Directory with images\n", + " image_path = str(image_dir / image_name) # Full path to image file\n", + " try:\n", + " # ipyturtle may offer add_shape/shape; try if available\n", + " if hasattr(t, 'add_shape'):\n", + " t.add_shape(image_path)\n", + " if hasattr(t, 'shape'):\n", + " t.shape(image_path)\n", + " else:\n", + " print('Added shape, but widget has no shape() method.')\n", + " else:\n", + " # Fallback: display the image next to the turtle widget\n", + " display(HTML(f\"{image_name}\"))\n", + " print('Displayed image as a fallback; ipyturtle may not support custom shapes.')\n", + " except Exception as e:\n", + " print('Could not set custom shape:', e)\n", + "\n", + "# Create the ipyturtle widget and show it in the notebook\n", + "t = Turtle(width=600, height=600) # Create the turtle widget\n", + "display(t) # Render the widget in Jupyter\n", + "\n", + "# Attempt to set an image (best-effort)\n", + "set_turtle_image(t, 'pikachu.gif')\n", + "\n", + "# Move the turtle around using widget-friendly calls\n", + "t.penup() # Don't draw when moving\n", + "# ipyturtle exposes many turtle-like methods; speed is often an attribute\n", + "try:\n", + " t.speed = 3\n", + "except Exception:\n", + " pass\n", + "\n", + "# Draw a simple figure-eight-ish path using absolute moves (goto may be supported)\n", + "try:\n", + " t.goto(200, 200)\n", + " t.goto(-200, -200)\n", + " t.goto(200, -200)\n", + " t.goto(-200, 200)\n", + " t.goto(200, 200)\n", + "except Exception:\n", + " # Fallback to relative moves if goto isn't available\n", + " t.setheading(0)\n", + " for _ in range(2):\n", + " t.forward(200)\n", + " t.left(90)\n", + " t.forward(200)\n", + " t.left(90)\n", + "\n", + "# Note: ipyturtle widgets remain interactive in the notebook; there's no blocking call like turtle.done()" ] }, { From e07bcd933c7430938dc4849b88446f1d4a8a5c2d Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 14 Nov 2025 15:58:22 -0500 Subject: [PATCH 072/159] fixed a change that was made that did not need to be --- .../10_More_Turtle_Programs.ipynb | 86 +++++++------------ 1 file changed, 31 insertions(+), 55 deletions(-) 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 898ecb4f..d1f50c60 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 @@ -47,63 +47,39 @@ "outputs": [], "source": [ "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", - "# Converted to use `ipyturtle3` widget-style syntax (best-effort).\n", - "from ipyturtle import Turtle # ipyturtle3 widget\n", - "from IPython.display import display, HTML # Display utilities for Jupyter\n", - "import os # For operating system interactions\n", - "from pathlib import Path # For handling file paths\n", "\n", + "import os # For operating system interactions\n", + "import turtle # For turtle graphics\n", + "from pathlib import Path # For handling file paths\n", + "\n", + "# Function to set the turtle's image\n", "def set_turtle_image(t, image_name):\n", - " \"\"\"Try to set a custom image for the turtle (best-effort).\"\"\"\n", - " image_dir = Path(os.getcwd()).parent / 'images' # Directory with images\n", - " image_path = str(image_dir / image_name) # Full path to image file\n", - " try:\n", - " # ipyturtle may offer add_shape/shape; try if available\n", - " if hasattr(t, 'add_shape'):\n", - " t.add_shape(image_path)\n", - " if hasattr(t, 'shape'):\n", - " t.shape(image_path)\n", - " else:\n", - " print('Added shape, but widget has no shape() method.')\n", - " else:\n", - " # Fallback: display the image next to the turtle widget\n", - " display(HTML(f\"{image_name}\"))\n", - " print('Displayed image as a fallback; ipyturtle may not support custom shapes.')\n", - " except Exception as e:\n", - " print('Could not set custom shape:', e)\n", - "\n", - "# Create the ipyturtle widget and show it in the notebook\n", - "t = Turtle(width=600, height=600) # Create the turtle widget\n", - "display(t) # Render the widget in Jupyter\n", - "\n", - "# Attempt to set an image (best-effort)\n", - "set_turtle_image(t, 'pikachu.gif')\n", - "\n", - "# Move the turtle around using widget-friendly calls\n", - "t.penup() # Don't draw when moving\n", - "# ipyturtle exposes many turtle-like methods; speed is often an attribute\n", - "try:\n", - " t.speed = 3\n", - "except Exception:\n", - " pass\n", - "\n", - "# Draw a simple figure-eight-ish path using absolute moves (goto may be supported)\n", - "try:\n", - " t.goto(200, 200)\n", - " t.goto(-200, -200)\n", - " t.goto(200, -200)\n", - " t.goto(-200, 200)\n", - " t.goto(200, 200)\n", - "except Exception:\n", - " # Fallback to relative moves if goto isn't available\n", - " t.setheading(0)\n", - " for _ in range(2):\n", - " t.forward(200)\n", - " t.left(90)\n", - " t.forward(200)\n", - " t.left(90)\n", - "\n", - "# Note: ipyturtle widgets remain interactive in the notebook; there's no blocking call like turtle.done()" + " \"\"\"Set the turtle's shape to a custom image.\"\"\"\n", + " image_dir = Path(os.getcwd()).parent / 'images' # Directory with images\n", + " image_path = str(image_dir / image_name) # Full path to image file\n", + " screen = turtle.getscreen() # Get the turtle screen\n", + " screen.addshape(image_path) # Add the image as a shape\n", + " t.shape(image_path) # Set the turtle's shape to the image\n", + "\n", + "# Set up the screen\n", + "screen = turtle.Screen() # Get the screen\n", + "screen.setup(width=600, height=600) # Set the screen size\n", + "\n", + "# Create a turtle and set its shape to the custom GIF\n", + "t = turtle.Turtle() # Create a turtle\n", + "set_turtle_image(t, 'pikachu.gif') # Set turtle to Pikachu image\n", + "\n", + "# Move the turtle around\n", + "t.penup() # Don't draw when moving\n", + "t.speed(3) # Set a moderate speed\n", + "for i in range(4): # Move in a figure eight\n", + " t.goto(200, 200) # Move to top-right corner\n", + " t.goto(-200, -200) # Move to bottom-left corner\n", + " t.goto(200, -200) # Move to bottom-right corner\n", + " t.goto(-200, 200) # Move to top-left corner\n", + "t.goto(200, 200) # Return to top-right corner\n", + "\n", + "turtle.done() # Wait for a click to close the window" ] }, { From c75d5a8482a7c7f85a9ba0c38ea1695cede15c80 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 14 Nov 2025 16:05:10 -0500 Subject: [PATCH 073/159] Switched code cells within 10_More_Turtle_Programs.ipynb to markdown cells (I'm not sure if I can get this working with ipyturtle3 without confusing students) --- .../10_More_Turtle_Programs.ipynb | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) 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 d1f50c60..585f9bb6 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 @@ -35,17 +35,14 @@ "## **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.\n", - "\n", - "> **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`)." + "`set_turtle_image()` function into your programs to reuse this behavior." ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ + "```python\n", "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "\n", "import os # For operating system interactions\n", @@ -79,7 +76,15 @@ " t.goto(-200, 200) # Move to top-left corner\n", "t.goto(200, 200) # Return to top-right corner\n", "\n", - "turtle.done() # Wait for a click to close the window" + "turtle.done() # Wait for a click to close the window\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`)." ] }, { @@ -93,11 +98,10 @@ ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ + "```python\n", "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "\n", "import os # For operating system interactions\n", @@ -121,7 +125,8 @@ "screen = turtle.Screen() # Create the screen\n", "set_background_image(screen, 'emoji.png') # Set the background image\n", "\n", - "turtle.exitonclick() # Wait for a click to close the window" + "turtle.exitonclick() # Wait for a click to close the window\n", + "```" ] }, { @@ -134,11 +139,10 @@ ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ + "```python\n", "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "\n", "import turtle # For turtle graphics\n", @@ -159,7 +163,8 @@ " t1.goto(i, i) # Move the first turtle\n", " t2.goto(i, -i) # Move the second turtle\n", "\n", - "turtle.exitonclick() # Wait for a click to close the window" + "turtle.exitonclick() # Wait for a click to close the window\n", + "```" ] }, { @@ -172,11 +177,10 @@ ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ + "```python\n", "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "\n", "import turtle # For turtle graphics\n", @@ -196,7 +200,8 @@ " t.goto(x, y) # Move the turtle to the clicked position\n", "\n", "screen.onclick(screen_clicked) # Tell Python which function to call when the screen is clicked\n", - "turtle.done() # Use `done()` (keeps the window open)" + "turtle.done() # Use `done()` (keeps the window open)\n", + "```" ] }, { @@ -210,11 +215,10 @@ ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ + "```python\n", "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "\n", "import turtle # For turtle graphics\n", @@ -235,14 +239,15 @@ "# Connect the turtle to the turtle_clicked function (use a lambda to pass `t`)\n", "t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y)) # Connect the click event\n", "\n", - "turtle.done() # Use `done()` (keeps the window open)" + "turtle.done() # Use `done()` (keeps the window open)\n", + "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "In the line `t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y))`, the `lambda`\n", + ">**Note:** In the line `t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y))`, the `lambda`\n", "keyword creates a small anonymous function. We'll cover lambdas later, but\n", "for now you can copy this pattern to pass the turtle object into the handler." ] @@ -258,11 +263,10 @@ ] }, { - "cell_type": "code", - "execution_count": 1, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ + "```python\n", "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "\n", "import turtle # For turtle graphics\n", @@ -285,7 +289,8 @@ "# What this line does is create a small anonymous function (a lambda) that\n", "# captures the current turtle `t` and passes it to the `turtle_clicked` function\n", "t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y)) # Connect the click event\n", - "turtle.done() # Use `done()` (keeps the window open)" + "turtle.done() # Use `done()` (keeps the window open)\n", + "```" ] }, { From ba8818f858fd1ec122afd2a1769161ff31eb1df9 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 14 Nov 2025 17:19:25 -0500 Subject: [PATCH 074/159] Fixed code representations within 10_More_Turtle_Programs.ipynb --- .../10_More_Turtle_Programs.ipynb | 137 ++++++++---------- 1 file changed, 61 insertions(+), 76 deletions(-) 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 585f9bb6..31435930 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 @@ -45,16 +45,17 @@ "```python\n", "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", "\n", - "import os # For operating system interactions\n", "import turtle # For turtle graphics\n", - "from pathlib import Path # For handling file paths\n", "\n", "# Function to set the turtle's image\n", - "def set_turtle_image(t, image_name):\n", + "def set_turtle_image(t, image_name): # create two parameters: the turtle and the image name\n", " \"\"\"Set the turtle's shape to a custom image.\"\"\"\n", - " image_dir = Path(os.getcwd()).parent / 'images' # Directory with images\n", - " image_path = str(image_dir / image_name) # Full path to image file\n", - " screen = turtle.getscreen() # Get the turtle screen\n", + "\n", + " from pathlib import Path # For handling file paths\n", + " image_dir = Path(__file__).parent.parent / \"images\" # Set the image directory to the parent images folder\n", + " image_path = str(image_dir / image_name) # Full path to the image\n", + "\n", + " screen = t.getscreen() # Get the turtle's screen\n", " screen.addshape(image_path) # Add the image as a shape\n", " t.shape(image_path) # Set the turtle's shape to the image\n", "\n", @@ -63,20 +64,20 @@ "screen.setup(width=600, height=600) # Set the screen size\n", "\n", "# Create a turtle and set its shape to the custom GIF\n", - "t = turtle.Turtle() # Create a turtle\n", + "t = turtle.Turtle() # Create a turtle named t\n", "set_turtle_image(t, 'pikachu.gif') # Set turtle to Pikachu image\n", "\n", "# Move the turtle around\n", "t.penup() # Don't draw when moving\n", "t.speed(3) # Set a moderate speed\n", "for i in range(4): # Move in a figure eight\n", - " t.goto(200, 200) # Move to top-right corner\n", " t.goto(-200, -200) # Move to bottom-left corner\n", " t.goto(200, -200) # Move to bottom-right corner\n", " t.goto(-200, 200) # Move to top-left corner\n", - "t.goto(200, 200) # Return to top-right corner\n", + " t.goto(200, 200) # Move to top-right corner\n", + "t.goto(0, 0) # Return to center\n", "\n", - "turtle.done() # Wait for a click to close the window\n", + "turtle.exitonclick() # Wait for a click to close the window once done\n", "```" ] }, @@ -103,29 +104,31 @@ "source": [ "```python\n", "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", - "\n", - "import os # For operating system interactions\n", - "import turtle # For turtle graphics\n", - "from pathlib import Path # For handling file paths\n", + "import turtle\n", "\n", "# Function to set the background image\n", - "def set_background_image(window, image_name): \n", - " \"\"\"Set the turtle window background to the named image.\"\"\"\n", - " image_dir = Path(os.getcwd()).parent / 'images' # Get the images directory\n", - " image_path = str(image_dir / image_name) # Build the full image path\n", - " image = Image.open(image_path) # Open the image\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", - " # Resize the window to match the image and set the background\n", - " window.setup(image.width, image.height, startx=0, starty=0) # Resize window\n", - " window.bgpic(image_path) # Set the background picture\n", + " from pathlib import Path # For working with file paths\n", + " from PIL import Image # For working with images\n", + "\n", + " image_dir = Path(__file__).parent.parent / \"images\" # Get the images directory from the parent folder\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 file\n", + " \n", + " window.setup(image.width, image.height, startx=0, starty=0) # Set the window size to match the image size\n", + " window.bgpic(image_path) # Set the background image of the window\n", "\n", "# Set up the screen\n", - "turtle.setup(width=600, height=600) # Set initial window size\n", - "tina = turtle.Turtle() # Create a turtle\n", - "screen = turtle.Screen() # Create the screen\n", - "set_background_image(screen, 'emoji.png') # Set the background image\n", + "turtle.setup(width=600, height=600) # Set the size of the window\n", + "t = turtle.Turtle() # Create a turtle named t\n", + "screen = turtle.Screen() # Get the screen that t is on\n", "\n", - "turtle.exitonclick() # Wait for a click to close the window\n", + "set_background_image(screen, \"emoji.png\") # Set the background image of the screen\n", + "\n", + "turtle.exitonclick() # Wait for a click to close the window once done\n", "```" ] }, @@ -210,7 +213,7 @@ "source": [ "## **Clicking The Turtle Directly**\n", "\n", - "You can run a function when the user clicks a turtle. The turtle library calls your\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." ] }, @@ -223,23 +226,26 @@ "\n", "import turtle # For turtle graphics\n", "\n", - "turtle.setup(width=600, height=600) # Set up the screen\n", - "t = turtle.Turtle() # Create a turtle\n", + "screen = turtle.Screen() # Set up the screen and window\n", + "screen.setup(width=600, height=600)\n", + "screen.bgcolor('white') # Optional background color\n", "\n", - "t.shape('turtle')\n", + "t = turtle.Turtle() # Create a turtle\n", + "t.penup() # Don't draw when moving\n", + "t.shape('turtle') # Use turtle shape\n", "t.turtlesize(stretch_wid=10, stretch_len=10, outline=4) # Make the turtle large\n", "\n", "# Function to handle turtle clicks\n", "def turtle_clicked(t, x, y):\n", " \"\"\"Called when the user clicks the turtle. Tilts the turtle in a circle.\"\"\"\n", " print('turtle clicked!')\n", - " for _ in range(18): # Tilt the turtle in a circle\n", - " t.tilt(20) # 18 * 20 = 360 degrees\n", + " for _ in range(18): # Tilt the turtle in a circle\n", + " t.tilt(20) # 18 * 20 = 360 degrees\n", "\n", - "# Connect the turtle to the turtle_clicked function (use a lambda to pass `t`)\n", - "t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y)) # Connect the click event\n", + "# Connect the turtle to the handler (use a lambda to pass `t`)\n", + "t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y))\n", "\n", - "turtle.done() # Use `done()` (keeps the window open)\n", + "turtle.done() # Keep the window open\n", "```" ] }, @@ -247,50 +253,29 @@ "cell_type": "markdown", "metadata": {}, "source": [ - ">**Note:** In the line `t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y))`, the `lambda`\n", - "keyword creates a small anonymous function. We'll cover lambdas later, but\n", - "for now you can copy this pattern to pass the turtle object into the handler." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **How to Tell Which Turtle Was Clicked**\n", + "
\n", + "
\n", + " Click Here For Further Explanation\n", "\n", - "When you have multiple turtles, you may want to know exactly which turtle was\n", - "clicked. The lambda trick above lets each turtle pass itself into the handler." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```python\n", - "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", + "When your program creates more than one turtle, the click handler needs to know which turtle was clicked — not just the click coordinates.\n", "\n", - "import turtle # For turtle graphics\n", + "The pattern `t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y))` creates a tiny anonymous function (a `lambda`) that accepts the click coordinates `x` and `y` and also captures the current turtle by using the default parameter `t=t`.\n", "\n", - "screen = turtle.Screen() # Set up the screen\n", - "screen.setup(width=600, height=600) # Set the screen size\n", - "screen.bgcolor('white') # Set the background color\n", + "Why `t=t`? Without it the handler would look up the variable `t` later (after the loop finishes) and often end up referring to the last turtle created. Using `t=t` binds the current turtle to the handler immediately so each turtle passes itself correctly.\n", "\n", - "t = turtle.Turtle() # Create a turtle\n", - "t.penup() # Lift the pen\n", - "t.shape('turtle') # Set the shape to 'turtle'\n", - "t.turtlesize(stretch_wid=10, stretch_len=10, outline=4) # Make the turtle large\n", + "If you prefer a named function instead of a `lambda`, you can achieve the same result by returning a handler that closes over `t`:\n", "\n", - "# Function to handle turtle clicks\n", - "def turtle_clicked(t, x, y):\n", - " print('turtle clicked!') # Print a message\n", - " for _ in range(18): # Tilt the turtle in a circle\n", - " t.tilt(20) # 18 * 20 = 360 degrees\n", - "\n", - "# What this line does is create a small anonymous function (a lambda) that\n", - "# captures the current turtle `t` and passes it to the `turtle_clicked` function\n", - "t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y)) # Connect the click event\n", - "turtle.done() # Use `done()` (keeps the window open)\n", - "```" + "```python\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 approaches ensure the handler has a reference to the specific turtle that was clicked. Copy whichever pattern you find clearer into your programs.\n", + "
" ] }, { @@ -303,7 +288,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": ".venv", "language": "python", "name": "python3" }, From 6d4c20e00d7c5f44810454d9b55eada44e2233d0 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 14 Nov 2025 17:26:04 -0500 Subject: [PATCH 075/159] Fixed comment instructions to match newer updates --- lessons/10_Turtles/70_Projects/30_Tash_Me_Click.py | 13 +++++++------ lessons/10_Turtles/70_Projects/40_Tash_Me_Twirl.py | 7 +++---- 2 files changed, 10 insertions(+), 10 deletions(-) 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..539fea99 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 'Respond to Screen Clicks' """ ... # Your code here \ No newline at end of file From b62bb704d7331950a5e2f22127543eb65ea6fd99 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 17 Nov 2025 12:07:35 -0500 Subject: [PATCH 076/159] Made syl updates to fix lesson tracking --- lessons/.jtl/syllabus.yaml | 283 +++++++++--------- .../10_Variables.ipynb | 19 +- 2 files changed, 143 insertions(+), 159 deletions(-) diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index 05a13181..ad93a29b 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -2,212 +2,211 @@ name: Python Apprentice module_dir: .. uid: 283c41e4-8abe-49bd-9335-e19ba693277b modules: -- name: Turtles +- name: Introduction to Python with Turtle Graphics uid: Io0hFJiW +- name: Welcome + uid: AZCeRx3O lessons: - name: Welcome - uid: AZCeRx3O - lessons: - - name: Welcome - uid: RRTPqCQu - exercise: 10_Turtles/10_Welcome/10_Welcome.ipynb - - name: Open The Screen - uid: KmgIQbhr - exercise: 10_Turtles/10_Welcome/20_Open_The_Screen.ipynb - - name: Run Programs - uid: cNLK6qtR - exercise: 10_Turtles/10_Welcome/30_Run_Programs.ipynb - - name: Introducing Tina - uid: NtvhO8WR - lessons: - - name: Meet Tina - uid: tvO1dlwm - lesson: 10_Turtles/20_Introducing_Tina/10_Meet_Tina/README.md - exercise: 10_Turtles/20_Introducing_Tina/10_Meet_Tina/Meet_Tina.py - display: true - - name: What Can Tina Do - uid: 7tUP3zAZ - exercise: 10_Turtles/20_Introducing_Tina/20_What_Can_Tina_Do.ipynb - - name: Squares and Circles - uid: E7KlecQ3 - lesson: 10_Turtles/20_Introducing_Tina/30_Squares_and_Circles/README.md - exercise: 10_Turtles/20_Introducing_Tina/30_Squares_and_Circles/Squares_and_Circles.py - display: true - - name: Check In Your Code - uid: doD6P7fk - exercise: 10_Turtles/20_Introducing_Tina/40_Check_In_Your_Code.ipynb + uid: RRTPqCQu + exercise: 10_Turtles\10_Welcome\10_Welcome.ipynb + - name: Open The Screen + uid: KmgIQbhr + exercise: 10_Turtles\10_Welcome\20_Open_The_Screen.ipynb + - name: Run Programs + uid: cNLK6qtR + exercise: 10_Turtles\10_Welcome\30_Run_Programs.ipynb +- name: Introducing Tina + uid: NtvhO8WR + lessons: + - name: Meet Tina + uid: tvO1dlwm + lesson: 10_Turtles\20_Introducing_Tina\10_Meet_Tina\README.md + exercise: 10_Turtles\20_Introducing_Tina\10_Meet_Tina\Meet_Tina.py + display: true + - name: What Can Tina Do + uid: 7tUP3zAZ + exercise: 10_Turtles\20_Introducing_Tina\20_What_Can_Tina_Do.ipynb + - name: Squares and Circles + uid: E7KlecQ3 + lesson: 10_Turtles\20_Introducing_Tina\30_Squares_and_Circles\README.md + exercise: 10_Turtles\20_Introducing_Tina\30_Squares_and_Circles\Squares_and_Circles.py + display: true + - name: Check In Your Code + uid: doD6P7fk + exercise: 10_Turtles\20_Introducing_Tina\40_Check_In_Your_Code.ipynb +- name: Turtle Tricks + uid: c6l7hD7a + lessons: + - name: Turtle Tricks + exercise: 10_Turtles\30_Turtle_Tricks\10_Turtle_Tricks.py + display: true + - name: Turtle Tricks + exercise: 10_Turtles\30_Turtle_Tricks\20_Turtle_Tricks.py + display: true - name: Turtle Tricks - uid: c6l7hD7a - lessons: - - name: Turtle Tricks - exercise: 10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py - display: true - - name: Turtle Tricks - exercise: 10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py - display: true - - name: Turtle Tricks - exercise: 10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py - display: true + exercise: 10_Turtles\30_Turtle_Tricks\30_Turtle_Tricks.py + display: true +- name: Loops + uid: wVDXfv96 + lessons: - name: Loops - uid: wVDXfv96 - lessons: - - name: Loops - uid: abX8sNwB - exercise: 10_Turtles/40_Loops/10_Loops.ipynb - - name: Loop with Turtle - exercise: 10_Turtles/40_Loops/20_Loop_with_Turtle.py - display: true - - name: Loop with Turtle - exercise: 10_Turtles/40_Loops/30_Loop_with_Turtle.py - - name: Variables and Functions - uid: O1qsljMz - lessons: - - name: Variables and Functions - uid: HOBo0wvj - exercise: 10_Turtles/50_Variables_and_Functions/10_Variables_and_Functions.ipynb - - name: Variables and Functions - uid: 0CwXIYSb - exercise: 10_Turtles/50_Variables_and_Functions/20_Variables_and_Functions.ipynb - - name: Efficient Turtle - exercise: 10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py - display: true + uid: abX8sNwB + exercise: 10_Turtles\40_Loops\10_Loops.ipynb + - name: Loop with Turtle + exercise: 10_Turtles\40_Loops\20_Loop_with_Turtle.py + display: true + - name: Loop with Turtle + exercise: 10_Turtles\40_Loops\30_Loop_with_Turtle.py +- name: Variables and Functions + uid: O1qsljMz + lessons: + - name: Variables + uid: HOBo0wvj + exercise: 10_Turtles\50_Variables_and_Functions\10_Variables.ipynb + - name: Functions + uid: 0CwXIYSb + exercise: 10_Turtles\50_Variables_and_Functions\20_Functions.ipynb + - name: Efficient Turtle + exercise: 10_Turtles\50_Variables_and_Functions\30_Efficient_Turtle.py + display: true +- 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 - 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/30_More_Turtle_Programs.py - - name: More Turtle Programs - exercise: 10_Turtles/60_More_Turtle_Programs/40_More_Turtle_Programs.py - - name: Projects - uid: eo3RVnyt - lessons: - - name: LeagueBot - exercise: 10_Turtles/70_Projects/10_LeagueBot.py - display: true - - name: Tash Me - exercise: 10_Turtles/70_Projects/20_Tash_Me.py - - name: Tash Me Click - 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 - uid: g4kLhJ2U - lessons: - - name: Lists - uid: 0KEhJUGe - exercise: 10_Turtles/80_Introducting_Lists/10_Lists.ipynb - - name: Color Lines - exercise: 10_Turtles/80_Introducting_Lists/20_Color_Lines.py - display: true - - name: Graphics Projects - uid: 3shr4ruR - lessons: - - name: Flaming Ninja Star - exercise: 10_Turtles/90_Graphics_Projects/10_Flaming_Ninja_Star.py - display: true - - name: Crazy Spiral - exercise: 10_Turtles/90_Graphics_Projects/20_Crazy_Spiral.py - - name: Pentagon Crazy - exercise: 10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py - display: true - - name: Turtle Spiral - exercise: 10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py - display: true -- name: Types and Logic + exercise: 10_Turtles\60_More_Turtle_Programs\30_More_Turtle_Programs.py + - name: More Turtle Programs + exercise: 10_Turtles\60_More_Turtle_Programs\40_More_Turtle_Programs.py +- name: Projects + uid: eo3RVnyt + lessons: + - name: LeagueBot + exercise: 10_Turtles\70_Projects\10_LeagueBot.py + display: true + - name: Tash Me + exercise: 10_Turtles\70_Projects\20_Tash_Me.py + - name: Tash Me Click + 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 + uid: g4kLhJ2U + lessons: + - name: Lists + uid: 0KEhJUGe + exercise: 10_Turtles\80_Introducting_Lists\10_Lists.ipynb + - name: Color Lines + exercise: 10_Turtles\80_Introducting_Lists\20_Color_Lines.py + display: true +- name: Graphics Projects + uid: 3shr4ruR + lessons: + - name: Flaming Ninja Star + exercise: 10_Turtles\90_Graphics_Projects\10_Flaming_Ninja_Star.py + display: true + - name: Crazy Spiral + exercise: 10_Turtles\90_Graphics_Projects\20_Crazy_Spiral.py + - name: Pentagon Crazy + exercise: 10_Turtles\90_Graphics_Projects\30_Pentagon_Crazy.py + display: true + - name: Turtle Spiral + exercise: 10_Turtles\90_Graphics_Projects\40_Turtle_Spiral.py + display: true +- name: PCEP Alignment uid: ryHvW6vk lessons: - name: Numbers and Strings uid: HXQZ0Iui - exercise: 20_Types_and_Logic/10_Numbers_and_Strings.ipynb + exercise: 20_Types_and_Logic\10_Numbers_and_Strings.ipynb - name: Control Flow uid: g6JPkFUs - exercise: 20_Types_and_Logic/20_Control_Flow.ipynb + exercise: 20_Types_and_Logic\20_Control_Flow.ipynb - name: My Ages - exercise: 20_Types_and_Logic/30_My_Ages.py + exercise: 20_Types_and_Logic\30_My_Ages.py display: true - name: Simple Adder - exercise: 20_Types_and_Logic/40_Simple_Adder.py + exercise: 20_Types_and_Logic\40_Simple_Adder.py - name: Infuriating Calculator - exercise: 20_Types_and_Logic/50_Infuriating_Calculator.py + exercise: 20_Types_and_Logic\50_Infuriating_Calculator.py - name: Code Challenges uid: jJI6aomB - exercise: 20_Types_and_Logic/60_Code_Challenges.ipynb -- name: Loops + exercise: 20_Types_and_Logic\60_Code_Challenges.ipynb +- name: PCEP Alignment uid: TzgRqJlw lessons: - name: Iteration uid: ITpqcvxv - exercise: 30_Loops/010_Iteration.ipynb + exercise: 30_Loops\010_Iteration.ipynb - name: Loops with Range uid: WcGpR3Xg - exercise: 30_Loops/020_Loops_with_Range.ipynb + exercise: 30_Loops\020_Loops_with_Range.ipynb - name: Lists uid: 7zsKa84X - exercise: 30_Loops/030_Lists.ipynb + exercise: 30_Loops\030_Lists.ipynb - name: Crazy Tina - exercise: 30_Loops/040_Crazy_Tina.py + exercise: 30_Loops\040_Crazy_Tina.py display: true - name: Tuples uid: 9zTQza2e - exercise: 30_Loops/050_Tuples.ipynb + exercise: 30_Loops\050_Tuples.ipynb - name: Indexing and Slicing uid: P27f2L8k - exercise: 30_Loops/060_Indexing_and_Slicing.ipynb + exercise: 30_Loops\060_Indexing_and_Slicing.ipynb - name: List Story - exercise: 30_Loops/070_List_Story.py + exercise: 30_Loops\070_List_Story.py - name: Strings uid: Tmg4QRhJ - exercise: 30_Loops/080_Strings.ipynb + exercise: 30_Loops\080_Strings.ipynb - name: Fizz Buzz Badgers - exercise: 30_Loops/090_Fizz_Buzz_Badgers.py + exercise: 30_Loops\090_Fizz_Buzz_Badgers.py - name: For Loop Gauntlet uid: 8yGSkBgV - exercise: 30_Loops/100_For_Loop_Gauntlet.ipynb + exercise: 30_Loops\100_For_Loop_Gauntlet.ipynb - name: FizzBuzz Gui Grid - exercise: 30_Loops/110_FizzBuzz_Gui_Grid.py + exercise: 30_Loops\110_FizzBuzz_Gui_Grid.py display: true - name: More Iterables uid: PrKwTywv - exercise: 30_Loops/120_More_Iterables.ipynb + 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.ipynb - name: More Loops uid: RMSFNtMb - exercise: 30_Loops/140_More_Loops.ipynb + exercise: 30_Loops\140_More_Loops.ipynb - name: Number Guess - exercise: 30_Loops/150_Number_Guess.py + exercise: 30_Loops\150_Number_Guess.py - name: Extras uid: VJSgvOr5 - exercise: 30_Loops/160_Extras.ipynb -- name: Data Structures Func + exercise: 30_Loops\160_Extras.ipynb +- name: PCEP Alignment uid: fDPxSid0 lessons: - name: Functions uid: 4LyScnS5 - exercise: 40_Data_Structures_Func/10_Functions.ipynb + exercise: 40_Data_Structures_Func\10_Functions.ipynb - name: Dicts Sets uid: VXQdZcqg - exercise: 40_Data_Structures_Func/20_Dicts_Sets.ipynb + exercise: 40_Data_Structures_Func\20_Dicts_Sets.ipynb - name: Funny Words Db - exercise: 40_Data_Structures_Func/30_Funny_Words_Db.py + exercise: 40_Data_Structures_Func\30_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\40_Splat_Comprehension.ipynb - name: Tic Tac Toe - exercise: 40_Data_Structures_Func/50_Tic_Tac_Toe.py + exercise: 40_Data_Structures_Func\50_Tic_Tac_Toe.py display: true - name: Projects uid: rLq5eVeW lessons: - name: Hotel Management - lesson: 50_Projects/10_Hotel_Management.md + lesson: 50_Projects\10_Hotel_Management.md - name: Random Walk - exercise: 50_Projects/20_Random_Walk.py + exercise: 50_Projects\20_Random_Walk.py display: true diff --git a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb index 04f8c15f..9299635f 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb @@ -188,24 +188,9 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "e3e5c905c1ca49fc913ea59747854397", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Canvas(height=250, width=750)" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Run Me!\n", "\n", From 1067175120eedbde7ad02523e4116531f9da1d90 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 17 Nov 2025 12:38:01 -0500 Subject: [PATCH 077/159] Revert "Made syl updates to fix lesson tracking" This reverts commit b62bb704d7331950a5e2f22127543eb65ea6fd99. --- lessons/.jtl/syllabus.yaml | 283 +++++++++--------- .../10_Variables.ipynb | 19 +- 2 files changed, 159 insertions(+), 143 deletions(-) diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index ad93a29b..05a13181 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -2,211 +2,212 @@ name: Python Apprentice module_dir: .. uid: 283c41e4-8abe-49bd-9335-e19ba693277b modules: -- name: Introduction to Python with Turtle Graphics +- name: Turtles uid: Io0hFJiW -- name: Welcome - uid: AZCeRx3O lessons: - name: Welcome - uid: RRTPqCQu - exercise: 10_Turtles\10_Welcome\10_Welcome.ipynb - - name: Open The Screen - uid: KmgIQbhr - exercise: 10_Turtles\10_Welcome\20_Open_The_Screen.ipynb - - name: Run Programs - uid: cNLK6qtR - exercise: 10_Turtles\10_Welcome\30_Run_Programs.ipynb -- name: Introducing Tina - uid: NtvhO8WR - lessons: - - name: Meet Tina - uid: tvO1dlwm - lesson: 10_Turtles\20_Introducing_Tina\10_Meet_Tina\README.md - exercise: 10_Turtles\20_Introducing_Tina\10_Meet_Tina\Meet_Tina.py - display: true - - name: What Can Tina Do - uid: 7tUP3zAZ - exercise: 10_Turtles\20_Introducing_Tina\20_What_Can_Tina_Do.ipynb - - name: Squares and Circles - uid: E7KlecQ3 - lesson: 10_Turtles\20_Introducing_Tina\30_Squares_and_Circles\README.md - exercise: 10_Turtles\20_Introducing_Tina\30_Squares_and_Circles\Squares_and_Circles.py - display: true - - name: Check In Your Code - uid: doD6P7fk - exercise: 10_Turtles\20_Introducing_Tina\40_Check_In_Your_Code.ipynb -- name: Turtle Tricks - uid: c6l7hD7a - lessons: - - name: Turtle Tricks - exercise: 10_Turtles\30_Turtle_Tricks\10_Turtle_Tricks.py - display: true - - name: Turtle Tricks - exercise: 10_Turtles\30_Turtle_Tricks\20_Turtle_Tricks.py - display: true + uid: AZCeRx3O + lessons: + - name: Welcome + uid: RRTPqCQu + exercise: 10_Turtles/10_Welcome/10_Welcome.ipynb + - name: Open The Screen + uid: KmgIQbhr + exercise: 10_Turtles/10_Welcome/20_Open_The_Screen.ipynb + - name: Run Programs + uid: cNLK6qtR + exercise: 10_Turtles/10_Welcome/30_Run_Programs.ipynb + - name: Introducing Tina + uid: NtvhO8WR + lessons: + - name: Meet Tina + uid: tvO1dlwm + lesson: 10_Turtles/20_Introducing_Tina/10_Meet_Tina/README.md + exercise: 10_Turtles/20_Introducing_Tina/10_Meet_Tina/Meet_Tina.py + display: true + - name: What Can Tina Do + uid: 7tUP3zAZ + exercise: 10_Turtles/20_Introducing_Tina/20_What_Can_Tina_Do.ipynb + - name: Squares and Circles + uid: E7KlecQ3 + lesson: 10_Turtles/20_Introducing_Tina/30_Squares_and_Circles/README.md + exercise: 10_Turtles/20_Introducing_Tina/30_Squares_and_Circles/Squares_and_Circles.py + display: true + - name: Check In Your Code + uid: doD6P7fk + exercise: 10_Turtles/20_Introducing_Tina/40_Check_In_Your_Code.ipynb - name: Turtle Tricks - exercise: 10_Turtles\30_Turtle_Tricks\30_Turtle_Tricks.py - display: true -- name: Loops - uid: wVDXfv96 - lessons: + uid: c6l7hD7a + lessons: + - name: Turtle Tricks + exercise: 10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py + display: true + - name: Turtle Tricks + exercise: 10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py + display: true + - name: Turtle Tricks + exercise: 10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py + display: true - name: Loops - uid: abX8sNwB - exercise: 10_Turtles\40_Loops\10_Loops.ipynb - - name: Loop with Turtle - exercise: 10_Turtles\40_Loops\20_Loop_with_Turtle.py - display: true - - name: Loop with Turtle - exercise: 10_Turtles\40_Loops\30_Loop_with_Turtle.py -- name: Variables and Functions - uid: O1qsljMz - lessons: - - name: Variables - uid: HOBo0wvj - exercise: 10_Turtles\50_Variables_and_Functions\10_Variables.ipynb - - name: Functions - uid: 0CwXIYSb - exercise: 10_Turtles\50_Variables_and_Functions\20_Functions.ipynb - - name: Efficient Turtle - exercise: 10_Turtles\50_Variables_and_Functions\30_Efficient_Turtle.py - display: true -- 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 + uid: wVDXfv96 + lessons: + - name: Loops + uid: abX8sNwB + exercise: 10_Turtles/40_Loops/10_Loops.ipynb + - name: Loop with Turtle + exercise: 10_Turtles/40_Loops/20_Loop_with_Turtle.py + display: true + - name: Loop with Turtle + exercise: 10_Turtles/40_Loops/30_Loop_with_Turtle.py + - name: Variables and Functions + uid: O1qsljMz + lessons: + - name: Variables and Functions + uid: HOBo0wvj + exercise: 10_Turtles/50_Variables_and_Functions/10_Variables_and_Functions.ipynb + - name: Variables and Functions + uid: 0CwXIYSb + exercise: 10_Turtles/50_Variables_and_Functions/20_Variables_and_Functions.ipynb + - name: Efficient Turtle + exercise: 10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py + display: true - name: More Turtle Programs - exercise: 10_Turtles\60_More_Turtle_Programs\30_More_Turtle_Programs.py - - name: More Turtle Programs - exercise: 10_Turtles\60_More_Turtle_Programs\40_More_Turtle_Programs.py -- name: Projects - uid: eo3RVnyt - lessons: - - name: LeagueBot - exercise: 10_Turtles\70_Projects\10_LeagueBot.py - display: true - - name: Tash Me - exercise: 10_Turtles\70_Projects\20_Tash_Me.py - - name: Tash Me Click - 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 - uid: g4kLhJ2U - lessons: - - name: Lists - uid: 0KEhJUGe - exercise: 10_Turtles\80_Introducting_Lists\10_Lists.ipynb - - name: Color Lines - exercise: 10_Turtles\80_Introducting_Lists\20_Color_Lines.py - display: true -- name: Graphics Projects - uid: 3shr4ruR - lessons: - - name: Flaming Ninja Star - exercise: 10_Turtles\90_Graphics_Projects\10_Flaming_Ninja_Star.py - display: true - - name: Crazy Spiral - exercise: 10_Turtles\90_Graphics_Projects\20_Crazy_Spiral.py - - name: Pentagon Crazy - exercise: 10_Turtles\90_Graphics_Projects\30_Pentagon_Crazy.py - display: true - - name: Turtle Spiral - exercise: 10_Turtles\90_Graphics_Projects\40_Turtle_Spiral.py - display: true -- name: PCEP Alignment + 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/30_More_Turtle_Programs.py + - name: More Turtle Programs + exercise: 10_Turtles/60_More_Turtle_Programs/40_More_Turtle_Programs.py + - name: Projects + uid: eo3RVnyt + lessons: + - name: LeagueBot + exercise: 10_Turtles/70_Projects/10_LeagueBot.py + display: true + - name: Tash Me + exercise: 10_Turtles/70_Projects/20_Tash_Me.py + - name: Tash Me Click + 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 + uid: g4kLhJ2U + lessons: + - name: Lists + uid: 0KEhJUGe + exercise: 10_Turtles/80_Introducting_Lists/10_Lists.ipynb + - name: Color Lines + exercise: 10_Turtles/80_Introducting_Lists/20_Color_Lines.py + display: true + - name: Graphics Projects + uid: 3shr4ruR + lessons: + - name: Flaming Ninja Star + exercise: 10_Turtles/90_Graphics_Projects/10_Flaming_Ninja_Star.py + display: true + - name: Crazy Spiral + exercise: 10_Turtles/90_Graphics_Projects/20_Crazy_Spiral.py + - name: Pentagon Crazy + exercise: 10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py + display: true + - name: Turtle Spiral + exercise: 10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py + display: true +- name: Types and Logic uid: ryHvW6vk lessons: - name: Numbers and Strings uid: HXQZ0Iui - exercise: 20_Types_and_Logic\10_Numbers_and_Strings.ipynb + exercise: 20_Types_and_Logic/10_Numbers_and_Strings.ipynb - name: Control Flow uid: g6JPkFUs - exercise: 20_Types_and_Logic\20_Control_Flow.ipynb + exercise: 20_Types_and_Logic/20_Control_Flow.ipynb - name: My Ages - exercise: 20_Types_and_Logic\30_My_Ages.py + exercise: 20_Types_and_Logic/30_My_Ages.py display: true - name: Simple Adder - exercise: 20_Types_and_Logic\40_Simple_Adder.py + exercise: 20_Types_and_Logic/40_Simple_Adder.py - name: Infuriating Calculator - exercise: 20_Types_and_Logic\50_Infuriating_Calculator.py + exercise: 20_Types_and_Logic/50_Infuriating_Calculator.py - name: Code Challenges uid: jJI6aomB - exercise: 20_Types_and_Logic\60_Code_Challenges.ipynb -- name: PCEP Alignment + exercise: 20_Types_and_Logic/60_Code_Challenges.ipynb +- name: Loops uid: TzgRqJlw lessons: - name: Iteration uid: ITpqcvxv - exercise: 30_Loops\010_Iteration.ipynb + exercise: 30_Loops/010_Iteration.ipynb - name: Loops with Range uid: WcGpR3Xg - exercise: 30_Loops\020_Loops_with_Range.ipynb + exercise: 30_Loops/020_Loops_with_Range.ipynb - name: Lists uid: 7zsKa84X - exercise: 30_Loops\030_Lists.ipynb + exercise: 30_Loops/030_Lists.ipynb - name: Crazy Tina - exercise: 30_Loops\040_Crazy_Tina.py + exercise: 30_Loops/040_Crazy_Tina.py display: true - name: Tuples uid: 9zTQza2e - exercise: 30_Loops\050_Tuples.ipynb + exercise: 30_Loops/050_Tuples.ipynb - name: Indexing and Slicing uid: P27f2L8k - exercise: 30_Loops\060_Indexing_and_Slicing.ipynb + exercise: 30_Loops/060_Indexing_and_Slicing.ipynb - name: List Story - exercise: 30_Loops\070_List_Story.py + exercise: 30_Loops/070_List_Story.py - name: Strings uid: Tmg4QRhJ - exercise: 30_Loops\080_Strings.ipynb + exercise: 30_Loops/080_Strings.ipynb - name: Fizz Buzz Badgers - exercise: 30_Loops\090_Fizz_Buzz_Badgers.py + exercise: 30_Loops/090_Fizz_Buzz_Badgers.py - name: For Loop Gauntlet uid: 8yGSkBgV - exercise: 30_Loops\100_For_Loop_Gauntlet.ipynb + exercise: 30_Loops/100_For_Loop_Gauntlet.ipynb - name: FizzBuzz Gui Grid - exercise: 30_Loops\110_FizzBuzz_Gui_Grid.py + exercise: 30_Loops/110_FizzBuzz_Gui_Grid.py display: true - name: More Iterables uid: PrKwTywv - exercise: 30_Loops\120_More_Iterables.ipynb + 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.ipynb - name: More Loops uid: RMSFNtMb - exercise: 30_Loops\140_More_Loops.ipynb + exercise: 30_Loops/140_More_Loops.ipynb - name: Number Guess - exercise: 30_Loops\150_Number_Guess.py + exercise: 30_Loops/150_Number_Guess.py - name: Extras uid: VJSgvOr5 - exercise: 30_Loops\160_Extras.ipynb -- name: PCEP Alignment + exercise: 30_Loops/160_Extras.ipynb +- name: Data Structures Func uid: fDPxSid0 lessons: - name: Functions uid: 4LyScnS5 - exercise: 40_Data_Structures_Func\10_Functions.ipynb + exercise: 40_Data_Structures_Func/10_Functions.ipynb - name: Dicts Sets uid: VXQdZcqg - exercise: 40_Data_Structures_Func\20_Dicts_Sets.ipynb + exercise: 40_Data_Structures_Func/20_Dicts_Sets.ipynb - name: Funny Words Db - exercise: 40_Data_Structures_Func\30_Funny_Words_Db.py + exercise: 40_Data_Structures_Func/30_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/40_Splat_Comprehension.ipynb - name: Tic Tac Toe - exercise: 40_Data_Structures_Func\50_Tic_Tac_Toe.py + exercise: 40_Data_Structures_Func/50_Tic_Tac_Toe.py display: true - name: Projects uid: rLq5eVeW lessons: - name: Hotel Management - lesson: 50_Projects\10_Hotel_Management.md + lesson: 50_Projects/10_Hotel_Management.md - name: Random Walk - exercise: 50_Projects\20_Random_Walk.py + exercise: 50_Projects/20_Random_Walk.py display: true diff --git a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb index 9299635f..04f8c15f 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb @@ -188,9 +188,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e3e5c905c1ca49fc913ea59747854397", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Canvas(height=250, width=750)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Run Me!\n", "\n", From 2822d0d55a60fba403dd2bc27f1f4ac52ced0977 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 17 Nov 2025 12:50:09 -0500 Subject: [PATCH 078/159] Fixed lesson browser issue by updating syllabus.yaml manually --- lessons/.jtl/syllabus.yaml | 8 +++--- .../10_Variables.ipynb | 25 ++++--------------- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index 05a13181..566e2a1e 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -62,12 +62,12 @@ modules: - 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 - - name: Variables and Functions + exercise: 10_Turtles/50_Variables_and_Functions/10_Variables.ipynb + - name: Functions uid: 0CwXIYSb - exercise: 10_Turtles/50_Variables_and_Functions/20_Variables_and_Functions.ipynb + exercise: 10_Turtles/50_Variables_and_Functions/20_Functions.ipynb - name: Efficient Turtle exercise: 10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py display: true diff --git a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb index 04f8c15f..749c5f7e 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb @@ -188,40 +188,25 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "e3e5c905c1ca49fc913ea59747854397", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Canvas(height=250, width=750)" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Run Me!\n", "\n", "%run .lib/auto_turtle.py # This just handles the general imports for you\n", "\n", - "tina = turtle(myTS)\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", + "sides = 32 # Number of sides for the shape\n", "size = 60 # Size of each side\n", "color = \"blue\" # Color of the shape\n", "\n", "# Set Tina's color using our variable\n", - "tina.color(color)\n", + "tina.color(color) # Set the turtle's color\n", "\n", "# Calculate the angle using our sides variable\n", "angle = 360 / sides\n", From 99188ac6a058ff98d334f33a375ce10a8bb6ce9f Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 21 Nov 2025 15:26:16 -0500 Subject: [PATCH 079/159] Made slight improvements to sections that enhance notebook readability --- .../10_Turtles/10_Welcome/30_Run_Programs.ipynb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index cba5db1b..c8f04f8e 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -51,9 +51,9 @@ "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 Cell\"\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. Don’t worry! Technically something did, you just were not able to see it. This is because you still need to pick which interpreter (or version) for Python to use.\n", + "The first time you press the ▶️ button you might notice that nothing happens. Don’t worry! Technically something did, you just were not able to 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", @@ -79,8 +79,8 @@ "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. " + "* 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." ] }, { @@ -152,13 +152,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "
\n", + "
\n", "\n", - "**If a program is already running, you must close the program before starting another one.**\n", + "**WARNING:** **If a program is already running, you must close the program before starting another one.**\n", "\n", "**You have two options to close it:**\n", - "- **Click the ❌ in the top-right corner of the window.** \n", - " - **If the program's code ends with turtle.exitonclick(), you can simply click anywhere inside the window to close it.**\n", + "- **Click the ✖ in the top-right corner of the window.** \n", + " - **If the program's code ends with turtle.exitonclick(), you can simply click anywhere inside the window to close it.**\n", "- **If you are using the debugger, you can also stop the program by clicking the red square icon in the debugger toolbar.**\n", "\n", "
" From 65e5dbd52fe402a1453bab64e451f7cb0a374c1e Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 21 Nov 2025 16:00:21 -0500 Subject: [PATCH 080/159] Made the warning at the bottom clearer --- lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index c8f04f8e..e1eb6154 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -156,10 +156,11 @@ "\n", "**WARNING:** **If a program is already running, you must close the program before starting another one.**\n", "\n", - "**You have two options to close it:**\n", - "- **Click the ✖ in the top-right corner of the window.** \n", - " - **If the program's code ends with turtle.exitonclick(), you can simply click anywhere inside the window to close it.**\n", - "- **If you are using the debugger, you can also stop the program by clicking the red square icon in the debugger toolbar.**\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", "\n", "
" ] From 36d51f15406b333aa973b7ad4c9d4c98e3fd7916 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 21 Nov 2025 16:51:14 -0500 Subject: [PATCH 081/159] Made updates to enhance readability --- .../20_Introducing_Tina/40_Check_In_Your_Code.ipynb | 2 +- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) 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 2cc88f71..4c3b675f 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 @@ -97,7 +97,7 @@ "There are two ways to fix it:\n", "\n", "1. **By closing the edit message file**\n", - " - Click the 'X' (on the file tab), return to Source Control, enter your commit message, and click the ✓ Commit button again.\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", diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index c90151df..86a5f68d 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -95,16 +95,14 @@ "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", - "\n", - "# Now imagine how confusing it would be if we had `ipyturtle3` move the turtle 36 times to draw a star!" + "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.\n", + "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", "\n", "Let’s see how we can use loops to make this code cleaner and easier to manage. Maybe we can even add more shapes without making the code super long!" ] From fedbb8c83ff93ebc586b86eb3999a6fe93d6cb0d Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 21 Nov 2025 18:41:55 -0500 Subject: [PATCH 082/159] Fixed Turtle Example --- .../10_Variables.ipynb | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb index 749c5f7e..30174a06 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb @@ -188,9 +188,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "49ac7f3dd29541a7b568e3909e92d313", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Canvas(height=250, width=750)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Run Me!\n", "\n", @@ -202,7 +217,7 @@ "\n", "# Variables to control the shape\n", "sides = 32 # Number of sides for the shape\n", - "size = 60 # Size of each side\n", + "size = 10 # Size of each side\n", "color = \"blue\" # Color of the shape\n", "\n", "# Set Tina's color using our variable\n", From ab91d7cb94c665ca0e474b1bf17a63fe617eaabd Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 21 Nov 2025 18:42:39 -0500 Subject: [PATCH 083/159] Fixed Turtle Example --- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 86a5f68d..424b9149 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -162,7 +162,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, Tina will move forward and turn left four times, drawing a square. The loop makes the code much cleaner and easier to understand!\n", + "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 can make your code much cleaner and easier to understand!\n", "\n", "
\n", " Click here for a detailed explanation\n", @@ -193,7 +193,9 @@ "\n", "Try running these examples to see how loops can be used in different ways!\n", "\n", - "### **Drawing an Orange Square**" + "### **Drawing an Orange Square**\n", + "\n", + "This is a simple example that uses a loop to draw an orange square." ] }, { @@ -233,7 +235,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### **Drawing a Checkerboard Pattern**" + "### **Drawing a Checkerboard Pattern**\n", + "\n", + "This is a slightly more complex example that uses nested loops to create a checkerboard pattern:" ] }, { @@ -283,7 +287,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### **Drawing an American Flag**" + "### **Drawing an American Flag**\n", + "\n", + "This is a more complex example that uses loops to draw an American flag with stars and stripes." ] }, { @@ -340,7 +346,9 @@ "- `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", - "- The `elif` statement checks if the current row is even or odd to alternate between red (`🟥`) and white (`⬜️`) stripes.\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", "
\n", From 9b7b1c3f02126d5156bbb748c688d1d8a250d4e7 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 21 Nov 2025 23:57:58 -0500 Subject: [PATCH 084/159] Updated 10_Lists.ipynb to make it more presentable, added runnable ipyturtle3 cells, etc. --- .../80_Introducting_Lists/10_Lists.ipynb | 421 +++++++++++------- .../80_Introducting_Lists/20_Color_Lines.py | 4 +- 2 files changed, 270 insertions(+), 155 deletions(-) diff --git a/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb b/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb index 7b52c860..28ec3d09 100644 --- a/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb +++ b/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb @@ -1,159 +1,274 @@ { - "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. " - ] - }, + "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", + "- `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", + "- slicing: `list[start:stop]` returns a sub-list" + ] + }, + { + "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, comprehensions, nested\n", + "lists, and more), but 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": 22, + "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 + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "7ef516263d774610a179a9b6563e1961", + "version_major": 2, + "version_minor": 0 }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - }, - "syllabus": { - "uid": "0KEhJUGe" + "text/plain": [ + "Canvas(height=250, width=750)" + ] + }, + "metadata": {}, + "output_type": "display_data" } + ], + "source": [ + "%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", + "\n", + "for left in [90, 90, 90, 90]:\n", + " tina.forward(forward)\n", + " tina.left(left)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Excercises (Optional)**\n", + "\n", + "If 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" }, - "nbformat": 4, - "nbformat_minor": 2 + "syllabus": { + "uid": "0KEhJUGe" + } + }, + "nbformat": 4, + "nbformat_minor": 2 } diff --git a/lessons/10_Turtles/80_Introducting_Lists/20_Color_Lines.py b/lessons/10_Turtles/80_Introducting_Lists/20_Color_Lines.py index 790be08f..01217c64 100644 --- a/lessons/10_Turtles/80_Introducting_Lists/20_Color_Lines.py +++ b/lessons/10_Turtles/80_Introducting_Lists/20_Color_Lines.py @@ -1,7 +1,7 @@ """ -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 From 0db03034608fb9c281697e47cd40c55b208e676c Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 22 Nov 2025 00:18:51 -0500 Subject: [PATCH 085/159] Updated .py assignment descriptions to match the others --- .../10_Turtles/90_Graphics_Projects/10_Flaming_Ninja_Star.py | 3 ++- lessons/10_Turtles/90_Graphics_Projects/20_Crazy_Spiral.py | 4 ++-- lessons/10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py | 2 +- lessons/10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py | 3 ++- 4 files changed, 7 insertions(+), 5 deletions(-) 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..9c0773d8 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,4 +1,5 @@ -"""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. 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..d3dfc781 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,8 @@ """ -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 """ ... # Copy code to make a turtle and set up the window 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..c609bec8 100644 --- a/lessons/10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py +++ b/lessons/10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py @@ -1,5 +1,5 @@ """ -Pentagon Crazy +# 30_Pentagon_Crazy.py This program already works. Run it, then change it to make it draw a different pattern. """ 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 4deb3b36..e783ad88 100644 --- a/lessons/10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py +++ b/lessons/10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py @@ -1,4 +1,5 @@ -"""Penta Spiral +""" +# 40_Turtle_Spiral.py This program already works. See if you can change it to make it draw a different pattern. """ From 2ae71eb4fc82900f543ca30261538f8b181f2e8b Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 24 Nov 2025 04:31:14 -0500 Subject: [PATCH 086/159] Fix 10_Lists.ipynb' --- .../80_Introducting_Lists/10_Lists.ipynb | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb b/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb index 28ec3d09..d50cb4f8 100644 --- a/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb +++ b/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb @@ -163,24 +163,9 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "7ef516263d774610a179a9b6563e1961", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Canvas(height=250, width=750)" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "%run .lib/auto_turtle.py # This just handles the general imports for you\n", "\n", From 9eb115ee23fd48c6b5e382f29ba78d273690f2d0 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 24 Nov 2025 10:53:32 -0500 Subject: [PATCH 087/159] Made the 'Seeking Additional Information' heading collapsable --- .../20_Introducing_Tina/20_What_Can_Tina_Do.ipynb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 dd2bc652..f189627f 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 @@ -33,7 +33,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## **Seeking Additional Information**\n", + "
Seeking Additional Information\n", "\n", "Learning to program with Python's Turtle graphics is fun and creative! One of the best habits you can develop is regularly consulting official documentation and tutorials. This helps you understand commands, discover new features, and solve problems more efficiently. Here’s a detailed guide on how to find and use Python Turtle documentation.\n", "\n", @@ -75,11 +75,13 @@ "\n", "> **Tip:** Whenever you’re unsure about a command or want to try something new, searching for documentation or examples is one of the best ways to learn and grow as a programmer. The more you explore, the more creative and confident you’ll become!\n", "\n", - "
\n", + "
\n", "
\n", " Click Here To View The Official Python Turtle Documentation\n", - " \n", + " \n", + "
\n", + "\n", "
" ] }, From 752e0cab94c2a465c5465eb5668fa73812cc7111 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 24 Nov 2025 14:15:18 -0500 Subject: [PATCH 088/159] Made a temporary folder to store my project ideas and progress --- .../Temp_Project_Ideas/Spin_The_Wheel.ipynb | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb 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..f4ade7de --- /dev/null +++ b/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb @@ -0,0 +1,75 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ad97bb84", + "metadata": {}, + "source": [ + "## **Spin The Wheel Game** [IN PROGRESS]\n", + "\n", + "Do you know someone who likes to watch game shows on TV? Have you ever wanted to create your own? Well, instead of physical, let's create a digital game that you can play on your computer!\n", + "\n", + "**Example:**\n", + "
\n", + "\n", + "Typically, 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", + "\n", + "Software Development is all about problem-solving and creativity. So, think about how you can make your game engaging and enjoyable for players.\n", + "\n", + "It also might be a pretty good time to introduce the concept of the Software Development Life Cycle (SDLC), let's take a look. " + ] + }, + { + "cell_type": "markdown", + "id": "e4249dc9", + "metadata": {}, + "source": [ + "
The Software Development Life Cycle (SDLC)\n", + "\n", + "The SDLC is a structured approach to software development that consists of several phases, including planning, design, implementation, testing, and deployment. It's a helpful framework to ensure that software is developed efficiently and meets the desired requirements.\n", + "\n", + "- **Planning:** This is where you would think about what features you want in your game, like the number of sections on the wheel, or maybe types of prizes, or even how players will interact with it on a logical level. Is the prize monetary, points-based, or something else? Maybe it's just some funny message?\n", + "
\n", + "\n", + "- **Design:** Sketch out how the game will flow, including user inputs and outputs.\n", + "
\n", + "- **Implementation:** Write the code to create the game using a programming language of your choice (e.g., Python, JavaScript).\n", + "
\n", + "- **Testing:** Play the game multiple times to ensure it works as expected and fix any bugs.\n", + "
\n", + "- **Deployment:** Share your game with friends or online for others to play.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "7177e4ee", + "metadata": {}, + "source": [ + "### **Assignment Requirements**\n", + "**__1)__** Create a list of prizes that players can win.\n", + "
\n", + "**__2)__** Use the `random` module to simulate the spinning of the wheel.\n", + "
\n", + "**__3)__** Allow players to spin the wheel multiple times.\n", + "
\n", + "**__4)__** Keep track of the prizes won by each player.\n", + "
\n", + "**__5)__** Display the total prizes won at the end of the game." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 4f80b70601733854e0927377e93f06efa28b1435 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 24 Nov 2025 14:23:04 -0500 Subject: [PATCH 089/159] Made a temporary folder to store my project ideas and progress UPDATED --- .../Temp_Project_Ideas/Spin_The_Wheel.ipynb | 26 +------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb b/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb index f4ade7de..6443c100 100644 --- a/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb +++ b/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb @@ -14,31 +14,7 @@ "\n", "Typically, 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", "\n", - "Software Development is all about problem-solving and creativity. So, think about how you can make your game engaging and enjoyable for players.\n", - "\n", - "It also might be a pretty good time to introduce the concept of the Software Development Life Cycle (SDLC), let's take a look. " - ] - }, - { - "cell_type": "markdown", - "id": "e4249dc9", - "metadata": {}, - "source": [ - "
The Software Development Life Cycle (SDLC)\n", - "\n", - "The SDLC is a structured approach to software development that consists of several phases, including planning, design, implementation, testing, and deployment. It's a helpful framework to ensure that software is developed efficiently and meets the desired requirements.\n", - "\n", - "- **Planning:** This is where you would think about what features you want in your game, like the number of sections on the wheel, or maybe types of prizes, or even how players will interact with it on a logical level. Is the prize monetary, points-based, or something else? Maybe it's just some funny message?\n", - "
\n", - "\n", - "- **Design:** Sketch out how the game will flow, including user inputs and outputs.\n", - "
\n", - "- **Implementation:** Write the code to create the game using a programming language of your choice (e.g., Python, JavaScript).\n", - "
\n", - "- **Testing:** Play the game multiple times to ensure it works as expected and fix any bugs.\n", - "
\n", - "- **Deployment:** Share your game with friends or online for others to play.\n", - "
" + "Software Development is all about problem-solving and creativity. So, think about how you can make your game engaging and enjoyable for players." ] }, { From ba435f6e4f01707817d122a69ea54792ca3db374 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 28 Nov 2025 18:38:17 -0500 Subject: [PATCH 090/159] Made 10_More_Turtle_Programs.ipynb clearer --- .../10_More_Turtle_Programs.ipynb | 200 ++++++++++-------- .../40_More_Turtle_Programs.py | 2 +- .../Temp_Project_Ideas/Spin_The_Wheel.ipynb | 51 ----- 3 files changed, 109 insertions(+), 144 deletions(-) delete mode 100644 lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb 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 31435930..d7c34b5c 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 @@ -43,41 +43,38 @@ "metadata": {}, "source": [ "```python\n", - "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", + "# Double-click to copy!\n", "\n", - "import turtle # For turtle graphics\n", + "import turtle\n", "\n", - "# Function to set the turtle's image\n", - "def set_turtle_image(t, image_name): # create two parameters: the turtle and the image name\n", + "def set_turtle_image(turtle, image_name):\n", " \"\"\"Set the turtle's shape to a custom image.\"\"\"\n", "\n", - " from pathlib import Path # For handling file paths\n", - " image_dir = Path(__file__).parent.parent / \"images\" # Set the image directory to the parent images folder\n", - " image_path = str(image_dir / image_name) # Full path to the image\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 = t.getscreen() # Get the turtle's screen\n", - " screen.addshape(image_path) # Add the image as a shape\n", - " t.shape(image_path) # Set the turtle's shape to the image\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\n", - "screen = turtle.Screen() # Get the screen\n", - "screen.setup(width=600, height=600) # Set the screen size\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() # Create a turtle named t\n", - "set_turtle_image(t, 'pikachu.gif') # Set turtle to Pikachu image\n", - "\n", - "# Move the turtle around\n", - "t.penup() # Don't draw when moving\n", - "t.speed(3) # Set a moderate speed\n", - "for i in range(4): # Move in a figure eight\n", - " t.goto(-200, -200) # Move to bottom-left corner\n", - " t.goto(200, -200) # Move to bottom-right corner\n", - " t.goto(-200, 200) # Move to top-left corner\n", - " t.goto(200, 200) # Move to top-right corner\n", - "t.goto(0, 0) # Return to center\n", - "\n", - "turtle.exitonclick() # Wait for a click to close the window once done\n", + "t = turtle.Turtle()\n", + "\n", + "set_turtle_image(t, \"pikachu.gif\")\n", + "\n", + "t.penup() # Prevent drawing when moving\n", + "t.speed(3) # Set a moderate speed\n", + "\n", + "for i in range(4):\n", + " t.goto(200, 200)\n", + " t.goto(-200, -200)\n", + "\n", + "turtle.exitonclick() \n", "```" ] }, @@ -95,7 +92,9 @@ "## **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." + "*images* folder next to your program.\n", + "\n", + "#### Double-click this code to copy it!" ] }, { @@ -103,32 +102,31 @@ "metadata": {}, "source": [ "```python\n", - "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", + "# Double-click to copy!\n", + "\n", "import turtle\n", "\n", - "# Function to set the background image\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", - " from pathlib import Path # For working with file paths\n", - " from PIL import Image # For working with images\n", - "\n", - " image_dir = Path(__file__).parent.parent / \"images\" # Get the images directory from the parent folder\n", - " image_path = str(image_dir / image_name) # Create the full path to the image file\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 file\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 the window size to match the image size\n", - " window.bgpic(image_path) # Set the background image of the window\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", - "# Set up the screen\n", - "turtle.setup(width=600, height=600) # Set the size of the window\n", - "t = turtle.Turtle() # Create a turtle named t\n", - "screen = turtle.Screen() # Get the screen that t is on\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", - "set_background_image(screen, \"emoji.png\") # Set the background image of the screen\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() # Wait for a click to close the window once done\n", + "turtle.exitonclick() \n", "```" ] }, @@ -138,7 +136,9 @@ "source": [ "## **More Than One Turtle**\n", "\n", - "You can put multiple turtles on the same screen and control each independently." + "You can put multiple turtles on the same screen and control each independently.\n", + "\n", + "#### Double-click this code to copy it!" ] }, { @@ -146,27 +146,26 @@ "metadata": {}, "source": [ "```python\n", - "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", + "# Double-click to copy!\n", "\n", - "import turtle # For turtle graphics\n", + "import turtle as turtle # Import the turtle module with the name turtle\n", "\n", "screen = turtle.Screen() # Set up the screen\n", - "screen.setup(width=600, height=600) # Set the screen size\n", + "screen.setup(width=600, height=600) # Set the size of the window\n", "screen.bgcolor('white') # Set the background color\n", "\n", "t1 = turtle.Turtle() # Create the first turtle\n", - "t1.penup() # Lift the pen\n", - "t1.shape('turtle') # Set the shape to 'turtle'\n", + "t1.penup() # Lift the pen to move without drawing\n", + "t1.shape(\"turtle\") # Set the shape of the turtle\n", "\n", "t2 = turtle.Turtle() # Create the second turtle\n", - "t2.penup() # Lift the pen\n", - "t2.shape('arrow') # Set the shape to 'arrow'\n", - "\n", - "for i in range(-200, 200): # Move both turtles in a loop\n", - " t1.goto(i, i) # Move the first turtle\n", - " t2.goto(i, -i) # Move the second turtle\n", + "t2.penup() # Lift the pen to move without drawing\n", + "t2.shape(\"arrow\") # Set the shape of the turtle\n", "\n", - "turtle.exitonclick() # Wait for a click to close the window\n", + "# Move both turtles in a loop\n", + "for i in range(-200, 200):\n", + " t1.goto(i, i)\n", + " t2.goto(i, -i)\n", "```" ] }, @@ -184,26 +183,30 @@ "metadata": {}, "source": [ "```python\n", - "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", + "# Double-click to copy!\n", "\n", - "import turtle # For turtle graphics\n", + "import turtle as turtle\n", "\n", - "screen = turtle.Screen() # Set up the screen\n", - "screen.setup(width=600, height=600) # Set the screen size\n", - "screen.bgcolor('white') # Set the background color\n", + "screen = turtle.Screen() # Set up the screen\n", + "screen.setup(width=600, height=600) # Set the size of the window\n", + "screen.bgcolor('white') # Set the background color\n", "\n", - "t = turtle.Turtle() # Create a turtle\n", - "t.penup() # Lift the pen\n", - "t.shape('turtle') # Set the shape to 'turtle'\n", + "t = turtle.Turtle() # Create a turtle\n", + "t.penup() # Prevent drawing when moving\n", + "t.shape(\"turtle\") # Set the shape of the turtle\n", "\n", - "# Function to handle screen clicks\n", + "# This is the function that gets called when you click on the screen\n", "def screen_clicked(x, y):\n", - " \"\"\"Called when the screen is clicked. Prints the coordinates and moves the turtle.\"\"\"\n", - " print(f'You pressed: x={x}, y={y}') # Print the coordinates\n", - " t.goto(x, y) # Move the turtle to the clicked position\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", - "screen.onclick(screen_clicked) # Tell Python which function to call when the screen is clicked\n", - "turtle.done() # Use `done()` (keeps the window open)\n", + " t.goto(x, y) # Move the turtle to the clicked location\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", "```" ] }, @@ -214,7 +217,9 @@ "## **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." + "function with the click coordinates and the turtle object.\n", + "\n", + "#### Double-click this code to copy it!" ] }, { @@ -222,30 +227,39 @@ "metadata": {}, "source": [ "```python\n", - "# DOUBLE-CLICK TO COPY THIS EXAMPLE\n", + "# Double-click to copy!\n", "\n", - "import turtle # For turtle graphics\n", + "import turtle as turtle # Import the turtle module with the name turtle\n", "\n", - "screen = turtle.Screen() # Set up the screen and window\n", - "screen.setup(width=600, height=600)\n", - "screen.bgcolor('white') # Optional background color\n", + "screen = turtle.Screen() # Set up the screen\n", + "screen.setup(width=600, height=600) # Set the size of the window\n", + "screen.bgcolor('white') # Set the background color\n", "\n", - "t = turtle.Turtle() # Create a turtle\n", - "t.penup() # Don't draw when moving\n", - "t.shape('turtle') # Use turtle shape\n", - "t.turtlesize(stretch_wid=10, stretch_len=10, outline=4) # Make the turtle large\n", + "t = turtle.Turtle() # Create a turtle\n", + "t.shape(\"turtle\") # Set the shape of the turtle\n", + "t.turtlesize(stretch_wid=10, stretch_len=10, outline=4) # Make the turtle really big\n", "\n", - "# Function to handle turtle clicks\n", "def turtle_clicked(t, x, y):\n", - " \"\"\"Called when the user clicks the turtle. Tilts the turtle in a circle.\"\"\"\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", - " for _ in range(18): # Tilt the turtle in a circle\n", - " t.tilt(20) # 18 * 20 = 360 degrees\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 handler (use a lambda to pass `t`)\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() # Keep the window open\n", + "turtle.done() # Important! Use `done` not `exitonclick` to keep the window open\n", "```" ] }, @@ -257,15 +271,17 @@ "
\n", " Click Here For Further Explanation\n", "\n", - "When your program creates more than one turtle, the click handler needs to know which turtle was clicked — not just the click coordinates.\n", - "\n", - "The pattern `t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y))` creates a tiny anonymous function (a `lambda`) that accepts the click coordinates `x` and `y` and also captures the current turtle by using the default parameter `t=t`.\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", - "Why `t=t`? Without it the handler would look up the variable `t` later (after the loop finishes) and often end up referring to the last turtle created. Using `t=t` binds the current turtle to the handler immediately so each turtle passes itself correctly.\n", + "```python\n", + "# Lambda function\n", + "t.onclick(lambda x, y, t=t: turtle_clicked(t, x, y))\n", + "```\n", "\n", - "If you prefer a named function instead of a `lambda`, you can achieve the same result by returning a handler that closes over `t`:\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", @@ -274,7 +290,7 @@ "t.onclick(make_handler(t))\n", "```\n", "\n", - "Both approaches ensure the handler has a reference to the specific turtle that was clicked. Copy whichever pattern you find clearer into your programs.\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", "
" ] }, 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 aad360d0..e9f595b8 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, 10_More_Turtle_programs.ipynb, -from the section " Clicking the Turtle Directly" +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. diff --git a/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb b/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb deleted file mode 100644 index 6443c100..00000000 --- a/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb +++ /dev/null @@ -1,51 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "ad97bb84", - "metadata": {}, - "source": [ - "## **Spin The Wheel Game** [IN PROGRESS]\n", - "\n", - "Do you know someone who likes to watch game shows on TV? Have you ever wanted to create your own? Well, instead of physical, let's create a digital game that you can play on your computer!\n", - "\n", - "**Example:**\n", - "
\n", - "\n", - "Typically, 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", - "\n", - "Software 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", - "**__1)__** Create a list of prizes that players can win.\n", - "
\n", - "**__2)__** Use the `random` module to simulate the spinning of the wheel.\n", - "
\n", - "**__3)__** Allow players to spin the wheel multiple times.\n", - "
\n", - "**__4)__** Keep track of the prizes won by each player.\n", - "
\n", - "**__5)__** Display the total prizes won at the end of the game." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "name": "python", - "version": "3.13.5" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From 7752784456b6f31b4df555ba05b177878caabef9 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 29 Nov 2025 03:22:32 -0500 Subject: [PATCH 091/159] Working on 10_Numbers_and_Strings.ipynb' -- Trying to make it clearer --- .../10_Numbers_and_Strings.ipynb | 1187 +++++++++-------- 1 file changed, 612 insertions(+), 575 deletions(-) diff --git a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb b/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb index aa428552..8f086a59 100644 --- a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb +++ b/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb @@ -1,577 +1,614 @@ { - "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 == \" , 10 % 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 + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Numbers and Strings**\n", + "\n", + "In this lesson you'll explore how Python represents numbers and text, how to convert between them, and some useful operators and methods.\n", + "\n", + "
\n", + " Click here for a friendly reminder about Notebooks\n", + "\n", + "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", + "* Press esc to enter Command Mode if you want to move, copy, or delete cells (the blue outline disappears in Command Mode).\n", + "* When a cell is active you'll see a small menu in the upper-right with extra options.\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)` or `Python 3.13.5`).\n", + "\n", + "\n", + "\n", + "After selecting the interpreter, re-run the cell. \n", + "\n", + "Ask your instructor if you are still confused or need help connecting the kernel.\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Getting Started**\n", + "\n", + "You've already practiced with variables previously, but in this lesson, we'll look at variables in a bit more detail and explore the different types that are used in Python. \n", + "\n", + "In need of a refresher? That's okay, just go back and check out the [Variables lesson](../10_Turtles/50_Variables_and_Functions/10_Variables.ipynb).\n", + "\n", + "If you are ready to dive in, run the code below and let's get started!" + ] + }, + { + "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, etc. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "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 are whole numbers without decimals, like `3`\n", + "* floats are real numbers that can have a decimal part, like `3.5`\n", + "* strings are text data that is **immutable**, like `\"Alice\"`\n", + "* lists are ordered sequences that are **mutable**, like `['red', 'blue']`\n", + "\n", + "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", + "\n", + "Different types support different operations — for example, `+` performs arithmetic on numbers but concatenation on strings.\n", + "\n", + "Lets 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\n", + "x = 10\n", + "y = 20\n", + "\n", + "# Add x and y together\n", + "print(\"x + y =\", x + y)\n", + "\n", + "# Strings\n", + "x = \"10\"\n", + "y = \"20\"\n", + "\n", + "# Add x and y together\n", + "print(\"x + y =\", x + y) # Watch what happens!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you might have expected, when we added $10$ and $20$ as integers we got $30$, but when we added them as strings, Python concatenated them together instead!\n", + "\n", + "So 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 and didn't work because you can't add an integer to a string! But you *can* convert one to the other data type and then add them. \n", + "\n", + "### **Type Conversion**\n", + "To convert these variables, we will use both the `int()` and `str()` functions. " + ] + }, + { + "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(int(y) + x)\n", + "\n", + "# Convert x to a string, then add x and y together\n", + "print(str(x) + y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> **Tip:** Conversion functions have the same name as the type they convert to, this will help you remember them!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Numbers**\n", + "\n", + "Python has two main numeric types, integers (`int`) for whole numbers and floating-point numbers (`float`) for values with a fractional part (e.g., like a decimal: $3.14$).\n", + "\n", + "Integers can be written in four different bases:\n", + "\n", + "* **Binary (Base 2):** $0b100101$\n", + "* **Octal (Base 8):** $0o45$\n", + "* **Decimal (Base 10):** $37$\n", + "* **Hexadecimal (Base 16):** $0x25$\n", + "\n", + "For example, the decimal number $237$ equals $(2 \\times 100) + (3 \\times 10) + (7 \\times 1)$ — that's $2$ hundreds, $3$ tens, and $7$ ones.\n", + "\n", + "Other number bases work the same way, but they use different place values.\n", + "\n", + "This diagram shows how these bases relate to each other:\n", + "\n", + "\"Number\n", + "\n", + "If 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)" + ] + }, + { + "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 = 255 # 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$.\n", + "\n", + "Or in simple terms, it just means one followed by six zeros!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Operators**\n", + "\n", + "Below are some common math operators for numbers—you’ve likely used most of them, though a couple might be new.\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 |\n", + "\n", + "The `//` operator divides and drops the fractional part, returning an integer when used with integers.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Regular division will result in a float\n", + "\n", + "print(10 / 3)\n", + "\n", + "# Floor division is always an integer\n", + "\n", + "print(10 // 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The ` % ` (modulo) operator returns the remainder after division. It's useful for checking evenly divisible numbers (e.g., `n % 3 == 0`).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Modulo Operator\n", + "\n", + "print(\"9 % 3 == \" , 10 % 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", + "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 = '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": [ + "### **Candy Distribution**\n", + "\n", + "Start with two variables: `kids` (number of kids) and `candy_bars` (number of candy bars). Calculate how to evenly distribute the candy bars to the kids — print how many each kid gets and how many are left over.\n" + ] + }, + { + "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", + "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.\n" + ] + }, + { + "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. See the Code Check In Howto Guide for instructions on committing and restarting your Codespace:\n", + "\n", + "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 } From 6600eccf0239c24ea77d2e748c3a0e6df6d6a83f Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 29 Nov 2025 20:12:34 -0500 Subject: [PATCH 092/159] Cleaned up '30_Run_Programs.ipynb' to make the images scale better --- .../10_Welcome/30_Run_Programs.ipynb | 8 ++++---- .../10_Numbers_and_Strings.ipynb | 19 +++++++++++++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index e1eb6154..bcb34a62 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -42,7 +42,7 @@ "source": [ "Here’s what the code cell looks like with the run button. Try it out!\n", "\n", - "" + "" ] }, { @@ -59,11 +59,11 @@ "\n", "This should have made a dropdown appear in the top-middle of your editor, as shown below:\n", "\n", - "\n", + "\n", "\n", "Now all you need to do is 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", "\n", "> **Note:** You’ll need to pick a Python interpreter every time you open a new notebook." @@ -118,7 +118,7 @@ "\n", "In the bottom-left, you’ll see buttons like this:\n", "\n", - "\n", + "\n", "\n", "Here’s how it works, step by step:\n", "- Click ▶ Run to start your program\n", diff --git a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb b/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb index 8f086a59..40837535 100644 --- a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb +++ b/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb @@ -52,9 +52,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Name: John\n", + "Age: 14\n", + "Bank Account: 159.99\n", + "Colors: ['red', 'blue', 'green']\n" + ] + } + ], "source": [ "# Run Me!\n", "\n", @@ -248,7 +259,7 @@ "source": [ "# Conversion functions\n", "\n", - "number = 255 # You can change this number to test with other values\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", From f82b0bbd7ffe654b7f2b59e661c2c7d70c56d9b5 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 1 Dec 2025 10:32:33 -0500 Subject: [PATCH 093/159] Cleaned up '30_Run_Programs.ipynb' to make the images scale better --- lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index bcb34a62..bfab101f 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -118,7 +118,7 @@ "\n", "In the bottom-left, you’ll see buttons like this:\n", "\n", - "\n", + "\n", "\n", "Here’s how it works, step by step:\n", "- Click ▶ Run to start your program\n", From 812d3da3dc406ae7283903ac46fcfdda99ec4cb3 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 1 Dec 2025 11:36:29 -0500 Subject: [PATCH 094/159] Cleaned up '10_Numbers_and_Strings.ipynb' + fixed an issue in '20_What_Can_Tina_Do.ipynb' --- .../20_What_Can_Tina_Do.ipynb | 12 ++-- .../10_Numbers_and_Strings.ipynb | 64 +++++++++---------- .../Temp_Project_Ideas/Spin_The_Wheel.ipynb | 51 +++++++++++++++ 3 files changed, 89 insertions(+), 38 deletions(-) create mode 100644 lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb 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 f189627f..34bebdde 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 @@ -75,12 +75,12 @@ "\n", "> **Tip:** Whenever you’re unsure about a command or want to try something new, searching for documentation or examples is one of the best ways to learn and grow as a programmer. The more you explore, the more creative and confident you’ll become!\n", "\n", - "
\n", - "
\n", - " Click Here To View The Official Python Turtle Documentation\n", - " \n", - "
\n", + "
\n", + "
\n", + " Click Here To View The Official Python Turtle Documentation\n", + " \n", + "
\n", "\n", "" ] diff --git a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb b/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb index 40837535..5b74cb2b 100644 --- a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb +++ b/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb @@ -228,13 +228,20 @@ "* **Decimal (Base 10):** $37$\n", "* **Hexadecimal (Base 16):** $0x25$\n", "\n", - "For example, the decimal number $237$ equals $(2 \\times 100) + (3 \\times 10) + (7 \\times 1)$ — that's $2$ hundreds, $3$ tens, and $7$ ones.\n", + "For example, the decimal number $237$ equals $(2 \\times 100) + (3 \\times 10) + (7 \\times 1)$ — that's $2$ hundreds, $3$ tens, and $7$ ones, or in exponents, its $(2 \\times 10^2) + (3 \\times 10^1) + (7 \\times 10^0)$.\n", "\n", "Other number bases work the same way, but they use different place values.\n", "\n", "This diagram shows how these bases relate to each other:\n", "\n", - "\"Number\n", + "\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", "\n", "If you'd like more explanation on number systems, these **Khan Academy** videos are helpful:\n", "\n", @@ -311,13 +318,11 @@ "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$.\n", + "* **Decimal:** $1000000$\n", + "* **Underscores:** $1\\_000\\_000$\n", + "* **Scientific Notation:** $1e6$\n", "\n", - "Or in simple terms, it just means one followed by six zeros!" + "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!" ] }, { @@ -330,15 +335,15 @@ "\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 |\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$ |\n", "\n", - "The `//` operator divides and drops the fractional part, returning an integer when used with integers.\n" + "The `//` operator divides and drops the fractional part, which means it returns an integer when used with integers." ] }, { @@ -385,9 +390,7 @@ "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. " + "An interesting thing about the `//` and `%` operators is that they are closely related. They allow you to break down a number into parts that can be reassembled using a simple equation." ] }, { @@ -428,12 +431,9 @@ "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", + ">**Tip:** Modulo sort of works like a clock: the result goes around to 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``." + "The most common use for the modulo operator is to check if a number is evenly divisible by another number. If the remainder is 0, it divides evenly. For example, 6 is evenly divisible by 3 because ``6 % 3 == 0``." ] }, { @@ -585,17 +585,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## **Check In Your Code**\n", + "
\n", "\n", - "Don't forget to check in your code. See the Code Check In Howto Guide for instructions on committing and restarting your Codespace:\n", + "**REMINDER:** Don't forget to check in your code! If you need a refresher, review the Check in Code and Restart Codespaces guide or revisit the previous Check In Your Code lesson.\n", + "
\n", "\n", - "https://curriculum.jointheleague.org/howto/checkin_restart.html\n" + "
\n", + "REMINDER: \n", + "\n", + "Don't forget to check in your code! If you need a refresher, review the Check in Code and Restart Codespaces guide or revisit the previous Check In Your Code lesson.\n", + "
" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] } ], "metadata": { 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..6443c100 --- /dev/null +++ b/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb @@ -0,0 +1,51 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ad97bb84", + "metadata": {}, + "source": [ + "## **Spin The Wheel Game** [IN PROGRESS]\n", + "\n", + "Do you know someone who likes to watch game shows on TV? Have you ever wanted to create your own? Well, instead of physical, let's create a digital game that you can play on your computer!\n", + "\n", + "**Example:**\n", + "
\n", + "\n", + "Typically, 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", + "\n", + "Software 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", + "**__1)__** Create a list of prizes that players can win.\n", + "
\n", + "**__2)__** Use the `random` module to simulate the spinning of the wheel.\n", + "
\n", + "**__3)__** Allow players to spin the wheel multiple times.\n", + "
\n", + "**__4)__** Keep track of the prizes won by each player.\n", + "
\n", + "**__5)__** Display the total prizes won at the end of the game." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 89d29f3f33cd3bbaa66851269a8fb71cc057926b Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 1 Dec 2025 11:40:26 -0500 Subject: [PATCH 095/159] Turned check in your code reminder into an 'alert-info' div --- .../20_Types_and_Logic/10_Numbers_and_Strings.ipynb | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb b/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb index 5b74cb2b..e2a51fe5 100644 --- a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb +++ b/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb @@ -585,15 +585,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "
\n", - "\n", - "**REMINDER:** Don't forget to check in your code! If you need a refresher, review the Check in Code and Restart Codespaces guide or revisit the previous Check In Your Code lesson.\n", - "
\n", - "\n", - "
\n", - "REMINDER: \n", - "\n", - "Don't forget to check in your code! If you need a refresher, review the Check in Code and Restart Codespaces guide or revisit the previous Check In Your Code lesson.\n", + "
\n", + "REMINDER: Don't forget to 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", "
" ] } From 4a2050509e0efbf4d4f5a0c31b9859ff8385dae6 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 1 Dec 2025 11:51:49 -0500 Subject: [PATCH 096/159] Added the start of Terminal_Game.ipynb project --- .../Temp_Project_Ideas/Terminal_Game.ipynb | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 lessons/50_Projects/Temp_Project_Ideas/Terminal_Game.ipynb 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 +} From f31808d6908a7b85d812addcbba429e812660fb2 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 1 Dec 2025 11:53:21 -0500 Subject: [PATCH 097/159] Fixed 30_Run_Programs.ipynb warning + added a table to 10_Numbers_and_Strings.ipynb --- .../10_Welcome/30_Run_Programs.ipynb | 4 ++-- .../10_Variables.ipynb | 19 ++----------------- .../10_Numbers_and_Strings.ipynb | 19 ++++--------------- .../Temp_Project_Ideas/Spin_The_Wheel.ipynb | 16 ++++++---------- 4 files changed, 14 insertions(+), 44 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index bfab101f..fd75e779 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -152,9 +152,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "
\n", + "
\n", "\n", - "**WARNING:** **If a program is already running, you must close the program before starting another one.**\n", + "WARNING: If a program is already running, you must close the program before starting another one.\n", "\n", "You have two options to close it:\n", "
\n", diff --git a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb index 30174a06..b226ed16 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb @@ -188,24 +188,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "49ac7f3dd29541a7b568e3909e92d313", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Canvas(height=250, width=750)" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Run Me!\n", "\n", diff --git a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb b/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb index e2a51fe5..fb54926b 100644 --- a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb +++ b/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb @@ -52,20 +52,9 @@ }, { "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Name: John\n", - "Age: 14\n", - "Bank Account: 159.99\n", - "Colors: ['red', 'blue', 'green']\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Run Me!\n", "\n", @@ -585,7 +574,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "
\n", + "
\n", "REMINDER: Don't forget to 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", "
" ] diff --git a/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb b/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb index 6443c100..b8575113 100644 --- a/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb +++ b/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb @@ -5,7 +5,7 @@ "id": "ad97bb84", "metadata": {}, "source": [ - "## **Spin The Wheel Game** [IN PROGRESS]\n", + "## **Spin The Wheel Game**\n", "\n", "Do you know someone who likes to watch game shows on TV? Have you ever wanted to create your own? Well, instead of physical, let's create a digital game that you can play on your computer!\n", "\n", @@ -23,15 +23,11 @@ "metadata": {}, "source": [ "### **Assignment Requirements**\n", - "**__1)__** Create a list of prizes that players can win.\n", - "
\n", - "**__2)__** Use the `random` module to simulate the spinning of the wheel.\n", - "
\n", - "**__3)__** Allow players to spin the wheel multiple times.\n", - "
\n", - "**__4)__** Keep track of the prizes won by each player.\n", - "
\n", - "**__5)__** Display the total prizes won at the end of the game." + "- 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." ] } ], From 566de82d55b9084de47d7472866dc229dc1f61b0 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 4 Dec 2025 21:28:38 -0500 Subject: [PATCH 098/159] =?UTF-8?q?Updated=20'20=5FControl=5FFlow.ipynb'?= =?UTF-8?q?=20for=20added=20clarity=20=E2=80=94=20this=20is=20a=20work=20i?= =?UTF-8?q?n=20progress?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20_Types_and_Logic/20_Control_Flow.ipynb | 154 +++++++++++------- .../Temp_Project_Ideas/Spin_The_Wheel.ipynb | 8 + 2 files changed, 107 insertions(+), 55 deletions(-) diff --git a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb index a9f38e5b..b3c86336 100644 --- a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb +++ b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb @@ -4,10 +4,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Control Flow\n", + "# **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" + "Programs are much more useful when they can make decisions. In Python, we make decisions using `if` statements.\n", + "These types of instructions are often called \"conditionals,\" \"control flow,\" or \"branching.\"\n", + "\n", + "Here is an example:" ] }, { @@ -16,6 +18,8 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "# Conditionals \n", "\n", "a = 10\n", @@ -34,13 +38,9 @@ "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", + "In the `if` statements above, what comes after the `if` keyword is an expression that evaluates to a Boolean. This simply means that our code is checking *if the expression* `a == 11:` *evaluates to* `True`*, and if it does, it tells the kernel to run the indented code below it.*\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" + "Let's evaluate the conditional expressions in the code above:" ] }, { @@ -49,7 +49,9 @@ "metadata": {}, "outputs": [], "source": [ - "# Evaluating boolean expressions. \n", + "# Run Me!\n", + "\n", + "# Evaluating boolean expressions\n", "print(\"a == 11:\", a == 11)\n", "print(\"a == 10:\", a == 10)\n", "print(\"a < 20:\", a < 20)" @@ -59,19 +61,28 @@ "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", + "Notice that we use *two* equal signs (`==`) for conditional expressions!\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", + "| Operator | Purpose |\n", + "| :------- | :------: | \n", + "| $=$ | Assigns a value to a variable. | \n", + "| $==$ | Checks if two things are equal. | \n", "\n", - "Don't forget! This is a very common error. \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 a very common error, so don't feel discouraged. Try your best to memorize the differences!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\n", - "## Test Yourself\n", + "### **Test Yourself**\n", "\n", - "Add code to determine if the statements in the comments are True or False" + "Add code to determine if the statements in the comments are `True` or `False`." ] }, { @@ -111,11 +122,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## And and Or\n", + "## **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", + "What if you want to do something only if `a` is equal to 10 **and** `b` is equal to 15? In that case, you can use the logical operators `and` & `or`.\n", "\n", - "```python \n", + "* The `and` operator returns `True` only when **both** sides are `True`.\n", + "* The `or` operator returns `True` if **either** side is `True`.\n", + "\n", + "For example:\n", + "\n", + "```python\n", "True and True == True\n", "True and False == False\n", "False and False == False\n", @@ -125,13 +141,13 @@ "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", + "You'll probably never write code exactly like the above, but you might write something like this:\n", "\n", - "```python \n", + "```python\n", "if a == 5 and s.startswith('hello'):\n", " print(\"yeah!\")\n", "\n", - "# or \n", + "# or\n", "\n", "if time > \"11:30\" and time < \"12:30\":\n", " print(\"Lunch time!\")\n", @@ -142,14 +158,32 @@ " print(\"I am asleep\")\n", "```\n", "\n", - "## Test Yourself" + "> **Note:** There are functions in Python like `startswith` and `endswith` that can help you check parts of strings!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Test Yourself**\n", + "\n", + "Determine if the commented statements are `True` or `False`." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Ellipsis\n", + "Ellipsis\n" + ] + } + ], "source": [ "# Test Yourself\n", "\n", @@ -158,20 +192,22 @@ "c = '30'\n", "s = 'hello world'\n", "\n", + "# Set a_is_10 to true if `a` is equal to 10\n", "\n", - "# set a_is_10 to be true if a is equal to 10\n", - "\n", - "a_is_10 = (a == 10)\n", + "if (a == 10):\n", + " a_is_10 = True\n", + "#a_is_10 = (a == 10)\n", "\n", - "# Set last_is_world to be true if the last world in s is 'world'\n", + "# Set last_is_world to be true if the last word in `s` is 'world'\n", "\n", - "last_is_world = ...\n", + "if (s.endswith('world') == True):\n", + " last_is_world = True\n", "\n", - "# set last_is_hello to be true if the last world in s is 'hello'\n", + "# Set last_is_hello to be true if the last word 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", + "# If a is 10 and the last word in s is 'world' print 'success'\n", "\n", "if ...:\n", " print(...)\n", @@ -187,24 +223,32 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Else and Else if\n", + "## **Else / Elif (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", + "none of the `if` or `elif` conditions are `True`.\n", "\n", - "```python \n", - "\n", - "# Example of if-else\n", + "#### **Examples**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Using if/else Statements
\n", "\n", + "```python \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", "\n", - "# Example of if-elif-else\n", + "
Using if/elif/else Statements
\n", "\n", + "```python\n", "maybe_this = ... \n", "maybe_that = ...\n", "\n", @@ -216,10 +260,14 @@ " ...\n", "else:\n", " # do this if maybe_this == False and maybe_that == False\n", - " \n", - "```\n", - "\n", - "## Test Yourself\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Test Yourself**\n", "\n", "Write a program that sets a variable `fb` to a number, then write a conditional that\n", "\n", @@ -227,7 +275,7 @@ "* 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`. " + "Test your program with different values of `fb`." ] }, { @@ -254,12 +302,11 @@ "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", + "Here is a common way of using `if`/`elif`/`else` blocks to break a continuous value (like the amount of water in a cup) into categories.\n", "\n", - "You might also use this method for converting an age into an age range. " + "In this example, each category is larger than the one before it. The program checks conditions in order and will execute the block for the **first** condition that is true. This effectively finds the smallest category that fits.\n", + "\n", + "You might also use this method for things like converting a numerical score into a letter grade or an age into an age group." ] }, { @@ -268,6 +315,8 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "def convert_ml_to_imperial(ml):\n", " # Conversion values\n", " teaspoon_ml = 4.92892\n", @@ -298,11 +347,6 @@ "# Call the function to get the Imperial measure\n", "convert_ml_to_imperial(ml)" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] } ], "metadata": { diff --git a/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb b/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb index b8575113..b50c0675 100644 --- a/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb +++ b/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb @@ -38,7 +38,15 @@ "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" } }, From a04e7ac6b0a9fea2edc2696c31a2624afd41f833 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 4 Dec 2025 21:51:10 -0500 Subject: [PATCH 099/159] Updated a note --- .../20_Types_and_Logic/20_Control_Flow.ipynb | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb index b3c86336..d753cd0f 100644 --- a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb +++ b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb @@ -126,39 +126,43 @@ "\n", "What if you want to do something only if `a` is equal to 10 **and** `b` is equal to 15? In that case, you can use the logical operators `and` & `or`.\n", "\n", - "* The `and` operator returns `True` only when **both** sides are `True`.\n", - "* The `or` operator returns `True` if **either** side is `True`.\n", + "\n", + "| Operator | Purpose |\n", + "| :------- | :------: |\n", + "| `and` | Returns `True` only if *both* sides are `True`. |\n", + "| `or` | Returns `True` if *either* side is `True*. |\n", "\n", "For example:\n", "\n", "```python\n", - "True and True == True\n", - "True and False == False\n", - "False and False == False\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", + "```\n", "\n", - "True or True == True\n", - "True or False == True\n", - "False or False == False\n", + "```python\n", + "True or True == True # Both sides are True\n", + "True or False == True # One side is True\n", + "False or False == False # Both sides are False\n", "```\n", "\n", "You'll probably never write code exactly like the above, but you might write something like this:\n", "\n", "```python\n", + "# Using `and` to check if both conditions are true\n", "if a == 5 and s.startswith('hello'):\n", " print(\"yeah!\")\n", "\n", - "# or\n", - "\n", + "# Using `and` to check if both conditions are true\n", "if time > \"11:30\" and time < \"12:30\":\n", " print(\"Lunch time!\")\n", "\n", - "# or\n", - "\n", + "# Using `or` to check if either condition is true\n", "if time < \"07:00\" or time > \"22:00\":\n", " print(\"I am asleep\")\n", "```\n", "\n", - "> **Note:** There are functions in Python like `startswith` and `endswith` that can help you check parts of strings!" + "> **Note:** There are so many useful methods in Python like `startswith` or `endswith` for checking parts of strings, and `in` 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!" ] }, { From 7959103f92a7d1fde53869c82829abc02215cc1e Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 5 Dec 2025 18:49:58 -0500 Subject: [PATCH 100/159] Separated several markdown cells to enhance student comprehension, converted code blocks into runnable python cells and slightly modified their syntax for clarity --- .../20_Types_and_Logic/20_Control_Flow.ipynb | 238 ++++++++++++------ 1 file changed, 156 insertions(+), 82 deletions(-) diff --git a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb index d753cd0f..2088f558 100644 --- a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb +++ b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb @@ -6,10 +6,9 @@ "source": [ "# **Control Flow**\n", "\n", - "Programs are much more useful when they can make decisions. In Python, we make decisions using `if` statements.\n", - "These types of instructions are often called \"conditionals,\" \"control flow,\" or \"branching.\"\n", + "Programs become significantly more useful when they can make decisions, and in Python, we canuse `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", - "Here is an example:" + "Let's look at this example below:" ] }, { @@ -24,13 +23,13 @@ "\n", "a = 10\n", "\n", - "if a == 11:\n", - " print(\"a is 11\")\n", + "if a == 11: # Check if `a` is identical to 11\n", + " print(\"a is 11\") \n", "\n", - "if a == 10:\n", + "if a == 10: # Check if `a` is identical to 10\n", " print(\"a is 10\")\n", "\n", - "if a < 20:\n", + "if a < 20: # Check if `a` is less than 20\n", " print(\"a is less than 20\")" ] }, @@ -38,7 +37,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In the `if` statements above, what comes after the `if` keyword is an expression that evaluates to a Boolean. This simply means that our code is checking *if the expression* `a == 11:` *evaluates to* `True`*, and if it does, it tells the kernel to run the indented code below it.*\n", + "In the `if` statements above, what comes after the `if` keyword is an expression that evaluates to a Boolean. This simply means that our code is checking *if the expression* `a == 11:` *evaluates to* `True`, and if it does, it tells the kernel to run the indented code below it.*\n", "\n", "Let's evaluate the conditional expressions in the code above:" ] @@ -52,9 +51,10 @@ "# Run Me!\n", "\n", "# Evaluating boolean expressions\n", - "print(\"a == 11:\", a == 11)\n", - "print(\"a == 10:\", a == 10)\n", - "print(\"a < 20:\", a < 20)" + "\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" ] }, { @@ -80,9 +80,9 @@ "metadata": {}, "source": [ "\n", - "### **Test Yourself**\n", + "### **Challenge**\n", "\n", - "Add code to determine if the statements in the comments are `True` or `False`." + "Write code to determine if the commented-statements are `True` or `False`." ] }, { @@ -122,17 +122,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## **And / Or**\n", + "## **Working with Conditional Expressions**\n", "\n", - "What if you want to do something only if `a` is equal to 10 **and** `b` is equal to 15? In that case, you can use the logical operators `and` & `or`.\n", + "### **The $And$ Operator**\n", "\n", + "In Python, we can combine multiple conditional expressions using the `and` operator:\n", "\n", "| Operator | Purpose |\n", "| :------- | :------: |\n", "| `and` | Returns `True` only if *both* sides are `True`. |\n", - "| `or` | Returns `True` if *either* side is `True*. |\n", "\n", - "For example:\n", + "General Representation:\n", "\n", "```python\n", "True and True == True # Both sides are True\n", @@ -140,28 +140,81 @@ "False and False == False # Both sides are False\n", "```\n", "\n", + "> **Note:** You probably never want to, nor should you need to write code like this. However, 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", + "\n", + "We can also use the `or` operator:\n", + "\n", + "| Operator | Purpose |\n", + "| :------- | :------: |\n", + "| `or` | Returns `True` if *either* side is `True*. |\n", + "\n", + "Gerneral Representation:\n", + "\n", "```python\n", "True or True == True # Both sides are True\n", "True or False == True # One side is True\n", "False or False == False # Both sides are False\n", "```\n", "\n", - "You'll probably never write code exactly like the above, but you might write something like this:\n", + "> **Note:** You also probably never to illustrate `or` like this in your code either.\n", "\n", - "```python\n", - "# Using `and` to check if both conditions are true\n", - "if a == 5 and s.startswith('hello'):\n", - " print(\"yeah!\")\n", + "Here is a more practical example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", "\n", - "# Using `and` to check if both conditions are true\n", - "if time > \"11:30\" and time < \"12:30\":\n", - " print(\"Lunch time!\")\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(\"I am asleep\")\n", - "```\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 `in` 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!" ] }, @@ -169,25 +222,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### **Test Yourself**\n", + "### **Challenge**\n", "\n", "Determine if the commented statements are `True` or `False`." ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Ellipsis\n", - "Ellipsis\n" - ] - } - ], + "outputs": [], "source": [ "# Test Yourself\n", "\n", @@ -196,28 +240,24 @@ "c = '30'\n", "s = 'hello world'\n", "\n", - "# Set a_is_10 to true if `a` is equal to 10\n", + "# set `a_is_10` to be `true` if `a` is equal to `10`\n", "\n", - "if (a == 10):\n", - " a_is_10 = True\n", - "#a_is_10 = (a == 10)\n", + "a_is_10 = (a == 10)\n", "\n", - "# Set last_is_world to be true if the last word in `s` is 'world'\n", + "# Set `last_is_world` to be `true` if the last world in `s` is \"world\"\n", "\n", - "if (s.endswith('world') == True):\n", - " last_is_world = True\n", + "last_is_world = ...\n", "\n", - "# Set last_is_hello to be true if the last word in s is 'hello'\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", + "# 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", + "# If the integer value of `c` is `30` or `b` is evenly divisible by `5`, print \"success\"\n", "\n", "if ...:\n", " print(...)" @@ -227,57 +267,86 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## **Else / Elif (else if)**\n", + "## **Using $if$, $elif$, and $else$ Blocks**\n", + "\n", + "There are more to `if` statements than meets the eye. You can also turn them into blocks by combining theme 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", - "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` conditions are `True`.\n", + "> **Fun Fact:** Although most other programming languages refer to this as `else if`, in Python, we use the shortened form `elif`.\n", "\n", - "#### **Examples**" + "Let's see it in action:" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "
Using if/else Statements
\n", + "# Run Me!\n", "\n", - "```python \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", + "maybe_its_true = False\n", "\n", - "
Using if/elif/else Statements
\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", - "```python\n", - "maybe_this = ... \n", - "maybe_that = ...\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$\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:\n", + "if maybe_this > 7:\n", " # do this if maybe_this_ == True\n", - " ...\n", - "elif maybe_that:\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", - " ...\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": [ - "### **Test Yourself**\n", - "\n", - "Write a program that sets a variable `fb` to a number, then write a conditional that\n", + ">**Tip:** if-elif-else blocks are incredibly useful when paired with `try` and `except` blocks to handle errors in your programs! However, for now, we can worry about them later, as we will explain this in more detail in a later lesson." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Challenge**\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", + "Write a program that sets a variable `fb` to a number, then write a conditional that evaluates the following:\n", + "* If the number is evenly divisible by 5, and if so, it prints 'fizz'\n", + "* If the number is evenly divisible by 3, and if so, it prints 'buzz'\n", + "* If it is divisible by neither, it prints the number\n", "\n", "Test your program with different values of `fb`." ] @@ -306,11 +375,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Here is a common way of using `if`/`elif`/`else` blocks to break a continuous value (like the amount of water in a cup) into categories.\n", + "### **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 example, each category is larger than the one before it. The program checks conditions in order and will execute the block for the **first** condition that is true. This effectively finds the smallest category that fits.\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", - "You might also use this method for things like converting a numerical score into a letter grade or an age into an age group." + "**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)." ] }, { From 8568070d53666044d240c17e97e0c3dc94026c1b Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sun, 7 Dec 2025 23:57:01 -0500 Subject: [PATCH 101/159] Made slight changes to this notebook for readability enhancements --- .vscode/settings.json | 18 +- .../10_Numbers_and_Strings.ipynb | 272 +++++++++--------- 2 files changed, 153 insertions(+), 137 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b4e65206..3ebf23df 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,7 @@ "**/.git": true, "**/.svn": true, "**/.hg": true, + "**/.DS_Store": true, "**/Thumbs.db": true, "**/*.crswap": true, "**/__pycache__": true, @@ -14,7 +15,22 @@ "**/.nox": true, "**/.pytest_cache": true, "**/.ruff_cache": true, - "**/.tox": true + "**/.tox": true, + ".git": true, + ".github": true, + "**/.gitignore": true, + ".gitattributes": true, + ".pylintrc": true, + ".vscode": true, + ".yarn": true, + ".devcontainer": true, + "**/node_modules": true, + "**/.ipynb_checkpoints": true, + ".venv": true, + ".lib": true, + "**/.jtl": true, + "requirements.txt": true, + ".lessons": true } } \ 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 index fb54926b..7af54cbf 100644 --- a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb +++ b/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb @@ -11,12 +11,12 @@ "
\n", " Click here for a friendly reminder about Notebooks\n", "\n", - "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", + "This file is an executable Jupyter-style notebook. It's made of *cells* \u2014 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", + "* Click a cell to edit it, then press \u21e7 Shift + \u23ce Enter to run the cell.\n", + "* You can also use the \u25b6\ufe0f button in the cell toolbar to run a cell too.\n", "* Press esc to enter Command Mode if you want to move, copy, or delete cells (the blue outline disappears in Command Mode).\n", "* When a cell is active you'll see a small menu in the upper-right with extra options.\n", "\n", @@ -113,7 +113,7 @@ "\n", "### **Addition vs Concatenation**\n", "\n", - "Different types support different operations — for example, `+` performs arithmetic on numbers but concatenation on strings.\n", + "Different types support different operations \u2014 for example, `+` performs arithmetic on numbers but concatenation on strings.\n", "\n", "Lets see what happens when we add two integers and when we add two strings:" ] @@ -202,125 +202,13 @@ "> **Tip:** Conversion functions have the same name as the type they convert to, this will help you remember them!" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **Numbers**\n", - "\n", - "Python has two main numeric types, integers (`int`) for whole numbers and floating-point numbers (`float`) for values with a fractional part (e.g., like a decimal: $3.14$).\n", - "\n", - "Integers can be written in four different bases:\n", - "\n", - "* **Binary (Base 2):** $0b100101$\n", - "* **Octal (Base 8):** $0o45$\n", - "* **Decimal (Base 10):** $37$\n", - "* **Hexadecimal (Base 16):** $0x25$\n", - "\n", - "For example, the decimal number $237$ equals $(2 \\times 100) + (3 \\times 10) + (7 \\times 1)$ — that's $2$ hundreds, $3$ tens, and $7$ ones, or in exponents, its $(2 \\times 10^2) + (3 \\times 10^1) + (7 \\times 10^0)$.\n", - "\n", - "Other number bases work the same way, but they use different place values.\n", - "\n", - "This diagram shows how these bases relate to each other:\n", - "\n", - "\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", - "\n", - "If 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)" - ] - }, - { - "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": [ "### **Operators**\n", "\n", - "Below are some common math operators for numbers—you’ve likely used most of them, though a couple might be new.\n", + "Below are some common math operators for numbers\u2014you\u2019ve likely used most of them, though a couple might be new.\n", "\n", "| Operator | Description | Example | Result |\n", "|----------|-------------|---------|--------|\n", @@ -429,9 +317,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### **Test Yourself**\n", + "### **Candy Distribution**\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" + "Start with two variables: `kids` (number of kids) and `candy_bars` (number of candy bars). Calculate how to evenly distribute the candy bars to the kids \u2014 print how many each kid gets and how many are left over.\n" ] }, { @@ -442,26 +330,135 @@ "source": [ "# Test yourself\n", "\n", - "current_year = '2024' # Change to the current year. \n", - "birth_year = '1999' # Change to your birth year\n", + "kids = ...\n", + "candy_bars = ...\n", "\n", - "age = ... # Calculate the age\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", - "print(\"You are \", age, \" years old in decimal\")\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": [ + "## **Numbers**\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" + "Python has two main numeric types, integers (`int`) for whole numbers and floating-point numbers (`float`) for values with a fractional part (e.g., like a decimal: $3.14$).\n", + "\n", + "Integers can be written in four different bases:\n", + "\n", + "* **Binary (Base 2):** $0b100101$\n", + "* **Octal (Base 8):** $0o45$\n", + "* **Decimal (Base 10):** $37$\n", + "* **Hexadecimal (Base 16):** $0x25$\n", + "\n", + "For example, the decimal number $237$ equals $(2 \\times 100) + (3 \\times 10) + (7 \\times 1)$ \u2014 that's $2$ hundreds, $3$ tens, and $7$ ones, or in exponents, its $(2 \\times 10^2) + (3 \\times 10^1) + (7 \\times 10^0)$.\n", + "\n", + "Other number bases work the same way, but they use different place values.\n", + "\n", + "This diagram shows how these bases relate to each other:\n", + "\n", + "\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", + "\n", + "If 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)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### **Candy Distribution**\n", + "### **Using Conversion Functions**\n", + "\n", + "You can convert numbers to these representations with `oct()`, `hex()`, and `bin()` \u2014 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", - "Start with two variables: `kids` (number of kids) and `candy_bars` (number of candy bars). Calculate how to evenly distribute the candy bars to the kids — print how many each kid gets and how many are left over.\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" ] }, { @@ -472,14 +469,17 @@ "source": [ "# Test yourself\n", "\n", - "kids = ...\n", - "candy_bars = ...\n", + "current_year = '2024' # Change to the current year. \n", + "birth_year = '1999' # Change to your birth year\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", + "age = ... # Calculate the age\n", "\n", - "candy_left_over = ... # Calculate the number of candy bars left over\n", - "print(\"There are \", candy_left_over, \" candy bars left over\")" + "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" ] }, { @@ -604,4 +604,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file From 2f9e935938495825fee027d8840c801763b0f56e Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 8 Dec 2025 00:28:02 -0500 Subject: [PATCH 102/159] Made some minor fixes --- lessons/20_Types_and_Logic/20_Control_Flow.ipynb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb index 2088f558..a1c1543d 100644 --- a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb +++ b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb @@ -343,12 +343,15 @@ "source": [ "### **Challenge**\n", "\n", - "Write a program that sets a variable `fb` to a number, then write a conditional that evaluates the following:\n", - "* If the number is evenly divisible by 5, and if so, it prints 'fizz'\n", - "* If the number is evenly divisible by 3, and if so, it prints 'buzz'\n", - "* If it is divisible by neither, it prints the number\n", + "Write a program that sets a variable `fb` to a number, then write a conditional that evaluates the following conditions:\n", "\n", - "Test your program with different values of `fb`." + "__**1)**__ If the number is evenly divisible by 5, and if so, it prints '*fizz*'\n", + "
\n", + "__**2)**__ If the number is evenly divisible by 3, and if so, it prints '*buzz*'\n", + "
\n", + "__**3)**__ If it is divisible by neither, it prints the number\n", + "\n", + "Afterwards, you test your program with different values of `fb`." ] }, { From a47b7ae70002b071cd94583ab1cc55c9a12b404b Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 8 Dec 2025 00:45:28 -0500 Subject: [PATCH 103/159] Minor fixes --- .../20_Types_and_Logic/20_Control_Flow.ipynb | 83 ++++++++++++++----- 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb index a1c1543d..f0136ebf 100644 --- a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb +++ b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb @@ -6,7 +6,7 @@ "source": [ "# **Control Flow**\n", "\n", - "Programs become significantly more useful when they can make decisions, and in Python, we canuse `if` statements to handle this decision-making process. These types of instructions are often interchangeably referred to as conditionals, control flow, or branching.\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:" ] @@ -124,23 +124,36 @@ "source": [ "## **Working with Conditional Expressions**\n", "\n", + "There are several operators we can use to create conditional expressions. In case you forgot, conditional expressions are logical operators that can evaluate to either `True` or `False`. They can be used in `if` statements to control the flow of our programs by allowing us to execute different blocks of code based on certain conditions. However, each operator has its own specific purpose.\n", + "\n", "### **The $And$ Operator**\n", "\n", - "In Python, we can combine multiple conditional expressions using the `and` operator:\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`. |\n", - "\n", + "| `and` | Returns `True` only if *both* sides are `True`. |\n" + ] + }, + { + "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", - "```\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\n", - "> **Note:** You probably never want to, nor should you need to write code like this. However, this is just to illustrate how the `and` operator works.\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:" ] @@ -193,9 +206,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Don't disturb me, I am asleep.\n", + "It's the weekend!\n" + ] + } + ], "source": [ "# Run Me!\n", "\n", @@ -271,9 +293,14 @@ "\n", "There are more to `if` statements than meets the eye. You can also turn them into blocks by combining theme 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", - "> **Fun Fact:** Although most other programming languages refer to this as `else if`, in Python, we use the shortened form `elif`.\n", - "\n", - "Let's see it in action:" + "> **Fun Fact:** Although most other programming languages refer to this as `else if`, in Python, we use the shortened form `elif`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### $if$-$elif$" ] }, { @@ -343,22 +370,29 @@ "source": [ "### **Challenge**\n", "\n", - "Write a program that sets a variable `fb` to a number, then write a conditional that evaluates the following conditions:\n", - "\n", - "__**1)**__ If the number is evenly divisible by 5, and if so, it prints '*fizz*'\n", - "
\n", - "__**2)**__ If the number is evenly divisible by 3, and if so, it prints '*buzz*'\n", - "
\n", - "__**3)**__ If it is divisible by neither, it prints the number\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", - "Afterwards, you test your program with different values of `fb`." + "**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, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Ellipsis\n" + ] + } + ], "source": [ "# Test yourself\n", "\n", @@ -428,6 +462,13 @@ "# 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": { From 4094b57e0a156206ff43fca52b4b3e871301f3e5 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 8 Dec 2025 00:46:09 -0500 Subject: [PATCH 104/159] Minor fixes --- .../20_Types_and_Logic/20_Control_Flow.ipynb | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb index f0136ebf..ac96cd25 100644 --- a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb +++ b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb @@ -124,7 +124,7 @@ "source": [ "## **Working with Conditional Expressions**\n", "\n", - "There are several operators we can use to create conditional expressions. In case you forgot, conditional expressions are logical operators that can evaluate to either `True` or `False`. They can be used in `if` statements to control the flow of our programs by allowing us to execute different blocks of code based on certain conditions. However, each operator has its own specific purpose.\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", @@ -132,7 +132,7 @@ "\n", "| Operator | Purpose |\n", "| :------- | :------: |\n", - "| `and` | Returns `True` only if *both* sides are `True`. |\n" + "| `and` | Returns `True` only if *both* sides are `True`. |" ] }, { @@ -206,7 +206,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -382,17 +382,9 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Ellipsis\n" - ] - } - ], + "outputs": [], "source": [ "# Test yourself\n", "\n", From 385b69e424d39aa69663668874c0a0622c260dfd Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 8 Dec 2025 11:47:56 -0500 Subject: [PATCH 105/159] Made some minor fixes --- .../10_Numbers_and_Strings.ipynb | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb b/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb index 7af54cbf..02ff6b91 100644 --- a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb +++ b/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb @@ -11,12 +11,12 @@ "
\n", " Click here for a friendly reminder about Notebooks\n", "\n", - "This file is an executable Jupyter-style notebook. It's made of *cells* \u2014 pieces of text or code you can run one at a time.\n", + "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 \u21e7 Shift + \u23ce Enter to run the cell.\n", - "* You can also use the \u25b6\ufe0f button in the cell toolbar to run a cell too.\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", "* Press esc to enter Command Mode if you want to move, copy, or delete cells (the blue outline disappears in Command Mode).\n", "* When a cell is active you'll see a small menu in the upper-right with extra options.\n", "\n", @@ -43,11 +43,9 @@ "source": [ "### **Getting Started**\n", "\n", - "You've already practiced with variables previously, but in this lesson, we'll look at variables in a bit more detail and explore the different types that are used in Python. \n", + "Although we have already worked with variables before in the past [lesson](../10_Turtles/50_Variables_and_Functions/10_Variables.ipynb) (feel free to click the link for a refresher), we will now dive deeper into them and examine the various data types available in Python.\n", "\n", - "In need of a refresher? That's okay, just go back and check out the [Variables lesson](../10_Turtles/50_Variables_and_Functions/10_Variables.ipynb).\n", - "\n", - "If you are ready to dive in, run the code below and let's get started!" + "When you're set to begin, execute the code block below!" ] }, { @@ -113,7 +111,7 @@ "\n", "### **Addition vs Concatenation**\n", "\n", - "Different types support different operations \u2014 for example, `+` performs arithmetic on numbers but concatenation on strings.\n", + "Different types support different operations — for example, `+` performs arithmetic on numbers but concatenation on strings.\n", "\n", "Lets see what happens when we add two integers and when we add two strings:" ] @@ -208,7 +206,7 @@ "source": [ "### **Operators**\n", "\n", - "Below are some common math operators for numbers\u2014you\u2019ve likely used most of them, though a couple might be new.\n", + "Below are some common math operators for numbers—you’ve likely used most of them, though a couple might be new.\n", "\n", "| Operator | Description | Example | Result |\n", "|----------|-------------|---------|--------|\n", @@ -217,10 +215,12 @@ "| $*$ | 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", + "| $\\%$ | Modulo (*Remainder*) | $11 \\% 4$ | $3$ |\n", "| $**$ | Exponentiation (*Power*) | $2$ ** $3$ | $8$ |\n", "\n", - "The `//` operator divides and drops the fractional part, which means it returns an integer when used with integers." + "Floor division uses the `//` operator, it divides then drops the remaining fractional part, which means it returns an integer when used with integers, but a float when used with floats. \n", + "\n", + "For example, `11 // 4` results in `2` and `11.0 // 4.0` results in `2.0` even though normal division would result in `2.75`." ] }, { @@ -244,7 +244,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The ` % ` (modulo) operator returns the remainder after division. It's useful for checking evenly divisible numbers (e.g., `n % 3 == 0`).\n" + "Modulo uses the ` % ` operator and returns the remainder after division. It's useful for checking for evenly divisible numbers, like if `n` is divisible by `3` (e.g., `n % 3 == 0`).\n" ] }, { @@ -290,6 +290,15 @@ "print(\"fd * b + m == a == \", fd * b + m)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As shown above, when you divide `11` by `4`, the quotient is `2` and the remainder is `3`. You can reconstruct the original number using the formula below: \n", + "\n", + "`original_number = (divisor * quotient) + remainder`" + ] + }, { "cell_type": "code", "execution_count": null, @@ -308,9 +317,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - ">**Tip:** Modulo sort of works like a clock: the result goes around to the maximum number, then goes back to 0.\n", + ">**Tip:** Modulo works a bit like a clock. The numbers count up, but once they reach a certain limit (the divisor), they wrap back around to zero.\n", "\n", - "The most common use for the modulo operator is to check if a number is evenly divisible by another number. If the remainder is 0, it divides evenly. For example, 6 is evenly divisible by 3 because ``6 % 3 == 0``." + "The most common use for the modulo operator is to check if a number is evenly divisible by another. If the result is 0, it means there is no remainder, so it divides evenly. For example, 6 is evenly divisible by 3 because `6 % 3` is `0`." ] }, { @@ -319,7 +328,7 @@ "source": [ "### **Candy Distribution**\n", "\n", - "Start with two variables: `kids` (number of kids) and `candy_bars` (number of candy bars). Calculate how to evenly distribute the candy bars to the kids \u2014 print how many each kid gets and how many are left over.\n" + "Start with two variables: `kids` (number of kids) and `candy_bars` (number of candy bars). Calculate how to evenly distribute the candy bars to the kids — print how many each kid gets and how many are left over.\n" ] }, { @@ -355,7 +364,7 @@ "* **Decimal (Base 10):** $37$\n", "* **Hexadecimal (Base 16):** $0x25$\n", "\n", - "For example, the decimal number $237$ equals $(2 \\times 100) + (3 \\times 10) + (7 \\times 1)$ \u2014 that's $2$ hundreds, $3$ tens, and $7$ ones, or in exponents, its $(2 \\times 10^2) + (3 \\times 10^1) + (7 \\times 10^0)$.\n", + "For example, the decimal number $237$ equals $(2 \\times 100) + (3 \\times 10) + (7 \\times 1)$ — that's $2$ hundreds, $3$ tens, and $7$ ones, or in exponents, its $(2 \\times 10^2) + (3 \\times 10^1) + (7 \\times 10^0)$.\n", "\n", "Other number bases work the same way, but they use different place values.\n", "\n", @@ -382,7 +391,7 @@ "source": [ "### **Using Conversion Functions**\n", "\n", - "You can convert numbers to these representations with `oct()`, `hex()`, and `bin()` \u2014 examples below." + "You can convert numbers to these representations with `oct()`, `hex()`, and `bin()` — examples below." ] }, { @@ -604,4 +613,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} From 678b78119b752ed5eb07311588551c40285e44f9 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 8 Dec 2025 11:54:12 -0500 Subject: [PATCH 106/159] Made some minor fixes to wording --- .../20_Types_and_Logic/20_Control_Flow.ipynb | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb index ac96cd25..cee38fdc 100644 --- a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb +++ b/lessons/20_Types_and_Logic/20_Control_Flow.ipynb @@ -37,7 +37,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In the `if` statements above, what comes after the `if` keyword is an expression that evaluates to a Boolean. This simply means that our code is checking *if the expression* `a == 11:` *evaluates to* `True`, and if it does, it tells the kernel to run the indented code below it.*\n", + "In the `if` 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:" ] @@ -72,7 +72,7 @@ "* `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 a very common error, so don't feel discouraged. Try your best to memorize the differences!" + ">**Tip:** Confusing `=` and `==` is a very common error, so don't feel discouraged. Just try your best to memorize their differences and remember: *practice makes perfect!*" ] }, { @@ -208,16 +208,7 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Don't disturb me, I am asleep.\n", - "It's the weekend!\n" - ] - } - ], + "outputs": [], "source": [ "# Run Me!\n", "\n", @@ -293,7 +284,7 @@ "\n", "There are more to `if` statements than meets the eye. You can also turn them into blocks by combining theme 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", - "> **Fun Fact:** Although most other programming languages refer to this as `else if`, in Python, we use the shortened form `elif`." + "> **Note:** Most other programming languages refer to this as `else if`, but in Python, we use the shortened form `elif`." ] }, { @@ -361,7 +352,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - ">**Tip:** if-elif-else blocks are incredibly useful when paired with `try` and `except` blocks to handle errors in your programs! However, for now, we can worry about them later, as we will explain this in more detail in a later lesson." + ">**Tip:** $if$-$elif$-$else$ blocks are incredibly 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." ] }, { From d70df8ab1c3f0754cd82c501d4371acf06ba58d8 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 8 Dec 2025 13:31:39 -0500 Subject: [PATCH 107/159] Added tooltip icons + separated the numbers section into its own notebook (MAY NEED WORK TO GET syllabus.yaml CORRECTED) --- .../10_Turtles/10_Welcome/10_Welcome.ipynb | 19 +- .../10_Welcome/20_Open_The_Screen.ipynb | 93 +-- .../10_Welcome/30_Run_Programs.ipynb | 397 ++++----- .../20_What_Can_Tina_Do.ipynb | 241 +++--- .../40_Check_In_Your_Code.ipynb | 37 +- .../30_Turtle_Tricks/10_Turtle_Tricks.py | 4 +- .../30_Turtle_Tricks/20_Turtle_Tricks.py | 4 +- .../30_Turtle_Tricks/30_Turtle_Tricks.py | 4 +- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 779 +++++++++--------- .../40_Loops/20_Loop_with_Turtle.py | 4 +- .../40_Loops/30_Loop_with_Turtle.py | 4 +- .../10_Variables.ipynb | 573 ++++++------- .../20_Functions.ipynb | 707 ++++++++-------- .../30_Efficient_Turtle.py | 5 +- .../10_More_Turtle_Programs.ipynb | 653 +++++++-------- .../20_More_Turtle_programs.py | 8 +- .../30_More_Turtle_Programs.py | 8 +- .../40_More_Turtle_Programs.py | 6 +- .../10_Turtles/70_Projects/10_LeagueBot.py | 7 +- lessons/10_Turtles/70_Projects/20_Tash_Me.py | 7 +- .../70_Projects/30_Tash_Me_Click.py | 3 + .../70_Projects/40_Tash_Me_Twirl.py | 6 +- .../80_Introducting_Lists/10_Lists.ipynb | 515 ++++++------ .../80_Introducting_Lists/20_Color_Lines.py | 5 +- .../10_Flaming_Ninja_Star.py | 5 +- .../90_Graphics_Projects/20_Crazy_Spiral.py | 3 + .../90_Graphics_Projects/30_Pentagon_Crazy.py | 3 + .../90_Graphics_Projects/40_Turtle_Spiral.py | 3 + .../10_Numbers_and_Strings.ipynb | 616 -------------- .../10_Types_and_Operators.ipynb | 387 +++++++++ .../20_Work_With_Numbers.ipynb | 264 ++++++ ...ntrol_Flow.ipynb => 30_Control_Flow.ipynb} | 15 +- .../{30_My_Ages.py => 40_My_Ages.py} | 4 + .../50_Infuriating_Calculator.py | 3 + ...{40_Simple_Adder.py => 60_Simple_Adder.py} | 3 + ...llenges.ipynb => 70_Code_Challenges.ipynb} | 5 +- lessons/30_Loops/010_Iteration.ipynb | 5 +- lessons/30_Loops/020_Loops_with_Range.ipynb | 15 +- lessons/30_Loops/030_Lists.ipynb | 5 +- lessons/30_Loops/040_Crazy_Tina.py | 5 +- lessons/30_Loops/050_Tuples.ipynb | 7 +- .../30_Loops/060_Indexing_and_Slicing.ipynb | 5 +- lessons/30_Loops/070_List_Story.py | 3 + lessons/30_Loops/080_Strings.ipynb | 7 +- lessons/30_Loops/090_Fizz_Buzz_Badgers.py | 6 +- lessons/30_Loops/100_For_Loop_Gauntlet.ipynb | 5 +- lessons/30_Loops/110_FizzBuzz_Gui_Grid.py | 5 +- lessons/30_Loops/120_More_Iterables.ipynb | 5 +- lessons/30_Loops/130_Iterable_Turtle.ipynb | 5 +- lessons/30_Loops/140_More_Loops.ipynb | 5 +- lessons/30_Loops/150_Number_Guess.py | 7 +- lessons/30_Loops/160_Extras.ipynb | 7 +- .../10_Functions.ipynb | 5 +- .../20_Dicts_Sets.ipynb | 5 +- .../30_Funny_Words_Db.py | 5 + .../40_Splat_Comprehension.ipynb | 3 +- .../40_Data_Structures_Func/50_Tic_Tac_Toe.py | 9 +- lessons/50_Projects/20_Random_Walk.py | 7 +- 58 files changed, 2840 insertions(+), 2691 deletions(-) delete mode 100644 lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb create mode 100644 lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb create mode 100644 lessons/20_Types_and_Logic/20_Work_With_Numbers.ipynb rename lessons/20_Types_and_Logic/{20_Control_Flow.ipynb => 30_Control_Flow.ipynb} (80%) rename lessons/20_Types_and_Logic/{30_My_Ages.py => 40_My_Ages.py} (97%) rename lessons/20_Types_and_Logic/{40_Simple_Adder.py => 60_Simple_Adder.py} (94%) rename lessons/20_Types_and_Logic/{60_Code_Challenges.ipynb => 70_Code_Challenges.ipynb} (98%) diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index b5365ae8..74f599fd 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -14,9 +14,9 @@ "* [**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", "\n", - "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist 🤖 that you can control with code. To do this, we'll use a pre-installed Python tool called the **turtle module**. \n", + "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist \ud83e\udd16 that you can control with code. To do this, we'll use a pre-installed Python tool called the **turtle module**. \n", "\n", - "You can tell your turtle 🐢 to move forward ↑, turn left ← or ➝ right, and change the color 🎨 of the lines it draws 🖍️— Soon, you'll be able to create awesome pictures like this one!\n", + "You can tell your turtle \ud83d\udc22 to move forward \u2191, turn left \u2190 or \u279d right, and change the color \ud83c\udfa8 of the lines it draws \ud83d\udd8d\ufe0f\u2014 Soon, you'll be able to create awesome pictures like this one!\n", "\n", "
\n", "\n", @@ -26,15 +26,15 @@ "\n", "
\n", "

Let's Get Set Up!

\n", - "

Before we can start drawing, we need to set up your coding environment. Don't worry if anything seems confusing—that's what your instructors are here for! Feel free to ask for help if you get stuck.

\n", - "

Ready to go? Click the blue Next Lesson button at the bottom-left of your screen to continue! 🚀

\n", + "

Before we can start drawing, we need to set up your coding environment. Don't worry if anything seems confusing\u2014that's what your instructors are here for! Feel free to ask for help if you get stuck.

\n", + "

Ready to go? Click the blue Next Lesson button at the bottom-left of your screen to continue! \ud83d\ude80

\n", "\n", "
\n", " Notes & Key Terms\n", "
    \n", - "
  • Python — A popular programming language known for its simplicity and readability.
  • \n", - "
  • Turtle Module — A built-in Python library that allows for drawing shapes and patterns using a virtual turtle.
  • \n", - "
  • Turtle Programming — A style of programming that uses the turtle module to create graphics and animations.
  • \n", + "
  • Python \u2014 A popular programming language known for its simplicity and readability.
  • \n", + "
  • Turtle Module \u2014 A built-in Python library that allows for drawing shapes and patterns using a virtual turtle.
  • \n", + "
  • Turtle Programming \u2014 A style of programming that uses the turtle module to create graphics and animations.
  • \n", "
\n", "
" ] @@ -59,9 +59,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "RRTPqCQu" + "uid": "RRTPqCQu", + "name": "Welcome" } }, "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 cac65775..6bc95888 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,49 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Let's Open Your Virtual Screen!**\n", - "\n", - "Ready to run your code and see what it does?\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 just need to open a virtual screen. Don't worry, this part is easy.\n", - "\n", - "Most of the time, this screen will open automatically. If for some reason it doesn't appear, just 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, just click on the connect button!\n", - "
\n", - " \"Virtual\n", - "
\n", - "\n", - "With your virtual screen open, you are now one step closer to interacting with your turtle!" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Let's Open Your Virtual Screen!**\n", + "\n", + "Ready to run your code and see what it does?\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 just need to open a virtual screen. Don't worry, this part is easy.\n", + "\n", + "Most of the time, this screen will open automatically. If for some reason it doesn't appear, just 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, just click on the connect button!\n", + "
\n", + " \"Virtual\n", + "
\n", + "\n", + "With your virtual screen open, you are now one step closer to interacting with your turtle!" + ] + } + ], + "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": { + "uid": "KmgIQbhr", + "name": "Open The Screen" + } }, - "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": "KmgIQbhr" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index fd75e779..aedcc898 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -1,200 +1,201 @@ { - "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! Just move your mouse over the code cell and click the ▶️ button." - ] + "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 \u25b6\ufe0f 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 \u23e9 **Run All** button at the top, and some code blocks will have a \u25b6\ufe0f button on the left.\n", + "\n", + "Try running the code below! Just move your mouse over the code cell and click the \u25b6\ufe0f button." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "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 = f\"Hello \ud83d\udc4b World \ud83c\udf0e ! Today is\"\n", + "\n", + "# Print the string and the date\n", + "print(s, date)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here\u2019s what the code cell looks like with the run button. Try it out!\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 \u25b6\ufe0f button you might notice that nothing happens. Don\u2019t worry! Technically something did, you just were not able to 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 all you need to do is click on a Python option from the list (look for one with a \u2605 or .venv in the name) and try running the code again!\n", + "\n", + "\n", + "
\n", + "\n", + "> **Note:** You\u2019ll 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 \u25b6\ufe0f button or by pressing \u21e7 Shift + \u23ce 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", + "**Try it yourself!**\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. Now all you have to do is press the play button and 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", + "Running `.py` files might seem new or even a little overwhelming at first, but don\u2019t worry\u2014you\u2019ll get the hang of it quickly! In the next lesson, you\u2019ll try running a Python program called `Meet_Tina.py`. There are a few different ways to do this, and you can use whichever feels easiest for you. Let\u2019s walk through each option together!\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\u2019ll see buttons like this:\n", + "\n", + "\n", + "\n", + "Here\u2019s how it works, step by step:\n", + "- Click \u25b6 Run to start your program\n", + "\n", + "- Click \u23f9 Stop to stop it\n", + "\n", + "- When you\u2019re 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 \u25b6\ufe0f 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 \u25b6\ufe0f run button.\n", + "3. When you\u2019re 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 \ud83e\ude9f, or fn + F5 on Mac \ud83c\udf4e. The first time, you\u2019ll be asked to select a debugger\u2014just pick the Python Debugger and then choose to debug the currently active Python file.\n", + "\n", + "You\u2019ll see a debug bar like this:\n", + "\n", + "\n", + "\n", + "Don\u2019t worry about all the buttons for now! You just need to know that you can press the red square to stop your program, and the green circle to run it again.\n", + "\n", + "> **Tip:** Remember, it\u2019s totally normal to feel unsure at first. Take your time, try out each method, and soon running Python programs will feel easy!\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "WARNING: If a program is already running, you must close the program before starting another one.\n", + "\n", + "You have two options to close it:\n", + "
\n", + "__1)__ Click the \u2612 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", + "\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 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": { + "uid": "cNLK6qtR", + "name": "Run Programs" + } }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "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 = f\"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. Try it out!\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. Don’t worry! Technically something did, you just were not able to 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 all you need to do is 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", - "**Try it yourself!**\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. Now all you have to do is press the play button and 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", - "Running `.py` files might seem new or even a little overwhelming at first, but don’t worry—you’ll get the hang of it quickly! In the next lesson, you’ll try running a Python program called `Meet_Tina.py`. There are a few different ways to do this, and you can use whichever feels easiest for you. Let’s walk through each option together!\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", - "Don’t worry about all the buttons for now! You just need to know that you can press the red square to stop your program, and the green circle to run it again.\n", - "\n", - "> **Tip:** Remember, it’s totally normal to feel unsure at first. Take your time, try out each method, and soon running Python programs will feel easy!\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "WARNING: If a program is already running, you must close the program before starting another one.\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", - "\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 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": { - "uid": "cNLK6qtR" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} + "nbformat": 4, + "nbformat_minor": 4 +} \ 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 34bebdde..d748e88f 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,122 +1,123 @@ { - "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. Don’t get intimidated—Tina’s magic 🧙‍♂️ is actually very simple and can be broken down into basic 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", - "But before we move ahead, let’s break down how this magic ✨ 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." - ] + "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. Don\u2019t get intimidated\u2014Tina\u2019s magic \ud83e\uddd9\u200d\u2642\ufe0f is actually very simple and can be broken down into basic steps!\n", + "\n", + "In the next program, `Squares_and_Circles.py`, you\u2019ll 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", + "But before we move ahead, let\u2019s break down how this magic \u2728 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** \u2014 it\u2019s an instruction that tells Tina exactly what to do, such as moving forward or changing color.\n", + "- The text *after* the `#` is a **comment** \u2014 it\u2019s a note for humans that explains what the command does. \n", + "\n", + "> **Tip:** Comments are ignored by Python and don\u2019t affect how the program runs." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Seeking Additional Information\n", + "\n", + "Learning to program with Python's Turtle graphics is fun and creative! One of the best habits you can develop is regularly consulting official documentation and tutorials. This helps you understand commands, discover new features, and solve problems more efficiently. Here\u2019s a detailed guide on how to find and use Python Turtle documentation.\n", + "\n", + "### **How to Find Python Turtle Documentation**\n", + "\n", + "1. **Use a Search Engine** by opening your preferred search engine (Google, Bing, DuckDuckGo, etc.).\n", + "\n", + "2. **Enter Your Query** by typing keywords such as `python turtle documentation`, `python turtle commands`, or specific questions like `how to draw a circle with turtle in python`.\n", + "\n", + "3. **Visit Official Sources** and look for links to the [Python Turtle documentation](https://docs.python.org/3/library/turtle.html) or other reputable sites. The official Python docs are usually among the top results and provide comprehensive information.\n", + "\n", + "4. **Explore Tutorials and Examples** on websites and forums (such as Real Python, GeeksforGeeks, Stack Overflow, and YouTube) that offer step-by-step guides, video tutorials, and sample code. These resources can help you understand how commands work in real programs.\n", + "\n", + "5. **Bookmark Useful Pages** so you can save helpful documentation, tutorials, or example projects for quick reference as you continue learning.\n", + "\n", + "6. **Read API References** in the official documentation, which includes an API reference page that lists all the available Turtle methods (like `forward()`, `right()`, `pencolor()`, etc.), their parameters, and example usage.\n", + "\n", + "7. **Check Community Q&A** if you encounter an error or want to achieve a specific effect, as searching forums like Stack Overflow can provide solutions and explanations from other programmers.\n", + "\n", + "### **Why Consulting Documentation Is Essential**\n", + "\n", + "- **Learn Correct Syntax** because documentation shows the exact way to use commands and functions, helping you avoid mistakes and understand how each function works.\n", + "\n", + "- **Discover New Features** since you might find commands or options you didn\u2019t know existed, allowing you to create more interesting and complex drawings.\n", + "\n", + "- **Troubleshoot Problems** if your code isn\u2019t working as expected, as documentation and community forums often have solutions, explanations, and troubleshooting tips.\n", + "\n", + "- **Build Independence** by regularly searching for answers, which helps you become a more confident and resourceful programmer, able to solve problems on your own.\n", + "\n", + "- **Stay Updated** because documentation is updated as Python evolves, so checking it helps you learn about new features and best practices.\n", + "\n", + "### **Extra Ways To Use Documentation Effectively**\n", + "\n", + "- **Try Out Examples** by copying and running example code from the documentation to see how it works. Experiment by changing parameters and observing the results.\n", + "\n", + "- **Use the Search Feature** on most documentation sites, which have a search bar you can use to quickly find information about specific commands or topics.\n", + "\n", + "- **Read the Introduction** at the beginning of the documentation, which often explains how to set up Turtle graphics and provides an overview of its capabilities.\n", + "\n", + "> **Tip:** Whenever you\u2019re unsure about a command or want to try something new, searching for documentation or examples is one of the best ways to learn and grow as a programmer. The more you explore, the more creative and confident you\u2019ll become!\n", + "\n", + "
\n", + "
\n", + " Click Here To View The Official Python Turtle Documentation\n", + " \n", + "
\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## **Assignment**\n", + "\n", + "Take a look at the steps below, and when you\u2019re ready, move on to the Next Lesson and keep exploring what Tina can do!\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\u2019s drawing changes.\n", + "\n", + "> **Tip:** If you\u2019re not sure what a command does, try changing it and running the program again. Experimentation is a great way to learn!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.13.5" + }, + "syllabus": { + "uid": "7tUP3zAZ", + "name": "What Can Tina Do" + } }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
Seeking Additional Information\n", - "\n", - "Learning to program with Python's Turtle graphics is fun and creative! One of the best habits you can develop is regularly consulting official documentation and tutorials. This helps you understand commands, discover new features, and solve problems more efficiently. Here’s a detailed guide on how to find and use Python Turtle documentation.\n", - "\n", - "### **How to Find Python Turtle Documentation**\n", - "\n", - "1. **Use a Search Engine** by opening your preferred search engine (Google, Bing, DuckDuckGo, etc.).\n", - "\n", - "2. **Enter Your Query** by typing keywords such as `python turtle documentation`, `python turtle commands`, or specific questions like `how to draw a circle with turtle in python`.\n", - "\n", - "3. **Visit Official Sources** and look for links to the [Python Turtle documentation](https://docs.python.org/3/library/turtle.html) or other reputable sites. The official Python docs are usually among the top results and provide comprehensive information.\n", - "\n", - "4. **Explore Tutorials and Examples** on websites and forums (such as Real Python, GeeksforGeeks, Stack Overflow, and YouTube) that offer step-by-step guides, video tutorials, and sample code. These resources can help you understand how commands work in real programs.\n", - "\n", - "5. **Bookmark Useful Pages** so you can save helpful documentation, tutorials, or example projects for quick reference as you continue learning.\n", - "\n", - "6. **Read API References** in the official documentation, which includes an API reference page that lists all the available Turtle methods (like `forward()`, `right()`, `pencolor()`, etc.), their parameters, and example usage.\n", - "\n", - "7. **Check Community Q&A** if you encounter an error or want to achieve a specific effect, as searching forums like Stack Overflow can provide solutions and explanations from other programmers.\n", - "\n", - "### **Why Consulting Documentation Is Essential**\n", - "\n", - "- **Learn Correct Syntax** because documentation shows the exact way to use commands and functions, helping you avoid mistakes and understand how each function works.\n", - "\n", - "- **Discover New Features** since you might find commands or options you didn’t know existed, allowing you to create more interesting and complex drawings.\n", - "\n", - "- **Troubleshoot Problems** if your code isn’t working as expected, as documentation and community forums often have solutions, explanations, and troubleshooting tips.\n", - "\n", - "- **Build Independence** by regularly searching for answers, which helps you become a more confident and resourceful programmer, able to solve problems on your own.\n", - "\n", - "- **Stay Updated** because documentation is updated as Python evolves, so checking it helps you learn about new features and best practices.\n", - "\n", - "### **Extra Ways To Use Documentation Effectively**\n", - "\n", - "- **Try Out Examples** by copying and running example code from the documentation to see how it works. Experiment by changing parameters and observing the results.\n", - "\n", - "- **Use the Search Feature** on most documentation sites, which have a search bar you can use to quickly find information about specific commands or topics.\n", - "\n", - "- **Read the Introduction** at the beginning of the documentation, which often explains how to set up Turtle graphics and provides an overview of its capabilities.\n", - "\n", - "> **Tip:** Whenever you’re unsure about a command or want to try something new, searching for documentation or examples is one of the best ways to learn and grow as a programmer. The more you explore, the more creative and confident you’ll become!\n", - "\n", - "
\n", - "
\n", - " Click Here To View The Official Python Turtle Documentation\n", - " \n", - "
\n", - "\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## **Assignment**\n", - "\n", - "Take a look at the steps below, and when you’re ready, move on to the Next Lesson and keep exploring what Tina can do!\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.\n", - "\n", - "> **Tip:** If you’re not sure what a command does, try changing it and running the program again. Experimentation is a great way to learn!" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "name": "python", - "version": "3.13.5" - }, - "syllabus": { - "uid": "7tUP3zAZ" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file 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 4c3b675f..3a0fa428 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 @@ -6,19 +6,19 @@ "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", + "Now that you\u2019ve created a few programs, it\u2019s important to save your progress so you don\u2019t 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. So, don’t risk losing your hard work and always make sure you frequently check in your code! \n", + "> **Note:** If you skip this step, your GitHub Codespace may eventually stop automatically, and after a few weeks, it will be deleted\u2014along with any unsaved changes. So, don\u2019t risk losing your hard work and always make sure you frequently check in your code! \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", + "Let\u2019s make a change to `Meet_Tina.py` so you have something to check in. \n", "\n", "You can change anything you like, whether you make the circle a different 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.circle(100, steps=50) # Change the circle\u2019s size\n", " tina.write(\"Why, hello there!\") # Modify what Tina says\n", "```\n", "\n", @@ -48,14 +48,14 @@ "\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", + "In this panel, you\u2019ll 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", + "2. Click the \u2713 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", @@ -71,14 +71,14 @@ " font-weight: 500;\n", " vertical-align: middle;\n", "\">\n", - " 🔄︎ Sync Changes 1 🡑\n", + " \ud83d\udd04\ufe0e Sync Changes 1 \ud83e\udc51\n", "
\n", "\n", "*Click the Sync button* to safely upload your changes to your GitHub repository.\n", "\n", - "Now your work is safely stored in your GitHub account! 🎉\n", + "Now your work is safely stored in your GitHub account! \ud83c\udf89\n", "\n", - "> **Tip:** Commit your code often—after every lesson, and always before you finish for the day.\n" + "> **Tip:** Commit your code often\u2014after every lesson, and always before you finish for the day.\n" ] }, { @@ -88,7 +88,7 @@ "\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", + "Sometimes, when you click the \u2713 Commit button, nothing seems to happen and you will see a screen like this:\n", "\n", "\n", "\n", @@ -97,14 +97,14 @@ "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", + " - Click the \u2612 (on the file tab), return to Source Control, enter your commit message, and click the \u2713 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", + "If you want to stop your Codespace manually (e.g., when you\u2019re 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!" ] @@ -115,7 +115,7 @@ "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", + "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\u2019t, 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", @@ -125,11 +125,11 @@ "\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", + "If you don\u2019t have that window open anymore, it\u2019s 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", + "2. Click on the <> Code \u2bc6 button\n", "3. In the popup window, select the \"Codespaces\" tab. \n", "\n", "> **Note:** You should *not* see the green Create Codespace button. \n", @@ -140,7 +140,7 @@ "
\n", "
\n", "\n", - "> **Note:** Codespaces often have fun, random names, like 'expert broccoli' 🥦 or 'organic spoon' 🥄 to you help identify them.\n", + "> **Note:** Codespaces often have fun, random names, like 'expert broccoli' \ud83e\udd66 or 'organic spoon' \ud83e\udd44 to you help identify them.\n", "\n", "### **Checking Codespace Status**\n", "\n", @@ -175,9 +175,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "doD6P7fk" + "uid": "doD6P7fk", + "name": "Check In Your Code" } }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file 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 1e61b20c..970b3e05 100644 --- a/lessons/10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py +++ b/lessons/10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py @@ -3,11 +3,13 @@ In this exercise, you will use Tina the Turtle to draw an equilateral triangle. -Objectives: - 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. + +uid: H5P0FcIJ +name: Turtle Tricks """ # These lines are needed in most turtle programs 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 da1c38c7..b2a1ef90 100644 --- a/lessons/10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py +++ b/lessons/10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py @@ -3,11 +3,13 @@ In this assignment, you will use Tina the Turtle to draw a pentagon. -Objectives: - 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. + +uid: jIpnMhwi +name: Turtle Tricks """ # These lines are needed in most turtle programs 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 d0b4c7a4..214778f3 100644 --- a/lessons/10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py +++ b/lessons/10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py @@ -3,13 +3,15 @@ In this assignment, you will use Tina the Turtle to draw multiple shapes on the screen. -Objectives: - 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. - Challenge yourself to experiment with different colors and positions! Refer to the previous program, Meet_Tina.py for examples of how to use these turtle commands. + +uid: TMoSqbTM +name: Turtle Tricks """ # These lines are needed in most turtle programs diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 424b9149..53f8cfbc 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -1,391 +1,392 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Loops**\n", - "\n", - "Welcome to the world of **loops**! In this lesson, you'll discover how loops make programming easier and more powerful by allowing you to automate repetitive tasks, save time, write cleaner code, and solve problems more efficiently. By pairing loops with *Tina the Turtle*, you'll be able to simplify your programs, reduce mistakes, and create amazing things with less effort!\n", - "\n", - "> **Tip:** Take your time to practice using loops. They are a core concept in every programming language, and mastering them will greatly enhance your coding skills." - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Loops**\n", + "\n", + "Welcome to the world of **loops**! In this lesson, you'll discover how loops make programming easier and more powerful by allowing you to automate repetitive tasks, save time, write cleaner code, and solve problems more efficiently. By pairing loops with *Tina the Turtle*, you'll be able to simplify your programs, reduce mistakes, and create amazing things with less effort!\n", + "\n", + "> **Tip:** Take your time to practice using loops. They are a core concept in every programming language, and mastering them will greatly enhance your coding skills." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Why Use Loops?**\n", + "\n", + "Instead 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", + "\n", + "Loops are super 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", + "\n", + "Let\u2019s 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", + "\n", + "Let\u2019s see how we can use loops to make this code cleaner and easier to manage. Maybe we can even add more shapes without making the code super long!" + ] + }, + { + "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\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 fast, but not too fast \n", + "\n", + "# Draw a square using a loop\n", + "for i in range(4): # Loop 4 times\n", + " tina.forward(100) # Move forward 100 units\n", + " tina.left(90) # Turn left 90 degrees\n", + "\n", + "tina.penup() # Lift the pen to move without drawing\n", + "tina.goto(125, 0) # Move tina to over to the right of the canvas\n", + "tina.pendown() # Put the pen down to start drawing\n", + "\n", + "# Draw a triangle using a loop\n", + "for 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\n", + "tina.penup() # Lift the pen to move without drawing\n", + "tina.goto(260, 50) # Move tina further to the right of the canvas\n", + "tina.pendown() # Put the pen down to start drawing\n", + "\n", + "tina.speed('fastest') # Set speed to fastest for a faster star drawing\n", + "\n", + "# Draw a star pattern using a loop\n", + "for 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 can make your code much cleaner and easier to understand!\n", + "\n", + "
\n", + " Click here for a detailed explanation\n", + "\n", + "Here's what\u2019s 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", + "\n", + "For 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\n", + "for 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", + "```\n", + "
\n", + "
\n", + "\n", + "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them\u2014understanding loops will help you solve problems more efficiently and write better code!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Practicing with Loops**\n", + "\n", + "Try running these examples to see how loops can be used in different ways!\n", + "\n", + "### **Drawing an Orange Square**\n", + "\n", + "This 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('\ud83d\udfe7' * size) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " Click here for a detailed explanation\n", + "\n", + "Here\u2019s what\u2019s 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 `'\ud83d\udfe7' * size`.\n", + "
\n", + "
\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('\u26ab\ufe0f', end='')\n", + " # Otherwise, print a white circle\n", + " else:\n", + " print('\u26aa\ufe0f', end='')\n", + " # Go to the next line\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " Click here for a detailed explanation\n", + "\n", + "Here\u2019s what\u2019s 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 (`\u26ab\ufe0f`); otherwise, it prints a white circle (`\u26aa\ufe0f`).\n", + "- `print()` at the end of the inner loop moves to the next line, creating a grid.\n", + "
\n", + "
\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('\u2b50', end='') \n", + " else:\n", + " # Print a blue square\n", + " print('\ud83d\udfe6', end='') \n", + " else:\n", + " # Alternate red and white stripes\n", + " if row % 2 == 0:\n", + " # Print a red stripe \n", + " print('\ud83d\udfe5', end='') \n", + " else:\n", + " # Print a white stripe\n", + " print('\u2b1c\ufe0f', end='')\n", + " # Move to the next line after each row \n", + " print() " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " Click here for a detailed explanation\n", + "\n", + "Here\u2019s what\u2019s 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 (`\ud83d\udfe6`).\n", + "- `else` checks if the current row is even or odd to determine whether to print a red stripe (`\ud83d\udfe5`) or a white stripe (`\u2b1c\ufe0f`).\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", + "
\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": ".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": "abX8sNwB", + "name": "Loops" + } }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Why Use Loops?**\n", - "\n", - "Instead 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", - "\n", - "Loops are super 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", - "\n", - "Let’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", - "\n", - "Let’s see how we can use loops to make this code cleaner and easier to manage. Maybe we can even add more shapes without making the code super long!" - ] - }, - { - "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\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 fast, but not too fast \n", - "\n", - "# Draw a square using a loop\n", - "for i in range(4): # Loop 4 times\n", - " tina.forward(100) # Move forward 100 units\n", - " tina.left(90) # Turn left 90 degrees\n", - "\n", - "tina.penup() # Lift the pen to move without drawing\n", - "tina.goto(125, 0) # Move tina to over to the right of the canvas\n", - "tina.pendown() # Put the pen down to start drawing\n", - "\n", - "# Draw a triangle using a loop\n", - "for 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\n", - "tina.penup() # Lift the pen to move without drawing\n", - "tina.goto(260, 50) # Move tina further to the right of the canvas\n", - "tina.pendown() # Put the pen down to start drawing\n", - "\n", - "tina.speed('fastest') # Set speed to fastest for a faster star drawing\n", - "\n", - "# Draw a star pattern using a loop\n", - "for 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 can make your code much cleaner and easier to understand!\n", - "\n", - "
\n", - " Click here for a detailed explanation\n", - "\n", - "Here'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", - "\n", - "For 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\n", - "for 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", - "```\n", - "
\n", - "
\n", - "\n", - "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them—understanding loops will help you solve problems more efficiently and write better code!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **Practicing with Loops**\n", - "\n", - "Try running these examples to see how loops can be used in different ways!\n", - "\n", - "### **Drawing an Orange Square**\n", - "\n", - "This 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": [ - "
\n", - " Click here for a detailed explanation\n", - "\n", - "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", - "
\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": [ - "
\n", - " Click here for a detailed explanation\n", - "\n", - "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", - "
\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": [ - "
\n", - " Click here for a detailed explanation\n", - "\n", - "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", - "
\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": ".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": "abX8sNwB" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "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 fc5a5abe..3df09fbe 100644 --- a/lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py +++ b/lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py @@ -6,8 +6,10 @@ In this exercise, you will modify the program to use a loop to draw the square instead. -Objectives: - 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 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 98166d97..1909d7d5 100644 --- a/lessons/10_Turtles/40_Loops/30_Loop_with_Turtle.py +++ b/lessons/10_Turtles/40_Loops/30_Loop_with_Turtle.py @@ -3,12 +3,14 @@ In this program, use a loop to draw a regular pentagon (5-sided shape) with Tina the Turtle. -Objectives: - 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 index b226ed16..26a3b346 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb @@ -1,288 +1,289 @@ { - "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" - ] + "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\u2014numbers, 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", + "\n", + "If 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! \ud83c\udfaf" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Exploring Variables in Loops**\n", + "\n", + "You may not have noticed, but you have been working with variables all along!\n", + "\n", + "Let'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\u2014they let you write code that adapts automatically!\n", + "\n", + "For 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", + "\n", + "With variables, Tina the turtle can draw any polygon\u2014triangle, square, hexagon, or even a 9-sided shape\u2014just 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\u2019t need to memorize all these types right now\u2014let\u2019s 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)\n", + "my_age = 12 # This is an integer (a whole number)\n", + "temperature = 98.6 # This is a float (a fractional number)\n", + "\n", + "# Text (strings)\n", + "my_name = \"Alice\" \n", + "favorite_color = \"blue\"\n", + "\n", + "# Boolean values (True/False)\n", + "is_sunny = True\n", + "is_raining = False\n", + "\n", + "print(\"Name:\", my_name)\n", + "print(\"Age:\", my_age)\n", + "print(\"Temperature:\", temperature)\n", + "print(\"Favorite color:\", favorite_color)\n", + "print(\"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\n", + "pizzas = 5 # Number of pizzas ordered for the party\n", + "slices_per_pizza = 8 # Number of slices in each pizza\n", + "people = 12 # Number of people sharing the pizzas\n", + "\n", + "# Now it's time for a little bit of math\n", + "total_slices = pizzas * slices_per_pizza # Multiply to get total slices\n", + "slices_per_person = total_slices / people # Divide to find slices each person gets\n", + "\n", + "# Finally, we can print our results\n", + "print(\"We have\", pizzas, \"pizzas\") # Show how many pizzas there are\n", + "print(\"Each pizza has\", slices_per_pizza, \"slices\") # Show slices per pizza\n", + "print(\"Total slices:\", total_slices) # Show total number of slices\n", + "print(\"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 (Optional)**\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": { + "uid": "HOBo0wvj", + "name": "Variables" + } }, - { - "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", - "\n", - "If 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", - "\n", - "You may not have noticed, but you have been working with variables all along!\n", - "\n", - "Let'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", - "\n", - "For 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", - "\n", - "With 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)\n", - "my_age = 12 # This is an integer (a whole number)\n", - "temperature = 98.6 # This is a float (a fractional number)\n", - "\n", - "# Text (strings)\n", - "my_name = \"Alice\" \n", - "favorite_color = \"blue\"\n", - "\n", - "# Boolean values (True/False)\n", - "is_sunny = True\n", - "is_raining = False\n", - "\n", - "print(\"Name:\", my_name)\n", - "print(\"Age:\", my_age)\n", - "print(\"Temperature:\", temperature)\n", - "print(\"Favorite color:\", favorite_color)\n", - "print(\"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\n", - "pizzas = 5 # Number of pizzas ordered for the party\n", - "slices_per_pizza = 8 # Number of slices in each pizza\n", - "people = 12 # Number of people sharing the pizzas\n", - "\n", - "# Now it's time for a little bit of math\n", - "total_slices = pizzas * slices_per_pizza # Multiply to get total slices\n", - "slices_per_person = total_slices / people # Divide to find slices each person gets\n", - "\n", - "# Finally, we can print our results\n", - "print(\"We have\", pizzas, \"pizzas\") # Show how many pizzas there are\n", - "print(\"Each pizza has\", slices_per_pizza, \"slices\") # Show slices per pizza\n", - "print(\"Total slices:\", total_slices) # Show total number of slices\n", - "print(\"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 (Optional)**\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": { - "uid": "HOBo0wvj" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb index d9769e40..e919707a 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb @@ -1,355 +1,356 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Functions**\n", - "\n", - "In 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" - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Functions**\n", + "\n", + "In 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\n", + "def 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\n", + "triangle_angle = calculate_angle(3) # Call the function with 3 sides (triangle)\n", + "square_angle = calculate_angle(4) # Call the function with 4 sides (square) \n", + "hexagon_angle = calculate_angle(6) # Call the function with 6 sides (hexagon)\n", + "\n", + "# Print the results\n", + "print(\"Triangle angle:\", triangle_angle)\n", + "print(\"Square angle:\", square_angle)\n", + "print(\"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", + "\n", + "def 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", + "\n", + "def multiply_numbers(a, b): # Multiplies two numbers and returns the product\n", + " return a * b # Return the product\n", + "\n", + "def calculate_area(length, width): # Calculates the area of a rectangle\n", + " area = length * width # Area = length \u00d7 width\n", + " return area # Return the area\n", + "\n", + "# Call the functions and store their results in variables\n", + "\n", + "sum_result = add_numbers(10, 5) # Store the sum of 10 and 5\n", + "product = multiply_numbers(4, 7) # Store the product of 4 and 7\n", + "room_area = calculate_area(12, 10) # Store the area of a 12\u00d710 room\n", + "\n", + "# Print the results returned from the functions\n", + "\n", + "print(\"10 + 5 =\", sum_result)\n", + "print(\"4 \u00d7 7 =\", product)\n", + "print(\"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", + "\n", + "# Decision function\n", + "def 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\n", + "def 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\n", + "def 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 \u00d7 slices each person gets\n", + " pizzas_needed = -(-total_slices_needed // 8) # Round up to nearest whole pizza (8 slices per pizza)\n", + " return total_slices_needed, pizzas_needed # Return both values at once!\n", + "\n", + "# Test functions and print results\n", + "weather_report = check_temperature(75) # Test temperature function with 75 degrees\n", + "print(weather_report)\n", + "\n", + "age_valid = is_valid_age(25) # Test age validation with 25 years old\n", + "print(\"Is age 25 valid?\", age_valid)\n", + "\n", + "slices, pizzas = pizza_calculator(10) # Calculate pizza needs for 10 people (uses default 3 slices each)\n", + "print(f\"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", + "\n", + "Now 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", + "\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", + "# Function to draw any polygon\n", + "def 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\n", + "def 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!\n", + "draw_polygon(3, 50, \"red\") # Draw a red triangle\n", + "move_tina(100, 0) # Move to a new spot\n", + "draw_polygon(4, 60, \"blue\") # Draw a blue square\n", + "move_tina(225, 0) # Move again\n", + "draw_polygon(6, 40, \"green\") # Draw a green hexagon\n", + "\n", + "# See how functions make our code cleaner and easier to reuse?\n", + "# Try calling the functions with different parameters!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Function Challenges (Optional)**\n", + "\n", + "Now it's your turn! Write 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 \u00d7 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": { + "uid": "0CwXIYSb", + "name": "Functions" + } }, - { - "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\n", - "def 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\n", - "triangle_angle = calculate_angle(3) # Call the function with 3 sides (triangle)\n", - "square_angle = calculate_angle(4) # Call the function with 4 sides (square) \n", - "hexagon_angle = calculate_angle(6) # Call the function with 6 sides (hexagon)\n", - "\n", - "# Print the results\n", - "print(\"Triangle angle:\", triangle_angle)\n", - "print(\"Square angle:\", square_angle)\n", - "print(\"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", - "\n", - "def 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", - "\n", - "def multiply_numbers(a, b): # Multiplies two numbers and returns the product\n", - " return a * b # Return the product\n", - "\n", - "def 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", - "\n", - "sum_result = add_numbers(10, 5) # Store the sum of 10 and 5\n", - "product = multiply_numbers(4, 7) # Store the product of 4 and 7\n", - "room_area = calculate_area(12, 10) # Store the area of a 12×10 room\n", - "\n", - "# Print the results returned from the functions\n", - "\n", - "print(\"10 + 5 =\", sum_result)\n", - "print(\"4 × 7 =\", product)\n", - "print(\"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", - "\n", - "# Decision function\n", - "def 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\n", - "def 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\n", - "def 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 = -(-total_slices_needed // 8) # Round up to nearest whole pizza (8 slices per pizza)\n", - " return total_slices_needed, pizzas_needed # Return both values at once!\n", - "\n", - "# Test functions and print results\n", - "weather_report = check_temperature(75) # Test temperature function with 75 degrees\n", - "print(weather_report)\n", - "\n", - "age_valid = is_valid_age(25) # Test age validation with 25 years old\n", - "print(\"Is age 25 valid?\", age_valid)\n", - "\n", - "slices, pizzas = pizza_calculator(10) # Calculate pizza needs for 10 people (uses default 3 slices each)\n", - "print(f\"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", - "\n", - "Now 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", - "\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", - "# Function to draw any polygon\n", - "def 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\n", - "def 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!\n", - "draw_polygon(3, 50, \"red\") # Draw a red triangle\n", - "move_tina(100, 0) # Move to a new spot\n", - "draw_polygon(4, 60, \"blue\") # Draw a blue square\n", - "move_tina(225, 0) # Move again\n", - "draw_polygon(6, 40, \"green\") # Draw a green hexagon\n", - "\n", - "# See how functions make our code cleaner and easier to reuse?\n", - "# Try calling the functions with different parameters!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **Function Challenges (Optional)**\n", - "\n", - "Now it's your turn! Write 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": { - "uid": "0CwXIYSb" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py b/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py index 40dcd72f..070f1da2 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py +++ b/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py @@ -1,13 +1,14 @@ - """ # 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. -Objectives: - 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. + +uid: xvDQudwV +name: Efficient Turtle """ import turtle # Tell Python we want to work with the turtle 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 d7c34b5c..4719048b 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,329 +1,330 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Turtle Tricks and Tips**\n", - "\n", - "Although we've used a lot of Turtle examples so far, the turtle module has many more\n", - "features. The official Python documentation lists everything the turtle can do.\n", - "\n", - "A 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", - "\n", - "Check 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" + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Turtle Tricks and Tips**\n", + "\n", + "Although we've used a lot of Turtle examples so far, the turtle module has many more\n", + "features. The official Python documentation lists everything the turtle can do.\n", + "\n", + "A 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", + "\n", + "Check 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", + "\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 # 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\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() # Prevent drawing when moving\n", + "t.speed(3) # Set a moderate speed\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": [ + "> **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", + "\n", + "import turtle as turtle # Import the turtle module with the name turtle\n", + "\n", + "screen = turtle.Screen() # Set up the screen\n", + "screen.setup(width=600, height=600) # Set the size of the window\n", + "screen.bgcolor('white') # Set the background color\n", + "\n", + "t1 = turtle.Turtle() # Create the first turtle\n", + "t1.penup() # Lift the pen to move without drawing\n", + "t1.shape(\"turtle\") # Set the shape of the turtle\n", + "\n", + "t2 = turtle.Turtle() # Create the second turtle\n", + "t2.penup() # Lift the pen to move without drawing\n", + "t2.shape(\"arrow\") # Set the shape of the turtle\n", + "\n", + "# Move both turtles in a loop\n", + "for 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", + "\n", + "import turtle as turtle\n", + "\n", + "screen = turtle.Screen() # Set up the screen\n", + "screen.setup(width=600, height=600) # Set the size of the window\n", + "screen.bgcolor('white') # Set the background color\n", + "\n", + "t = turtle.Turtle() # Create a turtle\n", + "t.penup() # Prevent drawing when moving\n", + "t.shape(\"turtle\") # Set the shape of the 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) # Move the turtle to the clicked location\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": [ + "## **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", + "\n", + "import turtle as turtle # Import the turtle module with the name turtle\n", + "\n", + "screen = turtle.Screen() # Set up the screen\n", + "screen.setup(width=600, height=600) # Set the size of the window\n", + "screen.bgcolor('white') # Set the background color\n", + "\n", + "t = turtle.Turtle() # Create a turtle\n", + "t.shape(\"turtle\") # Set the shape of the 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", + "
\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 would be a great time to do so! 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" + }, + "syllabus": { + "uid": "IloYptI2", + "name": "More Turtle Programs" } - }, - "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", - "\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 # 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\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() # Prevent drawing when moving\n", - "t.speed(3) # Set a moderate speed\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": [ - "> **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", - "\n", - "import turtle as turtle # Import the turtle module with the name turtle\n", - "\n", - "screen = turtle.Screen() # Set up the screen\n", - "screen.setup(width=600, height=600) # Set the size of the window\n", - "screen.bgcolor('white') # Set the background color\n", - "\n", - "t1 = turtle.Turtle() # Create the first turtle\n", - "t1.penup() # Lift the pen to move without drawing\n", - "t1.shape(\"turtle\") # Set the shape of the turtle\n", - "\n", - "t2 = turtle.Turtle() # Create the second turtle\n", - "t2.penup() # Lift the pen to move without drawing\n", - "t2.shape(\"arrow\") # Set the shape of the turtle\n", - "\n", - "# Move both turtles in a loop\n", - "for 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", - "\n", - "import turtle as turtle\n", - "\n", - "screen = turtle.Screen() # Set up the screen\n", - "screen.setup(width=600, height=600) # Set the size of the window\n", - "screen.bgcolor('white') # Set the background color\n", - "\n", - "t = turtle.Turtle() # Create a turtle\n", - "t.penup() # Prevent drawing when moving\n", - "t.shape(\"turtle\") # Set the shape of the 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) # Move the turtle to the clicked location\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": [ - "## **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", - "\n", - "import turtle as turtle # Import the turtle module with the name turtle\n", - "\n", - "screen = turtle.Screen() # Set up the screen\n", - "screen.setup(width=600, height=600) # Set the size of the window\n", - "screen.bgcolor('white') # Set the background color\n", - "\n", - "t = turtle.Turtle() # Create a turtle\n", - "t.shape(\"turtle\") # Set the shape of the 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", - "
\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 would be a great time to do so! 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" }, - "syllabus": { - "uid": "IloYptI2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "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 index d58db138..5541f705 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 @@ -3,5 +3,9 @@ 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. + +uid: rW4r7JTo +name: More Turtle Programs +""" + 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 0fd19961..0f9e310b 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 @@ -3,5 +3,9 @@ from the section "Set a Background Picture" 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. + +uid: bOC8a3SH +name: More Turtle Programs +""" + 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 e9f595b8..7fc2b61f 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 @@ -10,4 +10,8 @@ import random x = random.randint(-300, 300) y = random.randint(-300, 300) -""" \ No newline at end of file + +uid: VkkULcqs +name: More Turtle Programs +""" + diff --git a/lessons/10_Turtles/70_Projects/10_LeagueBot.py b/lessons/10_Turtles/70_Projects/10_LeagueBot.py index 64076070..762cefb3 100644 --- a/lessons/10_Turtles/70_Projects/10_LeagueBot.py +++ b/lessons/10_Turtles/70_Projects/10_LeagueBot.py @@ -1,4 +1,4 @@ -""" +""" LeagueBot Write your own turtle program! Here is what your program should do @@ -6,7 +6,10 @@ 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. + +uid: SVGBk0CR +name: Leaguebot """ 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..b90bdcc7 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,9 @@ 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 'Change the Background Image' and +uid: MslKVn8T +name: Tash Me """ ... # 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 5a7cde90..5521b72e 100644 --- a/lessons/10_Turtles/70_Projects/30_Tash_Me_Click.py +++ b/lessons/10_Turtles/70_Projects/30_Tash_Me_Click.py @@ -4,6 +4,9 @@ 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' +uid: fkXwl2jK +name: Tash Me Click """ + ... # 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 539fea99..cb84297b 100644 --- a/lessons/10_Turtles/70_Projects/40_Tash_Me_Twirl.py +++ b/lessons/10_Turtles/70_Projects/40_Tash_Me_Twirl.py @@ -1,9 +1,11 @@ -""" +""" # 40_Tash_Me_Twirl.py -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. +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 10_More_Turtle_Programs, section 'Respond to Screen Clicks' +uid: OjU5u3MU +name: Tash Me Twirl """ ... # Your code here \ No newline at end of file diff --git a/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb b/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb index d50cb4f8..b626a4f1 100644 --- a/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb +++ b/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb @@ -1,259 +1,260 @@ { - "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:" - ] + "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", + "- `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", + "- slicing: `list[start:stop]` returns a sub-list" + ] + }, + { + "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, comprehensions, nested\n", + "lists, and more), but 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", + "\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", + "\n", + "for left in [90, 90, 90, 90]:\n", + " tina.forward(forward)\n", + " tina.left(left)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Excercises (Optional)**\n", + "\n", + "If 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": { + "uid": "0KEhJUGe", + "name": "Lists" + } }, - { - "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", - "- `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", - "- slicing: `list[start:stop]` returns a sub-list" - ] - }, - { - "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, comprehensions, nested\n", - "lists, and more), but 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", - "\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", - "\n", - "for left in [90, 90, 90, 90]:\n", - " tina.forward(forward)\n", - " tina.left(left)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **Excercises (Optional)**\n", - "\n", - "If 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": { - "uid": "0KEhJUGe" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "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_Introducting_Lists/20_Color_Lines.py index 01217c64..bba9235f 100644 --- a/lessons/10_Turtles/80_Introducting_Lists/20_Color_Lines.py +++ b/lessons/10_Turtles/80_Introducting_Lists/20_Color_Lines.py @@ -1,7 +1,10 @@ """ # 20_Color_Lines.py -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. + +uid: qR8SGkHW +name: Color Lines """ import turtle # Tell Python we want to work with the turtle 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 9c0773d8..e727ca25 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 @@ -2,7 +2,10 @@ # 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. +Then change it to make it draw a different pattern. + +uid: ejUIkGvk +name: Flaming Ninja Star """ import random 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 d3dfc781..c7fc826d 100644 --- a/lessons/10_Turtles/90_Graphics_Projects/20_Crazy_Spiral.py +++ b/lessons/10_Turtles/90_Graphics_Projects/20_Crazy_Spiral.py @@ -3,6 +3,9 @@ Make your own crazy spiral with a pattern like 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 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 c609bec8..1c54825e 100644 --- a/lessons/10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py +++ b/lessons/10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py @@ -2,6 +2,9 @@ # 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 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 e783ad88..37d06dfa 100644 --- a/lessons/10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py +++ b/lessons/10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py @@ -2,6 +2,9 @@ # 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 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 02ff6b91..00000000 --- a/lessons/20_Types_and_Logic/10_Numbers_and_Strings.ipynb +++ /dev/null @@ -1,616 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Numbers and Strings**\n", - "\n", - "In this lesson you'll explore how Python represents numbers and text, how to convert between them, and some useful operators and methods.\n", - "\n", - "
\n", - " Click here for a friendly reminder about Notebooks\n", - "\n", - "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", - "* Press esc to enter Command Mode if you want to move, copy, or delete cells (the blue outline disappears in Command Mode).\n", - "* When a cell is active you'll see a small menu in the upper-right with extra options.\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)` or `Python 3.13.5`).\n", - "\n", - "\n", - "\n", - "After selecting the interpreter, re-run the cell. \n", - "\n", - "Ask your instructor if you are still confused or need help connecting the kernel.\n", - "\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Getting Started**\n", - "\n", - "Although we have already worked with variables before in the past [lesson](../10_Turtles/50_Variables_and_Functions/10_Variables.ipynb) (feel free to click the link for a refresher), we will now dive deeper into them and examine the various data types available in Python.\n", - "\n", - "When you're set to begin, execute 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, etc. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "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 are whole numbers without decimals, like `3`\n", - "* floats are real numbers that can have a decimal part, like `3.5`\n", - "* strings are text data that is **immutable**, like `\"Alice\"`\n", - "* lists are ordered sequences that are **mutable**, like `['red', 'blue']`\n", - "\n", - "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", - "\n", - "Different types support different operations — for example, `+` performs arithmetic on numbers but concatenation on strings.\n", - "\n", - "Lets 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\n", - "x = 10\n", - "y = 20\n", - "\n", - "# Add x and y together\n", - "print(\"x + y =\", x + y)\n", - "\n", - "# Strings\n", - "x = \"10\"\n", - "y = \"20\"\n", - "\n", - "# Add x and y together\n", - "print(\"x + y =\", x + y) # Watch what happens!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you might have expected, when we added $10$ and $20$ as integers we got $30$, but when we added them as strings, Python concatenated them together instead!\n", - "\n", - "So 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 and didn't work because you can't add an integer to a string! But you *can* convert one to the other data type and then add them. \n", - "\n", - "### **Type Conversion**\n", - "To convert these variables, we will use both the `int()` and `str()` functions. " - ] - }, - { - "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(int(y) + x)\n", - "\n", - "# Convert x to a string, then add x and y together\n", - "print(str(x) + y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "> **Tip:** Conversion functions have the same name as the type they convert to, this will help you remember them!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Operators**\n", - "\n", - "Below are some common math operators for numbers—you’ve likely used most of them, though a couple might be new.\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$ |\n", - "\n", - "Floor division uses the `//` operator, it divides then drops the remaining fractional part, which means it returns an integer when used with integers, but a float when used with floats. \n", - "\n", - "For example, `11 // 4` results in `2` and `11.0 // 4.0` results in `2.0` even though normal division would result in `2.75`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Regular division will result in a float\n", - "\n", - "print(10 / 3)\n", - "\n", - "# Floor division is always an integer\n", - "\n", - "print(10 // 3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Modulo uses the ` % ` operator and returns the remainder after division. It's useful for checking for evenly divisible numbers, like if `n` is divisible by `3` (e.g., `n % 3 == 0`).\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Modulo Operator\n", - "\n", - "print(\"9 % 3 == \" , 10 % 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 the `//` and `%` operators is that they are closely related. They allow you to break down a number into parts that can be reassembled using a simple 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": "markdown", - "metadata": {}, - "source": [ - "As shown above, when you divide `11` by `4`, the quotient is `2` and the remainder is `3`. You can reconstruct the original number using the formula below: \n", - "\n", - "`original_number = (divisor * quotient) + remainder`" - ] - }, - { - "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": [ - ">**Tip:** Modulo works a bit like a clock. The numbers count up, but once they reach a certain limit (the divisor), they wrap back around to zero.\n", - "\n", - "The most common use for the modulo operator is to check if a number is evenly divisible by another. If the result is 0, it means there is no remainder, so it divides evenly. For example, 6 is evenly divisible by 3 because `6 % 3` is `0`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Candy Distribution**\n", - "\n", - "Start with two variables: `kids` (number of kids) and `candy_bars` (number of candy bars). Calculate how to evenly distribute the candy bars to the kids — print how many each kid gets and how many are left over.\n" - ] - }, - { - "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": "markdown", - "metadata": {}, - "source": [ - "## **Numbers**\n", - "\n", - "Python has two main numeric types, integers (`int`) for whole numbers and floating-point numbers (`float`) for values with a fractional part (e.g., like a decimal: $3.14$).\n", - "\n", - "Integers can be written in four different bases:\n", - "\n", - "* **Binary (Base 2):** $0b100101$\n", - "* **Octal (Base 8):** $0o45$\n", - "* **Decimal (Base 10):** $37$\n", - "* **Hexadecimal (Base 16):** $0x25$\n", - "\n", - "For example, the decimal number $237$ equals $(2 \\times 100) + (3 \\times 10) + (7 \\times 1)$ — that's $2$ hundreds, $3$ tens, and $7$ ones, or in exponents, its $(2 \\times 10^2) + (3 \\times 10^1) + (7 \\times 10^0)$.\n", - "\n", - "Other number bases work the same way, but they use different place values.\n", - "\n", - "This diagram shows how these bases relate to each other:\n", - "\n", - "\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", - "\n", - "If 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)" - ] - }, - { - "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 = '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": "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", - "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.\n" - ] - }, - { - "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": [ - "
\n", - "REMINDER: Don't forget to 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": { - "uid": "HXQZ0Iui" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb b/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb new file mode 100644 index 00000000..366bfed6 --- /dev/null +++ b/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb @@ -0,0 +1,387 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Types and Operators**\n", + "\n", + "In this lesson you'll explore how Python represents numbers and 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* \u2014 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 \u21e7 Shift + \u23ce Enter to run the cell.\n", + "* You can also use the \u25b6\ufe0f button in the cell toolbar to run a cell too.\n", + "* Press esc to enter Command Mode if you want to move, copy, or delete cells (the blue outline disappears in Command Mode).\n", + "* When a cell is active you'll see a small menu in the upper-right with extra options.\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) or Python 3.13.5).\n", + "\n", + "\n", + "\n", + "After selecting the interpreter, re-run the cell. \n", + "\n", + "Ask your instructor if you are still confused or need help connecting the kernel.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Getting Started**\n", + "\n", + "Although we have already worked with variables before in the past [lesson](../10_Turtles/50_Variables_and_Functions/10_Variables.ipynb) (feel free to click the link for a refresher), we will now dive deeper into them and examine the various data types available in Python.\n", + "\n", + "When you're set to begin, execute 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, etc. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "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 are whole numbers without decimals, like `3`\n", + "* floats are real numbers that can have a decimal part, like `3.5`\n", + "* strings are text data that is **immutable**, like `\"Alice\"`\n", + "* lists are ordered sequences that are **mutable**, like `['red', 'blue']`\n", + "\n", + "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", + "\n", + "Different types support different operations \u2014 for example, `+` performs arithmetic on numbers but concatenation on strings.\n", + "\n", + "Lets 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\n", + "x = 10\n", + "y = 20\n", + "\n", + "# Add x and y together\n", + "print(\"x + y =\", x + y)\n", + "\n", + "# Strings\n", + "x = \"10\"\n", + "y = \"20\"\n", + "\n", + "# Add x and y together\n", + "print(\"x + y =\", x + y) # Watch what happens!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you might have expected, when we added `10` and `20` as integers we got `30`, but when we added them as strings, they were concatenated together instead!\n", + "\n", + "So 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 and didn't work because you can't add an integer to a string! But you *can* convert one to the other data type and then add them. \n", + "\n", + "### **Type Conversion**\n", + "To convert these variables, we will use both the `int()` and `str()` functions. " + ] + }, + { + "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(int(y) + x)\n", + "\n", + "# Convert x to a string, then add x and y together\n", + "print(str(x) + y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> **Tip:** Conversion functions have the same name as the type they convert to, this will help you remember them!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Operators**\n", + "\n", + "Below are some common math operators for numbers\u2014you\u2019ve likely used most of them, though a couple might be new.\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$ |\n", + "\n", + "Floor division uses the `//` operator, it divides then drops the remaining fractional part, which means it returns an integer when used with integers, but a float when used with floats. \n", + "\n", + "For example, `11 // 4` results in `2` and `11.0 // 4.0` results in `2.0` even though normal division would result in `2.75`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Regular division will result in a float\n", + "\n", + "print(10 / 3)\n", + "\n", + "# Floor division is always an integer\n", + "\n", + "print(10 // 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Modulo uses the ` % ` operator and returns the remainder after division. It's useful for checking for evenly divisible numbers, like if `n` is divisible by `3` (e.g., `n % 3 == 0`).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "# Modulo Operator\n", + "\n", + "print(\"9 % 3 == \" , 10 % 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 the `//` and `%` operators is that they are closely related. They allow you to break down a number into parts that can be reassembled using a simple 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": "markdown", + "metadata": {}, + "source": [ + "As shown above, when you divide `11` by `4`, the quotient is `2` and the remainder is `3`. You can reconstruct the original number using this formula: \n", + "\n", + "$$\\text{original number} = (\\text{divisor} \\times \\text{quotient}) + \\text{remainder}$$" + ] + }, + { + "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": [ + "Modulo works a bit like a clock. The numbers count up from zero, but once they reach a certain limit (the divisor), they wrap back around to zero.\n", + "\n", + "The most common use for the modulo operator is to check if a number is evenly divisible by another. If the result is 0, it means there is no remainder, so it divides evenly. For example, `6` is evenly divisible by `3` because `6 % 3` is `0`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Candy Distribution**\n", + "\n", + "Start with two variables: `kids` (number of kids) and `candy_bars` (number of candy bars). Calculate how to evenly distribute the candy bars to the kids \u2014 print how many each kid gets and how many are left over.\n" + ] + }, + { + "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": "markdown", + "metadata": {}, + "source": [ + "
\n", + "REMINDER: Don't forget to 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": { + "uid": "HZQX0Iu2", + "name": "Types And Operators" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/lessons/20_Types_and_Logic/20_Work_With_Numbers.ipynb b/lessons/20_Types_and_Logic/20_Work_With_Numbers.ipynb new file mode 100644 index 00000000..6acc1856 --- /dev/null +++ b/lessons/20_Types_and_Logic/20_Work_With_Numbers.ipynb @@ -0,0 +1,264 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "289824a5", + "metadata": {}, + "source": [ + "## **Numbers**\n", + "\n", + "Python has two main numeric types, integers (`int`) for whole numbers and floating-point numbers (`float`) for values with a fractional part (e.g., like a decimal: $3.14$).\n", + "\n", + "Integers can be written in four different bases:\n", + "\n", + "* **Binary (Base 2):** $0b100101$\n", + "* **Octal (Base 8):** $0o45$\n", + "* **Decimal (Base 10):** $37$\n", + "* **Hexadecimal (Base 16):** $0x25$\n", + "\n", + "For example, the decimal number $237$ equals $(2 \\times 100) + (3 \\times 10) + (7 \\times 1)$ \u2014 that's $2$ hundreds, $3$ tens, and $7$ ones, or in exponents, its $(2 \\times 10^2) + (3 \\times 10^1) + (7 \\times 10^0)$.\n", + "\n", + "Other number bases work the same way, but they use different place values.\n", + "\n", + "This diagram shows how these bases relate to each other:\n", + "\n", + "\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", + "\n", + "If 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)" + ] + }, + { + "cell_type": "markdown", + "id": "a2aaaece", + "metadata": {}, + "source": [ + "### **Using Conversion Functions**\n", + "\n", + "You can convert numbers to these representations with `oct()`, `hex()`, and `bin()` \u2014 examples below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc5c8cde", + "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", + "id": "cc6dad7f", + "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, + "id": "2c9af1db", + "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", + "id": "825ef89f", + "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", + "id": "f16323c8", + "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, + "id": "9083601e", + "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": "code", + "execution_count": null, + "id": "c648ec96", + "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": "4c50da78", + "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, + "id": "a5074591", + "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": "dde1ed85", + "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.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0148be28", + "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": { + "name": "python", + "version": "3.13.5" + }, + "syllabus": { + "uid": "deN7dbNb", + "name": "Work With Numbers" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb b/lessons/20_Types_and_Logic/30_Control_Flow.ipynb similarity index 80% rename from lessons/20_Types_and_Logic/20_Control_Flow.ipynb rename to lessons/20_Types_and_Logic/30_Control_Flow.ipynb index cee38fdc..60b8e4db 100644 --- a/lessons/20_Types_and_Logic/20_Control_Flow.ipynb +++ b/lessons/20_Types_and_Logic/30_Control_Flow.ipynb @@ -6,7 +6,7 @@ "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", + "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:" ] @@ -37,7 +37,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In the `if` 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", + "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:" ] @@ -69,8 +69,8 @@ "| $==$ | 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", + "* `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 a very common error, so don't feel discouraged. Just try your best to memorize their differences and remember: *practice makes perfect!*" ] @@ -228,7 +228,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "> **Note:** There are so many useful methods in Python like `startswith` or `endswith` for checking parts of strings, and `in` 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!" + "> **Note:** There are so many useful methods in Python like `.startswith()` or `.endswith()` for checking parts of strings, and `.in()` 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!" ] }, { @@ -473,9 +473,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "g6JPkFUs" + "uid": "g6JPkFUs", + "name": "Control Flow" } }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/lessons/20_Types_and_Logic/30_My_Ages.py b/lessons/20_Types_and_Logic/40_My_Ages.py similarity index 97% rename from lessons/20_Types_and_Logic/30_My_Ages.py rename to lessons/20_Types_and_Logic/40_My_Ages.py index 59128593..b78c4439 100644 --- a/lessons/20_Types_and_Logic/30_My_Ages.py +++ b/lessons/20_Types_and_Logic/40_My_Ages.py @@ -26,6 +26,10 @@ the second is the message to show the user. messagebox.showinfo('What you are', "You are a baby.") + +TODO: +uid: ngBHdtPY +name: My Ages """ from tkinter import messagebox, simpledialog, Tk # import required modules diff --git a/lessons/20_Types_and_Logic/50_Infuriating_Calculator.py b/lessons/20_Types_and_Logic/50_Infuriating_Calculator.py index 0a25a999..dff05648 100644 --- a/lessons/20_Types_and_Logic/50_Infuriating_Calculator.py +++ b/lessons/20_Types_and_Logic/50_Infuriating_Calculator.py @@ -18,6 +18,9 @@ For the number, you can ask for a float or an integer with simpledialog.askfloat() or simpledialog.askinteger(), and for the math operation you can ask for a string with simpledialog.askstring(). + +uid: ZfzBcb1j +name: Infuriating Calculator """ # Import the required modules diff --git a/lessons/20_Types_and_Logic/40_Simple_Adder.py b/lessons/20_Types_and_Logic/60_Simple_Adder.py similarity index 94% rename from lessons/20_Types_and_Logic/40_Simple_Adder.py rename to lessons/20_Types_and_Logic/60_Simple_Adder.py index 62cc665c..daa8a963 100644 --- a/lessons/20_Types_and_Logic/40_Simple_Adder.py +++ b/lessons/20_Types_and_Logic/60_Simple_Adder.py @@ -5,6 +5,9 @@ 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. + +uid: Dn6cL8wy +name: Simple Adder """ # Import the required modules diff --git a/lessons/20_Types_and_Logic/60_Code_Challenges.ipynb b/lessons/20_Types_and_Logic/70_Code_Challenges.ipynb similarity index 98% rename from lessons/20_Types_and_Logic/60_Code_Challenges.ipynb rename to lessons/20_Types_and_Logic/70_Code_Challenges.ipynb index 54f6dec9..f0af3bfd 100644 --- a/lessons/20_Types_and_Logic/60_Code_Challenges.ipynb +++ b/lessons/20_Types_and_Logic/70_Code_Challenges.ipynb @@ -126,9 +126,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "jJI6aomB" + "uid": "jJI6aomB", + "name": "Code Challenges" } }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file diff --git a/lessons/30_Loops/010_Iteration.ipynb b/lessons/30_Loops/010_Iteration.ipynb index b14d9644..ee85bc6f 100644 --- a/lessons/30_Loops/010_Iteration.ipynb +++ b/lessons/30_Loops/010_Iteration.ipynb @@ -104,9 +104,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "ITpqcvxv" + "uid": "ITpqcvxv", + "name": "Iteration" } }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/lessons/30_Loops/020_Loops_with_Range.ipynb b/lessons/30_Loops/020_Loops_with_Range.ipynb index b72e68ed..cb6b8340 100644 --- a/lessons/30_Loops/020_Loops_with_Range.ipynb +++ b/lessons/30_Loops/020_Loops_with_Range.ipynb @@ -110,17 +110,17 @@ "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", + " 3, write \u201cFizz\u201d, and for every multiple of 5, write \u201cBuzz\u201d. For numbers which\n", + " are multiples of both 3 and 5, it should write \u201cFizzBuzz\u201d; 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 the number is evenly divisible by 5, print '\ud83e\udda1 badger'\n", + "* If the number is evenly divisible by 3, print '\ud83c\udf44 mushroom'\n", + "* If the number is evenly divisible by both 3 and 5, print '\ud83d\udc0d snake!'\n", "* If it is divisible by neither, print the number.\n", "\n", "Bonus: write the program without using the ``or`` operator." @@ -165,9 +165,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "WcGpR3Xg" + "uid": "WcGpR3Xg", + "name": "Loops With Range" } }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/lessons/30_Loops/030_Lists.ipynb b/lessons/30_Loops/030_Lists.ipynb index 62244960..402a7314 100644 --- a/lessons/30_Loops/030_Lists.ipynb +++ b/lessons/30_Loops/030_Lists.ipynb @@ -420,9 +420,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "7zsKa84X" + "uid": "7zsKa84X", + "name": "Lists" } }, "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/040_Crazy_Tina.py index 0ca78c00..060b6ae5 100644 --- a/lessons/30_Loops/040_Crazy_Tina.py +++ b/lessons/30_Loops/040_Crazy_Tina.py @@ -5,9 +5,12 @@ 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 diff --git a/lessons/30_Loops/050_Tuples.ipynb b/lessons/30_Loops/050_Tuples.ipynb index d3888cd1..2fb7646b 100644 --- a/lessons/30_Loops/050_Tuples.ipynb +++ b/lessons/30_Loops/050_Tuples.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Tuples — Lists You Can't Change\n", + "# Tuples \u2014 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", @@ -102,9 +102,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "9zTQza2e" + "uid": "9zTQza2e", + "name": "Tuples" } }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/lessons/30_Loops/060_Indexing_and_Slicing.ipynb b/lessons/30_Loops/060_Indexing_and_Slicing.ipynb index 253f052d..75a6519f 100644 --- a/lessons/30_Loops/060_Indexing_and_Slicing.ipynb +++ b/lessons/30_Loops/060_Indexing_and_Slicing.ipynb @@ -169,9 +169,10 @@ "version": "3.11.6" }, "syllabus": { - "uid": "P27f2L8k" + "uid": "P27f2L8k", + "name": "Indexing And Slicing" } }, "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/070_List_Story.py index 85d9ece2..8e514a98 100644 --- a/lessons/30_Loops/070_List_Story.py +++ b/lessons/30_Loops/070_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/080_Strings.ipynb b/lessons/30_Loops/080_Strings.ipynb index 9eea876d..57931887 100644 --- a/lessons/30_Loops/080_Strings.ipynb +++ b/lessons/30_Loops/080_Strings.ipynb @@ -37,7 +37,7 @@ "```python \n", "\n", "message = \"\"\"\n", - "“Hope” is the thing with feathers -\n", + "\u201cHope\u201d 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", @@ -479,9 +479,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "Tmg4QRhJ" + "uid": "Tmg4QRhJ", + "name": "Strings" } }, "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/090_Fizz_Buzz_Badgers.py index 243c6400..c79ff4ba 100644 --- a/lessons/30_Loops/090_Fizz_Buzz_Badgers.py +++ b/lessons/30_Loops/090_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/100_For_Loop_Gauntlet.ipynb b/lessons/30_Loops/100_For_Loop_Gauntlet.ipynb index 19ee75eb..827ad113 100644 --- a/lessons/30_Loops/100_For_Loop_Gauntlet.ipynb +++ b/lessons/30_Loops/100_For_Loop_Gauntlet.ipynb @@ -355,9 +355,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "8yGSkBgV" + "uid": "8yGSkBgV", + "name": "For Loop Gauntlet" } }, "nbformat": 4, "nbformat_minor": 5 -} +} \ 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..e30d2b56 100644 --- a/lessons/30_Loops/110_FizzBuzz_Gui_Grid.py +++ b/lessons/30_Loops/110_FizzBuzz_Gui_Grid.py @@ -24,8 +24,11 @@ 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 diff --git a/lessons/30_Loops/120_More_Iterables.ipynb b/lessons/30_Loops/120_More_Iterables.ipynb index a8deec7c..17c7b588 100644 --- a/lessons/30_Loops/120_More_Iterables.ipynb +++ b/lessons/30_Loops/120_More_Iterables.ipynb @@ -324,9 +324,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "PrKwTywv" + "uid": "PrKwTywv", + "name": "More Iterables" } }, "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 index e3ac97e7..49f2b126 100644 --- a/lessons/30_Loops/130_Iterable_Turtle.ipynb +++ b/lessons/30_Loops/130_Iterable_Turtle.ipynb @@ -38,9 +38,10 @@ "name": "python" }, "syllabus": { - "uid": "vTyp2WhX" + "uid": "vTyp2WhX", + "name": "Iterable Turtle" } }, "nbformat": 4, "nbformat_minor": 2 -} +} \ 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..4baa5765 100644 --- a/lessons/30_Loops/140_More_Loops.ipynb +++ b/lessons/30_Loops/140_More_Loops.ipynb @@ -229,12 +229,13 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "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..d0022334 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,11 @@ 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. + +Note: The prompts and output for your program will be in the teminal +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..90ecaf50 100644 --- a/lessons/30_Loops/160_Extras.ipynb +++ b/lessons/30_Loops/160_Extras.ipynb @@ -51,7 +51,7 @@ "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())))" + "print(list(set(\"Once upon a time a \ud83d\udc66 with a \ud83d\udc15 met a \ud83d\udc67 who had a \ud83d\udc08 and they went to the park to play with a \u26bd.\".split())))" ] }, { @@ -83,9 +83,10 @@ "version": "3.12.11" }, "syllabus": { - "uid": "VJSgvOr5" + "uid": "VJSgvOr5", + "name": "Extras" } }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/lessons/40_Data_Structures_Func/10_Functions.ipynb b/lessons/40_Data_Structures_Func/10_Functions.ipynb index cb8aee1c..cfcb875a 100644 --- a/lessons/40_Data_Structures_Func/10_Functions.ipynb +++ b/lessons/40_Data_Structures_Func/10_Functions.ipynb @@ -501,9 +501,10 @@ "version": "3.12.11" }, "syllabus": { - "uid": "4LyScnS5" + "uid": "4LyScnS5", + "name": "Functions" } }, "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 index 9c36766a..66f82783 100644 --- a/lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb +++ b/lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb @@ -555,9 +555,10 @@ "version": "3.12.11" }, "syllabus": { - "uid": "VXQdZcqg" + "uid": "VXQdZcqg", + "name": "Dicts Sets" } }, "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/30_Funny_Words_Db.py index d7b57f86..25e4d9fb 100644 --- a/lessons/40_Data_Structures_Func/30_Funny_Words_Db.py +++ b/lessons/40_Data_Structures_Func/30_Funny_Words_Db.py @@ -1,3 +1,8 @@ +""" +uid: VenVwSQz +name: Funny Words Db +""" + from guizero import App, Box, Text, TextBox, PushButton, ListBox, error """ diff --git a/lessons/40_Data_Structures_Func/40_Splat_Comprehension.ipynb b/lessons/40_Data_Structures_Func/40_Splat_Comprehension.ipynb index 06f40ad2..236d2f95 100644 --- a/lessons/40_Data_Structures_Func/40_Splat_Comprehension.ipynb +++ b/lessons/40_Data_Structures_Func/40_Splat_Comprehension.ipynb @@ -624,7 +624,8 @@ "version": "3.11.9" }, "syllabus": { - "uid": "mU94qia6" + "uid": "mU94qia6", + "name": "Splat Comprehension" } }, "nbformat": 4, diff --git a/lessons/40_Data_Structures_Func/50_Tic_Tac_Toe.py b/lessons/40_Data_Structures_Func/50_Tic_Tac_Toe.py index 9c90264b..bf7fff05 100644 --- a/lessons/40_Data_Structures_Func/50_Tic_Tac_Toe.py +++ b/lessons/40_Data_Structures_Func/50_Tic_Tac_Toe.py @@ -1,3 +1,10 @@ +""" +# 50_Tic_Tac_Toe.py + +uid: mNMfKPiT +name: Tic Tac Toe +""" + #imports from guizero import App, Box, PushButton, Text, info @@ -104,4 +111,4 @@ def do_turn(self, x, y): info("Tic-tac-toe","It's a draw!") ttt = TicTacToe(check_win) -ttt.start() +ttt.start() \ No newline at end of file diff --git a/lessons/50_Projects/20_Random_Walk.py b/lessons/50_Projects/20_Random_Walk.py index 51c445e7..44a0e433 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 @@ -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 From 2caa4b46dafde324fe9b4e9b4ac78cda01b859f2 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 8 Dec 2025 13:40:11 -0500 Subject: [PATCH 108/159] Trying to fix syllabus.yaml to read with lesson browser --- lessons/20_Types_and_Logic/20_Work_With_Numbers.ipynb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lessons/20_Types_and_Logic/20_Work_With_Numbers.ipynb b/lessons/20_Types_and_Logic/20_Work_With_Numbers.ipynb index 6acc1856..fc238364 100644 --- a/lessons/20_Types_and_Logic/20_Work_With_Numbers.ipynb +++ b/lessons/20_Types_and_Logic/20_Work_With_Numbers.ipynb @@ -5,7 +5,7 @@ "id": "289824a5", "metadata": {}, "source": [ - "## **Numbers**\n", + "## **Working With Numbers**\n", "\n", "Python has two main numeric types, integers (`int`) for whole numbers and floating-point numbers (`float`) for values with a fractional part (e.g., like a decimal: $3.14$).\n", "\n", @@ -16,7 +16,7 @@ "* **Decimal (Base 10):** $37$\n", "* **Hexadecimal (Base 16):** $0x25$\n", "\n", - "For example, the decimal number $237$ equals $(2 \\times 100) + (3 \\times 10) + (7 \\times 1)$ \u2014 that's $2$ hundreds, $3$ tens, and $7$ ones, or in exponents, its $(2 \\times 10^2) + (3 \\times 10^1) + (7 \\times 10^0)$.\n", + "For example, the decimal number $237$ equals $(2 \\times 100) + (3 \\times 10) + (7 \\times 1)$ — that's $2$ hundreds, $3$ tens, and $7$ ones, or in exponents, its $(2 \\times 10^2) + (3 \\times 10^1) + (7 \\times 10^0)$.\n", "\n", "Other number bases work the same way, but they use different place values.\n", "\n", @@ -44,7 +44,7 @@ "source": [ "### **Using Conversion Functions**\n", "\n", - "You can convert numbers to these representations with `oct()`, `hex()`, and `bin()` \u2014 examples below." + "You can convert numbers to these representations with `oct()`, `hex()`, and `bin()` — examples below." ] }, { @@ -256,9 +256,9 @@ }, "syllabus": { "uid": "deN7dbNb", - "name": "Work With Numbers" + "name": "Working With Numbers" } }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} From c0d09d83c520ccb1f173d668b800f603d930bc16 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 8 Dec 2025 13:59:47 -0500 Subject: [PATCH 109/159] Final fix to syllabus.yaml --- lessons/.jtl/syllabus.yaml | 17 ++++++++++------- .../10_Types_and_Operators.ipynb | 4 ++-- ...bers.ipynb => 20_Working_With_Numbers.ipynb} | 15 +-------------- .../20_Types_and_Logic/30_Control_Flow.ipynb | 6 +++--- lessons/20_Types_and_Logic/40_My_Ages.py | 4 ---- .../{60_Simple_Adder.py => 50_Simple_Adder.py} | 3 --- ...lculator.py => 60_Infuriating_Calculator.py} | 3 --- .../20_Types_and_Logic/70_Code_Challenges.ipynb | 6 +++--- 8 files changed, 19 insertions(+), 39 deletions(-) rename lessons/20_Types_and_Logic/{20_Work_With_Numbers.ipynb => 20_Working_With_Numbers.ipynb} (96%) rename lessons/20_Types_and_Logic/{60_Simple_Adder.py => 50_Simple_Adder.py} (94%) rename lessons/20_Types_and_Logic/{50_Infuriating_Calculator.py => 60_Infuriating_Calculator.py} (96%) diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index 566e2a1e..9c9aeb2a 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -121,22 +121,25 @@ modules: - name: Types and Logic uid: ryHvW6vk lessons: - - name: Numbers and Strings + - name: Types and Operators uid: HXQZ0Iui - exercise: 20_Types_and_Logic/10_Numbers_and_Strings.ipynb + exercise: 20_Types_and_Logic/10_Types_and_Operators.ipynb + - name: Working With Numbers + uid: b1Q9Y1j1 + exercise: 20_Types_and_Logic/20_Working_With_Numbers.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: My Ages - exercise: 20_Types_and_Logic/30_My_Ages.py + exercise: 20_Types_and_Logic/40_My_Ages.py display: true - name: Simple Adder - exercise: 20_Types_and_Logic/40_Simple_Adder.py + exercise: 20_Types_and_Logic/50_Simple_Adder.py - name: Infuriating Calculator - exercise: 20_Types_and_Logic/50_Infuriating_Calculator.py + exercise: 20_Types_and_Logic/60_Infuriating_Calculator.py - name: Code Challenges uid: jJI6aomB - exercise: 20_Types_and_Logic/60_Code_Challenges.ipynb + exercise: 20_Types_and_Logic/70_Code_Challenges.ipynb - name: Loops uid: TzgRqJlw lessons: diff --git a/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb b/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb index 366bfed6..505952da 100644 --- a/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb +++ b/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb @@ -378,8 +378,8 @@ "version": "3.13.5" }, "syllabus": { - "uid": "HZQX0Iu2", - "name": "Types And Operators" + "uid": "HXQZ0Iui", + "name": "Types and Operators" } }, "nbformat": 4, diff --git a/lessons/20_Types_and_Logic/20_Work_With_Numbers.ipynb b/lessons/20_Types_and_Logic/20_Working_With_Numbers.ipynb similarity index 96% rename from lessons/20_Types_and_Logic/20_Work_With_Numbers.ipynb rename to lessons/20_Types_and_Logic/20_Working_With_Numbers.ipynb index fc238364..b79036ca 100644 --- a/lessons/20_Types_and_Logic/20_Work_With_Numbers.ipynb +++ b/lessons/20_Types_and_Logic/20_Working_With_Numbers.ipynb @@ -2,7 +2,6 @@ "cells": [ { "cell_type": "markdown", - "id": "289824a5", "metadata": {}, "source": [ "## **Working With Numbers**\n", @@ -39,7 +38,6 @@ }, { "cell_type": "markdown", - "id": "a2aaaece", "metadata": {}, "source": [ "### **Using Conversion Functions**\n", @@ -50,7 +48,6 @@ { "cell_type": "code", "execution_count": null, - "id": "fc5c8cde", "metadata": {}, "outputs": [], "source": [ @@ -67,7 +64,6 @@ }, { "cell_type": "markdown", - "id": "cc6dad7f", "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" @@ -76,7 +72,6 @@ { "cell_type": "code", "execution_count": null, - "id": "2c9af1db", "metadata": {}, "outputs": [], "source": [ @@ -105,7 +100,6 @@ }, { "cell_type": "markdown", - "id": "825ef89f", "metadata": {}, "source": [ "For large numbers you can use underscores like commas to group digits together or scientific notation. \n", @@ -120,7 +114,6 @@ }, { "cell_type": "markdown", - "id": "f16323c8", "metadata": {}, "source": [ "### **Test Yourself**\n", @@ -131,7 +124,6 @@ { "cell_type": "code", "execution_count": null, - "id": "9083601e", "metadata": {}, "outputs": [], "source": [ @@ -153,7 +145,6 @@ { "cell_type": "code", "execution_count": null, - "id": "c648ec96", "metadata": {}, "outputs": [], "source": [ @@ -180,7 +171,6 @@ }, { "cell_type": "markdown", - "id": "4c50da78", "metadata": {}, "source": [ "There are also many string methods, such as `.upper()`, `.lower()`, `.replace()`, and `.split()`. You should \n", @@ -190,7 +180,6 @@ { "cell_type": "code", "execution_count": null, - "id": "a5074591", "metadata": {}, "outputs": [], "source": [ @@ -215,7 +204,6 @@ }, { "cell_type": "markdown", - "id": "dde1ed85", "metadata": {}, "source": [ "### **Test Yourself**\n", @@ -226,7 +214,6 @@ { "cell_type": "code", "execution_count": null, - "id": "0148be28", "metadata": {}, "outputs": [], "source": [ @@ -255,7 +242,7 @@ "version": "3.13.5" }, "syllabus": { - "uid": "deN7dbNb", + "uid": "b1Q9Y1j1", "name": "Working With Numbers" } }, diff --git a/lessons/20_Types_and_Logic/30_Control_Flow.ipynb b/lessons/20_Types_and_Logic/30_Control_Flow.ipynb index 60b8e4db..9f31874a 100644 --- a/lessons/20_Types_and_Logic/30_Control_Flow.ipynb +++ b/lessons/20_Types_and_Logic/30_Control_Flow.ipynb @@ -473,10 +473,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "g6JPkFUs", - "name": "Control Flow" + "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_My_Ages.py b/lessons/20_Types_and_Logic/40_My_Ages.py index b78c4439..59128593 100644 --- a/lessons/20_Types_and_Logic/40_My_Ages.py +++ b/lessons/20_Types_and_Logic/40_My_Ages.py @@ -26,10 +26,6 @@ the second is the message to show the user. messagebox.showinfo('What you are', "You are a baby.") - -TODO: -uid: ngBHdtPY -name: My Ages """ from tkinter import messagebox, simpledialog, Tk # import required modules diff --git a/lessons/20_Types_and_Logic/60_Simple_Adder.py b/lessons/20_Types_and_Logic/50_Simple_Adder.py similarity index 94% rename from lessons/20_Types_and_Logic/60_Simple_Adder.py rename to lessons/20_Types_and_Logic/50_Simple_Adder.py index daa8a963..62cc665c 100644 --- a/lessons/20_Types_and_Logic/60_Simple_Adder.py +++ b/lessons/20_Types_and_Logic/50_Simple_Adder.py @@ -5,9 +5,6 @@ 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. - -uid: Dn6cL8wy -name: Simple Adder """ # Import the required modules diff --git a/lessons/20_Types_and_Logic/50_Infuriating_Calculator.py b/lessons/20_Types_and_Logic/60_Infuriating_Calculator.py similarity index 96% rename from lessons/20_Types_and_Logic/50_Infuriating_Calculator.py rename to lessons/20_Types_and_Logic/60_Infuriating_Calculator.py index dff05648..0a25a999 100644 --- a/lessons/20_Types_and_Logic/50_Infuriating_Calculator.py +++ b/lessons/20_Types_and_Logic/60_Infuriating_Calculator.py @@ -18,9 +18,6 @@ For the number, you can ask for a float or an integer with simpledialog.askfloat() or simpledialog.askinteger(), and for the math operation you can ask for a string with simpledialog.askstring(). - -uid: ZfzBcb1j -name: Infuriating Calculator """ # Import the required modules diff --git a/lessons/20_Types_and_Logic/70_Code_Challenges.ipynb b/lessons/20_Types_and_Logic/70_Code_Challenges.ipynb index f0af3bfd..4b0c66b8 100644 --- a/lessons/20_Types_and_Logic/70_Code_Challenges.ipynb +++ b/lessons/20_Types_and_Logic/70_Code_Challenges.ipynb @@ -126,10 +126,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "jJI6aomB", - "name": "Code Challenges" + "name": "Code Challenges", + "uid": "jJI6aomB" } }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} From 9381249c93df45808ab00d8f2f3777b729dfc672 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 8 Dec 2025 15:35:51 -0500 Subject: [PATCH 110/159] Added tooltips, important markers, and bookmark svg symbols to enhance course readability --- .../10_Turtles/10_Welcome/10_Welcome.ipynb | 35 +++++--- .../20_What_Can_Tina_Do.ipynb | 90 ++++++++----------- .../30_Turtle_Tricks/10_Turtle_Tricks.py | 3 - .../30_Turtle_Tricks/20_Turtle_Tricks.py | 3 - .../30_Turtle_Tricks/30_Turtle_Tricks.py | 3 - lessons/10_Turtles/40_Loops/10_Loops.ipynb | 44 ++++----- .../10_Variables.ipynb | 39 +++++--- .../20_Functions.ipynb | 28 +++--- .../30_Efficient_Turtle.py | 3 - .../20_More_Turtle_programs.py | 3 - .../30_More_Turtle_Programs.py | 3 - .../40_More_Turtle_Programs.py | 3 - .../10_Turtles/70_Projects/10_LeagueBot.py | 3 - lessons/10_Turtles/70_Projects/20_Tash_Me.py | 2 - .../70_Projects/30_Tash_Me_Click.py | 3 - .../70_Projects/40_Tash_Me_Twirl.py | 2 - .../80_Introducting_Lists/10_Lists.ipynb | 8 +- .../80_Introducting_Lists/20_Color_Lines.py | 3 - .../10_Types_and_Operators.ipynb | 41 +++++---- .../20_Working_With_Numbers.ipynb | 12 ++- 20 files changed, 159 insertions(+), 172 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index 74f599fd..8aca77ef 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -6,7 +6,7 @@ "source": [ "# **Getting Started with Python**\n", "\n", - "Hello future coders, programmers, and engineers! Welcome to your first **Python** lesson with the *League of Amazing Programmers*. We're super excited to help you take your first steps into becoming an amazing programmer! \n", + "Hello future coders, programmers, and engineers! Welcome to your first **Python** lesson with the *League of Amazing Programmers*. We're super excited to help you take your first steps into becoming an amazing programmer! \n", "\n", "To follow along, make sure you're reading this lesson in one of these spots:\n", "\n", @@ -14,9 +14,9 @@ "* [**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", "\n", - "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist \ud83e\udd16 that you can control with code. To do this, we'll use a pre-installed Python tool called the **turtle module**. \n", + "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist 🤖 that you can control with code. To do this, we'll use a pre-installed Python tool called the **turtle module**. \n", "\n", - "You can tell your turtle \ud83d\udc22 to move forward \u2191, turn left \u2190 or \u279d right, and change the color \ud83c\udfa8 of the lines it draws \ud83d\udd8d\ufe0f\u2014 Soon, you'll be able to create awesome pictures like this one!\n", + "You can tell your turtle 🐢 to move forward ↑, turn left ← or ➝ right, and change the color 🎨 of the lines it draws 🖍️— Soon, you'll be able to create awesome pictures like this one!\n", "\n", "
\n", "\n", @@ -26,18 +26,27 @@ "\n", "
\n", "

Let's Get Set Up!

\n", - "

Before we can start drawing, we need to set up your coding environment. Don't worry if anything seems confusing\u2014that's what your instructors are here for! Feel free to ask for help if you get stuck.

\n", - "

Ready to go? Click the blue Next Lesson button at the bottom-left of your screen to continue! \ud83d\ude80

\n", + "

Before we can start drawing, we need to set up your coding environment. Don't worry if anything seems confusing—that's what your instructors are here for! Feel free to ask for help if you get stuck.

\n", + "

Ready to go? Click the blue Next Lesson button at the bottom-left of your screen to continue! 🚀

\n", "\n", - "
\n", - " Notes & Key Terms\n", + "
\n", + "

Course Symbols

\n", + " There are various symbols used throughout this course to help you identify important information quickly without it being too confusing. So, take a moment to familiarize yourself with them.\n", "
    \n", - "
  • Python \u2014 A popular programming language known for its simplicity and readability.
  • \n", - "
  • Turtle Module \u2014 A built-in Python library that allows for drawing shapes and patterns using a virtual turtle.
  • \n", - "
  • Turtle Programming \u2014 A style of programming that uses the turtle module to create graphics and animations.
  • \n", + "
  • Tooltip — These are small pop-up boxes that appear when you hover your mouse cursor over specific text or icons. You will find these helpful guides throughout the course, providing instant definitions and explanations for key terms to help you learn.
  • \n", + "
  • Important — These highlight critical concepts, rules, or warnings that are essential for your success. Pay close attention to these points, as they often contain information you'll need to remember for future lessons and challenges.
  • \n", + "
  • Bookmarks — These sections provide additional details, background information, or interesting facts about a topic. While they aren't required to complete the lesson, exploring them can help deepen your understanding and give you a bigger picture of how things work.
  • \n", "
\n", + "
WARNING: If you skip any of these symbols it is highly likely you might get confused!
\n", "
" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ">**Note:** Please take your time to **read each lesson carefully**. This isn't a race to the finish line! To get the most out of this course, make sure to study the code examples, experiment with them, and ask questions. Rushing through the material without practicing can make things confusing, so pace yourself and enjoy the learning process." + ] } ], "metadata": { @@ -59,10 +68,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "RRTPqCQu", - "name": "Welcome" + "name": "Welcome", + "uid": "RRTPqCQu" } }, "nbformat": 4, "nbformat_minor": 4 -} \ 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 d748e88f..2d826c55 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 @@ -6,11 +6,11 @@ "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. Don\u2019t get intimidated\u2014Tina\u2019s magic \ud83e\uddd9\u200d\u2642\ufe0f is actually very simple and can be broken down into basic steps!\n", + "In the last lesson, you met Tina the Turtle in a program that might have looked a little complicated. Don’t get intimidated—Tina’s magic 🧙‍♂️ is actually very simple and can be broken down into basic steps!\n", "\n", - "In the next program, `Squares_and_Circles.py`, you\u2019ll 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", + "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", - "But before we move ahead, let\u2019s break down how this magic \u2728 works!\n", + "But before we move ahead, let’s break down how this magic ✨ works!\n", "\n", "### **How Tina Follows Commands**\n", "\n", @@ -23,66 +23,38 @@ "tina.right(90) # Turn tina right by 90 degrees\n", "```\n", "\n", - "- The code *before* the `#` is a **command** \u2014 it\u2019s an instruction that tells Tina exactly what to do, such as moving forward or changing color.\n", - "- The text *after* the `#` is a **comment** \u2014 it\u2019s a note for humans that explains what the command does. \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\u2019t affect how the program runs." + "> **Tip:** Comments are ignored by Python and don’t affect how the program runs." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "
Seeking Additional Information\n", + "## **Additional Help**\n", "\n", - "Learning to program with Python's Turtle graphics is fun and creative! One of the best habits you can develop is regularly consulting official documentation and tutorials. This helps you understand commands, discover new features, and solve problems more efficiently. Here\u2019s a detailed guide on how to find and use Python Turtle documentation.\n", + "Learning to use documentation is a superpower for programmers! It helps you find commands, learn new tricks, and solve problems on your own.\n", "\n", - "### **How to Find Python Turtle Documentation**\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", - "1. **Use a Search Engine** by opening your preferred search engine (Google, Bing, DuckDuckGo, etc.).\n", + "### **Why Use Documentation?**\n", "\n", - "2. **Enter Your Query** by typing keywords such as `python turtle documentation`, `python turtle commands`, or specific questions like `how to draw a circle with turtle in python`.\n", + "* **Learn Correct Syntax:** See exactly how to write a command so it works.\n", + "* **Discover Features:** Find cool new things Tina can do!\n", + "* **Fix Errors:** Find out why your code might not be working and how to fix it.\n", "\n", - "3. **Visit Official Sources** and look for links to the [Python Turtle documentation](https://docs.python.org/3/library/turtle.html) or other reputable sites. The official Python docs are usually among the top results and provide comprehensive information.\n", + "> **Tip:** Don't be afraid to copy example code and change it to see what happens. Experimenting is the best way to learn!\n", "\n", - "4. **Explore Tutorials and Examples** on websites and forums (such as Real Python, GeeksforGeeks, Stack Overflow, and YouTube) that offer step-by-step guides, video tutorials, and sample code. These resources can help you understand how commands work in real programs.\n", - "\n", - "5. **Bookmark Useful Pages** so you can save helpful documentation, tutorials, or example projects for quick reference as you continue learning.\n", - "\n", - "6. **Read API References** in the official documentation, which includes an API reference page that lists all the available Turtle methods (like `forward()`, `right()`, `pencolor()`, etc.), their parameters, and example usage.\n", - "\n", - "7. **Check Community Q&A** if you encounter an error or want to achieve a specific effect, as searching forums like Stack Overflow can provide solutions and explanations from other programmers.\n", - "\n", - "### **Why Consulting Documentation Is Essential**\n", - "\n", - "- **Learn Correct Syntax** because documentation shows the exact way to use commands and functions, helping you avoid mistakes and understand how each function works.\n", - "\n", - "- **Discover New Features** since you might find commands or options you didn\u2019t know existed, allowing you to create more interesting and complex drawings.\n", - "\n", - "- **Troubleshoot Problems** if your code isn\u2019t working as expected, as documentation and community forums often have solutions, explanations, and troubleshooting tips.\n", - "\n", - "- **Build Independence** by regularly searching for answers, which helps you become a more confident and resourceful programmer, able to solve problems on your own.\n", - "\n", - "- **Stay Updated** because documentation is updated as Python evolves, so checking it helps you learn about new features and best practices.\n", - "\n", - "### **Extra Ways To Use Documentation Effectively**\n", - "\n", - "- **Try Out Examples** by copying and running example code from the documentation to see how it works. Experiment by changing parameters and observing the results.\n", - "\n", - "- **Use the Search Feature** on most documentation sites, which have a search bar you can use to quickly find information about specific commands or topics.\n", - "\n", - "- **Read the Introduction** at the beginning of the documentation, which often explains how to set up Turtle graphics and provides an overview of its capabilities.\n", - "\n", - "> **Tip:** Whenever you\u2019re unsure about a command or want to try something new, searching for documentation or examples is one of the best ways to learn and grow as a programmer. The more you explore, the more creative and confident you\u2019ll become!\n", - "\n", - "
\n", - "
\n", - " Click Here To View The Official Python Turtle Documentation\n", + "
Official Turtle Documentation \n", + "
\n", " \n", - "
\n", - "\n", - "
" + "
" ] }, { @@ -92,32 +64,40 @@ "\n", "## **Assignment**\n", "\n", - "Take a look at the steps below, and when you\u2019re ready, move on to the Next Lesson and keep exploring what Tina can do!\n", + "Take a look at the steps below, and when you’re ready, move on to the Next Lesson and keep exploring what Tina can do!\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\u2019s drawing changes.\n", + "4. **Run the program again** and see how Tina’s drawing changes.\n", "\n", - "> **Tip:** If you\u2019re not sure what a command does, try changing it and running the program again. Experimentation is a great way to learn!" + "> **Tip:** If you’re not sure what a command does, try changing it and running the program again. Experimentation is a great way to learn!" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "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": "7tUP3zAZ", - "name": "What Can Tina Do" + "name": "What Can Tina Do", + "uid": "7tUP3zAZ" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} 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 970b3e05..a137db93 100644 --- a/lessons/10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py +++ b/lessons/10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py @@ -7,9 +7,6 @@ - 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. - -uid: H5P0FcIJ -name: Turtle Tricks """ # These lines are needed in most turtle programs 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 b2a1ef90..b8b73c26 100644 --- a/lessons/10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py +++ b/lessons/10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py @@ -7,9 +7,6 @@ - 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. - -uid: jIpnMhwi -name: Turtle Tricks """ # These lines are needed in most turtle programs 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 214778f3..8d44889f 100644 --- a/lessons/10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py +++ b/lessons/10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py @@ -9,9 +9,6 @@ - Challenge yourself to experiment with different colors and positions! Refer to the previous program, Meet_Tina.py for examples of how to use these turtle commands. - -uid: TMoSqbTM -name: Turtle Tricks """ # These lines are needed in most turtle programs diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 53f8cfbc..fdb23a63 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -6,7 +6,7 @@ "source": [ "# **Loops**\n", "\n", - "Welcome to the world of **loops**! In this lesson, you'll discover how loops make programming easier and more powerful by allowing you to automate repetitive tasks, save time, write cleaner code, and solve problems more efficiently. By pairing loops with *Tina the Turtle*, you'll be able to simplify your programs, reduce mistakes, and create amazing things with less effort!\n", + "Welcome to the world of **loops**! In this lesson, you'll discover how loops make programming easier and more powerful by allowing you to automate repetitive tasks, save time, write cleaner code, and solve problems more efficiently. By pairing loops with *Tina the Turtle*, you'll be able to simplify your programs, reduce mistakes, and create amazing things with less effort!\n", "\n", "> **Tip:** Take your time to practice using loops. They are a core concept in every programming language, and mastering them will greatly enhance your coding skills." ] @@ -27,7 +27,7 @@ "\n", "### **How Do Loops Work?**\n", "\n", - "Let\u2019s look at this simple example of a `for` loop:" + "Let’s look at this simple example of a `for` loop:" ] }, { @@ -104,7 +104,7 @@ "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", "\n", - "Let\u2019s see how we can use loops to make this code cleaner and easier to manage. Maybe we can even add more shapes without making the code super long!" + "Let’s see how we can use loops to make this code cleaner and easier to manage. Maybe we can even add more shapes without making the code super long!" ] }, { @@ -167,7 +167,7 @@ "
\n", " Click here for a detailed explanation\n", "\n", - "Here's what\u2019s happening:\n", + "Here'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", @@ -182,7 +182,7 @@ "
\n", "
\n", "\n", - "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them\u2014understanding loops will help you solve problems more efficiently and write better code!" + "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them—understanding loops will help you solve problems more efficiently and write better code!" ] }, { @@ -211,7 +211,7 @@ "# Loop will run 'size' times \n", "for i in range(size):\n", " # Print a row of 'size' orange squares\n", - " print('\ud83d\udfe7' * size) " + " print('🟧' * size) " ] }, { @@ -221,10 +221,10 @@ "
\n", " Click here for a detailed explanation\n", "\n", - "Here\u2019s what\u2019s happening:\n", + "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 `'\ud83d\udfe7' * size`.\n", + "- Each time the loop runs, it prints a row of orange squares using `'🟧' * size`.\n", "
\n", "
\n", "\n", @@ -257,10 +257,10 @@ " 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('\u26ab\ufe0f', end='')\n", + " print('⚫️', end='')\n", " # Otherwise, print a white circle\n", " else:\n", - " print('\u26aa\ufe0f', end='')\n", + " print('⚪️', end='')\n", " # Go to the next line\n", " print()" ] @@ -272,10 +272,10 @@ "
\n", " Click here for a detailed explanation\n", "\n", - "Here\u2019s what\u2019s happening:\n", + "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 (`\u26ab\ufe0f`); otherwise, it prints a white circle (`\u26aa\ufe0f`).\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", "
\n", @@ -319,18 +319,18 @@ " # Alternate stars and blue squares in the canton \n", " if (row + col) % 2 == 0: \n", " # Print a star\n", - " print('\u2b50', end='') \n", + " print('⭐', end='') \n", " else:\n", " # Print a blue square\n", - " print('\ud83d\udfe6', end='') \n", + " print('🟦', end='') \n", " else:\n", " # Alternate red and white stripes\n", " if row % 2 == 0:\n", " # Print a red stripe \n", - " print('\ud83d\udfe5', end='') \n", + " print('🟥', end='') \n", " else:\n", " # Print a white stripe\n", - " print('\u2b1c\ufe0f', end='')\n", + " print('⬜️', end='')\n", " # Move to the next line after each row \n", " print() " ] @@ -342,11 +342,11 @@ "
\n", " Click here for a detailed explanation\n", "\n", - "Here\u2019s what\u2019s happening:\n", + "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 (`\ud83d\udfe6`).\n", - "- `else` checks if the current row is even or odd to determine whether to print a red stripe (`\ud83d\udfe5`) or a white stripe (`\u2b1c\ufe0f`).\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", @@ -383,10 +383,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "abX8sNwB", - "name": "Loops" + "name": "Loops", + "uid": "abX8sNwB" } }, "nbformat": 4, "nbformat_minor": 2 -} \ 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 index 26a3b346..63da1bba 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb @@ -6,7 +6,7 @@ "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" + "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" ] }, { @@ -28,7 +28,7 @@ "source": [ "## **What is a Variable?**\n", "\n", - "A variable is like a labeled box that stores information\u2014numbers, 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", + "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:" ] @@ -59,7 +59,7 @@ "- `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", "\n", - "If 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! \ud83c\udfaf" + "If 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! 🎯" ] }, { @@ -91,11 +91,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Notice how the angle changes for each number of sides. This shows the power of variables\u2014they let you write code that adapts automatically!\n", + "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", "\n", "For 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", "\n", - "With variables, Tina the turtle can draw any polygon\u2014triangle, square, hexagon, or even a 9-sided shape\u2014just by changing one number. This makes your code flexible and easy to experiment with!" + "With 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!" ] }, { @@ -104,9 +104,9 @@ "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", + "Variables can store different types of data, such as **integers**, **floats**, **strings**, **booleans**, **lists**, **tuples**, **dictionaries**, **sets**, and **NoneType**. \n", "\n", - "You don\u2019t need to memorize all these types right now\u2014let\u2019s just look at some examples to see how they work!" + "You don’t need to memorize all these types right now—let’s just look at some examples to see how they work!" ] }, { @@ -188,9 +188,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "892dbe57666c4b65aa443a2ff78f7b48", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Canvas(height=250, width=750)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Run Me!\n", "\n", @@ -280,10 +295,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "HOBo0wvj", - "name": "Variables" + "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/20_Functions.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb index e919707a..eed7b02f 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb @@ -6,7 +6,7 @@ "source": [ "# **Functions**\n", "\n", - "In 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" + "In 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" ] }, { @@ -15,12 +15,12 @@ "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", + "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" + "- **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" ] }, { @@ -62,7 +62,7 @@ "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", + "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", @@ -168,19 +168,19 @@ " return a * b # Return the product\n", "\n", "def calculate_area(length, width): # Calculates the area of a rectangle\n", - " area = length * width # Area = length \u00d7 width\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", "\n", "sum_result = add_numbers(10, 5) # Store the sum of 10 and 5\n", "product = multiply_numbers(4, 7) # Store the product of 4 and 7\n", - "room_area = calculate_area(12, 10) # Store the area of a 12\u00d710 room\n", + "room_area = calculate_area(12, 10) # Store the area of a 12×10 room\n", "\n", "# Print the results returned from the functions\n", "\n", "print(\"10 + 5 =\", sum_result)\n", - "print(\"4 \u00d7 7 =\", product)\n", + "print(\"4 × 7 =\", product)\n", "print(\"Room area:\", room_area, \"square feet\")\n", "\n", "# Try changing the numbers to see different results!" @@ -221,7 +221,7 @@ "\n", "# Pizza calculator\n", "def 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 \u00d7 slices each person gets\n", + " total_slices_needed = people * slices_per_person # Total slices = people × slices each person gets\n", " pizzas_needed = -(-total_slices_needed // 8) # Round up to nearest whole pizza (8 slices per pizza)\n", " return total_slices_needed, pizzas_needed # Return both values at once!\n", "\n", @@ -304,7 +304,7 @@ "outputs": [], "source": [ "# Exercise 1: Write a function that converts Celsius to Fahrenheit\n", - "# Formula: F = (C \u00d7 9/5) + 32\n", + "# Formula: F = (C × 9/5) + 32\n", "def celsius_to_fahrenheit(celsius):\n", " # Your code here\n", "\n", @@ -347,10 +347,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "0CwXIYSb", - "name": "Functions" + "name": "Functions", + "uid": "0CwXIYSb" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py b/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py index 070f1da2..8b92b111 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py +++ b/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py @@ -6,9 +6,6 @@ - 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. - -uid: xvDQudwV -name: Efficient Turtle """ import turtle # Tell Python we want to work with the turtle 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 index 5541f705..8cd8ec77 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 @@ -4,8 +4,5 @@ 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. - -uid: rW4r7JTo -name: More Turtle Programs """ 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 0f9e310b..c82fd4e3 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 @@ -4,8 +4,5 @@ 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. - -uid: bOC8a3SH -name: More Turtle Programs """ 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 7fc2b61f..c9486db8 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 @@ -10,8 +10,5 @@ import random x = random.randint(-300, 300) y = random.randint(-300, 300) - -uid: VkkULcqs -name: More Turtle Programs """ diff --git a/lessons/10_Turtles/70_Projects/10_LeagueBot.py b/lessons/10_Turtles/70_Projects/10_LeagueBot.py index 762cefb3..dff4379e 100644 --- a/lessons/10_Turtles/70_Projects/10_LeagueBot.py +++ b/lessons/10_Turtles/70_Projects/10_LeagueBot.py @@ -7,9 +7,6 @@ 2) Change the turtle size to 10x10 3) Change the turtle line color to 'blue' 4) Draw a hexagon using a loop and variables. - -uid: SVGBk0CR -name: Leaguebot """ 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 b90bdcc7..9ed6c36b 100644 --- a/lessons/10_Turtles/70_Projects/20_Tash_Me.py +++ b/lessons/10_Turtles/70_Projects/20_Tash_Me.py @@ -7,8 +7,6 @@ 3) Move the moustache to the right spot on the emoji Hint: See the `10_More_Turtle_Programs` section labeled 'Change the Background Image' and -uid: MslKVn8T -name: Tash Me """ ... # 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 5521b72e..5a7cde90 100644 --- a/lessons/10_Turtles/70_Projects/30_Tash_Me_Click.py +++ b/lessons/10_Turtles/70_Projects/30_Tash_Me_Click.py @@ -4,9 +4,6 @@ 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' -uid: fkXwl2jK -name: Tash Me Click """ - ... # 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 cb84297b..f593cef6 100644 --- a/lessons/10_Turtles/70_Projects/40_Tash_Me_Twirl.py +++ b/lessons/10_Turtles/70_Projects/40_Tash_Me_Twirl.py @@ -4,8 +4,6 @@ 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 10_More_Turtle_Programs, section 'Respond to Screen Clicks' -uid: OjU5u3MU -name: Tash Me Twirl """ ... # Your code here \ No newline at end of file diff --git a/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb b/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb index b626a4f1..85d83c7e 100644 --- a/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb +++ b/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb @@ -6,7 +6,7 @@ "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", + "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", @@ -251,10 +251,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "0KEhJUGe", - "name": "Lists" + "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_Introducting_Lists/20_Color_Lines.py index bba9235f..39444f04 100644 --- a/lessons/10_Turtles/80_Introducting_Lists/20_Color_Lines.py +++ b/lessons/10_Turtles/80_Introducting_Lists/20_Color_Lines.py @@ -2,9 +2,6 @@ # 20_Color_Lines.py Finish the program to make Tina draw a square with each side being a different color. - -uid: qR8SGkHW -name: Color Lines """ import turtle # Tell Python we want to work with the turtle diff --git a/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb b/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb index 505952da..c0135034 100644 --- a/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb +++ b/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb @@ -6,18 +6,18 @@ "source": [ "# **Types and Operators**\n", "\n", - "In this lesson you'll explore how Python represents numbers and text, how to convert between them, and some useful operators and methods.\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* \u2014 pieces of text or code you can run one at a time.\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 \u21e7 Shift + \u23ce Enter to run the cell.\n", - "* You can also use the \u25b6\ufe0f button in the cell toolbar to run a cell too.\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", "* Press esc to enter Command Mode if you want to move, copy, or delete cells (the blue outline disappears in Command Mode).\n", "* When a cell is active you'll see a small menu in the upper-right with extra options.\n", "\n", @@ -72,7 +72,7 @@ "source": [ "## **Data Types**\n", "\n", - "Although variable assignments look similar, each can hold different **data types**, like integers, strings, lists, etc. " + "Although variable assignments look similar, each can hold different **data types**, like integers, strings, lists, etc. " ] }, { @@ -104,14 +104,14 @@ "\n", "* integers are whole numbers without decimals, like `3`\n", "* floats are real numbers that can have a decimal part, like `3.5`\n", - "* strings are text data that is **immutable**, like `\"Alice\"`\n", - "* lists are ordered sequences that are **mutable**, like `['red', 'blue']`\n", + "* strings are text data that is **immutable**, like `\"Alice\"`\n", + "* lists are ordered sequences that are **mutable**, like `['red', 'blue']`\n", "\n", "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", "\n", - "Different types support different operations \u2014 for example, `+` performs arithmetic on numbers but concatenation on strings.\n", + "Different types support different operations — for example, `+` performs arithmetic on numbers but concatenation on strings.\n", "\n", "Lets see what happens when we add two integers and when we add two strings:" ] @@ -168,7 +168,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Oh, that caused a TypeError and didn't work because you can't add an integer to a string! But you *can* convert one to the other data type and then add them. \n", + "Oh, that caused a TypeError and didn't work because you can't add an integer to a string! But you *can* convert one to the other data type and then add them. \n", "\n", "### **Type Conversion**\n", "To convert these variables, we will use both the `int()` and `str()` functions. " @@ -206,7 +206,7 @@ "source": [ "### **Operators**\n", "\n", - "Below are some common math operators for numbers\u2014you\u2019ve likely used most of them, though a couple might be new.\n", + "Below are some common math operators for numbers—you’ve likely used most of them, though a couple might be new.\n", "\n", "| Operator | Description | Example | Result |\n", "|----------|-------------|---------|--------|\n", @@ -328,14 +328,23 @@ "source": [ "### **Candy Distribution**\n", "\n", - "Start with two variables: `kids` (number of kids) and `candy_bars` (number of candy bars). Calculate how to evenly distribute the candy bars to the kids \u2014 print how many each kid gets and how many are left over.\n" + "Start with two variables: `kids` (number of kids) and `candy_bars` (number of candy bars). Calculate how to evenly distribute the candy bars to the kids — print how many each kid gets and how many are left over.\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Each kid gets Ellipsis candy bars\n", + "There are Ellipsis candy bars left over\n" + ] + } + ], "source": [ "# Test yourself\n", "\n", @@ -378,10 +387,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "HXQZ0Iui", - "name": "Types and Operators" + "name": "Types and Operators", + "uid": "HXQZ0Iui" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/lessons/20_Types_and_Logic/20_Working_With_Numbers.ipynb b/lessons/20_Types_and_Logic/20_Working_With_Numbers.ipynb index b79036ca..d99c769a 100644 --- a/lessons/20_Types_and_Logic/20_Working_With_Numbers.ipynb +++ b/lessons/20_Types_and_Logic/20_Working_With_Numbers.ipynb @@ -238,12 +238,20 @@ "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": "b1Q9Y1j1", - "name": "Working With Numbers" + "name": "Working With Numbers", + "uid": "b1Q9Y1j1" } }, "nbformat": 4, From c3ade0ad43c0f3cd6fa5b9864799313bc628713c Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 8 Dec 2025 15:39:52 -0500 Subject: [PATCH 111/159] Bolded link --- lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb b/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb index c0135034..716b6c63 100644 --- a/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb +++ b/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb @@ -43,7 +43,7 @@ "source": [ "### **Getting Started**\n", "\n", - "Although we have already worked with variables before in the past [lesson](../10_Turtles/50_Variables_and_Functions/10_Variables.ipynb) (feel free to click the link for a refresher), we will now dive deeper into them and examine the various data types available in Python.\n", + "Although we have already worked with variables before in the past [**lesson**](../10_Turtles/50_Variables_and_Functions/10_Variables.ipynb) (feel free to click the link for a refresher), we will now dive deeper into them and examine the various data types available in Python.\n", "\n", "When you're set to begin, execute the code block below!" ] From a427383fbc8e5a7ed1118098a536b4b9aff039c8 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sun, 14 Dec 2025 23:08:28 -0500 Subject: [PATCH 112/159] Made some fixes to wording to make explanations clearer + altered code examples to make them less confusing --- .../10_Types_and_Operators.ipynb | 799 +++++++++--------- 1 file changed, 406 insertions(+), 393 deletions(-) diff --git a/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb b/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb index 716b6c63..c4dae8e5 100644 --- a/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb +++ b/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb @@ -1,396 +1,409 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Types and Operators**\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", - "* Press esc to enter Command Mode if you want to move, copy, or delete cells (the blue outline disappears in Command Mode).\n", - "* When a cell is active you'll see a small menu in the upper-right with extra options.\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) or Python 3.13.5).\n", - "\n", - "\n", - "\n", - "After selecting the interpreter, re-run the cell. \n", - "\n", - "Ask your instructor if you are still confused or need help connecting the kernel.\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Getting Started**\n", - "\n", - "Although we have already worked with variables before in the past [**lesson**](../10_Turtles/50_Variables_and_Functions/10_Variables.ipynb) (feel free to click the link for a refresher), we will now dive deeper into them and examine the various data types available in Python.\n", - "\n", - "When you're set to begin, execute 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, etc. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "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 are whole numbers without decimals, like `3`\n", - "* floats are real numbers that can have a decimal part, like `3.5`\n", - "* strings are text data that is **immutable**, like `\"Alice\"`\n", - "* lists are ordered sequences that are **mutable**, like `['red', 'blue']`\n", - "\n", - "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", - "\n", - "Different types support different operations — for example, `+` performs arithmetic on numbers but concatenation on strings.\n", - "\n", - "Lets 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\n", - "x = 10\n", - "y = 20\n", - "\n", - "# Add x and y together\n", - "print(\"x + y =\", x + y)\n", - "\n", - "# Strings\n", - "x = \"10\"\n", - "y = \"20\"\n", - "\n", - "# Add x and y together\n", - "print(\"x + y =\", x + y) # Watch what happens!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you might have expected, when we added `10` and `20` as integers we got `30`, but when we added them as strings, they were concatenated together instead!\n", - "\n", - "So 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 and didn't work because you can't add an integer to a string! But you *can* convert one to the other data type and then add them. \n", - "\n", - "### **Type Conversion**\n", - "To convert these variables, we will use both the `int()` and `str()` functions. " - ] - }, - { - "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(int(y) + x)\n", - "\n", - "# Convert x to a string, then add x and y together\n", - "print(str(x) + y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "> **Tip:** Conversion functions have the same name as the type they convert to, this will help you remember them!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Operators**\n", - "\n", - "Below are some common math operators for numbers—you’ve likely used most of them, though a couple might be new.\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$ |\n", - "\n", - "Floor division uses the `//` operator, it divides then drops the remaining fractional part, which means it returns an integer when used with integers, but a float when used with floats. \n", - "\n", - "For example, `11 // 4` results in `2` and `11.0 // 4.0` results in `2.0` even though normal division would result in `2.75`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Regular division will result in a float\n", - "\n", - "print(10 / 3)\n", - "\n", - "# Floor division is always an integer\n", - "\n", - "print(10 // 3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Modulo uses the ` % ` operator and returns the remainder after division. It's useful for checking for evenly divisible numbers, like if `n` is divisible by `3` (e.g., `n % 3 == 0`).\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Modulo Operator\n", - "\n", - "print(\"9 % 3 == \" , 10 % 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 the `//` and `%` operators is that they are closely related. They allow you to break down a number into parts that can be reassembled using a simple 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": "markdown", - "metadata": {}, - "source": [ - "As shown above, when you divide `11` by `4`, the quotient is `2` and the remainder is `3`. You can reconstruct the original number using this formula: \n", - "\n", - "$$\\text{original number} = (\\text{divisor} \\times \\text{quotient}) + \\text{remainder}$$" - ] - }, - { - "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": [ - "Modulo works a bit like a clock. The numbers count up from zero, but once they reach a certain limit (the divisor), they wrap back around to zero.\n", - "\n", - "The most common use for the modulo operator is to check if a number is evenly divisible by another. If the result is 0, it means there is no remainder, so it divides evenly. For example, `6` is evenly divisible by `3` because `6 % 3` is `0`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Candy Distribution**\n", - "\n", - "Start with two variables: `kids` (number of kids) and `candy_bars` (number of candy bars). Calculate how to evenly distribute the candy bars to the kids — print how many each kid gets and how many are left over.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Each kid gets Ellipsis candy bars\n", - "There are Ellipsis candy bars left over\n" - ] - } - ], - "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": "markdown", - "metadata": {}, - "source": [ - "
\n", - "REMINDER: Don't forget to 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": "Types and Operators", - "uid": "HXQZ0Iui" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Types and Operators**\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", + "
" + ] }, - "nbformat": 4, - "nbformat_minor": 2 + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Getting Started**\n", + "\n", + "Although 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", + "\n", + "When you're set to begin, execute 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", + "\n", + "Different types support different operations — for example, `+` performs arithmetic on numbers but concatenation on strings.\n", + "\n", + "Let'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\n", + "x = 10\n", + "y = 20\n", + "\n", + "# Add x and y together\n", + "print(\"x + y =\", x + y)\n", + "\n", + "# Strings\n", + "x = \"10\"\n", + "y = \"20\"\n", + "\n", + "# Add x and y together\n", + "print(\"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 joined 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": [ + "
\n", + "REMINDER: Don't forget to 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": "Types and Operators", + "uid": "HXQZ0Iui" + } + }, + "nbformat": 4, + "nbformat_minor": 2 } From 502cf850ce074363c1dd9427bebd11373145744d Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sun, 14 Dec 2025 23:17:19 -0500 Subject: [PATCH 113/159] Made some fixes to wording to make explanations clearer + altered code examples to make them less confusing --- .../10_Welcome/30_Run_Programs.ipynb | 54 +++++++++---------- .../20_Working_With_Numbers.ipynb | 2 +- .../20_Types_and_Logic/30_Control_Flow.ipynb | 10 ++-- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index aedcc898..b4014715 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -9,13 +9,13 @@ "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 \u25b6\ufe0f play button.\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 \u23e9 **Run All** button at the top, and some code blocks will have a \u25b6\ufe0f button on the left.\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! Just move your mouse over the code cell and click the \u25b6\ufe0f button." + "Try running the code below! Just move your mouse over the code cell and click the ▶️ button." ] }, { @@ -30,7 +30,7 @@ "date = datetime.date.today()\n", "\n", "# Make a string with a message and the date\n", - "s = f\"Hello \ud83d\udc4b World \ud83c\udf0e ! Today is\"\n", + "s = f\"Hello 👋 World 🌎 ! Today is\"\n", "\n", "# Print the string and the date\n", "print(s, date)" @@ -40,7 +40,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Here\u2019s what the code cell looks like with the run button. Try it out!\n", + "Here’s what the code cell looks like with the run button. Try it out!\n", "\n", "" ] @@ -53,7 +53,7 @@ "\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 \u25b6\ufe0f button you might notice that nothing happens. Don\u2019t worry! Technically something did, you just were not able to see it. This is because you still need to pick which interpreter (or version) of Python to use.\n", + "The first time you press the ▶️ button you might notice that nothing happens. Don’t worry! Technically something did, you just were not able to 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", @@ -61,12 +61,12 @@ "\n", "\n", "\n", - "Now all you need to do is click on a Python option from the list (look for one with a \u2605 or .venv in the name) and try running the code again!\n", + "Now all you need to do is 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\u2019ll need to pick a Python interpreter every time you open a new notebook." + "> **Note:** You’ll need to pick a Python interpreter every time you open a new notebook." ] }, { @@ -74,7 +74,7 @@ "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 \u25b6\ufe0f button or by pressing \u21e7 Shift + \u23ce Enter on your keyboard.\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", @@ -110,42 +110,42 @@ "source": [ "## **How to Run `.py` Programs**\n", "\n", - "Running `.py` files might seem new or even a little overwhelming at first, but don\u2019t worry\u2014you\u2019ll get the hang of it quickly! In the next lesson, you\u2019ll try running a Python program called `Meet_Tina.py`. There are a few different ways to do this, and you can use whichever feels easiest for you. Let\u2019s walk through each option together!\n", + "Running `.py` files might seem new or even a little overwhelming at first, but don’t worry—you’ll get the hang of it quickly! In the next lesson, you’ll try running a Python program called `Meet_Tina.py`. There are a few different ways to do this, and you can use whichever feels easiest for you. Let’s walk through each option together!\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\u2019ll see buttons like this:\n", + "In the bottom-left, you’ll see buttons like this:\n", "\n", "\n", "\n", - "Here\u2019s how it works, step by step:\n", - "- Click \u25b6 Run to start your program\n", + "Here’s how it works, step by step:\n", + "- Click ▶ Run to start your program\n", "\n", - "- Click \u23f9 Stop to stop it\n", + "- Click ⏹ Stop to stop it\n", "\n", - "- When you\u2019re ready for the next lesson, just click Next Lesson\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 \u25b6\ufe0f button in your editor.\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 \u25b6\ufe0f run button.\n", - "3. When you\u2019re done, just close the window.\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 \ud83e\ude9f, or fn + F5 on Mac \ud83c\udf4e. The first time, you\u2019ll be asked to select a debugger\u2014just pick the Python Debugger and then choose to debug the currently active Python file.\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\u2019ll see a debug bar like this:\n", + "You’ll see a debug bar like this:\n", "\n", "\n", "\n", - "Don\u2019t worry about all the buttons for now! You just need to know that you can press the red square to stop your program, and the green circle to run it again.\n", + "Don’t worry about all the buttons for now! You just need to know that you can press the red square to stop your program, and the green circle to run it again.\n", "\n", - "> **Tip:** Remember, it\u2019s totally normal to feel unsure at first. Take your time, try out each method, and soon running Python programs will feel easy!\n" + "> **Tip:** Remember, it’s totally normal to feel unsure at first. Take your time, try out each method, and soon running Python programs will feel easy!\n" ] }, { @@ -158,7 +158,7 @@ "\n", "You have two options to close it:\n", "
\n", - "__1)__ Click the \u2612 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", + "__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", "\n", @@ -175,7 +175,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": ".venv", "language": "python", "name": "python3" }, @@ -192,10 +192,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "cNLK6qtR", - "name": "Run Programs" + "name": "Run Programs", + "uid": "cNLK6qtR" } }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/lessons/20_Types_and_Logic/20_Working_With_Numbers.ipynb b/lessons/20_Types_and_Logic/20_Working_With_Numbers.ipynb index d99c769a..e91a237c 100644 --- a/lessons/20_Types_and_Logic/20_Working_With_Numbers.ipynb +++ b/lessons/20_Types_and_Logic/20_Working_With_Numbers.ipynb @@ -155,7 +155,7 @@ "a = 'Hello' # Define with single quotes\n", "b = \"World\" # Define with double quotes\n", "\n", - "print(a + \" \" +b + '!') # Concatenate with + \n", + "print(a + \" \" + b + '!') # Concatenate with + \n", "\n", "print(a * 3) # Repeat with *\n", "\n", diff --git a/lessons/20_Types_and_Logic/30_Control_Flow.ipynb b/lessons/20_Types_and_Logic/30_Control_Flow.ipynb index 9f31874a..729bfeed 100644 --- a/lessons/20_Types_and_Logic/30_Control_Flow.ipynb +++ b/lessons/20_Types_and_Logic/30_Control_Flow.ipynb @@ -280,7 +280,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## **Using $if$, $elif$, and $else$ Blocks**\n", + "## **Using **if**, **elif**, and **else** Blocks**\n", "\n", "There are more to `if` statements than meets the eye. You can also turn them into blocks by combining theme 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", @@ -291,7 +291,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### $if$-$elif$" + "### ****if-elif****" ] }, { @@ -319,7 +319,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### $if$-$elif$-$else$\n", + "### ****if-elif-else****\n", "\n", "Now, let's add an `else` clause to catch all other cases:" ] @@ -352,7 +352,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - ">**Tip:** $if$-$elif$-$else$ blocks are incredibly 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." + ">**Tip:** `if-elif-else` blocks are incredibly 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." ] }, { @@ -364,7 +364,7 @@ "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", + "- 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", From 0bd8e26f67c18c76137f01d1ac1252a916450427 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 15 Dec 2025 00:01:59 -0500 Subject: [PATCH 114/159] Made some fixes to wording to make explanations clearer + altered code examples to make them less confusing --- .../70_Code_Challenges.ipynb | 238 ++++++++---------- lessons/30_Loops/010_Iteration.ipynb | 92 ++++--- 2 files changed, 170 insertions(+), 160 deletions(-) diff --git a/lessons/20_Types_and_Logic/70_Code_Challenges.ipynb b/lessons/20_Types_and_Logic/70_Code_Challenges.ipynb index 4b0c66b8..55954213 100644 --- a/lessons/20_Types_and_Logic/70_Code_Challenges.ipynb +++ b/lessons/20_Types_and_Logic/70_Code_Challenges.ipynb @@ -1,135 +1,111 @@ { - "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. " - ] + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" }, - { - "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": { - "name": "Code Challenges", - "uid": "jJI6aomB" - } + "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": 11, + "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 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", + "\n", + "Can you help her?" + ] + } + ], + "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": 4 + "syllabus": { + "name": "Code Challenges", + "uid": "jJI6aomB" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/lessons/30_Loops/010_Iteration.ipynb b/lessons/30_Loops/010_Iteration.ipynb index ee85bc6f..4745fe55 100644 --- a/lessons/30_Loops/010_Iteration.ipynb +++ b/lessons/30_Loops/010_Iteration.ipynb @@ -4,37 +4,49 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Loops and Iteration\n", + "# **Loops and Iteration**\n", "\n", - "We've already used loops a lot. They look like this: \n", + "In programming, we often need to repeat a block of code multiple times. This is called **iteration**. We use **loops** to achieve this.\n", "\n", - "```python \n", + "You may have seen loops before. A basic `for` loop in Python looks like this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "# This loop repeats 10 times, printing numbers 0 through 9\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", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Just like `if` statements, `for` loops have a specific structure.\n", + "They always start with the keyword `for`, followed by a **variable name**, then the keyword `in`, and finally an **iterable**.\n", "\n", - "So, the form of the loop line is always:\n", + "The general syntax is:\n", "\n", + "```python\n", + "for in :\n", + " # Code to repeat\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", + "### What is an \"Iterable\"?\n", + "An **iterable** is any collection of items that can be stepped through one by one. \"Iterate\" simply means \"to perform repeatedly\" or \"to go through items one by one\".\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", + "Common examples of iterables in Python include:\n", + "* **Lists**: `['apple', 'banana', 'cherry']`\n", + "* **Tuples**: `(1, 2, 3, 4, 5)`\n", + "* **Strings**: `\"Hello World\"`\n", + "* **Ranges**: `range(10)`\n", "\n", - "You can put any of those things into a loop and get one part of them at a time. " + "When you use a `for` loop, Python takes each item from the iterable, assigns it to your variable, and runs the code block." ] }, { @@ -44,8 +56,7 @@ "outputs": [], "source": [ "# Run Me!\n", - "# Iterate over range()\n", - "\n", + "# Iterate over range(5) - this gives us numbers 0, 1, 2, 3, 4\n", "for i in range(5):\n", " print(i)" ] @@ -57,6 +68,7 @@ "outputs": [], "source": [ "# Iterate a list\n", + "# The loop variable 'i' takes on the value of each item in the list\n", "for i in ['a','b','c','d']:\n", " print(i)" ] @@ -68,6 +80,7 @@ "outputs": [], "source": [ "# Iterate over a string\n", + "# The loop variable 'c' becomes each character in the string\n", "for c in \"Hello World!\":\n", " print(c)" ] @@ -76,13 +89,34 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In the next lessons we'll learn about each of these iterable types. " + "### The Loop Variable\n", + "\n", + "The variable name you choose (like `i` or `c` in the examples above) is up to you! It's best to choose a name that describes what the item is.\n", + "\n", + "* If you are looping over a list of names, use `for name in names:`.\n", + "* If you are looping over numbers, `i` or `number` is common.\n", + "* If you are looping over characters in a string, `char` or `letter` works well." + ] + }, + { + "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": [] + "source": [ + "In the next lessons we'll learn about each of these iterable types. " + ] } ], "metadata": { @@ -104,10 +138,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "ITpqcvxv", - "name": "Iteration" + "name": "Iteration", + "uid": "ITpqcvxv" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} From c8fc480886009c880769507c36e0d1357fa8d29a Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 15 Dec 2025 00:28:49 -0500 Subject: [PATCH 115/159] Slight updates for clarity --- lessons/30_Loops/020_Loops_with_Range.ipynb | 89 +++++++++++---------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/lessons/30_Loops/020_Loops_with_Range.ipynb b/lessons/30_Loops/020_Loops_with_Range.ipynb index cb6b8340..4078cfc0 100644 --- a/lessons/30_Loops/020_Loops_with_Range.ipynb +++ b/lessons/30_Loops/020_Loops_with_Range.ipynb @@ -4,19 +4,20 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# The Range Object\n", + "# **Mastering 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", + "We've used `range()` frequently, but now it's time to dive deeper into how it works.\n", "\n", - "As we've seen, `range()` is a way of creating a series of numbers:\n", + "`range()` generates a sequence of numbers. It is an iterable, similar to a list or a string, meaning you can loop through it.\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:" + "However, unlike a list, `range()` is *memory efficient*. It doesn't store every number in memory—instead, it calculates them on the fly as needed. \n", + "\n", + "Let's see what happens when we inspect a range object directly:" ] }, { @@ -37,9 +38,13 @@ "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", + "### **Efficiency of Range**\n", + "\n", + "`range()` is designed for efficiency. If you created a massive range like `range(1_000_000_000_000)`, storing every number would crash your computer's memory.\n", "\n", - "However, there is a way to get the numbers inside of it: convert it to a list: " + "By generating numbers only when requested, `range()` stays lightweight regardless of size.\n", + "\n", + "To visualize the numbers a range produces (for small ranges only!), you can convert it to a list:" ] }, { @@ -48,6 +53,8 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "print(list(range(10)))" ] }, @@ -55,15 +62,17 @@ "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", + "We'll cover `list()` in detail later. For now, treat it as a tool to peek inside our ranges.\n", + "\n", + "### **Customizing Ranges**\n", "\n", - "The `range()` actually can have one, two or three arguments. Here are the options: \n", + "The `range()` function is versatile and accepts up to three arguments:\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", + "* `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", - "So, here is how we can create odd numbers from 101 to 120:" + "For example, to generate odd numbers from 101 to 120:" ] }, { @@ -72,6 +81,8 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "list(range(101, 120, 2))" ] }, @@ -79,13 +90,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "What `range(101, 120, 2)` means is \"Start at 101 and skip numbers by 2 until 120`\n", + "`range(101, 120, 2)` translates to: *Start at 101, add 2 repeatedly, and stop before reaching 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", + "> **Note:** The `stop` value is **exclusive**. `range()` will never include the number you specify as the end point.\n", "\n", - "## Test Yourself\n", + "### **Challenge: Test Yourself**\n", "\n", - "Print out all of the odd years between the year you were born and today." + "**Task:**\n", + "Print out all the **odd years** between your birth year and the current year." ] }, { @@ -103,27 +115,29 @@ "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", + "### **Challenge**\n", "\n", - " Write a function to list the integers from 1 to 30, but for every multiple of\n", - " 3, write \u201cFizz\u201d, and for every multiple of 5, write \u201cBuzz\u201d. For numbers which\n", - " are multiples of both 3 and 5, it should write \u201cFizzBuzz\u201d; for every other\n", - " number, it should print the number unchanged.\n", + "These [Badgers](https://youtu.be/pzagBTcYsYQ?si=xr4QQ7ZkZBGow2j1) are fans of the classic *FizzBuzz* programming challenge, but they want their own version.\n", "\n", - "Instead, here are the Badger rules: \n", + "#### **FizzBuzz Rules:**\n", + "- Players count numbers sequentially, starting from 1.\n", + "- If the number is a multiple of 3, they print \"Fizz\" instead of the number.\n", + "- If the number is a multiple of 5, they print \"Buzz\" instead.\n", + "- If the number is a multiple of both 3 and 5 (e.g., a multiple of 15), they print \"FizzBuzz\".\n", + "- Otherwise, if none of the above apply, they print the number itself. \n", "\n", - "For the numbers from 1 to 30:\n", + "Instead, the badgers have their own rules. \n", "\n", - "* If the number is evenly divisible by 5, print '\ud83e\udda1 badger'\n", - "* If the number is evenly divisible by 3, print '\ud83c\udf44 mushroom'\n", - "* If the number is evenly divisible by both 3 and 5, print '\ud83d\udc0d snake!'\n", - "* If it is divisible by neither, print the number.\n", + "#### **Badger Badger Mushroom Rules:**\n", + "* Loop through the numbers from 1 to 30\n", + "* If the number is divisible by **5**, print `'🦡 badger'`.\n", + "* If the number is divisible by **3**, print `'🍄 mushroom'`.\n", + "* If the number is divisible by **both 3 and 5**, print `'🐍 snake!'`.\n", + "* Otherwise, just print the number itself.\n", "\n", - "Bonus: write the program without using the ``or`` operator." + "**Bonus Challenge:** Can you solve this without using the `or` operator?" ] }, { @@ -137,13 +151,6 @@ "for i in range(1, 31):\n", " ..." ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -165,10 +172,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "WcGpR3Xg", - "name": "Loops With Range" + "name": "Loops With Range", + "uid": "WcGpR3Xg" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} From 8bc410cc1b6f73b9b9db0f80d009bc66f5afc778 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 15 Dec 2025 10:57:01 -0500 Subject: [PATCH 116/159] Slight updates for clarity --- .vscode/settings.json | 18 +----------------- lessons/30_Loops/010_Iteration.ipynb | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 3ebf23df..b4e65206 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,6 @@ "**/.git": true, "**/.svn": true, "**/.hg": true, - "**/.DS_Store": true, "**/Thumbs.db": true, "**/*.crswap": true, "**/__pycache__": true, @@ -15,22 +14,7 @@ "**/.nox": true, "**/.pytest_cache": true, "**/.ruff_cache": true, - "**/.tox": true, - ".git": true, - ".github": true, - "**/.gitignore": true, - ".gitattributes": true, - ".pylintrc": true, - ".vscode": true, - ".yarn": true, - ".devcontainer": true, - "**/node_modules": true, - "**/.ipynb_checkpoints": true, - ".venv": true, - ".lib": true, - "**/.jtl": true, - "requirements.txt": true, - ".lessons": true + "**/.tox": true } } \ No newline at end of file diff --git a/lessons/30_Loops/010_Iteration.ipynb b/lessons/30_Loops/010_Iteration.ipynb index 4745fe55..0722b2e9 100644 --- a/lessons/30_Loops/010_Iteration.ipynb +++ b/lessons/30_Loops/010_Iteration.ipynb @@ -28,7 +28,7 @@ "metadata": {}, "source": [ "Just like `if` statements, `for` loops have a specific structure.\n", - "They always start with the keyword `for`, followed by a **variable name**, then the keyword `in`, and finally an **iterable**.\n", + "They always start with the keyword `for`, followed by a variable name, then the keyword `in`, and finally an **iterable**.\n", "\n", "The general syntax is:\n", "\n", @@ -37,7 +37,7 @@ " # Code to repeat\n", "```\n", "\n", - "### What is an \"Iterable\"?\n", + "### **What is an Iterable?**\n", "An **iterable** is any collection of items that can be stepped through one by one. \"Iterate\" simply means \"to perform repeatedly\" or \"to go through items one by one\".\n", "\n", "Common examples of iterables in Python include:\n", @@ -46,7 +46,9 @@ "* **Strings**: `\"Hello World\"`\n", "* **Ranges**: `range(10)`\n", "\n", - "When you use a `for` loop, Python takes each item from the iterable, assigns it to your variable, and runs the code block." + "When you use a `for` loop, Python takes each item from the iterable, assigns it to your variable, and runs the code block.\n", + "\n", + "Let's look at some examples to understand this better!" ] }, { @@ -56,7 +58,8 @@ "outputs": [], "source": [ "# Run Me!\n", - "# Iterate over range(5) - this gives us numbers 0, 1, 2, 3, 4\n", + "\n", + "# Iterate over range()\n", "for i in range(5):\n", " print(i)" ] @@ -67,8 +70,9 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "# Iterate a list\n", - "# The loop variable 'i' takes on the value of each item in the list\n", "for i in ['a','b','c','d']:\n", " print(i)" ] @@ -79,17 +83,18 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "# Iterate over a string\n", - "# The loop variable 'c' becomes each character in the string\n", - "for c in \"Hello World!\":\n", - " print(c)" + "for char in \"Hello World!\":\n", + " print(char)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### The Loop Variable\n", + "### **Loop Variable**\n", "\n", "The variable name you choose (like `i` or `c` in the examples above) is up to you! It's best to choose a name that describes what the item is.\n", "\n", From 56c9e1b7dbe7fcc4106872d5a764563660a87851 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 15 Dec 2025 11:37:13 -0500 Subject: [PATCH 117/159] Created 'String_Operations' notebook to break up some of the content from the other notebooks --- lessons/.jtl/syllabus.yaml | 17 +- .../20_What_Can_Tina_Do.ipynb | 2 +- .../10_Types_and_Operators.ipynb | 2 +- .../20_String_Operations.ipynb | 268 ++++++++++++++++++ ...rs.ipynb => 40_Working_With_Numbers.ipynb} | 88 ------ .../{40_My_Ages.py => 50_My_Ages.py} | 0 ...{50_Simple_Adder.py => 60_Simple_Adder.py} | 0 ...ulator.py => 70_Infuriating_Calculator.py} | 0 ...llenges.ipynb => 80_Code_Challenges.ipynb} | 0 .../10_Functions.ipynb | 10 +- 10 files changed, 285 insertions(+), 102 deletions(-) create mode 100644 lessons/20_Types_and_Logic/20_String_Operations.ipynb rename lessons/20_Types_and_Logic/{20_Working_With_Numbers.ipynb => 40_Working_With_Numbers.ipynb} (68%) rename lessons/20_Types_and_Logic/{40_My_Ages.py => 50_My_Ages.py} (100%) rename lessons/20_Types_and_Logic/{50_Simple_Adder.py => 60_Simple_Adder.py} (100%) rename lessons/20_Types_and_Logic/{60_Infuriating_Calculator.py => 70_Infuriating_Calculator.py} (100%) rename lessons/20_Types_and_Logic/{70_Code_Challenges.ipynb => 80_Code_Challenges.ipynb} (100%) diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index 9c9aeb2a..2bc307ca 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -124,22 +124,25 @@ modules: - name: Types and Operators uid: HXQZ0Iui exercise: 20_Types_and_Logic/10_Types_and_Operators.ipynb - - name: Working With Numbers - uid: b1Q9Y1j1 - exercise: 20_Types_and_Logic/20_Working_With_Numbers.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/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/40_My_Ages.py + exercise: 20_Types_and_Logic/50_My_Ages.py display: true - name: Simple Adder - exercise: 20_Types_and_Logic/50_Simple_Adder.py + exercise: 20_Types_and_Logic/60_Simple_Adder.py - name: Infuriating Calculator - exercise: 20_Types_and_Logic/60_Infuriating_Calculator.py + exercise: 20_Types_and_Logic/70_Infuriating_Calculator.py - name: Code Challenges uid: jJI6aomB - exercise: 20_Types_and_Logic/70_Code_Challenges.ipynb + exercise: 20_Types_and_Logic/80_Code_Challenges.ipynb - name: Loops uid: TzgRqJlw lessons: 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 2d826c55..500ce8c1 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 @@ -50,7 +50,7 @@ "\n", "> **Tip:** Don't be afraid to copy example code and change it to see what happens. Experimenting is the best way to learn!\n", "\n", - "
Official Turtle Documentation \n", + "
Official Turtle Documentation\n", "
\n", " \n", diff --git a/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb b/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb index c4dae8e5..3be8c62f 100644 --- a/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb +++ b/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb @@ -140,7 +140,7 @@ "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 joined together as `\"1020\"`.\n", + "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?" ] 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..00c58958 --- /dev/null +++ b/lessons/20_Types_and_Logic/20_String_Operations.ipynb @@ -0,0 +1,268 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3f053ace", + "metadata": {}, + "source": [ + "# **String Operations**\n", + "\n", + "Strings are sequences of characters used to represent text in Python. 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." + ] + }, + { + "cell_type": "markdown", + "id": "c56aabc5", + "metadata": {}, + "source": [ + "### **Basic String Operations**\n", + "\n", + "Even though strings are text, you can perform operations on them that look like math!\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**.\n", + "The syntax is `string[start:end]`, where:\n", + "* `start` is the index where the slice begins (inclusive).\n", + "* `end` is the index where the slice ends (exclusive)." + ] + }, + { + "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", + "* **Length (`len()`):** Returns the number of characters in a string.\n", + "* **Membership (`in`):** Checks if a substring exists within another string." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f31172c3", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "password = \"supersecretpassword\"\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 in the password\n", + "print(\"Contains '123':\", \"123\" 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** (cannot be changed), these methods return a *new* string rather than modifying the original one.\n", + "\n", + "There are 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. " + ] + }, + { + "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": "029bb659", + "metadata": {}, + "source": [ + "### **Escape Characters**\n", + "\n", + "Sometimes 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", + "* `\\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)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3517ae40", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", + "\n", + "print(\"Line 1\\nLine 2\") # Newline\n", + "print(\"Name:\\tAlice\") # Tab\n", + "print(\"She said, \\\"Hello!\\\"\") # Quotes" + ] + }, + { + "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": { + "name": "python", + "version": "3.13.5" + }, + "syllabus": { + "name": "String Operations", + "uid": "K1jP2RdA" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/20_Types_and_Logic/20_Working_With_Numbers.ipynb b/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb similarity index 68% rename from lessons/20_Types_and_Logic/20_Working_With_Numbers.ipynb rename to lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb index e91a237c..c1480150 100644 --- a/lessons/20_Types_and_Logic/20_Working_With_Numbers.ipynb +++ b/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb @@ -141,94 +141,6 @@ "... # Print the age in binary\n", "... # Print the age modulo 3" ] - }, - { - "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", - "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.\n" - ] - }, - { - "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" - ] } ], "metadata": { diff --git a/lessons/20_Types_and_Logic/40_My_Ages.py b/lessons/20_Types_and_Logic/50_My_Ages.py similarity index 100% rename from lessons/20_Types_and_Logic/40_My_Ages.py rename to lessons/20_Types_and_Logic/50_My_Ages.py diff --git a/lessons/20_Types_and_Logic/50_Simple_Adder.py b/lessons/20_Types_and_Logic/60_Simple_Adder.py similarity index 100% rename from lessons/20_Types_and_Logic/50_Simple_Adder.py rename to lessons/20_Types_and_Logic/60_Simple_Adder.py diff --git a/lessons/20_Types_and_Logic/60_Infuriating_Calculator.py b/lessons/20_Types_and_Logic/70_Infuriating_Calculator.py similarity index 100% rename from lessons/20_Types_and_Logic/60_Infuriating_Calculator.py rename to lessons/20_Types_and_Logic/70_Infuriating_Calculator.py diff --git a/lessons/20_Types_and_Logic/70_Code_Challenges.ipynb b/lessons/20_Types_and_Logic/80_Code_Challenges.ipynb similarity index 100% rename from lessons/20_Types_and_Logic/70_Code_Challenges.ipynb rename to lessons/20_Types_and_Logic/80_Code_Challenges.ipynb diff --git a/lessons/40_Data_Structures_Func/10_Functions.ipynb b/lessons/40_Data_Structures_Func/10_Functions.ipynb index cfcb875a..daee466f 100644 --- a/lessons/40_Data_Structures_Func/10_Functions.ipynb +++ b/lessons/40_Data_Structures_Func/10_Functions.ipynb @@ -484,7 +484,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": ".venv", "language": "python", "name": "python3" }, @@ -498,13 +498,13 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.11" + "version": "3.13.5" }, "syllabus": { - "uid": "4LyScnS5", - "name": "Functions" + "name": "Functions", + "uid": "4LyScnS5" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} From e2cbcfb3bef36120f2795f08817486e47c03e995 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 15 Dec 2025 11:44:05 -0500 Subject: [PATCH 118/159] Slight updates to notebook names and content --- lessons/.jtl/syllabus.yaml | 4 ++-- ...nd_Operators.ipynb => 10_Operators_and_Types.ipynb} | 4 ++-- lessons/20_Types_and_Logic/30_Control_Flow.ipynb | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) rename lessons/20_Types_and_Logic/{10_Types_and_Operators.ipynb => 10_Operators_and_Types.ipynb} (99%) diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index 2bc307ca..d29419c6 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -121,9 +121,9 @@ modules: - name: Types and Logic uid: ryHvW6vk lessons: - - name: Types and Operators + - name: Operators and Types uid: HXQZ0Iui - exercise: 20_Types_and_Logic/10_Types_and_Operators.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 diff --git a/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb b/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb similarity index 99% rename from lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb rename to lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb index 3be8c62f..81bd7558 100644 --- a/lessons/20_Types_and_Logic/10_Types_and_Operators.ipynb +++ b/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# **Types and Operators**\n", + "# **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", @@ -400,7 +400,7 @@ "version": "3.13.5" }, "syllabus": { - "name": "Types and Operators", + "name": "Operators and Types", "uid": "HXQZ0Iui" } }, diff --git a/lessons/20_Types_and_Logic/30_Control_Flow.ipynb b/lessons/20_Types_and_Logic/30_Control_Flow.ipynb index 729bfeed..e511d354 100644 --- a/lessons/20_Types_and_Logic/30_Control_Flow.ipynb +++ b/lessons/20_Types_and_Logic/30_Control_Flow.ipynb @@ -126,7 +126,7 @@ "\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", + "### **The `And` Operator**\n", "\n", "A common option for combining multiple conditional expressions is using the `and` operator:\n", "\n", @@ -183,7 +183,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### **The $Or$ Operator**\n", + "### **The `Or` Operator**\n", "\n", "We can also use the `or` operator:\n", "\n", @@ -280,7 +280,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## **Using **if**, **elif**, and **else** Blocks**\n", + "## **Using `if`, `elif`, and `else` Blocks**\n", "\n", "There are more to `if` statements than meets the eye. You can also turn them into blocks by combining theme 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", @@ -291,7 +291,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### ****if-elif****" + "### `if`**-**`elif` **Example**" ] }, { @@ -319,7 +319,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### ****if-elif-else****\n", + "### `if`**-**`elif`**-**`else` **Example**\n", "\n", "Now, let's add an `else` clause to catch all other cases:" ] From 13320059247fa58b11050d1cd3c6a2cc92b5c831 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 15 Dec 2025 11:50:22 -0500 Subject: [PATCH 119/159] Slight fixes to content --- .../10_Operators_and_Types.ipynb | 2 +- .../20_String_Operations.ipynb | 21 +++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb b/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb index 81bd7558..2f7c6d2b 100644 --- a/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb +++ b/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb @@ -19,7 +19,7 @@ "* 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", + "* 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", diff --git a/lessons/20_Types_and_Logic/20_String_Operations.ipynb b/lessons/20_Types_and_Logic/20_String_Operations.ipynb index 00c58958..3d56c08b 100644 --- a/lessons/20_Types_and_Logic/20_String_Operations.ipynb +++ b/lessons/20_Types_and_Logic/20_String_Operations.ipynb @@ -28,10 +28,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "9fecabaa", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello World!\n", + "HelloHelloHello\n", + "H\n", + "o\n", + "1234 1234\n", + "Embed a variable |1234| in a string\n" + ] + } + ], "source": [ "# Run Me!\n", "\n", @@ -259,8 +272,8 @@ "version": "3.13.5" }, "syllabus": { - "name": "String Operations", - "uid": "K1jP2RdA" + "name": "String Operations", + "uid": "K1jP2RdA" } }, "nbformat": 4, From 00044b02e79065f5ca0ac7b740efc913a680b3fc Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 18 Dec 2025 21:07:27 -0500 Subject: [PATCH 120/159] Make specific changes to enhance the understanding of numbers, improve the explanations of string operations, and enhance operators and types. --- .../10_Operators_and_Types.ipynb | 2 +- .../20_String_Operations.ipynb | 58 ++- .../40_Working_With_Numbers.ipynb | 442 +++++++++++------- 3 files changed, 315 insertions(+), 187 deletions(-) diff --git a/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb b/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb index 2f7c6d2b..146594cf 100644 --- a/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb +++ b/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb @@ -45,7 +45,7 @@ "source": [ "### **Getting Started**\n", "\n", - "Although 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", + "Although 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", "\n", "When you're set to begin, execute the code block below!" ] diff --git a/lessons/20_Types_and_Logic/20_String_Operations.ipynb b/lessons/20_Types_and_Logic/20_String_Operations.ipynb index 3d56c08b..29f8ae59 100644 --- a/lessons/20_Types_and_Logic/20_String_Operations.ipynb +++ b/lessons/20_Types_and_Logic/20_String_Operations.ipynb @@ -7,9 +7,11 @@ "source": [ "# **String Operations**\n", "\n", - "Strings are sequences of characters used to represent text in Python. You can create them by enclosing text in single (`'...'`) or double (`\"...\"`) quotes.\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." + "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." ] }, { @@ -19,32 +21,23 @@ "source": [ "### **Basic String Operations**\n", "\n", - "Even though strings are text, you can perform operations on them that look like math!\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", - "* **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)." + "| 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": 6, + "execution_count": null, "id": "9fecabaa", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello World!\n", - "HelloHelloHello\n", - "H\n", - "o\n", - "1234 1234\n", - "Embed a variable |1234| in a string\n" - ] - } - ], + "outputs": [], "source": [ "# Run Me!\n", "\n", @@ -74,10 +67,25 @@ "source": [ "### **Slicing Strings**\n", "\n", - "You can extract a part of a string (a *substring*) using **slicing**.\n", - "The syntax is `string[start:end]`, where:\n", - "* `start` is the index where the slice begins (inclusive).\n", - "* `end` is the index where the slice ends (exclusive)." + "You can extract a part of a string (a *substring*) using slicing.\n", + "\n", + "The syntax is `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", + "> **Visualizing Indices:**\n", + "> Think of indices as pointing **between** the characters.\n", + ">\n", + "> ```text\n", + "> +---+---+---+---+---+---+\n", + "> | P | y | t | h | o | n |\n", + "> +---+---+---+---+---+---+\n", + "> 0 1 2 3 4 5 6\n", + "> ```\n", + "> If you slice `[0:2]`, you get everything between `0` and `2` (\"Py\")." ] }, { diff --git a/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb b/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb index c1480150..a38f827a 100644 --- a/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb +++ b/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb @@ -1,171 +1,291 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **Working With Numbers**\n", - "\n", - "Python has two main numeric types, integers (`int`) for whole numbers and floating-point numbers (`float`) for values with a fractional part (e.g., like a decimal: $3.14$).\n", - "\n", - "Integers can be written in four different bases:\n", - "\n", - "* **Binary (Base 2):** $0b100101$\n", - "* **Octal (Base 8):** $0o45$\n", - "* **Decimal (Base 10):** $37$\n", - "* **Hexadecimal (Base 16):** $0x25$\n", - "\n", - "For example, the decimal number $237$ equals $(2 \\times 100) + (3 \\times 10) + (7 \\times 1)$ — that's $2$ hundreds, $3$ tens, and $7$ ones, or in exponents, its $(2 \\times 10^2) + (3 \\times 10^1) + (7 \\times 10^0)$.\n", - "\n", - "Other number bases work the same way, but they use different place values.\n", - "\n", - "This diagram shows how these bases relate to each other:\n", - "\n", - "\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", - "\n", - "If 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)" - ] - }, - { - "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" - ] - }, + "cells": [ + { + "cell_type": "markdown", + "id": "5ca9192c", + "metadata": {}, + "source": [ + "## **Working With Numbers**\n", + "\n", + "Python has two main numeric types, integers (`int`) for whole numbers and floating-point numbers (`float`) for values with a fractional part (e.g., like a decimal: $3.14$).\n", + "\n", + "Integers can be written in four different bases:\n", + "\n", + "* **Binary (Base 2):** $0b100101$\n", + "* **Octal (Base 8):** $0o45$\n", + "* **Decimal (Base 10):** $37$\n", + "* **Hexadecimal (Base 16):** $0x25$\n", + "\n", + "For example, the decimal number $237$ actually equals $(2 \\times 100) + (3 \\times 10) + (7 \\times 1)$ in base 10 because each digit represents a place value based on powers of $10$.\n", + "\n", + "Other number bases work the same way, just with different place values.\n", + "\n", + "This diagram shows how these bases relate to each other:\n", + "\n", + "\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", + "\n", + "If 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", + "\n", + "Or you can expand the optional sections below for more details on each number system:\n", + "\n", + "
\n", + " Binary\n", + "\n", + "Binary is a base-2 number system that uses a combination of bits and bytes to represent values. \n", + "\n", + "Let'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", + "\n", + "To convert a binary number to decimal, multiply each digit by its place value and add the results.\n", + "\n", + "| Step | Math |\n", + "|------|-------------|\n", + "| Multiply each digit by its 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:** You can also just add the place values where there is a 1: $128 + 32 + 16 + 4 + 1 = 181$.\n", + "\n", + "
\n", + "\n", + "
\n", + " Octal\n", + "\n", + "Octal is a base-8 number system that uses octal digits to represent values.\n", + "\n", + "Let's look at the octal number `345` in the table below:\n", + "\n", + "| Place Value | 64 | 8 | 1 |\n", + "|----------------------|-------|-------|-------|\n", + "| Octal Digit | 3 | 4 | 5 |\n", + "\n", + "To convert an octal number to decimal, multiply each digit by its place value and add the results.\n", + "\n", + "| Step | Math |\n", + "|------|-------------|\n", + "| Multiply each digit by its place value | $(3 \\times 64), (4 \\times 8), (5 \\times 1)$ |\n", + "| Add the results | $192 + 32 + 5 = 229$ |\n", + "\n", + "
\n", + "\n", + "> **Tip:** 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", + "\n", + "Hexadecimal is a base-16 number system that uses hex digits to represent values.\n", + "\n", + "Let's look at the hexadecimal number `2F3` in the table below:\n", + "\n", + "| Place Value | 256 | 16 | 1 |\n", + "|----------------------|-------|-------|-------|\n", + "| Hex Digit | 2 | F (15) | 3 |\n", + "\n", + "To convert a hexadecimal number to decimal, multiply each digit by its place value and add the results.\n", + "\n", + "| Step | Math |\n", + "|------|-------------|\n", + "| Multiply each digit by its place value | $(2 \\times 256), (15 \\times 16), (3 \\times 1)$ |\n", + "| Add the results | $512 + 240 + 3 = 755$ |\n", + "\n", + "
\n", + "\n", + "> **Tip:** 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": 1, + "metadata": {}, + "outputs": [ { - "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" - ] - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "In base 2, 11 is 0b1011\n", + "In base 8, 11 is 0o13\n", + "In base 10, 11 is 11\n", + "In base 16, 11 is 0xb\n" + ] + } + ], + "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": 2, + "metadata": {}, + "outputs": [ { - "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!" - ] - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "int 1305\n", + "float 1305.32\n", + "binary 37\n", + "binary 37\n", + "octal 37\n", + "octal 37\n", + "hex 37\n", + "hex 37\n" + ] + } + ], + "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": 3, + "metadata": {}, + "outputs": [ { - "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" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "You are Ellipsis years old in decimal\n", + "You are Ellipsis years old in hexadecimal\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" + "data": { + "text/plain": [ + "Ellipsis" ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" } - ], - "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" - } + ], + "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" + ] + } + ], + "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 + "syllabus": { + "name": "Working With Numbers", + "uid": "b1Q9Y1j1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 } From 5301eeacf80a892898e743240eb8586a0e2c44ac Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 18 Dec 2025 22:44:49 -0500 Subject: [PATCH 121/159] Finalized changes to Working_With_Numbers to improve clarity; added bookmarks to Loops, made slight modification to My_Ages --- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 10 +- .../20_String_Operations.ipynb | 94 +++++++++----- .../40_Working_With_Numbers.ipynb | 117 ++++++------------ lessons/20_Types_and_Logic/50_My_Ages.py | 2 + 4 files changed, 108 insertions(+), 115 deletions(-) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index fdb23a63..5f592f46 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -165,7 +165,7 @@ "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 can make your code much cleaner and easier to understand!\n", "\n", "
\n", - " Click here for a detailed explanation\n", + " Explanation\n", "\n", "Here's what’s happening:\n", "- **Square** repeats `tina.forward(100)` and `tina.left(90)` *4* times.\n", @@ -219,7 +219,7 @@ "metadata": {}, "source": [ "
\n", - " Click here for a detailed explanation\n", + " Explanation\n", "\n", "Here’s what’s happening:\n", "- `for i in range(size):` tells Python to repeat the indented code `size` times.\n", @@ -270,7 +270,7 @@ "metadata": {}, "source": [ "
\n", - " Click here for a detailed explanation\n", + " Explanation\n", "\n", "Here’s what’s happening:\n", "- `for row in range(size):` loops through each row of the checkerboard, repeating `size` times.\n", @@ -340,7 +340,7 @@ "metadata": {}, "source": [ "
\n", - " Click here for a detailed explanation\n", + " Explanation\n", "\n", "Here’s what’s happening:\n", "- `for row in range(flag_height):` loops through each row of the flag.\n", @@ -366,7 +366,7 @@ ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "Python 3", "language": "python", "name": "python3" }, diff --git a/lessons/20_Types_and_Logic/20_String_Operations.ipynb b/lessons/20_Types_and_Logic/20_String_Operations.ipynb index 29f8ae59..96e18a30 100644 --- a/lessons/20_Types_and_Logic/20_String_Operations.ipynb +++ b/lessons/20_Types_and_Logic/20_String_Operations.ipynb @@ -67,25 +67,27 @@ "source": [ "### **Slicing Strings**\n", "\n", - "You can extract a part of a string (a *substring*) using slicing.\n", + "You can extract a part of a string (a *substring*) using slicing by using the syntax `string[start:end]`.\n", "\n", - "The syntax is `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", - "> **Visualizing Indices:**\n", - "> Think of indices as pointing **between** the characters.\n", - ">\n", - "> ```text\n", - "> +---+---+---+---+---+---+\n", - "> | P | y | t | h | o | n |\n", - "> +---+---+---+---+---+---+\n", - "> 0 1 2 3 4 5 6\n", - "> ```\n", - "> If you slice `[0:2]`, you get everything between `0` and `2` (\"Py\")." + "| `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\"`)." ] }, { @@ -116,8 +118,15 @@ "source": [ "### **Length and Membership**\n", "\n", - "* **Length (`len()`):** Returns the number of characters in a string.\n", - "* **Membership (`in`):** Checks if a substring exists within another string." + "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." ] }, { @@ -129,7 +138,7 @@ "source": [ "# Run Me!\n", "\n", - "password = \"supersecretpassword\"\n", + "password = \"supersecretpassword123\"\n", "\n", "# Check the length\n", "print(\"Length:\", len(password))\n", @@ -137,8 +146,8 @@ "# Check if \"secret\" is in the password\n", "print(\"Contains 'secret':\", \"secret\" in password)\n", "\n", - "# Check if \"123\" is in the password\n", - "print(\"Contains '123':\", \"123\" in password)" + "# Check if \"123\" is not in the password\n", + "print(\"Contains '123':\", \"123\" not in password)" ] }, { @@ -148,10 +157,9 @@ "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** (cannot be changed), these methods return a *new* string rather than modifying the original one.\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, 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. " + "There are many string methods, but here are few: `.upper()`, `.lower()`, `.replace()`, and `.split()`. " ] }, { @@ -180,6 +188,14 @@ "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", @@ -189,9 +205,13 @@ "\n", "Sometimes 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", - "* `\\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)" + "| 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) |\n", + "| `\\r` | Carriage Return (moves the cursor to the beginning of the line) |" ] }, { @@ -205,7 +225,17 @@ "\n", "print(\"Line 1\\nLine 2\") # Newline\n", "print(\"Name:\\tAlice\") # Tab\n", - "print(\"She said, \\\"Hello!\\\"\") # Quotes" + "print(\"She said, \\\"Hello!\\\"\") # Quotes\n", + "print(\"This is a backslash: \\\\\") # Backslash\n", + "print(\"Hello World!\\rStart\") # Carriage Return" + ] + }, + { + "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\"`)." ] }, { @@ -276,7 +306,15 @@ "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": { diff --git a/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb b/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb index a38f827a..07480ec0 100644 --- a/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb +++ b/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb @@ -7,41 +7,39 @@ "source": [ "## **Working With Numbers**\n", "\n", - "Python has two main numeric types, integers (`int`) for whole numbers and floating-point numbers (`float`) for values with a fractional part (e.g., like a decimal: $3.14$).\n", + "Python 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", "\n", "Integers can be written in four different bases:\n", "\n", - "* **Binary (Base 2):** $0b100101$\n", - "* **Octal (Base 8):** $0o45$\n", - "* **Decimal (Base 10):** $37$\n", - "* **Hexadecimal (Base 16):** $0x25$\n", + "* **Binary (Base 2):** $0b11111111$\n", + "* **Octal (Base 8):** $0o377$\n", + "* **Decimal (Base 10):** $255$\n", + "* **Hexadecimal (Base 16):** $0xff$\n", "\n", - "For example, the decimal number $237$ actually equals $(2 \\times 100) + (3 \\times 10) + (7 \\times 1)$ in base 10 because each digit represents a place value based on powers of $10$.\n", + "> **Note:** The prefixes `0b`, `0o`, and `0x` are just used to indicate binary, octal, and hexadecimal numbers, respectively.\n", "\n", - "Other number bases work the same way, just with different place values.\n", - "\n", - "This diagram shows how these bases relate to each other:\n", - "\n", - "\n", + "This 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", + "| **Hexadecimal** | $16$ | $0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f$ |\n", + "\n", + "For 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 also powers of $10$ (e.g., $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", "\n", "If 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", "\n", - "Or you can expand the optional sections below for more details on each number system:\n", + "Or expand the sections below for more details on each number system:\n", "\n", "
\n", " Binary\n", "\n", - "Binary is a base-2 number system that uses a combination of bits and bytes to represent values. \n", + "Binary is a base-2 number system that uses a combination of bits and bytes (often with a `0b` prefix) to represent values. \n", "\n", "Let's look at the binary number `10110101` in the table below:\n", "\n", @@ -51,14 +49,14 @@ "\n", "To convert a binary number to decimal, multiply each digit by its place value and add the results.\n", "\n", - "| Step | Math |\n", + "| Sequential Step | Mathematical Calculation |\n", "|------|-------------|\n", - "| Multiply each digit by its 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", + "| 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:** You can also just add the place values where there is a 1: $128 + 32 + 16 + 4 + 1 = 181$.\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", @@ -67,22 +65,22 @@ "\n", "Octal is a base-8 number system that uses octal digits to represent values.\n", "\n", - "Let's look at the octal number `345` in the table below:\n", + "Let's look at the octal number `0o345` in the table below:\n", "\n", "| Place Value | 64 | 8 | 1 |\n", - "|----------------------|-------|-------|-------|\n", - "| Octal Digit | 3 | 4 | 5 |\n", + "|----------------------|-------|-------|-------\n", + "| Octal Digit | 3 | 4 | 5 |\n", "\n", "To convert an octal number to decimal, multiply each digit by its place value and add the results.\n", "\n", - "| Step | Math |\n", + "| Sequential Step | Mathematical Calculation |\n", "|------|-------------|\n", - "| Multiply each digit by its place value | $(3 \\times 64), (4 \\times 8), (5 \\times 1)$ |\n", + "| Multiply by place value | $(3 \\times 64), (4 \\times 8), (5 \\times 1)$ |\n", "| Add the results | $192 + 32 + 5 = 229$ |\n", "\n", "
\n", "\n", - "> **Tip:** Octal is often used in computing as a shorthand for binary, since each octal digit represents exactly three binary digits.\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", @@ -91,7 +89,7 @@ "\n", "Hexadecimal is a base-16 number system that uses hex digits to represent values.\n", "\n", - "Let's look at the hexadecimal number `2F3` in the table below:\n", + "Let's look at the hexadecimal number `0x2f3` in the table below:\n", "\n", "| Place Value | 256 | 16 | 1 |\n", "|----------------------|-------|-------|-------|\n", @@ -99,14 +97,14 @@ "\n", "To convert a hexadecimal number to decimal, multiply each digit by its place value and add the results.\n", "\n", - "| Step | Math |\n", + "| Sequential Step | Mathematical Calculation |\n", "|------|-------------|\n", - "| Multiply each digit by its place value | $(2 \\times 256), (15 \\times 16), (3 \\times 1)$ |\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", - "> **Tip:** 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", + "> **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", "
" ] @@ -122,20 +120,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "In base 2, 11 is 0b1011\n", - "In base 8, 11 is 0o13\n", - "In base 10, 11 is 11\n", - "In base 16, 11 is 0xb\n" - ] - } - ], + "outputs": [], "source": [ "# Conversion functions\n", "\n", @@ -157,24 +144,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "int 1305\n", - "float 1305.32\n", - "binary 37\n", - "binary 37\n", - "octal 37\n", - "octal 37\n", - "hex 37\n", - "hex 37\n" - ] - } - ], + "outputs": [], "source": [ "# String to integer and float\n", "\n", @@ -224,39 +196,20 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "You are Ellipsis years old in decimal\n", - "You are Ellipsis years old in hexadecimal\n" - ] - }, - { - "data": { - "text/plain": [ - "Ellipsis" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# Test yourself\n", "\n", - "current_year = '2024' # Change to the current year. \n", - "birth_year = '1999' # Change to your birth year\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\")\n", + "print(\"You are\", age, \"years old in decimal.\") # Print the age in decimal\n", "\n", - "print(\"You are \", ..., \" years old in hexadecimal\")\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" diff --git a/lessons/20_Types_and_Logic/50_My_Ages.py b/lessons/20_Types_and_Logic/50_My_Ages.py index 59128593..996ed1ec 100644 --- a/lessons/20_Types_and_Logic/50_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 From d6764231cd88573f519900954de505b4b15bb04a Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 19 Dec 2025 01:28:29 -0500 Subject: [PATCH 122/159] Updated Iteration to be comprehensive, clear, and visually appealing; updated Badger Badger Mushroom section in Loops with Range with a gif --- lessons/30_Loops/010_Iteration.ipynb | 384 ++++++++++++-------- lessons/30_Loops/020_Loops_with_Range.ipynb | 31 +- 2 files changed, 249 insertions(+), 166 deletions(-) diff --git a/lessons/30_Loops/010_Iteration.ipynb b/lessons/30_Loops/010_Iteration.ipynb index 0722b2e9..632b4373 100644 --- a/lessons/30_Loops/010_Iteration.ipynb +++ b/lessons/30_Loops/010_Iteration.ipynb @@ -1,152 +1,236 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Loops and Iteration**\n", - "\n", - "In programming, we often need to repeat a block of code multiple times. This is called **iteration**. We use **loops** to achieve this.\n", - "\n", - "You may have seen loops before. A basic `for` loop in Python looks like this:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\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": [ - "Just like `if` statements, `for` loops have a specific structure.\n", - "They always start with the keyword `for`, followed by a variable name, then the keyword `in`, and finally an **iterable**.\n", - "\n", - "The general syntax is:\n", - "\n", - "```python\n", - "for in :\n", - " # Code to repeat\n", - "```\n", - "\n", - "### **What is an Iterable?**\n", - "An **iterable** is any collection of items that can be stepped through one by one. \"Iterate\" simply means \"to perform repeatedly\" or \"to go through items one by one\".\n", - "\n", - "Common examples of iterables in Python include:\n", - "* **Lists**: `['apple', 'banana', 'cherry']`\n", - "* **Tuples**: `(1, 2, 3, 4, 5)`\n", - "* **Strings**: `\"Hello World\"`\n", - "* **Ranges**: `range(10)`\n", - "\n", - "When you use a `for` loop, Python takes each item from the iterable, assigns it to your variable, and runs the code block.\n", - "\n", - "Let's look at some examples to understand this better!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Iterate over range()\n", - "for i in range(5):\n", - " print(i)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Iterate a list\n", - "for i in ['a','b','c','d']:\n", - " print(i)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Iterate over a string\n", - "for char in \"Hello World!\":\n", - " print(char)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Loop Variable**\n", - "\n", - "The variable name you choose (like `i` or `c` in the examples above) is up to you! It's best to choose a name that describes what the item is.\n", - "\n", - "* If you are looping over a list of names, use `for name in names:`.\n", - "* If you are looping over numbers, `i` or `number` is common.\n", - "* If you are looping over characters in a string, `char` or `letter` works well." - ] - }, - { - "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": [ - "In the next lessons we'll learn about each of these iterable types. " - ] - } - ], - "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 + "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", + "# 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", + "\n", + "Try 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?**\n", + "An 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", + "\n", + "A `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", + "\n", + "Let’s dive into some examples to see this in action!\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", + "\n", + "The 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", + "\n", + "Take 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", + "\n", + "Below 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 } diff --git a/lessons/30_Loops/020_Loops_with_Range.ipynb b/lessons/30_Loops/020_Loops_with_Range.ipynb index 4078cfc0..1a8aa02c 100644 --- a/lessons/30_Loops/020_Loops_with_Range.ipynb +++ b/lessons/30_Loops/020_Loops_with_Range.ipynb @@ -92,9 +92,9 @@ "source": [ "`range(101, 120, 2)` translates to: *Start at 101, add 2 repeatedly, and stop before reaching 120.*\n", "\n", - "> **Note:** The `stop` value is **exclusive**. `range()` will never include the number you specify as the end point.\n", + "> **Note:** `range()` will never include the number you specify as the end point, so always remember it stops *before* that number.\n", "\n", - "### **Challenge: Test Yourself**\n", + "### **Challenge**\n", "\n", "**Task:**\n", "Print out all the **odd years** between your birth year and the current year." @@ -119,23 +119,22 @@ "\n", "### **Challenge**\n", "\n", - "These [Badgers](https://youtu.be/pzagBTcYsYQ?si=xr4QQ7ZkZBGow2j1) are fans of the classic *FizzBuzz* programming challenge, but they want their own version.\n", - "\n", - "#### **FizzBuzz Rules:**\n", - "- Players count numbers sequentially, starting from 1.\n", - "- If the number is a multiple of 3, they print \"Fizz\" instead of the number.\n", - "- If the number is a multiple of 5, they print \"Buzz\" instead.\n", - "- If the number is a multiple of both 3 and 5 (e.g., a multiple of 15), they print \"FizzBuzz\".\n", - "- Otherwise, if none of the above apply, they print the number itself. \n", + "These [Badgers](https://youtu.be/pzagBTcYsYQ?si=xr4QQ7ZkZBGow2j1) are fans of the classic FizzBuzz Programming Challenge, but instead, they have their own rules.\n", "\n", - "Instead, the badgers have their own rules. \n", + "\"Badger\n", "\n", "#### **Badger Badger Mushroom Rules:**\n", - "* Loop through the numbers from 1 to 30\n", - "* If the number is divisible by **5**, print `'🦡 badger'`.\n", - "* If the number is divisible by **3**, print `'🍄 mushroom'`.\n", - "* If the number is divisible by **both 3 and 5**, print `'🐍 snake!'`.\n", - "* Otherwise, just print the number itself.\n", + "\n", + "In 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?" ] From bc2b3190e4ae5b93899026be5da8ea8bab123015 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Fri, 19 Dec 2025 07:06:51 -0500 Subject: [PATCH 123/159] Created an ASCII Art Project --- .../Temp_Project_Ideas/ASCII_Art.ipynb | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 lessons/50_Projects/Temp_Project_Ideas/ASCII_Art.ipynb diff --git a/lessons/50_Projects/Temp_Project_Ideas/ASCII_Art.ipynb b/lessons/50_Projects/Temp_Project_Ideas/ASCII_Art.ipynb new file mode 100644 index 00000000..fdc5fbfc --- /dev/null +++ b/lessons/50_Projects/Temp_Project_Ideas/ASCII_Art.ipynb @@ -0,0 +1,220 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8e43baa", + "metadata": {}, + "source": [ + "# **So you want to be an ASCII Artist?**\n", + "\n", + "Perfect! You will use loops and other techniques that you've learned so far to generate patterns and designs using ASCII characters.\n", + "\n", + "These 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", + "\n", + "ASCII 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", + "\n", + "Examples of ASCII art can include simple designs like smiley faces to more complex images like landscapes, portraits, and intricate patterns.\n", + "\n", + "First we'll start with basics examples:\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", + "\n", + "Let's explore them deeper by looking at some advanced examples that incorporate animated effects:\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "\n", + "Now you might be thinking, man, that looks complicated. How would I even begin to create something like that?\n", + "\n", + "The answer is loops! \n", + "\n", + "By 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", + "\n", + "It 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", + "\n", + "Take 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", + "\n", + "Of course, feel free to explore and find other characters that you like! \n", + "\n", + "### **Additional Resources**\n", + "\n", + "You 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", + "\n", + "And no, it's not cheating to look at ASCII art generators online, there are plenty of free ones that can create some really cool designs!\n", + "\n", + "In 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_big_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", + " \"\"\"\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", + " 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", + " # Add a small gap between letters\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_big_ascii(\"ASCII ART IS COOL\")" + ] + }, + { + "cell_type": "markdown", + "id": "e63a0b03", + "metadata": {}, + "source": [ + "## **General Project Requirements**\n", + "\n", + "### **Requirements**\n", + "- **Functions:** Define at least two custom functions to organize your code (e.g., one for drawing, one for logic).\n", + "- **Data Structures:** Use lists or dictionaries to store your patterns or configuration settings.\n", + "- **Have Fun:** Create something that you enjoy! It can be funny, cool, or weird, it is totally up to you.\n", + "\n", + "### **Exclusions**\n", + "- **Hardcoding:** Don't manually type out every line of a large pattern using 50 `print()` statements when loops can do it for you.\n", + "- **Copy-Pasting:** Don't copy-paste the same logic multiple times, you must wrap it in a function instead.\n", + "- **Infinite Loops:** Ensure your loops have a way to stop so they don't crash your program.\n", + "\n", + "### **Additional Information**\n", + "- You may use external libraries like [art](https://pypi.org/project/art/) or [pyfiglet](https://pypi.org/project/pyfiglet/) if you wish, but it's not required.\n", + "- The goal is to practice your programming skills, so focus on using loops, conditionals, and functions effectively, but feel free to get creative! \n", + "- It does not matter if the art is done by hand or generated as a base, just make sure you are the one programming it.\n", + "
\n", + "\n", + "> **Note:** Create a `.py` file for your project's code and include comments that explain your logic and design choices." + ] + } + ], + "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 +} From e9eeae8879aa300fb5233cdaf0e5ac573b4193fe Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 22 Dec 2025 09:13:57 -0500 Subject: [PATCH 124/159] Made slight modifications for clarity --- .../Temp_Project_Ideas/ASCII_Art.ipynb | 220 ------------------ 1 file changed, 220 deletions(-) delete mode 100644 lessons/50_Projects/Temp_Project_Ideas/ASCII_Art.ipynb diff --git a/lessons/50_Projects/Temp_Project_Ideas/ASCII_Art.ipynb b/lessons/50_Projects/Temp_Project_Ideas/ASCII_Art.ipynb deleted file mode 100644 index fdc5fbfc..00000000 --- a/lessons/50_Projects/Temp_Project_Ideas/ASCII_Art.ipynb +++ /dev/null @@ -1,220 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "a8e43baa", - "metadata": {}, - "source": [ - "# **So you want to be an ASCII Artist?**\n", - "\n", - "Perfect! You will use loops and other techniques that you've learned so far to generate patterns and designs using ASCII characters.\n", - "\n", - "These 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", - "\n", - "ASCII 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", - "\n", - "Examples of ASCII art can include simple designs like smiley faces to more complex images like landscapes, portraits, and intricate patterns.\n", - "\n", - "First we'll start with basics examples:\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", - "\n", - "Let's explore them deeper by looking at some advanced examples that incorporate animated effects:\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
\n", - "\n", - "Now you might be thinking, man, that looks complicated. How would I even begin to create something like that?\n", - "\n", - "The answer is loops! \n", - "\n", - "By 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", - "\n", - "It 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", - "\n", - "Take 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", - "\n", - "Of course, feel free to explore and find other characters that you like! \n", - "\n", - "### **Additional Resources**\n", - "\n", - "You 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", - "\n", - "And no, it's not cheating to look at ASCII art generators online, there are plenty of free ones that can create some really cool designs!\n", - "\n", - "In 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_big_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", - " \"\"\"\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", - " 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", - " # Add a small gap between letters\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_big_ascii(\"ASCII ART IS COOL\")" - ] - }, - { - "cell_type": "markdown", - "id": "e63a0b03", - "metadata": {}, - "source": [ - "## **General Project Requirements**\n", - "\n", - "### **Requirements**\n", - "- **Functions:** Define at least two custom functions to organize your code (e.g., one for drawing, one for logic).\n", - "- **Data Structures:** Use lists or dictionaries to store your patterns or configuration settings.\n", - "- **Have Fun:** Create something that you enjoy! It can be funny, cool, or weird, it is totally up to you.\n", - "\n", - "### **Exclusions**\n", - "- **Hardcoding:** Don't manually type out every line of a large pattern using 50 `print()` statements when loops can do it for you.\n", - "- **Copy-Pasting:** Don't copy-paste the same logic multiple times, you must wrap it in a function instead.\n", - "- **Infinite Loops:** Ensure your loops have a way to stop so they don't crash your program.\n", - "\n", - "### **Additional Information**\n", - "- You may use external libraries like [art](https://pypi.org/project/art/) or [pyfiglet](https://pypi.org/project/pyfiglet/) if you wish, but it's not required.\n", - "- The goal is to practice your programming skills, so focus on using loops, conditionals, and functions effectively, but feel free to get creative! \n", - "- It does not matter if the art is done by hand or generated as a base, just make sure you are the one programming it.\n", - "
\n", - "\n", - "> **Note:** Create a `.py` file for your project's code and include comments that explain your logic and design choices." - ] - } - ], - "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 -} From 7310a64dc99c3f9b3817c71986921da186bdc3b1 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 22 Dec 2025 09:14:09 -0500 Subject: [PATCH 125/159] Added more gifs --- .../Temp_Project_Ideas/30_ASCII_Art.ipynb | 241 ++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb 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..a2ad0fef --- /dev/null +++ b/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb @@ -0,0 +1,241 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a8e43baa", + "metadata": {}, + "source": [ + "# **So you want to be an ASCII Artist?**\n", + "\n", + "Perfect! You will use loops and other techniques that you've learned so far to generate patterns and designs using ASCII characters.\n", + "\n", + "These 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", + "\n", + "ASCII 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", + "\n", + "Examples of ASCII art can include simple designs like smiley faces to more complex images like landscapes, portraits, and intricate patterns.\n", + "\n", + "First 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", + "\n", + "Pretty cool, right? Oh there's more?!\n", + "\n", + "Check out these different ASCII animations to get a feel for what can be done:\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "\n", + "Okay, let's explore them further with some advanced examples:\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "\n", + "Now you might be thinking, man, that looks complicated. How would I even begin to create something like that?\n", + "\n", + "The answer is loops! \n", + "\n", + "By 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", + "\n", + "It 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", + "\n", + "Take 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", + "\n", + "Of course, feel free to explore and find other characters that you like! \n", + "\n", + "### **Additional Resources**\n", + "\n", + "You 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", + "\n", + "And no, it's not cheating to look at ASCII art generators online, there are plenty of free ones that can create some really cool designs!\n", + "\n", + "In 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_big_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", + " \"\"\"\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", + " 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", + " # Add a small gap between letters\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_big_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**\n", + "1. **Plan Your Design:** Choose the ASCII art you wish to make. Sketch it or use an online generator for ideas.\n", + "2. **Choose Characters:** Pick the ASCII characters for your design and break them down into manageable parts to work with.\n", + "3. **Define Functions:** Set up at least two functions: one for drawing and another for logic/settings.\n", + "4. **Use Data Structures:** Organize your patterns or settings using lists or dictionaries.\n", + "5. **Implement Loops:** Code repeated patterns or designs with loops to avoid hardcoding.\n", + "6. **Test and Refine:** Execute your code to check the output. Improve the design or add features as needed.\n", + "7. **Document Your Code:** Include comments explaining your logic and design decisions for future reference.\n", + "8. **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 +} From dae6824c1d954e0e7923b3ee9674ec96e5123f86 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 22 Dec 2025 09:20:01 -0500 Subject: [PATCH 126/159] Updated boilerplate code --- .../Temp_Project_Ideas/30_ASCII_Art.ipynb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb b/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb index a2ad0fef..a577a393 100644 --- a/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb +++ b/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb @@ -147,11 +147,12 @@ "metadata": {}, "outputs": [], "source": [ - "def print_big_ascii(text):\n", + "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", + " # 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", @@ -163,22 +164,22 @@ " 'L': [r\" /$$ \", r\"| $$ \", r\"| $$ \", r\"| $$ \", r\"| $$ \", r\"| $$ \", r\"| $$$$$$$$\", r\"|________/\"],\n", " ' ': [r\" \", r\" \", r\" \", r\" \", r\" \", r\" \", r\" \", r\" \"]\n", " }\n", - "\n", - " phrase = text.upper()\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", + " current_line = \"\" \n", " for char in phrase:\n", " if char in ascii_chars:\n", - " # Add a small gap between letters\n", - " current_line += ascii_chars[char][line_num] + \" \"\n", + " current_line += ascii_chars[char][line_num] + \" \" \n", " else:\n", - " current_line += \" \"\n", + " current_line += \" \" \n", " print(current_line)\n", "\n", "# Output your specific phrase\n", - "print_big_ascii(\"ASCII ART IS COOL\")" + "print_ascii(\"ASCII ART IS COOL\")" ] }, { From 4f1462fef35c8ba74b36d20d889ebba3816e6358 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 27 Dec 2025 11:03:59 -0500 Subject: [PATCH 127/159] Improved Loops with Range (edited the gif to loop only two times, added a table, made small clarity improvements). --- lessons/30_Loops/020_Loops_with_Range.ipynb | 25 +++++++++------------ 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/lessons/30_Loops/020_Loops_with_Range.ipynb b/lessons/30_Loops/020_Loops_with_Range.ipynb index 1a8aa02c..8494c8c5 100644 --- a/lessons/30_Loops/020_Loops_with_Range.ipynb +++ b/lessons/30_Loops/020_Loops_with_Range.ipynb @@ -6,16 +6,16 @@ "source": [ "# **Mastering the Range Object**\n", "\n", - "We've used `range()` frequently, but now it's time to dive deeper into how it works.\n", + "We have used `range()` frequently, but now it is time to explore how it works in more detail.\n", "\n", - "`range()` generates a sequence of numbers. It is an iterable, similar to a list or a string, meaning you can loop through it.\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 doesn't store every number in memory—instead, it calculates them on the fly as needed. \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:" ] @@ -40,11 +40,11 @@ "source": [ "### **Efficiency of Range**\n", "\n", - "`range()` is designed for efficiency. If you created a massive range like `range(1_000_000_000_000)`, storing every number would crash your computer's memory.\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 (for small ranges only!), you can convert it to a list:" + "To visualize the numbers a range produces (recommended for small ranges only!), you can convert it to a list:" ] }, { @@ -62,7 +62,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We'll cover `list()` in detail later. For now, treat it as a tool to peek inside our ranges.\n", + "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", @@ -92,12 +92,11 @@ "source": [ "`range(101, 120, 2)` translates to: *Start at 101, add 2 repeatedly, and stop before reaching 120.*\n", "\n", - "> **Note:** `range()` will never include the number you specify as the end point, so always remember it stops *before* that number.\n", + "> **Note:** `range()` never includes the stop value, so always remember that it stops *before* reaching that number.\n", "\n", "### **Challenge**\n", "\n", - "**Task:**\n", - "Print out all the **odd years** between your birth year and the current year." + "Let's print all the *odd years* between your birth year and the current year." ] }, { @@ -115,17 +114,15 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "\n", - "\n", "### **Challenge**\n", "\n", - "These [Badgers](https://youtu.be/pzagBTcYsYQ?si=xr4QQ7ZkZBGow2j1) are fans of the classic FizzBuzz Programming Challenge, but instead, they have their own rules.\n", + "These [Badgers](https://youtu.be/pzagBTcYsYQ?si=xr4QQ7ZkZBGow2j1) are fans of the classic FizzBuzz Programming Challenge, but they have their own rules.\n", "\n", - "\"Badger\n", + "\"Badger\n", "\n", "#### **Badger Badger Mushroom Rules:**\n", "\n", - "In this game you will loop through the numbers from 1 to 30.\n", + "In this game, you will loop through the numbers from 1 to 30.\n", "\n", "| Condition | Print |\n", "| :------- | :----- |\n", From bd8a8b0b600498655a3c50753d33c12fd843316a Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 27 Dec 2025 17:18:57 -0500 Subject: [PATCH 128/159] Created a concise python script that inherits from `auto_turtle.py` but sets up `tina` (for later code cells); updated 10_Lists.ipynb to make it a tiny bit clearer; altered 20_Loops_with_Range.ipynb and 30_Lists.ipynb to be clearer as well; Changed ipynb numbering in 30_Loops for concistency --- .lib/auto_turtle_concise.py | 29 ++ lessons/.jtl/syllabus.yaml | 18 +- .../80_Introducting_Lists/10_Lists.ipynb | 22 +- lessons/30_Loops/030_Lists.ipynb | 429 ---------------- ...010_Iteration.ipynb => 10_Iteration.ipynb} | 0 ..._Range.ipynb => 20_Loops_with_Range.ipynb} | 27 +- lessons/30_Loops/30_Lists.ipynb | 481 ++++++++++++++++++ .../{040_Crazy_Tina.py => 40_Crazy_Tina.py} | 0 .../{050_Tuples.ipynb => 50_Tuples.ipynb} | 0 ...ng.ipynb => 60_Indexing_and_Slicing.ipynb} | 0 .../{070_List_Story.py => 70_List_Story.py} | 0 .../{080_Strings.ipynb => 80_Strings.ipynb} | 0 ...uzz_Badgers.py => 90_Fizz_Buzz_Badgers.py} | 0 13 files changed, 548 insertions(+), 458 deletions(-) create mode 100644 .lib/auto_turtle_concise.py delete mode 100644 lessons/30_Loops/030_Lists.ipynb rename lessons/30_Loops/{010_Iteration.ipynb => 10_Iteration.ipynb} (100%) rename lessons/30_Loops/{020_Loops_with_Range.ipynb => 20_Loops_with_Range.ipynb} (86%) create mode 100644 lessons/30_Loops/30_Lists.ipynb rename lessons/30_Loops/{040_Crazy_Tina.py => 40_Crazy_Tina.py} (100%) rename lessons/30_Loops/{050_Tuples.ipynb => 50_Tuples.ipynb} (100%) rename lessons/30_Loops/{060_Indexing_and_Slicing.ipynb => 60_Indexing_and_Slicing.ipynb} (100%) rename lessons/30_Loops/{070_List_Story.py => 70_List_Story.py} (100%) rename lessons/30_Loops/{080_Strings.ipynb => 80_Strings.ipynb} (100%) rename lessons/30_Loops/{090_Fizz_Buzz_Badgers.py => 90_Fizz_Buzz_Badgers.py} (100%) 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/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index d29419c6..752cd8d5 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -148,29 +148,29 @@ modules: lessons: - name: Iteration uid: ITpqcvxv - exercise: 30_Loops/010_Iteration.ipynb + exercise: 30_Loops/10_Iteration.ipynb - name: Loops with Range uid: WcGpR3Xg - exercise: 30_Loops/020_Loops_with_Range.ipynb + exercise: 30_Loops/20_Loops_with_Range.ipynb - name: Lists uid: 7zsKa84X - exercise: 30_Loops/030_Lists.ipynb + exercise: 30_Loops/30_Lists.ipynb - name: Crazy Tina - exercise: 30_Loops/040_Crazy_Tina.py + exercise: 30_Loops/40_Crazy_Tina.py display: true - name: Tuples uid: 9zTQza2e - exercise: 30_Loops/050_Tuples.ipynb + exercise: 30_Loops/50_Tuples.ipynb - name: Indexing and Slicing uid: P27f2L8k - exercise: 30_Loops/060_Indexing_and_Slicing.ipynb + exercise: 30_Loops/60_Indexing_and_Slicing.ipynb - name: List Story - exercise: 30_Loops/070_List_Story.py + exercise: 30_Loops/70_List_Story.py - name: Strings uid: Tmg4QRhJ - exercise: 30_Loops/080_Strings.ipynb + exercise: 30_Loops/80_Strings.ipynb - name: Fizz Buzz Badgers - exercise: 30_Loops/090_Fizz_Buzz_Badgers.py + exercise: 30_Loops/90_Fizz_Buzz_Badgers.py - name: For Loop Gauntlet uid: 8yGSkBgV exercise: 30_Loops/100_For_Loop_Gauntlet.ipynb diff --git a/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb b/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb index 85d83c7e..5367b683 100644 --- a/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb +++ b/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb @@ -8,11 +8,11 @@ "\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", + "Things To Buy:\n", + "- `Apples`\n", + "- `Oranges`\n", + "- `Bread`\n", + "- `Milk`\n", "\n", " But in Python we write lists using square brackets, like this:" ] @@ -83,11 +83,13 @@ "\n", "Here are a few examples of common things you can do with lists:\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", - "- slicing: `list[start:stop]` returns a sub-list" + "| 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) |" ] }, { diff --git a/lessons/30_Loops/030_Lists.ipynb b/lessons/30_Loops/030_Lists.ipynb deleted file mode 100644 index 402a7314..00000000 --- a/lessons/30_Loops/030_Lists.ipynb +++ /dev/null @@ -1,429 +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", - "name": "Lists" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} \ No newline at end of file diff --git a/lessons/30_Loops/010_Iteration.ipynb b/lessons/30_Loops/10_Iteration.ipynb similarity index 100% rename from lessons/30_Loops/010_Iteration.ipynb rename to lessons/30_Loops/10_Iteration.ipynb diff --git a/lessons/30_Loops/020_Loops_with_Range.ipynb b/lessons/30_Loops/20_Loops_with_Range.ipynb similarity index 86% rename from lessons/30_Loops/020_Loops_with_Range.ipynb rename to lessons/30_Loops/20_Loops_with_Range.ipynb index 8494c8c5..d72b426f 100644 --- a/lessons/30_Loops/020_Loops_with_Range.ipynb +++ b/lessons/30_Loops/20_Loops_with_Range.ipynb @@ -68,9 +68,11 @@ "\n", "The `range()` function is versatile and accepts up to three arguments:\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", + "| 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:" ] @@ -83,17 +85,20 @@ "source": [ "# Run Me!\n", "\n", - "list(range(101, 120, 2))" + "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": [ - "`range(101, 120, 2)` translates to: *Start at 101, add 2 repeatedly, and stop before reaching 120.*\n", - "\n", - "> **Note:** `range()` never includes the stop value, so always remember that it stops *before* reaching that number.\n", - "\n", "### **Challenge**\n", "\n", "Let's print all the *odd years* between your birth year and the current year." @@ -114,11 +119,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "\n", + "\n", "### **Challenge**\n", "\n", - "These [Badgers](https://youtu.be/pzagBTcYsYQ?si=xr4QQ7ZkZBGow2j1) are fans of the classic FizzBuzz Programming Challenge, but they have their own rules.\n", + "These [Badgers](https://youtu.be/pzagBTcYsYQ?si=xr4QQ7ZkZBGow2j1) are fans of the classic FizzBuzz Programming Challenge, but instead, they have their own rules.\n", "\n", - "\"Badger\n", + "\"Badger\n", "\n", "#### **Badger Badger Mushroom Rules:**\n", "\n", diff --git a/lessons/30_Loops/30_Lists.ipynb b/lessons/30_Loops/30_Lists.ipynb new file mode 100644 index 00000000..959e8537 --- /dev/null +++ b/lessons/30_Loops/30_Lists.ipynb @@ -0,0 +1,481 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5a4acb70", + "metadata": {}, + "source": [ + "# **Lists and Iteration**\n", + "\n", + "We've introduced lists in a [previous lesson](../10_Turtles/80_Introducting_Lists/10_Lists.ipynb), but now, let's explore their power when combined with loops.\n", + "\n", + "As 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", + "\n", + "For example, consider a list representing the contents of a backpack:\n", + "\n", + "Backpack Contents:\n", + "- `map`\n", + "- `flashlight`\n", + "- `water bottle`\n", + "- `snack`\n", + "\n", + "In 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": 2, + "id": "9e90bab0", + "metadata": {}, + "outputs": [], + "source": [ + "# Turn a range() into a list:\n", + "\n", + "myList = 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", + "myList = list(\"Hello World!\")\n", + "print(myList)\n", + "\n", + "# That code above works the same as this code below:\n", + "myList = list()\n", + "for c in \"Hello World!\":\n", + " myList.append(c) # Adding to a list, more on this later\n", + "\n", + "print(myList)" + ] + }, + { + "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", + "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": "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", + "myList = list('adefibhgc')\n", + "myList.sort() # This sorts the list in place\n", + "print(myList)\n", + "print()\n", + "\n", + "myList = list('adefibhgc')\n", + "sortedList = sorted(myList) # This creates a new list & doesn't change the original\n", + "print(sortedList)" + ] + }, + { + "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", + "\n", + "myList = []\n", + "\n", + "# Add to the list using append\n", + "myList.append('item 1')\n", + "myList.append('item 2')\n", + "myList.append('item 3')\n", + "\n", + "# You can also use = with an empty list to create a new list\n", + "myList = myList + ['item 4', 'item 5']\n", + "\n", + "print(myList)" + ] + }, + { + "cell_type": "markdown", + "id": "ee522118", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "### **Try It!**\n", + "Add 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 (myList) 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)\n", + " tina.forward(forward)\n", + " tina.left(left)" + ] + }, + { + "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)\n", + " tina.left(left)" + ] + }, + { + "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", + "\n", + "forward = 100\n", + "lefts = [ 90, 90, 90, 90, 90, 90, 90, 90 ]\n", + "colors = [ 'red', 'blue', 'black', 'orange', 'purple', 'pink', 'cyan', 'green']\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)" + ] + }, + { + "cell_type": "markdown", + "id": "c8f4a193", + "metadata": {}, + "source": [ + "### **Challenge**\n", + "\n", + "Time to practice what you've learned!\n", + "\n", + "You 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": "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": "Lists", + "uid": "7zsKa84X" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/30_Loops/040_Crazy_Tina.py b/lessons/30_Loops/40_Crazy_Tina.py similarity index 100% rename from lessons/30_Loops/040_Crazy_Tina.py rename to lessons/30_Loops/40_Crazy_Tina.py diff --git a/lessons/30_Loops/050_Tuples.ipynb b/lessons/30_Loops/50_Tuples.ipynb similarity index 100% rename from lessons/30_Loops/050_Tuples.ipynb rename to lessons/30_Loops/50_Tuples.ipynb diff --git a/lessons/30_Loops/060_Indexing_and_Slicing.ipynb b/lessons/30_Loops/60_Indexing_and_Slicing.ipynb similarity index 100% rename from lessons/30_Loops/060_Indexing_and_Slicing.ipynb rename to lessons/30_Loops/60_Indexing_and_Slicing.ipynb diff --git a/lessons/30_Loops/070_List_Story.py b/lessons/30_Loops/70_List_Story.py similarity index 100% rename from lessons/30_Loops/070_List_Story.py rename to lessons/30_Loops/70_List_Story.py diff --git a/lessons/30_Loops/080_Strings.ipynb b/lessons/30_Loops/80_Strings.ipynb similarity index 100% rename from lessons/30_Loops/080_Strings.ipynb rename to lessons/30_Loops/80_Strings.ipynb diff --git a/lessons/30_Loops/090_Fizz_Buzz_Badgers.py b/lessons/30_Loops/90_Fizz_Buzz_Badgers.py similarity index 100% rename from lessons/30_Loops/090_Fizz_Buzz_Badgers.py rename to lessons/30_Loops/90_Fizz_Buzz_Badgers.py From 700951b6bcd9b8ccd5070c0853824e00281b6aeb Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 27 Dec 2025 17:23:45 -0500 Subject: [PATCH 129/159] Changed 30_Lists to 30_Looping_Through_Lists (to make it easier to differentiate between 10_Loops) --- lessons/.jtl/syllabus.yaml | 2 +- .../{30_Lists.ipynb => 30_Looping_Through_Lists.ipynb} | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename lessons/30_Loops/{30_Lists.ipynb => 30_Looping_Through_Lists.ipynb} (99%) diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index 752cd8d5..0c1c37a5 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -154,7 +154,7 @@ modules: exercise: 30_Loops/20_Loops_with_Range.ipynb - name: Lists uid: 7zsKa84X - exercise: 30_Loops/30_Lists.ipynb + exercise: 30_Loops/30_Looping_Through_Lists.ipynb - name: Crazy Tina exercise: 30_Loops/40_Crazy_Tina.py display: true diff --git a/lessons/30_Loops/30_Lists.ipynb b/lessons/30_Loops/30_Looping_Through_Lists.ipynb similarity index 99% rename from lessons/30_Loops/30_Lists.ipynb rename to lessons/30_Loops/30_Looping_Through_Lists.ipynb index 959e8537..9710aa89 100644 --- a/lessons/30_Loops/30_Lists.ipynb +++ b/lessons/30_Loops/30_Looping_Through_Lists.ipynb @@ -5,7 +5,7 @@ "id": "5a4acb70", "metadata": {}, "source": [ - "# **Lists and Iteration**\n", + "# **Looping Through Lists**\n", "\n", "We've introduced lists in a [previous lesson](../10_Turtles/80_Introducting_Lists/10_Lists.ipynb), but now, let's explore their power when combined with loops.\n", "\n", @@ -455,7 +455,7 @@ "notebook_metadata_filter": "-all" }, "kernelspec": { - "display_name": "Python 3", + "display_name": ".venv", "language": "python", "name": "python3" }, @@ -472,7 +472,7 @@ "version": "3.13.5" }, "syllabus": { - "name": "Lists", + "name": "Looping ThroughLists", "uid": "7zsKa84X" } }, From 912a12a8e0dafc31a0f11ab9bc4c633a90f69cbc Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 27 Dec 2025 17:25:28 -0500 Subject: [PATCH 130/159] Fixed typo, Looping Through Lists should now appear correctly in the lesson browser --- lessons/.jtl/syllabus.yaml | 2 +- lessons/30_Loops/30_Looping_Through_Lists.ipynb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index 0c1c37a5..449930c6 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -152,7 +152,7 @@ modules: - name: Loops with Range uid: WcGpR3Xg exercise: 30_Loops/20_Loops_with_Range.ipynb - - name: Lists + - name: Looping Through Lists uid: 7zsKa84X exercise: 30_Loops/30_Looping_Through_Lists.ipynb - name: Crazy Tina diff --git a/lessons/30_Loops/30_Looping_Through_Lists.ipynb b/lessons/30_Loops/30_Looping_Through_Lists.ipynb index 9710aa89..0dc1b519 100644 --- a/lessons/30_Loops/30_Looping_Through_Lists.ipynb +++ b/lessons/30_Loops/30_Looping_Through_Lists.ipynb @@ -472,7 +472,7 @@ "version": "3.13.5" }, "syllabus": { - "name": "Looping ThroughLists", + "name": "Looping Through Lists", "uid": "7zsKa84X" } }, From b46006c1a9143f465d117511b5f2b1a4c05b1002 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sun, 28 Dec 2025 13:34:12 -0500 Subject: [PATCH 131/159] Made minor improvements to Iteration and Looping_Through_Lists; added more detail to the Tuples notebook and enhanced its clarity. --- lessons/30_Loops/10_Iteration.ipynb | 3 +- .../30_Loops/30_Looping_Through_Lists.ipynb | 66 ++-- lessons/30_Loops/50_Tuples.ipynb | 303 +++++++++++------- 3 files changed, 229 insertions(+), 143 deletions(-) diff --git a/lessons/30_Loops/10_Iteration.ipynb b/lessons/30_Loops/10_Iteration.ipynb index 632b4373..18d6947b 100644 --- a/lessons/30_Loops/10_Iteration.ipynb +++ b/lessons/30_Loops/10_Iteration.ipynb @@ -20,6 +20,7 @@ "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)" @@ -41,7 +42,7 @@ "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", + "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", diff --git a/lessons/30_Loops/30_Looping_Through_Lists.ipynb b/lessons/30_Loops/30_Looping_Through_Lists.ipynb index 0dc1b519..02dd9782 100644 --- a/lessons/30_Loops/30_Looping_Through_Lists.ipynb +++ b/lessons/30_Loops/30_Looping_Through_Lists.ipynb @@ -168,14 +168,14 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "9e90bab0", "metadata": {}, "outputs": [], "source": [ "# Turn a range() into a list:\n", "\n", - "myList = list(range(5, 10))" + "my_list = list(range(5, 10))" ] }, { @@ -197,15 +197,15 @@ "\n", "# Turn a string into a list:\n", "\n", - "myList = list(\"Hello World!\")\n", - "print(myList)\n", + "my_list = list(\"Hello World!\")\n", + "print(my_list)\n", "\n", "# That code above works the same as this code below:\n", - "myList = list()\n", + "my_list = list()\n", "for c in \"Hello World!\":\n", - " myList.append(c) # Adding to a list, more on this later\n", + " my_list.append(c) # Adding to a list, more on this later\n", "\n", - "print(myList)" + "print(my_list)" ] }, { @@ -226,14 +226,14 @@ "# Run Me!\n", "\n", "# split a string at spaces, the default\n", - "s = 'One Two Three Four'\n", - "l = s.split()\n", - "print(l)\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", - "s = 'One,Two,Three,Four'\n", - "l = s.split(',')\n", - "print(l)" + "my_split = 'One,Two,Three,Four'\n", + "my_list = my_split.split(',')\n", + "print(my_list)" ] }, { @@ -255,14 +255,14 @@ "source": [ "# Run Me!\n", "\n", - "myList = list('adefibhgc')\n", - "myList.sort() # This sorts the list in place\n", - "print(myList)\n", + "my_list = list('adefibhgc')\n", + "my_list.sort() # This sorts the list in place\n", + "print(my_list)\n", "print()\n", "\n", - "myList = list('adefibhgc')\n", - "sortedList = sorted(myList) # This creates a new list & doesn't change the original\n", - "print(sortedList)" + "my_list = list('adefibhgc')\n", + "sorted_list = sorted(my_list) # This creates a new list & doesn't change the original\n", + "print(sorted_list)" ] }, { @@ -286,17 +286,17 @@ "source": [ "# Run Me!\n", "\n", - "myList = []\n", + "my_list = []\n", "\n", "# Add to the list using append\n", - "myList.append('item 1')\n", - "myList.append('item 2')\n", - "myList.append('item 3')\n", + "my_list.append('item 1')\n", + "my_list.append('item 2')\n", + "my_list.append('item 3')\n", "\n", "# You can also use = with an empty list to create a new list\n", - "myList = myList + ['item 4', 'item 5']\n", + "my_list = my_list + ['item 4', 'item 5']\n", "\n", - "print(myList)" + "print(my_list)" ] }, { @@ -350,9 +350,9 @@ "colors = [ 'red', 'blue', 'black', 'orange']\n", "\n", "for color in colors:\n", - " tina.color(color)\n", - " tina.forward(forward)\n", - " tina.left(left)" + " 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" ] }, { @@ -377,8 +377,8 @@ "forward = 100\n", "\n", "for left in [ 90, 90, 90, 90 ]:\n", - " tina.forward(forward)\n", - " tina.left(left)" + " 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" ] }, { @@ -410,9 +410,9 @@ " left = lefts[i]\n", " color = colors[i]\n", "\n", - " tina.color(color)\n", - " tina.forward(forward)\n", - " tina.left(left)" + " 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" ] }, { diff --git a/lessons/30_Loops/50_Tuples.ipynb b/lessons/30_Loops/50_Tuples.ipynb index 2fb7646b..42ce1b8a 100644 --- a/lessons/30_Loops/50_Tuples.ipynb +++ b/lessons/30_Loops/50_Tuples.ipynb @@ -1,111 +1,196 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Tuples \u2014 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", - "name": "Tuples" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **Tuples — Lists You Can't Change**\n", + "\n", + "Tuples 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 highly efficient and useful in many scenarios, so let's learn about them! \n", + "\n", + "You can create a tuple just like a list, but you use parentheses `()` instead of square brackets `[]`." + ] }, - "nbformat": 4, - "nbformat_minor": 2 -} \ No newline at end of file + { + "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 +} From b045dac5d077b74a726857e4ab4fb30ff9b11d1e Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 29 Dec 2025 11:49:44 -0500 Subject: [PATCH 132/159] Made general improvements for clarity, like adding visualization cells and tooltips, as well as a challenge --- .../30_Loops/60_Indexing_and_Slicing.ipynb | 202 ++++++++++++++---- 1 file changed, 162 insertions(+), 40 deletions(-) diff --git a/lessons/30_Loops/60_Indexing_and_Slicing.ipynb b/lessons/30_Loops/60_Indexing_and_Slicing.ipynb index 75a6519f..8e74e3f6 100644 --- a/lessons/30_Loops/60_Indexing_and_Slicing.ipynb +++ b/lessons/30_Loops/60_Indexing_and_Slicing.ipynb @@ -4,15 +4,15 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Indexing and Slicing\n", + "# **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", + "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", + "### **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", - "Indexing is when you get a single item from a list or string by its position (index)." + "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." ] }, { @@ -21,13 +21,14 @@ "metadata": {}, "outputs": [], "source": [ - "# Indexing ( Run Me! )\n", + "# 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", - "\n", "print(colors[-1]) # print the last item. Negative numbers are counted from the end" ] }, @@ -35,13 +36,37 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Slicing\n", + "### **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", - "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", + "* **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", - "Here are some examples of slicing:" + "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:" ] }, { @@ -50,7 +75,9 @@ "metadata": {}, "outputs": [], "source": [ - "# Slicing lists (Run Me!)\n", + "# Run Me!\n", + "\n", + "# Slicing lists\n", "\n", "colors = [ 'red', 'blue', 'black', 'orange'] # define a list of colors\n", "\n", @@ -63,22 +90,37 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "So, some of patterns you can use are: \n", + "Here are the common slice patterns:\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" + "| `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": [ - "We can also use negative numbers in slicing, which means \"from the end\"." + "### **Negative Indices**\n", + "\n", + "We can also use negative indices to count from the end of the list, which is super handy when you want the last few items but don't know how long the list is!\n", + "\n", + "#### **Visualizing Negative Indices**\n", + "\n", + "Think of it as counting backwards:\n", + "\n", + "```text\n", + " ┌────────┬────────┬─────────┬────────┐\n", + " │ red │ blue │ black │ orange │\n", + " └────────┴────────┴─────────┴────────┘\n", + "-4 -3 -2 -1\n", + "```\n", + "\n", + "For example, `[-1]` gives you `'orange'`, and a slice like `[-3:-1]` gives you `['blue', 'black']`." ] }, { @@ -87,7 +129,9 @@ "metadata": {}, "outputs": [], "source": [ - "# Slicing with negative ( Run me!)\n", + "# Run Me!\n", + "\n", + "# Slicing with negative indices\n", "\n", "nums = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n", "\n", @@ -95,23 +139,21 @@ "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", + "# 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\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" + "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 can also be positive or negative. A negative skip means to reverse the list." + "### **Skipping and Reversing**\n", + "\n", + "The 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", + "\n", + "We can also use a negative step for reversing the order; `[::-1]` is the standard way to walk backwards through a list in Python!" ] }, { @@ -120,6 +162,8 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "# Skipping items\n", "\n", "nums = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n", @@ -137,17 +181,95 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The most frequent use of skip is to reverse the list, using:\n", + "### **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", - "```python \n", - "l[::-1]\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": [] + "source": [ + "### **Pitfall: Indexing vs. Slicing Safety**\n", + "\n", + "Be careful! 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": [ + "However, slicing is forgiving. If you try to slice a range that doesn't exist, Python will just give you whatever it can find (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": { @@ -166,13 +288,13 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.6" + "version": "3.13.5" }, "syllabus": { - "uid": "P27f2L8k", - "name": "Indexing And Slicing" + "name": "Indexing And Slicing", + "uid": "P27f2L8k" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} From aff7f01fc24db56a3265441994321f564d870a76 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 29 Dec 2025 11:50:17 -0500 Subject: [PATCH 133/159] Added a section that talks about index errors --- lessons/30_Loops/60_Indexing_and_Slicing.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lessons/30_Loops/60_Indexing_and_Slicing.ipynb b/lessons/30_Loops/60_Indexing_and_Slicing.ipynb index 8e74e3f6..38f4904a 100644 --- a/lessons/30_Loops/60_Indexing_and_Slicing.ipynb +++ b/lessons/30_Loops/60_Indexing_and_Slicing.ipynb @@ -205,9 +205,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### **Pitfall: Indexing vs. Slicing Safety**\n", + "### **Watch Out For Index Errors**\n", "\n", - "Be careful! 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." + "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." ] }, { @@ -227,7 +227,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "However, slicing is forgiving. If you try to slice a range that doesn't exist, Python will just give you whatever it can find (or an empty list) without crashing." + "> **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." ] }, { From 624aee71d791e29ba7583fddc28e9dd0fafdf2ec Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 29 Dec 2025 17:26:54 -0500 Subject: [PATCH 134/159] Added ipyturtle3 to the requirements --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 6f681837..506fb404 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ guizero pillow IPython ipykernel +ipyturtle3 invoke \ No newline at end of file From 9c4dcbeea150b901a03c6f8156f40e2e373cbfa9 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sun, 4 Jan 2026 15:33:25 -0500 Subject: [PATCH 135/159] Made improvements for clarity --- lessons/30_Loops/80_Strings.ipynb | 1053 ++++++++++++++++------------- 1 file changed, 570 insertions(+), 483 deletions(-) diff --git a/lessons/30_Loops/80_Strings.ipynb b/lessons/30_Loops/80_Strings.ipynb index 57931887..0110e9ca 100644 --- a/lessons/30_Loops/80_Strings.ipynb +++ b/lessons/30_Loops/80_Strings.ipynb @@ -1,488 +1,575 @@ { - "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", - "\u201cHope\u201d 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:" - ] - }, + "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.\n", + "\n", + "### **Now You Try It!**" + ] + }, + { + "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": [ { - "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" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "['Python', 'is', 'fun', 'to', 'learn']\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", - "name": "Strings" + ], + "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" }, - "nbformat": 4, - "nbformat_minor": 2 -} \ No newline at end of file + "syllabus": { + "name": "Strings", + "uid": "Tmg4QRhJ" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From e391654bf5d24b62b57dc0263f89bd6a40c323a3 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sun, 4 Jan 2026 20:59:06 -0500 Subject: [PATCH 136/159] Minor updates to appearance and clarity --- lessons/30_Loops/100_For_Loop_Gauntlet.ipynb | 169 +++++++++++++------ 1 file changed, 116 insertions(+), 53 deletions(-) diff --git a/lessons/30_Loops/100_For_Loop_Gauntlet.ipynb b/lessons/30_Loops/100_For_Loop_Gauntlet.ipynb index 827ad113..44627161 100644 --- a/lessons/30_Loops/100_For_Loop_Gauntlet.ipynb +++ b/lessons/30_Loops/100_For_Loop_Gauntlet.ipynb @@ -5,16 +5,15 @@ "id": "539e9340", "metadata": {}, "source": [ - "# For Loop Gauntlet\n", + "# **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", + "Welcome to the *For Loop Gauntlet*! This assignment is designed to help you become a master at using `for` loops in Python.\n", "\n", - "## Single for-loops\n", + "Carefully 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", - "Write a for loop to do each of the following\n" + "## **Single For-Loops**\n", + "\n", + "Write a `for` loop to accomplish each of the following tasks." ] }, { @@ -24,7 +23,9 @@ "lines_to_next_cell": 0 }, "source": [ - "Display all numbers from 0 to 100 " + "#### **Display all numbers from 0 to 100**\n", + "\n", + "Write a loop that prints every number from 0 up to and including 100." ] }, { @@ -46,7 +47,9 @@ "lines_to_next_cell": 0 }, "source": [ - "Display all numbers from 100 to 0 " + "#### **Display all numbers from 100 to 0**\n", + "\n", + "Write a loop that prints every number from 100 down to 0 (counting backwards)." ] }, { @@ -68,7 +71,9 @@ "lines_to_next_cell": 0 }, "source": [ - "Display all even numbers from 2 to 100 " + "#### **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." ] }, { @@ -91,7 +96,9 @@ "lines_to_next_cell": 0 }, "source": [ - "Display all odd numbers from 1 to 99 " + "#### **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." ] }, { @@ -114,14 +121,17 @@ "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", + "#### **Odd or Even?**\n", "\n", - " 1 is odd \n", - " 2 is even \n", - " 3 is odd \n", - " 4 is even \n", - " 5 is odd...etc. \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", + "```" ] }, { @@ -131,7 +141,8 @@ "metadata": {}, "outputs": [], "source": [ - "# Display all numbers from 1 to 500.\n" + "# Display all numbers from 1 to 500.\n", + "# Write a loop that prints every number from 1 to 500." ] }, { @@ -139,7 +150,9 @@ "id": "969e00f1", "metadata": {}, "source": [ - "Display all multiples of 7 from 0 to 70" + "#### **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." ] }, { @@ -152,7 +165,8 @@ }, "outputs": [], "source": [ - "# Display all multiples of 7 from 0 to 777\n" + "# Display all multiples of 7 from 0 to 70\n", + "# Write a loop that prints every multiple of 7 from 0 to 70." ] }, { @@ -162,7 +176,19 @@ "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) " + "#### **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!)" ] }, { @@ -185,9 +211,9 @@ "lines_to_next_cell": 0 }, "source": [ - "## Nested for-loops\n", + "## **Nested For-Loops**\n", "\n", - "Write nested for loops (a for loop inside another for loop). Here is an example of nested loops. " + "Now let's practice writing loops inside of loops! These are called *nested* `for`*-loops*. " ] }, { @@ -215,17 +241,19 @@ "lines_to_next_cell": 0 }, "source": [ - "For your first nested loop, display this output: \n", + "#### **Pair of Numbers**\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", + "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", "```" ] @@ -250,12 +278,14 @@ "lines_to_next_cell": 0 }, "source": [ - "Display the numbers 1 through 9 in a 3x3 square grid like this: \n", + "#### **3 x 3 Grid**\n", "\n", - "```\n", - "1 2 3 \n", - "4 5 6 \n", - "7 8 9 \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", "```" ] }, @@ -279,7 +309,9 @@ "lines_to_next_cell": 0 }, "source": [ - "Display the numbers 1 through 100 in a 10x10 square grid. " + "#### **10 x 10 Grid**\n", + "\n", + "Display the numbers 1 through 100 in a 10x10 grid. Each row should have 10 numbers." ] }, { @@ -302,16 +334,18 @@ "lines_to_next_cell": 2 }, "source": [ - "Display the following output: \n", + "#### **Triangle Pattern**\n", "\n", - "```\n", - "* \n", - "* * \n", - "* * * \n", - "* * * * \n", - "* * * * * \n", + "Write a nested loop to display the following triangle pattern using asterisks:\n", + "\n", + "```text\n", + "*\n", + "* *\n", + "* * *\n", + "* * * *\n", + "* * * * *\n", "* * * * * *\n", - "``` " + "```" ] }, { @@ -326,9 +360,38 @@ }, { "cell_type": "markdown", - "id": "5a40192d", + "id": "dee7d3d2", "metadata": {}, - "source": [] + "source": [ + "#### **Bonus Challenge**\n", + "\n", + "Congratulations on making it this far! If you are feeling adventurous, create a 1 x 10 multiplication table.\n", + "\n", + "The output should look like this:\n", + "\n", + "```text\n", + "1 2 3 4 5 6 7 8 9 10\n", + "2 4 6 8 10 12 14 16 18 20\n", + "3 6 9 12 15 18 21 24 27 30\n", + "4 8 12 16 20 24 28 32 36 40\n", + "5 10 15 20 25 30 35 40 45 50\n", + "6 12 18 24 30 36 42 48 54 60\n", + "7 14 21 28 35 42 49 56 63 70\n", + "8 16 24 32 40 48 56 64 72 80\n", + "9 18 27 36 45 54 63 72 81 90\n", + "10 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": { @@ -355,10 +418,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "8yGSkBgV", - "name": "For Loop Gauntlet" + "name": "For Loop Gauntlet", + "uid": "8yGSkBgV" } }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} From 77d6920e817180c87528eb15689111a433357ba4 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 8 Jan 2026 19:05:33 -0500 Subject: [PATCH 137/159] Created Quiz examples to showcase at next meeting. Each quiz covers a specific module (e.g., Turtles, Types, Loops, Data). As of right now the ipynb's only include the quiz name and code to start them. The .json files contain quiz answers and are stored separately. --- lessons/10_Turtles/Module_One_Quiz.ipynb | 278 ++++++++++++++++ .../20_Types_and_Logic/Module_Two_Quiz.ipynb | 278 ++++++++++++++++ lessons/30_Loops/Module_Three_Quiz.ipynb | 278 ++++++++++++++++ .../Module_Four_Quiz.ipynb | 45 +++ lessons/Quiz_Data/Module_Four_Quiz.json | 202 ++++++++++++ lessons/Quiz_Data/Module_One_Quiz.json | 302 ++++++++++++++++++ lessons/Quiz_Data/Module_Three_Quiz.json | 203 ++++++++++++ lessons/Quiz_Data/Module_Two_Quiz.json | 202 ++++++++++++ 8 files changed, 1788 insertions(+) create mode 100644 lessons/10_Turtles/Module_One_Quiz.ipynb create mode 100644 lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb create mode 100644 lessons/30_Loops/Module_Three_Quiz.ipynb create mode 100644 lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb create mode 100644 lessons/Quiz_Data/Module_Four_Quiz.json create mode 100644 lessons/Quiz_Data/Module_One_Quiz.json create mode 100644 lessons/Quiz_Data/Module_Three_Quiz.json create mode 100644 lessons/Quiz_Data/Module_Two_Quiz.json diff --git a/lessons/10_Turtles/Module_One_Quiz.ipynb b/lessons/10_Turtles/Module_One_Quiz.ipynb new file mode 100644 index 00000000..01abd166 --- /dev/null +++ b/lessons/10_Turtles/Module_One_Quiz.ipynb @@ -0,0 +1,278 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "id": "495aaca3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "var questionsxsJmzEETFCtx=[\n {\n \"question\": \"What does forward() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Moves the turtle forward\", \"correct\": true, \"feedback\": \"Correct! forward() moves the turtle in the direction it's facing.\" },\n { \"answer\": \"Turns the turtle\", \"correct\": false, \"feedback\": \"Incorrect. forward() moves, not turns.\" },\n { \"answer\": \"Changes color\", \"correct\": false, \"feedback\": \"Incorrect. forward() is for movement.\" },\n { \"answer\": \"Draws a circle\", \"correct\": false, \"feedback\": \"Incorrect. Use circle() to draw circles.\" }\n ]\n },\n {\n \"question\": \"Which command turns the turtle left?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"left()\", \"correct\": true, \"feedback\": \"Correct! left() turns the turtle to the left.\" },\n { \"answer\": \"turn()\", \"correct\": false, \"feedback\": \"Incorrect. Use left() or right().\" },\n { \"answer\": \"rotate()\", \"correct\": false, \"feedback\": \"Incorrect. Use left() to turn.\" },\n { \"answer\": \"spin()\", \"correct\": false, \"feedback\": \"Incorrect. Use left() to turn left.\" }\n ]\n },\n {\n \"question\": \"How many degrees is a square corner?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"90 degrees\", \"correct\": true, \"feedback\": \"Correct! Square corners are 90 degrees.\" },\n { \"answer\": \"45 degrees\", \"correct\": false, \"feedback\": \"Incorrect. Squares have 90-degree corners.\" },\n { \"answer\": \"180 degrees\", \"correct\": false, \"feedback\": \"Incorrect. That's a straight line.\" },\n { \"answer\": \"360 degrees\", \"correct\": false, \"feedback\": \"Incorrect. That's a full circle.\" }\n ]\n },\n {\n \"question\": \"What does penup() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Stops drawing when the turtle moves\", \"correct\": true, \"feedback\": \"Correct! penup() lifts the pen so the turtle doesn't draw.\" },\n { \"answer\": \"Stops the turtle\", \"correct\": false, \"feedback\": \"Incorrect. The turtle can still move.\" },\n { \"answer\": \"Moves the turtle up\", \"correct\": false, \"feedback\": \"Incorrect. It controls drawing, not direction.\" },\n { \"answer\": \"Deletes the drawing\", \"correct\": false, \"feedback\": \"Incorrect. It just lifts the pen.\" }\n ]\n },\n {\n \"question\": \"When would you use penup()?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"When you want to move without drawing\", \"correct\": true, \"feedback\": \"Correct! Use penup() to move the turtle without leaving a line.\" },\n { \"answer\": \"When you want to draw faster\", \"correct\": false, \"feedback\": \"Incorrect. Use speed() to change drawing speed.\" },\n { \"answer\": \"When you want to change colors\", \"correct\": false, \"feedback\": \"Incorrect. Use pencolor() to change colors.\" },\n { \"answer\": \"When you want to stop the program\", \"correct\": false, \"feedback\": \"Incorrect. penup() just stops drawing.\" }\n ]\n },\n {\n \"question\": \"Which command changes the pen color?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"pencolor()\", \"correct\": true, \"feedback\": \"Correct! pencolor() sets the drawing color.\" },\n { \"answer\": \"color()\", \"correct\": false, \"feedback\": \"Close, but pencolor() is more specific for the pen.\" },\n { \"answer\": \"setcolor()\", \"correct\": false, \"feedback\": \"Incorrect. Use pencolor().\" },\n { \"answer\": \"changecolor()\", \"correct\": false, \"feedback\": \"Incorrect. Use pencolor().\" }\n ]\n },\n {\n \"question\": \"What does goto(100, 100) do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Moves turtle to position (100, 100)\", \"correct\": true, \"feedback\": \"Correct! goto() moves to specific coordinates.\" },\n { \"answer\": \"Moves forward 100 steps\", \"correct\": false, \"feedback\": \"Incorrect. goto() moves to coordinates.\" },\n { \"answer\": \"Turns 100 degrees\", \"correct\": false, \"feedback\": \"Incorrect. goto() is for position, not turning.\" },\n { \"answer\": \"Draws 100 circles\", \"correct\": false, \"feedback\": \"Incorrect. goto() moves the turtle.\" }\n ]\n },\n {\n \"question\": \"What is a loop?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Code that repeats multiple times\", \"correct\": true, \"feedback\": \"Correct! Loops repeat code automatically.\" },\n { \"answer\": \"Code that runs once\", \"correct\": false, \"feedback\": \"Incorrect. Loops repeat code.\" },\n { \"answer\": \"A type of variable\", \"correct\": false, \"feedback\": \"Incorrect. Loops are for repetition.\" },\n { \"answer\": \"A drawing command\", \"correct\": false, \"feedback\": \"Incorrect. Loops control how many times code runs.\" }\n ]\n },\n {\n \"question\": \"How many times does range(5) repeat?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"5 times\", \"correct\": true, \"feedback\": \"Correct! range(5) creates 0, 1, 2, 3, 4 - that's 5 numbers.\" },\n { \"answer\": \"4 times\", \"correct\": false, \"feedback\": \"Incorrect. range(5) gives you 5 numbers.\" },\n { \"answer\": \"6 times\", \"correct\": false, \"feedback\": \"Incorrect. range(5) stops at 4.\" },\n { \"answer\": \"Forever\", \"correct\": false, \"feedback\": \"Incorrect. range(5) repeats exactly 5 times.\" }\n ]\n },\n {\n \"question\": \"What is the correct syntax for a for loop?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"for i in range(5):\\n tina.forward(50)\", \"correct\": true, \"feedback\": \"Correct! Use 'for', 'in', 'range()', and a colon.\" },\n { \"code\": \"for i range(5)\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Missing 'in' and the colon.\" },\n { \"code\": \"loop 5 times:\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Python uses 'for i in range()'.\" },\n { \"code\": \"repeat(5):\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Use 'for i in range(5):'.\" }\n ]\n },\n {\n \"question\": \"Why use loops instead of writing the same code many times?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Loops are shorter and easier to change\", \"correct\": true, \"feedback\": \"Correct! Loops save time and reduce mistakes.\" },\n { \"answer\": \"Loops make programs slower\", \"correct\": false, \"feedback\": \"Incorrect. Loops are efficient.\" },\n { \"answer\": \"Loops are harder to read\", \"correct\": false, \"feedback\": \"Incorrect. Loops make code clearer.\" },\n { \"answer\": \"You can't use loops in Python\", \"correct\": false, \"feedback\": \"Incorrect. Loops are essential in Python.\" }\n ]\n },\n {\n \"question\": \"What is a variable?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A container that stores a value\", \"correct\": true, \"feedback\": \"Correct! Variables hold information you can use later.\" },\n { \"answer\": \"A type of loop\", \"correct\": false, \"feedback\": \"Incorrect. Variables store data.\" },\n { \"answer\": \"A drawing command\", \"correct\": false, \"feedback\": \"Incorrect. Variables store values.\" },\n { \"answer\": \"A color\", \"correct\": false, \"feedback\": \"Incorrect. Variables can store many types of data.\" }\n ]\n },\n {\n \"question\": \"Which creates a variable named 'size' with value 100?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"size = 100\", \"correct\": true, \"feedback\": \"Correct! Use = to assign a value to a variable.\" },\n { \"code\": \"size == 100\", \"correct\": false, \"feedback\": \"Incorrect. == checks equality. Use = to assign.\" },\n { \"code\": \"100 = size\", \"correct\": false, \"feedback\": \"Incorrect. Variable name goes on the left.\" },\n { \"code\": \"var size = 100\", \"correct\": false, \"feedback\": \"Incorrect. Python doesn't use 'var'.\" }\n ]\n },\n {\n \"question\": \"What happens when you run this code: x = 5; x = x + 1?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"x becomes 6\", \"correct\": true, \"feedback\": \"Correct! x is updated to its old value plus 1.\" },\n { \"answer\": \"x stays 5\", \"correct\": false, \"feedback\": \"Incorrect. x gets a new value.\" },\n { \"answer\": \"x becomes 0\", \"correct\": false, \"feedback\": \"Incorrect. x increases by 1.\" },\n { \"answer\": \"Error\", \"correct\": false, \"feedback\": \"Incorrect. This is valid Python code.\" }\n ]\n },\n {\n \"question\": \"What is a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Reusable code with a name\", \"correct\": true, \"feedback\": \"Correct! Functions are named blocks of code you can use repeatedly.\" },\n { \"answer\": \"A variable\", \"correct\": false, \"feedback\": \"Incorrect. Functions contain code, not just values.\" },\n { \"answer\": \"A loop\", \"correct\": false, \"feedback\": \"Incorrect. Functions and loops are different.\" },\n { \"answer\": \"A color\", \"correct\": false, \"feedback\": \"Incorrect. Functions are code blocks.\" }\n ]\n },\n {\n \"question\": \"Which keyword starts a function definition?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"def\", \"correct\": true, \"feedback\": \"Correct! Use 'def' to define a function.\" },\n { \"answer\": \"function\", \"correct\": false, \"feedback\": \"Incorrect. Python uses 'def'.\" },\n { \"answer\": \"func\", \"correct\": false, \"feedback\": \"Incorrect. Use 'def'.\" },\n { \"answer\": \"define\", \"correct\": false, \"feedback\": \"Incorrect. The keyword is 'def'.\" }\n ]\n },\n {\n \"question\": \"What is the correct way to define a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"def my_function():\\n tina.forward(50)\", \"correct\": true, \"feedback\": \"Correct! Use def, name, parentheses, colon, and indented code.\" },\n { \"code\": \"def my_function()\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Missing the colon after ().\" },\n { \"code\": \"function my_function():\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Use 'def', not 'function'.\" },\n { \"code\": \"my_function():\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Missing 'def' keyword.\" }\n ]\n },\n {\n \"question\": \"When would you use a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"When you want to reuse the same code\", \"correct\": true, \"feedback\": \"Correct! Functions let you use code multiple times.\" },\n { \"answer\": \"When you want to store a number\", \"correct\": false, \"feedback\": \"Incorrect. Use variables to store values.\" },\n { \"answer\": \"When you want to repeat code a specific number of times\", \"correct\": false, \"feedback\": \"Incorrect. That's what loops are for.\" },\n { \"answer\": \"When you want to change colors\", \"correct\": false, \"feedback\": \"Incorrect. Functions are for organizing reusable code.\" }\n ]\n },\n {\n \"question\": \"What does return do in a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Sends a value back from the function\", \"correct\": true, \"feedback\": \"Correct! return gives back a result.\" },\n { \"answer\": \"Prints to the screen\", \"correct\": false, \"feedback\": \"Incorrect. That's what print() does.\" },\n { \"answer\": \"Stops the entire program\", \"correct\": false, \"feedback\": \"Incorrect. return only exits the function.\" },\n { \"answer\": \"Creates a variable\", \"correct\": false, \"feedback\": \"Incorrect. return sends values back.\" }\n ]\n },\n {\n \"question\": \"What is a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A collection of items in order\", \"correct\": true, \"feedback\": \"Correct! Lists hold multiple items like [1, 2, 3].\" },\n { \"answer\": \"A single number\", \"correct\": false, \"feedback\": \"Incorrect. Lists hold multiple items.\" },\n { \"answer\": \"A function\", \"correct\": false, \"feedback\": \"Incorrect. Lists are data containers.\" },\n { \"answer\": \"A loop\", \"correct\": false, \"feedback\": \"Incorrect. Lists store data.\" }\n ]\n },\n {\n \"question\": \"Which is the correct way to create a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"colors = ['red', 'blue', 'green']\", \"correct\": true, \"feedback\": \"Correct! Use square brackets with items separated by commas.\" },\n { \"code\": \"colors = ('red', 'blue', 'green')\", \"correct\": false, \"feedback\": \"Incorrect. Parentheses create tuples. Use [].\" },\n { \"code\": \"colors = {'red', 'blue', 'green'}\", \"correct\": false, \"feedback\": \"Incorrect. Curly braces create sets. Use [].\" },\n { \"code\": \"colors = 'red', 'blue', 'green'\", \"correct\": false, \"feedback\": \"Incorrect. Use square brackets [].\" }\n ]\n },\n {\n \"question\": \"How do you get the first item from a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"list[0]\", \"correct\": true, \"feedback\": \"Correct! Python lists start at index 0.\" },\n { \"answer\": \"list[1]\", \"correct\": false, \"feedback\": \"Incorrect. That's the second item. Use [0].\" },\n { \"answer\": \"list.first()\", \"correct\": false, \"feedback\": \"Incorrect. Use [0] to get the first item.\" },\n { \"answer\": \"first(list)\", \"correct\": false, \"feedback\": \"Incorrect. Use list[0].\" }\n ]\n },\n {\n \"question\": \"What does append() do to a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Adds an item to the end\", \"correct\": true, \"feedback\": \"Correct! append() adds items to the end of the list.\" },\n { \"answer\": \"Removes an item\", \"correct\": false, \"feedback\": \"Incorrect. Use remove() to delete items.\" },\n { \"answer\": \"Sorts the list\", \"correct\": false, \"feedback\": \"Incorrect. Use sort() to order items.\" },\n { \"answer\": \"Counts items\", \"correct\": false, \"feedback\": \"Incorrect. Use len() to count items.\" }\n ]\n },\n {\n \"question\": \"How do you loop through each item in a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"for item in my_list:\\n print(item)\", \"correct\": true, \"feedback\": \"Correct! This loops through each item in the list.\" },\n { \"code\": \"for my_list in item:\\n print(item)\", \"correct\": false, \"feedback\": \"Incorrect. It should be 'for item in my_list'.\" },\n { \"code\": \"loop my_list:\\n print(item)\", \"correct\": false, \"feedback\": \"Incorrect. Use 'for item in my_list:'.\" },\n { \"code\": \"for item:\\n print(my_list)\", \"correct\": false, \"feedback\": \"Incorrect. Missing 'in my_list'.\" }\n ]\n },\n {\n \"question\": \"What does circle(50) draw?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A circle with radius 50\", \"correct\": true, \"feedback\": \"Correct! The number is the radius.\" },\n { \"answer\": \"50 circles\", \"correct\": false, \"feedback\": \"Incorrect. It draws one circle.\" },\n { \"answer\": \"A square\", \"correct\": false, \"feedback\": \"Incorrect. circle() draws circles.\" },\n { \"answer\": \"A line 50 units long\", \"correct\": false, \"feedback\": \"Incorrect. Use forward() for lines.\" }\n ]\n },\n {\n \"question\": \"Where is position (0, 0) on the turtle screen?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Center of the screen\", \"correct\": true, \"feedback\": \"Correct! (0, 0) is in the middle.\" },\n { \"answer\": \"Top left corner\", \"correct\": false, \"feedback\": \"Incorrect. (0, 0) is the center.\" },\n { \"answer\": \"Bottom right corner\", \"correct\": false, \"feedback\": \"Incorrect. (0, 0) is the center.\" },\n { \"answer\": \"Top right corner\", \"correct\": false, \"feedback\": \"Incorrect. (0, 0) is in the middle.\" }\n ]\n },\n {\n \"question\": \"What does speed(0) do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Makes turtle draw fastest\", \"correct\": true, \"feedback\": \"Correct! speed(0) is the fastest setting.\" },\n { \"answer\": \"Stops the turtle\", \"correct\": false, \"feedback\": \"Incorrect. 0 means fastest.\" },\n { \"answer\": \"Makes turtle slowest\", \"correct\": false, \"feedback\": \"Incorrect. 0 is fastest.\" },\n { \"answer\": \"Makes turtle invisible\", \"correct\": false, \"feedback\": \"Incorrect. Use hideturtle() for that.\" }\n ]\n },\n {\n \"question\": \"What does # at the start of a line mean?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"It's a comment (Python ignores it)\", \"correct\": true, \"feedback\": \"Correct! Comments explain code to humans.\" },\n { \"answer\": \"It's a command\", \"correct\": false, \"feedback\": \"Incorrect. # creates comments.\" },\n { \"answer\": \"It causes an error\", \"correct\": false, \"feedback\": \"Incorrect. Comments are ignored.\" },\n { \"answer\": \"It makes code run faster\", \"correct\": false, \"feedback\": \"Incorrect. Comments don't affect speed.\" }\n ]\n },\n {\n \"question\": \"Which code correctly draws a triangle?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"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.\" },\n { \"code\": \"for i in range(4):\\n tina.forward(100)\\n tina.left(120)\", \"correct\": false, \"feedback\": \"Incorrect. Triangle has 3 sides, not 4.\" },\n { \"code\": \"for i in range(3):\\n tina.forward(100)\\n tina.left(90)\", \"correct\": false, \"feedback\": \"Incorrect. Triangle needs 120-degree turns.\" },\n { \"code\": \"tina.triangle(100)\", \"correct\": false, \"feedback\": \"Incorrect. There's no triangle() method.\" }\n ]\n },\n {\n \"question\": \"What does exitonclick() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Keeps window open until you click\", \"correct\": true, \"feedback\": \"Correct! The window waits for a click before closing.\" },\n { \"answer\": \"Closes window immediately\", \"correct\": false, \"feedback\": \"Incorrect. It waits for a click.\" },\n { \"answer\": \"Opens a new window\", \"correct\": false, \"feedback\": \"Incorrect. It controls the current window.\" },\n { \"answer\": \"Saves your drawing\", \"correct\": false, \"feedback\": \"Incorrect. It manages window closing.\" }\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 input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\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 var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n 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 //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\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 aDiv.append(lab);\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) {\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(\"xsJmzEETFCtx\")) {\n show_questions(questionsxsJmzEETFCtx, xsJmzEETFCtx);\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(\"lessons/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 +} 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..8981f531 --- /dev/null +++ b/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb @@ -0,0 +1,278 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "id": "bcd265b8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
    " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "var questionshYlvlnulKRNy=[\n {\n \"question\": \"What is an int in Python?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A whole number\", \"correct\": true, \"feedback\": \"Correct! int stands for integer and represents whole numbers like 5, -10, or 0.\" },\n { \"answer\": \"A decimal number\", \"correct\": false, \"feedback\": \"Incorrect. Decimal numbers use the float type.\" },\n { \"answer\": \"A text value\", \"correct\": false, \"feedback\": \"Incorrect. Text values use the str type.\" },\n { \"answer\": \"A True/False value\", \"correct\": false, \"feedback\": \"Incorrect. True/False values use the bool type.\" }\n ]\n },\n {\n \"question\": \"What is a boolean?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"True or False value\", \"correct\": true, \"feedback\": \"Correct! A boolean (bool) can only be True or False.\" },\n { \"answer\": \"A number\", \"correct\": false, \"feedback\": \"Incorrect. Numbers are int or float types.\" },\n { \"answer\": \"A word or sentence\", \"correct\": false, \"feedback\": \"Incorrect. Words and sentences are strings (str).\" },\n { \"answer\": \"A list of items\", \"correct\": false, \"feedback\": \"Incorrect. Lists are a different data type.\" }\n ]\n },\n {\n \"question\": \"What does the % operator do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Returns the remainder\", \"correct\": true, \"feedback\": \"Correct! 7 % 3 gives 1 because 7 divided by 3 leaves a remainder of 1.\" },\n { \"answer\": \"Divides numbers\", \"correct\": false, \"feedback\": \"Incorrect. Use / or // for division.\" },\n { \"answer\": \"Multiplies numbers\", \"correct\": false, \"feedback\": \"Incorrect. Use * for multiplication.\" },\n { \"answer\": \"Calculates percentage\", \"correct\": false, \"feedback\": \"Incorrect. Despite the symbol, % finds the remainder, not percentages.\" }\n ]\n },\n {\n \"question\": \"What is // used for?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Division without decimals\", \"correct\": true, \"feedback\": \"Correct! 11 // 4 gives 2, dropping the decimal part.\" },\n { \"answer\": \"Regular division\", \"correct\": false, \"feedback\": \"Incorrect. Regular division uses /.\" },\n { \"answer\": \"Finding remainders\", \"correct\": false, \"feedback\": \"Incorrect. Use % to find remainders.\" },\n { \"answer\": \"Adding comments\", \"correct\": false, \"feedback\": \"Incorrect. Comments use #.\" }\n ]\n },\n {\n \"question\": \"What does str() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Converts to text\", \"correct\": true, \"feedback\": \"Correct! str(42) converts the number 42 to the text '42'.\" },\n { \"answer\": \"Converts to a number\", \"correct\": false, \"feedback\": \"Incorrect. Use int() or float() for numbers.\" },\n { \"answer\": \"Checks the data type\", \"correct\": false, \"feedback\": \"Incorrect. Use type() to check data types.\" },\n { \"answer\": \"Makes text uppercase\", \"correct\": false, \"feedback\": \"Incorrect. Use upper() for uppercase.\" }\n ]\n },\n {\n \"question\": \"Which is correct Python syntax?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"if x == 5:\", \"correct\": true, \"feedback\": \"Correct! Use == for comparison and : to start the if block.\" },\n { \"code\": \"if x = 5:\", \"correct\": false, \"feedback\": \"Incorrect. Use = for assignment, but == for comparison.\" },\n { \"code\": \"if x == 5\", \"correct\": false, \"feedback\": \"Incorrect. Missing the colon (:) at the end.\" },\n { \"code\": \"if (x == 5):\", \"correct\": false, \"feedback\": \"Incorrect. Parentheses aren't needed in Python if statements.\" }\n ]\n },\n {\n \"question\": \"What does upper() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Makes text UPPERCASE\", \"correct\": true, \"feedback\": \"Correct! 'hello'.upper() returns 'HELLO'.\" },\n { \"answer\": \"Makes text lowercase\", \"correct\": false, \"feedback\": \"Incorrect. Use lower() for lowercase.\" },\n { \"answer\": \"Capitalizes first letter\", \"correct\": false, \"feedback\": \"Incorrect. Use title() or capitalize() for that.\" },\n { \"answer\": \"Removes spaces\", \"correct\": false, \"feedback\": \"Incorrect. Use strip() to remove spaces.\" }\n ]\n },\n {\n \"question\": \"What is 10 % 3?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"1\", \"correct\": true, \"feedback\": \"Correct! 10 divided by 3 is 3 with remainder 1.\" },\n { \"answer\": \"3\", \"correct\": false, \"feedback\": \"Incorrect. That's 10 // 3 (quotient without remainder).\" },\n { \"answer\": \"3.33\", \"correct\": false, \"feedback\": \"Incorrect. That's 10 / 3 (regular division).\" },\n { \"answer\": \"0\", \"correct\": false, \"feedback\": \"Incorrect. There is a remainder when dividing 10 by 3.\" }\n ]\n },\n {\n \"question\": \"Which creates a string?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"name = \\\"Alice\\\"\", \"correct\": true, \"feedback\": \"Correct! Quotes create strings in Python.\" },\n { \"code\": \"name = Alice\", \"correct\": false, \"feedback\": \"Incorrect. Without quotes, Python looks for a variable named Alice.\" },\n { \"code\": \"name = str\", \"correct\": false, \"feedback\": \"Incorrect. This assigns the str type itself, not a string value.\" },\n { \"code\": \"name = 'Alice\", \"correct\": false, \"feedback\": \"Incorrect. Missing closing quote.\" }\n ]\n },\n {\n \"question\": \"What does type() tell you?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"The data type\", \"correct\": true, \"feedback\": \"Correct! type(5) returns int, type('hi') returns str.\" },\n { \"answer\": \"The value\", \"correct\": false, \"feedback\": \"Incorrect. type() shows the type, not the value itself.\" },\n { \"answer\": \"The length\", \"correct\": false, \"feedback\": \"Incorrect. Use len() for length.\" },\n { \"answer\": \"If it's valid code\", \"correct\": false, \"feedback\": \"Incorrect. type() doesn't validate code syntax.\" }\n ]\n },\n {\n \"question\": \"What does == check?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"If values are equal\", \"correct\": true, \"feedback\": \"Correct! 5 == 5 returns True because they're equal.\" },\n { \"answer\": \"Assigns a value\", \"correct\": false, \"feedback\": \"Incorrect. Use = for assignment, not ==.\" },\n { \"answer\": \"If values are not equal\", \"correct\": false, \"feedback\": \"Incorrect. Use != for not equal.\" },\n { \"answer\": \"If one is greater\", \"correct\": false, \"feedback\": \"Incorrect. Use > for greater than.\" }\n ]\n },\n {\n \"question\": \"What is 15 // 4?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"3\", \"correct\": true, \"feedback\": \"Correct! Floor division gives 3, ignoring the remainder.\" },\n { \"answer\": \"3.75\", \"correct\": false, \"feedback\": \"Incorrect. That's regular division (15 / 4).\" },\n { \"answer\": \"4\", \"correct\": false, \"feedback\": \"Incorrect. 4 goes into 15 three times, not four.\" },\n { \"answer\": \"3.0\", \"correct\": false, \"feedback\": \"Incorrect. With integers, // returns an integer (3), not a float.\" }\n ]\n },\n {\n \"question\": \"What does strip() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Removes spaces from ends\", \"correct\": true, \"feedback\": \"Correct! ' hi '.strip() returns 'hi'.\" },\n { \"answer\": \"Removes all spaces\", \"correct\": false, \"feedback\": \"Incorrect. strip() only removes spaces from the start and end.\" },\n { \"answer\": \"Splits text into parts\", \"correct\": false, \"feedback\": \"Incorrect. Use split() to divide text.\" },\n { \"answer\": \"Makes text lowercase\", \"correct\": false, \"feedback\": \"Incorrect. Use lower() for lowercase.\" }\n ]\n },\n {\n \"question\": \"What causes a TypeError?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"5 + \\\"10\\\"\", \"correct\": true, \"feedback\": \"Correct! Can't add a number and string directly. Convert first.\" },\n { \"code\": \"5 + 10\", \"correct\": false, \"feedback\": \"Incorrect. This is valid and equals 15.\" },\n { \"code\": \"\\\"5\\\" + \\\"10\\\"\", \"correct\": false, \"feedback\": \"Incorrect. This is valid string concatenation ('510').\" },\n { \"code\": \"int(\\\"10\\\") + 5\", \"correct\": false, \"feedback\": \"Incorrect. This is valid after conversion (equals 15).\" }\n ]\n },\n {\n \"question\": \"What does 'and' require?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Both conditions True\", \"correct\": true, \"feedback\": \"Correct! True and True is True, but anything else is False.\" },\n { \"answer\": \"At least one condition True\", \"correct\": false, \"feedback\": \"Incorrect. That's what 'or' requires.\" },\n { \"answer\": \"Both conditions False\", \"correct\": false, \"feedback\": \"Incorrect. Then the result would be False.\" },\n { \"answer\": \"One True, one False\", \"correct\": false, \"feedback\": \"Incorrect. Then 'and' returns False.\" }\n ]\n },\n {\n \"question\": \"What is float used for?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Decimal numbers\", \"correct\": true, \"feedback\": \"Correct! float represents numbers with decimals like 3.14 or 2.5.\" },\n { \"answer\": \"Whole numbers only\", \"correct\": false, \"feedback\": \"Incorrect. Use int for whole numbers.\" },\n { \"answer\": \"Text values\", \"correct\": false, \"feedback\": \"Incorrect. Use str for text.\" },\n { \"answer\": \"True/False values\", \"correct\": false, \"feedback\": \"Incorrect. Use bool for True/False.\" }\n ]\n },\n {\n \"question\": \"Which starts an if statement?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"if x > 5:\", \"correct\": true, \"feedback\": \"Correct! Use if, a condition, then a colon.\" },\n { \"code\": \"if x > 5\", \"correct\": false, \"feedback\": \"Incorrect. Missing the colon (:).\" },\n { \"code\": \"if: x > 5\", \"correct\": false, \"feedback\": \"Incorrect. The colon comes after the condition.\" },\n { \"code\": \"x > 5 if:\", \"correct\": false, \"feedback\": \"Incorrect. Wrong order; start with 'if'.\" }\n ]\n },\n {\n \"question\": \"What does lower() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Makes text lowercase\", \"correct\": true, \"feedback\": \"Correct! 'HELLO'.lower() returns 'hello'.\" },\n { \"answer\": \"Makes text uppercase\", \"correct\": false, \"feedback\": \"Incorrect. Use upper() for uppercase.\" },\n { \"answer\": \"Removes spaces\", \"correct\": false, \"feedback\": \"Incorrect. Use strip() to remove spaces.\" },\n { \"answer\": \"Converts to number\", \"correct\": false, \"feedback\": \"Incorrect. Use int() or float() for numbers.\" }\n ]\n },\n {\n \"question\": \"What is 8 % 4?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"0\", \"correct\": true, \"feedback\": \"Correct! 8 divided by 4 is 2 with no remainder.\" },\n { \"answer\": \"2\", \"correct\": false, \"feedback\": \"Incorrect. That's 8 // 4 (quotient). % gives remainder (0).\" },\n { \"answer\": \"4\", \"correct\": false, \"feedback\": \"Incorrect. The remainder is 0, not 4.\" },\n { \"answer\": \"8\", \"correct\": false, \"feedback\": \"Incorrect. The remainder of 8 % 4 is 0.\" }\n ]\n },\n {\n \"question\": \"What does != mean?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Not equal\", \"correct\": true, \"feedback\": \"Correct! 5 != 3 is True because they're not equal.\" },\n { \"answer\": \"Equal\", \"correct\": false, \"feedback\": \"Incorrect. Use == for equal.\" },\n { \"answer\": \"Greater than\", \"correct\": false, \"feedback\": \"Incorrect. Use > for greater than.\" },\n { \"answer\": \"Less than\", \"correct\": false, \"feedback\": \"Incorrect. Use < for less than.\" }\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 input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\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 var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n 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 //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\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 aDiv.append(lab);\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) {\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(\"hYlvlnulKRNy\")) {\n show_questions(questionshYlvlnulKRNy, hYlvlnulKRNy);\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(\"lessons/Quiz_Data/Module_Two_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 +} diff --git a/lessons/30_Loops/Module_Three_Quiz.ipynb b/lessons/30_Loops/Module_Three_Quiz.ipynb new file mode 100644 index 00000000..1d51da9e --- /dev/null +++ b/lessons/30_Loops/Module_Three_Quiz.ipynb @@ -0,0 +1,278 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "id": "e88338fc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
      " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "var questionsYyrnTJijUcXL=[\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 input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\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 var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n 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 //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\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 aDiv.append(lab);\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) {\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(\"YyrnTJijUcXL\")) {\n show_questions(questionsYyrnTJijUcXL, YyrnTJijUcXL);\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(\"lessons/Quiz_Data/Module_Three_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 +} 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..b4c6b537 --- /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", + "\n", + "display_quiz(\"lessons/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" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lessons/Quiz_Data/Module_Four_Quiz.json b/lessons/Quiz_Data/Module_Four_Quiz.json new file mode 100644 index 00000000..956459af --- /dev/null +++ b/lessons/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/Quiz_Data/Module_One_Quiz.json b/lessons/Quiz_Data/Module_One_Quiz.json new file mode 100644 index 00000000..61d5a686 --- /dev/null +++ b/lessons/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/Quiz_Data/Module_Three_Quiz.json b/lessons/Quiz_Data/Module_Three_Quiz.json new file mode 100644 index 00000000..f0fd30ff --- /dev/null +++ b/lessons/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/Quiz_Data/Module_Two_Quiz.json b/lessons/Quiz_Data/Module_Two_Quiz.json new file mode 100644 index 00000000..87a86bb2 --- /dev/null +++ b/lessons/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 From fe466476f165f4bbb7fdea4858d4e605c7f6dd2d Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Thu, 8 Jan 2026 19:34:59 -0500 Subject: [PATCH 138/159] Added quizzes to the lesson browser --- lessons/.jtl/syllabus.yaml | 12 + lessons/10_Turtles/Module_One_Quiz.ipynb | 253 +---------------- .../20_Types_and_Logic/Module_Two_Quiz.ipynb | 257 +---------------- lessons/30_Loops/Module_Three_Quiz.ipynb | 261 ++---------------- .../Module_Four_Quiz.ipynb | 4 + 5 files changed, 58 insertions(+), 729 deletions(-) diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index 449930c6..4cf987fb 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -118,6 +118,9 @@ modules: - name: Turtle Spiral exercise: 10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py display: true + - name: Module One Quiz + uid: QvJp7kYB + exercise: 10_Turtles/Module_One_Quiz.ipynb - name: Types and Logic uid: ryHvW6vk lessons: @@ -143,6 +146,9 @@ modules: - name: Code Challenges uid: jJI6aomB exercise: 20_Types_and_Logic/80_Code_Challenges.ipynb + - name: Module Two Quiz + uid: NZt4sXmo + exercise: 20_Types_and_Logic/Module_Two_Quiz.ipynb - name: Loops uid: TzgRqJlw lessons: @@ -191,6 +197,9 @@ modules: - name: Extras uid: VJSgvOr5 exercise: 30_Loops/160_Extras.ipynb + - name: Module Three Quiz + uid: L2cMhRfP + exercise: 30_Loops/Module_Three_Quiz.ipynb - name: Data Structures Func uid: fDPxSid0 lessons: @@ -209,6 +218,9 @@ modules: - name: Tic Tac Toe exercise: 40_Data_Structures_Func/50_Tic_Tac_Toe.py display: true + - name: Module Four Quiz + uid: bX9eUdqT + exercise: 40_Data_Structures_Func/Module_Four_Quiz.ipynb - name: Projects uid: rLq5eVeW lessons: diff --git a/lessons/10_Turtles/Module_One_Quiz.ipynb b/lessons/10_Turtles/Module_One_Quiz.ipynb index 01abd166..e4176fbc 100644 --- a/lessons/10_Turtles/Module_One_Quiz.ipynb +++ b/lessons/10_Turtles/Module_One_Quiz.ipynb @@ -1,252 +1,19 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "e55e7d8e", + "metadata": {}, + "source": [ + "# **Module One Quiz**" + ] + }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "495aaca3", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
        " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/javascript": "var questionsxsJmzEETFCtx=[\n {\n \"question\": \"What does forward() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Moves the turtle forward\", \"correct\": true, \"feedback\": \"Correct! forward() moves the turtle in the direction it's facing.\" },\n { \"answer\": \"Turns the turtle\", \"correct\": false, \"feedback\": \"Incorrect. forward() moves, not turns.\" },\n { \"answer\": \"Changes color\", \"correct\": false, \"feedback\": \"Incorrect. forward() is for movement.\" },\n { \"answer\": \"Draws a circle\", \"correct\": false, \"feedback\": \"Incorrect. Use circle() to draw circles.\" }\n ]\n },\n {\n \"question\": \"Which command turns the turtle left?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"left()\", \"correct\": true, \"feedback\": \"Correct! left() turns the turtle to the left.\" },\n { \"answer\": \"turn()\", \"correct\": false, \"feedback\": \"Incorrect. Use left() or right().\" },\n { \"answer\": \"rotate()\", \"correct\": false, \"feedback\": \"Incorrect. Use left() to turn.\" },\n { \"answer\": \"spin()\", \"correct\": false, \"feedback\": \"Incorrect. Use left() to turn left.\" }\n ]\n },\n {\n \"question\": \"How many degrees is a square corner?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"90 degrees\", \"correct\": true, \"feedback\": \"Correct! Square corners are 90 degrees.\" },\n { \"answer\": \"45 degrees\", \"correct\": false, \"feedback\": \"Incorrect. Squares have 90-degree corners.\" },\n { \"answer\": \"180 degrees\", \"correct\": false, \"feedback\": \"Incorrect. That's a straight line.\" },\n { \"answer\": \"360 degrees\", \"correct\": false, \"feedback\": \"Incorrect. That's a full circle.\" }\n ]\n },\n {\n \"question\": \"What does penup() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Stops drawing when the turtle moves\", \"correct\": true, \"feedback\": \"Correct! penup() lifts the pen so the turtle doesn't draw.\" },\n { \"answer\": \"Stops the turtle\", \"correct\": false, \"feedback\": \"Incorrect. The turtle can still move.\" },\n { \"answer\": \"Moves the turtle up\", \"correct\": false, \"feedback\": \"Incorrect. It controls drawing, not direction.\" },\n { \"answer\": \"Deletes the drawing\", \"correct\": false, \"feedback\": \"Incorrect. It just lifts the pen.\" }\n ]\n },\n {\n \"question\": \"When would you use penup()?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"When you want to move without drawing\", \"correct\": true, \"feedback\": \"Correct! Use penup() to move the turtle without leaving a line.\" },\n { \"answer\": \"When you want to draw faster\", \"correct\": false, \"feedback\": \"Incorrect. Use speed() to change drawing speed.\" },\n { \"answer\": \"When you want to change colors\", \"correct\": false, \"feedback\": \"Incorrect. Use pencolor() to change colors.\" },\n { \"answer\": \"When you want to stop the program\", \"correct\": false, \"feedback\": \"Incorrect. penup() just stops drawing.\" }\n ]\n },\n {\n \"question\": \"Which command changes the pen color?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"pencolor()\", \"correct\": true, \"feedback\": \"Correct! pencolor() sets the drawing color.\" },\n { \"answer\": \"color()\", \"correct\": false, \"feedback\": \"Close, but pencolor() is more specific for the pen.\" },\n { \"answer\": \"setcolor()\", \"correct\": false, \"feedback\": \"Incorrect. Use pencolor().\" },\n { \"answer\": \"changecolor()\", \"correct\": false, \"feedback\": \"Incorrect. Use pencolor().\" }\n ]\n },\n {\n \"question\": \"What does goto(100, 100) do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Moves turtle to position (100, 100)\", \"correct\": true, \"feedback\": \"Correct! goto() moves to specific coordinates.\" },\n { \"answer\": \"Moves forward 100 steps\", \"correct\": false, \"feedback\": \"Incorrect. goto() moves to coordinates.\" },\n { \"answer\": \"Turns 100 degrees\", \"correct\": false, \"feedback\": \"Incorrect. goto() is for position, not turning.\" },\n { \"answer\": \"Draws 100 circles\", \"correct\": false, \"feedback\": \"Incorrect. goto() moves the turtle.\" }\n ]\n },\n {\n \"question\": \"What is a loop?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Code that repeats multiple times\", \"correct\": true, \"feedback\": \"Correct! Loops repeat code automatically.\" },\n { \"answer\": \"Code that runs once\", \"correct\": false, \"feedback\": \"Incorrect. Loops repeat code.\" },\n { \"answer\": \"A type of variable\", \"correct\": false, \"feedback\": \"Incorrect. Loops are for repetition.\" },\n { \"answer\": \"A drawing command\", \"correct\": false, \"feedback\": \"Incorrect. Loops control how many times code runs.\" }\n ]\n },\n {\n \"question\": \"How many times does range(5) repeat?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"5 times\", \"correct\": true, \"feedback\": \"Correct! range(5) creates 0, 1, 2, 3, 4 - that's 5 numbers.\" },\n { \"answer\": \"4 times\", \"correct\": false, \"feedback\": \"Incorrect. range(5) gives you 5 numbers.\" },\n { \"answer\": \"6 times\", \"correct\": false, \"feedback\": \"Incorrect. range(5) stops at 4.\" },\n { \"answer\": \"Forever\", \"correct\": false, \"feedback\": \"Incorrect. range(5) repeats exactly 5 times.\" }\n ]\n },\n {\n \"question\": \"What is the correct syntax for a for loop?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"for i in range(5):\\n tina.forward(50)\", \"correct\": true, \"feedback\": \"Correct! Use 'for', 'in', 'range()', and a colon.\" },\n { \"code\": \"for i range(5)\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Missing 'in' and the colon.\" },\n { \"code\": \"loop 5 times:\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Python uses 'for i in range()'.\" },\n { \"code\": \"repeat(5):\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Use 'for i in range(5):'.\" }\n ]\n },\n {\n \"question\": \"Why use loops instead of writing the same code many times?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Loops are shorter and easier to change\", \"correct\": true, \"feedback\": \"Correct! Loops save time and reduce mistakes.\" },\n { \"answer\": \"Loops make programs slower\", \"correct\": false, \"feedback\": \"Incorrect. Loops are efficient.\" },\n { \"answer\": \"Loops are harder to read\", \"correct\": false, \"feedback\": \"Incorrect. Loops make code clearer.\" },\n { \"answer\": \"You can't use loops in Python\", \"correct\": false, \"feedback\": \"Incorrect. Loops are essential in Python.\" }\n ]\n },\n {\n \"question\": \"What is a variable?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A container that stores a value\", \"correct\": true, \"feedback\": \"Correct! Variables hold information you can use later.\" },\n { \"answer\": \"A type of loop\", \"correct\": false, \"feedback\": \"Incorrect. Variables store data.\" },\n { \"answer\": \"A drawing command\", \"correct\": false, \"feedback\": \"Incorrect. Variables store values.\" },\n { \"answer\": \"A color\", \"correct\": false, \"feedback\": \"Incorrect. Variables can store many types of data.\" }\n ]\n },\n {\n \"question\": \"Which creates a variable named 'size' with value 100?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"size = 100\", \"correct\": true, \"feedback\": \"Correct! Use = to assign a value to a variable.\" },\n { \"code\": \"size == 100\", \"correct\": false, \"feedback\": \"Incorrect. == checks equality. Use = to assign.\" },\n { \"code\": \"100 = size\", \"correct\": false, \"feedback\": \"Incorrect. Variable name goes on the left.\" },\n { \"code\": \"var size = 100\", \"correct\": false, \"feedback\": \"Incorrect. Python doesn't use 'var'.\" }\n ]\n },\n {\n \"question\": \"What happens when you run this code: x = 5; x = x + 1?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"x becomes 6\", \"correct\": true, \"feedback\": \"Correct! x is updated to its old value plus 1.\" },\n { \"answer\": \"x stays 5\", \"correct\": false, \"feedback\": \"Incorrect. x gets a new value.\" },\n { \"answer\": \"x becomes 0\", \"correct\": false, \"feedback\": \"Incorrect. x increases by 1.\" },\n { \"answer\": \"Error\", \"correct\": false, \"feedback\": \"Incorrect. This is valid Python code.\" }\n ]\n },\n {\n \"question\": \"What is a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Reusable code with a name\", \"correct\": true, \"feedback\": \"Correct! Functions are named blocks of code you can use repeatedly.\" },\n { \"answer\": \"A variable\", \"correct\": false, \"feedback\": \"Incorrect. Functions contain code, not just values.\" },\n { \"answer\": \"A loop\", \"correct\": false, \"feedback\": \"Incorrect. Functions and loops are different.\" },\n { \"answer\": \"A color\", \"correct\": false, \"feedback\": \"Incorrect. Functions are code blocks.\" }\n ]\n },\n {\n \"question\": \"Which keyword starts a function definition?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"def\", \"correct\": true, \"feedback\": \"Correct! Use 'def' to define a function.\" },\n { \"answer\": \"function\", \"correct\": false, \"feedback\": \"Incorrect. Python uses 'def'.\" },\n { \"answer\": \"func\", \"correct\": false, \"feedback\": \"Incorrect. Use 'def'.\" },\n { \"answer\": \"define\", \"correct\": false, \"feedback\": \"Incorrect. The keyword is 'def'.\" }\n ]\n },\n {\n \"question\": \"What is the correct way to define a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"def my_function():\\n tina.forward(50)\", \"correct\": true, \"feedback\": \"Correct! Use def, name, parentheses, colon, and indented code.\" },\n { \"code\": \"def my_function()\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Missing the colon after ().\" },\n { \"code\": \"function my_function():\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Use 'def', not 'function'.\" },\n { \"code\": \"my_function():\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Missing 'def' keyword.\" }\n ]\n },\n {\n \"question\": \"When would you use a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"When you want to reuse the same code\", \"correct\": true, \"feedback\": \"Correct! Functions let you use code multiple times.\" },\n { \"answer\": \"When you want to store a number\", \"correct\": false, \"feedback\": \"Incorrect. Use variables to store values.\" },\n { \"answer\": \"When you want to repeat code a specific number of times\", \"correct\": false, \"feedback\": \"Incorrect. That's what loops are for.\" },\n { \"answer\": \"When you want to change colors\", \"correct\": false, \"feedback\": \"Incorrect. Functions are for organizing reusable code.\" }\n ]\n },\n {\n \"question\": \"What does return do in a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Sends a value back from the function\", \"correct\": true, \"feedback\": \"Correct! return gives back a result.\" },\n { \"answer\": \"Prints to the screen\", \"correct\": false, \"feedback\": \"Incorrect. That's what print() does.\" },\n { \"answer\": \"Stops the entire program\", \"correct\": false, \"feedback\": \"Incorrect. return only exits the function.\" },\n { \"answer\": \"Creates a variable\", \"correct\": false, \"feedback\": \"Incorrect. return sends values back.\" }\n ]\n },\n {\n \"question\": \"What is a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A collection of items in order\", \"correct\": true, \"feedback\": \"Correct! Lists hold multiple items like [1, 2, 3].\" },\n { \"answer\": \"A single number\", \"correct\": false, \"feedback\": \"Incorrect. Lists hold multiple items.\" },\n { \"answer\": \"A function\", \"correct\": false, \"feedback\": \"Incorrect. Lists are data containers.\" },\n { \"answer\": \"A loop\", \"correct\": false, \"feedback\": \"Incorrect. Lists store data.\" }\n ]\n },\n {\n \"question\": \"Which is the correct way to create a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"colors = ['red', 'blue', 'green']\", \"correct\": true, \"feedback\": \"Correct! Use square brackets with items separated by commas.\" },\n { \"code\": \"colors = ('red', 'blue', 'green')\", \"correct\": false, \"feedback\": \"Incorrect. Parentheses create tuples. Use [].\" },\n { \"code\": \"colors = {'red', 'blue', 'green'}\", \"correct\": false, \"feedback\": \"Incorrect. Curly braces create sets. Use [].\" },\n { \"code\": \"colors = 'red', 'blue', 'green'\", \"correct\": false, \"feedback\": \"Incorrect. Use square brackets [].\" }\n ]\n },\n {\n \"question\": \"How do you get the first item from a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"list[0]\", \"correct\": true, \"feedback\": \"Correct! Python lists start at index 0.\" },\n { \"answer\": \"list[1]\", \"correct\": false, \"feedback\": \"Incorrect. That's the second item. Use [0].\" },\n { \"answer\": \"list.first()\", \"correct\": false, \"feedback\": \"Incorrect. Use [0] to get the first item.\" },\n { \"answer\": \"first(list)\", \"correct\": false, \"feedback\": \"Incorrect. Use list[0].\" }\n ]\n },\n {\n \"question\": \"What does append() do to a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Adds an item to the end\", \"correct\": true, \"feedback\": \"Correct! append() adds items to the end of the list.\" },\n { \"answer\": \"Removes an item\", \"correct\": false, \"feedback\": \"Incorrect. Use remove() to delete items.\" },\n { \"answer\": \"Sorts the list\", \"correct\": false, \"feedback\": \"Incorrect. Use sort() to order items.\" },\n { \"answer\": \"Counts items\", \"correct\": false, \"feedback\": \"Incorrect. Use len() to count items.\" }\n ]\n },\n {\n \"question\": \"How do you loop through each item in a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"for item in my_list:\\n print(item)\", \"correct\": true, \"feedback\": \"Correct! This loops through each item in the list.\" },\n { \"code\": \"for my_list in item:\\n print(item)\", \"correct\": false, \"feedback\": \"Incorrect. It should be 'for item in my_list'.\" },\n { \"code\": \"loop my_list:\\n print(item)\", \"correct\": false, \"feedback\": \"Incorrect. Use 'for item in my_list:'.\" },\n { \"code\": \"for item:\\n print(my_list)\", \"correct\": false, \"feedback\": \"Incorrect. Missing 'in my_list'.\" }\n ]\n },\n {\n \"question\": \"What does circle(50) draw?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A circle with radius 50\", \"correct\": true, \"feedback\": \"Correct! The number is the radius.\" },\n { \"answer\": \"50 circles\", \"correct\": false, \"feedback\": \"Incorrect. It draws one circle.\" },\n { \"answer\": \"A square\", \"correct\": false, \"feedback\": \"Incorrect. circle() draws circles.\" },\n { \"answer\": \"A line 50 units long\", \"correct\": false, \"feedback\": \"Incorrect. Use forward() for lines.\" }\n ]\n },\n {\n \"question\": \"Where is position (0, 0) on the turtle screen?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Center of the screen\", \"correct\": true, \"feedback\": \"Correct! (0, 0) is in the middle.\" },\n { \"answer\": \"Top left corner\", \"correct\": false, \"feedback\": \"Incorrect. (0, 0) is the center.\" },\n { \"answer\": \"Bottom right corner\", \"correct\": false, \"feedback\": \"Incorrect. (0, 0) is the center.\" },\n { \"answer\": \"Top right corner\", \"correct\": false, \"feedback\": \"Incorrect. (0, 0) is in the middle.\" }\n ]\n },\n {\n \"question\": \"What does speed(0) do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Makes turtle draw fastest\", \"correct\": true, \"feedback\": \"Correct! speed(0) is the fastest setting.\" },\n { \"answer\": \"Stops the turtle\", \"correct\": false, \"feedback\": \"Incorrect. 0 means fastest.\" },\n { \"answer\": \"Makes turtle slowest\", \"correct\": false, \"feedback\": \"Incorrect. 0 is fastest.\" },\n { \"answer\": \"Makes turtle invisible\", \"correct\": false, \"feedback\": \"Incorrect. Use hideturtle() for that.\" }\n ]\n },\n {\n \"question\": \"What does # at the start of a line mean?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"It's a comment (Python ignores it)\", \"correct\": true, \"feedback\": \"Correct! Comments explain code to humans.\" },\n { \"answer\": \"It's a command\", \"correct\": false, \"feedback\": \"Incorrect. # creates comments.\" },\n { \"answer\": \"It causes an error\", \"correct\": false, \"feedback\": \"Incorrect. Comments are ignored.\" },\n { \"answer\": \"It makes code run faster\", \"correct\": false, \"feedback\": \"Incorrect. Comments don't affect speed.\" }\n ]\n },\n {\n \"question\": \"Which code correctly draws a triangle?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"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.\" },\n { \"code\": \"for i in range(4):\\n tina.forward(100)\\n tina.left(120)\", \"correct\": false, \"feedback\": \"Incorrect. Triangle has 3 sides, not 4.\" },\n { \"code\": \"for i in range(3):\\n tina.forward(100)\\n tina.left(90)\", \"correct\": false, \"feedback\": \"Incorrect. Triangle needs 120-degree turns.\" },\n { \"code\": \"tina.triangle(100)\", \"correct\": false, \"feedback\": \"Incorrect. There's no triangle() method.\" }\n ]\n },\n {\n \"question\": \"What does exitonclick() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Keeps window open until you click\", \"correct\": true, \"feedback\": \"Correct! The window waits for a click before closing.\" },\n { \"answer\": \"Closes window immediately\", \"correct\": false, \"feedback\": \"Incorrect. It waits for a click.\" },\n { \"answer\": \"Opens a new window\", \"correct\": false, \"feedback\": \"Incorrect. It controls the current window.\" },\n { \"answer\": \"Saves your drawing\", \"correct\": false, \"feedback\": \"Incorrect. It manages window closing.\" }\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 input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\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 var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n 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 //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\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 aDiv.append(lab);\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) {\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(\"xsJmzEETFCtx\")) {\n show_questions(questionsxsJmzEETFCtx, xsJmzEETFCtx);\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" - } - ], + "outputs": [], "source": [ "from jupyterquiz import display_quiz\n", "\n", diff --git a/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb b/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb index 8981f531..32cb9d66 100644 --- a/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb +++ b/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb @@ -1,252 +1,19 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "04a3f84d", + "metadata": {}, + "source": [ + "# **Module Two Quiz**" + ] + }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "bcd265b8", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
          " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/javascript": "var questionshYlvlnulKRNy=[\n {\n \"question\": \"What is an int in Python?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A whole number\", \"correct\": true, \"feedback\": \"Correct! int stands for integer and represents whole numbers like 5, -10, or 0.\" },\n { \"answer\": \"A decimal number\", \"correct\": false, \"feedback\": \"Incorrect. Decimal numbers use the float type.\" },\n { \"answer\": \"A text value\", \"correct\": false, \"feedback\": \"Incorrect. Text values use the str type.\" },\n { \"answer\": \"A True/False value\", \"correct\": false, \"feedback\": \"Incorrect. True/False values use the bool type.\" }\n ]\n },\n {\n \"question\": \"What is a boolean?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"True or False value\", \"correct\": true, \"feedback\": \"Correct! A boolean (bool) can only be True or False.\" },\n { \"answer\": \"A number\", \"correct\": false, \"feedback\": \"Incorrect. Numbers are int or float types.\" },\n { \"answer\": \"A word or sentence\", \"correct\": false, \"feedback\": \"Incorrect. Words and sentences are strings (str).\" },\n { \"answer\": \"A list of items\", \"correct\": false, \"feedback\": \"Incorrect. Lists are a different data type.\" }\n ]\n },\n {\n \"question\": \"What does the % operator do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Returns the remainder\", \"correct\": true, \"feedback\": \"Correct! 7 % 3 gives 1 because 7 divided by 3 leaves a remainder of 1.\" },\n { \"answer\": \"Divides numbers\", \"correct\": false, \"feedback\": \"Incorrect. Use / or // for division.\" },\n { \"answer\": \"Multiplies numbers\", \"correct\": false, \"feedback\": \"Incorrect. Use * for multiplication.\" },\n { \"answer\": \"Calculates percentage\", \"correct\": false, \"feedback\": \"Incorrect. Despite the symbol, % finds the remainder, not percentages.\" }\n ]\n },\n {\n \"question\": \"What is // used for?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Division without decimals\", \"correct\": true, \"feedback\": \"Correct! 11 // 4 gives 2, dropping the decimal part.\" },\n { \"answer\": \"Regular division\", \"correct\": false, \"feedback\": \"Incorrect. Regular division uses /.\" },\n { \"answer\": \"Finding remainders\", \"correct\": false, \"feedback\": \"Incorrect. Use % to find remainders.\" },\n { \"answer\": \"Adding comments\", \"correct\": false, \"feedback\": \"Incorrect. Comments use #.\" }\n ]\n },\n {\n \"question\": \"What does str() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Converts to text\", \"correct\": true, \"feedback\": \"Correct! str(42) converts the number 42 to the text '42'.\" },\n { \"answer\": \"Converts to a number\", \"correct\": false, \"feedback\": \"Incorrect. Use int() or float() for numbers.\" },\n { \"answer\": \"Checks the data type\", \"correct\": false, \"feedback\": \"Incorrect. Use type() to check data types.\" },\n { \"answer\": \"Makes text uppercase\", \"correct\": false, \"feedback\": \"Incorrect. Use upper() for uppercase.\" }\n ]\n },\n {\n \"question\": \"Which is correct Python syntax?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"if x == 5:\", \"correct\": true, \"feedback\": \"Correct! Use == for comparison and : to start the if block.\" },\n { \"code\": \"if x = 5:\", \"correct\": false, \"feedback\": \"Incorrect. Use = for assignment, but == for comparison.\" },\n { \"code\": \"if x == 5\", \"correct\": false, \"feedback\": \"Incorrect. Missing the colon (:) at the end.\" },\n { \"code\": \"if (x == 5):\", \"correct\": false, \"feedback\": \"Incorrect. Parentheses aren't needed in Python if statements.\" }\n ]\n },\n {\n \"question\": \"What does upper() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Makes text UPPERCASE\", \"correct\": true, \"feedback\": \"Correct! 'hello'.upper() returns 'HELLO'.\" },\n { \"answer\": \"Makes text lowercase\", \"correct\": false, \"feedback\": \"Incorrect. Use lower() for lowercase.\" },\n { \"answer\": \"Capitalizes first letter\", \"correct\": false, \"feedback\": \"Incorrect. Use title() or capitalize() for that.\" },\n { \"answer\": \"Removes spaces\", \"correct\": false, \"feedback\": \"Incorrect. Use strip() to remove spaces.\" }\n ]\n },\n {\n \"question\": \"What is 10 % 3?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"1\", \"correct\": true, \"feedback\": \"Correct! 10 divided by 3 is 3 with remainder 1.\" },\n { \"answer\": \"3\", \"correct\": false, \"feedback\": \"Incorrect. That's 10 // 3 (quotient without remainder).\" },\n { \"answer\": \"3.33\", \"correct\": false, \"feedback\": \"Incorrect. That's 10 / 3 (regular division).\" },\n { \"answer\": \"0\", \"correct\": false, \"feedback\": \"Incorrect. There is a remainder when dividing 10 by 3.\" }\n ]\n },\n {\n \"question\": \"Which creates a string?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"name = \\\"Alice\\\"\", \"correct\": true, \"feedback\": \"Correct! Quotes create strings in Python.\" },\n { \"code\": \"name = Alice\", \"correct\": false, \"feedback\": \"Incorrect. Without quotes, Python looks for a variable named Alice.\" },\n { \"code\": \"name = str\", \"correct\": false, \"feedback\": \"Incorrect. This assigns the str type itself, not a string value.\" },\n { \"code\": \"name = 'Alice\", \"correct\": false, \"feedback\": \"Incorrect. Missing closing quote.\" }\n ]\n },\n {\n \"question\": \"What does type() tell you?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"The data type\", \"correct\": true, \"feedback\": \"Correct! type(5) returns int, type('hi') returns str.\" },\n { \"answer\": \"The value\", \"correct\": false, \"feedback\": \"Incorrect. type() shows the type, not the value itself.\" },\n { \"answer\": \"The length\", \"correct\": false, \"feedback\": \"Incorrect. Use len() for length.\" },\n { \"answer\": \"If it's valid code\", \"correct\": false, \"feedback\": \"Incorrect. type() doesn't validate code syntax.\" }\n ]\n },\n {\n \"question\": \"What does == check?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"If values are equal\", \"correct\": true, \"feedback\": \"Correct! 5 == 5 returns True because they're equal.\" },\n { \"answer\": \"Assigns a value\", \"correct\": false, \"feedback\": \"Incorrect. Use = for assignment, not ==.\" },\n { \"answer\": \"If values are not equal\", \"correct\": false, \"feedback\": \"Incorrect. Use != for not equal.\" },\n { \"answer\": \"If one is greater\", \"correct\": false, \"feedback\": \"Incorrect. Use > for greater than.\" }\n ]\n },\n {\n \"question\": \"What is 15 // 4?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"3\", \"correct\": true, \"feedback\": \"Correct! Floor division gives 3, ignoring the remainder.\" },\n { \"answer\": \"3.75\", \"correct\": false, \"feedback\": \"Incorrect. That's regular division (15 / 4).\" },\n { \"answer\": \"4\", \"correct\": false, \"feedback\": \"Incorrect. 4 goes into 15 three times, not four.\" },\n { \"answer\": \"3.0\", \"correct\": false, \"feedback\": \"Incorrect. With integers, // returns an integer (3), not a float.\" }\n ]\n },\n {\n \"question\": \"What does strip() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Removes spaces from ends\", \"correct\": true, \"feedback\": \"Correct! ' hi '.strip() returns 'hi'.\" },\n { \"answer\": \"Removes all spaces\", \"correct\": false, \"feedback\": \"Incorrect. strip() only removes spaces from the start and end.\" },\n { \"answer\": \"Splits text into parts\", \"correct\": false, \"feedback\": \"Incorrect. Use split() to divide text.\" },\n { \"answer\": \"Makes text lowercase\", \"correct\": false, \"feedback\": \"Incorrect. Use lower() for lowercase.\" }\n ]\n },\n {\n \"question\": \"What causes a TypeError?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"5 + \\\"10\\\"\", \"correct\": true, \"feedback\": \"Correct! Can't add a number and string directly. Convert first.\" },\n { \"code\": \"5 + 10\", \"correct\": false, \"feedback\": \"Incorrect. This is valid and equals 15.\" },\n { \"code\": \"\\\"5\\\" + \\\"10\\\"\", \"correct\": false, \"feedback\": \"Incorrect. This is valid string concatenation ('510').\" },\n { \"code\": \"int(\\\"10\\\") + 5\", \"correct\": false, \"feedback\": \"Incorrect. This is valid after conversion (equals 15).\" }\n ]\n },\n {\n \"question\": \"What does 'and' require?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Both conditions True\", \"correct\": true, \"feedback\": \"Correct! True and True is True, but anything else is False.\" },\n { \"answer\": \"At least one condition True\", \"correct\": false, \"feedback\": \"Incorrect. That's what 'or' requires.\" },\n { \"answer\": \"Both conditions False\", \"correct\": false, \"feedback\": \"Incorrect. Then the result would be False.\" },\n { \"answer\": \"One True, one False\", \"correct\": false, \"feedback\": \"Incorrect. Then 'and' returns False.\" }\n ]\n },\n {\n \"question\": \"What is float used for?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Decimal numbers\", \"correct\": true, \"feedback\": \"Correct! float represents numbers with decimals like 3.14 or 2.5.\" },\n { \"answer\": \"Whole numbers only\", \"correct\": false, \"feedback\": \"Incorrect. Use int for whole numbers.\" },\n { \"answer\": \"Text values\", \"correct\": false, \"feedback\": \"Incorrect. Use str for text.\" },\n { \"answer\": \"True/False values\", \"correct\": false, \"feedback\": \"Incorrect. Use bool for True/False.\" }\n ]\n },\n {\n \"question\": \"Which starts an if statement?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"if x > 5:\", \"correct\": true, \"feedback\": \"Correct! Use if, a condition, then a colon.\" },\n { \"code\": \"if x > 5\", \"correct\": false, \"feedback\": \"Incorrect. Missing the colon (:).\" },\n { \"code\": \"if: x > 5\", \"correct\": false, \"feedback\": \"Incorrect. The colon comes after the condition.\" },\n { \"code\": \"x > 5 if:\", \"correct\": false, \"feedback\": \"Incorrect. Wrong order; start with 'if'.\" }\n ]\n },\n {\n \"question\": \"What does lower() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Makes text lowercase\", \"correct\": true, \"feedback\": \"Correct! 'HELLO'.lower() returns 'hello'.\" },\n { \"answer\": \"Makes text uppercase\", \"correct\": false, \"feedback\": \"Incorrect. Use upper() for uppercase.\" },\n { \"answer\": \"Removes spaces\", \"correct\": false, \"feedback\": \"Incorrect. Use strip() to remove spaces.\" },\n { \"answer\": \"Converts to number\", \"correct\": false, \"feedback\": \"Incorrect. Use int() or float() for numbers.\" }\n ]\n },\n {\n \"question\": \"What is 8 % 4?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"0\", \"correct\": true, \"feedback\": \"Correct! 8 divided by 4 is 2 with no remainder.\" },\n { \"answer\": \"2\", \"correct\": false, \"feedback\": \"Incorrect. That's 8 // 4 (quotient). % gives remainder (0).\" },\n { \"answer\": \"4\", \"correct\": false, \"feedback\": \"Incorrect. The remainder is 0, not 4.\" },\n { \"answer\": \"8\", \"correct\": false, \"feedback\": \"Incorrect. The remainder of 8 % 4 is 0.\" }\n ]\n },\n {\n \"question\": \"What does != mean?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Not equal\", \"correct\": true, \"feedback\": \"Correct! 5 != 3 is True because they're not equal.\" },\n { \"answer\": \"Equal\", \"correct\": false, \"feedback\": \"Incorrect. Use == for equal.\" },\n { \"answer\": \"Greater than\", \"correct\": false, \"feedback\": \"Incorrect. Use > for greater than.\" },\n { \"answer\": \"Less than\", \"correct\": false, \"feedback\": \"Incorrect. Use < for less than.\" }\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 input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\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 var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n 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 //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\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 aDiv.append(lab);\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) {\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(\"hYlvlnulKRNy\")) {\n show_questions(questionshYlvlnulKRNy, hYlvlnulKRNy);\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" - } - ], + "outputs": [], "source": [ "from jupyterquiz import display_quiz\n", "\n", @@ -273,6 +40,10 @@ "version": "3.13.5" } }, + "syllabus": { + "name": "Module Two Quiz", + "uid": "NZt4sXmo" +}, "nbformat": 4, "nbformat_minor": 5 } diff --git a/lessons/30_Loops/Module_Three_Quiz.ipynb b/lessons/30_Loops/Module_Three_Quiz.ipynb index 1d51da9e..2d7cccf1 100644 --- a/lessons/30_Loops/Module_Three_Quiz.ipynb +++ b/lessons/30_Loops/Module_Three_Quiz.ipynb @@ -1,252 +1,23 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "b5757e58", + "metadata": { + "tags": [ + "parameters" + ] + }, + "source": [ + "# **Module Three Quiz**" + ] + }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "e88338fc", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
            " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/javascript": "var questionsYyrnTJijUcXL=[\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 input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\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 var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n 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 //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\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 aDiv.append(lab);\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) {\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(\"YyrnTJijUcXL\")) {\n show_questions(questionsYyrnTJijUcXL, YyrnTJijUcXL);\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" - } - ], + "outputs": [], "source": [ "from jupyterquiz import display_quiz\n", "\n", @@ -273,6 +44,10 @@ "version": "3.13.5" } }, + "syllabus": { + "name": "Module Three Quiz", + "uid": "L2cMhRfP" +}, "nbformat": 4, "nbformat_minor": 5 } diff --git a/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb b/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb index b4c6b537..6f274133 100644 --- a/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb +++ b/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb @@ -40,6 +40,10 @@ "version": "3.13.5" } }, + "syllabus": { + "name": "Module Four Quiz", + "uid": "bX9eUdqT" +}, "nbformat": 4, "nbformat_minor": 5 } From 720feac00d39045f0029a9b33e13097bde47dcee Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 12 Jan 2026 02:30:14 -0500 Subject: [PATCH 139/159] Made quality of life improvements --- lessons/30_Loops/120_More_Iterables.ipynb | 258 ++++++++++++++-------- 1 file changed, 160 insertions(+), 98 deletions(-) diff --git a/lessons/30_Loops/120_More_Iterables.ipynb b/lessons/30_Loops/120_More_Iterables.ipynb index 17c7b588..136683fc 100644 --- a/lessons/30_Loops/120_More_Iterables.ipynb +++ b/lessons/30_Loops/120_More_Iterables.ipynb @@ -4,17 +4,20 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Iteration Functions\n", + "# **Iteration Functions**\n", "\n", - "Python has some very important iteration functions that you can use in your programs. \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", + "| 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. ( We'll work on ``enumerate()`` and ``zip()`` and leave\n", - "the others for later. )\n" + "Let's try some of these out. \n", + "\n", + "We'll work on `enumerate()` and `zip()` first, and leave the others for later." ] }, { @@ -23,9 +26,11 @@ "metadata": {}, "outputs": [], "source": [ - "# Enumerate\n", + "# Run Me!\n", + "\n", + "# Enumerate with Unpacking\n", "\n", - "colors = [ 'red', 'blue', 'black', 'orange']\n", + "colors = ['red', 'blue', 'black', 'orange']\n", "\n", "for i in enumerate(colors):\n", " print(i)" @@ -35,9 +40,11 @@ "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: " + "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:" ] }, { @@ -46,10 +53,15 @@ "metadata": {}, "outputs": [], "source": [ - "t = (1,2,3)\n", - "a,b,c = t\n", - "print(t)\n", - "print(a,b,c)" + "# 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)" ] }, { @@ -58,11 +70,12 @@ "source": [ "Do you see what happened? When we wrote:\n", "\n", - "```python \n", - "a,b,c = t\n", + "```python\n", + "a, b, c = my_tuple\n", "```\n", + "The first item in `t` was assigned to `a`, the second to `b`, and so on. \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:" + "This means that when we use `enumerate()`, we can write code like this:" ] }, { @@ -71,11 +84,13 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "# Enumerate with Unpacking\n", "\n", - "colors = [ 'red', 'blue', 'black', 'orange']\n", + "colors = ['red', 'blue', 'black', 'orange']\n", "\n", - "for index, color in enumerate(colors): # Unpacking the tuple from enumerate()\n", + "for index, color in enumerate(colors): # Unpacking the tuple from enumerate()\n", " print(\"#\", index, \"color is\", color)" ] }, @@ -83,43 +98,41 @@ "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", + "## **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", + "a, b = 1, 2\n", "```\n", "\n", - "That code will be equivalent to:\n", + "This code is equivalent to:\n", "\n", "```python\n", "a = 1\n", - "b = 1\n", + "b = 2\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", + "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", - "One more thing; you will sometimes use parentheses in the unpacking, such as when we\n", - "write:\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", "\n", "for color, (item1, item2) in enumerate(colors, pairs):\n", - " ...\n", + " # Do something with color, item1, and item2\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. " + "This is more complex—it means that `enumerate()` is returning a tuple as its second element, and that tuple should also be unpacked." ] }, { @@ -128,44 +141,46 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "# Unpack a range\n", - "a,b,c = range(3)\n", - "print(a,b,c)\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", + "# 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", + "# 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", + "list_one = [1, 2, 3]\n", + "tuple_one = ('x', 'y', 'z')\n", "\n", - "t2 = [ l1, t1 ] # two levels deep!\n", + "tuple_two = [list_one, tuple_one] # Two levels deep!\n", "\n", "# Unpack all of the levels\n", - "(a,b,c), (d,e,f) = t2\n", + "(a, b, c), (d, e, f) = tuple_two\n", "\n", - "print(a,b,c,d,e,f)" + "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." + "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", + "## **zip()**\n", "\n", - "Zip is another really important iteration tool. It lets you iterate over two lists at the same time. " + "`zip()` is another really important iteration tool. It lets you iterate over two (or more) lists at the same time." ] }, { @@ -174,20 +189,26 @@ "metadata": {}, "outputs": [], "source": [ - "# Zip \n", + "# Run Me!\n", "\n", - "list1 = ['a','b','c','d']\n", - "list2 = ['1','2','3','4']\n", + "# zip() Example\n", "\n", - "for l1, l2 in zip(list1, list2): # <- Ok, look, unpacking!\n", - " print(l1, l2)" + "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()`` 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?" + "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?" ] }, { @@ -196,11 +217,13 @@ "metadata": {}, "outputs": [], "source": [ - "# Use zip to iterate over two lists at once\n", + "# Run Me!\n", "\n", - "colors = [ 'red', 'blue', 'black', 'orange']\n", + "# Use `zip()` to iterate over two lists at once\n", "\n", - "# Each tuple in the directions is first, how far to turn, then how far to go\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", @@ -216,18 +239,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# islice\n", + "## **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", + "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 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", + "For a normal list, you could just do this with `l[:10]`, so why would you need `islice()`?\n", "\n", - "For example:" + "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." ] }, { @@ -236,16 +254,19 @@ "metadata": {}, "outputs": [], "source": [ - "from itertools import islice # Important! \n", + "# Run Me!\n", + "\n", + "# islice() Example\n", "\n", - "N = 1_000_000 \n", - "r = range(N)\n", + "from itertools import islice # We'll need this!\n", "\n", - "# range( iterator, stop ) or\n", - "# range( iterator, start, stop, step)\n", - "l = list( islice( r,N-5, N) )\n", + "my_num = 1_000_000\n", + "my_range = range(my_num)\n", "\n", - "l" + "# 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)" ] }, { @@ -254,13 +275,15 @@ "metadata": {}, "outputs": [], "source": [ - "# Demonstrate islice\n", + "# Run Me!\n", "\n", - "from itertools import islice # Important! \n", + "# Demonstrate islice()\n", "\n", - "l = [0,1,2,3,4,5,6,7,8,9]\n", + "from itertools import islice # Important, but you knew that!\n", "\n", - "for i in islice(l, 5): # Stop at 5\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)" ] }, @@ -268,19 +291,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# cycle\n", + "## **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", + "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", - "What if you had four colors, in a list: \n", + "For example, what if you had four colors in a list: \n", "\n", "```python \n", - "colors = [ 'red', 'blue', 'black', 'orange']\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:" + "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:" ] }, { @@ -289,11 +312,13 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "# Use cycle and islice\n", "\n", - "from itertools import cycle, islice\n", + "from itertools import cycle, islice # You will need both for this!\n", "\n", - "colors = [ 'red', 'blue', 'black', 'orange']\n", + "colors = ['red', 'blue', 'black', 'orange']\n", "\n", "for color in islice(cycle(colors), 25):\n", " print(color, end=' ')" @@ -302,12 +327,49 @@ { "cell_type": "markdown", "metadata": {}, - "source": [] + "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", + "...\n", + "\n", + "# Data for our team 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", + "# Let's get to work!\n", + "...\n", + "\n", + "# Bonus Challenges\n", + "# Create a dictionary that maps each player to their assigned team.\n", + "# Assign skill levels to each player (e.g., Beginner, Intermediate, Advanced) and include that in the output.\n", + "# Create a tournament schedule that ensures each team plays against every other team at least once.\n", + "..." + ] } ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -324,10 +386,10 @@ "version": "3.13.5" }, "syllabus": { - "uid": "PrKwTywv", - "name": "More Iterables" + "name": "More Iterables", + "uid": "PrKwTywv" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} From 0e6f8be5843458b6597767e234add695a2335621 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 17 Jan 2026 15:21:06 -0500 Subject: [PATCH 140/159] Fixed 130_Iterable_Turtle.ipynb to correctly be a .py --- lessons/.jtl/syllabus.yaml | 2 +- .../10_Turtles/10_Welcome/10_Welcome.ipynb | 2 +- lessons/10_Turtles/Module_One_Quiz.ipynb | 245 ++++++++++++- .../20_Types_and_Logic/Module_Two_Quiz.ipynb | 4 - lessons/30_Loops/120_More_Iterables.ipynb | 337 +++++++++++++++++- lessons/30_Loops/130_Iterable_Turtle.ipynb | 47 --- lessons/30_Loops/130_Iterable_Turtle.py | 24 ++ lessons/30_Loops/Module_Three_Quiz.ipynb | 251 ++++++++++++- 8 files changed, 840 insertions(+), 72 deletions(-) delete mode 100644 lessons/30_Loops/130_Iterable_Turtle.ipynb create mode 100644 lessons/30_Loops/130_Iterable_Turtle.py diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index 4cf987fb..f2375d6b 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -188,7 +188,7 @@ modules: 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 diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index 8aca77ef..fd822b2b 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -51,7 +51,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": ".venv", "language": "python", "name": "python3" }, diff --git a/lessons/10_Turtles/Module_One_Quiz.ipynb b/lessons/10_Turtles/Module_One_Quiz.ipynb index e4176fbc..eacfb375 100644 --- a/lessons/10_Turtles/Module_One_Quiz.ipynb +++ b/lessons/10_Turtles/Module_One_Quiz.ipynb @@ -10,10 +10,251 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "495aaca3", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
              " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "var questionsmROGvaXVwQFo=[\n {\n \"question\": \"What does forward() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Moves the turtle forward\", \"correct\": true, \"feedback\": \"Correct! forward() moves the turtle in the direction it's facing.\" },\n { \"answer\": \"Turns the turtle\", \"correct\": false, \"feedback\": \"Incorrect. forward() moves, not turns.\" },\n { \"answer\": \"Changes color\", \"correct\": false, \"feedback\": \"Incorrect. forward() is for movement.\" },\n { \"answer\": \"Draws a circle\", \"correct\": false, \"feedback\": \"Incorrect. Use circle() to draw circles.\" }\n ]\n },\n {\n \"question\": \"Which command turns the turtle left?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"left()\", \"correct\": true, \"feedback\": \"Correct! left() turns the turtle to the left.\" },\n { \"answer\": \"turn()\", \"correct\": false, \"feedback\": \"Incorrect. Use left() or right().\" },\n { \"answer\": \"rotate()\", \"correct\": false, \"feedback\": \"Incorrect. Use left() to turn.\" },\n { \"answer\": \"spin()\", \"correct\": false, \"feedback\": \"Incorrect. Use left() to turn left.\" }\n ]\n },\n {\n \"question\": \"How many degrees is a square corner?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"90 degrees\", \"correct\": true, \"feedback\": \"Correct! Square corners are 90 degrees.\" },\n { \"answer\": \"45 degrees\", \"correct\": false, \"feedback\": \"Incorrect. Squares have 90-degree corners.\" },\n { \"answer\": \"180 degrees\", \"correct\": false, \"feedback\": \"Incorrect. That's a straight line.\" },\n { \"answer\": \"360 degrees\", \"correct\": false, \"feedback\": \"Incorrect. That's a full circle.\" }\n ]\n },\n {\n \"question\": \"What does penup() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Stops drawing when the turtle moves\", \"correct\": true, \"feedback\": \"Correct! penup() lifts the pen so the turtle doesn't draw.\" },\n { \"answer\": \"Stops the turtle\", \"correct\": false, \"feedback\": \"Incorrect. The turtle can still move.\" },\n { \"answer\": \"Moves the turtle up\", \"correct\": false, \"feedback\": \"Incorrect. It controls drawing, not direction.\" },\n { \"answer\": \"Deletes the drawing\", \"correct\": false, \"feedback\": \"Incorrect. It just lifts the pen.\" }\n ]\n },\n {\n \"question\": \"When would you use penup()?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"When you want to move without drawing\", \"correct\": true, \"feedback\": \"Correct! Use penup() to move the turtle without leaving a line.\" },\n { \"answer\": \"When you want to draw faster\", \"correct\": false, \"feedback\": \"Incorrect. Use speed() to change drawing speed.\" },\n { \"answer\": \"When you want to change colors\", \"correct\": false, \"feedback\": \"Incorrect. Use pencolor() to change colors.\" },\n { \"answer\": \"When you want to stop the program\", \"correct\": false, \"feedback\": \"Incorrect. penup() just stops drawing.\" }\n ]\n },\n {\n \"question\": \"Which command changes the pen color?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"pencolor()\", \"correct\": true, \"feedback\": \"Correct! pencolor() sets the drawing color.\" },\n { \"answer\": \"color()\", \"correct\": false, \"feedback\": \"Close, but pencolor() is more specific for the pen.\" },\n { \"answer\": \"setcolor()\", \"correct\": false, \"feedback\": \"Incorrect. Use pencolor().\" },\n { \"answer\": \"changecolor()\", \"correct\": false, \"feedback\": \"Incorrect. Use pencolor().\" }\n ]\n },\n {\n \"question\": \"What does goto(100, 100) do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Moves turtle to position (100, 100)\", \"correct\": true, \"feedback\": \"Correct! goto() moves to specific coordinates.\" },\n { \"answer\": \"Moves forward 100 steps\", \"correct\": false, \"feedback\": \"Incorrect. goto() moves to coordinates.\" },\n { \"answer\": \"Turns 100 degrees\", \"correct\": false, \"feedback\": \"Incorrect. goto() is for position, not turning.\" },\n { \"answer\": \"Draws 100 circles\", \"correct\": false, \"feedback\": \"Incorrect. goto() moves the turtle.\" }\n ]\n },\n {\n \"question\": \"What is a loop?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Code that repeats multiple times\", \"correct\": true, \"feedback\": \"Correct! Loops repeat code automatically.\" },\n { \"answer\": \"Code that runs once\", \"correct\": false, \"feedback\": \"Incorrect. Loops repeat code.\" },\n { \"answer\": \"A type of variable\", \"correct\": false, \"feedback\": \"Incorrect. Loops are for repetition.\" },\n { \"answer\": \"A drawing command\", \"correct\": false, \"feedback\": \"Incorrect. Loops control how many times code runs.\" }\n ]\n },\n {\n \"question\": \"How many times does range(5) repeat?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"5 times\", \"correct\": true, \"feedback\": \"Correct! range(5) creates 0, 1, 2, 3, 4 - that's 5 numbers.\" },\n { \"answer\": \"4 times\", \"correct\": false, \"feedback\": \"Incorrect. range(5) gives you 5 numbers.\" },\n { \"answer\": \"6 times\", \"correct\": false, \"feedback\": \"Incorrect. range(5) stops at 4.\" },\n { \"answer\": \"Forever\", \"correct\": false, \"feedback\": \"Incorrect. range(5) repeats exactly 5 times.\" }\n ]\n },\n {\n \"question\": \"What is the correct syntax for a for loop?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"for i in range(5):\\n tina.forward(50)\", \"correct\": true, \"feedback\": \"Correct! Use 'for', 'in', 'range()', and a colon.\" },\n { \"code\": \"for i range(5)\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Missing 'in' and the colon.\" },\n { \"code\": \"loop 5 times:\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Python uses 'for i in range()'.\" },\n { \"code\": \"repeat(5):\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Use 'for i in range(5):'.\" }\n ]\n },\n {\n \"question\": \"Why use loops instead of writing the same code many times?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Loops are shorter and easier to change\", \"correct\": true, \"feedback\": \"Correct! Loops save time and reduce mistakes.\" },\n { \"answer\": \"Loops make programs slower\", \"correct\": false, \"feedback\": \"Incorrect. Loops are efficient.\" },\n { \"answer\": \"Loops are harder to read\", \"correct\": false, \"feedback\": \"Incorrect. Loops make code clearer.\" },\n { \"answer\": \"You can't use loops in Python\", \"correct\": false, \"feedback\": \"Incorrect. Loops are essential in Python.\" }\n ]\n },\n {\n \"question\": \"What is a variable?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A container that stores a value\", \"correct\": true, \"feedback\": \"Correct! Variables hold information you can use later.\" },\n { \"answer\": \"A type of loop\", \"correct\": false, \"feedback\": \"Incorrect. Variables store data.\" },\n { \"answer\": \"A drawing command\", \"correct\": false, \"feedback\": \"Incorrect. Variables store values.\" },\n { \"answer\": \"A color\", \"correct\": false, \"feedback\": \"Incorrect. Variables can store many types of data.\" }\n ]\n },\n {\n \"question\": \"Which creates a variable named 'size' with value 100?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"size = 100\", \"correct\": true, \"feedback\": \"Correct! Use = to assign a value to a variable.\" },\n { \"code\": \"size == 100\", \"correct\": false, \"feedback\": \"Incorrect. == checks equality. Use = to assign.\" },\n { \"code\": \"100 = size\", \"correct\": false, \"feedback\": \"Incorrect. Variable name goes on the left.\" },\n { \"code\": \"var size = 100\", \"correct\": false, \"feedback\": \"Incorrect. Python doesn't use 'var'.\" }\n ]\n },\n {\n \"question\": \"What happens when you run this code: x = 5; x = x + 1?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"x becomes 6\", \"correct\": true, \"feedback\": \"Correct! x is updated to its old value plus 1.\" },\n { \"answer\": \"x stays 5\", \"correct\": false, \"feedback\": \"Incorrect. x gets a new value.\" },\n { \"answer\": \"x becomes 0\", \"correct\": false, \"feedback\": \"Incorrect. x increases by 1.\" },\n { \"answer\": \"Error\", \"correct\": false, \"feedback\": \"Incorrect. This is valid Python code.\" }\n ]\n },\n {\n \"question\": \"What is a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Reusable code with a name\", \"correct\": true, \"feedback\": \"Correct! Functions are named blocks of code you can use repeatedly.\" },\n { \"answer\": \"A variable\", \"correct\": false, \"feedback\": \"Incorrect. Functions contain code, not just values.\" },\n { \"answer\": \"A loop\", \"correct\": false, \"feedback\": \"Incorrect. Functions and loops are different.\" },\n { \"answer\": \"A color\", \"correct\": false, \"feedback\": \"Incorrect. Functions are code blocks.\" }\n ]\n },\n {\n \"question\": \"Which keyword starts a function definition?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"def\", \"correct\": true, \"feedback\": \"Correct! Use 'def' to define a function.\" },\n { \"answer\": \"function\", \"correct\": false, \"feedback\": \"Incorrect. Python uses 'def'.\" },\n { \"answer\": \"func\", \"correct\": false, \"feedback\": \"Incorrect. Use 'def'.\" },\n { \"answer\": \"define\", \"correct\": false, \"feedback\": \"Incorrect. The keyword is 'def'.\" }\n ]\n },\n {\n \"question\": \"What is the correct way to define a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"def my_function():\\n tina.forward(50)\", \"correct\": true, \"feedback\": \"Correct! Use def, name, parentheses, colon, and indented code.\" },\n { \"code\": \"def my_function()\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Missing the colon after ().\" },\n { \"code\": \"function my_function():\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Use 'def', not 'function'.\" },\n { \"code\": \"my_function():\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Missing 'def' keyword.\" }\n ]\n },\n {\n \"question\": \"When would you use a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"When you want to reuse the same code\", \"correct\": true, \"feedback\": \"Correct! Functions let you use code multiple times.\" },\n { \"answer\": \"When you want to store a number\", \"correct\": false, \"feedback\": \"Incorrect. Use variables to store values.\" },\n { \"answer\": \"When you want to repeat code a specific number of times\", \"correct\": false, \"feedback\": \"Incorrect. That's what loops are for.\" },\n { \"answer\": \"When you want to change colors\", \"correct\": false, \"feedback\": \"Incorrect. Functions are for organizing reusable code.\" }\n ]\n },\n {\n \"question\": \"What does return do in a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Sends a value back from the function\", \"correct\": true, \"feedback\": \"Correct! return gives back a result.\" },\n { \"answer\": \"Prints to the screen\", \"correct\": false, \"feedback\": \"Incorrect. That's what print() does.\" },\n { \"answer\": \"Stops the entire program\", \"correct\": false, \"feedback\": \"Incorrect. return only exits the function.\" },\n { \"answer\": \"Creates a variable\", \"correct\": false, \"feedback\": \"Incorrect. return sends values back.\" }\n ]\n },\n {\n \"question\": \"What is a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A collection of items in order\", \"correct\": true, \"feedback\": \"Correct! Lists hold multiple items like [1, 2, 3].\" },\n { \"answer\": \"A single number\", \"correct\": false, \"feedback\": \"Incorrect. Lists hold multiple items.\" },\n { \"answer\": \"A function\", \"correct\": false, \"feedback\": \"Incorrect. Lists are data containers.\" },\n { \"answer\": \"A loop\", \"correct\": false, \"feedback\": \"Incorrect. Lists store data.\" }\n ]\n },\n {\n \"question\": \"Which is the correct way to create a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"colors = ['red', 'blue', 'green']\", \"correct\": true, \"feedback\": \"Correct! Use square brackets with items separated by commas.\" },\n { \"code\": \"colors = ('red', 'blue', 'green')\", \"correct\": false, \"feedback\": \"Incorrect. Parentheses create tuples. Use [].\" },\n { \"code\": \"colors = {'red', 'blue', 'green'}\", \"correct\": false, \"feedback\": \"Incorrect. Curly braces create sets. Use [].\" },\n { \"code\": \"colors = 'red', 'blue', 'green'\", \"correct\": false, \"feedback\": \"Incorrect. Use square brackets [].\" }\n ]\n },\n {\n \"question\": \"How do you get the first item from a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"list[0]\", \"correct\": true, \"feedback\": \"Correct! Python lists start at index 0.\" },\n { \"answer\": \"list[1]\", \"correct\": false, \"feedback\": \"Incorrect. That's the second item. Use [0].\" },\n { \"answer\": \"list.first()\", \"correct\": false, \"feedback\": \"Incorrect. Use [0] to get the first item.\" },\n { \"answer\": \"first(list)\", \"correct\": false, \"feedback\": \"Incorrect. Use list[0].\" }\n ]\n },\n {\n \"question\": \"What does append() do to a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Adds an item to the end\", \"correct\": true, \"feedback\": \"Correct! append() adds items to the end of the list.\" },\n { \"answer\": \"Removes an item\", \"correct\": false, \"feedback\": \"Incorrect. Use remove() to delete items.\" },\n { \"answer\": \"Sorts the list\", \"correct\": false, \"feedback\": \"Incorrect. Use sort() to order items.\" },\n { \"answer\": \"Counts items\", \"correct\": false, \"feedback\": \"Incorrect. Use len() to count items.\" }\n ]\n },\n {\n \"question\": \"How do you loop through each item in a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"for item in my_list:\\n print(item)\", \"correct\": true, \"feedback\": \"Correct! This loops through each item in the list.\" },\n { \"code\": \"for my_list in item:\\n print(item)\", \"correct\": false, \"feedback\": \"Incorrect. It should be 'for item in my_list'.\" },\n { \"code\": \"loop my_list:\\n print(item)\", \"correct\": false, \"feedback\": \"Incorrect. Use 'for item in my_list:'.\" },\n { \"code\": \"for item:\\n print(my_list)\", \"correct\": false, \"feedback\": \"Incorrect. Missing 'in my_list'.\" }\n ]\n },\n {\n \"question\": \"What does circle(50) draw?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A circle with radius 50\", \"correct\": true, \"feedback\": \"Correct! The number is the radius.\" },\n { \"answer\": \"50 circles\", \"correct\": false, \"feedback\": \"Incorrect. It draws one circle.\" },\n { \"answer\": \"A square\", \"correct\": false, \"feedback\": \"Incorrect. circle() draws circles.\" },\n { \"answer\": \"A line 50 units long\", \"correct\": false, \"feedback\": \"Incorrect. Use forward() for lines.\" }\n ]\n },\n {\n \"question\": \"Where is position (0, 0) on the turtle screen?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Center of the screen\", \"correct\": true, \"feedback\": \"Correct! (0, 0) is in the middle.\" },\n { \"answer\": \"Top left corner\", \"correct\": false, \"feedback\": \"Incorrect. (0, 0) is the center.\" },\n { \"answer\": \"Bottom right corner\", \"correct\": false, \"feedback\": \"Incorrect. (0, 0) is the center.\" },\n { \"answer\": \"Top right corner\", \"correct\": false, \"feedback\": \"Incorrect. (0, 0) is in the middle.\" }\n ]\n },\n {\n \"question\": \"What does speed(0) do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Makes turtle draw fastest\", \"correct\": true, \"feedback\": \"Correct! speed(0) is the fastest setting.\" },\n { \"answer\": \"Stops the turtle\", \"correct\": false, \"feedback\": \"Incorrect. 0 means fastest.\" },\n { \"answer\": \"Makes turtle slowest\", \"correct\": false, \"feedback\": \"Incorrect. 0 is fastest.\" },\n { \"answer\": \"Makes turtle invisible\", \"correct\": false, \"feedback\": \"Incorrect. Use hideturtle() for that.\" }\n ]\n },\n {\n \"question\": \"What does # at the start of a line mean?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"It's a comment (Python ignores it)\", \"correct\": true, \"feedback\": \"Correct! Comments explain code to humans.\" },\n { \"answer\": \"It's a command\", \"correct\": false, \"feedback\": \"Incorrect. # creates comments.\" },\n { \"answer\": \"It causes an error\", \"correct\": false, \"feedback\": \"Incorrect. Comments are ignored.\" },\n { \"answer\": \"It makes code run faster\", \"correct\": false, \"feedback\": \"Incorrect. Comments don't affect speed.\" }\n ]\n },\n {\n \"question\": \"Which code correctly draws a triangle?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"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.\" },\n { \"code\": \"for i in range(4):\\n tina.forward(100)\\n tina.left(120)\", \"correct\": false, \"feedback\": \"Incorrect. Triangle has 3 sides, not 4.\" },\n { \"code\": \"for i in range(3):\\n tina.forward(100)\\n tina.left(90)\", \"correct\": false, \"feedback\": \"Incorrect. Triangle needs 120-degree turns.\" },\n { \"code\": \"tina.triangle(100)\", \"correct\": false, \"feedback\": \"Incorrect. There's no triangle() method.\" }\n ]\n },\n {\n \"question\": \"What does exitonclick() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Keeps window open until you click\", \"correct\": true, \"feedback\": \"Correct! The window waits for a click before closing.\" },\n { \"answer\": \"Closes window immediately\", \"correct\": false, \"feedback\": \"Incorrect. It waits for a click.\" },\n { \"answer\": \"Opens a new window\", \"correct\": false, \"feedback\": \"Incorrect. It controls the current window.\" },\n { \"answer\": \"Saves your drawing\", \"correct\": false, \"feedback\": \"Incorrect. It manages window closing.\" }\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 input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\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 var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n 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 //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\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 aDiv.append(lab);\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) {\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(\"mROGvaXVwQFo\")) {\n show_questions(questionsmROGvaXVwQFo, mROGvaXVwQFo);\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", diff --git a/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb b/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb index 32cb9d66..abe2c87d 100644 --- a/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb +++ b/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb @@ -40,10 +40,6 @@ "version": "3.13.5" } }, - "syllabus": { - "name": "Module Two Quiz", - "uid": "NZt4sXmo" -}, "nbformat": 4, "nbformat_minor": 5 } diff --git a/lessons/30_Loops/120_More_Iterables.ipynb b/lessons/30_Loops/120_More_Iterables.ipynb index 136683fc..92bd9d1f 100644 --- a/lessons/30_Loops/120_More_Iterables.ipynb +++ b/lessons/30_Loops/120_More_Iterables.ipynb @@ -73,7 +73,7 @@ "```python\n", "a, b, c = my_tuple\n", "```\n", - "The first item in `t` was assigned to `a`, the second to `b`, and so on. \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:" ] @@ -94,6 +94,37 @@ " 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": {}, @@ -235,6 +266,107 @@ " 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", + "\n", + "names = ['Alice', 'Bob', 'Charlie', 'Diana']\n", + "ages = [25, 30, 35, 28]\n", + "cities = ['New York', 'London', 'Tokyo', 'Paris']\n", + "\n", + "# Create a dictionary mapping names to ages\n", + "age_dict = dict(zip(names, ages))\n", + "print(\"Age Dictionary:\", age_dict)\n", + "\n", + "# Create a dictionary mapping names to cities\n", + "city_dict = dict(zip(names, cities))\n", + "print(\"City Dictionary:\", city_dict)\n", + "\n", + "# You can even zip three lists to create a more complex structure\n", + "people = list(zip(names, ages, cities))\n", + "print(\"\\nPeople list (tuples):\", people)\n", + "\n", + "# Or create a dictionary with tuple values\n", + "people_dict = {name: (age, city) for name, age, city in zip(names, ages, cities)}\n", + "print(\"\\nPeople dictionary:\", people_dict)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -269,6 +401,15 @@ "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, @@ -324,6 +465,154 @@ " 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": {}, @@ -341,6 +630,34 @@ "> **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": "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": "code", "execution_count": null, @@ -350,20 +667,20 @@ "# Challenge!\n", "\n", "# You are probably going to want to import some things...\n", - "...\n", + "from itertools import cycle\n", "\n", - "# Data for our team and players\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", - "# Let's get to work!\n", - "...\n", + "# Your code here!\n", + "# Hint: Use enumerate() for numbering, zip() for pairing, and cycle() for repeating teams\n", + "\n", "\n", - "# Bonus Challenges\n", - "# Create a dictionary that maps each player to their assigned team.\n", - "# Assign skill levels to each player (e.g., Beginner, Intermediate, Advanced) and include that in the output.\n", - "# Create a tournament schedule that ensures each team plays against every other team at least once.\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." ] } ], diff --git a/lessons/30_Loops/130_Iterable_Turtle.ipynb b/lessons/30_Loops/130_Iterable_Turtle.ipynb deleted file mode 100644 index 49f2b126..00000000 --- a/lessons/30_Loops/130_Iterable_Turtle.ipynb +++ /dev/null @@ -1,47 +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", - "name": "Iterable Turtle" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} \ No newline at end of file diff --git a/lessons/30_Loops/130_Iterable_Turtle.py b/lessons/30_Loops/130_Iterable_Turtle.py new file mode 100644 index 00000000..ba0eefde --- /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 04_Crazy_Tina.py + +colors = ... # Make a list of colors + +directions = [ # Create a list of directions and angles + ( , ), + ( , ), ... # Add more +] + +# Zip the colors and directions together, then unpack them. THere is a good example of this +# in 10_More_iterables.ipynb in the discussion of zip() + +for ... 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/Module_Three_Quiz.ipynb b/lessons/30_Loops/Module_Three_Quiz.ipynb index 2d7cccf1..0ec9a5a8 100644 --- a/lessons/30_Loops/Module_Three_Quiz.ipynb +++ b/lessons/30_Loops/Module_Three_Quiz.ipynb @@ -14,10 +14,251 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "e88338fc", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
                " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "var questionssmvYwokwXykO=[\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 input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\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 var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n 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 //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\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 aDiv.append(lab);\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) {\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(\"smvYwokwXykO\")) {\n show_questions(questionssmvYwokwXykO, smvYwokwXykO);\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", @@ -27,7 +268,7 @@ ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -44,10 +285,6 @@ "version": "3.13.5" } }, - "syllabus": { - "name": "Module Three Quiz", - "uid": "L2cMhRfP" -}, "nbformat": 4, "nbformat_minor": 5 } From dd0c4b6e16a2ed5d8a35935e2e85bb4080f48897 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 17 Jan 2026 15:27:12 -0500 Subject: [PATCH 141/159] Fixed the errors in Iterable Turtle --- lessons/30_Loops/130_Iterable_Turtle.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lessons/30_Loops/130_Iterable_Turtle.py b/lessons/30_Loops/130_Iterable_Turtle.py index ba0eefde..4871fb18 100644 --- a/lessons/30_Loops/130_Iterable_Turtle.py +++ b/lessons/30_Loops/130_Iterable_Turtle.py @@ -9,11 +9,11 @@ colors = ... # Make a list of colors directions = [ # Create a list of directions and angles - ( , ), - ( , ), ... # Add more + ( ), # Add an (angle, distance) tuple for each line + ( ), ] -# Zip the colors and directions together, then unpack them. THere is a good example of this +# Zip the colors and directions together, then unpack them. There is a good example of this # in 10_More_iterables.ipynb in the discussion of zip() for ... in zip( ... , ...): From 7034201f217807c2b3730601049acaa009251d5b Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 17 Jan 2026 15:40:22 -0500 Subject: [PATCH 142/159] Updated 140_More_Loops.ipynb and made mostly style changes only to speed up the process. --- lessons/30_Loops/140_More_Loops.ipynb | 489 +++++++++++++------------- 1 file changed, 250 insertions(+), 239 deletions(-) diff --git a/lessons/30_Loops/140_More_Loops.ipynb b/lessons/30_Loops/140_More_Loops.ipynb index 4baa5765..87394092 100644 --- a/lessons/30_Loops/140_More_Loops.ipynb +++ b/lessons/30_Loops/140_More_Loops.ipynb @@ -1,241 +1,252 @@ { - "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.13.5" - }, - "syllabus": { - "name": "More Loops", - "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 -} \ No newline at end of file + { + "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 in your programming journey!" + ] + } + ], + "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 +} From 9f500cb27d685a386619e506d7888df9afb40950 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 17 Jan 2026 15:59:37 -0500 Subject: [PATCH 143/159] Made design improvements to 160_Extras.ipynb --- lessons/30_Loops/160_Extras.ipynb | 208 +++++++++++++++++------------- 1 file changed, 118 insertions(+), 90 deletions(-) diff --git a/lessons/30_Loops/160_Extras.ipynb b/lessons/30_Loops/160_Extras.ipynb index 90ecaf50..f9f81f3c 100644 --- a/lessons/30_Loops/160_Extras.ipynb +++ b/lessons/30_Loops/160_Extras.ipynb @@ -1,92 +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 \ud83d\udc66 with a \ud83d\udc15 met a \ud83d\udc67 who had a \ud83d\udc08 and they went to the park to play with a \u26bd.\".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", - "name": "Extras" - } + "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 -} \ No newline at end of file + { + "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 +} From 1a367514845ffa86468f83f52b2fda85552c9a79 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 17 Jan 2026 16:39:49 -0500 Subject: [PATCH 144/159] Made subtle changes to these notebooks to ensure the designs are consistent with the rest. Minor improvements to clarity were also made, but they still need adjustments. --- .../10_Functions.ipynb | 378 ++++++++++-------- .../20_Dicts_Sets.ipynb | 331 ++++++++------- .../40_Splat_Comprehension.ipynb | 5 +- 3 files changed, 398 insertions(+), 316 deletions(-) diff --git a/lessons/40_Data_Structures_Func/10_Functions.ipynb b/lessons/40_Data_Structures_Func/10_Functions.ipynb index daee466f..b45aa710 100644 --- a/lessons/40_Data_Structures_Func/10_Functions.ipynb +++ b/lessons/40_Data_Structures_Func/10_Functions.ipynb @@ -4,33 +4,36 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Functions and Data Structures\n", + "# **Functions and Data Structures**\n", "\n", - "## Functions\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", + "We've seen functions a few times, but haven't explained how they work in detail. Let's explore functions more thoroughly.\n", "\n", - "A function looks like this:\n", + "A basic 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", + "Then we can call the function like this:\n", "\n", "```python \n", "y = add_ten(5)\n", + "print(y) # Output: 15\n", "```\n", "\n", - "The important parts of a function are: \n", + "### **Parts of a Function**\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", + "Every function has several key components:\n", "\n", - "The simplest function you can write is:" + "| 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) |" ] }, { @@ -39,41 +42,51 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "def do_nothing():\n", - " pass\n", + " pass # 'pass' means \"do nothing\" — it's just a placeholder\n", "\n", "y = do_nothing()\n", - "print(y)" + "print(\"Return value:\", 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", + "### **About the `pass` Keyword**\n", + "\n", + "The `pass` keyword means \"do nothing\" — it's just a placeholder that takes up space so the function has a properly indented body. Since `do_nothing()` has no `return` statement, it returns the empty value `None`.\n", + "\n", + "### **Why Use Functions?**\n", + "\n", + "The most important reasons to write functions are:\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", + "1. **Reusability** — Write the code once, use it many times\n", + "2. **Clarity** — Breaking a program into functions makes it easier to understand\n", + "3. **Testing** — Smaller functions are easier to test and debug\n", + "4. **Naming** — Descriptive function and argument names help explain what the code does\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!" + "> **Tip:** Choose function and argument names that clearly indicate what they do or what data they hold. This makes your code self-documenting!\n", + "\n", + "> **Reminder:** Have you checked in your code? See the [check-in documentation](https://curriculum.jointheleague.org/howto/checkin_restart.html) if you need a refresher." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Function Arguments\n", + "---\n", + "\n", + "## **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", + "Function arguments are the values you pass into a function so it can compute and return a result. You can assign values to arguments in several ways:\n", "\n", - "For instance:" + "- **Positional arguments** — pass values in order\n", + "- **Keyword arguments** — pass values by name, in any order\n", + "\n", + "Let's see how this works:" ] }, { @@ -82,48 +95,35 @@ "metadata": {}, "outputs": [], "source": [ - "# Demonstrate function arguments\n", + "# Run Me!\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", + "# Method 1: Positional arguments (in order)\n", + "# 'Alice' goes to name, 'Bonjour' goes to greeting\n", + "greet_user(\"Alice\", \"Bonjour\")\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", + "# Method 2: Keyword arguments (by name, in any order)\n", + "# The order doesn't matter when using keywords\n", + "greet_user(greeting=\"Hello\", name=\"Alice\")\n", "\n", + "# Method 3: Mix positional and keyword\n", + "# Positional arguments MUST come first\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\")" + "# These will cause errors (uncomment to see):\n", + "# greet_user(name=\"Bob\", greeting=\"Hello\", extra=\"oops\") # Extra unknown argument\n", + "# greet_user(\"Bob\") # Missing required argument 'greeting'" ] }, { "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", - " " + "### **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:" ] }, { @@ -132,22 +132,28 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "def greet_user(name, greeting='Hello', punct=\"!\"):\n", " print(f\"{greeting}, {name}{punct}\")\n", "\n", - "# Use the default value for punct and hello\n", + "# Use default values for greeting and punct\n", "greet_user(\"Alice\")\n", "\n", - "# Or, override the default value\n", - "greet_user(\"Alice\", \"Hello\", \".\")" + "# 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": [ - "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:" + "### **Variable Scope: Declared vs Undeclared Variables**\n", + "\n", + "Variables used in a function must be **declared** — either as function arguments or defined within the function. Using an undefined variable causes an error:" ] }, { @@ -156,9 +162,13 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "def greet_user(name, greeting='Hello'):\n", + " # Error! 'punct' is not defined anywhere\n", " print(f\"{greeting}, {name}{punct}\")\n", "\n", + "# This will raise a NameError\n", "greet_user(\"Alice\")" ] }, @@ -166,33 +176,34 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Python didn't know what the variable `punct` is, so it could not run the function. " + "Python didn't know what the variable `punct` is, so it couldn't run the function and raised a `NameError`.\n", + "\n", + "> **Best Practice:** 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", "\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", + "## **Test Yourself**\n", "\n", - "If the chracter is not in the string, return -1\n", + "Write a function `find_char(character, string)` that:\n", + "- Takes a character and a longer string\n", + "- Iterates through the string to find the character's position\n", + "- Returns the **position** (index) where the character is found\n", + "- Returns `-1` if the character is not in the string\n", "\n", - "For instance: \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", + "```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. " + "**Hint:** Use `enumerate()` to get both the character and its position as you loop through the string." ] }, { @@ -214,36 +225,35 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Composition\n", + "---\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", + "## **Function Composition**\n", "\n", - "For instance, suppose we have the functions\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!\n", + "\n", + "For example, if you have these functions:\n", "\n", "```python \n", - "def f(a,b):\n", - " pass\n", + "def f(a, b):\n", + " return a + b\n", "\n", - "def g(c,d):\n", - " pass\n", + "def g(c, d):\n", + " return c * d\n", "```\n", "\n", - "The w can pass the output of f() directly into g()\n", + "You can pass the output of `f()` directly into `g()`:\n", "\n", "```python \n", - "g( f(1,2), f(3,4))\n", + "result = g(f(1, 2), f(3, 4)) # Same as: g(3, 7) → 21\n", "```\n", "\n", - "Or put expressions in the argument list: \n", + "Or combine them in expressions:\n", "\n", "```python \n", - "g( f(1,2)*f(3,4), f(5,6)-f(7,8))\n", + "result = 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. " + "This is called **function composition** — using the output of one function as input to another." ] }, { @@ -252,30 +262,42 @@ "metadata": {}, "outputs": [], "source": [ - "# Test Yourself\n", + "---\n", "\n", - "# Write a function to add two numbers and return the result\n", + "## **Test Yourself: Function Composition**\n", "\n", - "# Write a function to subtract two numbers and return the result \n", + "Write three functions and use them together to demonstrate:\n", "\n", - "# Write a function to print \"Same\" if two numbers are the same, otherwise print \"Different\"\n", + "1. A function to **add** two numbers\n", + "2. A function to **subtract** two numbers \n", + "3. A function to **compare** two numbers (print \"Same\" if equal, \"Different\" if not)\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" + "Then use these functions to show:\n", + "- That `2 + 2 = 4`\n", + "- That `(5 + 3) - 2 = 6`\n", + "- That `2 + 2 ≠ 3 + 3`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Oh No Closures\n", + "---\n", + "\n", + "## **Closures: A Warning and an Exciting Feature** ⚠️\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" + "Here's something surprising — this function works even though `name` is not in the argument list:\n", + "\n", + "```python\n", + "name = 'Bob'\n", + "\n", + "def greet_user(greeting):\n", + " print(f\"{greeting}, {name}\")\n", + "\n", + "greet_user(\"Hello\") # Output: Hello, Bob!\n", + "```\n", + "\n", + "The function **found** `name` outside the function scope. This behavior is called a **closure**, and while it's very useful, it can also cause problems if you're not careful." ] }, { @@ -284,25 +306,29 @@ "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\")" + "greet_user(\"Hello\") # Output: Hello, Bob!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Are you surprised that worked? If not, go read it again ...\n", + "**Best Practice:** 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!\n", + "\n", + "---\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", + "## **Exceptions**\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! ) " + "Sometimes things go wrong in your code. When that happens, Python raises an **exception** — 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?" ] }, { @@ -322,6 +348,9 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "# This will raise a ValueError\n", + "\n", "int(\"This is not an integer\")" ] }, @@ -329,7 +358,9 @@ "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: " + "The `ValueError` in the error message is the **exception type**. It's not just a message — it's a thing we can use in our program to **handle errors**. \n", + "\n", + "For example, suppose we have a list of things to convert to integers:" ] }, { @@ -338,16 +369,19 @@ "metadata": {}, "outputs": [], "source": [ - "for e in [0,1, 65, 'Bob', 23,'larry']:\n", + "# Run Me!\n", + "# This will crash when it hits 'Bob'\n", + "\n", + "for e in [0, 1, 65, 'Bob', 23, 'larry']:\n", " i = int(e)\n", - " print(f\"Converting {e} to an integer {i}\")" + " 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:" + "The program crashed on 'Bob'. We didn't make it through the whole list! But we can **catch** the exception using a `try/except` block and handle it gracefully:" ] }, { @@ -356,11 +390,13 @@ "metadata": {}, "outputs": [], "source": [ - "for e in [0,1, 65, 'Bob', 23,'larry']:\n", + "# Run Me!\n", + "# Using try/except to handle errors gracefully\n", "\n", + "for e in [0, 1, 65, 'Bob', 23, 'larry']:\n", " try:\n", " i = int(e)\n", - " print(f\"Converting {e} to an integer {i}\")\n", + " print(f\"Converting {e} to an integer: {i}\")\n", " except ValueError:\n", " print(f\"Could not convert {e} to an integer\")" ] @@ -369,11 +405,14 @@ "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" + "### **How Try/Except Works**\n", + "\n", + "Using a `try/except` block, you can:\n", + "1. **Try** to run code that might fail\n", + "2. **Except** (catch) specific exceptions if they occur\n", + "3. **Handle** the error gracefully instead of crashing\n", + "\n", + "The `except ValueError:` part catches the exception type you saw in the error message. Different error types have different exception names — this is just one example." ] }, { @@ -382,27 +421,32 @@ "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", + "# Run Me! Uncomment one line at a time to see different exception types\n", + "\n", + "my_list = [1, 2, 3, 4, 5]\n", "\n", - "my_list = [1,2,3,4,5]\n", + "# TypeError: Using wrong type for indexing\n", + "# print(my_list['Bob'])\n", "\n", - "#print(my_list['Bob']) # TypeError: list indices must be integers or slices, not str\n", + "# IndexError: Index out of range\n", + "# print(my_list[20])\n", "\n", - "#print(my_list[20]) # IndexError: list index out of range\n", + "# AssertionError: Assertion failed\n", + "# assert False\n", "\n", - "# assert False # AssertionError ( Although you don't usually catch assertions ) \n", + "# ZeroDivisionError: Division by zero\n", + "# x = 10 / 0\n", "\n", - "# x = 10 / 0 # ZeroDivisionError: division by zero\n" + "print(\"Uncomment one of the lines above to see different exception types!\")" ] }, { "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" + "### **Catching Multiple Exception Types**\n", + "\n", + "You can handle different exception types differently, or catch all exceptions at once:" ] }, { @@ -411,50 +455,52 @@ "metadata": {}, "outputs": [], "source": [ - "# You can list the different types of different `except` clauses:\n", + "# Run Me!\n", + "# Handling multiple exception types\n", "\n", + "# Approach 1: Catch different exceptions separately\n", "try:\n", - " pass# do something\n", + " pass # do something\n", "except ValueError as e:\n", - " print(f\"Got a value error {e}\")\n", + " print(f\"Got a value error: {e}\")\n", "except TypeError as e:\n", - " print(f\"Got a type error {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", + " 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", + " pass # do something\n", "except Exception as e:\n", - " # Get all types of exceptions. \n", - " print(f\"Got an exception {e}\")" + " # Get all types of exceptions\n", + " print(f\"Got an exception: {e}\")" ] }, { "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", + "---\n", + "\n", + "## **Test Yourself: Exception Handling**\n", + "\n", + "Write a program that:\n", + "\n", + "1. Runs in an **endless loop** until the user enters `'q'`\n", + "2. Gets a string from the user using `input()`\n", + "3. Tries to convert it to an integer\n", + "4. If successful:\n", + " - Check if the number is in the list `[5, 10, 45, 56]`\n", + " - If it's in the list, print \"Found it!\"\n", + " - If it's NOT in the list, tell the user what the n-th number in the list is (e.g., \"7 is not in the list, but the 7th number is 56\")\n", + " - Handle the case where the list is shorter than n using an `except` block\n", + "5. If the user enters `'q'`, exit the loop\n", + "6. If the string can't be converted to an integer, print an error message\n", + "\n", + "**Hint:** Check if something is in a list using `in`:\n", + "```python\n", + "if 5 in [1, 2, 3, 4, 5]:\n", + " print(\"It's in!\")\n", "```" ] }, @@ -465,20 +511,12 @@ "outputs": [], "source": [ "# Test Yourself\n", - "\n", "# Check if the user's numbers are in this list:\n", "\n", - "l = [5,10,45, 56]\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" + "# TODO: Add your code here" ] } ], diff --git a/lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb b/lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb index 66f82783..7114ae9b 100644 --- a/lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb +++ b/lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb @@ -4,24 +4,24 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Data Structures\n", + "# **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", + "| Data Structure | Description |\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", + "Now let's introduce a new one: the `set`. A **set** is important because it works like a list with key differences:\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. " + "* 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:" ] }, { @@ -30,26 +30,32 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "l = ['a','a','b','b','c','c']\n", - "print(l)\n", + "print(\"List: \", l)\n", "\n", "s = { 'a','a','b','b','c','c'}\n", - "print(s)" + "print(\"Set: \", 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", + "### **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", + "The formal name for these objects (string, set, list, and tuple) is **collections**.\n", "\n", - "The formal name of these objects, string, set, list and tuple, are \"Collections\".\n", + "---\n", "\n", - "# Creating Sets, Lists, Tuples\n", + "## **Creating Sets, Lists, Tuples**\n", "\n", - "So far we've seen one way for creating collections, using the braces, parentheses and quotes: \n", + "So far we've seen one way to create collections using braces, parentheses, and quotes:\n", "\n", "```python\n", "c = \"123\"\n", @@ -58,7 +64,7 @@ "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", + "But there's another way! You can also use the **constructor** functions. Here's how to create empty collections:\n", "\n", "```python \n", "c = str()\n", @@ -67,8 +73,7 @@ "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. " + "These constructor functions can take one argument—an **iterable**—making it easy to convert between collection types." ] }, { @@ -77,34 +82,37 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "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", + "print(\"Tuple from list:\", t)\n", "\n", "# Make a set from a list\n", "s = set(l)\n", - "print(s)\n", + "print(\"Set from list:\", s)\n", "\n", "# Make a list from a string\n", "a = list(c)\n", - "print(a)\n", + "print(\"List from string:\", 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)))" + "# Get the unique items from a list by converting to set then back to list\n", + "print(\"Unique items:\", 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. " + "### **Mutable vs Immutable Collections**\n", + "\n", + "When you create an empty collection, you can sometimes add items to it:\n", + "\n", + "* **Mutable** collections (lists and sets) can be changed — items can be added or removed\n", + "* **Immutable** collections (tuples and strings) cannot be changed after creation — but you can **concatenate** them to create new objects" ] }, { @@ -113,58 +121,64 @@ "metadata": {}, "outputs": [], "source": [ - "# Adding to mutable collections\n", + "# Run Me!\n", + "\n", + "# Adding to MUTABLE collections\n", "\n", "l = list()\n", "l.append('a')\n", "l.append('b')\n", "l.append('c')\n", - "print(l)\n", + "print(\"List:\", l)\n", "\n", "s = set()\n", "s.add('a')\n", "s.add('b')\n", "s.add('c')\n", - "print(s)\n", + "print(\"Set: \", s)\n", "\n", - "# Concatenating immutable collections to create new objects\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 + ('a',) # Note the comma — this creates a 1-element tuple\n", "t = t + ('b',)\n", "t = t + ('c',)\n", - "print(t)\n", + "print(\"Tuple:\", t)\n", "\n", "s = \"\"\n", "s = s + \"a\"\n", "s = s + \"b\"\n", "s = s + \"c\"\n", - "print(s)" + "print(\"String:\", s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "When we write `s = s + 'a'` this means:\n", + "### **Understanding String Concatenation**\n", + "\n", + "When we write `s = s + 'a'`, here's what happens:\n", + "\n", + "1. Combine `s` and `'a'` to get a new string\n", + "2. Assign that new string back to `s`\n", "\n", - "1. Combine `s` and \"a\" to get a new string\n", - "2. Assign that new string back to s\n", + "This operation **destroys the old `s`** and creates a new one with the same name. This is very different from using `list.append()` or `set.add()` because those methods modify the collection in place without creating a new one.\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" + "> **Key Difference:** Mutable objects use methods like `.append()` and `.add()` to modify themselves. Immutable objects must be recreated with concatenation." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Dictionaries\n", + "---\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: " + "## **Dictionaries**\n", + "\n", + "You know what a real dictionary is, right? It has **words** (keys) and their **definitions** (values), and you look up the words to find the definitions. Python dictionaries work the same way!\n", + "\n", + "Here's how we create a dictionary in Python:" ] }, { @@ -173,9 +187,8 @@ "metadata": {}, "outputs": [], "source": [ - "# Dictionary example\n", - "\n", - "# A dictionary of words for superior people\n", + "# Run Me!\n", + "# Dictionary example - a collection of superior words and their definitions\n", "\n", "superior_words = {\n", " \"abecedarian\": \"a person who is learning the alphabet\",\n", @@ -192,12 +205,12 @@ " \"xerebrose\": \"dry, uninteresting\"\n", "}\n", "\n", - "# one way to look up a word, using the key and \"[]\"\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", - "# another way to look up a word, using `.get()`\n", + "# Method 2: Look up a word using .get()\n", "word = \"xerebrose\"\n", "definition = superior_words.get(word)\n", "print(f\"{word}: {definition}\")" @@ -207,26 +220,27 @@ "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", + "### **Dictionary vs Set Syntax**\n", "\n", - "```python\n", - "s = { 1, 2, 3, 4}\n", - "```\n", + "Both dictionaries and sets use curly braces `{}`, but they're created differently:\n", "\n", - "but this is a dict:\n", + "| Collection | Syntax | Example | Description |\n", + "|-----------|--------|---------|-------------|\n", + "| **Set** | Single values | `s = {1, 2, 3, 4}` | Items only, no keys |\n", + "| **Dict** | Key-value pairs | `d = {'a': 1, 'b': 2}` | Uses colons to separate keys from values |\n", "\n", - "```python \n", - "d = { \n", - " 'a': 1, \n", - " 'b': 2,\n", - " 'c': 3\n", - " }\n", - "```\n", + "You can also create them with constructor functions:\n", "\n", - "And like the other containers, you can also use a constructor function and then add items:\n", + "```python\n", + "# Creating an empty set\n", + "s = set()\n", + "s.add('value')\n", "\n", - "```python \n", + "# Creating an empty dict\n", "d = dict()\n", + "d['key'] = 'value'\n", + "\n", + "# Or with initial values\n", "d['a'] = 1\n", "d['b'] = 2\n", "d['c'] = 3\n", @@ -237,9 +251,9 @@ "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" + "### **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:" ] }, { @@ -248,24 +262,27 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "d = { \n", " \"one\": 1, \n", - " \"one\": 10, \n", + " \"one\": 10, # This overwrites the previous \"one\" entry\n", " \"two\": 2,\n", - " \"two\": 20,\n", + " \"two\": 20, # This overwrites the previous \"two\" entry\n", " \"three\": 3,\n", - " \"three\": 30\n", - " }\n", + " \"three\": 30 # This overwrites the previous \"three\" entry\n", + "}\n", "\n", - "print(d) # Only stores the last value for the key" + "print(d) # Only the LAST value for each key is stored" ] }, { "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?" + "### **Converting Dictionaries to Other Types**\n", + "\n", + "What happens when you convert a dictionary to another collection type?" ] }, { @@ -274,26 +291,29 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "d = { \n", " 'a': 1, \n", " 'b': 2,\n", " 'c': 3\n", - " }\n", + "}\n", "\n", "l = list(d)\n", - "print(l)" + "print(l) # What happened to the values?" ] }, { "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", + "When you convert a dictionary to a list, it only includes the **keys**, not the values! To access both keys and values, use these dictionary 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. " + "| 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)])` |" ] }, { @@ -302,6 +322,7 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", "# Accessing dict keys and values\n", "\n", "d = { \n", @@ -310,13 +331,13 @@ " 'c': 3\n", "}\n", "\n", - "print(d.keys())\n", - "print(d.values())\n", - "print(d.items())\n", + "print(\"Keys: \", d.keys())\n", + "print(\"Values:\", d.values())\n", + "print(\"Items: \", d.items())\n", "\n", "print()\n", "\n", - "# Iterate over items in a dictionary\n", + "# A common pattern: Iterate over both key AND value in a dictionary\n", "\n", "for key, value in d.items():\n", " print(f\"{key} = {value}\")" @@ -326,16 +347,18 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Pay attention to this idiom: \n", + "### **Common Pattern: 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 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()`:" + "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()`:" ] }, { @@ -344,8 +367,15 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", "# Enumerate keys and values\n", "\n", + "d = { \n", + " 'a': 1, \n", + " 'b': 2,\n", + " 'c': 3\n", + "}\n", + "\n", "for index, (key, value) in enumerate(d.items()):\n", " print(f\"#{index} {key} = {value}\")" ] @@ -354,9 +384,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Removing items\n", + "---\n", "\n", - "You can also remove items from collections:" + "## **Removing Items**\n", + "\n", + "You can remove items from most collections:" ] }, { @@ -365,35 +397,37 @@ "metadata": {}, "outputs": [], "source": [ - "l = list(\"abcd\") # Make a list of characters from a string\n", + "# Run Me!\n", "\n", - "# Remove 'c' from the list\n", + "# Remove from a list\n", + "l = list(\"abcd\")\n", "l.remove('c')\n", - "print(l)\n", + "print(\"List after remove:\", l)\n", "\n", + "# Remove from a set\n", "s = set(\"abcd\")\n", - "# Remove 'c' from the set\n", "s.remove('c')\n", - "print(s)\n", + "print(\"Set after remove: \", s)\n", "\n", + "# Remove from a dict (use 'del' keyword)\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)" + "print(\"Dict after del: \", d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Is it in there?\n", + "---\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. " + "## **Checking if Items Exist**\n", + "\n", + "Use the `in` operator to check if an item is in a collection. Use `not in` to check if it's absent:" ] }, { @@ -402,13 +436,14 @@ "metadata": {}, "outputs": [], "source": [ - "# Check and see if something is in a collection\n", + "# Run Me!\n", + "# Check 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", + "print(\"List:\", 'a' in l, 'f' in l, 'g' not in l)\n", "\n", "s = set(l)\n", - "print('a' in s, 'f' in s, 'g' not in s)\n", + "print(\"Set: \", 'a' in s, 'f' in s, 'g' not in s)\n", "\n", "d = {\n", " 'a': 1,\n", @@ -416,28 +451,30 @@ " '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)" + "# For dicts, 'in' checks for the existence of a KEY, not a value\n", + "print(\"Dict:\", 'a' in d, 'f' in d, 'g' not in d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Test Yourself\n", + "---\n", + "\n", + "## **Test Yourself**\n", "\n", - "Write a function, `check_funny_words(sentence)` to check if any of these words are in a sentence:\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", + "* `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", + "**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", - "Write a loop to call your function on each of the sentences in `funny_sentences`\n", - "and print the return value of the function " + "**Then:** Write a loop to call your function on each sentence in `funny_sentences` and print the result." ] }, { @@ -479,9 +516,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# How big is it?\n", + "---\n", + "\n", + "## **How Big Is It?**\n", "\n", - "Use `len()` to see how many items are in a collection.\n" + "Use `len()` to find how many items are in a collection:" ] }, { @@ -490,30 +529,35 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "c = \"Hello\"\n", - "l = list(c) # Make a list of characters from a string\n", + "l = list(c)\n", "\n", - "print(len(c), len(l))\n", + "print(\"String length:\", len(c))\n", + "print(\"List length: \", len(l))\n", "\n", "d = { \n", " 'a': 1, \n", " 'b': 2,\n", " 'c': 3\n", - " }\n", + "}\n", "\n", - "print(len(d))" + "print(\"Dict length: \", len(d))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Sorting collections\n", + "---\n", + "\n", + "## **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. " + "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" ] }, { @@ -522,23 +566,24 @@ "metadata": {}, "outputs": [], "source": [ - "l = list(\"gqycprc\")\n", + "# Run Me!\n", "\n", - "# Sort the list\n", + "# Modifying a list in-place with .sort()\n", + "l = list(\"gqycprc\")\n", "l.sort()\n", - "print(l)\n", + "print(\"Sorted list:\", l)\n", "\n", - "# Use the sorted function to return a new sorted list\n", + "# Using sorted() to create a new sorted list (original unchanged)\n", "l = list(\"gqycprc\")\n", "sorted_l = sorted(l)\n", - "print(l)\n", - "print(sorted_l)" + "print(\"Original: \", l)\n", + "print(\"Sorted copy:\", sorted_l)" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": ".venv", "language": "python", "name": "python3" }, @@ -552,13 +597,13 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.11" + "version": "3.13.5" }, "syllabus": { - "uid": "VXQdZcqg", - "name": "Dicts Sets" + "name": "Dicts Sets", + "uid": "VXQdZcqg" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/lessons/40_Data_Structures_Func/40_Splat_Comprehension.ipynb b/lessons/40_Data_Structures_Func/40_Splat_Comprehension.ipynb index 236d2f95..0484f28c 100644 --- a/lessons/40_Data_Structures_Func/40_Splat_Comprehension.ipynb +++ b/lessons/40_Data_Structures_Func/40_Splat_Comprehension.ipynb @@ -624,10 +624,9 @@ "version": "3.11.9" }, "syllabus": { - "uid": "mU94qia6", - "name": "Splat Comprehension" + "uid": "mU94qia6" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} From 628fc1a359c28b666337438754b80d4e63745da8 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 19 Jan 2026 13:27:44 -0500 Subject: [PATCH 145/159] Improved to be easier to understand --- .../20_Dicts_Sets.ipynb | 427 +++++++++--------- 1 file changed, 209 insertions(+), 218 deletions(-) diff --git a/lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb b/lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb index 7114ae9b..8a8c0d54 100644 --- a/lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb +++ b/lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb @@ -6,20 +6,23 @@ "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", + "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** | 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", + "| **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 a new one: the `set`. A **set** is important because it works like a list with key differences:\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", + "* *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:" ] @@ -32,11 +35,11 @@ "source": [ "# Run Me!\n", "\n", - "l = ['a','a','b','b','c','c']\n", - "print(\"List: \", l)\n", + "my_list = ['a','a','b','b','c','c']\n", + "print(\"List: \", my_list)\n", "\n", - "s = { 'a','a','b','b','c','c'}\n", - "print(\"Set: \", s)" + "my_set = { 'a','a','b','b','c','c'}\n", + "print(\"Set: \", my_set)" ] }, { @@ -46,34 +49,14 @@ "### **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", - "The formal name for these objects (string, set, list, and tuple) is **collections**.\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", - "---\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:\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's another way! You can also use the **constructor** functions. Here's how to create empty collections:\n", - "\n", - "```python \n", - "c = str()\n", - "t = tuple()\n", - "l = list()\n", - "s = set()\n", - "```\n", - "\n", - "These constructor functions can take one argument—an **iterable**—making it easy to convert between collection types." + "So far we've seen one way to create collections using braces, parentheses, and quotes:" ] }, { @@ -84,35 +67,44 @@ "source": [ "# Run Me!\n", "\n", - "c = \"Hello\"\n", - "l = ['a','a','b','b','c','c']\n", - "\n", - "# Make a tuple from a list\n", - "t = tuple(l)\n", - "print(\"Tuple from list:\", t)\n", - "\n", - "# Make a set from a list\n", - "s = set(l)\n", - "print(\"Set from list:\", s)\n", - "\n", - "# Make a list from a string\n", - "a = list(c)\n", - "print(\"List from string:\", a)\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", - "# Get the unique items from a list by converting to set then back to list\n", - "print(\"Unique items:\", list(set(l)))" + "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": [ - "### **Mutable vs Immutable Collections**\n", + "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", - "When you create an empty collection, you can sometimes add items to it:\n", + "# Creating empty collections using constructors\n", + "my_string = str()\n", + "my_tuple = tuple()\n", + "my_list = list()\n", + "my_set = set()\n", "\n", - "* **Mutable** collections (lists and sets) can be changed — items can be added or removed\n", - "* **Immutable** collections (tuples and strings) cannot be changed after creation — but you can **concatenate** them to create new objects" + "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." ] }, { @@ -123,62 +115,85 @@ "source": [ "# Run Me!\n", "\n", - "# Adding to MUTABLE collections\n", + "my_string = \"Hello\" + \"World\"\n", + "my_list = ['a','a','b','b','c','c']\n", "\n", - "l = list()\n", - "l.append('a')\n", - "l.append('b')\n", - "l.append('c')\n", - "print(\"List:\", l)\n", + "# Make a tuple from a list\n", + "my_tuple = tuple(my_list)\n", + "print(\"Tuple from list:\", my_tuple)\n", "\n", - "s = set()\n", - "s.add('a')\n", - "s.add('b')\n", - "s.add('c')\n", - "print(\"Set: \", s)\n", + "# Make a set from a list\n", + "my_set = set(my_list)\n", + "print(\"Set from list:\", my_set)\n", "\n", - "# Concatenating IMMUTABLE collections to create new objects\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", - "t = tuple()\n", - "t = t + ('a',) # Note the comma — this creates a 1-element tuple\n", - "t = t + ('b',)\n", - "t = t + ('c',)\n", - "print(\"Tuple:\", t)\n", - "\n", - "s = \"\"\n", - "s = s + \"a\"\n", - "s = s + \"b\"\n", - "s = s + \"c\"\n", - "print(\"String:\", s)" + "# 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": [ - "### **Understanding String Concatenation**\n", + "
                  \n", "\n", - "When we write `s = s + 'a'`, here's what happens:\n", + "## **REMINDER: Mutable vs. Immutable Collections**\n", "\n", - "1. Combine `s` and `'a'` to get a new string\n", - "2. Assign that new string back to `s`\n", + "Collections are either mutable (can be modified) or immutable (cannot be modified):\n", "\n", - "This operation **destroys the old `s`** and creates a new one with the same name. This is very different from using `list.append()` or `set.add()` because those methods modify the collection in place without creating a new one.\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", - "> **Key Difference:** Mutable objects use methods like `.append()` and `.add()` to modify themselves. Immutable objects must be recreated with concatenation." + "
                  " ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "---\n", + "# Run Me!\n", "\n", - "## **Dictionaries**\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", - "You know what a real dictionary is, right? It has **words** (keys) and their **definitions** (values), and you look up the words to find the definitions. Python dictionaries work the same way!\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", - "Here's how we create a dictionary in Python:" + "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**!" ] }, { @@ -188,8 +203,8 @@ "outputs": [], "source": [ "# Run Me!\n", - "# Dictionary example - a collection of superior words and their definitions\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", @@ -226,25 +241,8 @@ "\n", "| Collection | Syntax | Example | Description |\n", "|-----------|--------|---------|-------------|\n", - "| **Set** | Single values | `s = {1, 2, 3, 4}` | Items only, no keys |\n", - "| **Dict** | Key-value pairs | `d = {'a': 1, 'b': 2}` | Uses colons to separate keys from values |\n", - "\n", - "You can also create them with constructor functions:\n", - "\n", - "```python\n", - "# Creating an empty set\n", - "s = set()\n", - "s.add('value')\n", - "\n", - "# Creating an empty dict\n", - "d = dict()\n", - "d['key'] = 'value'\n", - "\n", - "# Or with initial values\n", - "d['a'] = 1\n", - "d['b'] = 2\n", - "d['c'] = 3\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 |" ] }, { @@ -264,7 +262,7 @@ "source": [ "# Run Me!\n", "\n", - "d = { \n", + "my_dict = { \n", " \"one\": 1, \n", " \"one\": 10, # This overwrites the previous \"one\" entry\n", " \"two\": 2,\n", @@ -273,7 +271,7 @@ " \"three\": 30 # This overwrites the previous \"three\" entry\n", "}\n", "\n", - "print(d) # Only the LAST value for each key is stored" + "print(my_dict) # Only the LAST value for each key is stored" ] }, { @@ -293,21 +291,23 @@ "source": [ "# Run Me!\n", "\n", - "d = { \n", + "my_dict = { \n", " 'a': 1, \n", " 'b': 2,\n", " 'c': 3\n", "}\n", "\n", - "l = list(d)\n", - "print(l) # What happened to the values?" + "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, it only includes the **keys**, not the values! To access both keys and values, use these dictionary methods:\n", + "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", @@ -323,23 +323,22 @@ "outputs": [], "source": [ "# Run Me!\n", - "# Accessing dict keys and values\n", "\n", - "d = { \n", + "# Accessing dict keys and values\n", + "my_dict = { \n", " 'a': 1, \n", " 'b': 2,\n", " 'c': 3\n", "}\n", "\n", - "print(\"Keys: \", d.keys())\n", - "print(\"Values:\", d.values())\n", - "print(\"Items: \", d.items())\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", - "\n", - "for key, value in d.items():\n", + "for key, value in my_dict.items():\n", " print(f\"{key} = {value}\")" ] }, @@ -347,7 +346,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### **Common Pattern: Unpacking Key-Value Pairs**\n", + "### **Unpacking Key-Value Pairs**\n", "\n", "Pay close attention to this idiom:\n", "\n", @@ -356,7 +355,7 @@ " 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", + "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()`:" ] @@ -368,15 +367,16 @@ "outputs": [], "source": [ "# Run Me!\n", - "# Enumerate keys and values\n", "\n", - "d = { \n", + "# Enumerate keys and values\n", + "my_dict = { \n", " 'a': 1, \n", " 'b': 2,\n", " 'c': 3\n", "}\n", "\n", - "for index, (key, value) in enumerate(d.items()):\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}\")" ] }, @@ -384,9 +384,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", - "\n", - "## **Removing Items**\n", + "### **Removing Items**\n", "\n", "You can remove items from most collections:" ] @@ -400,34 +398,33 @@ "# Run Me!\n", "\n", "# Remove from a list\n", - "l = list(\"abcd\")\n", - "l.remove('c')\n", - "print(\"List after remove:\", l)\n", + "my_list = list(\"abcd\")\n", + "my_list.remove('c')\n", + "print(\"List after remove:\", my_list)\n", "\n", "# Remove from a set\n", - "s = set(\"abcd\")\n", - "s.remove('c')\n", - "print(\"Set after remove: \", s)\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", - "d = {\n", + "my_dict = {\n", " 'a': 1,\n", " 'b': 2,\n", " 'c': 3\n", "}\n", - "del d['c']\n", - "print(\"Dict after del: \", d)" + "\n", + "del my_dict['c']\n", + "print(\"Dict after del: \", my_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", - "\n", - "## **Checking if Items Exist**\n", + "### **Checking if Items Exist**\n", "\n", - "Use the `in` operator to check if an item is in a collection. Use `not in` to check if it's absent:" + "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:" ] }, { @@ -437,44 +434,31 @@ "outputs": [], "source": [ "# Run Me!\n", - "# Check if something is in a collection\n", "\n", - "l = list(\"abcd\")\n", - "print(\"List:\", 'a' in l, 'f' in l, 'g' not in l)\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", - "s = set(l)\n", - "print(\"Set: \", 'a' in s, 'f' in s, 'g' not in s)\n", + "my_set = set(my_list)\n", + "print(\"Set: \", 'a' in my_set, 'f' in my_set, 'g' not in my_set)\n", "\n", - "d = {\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 d, 'f' in d, 'g' not in d)" + "print(\"Dict:\", 'a' in my_dict, 'f' in my_dict, 'g' not in my_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "### **How Big Is It?**\n", "\n", - "## **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." + "Use `len()` to find how many items are in a collection:" ] }, { @@ -483,44 +467,33 @@ "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", + "# Run Me!\n", "\n", - "def check_funny_words(sentence):\n", - " \"\"\"\n", - " Checks if any funny words are present in the given sentence.\n", + "my_string = \"Hello\"\n", + "my_list = list(my_string)\n", "\n", - " Args:\n", - " sentence (str): The sentence to check for funny words.\n", + "print(\"String length:\", len(my_string))\n", + "print(\"List length: \", len(my_list))\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", + "my_dict = { \n", + " 'a': 1, \n", + " 'b': 2,\n", + " 'c': 3\n", + "}\n", "\n", - "for s in funny_sentences:\n", - " print(check_funny_words(s))" + "print(\"Dict length: \", len(my_dict))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "### **Sorting Collections**\n", "\n", - "## **How Big Is It?**\n", + "Sorting puts items in a collection into order (numerical for numbers, alphabetical for strings). \n", "\n", - "Use `len()` to find how many items are in a collection:" + "* *Lists* can be sorted in-place using `.sort()`\n", + "* *Immutable collections* must use `sorted()` to create a new sorted collection" ] }, { @@ -531,33 +504,36 @@ "source": [ "# Run Me!\n", "\n", - "c = \"Hello\"\n", - "l = list(c)\n", - "\n", - "print(\"String length:\", len(c))\n", - "print(\"List length: \", len(l))\n", - "\n", - "d = { \n", - " 'a': 1, \n", - " 'b': 2,\n", - " 'c': 3\n", - "}\n", + "# Modifying a list in-place with .sort()\n", + "l = list(\"gqycprc\")\n", + "l.sort()\n", + "print(\"Sorted list:\", l)\n", "\n", - "print(\"Dict length: \", len(d))" + "# 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": [ - "---\n", + "## **Test Yourself**\n", "\n", - "## **Sorting Collections**\n", + "Write a function, `check_funny_words(sentence)` that checks if any of these words appear in a sentence:\n", "\n", - "Sorting puts items in a collection into order (numerical for numbers, alphabetical for strings). \n", + "* `snollygoster`\n", + "* `skedaddle`\n", + "* `lollygag`\n", + "* `collywobble`\n", "\n", - "* **Lists** can be sorted in-place using `.sort()`\n", - "* **Immutable collections** must use `sorted()` to create a new sorted collection" + "**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." ] }, { @@ -566,18 +542,33 @@ "metadata": {}, "outputs": [], "source": [ - "# Run Me!\n", + "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", - "# Modifying a list in-place with .sort()\n", - "l = list(\"gqycprc\")\n", - "l.sort()\n", - "print(\"Sorted list:\", l)\n", + "def check_funny_words(sentence):\n", + " \"\"\"\n", + " Checks if any funny words are present in the given sentence.\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)" + " 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))" ] } ], From fbd42a12ae5c6ee991cf5cc2f7224892bda2bca7 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Sat, 24 Jan 2026 00:51:05 -0500 Subject: [PATCH 146/159] Separated `Functions` into two notebooks: `10_Functions.ipynb` & `20_Exceptions.ipynb` and updated the syllabus/numbers to match this change; Reworked Functions to be a bit clearer and significantly enhanced Exceptions. --- lessons/.jtl/syllabus.yaml | 11 +- .../10_Functions.ipynb | 998 ++++++++---------- .../20_Exceptions.ipynb | 304 ++++++ ...0_Dicts_Sets.ipynb => 30_Dicts_Sets.ipynb} | 0 ...Funny_Words_Db.py => 40_Funny_Words_Db.py} | 0 ...ion.ipynb => 50_Splat_Comprehension.ipynb} | 97 +- .../{50_Tic_Tac_Toe.py => 60_Tic_Tac_Toe.py} | 0 7 files changed, 814 insertions(+), 596 deletions(-) create mode 100644 lessons/40_Data_Structures_Func/20_Exceptions.ipynb rename lessons/40_Data_Structures_Func/{20_Dicts_Sets.ipynb => 30_Dicts_Sets.ipynb} (100%) rename lessons/40_Data_Structures_Func/{30_Funny_Words_Db.py => 40_Funny_Words_Db.py} (100%) rename lessons/40_Data_Structures_Func/{40_Splat_Comprehension.ipynb => 50_Splat_Comprehension.ipynb} (85%) rename lessons/40_Data_Structures_Func/{50_Tic_Tac_Toe.py => 60_Tic_Tac_Toe.py} (100%) diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index f2375d6b..f2b95c79 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -206,17 +206,20 @@ modules: - 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 + 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 + exercise: 40_Data_Structures_Func/60_Tic_Tac_Toe.py display: true - name: Module Four Quiz uid: bX9eUdqT diff --git a/lessons/40_Data_Structures_Func/10_Functions.ipynb b/lessons/40_Data_Structures_Func/10_Functions.ipynb index b45aa710..681fd5e3 100644 --- a/lessons/40_Data_Structures_Func/10_Functions.ipynb +++ b/lessons/40_Data_Structures_Func/10_Functions.ipynb @@ -1,548 +1,466 @@ { - "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 more thoroughly.\n", - "\n", - "A basic function looks like this:\n", - "\n", - "```python \n", - "def add_ten(x):\n", - " return x + 10\n", - "```\n", - "\n", - "Then we can call the function like this:\n", - "\n", - "```python \n", - "y = add_ten(5)\n", - "print(y) # Output: 15\n", - "```\n", - "\n", - "### **Parts of a Function**\n", - "\n", - "Every function has several key components:\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) |" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "def do_nothing():\n", - " pass # 'pass' means \"do nothing\" — it's just a placeholder\n", - "\n", - "y = do_nothing()\n", - "print(\"Return value:\", y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **About the `pass` Keyword**\n", - "\n", - "The `pass` keyword means \"do nothing\" — it's just a placeholder that takes up space so the function has a properly indented body. Since `do_nothing()` has no `return` statement, it returns the empty value `None`.\n", - "\n", - "### **Why Use Functions?**\n", - "\n", - "The most important reasons to write functions are:\n", - "\n", - "1. **Reusability** — Write the code once, use it many times\n", - "2. **Clarity** — Breaking a program into functions makes it easier to understand\n", - "3. **Testing** — Smaller functions are easier to test and debug\n", - "4. **Naming** — Descriptive function and argument names help explain what the code does\n", - "\n", - "> **Tip:** Choose function and argument names that clearly indicate what they do or what data they hold. This makes your code self-documenting!\n", - "\n", - "> **Reminder:** Have you checked in your code? See the [check-in documentation](https://curriculum.jointheleague.org/howto/checkin_restart.html) if you need a refresher." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "\n", - "## **Function Arguments**\n", - "\n", - "Function arguments are the values you pass into a function so it can compute and return a result. You can assign values to arguments in several ways:\n", - "\n", - "- **Positional arguments** — pass values in order\n", - "- **Keyword arguments** — pass values by name, in any order\n", - "\n", - "Let'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 (in order)\n", - "# 'Alice' goes to name, 'Bonjour' goes to greeting\n", - "greet_user(\"Alice\", \"Bonjour\")\n", - "\n", - "# Method 2: Keyword arguments (by name, in any order)\n", - "# The order doesn't matter when using keywords\n", - "greet_user(greeting=\"Hello\", name=\"Alice\")\n", - "\n", - "# Method 3: Mix positional and keyword\n", - "# Positional arguments MUST come first\n", - "greet_user(\"Bob\", greeting=\"Hello\")\n", - "\n", - "# These will cause errors (uncomment to see):\n", - "# greet_user(name=\"Bob\", greeting=\"Hello\", extra=\"oops\") # Extra unknown argument\n", - "# greet_user(\"Bob\") # Missing required argument 'greeting'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **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": [ - "### **Variable Scope: Declared vs Undeclared Variables**\n", - "\n", - "Variables used in a function must be **declared** — either as function arguments or defined within the function. Using an undefined variable causes an error:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "def greet_user(name, greeting='Hello'):\n", - " # Error! 'punct' is not defined anywhere\n", - " print(f\"{greeting}, {name}{punct}\")\n", - "\n", - "# This will raise a NameError\n", - "greet_user(\"Alice\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Python didn't know what the variable `punct` is, so it couldn't run the function and raised a `NameError`.\n", - "\n", - "> **Best Practice:** Always declare all variables you use in your function, either in the argument list or within the function body." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "\n", - "## **Test Yourself**\n", - "\n", - "Write a function `find_char(character, string)` that:\n", - "- Takes a character and a longer string\n", - "- Iterates through the string to find the character's position\n", - "- Returns the **position** (index) where the character is found\n", - "- Returns `-1` if the character is not in the string\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 get both the character and its position as you loop through the string." - ] - }, - { - "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": [ - "---\n", - "\n", - "## **Function 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!\n", - "\n", - "For example, if you have these functions:\n", - "\n", - "```python \n", - "def f(a, b):\n", - " return a + b\n", - "\n", - "def g(c, d):\n", - " return c * d\n", - "```\n", - "\n", - "You can pass the output of `f()` directly into `g()`:\n", - "\n", - "```python \n", - "result = g(f(1, 2), f(3, 4)) # Same as: g(3, 7) → 21\n", - "```\n", - "\n", - "Or combine them in expressions:\n", - "\n", - "```python \n", - "result = g(f(1, 2) * f(3, 4), f(5, 6) - f(7, 8))\n", - "```\n", - "\n", - "This is called **function composition** — using the output of one function as input to another." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "---\n", - "\n", - "## **Test Yourself: Function Composition**\n", - "\n", - "Write three functions and use them together to demonstrate:\n", - "\n", - "1. A function to **add** two numbers\n", - "2. A function to **subtract** two numbers \n", - "3. A function to **compare** two numbers (print \"Same\" if equal, \"Different\" if not)\n", - "\n", - "Then use these functions to show:\n", - "- That `2 + 2 = 4`\n", - "- That `(5 + 3) - 2 = 6`\n", - "- That `2 + 2 ≠ 3 + 3`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "\n", - "## **Closures: A Warning and an Exciting Feature** ⚠️\n", - "\n", - "Here's something surprising — this function works even though `name` is not in the argument list:\n", - "\n", - "```python\n", - "name = 'Bob'\n", - "\n", - "def greet_user(greeting):\n", - " print(f\"{greeting}, {name}\")\n", - "\n", - "greet_user(\"Hello\") # Output: Hello, Bob!\n", - "```\n", - "\n", - "The function **found** `name` outside the function scope. This behavior is called a **closure**, and while it's very useful, it can also cause problems if you're not careful." - ] - }, - { - "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\") # Output: Hello, Bob!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Best Practice:** 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!\n", - "\n", - "---\n", - "\n", - "## **Exceptions**\n", - "\n", - "Sometimes things go wrong in your code. When that happens, Python raises an **exception** — 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": "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": [ - "# Run Me!\n", - "# This will raise a ValueError\n", - "\n", - "int(\"This is not an integer\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `ValueError` in the error message is the **exception type**. It's not just a message — it's a thing we can use in our program to **handle errors**. \n", - "\n", - "For example, suppose we have a list of things to convert to integers:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "# This will crash when it hits 'Bob'\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", - "metadata": {}, - "source": [ - "The program crashed on 'Bob'. We didn't make it through the whole list! But we can **catch** the exception using a `try/except` block and handle it gracefully:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\n", - "# Using try/except to handle errors gracefully\n", - "\n", - "for e in [0, 1, 65, 'Bob', 23, 'larry']:\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": [ - "### **How Try/Except Works**\n", - "\n", - "Using a `try/except` block, you can:\n", - "1. **Try** to run code that might fail\n", - "2. **Except** (catch) specific exceptions if they occur\n", - "3. **Handle** the error gracefully instead of crashing\n", - "\n", - "The `except ValueError:` part catches the exception type you saw in the error message. Different error types have different exception names — this is just one example." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me! Uncomment one line at a time to see different exception types\n", - "\n", - "my_list = [1, 2, 3, 4, 5]\n", - "\n", - "# TypeError: Using wrong type for indexing\n", - "# print(my_list['Bob'])\n", - "\n", - "# IndexError: Index out of range\n", - "# print(my_list[20])\n", - "\n", - "# AssertionError: Assertion failed\n", - "# assert False\n", - "\n", - "# ZeroDivisionError: Division by zero\n", - "# x = 10 / 0\n", - "\n", - "print(\"Uncomment one of the lines above to see different exception types!\")" - ] - }, - { - "cell_type": "markdown", - "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, - "metadata": {}, - "outputs": [], - "source": [ - "# Run Me!\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}\")" - ] - }, + "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", + "\n", + "Functions 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", + "\n", + "Arguments 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", + "\n", + "Let'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 SyntaxError.\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": [ - "---\n", - "\n", - "## **Test Yourself: Exception Handling**\n", - "\n", - "Write a program that:\n", - "\n", - "1. Runs in an **endless loop** until the user enters `'q'`\n", - "2. Gets a string from the user using `input()`\n", - "3. Tries to convert it to an integer\n", - "4. If successful:\n", - " - Check if the number is in the list `[5, 10, 45, 56]`\n", - " - If it's in the list, print \"Found it!\"\n", - " - If it's NOT in the list, tell the user what the n-th number in the list is (e.g., \"7 is not in the list, but the 7th number is 56\")\n", - " - Handle the case where the list is shorter than n using an `except` block\n", - "5. If the user enters `'q'`, exit the loop\n", - "6. If the string can't be converted to an integer, print an error message\n", - "\n", - "**Hint:** Check if something is in a list using `in`:\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", - "# 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", - "# TODO: Add your code here" + "data": { + "text/plain": [ + "17" ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" } - ], - "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": "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 } 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..87633837 --- /dev/null +++ b/lessons/40_Data_Structures_Func/20_Exceptions.ipynb @@ -0,0 +1,304 @@ +{ + "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": [ + "Oh, that didn't work! This happened because the program tried to convert `Bob` to an integer and that caused an exception to be thrown, which led to a crash before completing the loop could be completed. \n", + "\n", + "What if we could catch that exception using a try/except block and provide instructions on what to do when an error occurs?\n", + "\n", + "Let'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. Memorizing this technique is essential for programs that deal with 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 +} diff --git a/lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb b/lessons/40_Data_Structures_Func/30_Dicts_Sets.ipynb similarity index 100% rename from lessons/40_Data_Structures_Func/20_Dicts_Sets.ipynb rename to lessons/40_Data_Structures_Func/30_Dicts_Sets.ipynb 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 100% rename from lessons/40_Data_Structures_Func/30_Funny_Words_Db.py rename to lessons/40_Data_Structures_Func/40_Funny_Words_Db.py diff --git a/lessons/40_Data_Structures_Func/40_Splat_Comprehension.ipynb b/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb similarity index 85% rename from lessons/40_Data_Structures_Func/40_Splat_Comprehension.ipynb rename to lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb index 0484f28c..c9ce0b9e 100644 --- a/lessons/40_Data_Structures_Func/40_Splat_Comprehension.ipynb +++ b/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb @@ -4,10 +4,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Tic Tac Toe\n", + "# **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:" + "In the next project you will be building a Tic Tac Toe game, but before we can do that cleanly, you will probably want a quick refresher on **multi-dimensional data**.\n" ] }, { @@ -16,30 +15,29 @@ "metadata": {}, "outputs": [], "source": [ - "# A 2 dimensional array is an array of arrays. Here is an example:\n", + "# Run Me!\n", "\n", - "row1 = [1, 2, 3]\n", - "row2 = [4, 5, 6]\n", - "row3 = [7, 8, 9]\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", - " row1, \n", - " row2, \n", - " row3\n", + " row_one, \n", + " row_two, \n", + " row_three\n", " ]\n", "\n", "print(two_dimensional_array)\n", "\n", - "# Or , The more compact usual way: \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 use '[][]' to access the elements of the 2D array.\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" @@ -49,7 +47,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "THe `[][]` construct is for indexing in two dimensions. To understand the two\n", + "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", @@ -61,11 +59,6 @@ "```" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -83,17 +76,16 @@ "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", + "my_list = [ [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", + "v = my_list[row_num][col_num]\n", "\n", "# Expand it out\n", - "row = l[row_num]\n", + "row = my_list[row_num]\n", "v = row[col_num]\n", - "\n", "```" ] }, @@ -196,42 +188,41 @@ "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", + " 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. To make it clearer, we can color code the items.\n", "\n", - "code:\n", + "**Input:**\n", "
                  \n",
                  -        "for e in zip([1, 2, 3], [4, 5, 6],[7, 8, 9]):\n",
                  +        "for e in zip([1, 2, 3], [4, 5, 6],[7, 8, 9]):\n",
                           "    print(e)\n",
                           "
                  \n", "\n", - "output:\n", + "**Output:**\n", "
                  \n",
                  -        "(1, 4, 7)\n",
                  -        "(2, 5, 8)\n",
                  -        "(3, 6, 9)\n",
                  -        "
                  " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ + "(1, 4, 7)\n", + "(2, 5, 8)\n", + "(3, 6, 9)\n", + "\n", "\n", - "But, we don't want a tuple of tuples, we want a list of lists, so we have to do some converstion. \n" + "But, we don't want a tuple of tuples, we want a list of lists, so we have to do some conversion. " ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'board' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# First transpose:\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m t = \u001b[38;5;28mzip\u001b[39m(*\u001b[43mboard\u001b[49m)\n\u001b[32m 4\u001b[39m l = []\n\u001b[32m 5\u001b[39m \u001b[38;5;66;03m# convert each row from tuple to list\u001b[39;00m\n", + "\u001b[31mNameError\u001b[39m: name 'board' is not defined" + ] + } + ], "source": [ "# First transpose:\n", "t = zip(*board)\n", @@ -250,7 +241,9 @@ "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" + "That conversion code is kinda ugly, but we can make it much prettier and more closer to Python with a list comprehension. \n", + "\n", + "Here is what that looks like: \n" ] }, { @@ -267,7 +260,7 @@ "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", + "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", @@ -329,7 +322,7 @@ "[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", + "Then the two diagonals are: `[1,5,9]`, and `[7,5,3]`. Let's try to use a comprehension to \n", "find the diagonals: \n" ] }, @@ -621,7 +614,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.13.5" }, "syllabus": { "uid": "mU94qia6" 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 100% rename from lessons/40_Data_Structures_Func/50_Tic_Tac_Toe.py rename to lessons/40_Data_Structures_Func/60_Tic_Tac_Toe.py From c4a36a2e50cdcc11f7b2f7a5dccd970a747843ff Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 26 Jan 2026 05:11:07 -0500 Subject: [PATCH 147/159] Made several quality of life improvements --- .../50_Splat_Comprehension.ipynb | 444 +++++++++++------- 1 file changed, 283 insertions(+), 161 deletions(-) diff --git a/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb b/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb index c9ce0b9e..60d6e8e6 100644 --- a/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb +++ b/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb @@ -6,7 +6,7 @@ "source": [ "# **Tic Tac Toe**\n", "\n", - "In the next project you will be building a Tic Tac Toe game, but before we can do that cleanly, you will probably want a quick refresher on **multi-dimensional data**.\n" + "In 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." ] }, { @@ -17,7 +17,7 @@ "source": [ "# Run Me!\n", "\n", - "# A 2 dimensional array is an array of arrays:\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", @@ -26,7 +26,7 @@ " row_one, \n", " row_two, \n", " row_three\n", - " ]\n", + "]\n", "\n", "print(two_dimensional_array)\n", "\n", @@ -38,54 +38,53 @@ " ]\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" + "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", + "The `[][]` construct lets you access elements in a grid. Think of it as giving directions: first the row, then the column.\n", "\n", - "Can you guess what the first index on the array will return? What do you think this will print out?\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", - "\n", "print( two_dimensional_array[1] )\n", - "\n", "```" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "# Try it! \n", - "# See what print( two_dimensional_array[1] ) produces\n" + "# See what print( two_dimensional_array[1] ) produces" ] }, { "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", + "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 typical way:\n", + "# The shortcut way:\n", "v = my_list[row_num][col_num]\n", "\n", - "# Expand it out\n", - "row = my_list[row_num]\n", - "v = row[col_num]\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", "```" ] }, @@ -93,9 +92,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Who Won ?\n", + "## **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", - "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", + "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", @@ -105,14 +106,15 @@ " ]\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" + "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()`!" ] }, { @@ -121,8 +123,10 @@ "metadata": {}, "outputs": [], "source": [ + "# Run Me!\n", + "\n", "def pretty_print_2d(a):\n", - " \"\" \"Prints a 2D array in a pretty way\" \"\"\n", + " \"\"\"Prints a 2D array in a pretty way\"\"\"\n", " for row in a:\n", " print(row)\n", "\n", @@ -136,42 +140,47 @@ "print()\n", "\n", "transposed = list(zip(*board)) # <--- HERE IS THE MAGIC\n", - "pretty_print_2d(transposed)\n" + "pretty_print_2d(transposed)" ] }, { "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", + "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", "\n", - "Here is the line of code: \n", + "Did you see how the brackets changed from `[]` to `()`? \n", "\n", - "```python \n", - "list(zip(*board))\n", - "```\n", + "That's because `zip()` automatically creates tuples instead of lists. Don't worry though, we'll fix this in a moment. \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", + "First, let's understand how `zip()` performs this magic trick.\n", "\n", - "```python \n", - "list(zip(board[0],board[1],board[2])))\n", - "```\n", + "Here is the syntax:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Run Me!\n", "\n", - "And that is equivalent to:\n", + "list(zip(*board)) # Basic usage of zip with splat operator" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "\n", - "```python \n", - "list(zip([1, 2, 3],[4, 5, 6],[7, 8, 9])))\n", - "```\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", - "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" + "So instead of passing one big board to `zip()`, we pass each row separately, like this:" ] }, { @@ -180,7 +189,50 @@ "metadata": {}, "outputs": [], "source": [ - "for e in zip([1, 2, 3],[4, 5, 6],[7, 8, 9]):\n", + "# 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)" ] }, @@ -188,7 +240,11 @@ "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. To make it clearer, we can color code the items.\n", + "Perfect! Now you can see exactly what `zip()` does. \n", + "\n", + "The first tuple contains the first item from each list, the second tuple contains the second item from each list, and so on. \n", + "\n", + "To make this pattern even clearer, let's look at this color-coded version:\n", "\n", "**Input:**\n", "
                  \n",
                  @@ -203,47 +259,39 @@
                           "(3, 6, 9)\n",
                           "
                  \n", "\n", - "But, we don't want a tuple of tuples, we want a list of lists, so we have to do some conversion. " + "But we don't want tuples—we want lists! \n", + "\n", + "So we need to convert them:" ] }, { "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'board' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# First transpose:\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m t = \u001b[38;5;28mzip\u001b[39m(*\u001b[43mboard\u001b[49m)\n\u001b[32m 4\u001b[39m l = []\n\u001b[32m 5\u001b[39m \u001b[38;5;66;03m# convert each row from tuple to list\u001b[39;00m\n", - "\u001b[31mNameError\u001b[39m: name 'board' is not defined" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ + "# Run Me!\n", + "\n", "# First transpose:\n", - "t = zip(*board)\n", + "my_tuple = zip(*board)\n", "\n", - "l = []\n", - "# convert each row from tuple to list\n", - "for e in t:\n", - " l.append(list(e))\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(l)\n" + "pretty_print_2d(my_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Introducing Comprehensions\n", + "## **Introducing Comprehensions**\n", "\n", - "That conversion code is kinda ugly, but we can make it much prettier and more closer to Python with a list comprehension. \n", + "That conversion code is kind of **verbose**, but we can make it much cleaner with a **list comprehension**! \n", "\n", - "Here is what that looks like: \n" + "Here's how:" ] }, { @@ -252,24 +300,27 @@ "metadata": {}, "outputs": [], "source": [ - "l = [list(e) for e in zip(*board)]\n", - "pretty_print_2d(l)" + "# 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 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", + "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", + "[ 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" + "The `` part is what gets added to the new list for each iteration. \n", + "\n", + "These two code snippets do the exact same thing:" ] }, { @@ -278,8 +329,9 @@ "metadata": {}, "outputs": [], "source": [ - "# The old way to square every number:\n", + "# Run Me!\n", "\n", + "# The old way to square every number:\n", "nums = [1, 2, 3, 4, 5]\n", "\n", "squared = []\n", @@ -289,41 +341,43 @@ "\n", "print(squared)\n", "\n", - "# The comprehension way:\n", - "\n", + "# The simpler, comprehension way:\n", "squared = [n * n for n in nums]\n", - "print(squared)\n", - "\n" + "print(squared)" ] }, { "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. " + "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 very popular in Python because they make code more concise and readable!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Yes, but Who Won?\n", + "## **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", + "Now 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", - "But, we still have to check the two diagonals. Let's look at the coordinates of the diagonals.\n", - "if your board is:\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", "\n", + "Now remember, we still need to check the two diagonals. Let's look at their coordinates.\n", + "\n", + "If this is your board:\n", + "\n", + "```python\n", + "[1, 2, 3]\n", + "[4, 5, 6]\n", + "[7, 8, 9]\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,3]`. Let's try to use a comprehension to \n", - "find the diagonals: \n" + "Then the two diagonals are: `[1, 5, 9]` and `[3, 5, 7]`. \n", + "\n", + "Let's use what we've learned about comprehension to find these diagonals:" ] }, { @@ -332,7 +386,7 @@ "metadata": {}, "outputs": [], "source": [ - "# Text Yourself\n", + "# Test Yourself!\n", "# In the comprehensions below, replace the ...\n", "# with code that will produce the expected output.\n", "\n", @@ -343,37 +397,51 @@ " ]\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", + "# diag1 = [ board[...][...] for i in range(3) ]\n", + "# assert diag1 == [1, 5, 9]\n", "\n", - "#d2 = [ board[...][...] for i in range(3) ]\n", - "#assert d2 == [3, 5, 7]\n", - "\n" + "# diag2 = [ board[...][...] for i in range(3) ]\n", + "# assert diag2 == [3, 5, 7]" ] }, { "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" + "> **Hint:** Notice the pattern in the coordinates!\n", + "\n", + "For 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", + "\n", + "For 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! Just start simple with something like: `l = [board[0][0], board[1][1], board[2][2]]`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Ok, now we have explained: \n", + "## **Checking for a Winner**\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", + "Ok, so far we know how to get: \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" + "* 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:" ] }, { @@ -382,54 +450,59 @@ "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", + "# Run Me!\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" + "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": [ - "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", + "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", + "## 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" + "> **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, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -563,9 +636,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Make It Better!\n", + "## Challenge: 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" + "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:" ] }, { @@ -574,20 +651,65 @@ "metadata": {}, "outputs": [], "source": [ - "l = [\n", + "# Run Me!\n", + "\n", + "board = [\n", " [1, 2, 3],\n", " [4, 5, 6],\n", " [7, 8, 9]\n", " ]\n", "\n", "def transpose(a):\n", - " return list(zip(*a))\n", + " \"\"\"Convert rows to columns and columns to rows\"\"\"\n", + " return [list(row) for row in 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", + "# Start with all rows\n", + "all_lines = board[:] # Copy the original rows \n", + "all_lines.extend(transpose(board)) # Add the transposed columns\n", "\n", - "for e in m:\n", - " print(e)" + "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", + "# HINT: The structure should look something like:\n", + "# all_lines = board[:] \n", + "# all_lines.extend(...) # Add columns\n", + "# all_lines.append(...) # Add first diagonal\n", + "# all_lines.append(...) # Add second diagonal\n", + "# \n", + "# for line in all_lines:\n", + "# # Check if there's a winner in this line\n", + "#\n", + "# Test your optimized function with the same test boards!\n" ] }, { From 31cb91ba8a9c346028cb3d026d63b397945f711e Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 26 Jan 2026 05:11:43 -0500 Subject: [PATCH 148/159] Made several quality of life improvements (accidentally didnt push them all) --- lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb b/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb index 60d6e8e6..6610e228 100644 --- a/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb +++ b/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb @@ -636,7 +636,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Challenge: Make It Better!\n", + "## **Challenge: Make It Better!**\n", "\n", "Once your `check_board(board)` function works, let's **optimize** it!\n", "\n", From abfc4efbb1cd07d7521008d9fda6fe903261495d Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 26 Jan 2026 11:52:05 -0500 Subject: [PATCH 149/159] Quality of life improvements that did not push for some reason --- .../50_Splat_Comprehension.ipynb | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb b/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb index 6610e228..8d43b73a 100644 --- a/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb +++ b/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb @@ -6,7 +6,7 @@ "source": [ "# **Tic Tac Toe**\n", "\n", - "In 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." + "In 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." ] }, { @@ -700,15 +700,6 @@ "# 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", - "# HINT: The structure should look something like:\n", - "# all_lines = board[:] \n", - "# all_lines.extend(...) # Add columns\n", - "# all_lines.append(...) # Add first diagonal\n", - "# all_lines.append(...) # Add second diagonal\n", - "# \n", - "# for line in all_lines:\n", - "# # Check if there's a winner in this line\n", - "#\n", "# Test your optimized function with the same test boards!\n" ] }, From 54f6c2eed020375576bca3c636640b4255692a3f Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 26 Jan 2026 11:56:18 -0500 Subject: [PATCH 150/159] QoL --- .../50_Splat_Comprehension.ipynb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb b/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb index 8d43b73a..dcbec92d 100644 --- a/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb +++ b/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb @@ -502,7 +502,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -614,8 +614,7 @@ "#\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" + "# You might use a comprehension, like [ check_board(board) for board in x_wins_boards ] " ] }, { @@ -700,14 +699,16 @@ "# 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!\n" + "# Test your optimized function with the same test boards!" ] }, { "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" + "
                  \n", + "REMINDER: Don't forget to 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" ] } ], From 2c6744c14132a290a5f4b0166e962ec29e6118e1 Mon Sep 17 00:00:00 2001 From: Jay Sausa Date: Mon, 26 Jan 2026 11:59:03 -0500 Subject: [PATCH 151/159] Pushing changes that were previously not committed --- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 20 ------------------ .../10_Variables.ipynb | 21 +++---------------- .../20_Functions.ipynb | 2 +- 3 files changed, 4 insertions(+), 39 deletions(-) diff --git a/lessons/10_Turtles/40_Loops/10_Loops.ipynb b/lessons/10_Turtles/40_Loops/10_Loops.ipynb index 5f592f46..55606821 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -164,9 +164,6 @@ "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 can make your code much cleaner and easier to understand!\n", "\n", - "
                  \n", - " Explanation\n", - "\n", "Here'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", @@ -179,8 +176,6 @@ " tina.forward(150) # Move forward 150 units\n", " tina.left(60) # Turn left 60 degrees\n", "```\n", - "
                  \n", - "
                  \n", "\n", "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them—understanding loops will help you solve problems more efficiently and write better code!" ] @@ -218,15 +213,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "
                  \n", - " Explanation\n", - "\n", "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", - "
                  \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!" ] @@ -269,16 +259,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "
                  \n", - " Explanation\n", - "\n", "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", - "
                  \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." ] @@ -339,9 +324,6 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "
                  \n", - " Explanation\n", - "\n", "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", @@ -350,8 +332,6 @@ "- 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", - "
                  \n", "\n", "> **Tip:** You can use if-elif-else statements inside loops to create complex patterns and designs, like the American flag. " ] diff --git a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb index 63da1bba..1ab6d2ef 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb @@ -188,24 +188,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "892dbe57666c4b65aa443a2ff78f7b48", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Canvas(height=250, width=750)" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Run Me!\n", "\n", @@ -236,7 +221,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## **Variable Excercises (Optional)**\n", + "## **Variable Excercises**\n", "\n", "Try these exercises to practice working with variables:\n", "\n", diff --git a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb index eed7b02f..6e884bed 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb @@ -292,7 +292,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## **Function Challenges (Optional)**\n", + "## **Function Challenges**\n", "\n", "Now it's your turn! Write these functions using parameters, return values, and logic:" ] From c648e13d6ca32fa3b431f27df40135ab0ad97435 Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Thu, 16 Apr 2026 20:31:31 -0700 Subject: [PATCH 152/159] Add new lessons on Turtle graphics and Lists, including exercises for drawing and color manipulation --- .../{20_More_Turtle_programs.py => 20_More_Turtle_Programs.py} | 0 .../10_Lists.ipynb | 0 .../20_Color_Lines.py | 0 .../{80_Introducting_Lists => 80_Introducing_Lists}/README.md | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename lessons/10_Turtles/60_More_Turtle_Programs/{20_More_Turtle_programs.py => 20_More_Turtle_Programs.py} (100%) rename lessons/10_Turtles/{80_Introducting_Lists => 80_Introducing_Lists}/10_Lists.ipynb (100%) rename lessons/10_Turtles/{80_Introducting_Lists => 80_Introducing_Lists}/20_Color_Lines.py (100%) rename lessons/10_Turtles/{80_Introducting_Lists => 80_Introducing_Lists}/README.md (100%) 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 100% 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 diff --git a/lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb b/lessons/10_Turtles/80_Introducing_Lists/10_Lists.ipynb similarity index 100% rename from lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb rename to lessons/10_Turtles/80_Introducing_Lists/10_Lists.ipynb 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 100% rename from lessons/10_Turtles/80_Introducting_Lists/20_Color_Lines.py rename to lessons/10_Turtles/80_Introducing_Lists/20_Color_Lines.py diff --git a/lessons/10_Turtles/80_Introducting_Lists/README.md b/lessons/10_Turtles/80_Introducing_Lists/README.md similarity index 100% rename from lessons/10_Turtles/80_Introducting_Lists/README.md rename to lessons/10_Turtles/80_Introducing_Lists/README.md From 69e9572b7d1796d0afe07a05b93df14c820d298a Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Thu, 16 Apr 2026 20:31:59 -0700 Subject: [PATCH 153/159] Minor Edits --- CLAUDE.md | 99 ++ REVIEW.md | 269 +++++ lessons/.jtl/syllabus.yaml | 10 +- .../10_Turtles/10_Welcome/10_Welcome.ipynb | 112 +-- .../10_Welcome/20_Open_The_Screen.ipynb | 77 +- .../10_Welcome/30_Run_Programs.ipynb | 291 ++---- .../10_Meet_Tina/Meet_Tina.py | 49 +- .../20_What_Can_Tina_Do.ipynb | 143 +-- .../Squares_and_Circles.py | 28 +- .../40_Check_In_Your_Code.ipynb | 369 +++---- .../30_Turtle_Tricks/10_Turtle_Tricks.py | 2 +- .../30_Turtle_Tricks/20_Turtle_Tricks.py | 2 +- .../30_Turtle_Tricks/30_Turtle_Tricks.py | 6 +- lessons/10_Turtles/40_Loops/10_Loops.ipynb | 647 +++++------- .../40_Loops/20_Loop_with_Turtle.py | 13 +- .../10_Variables.ipynb | 511 +++++----- .../20_Functions.ipynb | 578 +++++------ .../30_Efficient_Turtle.py | 4 +- .../10_More_Turtle_Programs.ipynb | 510 ++++------ .../20_More_Turtle_Programs.py | 2 +- .../30_More_Turtle_Programs.py | 6 +- .../40_More_Turtle_Programs.py | 2 +- lessons/10_Turtles/70_Projects/20_Tash_Me.py | 2 +- .../70_Projects/40_Tash_Me_Twirl.py | 2 +- .../80_Introducing_Lists/10_Lists.ipynb | 500 +++++----- .../80_Introducing_Lists/20_Color_Lines.py | 4 +- .../10_Turtles/80_Introducing_Lists/README.md | 2 +- .../10_Flaming_Ninja_Star.py | 55 +- .../90_Graphics_Projects/20_Crazy_Spiral.py | 4 +- .../90_Graphics_Projects/30_Pentagon_Crazy.py | 30 +- .../90_Graphics_Projects/40_Turtle_Spiral.py | 22 +- .../30_Loops/30_Looping_Through_Lists.ipynb | 944 +++++++++--------- 32 files changed, 2484 insertions(+), 2811 deletions(-) create mode 100644 CLAUDE.md create mode 100644 REVIEW.md 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/REVIEW.md b/REVIEW.md new file mode 100644 index 00000000..a0e481c8 --- /dev/null +++ b/REVIEW.md @@ -0,0 +1,269 @@ +# Review: remainder of 10_Turtles + +Delete any bullet you don't want applied, then hand the file back and I'll apply the rest. + +--- + +## lessons/10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py + +- **Technical** (line 14): `turtle.setup(600,600,0,0)` — add spaces after commas: + `turtle.setup(600, 600, 0, 0)`. + +--- + +## lessons/10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py + +- **Technical** (line 14): `turtle.setup(600,600,0,0)` — add spaces after commas. + +--- + +## lessons/10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py + +- **Technical** (line 16): `turtle.setup(600,600,0,0)` — add spaces after commas. +- **Voice** (line 9, docstring bullet): "Challenge yourself to experiment with different colors and positions!" — + drop exclamation: "Try different colors and positions." +- **Voice** (line 26): `# Dont forget to check in your code!` — fix "Dont" → "Don't", drop exclamation, or + rewrite as `# Save your progress by checking in your code.` + +--- + +## lessons/10_Turtles/40_Loops/10_Loops.ipynb + +- **Voice** (cell 0, opening): "Welcome to the world of loops!" plus the rest of the paragraph oversells. + Rewrite the whole opening as: "Loops 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." +- **Voice** (cell 0, Tip): "mastering them will greatly enhance your coding skills" — drop the tip or soften to + "Loops show up in every programming language, so it's worth taking time to get comfortable with them." +- **Voice** (cell 1, paragraph 1): "A loop will let you tell Tina to do something multiple times by using a simple + command!" — drop exclamation. +- **Voice** (cell 1, bullet intro): "Loops are super useful because they help us:" — drop "super". +- **Voice** (cell 6): "That's a lot of repetitive code!" — drop exclamation. +- **Voice** (cell 6): "Maybe we can even add more shapes without making the code super long!" — drop "super" and + the exclamation: "We can even add more shapes without making the code much longer." +- **Voice** (cell 9, paragraph 1): "Using loops can make your code much cleaner and easier to understand!" — drop + exclamation. +- **Voice** (cell 9, Tip): "Take your time to practice and experiment with them—understanding loops will help you + solve problems more efficiently and write better code!" — drop the tip; the same point is made in cell 0. +- **Voice** (cell 10): "Try running these examples to see how loops can be used in different ways!" — drop + exclamation. +- **Voice** (cell 12, Note): "Try changing the value of size to see how the output changes!" — drop exclamation. +- **Voice** (cell 15, Tip): "Nested loops are great for creating patterns like checkerboards." — fine, keep. No + change needed. +- **Voice** (cell 19, closing line): "open the file `20_Loop_with_Turtle.py`, and try running it!" — drop the + exclamation. +- **Technical** (cell 8, code): `tina.speed('fastest')` has extra whitespace before the aligned comment. Tighten. + +--- + +## lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py + +- **Technical** (line 16): `turtle.setup(600,600,0,0)` — add spaces after commas. +- **Technical** (line 21): `tina.speed(2) # Make the turtle move as fast, but not too fast. ` — the comment is + awkwardly worded and has a trailing space. Rewrite: `# Move at a moderate speed, not too fast.` +- **Technical** (line 26): `tina.forward(150) # Continue the last two steps three more times` — same bug as + Squares_and_Circles.py: comment sits in the second iteration, so "three more times" is wrong. Move the + explanatory comment above the whole block or rewrite as `# Repeat forward + left three more times to finish + the square.` + +--- + +## lessons/10_Turtles/40_Loops/30_Loop_with_Turtle.py + +- No findings. + +--- + +## lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb + +- **Voice** (cell 4, closing line): "Try changing sides to different numbers and watch how angle changes! 🎯" — + drop the exclamation and the 🎯 emoji. +- **Voice** (cell 5, opening): "You may not have noticed, but you have been working with variables all along!" — + drop the exclamation. +- **Voice** (cell 7, paragraph 1): "This shows the power of variables—they let you write code that adapts + automatically!" — drop the exclamation. +- **Voice** (cell 7, closing): "This makes your code flexible and easy to experiment with!" — drop the exclamation. +- **Voice** (cell 9, final comment): "Try changing the numbers, text, or boolean values and see what happens!" — + drop the exclamation. +- **Voice** (cell 11, final comment): "Try changing the number of pizzas, slices_per_pizza, or people, and see + what happens!" — drop the exclamation. +- **Technical** (cell 15, exercises): each blank line like `height_difference = ` is a SyntaxError if run. Put + `...` or `0 # TODO` after each bare `=` so the cell at least parses before students fill it in. Example: + `height_difference = ... # Complete this calculation using variables`. +- **Scope** (cell 8): lists dictionaries, sets, and NoneType. PCEP covers dicts and sets at a basic level, so fine + as a name-drop. No change needed — noting only because the rubric asks to flag scope. + +--- + +## lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb + +- **Voice** (cell 0, closing line): "By the end, you'll be able to write your own reusable code blocks to perform + calculations, make decisions, and more!" — drop the exclamation. +- **Voice** (cell 3, final comment): "Try changing the parameters to see how the angle changes or create new + shapes!" — drop the exclamation. +- **Voice** (cell 11, final comment): "Try changing the numbers to see different results!" — drop the exclamation. +- **Voice** (cell 13, `pizza_calculator`): `# Return both values at once!` — drop the exclamation. +- **Voice** (cell 13, final comment): "Try changing the inputs to see different results!" — drop the exclamation. +- **Voice** (cell 14, opening): "Now let's put our function knowledge to work with Tina!" — drop the exclamation. +- **Voice** (cell 15, closing): "See how functions make our code cleaner and easier to reuse? Try calling the + functions with different parameters!" — drop the exclamation. +- **Voice** (cell 16, opening): "Now it's your turn!" — drop the line; the "Function Challenges" heading already + signals this. +- **Flow** (cell 13): `print(f"For 10 people: need {slices} slices and {pizzas} pizzas")` uses an f-string, which + hasn't been introduced. Swap for comma-separated `print`: `print("For 10 people: need", slices, "slices and", + pizzas, "pizzas")`. + +- **Technical** (cell 13, `pizza_calculator`): `pizzas_needed = -(-total_slices_needed // 8)` is a cryptic ceiling- + division trick. Replace with `from math import ceil` at the top and `pizzas_needed = ceil(total_slices_needed / + 8)`, which is readable. + +--- + +## lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py + +- **Technical** (line 12): `turtle.setup(600,600,0,0)` — add spaces after commas. +- **Technical** (line 17): `tina.speed(2) # Make the turtle move as fast, but not too fast.` — same awkward + wording. Rewrite as `# Move at a moderate speed, not too fast.` + +--- + +## lessons/10_Turtles/60_More_Turtle_Programs/10_More_Turtle_Programs.ipynb + +- **Voice** (cell 0, heading): "Turtle Tricks and Tips" — the folder and file name are "More_Turtle_Programs"; + either rename the heading to match ("More Turtle Programs") or accept the mismatch. I'd align the heading. +- **Technical** (cell 0): `https://docs.python.org/3.14/library/turtle.html` — Python 3.14 docs may not exist yet + and the iframe below points to `/3/`. Change the link to `/3/library/turtle.html` to match. +- **Technical** (cell 2, example code): the demo loop `for i in range(4): t.goto(200, 200); t.goto(-200, -200)` + just bounces the turtle back and forth across a diagonal, which doesn't show off a custom-image turtle doing + anything interesting. Consider changing to a square pattern (goto each of 4 corners in sequence) so the demo + matches what the follow-up `.py` asks the student to write. +- **Technical** (cells 7, 9, 11): `import turtle as turtle` is a redundant alias. Change to plain `import turtle`. +- **Voice** (cell 13, Tip): "If you haven't checked in your code now would be a great time to do so!" — drop the + exclamation: "If you haven't checked in your code, now's a good time." + +--- + +## lessons/10_Turtles/60_More_Turtle_Programs/20_More_Turtle_programs.py + +- **Technical** (filename): lowercased `programs` is inconsistent with every other file and the folder + (`More_Turtle_Programs`). Rename the file to `20_More_Turtle_Programs.py`. +- **Technical** (line 2, docstring): references `10_More_Turtle_programs.ipynb` (lowercase p) but the actual file + is `10_More_Turtle_Programs.ipynb`. Fix the casing. + +--- + +## lessons/10_Turtles/60_More_Turtle_Programs/30_More_Turtle_Programs.py + +- **Technical** (line 2, docstring): references `10_More_Turtle_programs.ipynb` (lowercase p) — fix casing. +- **Flow** (docstring lines 5–6): the instructions tell the student to "move to the corners of the screen in a + square pattern", but this lesson is for the **background image** section, not the turtle-image section. + "Moves to the corners in a square pattern" is leftover copy-paste from `20_`. Rewrite to describe what the + background-image exercise actually asks for — e.g., "Set the background image, then draw a shape on top of it + with your turtle." + +--- + +## lessons/10_Turtles/60_More_Turtle_Programs/40_More_Turtle_Programs.py + +- **Technical** (line 2, docstring): references `10_More_Turtle_programs.ipynb` (lowercase p) — fix casing. + +--- + +## lessons/10_Turtles/70_Projects/10_LeagueBot.py + +- No findings. + +--- + +## lessons/10_Turtles/70_Projects/20_Tash_Me.py + +- **Technical** (line 9, Hint): the hint is cut off mid-sentence: "See the `10_More_Turtle_Programs` section + labeled 'Change the Background Image' and" — the sentence ends on "and". Drop the trailing "and" or finish the + thought. +- **Technical** (line 9, Hint): the notebook section is actually titled "Set a Background Picture", not "Change + the Background Image". Update the reference. + +--- + +## lessons/10_Turtles/70_Projects/30_Tash_Me_Click.py + +- No findings. + +--- + +## lessons/10_Turtles/70_Projects/40_Tash_Me_Twirl.py + +- **Technical** (line 6, Hint): references "section 'Respond to Screen Clicks'", but this exercise is about + clicking the *turtle*, not the screen. Change the reference to "Clicking The Turtle Directly". + +--- + +## lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb + +- **Technical** (folder name): `80_Introducting_Lists` is misspelled — should be `80_Introducing_Lists`. Flagging + separately since renaming a folder is a bigger change than an in-file edit; let me know if you want this. +- **Technical** (cell 13, heading): "Excercises (Optional)" — typo, should be "Exercises (Optional)". +- **Scope** (cell 8): mentions "comprehensions" as something lists can do. PCEP scope says comprehensions are out, + and we're mentioning them only as a "you can also do this" aside, which is fine, but for consistency consider + dropping "comprehensions" from the list so students don't try to look them up mid-lesson. +- **Technical** (cell 12, code): `for left in [90, 90, 90, 90]:` shadows the outer `left = 90` variable (OK here, + but noting it since the lesson is new to most students). Consider renaming the loop variable to `angle`. + +--- + +## lessons/10_Turtles/80_Introducting_Lists/20_Color_Lines.py + +- **Technical** (line 8): `turtle.setup(600,600,0,0)` — add spaces after commas. +- **Technical** (line 13): trailing whitespace after `too fast.` — remove. +- **Technical** (line 13): "Make the turtle move as fast, but not too fast." — same awkward "as fast" wording. + Rewrite as `# Move at a moderate speed, not too fast.` + +--- + +## lessons/10_Turtles/90_Graphics_Projects/10_Flaming_Ninja_Star.py + +- **Technical** (naming): `getRandomColor`, `getNextColor`, `baseSize`, `flameSize` are camelCase. PEP 8: use + snake_case for functions and variables. Rename to `get_random_color`, `get_next_color`, `base_size`, + `flame_size` (and update call sites). +- **Technical** (line 26): `turtle.setup(600,600,0,0)` — add spaces after commas. +- **Technical** (line 32): `t = turtle.Turtle()` has a trailing space — remove. +- **Technical** (lines 40–61): each statement inside the `for` loop is separated by a blank line, which is + unusual. Tighten by removing the blank lines between related calls so the loop body reads as one block. + +--- + +## lessons/10_Turtles/90_Graphics_Projects/20_Crazy_Spiral.py + +- **Scope** (line 26, instructions): "or it could use islice(), cycle(), or a list of numbers." — `islice()` and + `cycle()` are out of scope (from `itertools`). Drop those two: "for example 100, or a list of numbers." + +--- + +## lessons/10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py + +- **Technical** (naming): `getRandomColor`, `getNextColor`, `myTurtle` are camelCase. Rename to + `get_random_color`, `get_next_color`, `my_turtle` (PEP 8). +- **Technical** (lines 13–17): `getNextColor` references the global `colors` at call time, but `colors` is + defined below the function (line 23). It works because Python looks up globals lazily, but it's confusing for a + student reading top-to-bottom. Move the `colors = (...)` tuple above the function definitions. + +--- + +## lessons/10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py + +- **Technical** (naming): `getRandomColor`, `myTurtle` are camelCase. Rename to `get_random_color`, `my_turtle` + (PEP 8). +- **Voice** (line 47): `# Check the pattern against the picture in the recipe. If it matches, you are done!` — + drop the exclamation. +- **Voice** (line 51): `# Now check in your code!` — drop the exclamation. + +--- + +## lessons/10_Turtles/Module_One_Quiz.ipynb + +- No findings. + +--- + +## lessons/10_Turtles/README.md + +- No findings. diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index f2b95c79..02efef0a 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -77,8 +77,8 @@ modules: - 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 @@ -95,14 +95,14 @@ 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 diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index fd822b2b..76b718a2 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -1,77 +1,39 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Getting Started with Python**\n", - "\n", - "Hello future coders, programmers, and engineers! Welcome to your first **Python** lesson with the *League of Amazing Programmers*. We're super excited to help you take your first steps into becoming an amazing programmer! \n", - "\n", - "To 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", - "\n", - "We'll start by learning **turtle programming**, and you'll get to give commands to a virtual turtle, telling it where to move on the screen. It's almost like having a little robot artist 🤖 that you can control with code. To do this, we'll use a pre-installed Python tool called the **turtle module**. \n", - "\n", - "You can tell your turtle 🐢 to move forward ↑, turn left ← or ➝ right, and change the color 🎨 of the lines it draws 🖍️— Soon, you'll be able to create awesome pictures like this one!\n", - "\n", - "
                  \n", - "\n", - "
                  \n", - " \"A\n", - "
                  \n", - "\n", - "
                  \n", - "

                  Let's Get Set Up!

                  \n", - "

                  Before we can start drawing, we need to set up your coding environment. Don't worry if anything seems confusing—that's what your instructors are here for! Feel free to ask for help if you get stuck.

                  \n", - "

                  Ready to go? Click the blue Next Lesson button at the bottom-left of your screen to continue! 🚀

                  \n", - "\n", - "
                  \n", - "

                  Course Symbols

                  \n", - " There are various symbols used throughout this course to help you identify important information quickly without it being too confusing. So, take a moment to familiarize yourself with them.\n", - "
                    \n", - "
                  • Tooltip — These are small pop-up boxes that appear when you hover your mouse cursor over specific text or icons. You will find these helpful guides throughout the course, providing instant definitions and explanations for key terms to help you learn.
                  • \n", - "
                  • Important — These highlight critical concepts, rules, or warnings that are essential for your success. Pay close attention to these points, as they often contain information you'll need to remember for future lessons and challenges.
                  • \n", - "
                  • Bookmarks — These sections provide additional details, background information, or interesting facts about a topic. While they aren't required to complete the lesson, exploring them can help deepen your understanding and give you a bigger picture of how things work.
                  • \n", - "
                  \n", - "
                  WARNING: If you skip any of these symbols it is highly likely you might get confused!
                  \n", - "
                  " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ">**Note:** Please take your time to **read each lesson carefully**. This isn't a race to the finish line! To get the most out of this course, make sure to study the code examples, experiment with them, and ask questions. Rushing through the material without practicing can make things confusing, so pace yourself and enjoy the learning process." - ] - } - ], - "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" - } + "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 6bc95888..8469f2c5 100644 --- a/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb +++ b/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb @@ -1,49 +1,34 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Let's Open Your Virtual Screen!**\n", - "\n", - "Ready to run your code and see what it does?\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 just need to open a virtual screen. Don't worry, this part is easy.\n", - "\n", - "Most of the time, this screen will open automatically. If for some reason it doesn't appear, just 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, just click on the connect button!\n", - "
                  \n", - " \"Virtual\n", - "
                  \n", - "\n", - "With your virtual screen open, you are now one step closer to interacting with your turtle!" - ] - } - ], - "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": { - "uid": "KmgIQbhr", - "name": "Open The Screen" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": "# **Let's Open Your Virtual Screen**\n\nTo 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\nMost 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\nYou should now be looking at something like the image below. Click the *connect* button.\n
                  \n \"Virtual\n
                  " + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 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.5" + }, + "syllabus": { + "name": "Open The Screen", + "uid": "KmgIQbhr" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } \ No newline at end of file diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index b4014715..fb61aaab 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -1,201 +1,94 @@ { - "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! Just move your mouse over the code cell and click the ▶️ button." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "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 = f\"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. Try it out!\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. Don’t worry! Technically something did, you just were not able to 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 all you need to do is 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", - "**Try it yourself!**\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. Now all you have to do is press the play button and 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", - "Running `.py` files might seem new or even a little overwhelming at first, but don’t worry—you’ll get the hang of it quickly! In the next lesson, you’ll try running a Python program called `Meet_Tina.py`. There are a few different ways to do this, and you can use whichever feels easiest for you. Let’s walk through each option together!\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", - "Don’t worry about all the buttons for now! You just need to know that you can press the red square to stop your program, and the green circle to run it again.\n", - "\n", - "> **Tip:** Remember, it’s totally normal to feel unsure at first. Take your time, try out each method, and soon running Python programs will feel easy!\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
                  \n", - "\n", - "WARNING: If a program is already running, you must close the program before starting another one.\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", - "\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": ".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": "Run Programs", - "uid": "cNLK6qtR" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": "# **Running Programs**\n\nYou 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\nThis 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\nTry running the code below. Move your mouse over the cell and click the ▶️ button." }, - "nbformat": 4, - "nbformat_minor": 4 -} + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "import datetime\n\n# Get the current date\ndate = datetime.date.today()\n\n# Make a string with a message and the date\ns = \"Hello 👋 World 🌎! Today is\"\n\n# Print the string and the date\nprint(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\nThe 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\nLook at the top of Visual Studio Code for a box that looks like this , and click on it. \n\nThis should have made a dropdown appear in the top-middle of your editor, as shown below:\n\n\n\nNow 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\n1. Copy the Hello World code from above into the cell below, or type `print(\"Hello World!\")`
                  \n2. Change the string inside the quotation marks so that it also prints your name (e.g., change \"Hello World!\" to \"Your Name\").
                  \n3. 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\nIn 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\nThe Lesson Browser is a simple way to run `.py` programs in this course.\n\nIn the bottom-left, you'll see buttons like this:\n\n\n\nHere'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\nYou can also run a `.py` program by clicking the ▶️ button in your editor.\n\n1. Click the file name to open it.\n2. In the top right, look for these icons and click the ▶️ run button.\n3. When you're done, just close the window.\n\n### **Using the Debugger**\n\nIf 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\nYou'll see a debug bar like this:\n\n\n\nYou 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\nIf a program is already running, close it before starting another.\n\nYou 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": ".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": "Run Programs", + "uid": "cNLK6qtR" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file 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 1648e708..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 @@ -7,11 +7,13 @@ 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). -Don't worry if you don't understand all the code yet—future lessons will explain everything step by step! +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 @@ -25,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 @@ -67,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 @@ -94,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/20_What_Can_Tina_Do.ipynb b/lessons/10_Turtles/20_Introducing_Tina/20_What_Can_Tina_Do.ipynb index 500ce8c1..40949815 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,103 +1,44 @@ { - "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. Don’t get intimidated—Tina’s magic 🧙‍♂️ is actually very simple and can be broken down into basic 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", - "But before we move ahead, let’s break down how this magic ✨ 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." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **Additional Help**\n", - "\n", - "Learning to use documentation is a superpower for programmers! It helps you find commands, learn new tricks, and solve problems 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 cool new things Tina can do!\n", - "* **Fix Errors:** Find out why your code might not be working and how to fix it.\n", - "\n", - "> **Tip:** Don't be afraid to copy example code and change it to see what happens. Experimenting is the best way to learn!\n", - "\n", - "
                  Official Turtle Documentation\n", - "
                  \n", - " \n", - "
                  " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## **Assignment**\n", - "\n", - "Take a look at the steps below, and when you’re ready, move on to the Next Lesson and keep exploring what Tina can do!\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.\n", - "\n", - "> **Tip:** If you’re not sure what a command does, try changing it and running the program again. Experimentation is a great way to learn!" - ] - } - ], - "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": "What Can Tina Do", - "uid": "7tUP3zAZ" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": "# **What Can Tina Do?**\n\nIn 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\nIn 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\nBefore moving on, here's how it works.\n\n### **How Tina Follows Commands**\n\nHere is what some of the program will look like with comments to explain what each line does:\n\n```python\ntina = turtle.Turtle() # Make a turtle named tina\ntina.pencolor('blue') # Change tina's pen color to blue\ntina.forward(200) # Move tina forward by 200 steps\ntina.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\nReading 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\nRead the steps below, then move on to the Next Lesson.\n\n1. **Run the program** called `Squares_and_Circles.py` to see what Tina draws.\n2. **Read the code** for each line, and try to guess what Tina will do (The comments will help!).\n3. **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.\n4. **Run the program again** and see how Tina's drawing changes." + } + ], + "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": "What Can Tina Do", + "uid": "7tUP3zAZ" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ 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 2a225df2..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 @@ -10,12 +10,12 @@ """ 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 @@ -27,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 left by the left 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) @@ -49,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) @@ -64,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, 40_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 3a0fa428..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,184 +1,189 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Check In Your Code**\n", - "\n", - "Now that you\u2019ve created a few programs, it\u2019s important to save your progress so you don\u2019t 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\u2014along with any unsaved changes. So, don\u2019t risk losing your hard work and always make sure you frequently check in your code! \n", - "\n", - "### **How To Make Changes**\n", - "\n", - "Let\u2019s make a change to `Meet_Tina.py` so you have something to check in. \n", - "\n", - "You can change anything you like, whether you make the circle a different size, or change what Tina says!\n", - "\n", - "**For Example**\n", - "```python\n", - " tina.circle(100, steps=50) # Change the circle\u2019s size\n", - " tina.write(\"Why, hello there!\") # Modify what Tina says\n", - "```\n", - "\n", - "> **Tip:** After making your change, remember to save your file! You can do this quickly by pressing Ctrl + S (Windows), or Cmd + S (Mac)." - ] - }, - { - "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\u2019ll 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 \u2713 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", - " \ud83d\udd04\ufe0e Sync Changes 1 \ud83e\udc51\n", - "\n", - "\n", - "*Click the Sync button* to safely upload your changes to your GitHub repository.\n", - "\n", - "Now your work is safely stored in your GitHub account! \ud83c\udf89\n", - "\n", - "> **Tip:** Commit your code often\u2014after 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 \u2713 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 \u2612 (on the file tab), return to Source Control, enter your commit message, and click the \u2713 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\u2019re 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\u2019t, 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\u2019t have that window open anymore, it\u2019s 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 \u2bc6 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' \ud83e\udd66 or 'organic spoon' \ud83e\udd44 to you help 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" - ] - } - ], - "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": { - "uid": "doD6P7fk", - "name": "Check In Your Code" - } + "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 -} \ No newline at end of file + { + "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 a137db93..0cd83f93 100644 --- a/lessons/10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py +++ b/lessons/10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py @@ -11,7 +11,7 @@ # 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 b8b73c26..29f39316 100644 --- a/lessons/10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py +++ b/lessons/10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py @@ -11,7 +11,7 @@ # 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 8d44889f..0d4ae1b2 100644 --- a/lessons/10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py +++ b/lessons/10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py @@ -6,14 +6,14 @@ - 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. -- Challenge yourself to experiment with different colors and positions! +- 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 @@ -23,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 55606821..7ebbe6d8 100644 --- a/lessons/10_Turtles/40_Loops/10_Loops.ipynb +++ b/lessons/10_Turtles/40_Loops/10_Loops.ipynb @@ -1,372 +1,279 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Loops**\n", - "\n", - "Welcome to the world of **loops**! In this lesson, you'll discover how loops make programming easier and more powerful by allowing you to automate repetitive tasks, save time, write cleaner code, and solve problems more efficiently. By pairing loops with *Tina the Turtle*, you'll be able to simplify your programs, reduce mistakes, and create amazing things with less effort!\n", - "\n", - "> **Tip:** Take your time to practice using loops. They are a core concept in every programming language, and mastering them will greatly enhance your coding skills." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Why Use Loops?**\n", - "\n", - "Instead 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", - "\n", - "Loops are super 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", - "\n", - "Let’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", - "\n", - "Let’s see how we can use loops to make this code cleaner and easier to manage. Maybe we can even add more shapes without making the code super long!" - ] - }, - { - "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\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 fast, but not too fast \n", - "\n", - "# Draw a square using a loop\n", - "for i in range(4): # Loop 4 times\n", - " tina.forward(100) # Move forward 100 units\n", - " tina.left(90) # Turn left 90 degrees\n", - "\n", - "tina.penup() # Lift the pen to move without drawing\n", - "tina.goto(125, 0) # Move tina to over to the right of the canvas\n", - "tina.pendown() # Put the pen down to start drawing\n", - "\n", - "# Draw a triangle using a loop\n", - "for 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\n", - "tina.penup() # Lift the pen to move without drawing\n", - "tina.goto(260, 50) # Move tina further to the right of the canvas\n", - "tina.pendown() # Put the pen down to start drawing\n", - "\n", - "tina.speed('fastest') # Set speed to fastest for a faster star drawing\n", - "\n", - "# Draw a star pattern using a loop\n", - "for 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 can make your code much cleaner and easier to understand!\n", - "\n", - "Here'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", - "\n", - "For 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\n", - "for 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", - "```\n", - "\n", - "> **Tip:** Loops are a core concept in every programming language. Take your time to practice and experiment with them—understanding loops will help you solve problems more efficiently and write better code!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **Practicing with Loops**\n", - "\n", - "Try running these examples to see how loops can be used in different ways!\n", - "\n", - "### **Drawing an Orange Square**\n", - "\n", - "This 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" - } + "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." }, - "nbformat": 4, - "nbformat_minor": 2 -} + { + "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 3df09fbe..e4651fcb 100644 --- a/lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py +++ b/lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py @@ -13,18 +13,19 @@ """ 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) @@ -32,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/50_Variables_and_Functions/10_Variables.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb index 1ab6d2ef..a15f0fae 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb @@ -1,289 +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", - "\n", - "If 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", - "\n", - "You may not have noticed, but you have been working with variables all along!\n", - "\n", - "Let'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", - "\n", - "For 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", - "\n", - "With 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)\n", - "my_age = 12 # This is an integer (a whole number)\n", - "temperature = 98.6 # This is a float (a fractional number)\n", - "\n", - "# Text (strings)\n", - "my_name = \"Alice\" \n", - "favorite_color = \"blue\"\n", - "\n", - "# Boolean values (True/False)\n", - "is_sunny = True\n", - "is_raining = False\n", - "\n", - "print(\"Name:\", my_name)\n", - "print(\"Age:\", my_age)\n", - "print(\"Temperature:\", temperature)\n", - "print(\"Favorite color:\", favorite_color)\n", - "print(\"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\n", - "pizzas = 5 # Number of pizzas ordered for the party\n", - "slices_per_pizza = 8 # Number of slices in each pizza\n", - "people = 12 # Number of people sharing the pizzas\n", - "\n", - "# Now it's time for a little bit of math\n", - "total_slices = pizzas * slices_per_pizza # Multiply to get total slices\n", - "slices_per_person = total_slices / people # Divide to find slices each person gets\n", - "\n", - "# Finally, we can print our results\n", - "print(\"We have\", pizzas, \"pizzas\") # Show how many pizzas there are\n", - "print(\"Each pizza has\", slices_per_pizza, \"slices\") # Show slices per pizza\n", - "print(\"Total slices:\", total_slices) # Show total number of slices\n", - "print(\"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" - } + "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" + ] }, - "nbformat": 4, - "nbformat_minor": 2 -} + { + "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/20_Functions.ipynb b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb index 6e884bed..081fed9c 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb +++ b/lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb @@ -1,356 +1,226 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Functions**\n", - "\n", - "In 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\n", - "def 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\n", - "triangle_angle = calculate_angle(3) # Call the function with 3 sides (triangle)\n", - "square_angle = calculate_angle(4) # Call the function with 4 sides (square) \n", - "hexagon_angle = calculate_angle(6) # Call the function with 6 sides (hexagon)\n", - "\n", - "# Print the results\n", - "print(\"Triangle angle:\", triangle_angle)\n", - "print(\"Square angle:\", square_angle)\n", - "print(\"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", - "\n", - "def 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", - "\n", - "def multiply_numbers(a, b): # Multiplies two numbers and returns the product\n", - " return a * b # Return the product\n", - "\n", - "def 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", - "\n", - "sum_result = add_numbers(10, 5) # Store the sum of 10 and 5\n", - "product = multiply_numbers(4, 7) # Store the product of 4 and 7\n", - "room_area = calculate_area(12, 10) # Store the area of a 12×10 room\n", - "\n", - "# Print the results returned from the functions\n", - "\n", - "print(\"10 + 5 =\", sum_result)\n", - "print(\"4 × 7 =\", product)\n", - "print(\"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", - "\n", - "# Decision function\n", - "def 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\n", - "def 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\n", - "def 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 = -(-total_slices_needed // 8) # Round up to nearest whole pizza (8 slices per pizza)\n", - " return total_slices_needed, pizzas_needed # Return both values at once!\n", - "\n", - "# Test functions and print results\n", - "weather_report = check_temperature(75) # Test temperature function with 75 degrees\n", - "print(weather_report)\n", - "\n", - "age_valid = is_valid_age(25) # Test age validation with 25 years old\n", - "print(\"Is age 25 valid?\", age_valid)\n", - "\n", - "slices, pizzas = pizza_calculator(10) # Calculate pizza needs for 10 people (uses default 3 slices each)\n", - "print(f\"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", - "\n", - "Now 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", - "\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", - "# Function to draw any polygon\n", - "def 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\n", - "def 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!\n", - "draw_polygon(3, 50, \"red\") # Draw a red triangle\n", - "move_tina(100, 0) # Move to a new spot\n", - "draw_polygon(4, 60, \"blue\") # Draw a blue square\n", - "move_tina(225, 0) # Move again\n", - "draw_polygon(6, 40, \"green\") # Draw a green hexagon\n", - "\n", - "# See how functions make our code cleaner and easier to reuse?\n", - "# Try calling the functions with different parameters!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **Function Challenges**\n", - "\n", - "Now it's your turn! Write 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" - } + "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" }, - "nbformat": 4, - "nbformat_minor": 2 -} + { + "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/50_Variables_and_Functions/30_Efficient_Turtle.py b/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py index 8b92b111..bcf33089 100644 --- a/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py +++ b/lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py @@ -9,12 +9,12 @@ """ 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. def draw_polygon(sides): 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 4719048b..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,330 +1,188 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Turtle Tricks and Tips**\n", - "\n", - "Although we've used a lot of Turtle examples so far, the turtle module has many more\n", - "features. The official Python documentation lists everything the turtle can do.\n", - "\n", - "A 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", - "\n", - "Check 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", - "\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 # 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\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() # Prevent drawing when moving\n", - "t.speed(3) # Set a moderate speed\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": [ - "> **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", - "\n", - "import turtle as turtle # Import the turtle module with the name turtle\n", - "\n", - "screen = turtle.Screen() # Set up the screen\n", - "screen.setup(width=600, height=600) # Set the size of the window\n", - "screen.bgcolor('white') # Set the background color\n", - "\n", - "t1 = turtle.Turtle() # Create the first turtle\n", - "t1.penup() # Lift the pen to move without drawing\n", - "t1.shape(\"turtle\") # Set the shape of the turtle\n", - "\n", - "t2 = turtle.Turtle() # Create the second turtle\n", - "t2.penup() # Lift the pen to move without drawing\n", - "t2.shape(\"arrow\") # Set the shape of the turtle\n", - "\n", - "# Move both turtles in a loop\n", - "for 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", - "\n", - "import turtle as turtle\n", - "\n", - "screen = turtle.Screen() # Set up the screen\n", - "screen.setup(width=600, height=600) # Set the size of the window\n", - "screen.bgcolor('white') # Set the background color\n", - "\n", - "t = turtle.Turtle() # Create a turtle\n", - "t.penup() # Prevent drawing when moving\n", - "t.shape(\"turtle\") # Set the shape of the 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) # Move the turtle to the clicked location\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": [ - "## **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", - "\n", - "import turtle as turtle # Import the turtle module with the name turtle\n", - "\n", - "screen = turtle.Screen() # Set up the screen\n", - "screen.setup(width=600, height=600) # Set the size of the window\n", - "screen.bgcolor('white') # Set the background color\n", - "\n", - "t = turtle.Turtle() # Create a turtle\n", - "t.shape(\"turtle\") # Set the shape of the 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", - "
                  \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 would be a great time to do so! 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" - }, - "syllabus": { - "uid": "IloYptI2", - "name": "More Turtle Programs" + "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 index 8cd8ec77..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,5 +1,5 @@ """ -Copy the code from the previous lesson, 10_More_Turtle_programs.ipynb, +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' 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 c82fd4e3..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,8 +1,8 @@ """ -Copy the code from the previous lesson, 10_More_Turtle_programs.ipynb, +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 has a different image ( look in the 'images' -directory ) and moves to the corners of the screen in a square pattern. +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. """ 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 c9486db8..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,5 +1,5 @@ """ -Copy the code from the previous lesson, 10_More_Turtle_programs.ipynb, +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' diff --git a/lessons/10_Turtles/70_Projects/20_Tash_Me.py b/lessons/10_Turtles/70_Projects/20_Tash_Me.py index 9ed6c36b..9eec93b9 100644 --- a/lessons/10_Turtles/70_Projects/20_Tash_Me.py +++ b/lessons/10_Turtles/70_Projects/20_Tash_Me.py @@ -6,7 +6,7 @@ 2) Make the turtle shape a moustache 3) Move the moustache to the right spot on the emoji -Hint: See the `10_More_Turtle_Programs` section labeled 'Change the Background Image' and +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/40_Tash_Me_Twirl.py b/lessons/10_Turtles/70_Projects/40_Tash_Me_Twirl.py index f593cef6..8e648600 100644 --- a/lessons/10_Turtles/70_Projects/40_Tash_Me_Twirl.py +++ b/lessons/10_Turtles/70_Projects/40_Tash_Me_Twirl.py @@ -3,7 +3,7 @@ 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 10_More_Turtle_Programs, section 'Respond to Screen Clicks' +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 index 5367b683..58115acc 100644 --- a/lessons/10_Turtles/80_Introducing_Lists/10_Lists.ipynb +++ b/lessons/10_Turtles/80_Introducing_Lists/10_Lists.ipynb @@ -1,262 +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, comprehensions, nested\n", - "lists, and more), but 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", - "\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", - "\n", - "for left in [90, 90, 90, 90]:\n", - " tina.forward(forward)\n", - " tina.left(left)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **Excercises (Optional)**\n", - "\n", - "If 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" - } + "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:" + ] }, - "nbformat": 4, - "nbformat_minor": 2 -} + { + "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_Introducing_Lists/20_Color_Lines.py b/lessons/10_Turtles/80_Introducing_Lists/20_Color_Lines.py index 39444f04..07af8e75 100644 --- a/lessons/10_Turtles/80_Introducing_Lists/20_Color_Lines.py +++ b/lessons/10_Turtles/80_Introducing_Lists/20_Color_Lines.py @@ -5,12 +5,12 @@ """ 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_Introducing_Lists/README.md b/lessons/10_Turtles/80_Introducing_Lists/README.md index 4e809e0b..1028c78a 100644 --- a/lessons/10_Turtles/80_Introducing_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/90_Graphics_Projects/10_Flaming_Ninja_Star.py b/lessons/10_Turtles/90_Graphics_Projects/10_Flaming_Ninja_Star.py index e727ca25..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,7 @@ """ # 10_Flaming_Ninja_Star.py -This program already works; run it to see what it does. +This program already works; run it to see what it does. Then change it to make it draw a different pattern. uid: ejUIkGvk @@ -13,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 +base_size = 200 # the size of the black part of the star +flame_size = 130 # the length of the flaming arms -t = turtle.Turtle() - -t.shape("turtle") - -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 c7fc826d..cbae6cd0 100644 --- a/lessons/10_Turtles/90_Graphics_Projects/20_Crazy_Spiral.py +++ b/lessons/10_Turtles/90_Graphics_Projects/20_Crazy_Spiral.py @@ -22,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 1c54825e..2b3e8753 100644 --- a/lessons/10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py +++ b/lessons/10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py @@ -10,35 +10,35 @@ 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 37d06dfa..246d1902 100644 --- a/lessons/10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py +++ b/lessons/10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py @@ -11,41 +11,41 @@ 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(9 * 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/30_Loops/30_Looping_Through_Lists.ipynb b/lessons/30_Loops/30_Looping_Through_Lists.ipynb index 02dd9782..014d05ad 100644 --- a/lessons/30_Loops/30_Looping_Through_Lists.ipynb +++ b/lessons/30_Loops/30_Looping_Through_Lists.ipynb @@ -1,481 +1,465 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "5a4acb70", - "metadata": {}, - "source": [ - "# **Looping Through Lists**\n", - "\n", - "We've introduced lists in a [previous lesson](../10_Turtles/80_Introducting_Lists/10_Lists.ipynb), but now, let's explore their power when combined with loops.\n", - "\n", - "As 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", - "\n", - "For example, consider a list representing the contents of a backpack:\n", - "\n", - "Backpack Contents:\n", - "- `map`\n", - "- `flashlight`\n", - "- `water bottle`\n", - "- `snack`\n", - "\n", - "In 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", - "\n", - "my_list = []\n", - "\n", - "# Add to the list using append\n", - "my_list.append('item 1')\n", - "my_list.append('item 2')\n", - "my_list.append('item 3')\n", - "\n", - "# You can also use = with an empty list to create a new list\n", - "my_list = my_list + ['item 4', 'item 5']\n", - "\n", - "print(my_list)" - ] - }, - { - "cell_type": "markdown", - "id": "ee522118", - "metadata": { - "lines_to_next_cell": 0 - }, - "source": [ - "### **Try It!**\n", - "Add 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 (myList) 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", - "\n", - "forward = 100\n", - "lefts = [ 90, 90, 90, 90, 90, 90, 90, 90 ]\n", - "colors = [ 'red', 'blue', 'black', 'orange', 'purple', 'pink', 'cyan', 'green']\n", - "\n", - "for 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", - "\n", - "Time to practice what you've learned!\n", - "\n", - "You 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 -} + "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", + "\n", + "my_list = []\n", + "\n", + "# Add to the list using append\n", + "my_list.append('item 1')\n", + "my_list.append('item 2')\n", + "my_list.append('item 3')\n", + "\n", + "# You can also use = with an empty list to create a new list\n", + "my_list = my_list + ['item 4', 'item 5']\n", + "\n", + "print(my_list)" + ] + }, + { + "cell_type": "markdown", + "id": "ee522118", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "### **Try It!**\n", + "Add 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 (myList) 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", + "\n", + "forward = 100\n", + "lefts = [ 90, 90, 90, 90, 90, 90, 90, 90 ]\n", + "colors = [ 'red', 'blue', 'black', 'orange', 'purple', 'pink', 'cyan', 'green']\n", + "\n", + "for 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", + "\n", + "Time to practice what you've learned!\n", + "\n", + "You 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 From d8b43758894d4a2d734e48a42970f2e8d4d618aa Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Thu, 16 Apr 2026 22:03:30 -0700 Subject: [PATCH 154/159] Refactor code structure for improved readability and maintainability --- REVIEW.md | 284 +--- .../10_Operators_and_Types.ipynb | 51 +- .../20_String_Operations.ipynb | 30 +- .../20_Types_and_Logic/30_Control_Flow.ipynb | 915 ++++++----- .../40_Working_With_Numbers.ipynb | 107 +- lessons/20_Types_and_Logic/60_Simple_Adder.py | 2 +- .../70_Infuriating_Calculator.py | 17 +- .../80_Code_Challenges.ipynb | 21 +- .../20_Types_and_Logic/Module_Two_Quiz.ipynb | 259 ++- lessons/20_Types_and_Logic/lib/badgers.py | 13 +- lessons/30_Loops/100_For_Loop_Gauntlet.ipynb | 818 +++++----- lessons/30_Loops/10_Iteration.ipynb | 62 +- lessons/30_Loops/110_FizzBuzz_Gui_Grid.py | 15 +- lessons/30_Loops/120_More_Iterables.ipynb | 1370 ++++++++-------- lessons/30_Loops/130_Iterable_Turtle.py | 4 +- lessons/30_Loops/140_More_Loops.ipynb | 6 +- lessons/30_Loops/150_Number_Guess.py | 5 +- lessons/30_Loops/20_Loops_with_Range.ipynb | 356 +++-- .../30_Loops/30_Looping_Through_Lists.ipynb | 57 +- lessons/30_Loops/40_Crazy_Tina.py | 6 +- lessons/30_Loops/50_Tuples.ipynb | 10 +- .../30_Loops/60_Indexing_and_Slicing.ipynb | 573 ++++--- lessons/30_Loops/80_Strings.ipynb | 14 +- lessons/30_Loops/Module_Three_Quiz.ipynb | 253 +-- .../10_Functions.ipynb | 30 +- .../20_Exceptions.ipynb | 16 +- .../30_Dicts_Sets.ipynb | 1185 +++++++------- .../40_Funny_Words_Db.py | 22 +- .../50_Splat_Comprehension.ipynb | 1387 ++++++++--------- .../40_Data_Structures_Func/60_Tic_Tac_Toe.py | 28 +- .../Module_Four_Quiz.ipynb | 12 +- lessons/40_Data_Structures_Func/README.md | 10 +- lessons/50_Projects/10_Hotel_Management.md | 10 +- lessons/50_Projects/20_Random_Walk.py | 8 +- .../Temp_Project_Ideas/30_ASCII_Art.ipynb | 60 +- pyproject.toml | 15 + requirements.txt | 3 +- uv.lock | 728 +++++++++ 38 files changed, 4330 insertions(+), 4432 deletions(-) create mode 100644 pyproject.toml create mode 100644 uv.lock diff --git a/REVIEW.md b/REVIEW.md index a0e481c8..0b9c9220 100644 --- a/REVIEW.md +++ b/REVIEW.md @@ -1,269 +1,77 @@ -# Review: remainder of 10_Turtles +# Review: 50_Projects Delete any bullet you don't want applied, then hand the file back and I'll apply the rest. --- -## lessons/10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py +## lessons/50_Projects/README.md -- **Technical** (line 14): `turtle.setup(600,600,0,0)` — add spaces after commas: - `turtle.setup(600, 600, 0, 0)`. +- No findings. (The file just has the uid and the heading "Projects".) --- -## lessons/10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py +## lessons/50_Projects/10_Hotel_Management.md -- **Technical** (line 14): `turtle.setup(600,600,0,0)` — add spaces after commas. +- **Voice** (line 5): "This is going to be your 1st program you write all by yourself" — informal "1st". Rewrite as + "This is the first program you'll write on your own." +- **Technical** (line 10): `(it's the triple quotes """ stuff goes inside """)` — the quotes in the file are + curly smart quotes (`“””`), not straight quotes (`"""`). Python doesn't accept smart quotes, so a student who + copies this verbatim will get a SyntaxError. Replace with straight triple-quote characters. +- **Voice** (line 46): "Have Fun!!!" — three stacked exclamation points. Rewrite as "Have fun." or drop. --- -## lessons/10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py +## lessons/50_Projects/20_Random_Walk.py -- **Technical** (line 16): `turtle.setup(600,600,0,0)` — add spaces after commas. -- **Voice** (line 9, docstring bullet): "Challenge yourself to experiment with different colors and positions!" — - drop exclamation: "Try different colors and positions." -- **Voice** (line 26): `# Dont forget to check in your code!` — fix "Dont" → "Don't", drop exclamation, or - rewrite as `# Save your progress by checking in your code.` +- **Technical** (line 35, docstring): "Fur instance" — typo, should be "For instance". +- **Technical** (line 44, comment): "You can make the turle move randomly" — typo, should be "turtle". +- **Technical** (line 52, example code in comment): `angle_index = random.randint(0, 4)` — off-by-one bug. A + student who copies this literally will get an `IndexError` when `angle_index == 4`, because `directions` has + only four elements (indices 0–3). `random.randint(0, 4)` is inclusive of both ends. Fix to + `random.randint(0, 3)`. --- -## lessons/10_Turtles/40_Loops/10_Loops.ipynb +## lessons/50_Projects/Temp_Project_Ideas/ -- **Voice** (cell 0, opening): "Welcome to the world of loops!" plus the rest of the paragraph oversells. - Rewrite the whole opening as: "Loops 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." -- **Voice** (cell 0, Tip): "mastering them will greatly enhance your coding skills" — drop the tip or soften to - "Loops show up in every programming language, so it's worth taking time to get comfortable with them." -- **Voice** (cell 1, paragraph 1): "A loop will let you tell Tina to do something multiple times by using a simple - command!" — drop exclamation. -- **Voice** (cell 1, bullet intro): "Loops are super useful because they help us:" — drop "super". -- **Voice** (cell 6): "That's a lot of repetitive code!" — drop exclamation. -- **Voice** (cell 6): "Maybe we can even add more shapes without making the code super long!" — drop "super" and - the exclamation: "We can even add more shapes without making the code much longer." -- **Voice** (cell 9, paragraph 1): "Using loops can make your code much cleaner and easier to understand!" — drop - exclamation. -- **Voice** (cell 9, Tip): "Take your time to practice and experiment with them—understanding loops will help you - solve problems more efficiently and write better code!" — drop the tip; the same point is made in cell 0. -- **Voice** (cell 10): "Try running these examples to see how loops can be used in different ways!" — drop - exclamation. -- **Voice** (cell 12, Note): "Try changing the value of size to see how the output changes!" — drop exclamation. -- **Voice** (cell 15, Tip): "Nested loops are great for creating patterns like checkerboards." — fine, keep. No - change needed. -- **Voice** (cell 19, closing line): "open the file `20_Loop_with_Turtle.py`, and try running it!" — drop the - exclamation. -- **Technical** (cell 8, code): `tina.speed('fastest')` has extra whitespace before the aligned comment. Tighten. - ---- - -## lessons/10_Turtles/40_Loops/20_Loop_with_Turtle.py - -- **Technical** (line 16): `turtle.setup(600,600,0,0)` — add spaces after commas. -- **Technical** (line 21): `tina.speed(2) # Make the turtle move as fast, but not too fast. ` — the comment is - awkwardly worded and has a trailing space. Rewrite: `# Move at a moderate speed, not too fast.` -- **Technical** (line 26): `tina.forward(150) # Continue the last two steps three more times` — same bug as - Squares_and_Circles.py: comment sits in the second iteration, so "three more times" is wrong. Move the - explanatory comment above the whole block or rewrite as `# Repeat forward + left three more times to finish - the square.` - ---- - -## lessons/10_Turtles/40_Loops/30_Loop_with_Turtle.py - -- No findings. - ---- - -## lessons/10_Turtles/50_Variables_and_Functions/10_Variables.ipynb - -- **Voice** (cell 4, closing line): "Try changing sides to different numbers and watch how angle changes! 🎯" — - drop the exclamation and the 🎯 emoji. -- **Voice** (cell 5, opening): "You may not have noticed, but you have been working with variables all along!" — - drop the exclamation. -- **Voice** (cell 7, paragraph 1): "This shows the power of variables—they let you write code that adapts - automatically!" — drop the exclamation. -- **Voice** (cell 7, closing): "This makes your code flexible and easy to experiment with!" — drop the exclamation. -- **Voice** (cell 9, final comment): "Try changing the numbers, text, or boolean values and see what happens!" — - drop the exclamation. -- **Voice** (cell 11, final comment): "Try changing the number of pizzas, slices_per_pizza, or people, and see - what happens!" — drop the exclamation. -- **Technical** (cell 15, exercises): each blank line like `height_difference = ` is a SyntaxError if run. Put - `...` or `0 # TODO` after each bare `=` so the cell at least parses before students fill it in. Example: - `height_difference = ... # Complete this calculation using variables`. -- **Scope** (cell 8): lists dictionaries, sets, and NoneType. PCEP covers dicts and sets at a basic level, so fine - as a name-drop. No change needed — noting only because the rubric asks to flag scope. - ---- - -## lessons/10_Turtles/50_Variables_and_Functions/20_Functions.ipynb - -- **Voice** (cell 0, closing line): "By the end, you'll be able to write your own reusable code blocks to perform - calculations, make decisions, and more!" — drop the exclamation. -- **Voice** (cell 3, final comment): "Try changing the parameters to see how the angle changes or create new - shapes!" — drop the exclamation. -- **Voice** (cell 11, final comment): "Try changing the numbers to see different results!" — drop the exclamation. -- **Voice** (cell 13, `pizza_calculator`): `# Return both values at once!` — drop the exclamation. -- **Voice** (cell 13, final comment): "Try changing the inputs to see different results!" — drop the exclamation. -- **Voice** (cell 14, opening): "Now let's put our function knowledge to work with Tina!" — drop the exclamation. -- **Voice** (cell 15, closing): "See how functions make our code cleaner and easier to reuse? Try calling the - functions with different parameters!" — drop the exclamation. -- **Voice** (cell 16, opening): "Now it's your turn!" — drop the line; the "Function Challenges" heading already - signals this. -- **Flow** (cell 13): `print(f"For 10 people: need {slices} slices and {pizzas} pizzas")` uses an f-string, which - hasn't been introduced. Swap for comma-separated `print`: `print("For 10 people: need", slices, "slices and", - pizzas, "pizzas")`. - -- **Technical** (cell 13, `pizza_calculator`): `pizzas_needed = -(-total_slices_needed // 8)` is a cryptic ceiling- - division trick. Replace with `from math import ceil` at the top and `pizzas_needed = ceil(total_slices_needed / - 8)`, which is readable. - ---- - -## lessons/10_Turtles/50_Variables_and_Functions/30_Efficient_Turtle.py - -- **Technical** (line 12): `turtle.setup(600,600,0,0)` — add spaces after commas. -- **Technical** (line 17): `tina.speed(2) # Make the turtle move as fast, but not too fast.` — same awkward - wording. Rewrite as `# Move at a moderate speed, not too fast.` - ---- - -## lessons/10_Turtles/60_More_Turtle_Programs/10_More_Turtle_Programs.ipynb - -- **Voice** (cell 0, heading): "Turtle Tricks and Tips" — the folder and file name are "More_Turtle_Programs"; - either rename the heading to match ("More Turtle Programs") or accept the mismatch. I'd align the heading. -- **Technical** (cell 0): `https://docs.python.org/3.14/library/turtle.html` — Python 3.14 docs may not exist yet - and the iframe below points to `/3/`. Change the link to `/3/library/turtle.html` to match. -- **Technical** (cell 2, example code): the demo loop `for i in range(4): t.goto(200, 200); t.goto(-200, -200)` - just bounces the turtle back and forth across a diagonal, which doesn't show off a custom-image turtle doing - anything interesting. Consider changing to a square pattern (goto each of 4 corners in sequence) so the demo - matches what the follow-up `.py` asks the student to write. -- **Technical** (cells 7, 9, 11): `import turtle as turtle` is a redundant alias. Change to plain `import turtle`. -- **Voice** (cell 13, Tip): "If you haven't checked in your code now would be a great time to do so!" — drop the - exclamation: "If you haven't checked in your code, now's a good time." - ---- - -## lessons/10_Turtles/60_More_Turtle_Programs/20_More_Turtle_programs.py - -- **Technical** (filename): lowercased `programs` is inconsistent with every other file and the folder - (`More_Turtle_Programs`). Rename the file to `20_More_Turtle_Programs.py`. -- **Technical** (line 2, docstring): references `10_More_Turtle_programs.ipynb` (lowercase p) but the actual file - is `10_More_Turtle_Programs.ipynb`. Fix the casing. - ---- - -## lessons/10_Turtles/60_More_Turtle_Programs/30_More_Turtle_Programs.py - -- **Technical** (line 2, docstring): references `10_More_Turtle_programs.ipynb` (lowercase p) — fix casing. -- **Flow** (docstring lines 5–6): the instructions tell the student to "move to the corners of the screen in a - square pattern", but this lesson is for the **background image** section, not the turtle-image section. - "Moves to the corners in a square pattern" is leftover copy-paste from `20_`. Rewrite to describe what the - background-image exercise actually asks for — e.g., "Set the background image, then draw a shape on top of it - with your turtle." - ---- - -## lessons/10_Turtles/60_More_Turtle_Programs/40_More_Turtle_Programs.py - -- **Technical** (line 2, docstring): references `10_More_Turtle_programs.ipynb` (lowercase p) — fix casing. +- **Flow** (folder name): `Temp_Project_Ideas` — the prefix "Temp" suggests these are drafts. None of the three + files are referenced in `lessons/.jtl/syllabus.yaml`, so they don't appear in the student flow. Flagging for a + decision: keep and wire them into the syllabus, rename the folder to something less provisional (e.g. + `More_Project_Ideas`), or delete if abandoned. I'll only touch voice/technical issues in the files below + — not the folder itself — without your call. --- -## lessons/10_Turtles/70_Projects/10_LeagueBot.py +## lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb -- No findings. - ---- - -## lessons/10_Turtles/70_Projects/20_Tash_Me.py - -- **Technical** (line 9, Hint): the hint is cut off mid-sentence: "See the `10_More_Turtle_Programs` section - labeled 'Change the Background Image' and" — the sentence ends on "and". Drop the trailing "and" or finish the - thought. -- **Technical** (line 9, Hint): the notebook section is actually titled "Set a Background Picture", not "Change - the Background Image". Update the reference. - ---- - -## lessons/10_Turtles/70_Projects/30_Tash_Me_Click.py - -- No findings. - ---- - -## lessons/10_Turtles/70_Projects/40_Tash_Me_Twirl.py - -- **Technical** (line 6, Hint): references "section 'Respond to Screen Clicks'", but this exercise is about - clicking the *turtle*, not the screen. Change the reference to "Clicking The Turtle Directly". - ---- - -## lessons/10_Turtles/80_Introducting_Lists/10_Lists.ipynb - -- **Technical** (folder name): `80_Introducting_Lists` is misspelled — should be `80_Introducing_Lists`. Flagging - separately since renaming a folder is a bigger change than an in-file edit; let me know if you want this. -- **Technical** (cell 13, heading): "Excercises (Optional)" — typo, should be "Exercises (Optional)". -- **Scope** (cell 8): mentions "comprehensions" as something lists can do. PCEP scope says comprehensions are out, - and we're mentioning them only as a "you can also do this" aside, which is fine, but for consistency consider - dropping "comprehensions" from the list so students don't try to look them up mid-lesson. -- **Technical** (cell 12, code): `for left in [90, 90, 90, 90]:` shadows the outer `left = 90` variable (OK here, - but noting it since the lesson is new to most students). Consider renaming the loop variable to `angle`. - ---- - -## lessons/10_Turtles/80_Introducting_Lists/20_Color_Lines.py - -- **Technical** (line 8): `turtle.setup(600,600,0,0)` — add spaces after commas. -- **Technical** (line 13): trailing whitespace after `too fast.` — remove. -- **Technical** (line 13): "Make the turtle move as fast, but not too fast." — same awkward "as fast" wording. - Rewrite as `# Move at a moderate speed, not too fast.` - ---- - -## lessons/10_Turtles/90_Graphics_Projects/10_Flaming_Ninja_Star.py - -- **Technical** (naming): `getRandomColor`, `getNextColor`, `baseSize`, `flameSize` are camelCase. PEP 8: use - snake_case for functions and variables. Rename to `get_random_color`, `get_next_color`, `base_size`, - `flame_size` (and update call sites). -- **Technical** (line 26): `turtle.setup(600,600,0,0)` — add spaces after commas. -- **Technical** (line 32): `t = turtle.Turtle()` has a trailing space — remove. -- **Technical** (lines 40–61): each statement inside the `for` loop is separated by a blank line, which is - unusual. Tighten by removing the blank lines between related calls so the loop body reads as one block. - ---- - -## lessons/10_Turtles/90_Graphics_Projects/20_Crazy_Spiral.py - -- **Scope** (line 26, instructions): "or it could use islice(), cycle(), or a list of numbers." — `islice()` and - `cycle()` are out of scope (from `itertools`). Drop those two: "for example 100, or a list of numbers." - ---- - -## lessons/10_Turtles/90_Graphics_Projects/30_Pentagon_Crazy.py - -- **Technical** (naming): `getRandomColor`, `getNextColor`, `myTurtle` are camelCase. Rename to - `get_random_color`, `get_next_color`, `my_turtle` (PEP 8). -- **Technical** (lines 13–17): `getNextColor` references the global `colors` at call time, but `colors` is - defined below the function (line 23). It works because Python looks up globals lazily, but it's confusing for a - student reading top-to-bottom. Move the `colors = (...)` tuple above the function definitions. - ---- - -## lessons/10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py - -- **Technical** (naming): `getRandomColor`, `myTurtle` are camelCase. Rename to `get_random_color`, `my_turtle` - (PEP 8). -- **Voice** (line 47): `# Check the pattern against the picture in the recipe. If it matches, you are done!` — - drop the exclamation. -- **Voice** (line 51): `# Now check in your code!` — drop the exclamation. +- **Voice** (cell a8e43baa, opening): "Perfect!" — drop the line or drop the exclamation. +- **Voice** (cell a8e43baa, mid-paragraph): "Pretty cool, right? Oh there's more?!" — stacked `?!` and the + "Pretty cool" phrasing. Rewrite as "There's more where that came from." or similar. +- **Voice** (cell 2553aff8): "Of course, feel free to explore and find other characters that you like!" — drop + the exclamation. +- **Voice** (cell 2553aff8): "And no, it's not cheating to look at ASCII art generators online, there are plenty + of free ones that can create some really cool designs!" — drop "really cool", drop the exclamation. Rewrite as + "It's not cheating to use an ASCII-art generator online — there are plenty of free ones that can create good + designs." +- **Voice** (cell e63a0b03, requirements): "- **Have Fun:** Build something you enjoy!" — drop the exclamation. +- **Voice** (cell e63a0b03, additional info): "prioritize effective usage while being creative!" — drop the + exclamation. --- -## lessons/10_Turtles/Module_One_Quiz.ipynb +## lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb -- No findings. +- **Voice** (cell ad97bb84): "Well, instead of physical, let's create a digital game that you can play on your + computer!" — drop the exclamation. Also the phrasing "instead of physical" reads as a sentence fragment. + Rewrite: "Instead of a physical wheel, let's build a digital version you can play on your computer." +- **Voice** (cell ad97bb84): "However, feel free to add your own twist to the game! This is your chance to get + creative and have fun." — drop the exclamation. +- **Technical** (cell ad97bb84): "Software Development is all about problem-solving and creativity." — "Software + Development" shouldn't be capitalized here (it isn't a proper noun). Lowercase it: "Software development is all + about problem-solving and creativity." --- -## lessons/10_Turtles/README.md +## lessons/50_Projects/Temp_Project_Ideas/Terminal_Game.ipynb - No findings. diff --git a/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb b/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb index 146594cf..21546620 100644 --- a/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb +++ b/lessons/20_Types_and_Logic/10_Operators_and_Types.ipynb @@ -42,13 +42,7 @@ { "cell_type": "markdown", "metadata": {}, - "source": [ - "### **Getting Started**\n", - "\n", - "Although 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", - "\n", - "When you're set to begin, execute the code block below!" - ] + "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", @@ -96,45 +90,14 @@ { "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", - "\n", - "Different types support different operations — for example, `+` performs arithmetic on numbers but concatenation on strings.\n", - "\n", - "Let's see what happens when we add two integers and when we add two strings:" - ] + "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\n", - "x = 10\n", - "y = 20\n", - "\n", - "# Add x and y together\n", - "print(\"x + y =\", x + y)\n", - "\n", - "# Strings\n", - "x = \"10\"\n", - "y = \"20\"\n", - "\n", - "# Add x and y together\n", - "print(\"x + y =\", x + y) # Watch what happens!" - ] + "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", @@ -374,11 +337,7 @@ { "cell_type": "markdown", "metadata": {}, - "source": [ - "
                  \n", - "REMINDER: Don't forget to 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", - "
                  " - ] + "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": { @@ -406,4 +365,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/lessons/20_Types_and_Logic/20_String_Operations.ipynb b/lessons/20_Types_and_Logic/20_String_Operations.ipynb index 96e18a30..c9b3ab1a 100644 --- a/lessons/20_Types_and_Logic/20_String_Operations.ipynb +++ b/lessons/20_Types_and_Logic/20_String_Operations.ipynb @@ -192,27 +192,13 @@ "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!" - ] + "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", - "\n", - "Sometimes 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) |\n", - "| `\\r` | Carriage Return (moves the cursor to the beginning of the line) |" - ] + "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", @@ -220,15 +206,7 @@ "id": "3517ae40", "metadata": {}, "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "print(\"Line 1\\nLine 2\") # Newline\n", - "print(\"Name:\\tAlice\") # Tab\n", - "print(\"She said, \\\"Hello!\\\"\") # Quotes\n", - "print(\"This is a backslash: \\\\\") # Backslash\n", - "print(\"Hello World!\\rStart\") # Carriage Return" - ] + "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", @@ -324,4 +302,4 @@ }, "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 index e511d354..23eba322 100644 --- a/lessons/20_Types_and_Logic/30_Control_Flow.ipynb +++ b/lessons/20_Types_and_Logic/30_Control_Flow.ipynb @@ -1,482 +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 a very common error, so don't feel discouraged. Just try your best to memorize their differences and remember: *practice makes perfect!*" - ] - }, - { - "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", - "\n", - "We can also use the `or` operator:\n", - "\n", - "| Operator | Purpose |\n", - "| :------- | :------: |\n", - "| `or` | Returns `True` if *either* side is `True*. |\n", - "\n", - "Gerneral Representation:\n", - "\n", - "```python\n", - "True or True == True # Both sides are True\n", - "True or False == True # One side is True\n", - "False or False == False # Both sides are False\n", - "```\n", - "\n", - "> **Note:** You also probably never to illustrate `or` like this in your code either.\n", - "\n", - "Here 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 `.in()` 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", - "\n", - "There are more to `if` statements than meets the eye. You can also turn them into blocks by combining theme 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 incredibly 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" - } + "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:" + ] }, - "nbformat": 4, - "nbformat_minor": 2 -} + { + "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 index 07480ec0..91aa1f75 100644 --- a/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb +++ b/lessons/20_Types_and_Logic/40_Working_With_Numbers.ipynb @@ -4,110 +4,7 @@ "cell_type": "markdown", "id": "5ca9192c", "metadata": {}, - "source": [ - "## **Working With Numbers**\n", - "\n", - "Python 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", - "\n", - "Integers 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", - "\n", - "This 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", - "\n", - "For 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 also powers of $10$ (e.g., $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", - "\n", - "If 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", - "\n", - "Or expand the sections below for more details on each number system:\n", - "\n", - "
                  \n", - " Binary\n", - "\n", - "Binary is a base-2 number system that uses a combination of bits and bytes (often with a `0b` prefix) to represent values. \n", - "\n", - "Let'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", - "\n", - "To 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", - "\n", - "Octal is a base-8 number system that uses octal digits to represent values.\n", - "\n", - "Let's look at the octal number `0o345` in the table below:\n", - "\n", - "| Place Value | 64 | 8 | 1 |\n", - "|----------------------|-------|-------|-------\n", - "| Octal Digit | 3 | 4 | 5 |\n", - "\n", - "To 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), (4 \\times 8), (5 \\times 1)$ |\n", - "| Add the results | $192 + 32 + 5 = 229$ |\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", - "\n", - "Hexadecimal is a base-16 number system that uses hex digits to represent values.\n", - "\n", - "Let'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", - "\n", - "To 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", - "
                  " - ] + "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", @@ -241,4 +138,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/lessons/20_Types_and_Logic/60_Simple_Adder.py b/lessons/20_Types_and_Logic/60_Simple_Adder.py index 62cc665c..c35d3107 100644 --- a/lessons/20_Types_and_Logic/60_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/70_Infuriating_Calculator.py b/lessons/20_Types_and_Logic/70_Infuriating_Calculator.py index 0a25a999..cf7fbe5b 100644 --- a/lessons/20_Types_and_Logic/70_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 index 55954213..4e8d71a4 100644 --- a/lessons/20_Types_and_Logic/80_Code_Challenges.ipynb +++ b/lessons/20_Types_and_Logic/80_Code_Challenges.ipynb @@ -59,28 +59,15 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "# Your solution here\n", - "\n", - "for n in [3, 5, 7, 11, 13, 17, 19, 23]:\n", - " ..." - ] + "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", - "\n", - "Jenny 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", - "\n", - "Can you help her?" - ] + "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": { @@ -108,4 +95,4 @@ }, "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 index abe2c87d..29404bb1 100644 --- a/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb +++ b/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb @@ -10,20 +10,267 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "bcd265b8", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
                  " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "var questionshuTSxmDoHOMj=[\n {\n \"question\": \"What is an int in Python?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A whole number\", \"correct\": true, \"feedback\": \"Correct! int stands for integer and represents whole numbers like 5, -10, or 0.\" },\n { \"answer\": \"A decimal number\", \"correct\": false, \"feedback\": \"Incorrect. Decimal numbers use the float type.\" },\n { \"answer\": \"A text value\", \"correct\": false, \"feedback\": \"Incorrect. Text values use the str type.\" },\n { \"answer\": \"A True/False value\", \"correct\": false, \"feedback\": \"Incorrect. True/False values use the bool type.\" }\n ]\n },\n {\n \"question\": \"What is a boolean?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"True or False value\", \"correct\": true, \"feedback\": \"Correct! A boolean (bool) can only be True or False.\" },\n { \"answer\": \"A number\", \"correct\": false, \"feedback\": \"Incorrect. Numbers are int or float types.\" },\n { \"answer\": \"A word or sentence\", \"correct\": false, \"feedback\": \"Incorrect. Words and sentences are strings (str).\" },\n { \"answer\": \"A list of items\", \"correct\": false, \"feedback\": \"Incorrect. Lists are a different data type.\" }\n ]\n },\n {\n \"question\": \"What does the % operator do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Returns the remainder\", \"correct\": true, \"feedback\": \"Correct! 7 % 3 gives 1 because 7 divided by 3 leaves a remainder of 1.\" },\n { \"answer\": \"Divides numbers\", \"correct\": false, \"feedback\": \"Incorrect. Use / or // for division.\" },\n { \"answer\": \"Multiplies numbers\", \"correct\": false, \"feedback\": \"Incorrect. Use * for multiplication.\" },\n { \"answer\": \"Calculates percentage\", \"correct\": false, \"feedback\": \"Incorrect. Despite the symbol, % finds the remainder, not percentages.\" }\n ]\n },\n {\n \"question\": \"What is // used for?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Division without decimals\", \"correct\": true, \"feedback\": \"Correct! 11 // 4 gives 2, dropping the decimal part.\" },\n { \"answer\": \"Regular division\", \"correct\": false, \"feedback\": \"Incorrect. Regular division uses /.\" },\n { \"answer\": \"Finding remainders\", \"correct\": false, \"feedback\": \"Incorrect. Use % to find remainders.\" },\n { \"answer\": \"Adding comments\", \"correct\": false, \"feedback\": \"Incorrect. Comments use #.\" }\n ]\n },\n {\n \"question\": \"What does str() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Converts to text\", \"correct\": true, \"feedback\": \"Correct! str(42) converts the number 42 to the text '42'.\" },\n { \"answer\": \"Converts to a number\", \"correct\": false, \"feedback\": \"Incorrect. Use int() or float() for numbers.\" },\n { \"answer\": \"Checks the data type\", \"correct\": false, \"feedback\": \"Incorrect. Use type() to check data types.\" },\n { \"answer\": \"Makes text uppercase\", \"correct\": false, \"feedback\": \"Incorrect. Use upper() for uppercase.\" }\n ]\n },\n {\n \"question\": \"Which is correct Python syntax?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"if x == 5:\", \"correct\": true, \"feedback\": \"Correct! Use == for comparison and : to start the if block.\" },\n { \"code\": \"if x = 5:\", \"correct\": false, \"feedback\": \"Incorrect. Use = for assignment, but == for comparison.\" },\n { \"code\": \"if x == 5\", \"correct\": false, \"feedback\": \"Incorrect. Missing the colon (:) at the end.\" },\n { \"code\": \"if (x == 5):\", \"correct\": false, \"feedback\": \"Incorrect. Parentheses aren't needed in Python if statements.\" }\n ]\n },\n {\n \"question\": \"What does upper() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Makes text UPPERCASE\", \"correct\": true, \"feedback\": \"Correct! 'hello'.upper() returns 'HELLO'.\" },\n { \"answer\": \"Makes text lowercase\", \"correct\": false, \"feedback\": \"Incorrect. Use lower() for lowercase.\" },\n { \"answer\": \"Capitalizes first letter\", \"correct\": false, \"feedback\": \"Incorrect. Use title() or capitalize() for that.\" },\n { \"answer\": \"Removes spaces\", \"correct\": false, \"feedback\": \"Incorrect. Use strip() to remove spaces.\" }\n ]\n },\n {\n \"question\": \"What is 10 % 3?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"1\", \"correct\": true, \"feedback\": \"Correct! 10 divided by 3 is 3 with remainder 1.\" },\n { \"answer\": \"3\", \"correct\": false, \"feedback\": \"Incorrect. That's 10 // 3 (quotient without remainder).\" },\n { \"answer\": \"3.33\", \"correct\": false, \"feedback\": \"Incorrect. That's 10 / 3 (regular division).\" },\n { \"answer\": \"0\", \"correct\": false, \"feedback\": \"Incorrect. There is a remainder when dividing 10 by 3.\" }\n ]\n },\n {\n \"question\": \"Which creates a string?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"name = \\\"Alice\\\"\", \"correct\": true, \"feedback\": \"Correct! Quotes create strings in Python.\" },\n { \"code\": \"name = Alice\", \"correct\": false, \"feedback\": \"Incorrect. Without quotes, Python looks for a variable named Alice.\" },\n { \"code\": \"name = str\", \"correct\": false, \"feedback\": \"Incorrect. This assigns the str type itself, not a string value.\" },\n { \"code\": \"name = 'Alice\", \"correct\": false, \"feedback\": \"Incorrect. Missing closing quote.\" }\n ]\n },\n {\n \"question\": \"What does type() tell you?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"The data type\", \"correct\": true, \"feedback\": \"Correct! type(5) returns int, type('hi') returns str.\" },\n { \"answer\": \"The value\", \"correct\": false, \"feedback\": \"Incorrect. type() shows the type, not the value itself.\" },\n { \"answer\": \"The length\", \"correct\": false, \"feedback\": \"Incorrect. Use len() for length.\" },\n { \"answer\": \"If it's valid code\", \"correct\": false, \"feedback\": \"Incorrect. type() doesn't validate code syntax.\" }\n ]\n },\n {\n \"question\": \"What does == check?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"If values are equal\", \"correct\": true, \"feedback\": \"Correct! 5 == 5 returns True because they're equal.\" },\n { \"answer\": \"Assigns a value\", \"correct\": false, \"feedback\": \"Incorrect. Use = for assignment, not ==.\" },\n { \"answer\": \"If values are not equal\", \"correct\": false, \"feedback\": \"Incorrect. Use != for not equal.\" },\n { \"answer\": \"If one is greater\", \"correct\": false, \"feedback\": \"Incorrect. Use > for greater than.\" }\n ]\n },\n {\n \"question\": \"What is 15 // 4?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"3\", \"correct\": true, \"feedback\": \"Correct! Floor division gives 3, ignoring the remainder.\" },\n { \"answer\": \"3.75\", \"correct\": false, \"feedback\": \"Incorrect. That's regular division (15 / 4).\" },\n { \"answer\": \"4\", \"correct\": false, \"feedback\": \"Incorrect. 4 goes into 15 three times, not four.\" },\n { \"answer\": \"3.0\", \"correct\": false, \"feedback\": \"Incorrect. With integers, // returns an integer (3), not a float.\" }\n ]\n },\n {\n \"question\": \"What does strip() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Removes spaces from ends\", \"correct\": true, \"feedback\": \"Correct! ' hi '.strip() returns 'hi'.\" },\n { \"answer\": \"Removes all spaces\", \"correct\": false, \"feedback\": \"Incorrect. strip() only removes spaces from the start and end.\" },\n { \"answer\": \"Splits text into parts\", \"correct\": false, \"feedback\": \"Incorrect. Use split() to divide text.\" },\n { \"answer\": \"Makes text lowercase\", \"correct\": false, \"feedback\": \"Incorrect. Use lower() for lowercase.\" }\n ]\n },\n {\n \"question\": \"What causes a TypeError?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"5 + \\\"10\\\"\", \"correct\": true, \"feedback\": \"Correct! Can't add a number and string directly. Convert first.\" },\n { \"code\": \"5 + 10\", \"correct\": false, \"feedback\": \"Incorrect. This is valid and equals 15.\" },\n { \"code\": \"\\\"5\\\" + \\\"10\\\"\", \"correct\": false, \"feedback\": \"Incorrect. This is valid string concatenation ('510').\" },\n { \"code\": \"int(\\\"10\\\") + 5\", \"correct\": false, \"feedback\": \"Incorrect. This is valid after conversion (equals 15).\" }\n ]\n },\n {\n \"question\": \"What does 'and' require?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Both conditions True\", \"correct\": true, \"feedback\": \"Correct! True and True is True, but anything else is False.\" },\n { \"answer\": \"At least one condition True\", \"correct\": false, \"feedback\": \"Incorrect. That's what 'or' requires.\" },\n { \"answer\": \"Both conditions False\", \"correct\": false, \"feedback\": \"Incorrect. Then the result would be False.\" },\n { \"answer\": \"One True, one False\", \"correct\": false, \"feedback\": \"Incorrect. Then 'and' returns False.\" }\n ]\n },\n {\n \"question\": \"What is float used for?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Decimal numbers\", \"correct\": true, \"feedback\": \"Correct! float represents numbers with decimals like 3.14 or 2.5.\" },\n { \"answer\": \"Whole numbers only\", \"correct\": false, \"feedback\": \"Incorrect. Use int for whole numbers.\" },\n { \"answer\": \"Text values\", \"correct\": false, \"feedback\": \"Incorrect. Use str for text.\" },\n { \"answer\": \"True/False values\", \"correct\": false, \"feedback\": \"Incorrect. Use bool for True/False.\" }\n ]\n },\n {\n \"question\": \"Which starts an if statement?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"if x > 5:\", \"correct\": true, \"feedback\": \"Correct! Use if, a condition, then a colon.\" },\n { \"code\": \"if x > 5\", \"correct\": false, \"feedback\": \"Incorrect. Missing the colon (:).\" },\n { \"code\": \"if: x > 5\", \"correct\": false, \"feedback\": \"Incorrect. The colon comes after the condition.\" },\n { \"code\": \"x > 5 if:\", \"correct\": false, \"feedback\": \"Incorrect. Wrong order; start with 'if'.\" }\n ]\n },\n {\n \"question\": \"What does lower() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Makes text lowercase\", \"correct\": true, \"feedback\": \"Correct! 'HELLO'.lower() returns 'hello'.\" },\n { \"answer\": \"Makes text uppercase\", \"correct\": false, \"feedback\": \"Incorrect. Use upper() for uppercase.\" },\n { \"answer\": \"Removes spaces\", \"correct\": false, \"feedback\": \"Incorrect. Use strip() to remove spaces.\" },\n { \"answer\": \"Converts to number\", \"correct\": false, \"feedback\": \"Incorrect. Use int() or float() for numbers.\" }\n ]\n },\n {\n \"question\": \"What is 8 % 4?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"0\", \"correct\": true, \"feedback\": \"Correct! 8 divided by 4 is 2 with no remainder.\" },\n { \"answer\": \"2\", \"correct\": false, \"feedback\": \"Incorrect. That's 8 // 4 (quotient). % gives remainder (0).\" },\n { \"answer\": \"4\", \"correct\": false, \"feedback\": \"Incorrect. The remainder is 0, not 4.\" },\n { \"answer\": \"8\", \"correct\": false, \"feedback\": \"Incorrect. The remainder of 8 % 4 is 0.\" }\n ]\n },\n {\n \"question\": \"What does != mean?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Not equal\", \"correct\": true, \"feedback\": \"Correct! 5 != 3 is True because they're not equal.\" },\n { \"answer\": \"Equal\", \"correct\": false, \"feedback\": \"Incorrect. Use == for equal.\" },\n { \"answer\": \"Greater than\", \"correct\": false, \"feedback\": \"Incorrect. Use > for greater than.\" },\n { \"answer\": \"Less than\", \"correct\": false, \"feedback\": \"Incorrect. Use < for less than.\" }\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(\"huTSxmDoHOMj\")) {\n show_questions(questionshuTSxmDoHOMj, huTSxmDoHOMj);\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(\"lessons/Quiz_Data/Module_Two_Quiz.json\")" + "display_quiz(\"../Quiz_Data/Module_Two_Quiz.json\")" ] } ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "python-apprentice-jay (3.13.3)", "language": "python", "name": "python3" }, @@ -37,9 +284,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.5" + "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/100_For_Loop_Gauntlet.ipynb b/lessons/30_Loops/100_For_Loop_Gauntlet.ipynb index 44627161..db632fe5 100644 --- a/lessons/30_Loops/100_For_Loop_Gauntlet.ipynb +++ b/lessons/30_Loops/100_For_Loop_Gauntlet.ipynb @@ -1,427 +1,395 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "539e9340", - "metadata": {}, - "source": [ - "# **For Loop Gauntlet**\n", - "\n", - "Welcome to the *For Loop Gauntlet*! This assignment is designed to help you become a master at using `for` loops in Python.\n", - "\n", - "Carefully 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", - "\n", - "Write a `for` loop to accomplish each of the following tasks." - ] - }, - { - "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": [ - "# Display all numbers from 1 to 500.\n", - "# Write a loop that prints every number from 1 to 500." - ] - }, - { - "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", - "\n", - "Congratulations on making it this far! If you are feeling adventurous, create a 1 x 10 multiplication table.\n", - "\n", - "The output should look like this:\n", - "\n", - "```text\n", - "1 2 3 4 5 6 7 8 9 10\n", - "2 4 6 8 10 12 14 16 18 20\n", - "3 6 9 12 15 18 21 24 27 30\n", - "4 8 12 16 20 24 28 32 36 40\n", - "5 10 15 20 25 30 35 40 45 50\n", - "6 12 18 24 30 36 42 48 54 60\n", - "7 14 21 28 35 42 49 56 63 70\n", - "8 16 24 32 40 48 56 64 72 80\n", - "9 18 27 36 45 54 63 72 81 90\n", - "10 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" - } + "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 index 18d6947b..5af8ca48 100644 --- a/lessons/30_Loops/10_Iteration.ipynb +++ b/lessons/30_Loops/10_Iteration.ipynb @@ -29,14 +29,7 @@ { "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", - "\n", - "Try running the cell to see the output!" - ] + "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", @@ -55,31 +48,7 @@ { "cell_type": "markdown", "metadata": {}, - "source": [ - "## **What is an Iterable?**\n", - "An 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", - "\n", - "A `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", - "\n", - "Let’s dive into some examples to see this in action!\n", - "\n", - "##### **List Iterations**" - ] + "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", @@ -163,30 +132,7 @@ { "cell_type": "markdown", "metadata": {}, - "source": [ - "### **Choosing Iterable Names**\n", - "\n", - "The 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", - "\n", - "Take 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", - "\n", - "Below we can see an example of a `for` loop iterating over a list of `fruits`:" - ] + "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", @@ -234,4 +180,4 @@ }, "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 e30d2b56..6b2f208a 100644 --- a/lessons/30_Loops/110_FizzBuzz_Gui_Grid.py +++ b/lessons/30_Loops/110_FizzBuzz_Gui_Grid.py @@ -10,18 +10,23 @@ * 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) or you can convert the number to a string and iterate over the digits @@ -44,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 92bd9d1f..59254a80 100644 --- a/lessons/30_Loops/120_More_Iterables.ipynb +++ b/lessons/30_Loops/120_More_Iterables.ipynb @@ -1,712 +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", - "| 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", - "\n", - "names = ['Alice', 'Bob', 'Charlie', 'Diana']\n", - "ages = [25, 30, 35, 28]\n", - "cities = ['New York', 'London', 'Tokyo', 'Paris']\n", - "\n", - "# Create a dictionary mapping names to ages\n", - "age_dict = dict(zip(names, ages))\n", - "print(\"Age Dictionary:\", age_dict)\n", - "\n", - "# Create a dictionary mapping names to cities\n", - "city_dict = dict(zip(names, cities))\n", - "print(\"City Dictionary:\", city_dict)\n", - "\n", - "# You can even zip three lists to create a more complex structure\n", - "people = list(zip(names, ages, cities))\n", - "print(\"\\nPeople list (tuples):\", people)\n", - "\n", - "# Or create a dictionary with tuple values\n", - "people_dict = {name: (age, city) for name, age, city in zip(names, ages, cities)}\n", - "print(\"\\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": "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": "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 -} + "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.py b/lessons/30_Loops/130_Iterable_Turtle.py index 4871fb18..2028a2a8 100644 --- a/lessons/30_Loops/130_Iterable_Turtle.py +++ b/lessons/30_Loops/130_Iterable_Turtle.py @@ -4,7 +4,7 @@ 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 04_Crazy_Tina.py +t = ... # Create a turtle like in previous programs, like 40_Crazy_Tina.py colors = ... # Make a list of colors @@ -14,7 +14,7 @@ ] # Zip the colors and directions together, then unpack them. There is a good example of this -# in 10_More_iterables.ipynb in the discussion of zip() +# in 120_More_Iterables.ipynb in the discussion of zip() for ... in zip( ... , ...): t.color( ... ) diff --git a/lessons/30_Loops/140_More_Loops.ipynb b/lessons/30_Loops/140_More_Loops.ipynb index 87394092..df3c9674 100644 --- a/lessons/30_Loops/140_More_Loops.ipynb +++ b/lessons/30_Loops/140_More_Loops.ipynb @@ -219,9 +219,7 @@ { "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 in your programming journey!" - ] + "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": { @@ -249,4 +247,4 @@ }, "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 d0022334..07050694 100644 --- a/lessons/30_Loops/150_Number_Guess.py +++ b/lessons/30_Loops/150_Number_Guess.py @@ -26,9 +26,10 @@ Use the ask_integer function to get the user's guess, like this: guess = ask_integer("Guess a number between 1 and 100: ") -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 teminal +Note: The prompts and output for your program will be in the terminal. uid: rZO9PHOt name: Number Guess """ diff --git a/lessons/30_Loops/20_Loops_with_Range.ipynb b/lessons/30_Loops/20_Loops_with_Range.ipynb index d72b426f..c07a4104 100644 --- a/lessons/30_Loops/20_Loops_with_Range.ipynb +++ b/lessons/30_Loops/20_Loops_with_Range.ipynb @@ -1,184 +1,176 @@ { - "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": null, - "metadata": {}, - "outputs": [], - "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": null, - "metadata": {}, - "outputs": [], - "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": null, - "metadata": {}, - "outputs": [], - "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": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test yourself\n", - "\n", - "..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "### **Challenge**\n", - "\n", - "These [Badgers](https://youtu.be/pzagBTcYsYQ?si=xr4QQ7ZkZBGow2j1) are fans of the classic FizzBuzz Programming Challenge, but instead, they have their own rules.\n", - "\n", - "\"Badger\n", - "\n", - "#### **Badger Badger Mushroom Rules:**\n", - "\n", - "In 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": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Badgers!\n", - "\n", - "for i in range(1, 31):\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": "Loops With Range", - "uid": "WcGpR3Xg" - } + "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:" + ] }, - "nbformat": 4, - "nbformat_minor": 2 -} + { + "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": "### **Efficiency of Range**\n\nAlthough `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\nBy generating numbers only when requested, `range()` stays lightweight regardless of size.\n\nTo visualize the numbers a range produces (recommended for small ranges only), you can convert it to a list:" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "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": null, + "metadata": {}, + "outputs": [], + "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": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Test yourself\n", + "\n", + "..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "### **Challenge**\n", + "\n", + "These [Badgers](https://youtu.be/pzagBTcYsYQ?si=xr4QQ7ZkZBGow2j1) are fans of the classic FizzBuzz Programming Challenge, but instead, they have their own rules.\n", + "\n", + "\"Badger\n", + "\n", + "#### **Badger Badger Mushroom Rules:**\n", + "\n", + "In 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": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Badgers!\n", + "\n", + "for i in range(1, 31):\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": "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 index 014d05ad..58985469 100644 --- a/lessons/30_Loops/30_Looping_Through_Lists.ipynb +++ b/lessons/30_Loops/30_Looping_Through_Lists.ipynb @@ -267,21 +267,7 @@ "title": "[python]" }, "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "my_list = []\n", - "\n", - "# Add to the list using append\n", - "my_list.append('item 1')\n", - "my_list.append('item 2')\n", - "my_list.append('item 3')\n", - "\n", - "# You can also use = with an empty list to create a new list\n", - "my_list = my_list + ['item 4', 'item 5']\n", - "\n", - "print(my_list)" - ] + "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", @@ -289,10 +275,7 @@ "metadata": { "lines_to_next_cell": 0 }, - "source": [ - "### **Try It!**\n", - "Add more items to the list." - ] + "source": "### **Try It**\nAdd more items to the list." }, { "cell_type": "code", @@ -302,11 +285,7 @@ "title": "[python]" }, "outputs": [], - "source": [ - "# Run Me!\n", - "\n", - "# Try adding more items to the list (myList) and printing it out again!" - ] + "source": "# Run Me!\n\n# Try adding more items to the list (my_list) and printing it out again." }, { "cell_type": "markdown", @@ -381,39 +360,13 @@ "id": "085f3175", "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", - "lefts = [ 90, 90, 90, 90, 90, 90, 90, 90 ]\n", - "colors = [ 'red', 'blue', 'black', 'orange', 'purple', 'pink', 'cyan', 'green']\n", - "\n", - "for 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" - ] + "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", - "\n", - "Time to practice what you've learned!\n", - "\n", - "You 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." - ] + "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", diff --git a/lessons/30_Loops/40_Crazy_Tina.py b/lessons/30_Loops/40_Crazy_Tina.py index 060b6ae5..505ab475 100644 --- a/lessons/30_Loops/40_Crazy_Tina.py +++ b/lessons/30_Loops/40_Crazy_Tina.py @@ -14,18 +14,18 @@ """ 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 index 42ce1b8a..a6a31771 100644 --- a/lessons/30_Loops/50_Tuples.ipynb +++ b/lessons/30_Loops/50_Tuples.ipynb @@ -3,13 +3,7 @@ { "cell_type": "markdown", "metadata": {}, - "source": [ - "# **Tuples — Lists You Can't Change**\n", - "\n", - "Tuples 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 highly efficient and useful in many scenarios, so let's learn about them! \n", - "\n", - "You can create a tuple just like a list, but you use parentheses `()` instead of square brackets `[]`." - ] + "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", @@ -193,4 +187,4 @@ }, "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 index 38f4904a..7af11dfb 100644 --- a/lessons/30_Loops/60_Indexing_and_Slicing.ipynb +++ b/lessons/30_Loops/60_Indexing_and_Slicing.ipynb @@ -1,300 +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", - "\n", - "We can also use negative indices to count from the end of the list, which is super handy when you want the last few items but don't know how long the list is!\n", - "\n", - "#### **Visualizing Negative Indices**\n", - "\n", - "Think of it as counting backwards:\n", - "\n", - "```text\n", - " ┌────────┬────────┬─────────┬────────┐\n", - " │ red │ blue │ black │ orange │\n", - " └────────┴────────┴─────────┴────────┘\n", - "-4 -3 -2 -1\n", - "```\n", - "\n", - "For 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", - "\n", - "The 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", - "\n", - "We 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" - } + "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." + ] }, - "nbformat": 4, - "nbformat_minor": 2 -} + { + "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/80_Strings.ipynb b/lessons/30_Loops/80_Strings.ipynb index 0110e9ca..7797ddf7 100644 --- a/lessons/30_Loops/80_Strings.ipynb +++ b/lessons/30_Loops/80_Strings.ipynb @@ -113,11 +113,7 @@ { "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.\n", - "\n", - "### **Now You Try It!**" - ] + "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", @@ -256,11 +252,7 @@ { "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)." - ] + "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", @@ -572,4 +564,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/lessons/30_Loops/Module_Three_Quiz.ipynb b/lessons/30_Loops/Module_Three_Quiz.ipynb index 0ec9a5a8..9c8d1cc3 100644 --- a/lessons/30_Loops/Module_Three_Quiz.ipynb +++ b/lessons/30_Loops/Module_Three_Quiz.ipynb @@ -14,256 +14,11 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "e88338fc", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
                    " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/javascript": "var questionssmvYwokwXykO=[\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 input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\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 var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n 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 //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\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 aDiv.append(lab);\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) {\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(\"smvYwokwXykO\")) {\n show_questions(questionssmvYwokwXykO, smvYwokwXykO);\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(\"lessons/Quiz_Data/Module_Three_Quiz.json\")" - ] + "outputs": [], + "source": "from jupyterquiz import display_quiz\n\ndisplay_quiz(\"../Quiz_Data/Module_Three_Quiz.json\")" } ], "metadata": { @@ -287,4 +42,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/lessons/40_Data_Structures_Func/10_Functions.ipynb b/lessons/40_Data_Structures_Func/10_Functions.ipynb index 681fd5e3..1e59dbbc 100644 --- a/lessons/40_Data_Structures_Func/10_Functions.ipynb +++ b/lessons/40_Data_Structures_Func/10_Functions.ipynb @@ -60,21 +60,7 @@ { "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", - "\n", - "Functions 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", - "\n", - "Arguments 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", - "\n", - "Let's see how this works:" - ] + "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", @@ -223,11 +209,7 @@ { "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 SyntaxError.\n", - "\n", - "> **Tip:** Always declare all variables you use in your function, either in the argument list or within the function body." - ] + "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", @@ -431,11 +413,7 @@ { "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!" - ] + "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": { @@ -463,4 +441,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/lessons/40_Data_Structures_Func/20_Exceptions.ipynb b/lessons/40_Data_Structures_Func/20_Exceptions.ipynb index 87633837..a93be699 100644 --- a/lessons/40_Data_Structures_Func/20_Exceptions.ipynb +++ b/lessons/40_Data_Structures_Func/20_Exceptions.ipynb @@ -52,13 +52,7 @@ "cell_type": "markdown", "id": "5fe3fb51", "metadata": {}, - "source": [ - "Oh, that didn't work! This happened because the program tried to convert `Bob` to an integer and that caused an exception to be thrown, which led to a crash before completing the loop could be completed. \n", - "\n", - "What if we could catch that exception using a try/except block and provide instructions on what to do when an error occurs?\n", - "\n", - "Let's take a look at how that works:" - ] + "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", @@ -82,11 +76,7 @@ "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. Memorizing this technique is essential for programs that deal with user input or external data that may not always be in a predictable format." - ] + "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", @@ -301,4 +291,4 @@ }, "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 index 8a8c0d54..915d65c9 100644 --- a/lessons/40_Data_Structures_Func/30_Dicts_Sets.ipynb +++ b/lessons/40_Data_Structures_Func/30_Dicts_Sets.ipynb @@ -1,600 +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", - "\n", - "Collections 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 -} + "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/40_Funny_Words_Db.py b/lessons/40_Data_Structures_Func/40_Funny_Words_Db.py index 25e4d9fb..ed0b829e 100644 --- a/lessons/40_Data_Structures_Func/40_Funny_Words_Db.py +++ b/lessons/40_Data_Structures_Func/40_Funny_Words_Db.py @@ -1,16 +1,12 @@ """ +Funny Words Dictionary + uid: VenVwSQz name: Funny Words Db -""" - -from guizero import App, Box, Text, TextBox, PushButton, ListBox, error - -""" -Funny Words Dictionary 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, @@ -19,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): @@ -78,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 @@ -96,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 @@ -112,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): @@ -169,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/50_Splat_Comprehension.ipynb b/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb index dcbec92d..aa70c48d 100644 --- a/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb +++ b/lessons/40_Data_Structures_Func/50_Splat_Comprehension.ipynb @@ -1,739 +1,650 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# **Tic Tac Toe**\n", - "\n", - "In 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", - "\n", - "Did you see how the brackets changed from `[]` to `()`? \n", - "\n", - "That's because `zip()` automatically creates tuples instead of lists. Don't worry though, we'll fix this in a moment. \n", - "\n", - "First, let's understand how `zip()` performs this magic trick.\n", - "\n", - "Here 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": [ - "Perfect! Now you can see exactly what `zip()` does. \n", - "\n", - "The first tuple contains the first item from each list, the second tuple contains the second item from each list, and so on. \n", - "\n", - "To make this pattern even clearer, let's look at this color-coded version:\n", - "\n", - "**Input:**\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",
                      -        "
                      \n", - "\n", - "But we don't want tuples—we want lists! \n", - "\n", - "So 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", - "\n", - "That conversion code is kind of **verbose**, but we can make it much cleaner with a **list comprehension**! \n", - "\n", - "Here'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 very popular in Python because they make code more concise and readable!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## **Yes, but Who Won?**\n", - "\n", - "Now 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", - "\n", - "Now remember, we still need to check the two diagonals. Let's look at their coordinates.\n", - "\n", - "If this is your board:\n", - "\n", - "```python\n", - "[1, 2, 3]\n", - "[4, 5, 6]\n", - "[7, 8, 9]\n", - "```\n", - "\n", - "Then the two diagonals are: `[1, 5, 9]` and `[3, 5, 7]`. \n", - "\n", - "Let'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", - "\n", - "For 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", - "\n", - "For 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! Just 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": [ - "
                      \n", - "REMINDER: Don't forget to 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 -} + "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/60_Tic_Tac_Toe.py b/lessons/40_Data_Structures_Func/60_Tic_Tac_Toe.py index bf7fff05..2788a502 100644 --- a/lessons/40_Data_Structures_Func/60_Tic_Tac_Toe.py +++ b/lessons/40_Data_Structures_Func/60_Tic_Tac_Toe.py @@ -1,5 +1,5 @@ """ -# 50_Tic_Tac_Toe.py +# 60_Tic_Tac_Toe.py uid: mNMfKPiT name: Tic Tac Toe @@ -18,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 """ @@ -29,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 """ @@ -38,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) @@ -71,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)] @@ -111,4 +105,4 @@ def do_turn(self, x, y): info("Tic-tac-toe","It's a draw!") ttt = TicTacToe(check_win) -ttt.start() \ No newline at end of file +ttt.start() diff --git a/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb b/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb index 6f274133..a5ab5b52 100644 --- a/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb +++ b/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb @@ -14,11 +14,7 @@ "id": "5c4ce2ab", "metadata": {}, "outputs": [], - "source": [ - "from jupyterquiz import display_quiz\n", - "\n", - "display_quiz(\"lessons/Quiz_Data/Module_Four_Quiz.json\")" - ] + "source": "from jupyterquiz import display_quiz\n\ndisplay_quiz(\"../Quiz_Data/Module_Four_Quiz.json\")" } ], "metadata": { @@ -40,10 +36,10 @@ "version": "3.13.5" } }, - "syllabus": { + "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 44a0e433..820b0b04 100644 --- a/lessons/50_Projects/20_Random_Walk.py +++ b/lessons/50_Projects/20_Random_Walk.py @@ -32,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 @@ -41,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"]) @@ -49,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] # diff --git a/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb b/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb index a577a393..0620b7cd 100644 --- a/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb +++ b/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb @@ -4,63 +4,7 @@ "cell_type": "markdown", "id": "a8e43baa", "metadata": {}, - "source": [ - "# **So you want to be an ASCII Artist?**\n", - "\n", - "Perfect! You will use loops and other techniques that you've learned so far to generate patterns and designs using ASCII characters.\n", - "\n", - "These 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", - "\n", - "ASCII 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", - "\n", - "Examples of ASCII art can include simple designs like smiley faces to more complex images like landscapes, portraits, and intricate patterns.\n", - "\n", - "First 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", - "\n", - "Pretty cool, right? Oh there's more?!\n", - "\n", - "Check out these different ASCII animations to get a feel for what can be done:\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
                      \n", - "\n", - "Okay, let's explore them further with some advanced examples:\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
                      \n", - "\n", - "Now you might be thinking, man, that looks complicated. How would I even begin to create something like that?\n", - "\n", - "The answer is loops! \n", - "\n", - "By 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." - ] + "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", @@ -239,4 +183,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file 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 506fb404..9b510188 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,5 @@ pillow IPython ipykernel ipyturtle3 -invoke \ No newline at end of file +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" }, +] From b364854c978d6914b8308b33874eb441b05cc851 Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Thu, 16 Apr 2026 22:05:11 -0700 Subject: [PATCH 155/159] Refactor ASCII Art and Spin The Wheel lesson content for clarity and conciseness --- REVIEW.md | 77 --------------- .../Temp_Project_Ideas/30_ASCII_Art.ipynb | 95 +------------------ .../Temp_Project_Ideas/Spin_The_Wheel.ipynb | 15 +-- 3 files changed, 4 insertions(+), 183 deletions(-) delete mode 100644 REVIEW.md diff --git a/REVIEW.md b/REVIEW.md deleted file mode 100644 index 0b9c9220..00000000 --- a/REVIEW.md +++ /dev/null @@ -1,77 +0,0 @@ -# Review: 50_Projects - -Delete any bullet you don't want applied, then hand the file back and I'll apply the rest. - ---- - -## lessons/50_Projects/README.md - -- No findings. (The file just has the uid and the heading "Projects".) - ---- - -## lessons/50_Projects/10_Hotel_Management.md - -- **Voice** (line 5): "This is going to be your 1st program you write all by yourself" — informal "1st". Rewrite as - "This is the first program you'll write on your own." -- **Technical** (line 10): `(it's the triple quotes """ stuff goes inside """)` — the quotes in the file are - curly smart quotes (`“””`), not straight quotes (`"""`). Python doesn't accept smart quotes, so a student who - copies this verbatim will get a SyntaxError. Replace with straight triple-quote characters. -- **Voice** (line 46): "Have Fun!!!" — three stacked exclamation points. Rewrite as "Have fun." or drop. - ---- - -## lessons/50_Projects/20_Random_Walk.py - -- **Technical** (line 35, docstring): "Fur instance" — typo, should be "For instance". -- **Technical** (line 44, comment): "You can make the turle move randomly" — typo, should be "turtle". -- **Technical** (line 52, example code in comment): `angle_index = random.randint(0, 4)` — off-by-one bug. A - student who copies this literally will get an `IndexError` when `angle_index == 4`, because `directions` has - only four elements (indices 0–3). `random.randint(0, 4)` is inclusive of both ends. Fix to - `random.randint(0, 3)`. - ---- - -## lessons/50_Projects/Temp_Project_Ideas/ - -- **Flow** (folder name): `Temp_Project_Ideas` — the prefix "Temp" suggests these are drafts. None of the three - files are referenced in `lessons/.jtl/syllabus.yaml`, so they don't appear in the student flow. Flagging for a - decision: keep and wire them into the syllabus, rename the folder to something less provisional (e.g. - `More_Project_Ideas`), or delete if abandoned. I'll only touch voice/technical issues in the files below - — not the folder itself — without your call. - ---- - -## lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb - -- **Voice** (cell a8e43baa, opening): "Perfect!" — drop the line or drop the exclamation. -- **Voice** (cell a8e43baa, mid-paragraph): "Pretty cool, right? Oh there's more?!" — stacked `?!` and the - "Pretty cool" phrasing. Rewrite as "There's more where that came from." or similar. -- **Voice** (cell 2553aff8): "Of course, feel free to explore and find other characters that you like!" — drop - the exclamation. -- **Voice** (cell 2553aff8): "And no, it's not cheating to look at ASCII art generators online, there are plenty - of free ones that can create some really cool designs!" — drop "really cool", drop the exclamation. Rewrite as - "It's not cheating to use an ASCII-art generator online — there are plenty of free ones that can create good - designs." -- **Voice** (cell e63a0b03, requirements): "- **Have Fun:** Build something you enjoy!" — drop the exclamation. -- **Voice** (cell e63a0b03, additional info): "prioritize effective usage while being creative!" — drop the - exclamation. - ---- - -## lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb - -- **Voice** (cell ad97bb84): "Well, instead of physical, let's create a digital game that you can play on your - computer!" — drop the exclamation. Also the phrasing "instead of physical" reads as a sentence fragment. - Rewrite: "Instead of a physical wheel, let's build a digital version you can play on your computer." -- **Voice** (cell ad97bb84): "However, feel free to add your own twist to the game! This is your chance to get - creative and have fun." — drop the exclamation. -- **Technical** (cell ad97bb84): "Software Development is all about problem-solving and creativity." — "Software - Development" shouldn't be capitalized here (it isn't a proper noun). Lowercase it: "Software development is all - about problem-solving and creativity." - ---- - -## lessons/50_Projects/Temp_Project_Ideas/Terminal_Game.ipynb - -- No findings. diff --git a/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb b/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb index 0620b7cd..e675e591 100644 --- a/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb +++ b/lessons/50_Projects/Temp_Project_Ideas/30_ASCII_Art.ipynb @@ -10,69 +10,7 @@ "cell_type": "markdown", "id": "2553aff8", "metadata": {}, - "source": [ - "## **Before You Begin**\n", - "\n", - "It 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", - "\n", - "Take 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", - "\n", - "Of course, feel free to explore and find other characters that you like! \n", - "\n", - "### **Additional Resources**\n", - "\n", - "You 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", - "\n", - "And no, it's not cheating to look at ASCII art generators online, there are plenty of free ones that can create some really cool designs!\n", - "\n", - "In 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.
                      " - ] + "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", @@ -130,36 +68,7 @@ "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**\n", - "1. **Plan Your Design:** Choose the ASCII art you wish to make. Sketch it or use an online generator for ideas.\n", - "2. **Choose Characters:** Pick the ASCII characters for your design and break them down into manageable parts to work with.\n", - "3. **Define Functions:** Set up at least two functions: one for drawing and another for logic/settings.\n", - "4. **Use Data Structures:** Organize your patterns or settings using lists or dictionaries.\n", - "5. **Implement Loops:** Code repeated patterns or designs with loops to avoid hardcoding.\n", - "6. **Test and Refine:** Execute your code to check the output. Improve the design or add features as needed.\n", - "7. **Document Your Code:** Include comments explaining your logic and design decisions for future reference.\n", - "8. **Create Project File:** Save your code in a `.py` file, ensuring it includes comments explaining your logic." - ] + "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": { diff --git a/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb b/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb index b50c0675..303ae206 100644 --- a/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb +++ b/lessons/50_Projects/Temp_Project_Ideas/Spin_The_Wheel.ipynb @@ -4,18 +4,7 @@ "cell_type": "markdown", "id": "ad97bb84", "metadata": {}, - "source": [ - "## **Spin The Wheel Game**\n", - "\n", - "Do you know someone who likes to watch game shows on TV? Have you ever wanted to create your own? Well, instead of physical, let's create a digital game that you can play on your computer!\n", - "\n", - "**Example:**\n", - "
                      \n", - "\n", - "Typically, 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", - "\n", - "Software Development is all about problem-solving and creativity. So, think about how you can make your game engaging and enjoyable for players." - ] + "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", @@ -52,4 +41,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file From 2894fe1348ecdfd11494a6c29af6ab33d77d1170 Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Thu, 16 Apr 2026 22:44:31 -0700 Subject: [PATCH 156/159] Add quizzes for Modules One, Two, and Three - Created Module_One_Quiz.json with 30 multiple choice questions covering basic turtle graphics commands and programming concepts. - Created Module_Two_Quiz.json with 30 multiple choice questions focusing on data types, operators, and string methods in Python. - Created Module_Three_Quiz.json with 30 multiple choice questions addressing lists, tuples, and control flow in Python. --- lessons/{ => .jtl}/Quiz_Data/Module_Four_Quiz.json | 0 lessons/{ => .jtl}/Quiz_Data/Module_One_Quiz.json | 0 lessons/{ => .jtl}/Quiz_Data/Module_Three_Quiz.json | 0 lessons/{ => .jtl}/Quiz_Data/Module_Two_Quiz.json | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename lessons/{ => .jtl}/Quiz_Data/Module_Four_Quiz.json (100%) rename lessons/{ => .jtl}/Quiz_Data/Module_One_Quiz.json (100%) rename lessons/{ => .jtl}/Quiz_Data/Module_Three_Quiz.json (100%) rename lessons/{ => .jtl}/Quiz_Data/Module_Two_Quiz.json (100%) diff --git a/lessons/Quiz_Data/Module_Four_Quiz.json b/lessons/.jtl/Quiz_Data/Module_Four_Quiz.json similarity index 100% rename from lessons/Quiz_Data/Module_Four_Quiz.json rename to lessons/.jtl/Quiz_Data/Module_Four_Quiz.json diff --git a/lessons/Quiz_Data/Module_One_Quiz.json b/lessons/.jtl/Quiz_Data/Module_One_Quiz.json similarity index 100% rename from lessons/Quiz_Data/Module_One_Quiz.json rename to lessons/.jtl/Quiz_Data/Module_One_Quiz.json diff --git a/lessons/Quiz_Data/Module_Three_Quiz.json b/lessons/.jtl/Quiz_Data/Module_Three_Quiz.json similarity index 100% rename from lessons/Quiz_Data/Module_Three_Quiz.json rename to lessons/.jtl/Quiz_Data/Module_Three_Quiz.json diff --git a/lessons/Quiz_Data/Module_Two_Quiz.json b/lessons/.jtl/Quiz_Data/Module_Two_Quiz.json similarity index 100% rename from lessons/Quiz_Data/Module_Two_Quiz.json rename to lessons/.jtl/Quiz_Data/Module_Two_Quiz.json From bb8ff1634e7f0434987e4a36ef39a34dbee95016 Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Fri, 17 Apr 2026 10:15:23 -0700 Subject: [PATCH 157/159] Refactor quiz file paths and add syllabus.yaml for course structure - Updated file paths in Module_Three_Quiz.ipynb and Module_Four_Quiz.ipynb to point to the new Quiz_Data directory. - Added syllabus.yaml to define the course structure for the Python Apprentice program, including modules and lessons. - Made minor adjustments to the Iterable_Turtle.py code for clarity. --- lessons/.jtl/syllabus.yaml | 235 ++++++++++++++++ lessons/10_Turtles/Module_One_Quiz.ipynb | 253 +---------------- .../20_Types_and_Logic/Module_Two_Quiz.ipynb | 257 +----------------- lessons/30_Loops/130_Iterable_Turtle.py | 2 +- lessons/30_Loops/Module_Three_Quiz.ipynb | 2 +- .../Module_Four_Quiz.ipynb | 2 +- 6 files changed, 245 insertions(+), 506 deletions(-) create mode 100644 lessons/.jtl/syllabus.yaml diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml new file mode 100644 index 00000000..3252c3ee --- /dev/null +++ b/lessons/.jtl/syllabus.yaml @@ -0,0 +1,235 @@ +name: Python Apprentice +module_dir: .. +uid: 283c41e4-8abe-49bd-9335-e19ba693277b +modules: +- name: Introduction to Python with Turtle Graphics + uid: Io0hFJiW + lessons: + - name: Welcome + uid: AZCeRx3O + lessons: + - name: Welcome + uid: RRTPqCQu + exercise: 10_Turtles/10_Welcome/10_Welcome.ipynb + - name: Open The Screen + uid: KmgIQbhr + exercise: 10_Turtles/10_Welcome/20_Open_The_Screen.ipynb + - name: Run Programs + uid: cNLK6qtR + exercise: 10_Turtles/10_Welcome/30_Run_Programs.ipynb + - name: Introducing Tina + uid: NtvhO8WR + lessons: + - name: Meet Tina + uid: tvO1dlwm + lesson: 10_Turtles/20_Introducing_Tina/10_Meet_Tina/README.md + exercise: 10_Turtles/20_Introducing_Tina/10_Meet_Tina/Meet_Tina.py + display: true + - name: What Can Tina Do + uid: 7tUP3zAZ + exercise: 10_Turtles/20_Introducing_Tina/20_What_Can_Tina_Do.ipynb + - name: Squares and Circles + uid: E7KlecQ3 + lesson: 10_Turtles/20_Introducing_Tina/30_Squares_and_Circles/README.md + exercise: 10_Turtles/20_Introducing_Tina/30_Squares_and_Circles/Squares_and_Circles.py + display: true + - name: Check In Your Code + uid: doD6P7fk + exercise: 10_Turtles/20_Introducing_Tina/40_Check_In_Your_Code.ipynb + - name: Turtle Tricks + uid: c6l7hD7a + lessons: + - name: Turtle Tricks + exercise: 10_Turtles/30_Turtle_Tricks/10_Turtle_Tricks.py + display: true + - name: Turtle Tricks + exercise: 10_Turtles/30_Turtle_Tricks/20_Turtle_Tricks.py + display: true + - name: Turtle Tricks + exercise: 10_Turtles/30_Turtle_Tricks/30_Turtle_Tricks.py + display: true + - name: Loops + uid: wVDXfv96 + lessons: + - name: Loops + uid: abX8sNwB + exercise: 10_Turtles/40_Loops/10_Loops.ipynb + - name: Loop With Turtle + uid: 85lNu5qj + exercise: 10_Turtles/40_Loops/20_Loop_with_Turtle.py + display: true + - 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 + uid: HOBo0wvj + 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/30_More_Turtle_Programs.py + - name: More Turtle Programs + exercise: 10_Turtles/60_More_Turtle_Programs/40_More_Turtle_Programs.py + - name: Projects + uid: eo3RVnyt + lessons: + - name: LeagueBot + exercise: 10_Turtles/70_Projects/10_LeagueBot.py + display: true + - name: Tash Me + exercise: 10_Turtles/70_Projects/20_Tash_Me.py + - name: Tash Me Click + 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: Introducing Lists + uid: g4kLhJ2U + lessons: + - name: Lists + uid: 0KEhJUGe + exercise: 10_Turtles/80_Introducing_Lists/10_Lists.ipynb + - name: Color Lines + 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: PCEP Alignment + uid: ryHvW6vk + lessons: + - name: Operators and Types + uid: HXQZ0Iui + 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/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/50_My_Ages.py + display: true + - name: Simple Adder + exercise: 20_Types_and_Logic/60_Simple_Adder.py + - name: Infuriating Calculator + exercise: 20_Types_and_Logic/70_Infuriating_Calculator.py + - name: Code Challenges + uid: jJI6aomB + exercise: 20_Types_and_Logic/80_Code_Challenges.ipynb +- name: PCEP Alignment + uid: TzgRqJlw + lessons: + - name: Iteration + uid: ITpqcvxv + exercise: 30_Loops/10_Iteration.ipynb + - name: For Loop Gauntlet + uid: 8yGSkBgV + exercise: 30_Loops/100_For_Loop_Gauntlet.ipynb + - 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 + 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: 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/30_Dicts_Sets.ipynb + - name: Funny Words Db + uid: VenVwSQz + exercise: 40_Data_Structures_Func/40_Funny_Words_Db.py + display: true + - name: Splat Comprehension + uid: mU94qia6 + exercise: 40_Data_Structures_Func/50_Splat_Comprehension.ipynb + - name: Tic Tac Toe + uid: mNMfKPiT + exercise: 40_Data_Structures_Func/60_Tic_Tac_Toe.py + display: true +- name: Projects + uid: rLq5eVeW + lessons: + - 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/Module_One_Quiz.ipynb b/lessons/10_Turtles/Module_One_Quiz.ipynb index eacfb375..9f5bcbd3 100644 --- a/lessons/10_Turtles/Module_One_Quiz.ipynb +++ b/lessons/10_Turtles/Module_One_Quiz.ipynb @@ -10,256 +10,11 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "495aaca3", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
                      " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/javascript": "var questionsmROGvaXVwQFo=[\n {\n \"question\": \"What does forward() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Moves the turtle forward\", \"correct\": true, \"feedback\": \"Correct! forward() moves the turtle in the direction it's facing.\" },\n { \"answer\": \"Turns the turtle\", \"correct\": false, \"feedback\": \"Incorrect. forward() moves, not turns.\" },\n { \"answer\": \"Changes color\", \"correct\": false, \"feedback\": \"Incorrect. forward() is for movement.\" },\n { \"answer\": \"Draws a circle\", \"correct\": false, \"feedback\": \"Incorrect. Use circle() to draw circles.\" }\n ]\n },\n {\n \"question\": \"Which command turns the turtle left?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"left()\", \"correct\": true, \"feedback\": \"Correct! left() turns the turtle to the left.\" },\n { \"answer\": \"turn()\", \"correct\": false, \"feedback\": \"Incorrect. Use left() or right().\" },\n { \"answer\": \"rotate()\", \"correct\": false, \"feedback\": \"Incorrect. Use left() to turn.\" },\n { \"answer\": \"spin()\", \"correct\": false, \"feedback\": \"Incorrect. Use left() to turn left.\" }\n ]\n },\n {\n \"question\": \"How many degrees is a square corner?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"90 degrees\", \"correct\": true, \"feedback\": \"Correct! Square corners are 90 degrees.\" },\n { \"answer\": \"45 degrees\", \"correct\": false, \"feedback\": \"Incorrect. Squares have 90-degree corners.\" },\n { \"answer\": \"180 degrees\", \"correct\": false, \"feedback\": \"Incorrect. That's a straight line.\" },\n { \"answer\": \"360 degrees\", \"correct\": false, \"feedback\": \"Incorrect. That's a full circle.\" }\n ]\n },\n {\n \"question\": \"What does penup() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Stops drawing when the turtle moves\", \"correct\": true, \"feedback\": \"Correct! penup() lifts the pen so the turtle doesn't draw.\" },\n { \"answer\": \"Stops the turtle\", \"correct\": false, \"feedback\": \"Incorrect. The turtle can still move.\" },\n { \"answer\": \"Moves the turtle up\", \"correct\": false, \"feedback\": \"Incorrect. It controls drawing, not direction.\" },\n { \"answer\": \"Deletes the drawing\", \"correct\": false, \"feedback\": \"Incorrect. It just lifts the pen.\" }\n ]\n },\n {\n \"question\": \"When would you use penup()?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"When you want to move without drawing\", \"correct\": true, \"feedback\": \"Correct! Use penup() to move the turtle without leaving a line.\" },\n { \"answer\": \"When you want to draw faster\", \"correct\": false, \"feedback\": \"Incorrect. Use speed() to change drawing speed.\" },\n { \"answer\": \"When you want to change colors\", \"correct\": false, \"feedback\": \"Incorrect. Use pencolor() to change colors.\" },\n { \"answer\": \"When you want to stop the program\", \"correct\": false, \"feedback\": \"Incorrect. penup() just stops drawing.\" }\n ]\n },\n {\n \"question\": \"Which command changes the pen color?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"pencolor()\", \"correct\": true, \"feedback\": \"Correct! pencolor() sets the drawing color.\" },\n { \"answer\": \"color()\", \"correct\": false, \"feedback\": \"Close, but pencolor() is more specific for the pen.\" },\n { \"answer\": \"setcolor()\", \"correct\": false, \"feedback\": \"Incorrect. Use pencolor().\" },\n { \"answer\": \"changecolor()\", \"correct\": false, \"feedback\": \"Incorrect. Use pencolor().\" }\n ]\n },\n {\n \"question\": \"What does goto(100, 100) do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Moves turtle to position (100, 100)\", \"correct\": true, \"feedback\": \"Correct! goto() moves to specific coordinates.\" },\n { \"answer\": \"Moves forward 100 steps\", \"correct\": false, \"feedback\": \"Incorrect. goto() moves to coordinates.\" },\n { \"answer\": \"Turns 100 degrees\", \"correct\": false, \"feedback\": \"Incorrect. goto() is for position, not turning.\" },\n { \"answer\": \"Draws 100 circles\", \"correct\": false, \"feedback\": \"Incorrect. goto() moves the turtle.\" }\n ]\n },\n {\n \"question\": \"What is a loop?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Code that repeats multiple times\", \"correct\": true, \"feedback\": \"Correct! Loops repeat code automatically.\" },\n { \"answer\": \"Code that runs once\", \"correct\": false, \"feedback\": \"Incorrect. Loops repeat code.\" },\n { \"answer\": \"A type of variable\", \"correct\": false, \"feedback\": \"Incorrect. Loops are for repetition.\" },\n { \"answer\": \"A drawing command\", \"correct\": false, \"feedback\": \"Incorrect. Loops control how many times code runs.\" }\n ]\n },\n {\n \"question\": \"How many times does range(5) repeat?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"5 times\", \"correct\": true, \"feedback\": \"Correct! range(5) creates 0, 1, 2, 3, 4 - that's 5 numbers.\" },\n { \"answer\": \"4 times\", \"correct\": false, \"feedback\": \"Incorrect. range(5) gives you 5 numbers.\" },\n { \"answer\": \"6 times\", \"correct\": false, \"feedback\": \"Incorrect. range(5) stops at 4.\" },\n { \"answer\": \"Forever\", \"correct\": false, \"feedback\": \"Incorrect. range(5) repeats exactly 5 times.\" }\n ]\n },\n {\n \"question\": \"What is the correct syntax for a for loop?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"for i in range(5):\\n tina.forward(50)\", \"correct\": true, \"feedback\": \"Correct! Use 'for', 'in', 'range()', and a colon.\" },\n { \"code\": \"for i range(5)\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Missing 'in' and the colon.\" },\n { \"code\": \"loop 5 times:\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Python uses 'for i in range()'.\" },\n { \"code\": \"repeat(5):\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Use 'for i in range(5):'.\" }\n ]\n },\n {\n \"question\": \"Why use loops instead of writing the same code many times?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Loops are shorter and easier to change\", \"correct\": true, \"feedback\": \"Correct! Loops save time and reduce mistakes.\" },\n { \"answer\": \"Loops make programs slower\", \"correct\": false, \"feedback\": \"Incorrect. Loops are efficient.\" },\n { \"answer\": \"Loops are harder to read\", \"correct\": false, \"feedback\": \"Incorrect. Loops make code clearer.\" },\n { \"answer\": \"You can't use loops in Python\", \"correct\": false, \"feedback\": \"Incorrect. Loops are essential in Python.\" }\n ]\n },\n {\n \"question\": \"What is a variable?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A container that stores a value\", \"correct\": true, \"feedback\": \"Correct! Variables hold information you can use later.\" },\n { \"answer\": \"A type of loop\", \"correct\": false, \"feedback\": \"Incorrect. Variables store data.\" },\n { \"answer\": \"A drawing command\", \"correct\": false, \"feedback\": \"Incorrect. Variables store values.\" },\n { \"answer\": \"A color\", \"correct\": false, \"feedback\": \"Incorrect. Variables can store many types of data.\" }\n ]\n },\n {\n \"question\": \"Which creates a variable named 'size' with value 100?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"size = 100\", \"correct\": true, \"feedback\": \"Correct! Use = to assign a value to a variable.\" },\n { \"code\": \"size == 100\", \"correct\": false, \"feedback\": \"Incorrect. == checks equality. Use = to assign.\" },\n { \"code\": \"100 = size\", \"correct\": false, \"feedback\": \"Incorrect. Variable name goes on the left.\" },\n { \"code\": \"var size = 100\", \"correct\": false, \"feedback\": \"Incorrect. Python doesn't use 'var'.\" }\n ]\n },\n {\n \"question\": \"What happens when you run this code: x = 5; x = x + 1?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"x becomes 6\", \"correct\": true, \"feedback\": \"Correct! x is updated to its old value plus 1.\" },\n { \"answer\": \"x stays 5\", \"correct\": false, \"feedback\": \"Incorrect. x gets a new value.\" },\n { \"answer\": \"x becomes 0\", \"correct\": false, \"feedback\": \"Incorrect. x increases by 1.\" },\n { \"answer\": \"Error\", \"correct\": false, \"feedback\": \"Incorrect. This is valid Python code.\" }\n ]\n },\n {\n \"question\": \"What is a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Reusable code with a name\", \"correct\": true, \"feedback\": \"Correct! Functions are named blocks of code you can use repeatedly.\" },\n { \"answer\": \"A variable\", \"correct\": false, \"feedback\": \"Incorrect. Functions contain code, not just values.\" },\n { \"answer\": \"A loop\", \"correct\": false, \"feedback\": \"Incorrect. Functions and loops are different.\" },\n { \"answer\": \"A color\", \"correct\": false, \"feedback\": \"Incorrect. Functions are code blocks.\" }\n ]\n },\n {\n \"question\": \"Which keyword starts a function definition?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"def\", \"correct\": true, \"feedback\": \"Correct! Use 'def' to define a function.\" },\n { \"answer\": \"function\", \"correct\": false, \"feedback\": \"Incorrect. Python uses 'def'.\" },\n { \"answer\": \"func\", \"correct\": false, \"feedback\": \"Incorrect. Use 'def'.\" },\n { \"answer\": \"define\", \"correct\": false, \"feedback\": \"Incorrect. The keyword is 'def'.\" }\n ]\n },\n {\n \"question\": \"What is the correct way to define a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"def my_function():\\n tina.forward(50)\", \"correct\": true, \"feedback\": \"Correct! Use def, name, parentheses, colon, and indented code.\" },\n { \"code\": \"def my_function()\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Missing the colon after ().\" },\n { \"code\": \"function my_function():\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Use 'def', not 'function'.\" },\n { \"code\": \"my_function():\\n tina.forward(50)\", \"correct\": false, \"feedback\": \"Incorrect. Missing 'def' keyword.\" }\n ]\n },\n {\n \"question\": \"When would you use a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"When you want to reuse the same code\", \"correct\": true, \"feedback\": \"Correct! Functions let you use code multiple times.\" },\n { \"answer\": \"When you want to store a number\", \"correct\": false, \"feedback\": \"Incorrect. Use variables to store values.\" },\n { \"answer\": \"When you want to repeat code a specific number of times\", \"correct\": false, \"feedback\": \"Incorrect. That's what loops are for.\" },\n { \"answer\": \"When you want to change colors\", \"correct\": false, \"feedback\": \"Incorrect. Functions are for organizing reusable code.\" }\n ]\n },\n {\n \"question\": \"What does return do in a function?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Sends a value back from the function\", \"correct\": true, \"feedback\": \"Correct! return gives back a result.\" },\n { \"answer\": \"Prints to the screen\", \"correct\": false, \"feedback\": \"Incorrect. That's what print() does.\" },\n { \"answer\": \"Stops the entire program\", \"correct\": false, \"feedback\": \"Incorrect. return only exits the function.\" },\n { \"answer\": \"Creates a variable\", \"correct\": false, \"feedback\": \"Incorrect. return sends values back.\" }\n ]\n },\n {\n \"question\": \"What is a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A collection of items in order\", \"correct\": true, \"feedback\": \"Correct! Lists hold multiple items like [1, 2, 3].\" },\n { \"answer\": \"A single number\", \"correct\": false, \"feedback\": \"Incorrect. Lists hold multiple items.\" },\n { \"answer\": \"A function\", \"correct\": false, \"feedback\": \"Incorrect. Lists are data containers.\" },\n { \"answer\": \"A loop\", \"correct\": false, \"feedback\": \"Incorrect. Lists store data.\" }\n ]\n },\n {\n \"question\": \"Which is the correct way to create a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"colors = ['red', 'blue', 'green']\", \"correct\": true, \"feedback\": \"Correct! Use square brackets with items separated by commas.\" },\n { \"code\": \"colors = ('red', 'blue', 'green')\", \"correct\": false, \"feedback\": \"Incorrect. Parentheses create tuples. Use [].\" },\n { \"code\": \"colors = {'red', 'blue', 'green'}\", \"correct\": false, \"feedback\": \"Incorrect. Curly braces create sets. Use [].\" },\n { \"code\": \"colors = 'red', 'blue', 'green'\", \"correct\": false, \"feedback\": \"Incorrect. Use square brackets [].\" }\n ]\n },\n {\n \"question\": \"How do you get the first item from a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"list[0]\", \"correct\": true, \"feedback\": \"Correct! Python lists start at index 0.\" },\n { \"answer\": \"list[1]\", \"correct\": false, \"feedback\": \"Incorrect. That's the second item. Use [0].\" },\n { \"answer\": \"list.first()\", \"correct\": false, \"feedback\": \"Incorrect. Use [0] to get the first item.\" },\n { \"answer\": \"first(list)\", \"correct\": false, \"feedback\": \"Incorrect. Use list[0].\" }\n ]\n },\n {\n \"question\": \"What does append() do to a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Adds an item to the end\", \"correct\": true, \"feedback\": \"Correct! append() adds items to the end of the list.\" },\n { \"answer\": \"Removes an item\", \"correct\": false, \"feedback\": \"Incorrect. Use remove() to delete items.\" },\n { \"answer\": \"Sorts the list\", \"correct\": false, \"feedback\": \"Incorrect. Use sort() to order items.\" },\n { \"answer\": \"Counts items\", \"correct\": false, \"feedback\": \"Incorrect. Use len() to count items.\" }\n ]\n },\n {\n \"question\": \"How do you loop through each item in a list?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"for item in my_list:\\n print(item)\", \"correct\": true, \"feedback\": \"Correct! This loops through each item in the list.\" },\n { \"code\": \"for my_list in item:\\n print(item)\", \"correct\": false, \"feedback\": \"Incorrect. It should be 'for item in my_list'.\" },\n { \"code\": \"loop my_list:\\n print(item)\", \"correct\": false, \"feedback\": \"Incorrect. Use 'for item in my_list:'.\" },\n { \"code\": \"for item:\\n print(my_list)\", \"correct\": false, \"feedback\": \"Incorrect. Missing 'in my_list'.\" }\n ]\n },\n {\n \"question\": \"What does circle(50) draw?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A circle with radius 50\", \"correct\": true, \"feedback\": \"Correct! The number is the radius.\" },\n { \"answer\": \"50 circles\", \"correct\": false, \"feedback\": \"Incorrect. It draws one circle.\" },\n { \"answer\": \"A square\", \"correct\": false, \"feedback\": \"Incorrect. circle() draws circles.\" },\n { \"answer\": \"A line 50 units long\", \"correct\": false, \"feedback\": \"Incorrect. Use forward() for lines.\" }\n ]\n },\n {\n \"question\": \"Where is position (0, 0) on the turtle screen?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Center of the screen\", \"correct\": true, \"feedback\": \"Correct! (0, 0) is in the middle.\" },\n { \"answer\": \"Top left corner\", \"correct\": false, \"feedback\": \"Incorrect. (0, 0) is the center.\" },\n { \"answer\": \"Bottom right corner\", \"correct\": false, \"feedback\": \"Incorrect. (0, 0) is the center.\" },\n { \"answer\": \"Top right corner\", \"correct\": false, \"feedback\": \"Incorrect. (0, 0) is in the middle.\" }\n ]\n },\n {\n \"question\": \"What does speed(0) do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Makes turtle draw fastest\", \"correct\": true, \"feedback\": \"Correct! speed(0) is the fastest setting.\" },\n { \"answer\": \"Stops the turtle\", \"correct\": false, \"feedback\": \"Incorrect. 0 means fastest.\" },\n { \"answer\": \"Makes turtle slowest\", \"correct\": false, \"feedback\": \"Incorrect. 0 is fastest.\" },\n { \"answer\": \"Makes turtle invisible\", \"correct\": false, \"feedback\": \"Incorrect. Use hideturtle() for that.\" }\n ]\n },\n {\n \"question\": \"What does # at the start of a line mean?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"It's a comment (Python ignores it)\", \"correct\": true, \"feedback\": \"Correct! Comments explain code to humans.\" },\n { \"answer\": \"It's a command\", \"correct\": false, \"feedback\": \"Incorrect. # creates comments.\" },\n { \"answer\": \"It causes an error\", \"correct\": false, \"feedback\": \"Incorrect. Comments are ignored.\" },\n { \"answer\": \"It makes code run faster\", \"correct\": false, \"feedback\": \"Incorrect. Comments don't affect speed.\" }\n ]\n },\n {\n \"question\": \"Which code correctly draws a triangle?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"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.\" },\n { \"code\": \"for i in range(4):\\n tina.forward(100)\\n tina.left(120)\", \"correct\": false, \"feedback\": \"Incorrect. Triangle has 3 sides, not 4.\" },\n { \"code\": \"for i in range(3):\\n tina.forward(100)\\n tina.left(90)\", \"correct\": false, \"feedback\": \"Incorrect. Triangle needs 120-degree turns.\" },\n { \"code\": \"tina.triangle(100)\", \"correct\": false, \"feedback\": \"Incorrect. There's no triangle() method.\" }\n ]\n },\n {\n \"question\": \"What does exitonclick() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Keeps window open until you click\", \"correct\": true, \"feedback\": \"Correct! The window waits for a click before closing.\" },\n { \"answer\": \"Closes window immediately\", \"correct\": false, \"feedback\": \"Incorrect. It waits for a click.\" },\n { \"answer\": \"Opens a new window\", \"correct\": false, \"feedback\": \"Incorrect. It controls the current window.\" },\n { \"answer\": \"Saves your drawing\", \"correct\": false, \"feedback\": \"Incorrect. It manages window closing.\" }\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 input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\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 var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n 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 //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\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 aDiv.append(lab);\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) {\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(\"mROGvaXVwQFo\")) {\n show_questions(questionsmROGvaXVwQFo, mROGvaXVwQFo);\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(\"lessons/Quiz_Data/Module_One_Quiz.json\")" - ] + "outputs": [], + "source": "from jupyterquiz import display_quiz\n\ndisplay_quiz(\"lessons/.jtl/Quiz_Data/Module_One_Quiz.json\")" } ], "metadata": { @@ -283,4 +38,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ 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 index 29404bb1..3152cfd2 100644 --- a/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb +++ b/lessons/20_Types_and_Logic/Module_Two_Quiz.ipynb @@ -10,262 +10,11 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "bcd265b8", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
                        " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/javascript": "var questionshuTSxmDoHOMj=[\n {\n \"question\": \"What is an int in Python?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"A whole number\", \"correct\": true, \"feedback\": \"Correct! int stands for integer and represents whole numbers like 5, -10, or 0.\" },\n { \"answer\": \"A decimal number\", \"correct\": false, \"feedback\": \"Incorrect. Decimal numbers use the float type.\" },\n { \"answer\": \"A text value\", \"correct\": false, \"feedback\": \"Incorrect. Text values use the str type.\" },\n { \"answer\": \"A True/False value\", \"correct\": false, \"feedback\": \"Incorrect. True/False values use the bool type.\" }\n ]\n },\n {\n \"question\": \"What is a boolean?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"True or False value\", \"correct\": true, \"feedback\": \"Correct! A boolean (bool) can only be True or False.\" },\n { \"answer\": \"A number\", \"correct\": false, \"feedback\": \"Incorrect. Numbers are int or float types.\" },\n { \"answer\": \"A word or sentence\", \"correct\": false, \"feedback\": \"Incorrect. Words and sentences are strings (str).\" },\n { \"answer\": \"A list of items\", \"correct\": false, \"feedback\": \"Incorrect. Lists are a different data type.\" }\n ]\n },\n {\n \"question\": \"What does the % operator do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Returns the remainder\", \"correct\": true, \"feedback\": \"Correct! 7 % 3 gives 1 because 7 divided by 3 leaves a remainder of 1.\" },\n { \"answer\": \"Divides numbers\", \"correct\": false, \"feedback\": \"Incorrect. Use / or // for division.\" },\n { \"answer\": \"Multiplies numbers\", \"correct\": false, \"feedback\": \"Incorrect. Use * for multiplication.\" },\n { \"answer\": \"Calculates percentage\", \"correct\": false, \"feedback\": \"Incorrect. Despite the symbol, % finds the remainder, not percentages.\" }\n ]\n },\n {\n \"question\": \"What is // used for?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Division without decimals\", \"correct\": true, \"feedback\": \"Correct! 11 // 4 gives 2, dropping the decimal part.\" },\n { \"answer\": \"Regular division\", \"correct\": false, \"feedback\": \"Incorrect. Regular division uses /.\" },\n { \"answer\": \"Finding remainders\", \"correct\": false, \"feedback\": \"Incorrect. Use % to find remainders.\" },\n { \"answer\": \"Adding comments\", \"correct\": false, \"feedback\": \"Incorrect. Comments use #.\" }\n ]\n },\n {\n \"question\": \"What does str() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Converts to text\", \"correct\": true, \"feedback\": \"Correct! str(42) converts the number 42 to the text '42'.\" },\n { \"answer\": \"Converts to a number\", \"correct\": false, \"feedback\": \"Incorrect. Use int() or float() for numbers.\" },\n { \"answer\": \"Checks the data type\", \"correct\": false, \"feedback\": \"Incorrect. Use type() to check data types.\" },\n { \"answer\": \"Makes text uppercase\", \"correct\": false, \"feedback\": \"Incorrect. Use upper() for uppercase.\" }\n ]\n },\n {\n \"question\": \"Which is correct Python syntax?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"if x == 5:\", \"correct\": true, \"feedback\": \"Correct! Use == for comparison and : to start the if block.\" },\n { \"code\": \"if x = 5:\", \"correct\": false, \"feedback\": \"Incorrect. Use = for assignment, but == for comparison.\" },\n { \"code\": \"if x == 5\", \"correct\": false, \"feedback\": \"Incorrect. Missing the colon (:) at the end.\" },\n { \"code\": \"if (x == 5):\", \"correct\": false, \"feedback\": \"Incorrect. Parentheses aren't needed in Python if statements.\" }\n ]\n },\n {\n \"question\": \"What does upper() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Makes text UPPERCASE\", \"correct\": true, \"feedback\": \"Correct! 'hello'.upper() returns 'HELLO'.\" },\n { \"answer\": \"Makes text lowercase\", \"correct\": false, \"feedback\": \"Incorrect. Use lower() for lowercase.\" },\n { \"answer\": \"Capitalizes first letter\", \"correct\": false, \"feedback\": \"Incorrect. Use title() or capitalize() for that.\" },\n { \"answer\": \"Removes spaces\", \"correct\": false, \"feedback\": \"Incorrect. Use strip() to remove spaces.\" }\n ]\n },\n {\n \"question\": \"What is 10 % 3?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"1\", \"correct\": true, \"feedback\": \"Correct! 10 divided by 3 is 3 with remainder 1.\" },\n { \"answer\": \"3\", \"correct\": false, \"feedback\": \"Incorrect. That's 10 // 3 (quotient without remainder).\" },\n { \"answer\": \"3.33\", \"correct\": false, \"feedback\": \"Incorrect. That's 10 / 3 (regular division).\" },\n { \"answer\": \"0\", \"correct\": false, \"feedback\": \"Incorrect. There is a remainder when dividing 10 by 3.\" }\n ]\n },\n {\n \"question\": \"Which creates a string?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"name = \\\"Alice\\\"\", \"correct\": true, \"feedback\": \"Correct! Quotes create strings in Python.\" },\n { \"code\": \"name = Alice\", \"correct\": false, \"feedback\": \"Incorrect. Without quotes, Python looks for a variable named Alice.\" },\n { \"code\": \"name = str\", \"correct\": false, \"feedback\": \"Incorrect. This assigns the str type itself, not a string value.\" },\n { \"code\": \"name = 'Alice\", \"correct\": false, \"feedback\": \"Incorrect. Missing closing quote.\" }\n ]\n },\n {\n \"question\": \"What does type() tell you?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"The data type\", \"correct\": true, \"feedback\": \"Correct! type(5) returns int, type('hi') returns str.\" },\n { \"answer\": \"The value\", \"correct\": false, \"feedback\": \"Incorrect. type() shows the type, not the value itself.\" },\n { \"answer\": \"The length\", \"correct\": false, \"feedback\": \"Incorrect. Use len() for length.\" },\n { \"answer\": \"If it's valid code\", \"correct\": false, \"feedback\": \"Incorrect. type() doesn't validate code syntax.\" }\n ]\n },\n {\n \"question\": \"What does == check?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"If values are equal\", \"correct\": true, \"feedback\": \"Correct! 5 == 5 returns True because they're equal.\" },\n { \"answer\": \"Assigns a value\", \"correct\": false, \"feedback\": \"Incorrect. Use = for assignment, not ==.\" },\n { \"answer\": \"If values are not equal\", \"correct\": false, \"feedback\": \"Incorrect. Use != for not equal.\" },\n { \"answer\": \"If one is greater\", \"correct\": false, \"feedback\": \"Incorrect. Use > for greater than.\" }\n ]\n },\n {\n \"question\": \"What is 15 // 4?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"3\", \"correct\": true, \"feedback\": \"Correct! Floor division gives 3, ignoring the remainder.\" },\n { \"answer\": \"3.75\", \"correct\": false, \"feedback\": \"Incorrect. That's regular division (15 / 4).\" },\n { \"answer\": \"4\", \"correct\": false, \"feedback\": \"Incorrect. 4 goes into 15 three times, not four.\" },\n { \"answer\": \"3.0\", \"correct\": false, \"feedback\": \"Incorrect. With integers, // returns an integer (3), not a float.\" }\n ]\n },\n {\n \"question\": \"What does strip() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Removes spaces from ends\", \"correct\": true, \"feedback\": \"Correct! ' hi '.strip() returns 'hi'.\" },\n { \"answer\": \"Removes all spaces\", \"correct\": false, \"feedback\": \"Incorrect. strip() only removes spaces from the start and end.\" },\n { \"answer\": \"Splits text into parts\", \"correct\": false, \"feedback\": \"Incorrect. Use split() to divide text.\" },\n { \"answer\": \"Makes text lowercase\", \"correct\": false, \"feedback\": \"Incorrect. Use lower() for lowercase.\" }\n ]\n },\n {\n \"question\": \"What causes a TypeError?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"5 + \\\"10\\\"\", \"correct\": true, \"feedback\": \"Correct! Can't add a number and string directly. Convert first.\" },\n { \"code\": \"5 + 10\", \"correct\": false, \"feedback\": \"Incorrect. This is valid and equals 15.\" },\n { \"code\": \"\\\"5\\\" + \\\"10\\\"\", \"correct\": false, \"feedback\": \"Incorrect. This is valid string concatenation ('510').\" },\n { \"code\": \"int(\\\"10\\\") + 5\", \"correct\": false, \"feedback\": \"Incorrect. This is valid after conversion (equals 15).\" }\n ]\n },\n {\n \"question\": \"What does 'and' require?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Both conditions True\", \"correct\": true, \"feedback\": \"Correct! True and True is True, but anything else is False.\" },\n { \"answer\": \"At least one condition True\", \"correct\": false, \"feedback\": \"Incorrect. That's what 'or' requires.\" },\n { \"answer\": \"Both conditions False\", \"correct\": false, \"feedback\": \"Incorrect. Then the result would be False.\" },\n { \"answer\": \"One True, one False\", \"correct\": false, \"feedback\": \"Incorrect. Then 'and' returns False.\" }\n ]\n },\n {\n \"question\": \"What is float used for?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Decimal numbers\", \"correct\": true, \"feedback\": \"Correct! float represents numbers with decimals like 3.14 or 2.5.\" },\n { \"answer\": \"Whole numbers only\", \"correct\": false, \"feedback\": \"Incorrect. Use int for whole numbers.\" },\n { \"answer\": \"Text values\", \"correct\": false, \"feedback\": \"Incorrect. Use str for text.\" },\n { \"answer\": \"True/False values\", \"correct\": false, \"feedback\": \"Incorrect. Use bool for True/False.\" }\n ]\n },\n {\n \"question\": \"Which starts an if statement?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"code\": \"if x > 5:\", \"correct\": true, \"feedback\": \"Correct! Use if, a condition, then a colon.\" },\n { \"code\": \"if x > 5\", \"correct\": false, \"feedback\": \"Incorrect. Missing the colon (:).\" },\n { \"code\": \"if: x > 5\", \"correct\": false, \"feedback\": \"Incorrect. The colon comes after the condition.\" },\n { \"code\": \"x > 5 if:\", \"correct\": false, \"feedback\": \"Incorrect. Wrong order; start with 'if'.\" }\n ]\n },\n {\n \"question\": \"What does lower() do?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Makes text lowercase\", \"correct\": true, \"feedback\": \"Correct! 'HELLO'.lower() returns 'hello'.\" },\n { \"answer\": \"Makes text uppercase\", \"correct\": false, \"feedback\": \"Incorrect. Use upper() for uppercase.\" },\n { \"answer\": \"Removes spaces\", \"correct\": false, \"feedback\": \"Incorrect. Use strip() to remove spaces.\" },\n { \"answer\": \"Converts to number\", \"correct\": false, \"feedback\": \"Incorrect. Use int() or float() for numbers.\" }\n ]\n },\n {\n \"question\": \"What is 8 % 4?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"0\", \"correct\": true, \"feedback\": \"Correct! 8 divided by 4 is 2 with no remainder.\" },\n { \"answer\": \"2\", \"correct\": false, \"feedback\": \"Incorrect. That's 8 // 4 (quotient). % gives remainder (0).\" },\n { \"answer\": \"4\", \"correct\": false, \"feedback\": \"Incorrect. The remainder is 0, not 4.\" },\n { \"answer\": \"8\", \"correct\": false, \"feedback\": \"Incorrect. The remainder of 8 % 4 is 0.\" }\n ]\n },\n {\n \"question\": \"What does != mean?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n { \"answer\": \"Not equal\", \"correct\": true, \"feedback\": \"Correct! 5 != 3 is True because they're not equal.\" },\n { \"answer\": \"Equal\", \"correct\": false, \"feedback\": \"Incorrect. Use == for equal.\" },\n { \"answer\": \"Greater than\", \"correct\": false, \"feedback\": \"Incorrect. Use > for greater than.\" },\n { \"answer\": \"Less than\", \"correct\": false, \"feedback\": \"Incorrect. Use < for less than.\" }\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(\"huTSxmDoHOMj\")) {\n show_questions(questionshuTSxmDoHOMj, huTSxmDoHOMj);\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(\"../Quiz_Data/Module_Two_Quiz.json\")" - ] + "outputs": [], + "source": "from jupyterquiz import display_quiz\n\ndisplay_quiz(\"../.jtl/Quiz_Data/Module_Two_Quiz.json\")" } ], "metadata": { diff --git a/lessons/30_Loops/130_Iterable_Turtle.py b/lessons/30_Loops/130_Iterable_Turtle.py index 2028a2a8..aede29a4 100644 --- a/lessons/30_Loops/130_Iterable_Turtle.py +++ b/lessons/30_Loops/130_Iterable_Turtle.py @@ -16,7 +16,7 @@ # 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 ... in zip( ... , ...): +for something in zip( ... , ...): t.color( ... ) t.forward( ... ) t.left( ... ) diff --git a/lessons/30_Loops/Module_Three_Quiz.ipynb b/lessons/30_Loops/Module_Three_Quiz.ipynb index 9c8d1cc3..77c5491e 100644 --- a/lessons/30_Loops/Module_Three_Quiz.ipynb +++ b/lessons/30_Loops/Module_Three_Quiz.ipynb @@ -18,7 +18,7 @@ "id": "e88338fc", "metadata": {}, "outputs": [], - "source": "from jupyterquiz import display_quiz\n\ndisplay_quiz(\"../Quiz_Data/Module_Three_Quiz.json\")" + "source": "from jupyterquiz import display_quiz\n\ndisplay_quiz(\"../.jtl/Quiz_Data/Module_Three_Quiz.json\")" } ], "metadata": { diff --git a/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb b/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb index a5ab5b52..e93159d2 100644 --- a/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb +++ b/lessons/40_Data_Structures_Func/Module_Four_Quiz.ipynb @@ -14,7 +14,7 @@ "id": "5c4ce2ab", "metadata": {}, "outputs": [], - "source": "from jupyterquiz import display_quiz\n\ndisplay_quiz(\"../Quiz_Data/Module_Four_Quiz.json\")" + "source": "from jupyterquiz import display_quiz\n\ndisplay_quiz(\"../.jtl/Quiz_Data/Module_Four_Quiz.json\")" } ], "metadata": { From 2df164be5fdc3a248e13924b96265239c840ee1b Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Sat, 18 Apr 2026 20:48:41 -0700 Subject: [PATCH 158/159] Refactor code structure for improved readability and maintainability --- .vscode/settings.json | 6 +- .vscode/tasks.json | 2 +- .../10_Welcome/20_Open_The_Screen.ipynb | 19 +- .../10_Welcome/30_Run_Programs.ipynb | 137 ++++++++- .../20_What_Can_Tina_Do.ipynb | 69 ++++- .../10_Turtles/70_Projects/10_LeagueBot.py | 2 +- lessons/30_Loops/20_Loops_with_Range.ipynb | 96 ++++--- lessons/30_Loops/Module_Three_Quiz.ipynb | 269 +++++++++++++++++- .../images/badger_video_thumbnail.jpg | Bin 0 -> 139991 bytes 9 files changed, 531 insertions(+), 69 deletions(-) create mode 100644 lessons/30_Loops/images/badger_video_thumbnail.jpg 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/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb b/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb index 8469f2c5..d71a2085 100644 --- a/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb +++ b/lessons/10_Turtles/10_Welcome/20_Open_The_Screen.ipynb @@ -3,12 +3,23 @@ { "cell_type": "markdown", "metadata": {}, - "source": "# **Let's Open Your Virtual Screen**\n\nTo 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\nMost 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\nYou should now be looking at something like the image below. Click the *connect* button.\n
                          \n \"Virtual\n
                          " + "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 3", + "display_name": "Python-Apprentice (3.13.3)", "language": "python", "name": "python3" }, @@ -22,7 +33,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.5" + "version": "3.13.3" }, "syllabus": { "name": "Open The Screen", @@ -31,4 +42,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb index fb61aaab..a7619aea 100644 --- a/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb +++ b/lessons/10_Turtles/10_Welcome/30_Run_Programs.ipynb @@ -3,24 +3,79 @@ { "cell_type": "markdown", "metadata": {}, - "source": "# **Running Programs**\n\nYou 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\nThis 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\nTry running the code below. Move your mouse over the cell and click the ▶️ button." + "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": null, + "execution_count": 1, "metadata": {}, - "outputs": [], - "source": "import datetime\n\n# Get the current date\ndate = datetime.date.today()\n\n# Make a string with a message and the date\ns = \"Hello 👋 World 🌎! Today is\"\n\n# Print the string and the date\nprint(s, date)" + "outputs": [ + { + "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" + "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\nThe 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\nLook at the top of Visual Studio Code for a box that looks like this , and click on it. \n\nThis should have made a dropdown appear in the top-middle of your editor, as shown below:\n\n\n\nNow 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." + "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", @@ -39,7 +94,13 @@ { "cell_type": "markdown", "metadata": {}, - "source": "### **Your First Assignment**\n\n1. Copy the Hello World code from above into the cell below, or type `print(\"Hello World!\")`
                          \n2. Change the string inside the quotation marks so that it also prints your name (e.g., change \"Hello World!\" to \"Your Name\").
                          \n3. Press the play button to run the code.
                          " + "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", @@ -53,22 +114,72 @@ { "cell_type": "markdown", "metadata": {}, - "source": "## **How to Run `.py` Programs**\n\nIn 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\nThe Lesson Browser is a simple way to run `.py` programs in this course.\n\nIn the bottom-left, you'll see buttons like this:\n\n\n\nHere'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\nYou can also run a `.py` program by clicking the ▶️ button in your editor.\n\n1. Click the file name to open it.\n2. In the top right, look for these icons and click the ▶️ run button.\n3. When you're done, just close the window.\n\n### **Using the Debugger**\n\nIf 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\nYou'll see a debug bar like this:\n\n\n\nYou don't need to know all the buttons yet. For now: red square stops the program, green circle runs it again.\n" + "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\nIf a program is already running, close it before starting another.\n\nYou 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
                          " + "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." + "source": [ + "Now, click the Next Lesson button, open the file `Meet_Tina.py`, and try running it." + ] } ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "Python-Apprentice (3.13.3)", "language": "python", "name": "python3" }, @@ -82,7 +193,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.5" + "version": "3.13.3" }, "syllabus": { "name": "Run Programs", @@ -91,4 +202,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ 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 40949815..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 @@ -3,22 +3,79 @@ { "cell_type": "markdown", "metadata": {}, - "source": "# **What Can Tina Do?**\n\nIn 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\nIn 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\nBefore moving on, here's how it works.\n\n### **How Tina Follows Commands**\n\nHere is what some of the program will look like with comments to explain what each line does:\n\n```python\ntina = turtle.Turtle() # Make a turtle named tina\ntina.pencolor('blue') # Change tina's pen color to blue\ntina.forward(200) # Move tina forward by 200 steps\ntina.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." + "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." + ] }, { "cell_type": "markdown", "metadata": {}, - "source": "## **Additional Help**\n\nReading 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
                          " + "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\nRead the steps below, then move on to the Next Lesson.\n\n1. **Run the program** called `Squares_and_Circles.py` to see what Tina draws.\n2. **Read the code** for each line, and try to guess what Tina will do (The comments will help!).\n3. **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.\n4. **Run the program again** and see how Tina's drawing changes." + "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": ".venv", + "display_name": "Python-Apprentice (3.13.3)", "language": "python", "name": "python3" }, @@ -32,7 +89,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.5" + "version": "3.13.3" }, "syllabus": { "name": "What Can Tina Do", @@ -41,4 +98,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ 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 dff4379e..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 diff --git a/lessons/30_Loops/20_Loops_with_Range.ipynb b/lessons/30_Loops/20_Loops_with_Range.ipynb index c07a4104..70073175 100644 --- a/lessons/30_Loops/20_Loops_with_Range.ipynb +++ b/lessons/30_Loops/20_Loops_with_Range.ipynb @@ -22,9 +22,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "range(0, 10)\n" + ] + } + ], "source": [ "# Run Me!\n", "\n", @@ -37,13 +46,29 @@ { "cell_type": "markdown", "metadata": {}, - "source": "### **Efficiency of Range**\n\nAlthough `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\nBy generating numbers only when requested, `range()` stays lightweight regardless of size.\n\nTo visualize the numbers a range produces (recommended for small ranges only), you can convert it to a list:" + "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": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" + ] + } + ], "source": [ "# Run Me!\n", "\n", @@ -71,9 +96,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "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", @@ -98,9 +134,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "Ellipsis" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Test yourself\n", "\n", @@ -110,34 +157,11 @@ { "cell_type": "markdown", "metadata": {}, - "source": [ - "\n", - "\n", - "### **Challenge**\n", - "\n", - "These [Badgers](https://youtu.be/pzagBTcYsYQ?si=xr4QQ7ZkZBGow2j1) are fans of the classic FizzBuzz Programming Challenge, but instead, they have their own rules.\n", - "\n", - "\"Badger\n", - "\n", - "#### **Badger Badger Mushroom Rules:**\n", - "\n", - "In 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?" - ] + "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": null, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -150,7 +174,7 @@ ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "Python-Apprentice (3.13.3)", "language": "python", "name": "python3" }, @@ -164,7 +188,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.5" + "version": "3.13.3" }, "syllabus": { "name": "Loops With Range", diff --git a/lessons/30_Loops/Module_Three_Quiz.ipynb b/lessons/30_Loops/Module_Three_Quiz.ipynb index 77c5491e..c1f94b40 100644 --- a/lessons/30_Loops/Module_Three_Quiz.ipynb +++ b/lessons/30_Loops/Module_Three_Quiz.ipynb @@ -14,16 +14,275 @@ }, { "cell_type": "code", - "execution_count": null, + "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": "from jupyterquiz import display_quiz\n\ndisplay_quiz(\"../.jtl/Quiz_Data/Module_Three_Quiz.json\")" + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python-Apprentice (3.13.3)", "language": "python", "name": "python3" }, @@ -37,9 +296,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.5" + "version": "3.13.3" } }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} 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 0000000000000000000000000000000000000000..a8d328173e677ed112f5b1c6c9844ee3dd512ac6 GIT binary patch literal 139991 zcmbUIWl$S^+&>Bjmm)=iTe0F42@Wl^NT65qx*01-zMO>vI1BEfQAMDprKxXe=C3p02T%&E-pUq z|J#QDoDdh62=y64AR!S62^kp~E+GXK1u4~YQZiCBEG#Ts99&{NJYpd5`E%g^@%FC= zK!S_@0}FtG#tc9wLBk+H`_~Vk|8GPrwEt%Pzc(~=ENmQHObk5KVT

                            rc3y+kaKo%j8 zWmv;BrLdM~>bJL-MXYS$+f?ku|CRv+7-*>f9)kq%8gLW<57jcI%PLfc(o|5$bH0Tz z$#W_vkt1Y_-cfgNF7lY zQ_Q7JG4yyt6`Y>&!s$B#J<$D#(T6bD)%j^&h1sS{^p#CR@)(2GGT8An-3_)!Vvh#+`~ED4mV(z+taLukW`>V!OnqWtLk@( zXv-ypgTo=EjJ0M49~?IefdM#F73}50iiJVsEN-ft%H+V?Pslw;X+$Bn;GeQ#U9oY? zJ3)(Djb^zLm-r#>eiIRC;Yy+R3-Cc#y>fWmEL7V`Q4g*yj?n2h`aCYAsSJxg!G{zJ z*VCpd$IYOYX`USh1R6erX@OKZEks`mk1N>2^^9_6K-8rUEi{8AXUO`Sd3tYp8NJh6 zj_EgB#bcs7u)y*%v%U_d8qAL(V;)4<;yaKA_`H3!i{-hC^F}9}Cvw+C+q;i2i<+K_ z@%fIK2aY?L5ps`W==|)*&ue-~M(c?W@?N=k6YfCUD~W{5I9CRTacv^% zv@WgH^oelOIvmS3=@14`yRZtOZS0LRF}PVfFpu8xd#!b6o}?9pP_$a2&+6ZtdSQ}P z#c`UJyDD|f><_7;wr-b1C8NGadPhg} zNY9eB((H1!v?r&YNW;KCf^N_BCZ+;3Jj4j;D)HnDEr_^F8pX7iUQ_=(E1@wZwttHA z%^)%C5}@_7wR@S4%do#Sy_4GE(XZUYoudBgr;|H&ZvUHOO~+KORa4fB59Fi*0$`~~ zFxgiww-;di6b0~Z&q;D4f8guwPkL3@S-~;|?6Dm=h0TV#F>mG<@&a*FaP0D4?M)5( zOSq1>pRc#c+$+u1bRVi>%}o@rd<{hUrL3_FGJ#(ka!U8;L!i%czhMvaIIMAwfYZBk6h*7DnC}@ zya$M2Z0RJt*!pF-x7QXYgcbo)=czv@8WUx4h3*C--&K?}Rce2YnTy@MI2Q?~{`n7J zN~o|0oOC!$^BuSmM%{XCHUpf>Xv&a}khB7F z1P9jt`A(p)s$k&KR#S`G)%Lc04C_(4D`^lpK}rw+q&LuNC5KR&iOpF{(REER9B}7~sa4IRduX;-qyeGZgvF6BldLQ{>>a@D)K~zs`CY4HXYzVHO z8K^%j_LGVp%Zj-*jtFRe#BsU)G@0Inluma|%>7W%mC2`Rvhd}0k!H~EBuseCLFV}H z5*zm~@)2cqypzm5Q?P=fT2w#HaD_=lFW5l&u;)iDBw|h(;-0b$_i=36`8yMcH`~-R zvv_^1W67HKhHuMs>xI@;UmslHw+6RH%pdx_YS-ltmdiS=#jT^zG4`)qViuAoef@aa?LW=#x%! zOz$9gy7pO(YDnNjwG*~`F^c9cfErHdsHJVBNF4`7~wW=rcA zU@TY+%3gR@H)*icg|Wg=Xg&%&5Giybk`_Tf1`Um6Cc+-T`L?gX@{;fwqjFnTQg4>0m@D=CxiwZ=8j``ftecz9i3tpWHag&kXb`yNc@g0n#o zzNu;|ZwD(^i_77XFKq+acyi5hX=Z;?YG(TaviVq1UV-FkQmUwQD1=&}F1F7vWHq&* zxRo$;n4Dd*7Ib#MTzPi}^cECUl3s?dW*DngO?4qtb$gj&ZUU}(Q%kU*om?2|>K`#^ za~ZZcX+1D*Xqy4L@(0)EgKRm>$wlDlLV_?2)U|w8Go}=Uo3X0ln##~*3QpyiWKu#D)V`xeeJkZ1779n17ZF@S5T$UlSxCX`_ZC9b z`||bBR5=HDG6ibr=RiypcobrPuT6_J)q=t+eRx%k_0v}}g>)Q8ope-Da7n2#uTHEF zvT>v?H7cozvz{+!uc7qW)eW8hq5v~VEj=BP2N#xC8eP{bLv1pwvF$bA|A-%S;8Z4Q zLvYH4r6B+a1$$Y~DVGQ2T3S|BfRL^VwL|%+b@Q#Y#a(F`=qet0q{BulLj@pO!@54}VSBwHmVMO$5B}|p8g=RpT zx*P%dwwwA7U^22jAvCw3IqNMg>t@;6$xbQ|8HPJOkAvw=kQ6z&p{sm%Up;P(ct3yD zB{a!Ham36efPclU)}1NU^@DYUiXf~fjEK;qj1QX<1LIj}9l(qrSbL_47!8S^S$5L} zn{D~3+8H0qR&3Xin9$esJGMGF;(tUpdTLcq0_O1+ zpEns3E33R99infj`b;=tTxpcAwr8>pLI(#fc&reRDqsufuX(YE$<{Dmecz6DZO#{s zHuuVQ1;O983Qw&4E(p{HCfP0^FCP&fbND!oaSn&Uc+TwW9svA{!KwEbtf_-JCf?V9cdDrop%?{eFk=3?UQmQN6s;`h2z<1JUP$Yry*(B-qFGo5zXn-BZxJ^0aig z2HLsv4;y{a6|rxL-QG3l(_RnG=8?;oBz>6n8rug_(Y?|Mv)uML+HpAXTW z{vnzwwp|}kj5kE;2yY(ne!ty(rR|;>ZGMOxab|nmZS4*>H=IZzvRg6+vt;2~kEBpp z`Gw&=Rdy|4{v>8_2w=^*6D6o`#7m&-A`ICOPR_2H;=@`0C z6P~9Xroc}yei{~{@rEisDKX|Nvyc`Gu1tV1V7w)DFRoYFQ z$deVMIZ7Zp@~Z(QJ1k;I`H)ZU4B@ur-{u$QFIRj1KxC7M!#Qb-6R{zL(9CKx*VEJezp{l&yyu=Z?a5)Hqo`!%Hl!H*xk(n?i9OA$Q zrdUL9YH`TR6LM9Mn9YIMi%MgipfstPTJy~4JIruSwjw+XS_c_75 zTD-Bfjl*M}QQoYj6;!w3oIp4Elc`OvL3Vf}XZbi@c_jrc>euGY|FBY7VGQB&aRobt z-Tk7pLOI!5W8`+1_syqrlVWLo7M7+|hg+L!3cKDYq zacS(ke$q%uW%jN|&W#Xo$AT|zXL9rcWs>?l)4 zKM3(Kc#~`Or2kAxQE?}j9>k1;X7;{?S*Wcxmj>1x|vfo5lSX?NTY;C=oR^D>dX@pDuk&T7E?G9s`pE8a;Ie%>y1qKX4(vs!j&Pf-sxZEwEvKv9N1ppbb0#cmG}@?p>O_Z zQ{G{MeM_a2pecs!qgPwv>@m&=pC`%2kgI=hbuCWTbib1!+wd>(rzhf?8Lpyl zHow(g`~&P{>!ki7T|~^D@kQ#{E9<;Dq5MBbU08ik{sXyVK+NYP>G%xR`;SkxDQo>W zAqj_`_w~)j#o#%3gl}VS);@WinEkfHS9bi1vJ_+9Q)BkWE7MK&_H>zG35$COG%50g zu=1LU`PX!B`^wKAneyshjbb3^%a39j4UL4|AOze732==C)dm2_CJgJE(?fE4)sjJ#Znxv zzSuJ!3bD28Q0=tX(SW$%ClTm4D9GNAc;tuG&N%FG%{NvVZ+uQ9tX)i4*&J02GGegQ zc82Y!{idAGaM}BjFJSl&Fw7k)d1PwTuAbV;OZf4%j0H6x`%pdk6>(sznx=texR_6{ z!PSa-%b)bUNB{3515{WDSkx4hARum&s6;owZ6X#s-`UmLsVm7)pis*H$ul8tk1%lf_jH=whrY>(;Y)+V0 z!m$9lI($Xpo}VvyFXb+Fb_#XYj|#Q(oc7;ZfLVVX zv&i*xA%z6+D+?Nh1kkjhE@NN}j1>bu_QqQ6$*fx&mGrmVJu`hT8=g7t-?wgrlxQg* zKU06HA}+k*x0kLGY}&~Wnoi|0-pLEu+vFXYDtIBDRw30sTur%*W4l}$x#wE_J)(uV zGj@*`aTF)^C%$!v`;#P{m8&jt@3tJl$(u_j@kq5=rgF79QdK@xVP858S*mss*w6nU z9Q44L2%@J8brc1C72vmjbuEuPvNDbnHUX0s1txbDw^282r); zp0mL76UQ+iJ!_PFcVj7zXy2xP3T=n57psF&`!TYQt`okoN;^d3Q-!EHz?P>@S(Y7k zXB;4|`Wn>E@cZUKJTJkbPVLO`?Fqlb`WNGubd$oCv@jEGmTL8gofhdXU03U|g65!Q zf8}SlEZWl$IWTo zQFvH`K!imL&7b%0E2R?h!Qj=Mk6@`g-ylu?sX_ZSRsqL ziz=D_2XE7t$Prvh;XrJZ{R`EM3D?rpQffiHCkx^xK^?K8WiCpqSPZu)iiAOB>rukG z`F*$*nWnO~ibVlD6}lfmi(M>SUWkpt>q>AD(;^Di3*ag8E#KMGP?mDCa|2B`ge{nh z&?vT%2b$2ZY{*xAt>akkt}hh}Z(Up2ZxB9qWYHUI_=PN{H-|3NZ1d>CKB*RKB2{~r zl9eH!3Q=0K08ZWpqDFAM`(F=9LxTo|pkaI8rqy8n%|Y|x7#1~m5NLAVs5^xZpI5n- z7Cb&aUilDZ*rKp`cwE6j=>2T7(g~R+JVx6|OAlq!s@bXHo{%Y*36J9&Rl-o_tx;*c ziGvVyI^#clKKRN-Xz|lnfAKqoB<1IWR&(~CV`~AP+-*T3eP*6lz)X)Z@JZ+o{i?u3 zF|`@}R*{7_VYq&_@A0bl7^>>??S>amWtnzKi7c7NIL$8IkvgI=7rNUWi7psqG@!KI zmVgemH&yu!6F_H&2SY&6@+#|c#K$C-&y8!}sFk>RR%^%;Lcw=Bvi^%9?Q~&O*7a?% z0RP>TF5YeNyaiZ-4-VR-Npn}A(B`7BqZ;77yJWzG;yWWVEPB90>W{{1 z&c5ls*Qu(?9&4_}!Dm;FVL_^~^x2S4{Bz1zfz^E_{hg{?j`Wm~x1+%#EUsid=dvl_ zY{5~|j{8(`j}nUGp?T>5UGW#+ahB13$$KX{w?qb$=|*#U%tM|ROKVhxww#RUWxcUY!M}wEf^kMq&y>TJz*ZX zxC6duNic{SSerG+Dg|x02LqHabFv)8&;Pe95o|SU{(HS|AmG+HX`$u}*{|xU<8f+r zTf(UfIf#a&RffKwPQwg`5NatSJ{#2(#GTF3NB?BH#aYvY=ghcs3|E(b^UH5s2~4o( z#cj^qi!sY&l?>_$d3QGFGknzTsDC1%J&=8`D?qh)kd{N9L|`?zV@sQmebLQbfZPhU zB|o%)gVIHg69i>sb}ne=U2$@uh~3$%3`F2BwS__kS|OuUok)?}L*De%KUTU+sT=p! z-o3(}U%1UKMQ!=Iru77p75+N21QiTLc+}qvwWCCjXA6duGp(=-f}V(;>iI~(R_Lme zyfamhs>J1d>BWh$-P3~n%>vgnmy9TyHn>)T*lOp#_-nJV3_(#E!bGI^|J8NXUWj-I zIf1rE4vtF>@9*znUKK?^&d>h=1kfv1e?>vK=3oM%!?W! z{!#%WZvOy8>cN3-Vv5lf$i76~{&}xjC6?4pYeUQIE3@|DUljLi{re0re$)yd!Iz)b z4fSU{=?YwAQY2Cu_%}FNHgt~vU~W%P)y$X~ zP^RoA0MS@7l~##}Pil~8kPy&Sgw#}taE+`WF*Y6Gea)673d)8W2u>)gE`if&dCbIl zF9|v6#F)P}lyH>v4It35?H9!+>_^j~b2Rp`X~vf%%bDE2b}W@eR|WU%L5}i|9B%x% zYa;b-QRAnE>9env4O_G_Fl18t%eKaL1{^ErTjH|ukc_mv7rwgvTOnk^QA0ZV!7q1b zR%Ei=60d4i4gt_ zwa8UIJ`mP3mnwjOeubF%1?;2qUxzZ)6Tg@u4j6hRhqeY#TLHbC3P211)D~245X)+o z7PK+)98e>SMk%GVjjsdSd58HBb&xdV{wqYhyKWjKUA(KN%id#^lYjLY?y5Lj;N#$i z^bfTA`ie^wfs-8i;Bgu>h{GLNfgP`W8@Z(Xd2e#u1}8cATbEdh{yVX+``5bikCauqgLm`d$=m6| z>7sPCbTQQQ!Esfu*a# z0+`!}qPx<(npQEqzT-%quOu1Q*+}dkzPif~FdTSdc7* zT!+WnbD_ODI@CHiJ@c*6-)4)>K-bQ?W#afd-3M3N=hcUmNMY}^v^vpW7~GP zXnFDoHr!dD&$l{`3@8ItiM?&ME9o~#!DTe#vn2Q-a5qeaRk-R+P5XdmD7bEwti5CH zWe)Rt0gGzZbCP$|%b_3;t>_WUPhDG=tDfbTfOH z|MOWW$=aDqi$TJyN)tVFbtf|8+$UPb^S=NHGvxfrBYfVjfm;>Sj z2DPaemlbZOw$S#xMJsWemCk67{{m9eAOO#3U+Z_(?7MF10Udyn)b)TD~G2HMlGW>lZO=UpV z4~1@hc_L|gh>91^l@2pGrf8WkOickp1zTUF5@82+m4DwK^uU2|&E_KAcAg`vmPiMn zXfIgPnE(J&d*H~Q)RVJ5-RBNmyzs5JpI>xn`ypc9S=q5_edVK~t05Llv?02h3n~>> zRiff=Q&;Z%bUBvjOD6s#?|lHE#bd$wP9XPdlQM6oD z2wD#vsnFDtL(v(XO%(ZOrAdZPS6F~ybI541koiCE?hJYatjNQW{IH$!Uo*+)$WjLf zuVdw$=AzTb;+2nM0#r0pwUHGZYLd{RWyfY3%NQ}{^U188@$t4{LFR3xp`Z1;`8#|H zLt<5WbT>6gRa!BA%=6c9rAucwUqPn-G8U#cuF<_4LkOo#soU4=Ujy!Hecf+bBK$r$ z>JsJ+{s#K-ex1bnTJ!o7+yUmfO^?6bJ1_YOBd0D#o@q#w1FK!i1C1Jj#z=U)WJq9D znpSk4w;wE-VWVgCEyA73AURju84}wOyy{*wb-dj7_HH)MXNXa1f62Rocb_se*g`XZkiI1)&)e;}{3o{VFZV5e`1PDtm-)DV7UGG#~udyYAM@trlou~KzbdhI(^!WFUHmPpUOG`r~+H^oNa zaBkIIpnKl}$;!Ap;fPw?PcUP_X*86Khc%UC)VUo8Cm7kE0?prz0A%*JxZ;IA*fK#b4T#)17p5S8wj z!+aWOV|cTrtl3d|gg;>atP#GPl?Z`dbWhb3M6Mq@`dhSg5;&K6F^h+#l)=7qe_q^7 zZu?&CF8>7?x@@cS+#X!Ads=h<<<4j+V3XnXoG@7Y)CD)XPS9ZVDt$VRZeQqFen+^(hyfJ6q}+@xodkN&)0si{I2#igDjMm>8 zW(AWZ7@w3&N}tiHERcz4CHzsaZcg*{B{|!UvNi3NQ|xYWVmDVIsh5;dUmDw5`Wt0= z6}bnIn0-)_)ODr0JKbH%&=vCOT%OFn#;WrchLx7!H9^d6WrYzJ^VOnDkFMT)e}C=Ki&h3^KXx6*D<#FW**dG+;q0PP zoUksk=PQih=JEXLI9$2W=Sbd4)|NoUVsD+?!OX-n7E)+ZTH|%qwo{|Y@X6&@8~7HT z_#KwXI$@Vy!p~nYDVnZ_D!D?{@tcKcMhz|!svzmc9~+}+`g)z=FeD+3<6?e3FXAIBSr zI@?wN^GVGo(EZ)Xa>Ks8%-2Y@w7@jo+WGOF-Nc@?Sr5mIXIB|$h4louoB$jB(v9E6 z9Gds#>Jv5L=-o8IC2opz&kGVbk}cyY=ibSVR7&@tnZ&I9oCV@LJd3J&^pqN}w87#qj&mE^YT4``JqmpOA3tp2Kv@_+BYzTt|74 z>YLowwi-^CrIw>5dO=6LGsZ@GZexf`;raQSe&4zPO1u_;gfa8O~vA+d21!N!B+Ii%m^dL}7Hl8Pahktx&9+QdP#}W`g5PwEj%@ z9kGCBphYqJ6&6bpfvXhVK;g-(C*PX9W3p*+$%kMEqM2W~Gu|@(r|zZettAE^UT1;J z3o}g2mmk2dJUrfdRblh9JwXHW>witO7q640HuDUBcx3)s$-Th%&4bXy&-C+!*Zh8S zS_E$vlN$rIl<3>z{B8*G*Kc2pynAWVKQqN@vYO5(C=1KnmI067$svAR` z;KsOlx8Q#OYY+2{YU?h>p@n5Pl7orrG&94oeh;+VqHL^UhMloauj&t!$z^+*K8GPg z7h-(r47+lJQS20|?(Co4fGdz;v8}386$&_iP(X+vh^jIlhn&8>Gdk?%gX0X(7{eC)MO#2S5twHWTUpR?-~Y@r%f#k~~Bys?Y*|XZXKtS3Vp%r%+Mx zUka$DsY`+)z2VyLXt9gZK!Suxs9-XMX$fYE4+vO-Cb>6>G z5+i7L%)&F;@^9PI^x z^}nclWl|nEe{nN!8y?-_R&BS#5E20rdgDp0&lPsXB8<=`S-Er zu2Dh7=6fws8Z)k#e-92fgylBR{K+^2R=&zcEzJ)1==9 zwY%gY>vm6)6P(Vb+iO9~f6{$|3oaRn?83HxlC z->XIL-MVZeC?2JAC-x~?-bUt~)Rdjt_H4*mlHpkj&{=34TS_hN^@WG$B(`*A@Spwz z)Y9~Jx?btDw#>Mmi-r6HK;q3l*M#hVxmTB0@82mM=OtNZ;OkKw49nAc*J#ZAQkdU_ zuOFO$8jE*ni)T+>r%+vh?R+QYH|d`AHgmKiKYQvJzSyAkc5!X`wtb`H89Q6vsq0JD z!U|F~lTN<-Ow{NUFS*heR5dl~(n#dn!>XLzf{(b02c59~Rs+=}n z%UJ-AySFK~IW&}M>z6&Y{@3Wd%F{%VR4rkI2ALLgM!25!|EB0rd<Jd~GXf~#h zb^P@nh!!|yMCY+SA@gu;cuh+D5si46+i2OrsS|ue>^i}Cq2swn?2%%;C|BiQUC`#Q zA+U$-6CtN;i}tN#Wk)P^(Ghd&55tK@Tja(QVbHatoI&YIz3 zKMv@~kwVdi?{qD@Z#CM4d%GHb?t7_KSD%Hc5Uusr*QU%D1eH5nvW&O0-aupTm&E>( zhj$V|iSDkv`^b9ukdgzRO2i>ucPs14}KbE zVd|G?Py97?om=a0+37y@t)o*U_?%U*NVdf|0ULZ+Tp zAh1?ODSVsA`Uw>Crl%M@(8zbk52Xtc9NT5pBp}Y|cij|J>zTD?Bj3GeKEo%gh6q<6 zM^sreDDGIm{)U%7zS-%V6ZyZKedYO4+^VHj=9?PpYs|X^v4vXw+0rh!BQir!-JL~+ zJ2|d1Cx;jd!%PYQ+zqEE&QT=(fTs0Ig~rwlo+%rlnt^8f6yOzHcsl9RSc>=V$UgR{Su;M5|d zUi1=^CQ_KQq)h!*x*W(@ylI8%Fez~3IOvVeM!hcW^i^qf1^0k%o7z$+!4|$Tdf%;5 zM6A4A9U5{fs=qGho82#)rCg|gkg0z&Ez!#3t8*Eeq7O#*qJ2^czyG3Y-)OMs0OU}f z(Fr1&OSf5+LalP&xH7+`k?y;1*}7XI&*?Tz27xY?)c*kVq~-cU zk-D%t8F%rTq^*P(4YP%Y*|gE;<}QiUhe6u2TgC1-dKjj8bKYAaEZx)llp>UZhN^XJ zxLljI&v=~hM>=ye8JbhR*^-s*&Mtp>O{O%>i2Yz>eY{z%7StUcY^vC3EBizSw`qE9 zBcsuhVbt<8f{6RY(Wz$Bl$K9PwcoO_tRM2AbKRQ7s_kX%bE8b~Ml;g?6qJh=Zic%J-*OiXa( zAe|#)ed5i%OAvRs_qbZ%(m`i?$W>q#jlLn(sOU!ql*PdSz+VZ0{`S6?yRlCaxHbkw zCxsu5`}KI{2lNYuubVCs+Q-Z`2ARfJRM1b|=F2@uO#K5Snf=w$4rypmucV`N&G{ux zuoH0V%3gyZp&FGlk<+>yt#5I2@F*89@IRNo(a7Hq2nu8dy(wfE+C1a06gzx8QY=1;5qF#r1Q!wBlvb}>PW zd(T@!p_(<0gavYb>V~RXjScmnnQ9qOj?5%A929 zDO5P1sT0qod9G>r^muJ^eI>mGPBV5S75+?6u)Lx`7m-uU@Z|rTPLzd6p4E!sx-3zl z+RuxZkCua}sa2Ma;*v*`>9IT`(6qSmi^0Z&$=q~I&hMddvME2i<0W9J3dWA z(vz0INe*20CaDDeAXS-Xw24 zB4Rmd7;w8<4wzmo|6vkiDDQB>ICtt&Z&_I-qE%U1AZ7WBdW=NIn+0Cior`K4eJvL= z!J3EzdloYqk;PNR$R%XFM?!WWs3}&utCnDNT4{ooUP*?a3}Z?h2fd{h=dP)Y#NqSE zhsQQ3O!6NfK2(+?z$FXkgS?~Rz;0?3OwnO)Xy+c4-8u`TQ7Nxf zATQnWC}Ah1%-VJynO!NP=Ri@8mOSywWfPNkvJyw2&+Iz-s2QUb~wc+TI4jd(zgp{pVd%rpn^!xi%2d(&e&Q!7_bbn#QFT8cJ>aWJM6O z@5+(?Qu3TOMFlAn_=P@Oz~~j--S2)htSOuyW@;EY+|F&1I-lVpa@w;}yK7#kcVnFU%6^~geb?zfW!U4i9&yLV?Av~_E|JWd4 z!_?5#H~WyNDse8`ep*|f#VbXW?)zcy6slu7;?}oiYOwd3>6|O;?~%QhQSn>Sl}TGy z)=Zc_+}(Zkg;Cdsde4BjX;)?p&K)&v>mO1_xdznx_ITPx^!Xg4~R4cS3T0rxyGMs6#T(}ZkL9v254Ev?z+0)dm25gsvNGR)XXl6;6T+; zSFC6rMzU&~Ar7ZL6kPIP{q)$*faY-qZni8Toz6C{9fj7!UU&JzDd z|K|w}a`u8Bh(UX0ZDk`U4gAXChYF9DBEjIYZhVUP9L3vb$%o%r+_ZlN5gVj2BUrOr z)%o(i)q$I`Th@Me1QLJQ+jUdqnNhAAA{83uPND#5fPHvdusrf9@3yLreWKK@T0gyf z3mlH*vu)Gu*`+@a^}&0Ryjk^I4RoCqOZi6SHLh@n3cw!6d|wp}fTrbi;gTP}2&|W` zi_zwul>82tl$oYfYF!vFNY!qG9}9WPu%7EY`)r|US7HIsq8plLrN8_rQ4?o(O9TBK ztcahW_S4Y;Z0hi(rS+@LsHa3r+-}<8^yQ3uUSpTRa*FQ3;IBu${fWunaKBbPJdT-x zPn{p%J*9=MFkK|OcJ=RE05;02!uAxqL;R9qG3Up^Y-v zDBY?JetqW-HF}f6t;mcl4y#EhOinx1PK$db(`OjMLa0K5gI^Dc-5hVI=Fm>4nw#18 z$gp0kISROARuIV;5^u~fXChx>_$*Z0<<4~%QqA&Y;CA)1R!~trBm7oF zMX<{qK0}Q`^+U)RcSDNyui|M&{XKj_RPiqi=XdSmpFamCsT!*0!C807r6*H3%^~M* zdSF{2ruI1O7kZT`)Tjqk@Tw-7=oBbw7+iAon%R2c0}h^umv~5Flpe<`b)3-iV27zW zh*y?)$6TB6+G<5kbMn1RW=UY<89T49>9gLqj7$9}y{lUWs{CDYgN%)p75r#IL6rmLm`$&;I^8(P+nEbP(99L9sSSVm z2gnPIv#hU{GZEN6ar_mG3c{^d^i`aZ&%#m^*>dhOlwy6NrYvSefVwYt=UVnyeH1C& zzLYFm59YL_?_)Nk(;9l>3%jL-rl66ML~wr^6o}Jd+@0#{;-|=^sO}RnvByRd*jQW} zO^OHl!f2@B+q!*2oVmet7+cAsh9;dY3=te!@mvm5F%8FO64qSqWkB3>Ly|{l3~T5w zC}R_b_Pp^5KS4`km4gqKxTQ)ud}VRI^))}9`WNjG^*Ad(9Twf#FqAl|`Ez9!T}F6V zaQo{0KL9ktm*}++{_lT)tZztx!%DqhAE{p#eFX>dO{+%25c`2|1!1|9)2(Sp}LOsIa9 zq2LCzKVZ(WZQtQtlw*o>x;LLF!i}Q{RGq0N-+ufh?p<8wmB-Z35ysud$Lz<3IrLHt zaCMDCS`Fp9|Hd|f;KSI?V0ycd``4L5fSl0LrumXWp9w~k6@ z5iO^G<90ZihiL~dcb}A$l^!_5M1C%JI^7la1;7d=ik7K#UM(a~yy$Lel`6~>E?}?* z97q*x-T(_1esr)ZxE=Q2-b;kMb1TNeV=@nz2ldivQ&iAZ)vG#7 zH%&TwJGynH8ctI86<0g@&&OK`?$rj{1zY!3yWJEUPk6bKWbl5FVAOXzF_Px^exyND z+T3XTUqM+2Wi1R>RLZNNde|shObUeHe*%8Gn;CpoqF47hDu3;7OXHhhrgOQbA;487 zr_J!75(Z0kK%F0W8>tWvYEj!@Ky=-ixq0@_#dGjB_&nUvI5yFnPBuSV`RI{*k^K#K zj~iHi?jBZf!IX7>w7$b?(Q2Ahnor&lS?h5$&Wp$`l6zoP;Ad+mO>ha#G^qWG*5P-Z zqf}p6zC;3G*werc={4Tkt?pGVKK#*jH0)m?nzxEB&8kS%@+q;|eDCE&F7m5+xxy!o z>4d|Q44b}o^TWY-v6=tfS=oD8a^g$#zVT3^_^7ob(Eo9dqrw;1Ap~}1O%R~!IiS7) zzp1YOJ}0BJN@7{FaxA4^t*2*fa-us!9=$xYKY!V?wpvXWFr>Wi9M07f&R zjLH{oot7we{hO2T#Q>{}K!5a)QeZJgV|UxsAs6+cMLVC$z)=*zE&lz?LyMmzCmbm5UF16+7^sXrH|dfO4lz?_wEI_e*~T42GQh;iD;MEe>^P6xb` z>8F&fdy|K!4uVoxLTQ<>b2=YO9UfH3f>h6qn-e?i7dO1Pku&uEpKmp}2c+w<3Y!?(XiTJ$b)5-<&g(AIVH6 zf3jxp{j7Cg*HX0QtNE-;>aq$B@F1D_I(GqQS1YesIf}K)GL3a{*?3+`ZvbQUUxocg zCTmKpE+c_!4T07-$vx*^vc>*G#atEWr&nQ&^J^xAY@9+;Q#GHoM@+1DE9G0YtOj_t zsp2*dlKS&|yPQG-ytr;;#Q2Ll+DY$R@kRRouAQtw5Ft07#35UUU_PTblPGv8N#ZKu zccdqiwi+4|m9oM<)~LI(z+4TmbMx>_OpDecWc<_iuonC&6Q{*K4NTJvl!PL@x>RX8g^qz)dFSPBo){m`d9 zu{<8bx!HWm;LWl5G6z)b_;ds4AJyf@NK1q8&VqoPJ8VJiwypvnNfm zLX2-xS$8epF?dH2YxA$>rDR5!5{kz}e~Bg8p^lQwY;4H67As&T+P&kEu{!5GCF`n` zLqveCUn4Hy5mObuLkh?!GrE^gQJsN0S@D)4)x>v*kD&7AIFWNq+t=uzk5Ef#al}xw z5v~6!?jQ*ZeVG9GL~eH3Ud343o#BO|t)2Jni2YbM8xH9VF?tGnlST;T$2|lB5kEB) zneT#X=sRwoM><`WyvGktZ_bK`Loxc>MoXe@m+Y;q`}jAa>Xg3r+CI+xMW$bUiZ-kw zJJg`60#vs2UfN3aL{mTGJl>k+;l8F+S;Y1w#dZA)jRJj-py#xU2Fb2nvP-DhBUsmK zEc6kT)RA|AIUkZ{^ZB1&P z*J%8QqiB*=3j4!%am{*?(jMF{ORewgvkq;ZoO&_ZQ!1X|X;bO1SrTM&O=26#9;6eK zOjiymF$P-jH~a^G@OF#i!)(+);o!;~$ zc+ZNHs4eE}{f)mgAQd?UsmO*ptF--8hDg>V^|pFg0fY@aKlqlG7dDHD_N ze~xy!fR?XB98^7&B^lqVW=F0uE5}D=IN#M;gl)1$Sx1#e#@ze^VftQpXRniX19bjt zEv?R?k;Mc8!raS~`(TKWDJwU=0(+#y)Z7Iwzpzupdr`DjL7um(+cRE*(7 zb=`XPxV7EkQGP0CB zb;*}&%_cZJ&pMd9l`@*~Rd7o*KqZU8xG0Ok>%CVs>l#iJ#0MO&qVjCMIy~YsT$Im+ zgJrLQWuxDpO^XIh1OT~G=F&O2?F7W-%+;BDMd78r4x0)w{)qNmG7cLq?qF#-69-#H zy#X^JB>SzI`I`8SKe+P!^jbOl*x zqOG=-Qo8LD;vwH#+ZsRy+V;f=T@GW^`O;5a{1&upqjfK}^Pj`CedLL~9t${5Sv-Z- zNrWWsJ;hz3H`ktf)4~30mS$_3?Swj}_qKCvZ4D@LJMez_wf+5LrR^jLbq1HLO|@DQ zO!rd9UnGdzexr=^u4D_P2X*>v#qXo|3C7?`Mk0{^5XUEv4MtZHKi72Dv1fSqRLFkH zVkVon3s76YUM_v49=hteeRLzsO|5IX#o?` zM_*^XPl~<373H_B0`m2wdbV@A{SH4Jt})R*08jAi*DrXRC3bD37P3^3D=_{r(b@xs zH?C5Ck#7)4y*j8+4H_E?e+j==a-}XA6*0|-{c$r15>ew>xdch04LL_|=#i|&o?DviFfhZ*?|pr z)5={+SJY_^`_k1J!E=Z0ss38X80mVXt^~&^s8@+dV`L-s-D)7{LF0o0iF0lii0c5Yzb`2VQhRP$@Mff-+_y|UA3BGHFi~d z`RC-;Vsrm4$2OPNH-#hPp?uX1IfpXA4rX`lOkA^WP&BWZUAP2wRiXL7SV^pZ$GPrI z3v!R3dM?<$RHkh7Jd4@1-Fp`s%X@1(3$u2gGUEU;`Q0aBSd{No8FP41quJso6m>;& zesnjv%u?-JtwL1ifKA({r?7Nl?8kVkKsBYmbx0Vo72dd{$m`+fCtfN-9`q0usfuTn!F~bmt zB@M+~P;1W^f1bedCu^T~x8Ss9m!2J=kTf`3CmJmbC%%bW&VY^kv0}gaCQ@ndjisln zj5~~lQqs`b$-9I$w&7X#x!`1pk-oYbV1N$J-uRfwX0g?KT!28q+OVq85K+een5aV3 zC`Ra2)gerg&Hq?7>{R9ovoY)-u8ojyC;*z0?2vC8S-3bA++4a4pg7mJDjB`uBh8s& zo#JOX{OW&X@p_}&f5HyuFTnr4Mit{?1j!gcaOiAiImoYItII-qMT(I0ECnP35c_*U zqXuZFHR#oB=o4O@#IyiO%`&yA06H~B%+$xSLHZKXagHP*patEt6hmul+*@u*qq1xW zD^=Pkuaf;qMN!-)8gq#|BDFW#;UObSE9=5Me#Zzjn@o|qW9L*4?h`8?1(ko~uXWB0 z&Fdb|H7;CXB=qD_<|%DT>;D}xAW@nS%q$HCpc2n^JCqkQR;r;1j}5ktC5D3__Y2M4 z_2dc1d5rsdTH(>0ZTXU~7BC1RXo5>&yKnF!?$P84F>vKt5j+e>FG~bPrtd7Jg&@Du}uQp23SVoU4)5^9Hs9} za~0Q^IyXZ#vuC)$j|rUCoj*gd@ejRuN;)znwO9wDoP_eLK)jsYq{|B0n!7QM#} z4;z7?xnqhC55L`Gns%R$lZfk%R$gIT@DD1?>$W3+i~v>sM$I5?;kkGwa&T@~Mzciy z&yA&)lZ=&sQWIiBmmG=n3~~t0d4Zi8)UZ2oKW3Zm_`M#k=g^$wpe;QqKSsD1ad9y^ z{vj+zfT+D48sg2RO!0nO2#?6Uf3_F$+t1$O zA-ZooTycjAY2=CJ;q0S5@y!YRmJqV{c^uTmr%Q~t_G)}w2; zZWuZ!`4pV0_XCbFKV~@jL09VM;bcvpz|%$ zK@h-Gy@L23)M3Wzo54z)16GuKX*tvD`ZQBXlKA(O_&Z()_uH?VK1q76`mKjWmH2M( zp~T*YkGZr)E{)^IOuWzij8RsQGZ_rig=Yj>ZH=`DaZxg`G}*{s)zPljm6vxbEwX%M zBp?pyNR)yUk)C9!PvDx*mXY}Js@Tb)l<2`46T|*5bx-U3e-rmlulwsurNT>H)ewfh z!|G~li&uL}HgO(^Wg}Oen?PmqtK&aEOedZOs2o z+!aZ3_>nqFFng#1!5y^`D!~{kp|H^FT|j5YVwZYfk`D7_v2|S; z1k+U@xsRoL%rR~2tk{Hvc9*c=HK1}E(YzxEaL+Dcn)zDVm(E@(3jiL+)(I2d zeKnY0P0!jG9c1+O3-MREryf$|7tNVi2$;2+)cgXjWfazb9r?(V+BBu(p`=%@(u;_C zZav-6{FqCd0mB{YR8}!aK!O{UV%FcjK^kzly3t{}kK(wh8K>$l9vCCpMrx0PIDRFK&l?lc7bV}({y|B8_yg@xosY4%yqAiqHc9=!fdcV2!2`@QISg1l zH8Ew8sVxEW5T_xQi@qiZbv?ArgGBGF%)e1MZ}*q0Ws2LNztz?%bz^xV8%q zqf9K!nL{o;1Rm&Dj)8rb)ujw^hNjer6Ig07&A{xf0`b%*^)iEVk7v6$7(<<3m>1rm zS{%c_hYg^a?!?IHzOx+WyLU{?O@tosikGBK6k)3yh`4Q%XiYpb$kQrJ(w02Nze2HcEt~og_c_mb(9)>i}0EslC@i^Of z8{lT8=kL{gqL<3_*|%Ia3`P1^bq^0~;Uwkw&H9F_)Ka?$Y~1^7v7)R)_ z)eF*_X3|~lemzHvKEvj%YX$vvmJ$k&QG6g;Q*L>`sAAPLMT^zEYX%H;s=2!>lO9`3 z8~FO5y=?)MDRrF+IMB_TY8WF~O4K(EU;PnnaK%31K$*+R=8ecau@mc(AbF5?zItud zY0#e4l*rnoK|7l|%S(klSxZnDpxJ+w{`N8e^peG=E~66?E{N;#3I+ zCJe7?zMj`oYdE-?e)UzZXVWi!Gyg?ok~VC)?2ks}nB8;P&o;2LFHfuGQoxT8bpu<3 z@6KZcNr!aN;c{pO-wL6x{IvIJ_YIPOI`{8eklYc>r`tgl+74>+0w7eqmS zuWZ=I7+kg2O!gk~V>^Twh)A0Rl3wT@>t_`(s!AdM3IMj5oe!zdL5>Oq-QE-3_zwo8 zB|%5bny-BKTqk4^kQ9tKF`8R}orNBWONa+6C&J7viHYc-yDGCnVUx#;MK1OPW<$)qI;db#J}Do4DX`2+xqWnErb-8r((9RNi?u8aJa zo<@J$wVF5O=^p7(TWeC}$dX{I1g0eU*yOLz)hJhN;`oF*s{ zL2vAEhuDnlsYh7pSu$_WwT|X^HEq!@17qhU=8wBq%lV?`IRA3&DQ1ZJ6tnpM@?XrL z4F8lGT<#?Pi~NE7C3>HXE?)4xwW8_UYhwfJXOOUt-d#*ge8`g7A&Ec!!0Sr&Ydqty zn!TehUSGqAl}AGNIvJshVRb~BNa;gh*2P)Zj;$}Gj=iZ&?qtDK@$1c9$Em&HbgHt zJ(wH(4BmgWd!yx_Iyuy)-?QvQHaU8hW~_at^!32(-+h-3q!^v-obt})96(eWqO=PxAt19b_0IcT?wS{VRcVT|KH zGmdI{Bkp9$hS1(UUFTZJcgr!GYjF7NYvDq7==U{+2|aPLkJVC75(5c!HW)g)VePNB z<9J8Fu`ZMs&Rp`_hx=XqMN4SOC}JBdz6A=3Scadm3_&|k(B;XytgIx=lP2fKE8c%- zPVu5kn$DCb@>d^ZLIbld{gF%FKB>!VPC4DGC@R|au6KP;h%jz5k8*IRGkX>EaK&dE z)tI5=Qy*{s3=i)~M2x9-bg|b|gw9kRk+q^( zUV1-c39i(sle7AbT%k48{R7L4lz8@dJq))}d`9tHuQno&zy_?7SIi7<$jW0S2+vOS zy4Ob)u5QJvTlJEkUff@7(UBSrs^VDMXFPp^eHY*?%J z1#(1 zNxtcktT1Gc(bNNdZF3u0Pj-@^v$5mgzFNsCca&|HCTb#I4lfTh3cJg|UmR3SIoJ~hI{?~LG>LDiADG{V`XSP<7gDB&rxQvCww(VTkV z>sFTY_HgayL@8Cgta-mFaTG-`5iD_Byfk7>avw*R@V>u{L_4rnV0YKSYHBv*P79HM zwlhn1=qt+p8viuA{_KjU9gm(OGDeKKKemgU*?mcDfAAy~3IL@-Oq9zgn0M4R;#4GT zaS~V`(OvNm>Mp{Nd#=k)^9WpJ0}-X2#K1wEDqP>b1E>}jY07|RYN=f0D_}K<>I@mu z+fauPzKJ&M5(dn64Z%A#ih`b4dpK6JheC08R(FZfV}(p&+df@Ngm}mKj^i%pblsJzV!(-8>KOlgEw(XZftxYs zN6&@{e;J2M`Xj)2fS=0#y80<^h}hAIzo=zTM$a;{uCSQsfi?C)n6ame=h8By4tk(` z#nFlFn2 ze+f6zeaOrg&kLa6`rMfs%Gy4_+{X3#2h_WqohMmOoh zPutu_2gX)63%@Rq@9?9;gB5NT;SB2peDV|HfSrbqf^MTys}3_Wf+#hc5-0i}$m4(3 z`WJb#-lW%1d0i-Fxa`};@vq^+_vd{34nM{xrsT<8^vD9YuQWX|V{>es{FJlAi{dzfW34qRVH7>Pd|FJ{VZ{ zwSvpGeS zC+y68BVp2uN=(ww`%eWHxBtm7WMWi6?ajFsjFD?R&zA1S2^eCc01FPG@f~*O>IYAS z%ImA`74s$`@c;kWNVvzN&wQBEDmy-gd9igQ6c1Vl%8Dx3wty?8D_Tr~mr{lUOAIxx z3DsD*(E0LjHxp3JEd6ML!05hJDqbP8#Lew`jtx5xsh2=(#><6gd|_=fZaT(WxcN9A zkpPhaKpV@Bv_qCH6G;m%nEmS^Pg*eTNFtbhI`mCGu*UbiW6(KQ148vKE=(UIjygkN zUSBPB;g(Vc$q~pz*${bBwv4!`(D@1T#<;z{hYm$hYj-SRE|jaXSIfa|>}`Kn2jZq2 zskQ!`sC?|iPF>i|%t9Q@pRDFP%s(*dxOqZx+`B`m+-8@!SKORZX2q$!s{UMnhEK=a6zqc!*BL{* zwavKT>ZQRyD4jR(z}leA7Z2WFs_A!gTL<_v9DXedUk@E|KQ}%CwmK62&KU66UQ(RP zq^8isaAZdD)6Bh=rE|kR9;DHd@}Y#R>hlAWnuu2qSF46W*@_A5*9TqcCv-%fY~N-% zd7J0k#YEsbINFrc9m*`0*I>?8wbzEb4#G3JhYD)s@W-~^&&9TF4lRJ%#@5Fkk{a*? z-u&K}PUxJZ#!rhx4GtqqP4SZu?M5W)&V21_qO8Kk1{6XoNdF+KNm^M-b#GeuF>-+M zUIAxT2oFQlQwYoFwy$AX${U%VFu$x9wfw8-NQYivjaA-L(SmzfP|1L)$q zuP~6y^_zDlSFTQF*0C}>o(z_C(ZkNoHv!TL9vqLRLO0b7Gp{}tQMi19D`53eu92L^ zVqQ$1#oX5wfM)X{>UZI@Vcym^_$k#g}B$5Bxw$koOQZP@Z%XKDxu@fUHefsJYe=hdVUZhc*EtzZ2~LT_KSWqjoaR@_sy@TE zZ>_G_H?X@SHtrF$T=6$8zF#Qo(JkA~e`8>tUCulr{}eTDg`A}P@J_|to#(Ir&ZZsp z+H&yrbGzYjeqgyEM@hMg`5h&@3a)`@Yx%_k@4#LPIExC#;l&~zDIFM=BV8(G z9YB5;I&UIoXJ6ix3PSqsxBBga#ZN@J3%9^SVGOkP*gRd8KL2f44@i(M9VQoWB^>~G zz^61y6qfj|Bo#k2d%I}eO0*vK-uATepz({nrSfGhbuI34@MC>y4xwt_5myqUz_?zm zju9v6Y;f1zqiJ$1d(OJk|KmRKb5Bbh=kE>!?F<5f$tD!9qxN-~kj`)%;cw6Z%%^12 ziVl5@y|mi9jkB+>U+xU~x~P@u+S=UNi^;50VS)7uyy~c-Ncq?>?sKE|yD(27pEPY2<5KNlZsJ!F(1? zOs?E75?OX04daOKSE!zSP`9A0Ixz>g^^$f;hGsgnWU-6x0qS@uP4#8iSYl#}K5iyQ zEF<{?j$8qr>4=q~w{zMZ5%vWx!LY9RIzspf&&nmn%-@ZedNOmeAGFW$F5+-)Fnj?i za~%p1e$~hKv1f#OCg7h^Be|vJ@kV3a8Jtj5aj6LsCj34i4=`5&0|}ZA>yowu^Ink7 zg}J%K{Y}z64jnd6^A}hKkod0+GU*>3uUZ}o@Yts=0+~e%4=0EDqi323x$Wq}Epu4V zLllJ%Ic7#gW#T&qUl!gEt~UPE038=o3Pmx0Fa5d1n}F9?B+fDC;mHs%OXux${gvTy zN!4>UE~NHsH89BG0>U}K5GV0|jns4p7N zkI1f6K8G0kd@?|FfttEGR~6IidzU*t3eZ|La&F_>g~>sYZ~8t&b#lgf7`<+ zP}4D{a*fi`jp*%oIZ0n;sVkcxeo zRo+jtb&6GFlq>GI+Ve4xcg9HpdeBW>#XcigWV?B#c>a2{)Z(}H>YBbMJxu$1DH>%u z>>B@`q?z9}7#q8TdwjHJ23^T%!CJ@ZaPimE;cGnQCsn|Z;;2GKWi8l^lde@OnU!Dr z;0m@3hF$t9-KgqgHifwRPfa<&I9%E~NyrJ0^-#8An_KewhW41yBn2bWjC)R$&cem2 zr>N1BX78oM_!2$x#)%pNnTNfI1q~im$lI0&f5@>9^HkRlq))v)G2^9V4C#xL36`8q zkh9GMcdY`OZt`Yb_wrKPSiQ{||Ig-Lu&>6V8%5&)YpzbPca0@~jQ?M$8(o;?#dZIM zCF;6UJWXuj+5;@09u2Y-!)}9XxEhZepGa~t8rtJ-jSz!IG2=+f>>NAacN%jE%^Fhy z7a%Y9QuYIv$F)UX&8hA&ld9a&a{Hu?9y_bRuAOpm`z>_ypXFnUMpz68W`VF0 zj3y52MoD7p@?^e#RaAaAP#L~p4~HYGqj`mU+ME>ggJ<<7;C&E;LgQ9?u(a}e^Fkab z>qnqb>eJ!6le|OkH4vH&H>V1?E6VlFw5|YCm@UtIw zJzLHY2%DbM6J--~X@oeCR4V%)0MM~JW(SWnpt00Io29pIO^|?D%~R}W@H&pwBwmyjMp_n&Dekj^zb{*Qk+^z^@58pg!G|% zD~AawO553&Ci*HbxGlX*yWHCB=*{57-rH@jxyxx^-hg4bn0=eNlVBAk>yUZm_C$D& z)Z|2lI#8kmw0`)TDyjI`>McmdW7T`}zmG zl+P24p0-j84u*_sMy|PjTYNF9p(u_QBxJ@nvV@zi4 zSR44BNB?+esu+Lkbnl-L%v#^E?aEZy24w1WS@0nV|$_t zgPbxF4=>lLpzt5V+F;P&j!kSkBdb`*<#3awv}8W(5W{JzFL2~V*3+;ul^WWtve>T0 z{!Or+7G-GV`D0G~K=sVp%zR#Lb0W%bt*^M|n6g;9%sm==ZYpwNPnNkRuN+LAViN#W z-|k-LxR+1dGk0`v`)lEj1-fngEtkC%d=b`eF02J4D%>W zPL67QwW|>PdqB0Aema#BkJp30w$~RA(*%^DT|L{C83N#m*51?J)F#^HPn7ye=63&l z?X(D{SefFGxTt}RkX2&5Q2~!eyuk8}k~8)#no};Sk(#k(zUo_>i_y0lBUkSWd1|G5T*gY(neU?4UKKVft==B|ctXmY+t^G%>NNzG_ zr4yVZc}HOJ@-iD>=2zL*)b8Fh*%wsJ4ScOD%|6rbdwt19K__z%i*jv!wo zUMnQc`yvkFH?ov8>D)}_ok=N&{urHKl1oYd`-kaS=doB7ya7ykjvh{W$g8%Dj@4MY zmrk`7|0bApS7(|~9Y3s$sk5c-8F2VJedIR;yB-?#uD>b{=BLS8;)6sCW$e>1KUtVw zK@skXEWGq8I;yny2AD_YUoBB4B!d#HM*DpfyYjm_NTItDA|HJHzis(4wr~J9XgwL= z-IzL1n@0yy55-qrwAeke4&Hq&XPiqq`r5`#?Kb|$ke3_Q4s~!jv2tVDF=UBTtr4uE zrC@vHRuA_Y4h$Jq(yp{wOe==_v(SFIvwm!D-e(_2jT0MN7yIi>g0d2_jp2Z!VN)A| z_;oca(+RGNDER9T{@pgmqo2q~VtH>EW1ff$|5F>3+qiN{U}|O|t*NC3NDI=`B*HZ} zPBZctcmiEk!)YVJ&3b3P{g*{5A$v3&Bh zZl-hz^gsf=So5xY;i$0GaRj($e1eT>$NRdz?hN-0JWsIN`vzi8+i?Ayc_ z1SbPOqN9XR1K$Rz&;MPoA#NcA09{+2c*LOvB2?Vggwu|^fX=A)K-mFuOm5B%;Ydo{ zW*4>1bsxySovlf)|Dfd6df>?a-~K5`mq+$kEt!=gKOdINin0iDDn|{hwN|!Ue^V_` z6BBrke62NPCR5L!wsNQ~v8rP}&@oPPo{BGL=w~DUgFpd7xv8#{QJEO}tSs^I7F8EVu-0}XfjMlFw!$jOt=upZm^LPCz!kArCg^vi=$-?qcfErY40qIYaU=> z;j)YlQ?${caW*^t=n%@0Z^0aW$%S6+)mTQ$bo0-@l8-$n1Tgf+1|tYU9%FF1C++PI z%f$a{H$v2b;@sSFMR2HUHa2uXGsxlvjqES0IJEaFkbQsF%^`ov+M!MevywMlw9jZ1 zayUw5JkExu$)Wg-FiXB6BC*rK2$ePd{jv~yn|RD2?KJ%7ke^}Q-}J1m?XG4wuv3?0 zUOIRA%V$>O{&7a4Pfl>FGcI!&@M?v=wvp{F*X>z|d@>JYtKlaB#UW42zjdjzC)xU? z)je~CvfS2PFEuYaQim3n5-B`_l8Cg4SY87`mtS6&Faojh&V;#0-vudeO< z8;OklX9WtGf_?ekAj#UXIEfqC{`_r@u6hKv`sP*fwsv-J=ySOXga^HJ zmz_E-3k!UXdeNU?V{k{}z}@I>mz;x<6!Dh?5uOk)txE~C_=D%N`qT3WB!u*lVc6d^ zueJUUYTkF|=m|xC^0tqmv~HKKXPN`3{^!cvdSO|swG(B1qA#qam$0XK>47$VNT=PS zBSo<-Ub*8fR%IM(`VQ78eOhqDLHwix_U;}_Y2;!)rfBKmU0TeVrtqgvMeeuFF%bcsd?L^S=?~NYqJLLIq3UY7R0o;GP zw2WZ&ZG@|}1%Gu)ngBa~J-Ft8!(@p_G%TrJKQVx{sW)O9)bkNHSh)RKR) zXu9I-m)o~x8id-i)AG7pGbhFcT;WLosaP5pK6^c#B7LUbgfq6DjzY|nQIu&AE%*f5 zD~bz`;I&~cFE5rnn5ynh_BiVxDQh$8*1|-AEY9yu7&%lq{As5hp})_Q+>Z69w10_aTh2)@CA=D2sZX@r+U$C>|D4^3 zsY_?7St={zoxu=qMU7U_yX5R+F@<$ouxF&JkOh5FB8%AZVHQs6p94s}trDhtqhE(fqrPUwtwNKkCW@$5 zXIxwve8H05s1YEux`ieE*|pi=aU)1(fv%T;skeEEx!TZT%^jlz0Hy8gTqcW%!=!4U z<#yqil;j2lZ}Hb96urJR4QK{B|ASiI-28R&MpA-ofzLrtuL8cGsBhgFYP^p{y`0Gj$?#NT*>ttLi|PkY2&yAQt~VuTFRz+vvN{$T+f^{ z4#_PUyb#T3M^{#PUbyqrNKTq-AKeij*{VL}0W~^LQH@;N_zIA<+jAK!>1M_yfbW^` z*yh~vt0M83d9@*CfBcYi-O1~!5vrPCIYvr|;vFC#qANPl*K*=k zP2iTP>|Jq|ekb)q_Fh2;-s2g|$mf`;qL&JL*4D}+W<>LiI&7=lB|vG74d5EWG{-Rl z%lJIQ`12nWkqGYV@`%9Ybh)Q{g@LHIt0#TB`VJ2wvGqk9rADl8qSwLiFKcQb<&*@d zxIjQOeMa`^xYXtir8NdS#(LhH9QpROX`ub5mhKE+EIBn2U!912&M3Ea0-Qv#qmj+2 zKp~Q7X_>}Ikx1g&F=Df3+Y}q@ao?JCac@ouv>F3?6yrS8u*9xshS0C0@Rh;EJ|Fm( z#Vz)N;cl-Eb#=y|ti3d@lYHLP8J9@G_Bn(GFup}XW-xR}2kvX7xhqYzr*DN}5c9}X znK!Xy$ZY224-X^L;0<2-qe1q9OkAj--(-?x4u0|wxCBbz zQv=y2b?qu0I77uv)dZWRztx%9x}q8Lo^klwrM7{98O~cW>|Z(s>RAf84cAH1@5EFJ z6Y%Z!nmR2C<7rsF%gJ106oQisIJip zGsLEzC(UOmN1on*^%-uxTE_}?SzqcL2R%h|`06!$>^t8YhD9UMPwN46=qc;c{lye% z!dj_DA89VTq}aROGoDp5)z%k=wmICvXDc*5LTEh~_I&^j7|>ABEnq)_%XWN|s!dx1 zZq2sRZl_I+hdTQ;)B(;pn8BQ z@eYQH@7d-UsnQ=~o7goxTxDNE7K-LBnmMDGrs3vlE*Bms;$dm@u3F0g#ZJY|=6eI% zT)_ZA5TdoQt;up?^B3~EB0CDp1cHec$K1Dm6p{yqshfrJV`UXd<5u3EIrec?Ph1-rXa z1yRIgic-J?k8F(gy99gOKqWcUYO96huE{w%Iun4!Z6S${eU#|^EPRf4jbM>k07Qv6 zpN!|m-o#7CXCj$2PQ;}?{35I=b-E*0J@P%VlpX*l6f#9KrT`29DPJhTpKSPRKWRz; z$;KzczAn_aBImSg`T`X+S}-R%;>x;@tbOWh8cQt6gZY<9xkH@;3O>gdS6VMk&!EQj zM^o$$MXqr<#hPt)K#rxR=LiG0HU3N!dEfPJs;7h1{6IJ7S~B^3nG-dY%%?QKv#5nM zqaOJNGGbzYTTRdj=as;oOaR6%D$gixn^xOGT^;VX5bDFyi}bG>q;+G{W>oo2w4JRo zsESU5>Y{uA%9L*Wz`SX+AxdK%TSKNb`w{};H;?kFQ5p4(Gt3qJVdZ8H>J_q(C;xV$ z@X{s6nwIt$xSq^!SG_Gx``LS{*V>xWq&IYEe=kgC3un2sbB$9JTTp~QCQ@$Cmzvhf zws7OrxIU@ks^Yjo%=}`{P@i@Ajw6fa>>RlXX2#dV?vip{8}Q@VpQjEreAj*)*E1{e zfuPqV!ubw+G=W_)!SKSd0a4L7R6h4;CPRWurj&E|NAHR%Bqg5$UVO^;FY0MA&hhY; zAuq!^78Y7w?0Wu~p2_fJT=y+%I>CmaMSRLqFL&C|P5ntrNACRGk6BP_d+x>zrgoAA zGrcDjSFrSBLJizXJsKmtGR7s{&3#%|A7fCXnXY>L1npat0nrBOO7Ap!srEz=n6aD> zd;IUHJ=!4hgRi}Qar&M@J185kGjR%ZFO#nj8inG#8!p3WRL?%0bt%&vgS*4tc{NOR@hV7~q2DbI*gK|STxJ)kCk?Ag5I zQBWiVv6NnW-cyisyg77d-mSe%Ga8sDh8&ai)9Ad7EgEVkQPM_M)NckTrUm0fOLV5o z6s|9_Gm0O=gu;J$G`dZXc{?_($1~(}+B@az0a2bR`0jMtpOdOkxhysF-l(U2LR|mt z=ir3wUfawvMK^&|kiCrNUp|vMAhmLD50+f2&)s`0-7D^Jgyh2J2g(w> zqR}B)&Z_9&ClR7Y0ihkyr-4)fpV`M~I-H8Loqn%CvyEdgHP7_T8&B|@gkBN01X>7v zWoZj^G$t^Yp}wM4L3gz$y@L!w`7VmDbA8Xd9@hD4_u>&$WN_#jrDJ9>p#Z8^P)KzO zp-UNY>4%UlSVA<`c#(xoZ^{#uAb*UVzN|Mqxo7CI)Y{x#lcFk!qCnzydqZD19q+KO z7yWaYuo(l4Lri>QaaGgnC&>u?mI!u^-B}HhVGKL5C{TRAF@L zHx5vilZjb-`wKrU04xyNCS$04#k@1>o!DQdZJX_fxPh%}aFzvL4V`0+jbg!^z=?MN_{Yxl}(cf4+sQ z75*50&QP3jcKw>J2?LY4^I3JLsz?LfBznjazg4Lq26%WTpcUdz<~3zF$-qhxvpO$A z7U6r^xLZcA&%G*R4B{1#_1C1uYa3Q3DsED+{DKgq4iDRu@1Y564q4|m+7mXYj7f{{ zBx5r35QCbc7N;lmr;@G&)SF>j8|8SRr(|C55L30WDxblVBIoE#>P;Qhp$)S@2B&_( z4+lcG?uoJKDL8f&_97mF z&sJG3rkh4L1-?_|UzNotT?|doD^vG&k$vrlQ3*nnK7SW(Vo`Z`NTGB+sbef`>5)dR zNcOcZ&PRmjav455*)IcRm>T8bJ1<;DjvBIUY<3YArC%+|3`RkdU>LZ-9(vb>6`c;i#{k`2&Ura}oHXl9& zq$5N_e+k(}+cI1V#8(Px6DT5K#bjsYL>6VFSC=;uS1yPcD19;HDn#|h+h^e7qF z36^v;?AMl0NT=CT2P=xQUaOAjSM^cgZfa}viAH1?xmTRL!+8;IYNs}lHa`zGGxE{O!HbS3CA_=h^bt+c znr2r(m$4Y#$UiBasa^H>KUAGnSX&LlWuX*zD6TC~+}#TlFD}8|gG(S-k>c(S#oZ;r zp}4!dyF0XfC-3*qd^2--a(nU|*=O&yauhI?+*GW@)Rc2Jy4pz+y#1yFMLDCdM4Nyo ze?NRrGQ`b4eO#)i8z{Ol`0`d&FW@0IlQ8`jd*5evvIGb|-wkyugsxYVlzb*Mplum= z5I1i;<}{jlW1DRrV09z%@Q9{6r~Gl&f-F9fAv@#jw+tKd@;6`tq>eJ;jdW`Mg5Pa3 ztbw;jY=j=}(;S=EWV&4A;aR?@qT1gh^k+YtRhiwMYCU$wWj@|%-^v?e{^kB}IkZqvvYM!C>Bd?MDZC7G}H;x{_GhQ08f#nABy=@HIhtqH3Wc zO^aDi2>yu(g)N{EEh#E0QEA{RX5su*EyWqxhO{K~_aguMuM-ND0f?Kt2>&oDU!K1ap!UqvOYY4tp^-P*wY8n~?8>hD- zXY{>QfH4Ar=QZAHhZdi!iTkUTvE_ji1?|7S(f?9M|BE1HLV1rPBXNo?bvdBKfFTv` z`}2n6t7BAAY}(XN8EEGx2H@Cgb#|*8+<&fnLocRAqxLR|FiN z?C2jfTeVI!K&UrR1$e}Vsn$r4iQP>bWtkr2;osxPI6((p0FflY;;hAM~T;7wL#0-&Huto4MoE(zWf z-A1M^E!d9fJ)pn0YM`W;kNJhu6G3xtY>y;1b1v*C`r!VPDzCOHK$9zY9w9aZZf_?u z^WT$>Ji5Cn$mb6DO^1BH6mWf!#KDsI>Gux$tNwFY5)I8VsIr&=BUk*hpNd+8l1Av2 zLvtm7t@?c=&xRzUxg#J{hs}aB;=|EzM`G88J0zrLev)hL>Oz4i&AFO$ooq&EL<;$y zK&-L4Qdb1;GNq&^K=MvI1W%?Rb(JXNSMDJ*G&?*QBEc=qd@&!sB#P*fv(ea;GTyfmDJlYc^#)G_POF*zrsbQor_*bgJxr0MWIPgeMm2*vknE_78W?Wmqg4$bd zKr5~|UeV3gL1n}#{QTZeO1Dd{`miC!%8-*%xH99X?z?q`+1`(_AIYVQ4-m)R)4*-3 zl71=zRLadcveU(`E|NI1eRh=l8A2*=VcrGPe<{O@tJM^4cYnH2I34Sr_Yi0rdwJIc zl3X`pk(wPfm>?g0A2$6bE}iW!B!IMUBrLk=0`Kdaix)Fm?GI6}Bu9NqunJ>9tPimN0Bexf|G-C?f4tw(m(cK)Z*3%O&o-r;**~ld`tn4Wu@t|E8J#DWg+GI&`53Zf_IN{KVp{S)= zX=ayt@>Z^g^*@t%Bd^(QSNWQcS2p1J{(-A5r5Jy6!_y-F{qxrmgYwWS3W<+)e#EbZ zzA5!81LMzCF!Byhd8b}P>8##iWJheXXQt5g6a`qxhaj|NZ$P0!BC@2|oOmIyY(jOG z;dB(cGvE)8?uTq7b(idS+;NVgDKH@gV>z3&$6YH#UTrt!eZp|DA=!eZs06|Nr=O(K z(Dr;1f%fVnX3)xtMVnOf!B1QFCC5&qmGlep;A2NPCn`^ch3V1Z-s#(h>`+Ri?l1Vr z)G@LB<}{Rjd1%uV^^X)ZJgcG62w^6(wmr0dn_JAZTqD#NsQ2w1*0TbWzuG`?w)S?# zQiK<*qrOze^mDEEE~jn6#cH(#vL9f-skoi(zj}%rL8o~=S`ElqD$m#TIDE@#At9Llj4gMDfO?*VYcJL|LcaX^+ z3rSq)ulLnIaGy3g-m3ew-^pUTm1OX&OuqCZ^A#N(fg5|1b|u7k`-GYyejgH^8!n!| zgc?sG+F!kid!4vnSUG&QItbH`Q0Gz}^Ra0dLm3JaaT2obzs0tMpYzuRbS`155L1(7 zjS+V-B~|xfcwHbrEa3nQUqD8#C$9!1f6`Pvg*|ZA}3FCy1- zwt9paKRwmu9cZlh zZ(|St&`5Y##0iVy*~D0_bNjx=Tqk^Wx}2xdlh+xttA=L@oR@J`cSiM~pv_+M+#+CnwvIfj37 zOcG_J>ZtRXPwIU8;Z0uX+;{p6yB==%e{)^AzQQoRp?Knw7HKpXR|6OEJtp!Nw^2_O z=_$Jk`A-;B_lnT*6?$UjmGtC#fHqc5{Rj|PZbw}?@i6MQeh#TK+rP+z`kb7svN8J* zt%Fbq$JDy-4JpitGgq-pWE9^Sse%NxwrJsRFVaS7RCaArr=25>^<{LcFPnvjg5+|B zXhh_#M`yeXi-rV9$h3bAO<4XjJ# z*Ry@)Yo+1J%BzgSAAHiS4!+Tvyz2@V-KMrk^b4aw_<(qex+0ip^&nxH9Dp#$PaD8N z;ua(VqNz5|Ov6h%{2JraLSEW2!qN>7Bd-2tiUAtn|B4ukK>e&@cKZk6X+=C6VEyt5 zEPfI!7pJk*wC1xmp@QFsxc^DQ)&`vgUM<4tR;#~gn_P%(nFkqdb$q6wjhrhx_#ZeU zf}9hfaeiXBTlqaQkH3sBeRoU8*mz(eXUl8HJ1Iy=?5-kx?!-cUtO@JBmN=3+cNe!F zcT?@B9zuEOcTL!fE?sOvBskXXM`sjymOa9t-^CXjA+5r2R$hm92Td1tlR)qu8klaM zeOR{Odi+Omzx4?-$8z+A(Hr`5sgcbx2ZgMRaky|K!VJJ4Z^uvyklb6cq6p^N%WKm@ z#j)R>5RQ*8hs#q61%oDZX>y3;YocX?8j>E-x{O?83R%!?o*JVBdJmc-Qa6_|8@}Nn z%A70&ioGJ_3p4Iuk!`>DpTMl@C1aHXNe&LEz@*XxL4zXaQ1GQ3t=lb@YCkriFfjPd zVU|~=4Znx&Bon($0$<59CXUJDLM6GuL51@fT zd>4O+PdOq-A0zIX%uyD!5u$uaAVAq@(n_VEUFjd!x4Y3Fs%@nz^X|50kL*=eY%9E0 z7V9L}0k*7Z=pq?j$LgrS4kxKh5LiD)R#%3jTKVF2)qawT&MYzE;ltF6^XSH5Wad)0 zMP*yld0rdLde2O3pAR>T%sakp1U{r~5YX6I zW0Um{uHeqz&O?~?x-vCr{N`3mrLo_XmxO%aK9*)(TQ=9Y-Z!Yy-{1(Ex;J9N)5kzr{apcUZxm;4YZKg=200l{TP{(ANEoN`zeJcI-JOJSgxpKi(!Qg z9*y=|j%0v;;8=5in7Ez*cTcD8y0^h63M+;wkF1q981Ehzs<#`ti3!1ZbeVjX89(H} zLHPzw(W!7=30)+62G0EGcHY&0E?TmzdC!9c=xhoY-o>g0t`vJnQ^?3u0Be6|zOg%! zgg)snwO1+@l2PN0;VPmtjM!R$3qi_YIZ4kIbG7*dJ=v&&c>!!==*Hu`X!rD`$7_L70|Fnm% zW<40GP3Kv|6ja8_2h9pSr`vyZ<)5cZ-MXl@UP+@`zb^VEop3Nso7|FL8gFQ?E%Txz zrP9Mn3^1pSXPZQiN6_{-==}8rP_-JmybsZ{E1jk``DDEHdK6LfX<%K^I=t(q_Z-XFZtibBBjW)!vLL z&M7B_Ey+-B9y2v6|K zTs|mS9H2@XO^!@O*R-oJ`~&wn%qd$|$~hwM@otSR?U(j1#lj<^ImSCyq0jwG7%1eI z%c28NdN8qpxHwo7u@r3FjQ`i%Y%tkigi*S;&Ta{@C+sHLNgkC@kw-oLyCM0dm*T7LZvw9&}PxhK$_{valpf z;iuv%V)V)*?j%@B#o?TKY9#yvcT*+lAhhwv#$0>=?oUA%sO=jrMxaW>}ju{e!xJ#IU#nmNtuwA-Go zGgWbEgPCBA^7mA@ou-@u3dt0(xirPp=UEIwpo%dj&lYiKq9Je%y8I^t)K_sFXo`8% z!W_VIWbeu_Ijs^j{csX$*z&H8a14Tp2Sm@hPcB-VaqIlCMel0iBqOkQ+7N1`HvS_p z=f=P`6Htnssa;56coyluJ}?B;>5!+nEU}Y>t(hAhZy*O_R6ds&FTiww&5+e!2l<1UMeZ$W28;7 zFb#2IJ2|Xpn@ZmU#~dnbwkHEa^w;z{@u?&<$Pn*Cl%pj`Flj6&J;{5#wC`^l_G)=s z3`sZs8odZ!j%E{ABVc@d8to0+E_?%~0(B?rJ^h(P_`14e9j`45H`%hr?W!ABR0>2a zS7Dxwm&jukT7RS~N+r+^yj+w->5;!Jip`}pRS;^l&)6IP>^gd%+v!uws$ZAXx){k# z!UqL(MP{EAc;P*Igu(Wyu=_HW>g^`Tmw0`cwy|qKE#^;^chbe@YFK&%eqM2mz3dJ= zzfLL=r|VQ+(mmi7ff*2QWz8&nWRJINNx`s_Ur#(K60bnh0$uQ&puKz zlimqZiv0ANXVQcXdKFzo1w`n+8G5M})iO3Eo_x$tmqb+>^k?n8!6dLf}>ZEo-(lQ0i6zqi$Q0!%$LY9Owbu%HH3^-0>&tzmW; ztIMM=O4@wv_B6d4%&xHxUt*3heLy6ora)aiKKwq2U&_QN|Q z(SHW|hR|Q7rF6CqG6Q#iE>%<$Yw0xtWDwZL>jls%YDgWq450VCtuGP>uQ?8t(z%J${VS`GtMWCHn&DB!h0L{0E`7d)!-! zjEqPa50;?EJLT5|OG6Q(W1`$;yo^o})3`!^G328Jg4h*SO8KD zRfGm{Vf)(_K)8+B6=mXKuFr=p5ZuI6>0Z5s5N1msY%eXS(1oO2;OU6xZ_IDqW)!wK zSy`+LRf#zF(2<@9>G#CRwy2TH7w3~(Mv8eo@1dx;$($TiP&jiLwVTahgla0u+uK(1 zE=HR-D>%Am0+6CijzN|CHU$i{oSg>>oov%GqDO(4Yhc`xT^c<67 z7N>v98O{Af#>DF#1=L>8+;8d30$o}YY25{THLA|i-NpQes?ec|Ak9_&U=`6&!yz}x z#pNt*DE#$&OLICsYgn+lwVq;MET{d5_|UY2H4%v%^VO>Ebz$93EqAS?9qUTD1)IBX zlZ}^gszUyrcPjrvRu?v7_?!2A1gV5bxs5(qPNzA0A{d%PRXs8!yCzC}U*TK+e66d? zmI0(c%aO5LJo2y0gtHzsQWZ9w9!pKEy z);aa5;cug2Sa39^$S>kHC0I5pjVckjlxUy9FIFK-91k>If;K8-C>qJnnwrfW2iz zzS~&&ZbS52+>>A8QYi&KByr$vvDOpe`b9>(2l*1mLZ|nZjS{!1= z51Nc4Z<%5+tLU9aMgF;$fsXwZ)|pyLtwsNk0M_yQ;f3QX+f|cIG>{n@;|$4k7}Hn& zS<6)+)R0?A>&gf)c0q>0Wwd4u2@5m(jKq{QTNHzj(`1XiQItuCPV&omtJ^Uy%*Mhm z%*PY@%4I6jv_pdGlgLKiMVI<}UM-;D5fL3EcLjY>{VdSqqad_7)OsV2nmDEy_PA zjy$I1i|Qqu2{?t6?n9%NSEQ~mu*Aj0MyFzcWS&f;eH9IL&p{~F*}lMIDWU%_CuVkj z5eL!m#ytLMQbNZctbpAto^r53cq-LBJ`9m6v?5gl6MY? zSUo1HB*vIFpiFyQ-Q~3*2M3f-0)@!Q7W;|HiuJUKc_h4h%B`kc2qSxJW@cB-%m^t>s9y*1;+;wkLaiL&0R6@%5U zJ+6i_kDe?By8>otv$S{kfeYM0emKls9uw-Dqo_5P#X77Pd$v|7lLRypsq_^72<59# zA_(Q2qgU$n`1*{FeyX9^O#Q7~uhay4%dTXr_*b05d|E7@wx-OLcqk{Nnv5CaK(1F? zqY)v_t9oHYHW_iFlC`U83T*R56RN2q7;sb-0>}Ls>yQ4-nqZZpT4SsSBlYkp%5wQ* z@I{rd`p(O)Smaar_Sn*yQMaX6{fZxkgE)~MW39reFE$@DsvYAN+)r5z!o}}mk0rBlm(N;H&s0Qi zP9sQO`~mA?xNIB-{$iY36k%p>oc?4}h%Y_HVZcp;vobR14A$8rsNCV9LRIiyuw<-3 zcfDv?+H8x)WF_olG%9p8fOxKb|Ji-R@A$g0sf`pmfYOU2lPI_xBs>y~!;frF(e|uO zP<9hr=_;jEwkc>BHa6V#H~b5|_XdAu3h9ja=13z&TXr~5nM7)K{fPVN3z>QY|6ob*cXB>p_d|1Q&hXvF)=+> zH~-yQnYo<#=}KTY@?O_)4E`uo_meN~1b&mF<{tC-4|03HCxG_}7f38O8q{VyxZ9`Z zQb#3rioXqic5yRdr6&sqa;k(&1(A!p8kn#`Jjja(4(`8xt06<^b9VLKUuwQso-zk) zI#XeTa;lfNrM@!{qJ9<*fS|3g_2YA}a835_gdGwzJe!Xa-!a+oQM2*qE=)@FtoQ7> zEEW3j)fDVv(_UhnJIZLTNeInj*`7jDB56notb7yM-d^B8KA4V+B;v#; zhhlJ#4#8$W4q~J2AWP0wppL}~-XFKgu7WaXG`5OZ5(yXQc{MiwbX&4U`48_AKcXX$ z+r3z${CE>z3FQMvt)95n%SJX;einPP(eOF%q)*5nVA*W|n?`yStYgVCW1So`RD{g; z0b_~v#v0R32y;00lm;Wi4I={(>$T9T$S6Y{gqEuT#o$8p&rif)b6g$Cq8Ul*D?6!KXSlWSvK0~j4eyW}Vc4oP;! zki`+{OSh=&9d*djD!UHL#d}RHEI;MSPE9<#m1@;CmX`l0LlVk8ov>%*ou29b$fwM+ zKRJIv)Eq-2)iHZN)~aZ93gKMqeXnB`LFW-}VBjT7BwP`FxwcKI5&hWB2TcmND1>AJ zX_Jk=5_)|G7bkwXu1iV*XTimzAXB&)b}j>etHy+SPc7;n+5>6s&KPBBA20XFx7`y7 zO_n`qy~rsl+<`I`9SRCq!js6|P_8kaQa6^_VfnMPg!H4WPU)Pg#x|+Pij#Q+%l_j>NtP~r6IyzKWSvS+~EGPA?2myb2tPggelcbB3_F^i$$-=YR znM$#_sw~6yopOJLQ(}jM7FR#?(o_T&-nM;jZdsC;n>=}gJ$D`?uudt7i~j0UMCpJ- z`PLV~!`akFW}l#Wy-lcO+~E^oOQhc7FJHkxFD;ihq1$xxS?XFkcTZH>QPfxcRf^Nbik2yprw9`>D!OT~>LSNQ_Jo2m1Bw5xE6 z>&k`NNbATR!o)zk+d+-hC0-J+IOW75o00gH$@q^YQYO=eD}U zT_N?}8`gtkO;_O!XtA;R$vEVqrQf=`;=%K}>+C*DKk#>!V==B`r6FrYo`Ry>2SxrV z@)!aCufj5Zln&`J;S-KZ-1UFSL*X5s*fvc*l&89E8u0VE4*I3@y_plQ5f3s=EFal} zGLPtrPFX#~WX*A=T$lcW>~VTMnq`JW5)cXV(NT z&EtlcGE?+pAawQK{7;Q?z+4r={iAOSY%HodyP2F|V#?H!@zcICvZT5j;U&lq;djk} z{cXkfnPu9e`svcm^=pogyfAI({^gewh~9dNYAyGy@5tPZVH?y637_a*fS*R-}W5*(?O+vero?8EV=9 z?*P!+5W+IE8m~Kj@)62drR_HpZJ#Jq#B~=27E9G)x-Ueo>(O$9o(5j~ZlP)35Znl& zAuCFzzB1lrcfJn#G#OkUv;92jjivmoLuqxcUqz6TtF@*FO@1j=T8)VyRHbyb17+?Y z_QHtl&U<*}@Ajc?QD-L8eu+1&&LDkJ3`AL!tA zDm@uOZu3aX&ac>;sA1Px!P8qocA7j}mT~{KDId)XefhTO`wgba<8}Gh0jGEg7u*O| zT-t;2>yRXFnc!*ri(PKL!L4uygT0P@`jc|C$7q;=u)dJQy>9+=vPdua@af#Uyxr%U zHXzi!kWpTDr0W2>D*EFZ!G*GvAd65@E`EJWE)0WpI)FxM`&LjBxrb1UVC$7H!^s*$ zyQQv(@R~Z7*qpp9TnJ8VE)P%1jVfN`{htk}&inxNT!DVT3gYhFkd3DMvO<;e(5%6c zy@#m%q2~S->*jb-?%`a7Hh=CUnUs0OlidYtw8E@Buxt?Lj(9IoV`@|s=Vx}%fQMh( z`jABss`}ppGowpe6uC>XEI}*vFZW#VllvGsBQ-Fo)q9gmlwM?K*}6ArJ7wBikDaJi zqg`fT4xg@0N9nf?Xj7~VJ#ChyIbLp8Mf8#HQW|d9P0Jdz74B0x_9m_E9~62`*z*b>E3S%D-uq1l8A zCiYF$>2Ns}$vC3tA`x4&Bb=HtV0T3&aSP{0-ZL$oxoO zzF=Yw3?%dnjjVr7Vt8iO%l0R!(Woqv{?}`x9sro#gl&|tM7yYe%f|SgsAkjfR?@i} z_nB4fR4^r5?9sKBS{YUI(mLc#JAv<2dY*kTePYb^`h1<{G4}jBTFJLCB9A90-b8jb z{70`Y^?`(3x~`33oqhaN>2CsRu2yInAVrq#~irs6F>1&NzVl85?$%w8Zj=4+4kcPc)rJPq69lk zE+)1G_{#vaj)=w6_e6n5I!MLJ+UqlmWq@CYJiO)5`BK&_R@zspX6{lSuG?@(R_d04 z1dwf%-4tW-QhsQ(AKpfUpL}dSS8q!jpf(=GqE1^sj6fUSn*cKy_L(u?{nQ4m`~&xI z`D&-i5#`KU8+p$l=qW8 z>?-XN)d#3;f=hr}kLGKRX;rQgoTPql4u^MQW+~kP&>n!eH;j28P+hD~z%*Ua{PQxF zKW?mEfV3v{GZvQt6=m`h?`%yBbA;SbYg7qIQ_z6XN|T27;z;oLlv0wZw#^P3pHLCui4XATFRZ%Tjw=B znR<{y$2mFgMJ)y5pSP*kkRVn}>G7~{5U{m}Jsgan+E7^)OZmfQPBjieb>dE_HI~gQ zv;FYN)d25QACXbj?k48rFao`aP|~!YKS6&^r_0dBg!F#IOO=CQ^?AVYSityyWft9eeErl=?{6hqER-jKvS4ZA#>CUK!(jY6jo> z(npu?(Y1*+a_&&cbxHqtp`d>q9dqKv5|5fNBx2hw`Kh_M==&EY3otXRy7}QvndjoY zZ2JXwbx`FTg6BZ`7iA-^6;sAdH7XhM=`R_k2EI0rE%T@YZJSY_^oyR$Os2*3#nygx zmU|BdMq`_4Hv<71aK#&wg^(CFwTnz*)V_Cul@CjE{YNm{0kH zo2xS)8%Gn+wKML^EO5a+^7uO5IT$}trhk+*k(I)>V~kTO0-<&Up)FsFDxaQX2g-z% zep2{pY4%!Gi`E#P%&|_y^ta5_{E`bN`R}#GkZ2_@S-RtQ>gy`H zE=bJ>#R}pIdeI4BMGoC=c+S;3WCG-&$5cwZuv@bs8H>#LA~}!&!cu;@tkA6uxCrI|ACRj$y$`z51(Atxb_@dPA?SW@8g8%O=r#hF-#|PWd=7rU| zkHj?pX-b{*K-(s8xi3tR*Ci@BRnco+I+n%1en>4v*W1z`azpnWK|1wM6#CZ#o*Znj zv-&-0VFDsL8F}G}EFZ0d z4f91YwG2NzPyaV6{`W5S-vfk^3fr4?cIKEtu#WerU*Xzy6~5NoKEx3~Mx`}Wv9~Jv z_jGyTrlUnZxhQJ9^a6PmJMbs(W{wYQM}!ETh{o66y+l5ytk8C!y2(v@k15`{ z%a`hh2&q~S4G03YDFl=3CBLKjEU?mQOgUcPE23Ltb7k+JZ1KwV{ls*8VXwYD6&26M z!lpPPWiNravao!#q3&3siqNm{*>^Rz*f0^Wkhq!GZ0vnmkJ>BmAg&gpp@%TC1u0%o zIBDyXB`i>=ZaNjD=|D!qDI=C28bTH}M<)9VWwTA*Y#3M*8@jrJjn;w_QuszF3 z%%UIed=&K~O!w7h^yF!*%y$Vn?|?tgbik}9Y1#&&rE5lO6AmJ-#z`0_8^h&n`5rtL zCGbWT3CbvwZ~OZ>d0+l-R-MmNjep><3Z8X9O0lG8D=Zsazx{Y$%@U|abKjGys>gdX zB#f=8N*XrW*lb?8-jZPP58NBqpux9OKeDr?gE99%p@c`$lq|(iAS3sv0UVn9CLEZA}7u@>c zjV##fVko;ND1G(W0uSJtMqm#Ya%D5}qZ46>oIXK})LbD3Dyk;EBKDJoO9 zDc5dd7g8hNMN>@y{%}WpOq)se{017?T)AK<%RcJq?d+?@*mic zh?t0D%{Zgk>tkn*rY{LBz*I`f^{s(ghK<9$v{6|LCIj-lBybO_O&4@CWqW34s@fH; ze&TWT%M<$I!!(gm)zW&f?eD?O{#ag$Vp8=m7Do#x&}CN;o%~IOn|~hB%Y8(!AfhYT z=KBo6+3n99%SFAfBeL}k?vCmRg#+VNt*0ED7rm4Ojyro# zPPs`WfynaW6`itO$;Fl!;l<2mAk>wl8`1N4lJ+QJ+DPDv7PyF!xozORE;TCcW$``a z07V&V9tpVLzC3%z!t19la^l=d*m6-f|2blX)_M>-bLf*fm1rQvhdYpg%?HGPO>gi4 zuqQP5X|!isZJgBsg60k&Y`T;ng@?f;6G@l;50dUSW# zV&b$i>f^pH0bJs+bWayOg&cl=@==6FYskoxXarSN@yipW=;}D41qlI;y6q^H4K#jE zfeG^*Tq(t6g80qRG zYgqLo=KKvhD^6rpAT!m4v3F`+`aZ+^>tY#BpNi_o1^qs{@i>gqOjV5QABG+{lFx=T zScY1{g&{xTgeOQ7DVk0G`Jq^f+!2IOE)4%LjjuTj{~kX;;(boQdzcOcbJge!U*4gpKe0)1 zg5^#imN$uQ67Lw^kY7kMsqp#mvoR+!2)ygX}QF~vmkYybRmvYRE(d0w5I4#&ix1(c|4lov$uQ69V|qMx5Ar zJIZcFVzw&osse`lcap_qdI*TmS~_+dO!q7xo}b@M3>Up}%2TV3cTGBKA#aPOoc8S5 z?lBBDv-25Egq0#kl|~j-%)y>|?mgpBZUXi;spgw(a|Jw_n$?)xV?86aQWq~dL*Pl} zRE6TyvX(V-Wn)U4lW{Ct+FH`g(4~1hZaCk+gZ-%k$-E*Tu1mT-i#H_7bVg0vTKv7% zNYt?lZsfYf=+(8-mO;I6(Pu4Fd<#Cg40C&zyYu?c1&6>xA^++Rc7kO6urH4!xIMj` z-BD(p)@4ht6HnR0)=Y%9ilReI6@zz#bj2|rOa5vsz3&W3{NY_J*x$@3@D5ht2KuW#f)ED+1i`D~k z&Erlln6=W>E3eVd)>~FIK?u>ZOh9FHF)$ttx(CP{6+gp2u|;7%NS5bjEf-zV-ph~5l2eyQyue`0Ud zNp;x>kY+$ME_ceLG@(_oYWk!3^;%!Cr?l%(N=p(&XnCn!LICMsMr{u=H{PG2pZeju z%rcPt?>6)wBWyP=WJE%*7uYfC)(7YSZ3=PS*+sbfjHK~ z%XoqqTDPaOuFvsEAC?6oI6!3i(7|vh+p@-ve@HL(<>G-W1~+vkcOJ#)eT&WRfm5P- zM52gp#91f&t0~WLFxzPGYCprVipC}Kd0C|UceRDK*DA7nm}aFNuwPPGYzMHnXm7RM z7ZiCS!c%?`IK4{hLXHclCFsg1(`y?;_8~8WsYHvJPL(&KWO@UyUyreu#^-on68c2D zZ%RUy>gOHF+Kr@u6<->gEYq028?bvvBfNvRcVCYDbt$Z{i`>dTQFUqr8cH8$JJiU= z4XEhV@)Qdv-VC@BXB{SJlA~q&Fi@_aA}MacPCgTl9S9%woe(-$HTPIrZ7Ac=%qnyH zcdhd-Byv}cp0!6kNE!@pL0+|&Ozz0HY<~6gwBTHfhBNtKs(2#T(J>3V@Z}gih0)02 z)2}<-c0+!p9w$AY=z#i93F9lY_NbIK^4DafA$s+tM1t*f&5%}{a&?~FBYKOLmZ^5A zTIxsH=5U0&rJz0i=D4a+m3jp6BelpiZ87E4@1K{va~Ap|SJF~>VrY$=w7?me;<5-L zLw+|q8vnp~VhZPFU50FVPUW7pM=gwqZ>7o^&B4!&4liZxtm?#zE2=Veh!oQjGm`EDCcauL6)l!?%QBvy zS0xqrRhEnCIiJs0DEJMW>g&~+4f^)Z)KZ%)yZs(Zm##K>oW8jkgm%aEEV#6H6g>Vi z_GULf+l2DCc{~)=2}XLYG6EK5iG}ZJdeb(!nD-2EtB)U-$~O%98yFsxPme*H{03YU zVNyF?K)?;a+LP}kb&g-vgE4?U+F{}Q5VrUBpDPW0F>L-~E7taDbl~Nv0gvGeG_|e1|1K1FV|&^)9)|*hlFb`=SynFu>@N~U%$pp z85+{U;~#$Fb}gQGI`P|trw2Eq7HW5 zef&RgJ8!_LsD7po>s!`}T(9Cu^Aj$0eNt|oNw4+#TlMZuwqn!J0tc(tBh_>QZVp@- z<*yvn2az9l8ltm3tjzax{xGwirv>jX@uK@s6H6qqu1Ec4TNT~%eVqiWn)`NnBj_n; z)M{iUiREWszgS^ijg%^gjxsNuS>4?#Y&-5EFJ9swIL;4P5t|IzSA9*q`U{)$Z4RP5 zRDMT$Ue%XHqg5-D+mrq(y`uHOR1+Hxj^MF+4^u*|Wi|L_|xa=#foL zKh5mQ2Nv6RnMDF)M2ZBCF1re*VTPl*s9t!DlvMLkHu^#2Nh66Rn_E%4EirX+&nGGs zj-KC;)c`Mt;fI1~*O&gkyA?aKTXLeI(4}JF$|Czea2VcO^*+i2+e5N7o0eLR9k&0# ztr|ZVk2AY~*__!o$o9%6Tbt%|btMo|fi3E0O0VIAv|_ipv&@<{ZT3q^Qb7Aq`Ab@z z0*SkvSR|DI3i)feB`=WYue#$!zExl0aLo+bE$(mo9kVSeldhm^VgwWBf#e+#2PLp$ zT=6xZ3W~SXEe6p@x%i@X+ex%o(#0LXiH4?_QQzfmxXd&&N$1JNR|6}?+^tLn8X~4F zvOUHfpYWA)x-p)6o6DDpD>2gHQhN1+wO*0!93VMUo{jZfmJ_VLZ1V`Y=`Gc-;`a?T zlxj7r3#Qh17w)H-Ty8A_U9ORs1gb1G>dlvQWCGjMuIQl&#VN0r>-*bY49oD_aJ3}W z`1o2{W&0^f+kVCg+w_6G>iO!9>d~N5xRCZ&vWTdk&xdg6q8%$_nvm$)EC>c>d6aM z8k893nYf`U5PL@|W0pYAL7lk|Bh{gU=Tj{c0J(!ukWftIHVVD35?(ZnWz3{MwBT=g z93bdflXw2sB+71}3?vJ%RD&3R)!PGewMdMAGe0I~szTdQ%+x_7OwteJ~qYA`l4nTpNM zzhqSrKIlWC#MluL!pd+B+}n}f!bUn+%0Obop*p`c>StGkR8$v0c{s@p@pPaZg9Hm6y5-zHUlSw`ST$`RopdvvpU8y8dx;P9@u&^< zJ!t-}tgg?2&@DcdBtdk;(K6MWH9BFb5mM#$hDP#BV@~=Z&AG#fO5myJKX9aD%m79j zmwK|`Im#Q|Cu}<-7fSh;uRZZT-aBE?f8ZS4HTEQr{@-Bm|4RwqMHB)Mb&#})sV`Bg zxWjq)g1KZ^-6T{=9$woxsOc0ml7rkN+T^lV!uO@TbsuCh9NOsQ!T6AYT>v8P7^ zG)i@E=bnRH?y@-7ugO_;=yXIL3cVf>GD zq_lHY5my~XRVmY`jKq77_oR(3Te!V255g%w;#ch%qCP^^OF6lMLoKu7eG65|oVENK z<6Fy%UiXJMnMFEu3ocf6@(bYj#K%ah-p`-Lore}Ch0MwFM$Lm9c_$bXC)U^XAUU${ z>Mt*UMW>ItA##~}m_tV>KSP@xD7?r=bEya~6x6lzP-Xsx_1<oIgLmdJ*|nbep;b!jY7T2rWLJ;8Ln820}n>n)?&Y}D-U zP^_g$f#O=Ew77ea7N^6}paAOf5XDGfmQC#&Qh=c%svK;-(pL|aDzqT6{_ zU#&&rC6?l0+QdExy?#AWKq^B8z(-7LT{es_Z%sI*$_go`PF)iCHL7kaR!pOj3de== z`Bx6`I;$c51ti?TJhuKve*WhytGx{Buq#+xwIYxkLN1qM$ikhZ0pFkPDtaxb?@N+g z(vDYnxs~XqkGXtyY#I#XLii9UbW5$O{~eAkY=5qIziv;(q-d}=ZrEv_mDICbkfcqZ zvuPq=&+?AF>3@d(hpt({G@gYNIYuKj=EpAan~Y&j2C?&ztK|5Vk0wPnEXkI*&Nl4u zT;2E~jmx6|Aym)4EaF}ng&b`=;U&-bp>)8@n33j|FVfZ2Di zh1DYweje~2cm4Ek1+rbouNC!rm2n2CxnTZeuNa6<8w%+BCIEQ zujb8CN~j|-u#<0qwZ^uTo4V{PJGu4nb9# z)AL=ta*^-x^WNRX-Vb&}*Eg)ZCb{u+1+1J`%Y}-^lT!h!CSfq(cRU#f%jX zs2=|>pk_@zM*IZ%#a!64ln8Fc`6gHp>wrLaclIod+?9PuvL{k8R4?xDQJep@du;(- zdCsuD9ZFN1-hYaQlCAA=G1<9FEHwYb4yH72)g4D=wFdp1ZmLi3U#7 z$|l4Y6|H>Km3SdDK5*@G3uFV8D*b%kkzsdO@%5Ontgs@jTDtHp5e!?D=%}$!rb9J* zC5A{@F|C)F=Bm9@Rer=%d8DIH1IG%Y`f$U$1l7)akxLK2MDTL=$l$bD!$@q9(smMa6xA4Ax<XI%DD<9q?j%$OX9sm)?L zrEuN8YWbXZ7HTwO4NKXLkw6i_5mp)XwYKzE*hp$I`swv%hV;zcQy&x_?os0( zJuG&V$iX@G~`|Iu&hWglBaK&AHJ&YKi2{k0W-UBK`rKW$ETx6|v>K z5(yBQT4JSS4NA=^`=xoLiPEes|5fEtVGH(bwA{b)8gQqJiTF=g17ZeiJOZri?f~A7 zu65@sxdhzD(k+#lkv5W+O=DkFQyCYFDKnAiTF;sq;O05jYi>B!QgvhK$fG0=|1q=T zQwCz=wQ>)$O;mGli}ZC*DpLy7;FZ(msCJhOEqAd%p8I+1@P<|v+1b=#j*LcecpY0k z2K=)TH?mHTIAg3zPm^tJdH8J?D;b%+tG9_0Ryir!33Q^ddq%4+>T0SB34u{k@Ir>$ zeTA$a>+9BQA+ABE7t)gC5j_%1o~lb-oI9b90jayVU2PVp0o#2pe_T^oxwu5QG$1Cu zg{pNU_qc@Agns&c}+Azgd zM#mHtG%@mmj)!KEJeVC%Xubz!puwOnPFWuxFcBHNhW^JO{!KX>NLbyIQ{k}YHRB)5B;UY=%U4I$6ZBkB@f9wKa}e2 zbE;!-+9h*zF*L1UWMoZC_Wix%GCP!HEI-cHaw=q;#z3`dB?18^qU`tl7jUvYc&*>s z7|T@@E>#RQo3|3GeeWa?Ky?J_)YLH0M@YqD!uyITuO;`tmvzH$NW7X&EsBLxpj?EEn|Af>ncUvb3vi9(MfWu0#7kf5l2nX2lcSFy zklLV1C|e3KDpnKkrs<5Sk{uC`So3Nz4`^nck;NRN#}RUqCKAg9^l{%R?d z=dhPS`bH%qf%(+{dxd*m;o_^d6{7A|?mcrDGgtQWW*+`qFl7QOZEuCcAwv++_`}`> z&^dzJucd=b-F#77AH2FUCKUAZ^uEq>ju{ zw!C)rjm)fBRrzu;&i!*Ii^u$6@t7MZ1u=)%W078X)=njEWqej8LNc8{1EiTZ8BJfM zM_jq(O%3g?q$k(>;i;V8)(HsE(zDCcDDN0aPQ)s2 zaD^PtOy1aFe+m`?G^pE*0MBSw2{*N}~q$8r=KX3-ldh50Sav;kG%Wb%KxTQwIN8 z&LQhUErw?mOzl1Dv`8ot$rTphAeN&g+)V+#se5Ae%UXgzd<|7Tp*9Xj%j5w`CE5+) z0-yZ1Q?>E&<4NO*kRdyEK~4zEd;UBE?(?~@T5#DLukc1KB37AegS=v17SiygU0t!?We> z1Z3)1du{2N+pC!eg@`=S$9a|)>8{dV#0PE%RqJHg4nFPiMH3}0j3FWDXpmUJYC}UY z)A6qy7|#H@Y$*0T7ZG$X_2^%e42F!RjB)uj#7-;#vaFRG3Q{e{u#Iud2w9UWYktIQ zb1^zJc?AW_J6Igl`0^ZQeuTCs|I#8U^ec6w#7+TU2mgF6XxG+TZ%rr2K{st@yEDt- z=92NBEo8?rM=S`baoX6%@6sl^^L*Pzyqwn5-y~=Cxy&Es!DPJoGjMKkO{TFORtI|P z{)!)kRtqIafwtTFa{5T`FTgPWxBN7jC1ZX3s-^1jBiqS!mZ92bnFV=4hHVcQ($6&2 zl~P;(LTHFG_C5jdZmlNCL9I?J-Kn%K-jM0g3Yqlx^&pf_)xyKPG%rW*e5|TMp~W*! z-as>Tnqez5_d!F;7rMHuu(c-wuWC+ns};kKO~pF;Y=)R(pjcAx#9A-PexRA)xYO3} z0~9tH(APy6+89J%+wvl=?HE5v1mVnPW;#_Rqn-o{%pOfVbH>@!4BTKk>HA8@gi(7a zJHS>+HVZo+;F?L|aO?=z@+TbbRWYyZ`|GDNue<93efyh3noz&C=8tPb3X1z=I}2S) zoV0EG3GOhQDuFm@I!Eatnp>L8{CPzFzM~3Ua)Flt?SD44+4DxhOtGz&HzN@(_PZ+GN@J_M-{G-7AdH%*9{|+IT&f4vAhjDv~ynfpo zTZ2(gCM!|Wc(n+C#C%zx0^(Tj@9?;FTRX*LhH**N1DbO4l%F_aFUh}=zfmeXtv?AbAeEa|jU z*XCbnYWgLM%C2c-cj$&SM!!rAN?U`W||p>^YYZd*715HQW73rvuz;{lyF6 z!s^Exg8$Cu{yWw5AL4~^t9Ul*UJ^p^N;PJ5G~OMLZY!9o0HG|Yp&>rm#>r9z5>@8F z*le;O76)P{;X^I%N@JwFU`WS+M|TRhPI;PrA5e#eS7T@)6@lHcjsg`yl%n|B}u8!jBu9_S;J6V}j)|pH)8e zzQu_F&(zr2L*CWNGzaWR!XO2Kuw;iFE%7EW+Q;O+4cYp(Nzf|qQx)|@{;Ce*5}g!{ zIr^vQbenB@NGd!v2-cc zN&jm3cJ$BuE_oPMyBU4nS~s+A?2k^m`l^=3!Bm#aqrN~B;4dJg#+mK&^XEm9zW@}! zU0pX&V@G3Sd&>u??XKlM_1rl{DRiyW?>+n`7xj<9{|Wiu^TNLl5W9NuGj7_W}moL+j57TW?SHh%%gqQg+~ok zAHM;Zh>m;9U2O;9UqHTA=dr*RTq>USegFL!t?Lt(UM#8yr<5m0M6~@)^d3q*(1;Yv znpm1fcxz7NcL(=(_N@Bq$A3=b1wDCUlY1S}d`9Gi9sylhs)&1gQQX>;@dfJ;c=rhS z=AAl^$GZ#7prvEo6e;lbUcF%upUr7OtSzn1`Bgj%ORM%PZmwz0SO4?n!T+(f3Q4P5 zg`wzTk19u?F4Tr*X;Zt=j?9KKQgu8J{ma|>3Li^LV?j53PiN+D7me1)kc$9wj=wi3gmE_|^-*M%B#W#8)_@3T#vcsVB)iZE5` zz3D|5T}O+X$4G4l>*Yd$1j|rS%|qs&)74!y%2`C284TrJgIU<9n!#jh?1Zn%ONI%V z)i5R6pMzWgkm7A#5)rj27`nA3@cx|kA%+&zp(C$y8e`&Tjpxw#m@5cCpsI( z3`?M;|HjsFTdpvV&RtER0`dFI;5WovA&~y80al^8?jlmg0LdD5KQ?Cu@2?3_(3C$H zKUAdd&bR4GN=|w2_xJ3~=_jxzaOwQj`qii}N;be5;z)>o1bcRm?5_qYysjwdBccVwPFwo zAsX<)Ehg<`?icIFVkPV%V_?V7&ngsX`{;gl`q6!jdz9ym-=ISj2-WsyWIu+B8}i-H zR=L8JK2!U8;*PZD_q?t_%B&wFH9Q?iT`u1AF^ zjyb9aCr1?R@id&AZckd1Gtb8>)U>wQ%PcJqu@U+Vv*ZGijS#HSH@5jMxH{{Dz=wm0 z;Y5XAX+hl;9DGdFF^zyYDZ<0C>6#2&>8%&*)q|vK-Bme*bQrPrnnZl9j8_p`ejFCR zDr5REQS|WVu9a{C3`+v;Y8~297Q{S4JwAfOd8ySTae6n>0x8IhB)jlbWyVha-u&Dx z=v>=uAi=bQ<|lN0`!NsUq_68hk73nl}_>y;7GcR8kt z(=uJyvU~`lG`8Ry$*NV9ZP}@tHlYK`rn)+*LhJ_qH|(Qd+EH28Ar|XYnW*qmPF=^n zQEBG}JIpMn&&=WI4yDqxt>#ne=ftY)#zLWh2)mi?f8~~lIf5=S%pp?$BbQCZBM@)E>***H4 zb2eMrs)EuAq$$D|)!ldIC2=<(6iRLV?MGPbhEc54I%QOF&f%t}Uq#~R8_M1gsL*f3 zJM#jB7O!_#*)7$9kcw$tt@mgjSm?#QtPzBM##K}Bg+v9meTXW`?((uc{kV9m2D@S6 zwN!ROYJB*XA;n(7v8r7(^kq8{HaWz%N5|Wr1+&%~7E4`8l;9;7EJrzVn#FGa)1%i; z(|iv3kWAWo_J+J05i#0Tv{IMyMq*^Ag)Y8*RV#{`;rGd_AUp!ZZ$qNF|@tCZPQs~;Vh_-{u+N%sUC(yNk4>@rv6`eIZ`ynBOm);=x+k42=H=48?s zLCHn&d%LD_B!rXMF>yGSXJ@VrIw$}FHD11P?y**AqmZ0{f_6B=n8>1z6-5POVXFs3 zDPQl3M=T}$&j^uVcFbEczqe7bSE6-4c(ujcjr=*dT3Y*&cFxTRaH$3J&#pZQ&4eho z5nnZ*1)40Vf8BLcarBjSsEMrS0=HGnfr(BqUQb%(b4vpwt--~UckK$#>Ub%Q%~Ac} z{N1qvNWZxW9lEHCMno8^hgt(szdQm=J*oQ3Ir>zqa&I3^nD}QZ)(aDxu8Ju~hKyDS zQ~50kIJh#2$R&y*j;P{v8*w}+%ZYH05mR7LQP44tuSv{ia)&I9ApVQ58dG#K)(;q` zTQ`Ur!-{ve0t<2j(|_YhA^nn^Zm)ag!+ z?2(fJlEA)d&m5RdoY>XBjYNJe-=q@1>fQnbf~_Lx=gZNZEf=U{CI~ z{T7@rK+0pPx`E~?PRjwh&EgV075e7AwAYD*Yun1|IBrz9sG5r4WWFSMSsz#SVHBYQ zkrca#yQzwT-%5YvoF&3bm)FFS@#7^OkODq4ej~#Q>UE!=2=1GPR-i8SZZ3{TZ#$M8 z85x>(%UbKJ+yUEA>&ha%98S)4YnxkU&jIcJ9^Wc_Y#YT0(aIQ!m~4x4RLxWD1EvLF zrmF0SG4K{?U>&*{Chsq+p*ov^Jc*mAdf5<{5(#!?P@LCN+a2(DZj9qPs(F9N?T7Ea zM#dLs29FZ@MvUXH76lP+A|4qx?t=%W6@9cCFL?}8$#!RXLf_QwrkFN01cSZmkZ_{0 zdBcB24jc3Fp>owuqT4()s|T=*kP$~YE!(VJ+st6_7o{3(2KRgxm$i|-u?^d+sJmrO zK$iQgS@R1mSNfIz=$BG|SzJE=5l#FgT5;;Mo?5!g9yho^yPnwv4|x3tzTZ?m-e~$Tn zC`)Ct*zl^AxQOCo#;5ZGnbY=+)0?!kA(kIr4_Vrc3UT#Jcu}5$JM3fJz}S*q@_Rcx zyxCWfbe3GvB5m!3WfmIzsn>brbR;!Zm##*m3Zcg8qS{a_^vatA{69eRQBUcN3GM*} zY!)78f=`fAIrKb?N+f)l<;PMyiSYT`v9~{~I2I9@ z&BNYtdI7^rQ=2o2QyObvtJz%(Fy!h_Jk0j{@^=fM7ik)l=7s$itW(O#@ym^S09 zDmD{&?O?ahWAgNB7dP;I6|3Fy8lJx0I&WKiTb5|L$@%>yU!P02xB7=19_=~ScUR=L zUluHO)%vxFR1;%t+b|_r2T`r6Q(rJ(wc6Uh^_H&Sh~)2De5&kEA(tTXsWzty%QgAw zfeELarbM=^rt8LYbuuvm%!T+1ldh-j>AF!pIbILddZ>;so#oW|8x3o<663fcVaN?d zJ+MKSPDr`^F)+RUN)UH|hC^(nTAaRGDiVfX&dyEw)Kw8^NDMaScQz-Y{nS#_1f`R> zY`|EynvY-5Wj0Q+CjrzdT+>Da$>d){-))nVH&34xRko;UhdY)&M~+62;fpz2qnAji zOuDS2ay`BSt3Qa|*}5#7BrV@djOmXh(}(g8UJ_9yhFqu`wE?MTUxBf5JH=)c64lOaA2L| z-OsQPqiU)bFGQoL%*na*WRp-OUe#roipOHA1&O{;MqBVbF&zJg>savle;;^5Pp4Ha zR!lVv{=`VOVyD7VGtxw<#g-EfBqdZD?Of-o^6j&0-T{!}JOTXiTGu+wxN(_Dvh?n<+2(GdTK$Az+7UO4IlV~axEG8VG!i)?h$!ZhKu`OQW6y9|Ogtmw*R(uUvx1B>_oIPn4~^hx z0}u7{CR%ty83|brOzxk}lK+`*5cRV%m0G3mRrF>HL3=@KqNa8Ml~Ge-cF_)L?RMMh zPOgEFNFMoFmZ5>vB?VUM!%}jEn_{of<9gyELqaD;zdIJgyx| z<&VrHIGqpLz#$xCc)bmPx4HBIzf3h}~)H$Adf zeGtegWJw|=+y9f6I!$dPsa4iq7OyR=rc`o>a}kb5i9b^(@N|Z$ z8asBD$fF}%Kl4*I0HqTExB$4Lo#-u*OMon|DC^$#Ic`S=MK-T{4 z?<%E1$wc#0oY+s6gKzgn8KGA$+j8yqyG;bR4T5+5&?5v~e$H~Z# zZIAQBdg5$S8Hxr8i}hrGYv*TFz#rDy|8-GdoiUIa;cPMW|-= zh4$v}4`BS_C-ZT;baly7c4AIB2wMrrW&lBJNJP4m!5N0Br`*+wL^bn39?O*Bthbvn)nPS0a#ef;!<>smwrDm57i z`P56@@)hG2Q4*P)$B0QX-9*W`ZrKmmmGv~?Bk}RLHchj~tq@timrNiZAhK(P;uUa? zD~-MT-^2X-5#mPrPWH;4&j+FxJOuHEK8IH>R$w ziaDZ$S(=7i-6xetOz{TFBCv>#Vpw<3k0C8wVmu*)oX4&GVdejv^x^&XaVAmPU%)%i z>eYvn2JzaNlix}6Ug2$D{w!$}#C@3@0Ef1;q=_}C1ntHF_}0_XV%V>OUR9m5yw%fQ z#WxT(t!azMy2jP6oVeNxr73TqjFTDMM^jEz&aG+h<|V6p(V8ENPaWsJjudUhpQjvs z_p}=$Nzg4J%w5T2kR3nyN{hMw&8KNAH&cINKm0| zZgd8<7cxQ}WJ79}#-cd9I`@;B5)4746Yu7~E_g(uQ-#oE$?01Rscr81lc|8IB1ftQ z1*m!ioN3X+SwYPi$L2v}7AFbRlZ5X=uW7{J!gL-SFA;)z6lPSTVK5fKyxu+&>;&h- zwGpB}lu)t%5SK2%cQy`hB^vi=Wv)pP0^03?DP`(}14Br!g}|lJTm9lDiQx`&ouf*a zd8rbQFL*B)BF?aw+lZ3FBizd9rp7k;gNl>DoRIunxi+N)hNj!J0%tOC79A!LKPb4U z`+4=|09NDQ>vzwzM*m}cn9rE4BG1pm$xj**tuJ`UOl!nJJQH;aX4$>+2|{bFE9IG% zDlLsw10f`YQst|nr3NtP;hnk#`JPzvM`c{^H+*F7sOcUNM1|=-4bGGyoDL3_na2>k z3y-3(;j6AWB!ICB%EcTcvjdlKid-I~=}3O}uMJPs===rb9vyb38`8iqD=kRnp=OEI zB{qV-S)LwFqc~f-;|tpTLVyN^3#Zgi!-Z%H-;>E}A+)RJ3lqNNI+%H}Pvul{w@E6v44Y|>7BsypI`)sU|{KmC#7qHK0q><(ts znl@W`WRJZ<+Ri(T_v>%q=cRn-hzVz%pyuE`3~0hPsWZy0nu#>F;>XK_wLcH-z~$1| zc;swhbm*Uw@-tk1@DOCJt!%|H^23EOkseg*D)<04w#H~4x`g7;Qn|c3mAnU(To3215Vmd4nesMng zm{9QT8voCSx$LTj#?~fZDW*J26FZ~g1JrO_Kh#jFMwTe|I3K;ceZ|Hl+2G+qX^mV( zoKMLs#efC$^ep04A<(nSnp7%Yi+?O$mSR5^=TF9@3yLnnn5SVc45wYIrg0WMUwVPO zj*1Pp3X~<_l@7;%nx3xO&dpc&E7R$bl)9=1ojptHGIx!A2b5)&snW0&6gDtJ{-A;K z{9eHlg14}b%-vG_)#9s1`F9r2jo6V_-km*2oRL2X`AB6Ohj`i}Ez|Z&gztCeWIz z`GY{ptL9eNM-$^|lU@~6?ZdbHu1-#Zz?H@>etBjRLO|JLHH;XR;0X7w>5DB(A z^qmpRFp5vz{u*mwGgO)Kt9w9ri_S=kfLE>g)8 zIwgMGHsw{Tp<1PA7cbEegoh}w0$z9QMpwNYUEpjE92S-=HZ05|W^K)5ij%fUNZodG zU21OOf%lA1Sw&k4!g$z)QVB&3k$!n|T6cR;$2`j7q*Ll_(=VgjwSl{`E$tV&kPDix zMD1|IalG(Qc!X$f>3UP|=V3wlhboG;cS7ICukoV$XM6kU=^Yp1WJVm@8rx1Ujq>m~ z%l1C?x!?Oaw^<3vKAE~Hg+Sl^I-|P0q1C<2N_E7lZd|X^!-*E_NK=Tqv_q1K(2!hh z)jinPC?uk8d#$=d_7K#=3mrY>38$K}X13ey;k3ek{TDDi{wG4|wEZn<{04#$UN>># z4UB!n!Qn%a$Bw0GY;#K!ZPYSC{G{_z-OA}S2z0eh9#9Q$r-{mnNK3oKUdX*G0zR=c zkN7}br)K&~xVZ58u%d{@eAR$yR|GJ~dnBVgWqE64;}ub)sgFG9@+7|b6gc3Fkcz1i zV|9?E;$UalXCm}+R@J8vDD&xhjoGAaKxZ{~n$we(I(;7vAi3TQm8O`q>lb=Z|8qV} zWeWW-V4%y80p0;VGp^@{;fgWhE=K9pjP&@#VISMB4NJ&Kv)KsQ>zS{P`oOEcWyxC5Y*4jf%Ew!_&%R|0l7E=h2?d2|Yr5N5t45 z?$-|a+RLM{Z%6j$U#1%5Ki~NxsqglF9?lWzL?rBIi>!)3cfP~&JJc_Bo4%5yzS)Uf z1_IUSDuQL6n`^Ceaks)ev1hSf$qNw)6C)nO4UvE3)J#{B#olx|Unag@P|{KS`rsQ( zro{&_2jge!pX>p#es3)k5m31U+KSw2tRWGyc-^X4o}!B zqAE)xl0P0p`&u)cFNi+&>T^zU@c!} z0%tRp6_Be#c4GG$eWm88^^7~h$^Eb_@6^qT;_2SLdK2{$97gisCv*ZOp+MZcggRXv zPu^s_J7^Y)H^(bYXlidZFiG(3TH!VOtr0U0R?HKb z*D1GelQPyoNBg`N0Ca>~0R3(cv2@DMC?RpohlkURywy{#y~(~Rr+u2hfxKgbdS5r@ z-{ohEQ;FY9zZn}2iYjfeg{4sc^QEC3*imD?fvaHg>Wz&J9_8%yNeEq;e582bp}g@g z$buzRL}mi-7&5`O9&{Ih<9U!}z20w%7N`3>xPe5YRdVQ{L4}AE`&A!gJBw@qI64pT zpXWKk5V`JsL4~un_bFb+POp1VrUkChDdbN_X;XJYa8lg>cMI|NxEZEk`M}*Kw~ue0 z7)&217mEW_8}|e!xLRplJ2m1YLFAP299YF(3<&wR-g??<*V1ma5t#96mmW5t#{8Bg ziH9*cWqMKQ2jlRSV&c+v7s|<0Qx`=pxleNJ&4?nFU$z+V#{V&|90_1d?~sgy4vKh+ zdwbA1Bz_&pf3<_5l1Uw8Tcw5Is9EfY$MFz4+Y@Pc!K->r9j)gJ^+VK*Tbz@*MwXht zK~+)|5Or0f&KOdk&v*j@>b^zuI5iH1JW1>n&CPn%oRlZEw$odxefX9U)Lhjins%(62VV&f4)$JCY3%v>gDMAavb|Jh?}*Gcy(jum~4JS zjEG4X$2;Wb%^cLGU1c6`YjM+kpSC*3llWSawYRepIab6r+&lT6ks?#Wp(C%m5XGfmgd(JkTJ(?jrg{0;B)LXJSn|FYi3*7B^u%1@3h4h-SEekF0!#E#sN-ewg?|1UV>j_M9{Xe!E`5 zqiP};QP}!O!bU7gP~r2XIqb7+$Cs^NRc2eQCGi8O4Il3#n#Dt%NUt1n=h7-LCKLX-uwx4 z0u{C9-R%*>99o)5#JpiTZcsLFmFKe>cDA^Q)X$)CO zYS1x1;OtL_MH!^97;_p^oOt+fwo6`?;N4w00pffRk`4j#3=DA6EfYBAXkb_ZX9%(nS5Ybe&F)*9%x z{wqQUGmV?1BHtCSphQoLFg3vxf@-J2@c@+$7(H}vRehg~+UEj1zIM7n3~+CepWFPW z;-5}5%7sYNh>lm&$ePop>WHU*v4YO>SsOpp(7|_uBll+mJRK3)=rn?3vl$=f{VH`c z@~wJPRSI+Ye%#uY;MEAOV77Mzf4ATbwv7rw-tSHYC3&*@&HzKi`i(lqgc*b=wM zV>rik!L%yMShFvqNP1Pvo6jY^TJNo?0vU$pvEjLgzR9$<&Ct5bT*Dcg?w2d=P})p4 z_A!py>cNFQ57Hzv-UXBR$_4e#(>4ZM4>o(Ae~8Jz3@7u#YAI17O=t)clJB1{NWtX2 zs1Sn#r9{s#2CwIO7r_~}yu^+Dkj%juZJb44?66}mevIp#`_4SEnaN>4eZ9cUlkI7} zwo#;=Q(`uXpX1(OlUcOXHB044-cOQ_LTUrsrP~g;y&$)QWxLhc2Ie2|wxdRm*N%d& z#VAet!~0Q9%5Z3P+y6kTF9JDX*b)9Nu`k%9VHj&j$q z|Iuqu=N>}UD548s!yH~K5pB;xYS}#Na~e22tbV7V@jeqb-AF3UWrggK;uV;-Jc5p> ztZ$V=q2fycT2zKqR4Lqkg%ndU}i6iFJ1a7+dTnk^k6d#zLPN9M0r z#zt>I3ny0N)Ohm#%4usR?B3CMsdI^pjJDeZbZ(|K1t%?D*i&^#^H_@=R$8FO9Gl%U z+p6T6EO>7k&6W3Jf^jKsG^tv{FhNv*cu{3&_xjqks0GI%C28AjKdWmb;di2$WwoS_ zsf9;$&SA}&NE>sSoaJ+nnc1Y}t0jx?pU3&0O1|$Ya$DcPf!HNCX3dmucJ2cy z`#DY=fmX8#h$NW)iTOqhGIf%C4np}uSHqgFID}f-*$`j5a37{q{?y$3S#?%nKEg6A za{E!@wv4f#n0xgtP-gqhV4bpSzCu)$VekQxJX?HOiB%vn<6xX_dHi-PENh zgzYGT=4ocrn&!x<*k2MSt!09uVflmXk_-S$c87H0T8&js=4!g~BB3q*cRs!9=6A=7 zJD#UGY=Q6r%)CA!Pk`S~6H;$jllgNWwY#zXab8MyE0LJb*eIIBzdWJ{S%W;N#q4Xw zSN5J`+kY~<>RDK4h zdvtp|B7Xt1-4ok%t*ep5>ihQmm~p!Bm@+ zjLL@sucMdBN~j)MeH2OG3_uw298YifJnVx{lJ|o9V&#*#c~TZ#$NYZQxTBgkj$?II zn-4g)+SKJvi`W_2gE8wTj_~+lA=%KA? z$B1}snJS@S_rCyzE!o#xKz@0P9c0tM%8zv!u5oXY_Zop%5=g9rRMo?X6sHV|_|1AH z_TPIC@9i3DMSdS}uj4&b;9LbNO#+0v!p_baZ1l(6ofH-A@1wzH0R|d!4n)7~X5BtB z`z%6n|65FaRczp)X&9a$%TcbB%qvP0f&DfMY0nT&mrSv*kHpNdv@`_tawlDC51ebn zu&}M1Ib=aQ*!8{yq+6@ZpcbNeio9g2ACcwfOp!V}t5cvL1RrJ#)&3vH1C!wj*3J%& z{Te6Uo@NW|Du}Zrp-`t=K7){G3lBs0Teo!e7YTlKa|`n*)H!v)pTR=pD0PAV>&5(^ zgHI?@032yF01@r4O%cQrnjb$-D_no%tsdbHm>b(M&8;+@ZTx(7I?OIj6DUT`-23U0 zYhm@r-5a}HH_xC#m`n`CM2tDHGMFpdvwpfGUUP8z+LDx#=gY&mO|inF#R;bMm5^f z+<>pVPniSRSzVE3&3bq^N#+AhgO|3dMoxWors}mZlEq|b!&$W)DlyB*Ips9H0`ZwkvkECcw6;>rV?sK!6{ip@OJ7wzj+~zeL->I=KfxH zA0n*VzW`WNdRftX2fVuyo(wA<7B{EW{EwFSv`(bnD`DL>d75S{jzf`7uSYv`;sQ-5sg=zh3$ zI_HLvjJHdx^r^n2XKi{vHl@CzLr46KEQN$U8PG^oT5cS~LQVkYo7*<%rEg9&@fndA z?xD;W|07I1bX-y7R7~d}qZ}v>kGg{N*qYgThK2hZJ}gj364`bjsFzkb)Y9gF{sQPI z={9E{?w=05Hgic!y~`s74JR*v9M3J2(7WIIGeE+!~5Ah7o~bC7N)3LS=v@VjtFbR_B| z_Sc_X_8+`uv>x4X2K)%3hAs$OX4BVgxsx?%>anGx2vl4)xlv?R%t4sxJkpq2$o$k0 zH(nJ(LW_EbNG+DDbIZI%y0ZDT2LNWNETGk)= z9PY<8(rq$+x9e_B{jcZF7=zk_lxub?pK-XWN~zwS0O7I&v41YOfsnG?Y7uZUs_G?$ z80Vdf=^m~p{9+Z2^;X+q^Wu4JWgb;+f?;sUDV^1`|0+$Oi;{5jj^v9k#RIK7YvyBz-3*JIKqDspaVGe?C{b?MxDk@m8%eiMl#eG=mV_Ty zMt?YR#6FzvlnJ`7e6>pEkhSPzw&`_eSl7YpUeZBqW?@h2dbp&7mYmKEo8Gj2k*Aof z?VoiqaSoFF)htSSlJiuYqe#B{Ahn)C;1dq2+OQ?s5~7-ixCexkpK@*%4Ng0LH73g# zTPZ+wf%{MUks6=(V^^Xi0GN*8w^>FODqU)kmc_?kq;1rfAXN@aniagoK=U>-A~hxz zWrqDi@FhcuR#;-0b!%9uxVVRlt^|oxrdKewDB)flXo3cOv-0Dhs3ZHbv$!8q0Yra# z!Q6X?R;-?^&=$0bZ$TSnk@mqPbY5zqWY>V~4Js1$CEgL9t%HMWTn&rZ!|JjmyC4VE z$ot|U8e2r_Jal&U%uy&OwX+}!!Q6vHmp?PfjJcr;4ealjqH;e_h**yf8aBrUP+ zlg8uMKHrD$HT^a2`RWKUK_DU|XR3NEyG;2RO{XJ%9BC(OElpeN>vvwf7y7B_iR|4x zq<;8BgoERpl9mY=_bhhsWa(hdG zwMo$A^R{Px^@P*;}!Gp8WxVG>6a>u{al zFk)BFBpIz!F_L?cAt@DtgN{#tgHG1f*eK#lgui`KF3qBa*B#Fsdm$1EYC=0{B#&{1Dk zE`d{kY6)FKwHvw#Ku(>3^~ec1tkTZ6wS<9-z0(Dxr>JV`r{U{&=Bpp9Q`S=%nli!# z%N+dhhNvffb*u?~q3s^2!P~nH_aX1KrkTj|(l=YhHD$QnSn;Iqh=Nw+SbQDd*t%-ph$`D23g2?$4BW2!njA8j19No zToWayzlhto>+~m48!bEtn{)V?KaxZbYbR_&6pjbnu9+@R7l&%N#x&fYLJg)Fp)PM; zWSxf#?M)Lyq;mD!%}vQq_PZ+*hceX!o%Zj;3zj>Mb@`$_MVH{v#<9)DP-&(6(LbG5 z?kCDU(~jFsF3(uZ;U>ysmzOy=1c^Mu4Bx>k&12FPRyFGiz}=DS6&0a>WCwUVwE4$uEb)>{U})plvyO&~ymli(UOxVuB};Ly0cH16)f-95Ow zTX1RI-QC^g{`$^5Gd1&m{GxV27aOSE*IK6>gYg_6yuj5rZGn58v%(|9JSm_BejAkL znEsXZ$BhKwN^!r84XatHynNKIXIbh#o84pTw#Czo%kgfquqKvJWt-1Vcdq%kfgVE@ zS56%v^&-LPBP8f$^}-bvjk^s&Wvd^1K|p~yTAIC>xaAbf@5?+PQFdc7SMkwORH(s` z_jn})H0d5P*PiY52hGzsRTVecY}P7mq0U1}S(?5aN5O*uPMlgBpLFsYb{c8?*CB<{ za2i5MdL_~*>c=V>l?M#$T8ZzGtHK}-6^WgnHZ29q# z_xx}sS6_=b=j>#qo@;ACl${en{ZvwZhukT%^3yFXPq{;E7!sz659<>QqfS}WulY%RllGNa9p zy9^Hk%1cEW9JD%CqsJZ8qv4M;k00p06`2m>=VE-+jxNi8ff4F-n_WJ5{19-U4@LdX zJOs*Ojn2X`@_Zg01xpSCqSGwZR(}46QU2C>aaOq#&_4q~lUhL5z^|1Jea8?cG1J#M z3BHJ>gev-f(H`aDJ9Q`+`g3w`jwBr_M+{;A+mQ7icUBo3jz-^9sJg#Y8)c(VY(m$) zDc9wC)m@NX!655u7TR|rO~Bv{Mh=TEh4-`*A;DZlWLWCfEM-$&TQw~P1EwURyzeNA z3o~zjVn=>J9hz=&PjmWEa&<_hSa$zH6VnK~B+rr>2|I9m-Mpf0r386BTzs9H-XW3f zROl>L(Ed-dE~;^TCLQ)EWoM?iTZo?}q>4#+INqWMxdm6PxJMZ8K?kyAqh42JY`n%&d9Q6VXlOAS`=F@D4bwnvq4s zu9+FE1y6d#h@a-r&;CCX7|KNS_l5m!KFDNw7mE^2o>+?b;w;Z`@4-?2p>E>4&)fFG zA3~z6Wk@aO!asn4YVQ9`(qI359nJ9Jm72h9NN}gMHEn);-fRm2(afmDS@HUN>^RA1 zE{;8}gUf!XB5@UH`#x&FHWmW^V!R~&Zq4-C4N5OVAPw6ih-Bzuf|*7~iX}qSLJhms zeR}6oqqVB;e8iCs0jX$lAbvBziH5I#YC<TwopNHf0MSAP;CoZTt zq7!kZg;{Hrkt~3AtOc6#Gh<%YXYkQqm@8{)q zW1*qjEz@CWzWpVL9bHK`sZ%M&(z5DUu4=%rJxRy4Mbgs2)VDErTA1+?z9+&jOK-J{ zgE5C~E@(G~(RBsJOD`I=w-4x+XMx~A6Qpg!(|WY8vmOIlm7n z9=w>STNCs+f)EZNo6?ye*RThvX6ZHltqSWrV{8TDgouO&*(IMzQ-s2c!VT%{GZP76sl*>*woK2s(3;JCcdnJ z*X=$b*8cDqplNm{MvL&9sepQ>l$vhjSMsF4Vy%_-u%Y(#oR`K~2aY>S5+i*&x*b%| z<&mR0$8|MMe0|0-oFFe-u9Frh@LL;3GfQ4Ms@;^wYfYTMrA%~=r!AnrJvrgz2X!&6 z>A9wU3!fpxqvTqz=I6Q2ohxXu%j*YO8si84SYS*KJy(!;l2lGaEz^5OXofNNTZEcx z*Y5+SsTB0To%+*Q8Xd&eB$36Sp^06PGJXE)M5cNmM`(wi*70^gai`In_WEHwaUj*@gCRk8=SZXt<$UeFrBre(&6~s4;isAEhl4_c zdE@xtZlSw{M#x2AM)Tp6B^WuiP%w^IIz3`_)x#}+oz=><+|&1^yZpMjF=5{ZCo)cM zsAzYv&g0BG9A8PwK`zPADEBqQjp=m#MK`x!xUi%IqatnFSZ{U?FlP`nI}DqzLQ-1# zD_}aGlNJ}8QdNG zLo~@%Bk0*pVByx9^f>9pz4G4Gz-R0?sl@Z?W~68S&*MReVUIRIuW2^_;w@x%M>{v4^jnl zTopIxp?Iy!+U#t5c+9JuQ(!Z&|F0L|Nn5a%Mr@_51d_&hwLFo6)^}!cU(mwoZ&f$Y zm7J{~6N18XVbT6k3t(S#%O7>$NddN(M-Pw4BRKVINTZFE9&%?p^C#R(EQA0CcEu=d zw$lcojxKX}r_3j59>URS9#OeHx(-q1bV^m12f&u8q)kn-*V}IB*k3pgl0=4a<8Q>7 zaQLAqdwkD`NXE#zbHd{aWZuqJdr4vPneo`HJ3cj8ISzGSVYD63bw882gEF(SQBIwm zCs#`DLt?&Jc??mWlvVt@S#rIrkVW96Y-_Q@=%|MPeAX`>LMO~^}*`JTLudNMtS?SQ!(jJ^a1l=^)d!}yKNJGkE zoL2+94nVw*cK8NzOCP@b^bFih5&p=awsyj+fPfnu4?RLMc3{ts)EIR;C0Md=PE;i` zRl3@lEc4Vg%^NL8KGtI3++{9B#DNb6#A7ojI!YfP{}cOv=sRcGtceAre&n0 z>-dhL*2M{On)=XKs6L-tf1-sa7LCrZn+|S9Y1HA{=I5ScXf~LmJu5G-tW2XZH&okA zA2m5oaHJ#qIYnXwzRPq#go)d88pO4>RX=AGwzEfW@_ll zY~X1z%-m-moge23ijg?y$<0b;5VRL#S;}pIv?$;MUb%{%5|SzQ)hW#Phz+ope03z0 zFGhTs<&@$ZZP`ECu2GNu1CTISFOx-W4VPNbyx*|Av)7dI!O|U=FxhKPi&aphQ8f-! zQKbI(i61Z9RBmDmxO6B!ol{H@AVDWRLKBH%sRH~kX-4Cbg8+L zX{*CxN4BM@v%y&Q|LI~$P>w_oLzzTg`tr-KA7b5RPpvq+~x*bk96=m zg%s05bV6fxQKE{zSB%|V5$a;U8+1;}cg;4vW8HlHUE-8m0hRi;a6(BW0)`6E#N=~nsd{S4 zmWqmt37Ie8B<({ldx1l~1*UrgWKGst(&z(SQ?szMkoV|UzG98%udybxNL`^f# zGb@{O~IH zjAL&<#L$gdaKeM_S~HeB>_EFMXh%$(+pDy$JV=u_ezgTBV$)c`W!0n!#R{LJH+d?$ zO>tQrTKTK z%kNkX14@UcXQC2j0w)rg_IVS)N!@A@cIlaTu$zVcU?=8VPAo zFz;&Q40aju{oIV&RT3(ovC(I?r=;(bU8r7!Hg zxJt_-Qc|UthLk3c)&3h|RD{CRFELf6ME)yPdZtNw$M9jtl|8ldT$T7lHSS@U)boST zUlkDjcS2s-9|}gLwECuq+P*$9jeDKwiwKsaasfG~bl7tCL2UD*4H!YSsUd55mu`0- zQ(AL=Px5!nF3r!<_NLD*i_er(IOCEP+@q5JGPP+(B~1)l3}koXJ1wXb%R)+2$T>W$ zd6&GU1G;1#dX*HMaP6D$>^0*Kf#TE4Sg~=Ov+Z%r`DS^v=eyLU(dd%UCzboBF8(vZ zGh6kGt4u695=__0C?|+fr2&a>EpYV;x;Ez zY@(Fl6pN!S@hx#m^SF}ej5M&4RdcDl2yp$HkBrTjw6+we2%4&fCxwQAl-RACRCg|N zViGtHZ*poD3&o9FQ5nUcEDjqm;cZf_x_Z!9PI*3UQa`pv-zj-`k36sc1Mtfpa&QSO zDG^}Sd?pfb>wo1bd8$u8X!ZOASGNc$czR}0Kjd1qM&5i8x~gz0JE~h6p=%hGLA-1t ztLfy5t;xK!6@oz(>qE69-aJzx9a}x;2+2lkp}KP-ya;msI-{x?6_;jlIBYJZBK7|p zq^s#^v0KtNhJ4=Y%kQh#Ie($&J@UFfPww2J2|6FVWojJw*Y42XzQLimF!nAQOIA&*STXm7eYEd32`Cf-F`Z*HhWKXUqQ^(4+ZsrW*y~bRPf-pG?z?5VU{x z;zmw ziz3?#U>O5EjjiRCJv3wVKf>@QZzx3YcA`ntc&% zbglJCZ&l)2{jwOfr;R|vwWTl}^g2Ji#q`hK?_ky$#R;;YpY{hmi zfBW~d&aI=D5m&XlJ!+p2se>DD_lLRk0J#&V2cGv>)wDA>;NPv0)Q6ewwB@6;T@GuB zNFB9&PRiX2+TDg2Jy(0#4Uje|Hh?e(@zqXE6(dTz(8p2k9w2Z#Z<7Y4c``KNCv%@i zQNvBdKHY(5@1<^26eYT{vi76DBQ=#*)I1A)(%MP7O`Ch1|9byi0pD=H7|G*= zaV}S5*FsD*g*^5swjxs9FheIkyi$hcsb81{WR~N3yBQi^yc~ko`;Hg}G5!`qNa|z& zD6)88z-pYFkdnp?SRe#BJ;$Oh4Zc?q0&pfqb@ylH?^W(cF9etV@&D~3u2I?X%9p!# z%<`{GfqZTj;!+4fHu!3sni`@u=~{;h*XRrJ%Njk@bsL-Gq#Yj%A)-P1U!!VP`3C_F zJTcU6VgHDDkZh*7`p7iS80eD@;7X^~1h9>k>zFgT)5}pue-y>|<;LgC1cxkUT^ZrR z-97}AOdvd-XO=vt^Sh6dpSDt~@?CCx%=t=DoTVaa>a6_ZW9nj%iafJ=$|(TSQ+)uY z0nfl{a|DsWMC30IYZde1&>AH2F`#WQ4u%EP$>Z%b@y{U#p|2dxgUU{n%CK1qO~(?&MN*o6Q;<52~LW)h=0xE2k)u6o~EZTn9K z(Je@D$Z+;rgDq`~^w0`oe;8j2_SC$w<(jkWrxhp$eMaeT068|K16SHy=-PgoFgJKr zM>spITg{c04eA#QahG+oDv`#a<;D_dRdH}oDZ15wmjyPON%mqcmJ6Ec4^$+P)zniDC>o6Nqq|UQY;#)z5H<3u@_CnPzaX*_srFX=s1Xbikay=lqig$@7E>Zn&mV zxDSdmT|akFIc`u=xQm^s{U?8;^IxvNm$-UZe!)xOGXc>2*@@Fl{wzU;K4i-=EG0i% zIUe;<`Hv^bqSSxXtL8N|9YfK-&yv~Y4&r|Afn>n{Mj^aZ?kZCqmc4)23Sb{|({7*0 zw0gL)A1DNe8RPWGd@(lCVlgBoO^Fd&Z!N3MrYb|2!f33+scmlD<5g!F*mux6Ww2I?m;G&q{PnvNqQ{2y4w$NOLrfy4|WW z3Yz9Oq%Qt95D)FLs<;w%OO|)r8@>g^?z$U9;^cN)HmigKv^H_og&ELtfFd(08JDp7 zd~7|^<}`WNABohXrpuG>ZRohAz3Tw>Ul;zc>VRsCHH~M47`F4YAGE{#GLA5y1VM_b zu1DR?SGLOW1910xRnU@YcmSpV2{MT~;G<^7Qu&sPHG!4)PVdx^zwXBK$12{~jEm%Q)1 zrzDr0Gp8vgPL2eAi5%~8Vw>xfSc_wYat%;wqTw2vAx^cO91tPZ;XW|OM@L0V)Ris+ zipIsnUbvB^(43l%)@K5&xGx2%o53V`)71#fQJ zN9lQ`-Y*#6*X%z0K7pTE5<-qF%X3M3PE7d5Wwb)Fz09t5Zm8GZ8MBYG7!l((66&b) zZ8ChY;^<2~QwEZfjFO8nbyk!j(*&G6ydDRfexs*rseRmH)rsgbh@PD~PE;XDFUN^i zIJ*9n2F{yG2Xn#koTTo9w`1GI*-iqjQoJ>2$XP2&j76-^>w7Jut>su>Yhhx!i$Xuf zdjTu029G74N0tjc95d(nGi0*gN!V_Tj+cu!-hK8z!F^9-J>y=Q*OZ|)LGKvN@l4HN zsVj%K?UVFB1q{+#%p(Vc&%0>dqnEMX<0gmQdRdMysBL4!Sm5>{6bn!5t7#6W%%D}B zl2HRlIjP%QpxIOki+exFNqBhMKDS#o@#ga|;ERF~Yfzh7JzXNLoe2&+;O(HlG|@=I zKvNb@dz)X1Mg^;1;E4ga4!jd_OeCDP#Fxg&AwsSEfNUat}ZA62EI z(PX8q*_S2z@}|1;DK;6T?%M2K1P4iT#tXXqafidqZP4((FtYnWfq~$G=fP4`woN1r z*5|k)n*OEqc^bH11B#9(qEhIaa|^mHnW;Z0KQHNK1r(xUQJ!Jx9}~`$A{Kd1x`7 zIrN=KefXKyd+dB8jCJ|{7{=J9kY@kiC>5Yg9AoLBIu2HcLB=cJ(eB>nRika%x5W+H zlFqnaL!e-UV5+4y!Es;x%ml3jl3rB9du*8U)){hpbnCRCDDRM~$Yi~p&@%11J?3>i z5SiPyVdJ-zyCMDY zGeBm)2g?0b6&LwsaSgSvFvGC{tVDL((EDAGOI-c{%uennBnrEn57o4e;ko) zxLG_48*)8*Mq7uBUl(JqCtb!I-!f{ixwMO%G8)D5{?j#9t6x`hq@~D>vN^G+Idxfm z-_v1)CQe5TT2C6?mL207;-}PL^Kz%Lmxm?`C=CE(U8ckN1VOnJ7!3Cp@h3nNd3>8V z&=_Jp>C1$P&5V|JFS%0WtA{Iu9D}P0D%#^(MG--?wcLYWC77=zcBx5FFlhy!PY$$M zc6FCVm9VyXD%E*Xjhbfiy1$VN8aB5WW?_J?_L$pDtf~pLi0?6Ffwzp*hOj*V%qW36>8s4!W_Lc=EAz;QlQ!6DL{dG2D#E|~a9 zsQ7!JVfV8`4_Ta`@-P@ft{gBtOo+2`n!{i#92=#qB6|NF;B5ivrTjZOQ>PLrV;W!x z4jWFQctKGP@-aGOr3^eh0C7sCf)*Fcas+?Yim4lN5+Y2Z%%~R7dBwMCKt~(Oifd)3 z_(nfpUR!eax7&5Jf5yi6?&d5yQT@6DQMp<{^SMXsKemk#m4ENj!gsGS!rWa_bXN6Y z*?Esx!*cIw!>u$|MZ;*>gRdSXTA(lDeI_+e%X@tL%QOFs zv)=`9XDh23Tn5LqdSw=*Awdc-COT@8Z9?q_Pbw8o1AcNx)frV5B*-o(-hBfCX6S9j z`sdjuHjq1Ls^1#MS#Pjhq>)>V-lUfu7~nZ4sFMiz?Y#?*jRj}tNJ=rr$E|!zgOn7h z$@~75S|onk{S`wigJva4Fg_F(Yl%8ye6}#t7Ih-Yk(X}{>$m?0ka+ELhVW%9V+o(Z zf7^6}>uO@ZG6%Nh3)opXSv!@KJX5beSWpC6{inWvlpZse4oht8kyAylFjb2Q*4`&~ ztH`Dwq@JKB(HAtcEj93pXm;l4h9$cIJN1#W2)3v)0h;hz0EV{HPSM{bTJd`=fIt$m z`iHicT>0SJ%eiF7ooyxk6#n^K7Uo)WdL6+s{7mZpa14ey!H={)dXH3faKeYMeQt2A z*4DeAfPh~{cg zy&t_PLw94{-*zhRsUr=A)(9!|QOzPud#uTbNVg}=?(H(3PW&EFn$l;dgU(Ibv z2>Ubyg~i?IJjn(>?6g9OH{*n8)L3!S9a7%rtE$N;z@`j?@UOAk5oKSF)7m(r z=8U`jG?$TZVInAkkp=kkad3oixTTx<*!dFns71<_$S&|qE!d|oJ$Uov`v9NK{^h3`GA^yoX zHWT5Fd1Zt}N=ml*oeScOu(9!KI9}DY?YIZkKW4M18PV%-E}pIb+!AVT=DG_3ASEDX z_$9i@JpEX=V;L?wdfO`ut{7RfqL`Jct(9?VRAdBy?nCAnG1^I%{lgv0@&E@NQq^JrrX%IT$1-d*zLieOCvx)GwX~mVKnJ&n}PnjbkG=I{8}4 z7SiH*J^;R$9{I|wQKS1&k;^};ZS$3eXX>@Iz64R)sYqwV!F%-ZN)F#NHu`rIFvcNB zC%g_KmujuAQW6DDM7vXQJ??ih^vG;+XGa)$2ebM0nMJ8;TIR>U&{aV?^)Sf;@*fE@ zd%ZerJ2r`4BtiJFw8=HY*2*;33}B31gI?qhHC;TLXBti5Kf$*ZQrLH6%rCMWjCoJ1 zcB?;(JABKvXG5Sb$OJiZvB)H6?HBl@dmffvM};smeEB%=jF)I;7LXE9p1lhU}a3( z6|7o(;W{gWt{Ls~(?c~sMI!R3B?Cxeka6shQEJO}@J`w+dCI~OL_6DqDdG^=)0?r7 z+fw@4Bf*Q~upGolb+JRQpcUAl{+Mun;;Dj}{GZ|4veOFQ_tuLSKjyDR5!zvjk%)@M zBDSzU0FJB|JQ$`q3uiTsZ5^0l*_-SLrOU)UjW^@HqN(?=F*tGX5WOK4VvFZhJsg(! zPgzUpStE*m8m#H@NLliI-P~Cv84*V49Oc_P3JZ@;Qy?el;b@{bo!BuBJaw1rZnc}BrG3}Ddp!_$t=yM3c3mT%ju_8hXOg-sAYk+>8ki{zTGW!D z)Y2u*(rm%SWPhw6Ox{HPrpf_AT}rQV{u~gH`DzJ2W@Sw}O>13{BtfSE=CB(4u*x?g z+@3edG8AUo>wr3%vl($vQr+~)gPtEJ`gC}|Oxa8+H?8Pvyo)n%`SH%5Q(84PLvzTj z8!k32Fy4W<+B@;&sZLmSS} z4e>HZz|pA$K!!1MCe~LBWzymmCFb^9dvKI(wjsYgvRVC+x!<_c~{5){;Zei5iGOvvGNU-Q=%va|g5js9C49JRzZW8px5WT9Tw zebyLO(Ogf5fib+DB3>(nnsad48~41;PJ_QksvIrwN|N>0VoizjYtJ91gADKljr(*P z*pnkRJhPp{;zL-|#!<3+!h9<*8$nBZoyWztjKl$cayZHyUIkm#K+PA%q!OdOJ(81G zn*0K7Y#p&~(R@cAPcU6udVwMJL-CT^Pnm^uD}JB!L7Hy4FLXy55ENBk#~T(o_S)jo z17E0FA$T1OKUSVp9g^D?P9e^*YM zNJZ=@{M?G9S9Ml8+Ed6y^zwc2F{4DujkE_#WtmyR4!NA|f(OO#6tA26Pc7@BHCmQy zWxjWtgv}P1Uj;Tw*QFh47t|&c7>z~zS6~sjfVG4q4`Xa%3=h>JUIk4d4ArbKXHKn{ zBoLP*XLdW~w^$gG+a2C_Yf@L!P>L`?m7Q1-8t57rqDXg%;(iF&%zb#N@`sY0i{?1_ zS#;e}JznJ@4n!d2SZXHYq|`qVy8wQ57DQkh3EI>({%{TdIeaU zHGXKoE_}SnWM&kBDXB~aa~+$di`Eor>(zdSM^iZ(2#Gx`-I{W{MG-ts@M9`^UbNgV ztq}-(8zW>7U%2^gw81aeIH6yAq)Q{&R~*MsdW`MmO=LZHR}nM*|LTpf7z38iI|5Ry z0tG&$ZSnQmO6L=%N!-5QGC{YYL(Q^SYu5UTSp@ms6#lVSPAZFv^LJR+Q^ivfXXUcfCvV!$dz__D1Yz=um+I7*i z`vlxR_9_Kk*B<5SFj~)fuFXX;Ta6_$_U00;^VTQ zSBh`SZ$A!>%RTBkG14{KqrjAC$?D`uCivT&J2dHdN1v8=1SQ>Cb$- zJfu(;pS#$iY$?BLnLS8)fWX=+u~S`Cv~p2qi#ybxKHD=omthyEOecpnc4SpN=&Ydd zQ>i!_Z-c5#%e)pEN3x=(A0=-!8*Af?GeUL}jg*=EYaKp<(QUnm%**6Zjw#j!m!8-m zmO|C8S1|`&Z^muBOvP>Kk)ne*5nlk5VZ@hV^qu`;P7Len2DcP(+`Z^UxKPS2C5Smj zK_$(hcHYwI5mIVZ+z&J#A5pJZ*fBI#ED`O3=IVkz#V##? zGF zhc@`?aLV?K{6BxF#B$G)$0#q#;D+=SbF$#AZ~oZEL1B{k$*RuxJ)(BUiC@0l$z!`D z1q~vs9)XHZG%}o3hk8zA&}i1Q$$Or0*kkci2ykM60-1Ipx^ikNz|APwrD z+1G33$P7^{Jj#@vZ!Qo*O%5SWl~*9kNpj<}SIM5bE+ETKMS7UUUrac+1|b{3op#vb zK{C0%OarXPpUTnYj!3E0_TL;j5B&6Lb<2yjdw~z!RMF&w-E z#h%&)fpo~WT>k0crZ38LkMAPC1)N=2lAnps~M|X!3u@l z?!pwu!Dg;E7Y9;bH@gv(A!EDdHEg{%BJUTC=Eg(=UTb5e1Q6j~(~LvVku&9>Ng}9~ zJ?vD|i-Y>#QI$rE?cZ6IN=!>sxG?wM5mmSh;|4=xvISFyMJl+I#HB`_P2S>B*+E6h zb%iiyPM6IMdQMnMEyFTzzCz1|-~wE4Vw}$Up~39~td+=c#)gx{lcM;=9FR8uYIfV3V>} z)oj$SoXL47i|cxi`#*}z)QXAIvcXhREBtbAk#c=;c?9m6DA_i^DP}slCoTa7+;g;Fo#2H!3t9PXS13*4(Y;|hK1j?6VHXg0@ zqMIgSkw=|Ml0lkeCCTDdKSPHJoY4n-x!lc`MUoXDy0TY?^v3N~;6OYu@XKp7>)J5c ziBF_Wl;7-{+?WHJDT%38rpE84vhNd*qV8AuT)(DlHqX-K+Zbw#o0I9i0pnD^A3dw| zcVuXcCG6o+npFyRnQ6AeM{q@vM+WqL-ZAhLmsXjvt97;s{m#Ra{{DRqp!~U%o4Qzq z2+*dY66NgEcT%$Q5J)YDv)df$5M-F8s`WYm$hLzq+O8FN<_CAl(lc9VPFR?i{Z@N` z6fC%BwnX?+M$_3ag#8Koa*GE*^~!k!iiBOBXmm8h;m{XKBp^PE)30Mo4CW8|K&ce& z=4ZGhBv#ZPqL6gXm@M2T*LOO5$%#5Pw3seS>UsOHroL5;e2qnSq4*$s*6FfRWEy)# zkx5<%TfO1)ZW~mC~eoM%HT(Swf{e@s_k-?A5 zARmSL8GUeh)r12h4ywigS5eGG9a^AnXRO!V{JR2QSbyKAgL%GH+;*xc6BuzHTwlyj zE1Z&FKcH*jo5ERP^teLHPrk6vtb^RQ*%q7#foTJa;bjd`^d?OW3OF>lP^3H({mS(z z!#%TM1myW@BIbzf)x;3&R~&Ypw!b)Tkiz}vNkVxYWp|UHb+seD^^!7VBZKmFW1|-v z)*OSa2LVc)%iC0ULesNBu%XsA{q_7}V{Ab)^?cH`gBQ`)GNpDOch)Vi(AU?0+KoPb4&hVV7VYVUAMPmz*lf$_ z)DvLP4YKI(lg?kt+53RpZ^!s~oc1ETzhtX-QZAiWY5(7Hf-;*g_?mK$)ci}RL}RMd zmv)~WG{^aG1mZ+|j&j%ztEMZ_cG#;orIB<#1Y3eW&l%7{Q)V)dr}_Wk{B+5*69Ljq z%Wd7M)gjdusq3)(DfZ-rxleMn>wut{sfc$M5A7}NdFCRpoz7z=c&yXkCf2YmP2Tzo zO{;%|;I4>+MN?LY6b?EI3p!P^&&pN^nsPgjv%a0ZYHhWm{LF?}*;_J+f~kqjw@lx6 z$q(b{VCfgWj_up2Mo{WLvAvdNojt@w>S9J_MX3EoZp&N_wQ58(Ne{Oj&Sx=>Bs?H{ zzrH1>5F9z1n1mz$TuTG1$hU+8gA1L}#&TV1xF%Y)jL4{HQ;zoQJ1Oy)QC}km-ay~m znHq25Bj&gxzWQ%TjPZ2$opV3Gmb~DtFtKFn{AUDG)XHZ3N+Hr2&gl%*JoZdC54~fB z^7F!`U1CHs>mTCKIc86In?04h9|v60SmXF=)alMu+d8WuXItGKau z5h~LWLCTNv^I;Ts1L1SSh;c)fuMt@)^ajzCIK2KPWfNb`=dWtD)RIBmC#e;I6%%}Q z-#%#4gZFU0B&OPkjmUF5k*JIhnqq6r``xk<-K!RugGrGa^bfs1@nTajfsDc;cQh8)746Qz1zD9%Www>{W8 zrw@?x60PYr5FE^@U>;P_4yHg+*v4vz{-P#d|1Pnq1Rcbas))2!$U6h9hGKPBc$Wiq zSX|8QpqheNrqEtWdQT-40R#Ij!i~C#$kjHS(JBJWOBJWv8~hQNQ!ZI}o^23=yjVj4 z)a4(Bp&oM6G~pWI5atQia?(g;*BTRy^|egfV{=@m;B-PR5ZyG1xLEvrVk_dlm_z(o zCC90%B=;}W!~4~YE6rj0-*(6Pip%Uw=#^gNEZBa~l+!~;+T%1)t_q`s9rz@h85^2Q zgeUyhZx|hM)D|Dj1nADmTC1l)B#mz0IFiB9yez!TIev)Us~XNk8!nCNtnvc$O}SA|nx;jhHXb}VPJ$N9IpkzFYt1o8aY+e9QN=?br(HKa+FnlC zA>EcrVTwc6N=Z!Y#4ORav!gB9MJVCPKx;|6JT_ia`^!?2?(FQTTIibrGxKg#(1g^f z8WAifH=R_4MU->|2>C4XSz2L07F{uTpo#5I`xL(T06zD1(u_3&Nl??dP0qJG{^;EJ zzOivW(vzUUZ>zhi^OTT!qs+#TUlFs@O@8PE;VA3Z=m9-D!=evA)pj*r!Zi=FNoHFluJ%% zdy*e|(eB{_N6t8EB2`6^xT9M!sXtkkIZ|H%#2zqLN2em+jQ14+;@HRQ$Mh`aGjaaK z_GQzJ*|r2R;0328xN}qnHx*4z7El3yhR(m6D(pMOJXIIpSnLroXhr)IMw1fWa0i&G zW2L>6##>SUq1Otx5s&Z~9aGwOihZgOYJ5=f)@s3Ak50KrO^asX2M@^<->dLj@Fl85 zMw6@zAws@XJdMcMNC$FB4)P+pG@)>Lr-S{!S8Q-!&v%GcmVfOu`OFOK7Se#4SNc@< zqw&s)Eg)6BRqg)(MkTc=yZ4M0RnEG_-v?|+N8Y6lW^ByeHp8>bvPkDChF-CT{{g5G zHP(LaX{Ps9fAS;~Bhq*lxq()%aNN=R&sp<-s)i6XqgC(Qd@_lSiOwnpcXdlkenqer z+N?aoG{JY+&r5T&!)>l**R3_??KC`bM%N1S&j~E)oOiiKvX1F2Z=3V9xq&WD4|p~Q z-Sq9Oe^-AZmbl{R{|MzEQvIkrhlZ5QXUlTculm<%dFVr8jmW{##63Ipnv|7`z|Kc_ zzD2I-pZCZq6#R zj>4r^qAzHoMm5pzG4<$@{K!i=aH`Pi-KDT^X#a0{Homd ze*og{?HViMsC$tZSbb0n+wR$wo%K0i-%BCBVawCu=HNHuX1TXSeG*AmS;6M`#56os z;;3?rjY>2L{$^GICa!J`Jxg2lztMgONa(def0m=nM`2uIHxR@j z)Y*8E4-aA;X2S~9mL0ENUQSh{zgAsI9lmYLKVA)!-2`PU>=&8yx|LiZt!x}OQMj#4 zi4q24UOkH1pe}O`F1yFc-okRZt=$Z--aKfXt+}*!9Jq|S@r-M?JlSP{GPdiyzrtvW zX8#%N8XA1>WDDpra^u>jQH1s<;&9z33|kCG78RdpPA!g|T+HO|;1s1hvm?CAK$+9P$Q0JZrAH3tX08 zXKlqKO0!YUk1hLyv+zVJ#+{1GMOhX=cY{XY6jveOY+dVPOhCX1twg>NXq^b825Fcr zt2fI+*j@6p>GW1QTRW6)Khq%fceQH8+TaAQ+7PXdep0`*NvrDP0x2bDq^w?F`-WfBG3**MoLZd1|EEzLH{;$f<;Nl%CC+)hO z`5MI@mB5Ci_4wIR(K6g#6%epH;>(B|EIV{(g4q8@*I5R|*=^gp2?Pil+#P~@aCZw3 ztZ{d@;O=gXg~mc~cXxLP?(XgqEVr}wckbEu)VWps=(qU;Ro!dOHRhPl=t8NkX{1N; zqPwu~VTO@Xnj4jZ-!p_VT2TMe`zyM^SDL6sVbk0&a-6AEW9GLHE|ck@Xw5(_g(FB~ zXPaH?Io7@ZPLs?$TndJ8HQp_xIST85X_IysA=mEva(h*>rY84F`UAFB1B5?^-OkZx@deNF$Ni6(nlvBB`Kvui#Nxs43I%A{hEI zPsW!fP1?lz2n%IrsI$-)ZwmJs>u}A0g3dguzRKnCc^T2heCoyY*fI6XgUS2N#bH6x z5#QvLHSm;Dw%^1je|qf;H|z#9i6mreJl?b-Mh)u@r)*o|wt@IQgZPLmDPx^v?h|!$ zm+TXK@~F(W>2KT}K2%p91OnWKK&M&PYJ#yt)jM#`nKc)5c4N0I0`F))x8gF)NH@jw zxgKJ$=nkdaq*8dG^7ZVcQ~#nCu==qrKpm>)@BqW-mx#AZ65Kog2ox`Y?J_{|w*fpr zO&9!^>E!RW^1rsu;I`E0$$gl+p=z4IA##~#FZe~Qz_FH_n2ZK2+HH%tR%Y=hR z*idF|)}cD*{LU$2owhr9cBo4YiRf%!0!4GzOyT!d92lf4!2M8 zXY1+CK>`^C)CMe+%B7U-G{Zf^7=xsEe9|cGe79j*{BKY1k@==M|*m9D=<&LLu8Ve+`+6m6tI>e?sWTrjH*LU$0pEh?gAL*(w zm*ETS=8FgnwEoX^g|$}8Hf>Z_9-BLN74V6oIW;|q?^)e|@6mM_dRoDNprqE<^$Hbr zhixUMrB1AOS!G#Z&UC6?E}b@~r4B4-{hd$->9Sdd!f`4&)p1*3GeOw5f4aGH*nR|v z?A0>uQV_eMxkQ?6(l~ohJq36lcw)PAxTapLG#%)#oP61t3gRItq_le@;gGo^K8C*0 zj=qC-^DA1z$mZ^7O~Sa*c7a)g$t}Xb#yWNEiWh zb&o;?WkYLCs7I8C_N$ajO-3i`XNw);M*hl|Rikd9ZJdo8k42ly{4PQ^wD+6bFLAnvnqB+LFJ2JT9#U3ApH~~*!5&5LXrA?gtxXQ2-52$H?-Eg` ziOA^!c;d1iwSi}2KJWn3XVWZ`CdTjPWZ`)GaIHhiKEcI!MD)krpFVMeS;~oKWGd!b ztF8;cE!%dn2-l4ZHV%zU95i8LZIP@@g7@1S$+eG$r8TVrX9z@E^N8!xDLg-A5SLdy zc3Eqg*(G-+V`Do=3zklH^uah8^myyEc!GCfEYF^ClX|gO^BimMOj>3tOru(z$)s0@ z?a5478+~ki@YtdyrH2+Fqd$$onSR+1Tc%i4J+fD+Vzu%vrFU$OjTm=!cqzhoxNKP= z3KCXXGtY=KE_xoPR|Okfc_tA&sUw@pB;k_=DKeY2qsC60piSizzCW zDWxp2R=vfCH5GG#D8|l9(GHQd&-6~shEWQSi9SwD8+_;{wX`4C2BJ#P&Go>bk0(fo z#1Y;^4~}iRDk+6psQXQIz85eekK)1N;e@KgHEz{Ubf%Bi}iDIKOx41|9jf}orsz2NQ`&*fM}ncget05G!4qF;` z2_r~kO7LA3Dir!kfX^?Yee`1dm|0@=*0`e74xtHo{Bor$WDeN9gFmG>b0-)Ji zH8Yb9uPp}P$K`GA05*}QcD0u_d?>DnrlAQx$@#Fb3|WSK=He3bjR*_3ogF^KnkR45 z!cMj~hQUs$F5|qvD*HOnj)iG}lsSAwc`oy*z3v|gRGdeb75w7FF>C%{TnZHZjn z!MkynZqBe10G>~?pQkhiRIp#KE_m+%8>(ThkLC@kF45PMlj8HiLd0D3Jx zm};e>k472p-?KI{PUMj9(=PYa^14z|pU*VIgBB zA(3Fn=EWKfaHSm^l_JHg{t>uHoTB|o!b0JqDEYQD;V<|JfqkL$LHD^w-&Y-2`yS=^m zjKmw`7#v21GFSK?AWwvM*JH3UiztOlR~07LLD%I~(@4v{ffTd#l#GO_;)#Y9J9ypt zDwE3RAI4vn)93N6DnCEf<*L#lNYzNuMNI)CJA_ z*90;TJ+tvPI?Bt}<=l|l!fRz{MNJLbPOLFy--)0cORFJn2|jJ>5pCOwDNvPF95f6MYpg!lxUS{$00qMdf~xv$h)^5b?Eu(*oNWNK zaRyP^57eY7VbF#R?_z@nDUY-yKxW*_Ew@&Ts%5!u#2sP2wO^)@QdtJn7IKxW@=Xbv z?r!<+QsZdeCh*s#=1z0R+w#(fejy1z3V%Z2V(oYt>^k?ct&a)A`0RK+R3HQUj?)4D z`LzvhMY~h(I1c<=5hr;Y z6>Sx{U~qdj^^BCBq!$N(2)U}*5f*+~hYK)EKH@??u6R*CZy(?sHz zu<8PxhT-j**u20oi3sW142o z5R3!23sHkNV<43Jrl1YaT`cJW?C1RGAY8(`Hn0ZS#za59Uf1 z4yo}G_6J{?sPOhAU=onT1dArlZo=;kkgl7FA<2J4YRZruM0FG)&6{H#z6qZEC*kTl zHGJkfhn>4@!TjE~ASKAvQ|WU9{cxEMykV9yBK%gukdE`m@G;?sGrg9EEA=zJjeaJf zpH}Umk;xi`{gs+-h*)&LqIRfGxGO_*_nU&fa2t1v?T^a?H2k1N(kK%TNujq7o-yYQ z^+mH~|LGyiaH3-EOdh3P@#kGM)N!?W$ELCP#u@&&}u1%C#-4bY;~o?PQ{o#8r}W@RR3bX3P9NR$m=B zEMm;6!e0A|^0g3lx=biy?1brGPe0T|f93%JU0BLVxlH6G;7D+eBRXAvi3G5YsSjLI zTv8G;D?6tqJEv;87_FWNY>i!}wWH#W#a-7#S)sJjxVu_zFV%7$$&DP(kM{g#zUPA8 zBZ`8cEjm>j^k_5E37Cr?sbU-0&JppUZY;L-4I)c1&|^nKppROaZG1u|FfA6$_wg#0 zWx1|)XlB4@yH$fGZmTZRNA)jV)8dw0X$_EB9Fofpb?E$#+E8(~K?dJL$GV!Fsch&r~{LUQkpkxmVk2J`@JKYSNxp=bc!6e-H04B9j1VF^qg>^>`dw zY)ZN1?o^Mt^siH+1V_hjSxJ_2S$b|@ zP>37=2>$jBRd~HP<0Al#_sC7bY z&;(-|QL3L>1o832k+8J_$|*r`UFwC+t(@L1t0Cj4y!WxEzj%xeW~NiXohqc69b(=u4TTlJYlvnln-bLt?@4KSp0=e&OHHm`sDAsDX+f1c?V;IpUT5=T7rw zkXR)92m?{8;%r@|KnKZWzllg9sfU*Nx+uk;eY4`vb7iZ8^gKJbk2)isL4U zUK?eQk|%VrQX(oBk$VWQ6To5e$Jy!RLktX*F_Jce&f_TkIq28>Ze6CE^A z1IS1Ve2Bsr4KrlQh$N5CUk>ih#vU{n2m?MxALoaT36r_5O#NU?IT|VHpeqsVg1b|T zi#hi&k{;9~puSXrdo}7bHNmo)Zt|742iDIRQ;D%?Qf{F26>ofbg@;Qu%xs3hCPQQs z3UPS&C@Y@ODR^l5N3>IJO55VT^_u_ax+axjHU}Tf?ir)mNh1<^3YXlVv~rh^F@+d; zeo=hGkG&v{JzQy4So`$_izZxafO8)6Q+lb;z$qUED~PC&gkH|jbb z5|(;iy$@F=&MW;y)7}XQ&E=Uod4?KJwE&fLZ&*J39b%-Wy^O-(aH;tpRWIG8wTF6S zvt3p~IXp}Qcd2DdHJ-;6__RKlxvL3j(@EuZBgBkSa)EwJADw+TZZ&+2D;M;2c^CVl z@SLfcy1xS)H#zUsfBwD1Lp|YIL3&j0=r}pjSyjlv$5=xvtD0aN|My)Ta*=#wI_bK} zn5`aZpYXv~4E^N0C)4FHw+DLQg|sTK%c4=gsy$*rs|^<^BzzNWUFFVRXvJ|bp`<(f zJS^Pgu)Mzw_3--{N@z|SwkiuT4D4$UKNUB>?*IC!)=-zb>A&@Ze|N0d=af1t3WAp; z!={j0;cIPlBKMt|q+95)+*#Nr{R5l}20DN>XPDIcm$%hxZyQI^F*Pe{ncz)RDU;}rPO_D+b-8^P2 z%df{hD`RT~;19OX=Py!}j4}DUdZwmyD@Au5EiXMgzVw~*75lR;^kFTn8GE}nfD%sW z9f@1x2+3|$0DY~{>B?Lm*sk74Bc;AGa0lkJdR98~;X_pkW(je~xwU`gJY0eFAK|A5gHi)&0cEk|}3&aW_dS}E|&ErAQ#?STvb zuFsFk{AoqexRYb7m`+a`F3b&s4tm?%@x}r%%tY_YmoIMKlABCppPNXW7pz;0(hC(u znakVg3vXNDZH;2r*J(Bbu|8lbkxK7~OsQcSAly#>ix-|#PP<3HvKNOSYMXO@!Os>Y3TE=y;xseP-mjRMfnV65m)OO#E%Iq0;si&%pEu+L{~opAX*{<^rURNd@q;Wl-eJ_`vKp9 zeh;dtbM-RjI~4mnnIHU^C3_L6^FaL`7+>(ue_}U2XdT8vomq#T=AIkQ9L%Wi_F{=R zt`q%^8p&(De%7YE`F2d7SyEv&xX)us=p%#!yg#&cyYwj<*!NH)?8%NQFSm=}*yz45 zaCMDYmJIU-U$DgE*-r6?1rt2Vk8w`7!eR`Ryw5OxW0J5;jFtnkqhwdonN| zG*bNlYv4v>hf2kSp9Fr5j-0K=hm$zV2g;SxdX`x}X2Z>zHpFN$wA!u-2u@% zf_4m_c_ekqg=8D^y0$+f5}_`Qpr2VjskKwf&UJ>L?Shw&aYgHDx)~h9?8F%kRh>Ce zX*QO$Ql+nhYkZYl3>~2;1C5fYCG??Naxu>PO`iO0UWAyL>~;gb2Sk)t0O0tr-KUb( zee=$CBKrkM#;RtNznPp~)Q)KLRu~rMrWIgYDMY77DVirW*gkRb?1UP5X-B2=gi=Y; zNWQf(j4Te}!4Sqdl!4kZ8zEK&X?;sKhgHO644KvVQNqzf78{Z*SjT|byK$vFE63Fe z)exWN9cmZB3kyNmRwne;a!C&ST9AA}+01tAMqnTBEty(D2FRAfEE{Vb((tMOxcl4t zmbb~8i?!N50K~I7IPjW0B7p}xd$8QiQQB8W@Tsq2LL>s8O2o8y#c0>^}hK;+M|p zSfT=X{`vBb$y;#NMNFI`=^CxP*M0P755M zB6>{QlxCz3lKg;A>tWi@_wZC6c zn8TwO`SZ#Jd+9Vz+~dAe^oZ9kY|obtGOD-D8oHIrw!URbWQlp_pZn84cayWESayB< z7tnc83O3VOqq@d@beX<>{OVF$11+QY{s9F4bMP|n5s0jtv+LN^>0GxkM;%?hUZK=w zuf<10tYiiI7UwZ>{YWV7|I`ZAF5E9q`(Hipf66Ct`;`{>-{w723T?UfQs|$5?C{*} zdtkNP(j4|)hG-oB0W3?_K8UHzG1uVgZ0JY_6YjITu8NbOt^M@TD1HAQI!y9*^PKI@ z-M+Aof2xpC5O5`nHo!;R19ca5vUC3j^KF^nPGo^#o<%lc^IPQV4a<`MMUazQS@y`X6-Mj_qgen^vleU<9DO|q#n(#2(%3s8H4Y=_xC+Jp7McKYOse2{g zcjSr81%jfiE}S~%)w-VwX>Z&Fqev2|_ZuT$P>&wxwYA!8r)wt&XWO=gq+fmlNH$LG zD@km1KI#6?(vNUy&8y=^A9}0f2|_77r#N74X}|r!(UD_LiKHwNwCNd2JSRxr4o0b& zA57ZYrgnLvK4QKHnink&I<$e19!tG<1?M;*5CD^u@sX9{JHl`FrCrZONN3E(tZ8!r z(X@7k5ABiy9k}J~yD`1Ep%>a|?2M;z?`w&!U=+mjebSPgvTwu+e(x=w6Veah276=c zzf;{+_r3SpC-0eHXllsfsLCWE(zCmm~fgqkb;Ue8SHFJ_|+XR`pfy!YNl(QE5H?cRS`stCI7+@VEWu>jT*D`(#4AOw?3x&@r( zn$eHtf3bV{5&aOAu?ei2{&pLH`CjjmiRkJG-Octjt)exj+57TQ2#@Poy)2cof$8-X z$aNZoNTpqNETl;!4kYNV)jJC?WSodLQhq*Qh}3>KFQ$NqgVxJy3uR(a%FXX!LTwP6=p{P zKy!96M?By`s4efJ6FC`2h`E_>k_d4lfm5w_T9P|a^$o#PAc$@aD8B?r`+B`6fx(+x z-T>F|URk8P&VDQm|I)a{%HYT6Yjr>W^yR;n5&nIPOGXmD>W8}O)ET6CgCIWVILhhE zAdWfTITs79>xc9&Rp*&IY(Kc%=!g2;fO}z1!2w6ZF|i|_3)J|~w~G~;wOO~8A4GhU)|O|ggT$Bpuir28NH1Q5MU zY`in;D5fF=u!ir_ldvf-gk2UaiM4orT5;9W%^e`p>OdIyk9R(dE_ZYlLv2kb0!vnCl!&&E!{Ee7$N%8|^@ zFJrrUJb4MdFD3HlY-VTA(GmOnVtyG1^Wmr@U<~3<&PSRl63)A_t#vv-G(Q_+zw zsj}&$qxacj>am8{nidJCy2v_{*EuSjO?qF?6Vnf{ zG*zg=^~BR^68{L-z;1CjX|9b3g@bi#Tk^{f((UgdA%6gF_7x(;x;Rs@ZyYf7{WUwk z9N-FZ!pb;@blObvD&p9ITx+zl#bP=h@>YV@z2Gtb&Gr`Um>OpnN92Ys_idHVQ1&ey zDBa<`D9p;uwIHrTZ4H@(JhIb&EEU9jf9eTRphlu(Gy#;#JA zz-gi*^2odRG{$e|wl}oznJRFWM#_xfF7hfW|F!hMu4k+KhNzL()C{H}4UrrDJW<_` zk5YEN`r(2}rH;nFZ)aI{Gl#2#Fi4W+prSCC(GU2*DX z;#_ScB>G&^&a4=_#`Dn^gSf({@R@$$ZwZFJk25^d^RO}W)1z704!T|Vne@IwkEG82 zN~bgMT!_6)zLK|1JKQb*0Ypx=>!QAuK=&ix4j;>rzu@ki zR+{LDxpwY&w2y`R39{>Xd@c<+fyq`*e4-zv{L)cV^CCi0;NL0M?~JO&y|T{MxuTcQ zze3pE88%|-+bKm}t@RbphEVG}|78`=BE>uShm--j-ZDMLz8LaCSAt$1Y652xq}wAs zgrcuRO1ldMqxJ>f<()1E=;_~gQ86B_q$Ad<(VA(5aeMt;kF>?Gy64@TdR8w-DVYO# zwq0YY9Hb#68BP*Y-P;QGxpFZ?AJJo$Q(gb$Se-lL@r*<*V(Xll{)J2zH9duUN8i@Y zNs4Tor#5pND- z#v{Vu>=fYy$>YTw&GQUD`>&UOUSAw?B}dIW90c&bhG zHmnmRsAXMC;~uaMTYejBqD1;`Y{~;5ii>eC%>5ngHkEDES#&KYRf~I(E7QEs*-lLhA-ys|NUuJe(>jp+(65mIabX8GEG%_y;>r*c9U}W-fN^zl6iGLp z+EWAd<3LrZ#h2)!j#?I_yZO;jmdGMrx)2fo3;iLyC`)9t8VD$tLp1U6Yh*OAzZz>g znI=D6?R{Ffno_fw5tb^{Tm&i9MKL;iRxdZq6>%YQVlk&u>>t2a!+7jD1-`b}7(ST-y<7fKlEgrTgy@MpzpS{(RhbeL8X@R6%{D$wU4(#S6@S) z%HPJ(xoXSdd_+)s)E*Qs@xH_Nlgz}1Fa$+8s`TS4mL!%i0zUv6pr-oIWVO+{<>$&L zS2j*@%d?{t?n_L#N&UI`MMH<_s$11_OrK8=UhQ+ilYGr)u9VfI=$_f`&vdQs`z>io zA}Jobsv-?~QU*oMd`KX;Ds}yP#U-DV1X~T^%=S4JUm(jmQ*ciYf;oGGl+32NiOCuiN=S0(^xmG$PL{Ssx{)eGcR*?l znzf&ARW2wWRdf`RO!BgOEsC0vy>Q`v@;Tgz5vqElOm4>SHz3S!-fwQ)UEE1!<%=Km z34f*FdgU5k&~RA0>(6+vjli@1btj3~K*^P`uL9+n1mW19PTr%$azIeeD)7{Pew5>L zCmu70gsYG>&x7=}bsSIYoQZ*w4}rBybm_~co|MCA93jF)on9Ze>rzlUSF?c(u?s(l zbZC#X4_=ZW@d&!(4Vr*db)Fh6uQ|pCR5Ja{8CNpE+29vjexPbyGIghM4FMh1mt1R$ zAh$$&vJ51|gbG@$ZX#ScrlPDIH`Ffal+-!pAZTEp?*bm2D&YA?+!o_v@7#ZseR1TV zP*PL&9A4u)YhnFW&Y`3#e?skb)!a?!*sIPfKReGQH%cjg*q9q(WaQPHnj8-0Kf&Ix zc}};Mx53dCMgC|rdYg2I)xlqKk<*sK=jFcbQO4Dj=9bFDHov;wuUsKj_gtDy-@2z! z@;!rrZZ0OD%C)_{b)rAjh*}>NAsH)ge(vmS-R8t2MM7OH*)du0;+W&MOemN`@T@gV zFeqC%EAnkJ$T#7ltfvO9?*HwjAz5VCEsZj&d69V9MMB?$Hhr<~@}UB)m)Tc{4O4?} z{ji=F?n(S&OxGe^(}hr14V&|4_U#u~-ua+=sCPzxy8x&?2E>%kLw3VSKdyXqgYjb3 za6_~BL^%495hwDF6@oVH_ya(wj_6hCQ~6d>+J7NYGHa-Xf}#H9d;)n}TV89KZ-o{q zURri9DYvw^m!kMi1HVOAhf?}_sGA}NZ*5XS9Hy4U0D!-xH^L{@x^)to1^5(b@P53M zi+l_ty_NqZZ{S#C%U?jcqCnngA-Ya`$i|#=Mn_~#4$nw$-KNyd=@hX?_WB0EM$pSK zeB*9P zkl^v*7gAQ)VG_#zz+g8gG%gA!VPfim$~MDpLXn@j62+EJF)__9#_NMLb(W4HpCrcQ zKk?dwmxu$mGID8DQ_5#*Yg=}~4R5Yy*86irrZnR#1rsM_PnzrUC31VnMK`T;#_j~>0?(PO1svjd3Pmq}?`i3)J!@p7jGZ=GO&+Lb6C&9vV@xIT%Y#er;;d}y$$Aeu@q$VnO3+q@b#wL_z|;Hyrl8o-r?FK z&psreZziWgb^7di$vCb%1N!pVkA&``~ z(xpV_=Su3|pI0ukDTF`-XO%y1rBJt>z-R|V_I<1EUz!^6AWWGhgunmCKgu)R=}GOl zzP=OKv{VkC1$|9g92mb_b-Qny){cGqk=^ze&|^2f3*WBY%bSB+%e~B?0nE)e5KBu5 zmPv}}`8Z9A7AHkYc#)O7_F?gna>L6#}|pp zvkTK0L&4fB5XJyUqmi)f)jFs4(g+H@*<~EE#u4X{dErWadyC0+v6-7IkB3=`sGg<9 zZmN?;VsmNJ4|0hiie>m`U#!Okep9@JmQpMM$zuo4i!J?{f=-gh^^N9P4jR8<{dK9| zIOxoGU$KAo`G4Tn*+h3>Zyv1moOmRtO{-M1W71jC=EMU4#@P~TX{djRB^(hRbh*!^ z*ZR;Qwl0G9b~G^9c!x%Q>gg+!f!I?hC>D{w3FEF5@IuYtt`K)eUhc3mWhWAyw#$OO zG!1^a^C{HyirEw>Ue_ctWGne3KK@X90V`LtrKdbNPlL2WXk{b*Za`cggT1+w_%VKf zXkqabe1F12Lf5*9=q0VeJ{Kq(5R!%?rLqKRb5&~`!sb9%bZo}41De9*%yb%*YivSDApCaev} zcjVX<3iOY-&}Tju6)55BaX;9l;;^5~5u7mz z_1l^tl%#=Cwdj41DIDMORVYGWKmY(prVC&v zL;d3i-KQ$MMLv~jZmpFG7oIDM&>L;;st0wOrjcG^SEYnhBTV^2{y^pIUf2?siWGvOqIb)c=kYRBV0Q-)V z;Qd#?Sv!l*O@wuHQZh_bdn#_6!nfZ6tcNAL2Wt2T9f$bP z@)1-c{j;c<)V+t5ou?2A%clgYQQN}-0ZaHruO#pNvi*^HZdED#>!PP&L8`cj$~@K* z8VM*hNdOU`MJ?sr#!Wu zr1xM2{CWYbh0WbRfO7ElaE~JiP@N>WWFr?V0TP0~yT$dpw)ine|ChW+-j-LwuYrxh z!Ka>Dt*^Myhg`-lGD5IoPskqmGY9%wcyL#!cfg?n3uOHf=V-#)0Tk!cey{!MESl$~ zjxQh>$Fqq+XON%;z(--;>#ka;zuXpytuChaJ@F#dGjiBd8OsLebr;; zXfg{wOEyEhZtRyi(un|P%)_xwm0|PBx=>g;$0R@9^F56b8|F6g+teQr$Yt16E>v&K zyIh_PhE75po6I+S*~f}c{nq+4zSG~fCe-+9fiuqqbP;bG1;^V^8W9irOztEBOb+rqQx@Sjha! zUe&-rp>B9_WmAN`ZX^+?g;X1J2!wulh~A8oOc!m}YsoL`OPh!n9%lon=4fcW>_KJ{ zGZn_%eT)AXilheVrCaAhv(nhx%k78$%Bl!^l;)=h=JMta$?df`*S*<5m~etL4TC+v z#0N~c!XLbvB8%-jL&r>G}si z8Q*J9z+T3Z!+_apFtl5XzDVS0t82d(R8F8k(i}mMBKI6+&GC>c`zZsGwMm5(bW%a^ zZ-|C0^p=-Xe6IAtl+t}RKwuZVupF{7aYm83^5asC;63r?dtLX|uNNlgNl$3#s2f5P zLh6bQHN?H#%;yt7AYxHn{X4JR23+XtuNMoM1Mp9DbH*4Ep~Oigv%X){mW(mNLlB%w z1*X)^R<4at{{Y5+K395tyf}wtm>SH-1Z<9uj3y;4+CDVh&tF5_+_I5H{&eIeKVu_H zc+{kt0C02qvRgbArYa=i$(C2E%}j_leLFl^hWxf}THbCtfGv4Hx<4vaQ@9-9R^c~#*y_Q|@kad|gVP7rHq7${Wrh<|#dZ8)b~!x3M066zwv9f#x#WPyIYT%~`r{R8NzM%L;s z-?dnAoYU@3x)mZhE&04M5HPQH+%6SFyI5S3%qdU5XX#M$b100im-NjDwT-CsFZ6nPv3|(Yqv^1$U~!(H+^CGKtm+VI3^H`FhU)n zs|i_r{wP(4zp&gNq&;NJ7hAL;qbPQDDTL4nOrq=Vu{tXK{FsnI!QC{hEBOARZi28f(KA#`b-d&gpnvJ`Xww<3-1ACCkPj5C@eF7aj}4GBVj$rjna_c;X{ zQfelSklV~O_g!LfLkDDZLd0QislxFpe+dq?($8?alG+-=K53B0USF-t5Sxjlz!Qas zGPxR$_?z1tKog$qns!Ovzup!9`rVP@VJmZI+5x7eFkbLIre)ve$=AoK7{Y`_R-IFm z_l}HHA^%yzAuo@;_;);05YGDd?Y~d&^5#-wIr3&~>?=1+>*~SwYKzX~#5&>Kc6xR? z@th|O?COWg$yy18MAw!~4W}a6**@*ZJVq>$UomUW~aNmryBGnEZm_!IOjCn$xbyeJ+s??k?;n$yU_dU!&9W?fP z=()a6GZvOPKbuKM$gN;=l7}+Ab~z1p=u;UN7|gK38Ae!rFiu&=3Lcv3#deDiqTicf zQ|JON|0_)dV12FFz~_wJSdnu%cSllPv2Xg)r+#8sIa*1NjCIPLtrL+{GWv~an?DaO z_MGM%nwDZ3FCi&mJDQ&c*%Yg`rY$lr&%1pGs>T_Fk-h|iFKY~Zd^_$cb4qEh$A$f5 zufTd1F{#i|ykd`1e|Ktig?+m+0xjWz6kk*IUNgxp5|{YLb=6~4Nvq1{;q88`m+PxN zA6uC?=!&<<2A1+B2XCXOl7H`mT>r5Bs!pV(HO03h-e4{z%KLN5NRed2M)`09Hz1r5 zs_Y#w__Q@rA=`l>x!Te}bTliW170Sq7RKKvcaz>{o*F}}Uv`yvXGXzu2y#Jc+wK2b zMlk##^vh;y13}8$GQMyxiJ5Xj$yhu6j1PP<(^S95O!)YqZq$X)R6=;M<|yf(^WBQGbXc@qM$42+l6IYH=r5a2CA~+xP;ec$Y@yM5s zvjOQn@QC|&UU|#0SY9?~P@!K>6Xc*5RJ8<)M*1ZkB za`Lx`ytaHkJY84zqNBrzGU4y^H5EDN{xEE;qzofJu6bTCs^7a8Y~#eN^PuH|I88uY z%9RAgdnPV?rSIU$JHL)wp09Z!)^2~oNtv$5E)EQoi#^H@J!TzgYeb{3Fj2+Tgqjrg zfPfuFeF9wFSEY)n>imC)l;jWO?bwL+2Y-DBPgY>D=EMu*yvhCqjX+2D6HpR~r6&3J zN0)? zWE^*~TJmVljnzZc>LGEWqhQhi_)G@;j_xJhKi?#;8#Fxh|LBFPHH)7G8Nz^c zbjBI`t{XomemP^P;RK8h?WS-8>`yBh0VUfp-$TA?LNHh+iIcW!4jFcSJvNM+H2Fn*E z2PWi`bR|wWqWjJQos{BA2D{jS7x_?+OV0L`U#>>t2oq{46Izno*FFHHfOtS8c+zwD z%I_8J*H5RxBBa};@YPtod0KteHF*ahdh1`!i!z4;k6rulE2+jhVZyeG)OIK{98u{_ z_2?6Zkvyag>h8haQDqg=l46E}iUyJ%ul_+1uBnFB*u_4!h95(h=>uR`BlHY-uqcez zrR6wv*bNe6ZQRw;#pfz)^0lL6b{jL8wP_&|E4;on5S(U;X%6!b^i7 z2rzysDJh`E@;4<*x}|dxTrO!JtOLp|xgp7w9lPRx6V7^avHXto;QA};ep_&T!)9a@DnRkcCQ2OYY_C=4+f9>1&e!%9wEouMKXu?vwr{;OtGxjAiL_j4UnM3 zWK(8sjS`scqocsJ!z4dKNS#_?@P|9?45+DAWsAyy`M8rfyO=cq_6U0^V4^Zh9u+WAr z%fvqR1+T1Sn0|>GnLizJvRgTEa|?OVKpmO)6H4FH`s_ z+B}4sa9%bqmd1d>#$mEvF-LE^_{M3bO*P2yiH?KvCA0XBJ>mAu4lj*D!q=$vNifpD z?!&#iSw+41wk_;KXvOmUk;hhiN~{{LT=;;XvcM#Lg)og`TsSHCw%wA6%6*C;S^YcD zRX1SI@$inAW0h7dM1yw5vj0rpk#~=`x)_U_udtr|sM7N1zMyV!RGaN7pQ$h0Gd)x? z0;mk0G+yKweg&)cgkbxO`a41RU>@8E)J>8B>|?;f+GT0*Bpf8!eg=|kuON1qI7RYI zV0iC!!d|XJ&%NcN(y1UNM30O2ymv^j^NZx-2lMCu<+RaI#M#K3rpwva2+%Y9RV|nz zkZY)3x=z+`yF)HT460W0_Y4YD@NMZeN5BoHN)?QtfQdtjvxQpd5Nl6#n5ebHbq^5}&cK2PeLqP5D7&`c+qAZwS#(UFv z!uHe-WnIuF1)>nVwp(eft!p(sw`FZKyQaMXPe|?{3=Zjk&8XT}`SNtEa5(3&Bi40w zN5}%$01RTR##!M-6}yjg_?HV>zp)$`SeH9G*|3|NJ<7`wzeO(7B0067=2M1C`h>I2 z?jmn3O@3H8Ou=rkZMa$)3{AwfEHEDbJjUij?KHTS;SLm&{ff(>Y*iAZPfjsA#38S0 z1#SvwRf6CaBpT?zB|l~pU?nVt7}mVTKYy_8y&f)FU0Gp5gk>EhZT|kQ6#)EL$oFyA z9=w)3kfqBnZ9ZF8*XNxdQVo_+Y4mdyx$P3H8N`RA3bhN#Hk^IvdMV{793j?z)7^L} zlU$KU?B#sj>oe~D5DzgkI{v=Wd^LJOw0~wO9E9)8XnX^Y&>xD5{#&@E`_K(aL{8!=YDgG1s2^r_v%~8wc9QV` zcWFo)G#T$Zvrx4>pc@ly0?ZQ{V7vk@H%24#cI$K7acGAbrZQOR`${MVSQ_a!d$r0IJ0q=020=@*7d^9l+PlP)EFJwl)gU@L>Zmfn(+~C;TO=hr#&fJiYbS} z#D8F(HCEaAP=-Z7atVR=41D8S*yp%XJk5Y*7|I%zb!vbvEHES};6ss^*chU9b*!kI z?FS>LR9?fNTYTcW;M|vz-ffNQ5X%Ydrry2jB*g_hZg(np;ZAD}icdi-YK_yUxwh3u z@E;pC(5-J8hn;JFbLVOV;>4xP*O)ZV?g~~qA>y9BUa;qU@%ebLzi@O7NzUvdfRJr? zq0TG6kmq8izFTo~`;=Vf_2q0Ql-s+_ttC>&J^Q}ls|}nD^hoZ_elV;&<)myVM`;{_B$Fl;r}ppmQitS>AJ=O3GNa!K=5D* z!7T~yQn-bpaCe75aCdhKcXxMpceldb_N~3U`|R#>&y_z6RzZy#wZ@ure)c}^_gd$t z@ar&S9J^xOP7v8U1u7h-hNBO^V51G!v+2tuA-=;5EJ5AN%wA_>lzKcHyxU^FgygW9 z`2a#-^$~Qe=yPu z1Sei2v&$GfilM>=G_3pq6OClGU2%Ml=UGu13_FbNH?B0Bvej>=lMI(QL5G~l5ataJ zgW&-9FFM5keDeRZd-K0df|e~I#ZA#g?Z%6~BT*49pCL#LX8 zt$s~dm2KgO6zgJQ%mk{1tKvT=?NOX?4(gWTN=N;6^fk>Iblzmx^!NK&TyPkM5mwqc(}}3u4YrfpwT}G-As;qp0^}!ezG!j0 zfO~85-SI1;X})(3})<)pQMPN8ZB#@8-aB;4vdnwRv+k5&xbbZS)2z-eqUau zhT^T)_5K}y{i}y2^q}EbTyP+huWs`BQ|suEoShwaFi|p>zHyEZhs0zIJFm2@aEC8B zFGqWrb$;CLLrUNokk(Aojo9Iy*r17ujCduZep5)FzrY5vk2(Hk^~5^&dQakPvB+;@GvQ>=WBV0Wg{XxwnZekL2rzL03^K)8ZIZeG~N z@>XlqVZsaNq+r7Wh+Mkka2$e8P|OinvkEF zmG}TXPJbu^0C0J~qk!82cvZ6-U+d3GV-{=qe#$jTF$QU3qf;r%yW(5Wy@(Bl7VCY zQM(b9f6kV5%kUTGXd2}LK`s(Jy#+eq+r1V=n>RB%A_&ed7z`znQ<94hMSK!>$fszk z0VT{2Eqh~2o}gVI6I&zWwG>vWrRP$y_=I^$2Yy}r?Y(?vH}VH$9@K&{Mk`kjDJ2j= z>0Mki#7nr^{UX2kiPN*z5-Kg|)3^h<&W1%FseWyV#JLP4`EpKA{f;KkwZM@ZVVjC; zozO3A_}ek1>yT$QdaqnV@C!0@nm_@HQ}3t5b>Q_^A*6@^L6sY%>1}?FKlD;iqQ{Nz z?FJCJGabzz%`z;-iAuu54+Rh34aFJ6-t{df@6NT--5Dg^4I3QJN=UiZLhI8?GuxpbPG1Qo`N5;9_XxdIeN&8ut8pE$38d&kJ!P2^ zcu^e;-3a85$7gq{{Z+3#GYy5q7oU02NLMK1hZx zc(;Hu>)yJ&rtV(swQ(HbpCnOZ`o_EqYrA=Yv4{Y+pTe~99AoIp_Md=hsWx%`?Ley{ zlc~G9d1Ob5k|^Uwy+TQ>wk5cB?lj!bZOehhKjM#q`@{u8soup_&)pV#WRFG$E z6|4CCu_0W$q2?qGo!0KU4zMiPSSwudzV8}S500WPquYF*r(G0R$ zZyRy^xA&U~2qb|j!u8*}3r4LC(=65{NWSk^AUqKilZSuSPUx)DEc}+othsjsoF#oZ zn^7R@M_;~^|B+uv0=cjoji~r_18vupB`g24PT_bLHIM2N?uWOWPyJX@wZ>ULI=e*7 z{~Gcwgqe@`aSMx>`>l6EWZ(pBwDtf1#yMKNeA<(E8hdDNZi*F7Q?ncH*EE)BVf~_z z+u^?-XG#o+Z`2S~5ZEiAM&WNMZV;SRi5?^#=qgMcWbp!7saqadKe*V6eThdsL83X4 zrVa(ZON(#j`7KW#<4)hF+Vq{HWU{%NxN6Xhl}!rXpKsG8A^Gc=?QbAecd3O3Qkea6 z2XC`q`1jCW&Org22|L$MH#pj1OLzJ$f<4Eh1sVv=Fo9=~2QDyn=~T5F!r*wPs-WrV)pZclI5P3pBT=XAlrdE zM#of*PJMTaLicX;(KKLs7IAkwVf13&1X2IzIZX@etzQWBST*VT$;lxuD+v63Bsy_sAfIrkjvbv*gwuqlk8NL)e`{@BFkSGm*O|q68jy&2 zH?4R70ctBQ`i9E8a1_rWXJGZ&sk|)BjHyxQK3p6~ZAV0|x01hCz_$)CCZ(LUETalH zD=3XOc^7}W-NM$x2?d~wmYlktWX4m&&b25>j!E%;t~?r%!nvR=RY<Dy*uPyhfhsrohZ#xLzQvdv=& z8mW<*f`7mu>A%T^wK<9*AChd}CvBQ51?y5s6qMXmkM-Y2=6&6Z%|XaRPHR%=U9{G6MLI?t@X}NpE;b@g92nJDMk_jiWD}LvhyFUbVRl0@zaL@c zF7Rd4Nu?IB8@Tc?A~1Gz`K4)jg-o5`-ru|PKOX)6EnfStIh_CLFRe}Zwbusim9jdI6|};{~ox$f&p4KR;*k+RQjNPVokoZdTSdrklNuW|EB> z;Kr3>;okQY(b|R=1emjYLSOBxWjxT3&OH1&Mc>r$4f%+ZEpiBl{FzDis~~p*KU~a6 zo_BEjIcUe`OSYIYda=(koN`+A=3EJG=GWhrgdp*s5T&SxqM|vK=@xS$!a}IfK0JNh zxHKqlwd5kV_{x8Kq1%w$IFlb*{CFoy;ltzec7e1L!Q7EI_<#)Q5*U!f|cnjEDfgbm~v|TA1C)r=D0!5WcUsb!o=CEyN0mg!s`M{$FGBK!ZlmG z)NQq-)5V0RrP{s{W~>jP9q1q`)G|aua`;G?5tF|+KsC2-TZvyZQA%ZZ4V*2o)T1f# z#8@bQ1h@2{#z#5PS{%cl7}aZY25OEP$Tc^8~gIYTl{+*h>~#h>=5=?ukGkl%5wK>fkomg}(=O&>>J!THs# zwF|W%_8$`s&ZQLfk+>c&#&#Jb^X)^Xo>xH*)fp;oYJD%ftpcOshjrWC%MhB{(SL@z zg!81se!uujPQ8L*$dBI-^Tl6-8fWJmcPabU)MxNv?!tLv_44Q7TY#rr9DX*_?z!I= ziTgU`3E@)Fj|tx;K@?-`1*Lncryj z7|o{fp^4Vlf|jdiELRlU%1PY^%1CS)MM`%RW3#@Ah!+(3y~|QkP&Agn0w^OVvlk#I zBLgJ=|P{W(~6zPY=13G*J5DZAJi+2I4 z#&c;zMUjpss{*k)U`6Gfd?drVJWsES1oV|dRVB66a;Ax>Xif>N0Cw@PEwK2#C6BLa zI@T=j?5WmsE^v~znBujv;$Bte_F=K>I6hk(7fp2HvnKXRF+-*7CDQo{ZMNJu#0{Ku zo{_q#D4*!AU3EEtJ+zMCE{xc0`8~Cpy}R%2>@dbGnE0 z;+Jyf1lM}3l8`#bpIaq7P#@AV|5iW>yU#x=KmXh+N}jc;rO>Grd&8;vdbrk81V$Y0 zXSXT^_^c#hH}J;i#F4I%;T^pepGuCRj`RBFM4yuEEcljEKce&pQrcKBWLzjYjlbXT zRsV&g-`cuVj*UhoIpY9lJ$E~ze>eUCvjeGRm=dF`Ry8s#E#jWT#LmhLfirs80qyys z4_`BgqA8`3yZPqFo(j-> zCHwAynl8kRiC5g?RQHuI(WMb2=xs-XbVy%W#pHzvS9VD2VD`Oi%4d{$Rd;VnXvD%< z*$tw(I*y}H3nRvL3%>oCrm~s_C#wzwl=VVmuD{B6XmqB=zo6cvT>XKcu8VnFMGt*C zO;99X=co)s$e2a|LKmNOf^oqrrkJX>CLc$umbtEm%|ctAlp~}<_{XACt1x>?5>)AN z%PSTxX(@eYa41TJJMo{VT7Fx+*K~P@obHyCZldY2Cdyzj%{QZvL6*1i4og%b_aCvJ^u4Rr#u{_=HcDSnem(&x|Axw&W zozdbeAa(Jhw;T_@VR`BH^o0PV>Ooz<&S*_vNKX>{5yQYHOO`8hbK9*{S?MY!r^KsC z29YoJh)VzX%>QUxA(k{nygPK_z6#^%GY`f2l#6cdM8%Z$70(q68z2Zu{o0>+z$0NE z6Z6w*CbECeO_{u?b&lj+_VRY))R%?I>FA*qV7}aUOP?wpc|ux~)u;m!F>H~-1yKkl ze{TK_a5uB4czcI*#w^4l%eJ4k)b%L0GIyGpama$cqMxeZ=H zbh$q2b3G#0Ru>9=3)MsOUVz;^B^yfq>;57(q6`;{W0}4O9B7xF?EhLmE7=W;CEr^8 z118(7+$^J|@yjGl9MCQd;((|1guO*1AMpzmae|9P`hRKZ{Y_Q-SFz+jk}TBq%GZ$_ z^pB0F76A@sI4tS9JXa!?=$p89=(mnUUGQgIq^`m9g8}}yFo$0BFmM`=Y7c=&HkevQ zt0!en8lDhS6#6L;9ofZ~^!GsB10OA|oKTS^>sgoO2nbaS&G2b;#Si^7|J1|{e z>_$&m+(05oAXDzNg`+NJRu25Rb=Y!*kxQ}%SKo*o)TIpyJhiZ4xyJbDP{8y$7P61T z#ybrS@mzX-D8paMD*NBl4H`KviohO>+Nj23#5$+Sb*~6=u}138)Y7Hg>ZIPrhawI} zejmU9H-%Nn#0*6YxymTZgon4(vlus86#I}gW-~u zXXFXONv7O)gYonEW7u1CiF9Cd29LC z4JvZO1YvJIGR(#xopHZ>5g?+utSiefMmDC|ytO!5{=Uq0`^?{ojNgo|JPcAL?az(G zo`L?G91i1GCPRH1wfsS6ToW`D*L?q|g1=^yH&_qGA>R^5sz2a@jyD$5PTkOz;l>I? z_Op|#dkRdR;Ud*RckSYJ#0yJsj^zA%&`P}RKv8t!+*fr46kG{K5k^X%5mC{Ve!{`g z=vc)4*_Pb<;H@DkzGYz>8rYdn6W|W%N6DzvY+kg+ej!;HE=pb`$k>%gO2aHoQ~MNH zK9?_yzc+@mQr%)nx~8b7sK)%hk5JEtTzbm;1@C0PW|UZ0amit6kRf!?jG={7VIwNu zN1m{3t!h`UuHtmcJ@eWF|K4O2Z*-+%h^pP0Adab<&Z4=4Z%>}cHqa5_EXudLqM3?1 za3orE#W)hU5r3gmu6UiJs{ch z=^0dL`gOmn;mo^XSYe`!?@GFXReU8|nSzgiE_aJJFno%6A4$J)Avh2bk3fK9% z6ZuDcXo(*3J_d$sx3$cZ*;G6@2rLBb8J0m#bow+s2*iCXWiOb1j?=ifs1ZmT74Cq> z+c|PgRxPA)sc!OG-zB;q(km#q>sGXmo9VnOy-Y*o2w}Nf`==jK$#bdOD#jM$N*OO0%rK_Ctgs9r)Zpv ztQ?H@Blu5Ng@KCqWyZ;fcv$H|4?<<=5te9)2Iv#E7!LzvBs~fkp7OkO0IMj(jbK0; z$d6`FRWgwv~ak|$Pfw9dcWi8VSNkUo3eVs+2A7HUf5n^s%qXYHNFyPfH3zw{NIXy8F8GJ0j^}iP#&QOqKS499p=Z10Pn5yYZrw=+d0l@j&S= zG)R9zD%QNIJ&Y8RACmKaCT;uUgB;j~Cyy8hRfI#4IAd?sP)bs<_2b3ghZc~}=^Z0# zI!qJ!_8Y3}_k+d#rRsMV5`;H_Z@k{giY-qcXY%;IjzS^#6>~LPPBks{O$v7jl{}aN zXvY2bH)VW<9HLEI{HiH4wPt4G0hN?BeklT1Owk#?$cFM?#~@atH^a;L3%Vv{LbM3O zCJf>Pu{V}tiBF9(n(mpH<(S8rH&?fUO1(G{BOTD=G-*2#gHI90TE5UShvJ04AF+f} zRS}BH@AdJeWvCq~dgLZsl7J%!-n!(Q5agtcEsgqjegW!5q8;kgfPF zrD0sGmZ`5Owz;P21IX`q;VahKzw)YW_0Qc%K_o(KSESwi>|#ipU?M4C(B-j&`)g8n zcG3ejuIL_QIkpU2l6h6v-J0v#n(-Gyy8My=u9e6F)LV7hE#EkPK;5n*3iK<=Fzfcy zG`lFYo^AZdO`c~{=k4hJD{qFadQNgyK3anKu>03l?YB3fzI$WQ_dzDSw~P$uT(s;7 zZ~XPg3iT)Z!CE821VC(w`a*~l_eVdrcCnaN^9=*rd7-~XS^gu&{y$vIc&gT=ouM5e zIQUx#{5rEXD5Q5Vf?!6T)q&$o;B*36)`7Hyabr0Px?7wLh>QPd2)?3%IiUA8_! zlJee_xqexhpJEoh5LNCsi;BvSX=U=sCk!PdH3?nzxd)Req*z7?O0OKvc)HapBYS3WR9r3t+tWBUe@#P2Nk!$B^9GVs`g9xf21 zJvjG1?0TM^?+$yAZ;JCDQ&^nRE#VTtdPR4}+3ux}Ygu&l)%DSPh6hn)HuH|aD&|$S zUr@5Aj_jZf2=o!p@g&L;u8tGb8e{r?nF*{h)lHRrB(f%4*@G36^iI)wVfSus{-nM_ z+=9rm)+a$J3yrksGqL^8q3`0>s3mWCU2{o0OP8to5kL`4Hb49#M4(8lm}$LCj8P3{ zyF9kMXIYe?gt@f@#jO`o!?ff>X}xC^9Ox?>O$h`C$K2yX7*bRA{55^^eCuxEAY{fKt&xK#l zcKKhH!$O$4$HK|j3YJrrX;yDr%bRUn!1U^$DWGeCoUzzGPTH1*yTRpuqB(5O9eTn{ku2i&NG!aGgBUi^G88KCE;7==P}> z*ffq-ybC!mo$0@lc$)H1dMFwA&?Zi;b|??l3FKnjhbt4sZ*~kHVVPOz+M)Fq#p&$Jp>_iYg=S zN1PUR-D9abYoD-5V#ytn!%e6GL&hCax)QQy9 zMEUobxP*1eNfq^B8pXa2S&P2q*VIaoy~0z)A>ngU()7G8c|l%1nZMF#n#CoUys4B{ z4lXnkZVXdsyfi)@HYpumLqvht@wA?58(8&eoHwSMP)`o(HvoAC)7;3y0zCOF>Nhg< z-^8ONm4mTG+5>Ae-R@~>n!*hH4WP3SgrT8GTGx6`p2;g$LRwepb$hK8($b$dCZ=Abu93XXIxshk6SzoQJgL3j=iiR|Hi$_bPmyR zhsuQm`B8p*){B*waK(;#ahLK;M$)GjjEt7uR_tf3KJpw4k^ahn8!X?O`KAiUhxDOS zDHHJ})%paaNo>&*Z{M(fR;q5GdYI#nQ~O_kpQQAX@T&{>Wlc_m#`CD(3ft7)oxgaQ z-WufxaSEE0Chgkg-;qdoLg#lfD;lA0?^61n%clF5G-;4C%5<8YcsA6@hNG$y5hw!9~2{+wWfx{o5Hc_YM}k{Y9@UgAs; z4i}q-w)u#?2}Nr=?agoK?|3Un=f+sfsO`dSon!p?(XS?l0K0f~B~&lSQfmP7Y3lQP zFU+~#Kf5=m?_bxpCrOz5?ygD&oQQ$jTJ-Gm3 zNO8?3{h%lVnYU9)<3;-7MMIQ}3{Tn@c`w@Azg%+F*^1`u2%d zc~0NgSvV=Q%$_c|1>+yQJ0Y+(m@It zdxu(4cq5PHvjglf>OM>(9>Q1s<%a=olS@s1g$!rmCz+kxNk#wQAF*PpX6vGixBNkE z1fMy`J2y7<#6AOt1)o~{I*;mZpQ|*$D4AQ?97Ud%3;I;Dp$3GZ0}S($qZS-cKCtlUb_y#zmg6&U=qC^)6Ac`Z-!tJu-5NVusd z0hv$F;nM|MJH4Qa8BT-Wwth5~=gl?klov z^Z|vu{H;K+v+wbf@Zwrqb5VxTYS=olpRhl^ZBJCK`~N6b4)JfhrjEv`TI1!lU#W!m-XiJotvv_xg(^I_ihl9Tn{ zJ+H~|Kp8h=*QvgC0^P~vi_!8WmislUPF2AS!V*O2JM*>*kcy%?XNm-6T&TCEuk_&0 zQ!_?zRal_Y6)da6p{N}3Qx^c1R5Q*5L)p$Vqp>xC zK2O>oFiiCsPdp1}Nx1yo;xZ|}Kkj`cPcO9hZCK_C5W|r^WtsL0Sn9&{iN*qIh z<`m?qF(rG+s=l2%Y_3g(7lO36#xFKGSyH@+f~pA-4Bpg=bxp5MNGKM?YP4eUj1c zRe58_H!aTQ;#(_bd>-n^yv9;9X@`1-6YiK&<5{$N0fB2A9@4G3-&Ox2BOnXfgL^fg z$g#bwsQVuFLctv<$eG|?K{if@zQ9`s9lQZO$V{DHetsgC?u6~TbU@vnJ>VQ?;X+=n zQ5DtFvPq1qcc(hCT4o=kC6L=WFO^gB z>6SH$e9~=KMRG5n7j1#1hWQ_y|F(O(vwQ+3+71M7uW^z?Ra1aFFl_JvPa@09VPAlF{NC(lJkH;*v zlPY{)2yjFR;gGVAgjYandN*D-d|tRlX^s<=;tNgxN8m z;e&%!Ph${qhlKPDV`%`4+GOgNOfFMWp+##`D9l_{M@38g&sIa#5n&M--~0qXt%$xx zVoFg{4qyLrqG-P0J{ZbYFA9kgV?Grg9Hf?hirF&zGF9ewkR8*T;MIlT|GY69Qkxs*U;+5KgB7y)@C_K_IM`3$ zul3?^v{7gWtD8lIQG@tet^0iS?w1eE~yCB7xn!k+C)S(`IELl?WTeESCJnZ&2~Nh%!G7Fth+86iz&7GNQ5Z1gql%v z`7085JbK3{yG#sRFLWE%8)@w7v6gS+uW39?Ez)D?5pq}R7E>$aa>QMma<{M z?{{l+ryY<}zM@8=22$wuL`u*n!%yt}@(Qj}8_X&e5N{ zu!xE3smIA-vDu3ELCOt<&bk1fkWw~(vGGWPI$nS}W?P$}lY|351t#=ozB@-xhvn&K zKcd6;JKh3qx${<^(ml~OhAp_i!_YKRO*hyyj4NPA|Bz}tY`hQ{N&m-@2V7C=!guwi zQQk=*3+wR=^?teg?Q0p9;mG&_3_Gr+*;TJ$wxuwRLLy@AJK@zz#U1w2zgWrPa?&2E zBnvlYpj>T2?ch8Cj~d-9K~*7q89c?U@9HD54z|=h7T*&KR#vBqv~6M?sT$ZKZV{%kyK zWWQdjwZ%>NIqKJD}FYE+CokWcxOuHJR5O_MV;lo<5U{?D6K*Ab#(gbOQUJ9gS;3 z+LG${MdEzl{~=6x$Q`i#4qacY%iAZ1I#Ox+OM)7J@ky;l=%6|n_6!V6;8P`h38(RL z!}L8N;;?Aia`V68rXt(jv#PBUfk5hOT0;ig+MQlt#XZP%I@EQ&+GV@fkkFi=)ufN#LbutaxY2Aq+c?2# ze06bI#&)~FkLbacFkByz;DaX7aqyuf{t2UaEzH`NAfO4HzJZ2@91XXwmT5p3N`(`hTV&~8jjU`8J zduDU_a@>@z02Yig83g8%YeT*?YkZzv5m&SpRiffV2jU!wx9$CAS&Ltahvx0lkEUp7 zQJ3uo7Ra*cX}f9RskqB{8j>}Gh*r~0!1^O2Qq9el5&|^!tr!D%F79Pjmr9pPTMsuZ zx(bumVzd`fsRID8Q_dm4lA(CrmNTwSfMnthS#P+Oe@=Xq*;M9i}DpoP)u|Nyc2K9$*@&y@A%DDEEfKkX!QJrg2!+C&j7fY{pfnAIaU@;e5 zFZ$fC0QrqixqV!FI>Pk~mp|4v0z5M}E|MT50$OZ!z$2AYl=7u_EG^#}(|QB|q3|amR5O#SwMwS@uLt zqI#iNxA5R{+SzUKwpWKQxch1=Kvrsm8szqG6^Fl80&vB){Ue#|+sF-InW~pR(qZYv z4u5w`AF4C!3tmQ03a~4}46I{Yaj!Y-( zKl9-MgbML`Q#cN?vy>!}eL)A%IRyq0;RHnnJPtg{(yB`axZlbXfaU~j0Shv-aVDa2 z3vSaLMy~jxO`w7$lM{ZeRq{F+Ud$l6*&s)q*{U2SxMIFCNemZg9tQ)M+M37PjHYCG zCYz7{velNfU&p=s=F8JmZ77ug6J!0&~FuXlaNT z7odqrKfQQ{8u$7?9Dh!4<2SwQ2$)E8w#$DkeTg&L+Cy{m98xaUS;b=zRz$tTR1OR5 zuPWCq_RQO24^OJ zn-qUCsqG&yUlhEEAe{|?CN3CwMcU@hvOX8h@UAwr#3nnfsg?u1s-DWa{S)>9C zvyzJ_sQm$h-v%D6S^(YiI2(K5)q~GsAVyKm%_+2%mZUIWa~K|FMoB9KIfNOR+VQy+ zPE{TO)qd{g(7|x=9Qn$=;J}?I_8nubZ(qh;1{O z+)oi)Kd%=Xo!ne(=Pt=|F*Imjv1|pXkT%mCMS49hLs0xjD$9?ZEUQFeX{hc75Z7k! zt&(j7LhzokHKi&%Ci_cCLYl8+2|^_rzYD13rPde2cH_- zyZXO}Dt`ts`YjUW`>Y*~2bUP+ruoh5$`=C7qoWA4lZVv4`)&Za$>s7epq@My6f5mf z0x?L6=gM(0Fu%+-9fNpFbkR|tyjxx{zC}E7r&sTbSrllu#m!4$V8rCkch*Bjf9?fY zuVj+Lc&--HO+RPUI*ohSfDqY+;0Nis$2^n?&!5bQHMu(#7I`XkM_T>a+w6RDsvI)g zlibh*C0AK?E()$-SW1Q<@w&*LJTbbLEnSu3MHc6@gXLyIl%`6`n8&J46g_i|kqeLyF`k+apZpsd`xVw!tw z69+eII$~s%)3P<&a=TOu(z!b-yteP2$DuQ;tGz7j{bk z91Bfe1}pqXg3KwzSC>0-)YF%jKK$r6gVHi`9gC$=W89jBjrq(En2f!ahJ~$5T z6H!f)dc=j7<|hK4cx*i!b*_jt<<2W#(zx52SzLNujZlvWKH{(S9qC*fvR4caY{#|% z26m}papt?KUdSuMDbk{JCM}|3IoDVrZ zn-x;W>ZQK+M$5cdT9k$BYv<-RQ{(RUCx-H*M=nzf#vJgnqh=4syW_a^H_L?OWjLLINv zu8;WRPABaHA^u`udO{NQb7+L`s936eTnVqytxd`p!BKKG~?khDyQM^{Zkiz zZRpr%F3`P`hUT4!ojXB$=EZhpRM(UPx&GcLc+u*O=7EZWy6~aI@ydvkNxqeOP=CQ^ zZ$6mYXxZisqd=hDF!GN_dUVd*NO-v~)85&7ee;X*TgFuuJNgkv=enCVq6R-a38 z9bIipc|vz%s7jL~qOEOSdA`|jvH4s+6`C;!^*2{9H~4IO#LP!c60p6>h%uH4Sg&P% z)4J(4UC&Wc@@ixgZV)ZdI+Gy^K!E!?#?HTB-2Tb79^uVMU=LacJhB&S*;7D|Qn`$B z-WidF3@LM(_)KuB_ViaYDD33l$@KE_gyFtEf zeS_zE6tmkxH6bzgRBuvbuR>iYO|#^)i^`lA!-MfS;PNtugKz-Ixc~s7b}&tmMlQg? zqUsWlGE;em$bk}Ck-?KMU_X)XRa7j-ey^(&^&xE(xP+L6z$$gFCMzNRcjBTN$ z4i76-#wz%-N=1sl)tUJdBNcLG`nGZ(NYQ^sgI*`-XRo{D%m`m$Lu)Q_GF1OWmmH;_ zNGF4%Bcs2?m6@&{!KBd5IItE!P|!P`b!dNUrPI{UE7zjw!Kj=RIZz0)iPO=F9FWhD z_7>V3<6pMY3H>BUq=C6l-Mo0v-K~=g90eb8VX8QF+1P6KrywRKdpkKuE zX9s;lW#nJ9;a(WkiF_TXef-}YI#}bai^s*6Q6i{7597GxYCu-JA9t~zGUx6#;Ud#I ze_F~h6%Xyo+po#(7=fgqxE!G$Qt(o8#^0AS%TMD|8FB?cCR;J7Yj$CU^NVO2c(S zs?HwM{27H8k&e(QH0Ne!E%MR*){t{Xjpg*sDTSwpregZ8@J9-gilzRT)Q>eE zl&JE=Kh-MK@SKRF<2_A?gX>Cs1>XqI4L8|GWIor;f2zcv;mm1%6rSFMTTBD&ia6*0q! z!tBBdDR?us@%_FJCT=6DuE7z#dv9NFEraR6-cOQXCkGpacm)2~L0kUO7|MYGhh;@` zIH$S1XXl5@5QERWcVkr6JxDG`(+X1+X-fZq!KS`R)%Hts(je(ArCPYVFcR@E6=NWk z!s5w}tbM8zc_GYHCFR0Y-ff;H;0UiRUm3ER-alypAqos17wQfac zT+m>3eIKKsOObxbDC?qcHVW#Vx9RKDc~xVoXH18zx1=C1 zV~n2L`GuENHE!+kuzwtlus)c{sw#?U9lPyl$^P@(O77OTt%Jv&xVy(9MJb^ygGvDp zz?PX+r^wP$C?2WsaIE7Ag)rcisI23JQ({<4r{V_22j|R6R$rj(#KW*A!zh_>`pXj| z-_I;>BG;YK59}kd`}@t$FnF^hSh){zX*CExXSsK7@2YU9lkV}Uj>^Gu^sfJUj0@w1 zGOIC*c&agB?qjCBi{gH;GkOm}0bigGzwWUrp#VchtZg#By6KFD9k-x!l)m3|p{#FQ zR<8BYtzo(PrGsy+y$F^~fBx2*9PuXbkyE4R+Ee`MFdleVXA>o40PwzT?-sDbNdM88 zz9`29@1i#*cM-$Xy`tvED3FDVBzWu6BPQFZ3VdAC60=5p4IF3kC8LuNgcA$1x`sLg z4)FSLB0v#g#oruby4&qe14VjUo~MLQbkcTod-d5)f1Z^*RgQhbfM2i3wyrkBIx z4BupRJtI#LL9>T{OYZ;Y3)Z$3hqK0HnsS$PmGH^!(CY`YcuixW{-9_Ba&1pXh7B0L zuVp=#+jGA)0BQcBR%d{Lg*{#hnlX$jmEX@cS}uQY2LIBv_Hh+!^mDX`c}6#!-)3h_ zc^2K;2$Zj|I0o!$IXoh}dkPG7$-wkV21jZ35B67=z09k&ggUWdGi^q*|Cj=rMM`x3 zSV8r9$4v@bjt)#z?3jo%O^M&_ZF}eGDu)jG&ljZHL8O6Ql_9030=KF=&Lt14DwrrZ z>#52tIA-Rc}lr<4?oG5*B6G!r0A=EgwS_8qj zHFlc{8SvATvcl1xUTyXMjJ;)t)@z*{rCmt=_H;)=ybcIP`*5&i7wXa}tj;9LjMj3d zd+GfTdmvl$-VGc1(oR8oJ2~L<6_?ewVA?or^UJS7ox9gJp z`4dCZxyoTbkL%>hk)?v1OS&~XtrfZ#w?BRAb}%I?D%8zone!y#%CKqucRHemgr$3T z8ILrfD{%+z6rk(FUww(QTE z$xr~0$5S?k`{fOD4x{pU(F?INORf7SI#DbU^T&?8bf?wTKzIt*oQ0yCuBzm%C5cqN zZk(O=*=_@m^7xMr zyg15Q$8kRS!h%A$%;6}JZ+Y3dfI3~t-fwxa#pVrBl+bJCKSLPkIEu)RrXVQKQ9kR7 zIGFz1CTe3jUxjK3GG0-iEN`H47EviUeQU0?dTCCM+NGTOcJ>q;vPlISHaJt))2GAE zxF6iQqA}ZM{I=ZI-vs(B|g~U&Y_OteH;-93!(g zQ3zDTla``Y6yRPs>C~VaW>G#psd*B*Hw!8JX#60&AHK%Q-5C7HSI%JmmNq4;Z?Tmm zXWQ;Q+mq@uuZO=94S1U^c3$SG?g`yuQ_AylOHdy)KEqFb9X#ivHV?PF35tE*DrkRm zo%jOw92zuhBo+GS4gbzcD$5DKCEJ1FA>$AbrHf=zwJ1|8V}d(>=*EPlC5e&<4ClHH zB8&@bOuOu`BiZIfh0V0&<(w%!K`-|7?^+WLCw^-v>b5GmXZQ~HYn{z=_6{h7ON$}K z|G;q^=qbZV@RNlrprxjsn?A2^(=tzK1YS!HCxb(g)de@9+fqJMRbjY$Zb~~Zi`{9S zwNJm+gtcpu1pRJwo=`0E*T_>PGNLt`YB0Qli>UPtIRC9#oulIgR3@8Woy%JAL+8kN zZwyIgXeXwzeAJV8p1Ec@(8}D<%&pmW6!ieN1vHsssG}!jscP{Ux?}mqGuK5#Y`ok3 zM^547S>**LH+gnDy$r0^CK-Oy%<69nCSx%-zVvLIHvY2x1x90I{`du9|3;%0k2>>S zOsX@iDzV-c9>91tR0wJ23^6xE=xIylqW{c&of}zf*^zq6{uREfs;GO*2Eb9P(&L&X zqxPV&+>af&B@)g7WlAta_>^_E)qtHf8=ax|pM%q6<-LX6DnD;BGh|L(k#b;yr-I%F zRw)j)p6f<@pyO~!K%BpdjBa{q?dGcFFgVEB8dtmPxr=xf0Q;SjUKMaKoCF0USkl zIm0UWOA!TMJN7uSz49m1_27?n1Rv#tI@b}pD^3~>QooW|$sPtB&pv_P|0 zRg`o-M>!ooT1CgIdK}a9uW9>Ea}`l>>`u{c+?0r-;|-U7uN?M5r7{cui?a6&XsYSf zhGRh#3!;Ehqf{Xj>4HE6r3;}WT|q#)H0eY{rGs>(iV%U&q;~?+yL9O#bO^nMBz!yQ z^W672=l%2j%4F}gXJ*fyS+nL^*Q`iia0 z<;(UJg)$S8M&?16ai>hloZ!TFVrbG_d$eQHl-I>q$F-aHmo(x&-^XTuUUl5}_86uW zFNlhJ_b^jnea<4pTX6-|^i}9us@uU0C2@aVf#T3D(?l;&9X5M8&5B=7nW~fh#*^^| z@6XQ8P>*(b>}qooC&1zA&x>?J#|uQqP|H9)RxV->@@q^aA`HF=E}a=~+>uFfZ0|&w z6ow-USI#k_@uce?(j)FMYcc84Pr&V#~U2K;-aoE|0=c-eJM>I zcIoiCL`)xBvAwkiZ~P>UrU~SSnbb6k!KFA1=hR{H$~jkkl|oMW_xxi1vJ5oO&R)a@ z4HSSH?B4sbv-=FN!eNkim>}{5wOo+Qhwu%&k!Z2HpNzA zT2TidXL$(|xIW{fpc>1Gysh`s6xR}iJOoR-{TSzW!tA?@3{(PUH+zS2#T*O;%6^pS z+{;xqvV-I%NhvP!jl!thy3)_B?K}+cpBj!mDB-yuVy2@QeRVT-`vNX5(PD+Ipm;l+ zhFeQ725Vujq%B_X)A}Jt3u@ZA1QKbs{dV>VtD*DWivsPU==+kJ7D)KUrAA|X)loWX zhblhI&zxUJFZOuq+z9q21+m7nF|8+{gZKZfGJNu80O}ZM2lxUfiqDC+;IQ`GE*o!Rt+C5@jr(jA(RWF$n z&=@>k=>)_F`&|_2o%1K4&l#Nt9cwb$B}vkzJi_Nox?9{&NjkK{lN(+20uVc4~8EZ zPyBf+*to{7vQHQgJS0TD&^J};RAdnP`YV8Awi8yonmTJ+W_@N;zGXYR;;!h$MB{(@ zZJdB0$5{tt!X(nk9XV=fU z5YA&idmQ^b+~tBV@%IGeB5AS!HQj!JPQ4c4{yXb?7A3*?I;>N?Gtzy+1bL9LVGkc z4-mOhr0wU!_caL_9u~h6j!f~9d5ITGDFsbGXwaoa=kt zaOaG#&eZ16!6EtF7#{m;H@x0FVnJ);mV!oje3IPgR8$Q%^s@Y}j&Nu8q*zd**TCGd zKH#_u4ObjG0nH#bfHSnqj(1N$S9#%Y3CgIcTvy!nKJfGj2*yfHP0X*x2HdQZaML(RHhDO;RtiT%} zQ`2xDwM@9UOuh-9gO6(9B)TMl4}&(DO{2;?0X0B+P9=!4e^V*bVL(_oQi2>l9p13k zv*yFTF3iZGw}a!a-hWK2f>g6ea_j%hR_#!-lR(V&MPJF{;S2XQ|0CfOkWb3Yhs90k zpSL{k?0*Q|hGLj^&H>D{z~fMV#o@FNUJ0NqwyBMN-2K0~!#3sQ;eIC|WGZ{Z&f;B|z@#8$m!cmg_ibd`XI)=S(WkfVX7THrAwYS^9vji5T{ z&sEek{4Q$h>+GMzWX2(TI+$3xT&GP7arme7{Qz&4ZXAMJi49IY@i#vW+|54nX@UqI zA^FlA=Kp)EfQ!@4cqhysA#FF>+14o8ZaybN2l~QTsc_QW(f?vy;F3d$Z zirqhIxed^7f_CXbg%_+LG4rw1^>-L`G*(*QhyHpek_W&kWb44&<30kPp&(4`ho3G* z@KQwl4R_Yyc#cO8+T+g83)6wkY_SigH{>^Yn{=G#Vg9Rh4lCQP&aQNC!lsM3E^ld# z8}cw}jeC^ZEv+y2d$3i|*Q@oglwbZR85}X2`wQ%1^xBHxDcDBmaQ7HN2Vn>0^AL<#XC7PzM;@ z&8rA=@brAy>7Xa|w?)0jq1KVOmd;Z}A}PjpSFt0UT%V|D;1il<c^GO3*pgLAbp&o z8g6UdG_5TDAUZwlr_|jW3Y}q(ovyEj@TX9$36*@qFsnVHpzfu3q^0Hi6FBMY>m^~3 z_GNi-MvL+sXyDI76r|hE)@H6c4izq^8`8O=nXS~+i>|3j5Pg%7hdf(cw(=$Gm>V7e zKa7DQ?K0L%2*3n0I}AiIIWHJY91H*#tyd_HXUD}?9in)x-vw%xzs(D{M>FZt`u{f<;Fi8YQ<}m{O6e;M!jE^eO~1Wk#v4) z8qavG$2!Ml{92*oWzvo=hGA1C`MHBti7{2Sd8u+)+0oN5ed%W&qdOB8?iQykQ9fvy6?XuH%9eo$h~fV!%#o z_3Wt)O8jZ{xseQCZ>GlCm7(y!;<-`0X-&e&?iE(OAPvWbu1QX7IRa8Iqp<(z77&FI zHmIbzuLlS0fh}u#9LX{}QPKXEYnGvF)RHcKh57GYE4UW7#)u^@_yp9Hi8|w*0Z6Az%K|nRapHI% zYc1upewZ+nt{sHea{14@q#*OM*NO>Jz#!=VA2|mxNKD=I42tiC{!3!-PtH)BUdACs zI_mFC$J_tRCwU{V21zyqWL-kcnj9!Q{Y@_Nm>c>DepmwDWLALt5+{(looW#VyN$pq z#CzH;n5~}Yp7v>Ffh>KH9;utfr{i&O#phgcJY6*m7k*5Bjro54ps`ZbtnDR?brZ`I zH|;XJzCGpvy%W&s2cE;Hu8?My5`g!e)v0A9DGr*{3H&n$=b5~aR@p>36pC6$R_6;P zA8Q5uGNl&I%pmXDj=TNEkNw{IR9w}TD2WeMC0#gg&j-}D@(JihNj~MKOKgUV#NO{O z>pTYHAIvzbI065W1Qi()v`_YX*Sem^!M90l5N8;zr=8$wyq!my#+Ive*|=G=cG21i zemQ>eQQ7*}Krh7|5g;`EYQDFH-+PP%@F_Uf2;8J(dhpA*j1y3(J&J$lR27d(I=t{1 zc5SR%)?Ga0u*C6hrx1jg-(#R32pT7#=Byoo`@{*_zgwJrbp7x%6+{A1SpT_;a8v9t z(Ut|h(`(?GOQd2)u1O!g=&ggzheCP{r)S`*T)@AO!WlwJgx*?4Y?qkUH zUi}}ULHA{84LqecA|Jy1i}ej9FJicI(WRvUjxAS|+L8PUl0zm6EQyf{-PZ9yEaM?= zbl64a{%;>yp3R1GGpJF3fM9;!Z$Ua!dk({JEcrqqyanmi)hXFCdq{cI@&4Fw>Sl(e zK?ATlN8w3#ETQOuuyLU&g5Obb%2w8n$o*p|VE-=)-^93jDKC!V(_m@Vs$V-45L28X z7A#{5zCpNXb>VKbjc-9q82Ja+hBp&=*QKZdsNlk2D-c4=bu8!?{~P1<8}dPB=Od{s ze6-Hbaqhg_4)bG4Dq~9#a5w>3Y~%Xh}^xU6EDwOqgIcUUjna1AA8aloE{ z?y0kBKQd-;3`SMv(#lzkx}^7xOS9{m*u7*kk)6&uZ~9t_c{QJF7koeKGE5!Nw{`J- zIh00SK|MNPN(I|ICYp|by|eg{6hi<9 zZs)3vVSY%ycwK(?{Z=g9J5uMZb+K@T)t9Rt^sYgsvHBkZLT=AQKgm6m(fQ(Eo~Tv0 z_;!t@>|^io{zp=GrVFN>2>gq#aGi+_q2-1pYEuCQsUVC&V}e*ruvm8w_I%PZbvCVDu%rN-iROe*z?4{T{K1-aBpZR^ly@tu45 zg_1M&Ki#d<{nDuyy21xR-bQ#IQod_1un=|atu;TDS6me1FY;cxZ@$^Rc0*tf zd4V8w=DSPwg_2}X^VRdA;3nB6pAt@=%5Cj&jKzDS>EKmOUd~MYfC004nGqJL*>?Vm zE3y(X*}F+Ro)u51w~E?TF4mus$i2(_OAVdU|Hw|VnF*4S#uy2ENdDGtV>dOYuUhOv zV(h2KHpw}B@4NXrJ&H_6pGEBr7s`=cGNHWM#sUHkkNC=&11m9P?#@Z)6yPpp48*YYmU-0jG?Q(HGkcl)A$2bx$rjGYqFAu zsS@IH;v)WW8-wPevT}cYzQjI^&)R z2wUl`H7L$#YR2jEdc#>S67w7XaU?BfEM!@d&U0U&N6}ZqkY$9#Q_x#jzl=hT6 z-|L;;-)jb&w%kU4SB4=hMHP1T9R-nt8QLluz`5_}X=PSfl4y&`9tgPIf-P6C$6Uk&#K?U2emRvf?USTMFJpR*9Nz@6AI%6nE^uA(~Rtkc4Q+BzLwqay zp`dzb^Gd^@iv)xB;3RfE=IR`;z1R+OI2v+w@Ymse=w8qQ&w!is69K)}Gv$8$?;hP} z(WbAxhLs}NrR>ZcNtW{O5!Jfi_@F$Hc?QVHH^ z;!p%65G0)MFA*Pq{|v#Q#$AY1k5Ruu2E=nvyTgh6j!Xp+DW|HzlnBlbr2ro^E0vl= zOEV}_>+;ih^_V9zLC7p*roKtKre;toHHV_sWt#|~h*wgPoL99pOVqBLv1KB=)YPBQ zC<7tU(+47KKUu{hk+E4!WA}s}Ynf+S#ouW~Ue(d0{bCyhY<8u1t~o#!pK2TV>Yf#e zYM_uR(kfNIV=(fCq|55FGkv z_8s;M{i`GI5fTIZr@JW$roi`6@T*C_*mKx6^LW#e3br7W_P|gyFIR4}mtanc&GvW| z_jA&Xhs-spGw`m8or>M#Poe=2j;}hxFAhQtW$Dw{CkYo~5E^*12fHGZ1X88G5;x3i zpM$a;0EfQdUs8WOIn^zP<6KzCv?bHdmB>35B%ak#MQDZ0vs*jj#xAxr&xbF)SEVzG z$z;WttYJ$_SGO$bfY6W&n8)gp$T9Yx?9f$-GnK0|a7+5FYJoeE9JgY6EAJB~{a12t zIgDNd*4jAZJNqou=$&Y`++lI$%(m6Z@s#&Derr!v=@fZ7cU#jp48Onf?QvoWVmi3+ z{2c#r;8crHgy9xXl6`Sp>}qJmlb}Y=ci)wr5OcW6=)Ri=eYNn1@udc<`+Ob;_rqV? znQoF*2rZ?(&(+_59krs24gQfPdQ@Pa#X#acY95=M)+I`~c1zW#LV5_A`KH*Fdf%8d z-u?vCLVE&Q$-U{&LJdiN%;moUxO%n|ODwA}B$YB&q_w&V&`-g5moE)Vc^1Yl)Rkth zu=~j?x{^HKmxP9khU{|XiVf9mwmUy@Y<^_vnU|;WACNH>i^28JuUkYK)3>kH`KZ<9 z%6T^Hy-#9&=3jX47r6a3Y$T|tjq5=9qotj3omo2bVcLTn0=S-VH`hkyS(8KQl;iFf zGCfhX?16Zr6o(S~!}#Va{^ingUE%y9Sdi~t{14+TZ0OfA?t|^Jdx!5;6#!NJrsaM9 zd`s(rbr}M}2~Vr?u|iqmQx1EaQffZ%5fT^(sYXpPuh!4)?|HqfFd_ta>Uz$7u>f>v%gWY2=qsxK~ib#Yk zH7u3+GLc2=gs)sKF=&yM$S|rwgrqj%7vwS$7D&94Cp(7lkMVuY~ zcb?*GE!XIzsj4jc?HWt1gv7D}MfGY#DzFP(n`BJbe`@S9<*S!aJWCZm^d35>_q{4P(43bhuBoHjKk`__gUjxVA)HRi*kfdzs2p-XADpU? zi~N>~S{Hv1!wX(E`Ti|KNLDRMos&da)mJbRagnH=PPH**>ZO~C>X}lJ98!v@XJvp4 zsFWg1hx>zR`R9o%Bq99 zNLt{O;V8{P^USPRAVX;`qCS@aG8d#2q>|=3)q;1LfZ#ur+=Z5kfVNH7RR0F3`~Rtq zX#jP6om~i+Rx)Lx9xKwOUf`6Gkw;#CIWVBv`-wPR3=*40^^ZEf2MGUB$1iD!lNSjw zdnTZeuf!?rbnYTfFcg3~&in*$DrKUyw3v`cel8%UBcO{(fD4a-W(MX2pk;&a>#4{5 z09%0d6-L<=u5VY~H@?Uy#6w_TJ3n3No?c|9dhLX<7+%t@pCX=%JaS8WX$mf-ilIUs z2@zhyrw0g`07TNpyAuWIeiN*^p1R-ni2}olJ*t7Fg4w$lW ztI=hz!e`7;-Q+mV)T+mEKZova#e@?gj9u2&+$O=uaq@nS{K7ZxY>Xuykect($KI&-ErJqu1RQ5}e?kQS_cqM0icqa^4=u-y7yLveX$Z}h z59Or{SAGW8!4cStxd`$rmdkxZyl5xUV}tmE$7c56Y)X#JC*VY~j2`;D@U_>q0dkXa zy9+nKMQKYxf{nsau*xQ`mgVynCm0!G1{bT9beCou@*-Cv~zdX&{($1||EF6=#XS+Sl+gDGc8XDWvrM9vZf~B6Xk<6x5 z8Gf6Wu&?kUc&6(v?l(9&Zz%B$2p{YIQt~d92^bN+n2R(^heh^I^C0Ug;}afwz0Qn=%D~uj{ax!re8^7~zWDD)X=iy;3N@-lSM7$K$SPBX&lQ zz2ppb6zaTs)VpH{9*eHidz8%Uj9O~BE7X;gReR@yPxJ^j;&w`(=QrM+O9eeRn1 zV@{nWjn(_g!o7=;4>X1&Dx_9J1G&8G&iWvieVPunSig|-uIhvQNz*z|Z!*S~&n`|2 z3>BlyX??aQevHVuP)|y{WL}vE>*m!>OHe-3dwdDZDd*0&#otzS?vVL1PO;l^(rR@! zmX^0I1N9v?Uh0~>c6v8}+srLm zbMNeYqYsPHK~6!z!SGb?UTEE&~3KAyb8WS<}LEzht#FwG*g=A}vKmWCe|QvA@iklZ}Vx2AeY zVQW4Ca2heCm^sQZ=)3Af9$q@z4h?xLf|aTrUGgC>@F-bPg)53pWQ`4U?#pQRM~h}3 zQ1cl|{U!x14P&`O2Nx17ogA2_NaLURJ-oPgx%|^j^!q1j>uLtIkOHsIr~^1KYDF_o zKqb7^&-*L)IG(3+I$9n!l?c~{R50RJ96wObVoK~*%+PxrGd+{mp}9$o1>OXdwN z>Y;6~3_c^1q0P$B`rnCz$^E61G%@HP38{__rYC;uKIzpx%;%+IOF?FzuS8zh!}u@j zHW*2YHeo74&qymD0Gt`O;{x=Le2?@vSvOd zS_|7#^VwoWrvX(x1Z@rSK_osr?lHv$5Jo#c4^>rR@;`oaXY!V6XxyOleUT;1D0~n7 z#Wvu98S@>q6-v6+)UZTW3-EvB*-bRgi0$kcq-b@-t>^-|?L*thB1SB(5q1DV1`A}l zz9wx6Tb*y8F;Tq3;=Oveq{{D7=PmK!>)(=Ef75U1zRnzKLN9fwr?1Ft+bJ9f%eq+0 zjH)Q*7G0HyeZ8?E5x#9a^^D!#fHdv*((tghhiz_6qwUKZuhPg?CyhrN4<%{j%ntc- zN3HdblnB#SP=5feD_&dek1^fiZ|+q|A9Z?Xt&2D7Q~6+78Bygqd^7xm@}r#Wk5ytr zd-t?gT42_}Z!DzlR1^mHg;p54vzKfCJ}xlqpa@Fy538}fmpg3B>eFwQh^BIX$b5@3 zXJe0k7Cz}u@ca(%8VGAXOgTWDfRdL}c3fuhqH(~P%doAMNnN)fV_ny@^9YNS%*C68 zUDQsO2*K(<=?MXXT^7~}faHx*(Baj7jT+dFn%lRta@O96HlBcLcyT)bw3(1CLbWNG zo1X4yC2T&(=0tB}QIhcGXDlSxJu$B?@e_$ktgT~v9$^ry{jWaK1B?9 z)zd!Rf*fmHY;j6OkqS1LNx4^r-IyD(z{ZA1>rbl{RigOLekesL;R8g2*tyjavvY%U z2g2n%a{l+Dg0#Jq>{pB%rfR5-+u+_XAPLmzG;L0qXN{Hn+lLl-n|l&A9#oa)3SXA6 zP%9}3yi*pK`{Z?Vs@l2Vdh+9Glk1DKg9~fPKk0JymCMRQ**@yOib9*yiBiyEyAfEd=97Kv8mh3>k_1VH`A|Jr2UN5 z>;>aYtni&s?3@lm^BeFPETp>JZ@*-CwjcgvuRg+=c;(wGHm2NHLtBxKMWK0P*zq27 ziZaiH6dqI zQA$RWP)uk|ajq*Z4bxLT>Bt*39zg4qO<3`A_KcT98H2H+)?J^_4mVqRLy!s?tOac) ztIR6OZMLFsH3zg`r0tvL8$G5PEKTgI7rVst$?(}7X9;qyII@mCU&G*=!D$~AwK8sJ z6mDC3^k~NGcRtY!>XtLbDIE_?^$1QKAv51V8&Ng3xE(;}#pmBzt|r9-#|YAU&zG@w z)?4ZhuSorKJ7eWvvKfCxZybDblqKUtc0<*#vEo*XXJZX1lA3v z{`&%&LvEgT{*m)Z1KM!tPaDP&GX%&8KvOuFlNi-pWA#jU@|uQmWr7AKtMi2_`Z$2C z#!I8Yb<`l31Yc1W^GiO{?43a>>tiP=X5aFMH}>II{9v(`U2)U$&3HfetxkN}Vkr|*mqQfEv34~EOuBy$ zd+DAOJ(K54bDsYCwY`cZb6Z*$D;%;UCZ=S*@4ztGW3y6Qwn#|X=G^aLK&zGQoN6+> ztlK|I@E24Cpr@?%;~Wl>8?YvlF;>=kz^ zUm&Z9HoxPSm-42&cgihn+L*5Twult5Wn5W3Cwi~Bn9lRYkiPzS*(Ya2+vm@DQQMx= z-heAfA!v37R$7si#}nPb=_INd{o(vED9?s!s8%VZ{1ZXQ9nY6@Enz5$>ufYj|FFJB z(fHA=`+=eue(*q*Kg#2@al-#e0wI~A2oX*2(;55Imcv|_&UV5~2ZJygrbPG5R$JeF zQs#sE z0{1<{kGKW>n^csdI#PzXE!~QRgyh~mDPVQWDMMV^Y08rkH!8^hyhC8Q_%#bjaS9Ut zUBdw@Sf)B8l0;dJxXcAsZm)oqCa{`2UHfK6m|s>@FFm(5>mD#}BRhS3aU5J)hA5tW zqHtlR*8ooQ7;gBgK;O8iY7*Nm#NPL|n0@-vs}s;KPD$jV&-KkPH!GLnUr{imG1an= z_r26yMJmbcGB_c0;>CywjNXautGih!i}6s}pxG5M<&FIEwNSVh-O&%gm8LVv{*8{+W;`N8TC9YkI4j0V6n0pPjV?xt zG9H+z9YxfRj)_|%gU338KZwfu$k!wdJZGX^*(HX;GgB*b+3!f0R`!r*qHV)sXAaZ{Prod&1Vc1 z`?;;}yue*a8UwqBJw3M{UlUX}uN$MQnHo5dnBY-D-BH#9IC+r2eww^oR6!(+S@K#A zVU1s54Stg|j}>gj3%6`X?A*h)W@Dxjtb}(S)Ax4J_ZCtI4j#FGJm zVO=s{Q0LYKe&ciI7YH_{V+akOY2}yUQV|ohVQHxg)20f`|3>qky5vDtOCn23;|`3y zk<|^s+Rd|o?mHG4Fmj;W3ix_;JMOIfZ%Xi-mj34=x0hGkcI&&U^m;3Hch{Lm+j<+V z1OlW#Cos-ylTLpxZVnzz4D`L0&4iyjVx9?(zJu5LV(FbdMKRR3iCtNz5Fu|Ve;|ysnzTnlvOP0@W*8dTtWE>NM^NNPXMdUaLZCpVO22eP&k}eAEZ{%5Rf4)A?deze*&+t*4$DWo%wSZ+>gcKKiA@@FtC#r4){!4}u2(e1g@y zQWV;dO@2PS2Z)~Hyk~aQE*Ipv8uL)aII@&IGa?T2NFE49v!mUWy5}OGXeicE%RBng z@r%{0X}`IbaP@g>^mZlEY_t8aV%-m5JM?FF_W(&bu7(r3d=Fc6(32T$485 z&*1%_%CqfCe19=c3jRH^1a^fmUVwYk5AZS>Z0#ZW6AxjHx3HEF&CMQ62e`!+x&z-5hCc~!?itkg^^9QCY;v*TDDfJ5_Wzl8p z7t#(r;KwT6r3$&W^W4Y2nFPiZLVsZnwHm=A{P+_g-TgkW_zKtCON1NR&y^hq0$VXs z`b!*TTu_ly>nzYvBe=@ofZ)!7$MNbQvrxQGN7F88wkp!${?Ek|`-aC>aY^oogxs7j>xH%_KUe}uG0i6O)1-hEYPc>2#;)Aw?2Dac` zs+Tv8^x*K{9eW4Nya*11;_V>q7>r*@(qlQ4HN?%<=&t>hRD|y39fh7w{=5qk6xxzE zFb$ej)kP(dlU=6e6+yQ+3wKCd$rK~;uDS1GL(o>L&jJugbZ(7ZNt;!b!IG7FOnwAa z-OwbGKt}L2+%dRnyOKYhzeLq5Na_s-tf&gs@(F@MN@)pilkEuz5n0JuG2AAwKa_Ep zi`p&JkM{QNRZTaSsmVR<3aWzzDLzE=JypbI4Bna3CMB2FT9J(w zUlxqhzXz<9EE#H7rf?XDCU^T?`FwMTnPFeSD+5E>3hsvf#s=ir<=jvI>4 zFw50x?zkoQ@1BQBgM)V0NDUC1(&7#a&s7(uRa>}^zU+e4&5&l2_L+R=6D&mZd^k)AC~ohBRujrHtyFVG4Cm3V*wFUtEC&B(6Z!W1yR-kZGXc z#Qu#ujs7KuQYl%hr!4y>hV5Oa}xmaMdb(nWnHqgF2}{Fd#}x>Fb6oe zL0RUF$4K>jIjJ03|IYUmgpHihKC9)Gt*I74hxY3IBi-xw>vbK9ET(kXvPWva2rJ6& zcY3{usBj|KshELPsRG_mc~H+T&|}0YT9XOO;e;>YBl_>kvy~sx3g|1#W$i0J=t-E6 z;Y|6$FDN(DiSYA&_run;FxxjuIIdgdNXbVQOOUGYAA%q(buvw|Rb3IHa#_U&A9NdR z^bMb)C17v42Vy$+d-`Lg61}t^kuPS89nIBE~Go4n#)$_7=$d#VVF@f4A-*)yLZ zsLVg}j=Ybjf+br=c>J6W_u^AHI#S3htXyk&3fJhqi@OvYegbk9(p!JX8bE0A#5DlDIC4>6Z_A~fOURYeA zweO1Ylc^6^0c(k^AB8HxJ&2E}%-TCL_n-4oa%!u6 zU`d`R8#mGVUHjDd7?lA}9Dt9;m9+e?{}KPSs^H@IcgK^>Htl{Is@9+4vpN`cj4R)y z1n}|aqZJouk~Z`N;Mvb}#}N8;PcdVY-7%1>3g8V88% z_UdSm&8M)yCm!ZE(qW%vXMHdc96Ly?UGtVp5X4ulJQ^z9j>Fdtz{z32o`;d4Hlf^V z1nRXAxB;+NM&D??Ylrrzoq!y{1L17o{eo4aqVUyR91!Bi;V#Z<2m2;ZKCf(BAK&P| zmEE&ETKO^AXT#XrBi+0RHuC1U3jvseNBN1v^S;OSN;vIBpaQZ(+uL=g`q&B2rIlt~ zzLc_FDFMTN`SJV&WD4%NA#;yIpJIt|blt8S?j+dLXp2f!oJp~D6|sO;#9Pb?PyIqT zO|4y@&Iz{2w}w+HU)3CMNN z_-D}x$b3X6ssK8X%e<9_zfv-m=`&}%2LIIwpIO^yNCRpc=r1lVb*u-lFi_t-rdun? zfpc~yz5e%=zH0DM2^K(8Ovn6Y?WdS~jP-F0yt^w}zhZiP{*C56>zkU4fyZH)p5-s1 zdd}ztw-+7okhgpzgz1fyWL9TG9j9>~yi4=>JmEfDX$QSzK8~Vqb_Ii(>}Jh)=vt|S}PE;8+7S_NN2ma*xPkqz89|<@`LnzrU}1*4(%co@&dd16sL#A zK3k=VGTUk~Nt0G-*BPz6>GPAr0N8&q6B~6rZ-!d{A7lq|H&emS`}k`3u2C^nmdbr< zfD8{N#MYE$>kS`7mV}PXeL1#P$7xStbs^ufle_@-tgV%C5a5_qVs+MXN&;fhY8=54 z6wimwjSYpncq7{EEiR8gwJ3W*>$pRuz~9n$#|9#qDfI5=teJl^WfS4K$u=r;cD&3RV|p30r6-_% z1w(d&kvZ>UYfWM+TSC%TFMWsBa1w1&XYode!I2{1`!N3S$M<2XU9lYlC0;4@b&ENf#3o zKSm3+Uis3MtaAb?-9KY%jv*G!9#4~pl% zqB%h1E;uQwzbDHAP%a{Aj7Wme45A(ZxC<;Kf4B>PNp^+EoFLLWsQzQdJ<<#c0*GgW zS^zDg6`-lSzfC1b37BCTYMOWc@Eo}6Efuxvyc2+aEO! zrpY7hcfHXFhxq7u6_uOX)*hZfiXdz7h9E@{h zXuzV_C*L0NE!NfagBF7VTkg1ABX#Me9)~*y*obgN1&LNar>zI)8i(T0MttQ1W6`*n z!o+kkr${mJT;FcO9cMkS}E{DIFM3N78Kc<&wS znC{YEv-fj`lva<}8lkP!qjL?K{u4m+s$O}Dfj`W5olQdc329&K8)cg))Em#So=3p^j?HE)}o zfF>{xaSLgLftozgzZdX-FXUUiC*KAPA_w@>;EKhyTa!lLZ7TnN*)g~O+A-#*c8nF= z(Ww7*Jv=sCr+Q;WJ*FX4T<5HV2&QAB#qI>Ob3?~F{@-BHzu}=pC88Yz?0-5nWD5R| zA@gE)_oW;4gzIQN;l(iPb$JtkpY9b)v@kd=g z&%md;42}pYwzcQwA|KN;o-Kp+HR%agg#{8;(*}80B7RK&%Z%~z^r8A6z>U!OM3T?K za!SDH2}nAoNBj<-Kny<0|hi0uuK*O4FgomD$If)!b&IXm;O=kR?+LEf-VFGGa9eh_%;k%+;yLAZTHO-7UHXona|(ML+QS31 zj1VQZMJ~Cv8w5P79e!WEwu zdjiIuR;P+2-sFd?CL21}dUVWoq)$bNm7)3ah>rQ@>!a#dcIM?G?|wr)3i8<#-0O{u zq-wopic-Eo#1Nd#>^C(i)CQ3o>@|B3j|dutvNw=_K|A_y{-8e4@W%k^6WO~lDd>Y` zg>}o_|F@lkzH6B9yJcNC>M#*Ltlyn{0*cKzz=xm$#zShO5sv-sfCeV3t5RL-zUram zZeAHbKGwowRlsF)TfTk7&S9ow29vWN1HT%S{V@}%9+O@sS@X6Je&AM!->N34kRMnl z&e%OqKYZ)cYq<4aP^WKKguf9j?i!vDn}^7^jT=H`QHj7KjWM}OTl(;2qyGc$q#>qG z!JV%FS9_=(7~UlL!Oms_wmde5qk@%z-z*+ozGsJcYy)d95y4Na!M*IEQGWQ*iQ=4( zb4BLJkGbOPYuhd>JQH+2D zFn)t5u1LbPHIxKf)T%WE3p+LC&XAeCal9b=`m*+F6J+dNr^9XGiA&{Wwd3;b92-Ih zW`=S4o47K8$1W1}6=4rX%%2V13;p72w05;eB-DmhJ=df_%Rz`iudh0JA1h3y!2R>z zfK7L)kaakR@%UAx&0+xZOp5W~58o+t5J|vq#FF0BJBjn~1GwH@X8fMH1q) zPYXac0D&E#T)w}ZMRoT3H>n`N-$jIPe3{bK-vQ{&)4s2Oxj?iv{;(f|01JXhJikLE zSCVJ~7Q?fz2$Db62$Fw{NM3(Sv>1qVeCaEvbW5gF?j%1KK)9C%EErXQHAy7w|Hp&? zlJ9kyJ_1bjKjeAp=fFpKF4+w@=oV@#*XzbucK6B3)YC0n^z;L|t;p8)OfucIJbr); zb=fK-u%(mrKX8MaIfhUBS6ZsKV03LXM&XT^m$*%+fz$U(3ZU*z@|MiWQe!fQrX&J0 z_rd|G-GW9@%5-|2&Y_L>q|i63i#>XSMLBt47W2yYdRAG>%D#MPnYkN0#u8nTZ!eJV zW*^L3jN{^8xUTX%+No)H;hhsW94S{@$`&{pllb|TTAS4*S87HuI@;*P5nFkRzuT&6 z(V2%m58m4_rS#P?USiKfU5QxF*-*epHa`-d6}8p3nWjO5FNC@W5LlgX=ApAoP*!Um zhXeb)_(3mZjYRwE*R?LFabfu+T_(%HB}$Px0JFj(=MBP58>w%do6EyYNnViA#tuo7 z;Vx!36?b1SiFmuYKJH8K&0$&UzSgJgk_Nt~_>flYUG=c*=Hu!jnOjF~6wo1|uE_DH zatM}2@7+j>y&c+8Lo49|dzSrW%tGvar;RqGY$Bq7t^HnE`I3ZAF*nZ@U(uwNE?*!# zBEI4M2}torD7=I&t1vc#8=0~dqOBXS|5hi0d|-;UuQKixt)}SZ*A_2k&a0p&w%?yW zzLX=Uk5P0bvB3~V2-2uV?~9FH~OEA^;Y{?WEiHsqx+D%SBi_hD<=R+4}FgX5-VQ_@k_sh=h58JRF= zJf<;U0DUH1be^$^;kxHXNSA)HMq2cbLM`C%I0M>k9Sg=uw1{8FU70LcyAJ>*FpSHk zg&glo5=xddfW@yzGiTIS`4dY$gJ>m1ik;Zt zWT7S>MNaGO2)?(NW@i)n5?9`%bA@_5sb!{<@^ zWLpZ^J2OP`_pq0CK!oHy-+^mZ)XRByB>tdQGMojYF?yb+&@9&C& zua%S^UM|rx;Ep+Powen8q&3Ad!?LS#G%4IuT3%}wVRAq6joz;rmKtl{^x4-!_iC-< z#lFWNch`sLpBs#v^)jO2OTmfW0vx}FXp9C-!lcsJ)s+TM{_Jt1MRAvPcJ~lBNMf*f z8+^@)UJ)L08Qk>@*u;?Ue7#8NLeiz~XZJTQ?q6b|Vc(>rjW(IXP`%M&+h?}kvZvOw zZ1)^q{2YR)v}gA6Vm7X|&lc+OMr@6>PT>15e0s;7`)Nkak^jON&m-3E#f-P-47PME z>td-n%_0JMT^zcGM+;EtYpxSpn?k-F`AW>U66P7oEV!g38M|Z3=k)l{=15cV&%9|( zd)r1)yze?(l1prZ;Yx4l8!fe=an< zxt@#+h3MKSycW)UFxopVkEEr17_IJ&kb>H3b6Oo~dsRG%@Q?Y3FWtm(#x(t9QZ5eaC`hf*UJuc70pTqcSTK&~KdXbFT6!b1(9qO_tyK_w2uts)kW0*#o8 zARYvW{cXf)`_G#<`*!#3{$_Xf_xAVw>^q-4*gbAN<*cUi7?*~IYe2umyc?lWRzH#! z_%hBg;(pp}5k=66E84)xTq()9*%v|Qgxd6b?b>fWx#1&WMRINVsy{geTZoUW4BnlL z*v`s`#9U3+X@tzn3Wqnno8*d4gTYm0vxPasouA)O?vIBFV%FTQ8c{@~7))UCAiuX` z^F=oPxS7dON7+}1E zOC)*TwQ#;w+PZ|6*;HoHoe9fvzjs))yfWI_E9roQj_Rmr>sC7q3NPY#SY5m15G2~M ze9)EKB@2<7OT3Ri$l6XJ`U}EXB*&&Eb?v9_uaUFklo)-51^OmDrSMG@UzB zz=iV3&;krI&B`@aeSJzhf6{i!>9IpcL@Q~+SX67HGIW}-9%nF*po|!Xv0LgArbLj9 za)Zy8E9g0D3zkK*fi|{y2)!475n5x+TEQ&2nMXaL&sIfphg!DiYhg#B2hJl@6Fhvu z-sUEKonOwjdR0Crk`5;jz+JvgVac--ed}% zC0HX{qpZi5$#UZ)mU9ygQOEp>Dms|0BsGn(wRx!@vXXtQEZz87D49oC2FEcPtMJtH zfL6@mkr0{19RDO%BO)qCJ@#5$iI*T#7qEeK=BjMwvaFm zkQBENbg)Q|7hv$2K2MK;l^O@H4HQ6d=JY3=41M$Kw*!RmggOVXFvu%q2u;~~iHrVD zKm5_}A|UR5nkeC>DX&m1`QqMl&)tN1mt4kQbbV1=`1{mL)tH-B<=4E8!3padI+V2k zs%)G|w66cL&6&bm@D`ggN)R{MzB&Bbj$0hQlTk0d*LY9hf>D&jZ?Z?vJecVX=qhaN z*O2xN_0}H`Iym30TX;4^LyoLcJ}%X&CTDxLs-2X}M!9#P`q%^sAQW8U(ME*HxNI>G`D$E27&kUBfR zTDbkZ{D8>TRm)Mt4)E+gok2+qh#K;rRVq9L4Ch8c)wXuXekf`oS=PPL`KEowDc) z-^lK)9Kn-HcX11j6qZ!KZm=)gdVlrbsW$b+HXc;}Mq;A}C`Hq6YpWmbYRyjArb&8` zmd_a;n}HF_q-CpJ2Zdujog-vg`2J^CoV%A-9xf~?Y&{<2?(uEb%P1oJaupj`(&QM@VTQ#kZVJMbp4w5d|ywVdhAOLfu#%89VK@P(k`K5Yf2yU%P%1!*i?ObG*8)ynHEufAa z8EDLg27M37|AL~3LSlixi}YZ#aYPVx;WBKK0>vb83V=i4v3|yqa2GT+j4-w&P7PoCtVwf(9?^IfSI`QoW!Q3bAiQT zECmpB4n$;;8*Wm-7AbP0`64SyD{{y)5nmO8eDPot*t6~6yw{Y*rzJCA`oxTA8 literal 0 HcmV?d00001 From d692b034fe8517fa1c49b080b4d023bf1f763a4c Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Thu, 30 Apr 2026 20:54:31 -0700 Subject: [PATCH 159/159] Update syllabus and lesson content for clarity and alignment with course objectives --- lessons/.jtl/syllabus.yaml | 6 +-- .../10_Turtles/10_Welcome/10_Welcome.ipynb | 50 +++++++++++++++++-- .../10_Turtles/70_Projects/10_LeagueBot.py | 2 +- lessons/10_Turtles/README.md | 4 +- lessons/20_Types_and_Logic/README.md | 9 +++- lessons/30_Loops/README.md | 8 ++- lessons/40_Data_Structures_Func/README.md | 9 +++- lessons/50_Projects/README.md | 4 ++ notes | 0 9 files changed, 78 insertions(+), 14 deletions(-) create mode 100644 notes diff --git a/lessons/.jtl/syllabus.yaml b/lessons/.jtl/syllabus.yaml index 3252c3ee..b16888fb 100644 --- a/lessons/.jtl/syllabus.yaml +++ b/lessons/.jtl/syllabus.yaml @@ -124,7 +124,7 @@ modules: uid: rkftzcAi exercise: 10_Turtles/90_Graphics_Projects/40_Turtle_Spiral.py display: true -- name: PCEP Alignment +- name: Types and Logic uid: ryHvW6vk lessons: - name: Operators and Types @@ -149,7 +149,7 @@ modules: - name: Code Challenges uid: jJI6aomB exercise: 20_Types_and_Logic/80_Code_Challenges.ipynb -- name: PCEP Alignment +- name: Loops uid: TzgRqJlw lessons: - name: Iteration @@ -201,7 +201,7 @@ modules: - name: Fizz Buzz Badgers uid: U1uGzJ1r exercise: 30_Loops/90_Fizz_Buzz_Badgers.py -- name: PCEP Alignment +- name: Data Structures and Functions uid: fDPxSid0 lessons: - name: Functions diff --git a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb index 76b718a2..7349effd 100644 --- a/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb +++ b/lessons/10_Turtles/10_Welcome/10_Welcome.ipynb @@ -3,17 +3,57 @@ { "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
                            " + "source": [ + "# **Getting Started with Python**\n", + "\n", + "Welcome to your first **Python** lesson with the *League of Amazing Programmers*. This course will help you take your first steps into programming.\n", + "\n", + "To 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", + "\n", + "We'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", + "\n", + "You 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", + "
                            " + ] }, { "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." + "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", + "display_name": "Python-Apprentice (3.13.3)", "language": "python", "name": "python3" }, @@ -27,7 +67,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.5" + "version": "3.13.3" }, "syllabus": { "name": "Welcome", @@ -36,4 +76,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ 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 5bea5a3b..dff4379e 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 diff --git a/lessons/10_Turtles/README.md b/lessons/10_Turtles/README.md index 58454632..c43478cb 100644 --- a/lessons/10_Turtles/README.md +++ b/lessons/10_Turtles/README.md @@ -5,7 +5,7 @@ uid: Io0hFJiW # Introduction to Python with Turtle Graphics This first module of the Python course teaches the basic syntax of Python -and how commands string together to form program. It also lightly +and how commands string together to form programs. It also lightly addresses important concepts that we will consider in more detail later, such as: @@ -15,7 +15,7 @@ such as: * Lists -# PCEP Alignment +## PCEP Alignment * PCEP-30-01 1.1 – Understand Fundamental Terms and Definitions * Interpreting and the Interpreter, Compilation and the Compiler diff --git a/lessons/20_Types_and_Logic/README.md b/lessons/20_Types_and_Logic/README.md index e6687815..26851e72 100644 --- a/lessons/20_Types_and_Logic/README.md +++ b/lessons/20_Types_and_Logic/README.md @@ -2,7 +2,14 @@ uid: ryHvW6vk --- -# PCEP Alignment +# Types and Logic + +This module introduces Python's core data types, expressions, input and +output, string operations, and conditional logic. It gives students the +foundations they need to read values, transform them, and make decisions +in their programs. + +## PCEP Alignment * PCEP-30-01 1.3 – Use and Understand Different Types of Literals and Numeral Systems * Boolean, Integers, Floating-Point Numbers diff --git a/lessons/30_Loops/README.md b/lessons/30_Loops/README.md index ba6fd1b3..b129fd16 100644 --- a/lessons/30_Loops/README.md +++ b/lessons/30_Loops/README.md @@ -2,7 +2,13 @@ uid: TzgRqJlw --- -# PCEP Alignment +# Loops + +This module focuses on repetition and working with collections of data. +Students practice `while` and `for` loops, iteration with `range()`, and +the basics of lists, tuples, indexing, and slicing. + +## PCEP Alignment * PCEP-30-01 3.2 – Perform Different Types of Loops * The `pass` Instruction diff --git a/lessons/40_Data_Structures_Func/README.md b/lessons/40_Data_Structures_Func/README.md index 424bbd5f..cd1d233e 100644 --- a/lessons/40_Data_Structures_Func/README.md +++ b/lessons/40_Data_Structures_Func/README.md @@ -2,7 +2,14 @@ uid: fDPxSid0 --- -# PCEP Alignment +# Data Structures and Functions + +This module extends students' programming toolkit with reusable functions, +error handling, and larger data structures. It introduces dictionaries and +sets while continuing to build problem-solving skills through practical +programs. + +## PCEP Alignment * PCEP-30-02 4.3 – Collect and Process Data Using Dictionaries * Dictionaries: Building, Indexing, Adding and Removing Keys diff --git a/lessons/50_Projects/README.md b/lessons/50_Projects/README.md index 5fb04ced..5dde6bc1 100644 --- a/lessons/50_Projects/README.md +++ b/lessons/50_Projects/README.md @@ -7,3 +7,7 @@ uid: rLq5eVeW # Projects +This module gives students room to apply what they have learned in larger, +more open-ended programs. The lessons here emphasize planning, combining +multiple concepts, and building complete projects from a prompt. + diff --git a/notes b/notes new file mode 100644 index 00000000..e69de29b