<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Invent with Python</title><link href="https://inventwithpython.com/" rel="alternate"></link><link href="https://inventwithpython.com/feeds/all.atom.xml" rel="self"></link><id>https://inventwithpython.com/</id><updated>2025-09-22T10:00:00-04:00</updated><entry><title>Fractal Art Maker for Python Turtle.py</title><link href="https://inventwithpython.com/blog/fractal-art-maker-python-package.html" rel="alternate"></link><published>2025-09-22T10:00:00-04:00</published><updated>2025-09-22T10:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-09-22:/blog/fractal-art-maker-python-package.html</id><summary type="html">&lt;p&gt;Fractals are beautiful, self-similar patterns that can be created by a computer from very simple rules. My &lt;a href="https://pypi.org/project/FractalArtMaker/"&gt;Fractal Art Maker&lt;/a&gt; package makes it easy to use Python's turtle.py package to draw your own. You don't need to know recursion, but you can read my free book, &lt;a href="https://inventwithpython.com/recursion/"&gt;The Recursive Book of Recursion&lt;/a&gt; if you're curious. If you want to learn how to draw pictures with Python code, I also have &lt;a href="https://inventwithpython.com/stt/"&gt;The Simple Turtle Tutorial&lt;/a&gt;.&lt;br&gt;&lt;img src="/blogstatic/fractal-art-maker-fractal.webp" style="max-width: 50%"&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;Fractals are beautiful, self-similar patterns that can be created by a computer from very simple rules. My &lt;a href="https://pypi.org/project/FractalArtMaker/"&gt;Fractal Art Maker&lt;/a&gt; package makes it easy to use Python's turtle.py package to draw your own. You don't need to know recursion, but you can read my free book, &lt;a href="https://inventwithpython.com/recursion/"&gt;The Recursive Book of Recursion&lt;/a&gt; if you're curious. If you want to learn how to draw pictures with Python code, I also have &lt;a href="https://inventwithpython.com/stt/"&gt;The Simple Turtle Tutorial&lt;/a&gt;.&lt;br&gt;&lt;img src="/blogstatic/fractal-art-maker-fractal.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;h2&gt;Installation and Quick Start&lt;/h2&gt;
&lt;p&gt;The package and documentation is &lt;a href="https://pypi.org/project/FractalArtMaker/"&gt;hosted on PyPI&lt;/a&gt; and &lt;a href="https://github.com/asweigart/fractalartmaker"&gt;the git repo&lt;/a&gt; is also available. You can install it by running &lt;code&gt;pip install fractalartmaker&lt;/code&gt; on Windows or &lt;code&gt;pip3 install fractalartmaker&lt;/code&gt; on macOS/Linux. You can try this code out in the interactive shell:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;fractalartmaker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;fam&lt;/span&gt;
&lt;span class="n"&gt;fam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;draw_fractal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_square_drawing_function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;size&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.96&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;angle&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max_depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you run the package as a module by running &lt;code&gt;python3 -m fractalartmaker&lt;/code&gt;, you'll see several demo fractals.&lt;/p&gt;
&lt;h2&gt;How to Draw Fractals with FAM&lt;/h2&gt;
&lt;p&gt;The main function we'll use in the &lt;code&gt;fam&lt;/code&gt; module is &lt;code&gt;fam.draw_fractal()&lt;/code&gt;. We'll need to pass it a &lt;em&gt;drawing function&lt;/em&gt;, which is a function that takes a &lt;code&gt;size&lt;/code&gt; argument and draws a simple shape. Here's the code for &lt;code&gt;square()&lt;/code&gt; function that draws a square:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_square_drawing_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Move to the top-right corner before drawing:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;penup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pendown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Draw a square with sides of length `size`:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Draw four lines.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;right&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On its own, this function will just draw a square of a given size. (Note that this function isn't recursive; it just draws one square.) But the &lt;code&gt;fam.draw_fractal()&lt;/code&gt; function can use functions like this to create fractals.&lt;/p&gt;
&lt;p&gt;To make a fractal, call &lt;code&gt;fam.draw_fractal()&lt;/code&gt; and pass it this drawing function, a starting size (let's go with &lt;code&gt;100&lt;/code&gt;), and a list of &lt;em&gt;recursion specification dictionaries&lt;/em&gt;. By recursion specification dictionary, I mean a list of dictionaries with (optional) keys &lt;code&gt;'size'&lt;/code&gt;, &lt;code&gt;'x'&lt;/code&gt;, &lt;code&gt;'y'&lt;/code&gt;, and &lt;code&gt;'angle'&lt;/code&gt;. I'll explain what these keys do later, but try calling this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;fam.draw_fractal(my_square_drawing_function, 100, [{&amp;#39;size&amp;#39;: 0.96, &amp;#39;y&amp;#39;: 0.5, &amp;#39;angle&amp;#39;: 11}])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/my_square1.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;p&gt;This draws a fractal similar to the &lt;code&gt;fam.demo_horn()&lt;/code&gt; fractal. The &lt;code&gt;100&lt;/code&gt; is the initial size for the first square to draw. The list of recursion specification dictionaries &lt;code&gt;[{'size': 0.96, 'y': 0.5, 'angle': 11}]&lt;/code&gt; has one dictionary in it. This means for each shape drawn with the drawing function, we will draw one more recursive shape. The new square is drawn at 96% the original size (because the &lt;code&gt;'size'&lt;/code&gt; key is &lt;code&gt;0.96&lt;/code&gt;), located 50% of the size above the square (because the &lt;code&gt;'y'&lt;/code&gt; key is &lt;code&gt;0.5&lt;/code&gt;), after rotating it counterclockwise by 11 degrees (because the &lt;code&gt;'angle'&lt;/code&gt; is set to &lt;code&gt;11&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;By default, &lt;code&gt;fam.draw_fractal()&lt;/code&gt; only goes 8 recursive levels deep. You can change this by passing a &lt;code&gt;max_depth&lt;/code&gt; argument.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;fam.draw_fractal(my_square_drawing_function, 100, [{&amp;#39;size&amp;#39;: 0.96, &amp;#39;y&amp;#39;: 0.5, &amp;#39;angle&amp;#39;: 11}], max_depth=50)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/my_square2.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;p&gt;Let's make each square draw &lt;em&gt;two&lt;/em&gt; squares by putting a &lt;em&gt;second&lt;/em&gt; recursion specification dictionary in the list. Because each square will draw two squares (and both of those two squares will draw two squares each, and so on and so on), be sure to set &lt;code&gt;max_depth&lt;/code&gt; to something small like it's default &lt;code&gt;8&lt;/code&gt;. Otherwise, the exponentially large amount of drawing will slow your program down to a crawl.&lt;/p&gt;
&lt;p&gt;Let's make the second recursion specification dictionary the same as the first, but it's &lt;code&gt;'angle'&lt;/code&gt; key is &lt;code&gt;-11&lt;/code&gt; instead of &lt;code&gt;11&lt;/code&gt;. This will make it veer off clockwise by 11 degrees instead of the normal counterclockwise:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;fam.draw_fractal(my_square_drawing_function, 100, [{&amp;#39;size&amp;#39;: 0.96, &amp;#39;y&amp;#39;: 0.5, &amp;#39;angle&amp;#39;: 11}, {&amp;#39;size&amp;#39;: 0.96, &amp;#39;y&amp;#39;: 0.5, &amp;#39;angle&amp;#39;: -11}], max_depth=8)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/my_square3.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;p&gt;As you can see, it doesn't take much to fill up the window with too many shapes. The key to making aesthetically pleasing fractals is to take a light touch and spend a lot of time experimenting. For example, we could pass a list of three recursion specification dictionaries to draw squares in three of the corners of the parent square:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;fam.draw_fractal(my_square_drawing_function, 350,
    [{&amp;#39;size&amp;#39;: 0.5, &amp;#39;x&amp;#39;: -0.5, &amp;#39;y&amp;#39;: 0.5},
     {&amp;#39;size&amp;#39;: 0.5, &amp;#39;x&amp;#39;: 0.5, &amp;#39;y&amp;#39;: 0.5},
     {&amp;#39;size&amp;#39;: 0.5, &amp;#39;x&amp;#39;: -0.5, &amp;#39;y&amp;#39;: -0.5}], max_depth=4)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/my_square4.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;p&gt;If we increase the max depth to &lt;code&gt;10&lt;/code&gt;, we can see a new pattern emerge:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;fam.draw_fractal(my_square_drawing_function, 350,
    [{&amp;#39;size&amp;#39;: 0.5, &amp;#39;x&amp;#39;: -0.5, &amp;#39;y&amp;#39;: 0.5},
     {&amp;#39;size&amp;#39;: 0.5, &amp;#39;x&amp;#39;: 0.5, &amp;#39;y&amp;#39;: 0.5},
     {&amp;#39;size&amp;#39;: 0.5, &amp;#39;x&amp;#39;: -0.5, &amp;#39;y&amp;#39;: -0.5}], max_depth=10)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/my_square5.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;fractalartmaker&lt;/code&gt; module comes with a &lt;code&gt;fam.square&lt;/code&gt; and &lt;code&gt;fam.triangle&lt;/code&gt; drawing functions that you can play with. Also take a look at the code for the demo functions inside the &lt;em&gt;__init__.py&lt;/em&gt; file for more ideas. Check out the rest of this documentation for advanced tips. Good luck!&lt;/p&gt;
&lt;p&gt;NOTE: Calling &lt;code&gt;fam.draw_fractal()&lt;/code&gt; automatically calls &lt;code&gt;turtle.reset()&lt;/code&gt; to clear the window and move the turtle cursor back to 0, 0. If you don't want this behavior, pass &lt;code&gt;reset=False&lt;/code&gt; to &lt;code&gt;fam.draw_fractal()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;NOTE: To free you from having to import the &lt;code&gt;turtle&lt;/code&gt; module yourself, you can call most &lt;code&gt;turtle&lt;/code&gt; functions from the &lt;code&gt;fam&lt;/code&gt; module: &lt;code&gt;fam.reset()&lt;/code&gt;, &lt;code&gt;fam.update()&lt;/code&gt;, and so on.&lt;/p&gt;
&lt;h2&gt;Gallery of Demo Fractals&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;def demo_four_corners(size=350, max_depth=5, **kwargs):
    # Four Corners:
    if &amp;#39;colors&amp;#39; not in kwargs:
        kwargs[&amp;#39;colors&amp;#39;] = ((&amp;#39;black&amp;#39;, &amp;#39;white&amp;#39;), (&amp;#39;black&amp;#39;, &amp;#39;gray&amp;#39;))
    draw_fractal(square, size,
        [{&amp;#39;size&amp;#39;: 0.5, &amp;#39;x&amp;#39;: -0.5, &amp;#39;y&amp;#39;: 0.5},
         {&amp;#39;size&amp;#39;: 0.5, &amp;#39;x&amp;#39;: 0.5, &amp;#39;y&amp;#39;: 0.5},
         {&amp;#39;size&amp;#39;: 0.5, &amp;#39;x&amp;#39;: -0.5, &amp;#39;y&amp;#39;: -0.5},
         {&amp;#39;size&amp;#39;: 0.5, &amp;#39;x&amp;#39;: 0.5, &amp;#39;y&amp;#39;: -0.5}], max_depth=max_depth, **kwargs)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/four-corners.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;def demo_spiral_squares(size=600, max_depth=50, **kwargs):
    # Spiral Squares:
    if &amp;#39;colors&amp;#39; not in kwargs:
        kwargs[&amp;#39;colors&amp;#39;] = ((&amp;#39;black&amp;#39;, &amp;#39;white&amp;#39;), (&amp;#39;black&amp;#39;, &amp;#39;gray&amp;#39;))
    draw_fractal(square, size, [{&amp;#39;size&amp;#39;: 0.95,
        &amp;#39;angle&amp;#39;: 7}], max_depth=max_depth, **kwargs)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/spiral-squares.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;def demo_double_spiral_squares(size=600, **kwargs):
    # Double Spiral Squares:
    if &amp;#39;colors&amp;#39; not in kwargs:
        kwargs[&amp;#39;colors&amp;#39;] = ((&amp;#39;black&amp;#39;, &amp;#39;white&amp;#39;), (&amp;#39;black&amp;#39;, &amp;#39;gray&amp;#39;))
    draw_fractal(square, 600,
        [{&amp;#39;size&amp;#39;: 0.8, &amp;#39;y&amp;#39;: 0.1, &amp;#39;angle&amp;#39;: -10},
         {&amp;#39;size&amp;#39;: 0.8, &amp;#39;y&amp;#39;: -0.1, &amp;#39;angle&amp;#39;: 10}], **kwargs)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/double-spiral-squares.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;def demo_triangle_spiral(size=20, max_depth=80, **kwargs):
    # Triangle Spiral:
    draw_fractal(triangle, size,
        [{&amp;#39;size&amp;#39;: 1.05, &amp;#39;angle&amp;#39;: 7}], max_depth=max_depth, **kwargs)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/triangle-spiral.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;def demo_glider(size=600, **kwargs):
    # Conway&amp;#39;s Game of Life Glider:
    if &amp;#39;colors&amp;#39; not in kwargs:
        kwargs[&amp;#39;colors&amp;#39;] = ((&amp;#39;black&amp;#39;, &amp;#39;white&amp;#39;), (&amp;#39;black&amp;#39;, &amp;#39;gray&amp;#39;))
    third = 1 / 3
    draw_fractal(square, 600,
        [{&amp;#39;size&amp;#39;: third, &amp;#39;y&amp;#39;: third},
         {&amp;#39;size&amp;#39;: third, &amp;#39;x&amp;#39;: third},
         {&amp;#39;size&amp;#39;: third, &amp;#39;x&amp;#39;: third, &amp;#39;y&amp;#39;: -third},
         {&amp;#39;size&amp;#39;: third, &amp;#39;y&amp;#39;: -third},
         {&amp;#39;size&amp;#39;: third, &amp;#39;x&amp;#39;: -third, &amp;#39;y&amp;#39;: -third}], **kwargs)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/glider.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;def demo_sierpinski_triangle(size=600, **kwargs):
    # Sierpinski Triangle:
    toMid = math.sqrt(3) / 6
    draw_fractal(triangle, 600,
        [{&amp;#39;size&amp;#39;: 0.5, &amp;#39;y&amp;#39;: toMid, &amp;#39;angle&amp;#39;: 0},
         {&amp;#39;size&amp;#39;: 0.5, &amp;#39;y&amp;#39;: toMid, &amp;#39;angle&amp;#39;: 120},
         {&amp;#39;size&amp;#39;: 0.5, &amp;#39;y&amp;#39;: toMid, &amp;#39;angle&amp;#39;: 240}], **kwargs)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/sierpinski-triangle.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;def demo_wave(size=280, **kwargs):
    # Wave:
    draw_fractal(triangle, size,
        [{&amp;#39;size&amp;#39;: 0.5, &amp;#39;x&amp;#39;: -0.5, &amp;#39;y&amp;#39;: 0.5},
         {&amp;#39;size&amp;#39;: 0.3, &amp;#39;x&amp;#39;: 0.5, &amp;#39;y&amp;#39;: 0.5},
         {&amp;#39;size&amp;#39;: 0.5, &amp;#39;y&amp;#39;: -0.7, &amp;#39;angle&amp;#39;: 15}], **kwargs)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/wave.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;def demo_horn(size=100, max_depth=100, **kwargs):
    # Horn:
    if &amp;#39;colors&amp;#39; not in kwargs:
        kwargs[&amp;#39;colors&amp;#39;] = ((&amp;#39;black&amp;#39;, &amp;#39;white&amp;#39;), (&amp;#39;black&amp;#39;, &amp;#39;gray&amp;#39;))
    draw_fractal(square, size,
        [{&amp;#39;size&amp;#39;: 0.96, &amp;#39;y&amp;#39;: 0.5, &amp;#39;angle&amp;#39;: 11}], max_depth=max_depth, **kwargs)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/horn.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;def demo_snowflake(size=200, **kwargs):
    # Snowflake:
    if &amp;#39;colors&amp;#39; not in kwargs:
        kwargs[&amp;#39;colors&amp;#39;] = ((&amp;#39;black&amp;#39;, &amp;#39;white&amp;#39;), (&amp;#39;black&amp;#39;, &amp;#39;gray&amp;#39;))
    draw_fractal(square, size,
        [{&amp;#39;x&amp;#39;: math.cos(0 &lt;span class="gs"&gt;* math.pi / 180),&lt;/span&gt;
&lt;span class="gs"&gt;          &amp;#39;y&amp;#39;: math.sin(0 *&lt;/span&gt; math.pi / 180), &amp;#39;size&amp;#39;: 0.4},
         {&amp;#39;x&amp;#39;: math.cos(72 &lt;span class="gs"&gt;* math.pi / 180),&lt;/span&gt;
&lt;span class="gs"&gt;          &amp;#39;y&amp;#39;: math.sin(72 *&lt;/span&gt; math.pi / 180), &amp;#39;size&amp;#39;: 0.4},
         {&amp;#39;x&amp;#39;: math.cos(144 &lt;span class="gs"&gt;* math.pi / 180),&lt;/span&gt;
&lt;span class="gs"&gt;          &amp;#39;y&amp;#39;: math.sin(144 *&lt;/span&gt; math.pi / 180), &amp;#39;size&amp;#39;: 0.4},
         {&amp;#39;x&amp;#39;: math.cos(216 &lt;span class="gs"&gt;* math.pi / 180),&lt;/span&gt;
&lt;span class="gs"&gt;          &amp;#39;y&amp;#39;: math.sin(216 *&lt;/span&gt; math.pi / 180), &amp;#39;size&amp;#39;: 0.4},
         {&amp;#39;x&amp;#39;: math.cos(288 &lt;span class="gs"&gt;* math.pi / 180),&lt;/span&gt;
&lt;span class="gs"&gt;          &amp;#39;y&amp;#39;: math.sin(288 *&lt;/span&gt; math.pi / 180), &amp;#39;size&amp;#39;: 0.4}], **kwargs)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/snowflake.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;h2&gt;Advanced Features of FAM's Shape-Drawing Functions&lt;/h2&gt;
&lt;p&gt;All shape-drawing functions are passed a &lt;code&gt;size&lt;/code&gt; argument. We can make the white-and-gray alternating colors by adding the optional &lt;code&gt;depth&lt;/code&gt; parameter to our drawing function. The &lt;code&gt;draw_fractal()&lt;/code&gt; function will pass the recursion depth (&lt;code&gt;1&lt;/code&gt; for the first depth level, &lt;code&gt;2&lt;/code&gt; for the next, and so on) to the drawing function. In the following &lt;code&gt;square_alternating_white_gray()&lt;/code&gt; drawing function, the fill color for the square is set to white or gray depending on the &lt;code&gt;depth&lt;/code&gt; argument:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;def square_alternating_white_gray(size, depth):
    # Move to the top-right corner before drawing:
    turtle.penup()
    turtle.forward(size // 2)
    turtle.left(90)
    turtle.forward(size // 2)
    turtle.left(180)
    turtle.pendown()

    # Set fill color based on recursion depth level:
    if depth % 2 == 0:
        turtle.fillcolor(&amp;#39;white&amp;#39;)
    else:
        turtle.fillcolor(&amp;#39;gray&amp;#39;)

    # Draw a square:
    turtle.begin_fill()
    for i in range(4):  # Draw four lines.
        turtle.forward(size)
        turtle.right(90)
    turtle.end_fill()

draw_fractal(square_alternating_white_gray, 300,
    [{&amp;#39;size&amp;#39;: 0.5, &amp;#39;x&amp;#39;: -0.5, &amp;#39;y&amp;#39;: 0.5},
    {&amp;#39;size&amp;#39;: 0.5, &amp;#39;x&amp;#39;: 0.5, &amp;#39;y&amp;#39;: 0.5},], max_depth=5)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/alternating-white-gray-squares.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;p&gt;You can also pass any custom keyword argument to &lt;code&gt;draw_fractal()&lt;/code&gt;, and it will be forwarded to the drawing function. For example, I set up &lt;code&gt;square_random_fill()&lt;/code&gt; draw function with a &lt;code&gt;custom_fill_colors&lt;/code&gt; parameter. If you pass &lt;code&gt;custom_fill_colors=['blue', 'red', 'yellow', 'black', 'white']&lt;/code&gt; to &lt;code&gt;draw_fractal()&lt;/code&gt;, this list will be forwarded to the draw function. Note that if you pass a custom argument like &lt;code&gt;custom_fill_colors&lt;/code&gt; to &lt;code&gt;draw_fractal()&lt;/code&gt;, the drawing function must have a parameter named &lt;code&gt;custom_fill_colors&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;random&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;square_random_fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;custom_fill_colors&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Move to the top-right corner before drawing:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;penup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pendown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Set fill color randomly:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fillcolor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;custom_fill_colors&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Draw a square:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin_fill&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Draw four lines.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end_fill&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;draw_fractal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;square_random_fill&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;size&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;size&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;},],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max_depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;custom_fill_colors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;blue&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;red&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;yellow&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;black&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;white&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/fractalartmaker/main/random-fill-squares.webp" style="max-width: 50%"&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Turtle Colors and RGB App</title><link href="https://inventwithpython.com/blog/turtle-colors.html" rel="alternate"></link><published>2025-09-18T10:00:00-04:00</published><updated>2025-09-18T10:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-09-18:/blog/turtle-colors.html</id><summary type="html">&lt;p&gt;I've been working on turtle-related projects and I needed a simple, interactive, single-file Python program to explain RGB color values. I wrote this one up as part of my work writing a &lt;a href="/stt"&gt;Simple Turtle Tutorial&lt;/a&gt; written in Wikipedia-style Simple English so it can be translated to non-English languages. You can download the app from &lt;a href="https://pypi.org/project/turtlecolors/"&gt;PyPI&lt;/a&gt; and look at its code on its &lt;a href="https://github.com/asweigart/turtlecolors"&gt;git repo&lt;/a&gt;&lt;br&gt;&lt;a href="/stt/orig_screenshot_turtlecolors1.webp"&gt;&lt;img src="/stt/orig_screenshot_turtlecolors1.webp" style="max-width: 40%"&gt;&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;I've been working on turtle-related projects and I needed a simple, interactive, single-file Python program to explain RGB color values. I wrote this one up as part of my work writing a &lt;a href="/stt"&gt;Simple Turtle Tutorial&lt;/a&gt; written in Wikipedia-style Simple English so it can be translated to non-English languages. You can download the app from &lt;a href="https://pypi.org/project/turtlecolors/"&gt;PyPI&lt;/a&gt; and look at its code on its &lt;a href="https://github.com/asweigart/turtlecolors"&gt;git repo&lt;/a&gt;&lt;br&gt;&lt;a href="/stt/orig_screenshot_turtlecolors1.webp"&gt;&lt;img src="/stt/orig_screenshot_turtlecolors1.webp" style="max-width: 40%"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The app has three sliders for the amount of red, blue, and green for the window's background color. The RGB values are presented under the sliders for the 0.0 to 1.0 scale, 0 to 255 scale, and hexadecimal 0x00 to 0xFF scale. You can drag the sliders or click on the slider lines directly. There are also 14 preset color buttons along the top to click on.&lt;/p&gt;
&lt;p&gt;&lt;a href="/stt/orig_screenshot_turtlecolors2.webp"&gt;&lt;img src="/stt/orig_screenshot_turtlecolors2.webp" style="max-width: 30%"&gt;&lt;/a&gt; &lt;a href="/stt/orig_screenshot_turtlecolors3.webp"&gt;&lt;img src="/stt/orig_screenshot_turtlecolors3.webp" style="max-width: 30%"&gt;&lt;/a&gt; &lt;a href="/stt/orig_screenshot_turtlecolors4.webp"&gt;&lt;img src="/stt/orig_screenshot_turtlecolors4.webp" style="max-width: 30%"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is written as a single-file Python program using only the standard library. Unlike similar web apps, it's simple and doesn't have ads. If you have Python installed, you can run this program and learn about RGB values.&lt;/p&gt;
&lt;p&gt;Install with &lt;code&gt;python -m pip install turtlecolors&lt;/code&gt; on Windows or &lt;code&gt;python3 -m pip install turtlecolors&lt;/code&gt; on macOS/Linux.&lt;/p&gt;
&lt;p&gt;Run with &lt;code&gt;python -m turtlecolors&lt;/code&gt; on Windows or &lt;code&gt;python3 -m turtlecolors&lt;/code&gt; on macOS/Linux. Or run the code &lt;code&gt;import turtlecolors;turtlecolors.main()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;When the program first runs, it also prints out the color name strings that Python's turtle module uses. I've also compiled &lt;a href="/blog/complete-list-tkinter-colors-valid-and-tested.html"&gt;a complete list of valid and tested Python tkinter color names.&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Automate the Boring Stuff Workbook Now Available for Preorder</title><link href="https://inventwithpython.com/blog/automate-workbook-preorder.html" rel="alternate"></link><published>2025-09-17T10:00:00-04:00</published><updated>2025-09-17T10:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-09-17:/blog/automate-workbook-preorder.html</id><summary type="html">&lt;p&gt;"&lt;a href="/amazon-automateworkbook"&gt;This workbook&lt;/a&gt; transforms Al Sweigart’s best-selling guide from a reading experience into a coding experience. Following Automate the Boring Stuff with Python chapter by chapter, this workbook will help you turn concepts into muscle memory through carefully designed exercises, projects, and real Python scripts."&lt;br&gt;&lt;a href="/nsp-automateworkbook"&gt;&lt;img src="/images/cover-automateworkbook.webp" class="img-thumb" style="max-width: 40%;" alt="Thumbnail cover of Automate the Boring Stuff with Python Workbook"&gt;&lt;/a&gt;&lt;br&gt;Preorder links: &lt;a href="/nsp-automateworkbook"&gt;Direct From the Publisher (preferred)&lt;/a&gt; | &lt;a href="/amazon-automateworkbook"&gt;Amazon&lt;/a&gt; | &lt;a href="/bn-automateworkbook"&gt;Barnes and Noble&lt;/a&gt; | &lt;a href="/bookshop-automateworkbook"&gt;Bookshop.org&lt;/a&gt; | &lt;a href="/powells-automateworkbook"&gt;Powell's&lt;/a&gt; | &lt;a href="/kobo-automateworkbook"&gt;Kobo&lt;/a&gt; | &lt;a href="/thriftbooks-automateworkbook"&gt;Thriftbooks&lt;/a&gt; | &lt;a href="/goodreads-automateworkbook"&gt;Goodreads&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;"&lt;a href="/amazon-automateworkbook"&gt;This workbook&lt;/a&gt; transforms Al Sweigart’s best-selling guide from a reading experience into a coding experience. Following Automate the Boring Stuff with Python chapter by chapter, this workbook will help you turn concepts into muscle memory through carefully designed exercises, projects, and real Python scripts."&lt;br&gt;&lt;a href="/nsp-automateworkbook"&gt;&lt;img src="/images/cover-automateworkbook.webp" class="img-thumb" style="max-width: 40%;" alt="Thumbnail cover of Automate the Boring Stuff with Python Workbook"&gt;&lt;/a&gt;&lt;br&gt;Preorder links: &lt;a href="/nsp-automateworkbook"&gt;Direct From the Publisher (preferred)&lt;/a&gt; | &lt;a href="/amazon-automateworkbook"&gt;Amazon&lt;/a&gt; | &lt;a href="/bn-automateworkbook"&gt;Barnes and Noble&lt;/a&gt; | &lt;a href="/bookshop-automateworkbook"&gt;Bookshop.org&lt;/a&gt; | &lt;a href="/powells-automateworkbook"&gt;Powell's&lt;/a&gt; | &lt;a href="/kobo-automateworkbook"&gt;Kobo&lt;/a&gt; | &lt;a href="/thriftbooks-automateworkbook"&gt;Thriftbooks&lt;/a&gt; | &lt;a href="/goodreads-automateworkbook"&gt;Goodreads&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Every concept from Automate is reinforced through carefully sequenced questions, exercises, and projects that help you think like a programmer and prove to yourself that you really get it.&lt;/p&gt;
&lt;p&gt;At the end of each chapter, you’ll tackle miniprojects that bring everything together. Whether you’re renaming files, scraping websites, converting text to speech, modifying spreadsheets, or sending emails, you’ll build scripts that do real work. Fun projects like image generators and word games are in the mix too, not just boring stuff.&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/workbook-preview1.webp"&gt;&lt;img src="/blogstatic/workbook-preview1.webp" style="max-width: 60%;" alt="Thumbnail preview image of Automate the Boring Stuff with Python Workbook interior"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/workbook-preview2.webp"&gt;&lt;img src="/blogstatic/workbook-preview2.webp" style="max-width: 60%;" alt="Thumbnail preview image of Automate the Boring Stuff with Python Workbook interior"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/workbook-preview3.webp"&gt;&lt;img src="/blogstatic/workbook-preview3.webp" style="max-width: 60%;" alt="Thumbnail preview image of Automate the Boring Stuff with Python Workbook interior"&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Before/After Photo Alignment Web App</title><link href="https://inventwithpython.com/blog/before-after-photo-alignment.html" rel="alternate"></link><published>2025-09-16T10:00:00-04:00</published><updated>2025-09-16T10:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-09-16:/blog/before-after-photo-alignment.html</id><summary type="html">&lt;p&gt;I created &lt;a href="/before-after-photo-alignment.html"&gt;a web app for aligning before/after photos&lt;/a&gt;. Ony of my hobbies is picking up litter, and I'll post before/after photos to the &lt;a href="https://reddit.com/r/detrashed"&gt;r/detrashed&lt;/a&gt; subreddit:&lt;br&gt;&lt;img src="/blogstatic/detrash1before.webp" style="maxwidth: 40%"&gt;&lt;img src="/blogstatic/detrash1after.webp" style="maxwidth: 40%" alt="Before (left) and after (right) photos of litter on the side of a highway and feeder road, with a Burger King in the far background."&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;I created &lt;a href="/before-after-photo-alignment.html"&gt;a web app for aligning before/after photos&lt;/a&gt;. Ony of my hobbies is picking up litter, and I'll post before/after photos to the &lt;a href="https://reddit.com/r/detrashed"&gt;r/detrashed&lt;/a&gt; subreddit:&lt;br&gt;&lt;img src="/blogstatic/detrash1before.webp" style="maxwidth: 40%"&gt;&lt;img src="/blogstatic/detrash1after.webp" style="maxwidth: 40%" alt="Before (left) and after (right) photos of litter on the side of a highway and feeder road, with a Burger King in the far background."&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/beforeaftertool1.webp" style="maxwidth: 80%" alt="Screenshot of the before/after photo alignment tool, with two buttons to upload the before and after photo."&gt;&lt;/p&gt;
&lt;p&gt;To get properly aligned photos, you have to take the photo from the same spot and at the same angle. I place a stick or soda can where I stood, and try to put some common object in a corner of the frame for both photos. Make sure the zoom amount is the same as well. Then, upload the before and after photo into the alignment web app:&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/beforeaftertool2.webp" style="maxwidth: 80%" alt="Screenshot of the before/after photo alignment tool, with two images loaded. One is slightly transparent and overlaid the other."&gt;&lt;/p&gt;
&lt;p&gt;Drag them over each other so that objects in the picture are more of less on top of each other:&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/beforeaftertool3.webp" style="maxwidth: 80%" alt="Screenshot of the before/after photo alignment tool, with the two images moved so that the subjects in both photos roughly overlay each other."&gt;&lt;/p&gt;
&lt;p&gt;Once they are more or less aligned, click the &lt;strong&gt;Crop Both Images&lt;/strong&gt; button. The web app will crop them so they are more aligned. Here are some more examples from my trash picking:&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/detrash2before.webp" style="maxwidth: 40%"&gt;&lt;img src="/blogstatic/detrash2after.webp" style="maxwidth: 40%"alt="Before (left) and after (right) photos of litter on a sidewalk, curb, and street."&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/detrash3before.webp" style="maxwidth: 40%"&gt;&lt;img src="/blogstatic/detrash3after.webp" style="maxwidth: 40%"alt="Another before (left) and after (right) photos of litter on a sidewalk, curb, and street."&gt;&lt;/p&gt;
&lt;p&gt;I created this tool with Claude Sonnet 4, which surprisingly needed only a little editing to get it right. Portability was important, so I made this as a JavaScript web app. I was sure to specify that it needed to be a single-page web app with no dependencies. All processing takes place in the browser and, as a result, you can download the &lt;a href="/before-after-photo-alignment.html"&gt;before-after-photo-alignment.html&lt;/a&gt; file and run it offline.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>List of 87 Programming Ideas for Beginners</title><link href="https://inventwithpython.com/blog/programming-ideas-beginners-big-book-python.html" rel="alternate"></link><published>2025-09-15T10:20:00-04:00</published><updated>2025-09-15T10:20:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-09-15:/blog/programming-ideas-beginners-big-book-python.html</id><summary type="html">&lt;p&gt;After you've learned the basic concepts, the best way to improve your coding skills is to work on projects. You may not know enough to contribute to popular open source projects, but you can create small programs on your own. Here's a list of 81 projects suitable for beginners. They're short, can be done in any language, and don't require additional software library installation. These projects are drawn from my free books, &lt;a href="https://inventwithpython.com/bigbookpython/"&gt;The Big Book of Small Python Projects&lt;/a&gt;, &lt;a href="https://inventwithpython.com/cracking/"&gt;Cracking Codes with Python&lt;/a&gt;, and &lt;a href="https://inventwithpython.com/invent4thed/"&gt;Invent Your Own Computer Games with Python&lt;/a&gt;. Each program has a link to a sample Python implementation, but I encourage you to make it from scratch.&lt;/p&gt;</summary><content type="html">&lt;p&gt;After you've learned the basic concepts, the best way to improve your coding skills is to work on projects. You may not know enough to contribute to popular open source projects, but you can create small programs on your own. Here's a list of 81 projects suitable for beginners. They're short, can be done in any language, and don't require additional software library installation. These projects are drawn from my free books, &lt;a href="https://inventwithpython.com/bigbookpython/"&gt;The Big Book of Small Python Projects&lt;/a&gt;, &lt;a href="https://inventwithpython.com/cracking/"&gt;Cracking Codes with Python&lt;/a&gt;, and &lt;a href="https://inventwithpython.com/invent4thed/"&gt;Invent Your Own Computer Games with Python&lt;/a&gt;. Each program has a link to a sample Python implementation, but I encourage you to make it from scratch.&lt;/p&gt;
&lt;p&gt;What you don't want to do is start on a large, ambitious project: it's frustrating to spend weeks on something that you never actually finish. You can learn a lot by instead working on several small, simple programs. There are plenty of terrible "1001 Programming Ideas" blog posts that copy/paste every idea from "flip a coin" programs to "make your own ChatGPT". My list is carefully curated to be achievable by beginners.&lt;/p&gt;
&lt;p&gt;More importantly, these projects can all be done with &lt;strong&gt;just printing text to the screen and taking keyboard input&lt;/strong&gt;. There are no graphics, GUIs, or web servers you need to set up. This list has games, simulations, puzzles, and digital art programs. These programs can be written in a few dozen or few hundred lines of code. I suggest trying to write them yourself from scratch, but you can also &lt;strong&gt;follow the links to working Python source code for each project&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=f9k6Q2yys0k"&gt;I also produce a trailer video for the projects in The Big Book of Small Python Projects&lt;/a&gt; if you want a quick preview tour.&lt;/p&gt;
&lt;p&gt;&lt;a href="#Bagels"&gt;Bagels&lt;/a&gt; | &lt;a href="#Birthday-Paradox"&gt;Birthday Paradox&lt;/a&gt; | &lt;a href="#Bitmap-Message"&gt;Bitmap Message&lt;/a&gt; | &lt;a href="#Blackjack"&gt;Blackjack&lt;/a&gt; | &lt;a href="#Bouncing-DVD-Logo"&gt;Bouncing DVD Logo&lt;/a&gt; | &lt;a href="#Caesar-Cipher"&gt;Caesar Cipher&lt;/a&gt; | &lt;a href="#Caesar-Hacker"&gt;Caesar Hacker&lt;/a&gt; | &lt;a href="#Calendar-Maker"&gt;Calendar Maker&lt;/a&gt; | &lt;a href="#Carrot-in-a-Box"&gt;Carrot in a Box&lt;/a&gt; | &lt;a href="#Cho-Han"&gt;Cho Han&lt;/a&gt; | &lt;a href="#Clickbait-Headline-Generator"&gt;Clickbait Headline Generator&lt;/a&gt; | &lt;a href="#Collatz-Sequence"&gt;Collatz Sequence&lt;/a&gt; | &lt;a href="#Conway's-Game-of-Life"&gt;Conway's Game of Life&lt;/a&gt; | &lt;a href="#Countdown"&gt;Countdown&lt;/a&gt; | &lt;a href="#Deep-Cave-/-Winding-River"&gt;Deep Cave / Winding River&lt;/a&gt; | &lt;a href="#Diamonds"&gt;Diamonds&lt;/a&gt; | &lt;a href="#Dice-Math"&gt;Dice Math&lt;/a&gt; | &lt;a href="#Dice-Roller"&gt;Dice Roller&lt;/a&gt; | &lt;a href="#Digital-Clock"&gt;Digital Clock&lt;/a&gt; | &lt;a href="#Digital-Stream-/-Matrix-Screensaver"&gt;Digital Stream / Matrix Screensaver&lt;/a&gt; | &lt;a href="#DNA-Visualization"&gt;DNA Visualization&lt;/a&gt; | &lt;a href="#Ducklings"&gt;Ducklings&lt;/a&gt; | &lt;a href="#Etching-Drawer"&gt;Etching Drawer&lt;/a&gt; | &lt;a href="#Factor-Finder"&gt;Factor Finder&lt;/a&gt; | &lt;a href="#Fast-Draw"&gt;Fast Draw&lt;/a&gt; | &lt;a href="#Fibonacci"&gt;Fibonacci&lt;/a&gt; | &lt;a href="#Fish-Tank"&gt;Fish Tank&lt;/a&gt; | &lt;a href="#Flooder"&gt;Flooder&lt;/a&gt; | &lt;a href="#Forest-Fire-Sim"&gt;Forest Fire Sim&lt;/a&gt; | &lt;a href="#Four-in-a-Row"&gt;Four in a Row&lt;/a&gt; | &lt;a href="#Guess-the-Number"&gt;Guess the Number&lt;/a&gt; | &lt;a href="#Gullible"&gt;Gullible&lt;/a&gt; | &lt;a href="#Hacking-Minigame"&gt;Hacking Minigame&lt;/a&gt; | &lt;a href="#Hangman-&amp;amp;-Guillotine"&gt;Hangman &amp;amp; Guillotine&lt;/a&gt; | &lt;a href="#Hex-Grid"&gt;Hex Grid&lt;/a&gt; | &lt;a href="#Hourglass"&gt;Hourglass&lt;/a&gt; | &lt;a href="#Hungry-Robots"&gt;Hungry Robots&lt;/a&gt; | &lt;a href="#J'Accuse!"&gt;J'Accuse!&lt;/a&gt; | &lt;a href="#Langton's-Ant"&gt;Langton's Ant&lt;/a&gt; | &lt;a href="#Leetspeak"&gt;Leetspeak&lt;/a&gt; | &lt;a href="#Lucky-Stars"&gt;Lucky Stars&lt;/a&gt; | &lt;a href="#Magic-Fortune-Ball"&gt;Magic Fortune Ball&lt;/a&gt; | &lt;a href="#Mancala"&gt;Mancala&lt;/a&gt; | &lt;a href="#Maze-Runner-2D"&gt;Maze Runner 2D&lt;/a&gt; | &lt;a href="#Maze-Runner-3D"&gt;Maze Runner 3D&lt;/a&gt; | &lt;a href="#Million-Dice-Statistics"&gt;Million Dice Statistics&lt;/a&gt; | &lt;a href="#Mondrian-Art-Generator"&gt;Mondrian Art Generator&lt;/a&gt; | &lt;a href="#Monty-Hall"&gt;Monty Hall&lt;/a&gt; | &lt;a href="#Multiplication-Table"&gt;Multiplication Table&lt;/a&gt; | &lt;a href="#Ninety-Nine-Bottles"&gt;Ninety Nine Bottles&lt;/a&gt; | &lt;a href="#niNety-nniinE-BoOttels"&gt;niNety nniinE BoOttels&lt;/a&gt; | &lt;a href="#Numeral-Systems"&gt;Numeral Systems&lt;/a&gt; | &lt;a href="#Periodic-Table-of-the-Elements"&gt;Periodic Table of the Elements&lt;/a&gt; | &lt;a href="#Pig-Latin"&gt;Pig Latin&lt;/a&gt; | &lt;a href="#Powerball-Lottery"&gt;Powerball Lottery&lt;/a&gt; | &lt;a href="#Prime-Numbers"&gt;Prime Numbers&lt;/a&gt; | &lt;a href="#Progress-Bar"&gt;Progress Bar&lt;/a&gt; | &lt;a href="#Rainbow"&gt;Rainbow&lt;/a&gt; | &lt;a href="#Rock-Paper-Scissors"&gt;Rock Paper Scissors&lt;/a&gt; | &lt;a href="#Rock-Paper-Scissors-(Always-Win-Version)"&gt;Rock Paper Scissors (Always Win Version)&lt;/a&gt; | &lt;a href="#ROT-13-Cipher"&gt;ROT 13 Cipher&lt;/a&gt; | &lt;a href="#Rotating-Cube"&gt;Rotating Cube&lt;/a&gt; | &lt;a href="#Royal-Game-of-Ur"&gt;Royal Game of Ur&lt;/a&gt; | &lt;a href="#Shining-Carpet"&gt;Shining Carpet&lt;/a&gt; | &lt;a href="#Simple-Substitution-Cipher"&gt;Simple Substitution Cipher&lt;/a&gt; | &lt;a href="#Sine-Message"&gt;Sine Message&lt;/a&gt; | &lt;a href="#Sliding-Tile-Puzzle"&gt;Sliding Tile Puzzle&lt;/a&gt; | &lt;a href="#Snail-Race"&gt;Snail Race&lt;/a&gt; | &lt;a href="#Soroban-Japanese-Abacus"&gt;Soroban Japanese Abacus&lt;/a&gt; | &lt;a href="#Sound-Mimic"&gt;Sound Mimic&lt;/a&gt; | &lt;a href="#sPoNgEcAsE"&gt;sPoNgEcAsE&lt;/a&gt; | &lt;a href="#Sudoku-Puzzle"&gt;Sudoku Puzzle&lt;/a&gt; | &lt;a href="#Text-To-Speech-Talker"&gt;Text To Speech Talker&lt;/a&gt; | &lt;a href="#Three-Card-Monte"&gt;Three Card Monte&lt;/a&gt; | &lt;a href="#Tic-Tac-Toe"&gt;Tic Tac Toe&lt;/a&gt; | &lt;a href="#Tower-of-Hanoi-Puzzle"&gt;Tower of Hanoi Puzzle&lt;/a&gt; | &lt;a href="#Twenty-Forty-Eight"&gt;Twenty Forty Eight&lt;/a&gt; | &lt;a href="#Vigenère-Cipher"&gt;Vigenère Cipher&lt;/a&gt; | &lt;a href="#Water-Bucket-Puzzle"&gt;Water Bucket Puzzle&lt;/a&gt; | &lt;a href="#Sonar-Treasure-Hunt"&gt;Sonar Treasure Hunt&lt;/a&gt; | &lt;a href="#Reversegam-/-Reversi-/-Othello"&gt;Reversegam / Reversi / Othello&lt;/a&gt; | &lt;a href="#Transposition-Cipher"&gt;Transposition Cipher&lt;/a&gt; | &lt;a href="#Transposition-Cipher-Hacker-/-English-Detection"&gt;Transposition Cipher Hacker / English Detection&lt;/a&gt; | &lt;a href="#Affine-Cipher"&gt;Affine Cipher&lt;/a&gt; | &lt;a href="#Frequency-Analysis-/-Vigenere-Cipher-Hacker"&gt;Frequency Analysis / Vigenere Cipher Hacker&lt;/a&gt; | &lt;a href="#Public-Key-Cryptography-/-Large-Prime-Number-Generation"&gt;Public Key Cryptography / Large Prime Number Generation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a id="Bagels" /&gt;&lt;/p&gt;
&lt;h3&gt;Bagels&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project1.html"&gt;Bagels&lt;/a&gt; is a deductive logic game where you must guess a 3-digit number. After each guess you are given clues: "Pico" means one digit is correct but in the wrong position, "Fermi" means one digit is correct and in the right position, and "Bagels" means no digit is correct.&lt;/p&gt;
&lt;p&gt;For example, game play could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Guess #1:
&amp;gt; 123
Pico
Guess #2:
&amp;gt; 456
Bagels
Guess #3:
&amp;gt; 178
Pico Pico

--snip--

Guess #7:
&amp;gt; 791
Fermi Fermi
Guess #8:
&amp;gt; 701
You got it!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Birthday-Paradox" /&gt;&lt;/p&gt;
&lt;h3&gt;Birthday Paradox&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://inventwithpython.com/bigbookpython/project2.html"&gt;Birthday Paradox&lt;/a&gt; is the surprisingly high probability that two people will have the same birthday even in a small group of people. Even in a group as small as 23 people, there’s a 50 percent chance of a matching birthday. This program performs several probability experiments to determine the percentages for groups of different sizes.&lt;/p&gt;
&lt;p&gt;You can find out more about the Birthday Paradox at https://en.wikipedia.org/wiki/Birthday_problem.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;How&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;many&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;birthdays&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;shall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;generate&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Max&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;
&lt;span class="nv"&gt;Here&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;birthdays&lt;/span&gt;:
&lt;span class="nv"&gt;Oct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Sep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;May&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Jul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Feb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Jan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Aug&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Feb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Dec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Jan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;,
&lt;span class="nv"&gt;May&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Sep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Oct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;May&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;May&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Oct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Dec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Jun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Jul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Dec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;,
&lt;span class="nv"&gt;Nov&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Aug&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Mar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;
&lt;span class="nv"&gt;In&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;simulation&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;multiple&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;people&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;birthday&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Jul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;
&lt;span class="nv"&gt;Generating&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;birthdays&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;,&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;times&lt;/span&gt;...
&lt;span class="nv"&gt;Press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;begin&lt;/span&gt;...
&lt;span class="nv"&gt;Let&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;s run another 100,000 simulations.&lt;/span&gt;
&lt;span class="err"&gt;0 simulations run...&lt;/span&gt;
&lt;span class="err"&gt;10000 simulations run...&lt;/span&gt;

&lt;span class="err"&gt;--snip--&lt;/span&gt;

&lt;span class="err"&gt;90000 simulations run...&lt;/span&gt;
&lt;span class="err"&gt;100000 simulations run.&lt;/span&gt;
&lt;span class="err"&gt;Out of 100,000 simulations of 23 people, there was a&lt;/span&gt;
&lt;span class="err"&gt;matching birthday in that group 50955 times. This means&lt;/span&gt;
&lt;span class="err"&gt;that 23 people have a 50.95 % chance of&lt;/span&gt;
&lt;span class="err"&gt;having a matching birthday in their group.&lt;/span&gt;
&lt;span class="nv"&gt;That&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;s probably more than you would think!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Bitmap-Message" /&gt;&lt;/p&gt;
&lt;h3&gt;Bitmap Message&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://inventwithpython.com/bigbookpython/project3.html"&gt;Bitmap Message&lt;/a&gt; replaces the non-space characters in a multiline string with your own text message. For example, this multiline string:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;   **************   &lt;span class="gs"&gt;*  *&lt;/span&gt;** **  &lt;span class="gs"&gt;*      *&lt;/span&gt;*****************************
  ********************* ** ** &lt;span class="gs"&gt;*  *&lt;/span&gt; ****************************** &lt;span class="gs"&gt;*&lt;/span&gt;
&lt;span class="gs"&gt; *&lt;/span&gt;*      *****************       ******************************
          *************          **  &lt;span class="gs"&gt;* *&lt;/span&gt;*** ** ************** &lt;span class="gs"&gt;*&lt;/span&gt;
&lt;span class="gs"&gt;           *&lt;/span&gt;********            *******   **************** &lt;span class="gs"&gt;* *&lt;/span&gt;
            ********           ***************************  &lt;span class="gs"&gt;*&lt;/span&gt;
&lt;span class="gs"&gt;   *&lt;/span&gt;        &lt;span class="gs"&gt;* *&lt;/span&gt;*** ***         *************** ******  ** &lt;span class="gs"&gt;*&lt;/span&gt;
&lt;span class="gs"&gt;               *&lt;/span&gt;***  &lt;span class="gs"&gt;*         *&lt;/span&gt;**************   *** ***  &lt;span class="gs"&gt;*&lt;/span&gt;
&lt;span class="gs"&gt;                 *&lt;/span&gt;*****         *************    **   **  &lt;span class="gs"&gt;*&lt;/span&gt;
&lt;span class="gs"&gt;                 *&lt;/span&gt;*******        *************    &lt;span class="gs"&gt;*  *&lt;/span&gt;* ***
                   ********         ********          &lt;span class="gs"&gt;* *&lt;/span&gt;** ****
                   *********         ******  &lt;span class="gs"&gt;*        *&lt;/span&gt;*** ** &lt;span class="gs"&gt;* *&lt;/span&gt;*
                   *********         ****** &lt;span class="gs"&gt;* *&lt;/span&gt;           *** &lt;span class="gs"&gt;*   *&lt;/span&gt;
                     ******          ***** **             *****   &lt;span class="gs"&gt;*&lt;/span&gt;
&lt;span class="gs"&gt;                     *&lt;/span&gt;****            **** &lt;span class="gs"&gt;*            *&lt;/span&gt;*******
                    *****             ****              *********
                    ****              **                 *******   &lt;span class="gs"&gt;*&lt;/span&gt;
&lt;span class="gs"&gt;                    *&lt;/span&gt;**                                       &lt;span class="gs"&gt;*    *&lt;/span&gt;
                    **     &lt;span class="gs"&gt;*                    *&lt;/span&gt;
....................................................................
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;...could be combined with the "message" string &lt;code&gt;Hello!&lt;/code&gt; to produce this string:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!He
   lo!Hello!Hello   l  !He lo  e      llo!Hello!Hello!Hello!Hello!He
  llo!Hello!Hello!Hello He lo H  l !Hello!Hello!Hello!Hello!Hello H
 el      lo!Hello!Hello!He       lo!Hello!Hello!Hello!Hello!Hel
          o!Hello!Hello          lo  e lo!H ll !Hello!Hello!H l
           !Hello!He            llo!Hel   Hello!Hello!Hell ! e
            Hello!He           ello!Hello!Hello!Hello!Hell  H
   l        H llo! ell         ello!Hello!Hell !Hello  el o
               lo!H  l         ello!Hello!Hell   ell !He  o
                 !Hello         llo!Hello!Hel    el   He  o
                 !Hello!H        lo!Hello!Hell    l  !H llo
                   ello!Hel         Hello!He          H llo Hell
                   ello!Hell         ello!H  l        Hell !H l o!
                   ello!Hell         ello!H l o           o!H l   H
                     lo!Hel          ello! el             o!Hel   H
                     lo!He            llo! e            llo!Hell
                    llo!H             llo!              llo!Hello
                    llo!              ll                 lo!Hell   e
                    llo                                       l    e
                    ll     l                    H
Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!He
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Blackjack" /&gt;&lt;/p&gt;
&lt;h3&gt;Blackjack&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project4.html"&gt;Blackjack&lt;/a&gt;, also known as 21, is a card game where players try to get as close to 21 points as possible without going over. The user plays against an AI blackjack dealer. (Email me if you are a VC who wants to invest in my blackjack AI. I'm in between banks right now, so make the checks out to "cash".)&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;
&lt;span class="n"&gt;How&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;much&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;QUIT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;
&lt;span class="n"&gt;Bet&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;

&lt;span class="n"&gt;DEALER&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;???&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;___&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;___&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="err"&gt;##&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="err"&gt;###&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;♥&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="err"&gt;##&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;__2&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

&lt;span class="n"&gt;PLAYER&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;___&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;___&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;♠&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;♦&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;__K&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;__7&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;


&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="n"&gt;tand&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="n"&gt;ouble&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;down&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;
&lt;span class="n"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;drew&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;♦&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;

&lt;span class="n"&gt;DEALER&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;___&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;___&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;___&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;♦&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;♥&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;♠&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;__K&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;__2&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;__6&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

&lt;span class="n"&gt;PLAYER&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;___&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;___&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;___&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;♠&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;♦&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;♦&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;__K&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;__7&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;__4&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

&lt;span class="n"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;won&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;$400&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;

&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Bouncing-DVD-Logo" /&gt;&lt;/p&gt;
&lt;h3&gt;Bouncing DVD Logo&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project5.html"&gt;The bouncing DVD logo!&lt;/a&gt; If you are of a certain age, you’ll remember those ancient technological devices called DVD players. When not playing DVDs, they would display a diagonally traveling DVD logo that bounced off the edges of the screen. This program simulates this colorful DVD logo by making it change direction each time it hits an edge. We’ll also keep track of how many times a logo hits a corner of the screen. This creates an interesting visual animation to look at, especially for the magical moment when a logo lines up perfectly with a corner.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://pypi.org/project/Bext/"&gt;Bext package&lt;/a&gt; can print directly to XY coordinates on the screen. Bext also lets you print in different colors. Otherwise, you could do the trick of printing fifty or so &lt;code&gt;\n&lt;/code&gt; newline characters to "clear" the screen before printing an updated screen of DVD logos.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://inventwithpython.com/bigbookpython/images/000011.png" style="max-width: 100%" alt="A command line text window with DVD text in various places." /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a id="Caesar-Cipher" /&gt;&lt;/p&gt;
&lt;h3&gt;Caesar Cipher&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://inventwithpython.com/bigbookpython/project6.html"&gt;Caesar cipher&lt;/a&gt; is an ancient encryption algorithm that encrypts letters by shifting them over by a certain number of places in the alphabet. We call the length of shift the &lt;em&gt;key&lt;/em&gt;. For example, if the key is 3, then A becomes D, B becomes E, C becomes F, and so on. To decrypt the message, you must shift the encrypted letters in the opposite direction. This program lets the user encrypt and decrypt messages according to this algorithm.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;e&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;ncrypt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;ecrypt&lt;/span&gt;?
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;e&lt;/span&gt;
&lt;span class="nv"&gt;Please&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;use&lt;/span&gt;.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;encrypt&lt;/span&gt;.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Meet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;me&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;rose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bushes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tonight&lt;/span&gt;.
&lt;span class="nv"&gt;QIIX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;QI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;XLI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;VSWI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FYWLIW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;XSRMKLX&lt;/span&gt;.

&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nv"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;

&lt;span class="k"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;e&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;ncrypt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;ecrypt&lt;/span&gt;?
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;
&lt;span class="nv"&gt;Please&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;use&lt;/span&gt;.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;decrypt&lt;/span&gt;.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;QIIX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;QI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;XLI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;VSWI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FYWLIW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;XSRMKLX&lt;/span&gt;.
&lt;span class="nv"&gt;MEET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;THE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ROSE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;BUSHES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TONIGHT&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you’d like to learn about ciphers and code breaking in general, you can read my book &lt;a href="https://inventwithpython.com/cracking/"&gt;Cracking Codes with Python&lt;/a&gt;.
&lt;a id="Caesar-Hacker" /&gt;&lt;/p&gt;
&lt;h3&gt;Caesar Hacker&lt;/h3&gt;
&lt;p&gt;This &lt;a href="https://inventwithpython.com/bigbookpython/project7.html"&gt;Caesar cipher hacking&lt;/a&gt; program can hack messages encrypted with the Caesar cipher even if you don’t know the key. There are only 26 possible keys for the Caesar cipher, so a computer can easily try all possible decryptions and display the results to the user. In cryptography, we call this technique a &lt;em&gt;brute-force attack&lt;/em&gt;. You'll be able to reuse some of the code you wrote for the Caesar cipher program.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Enter the encrypted Caesar cipher message to hack.
&amp;gt; QIIX QI FC XLI VSWI FYWLIW XSRMKLX.
Key #0: QIIX QI FC XLI VSWI FYWLIW XSRMKLX.
Key #1: PHHW PH EB WKH URVH EXVKHV WRQLJKW.
Key #2: OGGV OG DA VJG TQUG DWUJGU VQPKIJV.
Key #3: NFFU NF CZ UIF SPTF CVTIFT UPOJHIU.
Key #4: MEET ME BY THE ROSE BUSHES TONIGHT.
Key #5: LDDS LD AX SGD QNRD ATRGDR SNMHFGS.
Key #6: KCCR KC ZW RFC PMQC ZSQFCQ RMLGEFR.
--snip--
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you’d like to learn about ciphers and code breaking in general, you can read my book &lt;a href="https://inventwithpython.com/cracking/"&gt;Cracking Codes with Python&lt;/a&gt;.
&lt;a id="Calendar-Maker" /&gt;&lt;/p&gt;
&lt;h3&gt;Calendar Maker&lt;/h3&gt;
&lt;p&gt;This &lt;a href="https://inventwithpython.com/bigbookpython/project8.html"&gt;calendar maker&lt;/a&gt; program generates printable text files of monthly calendars for the month and year you enter. Dates and calendars are a tricky topic in programming because there are so many different rules for determining the number of days in a month, which years are leap years, and which day of the week a particular date falls on. Fortunately, Python’s &lt;code&gt;datetime&lt;/code&gt; module handles these details for you. This program focuses on generating the multiline string for the monthly calendar page. Other languages also have datetime and calendar packages. Consult the documentation for your language.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Enter the year for the calendar:
&lt;span class="k"&gt;&amp;gt; &lt;/span&gt;&lt;span class="ge"&gt;2029&lt;/span&gt;
Enter the month for the calendar, 1-12:
&lt;span class="k"&gt;&amp;gt; &lt;/span&gt;&lt;span class="ge"&gt;12&lt;/span&gt;
                                  December 2029
...Sunday.....Monday....Tuesday...Wednesday...Thursday....Friday....Saturday..
+----------+----------+----------+----------+----------+----------+----------+
|25        |26        |27        |28        |29        |30        | 1        |
|          |          |          |          |          |          |          |
|          |          |          |          |          |          |          |
|          |          |          |          |          |          |          |
+----------+----------+----------+----------+----------+----------+----------+
| 2        | 3        | 4        | 5        | 6        | 7        | 8        |
|          |          |          |          |          |          |          |
|          |          |          |          |          |          |          |
|          |          |          |          |          |          |          |
+----------+----------+----------+----------+----------+----------+----------+
| 9        |10        |11        |12        |13        |14        |15        |
|          |          |          |          |          |          |          |
|          |          |          |          |          |          |          |
|          |          |          |          |          |          |          |
+----------+----------+----------+----------+----------+----------+----------+
|16        |17        |18        |19        |20        |21        |22        |
|          |          |          |          |          |          |          |
|          |          |          |          |          |          |          |
|          |          |          |          |          |          |          |
+----------+----------+----------+----------+----------+----------+----------+
|23        |24        |25        |26        |27        |28        |29        |
|          |          |          |          |          |          |          |
|          |          |          |          |          |          |          |
|          |          |          |          |          |          |          |
+----------+----------+----------+----------+----------+----------+----------+
|30        |31        | 1        | 2        | 3        | 4        | 5        |
|          |          |          |          |          |          |          |
|          |          |          |          |          |          |          |
|          |          |          |          |          |          |          |
+----------+----------+----------+----------+----------+----------+----------+

Saved to calendar_2029_12.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Carrot-in-a Box" /&gt;&lt;/p&gt;
&lt;h3&gt;Carrot in a Box&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project9.html"&gt;Carrot in a box&lt;/a&gt; is a simple and silly bluffing game for two human players. Each player has a box. One box has a carrot in it, and each player wants to have the carrot. The first player looks in their box and then tells the second player they either do or don’t have the carrot. The second player gets to decide whether to swap boxes or not.&lt;/p&gt;
&lt;p&gt;I got the idea for this from &lt;a href="https://www.youtube.com/watch?v=0UGuPvrsG3E"&gt;a comedy sketch&lt;/a&gt; by 8 Out Of 10 Cats.&lt;/p&gt;
&lt;p&gt;You can draw boxes and carrots using text characters:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;   ___VV____
  |   VV    |
  |   VV    |
  |___||____|    __________
 /    ||   /|   /         /|
+---------+ |  +---------+ |
|   RED   | |  |   GOLD  | |
|   BOX   | /  |   BOX   | /
+---------+/   +---------+/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Your program should:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ask the players for their names.&lt;/li&gt;
&lt;li&gt;Explain the rules and show the two closed boxes to the players.&lt;/li&gt;
&lt;li&gt;Ask the second player to close their eyes while the first player presses Enter to view the contents of their box, and then press Enter again to clear the screen.&lt;/li&gt;
&lt;li&gt;Ask the second player if they want to swap boxes or not. The second player can ask the first player any question they want.&lt;/li&gt;
&lt;li&gt;Reveal who has the box with the carrot in it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Human player 1, enter your name: Alice
Human player 2, enter your name: Bob
HERE ARE TWO BOXES:
  __________     __________
 /         /|   /         /|
+---------+ |  +---------+ |
|   RED   | |  |   GOLD  | |
|   BOX   | /  |   BOX   | /
+---------+/   +---------+/
   Alice           Bob

Alice, you have a RED box in front of you.
Bob, you have a GOLD box in front of you.
Press Enter to continue...
--snip--
When Bob has closed their eyes, press Enter...
Alice here is the inside of your box:

   ___VV____
  |   VV    |
  |   VV    |
  |___||____|    __________
 /    ||   /|   /         /|
+---------+ |  +---------+ |
|   RED   | |  |   GOLD  | |
|   BOX   | /  |   BOX   | /
+---------+/   +---------+/
 (carrot!)
   Alice           Bob
Press Enter to continue...
--snip--
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Cho-Han" /&gt;&lt;/p&gt;
&lt;h3&gt;Cho-Han&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project10.html"&gt;Cho-han&lt;/a&gt; is &lt;a href="https://en.wikipedia.org/wiki/Cho-han"&gt;a dice game played in gambling houses of feudal Japan&lt;/a&gt;. Two six-sided dice are rolled in a cup, and gamblers must guess if the sum is even (cho) or odd (han). The house takes a small cut of all winnings. The simple random number generation and basic math used to determine odd or even sums make this project especially suitable for beginners.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;How&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;much&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;QUIT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dealer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;swirls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hear&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rattle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dealer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slams&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;still&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;covering&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;
&lt;span class="n"&gt;dice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;asks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;CHO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;even&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HAN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;odd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cho&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dealer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lifts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;reveal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;GO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;GO&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;won&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;house&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;collects&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c1"&gt;--snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can find more details and a sample Python implementation &lt;a href="https://inventwithpython.com/bigbookpython/project10.html"&gt;on the book website.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a id="Clickbait-Headline-Generator" /&gt;&lt;/p&gt;
&lt;h3&gt;Clickbait Headline Generator&lt;/h3&gt;
&lt;p&gt;Our website needs to trick people into looking at advertisements! But coming up with creative, original content is too hard. Luckily, with the &lt;a href="https://inventwithpython.com/bigbookpython/project11.html"&gt;clickbait headline generator&lt;/a&gt;, we can make a computer come up with millions of outrageous fake headlines. They’re all low quality, but readers don’t seem to mind. This program generates as many headlines as you need from a Mad Libs–style template.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Our website needs to trick people into looking at ads!
Enter the number of clickbait headlines to generate:
&amp;gt; 1000
Big Companies Hate Him! See How This New York Cat Invented a Cheaper Robot
What Telephone Psychics Don&amp;#39;t Want You To Know About Avocados
You Won&amp;#39;t Believe What This North Carolina Shovel Found in Her Workplace
--snip--
14 Reasons Why Parents Are More Interesting Than You Think (Number 1 Will Surprise You!)
What Robots Don&amp;#39;t Want You To Know About Cats
This Florida Telephone Psychic Didn&amp;#39;t Think Robots Would Take Her Job. She Was Wrong.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Collatz-Sequence" /&gt;&lt;/p&gt;
&lt;h3&gt;Collatz Sequence&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://inventwithpython.com/bigbookpython/project12.html"&gt;Collatz sequence&lt;/a&gt;, also called the 3n + 1 problem, is the simplest impossible math problem. (But don’t worry, the program itself is easy enough for beginners.) From a starting number, n, follow three rules to get the next number in the sequence:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If n is even, the next number n is n / 2.&lt;/li&gt;
&lt;li&gt;If n is odd, the next number n is n * 3 + 1.&lt;/li&gt;
&lt;li&gt;If n is 1, stop. Otherwise, repeat.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is generally thought, but so far not mathematically proven, that every starting number eventually terminates at 1. Write a program that takes a starting number from the user and prints the Collatz sequence from it.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Enter a starting number (greater than 0) or QUIT:
&amp;gt; 26
26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1

--snip--

Enter a starting number (greater than 0) or QUIT:
&amp;gt; 27
27, 82, 41, 124, 62, 31, 94, 47, 142, 71, 214, 107, 322, 161, 484, 242, 121, 364, 182, 91, 274, 137, 412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263, 790, 395, 1186, 593, 1780, 890, 445, 1336, 668, 334, 167, 502, 251, 754, 377, 1132, 566, 283, 850, 425, 1276, 638, 319, 958, 479, 1438, 719, 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644, 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308, 1154, 577, 1732, 866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Conway's-Game-of Life" /&gt;&lt;/p&gt;
&lt;h3&gt;Conway's Game of Life&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project13.html"&gt;Conway’s Game of Life&lt;/a&gt; is a cellular automata simulation that follows simple rules to create interesting patterns. It was invented by mathematician John Conway in 1970 and popularized by Martin Gardner’s “Mathematical Games” column in Scientific American. Today, it’s a favorite among programmers and computer scientists, though it’s more an interesting visualization than a true “game.” The two-dimensional board has a grid of “cells,” each of which follows three simple rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Living cells with two or three neighbors stay alive in the next step of the simulation.&lt;/li&gt;
&lt;li&gt;Dead cells with exactly three neighbors become alive in the next step of the simulation.&lt;/li&gt;
&lt;li&gt;Any other cell dies or stays dead in the next step of the simulation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tragically, Professor Conway passed away of complications from COVID-19 in April 2020. More information about Conway’s Game of Life can be found at https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life, and more information about Martin Gardner at https://en.wikipedia.org/wiki/Martin_Gardner.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/conwaysgameoflifedemo.gif" style="max-width: 100%" alt="A short animation of the patterns of Conway's Game of Life"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a id="Countdown" /&gt;&lt;/p&gt;
&lt;h3&gt;Countdown&lt;/h3&gt;
&lt;p&gt;Create a &lt;a href="https://inventwithpython.com/bigbookpython/project14.html"&gt;digital display that counts down&lt;/a&gt;, using the following "font" of digits made out of underscores and pipe characters:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;   __         __    __          __    __   __    __    __
  |  |    |   __|   __|  |__|  |__   |__     |  |__|  |__|
  |__|    |  |__    __|     |   __|  |__|    |  |__|   __|
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt; __   __       __   __       __   __
|  | |  |  &lt;span class="gs"&gt;*  |  | |  |  *&lt;/span&gt;   __| |__|
|__| |__|  &lt;span class="gs"&gt;*  |__| |__|  *&lt;/span&gt;  |__   __|

Press Ctrl-C to quit.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Deep-Cave-/-Winding-River" /&gt;&lt;/p&gt;
&lt;h3&gt;Deep Cave / Winding River&lt;/h3&gt;
&lt;p&gt;This program is a &lt;a href="https://inventwithpython.com/bigbookpython/project15.html"&gt;text animation of a deep cave&lt;/a&gt; that descends forever into the earth, or a top-down view of a winding river. Although short, this program takes advantage of the scrolling nature of the computer screen to produce an interesting and unending visualization, proof that it doesn’t take much code to produce something fun to watch.&lt;/p&gt;
&lt;p&gt;Your program should:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Track how wide the left bank of the "river" is, along with the width of the "river" itself.&lt;/li&gt;
&lt;li&gt;Randomly increase or decrease the width of the left bank and river.&lt;/li&gt;
&lt;li&gt;Print the left bank using &lt;code&gt;'#'&lt;/code&gt; characters and print the river using &lt;code&gt;' '&lt;/code&gt; space characters.&lt;/li&gt;
&lt;li&gt;Calculate the width of the right bank and print it with &lt;code&gt;'#'&lt;/code&gt; characters.&lt;/li&gt;
&lt;li&gt;Repeat endlessly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;####################          ########################################
####################         #########################################
####################          ########################################
####################          ########################################
#####################          #######################################
######################          ######################################
#####################          #######################################
####################          ########################################
###################          #########################################
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you find this kind of animation interesting, you can find more examples of "scrolling ASCII art" on my other website, &lt;a href="https://scrollart.org"&gt;The Scroll Art Museum&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a id="Diamonds" /&gt;&lt;/p&gt;
&lt;h3&gt;Diamonds&lt;/h3&gt;
&lt;p&gt;This program features a small algorithm for &lt;a href="https://inventwithpython.com/bigbookpython/project16.html"&gt;drawing ASCII-art diamonds of various sizes&lt;/a&gt;. It contains functions for drawing either an outline or filled-in-style diamond of the size you dictate. These functions are good practice for a beginner; try to understand the pattern behind the diamond drawings as they increase in size.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/\
\/

/\
\/

 /\
/  \
\  /
 \/

 /\
//\\
\\//
 \/

  /\
 /  \
/    \
\    /
 \  /
  \/

  /\
 //\\
///\\\
\\\///
 \\//
  \/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Dice-Math" /&gt;&lt;/p&gt;
&lt;h3&gt;Dice Math&lt;/h3&gt;
&lt;p&gt;This &lt;a href="https://inventwithpython.com/bigbookpython/project17.html"&gt;math quiz program&lt;/a&gt; rolls two to six dice whose sides you must add up as quickly as possible. But this program operates as more than just automated flash cards; it draws the faces of the dice to random places on the screen. The ASCII-art aspect adds a fun twist while you practice arithmetic.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;Add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;sides&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;displayed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;screen&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;have&lt;/span&gt;
&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;seconds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;answer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;many&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;possible&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;points&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;each&lt;/span&gt;
&lt;span class="nv"&gt;correct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;answer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;point&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;each&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;incorrect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;answer&lt;/span&gt;.

&lt;span class="nv"&gt;Press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;begin&lt;/span&gt;...
&lt;span class="w"&gt;                                     &lt;/span&gt;&lt;span class="o"&gt;+-------+&lt;/span&gt;
&lt;span class="w"&gt;                                     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;                                     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;                                     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;                                     &lt;/span&gt;&lt;span class="o"&gt;+-------+&lt;/span&gt;


&lt;span class="w"&gt;                                  &lt;/span&gt;&lt;span class="o"&gt;+-------+&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;+-------+&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;+-------+&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="o"&gt;+-------+&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;+-------+&lt;/span&gt;&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="o"&gt;+-------+&lt;/span&gt;
&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;sum&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Dice-Roller" /&gt;&lt;/p&gt;
&lt;h3&gt;Dice Roller&lt;/h3&gt;
&lt;p&gt;Dungeons &amp;amp; Dragons and other tabletop role-playing games &lt;a href="https://inventwithpython.com/bigbookpython/project18.html"&gt;use special dice&lt;/a&gt; that can have 4, 8, 10, 12, or even 20 sides. These games also have a specific notation for indicating which dice to roll. For example, 3d6 means rolling three six-sided dice, while 1d10+2 means rolling one ten-sided die and adding a two-point bonus to the roll. This program simulates this dice rolling, in case you forgot to bring your own. It can also simulate rolling dice that don’t physically exist, such as a 38-sided die.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt; 3d6
7 (3, 2, 2)
&amp;gt; 1d10+2
9 (7, +2)
&amp;gt; 2d38-1
32 (20, 13, -1)
&amp;gt; 100d6
364 (3, 3, 2, 4, 2, 1, 4, 2, 4, 6, 4, 5, 4, 3, 3, 3, 2, 5, 1, 5, 6, 6, 6, 4, 5, 5, 1, 5, 2, 2, 2, 5, 1, 1, 2, 1, 4, 5, 6, 2, 4, 3, 4, 3, 5, 2, 2, 1, 1, 5, 1, 3, 6, 6, 6, 6, 5, 2, 6, 5, 4, 4, 5, 1, 6, 6, 6, 4, 2, 6, 2, 6, 2, 2, 4, 3, 6, 4, 6, 4, 2, 4, 3, 3, 1, 6, 3, 3, 4, 4, 5, 5, 5, 6, 2, 3, 6, 1, 1, 1)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Digital-Clock" /&gt;&lt;/p&gt;
&lt;h3&gt;Digital Clock&lt;/h3&gt;
&lt;p&gt;Create a &lt;a href="https://inventwithpython.com/bigbookpython/project19.html"&gt;digital clock with the current time&lt;/a&gt;, using the following "font" of digits made out of underscores and pipe characters:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;   __         __    __          __    __   __    __    __
  |  |    |   __|   __|  |__|  |__   |__     |  |__|  |__|
  |__|    |  |__    __|     |   __|  |__|    |  |__|   __|
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt; __   __       __   __       __   __
|  | |__|  &lt;span class="gs"&gt;*   __|  __|  *&lt;/span&gt;   __| |__
|__|  __|  &lt;span class="gs"&gt;*   __|  __|  *&lt;/span&gt;   __| |__|

Press Ctrl-C to quit.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Digital-Stream-/-Matrix-Screensaver" /&gt;&lt;/p&gt;
&lt;h3&gt;Digital Stream / Matrix Screensaver&lt;/h3&gt;
&lt;p&gt;This program &lt;a href="https://inventwithpython.com/bigbookpython/project20.html"&gt;mimics the “digital stream” visualization from the science fiction movie The Matrix&lt;/a&gt;. Random beads of binary “rain” stream up from the bottom of the screen, creating a cool, hacker-like visualization.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;                     0                      0
                     0                      0
   1            0    0    1               1 0                             1
   0            0    0    1         0     0 0        0                    0
   0            1    0    0         0     1 0 0      1               0    1
   0            1    0    0         1     011 1      1               0    1 0
   0            1    0    0         0     000 11     0               0  1 1 0
   1     1      0 1  0    1         1     110 10  1  0               1  0 1 0
         1    101 0       0         1     000 11  1  1               11 1 1 1
         0    100 1       0               11  00  0  1               01     0
      1  1    001 1       1               0   1  10  0               10     0
      0  0    010 0       1                   1  11  11              0      0
--snip--
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="DNA-Visualization" /&gt;&lt;/p&gt;
&lt;h3&gt;DNA Visualization&lt;/h3&gt;
&lt;p&gt;Let's create a &lt;a href="https://inventwithpython.com/bigbookpython/project21.html"&gt;DNA animation program&lt;/a&gt;. Deoxyribonucleic acid is a tiny molecule that exists in every cell of our bodies and contains the blueprint for how our bodies grow. It looks like a double helix (a sort of twisted ladder) of pairs of nucleotide molecules: guanine, cytosine, adenine, and thymine. These are represented by the letters G, C, A, and T. DNA is a long molecule; it’s microscopic, but if it were stretched out, its 3 billion base pairs would be 2 meters long! This program is a simple animation of DNA.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;        #G-C#
       #C---G#
      #T-----A#
     #T------A#
    #A------T#
    #G-----C#
     #G---C#
     #C-G#
      ##
     #T-A#
     #C---G#
    #G-----C#
    #G------C#
     #T------A#
      #A-----T#
       #C---G#
        #G-C#
         ##
        #T-A#
       #T---A#
      #A-----T#
--snip--
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Ducklings" /&gt;&lt;/p&gt;
&lt;h3&gt;Ducklings&lt;/h3&gt;
&lt;p&gt;This program creates &lt;a href="https://inventwithpython.com/bigbookpython/project22.html"&gt;a scrolling field of ducklings&lt;/a&gt;. Each duckling has slight variations: they can face left or right and have two different body sizes, four types of eyes, two types of mouths, and three positions for their wings. This gives us 96 different possible variations, which the Ducklings program produces endlessly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;quot; )   =^^)    (``=   (&amp;quot;=  &amp;gt;&amp;quot;)    (&amp;quot;=
(  &amp;gt;)  (  ^)  (v  )  (^ )  ( &amp;gt;)  (v )
 ^ ^    ^ ^    ^ ^    ^^    ^^    ^^
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;                                             =&amp;quot; )
=&amp;quot;)                                          (  v)=&amp;quot;)
( ^)                                          ^ ^ ( v) &amp;gt;&amp;#39;&amp;#39;)
 ^^                                                ^^  (  ^)
                              &amp;gt;&amp;quot;)                       ^ ^
                              ( v)      =^^)
 (&amp;quot;&amp;lt;  (&amp;quot;&amp;lt;                &amp;gt;&amp;quot;)   ^^       (  &amp;gt;)
(^ ) (&amp;lt; )                ( ^)            ^ ^
 ^^   ^^             (&amp;quot;&amp;lt;  ^^                       (``&amp;lt;&amp;gt;^^)
 (^^=               (^ )                          (&amp;lt;  )(  ^)
(v  ) ( &amp;quot;&amp;lt;           ^^                            ^ ^  ^ ^
--snip--
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Etching-Drawer" /&gt;&lt;/p&gt;
&lt;h3&gt;Etching Drawer&lt;/h3&gt;
&lt;p&gt;When you move a pen point around the screen with the WASD keys, the &lt;a href="https://inventwithpython.com/bigbookpython/project23.html"&gt;etching drawer&lt;/a&gt; forms a picture by tracing a continuous line, like the Etch A Sketch toy. Let your artistic side break out and see what images you can create! This program also lets you save your drawings to a text file so you can print them out later. Plus, you can copy and paste the WASD movements of other drawings into this program, like the WASD commands for the Hilbert Curve fractal presented on lines 6 to 14 of the source code.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;WASD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;move&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;H&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;help&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;clear&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;save&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;QUIT&lt;/span&gt;.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DDSAASSDDWDDSDDWWAAWDDDDSDDWDDDDSAASDDSAAAAWAASSSDDWDDDDSAASDDSAAAAWA&lt;/span&gt;
──┐&lt;span class="w"&gt; &lt;/span&gt;┌───┐&lt;span class="w"&gt; &lt;/span&gt;┌───┐
┌─┘&lt;span class="w"&gt; &lt;/span&gt;└─┐&lt;span class="w"&gt; &lt;/span&gt;└─┘&lt;span class="w"&gt; &lt;/span&gt;┌─┘
│&lt;span class="w"&gt; &lt;/span&gt;┌─┐&lt;span class="w"&gt; &lt;/span&gt;│&lt;span class="w"&gt; &lt;/span&gt;┌─┐&lt;span class="w"&gt; &lt;/span&gt;└─┐
└─┘&lt;span class="w"&gt; &lt;/span&gt;└─┘&lt;span class="w"&gt; &lt;/span&gt;│&lt;span class="w"&gt; &lt;/span&gt;└───┘
&lt;span class="w"&gt;        &lt;/span&gt;│&lt;span class="w"&gt; &lt;/span&gt;┌───┐
&lt;span class="w"&gt;        &lt;/span&gt;└─┘&lt;span class="w"&gt; &lt;/span&gt;┌─┘
&lt;span class="w"&gt;         &lt;/span&gt;#┐&lt;span class="w"&gt; &lt;/span&gt;└─┐
&lt;span class="w"&gt;          &lt;/span&gt;└───┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Factor-Finder" /&gt;&lt;/p&gt;
&lt;h3&gt;Factor Finder&lt;/h3&gt;
&lt;p&gt;A &lt;a href="https://inventwithpython.com/bigbookpython/project24.html"&gt;number’s factors&lt;/a&gt; are any two other numbers that, when multiplied with each other, produce the number. For example, 2 × 13 = 26, so 2 and 13 are factors of 26. Also, 1 × 26 = 26, so 1 and 26 are also factors of 26. Therefore, we say that 26 has four factors: 1, 2, 13, and 26.&lt;/p&gt;
&lt;p&gt;If a number only has two factors (1 and itself), we call that a prime number. Otherwise, we call it a composite number. Use the factor finder to discover some new prime numbers! (Hint: Prime numbers always end with an odd number that isn’t 5.) You can also have the computer calculate them with Project 56, “Prime Numbers.”&lt;/p&gt;
&lt;p&gt;The math for this program isn’t too heavy, making it an ideal project for beginners.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Enter a number to factor (or &amp;quot;QUIT&amp;quot; to quit):
&amp;gt; 26
1, 2, 13, 26
Enter a number to factor (or &amp;quot;QUIT&amp;quot; to quit):
&amp;gt; 4352784
1, 2, 3, 4, 6, 8, 12, 16, 24, 29, 48, 53, 58, 59, 87, 106, 116, 118, 159, 174, 177, 212, 232, 236, 318, 348, 354, 424, 464, 472, 636, 696, 708, 848, 944, 1272, 1392, 1416, 1537, 1711, 2544, 2832, 3074, 3127, 3422, 4611, 5133, 6148, 6254, 6844, 9222, 9381, 10266, 12296, 12508, 13688, 18444, 18762, 20532, 24592, 25016, 27376, 36888, 37524, 41064, 50032, 73776, 75048, 82128, 90683, 150096, 181366, 272049, 362732, 544098, 725464, 1088196, 1450928, 2176392, 4352784
Enter a number to factor (or &amp;quot;QUIT&amp;quot; to quit):
&amp;gt; 9787
1, 9787
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Fast-Draw" /&gt;&lt;/p&gt;
&lt;h3&gt;Fast Draw&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project25.html"&gt;This program tests your reaction speed&lt;/a&gt;: press Enter as soon as you see the word DRAW. But careful, though. Press it before DRAW appears, and you lose. Are you the fastest keyboard in the west?&lt;/p&gt;
&lt;p&gt;This program works because pressing Enter during &lt;code&gt;time.sleep()&lt;/code&gt; will cause the next &lt;code&gt;input()&lt;/code&gt; call to return in less than a millisecond or two, which would be impossibly fast for a human. This is how you can tell if the player pressed Enter too early.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;reflexes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;see&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;fastest&lt;/span&gt;
&lt;span class="nv"&gt;draw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;west&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="nv"&gt;When&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;see&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;DRAW&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;.&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;seconds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Enter&lt;/span&gt;.
&lt;span class="nv"&gt;But&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;before&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;DRAW&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;appears&lt;/span&gt;.

&lt;span class="nv"&gt;Press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;begin&lt;/span&gt;...

&lt;span class="nv"&gt;It&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;high&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;noon&lt;/span&gt;...
&lt;span class="nv"&gt;DRAW&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;

&lt;span class="nv"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;took&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;.&lt;span class="mi"&gt;3485&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;seconds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;draw&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Too&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;slow&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;QUIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stop&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;play&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;again&lt;/span&gt;.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;quit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Fibonacci" /&gt;&lt;/p&gt;
&lt;h3&gt;Fibonacci&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://inventwithpython.com/bigbookpython/project26.html"&gt;Fibonacci sequence&lt;/a&gt; is a famous mathematical pattern credited to Italian mathematician Fibonacci in the 13th century (though others had discovered it even earlier). The sequence begins with 0 and 1, and the next number is always the sum of the previous two numbers. The sequence continues forever:&lt;/p&gt;
&lt;p&gt;0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 . . .&lt;/p&gt;
&lt;p&gt;The Fibonacci sequence has applications in music composition, stock market prediction, the pattern of florets in the head of sunflowers, and many other areas. This program lets you calculate the sequence as high as you are willing to go. More information about the Fibonacci sequence can be found at https://en.wikipedia.org/wiki/Fibonacci_number.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Enter the Nth Fibonacci number you wish to
calculate (such as 5, 50, 1000, 9999), or QUIT to quit:
&amp;gt; 50

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Fish-Tank" /&gt;&lt;/p&gt;
&lt;h3&gt;Fish Tank&lt;/h3&gt;
&lt;p&gt;Watch your own &lt;a href="https://inventwithpython.com/bigbookpython/project27.html"&gt;virtual fish in a virtual fish tank&lt;/a&gt;, complete with air bubblers and kelp plants. Each time you run the program, it randomly generates the fish using different fish types and colors. Take a break and enjoy the calm serenity of this software aquarium, or try programming in some virtual sharks to terrorize its inhabitants! You can’t run this program from your IDE or editor. This program uses the bext module and must be run from the Command Prompt or Terminal in order to display correctly. More information about the bext module can be found at https://pypi.org/project/bext/.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;                     o          (       (
                                 )       )
                      o         (        )             }-[[[*&amp;gt;
                                 )       )
 &amp;lt;==-&amp;lt;               o          (o[[&amp;lt;    )
                                (        )            &amp;lt;//&amp;lt;
                    &amp;lt;=^_.-._.-  (       (
                                 )       )
                                 )      (                      &amp;lt;//&amp;lt;
                                 )      (
                                (        )
                                 )      (
                                (        )
                                 )       )
                                 )       )
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Flooder" /&gt;&lt;/p&gt;
&lt;h3&gt;Flooder&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project28.html"&gt;Flooder&lt;/a&gt; is a colorful game where a player tries to fill the board with a single color by changing the color of the tile in the upper-left corner. This new color spreads to all neighboring tiles that matched the original color. It’s similar to the Flood It mobile game. This program also has a colorblind mode, which uses shapes instead of flat colored tiles. It relies on the &lt;a href="https://en.wikipedia.org/wiki/Flood_fill"&gt;recursive flood fill algorithm&lt;/a&gt; to paint the board and works similarly to the “paint bucket” or “fill” tool in many painting applications.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;┌────────────────┐
&amp;gt;♥♥♥♥♥♥♥♥♥♥♥♥♥♥▲▲│
│♥♥♥♥♥♥♥♥♥♦♦♥♥♦♦♦│
│♥♥♥♥♥♥♥♥♥♥♥♦♦♦♦♦│
│♥♥♥♥♥♥♥♥♥♥♥♦♦▲♦♦│
│♥♥♥♥♥♥♥♥♥♣♣♣♣♣♠●│
│♥♥♥♥♦♦♥♥♥♥♠♣♣♣♣♠│
│▲▲▲♥♥♥♥♥♥♥♥♥♦♠♥♠│
│▲▲♣♣●●●♠♥♥●♣♣♣♦♦│
│♣♣♣♠♠♠♠♦♠▲▲▲♠♠▲▲│
│●●♥●●●●●♣▲♠♠♠♥♣♣│
│♥♥♣♣♣♣♣♣▲♥♥♣♦♦♥♥│
│♣♣♦♠♥♥♦♦▲▲♠♠♠♠♠♥│
│▲▲♠●♥♥♥♥♠♠♣●●▲♠♠│
│♥♥●●♣♣♦♣♣♣♥♥♥♥▲▲│
└────────────────┘
Moves left: 12
Choose one of (H)eart, (T)riangle, (D)iamond, (B)all, (C)lub, (S)pade, or QUIT:
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Forest-Fire-Sim" /&gt;&lt;/p&gt;
&lt;h3&gt;Forest Fire Sim&lt;/h3&gt;
&lt;p&gt;This &lt;a href="https://inventwithpython.com/bigbookpython/project29.html"&gt;simulation shows a forest whose trees are constantly growing and then being burned down&lt;/a&gt;. On each step of the simulation, there is a 1 percent chance that a blank space grows into a tree and a 1 percent chance that a tree is struck by lightning and burns. Fires will spread to adjacent trees, so a densely packed forest is more likely to suffer a larger fire than a sparsely packed one. This simulation was inspired by Nicky Case’s Emoji Sim at &lt;a href="https://ncase.me/simulating/model/"&gt;https://ncase.me/simulating/model/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;        AA A  A   A   A    A   WA     AA    A      A               A      AA AA
A   A        A   A A           A       AA AAA    A     A   A  A               A
                    A       A  A    A   A  A A    A   WA      AA A    A      A
                A A      A        AAA          A     WW  A A   A   A A    W  A
   A                  AA   A          A        A  A              A   A
  A    A                            A          A A   A   A         A    W    A
     A      A  A  A      A    A           A    A A  A  AA  A WAA A           A
    A  A             A           A    A   A   A           AAAA     W    A
A   A            A                    A      A             A A      A   A A   A
       A          A   AA      A      A  A  A   A     AA  AA    AA
           AA      A AA           A     A A A        W       A A      A A A A
A          A  A     AA A    W  AA AA    A                 A   AA  A  A     A
          A  A  A AAA  A     W  A  A                 WA A     AA A     A  A
 A               A  AA        A    AA  A       A       A A  AA A   A        A A
 A   A            A    AA        A    A  A     A     A    A      A        A
          A    AA      A A       A    A  A             A     A    A     A   A
      AAA    A   A     AA   A             A  A           A        A    AA  A  A
         A        A    A    A                A        A              W   A    A
                        A A             A                                  A
          A  A    AA  A        A A     A A A       A   A          A
                              A  A             A      A A             A    A AA
    A      A     A      A      A A      A A    AA       A
Grow chance: 1.0%  Lightning chance: 1.0%  Press Ctrl-C to quit.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Four-in-a Row" /&gt;&lt;/p&gt;
&lt;h3&gt;Four in a Row&lt;/h3&gt;
&lt;p&gt;In this &lt;a href="https://inventwithpython.com/bigbookpython/project30.html"&gt;classic tile-dropping board game&lt;/a&gt; for two players, you must try to get four of your tiles in a row horizontally, vertically, or diagonally, while preventing your opponent from doing the same. This program is similar to Connect Four.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Player X, enter a column or QUIT:
&amp;gt; 3

     1234567
    +-------+
    |.......|
    |.......|
    |.......|
    |.......|
    |.......|
    |..X....|
    +-------+
Player O, enter a column or QUIT:
&amp;gt; 5
--snip--
Player O, enter a column or QUIT:
&amp;gt; 4

     1234567
    +-------+
    |.......|
    |.......|
    |XXX.XO.|
    |OOOOXO.|
    |OOOXOX.|
    |OXXXOXX|
    +-------+
Player O has won!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Guess-the-Number" /&gt;&lt;/p&gt;
&lt;h3&gt;Guess the Number&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project31.html"&gt;Guess the Number&lt;/a&gt; is a game for beginners to practice basic programming techniques. In this game, the computer thinks of a random number between 1 and 100. The player has 10 chances to guess the number. After each guess, the computer tells the player if it was too high or too low.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;I am thinking of a number between 1 and 100.
You have 10 guesses left. Take a guess.
&amp;gt; 50
Your guess is too high.
You have 9 guesses left. Take a guess.
&amp;gt; 25
Your guess is too low.
--snip--
You have 5 guesses left. Take a guess.
&amp;gt; 42
Yay! You guessed my number!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Gullible" /&gt;&lt;/p&gt;
&lt;h3&gt;Gullible&lt;/h3&gt;
&lt;p&gt;In this short and simple program, you can learn the secret and subtle art of &lt;a href="https://inventwithpython.com/bigbookpython/project32.html"&gt;keeping a gullible person busy for hours&lt;/a&gt;. I won’t spoil the punch line here. Copy the code and run it for yourself. This project is great for beginners, whether you’re smart or... not so smart.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;know&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;how&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;gullible&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;busy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;hours&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;y&lt;/span&gt;
&lt;span class="k"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;know&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;how&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;gullible&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;busy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;hours&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;y&lt;/span&gt;
&lt;span class="k"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;know&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;how&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;gullible&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;busy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;hours&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;yes&lt;/span&gt;
&lt;span class="k"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;know&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;how&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;gullible&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;busy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;hours&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;YES&lt;/span&gt;
&lt;span class="k"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;know&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;how&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;gullible&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;busy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;hours&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TELL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;KEEP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GULLIBLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PERSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;BUSY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HOURS&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;TELL ME HOW TO KEEP A GULLIBLE PERSON BUSY FOR HOURS&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;valid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;yes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;response&lt;/span&gt;.
&lt;span class="k"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;know&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;how&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;gullible&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;busy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;hours&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;y&lt;/span&gt;
&lt;span class="k"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;know&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;how&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;gullible&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;busy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;hours&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;y&lt;/span&gt;
&lt;span class="k"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;know&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;how&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;gullible&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;busy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;hours&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt;
&lt;span class="nv"&gt;Thank&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;day&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Hacking-Minigame" /&gt;&lt;/p&gt;
&lt;h3&gt;Hacking Minigame&lt;/h3&gt;
&lt;p&gt;In this game, &lt;a href="https://inventwithpython.com/bigbookpython/project33.html"&gt;the player must hack a computer by guessing a seven-letter word&lt;/a&gt; used as the secret password. The computer’s memory banks display the possible words, and the player is given hints as to how close each guess was. For example, if the secret password is MONITOR but the player guessed CONTAIN, they are given the hint that two out of seven letters were correct, because both MONITOR and CONTAIN have the letter O and N as their second and third letter. This game is similar to Project 1, “Bagels,” and the hacking minigame in the Fallout series of video games.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/projects/sevenletterwords.txt"&gt;Here is a text file of seven letter words.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Hacking&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Minigame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Al&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Sweigart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;al&lt;/span&gt;&lt;span class="nv"&gt;@inventwithpython&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;Find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;computer&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="mh"&gt;0x1150&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;$]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="o"&gt;|~~&lt;/span&gt;&lt;span class="n"&gt;RESOLVE&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mh"&gt;0x1250&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;+&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;&lt;span class="n"&gt;CHICKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;
&lt;span class="mh"&gt;0x1160&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;}@&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;|&amp;lt;|&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mh"&gt;0x1260&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;})?#@#ADDRESS&lt;/span&gt;
&lt;span class="n"&gt;0x1170  _;)&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="n"&gt;#?&amp;lt;&amp;amp;~$~+&amp;amp;}}    0x1270  ,#=)&amp;gt;{-;/DESPITE&lt;/span&gt;
&lt;span class="n"&gt;0x1180  %[!&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="n"&gt;REFUGEE&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mh"&gt;0x1280&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;}!&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;DISPLAY&lt;/span&gt;&lt;span class="o"&gt;%%/&lt;/span&gt;
&lt;span class="mh"&gt;0x1190&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="o"&gt;^%&lt;/span&gt;&lt;span class="err"&gt;[@}&lt;/span&gt;&lt;span class="o"&gt;^&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="err"&gt;@$&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mh"&gt;0x1290&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="o"&gt;*%&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mh"&gt;0x11a0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;&lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;PENALTY&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;&lt;span class="o"&gt;-=&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mh"&gt;0x12a0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;#IMPROVE&lt;/span&gt;&lt;span class="err"&gt;@$&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tries&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resolve&lt;/span&gt;
&lt;span class="n"&gt;Access&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Denied&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;correct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tries&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;improve&lt;/span&gt;
&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Hangman-&amp;-Guillotine" /&gt;&lt;/p&gt;
&lt;h3&gt;Hangman &amp;amp; Guillotine&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project34.html"&gt;This classic word game&lt;/a&gt; has the player guess the letters to a secret word. For each incorrect letter, another part of the hangman is drawn. Try to guess the complete word before the hangman completes. The secret words in this version are all animals like RABBIT and PIGEON, but you can replace these with your own set of words.&lt;/p&gt;
&lt;p&gt;The HANGMAN_PICS variable contains ASCII-art strings of each step of the hangman’s noose:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt; +--+     +--+     +--+     +--+     +--+     +--+     +--+
 |  |     |  |     |  |     |  |     |  |     |  |     |  |
    |     O  |     O  |     O  |     O  |     O  |     O  |
    |        |     |  |    /|  |    /|\ |    /|\ |    /|\ |
    |        |        |        |        |    /   |    / \ |
    |        |        |        |        |        |        |
=====    =====    =====    =====    =====    =====    =====
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For a French twist on the game, you can replace the strings in the HANGMAN_PICS variable with the following strings depicting a guillotine:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;|        |   |    |===|    |===|    |===|    |===|    |===|
|        |   |    |   |    |   |    |   |    || /|    || /|
|        |   |    |   |    |   |    |   |    ||/ |    ||/ |
|        |   |    |   |    |   |    |   |    |   |    |   |
|        |   |    |   |    |   |    |   |    |   |    |   |
|        |   |    |   |    |   |    |/-\|    |/-\|    |/-\|
|        |   |    |   |    |\ /|    |\ /|    |\ /|    |\O/|
|===     |===|    |===|    |===|    |===|    |===|    |===|
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt; +--+
 |  |
 O  |
/|  |
    |
    |
=====
The category is: Animals

Missed letters: A I S
O T T E _
Guess a letter.
&lt;span class="k"&gt;&amp;gt; &lt;/span&gt;&lt;span class="ge"&gt;r&lt;/span&gt;
Yes! The secret word is: OTTER
You have won!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Hex-Grid" /&gt;&lt;/p&gt;
&lt;h3&gt;Hex Grid&lt;/h3&gt;
&lt;p&gt;This short program produces &lt;a href="https://inventwithpython.com/bigbookpython/project35.html"&gt;a tessellated image of a hexagonal grid&lt;/a&gt;, similar to chicken wire. It shows that you don’t need a lot of code to make something interesting. A slightly more complicated variation of this program is Project 65, “Shining Carpet.”&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_
\_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_
\_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_
\_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_
\_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_
\_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For practice, try re-creating this program with larger hexagon grids, such as the following patterns:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt; /   \     /   \     /   \     /   \     /   \     /   \     /   \
/     \___/     \___/     \___/     \___/     \___/     \___/     \
\     /   \     /   \     /   \     /   \     /   \     /   \     /
 \___/     \___/     \___/     \___/     \___/     \___/     \___/
 /   \     /   \     /   \     /   \     /   \     /   \     /   \
/     \___/     \___/     \___/     \___/     \___/     \___/     \

  /     \         /     \         /     \         /     \
 /       \       /       \       /       \       /       \
/         \_____/         \_____/         \_____/         \_____
\         /     \         /     \         /     \         /
 \       /       \       /       \       /       \       /
  \_____/         \_____/         \_____/         \_____/
  /     \         /     \         /     \         /     \
 /       \       /       \       /       \       /       \
/         \_____/         \_____/         \_____/         \_____
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Hourglass" /&gt;&lt;/p&gt;
&lt;h3&gt;Hourglass&lt;/h3&gt;
&lt;p&gt;This visualization program has &lt;a href="https://inventwithpython.com/bigbookpython/project36.html"&gt;a rough physics engine that simulates sand falling&lt;/a&gt; through the small aperture of an hourglass. The sand piles up in the bottom half of the hourglass; then the hourglass is turned over so the process repeats.&lt;/p&gt;
&lt;p&gt;Each piece of sand moves down (and to the left or right) if the space underneath it is empty.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;                  ███████████████████
                  █                 █
                  █                 █
                  █░░           ░░░░█
                   █░░        ░░░░░█
                    █░░░     ░░░░░█
                     █░░░ ░░░░░░░█
                      █░░░░░░░░░█
                       █░░ ░░░░█
                        █░░░░░█
                         █░░░█
                          █░█
                         █ ░ █
                        █     █
                       █   ░   █
                      █         █
                     █     ░     █
                    █             █
                   █     ░ ░       █
                  █      ░░░░       █
                  █      ░░░░░░     █
                  █    ░░░░░░░░░ ░  █
                  ███████████████████
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Hungry-Robots" /&gt;&lt;/p&gt;
&lt;h3&gt;Hungry Robots&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project37.html"&gt;You are trapped in a maze with hungry robots!&lt;/a&gt; You don’t know why robots need to eat, but you don’t want to find out. The robots are badly programmed and will move directly toward you, even if blocked by walls. You must trick the robots into crashing into each other (or dead robots) without being caught.&lt;/p&gt;
&lt;p&gt;You are represented by an &lt;code&gt;@&lt;/code&gt; while the robots are &lt;code&gt;R&lt;/code&gt;. Destroyed robots are &lt;code&gt;X&lt;/code&gt;. For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░   ░ R             R ░  ░             ░
░ ░    ░░░   R░                    ░░  ░
░     ░ ░    ░ ░  ░         ░  ░░░     ░
░    R░   ░    ░      ░░   ░░     ░    ░
░ ░░  ░     ░ ░░░    ░           ░     ░
░ ░░    ░   RX░░░  ░  ░  ░      ░      ░
░          ░ R     R        R ░      ░ ░
░    ░   ░            ░        ░   R ░ ░
░ ░ R       R     ░   R ░   R          ░
░   ░  ░     ░       ░  ░       ░   ░  ░
░  @            ░          ░    R░░░ ░ ░
░   ░  ░░      ░░                 ░    ░
░  ░   ░░  ░            ░     R       ░░
░░X          ░  ░        ░ R ░░RR  ░ R ░
░RR R       R ░    ░          ░       R░
░   ░░  RRR   R                        ░
░           ░░R     ░                  ░
░      R  ░ ░                     ░    ░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
(T)eleports remaining: 2
                    (Q) (W) ( )
                    (A) (S) (D)
Enter move or QUIT: (Z) (X) ( )
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="J'Accuse!" /&gt;&lt;/p&gt;
&lt;h3&gt;J'Accuse!&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project38.html"&gt;You are the world-famous detective Mathilde Camus.&lt;/a&gt; Zophie the cat has gone missing, and you must sift through the clues. Suspects either always tell lies or always tell the truth. Will you find Zophie the cat in time and accuse the guilty party?&lt;/p&gt;
&lt;p&gt;In this game, you take a taxi to different locations around the city. At each location is a suspect and an item. You can ask suspects about other suspects and items, compare their answers with your own exploration notes, and determine if they are lying or telling the truth. Some will know who has catnapped Zophie (or where she is, or what item is found at the location of the kidnapper), but you must determine if you can believe them. You have five minutes to find the criminal but will lose if you make three wrong accusations. This game is inspired by &lt;a href="https://homestarrunner.com/wheresanegg"&gt;Homestar Runner’s “Where’s an Egg?” game&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nc"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;left&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sec&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TAXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;Where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;LBINO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ALLIGATOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PIT&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;OWLING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ALLEY&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;ITY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HALL&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;UCK&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;POND&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;IPSTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CAFE&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;O&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;LD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BARN&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;NIVERSITY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LIBRARY&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;IDEO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GAME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MUSEUM&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;OO&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;

&lt;span class="nc"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;left&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sec&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ALBINO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ALLIGATOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PIT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;ESPRESSA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TOFFEEPOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ONE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;COWBOY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BOOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;here&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;J&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;J&amp;#39;ACCUSE!&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accusations&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;they&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;know&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ZOPHIE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;THE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CAT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;Go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;back&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TAXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;about&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ESPRESSA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TOFFEEPOT&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;about&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ONE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;COWBOY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BOOT&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;They&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;give&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;clue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;DUKE HAUTDOG&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;Press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Langton's-Ant" /&gt;&lt;/p&gt;
&lt;h3&gt;Langton's Ant&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project39.html"&gt;Langton’s Ant&lt;/a&gt; is a cellular automata simulation on a two-dimensional grid, similar to Project 13, “Conway’s Game of Life.” In the simulation, an “ant” begins on a square that is one of two colors. If the space is the first color, the ant switches it to the second color, turns 90 degrees to the right, and moves forward one space. If the space is the second color, the ant switches it to the first color, turns 90 degrees to the left, and moves forward one space. Despite the very simple set of rules, the simulation displays complex emergent behavior. Simulations can feature multiple ants in the same space, causing interesting interactions when they cross paths with each other. Langton’s Ant was invented by computer scientist Chris Langton in 1986. More information about Langton’s Ant can be found at https://en.wikipedia.org/wiki/Langton%27s_ant.&lt;/p&gt;
&lt;p&gt;&lt;a id="Leetspeak" /&gt;&lt;/p&gt;
&lt;h3&gt;Leetspeak&lt;/h3&gt;
&lt;p&gt;There’s no better way to demonstrate your mad hacker skills than by &lt;a href="https://inventwithpython.com/bigbookpython/project40.html"&gt;replacing letters in your text with numbers&lt;/a&gt;: m4d h4x0r 5k1llz!!! This word program automatically converts plain English into leetspeak, the coolest way to talk online. Or at least it was in 1993.&lt;/p&gt;
&lt;p&gt;It takes a while to get used to, but with some practice, you’ll eventually be able to read leetspeak fluently. For example, 1t +@]&amp;lt;3s 4 w|-|1le +o g37 |&lt;em&gt;|s3|) 70, b|&lt;/em&gt;|+ y0u (an 3\/3nt|&lt;em&gt;|/-\lly r3a|) l33t$peak phl|&lt;/em&gt;|3n+ly. Leetspeak may be hard to read at first, but the program itself is simple and good for beginners. More information about leetspeak can be found at https://en.wikipedia.org/wiki/Leet.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;leet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;am&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;leet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hacker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fear&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mad&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;skills&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;were&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;over&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;decades&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ago&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;@m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;l33t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;er&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ph3&lt;/span&gt;&lt;span class="nv"&gt;@r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m4&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ll&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;h3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;w3r3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;ver&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tw0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ad3&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;g0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Copied&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;leetspeak&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Lucky-Stars" /&gt;&lt;/p&gt;
&lt;h3&gt;Lucky Stars&lt;/h3&gt;
&lt;p&gt;In this &lt;a href="https://inventwithpython.com/bigbookpython/project41.html"&gt;push-your-luck game&lt;/a&gt;, you roll dice to collect stars. The more you roll, the more stars you can get, but if you get three skulls you lose everything! This quick multiplayer game can support as many players as you want, making it ideal for parties. Inspired by the &lt;a href="https://en.wikipedia.org/wiki/Zombie_Dice"&gt;Zombie Dice game&lt;/a&gt; from Steve Jackson Games.&lt;/p&gt;
&lt;p&gt;On your turn, you pull three random dice from the dice cup and roll them. You can roll Stars, Skulls, and Question Marks. If you end your turn, you get one point per Star. If you choose to roll again, you keep the Question Marks and pull new dice to replace the Stars and Skulls. If you collect three Skulls, you lose all your Stars and end your turn.&lt;/p&gt;
&lt;p&gt;When a player gets 13 points, everyone else gets one more turn before the game ends. Whoever has the most points wins.&lt;/p&gt;
&lt;p&gt;There are six gold dice, four silver dice, and three bronze dice in the cup. Gold dice have more Stars, bronze dice have more Skulls, and silver is even.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;SCORES&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Alice&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Bob&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Alice&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s turn.&lt;/span&gt;

&lt;span class="s1"&gt;+-----------+ +-----------+ +-----------+&lt;/span&gt;
&lt;span class="s1"&gt;|           | |     .     | |           |&lt;/span&gt;
&lt;span class="s1"&gt;|           | |    ,O,    | |           |&lt;/span&gt;
&lt;span class="s1"&gt;|     ?     | | &amp;#39;&lt;/span&gt;&lt;span class="n"&gt;ooOOOoo&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; | |     ?     |&lt;/span&gt;
&lt;span class="s1"&gt;|           | |   `OOO`   | |           |&lt;/span&gt;
&lt;span class="s1"&gt;|           | |   O&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;O   | |           |&lt;/span&gt;
&lt;span class="s1"&gt;+-----------+ +-----------+ +-----------+&lt;/span&gt;
&lt;span class="s1"&gt;     GOLD          GOLD         BRONZE&lt;/span&gt;
&lt;span class="s1"&gt;Stars collected: 1   Skulls collected: 0&lt;/span&gt;
&lt;span class="s1"&gt;Do you want to roll again? Y/N&lt;/span&gt;
&lt;span class="s1"&gt;&amp;gt; y&lt;/span&gt;

&lt;span class="s1"&gt;+-----------+ +-----------+ +-----------+&lt;/span&gt;
&lt;span class="s1"&gt;|     .     | |    ___    | |           |&lt;/span&gt;
&lt;span class="s1"&gt;|    ,O,    | |   /   &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;   | |           |&lt;/span&gt;
&lt;span class="s1"&gt;| &amp;#39;&lt;/span&gt;&lt;span class="n"&gt;ooOOOoo&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; | |  |() ()|  | |     ?     |&lt;/span&gt;
&lt;span class="s1"&gt;|   `OOO`   | |   &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s1"&gt; ^ /   | |           |&lt;/span&gt;
&lt;span class="s1"&gt;|   O&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;O   | |    VVV    | |           |&lt;/span&gt;
&lt;span class="s1"&gt;+-----------+ +-----------+ +-----------+&lt;/span&gt;
&lt;span class="s1"&gt;     GOLD         BRONZE        BRONZE&lt;/span&gt;
&lt;span class="s1"&gt;Stars collected: 2   Skulls collected: 1&lt;/span&gt;
&lt;span class="s1"&gt;Do you want to roll again? Y/N&lt;/span&gt;
&lt;span class="s1"&gt;--snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Magic-Fortune-Ball" /&gt;&lt;/p&gt;
&lt;h3&gt;Magic Fortune Ball&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://inventwithpython.com/bigbookpython/project42.html"&gt;Magic Fortune Ball&lt;/a&gt; can predict the future and answer your yes/no questions with 100 percent accuracy using the power of Python’s random number module. This program is similar to a Magic 8 Ball toy, except you don’t have to shake it. It also features a function for slowly printing text strings with spaces in between each character, giving the messages a spooky, mysterious effect.&lt;/p&gt;
&lt;p&gt;Most of the code is dedicated to setting the eerie atmosphere. The program itself simply selects a message to display in response to a random number.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;M A G i C   F O R T U N E   B A L L ,   B Y   A L   S W E i G A R T

A S K   M E   Y O U R   Y E S / N O   Q U E S T i O N .

&amp;gt; Isn&amp;#39;t fortune telling just a scam to trick money out of gullible people?
L E T   M E   T H i N K   O N   T H i S . . .

. . . . . . . .

i   H A V E   A N   A N S W E R . . .

A F F i R M A T i V E
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Mancala" /&gt;&lt;/p&gt;
&lt;h3&gt;Mancala&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://inventwithpython.com/bigbookpython/project43.html"&gt;board game Mancala&lt;/a&gt; is at least 2,000 years old, making it almost as old as Project 63, “Royal Game of Ur.” It is a “seed-sowing” game in which two players select pockets of seeds to spread across the other pockets on the board while trying to collect as many in their store as possible. There are several variants of this game across different cultures. The name comes from the Arab word naqala, meaning “to move.”&lt;/p&gt;
&lt;p&gt;To play, grab the seeds from a pit on your side of the board and place one in each subsequent pit, going counterclockwise and skipping your opponent’s store. If your last seed lands in an empty pit of yours, move the opposite pit’s seeds into that pit. If the last placed seed is in your store, you get a free turn.&lt;/p&gt;
&lt;p&gt;The game ends when all of one player’s pits are empty. The other player claims the remaining seeds for their store, and the winner is the one with the most seeds. More information about Mancala and its variants can be found at https://en.wikipedia.org/wiki/Mancala.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;+------+------+--&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;----+------+------+------+&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;J&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;
&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;+------+------+------+------+------+------+&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;
&lt;span class="n"&gt;O&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;O&lt;/span&gt;
&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;
&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;
&lt;span class="o"&gt;+------+------+------+-&lt;/span&gt;&lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;-----+------+------+&lt;/span&gt;


&lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;choose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;QUIT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;

&lt;span class="o"&gt;+------+------+--&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;----+------+------+------+&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;J&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;
&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;+------+------+------+------+------+------+&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;
&lt;span class="n"&gt;O&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;O&lt;/span&gt;
&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;
&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;
&lt;span class="o"&gt;+------+------+------+-&lt;/span&gt;&lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;-----+------+------+&lt;/span&gt;
&lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;choose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;QUIT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Maze-Runner-2D" /&gt;&lt;/p&gt;
&lt;h3&gt;Maze Runner 2D&lt;/h3&gt;
&lt;p&gt;This &lt;a href="https://inventwithpython.com/bigbookpython/project44.html"&gt;two-dimensional maze runner&lt;/a&gt; shows the player a top-down, bird’s-eye view of a maze file you create in a text editor, such as the IDE you use to write your .py files. Using the WASD keys, the player can move up, left, down, and right, respectively, to navigate the @ symbol toward the exit marked by the X character.&lt;/p&gt;
&lt;p&gt;To make a maze file, open a text editor and create the following pattern. Don’t type the numbers along the top and left side; they are only there for reference:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt; 123456789
1#########
2#S# # # #
3#########
4# # # # #
5#########
6# # # # #
7#########
8# # # #E#
9#########
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The # characters represent walls, the S marks the start, and the E marks the exit. The # characters in bold represent walls that you can remove to form your maze. Don’t remove the walls at odd-numbered columns and odd-numbered rows, and don’t remove the borders of the maze. When you’re done, save the maze as a .txt (text) file. It could look something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;#########
#S    # #
# ### # #
# #   # #
# ##### #
&amp;lt;a id=&amp;quot;#-#-#&amp;quot; /&amp;gt;
#   #   #
### # # #
#     #E#
#########
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Of course, this is a simple maze. You can make maze files of any size as long as they have an odd number of rows and columns. Be sure it’ll still fit on the screen, though! You can also download maze files from &lt;a href="https://invpy.com/mazes/"&gt;https://invpy.com/mazes/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Enter the filename of the maze (or LIST or QUIT):
&amp;gt; maze65x11s1.txt
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░@░     ░       ░               ░                       ░   ░   ░
░ ░░░░░ ░ ░░░ ░ ░ ░░░░░░░ ░░░░░ ░░░░░░░░░░░░░░░░░░░░░ ░░░ ░ ░ ░ ░
░ ░   ░     ░ ░ ░   ░   ░     ░   ░           ░     ░   ░ ░   ░ ░
░ ░ ░ ░░░░░ ░ ░ ░░░░░ ░ ░░░░░ ░░░ ░ ░░░░░░░░░ ░ ░░░ ░░░ ░ ░░░░░ ░
░   ░     ░ ░ ░   ░   ░       ░ ░   ░       ░ ░ ░ ░   ░   ░     ░
░░░░░░░░░ ░░░ ░░░ ░ ░░░░░░░░░░░ ░░░░░ ░ ░░░░░ ░ ░ ░░░ ░░░░░ ░░░░░
░ ░     ░ ░   ░ ░ ░ ░           ░     ░       ░ ░     ░   ░     ░
░ ░ ░ ░░░ ░ ░░░ ░ ░ ░ ░░░░░░░░░░░ ░░░░░░░░░░░░░ ░ ░░░░░ ░ ░░░░░ ░
░   ░       ░       ░                           ░       ░      X░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
                           W
Enter direction, or QUIT: ASD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Maze-Runner-3D" /&gt;&lt;/p&gt;
&lt;h3&gt;Maze Runner 3D&lt;/h3&gt;
&lt;p&gt;This &lt;a href="https://inventwithpython.com/bigbookpython/project45.html"&gt;three-dimensional maze runner&lt;/a&gt; provides the player with a first-person view from inside a maze. Try to find your way out! You can generate maze files by following the instructions in Project 44, “Maze Runner 2D,” or by downloading maze files from &lt;a href="https://invpy.com/mazes/"&gt;https://invpy.com/mazes/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Enter the filename of the maze (or LIST or QUIT):
&lt;span class="k"&gt;&amp;gt; &lt;/span&gt;&lt;span class="ge"&gt;maze75x11s1.txt&lt;/span&gt;
░░░░░░░░░░░░░░░░░░░
░  \           /  ░
░   \_________/   ░
░    |       |    ░
░    |       |    ░
░    |       |    ░
░    |       |    ░
░    |       |    ░
░    |       |    ░
░    |       |    ░
░    |       |    ░
░    |_______|    ░
░   /         \   ░
░  /           \  ░
░░░░░░░░░░░░░░░░░░░
Location (1, 1)  Direction: NORTH
                   (W)
Enter direction: (A) (D)  or QUIT.
&lt;span class="k"&gt;&amp;gt; &lt;/span&gt;&lt;span class="ge"&gt;d&lt;/span&gt;
░░░░░░░░░░░░░░░░░░░
░  \              ░
░   \_____________░
░    |            ░
░    |            ░
░    |            ░
░    |            ░
░    |            ░
░    |            ░
░    |            ░
░    |            ░
░    |____________░
░   /             ░
░  /              ░
░░░░░░░░░░░░░░░░░░░
Location (1, 1)  Direction: EAST
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Million-Dice-Statistics" /&gt;&lt;/p&gt;
&lt;h3&gt;Million Dice Statistics&lt;/h3&gt;
&lt;p&gt;When you roll two six-sided dice, there’s a 17 percent chance you’ll roll a 7. That’s much better than the odds of rolling a 2: just 3 percent. That’s because there’s only one combination of dice rolls that gives you 2 (the one that occurs when both dice roll a 1), but many combinations add up to seven: 1 and 6, 2 and 5, 3 and 4, and so on.&lt;/p&gt;
&lt;p&gt;But what about when you roll three dice? Or four? Or 1,000? You could mathematically calculate the theoretical probabilities, &lt;a href="https://inventwithpython.com/bigbookpython/project46.html"&gt;or you can have the computer roll a number of dice one million times&lt;/a&gt; to empirically figure them out. This program takes that latter approach. In this program, you tell the computer to roll N dice one million times and remember the results. It then displays the percentage chance of each sum.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Enter how many six-sided dice you want to roll:
&amp;gt; 2
Simulating 1,000,000 rolls of 2 dice...
36.2% done...
73.4% done...
TOTAL - ROLLS - PERCENTAGE
  2 - 27590 rolls - 2.8%
  3 - 55730 rolls - 5.6%
  4 - 83517 rolls - 8.4%
  5 - 111526 rolls - 11.2%
  6 - 139015 rolls - 13.9%
  7 - 166327 rolls - 16.6%
  8 - 139477 rolls - 13.9%
  9 - 110268 rolls - 11.0%
  10 - 83272 rolls - 8.3%
  11 - 55255 rolls - 5.5%
  12 - 28023 rolls - 2.8%
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Mondrian-Art-Generator" /&gt;&lt;/p&gt;
&lt;h3&gt;Mondrian Art Generator&lt;/h3&gt;
&lt;p&gt;Piet Mondrian was a 20th-century Dutch painter and one of the founders of neoplasticism, an abstract art movement. His most iconic paintings relied on blocks of primary colors (blue, yellow, red), black, and white. Using a minimalist approach, he separated these colors with horizontal and vertical elements.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project47.html"&gt;This program generates random paintings that follow Mondrian’s style.&lt;/a&gt; You can find out more about Piet Mondrian at &lt;a href="https://en.wikipedia.org/wiki/Piet_Mondrian"&gt;https://en.wikipedia.org/wiki/Piet_Mondrian&lt;/a&gt;. &lt;a href="https://github.com/asweigart/mondrian_art_generator"&gt;An implementation of this algorithm with examples is in this git repo.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/mondrian_art_generator/refs/heads/master/mondrian11.png" style="max-width: 100%" alt="Mondrian-like art, with several rectangles, mostly white, but only using red, yellow, blue, black, and white."&gt;&lt;/p&gt;
&lt;p&gt;&lt;a id="Monty-Hall" /&gt;&lt;/p&gt;
&lt;h3&gt;Monty Hall&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://inventwithpython.com/bigbookpython/project48.html"&gt;Monty Hall Problem&lt;/a&gt; illustrates a surprising fact of probability. The problem is loosely based on the old game show Let’s Make a Deal and its host, Monty Hall. In the Monty Hall Problem, you can pick one of three doors. Behind one door is a prize: a new car. Each of the other two doors opens onto a worthless goat. Say you pick Door #1. Before the door you choose is opened, the host opens another door (either #2 or #3), which leads to a goat. You can choose to either open the door you originally picked or switch to the other unopened door.&lt;/p&gt;
&lt;p&gt;It may seem like it doesn’t matter if you switch or not, but your odds do improve if you switch doors! This program demonstrates the Monty Hall problem by letting you do repeated experiments.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;+------+  +------+  +------+
|      |  |      |  |      |
|   1  |  |   2  |  |   3  |
|      |  |      |  |      |
|      |  |      |  |      |
|      |  |      |  |      |
+------+  +------+  +------+
Pick a door 1, 2, or 3 (or &amp;quot;quit&amp;quot; to stop):
&lt;span class="k"&gt;&amp;gt; &lt;/span&gt;&lt;span class="ge"&gt;1&lt;/span&gt;

+------+  +------+  +------+
|      |  |      |  |  ((  |
|   1  |  |   2  |  |  oo  |
|      |  |      |  | /_/|_|
|      |  |      |  |    | |
|      |  |      |  |GOAT|||
+------+  +------+  +------+
Door 3 contains a goat!
Do you want to swap doors? Y/N
&lt;span class="k"&gt;&amp;gt; &lt;/span&gt;&lt;span class="ge"&gt;y&lt;/span&gt;

+------+  +------+  +------+
|  ((  |  | CAR! |  |  ((  |
|  oo  |  |    __|  |  oo  |
| /_/|_|  |  &lt;span class="ge"&gt;_/  |  | /_&lt;/span&gt;/|_|
|    | |  | /_ __|  |    | |
|GOAT|||  |   O  |  |GOAT|||
+------+  +------+  +------+
Door 2 has the car!
You won!

Swapping:     1 wins, 0 losses, success rate 100.0%
Not swapping: 0 wins, 0 losses, success rate 0.0%

Press Enter to repeat the experiment...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Multiplication-Table" /&gt;&lt;/p&gt;
&lt;h3&gt;Multiplication Table&lt;/h3&gt;
&lt;p&gt;This program generates &lt;a href="https://inventwithpython.com/bigbookpython/project49.html"&gt;a multiplication table from 0 × 0 to 12 × 12&lt;/a&gt;. While simple, it provides a useful demonstration of nested loops.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;  &lt;span class="o"&gt;|&lt;/span&gt;  0   1   2   3   4   5   6   7   8   9  10  11  12
--+---------------------------------------------------
 0|  0   0   0   0   0   0   0   0   0   0   0   0   0
 1|  0   1   2   3   4   5   6   7   8   9  10  11  12
 2|  0   2   4   6   8  10  12  14  16  18  20  22  24
 3|  0   3   6   9  12  15  18  21  24  27  30  33  36
 4|  0   4   8  12  16  20  24  28  32  36  40  44  48
 5|  0   5  10  15  20  25  30  35  40  45  50  55  60
 6|  0   6  12  18  24  30  36  42  48  54  60  66  72
 7|  0   7  14  21  28  35  42  49  56  63  70  77  84
 8|  0   8  16  24  32  40  48  56  64  72  80  88  96
 9|  0   9  18  27  36  45  54  63  72  81  90  99 108
10|  0  10  20  30  40  50  60  70  80  90 100 110 120
11|  0  11  22  33  44  55  66  77  88  99 110 121 132
12|  0  12  24  36  48  60  72  84  96 108 120 132 144
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Ninety-Nine-Bottles" /&gt;&lt;/p&gt;
&lt;h3&gt;Ninety Nine Bottles&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project50.html"&gt;“Ninety-Nine Bottles”&lt;/a&gt; is a folk song of undetermined origin known for its length and repetitiveness. The lyrics go, “Ninety-nine bottles of milk on the wall, ninety-nine bottles of milk. Take one down, pass it around, ninety-eight bottles of milk on the wall.” As the lyrics repeat, the number of bottles falls from ninety-eight to ninety-seven, then from ninety-seven to ninety-six, until it reaches zero: “One bottle of milk on the wall, one bottle of milk. Take it down, pass it around, no more bottles of milk on the wall!”&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;99&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;99&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;Take&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;down&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;around&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;98&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wall&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;

&lt;span class="mf"&gt;98&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;98&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;Take&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;down&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;around&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;97&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wall&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;
&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="niNety-nniinE-BoOttels" /&gt;&lt;/p&gt;
&lt;h3&gt;niNety nniinE BoOttels&lt;/h3&gt;
&lt;p&gt;In this version of the song “Ninety-Nine Bottles,” &lt;a href="https://inventwithpython.com/bigbookpython/project51.html"&gt;the program introduces small imperfections&lt;/a&gt; in each stanza by either removing a letter, swapping the casing of a letter, transposing two letters, or doubling a letter. As the song continues to play, these mutations add up, resulting in a very silly song.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;99&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;99&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;Take&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;down&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;around&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;98&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wall&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;

&lt;span class="mf"&gt;98&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;98&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;Take&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;around&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;97&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wall&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;

&lt;span class="mf"&gt;97&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;97&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottels&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;Take&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;around&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;96&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wall&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;
&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="mf"&gt;75&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;otlte&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mIl&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;teh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;75&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;ottels&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;miLk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;Take&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;und&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;74&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bbOttles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wall&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;
&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;otlE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;iml&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;oo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nteh&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;lall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Tle&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;FF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FmMLIIkk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;Taake&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;pAasSs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;itt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;au&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;more&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;milk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wall&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Numeral-Systems" /&gt;&lt;/p&gt;
&lt;h3&gt;Numeral Systems&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project52.html"&gt;This program displays numbers in decimal, hexadecimal, and binary.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We’re used to counting in the decimal numeral system, which uses 10 digits: 0 through 9. This system likely developed because humans counted on their fingers, and most people have 10 fingers. But other number systems exist. Computers make use of binary, a numeral system with only two digits, 0 and 1. Programmers also sometimes use hexadecimal, which is a base-16 numeral system that uses the digits 0 to 9 but also extends into the letters A to F.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Enter the starting number (e.g. 0) &amp;gt; 0
Enter how many numbers to display (e.g. 1000) &amp;gt; 20
DEC: 0    HEX: 0    BIN: 0
DEC: 1    HEX: 1    BIN: 1
DEC: 2    HEX: 2    BIN: 10
DEC: 3    HEX: 3    BIN: 11
DEC: 4    HEX: 4    BIN: 100
DEC: 5    HEX: 5    BIN: 101
DEC: 6    HEX: 6    BIN: 110
DEC: 7    HEX: 7    BIN: 111
DEC: 8    HEX: 8    BIN: 1000
DEC: 9    HEX: 9    BIN: 1001
DEC: 10    HEX: A    BIN: 1010
DEC: 11    HEX: B    BIN: 1011
DEC: 12    HEX: C    BIN: 1100
DEC: 13    HEX: D    BIN: 1101
DEC: 14    HEX: E    BIN: 1110
DEC: 15    HEX: F    BIN: 1111
DEC: 16    HEX: 10    BIN: 10000
DEC: 17    HEX: 11    BIN: 10001
DEC: 18    HEX: 12    BIN: 10010
DEC: 19    HEX: 13    BIN: 10011
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Periodic-Table-of the Elements" /&gt;&lt;/p&gt;
&lt;h3&gt;Periodic Table of the Elements&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://inventwithpython.com/bigbookpython/project53.html"&gt;periodic table of the elements&lt;/a&gt; organizes all known chemical elements into a single table. This program presents this table and lets the player access additional information about each element, such as its atomic number, symbol, melting point, and so on. I compiled this information from Wikipedia and stored it in a file called periodictable.csv that you can download from &lt;a href="https://inventwithpython.com/periodictable.csv"&gt;https://inventwithpython.com/periodictable.csv&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Periodic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="w"&gt;                                                  &lt;/span&gt;&lt;span class="n"&gt;He&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Li&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Be&lt;/span&gt;&lt;span class="w"&gt;                               &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;O&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Ne&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Na&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mg&lt;/span&gt;&lt;span class="w"&gt;                               &lt;/span&gt;&lt;span class="n"&gt;Al&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Si&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Cl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ar&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Ca&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Sc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ti&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Cr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Co&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ni&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Zn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ga&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Se&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Br&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Kr&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Rb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Sr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Zr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Nb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Tc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ru&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Rh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Pd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;In&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Sn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Sb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Te&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Xe&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ba&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;La&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Hf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ta&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;W&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Re&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Os&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Pt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Au&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Hg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Tl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Pb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Bi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Po&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;At&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Rn&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ra&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ac&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Rf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Sg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Bh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Hs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Rg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Nh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Lv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Og&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Ce&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Pr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Nd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Pm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Sm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Eu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Gd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Tb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Dy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ho&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Er&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Tm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Yb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Lu&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Th&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Pa&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Np&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Pu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Am&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Bk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Es&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Md&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Lr&lt;/span&gt;
&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;atomic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;examine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;QUIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="n"&gt;Atomic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mo&lt;/span&gt;
&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="n"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Molybdenum&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Origin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Greek&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;molýbdaina&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;piece of lead&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mólybdos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lead&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;Period&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="n"&gt;Atomic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;95.95&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;
&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="n"&gt;Density&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;10.22&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="n"&gt;Melting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2896&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="n"&gt;Boiling&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4912&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Specific&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;heat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.251&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;J&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="n"&gt;Electronegativity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.16&lt;/span&gt;
&lt;span class="n"&gt;Abundance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;earth&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s crust: 1.2 mg/kg&lt;/span&gt;
&lt;span class="n"&gt;Press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Pig-Latin" /&gt;&lt;/p&gt;
&lt;h3&gt;Pig Latin&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project54.html"&gt;Pig Latin&lt;/a&gt; is a word game that transforms English words into a parody of Latin. In Pig Latin, if a word begins with a consonant, the speaker removes this letter and puts it at the end, followed by “ay.” For example, “pig” becomes “igpay” and “latin” becomes “atinlay.” Otherwise, if the word begins with a vowel, the speaker simply adds “yay” to the end of it. For example, “elephant” becomes “elephantyay” and “umbrella” becomes “umbrellayay.”&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Enter your message:
&amp;gt; This is a very serious message.
Isthay isyay ayay eryvay erioussay essagemay.
(Copied pig latin to clipboard.)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Powerball-Lottery" /&gt;&lt;/p&gt;
&lt;h3&gt;Powerball Lottery&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://inventwithpython.com/bigbookpython/project55.html"&gt;Powerball Lottery&lt;/a&gt; is an exciting way to lose small amounts of money. If you purchase a $2 ticket, you can pick six numbers: five drawn from 1 to 69, and a sixth “Powerball” number drawn from 1 to 26. The order of the numbers doesn’t matter. If the lottery selects your six numbers, you win $1.586 billion dollars! Except you won’t win, because your odds are 1 in 292,201,338. But if you spent $200 on 100 tickets, your odds would be . . . 1 in 2,922,013. You won’t win that either, but at least you’ll lose 100 times as much money. The more you like losing money, the more fun the lottery is!&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;different&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;numbers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;69&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;spaces&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;between&lt;/span&gt;
&lt;span class="nv"&gt;each&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="k"&gt;For&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;example&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;powerball&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="nv"&gt;How&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;many&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;times&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;play&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Max&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;
&lt;span class="nv"&gt;It&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;costs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;$2000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;play&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;times&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;but&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;don&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;t&lt;/span&gt;
&lt;span class="nv"&gt;worry&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;m sure you&amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;ll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;win&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;back&lt;/span&gt;.
&lt;span class="nv"&gt;Press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;start&lt;/span&gt;...
&lt;span class="nv"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;winning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;numbers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;are&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lost&lt;/span&gt;.
&lt;span class="nv"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;winning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;numbers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;are&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lost&lt;/span&gt;.
&lt;span class="nv"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;winning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;numbers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;are&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;63&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lost&lt;/span&gt;.
&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nv"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="nv"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;winning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;numbers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;are&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;62&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lost&lt;/span&gt;.
&lt;span class="nv"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;winning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;numbers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;are&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lost&lt;/span&gt;.
&lt;span class="nv"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;winning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;numbers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;are&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;58&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lost&lt;/span&gt;.
&lt;span class="nv"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;wasted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;$2000000&lt;/span&gt;
&lt;span class="nv"&gt;Thanks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;playing&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Prime-Numbers" /&gt;&lt;/p&gt;
&lt;h3&gt;Prime Numbers&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project56.html"&gt;A prime number&lt;/a&gt; is a number that is evenly divisible only by one and itself. Prime numbers have a variety of practical applications, but no algorithm can predict them; we must calculate them one at a time. However, we do know that there is an infinite number of prime numbers to be discovered.&lt;/p&gt;
&lt;p&gt;This program finds prime numbers through brute-force calculation. Its code is similar to Project 24, “Factor Finder.” (Another way to describe a prime number is that one and the number itself are its only factors.) You can find out more about prime numbers from &lt;a href="https://en.wikipedia.org/wiki/Prime_number"&gt;https://en.wikipedia.org/wiki/Prime_number&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;searching&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;primes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;from&lt;/span&gt;:
&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000000000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;zeros&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;another&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;.&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="nv"&gt;Press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Ctrl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;any&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;quit&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;begin&lt;/span&gt;...
&lt;span class="mi"&gt;2&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;43&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;53&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;61&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;67&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;71&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;73&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;79&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;83&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;89&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;97&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;103&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;107&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;109&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;113&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;127&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;131&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;137&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;139&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;149&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;151&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;157&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;163&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;167&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;173&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;179&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;181&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;191&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;193&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;197&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;199&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;211&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;223&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;227&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;229&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;233&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;239&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;241&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;251&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;257&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;263&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;269&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;271&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;277&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;281&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;283&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;293&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;307&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;311&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;313&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;317&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;331&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;337&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;347&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;349&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;353&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;359&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;367&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;373&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;379&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;383&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;389&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;397&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;409&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;419&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;421&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;431&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;433&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;439&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;449&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;457&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;461&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;463&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;467&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;479&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;487&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;491&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;499&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;503&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;509&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;521&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;523&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;541&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;547&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;557&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;563&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;569&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;571&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;577&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;587&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;593&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;599&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;601&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;607&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;613&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;617&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;619&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;631&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;641&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;643&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;647&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nv"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Progress-Bar" /&gt;&lt;/p&gt;
&lt;h3&gt;Progress Bar&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project57.html"&gt;A progress bar is a visual element&lt;/a&gt; that shows how much of a task has been completed. Progress bars are often used alongside downloading files or software installations. This project creates a getProgressBar() function that returns a progress bar string based on the arguments passed to it. It simulates a downloading file, but you can reuse the progress bar code in your own projects.&lt;/p&gt;
&lt;p&gt;You can animate the text by printing &lt;code&gt;\b&lt;/code&gt; backspace characters to erase text you've already printed and then print a new progress bar.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Progress Bar Simulation, by Al Sweigart
[█████████                               ] 24.6% 1007/4098
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Rainbow" /&gt;&lt;/p&gt;
&lt;h3&gt;Rainbow&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project58.html"&gt;Rainbow is a simple program&lt;/a&gt; that shows a colorful rainbow traveling back and forth across the screen. The program makes use of the fact that when new lines of text appear, the existing text scrolls up, causing it to look like it’s moving.&lt;/p&gt;
&lt;p&gt;If you find this kind of animation interesting, you can find more examples of "scrolling ASCII art" on my other website, &lt;a href="https://scrollart.org"&gt;The Scroll Art Museum&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a id="Rock-Paper-Scissors" /&gt;&lt;/p&gt;
&lt;h3&gt;Rock Paper Scissors&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project59.html"&gt;In this version&lt;/a&gt; of the two-player hand game also known as Rochambeau or jan-ken-pon, the player faces off against the computer. You can pick either rock, paper, or scissors. Rock beats scissors, scissors beats paper, and paper beats rock. This program adds some brief pauses for suspense.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Enter&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;move:&lt;/span&gt; (&lt;span class="n"&gt;R&lt;/span&gt;)&lt;span class="n"&gt;ock&lt;/span&gt; (&lt;span class="n"&gt;P&lt;/span&gt;)&lt;span class="n"&gt;aper&lt;/span&gt; (&lt;span class="n"&gt;S&lt;/span&gt;)&lt;span class="n"&gt;cissors&lt;/span&gt; &lt;span class="o"&gt;or&lt;/span&gt; (&lt;span class="s"&gt;Q)uit&lt;/span&gt;
&lt;span class="s"&gt;&amp;gt; r&lt;/span&gt;
&lt;span class="s"&gt;ROCK versus...&lt;/span&gt;
&lt;span class="s"&gt;1...&lt;/span&gt;
&lt;span class="s"&gt;2...&lt;/span&gt;
&lt;span class="s"&gt;3...&lt;/span&gt;
&lt;span class="s"&gt;SCISSORS&lt;/span&gt;
&lt;span class="s"&gt;You win!&lt;/span&gt;
&lt;span class="s"&gt;1 Wins, 0 Losses, 0 Ties&lt;/span&gt;
&lt;span class="s"&gt;Enter your move: (R)&lt;/span&gt;&lt;span class="n"&gt;ock&lt;/span&gt; (&lt;span class="n"&gt;P&lt;/span&gt;)&lt;span class="n"&gt;aper&lt;/span&gt; (&lt;span class="n"&gt;S&lt;/span&gt;)&lt;span class="n"&gt;cissors&lt;/span&gt; &lt;span class="o"&gt;or&lt;/span&gt; (&lt;span class="s"&gt;Q)uit&lt;/span&gt;
&lt;span class="s"&gt;--snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Rock-Paper-Scissors (Always-Win Version)" /&gt;&lt;/p&gt;
&lt;h3&gt;Rock Paper Scissors (Always-Win Version)&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project60.html"&gt;This variant of Rock Paper Scissors&lt;/a&gt; is identical to “Rock Paper Scissors,” except the player will always win. The code selecting the computer’s move is set so that it always chooses the losing move. You can offer this game to your friends, who may be excited when they win... at first. See how long it takes before they catch on to the fact that the game is rigged in their favor.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;Wins&lt;/span&gt;, &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;Losses&lt;/span&gt;, &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;Ties&lt;/span&gt;
&lt;span class="n"&gt;Enter&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;move:&lt;/span&gt; (&lt;span class="n"&gt;R&lt;/span&gt;)&lt;span class="n"&gt;ock&lt;/span&gt; (&lt;span class="n"&gt;P&lt;/span&gt;)&lt;span class="n"&gt;aper&lt;/span&gt; (&lt;span class="n"&gt;S&lt;/span&gt;)&lt;span class="n"&gt;cissors&lt;/span&gt; &lt;span class="o"&gt;or&lt;/span&gt; (&lt;span class="s"&gt;Q)uit&lt;/span&gt;
&lt;span class="s"&gt;&amp;gt; p&lt;/span&gt;
&lt;span class="s"&gt;PAPER versus...&lt;/span&gt;
&lt;span class="s"&gt;1...&lt;/span&gt;
&lt;span class="s"&gt;2...&lt;/span&gt;
&lt;span class="s"&gt;3...&lt;/span&gt;
&lt;span class="s"&gt;ROCK&lt;/span&gt;
&lt;span class="s"&gt;You win!&lt;/span&gt;
&lt;span class="s"&gt;1 Wins, 0 Losses, 0 Ties&lt;/span&gt;
&lt;span class="s"&gt;Enter your move: (R)&lt;/span&gt;&lt;span class="n"&gt;ock&lt;/span&gt; (&lt;span class="n"&gt;P&lt;/span&gt;)&lt;span class="n"&gt;aper&lt;/span&gt; (&lt;span class="n"&gt;S&lt;/span&gt;)&lt;span class="n"&gt;cissors&lt;/span&gt; &lt;span class="o"&gt;or&lt;/span&gt; (&lt;span class="s"&gt;Q)uit&lt;/span&gt;
&lt;span class="s"&gt;&amp;gt; s&lt;/span&gt;
&lt;span class="s"&gt;SCISSORS versus...&lt;/span&gt;
&lt;span class="s"&gt;1...&lt;/span&gt;
&lt;span class="s"&gt;2...&lt;/span&gt;
&lt;span class="s"&gt;3...&lt;/span&gt;
&lt;span class="s"&gt;PAPER&lt;/span&gt;
&lt;span class="s"&gt;You win!&lt;/span&gt;
&lt;span class="s"&gt;2 Wins, 0 Losses, 0 Ties&lt;/span&gt;
&lt;span class="s"&gt;--snip--&lt;/span&gt;
&lt;span class="s"&gt;SCISSORS versus...&lt;/span&gt;
&lt;span class="s"&gt;1...&lt;/span&gt;
&lt;span class="s"&gt;2...&lt;/span&gt;
&lt;span class="s"&gt;3...&lt;/span&gt;
&lt;span class="s"&gt;PAPER&lt;/span&gt;
&lt;span class="s"&gt;You win!&lt;/span&gt;
&lt;span class="s"&gt;413 Wins, 0 Losses, 0 Ties&lt;/span&gt;
&lt;span class="s"&gt;Enter your move: (R)&lt;/span&gt;&lt;span class="n"&gt;ock&lt;/span&gt; (&lt;span class="n"&gt;P&lt;/span&gt;)&lt;span class="n"&gt;aper&lt;/span&gt; (&lt;span class="n"&gt;S&lt;/span&gt;)&lt;span class="n"&gt;cissors&lt;/span&gt; &lt;span class="o"&gt;or&lt;/span&gt; (&lt;span class="s"&gt;Q)uit&lt;/span&gt;
&lt;span class="s"&gt;--snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="ROT-13-Cipher" /&gt;&lt;/p&gt;
&lt;h3&gt;ROT 13 Cipher&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project61.html"&gt;The ROT13 cipher&lt;/a&gt;, one of the simplest encryption algorithms, stands for “rotate 13 spaces.” The cypher represents the letters A to Z as the numbers 0 to 25 in such a way that the encrypted letter is 13 spaces from the plaintext letter: A becomes N, B becomes O, and so on. The encryption process is identical to the decryption process, making it trivial to program. However, the encryption is also trivial to break. Because of this, you’ll most often find ROT13 used to conceal non-sensitive information, such as spoilers or trivia answers, so it’s not read unintentionally. More information about the ROT13 cipher can be found at &lt;a href="https://en.wikipedia.org/wiki/ROT13"&gt;https://en.wikipedia.org/wiki/ROT13&lt;/a&gt;. If you’d like to learn about ciphers and code breaking more generally, you can read my book Cracking Codes with Python (No Starch Press, 2018; &lt;a href="https://nostarch.com/crackingcodes/"&gt;https://nostarch.com/crackingcodes/&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Enter a message to encrypt/decrypt (or QUIT):
&amp;gt; Meet me by the rose bushes tonight.
The translated message is:
Zrrg zr ol gur ebfr ohfurf gbavtug.

(Copied to clipboard.)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Rotating-Cube" /&gt;&lt;/p&gt;
&lt;h3&gt;Rotating Cube&lt;/h3&gt;
&lt;p&gt;This project features &lt;a href="https://inventwithpython.com/bigbookpython/project62.html"&gt;an animation of a 3D cube rotating&lt;/a&gt; using trigonometric functions. You can adapt the 3D point rotation math and the line() function in your own animation programs.&lt;/p&gt;
&lt;p&gt;Although the block text characters we’ll use to draw the cube don’t look like thin, straight lines, this kind of drawing is called a wireframe model because it renders only the edges of an object’s surfaces.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;                     ██
                    █ ███
                   █   █ ██
                  █    █   ██
                 █      █    ██
                █        █     ██
               █          █      ██
              █            █       █
             █              █     █ █
            █               █    █  █
           █                 █  █    █
          █                   ██      █
         █                    ██       █
        █                    █  █      █
       █                    █    █      █
      █                    █     █       █
     █                    █       █       █
    █                    █         █      █
   █                    █           █      █
  █                    █           █ ██     █
   ██                 █           █    ██    █
   █ ██              █           █       █   █
    █  █            █           █         ██  █
     █  ██         █           █            █  █
      █   ██      █           █              ███
      █     █    █           █                 ██
       █     ██ █           █                    █
        █      █           █                    █
         █      █         █                    █
         █       █       █                    █
          █      █      █                    █
           █      █    █                    █
            █      █  █                    █
            █       ██                    █
             █      ██                   █
              █    █  █                 █
               █  █   █                █
               █ █     █              █
                ██      █            █
                  ██     █          █
                    ██    █        █
                      ██   █      █
                        ██ █     █
                          ███   █
                            ██ █
                              █
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Royal-Game-of Ur" /&gt;&lt;/p&gt;
&lt;h3&gt;Royal Game of Ur&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project63.html"&gt;The Royal Game of Ur&lt;/a&gt; is a 5,000-year-old game from Mesopotamia. Archeologists rediscovered the game in the Royal Cemetery at Ur, in modern-day southern Iraq, during excavations between 1922 and 1934. The rules were reconstructed from the game board and a Babylonian clay tablet, and they’re similar to Parcheesi. You’ll need both luck and skill to win.&lt;/p&gt;
&lt;p&gt;A video featuring YouTuber Tom Scott and British Museum curator Irving Finkel discussing the Royal Game of Ur can be found at https://www.youtube.com/watch?v=WZskjLq040I.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="n"&gt;XXXXXXX&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="p"&gt;.......&lt;/span&gt;
&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="n"&gt;Home&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="n"&gt;Goal&lt;/span&gt;
&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;
&lt;span class="o"&gt;+-----+-----+-----+--&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;--+&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;+--^--+-----+&lt;/span&gt;
&lt;span class="o"&gt;|*****|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;|*****|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|*&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;*&amp;lt;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;|*&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;*&amp;lt;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|****&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;|****&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+--&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;--+-----+-----+-----+-----+-----+-----+--^--+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|*****|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;*&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;*&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|****&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+--^--+-----+-----+-----+-----+-----+-----+--&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;--+&lt;/span&gt;
&lt;span class="o"&gt;|*****|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;|*****|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|*&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;*&amp;lt;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;|*&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;*&amp;lt;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|****&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;|****&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+-----+-----+-----+--^--+&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;+--&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;--+-----+&lt;/span&gt;
&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;
&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="n"&gt;Home&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="n"&gt;Goal&lt;/span&gt;
&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="n"&gt;OOOOOOO&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="p"&gt;.......&lt;/span&gt;

&lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;O&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;turn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;flip&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nl"&gt;Flips&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;Select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;spaces&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;
&lt;span class="n"&gt;O&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;landed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;flower&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;space&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;again&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;--snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Shining-Carpet" /&gt;&lt;/p&gt;
&lt;h3&gt;Shining Carpet&lt;/h3&gt;
&lt;p&gt;The Shining, a 1980 psychological horror film directed by Stanley Kubrick, takes place at the haunted Overlook Hotel. The hotel carpet’s hexagonal design became an iconic part of this famous movie. The carpet features alternating and interlocking hexagons whose mesmerizing effect is well-suited for such an unnerving film. The short program in this project, similar to Project 35, “Hex Grid,” prints this repetitive pattern on the screen.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;_ \ \ \_/ ___ \ \ \_/ ___ \ \ \_/ ___ \ \ \_/ ___ \ \ \_/ ___ \ \ \_/ __
 \ \ \___/ _ \ \ \___/ _ \ \ \___/ _ \ \ \___/ _ \ \ \___/ _ \ \ \___/ _
\ \ \_____/ \ \ \_____/ \ \ \_____/ \ \ \_____/ \ \ \_____/ \ \ \_____/
/ / / ___ \_/ / / ___ \_/ / / ___ \_/ / / ___ \_/ / / ___ \_/ / / ___ \_
&lt;span class="ge"&gt;_/ / / _&lt;/span&gt; \___/ / / _ \___/ / / _ \___/ / / _ \___/ / / _ \___/ / / _ \__
__/ / / \_____/ / / \_____/ / / \_____/ / / \_____/ / / \_____/ / / \___
&lt;span class="ge"&gt;_ \ \ \_&lt;/span&gt;/ ___ \ \ \_/ ___ \ \ \_/ ___ \ \ \_/ ___ \ \ \_/ ___ \ \ \_/ __
 \ \ \___/ _ \ \ \___/ _ \ \ \___/ _ \ \ \___/ _ \ \ \___/ _ \ \ \___/ _
\ \ \_____/ \ \ \_____/ \ \ \_____/ \ \ \_____/ \ \ \_____/ \ \ \_____/
/ / / ___ \_/ / / ___ \_/ / / ___ \_/ / / ___ \_/ / / ___ \_/ / / ___ \_
&lt;span class="ge"&gt;_/ / / _&lt;/span&gt; \___/ / / _ \___/ / / _ \___/ / / _ \___/ / / _ \___/ / / _ \__
__/ / / \_____/ / / \_____/ / / \_____/ / / \_____/ / / \_____/ / / \___
&lt;span class="ge"&gt;_ \ \ \_&lt;/span&gt;/ ___ \ \ \_/ ___ \ \ \_/ ___ \ \ \_/ ___ \ \ \_/ ___ \ \ \_/ __
 \ \ \___/ _ \ \ \___/ _ \ \ \___/ _ \ \ \___/ _ \ \ \___/ _ \ \ \___/ _
\ \ \_____/ \ \ \_____/ \ \ \_____/ \ \ \_____/ \ \ \_____/ \ \ \_____/
/ / / ___ \_/ / / ___ \_/ / / ___ \_/ / / ___ \_/ / / ___ \_/ / / ___ \_
&lt;span class="ge"&gt;_/ / / _&lt;/span&gt; \___/ / / _ \___/ / / _ \___/ / / _ \___/ / / _ \___/ / / _ \__
__/ / / \_____/ / / \_____/ / / \_____/ / / \_____/ / / \_____/ / / \___
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you find this kind of animation interesting, you can find more examples of "scrolling ASCII art" on my other website, &lt;a href="https://scrollart.org/wallpaper"&gt;The Scroll Art Museum&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a id="Simple-Substitution-Cipher" /&gt;&lt;/p&gt;
&lt;h3&gt;Simple Substitution Cipher&lt;/h3&gt;
&lt;p&gt;The Simple Substitution Cipher substitutes one letter for another. Since there are 26 possible substitutions for the letter A, 25 possible substitutions for B, 24 for C, and so on, the total number of possible keys is 26 × 25 × 24 × 23 × . . . × 1, or 403,291,461,126,605,635,584,000,000 keys! That’s far too many keys for even a supercomputer to brute force, so the code-breaking method used in Project 7, “Caesar Hacker,” can’t be used against the simple cipher. Unfortunately, devious attackers can take advantage of known weakness to break the code. If you’d like to learn more about ciphers and code breaking, you can read my book Cracking Codes with Python (No Starch Press, 2018; https://nostarch.com/crackingcodes/).&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;e&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;ncrypt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;ecrypt&lt;/span&gt;?
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;e&lt;/span&gt;
&lt;span class="nv"&gt;Please&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;specify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;use&lt;/span&gt;.
&lt;span class="nv"&gt;Or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RANDOM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;one&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;generated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;random&lt;/span&gt;
&lt;span class="nv"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WNOMTRCEHDXBFVSLKAGZIPYJQU&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;KEEP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;THIS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;SECRET&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;encrypt&lt;/span&gt;.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Meet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;me&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;rose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bushes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tonight&lt;/span&gt;.
&lt;span class="nv"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;encrypted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;:
&lt;span class="nv"&gt;Fttz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ft&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;zet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;asgt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nigetg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;zsvhcez&lt;/span&gt;.
&lt;span class="nv"&gt;Full&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;encrypted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;copied&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;clipboard&lt;/span&gt;.

&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nv"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;

&lt;span class="k"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;e&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;ncrypt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;ecrypt&lt;/span&gt;?
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;
&lt;span class="nv"&gt;Please&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;specify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;use&lt;/span&gt;.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WNOMTRCEHDXBFVSLKAGZIPYJQU&lt;/span&gt;
&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;decrypt&lt;/span&gt;.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Fttz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ft&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;zet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;asgt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nigetg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;zsvhcez&lt;/span&gt;.
&lt;span class="nv"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;decrypted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;:
&lt;span class="nv"&gt;Meet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;me&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;rose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bushes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tonight&lt;/span&gt;.
&lt;span class="nv"&gt;Full&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;decrypted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;copied&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;clipboard&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Sine-Message" /&gt;&lt;/p&gt;
&lt;h3&gt;Sine Message&lt;/h3&gt;
&lt;p&gt;This program displays a message of the user’s choice in a wavy pattern as the text scrolls up. It accomplishes this effect with &lt;code&gt;math.sin()&lt;/code&gt;, which implements the trigonometric sine wave function. But even if you don’t understand the math, this program is rather short and easy to copy.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;What&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;display&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Max&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;chars&lt;/span&gt;.&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                                 &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                                         &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                                                &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                                                       &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                                                            &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                                                                &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                                                                 &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                                                                 &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                                                               &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                                                          &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                                                    &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                                             &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                                     &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                             &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Programming&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nv"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Sliding-Tile-Puzzle" /&gt;&lt;/p&gt;
&lt;h3&gt;Sliding Tile Puzzle&lt;/h3&gt;
&lt;p&gt;This classic puzzle relies on a 4 × 4 board with 15 numbered tiles and one free space. The objective is to slide the tiles until the numbers are in the correct order, going left to right and top to bottom. Tiles can only slide; you’re not allowed to directly pick them up and rearrange them. Some versions of this puzzle toy feature scrambled images that form a complete picture once solved.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;+------+------+------+------+&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="c"&gt;|  5   |  10  |      |  11  |&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="nb"&gt;+------+------+------+------+&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="c"&gt;|  6   |  3   |  7   |  2   |&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="nb"&gt;+------+------+------+------+&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="c"&gt;|  14  |  1   |  15  |  8   |&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="nb"&gt;+------+------+------+------+&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="c"&gt;|  9   |  13  |  4   |  12  |&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="nb"&gt;+------+------+------+------+&lt;/span&gt;

&lt;span class="c"&gt;                          (W)&lt;/span&gt;
&lt;span class="c"&gt;Enter WASD (or QUIT): (A) ( ) (D)&lt;/span&gt;
&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="c"&gt; w&lt;/span&gt;

&lt;span class="nb"&gt;+------+------+------+------+&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="c"&gt;|  5   |  10  |  7   |  11  |&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="nb"&gt;+------+------+------+------+&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="c"&gt;|  6   |  3   |      |  2   |&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="nb"&gt;+------+------+------+------+&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="c"&gt;|  14  |  1   |  15  |  8   |&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="nb"&gt;+------+------+------+------+&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="c"&gt;|  9   |  13  |  4   |  12  |&lt;/span&gt;
&lt;span class="c"&gt;|      |      |      |      |&lt;/span&gt;
&lt;span class="nb"&gt;+------+------+------+------+&lt;/span&gt;

&lt;span class="c"&gt;                          (W)&lt;/span&gt;
&lt;span class="c"&gt;Enter WASD (or QUIT): (A) (S) (D)&lt;/span&gt;
&lt;span class="nb"&gt;--&lt;/span&gt;&lt;span class="c"&gt;snip&lt;/span&gt;&lt;span class="nb"&gt;--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Snail-Race" /&gt;&lt;/p&gt;
&lt;h3&gt;Snail Race&lt;/h3&gt;
&lt;p&gt;You won’t be able to handle the fast-paced excitement of these racing... snails. But what they lack in speed they make up for in ASCII-art cuteness. Each snail (represented by an &lt;code&gt;@&lt;/code&gt; character for the shell and &lt;code&gt;v&lt;/code&gt; for the two eyestalks) moves slowly but surely toward the finish line. Up to eight snails, each with a custom name, can race each other, leaving a slime trail in their wake. This program is good for beginners.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Snail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Race&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;@v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;snail&lt;/span&gt;

&lt;span class="n"&gt;How&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;many&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;snails&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;race&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Max&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;snail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;#1&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s name:&lt;/span&gt;
&lt;span class="s1"&gt;&amp;gt; Alice&lt;/span&gt;
&lt;span class="s1"&gt;Enter snail #2&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Bob&lt;/span&gt;
&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;snail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;#3&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Carol&lt;/span&gt;
&lt;span class="k"&gt;START&lt;/span&gt;&lt;span class="w"&gt;                                   &lt;/span&gt;&lt;span class="n"&gt;FINISH&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;                                       &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;Alice&lt;/span&gt;
&lt;span class="p"&gt;......&lt;/span&gt;&lt;span class="nv"&gt;@v&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;Bob&lt;/span&gt;
&lt;span class="p"&gt;.....&lt;/span&gt;&lt;span class="nv"&gt;@v&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="n"&gt;Carol&lt;/span&gt;
&lt;span class="p"&gt;.......&lt;/span&gt;&lt;span class="nv"&gt;@v&lt;/span&gt;
&lt;span class="c1"&gt;--snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Soroban-Japanese-Abacus" /&gt;&lt;/p&gt;
&lt;h3&gt;Soroban Japanese Abacus&lt;/h3&gt;
&lt;p&gt;An abacus, also called a counting frame, is a calculating tool used in many cultures long before electronic calculators were invented. The Japanese form of the abacus is called a soroban. Each wire represents a place in a positional numeral system, and the beads on the wire represent the digit at that place. For example, a soroban with two beads moved over on the rightmost wire and three beads moved over on the second-to-rightmost wire would represent the number 32. This program simulates a soroban. (The irony of using a computer to simulate a pre-computer computing tool is not lost on me.)&lt;/p&gt;
&lt;p&gt;Each column in the soroban represents a different digit. The rightmost column is the ones place, the column to its left is the tens place, the column to the left of that is the hundreds place, and so on. The Q, W, E, R, T, Y, U, I, O, and P keys along the top of your keyboard can increase the digit at their respective positions, while the A, S, D, F, G, H, J, K, L, and ; keys will decrease them. The beads on the virtual soroban will slide to reflect the current number. You can also enter numbers directly.&lt;/p&gt;
&lt;p&gt;The four beads below the horizontal divider are “earth” beads, and lifting them up against the divider counts as 1 for that digit. The bead above the horizontal divider is a “heaven” bead, and pulling it down against the divider counts as 5 for that digit, so pulling down one heaven bead and pulling up three earth beads in the tens column represents the number 80. More information about abacuses and how to use them can be found at https://en.wikipedia.org/wiki/Abacus.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;+================================+
I  O  O  O  O  O  O  O  O  O  O  I
I  |  |  |  |  |  |  |  |  |  |  I
I  |  |  |  |  |  |  |  |  |  |  I
+================================+
I  |  |  |  |  |  |  |  |  |  |  I
I  |  |  |  |  |  |  |  |  |  |  I
I  O  O  O  O  O  O  O  O  O  O  I
I  O  O  O  O  O  O  O  O  O  O  I
I  O  O  O  O  O  O  O  O  O  O  I
I  O  O  O  O  O  O  O  O  O  O  I
+==0==0==0==0==0==0==0==0==0==0==+
  +q  w  e  r  t  y  u  i  o  p
  -a  s  d  f  g  h  j  k  l  ;
(Enter a number, &amp;quot;quit&amp;quot;, or a stream of up/down letters.)
&amp;gt; pppiiiii

+================================+
I  O  O  O  O  O  O  O  |  O  O  I
I  |  |  |  |  |  |  |  |  |  |  I
I  |  |  |  |  |  |  |  O  |  |  I
+================================+
I  |  |  |  |  |  |  |  |  |  O  I
I  |  |  |  |  |  |  |  |  |  O  I
I  O  O  O  O  O  O  O  O  O  O  I
I  O  O  O  O  O  O  O  O  O  |  I
I  O  O  O  O  O  O  O  O  O  |  I
I  O  O  O  O  O  O  O  O  O  O  I
+==0==0==0==0==0==0==0==5==0==3==+
  +q  w  e  r  t  y  u  i  o  p
  -a  s  d  f  g  h  j  k  l  ;
(Enter a number, &amp;quot;quit&amp;quot;, or a stream of up/down letters.)
--snip--
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Sound-Mimic" /&gt;&lt;/p&gt;
&lt;h3&gt;Sound Mimic&lt;/h3&gt;
&lt;p&gt;Similar to the Simon electronic toy, this memorization game uses the third-party playsound module to play four different sounds, which correspond to the A, S, D, and F keys on the keyboard. As you successfully repeat the pattern the game gives you, the patterns get longer and longer. How many sounds can you hold in your short-term memory?&lt;/p&gt;
&lt;p&gt;If you look at the code, you’ll see that the playsound.playsound() function is passed the filename of the sound to play. You can download the sound files from these URLs:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/soundA.wav"&gt;https://inventwithpython.com/soundA.wav&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/soundS.wav"&gt;https://inventwithpython.com/soundS.wav&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/soundD.wav"&gt;https://inventwithpython.com/soundD.wav&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/soundF.wav"&gt;https://inventwithpython.com/soundF.wav&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Try to memorize a pattern of A S D F letters (each with its own sound)
as it gets longer and longer.
Press Enter to begin...

&amp;lt;screen clears&amp;gt;

Pattern: S

&amp;lt;screen clears&amp;gt;

Enter the pattern:
&amp;gt; s
Correct!

&amp;lt;screen clears&amp;gt;

Pattern: S F

&amp;lt;screen clears&amp;gt;

Enter the pattern:
&amp;gt; sf
Correct!

&amp;lt;screen clears&amp;gt;

Pattern: S F F

&amp;lt;screen clears&amp;gt;

Enter the pattern:
&amp;gt; sff
Correct!

&amp;lt;screen clears&amp;gt;

Pattern: S F F D
--snip--
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="sPoNgEcAsE" /&gt;&lt;/p&gt;
&lt;h3&gt;sPoNgEcAsE&lt;/h3&gt;
&lt;p&gt;You’ve probably seen the “Mocking SpongeBob” meme: a picture of SpongeBob SquarePants, with a caption whose text alternates between upper- and lowercase letters to indicate sarcasm, like this: uSiNg SpOnGeBoB MeMeS dOeS NoT mAkE YoU wItTy. For some randomness, the text sometimes doesn’t alternate capitalization.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;eNtEr YoUr MeSsAgE:
&amp;gt; Using SpongeBob memes does not make you witty.

uSiNg SpOnGeBoB MeMeS dOeS NoT mAkE YoU wItTy.
(cOpIed SpOnGeTexT to ClIpbOaRd.)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Sudoku-Puzzle" /&gt;&lt;/p&gt;
&lt;h3&gt;Sudoku Puzzle&lt;/h3&gt;
&lt;p&gt;Sudoku is a popular puzzle game in newspapers and mobile apps. The Sudoku board is a 9 × 9 grid in which the player must place the digits 1 to 9 once, and only once, in each row, column, and 3 × 3 subgrid. The game begins with a few spaces already filled in with digits, called givens. A well-formed Sudoku puzzle will have only one possible valid solution.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nv"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;C&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;E&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;F&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;G&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;H&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;I&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.
&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;------+-------+------&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.
&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;------+-------+------&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.
&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;.

&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;move&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;RESET&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;NEW&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;UNDO&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ORIGINAL&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;QUIT&lt;/span&gt;:
&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="k"&gt;For&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;example&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;looks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B4 9&amp;quot;&lt;/span&gt;.&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nv"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Text-To-Speech Talker" /&gt;&lt;/p&gt;
&lt;h3&gt;Text To Speech Talker&lt;/h3&gt;
&lt;p&gt;This program demonstrates the use of the pyttsx3 third-party module. Any message you enter will be spoken out loud by your operating system’s text-to-speech capabilities. Although computer-generated speech is an incredibly complex branch of computer science, the pyttsx3 module provides an easy interface for it, making this small program suitable for beginners. Once you’ve learned how to use the module, you can add generated speech to your own programs.&lt;/p&gt;
&lt;p&gt;More information about the pyttsx3 module can be found at &lt;a href="https://pypi.org/project/pyttsx3/"&gt;https://pypi.org/project/pyttsx3/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;speech&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;pyttsx3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;module&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;turn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;uses&lt;/span&gt;
&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;NSSpeechSynthesizer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;macOS&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;SAPI5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Windows&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;
&lt;span class="nv"&gt;eSpeak&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Linux&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;speech&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;engines&lt;/span&gt;.

&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;speak&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;QUIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;quit&lt;/span&gt;.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Hello&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;My&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Guido&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;van&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Robot&lt;/span&gt;.

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;computer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;speaks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;loud&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;quit&lt;/span&gt;
&lt;span class="nv"&gt;Thanks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;playing&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Three-Card-Monte" /&gt;&lt;/p&gt;
&lt;h3&gt;Three-Card Monte&lt;/h3&gt;
&lt;p&gt;Three-card monte is a common scam played on gullible tourists and other easy marks. Three playing cards, one of which is the “red lady” Queen of Hearts, are put facedown on a cardboard box. The dealer quickly rearranges the cards and then asks the mark to pick the Queen of Hearts. But the dealer can use all sorts of tricks to hide the card or otherwise cheat, guaranteeing that the victim never wins. It’s also common for the dealer to have shills in the crowd who secretly work with the dealer but pretend to win the game (to make the victim think they too could win) or purposefully lose badly (to make the victim think they could do much better).&lt;/p&gt;
&lt;p&gt;This program shows the three cards and then quickly describes a series of swaps. At the end, it clears the screen, and the player must pick a card. Can you keep up with the “red lady”? For the authentic three-card monte experience, you can enable the cheat feature, which causes the player to always lose, even if they select the correct card.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Find the red lady (the Queen of Hearts)! Keep an eye on how
the cards move.

Here are the cards:
 ___   ___   ___
|J  | |Q  | |8  |
| ♦ | | ♥ | | ♣ |
|__J| |__Q| |__8|
Press Enter when you are ready to begin...
swapping left and middle...
swapping right and middle...
swapping middle and left...
swapping right and left...
swapping left and middle...
--snip--

&amp;lt;screen clears&amp;gt;

Which card has the Queen of Hearts? (LEFT MIDDLE RIGHT)
&lt;span class="k"&gt;&amp;gt; &lt;/span&gt;&lt;span class="ge"&gt;middle&lt;/span&gt;
 ___   ___   ___
|Q  | |8  | |J  |
| ♥ | | ♣ | | ♦ |
|__Q| |__8| |__J|
You lost!
Thanks for playing, sucker!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Tic-Tac-Toe" /&gt;&lt;/p&gt;
&lt;h3&gt;Tic-Tac-Toe&lt;/h3&gt;
&lt;p&gt;Tic-tac-toe is a classic pencil-and-paper game played on a 3 × 3 grid. Players take turns placing their X or O marks, trying to get three in a row. Most games of tic-tac-toe end in a tie, but it is possible to outsmart your opponent if they’re not careful.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;Welcome&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Tic&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;Tac&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;Toe&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;

&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-+-+-&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-+-+-&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;
&lt;span class="nv"&gt;What&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;s move? (1-9)&lt;/span&gt;
&lt;span class="err"&gt;&amp;gt; 1&lt;/span&gt;

&lt;span class="err"&gt;      X| |   1 2 3&lt;/span&gt;
&lt;span class="err"&gt;      -+-+-&lt;/span&gt;
&lt;span class="err"&gt;       | |   4 5 6&lt;/span&gt;
&lt;span class="err"&gt;      -+-+-&lt;/span&gt;
&lt;span class="err"&gt;       | |   7 8 9&lt;/span&gt;
&lt;span class="nv"&gt;What&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;s move? (1-9)&lt;/span&gt;
&lt;span class="err"&gt;--snip--&lt;/span&gt;
&lt;span class="err"&gt;      X|O|X  1 2 3&lt;/span&gt;
&lt;span class="err"&gt;      -+-+-&lt;/span&gt;
&lt;span class="err"&gt;      X|O|O  4 5 6&lt;/span&gt;
&lt;span class="err"&gt;      -+-+-&lt;/span&gt;
&lt;span class="err"&gt;      O|X|X  7 8 9&lt;/span&gt;
&lt;span class="err"&gt;The game is a tie!&lt;/span&gt;
&lt;span class="err"&gt;Thanks for playing!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Tower-of-Hanoi Puzzle" /&gt;&lt;/p&gt;
&lt;h3&gt;Tower of Hanoi Puzzle&lt;/h3&gt;
&lt;p&gt;The Tower of Hanoi is a stack-moving puzzle game that features three poles on which you can stack various-sized disks. The object of the game is to move one tower of disks to another pole. However, only one disk can be moved at a time, and larger disks cannot be placed on top of smaller ones. Figuring out a certain pattern will help you solve this puzzle. Can you discover it? (Hint: Try setting the &lt;code&gt;TOTAL_DISKS&lt;/code&gt; variable to 3 or 4 to solve an easier version first.)&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Tower&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Hanoi&lt;/span&gt;

&lt;span class="n"&gt;Move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tower&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;disks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;disk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;another&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tower&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Larger&lt;/span&gt;
&lt;span class="n"&gt;disks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cannot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;top&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;smaller&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;disk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;More&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wikipedia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;wiki&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Tower_of_Hanoi&lt;/span&gt;

&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;@_1&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nb"&gt;@@_2&lt;/span&gt;&lt;span class="err"&gt;@@&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;@@_3&lt;/span&gt;&lt;span class="err"&gt;@@@&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@@&lt;/span&gt;&lt;span class="nb"&gt;@@_4&lt;/span&gt;&lt;span class="err"&gt;@@@@&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="err"&gt;@@@&lt;/span&gt;&lt;span class="nb"&gt;@@_5&lt;/span&gt;&lt;span class="err"&gt;@@@@@&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;

&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;letters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;from&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;to&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;towers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;QUIT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;moves&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;disk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tower&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tower&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nb"&gt;@@_2&lt;/span&gt;&lt;span class="err"&gt;@@&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;@@_3&lt;/span&gt;&lt;span class="err"&gt;@@@&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@@&lt;/span&gt;&lt;span class="nb"&gt;@@_4&lt;/span&gt;&lt;span class="err"&gt;@@@@&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="err"&gt;@@@&lt;/span&gt;&lt;span class="nb"&gt;@@_5&lt;/span&gt;&lt;span class="err"&gt;@@@@@&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;@_1&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;

&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;letters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;from&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;to&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;towers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;QUIT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;moves&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;disk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tower&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tower&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;

&lt;span class="c1"&gt;--snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Twenty-Forty-Eight" /&gt;&lt;/p&gt;
&lt;h3&gt;Twenty Forty Eight&lt;/h3&gt;
&lt;p&gt;Gabriele Cirulli, a web developer, invented the game 2048 in one weekend. It was inspired by Veewo Studios’ 1024 game, which in turn was inspired by Threes!, a game by the development team Sirvo. In 2048, you must merge numbers on a 4 × 4 board to clear them from the screen. Two 2s merge into a 4, two 4s merge into an 8, and so on. The game adds a new 2 to the board on each merging. The objective is to reach 2048 before the entire board fills up.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;--&lt;/span&gt;&lt;span class="c"&gt;snip&lt;/span&gt;&lt;span class="nb"&gt;--&lt;/span&gt;
&lt;span class="nb"&gt;+-----+-----+-----+-----+&lt;/span&gt;
&lt;span class="c"&gt;|     |     |     |     |&lt;/span&gt;
&lt;span class="c"&gt;|     |     |  2  |  16 |&lt;/span&gt;
&lt;span class="c"&gt;|     |     |     |     |&lt;/span&gt;
&lt;span class="nb"&gt;+-----+-----+-----+-----+&lt;/span&gt;
&lt;span class="c"&gt;|     |     |     |     |&lt;/span&gt;
&lt;span class="c"&gt;|     |  16 |  4  |  2  |&lt;/span&gt;
&lt;span class="c"&gt;|     |     |     |     |&lt;/span&gt;
&lt;span class="nb"&gt;+-----+-----+-----+-----+&lt;/span&gt;
&lt;span class="c"&gt;|     |     |     |     |&lt;/span&gt;
&lt;span class="c"&gt;|  2  |     |  4  |  32 |&lt;/span&gt;
&lt;span class="c"&gt;|     |     |     |     |&lt;/span&gt;
&lt;span class="nb"&gt;+-----+-----+-----+-----+&lt;/span&gt;
&lt;span class="c"&gt;|     |     |     |     |&lt;/span&gt;
&lt;span class="c"&gt;|     |     |     |  2  |&lt;/span&gt;
&lt;span class="c"&gt;|     |     |     |     |&lt;/span&gt;
&lt;span class="nb"&gt;+-----+-----+-----+-----+&lt;/span&gt;

&lt;span class="c"&gt;Score: 80&lt;/span&gt;
&lt;span class="c"&gt;Enter move: (WASD or Q to quit)&lt;/span&gt;
&lt;span class="nb"&gt;--&lt;/span&gt;&lt;span class="c"&gt;snip&lt;/span&gt;&lt;span class="nb"&gt;--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Vigenère-Cipher" /&gt;&lt;/p&gt;
&lt;h3&gt;Vigenère Cipher&lt;/h3&gt;
&lt;p&gt;The Vigenère cipher, misattributed to 19th-century cryptographer Blaise de Vigenère (others had independently invented it earlier), was impossible to crack for hundreds of years. It is essentially the Caesar cipher, except it makes use of a multipart key. The so-called Vigenère key is a word, or even a random series of letters. Each letter represents a number by which to shift the letter in the message: A represents shifting a letter in the message by 0, B represents 1, C represents 2, and so on.&lt;/p&gt;
&lt;p&gt;For example, if a Vigenère key is the word “CAT,” the C represents a shift of 2, the A represents 0, and the T represents 19. The first letter of the message gets shifted by 2, the second letter by 0, and the third letter by 19. For the fourth letter, we repeat the key of 2.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Vigen&lt;/span&gt;è&lt;span class="nv"&gt;re&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cipher&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;polyalphabetic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;substitution&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cipher&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;was&lt;/span&gt;
&lt;span class="nv"&gt;powerful&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;enough&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;remain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;unbroken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;centuries&lt;/span&gt;.
&lt;span class="k"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;e&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;ncrypt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;ecrypt&lt;/span&gt;?

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;e&lt;/span&gt;

&lt;span class="nv"&gt;Please&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;specify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;use&lt;/span&gt;.
&lt;span class="nv"&gt;It&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;word&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;any&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;combination&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;letters&lt;/span&gt;:

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PIZZA&lt;/span&gt;

&lt;span class="nv"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;encrypt&lt;/span&gt;.

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Meet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;me&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;rose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bushes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tonight&lt;/span&gt;.

&lt;span class="nv"&gt;Encrypted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;:
&lt;span class="nv"&gt;Bmds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;mt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;jx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;sht&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;znre&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;qcrgeh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bnmivps&lt;/span&gt;.
&lt;span class="nv"&gt;Full&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;encrypted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;copied&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;clipboard&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you’d like to learn more about ciphers and code breaking, you can read my book Cracking Codes with Python (No Starch Press, 2018; https://nostarch.com/crackingcodes/).&lt;/p&gt;
&lt;p&gt;&lt;a id="Water-Bucket-Puzzle" /&gt;&lt;/p&gt;
&lt;h3&gt;Water Bucket Puzzle&lt;/h3&gt;
&lt;p&gt;In this solitaire puzzle game, you must use three buckets (three-liter, five-liter, and eight-liter buckets) to collect exactly four liters of water in one of the buckets. Buckets can only be emptied, completely filled, or poured into another bucket. For example, you can fill the five-liter bucket and then pour its contents into the three-liter bucket, leaving you with a full three-liter bucket and two liters of water in the five-liter bucket.&lt;/p&gt;
&lt;p&gt;With some effort, you should be able to solve the puzzle. But can you figure out how to solve it with the minimal number of moves?&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Try to get 4L of water into one of these
buckets:

8|      |
7|      |
6|      |
5|      |  5|      |
4|      |  4|      |
3|      |  3|      |  3|      |
2|      |  2|      |  2|      |
1|      |  1|      |  1|      |
 +------+   +------+   +------+
    8L         5L         3L

You can:
  (F)ill the bucket
  (E)mpty the bucket
  (P)our one bucket into another
  (Q)uit
&amp;gt; f
Select a bucket 8, 5, 3, or QUIT:
&amp;gt; 5

Try to get 4L of water into one of these
buckets:

8|      |
7|      |
6|      |
5|      |  5|WWWWWW|
4|      |  4|WWWWWW|
3|      |  3|WWWWWW|  3|      |
2|      |  2|WWWWWW|  2|      |
1|      |  1|WWWWWW|  1|      |
 +------+   +------+   +------+
    8L         5L         3L
--snip--
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id="Sonar-Treasure-Hunt" /&gt;&lt;/p&gt;
&lt;h3&gt;Sonar Treasure Hunt&lt;/h3&gt;
&lt;p&gt;In &lt;a href="https://inventwithpython.com/invent4thed/chapter13.html"&gt;Sonar Treasure Hunt&lt;/a&gt;, the player tries to discover the position of 3 treasure chests in the sea, and the player has only 20 sonar devices to use to find them. When placed, they give the distance to the closest chest.&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;012345678901234567890123456789012345678901234567890123456789&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`````&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`````&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~~~&lt;/span&gt;&lt;span class="err"&gt;`````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;``````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;````````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;````````&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~~~&lt;/span&gt;&lt;span class="err"&gt;``````&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;`````&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;
&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~~~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;
&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;````````&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~~~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;
&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;012345678901234567890123456789012345678901234567890123456789&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="nx"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sonar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;device&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;treasure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;chest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nx"&gt;Where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;drop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sonar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;device&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;012345678901234567890123456789012345678901234567890123456789&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`````&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`````&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~~~&lt;/span&gt;&lt;span class="err"&gt;`````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;````````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;````````&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~~~&lt;/span&gt;&lt;span class="err"&gt;``````&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;`````&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;
&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~~~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~~~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;
&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;````````&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~~~~~~~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;````&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;
&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;``&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;``````&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This program is from &lt;a href="https://inventwithpython.com/invent4thed/"&gt;Invent Your Own Computer Games with Python&lt;/a&gt;.
&lt;a id="Reversegam-/-Reversi-/-Othello" /&gt;&lt;/p&gt;
&lt;h3&gt;Reversegam / Reversi / Othello&lt;/h3&gt;
&lt;p&gt;Reversegam, also known as Reversi or Othello. This two-player board game is played on a grid, so we’ll use a Cartesian coordinate system with x- and y-coordinates. This AI is so good that it will probably beat you almost every time you play. (I lose whenever I play against it!)&lt;/p&gt;
&lt;p&gt;For example, when the program runs it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;O&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;12345678&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+--------+&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;XO&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;OX&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+--------+&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;12345678&lt;/span&gt;
&lt;span class="nl"&gt;You&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;Computer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;quit&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;hints&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mi"&gt;53&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;12345678&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+--------+&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;XX&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;OX&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+--------+&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;12345678&lt;/span&gt;
&lt;span class="nl"&gt;You&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;Computer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Press&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;see&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;computer&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="c1"&gt;--snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This program is from &lt;a href="https://inventwithpython.com/invent4thed/"&gt;Invent Your Own Computer Games with Python&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a id="Transposition-Cipher" /&gt;&lt;/p&gt;
&lt;h3&gt;Transposition Cipher&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/cracking/chapter7.html"&gt;The transposition cipher&lt;/a&gt; is more difficult to brute-force because the number of possible keys depends on the message’s length. There are many different types of transposition ciphers, including the rail fence cipher, route cipher, Myszkowski transposition cipher, and disrupted transposition cipher. This program is the columnar transposition cipher.&lt;/p&gt;
&lt;p&gt;The transposition cipher doesn't replace characters when it encrypts them, but rather scrambles their order in such a way that can only be &lt;a href="https://inventwithpython.com/cracking/chapter8.html"&gt;decrypted&lt;/a&gt; if you have the right key.&lt;/p&gt;
&lt;p&gt;This program is from &lt;a href="https://inventwithpython.com/cracking/"&gt;Cracking Codes with Python&lt;/a&gt;.
&lt;a id="Transposition-Cipher-Hacker-/-English-Detection" /&gt;&lt;/p&gt;
&lt;h3&gt;Transposition Cipher Hacker / English Detection&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/cracking/chapter12.html"&gt;Hacking the transposition cipher&lt;/a&gt; is a matter of running through the many keys and finding the key that decrypts to &lt;a href="https://inventwithpython.com/cracking/chapter11.html"&gt;detectable English&lt;/a&gt;. You'll need a program that uses a &lt;a href="https://www.nostarch.com/crackingcodes/"&gt;dictionary file (which you can download from the No Starch Press page for my book)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This program is from &lt;a href="https://inventwithpython.com/cracking/"&gt;Cracking Codes with Python&lt;/a&gt;.
&lt;a id="Affine-Cipher" /&gt;&lt;/p&gt;
&lt;h3&gt;Affine Cipher&lt;/h3&gt;
&lt;p&gt;Once you learned the &lt;a href="https://inventwithpython.com/cracking/chapter13.html"&gt;basics of modular arithmetic&lt;/a&gt;, you can create an &lt;a href="https://inventwithpython.com/cracking/chapter14.html"&gt;affine cipher program&lt;/a&gt;. The affine cipher is similar to the Caesar cipher, but it's more complicated key generation makes it more secure. Still, it is possible to &lt;a href="https://inventwithpython.com/cracking/chapter15.html"&gt;hack the affine cipher&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This program is from &lt;a href="https://inventwithpython.com/cracking/"&gt;Cracking Codes with Python&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a id="Frequency-Analysis-/-Vigenere-Cipher-Hacker" /&gt;&lt;/p&gt;
&lt;h3&gt;Frequency Analysis / Vigenere Cipher Hacker&lt;/h3&gt;
&lt;p&gt;Writing programs that &lt;a href="https://inventwithpython.com/cracking/chapter19.html"&gt;can determine the letter frequencies in messages&lt;/a&gt; is the first step towards &lt;a href="https://inventwithpython.com/cracking/chapter20.html"&gt;hacking the Vigenere cipher&lt;/a&gt;. This approach was used by 19th-century mathematician Charles Babbage to break the previously-thought unbreakable Vigenere cipher.&lt;/p&gt;
&lt;p&gt;This program is from &lt;a href="https://inventwithpython.com/cracking/"&gt;Cracking Codes with Python&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a id="Public-Key-Cryptography-/-Large-Prime-Number-Generation" /&gt;&lt;/p&gt;
&lt;h3&gt;Public Key Cryptography / Large Prime Number Generation&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://inventwithpython.com/cracking/chapter24.html"&gt;Public-key cryptography&lt;/a&gt; uses pairs of encryption keys. Each key pair consists of a public key and a corresponding private key. Security of public-key cryptography depends on keeping the private key secret; the public key can be openly distributed without compromising security.&lt;/p&gt;
&lt;p&gt;You'll need to generate &lt;a href="https://inventwithpython.com/cracking/chapter22.html"&gt;large prime numbers&lt;/a&gt; to use during &lt;a href="https://inventwithpython.com/cracking/chapter23.html"&gt;key generation&lt;/a&gt;. While the math is a little tricky, this is how real-world cryptography works. (Though the programs you make are not suitable for actual use; they're great for a learning exercise though.)&lt;/p&gt;
&lt;p&gt;For example, running the public key encryption keys could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Making key files...
Generating p prime...
Generating q prime...
Generating e that is relatively prime to (p-1)*(q-1)...
Calculating d that is mod inverse of e...
Public key: (210902406316700502401968491406579417405090396754616926135810621
2161161913380865678407459875355468897928072386270510720443827324671435893274
8583937496850624116776147241821152026946322876869404394483922202407821672864
2424789208131826990008473526711744296548563866768454251404951960805224682425
498975230488955908086491852116348777849536270685085446970952915640050522212
204221803744494065881010331486468305317449607027884787770315729959789994713
265311327663776167710077018340036668306612665759417207845823479903440572724
068125211002329298338718615859542093721097258263595617482450199200740185492
04468791300114315056117093, 17460230769175161021731845459236833553832403910869
129054954200373678580935247606622265764388235752176654737805849023006544732896
308685513669509917451195822611398098951306676600958889189564599581456460070270
393693277683404354811575681605990659145317074127084557233537504102479937142530
0216777273298110097435989)
Private key: (21090240631670050240196849140657941740509039675461692613581062
1216116191338086567840745987535546889792807238627051072044382732467143589327
4858393749685062411677614724182115202694632287686940439448392220240782167286
4242478920813182699000847352671174429654856386676845425140495196080522468242
5498975230488955908086491852116348777849536270685085446970952915640050522212
204221803744494065881010331486468305317449607027884787770315729959789994713
265311327663776167710077018340036668306612665759417207845823479903440572724
068125211002329298338718615859542093721097258263595617482450199200740185492
04468791300114315056117093, 47676735798137710412166884916983765043173120289416
904341295971552286870991874666099933371008075948549008551224760695942666962465
968168995404993934508399014283053710676760835948902312888639938402686187075052
360773062364162664276144965652558545331166681735980981384493349313058750259417
68372702963348445191139635826000818122373486213256488077192893119257248107794
25681884603640028673273135292831170178614206817165802812291528319562200625082
55726168047084560706359601833919317974375031636011432177696164717000025430368
26990539739057474642785416933878499897014777481407371328053001838085314443545
845219087249544663398589)
The public key is a 617 and a 309 digit number.
Writing public key to file al_sweigart_pubkey.txt...
The private key is a 617 and a 309 digit number.
Writing private key to file al_sweigart_privkey.txt...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This program is from &lt;a href="https://inventwithpython.com/cracking/"&gt;Cracking Codes with Python&lt;/a&gt;.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Web version of the 1985 'Maze' book by Christopher Manson, an inspiration for Blue Prince</title><link href="https://inventwithpython.com/blog/the-maze-online.html" rel="alternate"></link><published>2025-09-11T10:00:00-04:00</published><updated>2025-09-11T10:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-09-11:/blog/the-maze-online.html</id><content type="html">&lt;p&gt;I wrote some Python scripts to help me create the HTML web pages for an online version of Maze, a 1985 visual puzzle book by Christopher Manson that was a large inspiration for &lt;a href="https://store.steampowered.com/app/1569580/Blue_Prince/"&gt;Blue Prince&lt;/a&gt; (my personal game of the year for 2025). I'm hosting it at &lt;a href="https://inventwithpython.com/mazewebsite/"&gt;https://inventwithpython.com/mazewebsite/&lt;/a&gt;&lt;br&gt;&lt;img src="/mazewebsite/images/frontcover_thumb.jpg" style="max-width:100%" alt="Book cover of Maze by Christopher Mason"&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Recursive Fract-Al</title><link href="https://inventwithpython.com/blog/recursive-fract-al.html" rel="alternate"></link><published>2025-09-10T10:00:00-04:00</published><updated>2025-09-10T10:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-09-10:/blog/recursive-fract-al.html</id><content type="html">&lt;p&gt;For Halloween 2024, I made a Recursive Fract-Al costume. It was literally an exponential amount of work! The idea came to me when I was writing &lt;a href="https://inventwithpython.com/recursion/"&gt;The Recursive Book of Recursion&lt;/a&gt;&lt;br&gt;&lt;a href="/blogstatic/recursive-fract-al.jpeg"&gt;&lt;img src="/blogstatic/recursive-fract-al.jpeg" style="max-width: 100%" alt="Photo of Al Sweigart in a red flannel and brown cap, his arms holding up posterboard with red flannel wrapping paper and a printout of his head with brown cap. Both fork into two more arms of red flannel with a smaller printout of his head. This goes on for five recursive layers until they end with the smallest photos of his head."&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>A Complete List of Python Tkinter Colors, Valid and Tested</title><link href="https://inventwithpython.com/blog/complete-list-tkinter-colors-valid-and-tested.html" rel="alternate"></link><published>2025-09-09T10:57:00-04:00</published><updated>2025-09-09T10:57:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-09-09:/blog/complete-list-tkinter-colors-valid-and-tested.html</id><summary type="html">&lt;p&gt;I needed a complete list of valid color names for Python's Tkinter package as part of my &lt;a href="https://pypi.org/project/ButtonPad/"&gt;ButtonPad&lt;/a&gt; GUI framework development. The lists I found on the internet were either incomplete, buried under ads, and often just plain wrong. Here's a list of all 760 color names (valid and personally tested) for Python Tkinter.&lt;/p&gt;</summary><content type="html">&lt;p&gt;I needed a complete list of valid color names for Python's Tkinter package as part of my &lt;a href="https://pypi.org/project/ButtonPad/"&gt;ButtonPad&lt;/a&gt; GUI framework development. The lists I found on the internet were either incomplete, buried under ads, and often just plain wrong. Here's a list of all 760 color names (valid and personally tested) for Python Tkinter.&lt;/p&gt;
&lt;p&gt;The color names are case insensitive, meaning that &lt;code&gt;'aliceblue'&lt;/code&gt; and &lt;code&gt;'ALICEBLUE'&lt;/code&gt; both work fine. Some color names also have spaces, such as &lt;code&gt;'alice blue'&lt;/code&gt; but the spaces are significant: &lt;code&gt;'a lice bl ue'&lt;/code&gt; is not a valid color string.&lt;/p&gt;
&lt;p&gt;Eventually, I was able to find &lt;a href="https://www.tcl-lang.org/man/tcl8.5/TkCmd/colors.htm"&gt;the full list of colors in the Tcl/Tk documentation.&lt;/a&gt; However, even the official docs aren't perfect: there's a typo and "agua" and "crymson" are listed as colors instead of "aqua" and "crimson".&lt;/p&gt;
&lt;p&gt;This Wellesley College CS page has &lt;a href="https://cs111.wellesley.edu/archive/cs111_fall14/public_html/labs/lab12/tkintercolor.html"&gt;a nice compact chart of all the colors.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you want all this data as a Python list of tuples, it's available in this text box:&lt;/p&gt;
&lt;textarea rows="5" style="width: 400px; max-width:100%"&gt;
(
('alice blue', '#f0f8ff', (240, 248, 255)),
('AliceBlue', '#f0f8ff', (240, 248, 255)),
('antique white', '#faebd7', (250, 235, 215)),
('AntiqueWhite', '#faebd7', (250, 235, 215)),
('AntiqueWhite1', '#ffefdb', (255, 239, 219)),
('AntiqueWhite2', '#eedfcc', (238, 223, 204)),
('AntiqueWhite3', '#cdc0b0', (205, 192, 176)),
('AntiqueWhite4', '#8b8378', (139, 131, 120)),
('aqua', '#00ffff', (0, 255, 255)),
('aquamarine', '#7fffd4', (127, 255, 212)),
('aquamarine1', '#7fffd4', (127, 255, 212)),
('aquamarine2', '#76eec6', (118, 238, 198)),
('aquamarine3', '#66cdaa', (102, 205, 170)),
('aquamarine4', '#458b74', (69, 139, 116)),
('azure', '#f0ffff', (240, 255, 255)),
('azure1', '#f0ffff', (240, 255, 255)),
('azure2', '#e0eeee', (224, 238, 238)),
('azure3', '#c1cdcd', (193, 205, 205)),
('azure4', '#838b8b', (131, 139, 139)),
('beige', '#f5f5dc', (245, 245, 220)),
('bisque', '#ffe4c4', (255, 228, 196)),
('bisque1', '#ffe4c4', (255, 228, 196)),
('bisque2', '#eed5b7', (238, 213, 183)),
('bisque3', '#cdb79e', (205, 183, 158)),
('bisque4', '#8b7d6b', (139, 125, 107)),
('black', '#000000', (0, 0, 0)),
('blanched almond', '#ffebcd', (255, 235, 205)),
('BlanchedAlmond', '#ffebcd', (255, 235, 205)),
('blue', '#0000ff', (0, 0, 255)),
('blue violet', '#8a2be2', (138, 43, 226)),
('blue1', '#0000ff', (0, 0, 255)),
('blue2', '#0000ee', (0, 0, 238)),
('blue3', '#0000cd', (0, 0, 205)),
('blue4', '#00008b', (0, 0, 139)),
('BlueViolet', '#8a2be2', (138, 43, 226)),
('brown', '#a52a2a', (165, 42, 42)),
('brown1', '#ff4040', (255, 64, 64)),
('brown2', '#ee3b3b', (238, 59, 59)),
('brown3', '#cd3333', (205, 51, 51)),
('brown4', '#8b2323', (139, 35, 35)),
('burlywood', '#deb887', (222, 184, 135)),
('burlywood1', '#ffd39b', (255, 211, 155)),
('burlywood2', '#eec591', (238, 197, 145)),
('burlywood3', '#cdaa7d', (205, 170, 125)),
('burlywood4', '#8b7355', (139, 115, 85)),
('cadet blue', '#5f9ea0', (95, 158, 160)),
('CadetBlue', '#5f9ea0', (95, 158, 160)),
('CadetBlue1', '#98f5ff', (152, 245, 255)),
('CadetBlue2', '#8ee5ee', (142, 229, 238)),
('CadetBlue3', '#7ac5cd', (122, 197, 205)),
('CadetBlue4', '#53868b', (83, 134, 139)),
('chartreuse', '#7fff00', (127, 255, 0)),
('chartreuse1', '#7fff00', (127, 255, 0)),
('chartreuse2', '#76ee00', (118, 238, 0)),
('chartreuse3', '#66cd00', (102, 205, 0)),
('chartreuse4', '#458b00', (69, 139, 0)),
('chocolate', '#d2691e', (210, 105, 30)),
('chocolate1', '#ff7f24', (255, 127, 36)),
('chocolate2', '#ee7621', (238, 118, 33)),
('chocolate3', '#cd661d', (205, 102, 29)),
('chocolate4', '#8b4513', (139, 69, 19)),
('coral', '#ff7f50', (255, 127, 80)),
('coral1', '#ff7256', (255, 114, 86)),
('coral2', '#ee6a50', (238, 106, 80)),
('coral3', '#cd5b45', (205, 91, 69)),
('coral4', '#8b3e2f', (139, 62, 47)),
('cornflower blue', '#6495ed', (100, 149, 237)),
('CornflowerBlue', '#6495ed', (100, 149, 237)),
('cornsilk', '#fff8dc', (255, 248, 220)),
('cornsilk1', '#fff8dc', (255, 248, 220)),
('cornsilk2', '#eee8cd', (238, 232, 205)),
('cornsilk3', '#cdc8b1', (205, 200, 177)),
('cornsilk4', '#8b8878', (139, 136, 120)),
('crimson', '#dc143c', (220, 20, 60)),
('cyan', '#00ffff', (0, 255, 255)),
('cyan1', '#00ffff', (0, 255, 255)),
('cyan2', '#00eeee', (0, 238, 238)),
('cyan3', '#00cdcd', (0, 205, 205)),
('cyan4', '#008b8b', (0, 139, 139)),
('dark blue', '#00008b', (0, 0, 139)),
('dark cyan', '#008b8b', (0, 139, 139)),
('dark goldenrod', '#b8860b', (184, 134, 11)),
('dark gray', '#a9a9a9', (169, 169, 169)),
('dark green', '#006400', (0, 100, 0)),
('dark grey', '#a9a9a9', (169, 169, 169)),
('dark khaki', '#bdb76b', (189, 183, 107)),
('dark magenta', '#8b008b', (139, 0, 139)),
('dark olive green', '#556b2f', (85, 107, 47)),
('dark orange', '#ff8c00', (255, 140, 0)),
('dark orchid', '#9932cc', (153, 50, 204)),
('dark red', '#8b0000', (139, 0, 0)),
('dark salmon', '#e9967a', (233, 150, 122)),
('dark sea green', '#8fbc8f', (143, 188, 143)),
('dark slate blue', '#483d8b', (72, 61, 139)),
('dark slate gray', '#2f4f4f', (47, 79, 79)),
('dark slate grey', '#2f4f4f', (47, 79, 79)),
('dark turquoise', '#00ced1', (0, 206, 209)),
('dark violet', '#9400d3', (148, 0, 211)),
('DarkBlue', '#00008b', (0, 0, 139)),
('DarkCyan', '#008b8b', (0, 139, 139)),
('DarkGoldenrod', '#b8860b', (184, 134, 11)),
('DarkGoldenrod1', '#ffb90f', (255, 185, 15)),
('DarkGoldenrod2', '#eead0e', (238, 173, 14)),
('DarkGoldenrod3', '#cd950c', (205, 149, 12)),
('DarkGoldenrod4', '#8b6508', (139, 101, 8)),
('DarkGray', '#a9a9a9', (169, 169, 169)),
('DarkGreen', '#006400', (0, 100, 0)),
('DarkGrey', '#a9a9a9', (169, 169, 169)),
('DarkKhaki', '#bdb76b', (189, 183, 107)),
('DarkMagenta', '#8b008b', (139, 0, 139)),
('DarkOliveGreen', '#556b2f', (85, 107, 47)),
('DarkOliveGreen1', '#caff70', (202, 255, 112)),
('DarkOliveGreen2', '#bcee68', (188, 238, 104)),
('DarkOliveGreen3', '#a2cd5a', (162, 205, 90)),
('DarkOliveGreen4', '#6e8b3d', (110, 139, 61)),
('DarkOrange', '#ff8c00', (255, 140, 0)),
('DarkOrange1', '#ff7f00', (255, 127, 0)),
('DarkOrange2', '#ee7600', (238, 118, 0)),
('DarkOrange3', '#cd6600', (205, 102, 0)),
('DarkOrange4', '#8b4500', (139, 69, 0)),
('DarkOrchid', '#9932cc', (153, 50, 204)),
('DarkOrchid1', '#bf3eff', (191, 62, 255)),
('DarkOrchid2', '#b23aee', (178, 58, 238)),
('DarkOrchid3', '#9a32cd', (154, 50, 205)),
('DarkOrchid4', '#68228b', (104, 34, 139)),
('DarkRed', '#8b0000', (139, 0, 0)),
('DarkSalmon', '#e9967a', (233, 150, 122)),
('DarkSeaGreen', '#8fbc8f', (143, 188, 143)),
('DarkSeaGreen1', '#c1ffc1', (193, 255, 193)),
('DarkSeaGreen2', '#b4eeb4', (180, 238, 180)),
('DarkSeaGreen3', '#9bcd9b', (155, 205, 155)),
('DarkSeaGreen4', '#698b69', (105, 139, 105)),
('DarkSlateBlue', '#483d8b', (72, 61, 139)),
('DarkSlateGray', '#2f4f4f', (47, 79, 79)),
('DarkSlateGray1', '#97ffff', (151, 255, 255)),
('DarkSlateGray2', '#8deeee', (141, 238, 238)),
('DarkSlateGray3', '#79cdcd', (121, 205, 205)),
('DarkSlateGray4', '#528b8b', (82, 139, 139)),
('DarkSlateGrey', '#2f4f4f', (47, 79, 79)),
('DarkTurquoise', '#00ced1', (0, 206, 209)),
('DarkViolet', '#9400d3', (148, 0, 211)),
('deep pink', '#ff1493', (255, 20, 147)),
('deep sky blue', '#00bfff', (0, 191, 255)),
('DeepPink', '#ff1493', (255, 20, 147)),
('DeepPink1', '#ff1493', (255, 20, 147)),
('DeepPink2', '#ee1289', (238, 18, 137)),
('DeepPink3', '#cd1076', (205, 16, 118)),
('DeepPink4', '#8b0a50', (139, 10, 80)),
('DeepSkyBlue', '#00bfff', (0, 191, 255)),
('DeepSkyBlue1', '#00bfff', (0, 191, 255)),
('DeepSkyBlue2', '#00b2ee', (0, 178, 238)),
('DeepSkyBlue3', '#009acd', (0, 154, 205)),
('DeepSkyBlue4', '#00688b', (0, 104, 139)),
('dim gray', '#696969', (105, 105, 105)),
('dim grey', '#696969', (105, 105, 105)),
('DimGray', '#696969', (105, 105, 105)),
('DimGrey', '#696969', (105, 105, 105)),
('dodger blue', '#1e90ff', (30, 144, 255)),
('DodgerBlue', '#1e90ff', (30, 144, 255)),
('DodgerBlue1', '#1e90ff', (30, 144, 255)),
('DodgerBlue2', '#1c86ee', (28, 134, 238)),
('DodgerBlue3', '#1874cd', (24, 116, 205)),
('DodgerBlue4', '#104e8b', (16, 78, 139)),
('firebrick', '#b22222', (178, 34, 34)),
('firebrick1', '#ff3030', (255, 48, 48)),
('firebrick2', '#ee2c2c', (238, 44, 44)),
('firebrick3', '#cd2626', (205, 38, 38)),
('firebrick4', '#8b1a1a', (139, 26, 26)),
('floral white', '#fffaf0', (255, 250, 240)),
('FloralWhite', '#fffaf0', (255, 250, 240)),
('forest green', '#228b22', (34, 139, 34)),
('ForestGreen', '#228b22', (34, 139, 34)),
('fuchsia', '#ff00ff', (255, 0, 255)),
('gainsboro', '#dcdcdc', (220, 220, 220)),
('ghost white', '#f8f8ff', (248, 248, 255)),
('GhostWhite', '#f8f8ff', (248, 248, 255)),
('gold', '#ffd700', (255, 215, 0)),
('gold1', '#ffd700', (255, 215, 0)),
('gold2', '#eec900', (238, 201, 0)),
('gold3', '#cdad00', (205, 173, 0)),
('gold4', '#8b7500', (139, 117, 0)),
('goldenrod', '#daa520', (218, 165, 32)),
('goldenrod1', '#ffc125', (255, 193, 37)),
('goldenrod2', '#eeb422', (238, 180, 34)),
('goldenrod3', '#cd9b1d', (205, 155, 29)),
('goldenrod4', '#8b6914', (139, 105, 20)),
('gray', '#808080', (128, 128, 128)),
('gray0', '#000000', (0, 0, 0)),
('gray1', '#030303', (3, 3, 3)),
('gray2', '#050505', (5, 5, 5)),
('gray3', '#080808', (8, 8, 8)),
('gray4', '#0a0a0a', (10, 10, 10)),
('gray5', '#0d0d0d', (13, 13, 13)),
('gray6', '#0f0f0f', (15, 15, 15)),
('gray7', '#121212', (18, 18, 18)),
('gray8', '#141414', (20, 20, 20)),
('gray9', '#171717', (23, 23, 23)),
('gray10', '#1a1a1a', (26, 26, 26)),
('gray11', '#1c1c1c', (28, 28, 28)),
('gray12', '#1f1f1f', (31, 31, 31)),
('gray13', '#212121', (33, 33, 33)),
('gray14', '#242424', (36, 36, 36)),
('gray15', '#262626', (38, 38, 38)),
('gray16', '#292929', (41, 41, 41)),
('gray17', '#2b2b2b', (43, 43, 43)),
('gray18', '#2e2e2e', (46, 46, 46)),
('gray19', '#303030', (48, 48, 48)),
('gray20', '#333333', (51, 51, 51)),
('gray21', '#363636', (54, 54, 54)),
('gray22', '#383838', (56, 56, 56)),
('gray23', '#3b3b3b', (59, 59, 59)),
('gray24', '#3d3d3d', (61, 61, 61)),
('gray25', '#404040', (64, 64, 64)),
('gray26', '#424242', (66, 66, 66)),
('gray27', '#454545', (69, 69, 69)),
('gray28', '#474747', (71, 71, 71)),
('gray29', '#4a4a4a', (74, 74, 74)),
('gray30', '#4d4d4d', (77, 77, 77)),
('gray31', '#4f4f4f', (79, 79, 79)),
('gray32', '#525252', (82, 82, 82)),
('gray33', '#545454', (84, 84, 84)),
('gray34', '#575757', (87, 87, 87)),
('gray35', '#595959', (89, 89, 89)),
('gray36', '#5c5c5c', (92, 92, 92)),
('gray37', '#5e5e5e', (94, 94, 94)),
('gray38', '#616161', (97, 97, 97)),
('gray39', '#636363', (99, 99, 99)),
('gray40', '#666666', (102, 102, 102)),
('gray41', '#696969', (105, 105, 105)),
('gray42', '#6b6b6b', (107, 107, 107)),
('gray43', '#6e6e6e', (110, 110, 110)),
('gray44', '#707070', (112, 112, 112)),
('gray45', '#737373', (115, 115, 115)),
('gray46', '#757575', (117, 117, 117)),
('gray47', '#787878', (120, 120, 120)),
('gray48', '#7a7a7a', (122, 122, 122)),
('gray49', '#7d7d7d', (125, 125, 125)),
('gray50', '#7f7f7f', (127, 127, 127)),
('gray51', '#828282', (130, 130, 130)),
('gray52', '#858585', (133, 133, 133)),
('gray53', '#878787', (135, 135, 135)),
('gray54', '#8a8a8a', (138, 138, 138)),
('gray55', '#8c8c8c', (140, 140, 140)),
('gray56', '#8f8f8f', (143, 143, 143)),
('gray57', '#919191', (145, 145, 145)),
('gray58', '#949494', (148, 148, 148)),
('gray59', '#969696', (150, 150, 150)),
('gray60', '#999999', (153, 153, 153)),
('gray61', '#9c9c9c', (156, 156, 156)),
('gray62', '#9e9e9e', (158, 158, 158)),
('gray63', '#a1a1a1', (161, 161, 161)),
('gray64', '#a3a3a3', (163, 163, 163)),
('gray65', '#a6a6a6', (166, 166, 166)),
('gray66', '#a8a8a8', (168, 168, 168)),
('gray67', '#ababab', (171, 171, 171)),
('gray68', '#adadad', (173, 173, 173)),
('gray69', '#b0b0b0', (176, 176, 176)),
('gray70', '#b3b3b3', (179, 179, 179)),
('gray71', '#b5b5b5', (181, 181, 181)),
('gray72', '#b8b8b8', (184, 184, 184)),
('gray73', '#bababa', (186, 186, 186)),
('gray74', '#bdbdbd', (189, 189, 189)),
('gray75', '#bfbfbf', (191, 191, 191)),
('gray76', '#c2c2c2', (194, 194, 194)),
('gray77', '#c4c4c4', (196, 196, 196)),
('gray78', '#c7c7c7', (199, 199, 199)),
('gray79', '#c9c9c9', (201, 201, 201)),
('gray80', '#cccccc', (204, 204, 204)),
('gray81', '#cfcfcf', (207, 207, 207)),
('gray82', '#d1d1d1', (209, 209, 209)),
('gray83', '#d4d4d4', (212, 212, 212)),
('gray84', '#d6d6d6', (214, 214, 214)),
('gray85', '#d9d9d9', (217, 217, 217)),
('gray86', '#dbdbdb', (219, 219, 219)),
('gray87', '#dedede', (222, 222, 222)),
('gray88', '#e0e0e0', (224, 224, 224)),
('gray89', '#e3e3e3', (227, 227, 227)),
('gray90', '#e5e5e5', (229, 229, 229)),
('gray91', '#e8e8e8', (232, 232, 232)),
('gray92', '#ebebeb', (235, 235, 235)),
('gray93', '#ededed', (237, 237, 237)),
('gray94', '#f0f0f0', (240, 240, 240)),
('gray95', '#f2f2f2', (242, 242, 242)),
('gray96', '#f5f5f5', (245, 245, 245)),
('gray97', '#f7f7f7', (247, 247, 247)),
('gray98', '#fafafa', (250, 250, 250)),
('gray99', '#fcfcfc', (252, 252, 252)),
('gray100', '#ffffff', (255, 255, 255)),
('green', '#008000', (0, 128, 0)),
('green yellow', '#adff2f', (173, 255, 47)),
('green1', '#00ff00', (0, 255, 0)),
('green2', '#00ee00', (0, 238, 0)),
('green3', '#00cd00', (0, 205, 0)),
('green4', '#008b00', (0, 139, 0)),
('GreenYellow', '#adff2f', (173, 255, 47)),
('grey', '#808080', (128, 128, 128)),
('grey0', '#000000', (0, 0, 0)),
('grey1', '#030303', (3, 3, 3)),
('grey2', '#050505', (5, 5, 5)),
('grey3', '#080808', (8, 8, 8)),
('grey4', '#0a0a0a', (10, 10, 10)),
('grey5', '#0d0d0d', (13, 13, 13)),
('grey6', '#0f0f0f', (15, 15, 15)),
('grey7', '#121212', (18, 18, 18)),
('grey8', '#141414', (20, 20, 20)),
('grey9', '#171717', (23, 23, 23)),
('grey10', '#1a1a1a', (26, 26, 26)),
('grey11', '#1c1c1c', (28, 28, 28)),
('grey12', '#1f1f1f', (31, 31, 31)),
('grey13', '#212121', (33, 33, 33)),
('grey14', '#242424', (36, 36, 36)),
('grey15', '#262626', (38, 38, 38)),
('grey16', '#292929', (41, 41, 41)),
('grey17', '#2b2b2b', (43, 43, 43)),
('grey18', '#2e2e2e', (46, 46, 46)),
('grey19', '#303030', (48, 48, 48)),
('grey20', '#333333', (51, 51, 51)),
('grey21', '#363636', (54, 54, 54)),
('grey22', '#383838', (56, 56, 56)),
('grey23', '#3b3b3b', (59, 59, 59)),
('grey24', '#3d3d3d', (61, 61, 61)),
('grey25', '#404040', (64, 64, 64)),
('grey26', '#424242', (66, 66, 66)),
('grey27', '#454545', (69, 69, 69)),
('grey28', '#474747', (71, 71, 71)),
('grey29', '#4a4a4a', (74, 74, 74)),
('grey30', '#4d4d4d', (77, 77, 77)),
('grey31', '#4f4f4f', (79, 79, 79)),
('grey32', '#525252', (82, 82, 82)),
('grey33', '#545454', (84, 84, 84)),
('grey34', '#575757', (87, 87, 87)),
('grey35', '#595959', (89, 89, 89)),
('grey36', '#5c5c5c', (92, 92, 92)),
('grey37', '#5e5e5e', (94, 94, 94)),
('grey38', '#616161', (97, 97, 97)),
('grey39', '#636363', (99, 99, 99)),
('grey40', '#666666', (102, 102, 102)),
('grey41', '#696969', (105, 105, 105)),
('grey42', '#6b6b6b', (107, 107, 107)),
('grey43', '#6e6e6e', (110, 110, 110)),
('grey44', '#707070', (112, 112, 112)),
('grey45', '#737373', (115, 115, 115)),
('grey46', '#757575', (117, 117, 117)),
('grey47', '#787878', (120, 120, 120)),
('grey48', '#7a7a7a', (122, 122, 122)),
('grey49', '#7d7d7d', (125, 125, 125)),
('grey50', '#7f7f7f', (127, 127, 127)),
('grey51', '#828282', (130, 130, 130)),
('grey52', '#858585', (133, 133, 133)),
('grey53', '#878787', (135, 135, 135)),
('grey54', '#8a8a8a', (138, 138, 138)),
('grey55', '#8c8c8c', (140, 140, 140)),
('grey56', '#8f8f8f', (143, 143, 143)),
('grey57', '#919191', (145, 145, 145)),
('grey58', '#949494', (148, 148, 148)),
('grey59', '#969696', (150, 150, 150)),
('grey60', '#999999', (153, 153, 153)),
('grey61', '#9c9c9c', (156, 156, 156)),
('grey62', '#9e9e9e', (158, 158, 158)),
('grey63', '#a1a1a1', (161, 161, 161)),
('grey64', '#a3a3a3', (163, 163, 163)),
('grey65', '#a6a6a6', (166, 166, 166)),
('grey66', '#a8a8a8', (168, 168, 168)),
('grey67', '#ababab', (171, 171, 171)),
('grey68', '#adadad', (173, 173, 173)),
('grey69', '#b0b0b0', (176, 176, 176)),
('grey70', '#b3b3b3', (179, 179, 179)),
('grey71', '#b5b5b5', (181, 181, 181)),
('grey72', '#b8b8b8', (184, 184, 184)),
('grey73', '#bababa', (186, 186, 186)),
('grey74', '#bdbdbd', (189, 189, 189)),
('grey75', '#bfbfbf', (191, 191, 191)),
('grey76', '#c2c2c2', (194, 194, 194)),
('grey77', '#c4c4c4', (196, 196, 196)),
('grey78', '#c7c7c7', (199, 199, 199)),
('grey79', '#c9c9c9', (201, 201, 201)),
('grey80', '#cccccc', (204, 204, 204)),
('grey81', '#cfcfcf', (207, 207, 207)),
('grey82', '#d1d1d1', (209, 209, 209)),
('grey83', '#d4d4d4', (212, 212, 212)),
('grey84', '#d6d6d6', (214, 214, 214)),
('grey85', '#d9d9d9', (217, 217, 217)),
('grey86', '#dbdbdb', (219, 219, 219)),
('grey87', '#dedede', (222, 222, 222)),
('grey88', '#e0e0e0', (224, 224, 224)),
('grey89', '#e3e3e3', (227, 227, 227)),
('grey90', '#e5e5e5', (229, 229, 229)),
('grey91', '#e8e8e8', (232, 232, 232)),
('grey92', '#ebebeb', (235, 235, 235)),
('grey93', '#ededed', (237, 237, 237)),
('grey94', '#f0f0f0', (240, 240, 240)),
('grey95', '#f2f2f2', (242, 242, 242)),
('grey96', '#f5f5f5', (245, 245, 245)),
('grey97', '#f7f7f7', (247, 247, 247)),
('grey98', '#fafafa', (250, 250, 250)),
('grey99', '#fcfcfc', (252, 252, 252)),
('grey100', '#ffffff', (255, 255, 255)),
('honeydew', '#f0fff0', (240, 255, 240)),
('honeydew1', '#f0fff0', (240, 255, 240)),
('honeydew2', '#e0eee0', (224, 238, 224)),
('honeydew3', '#c1cdc1', (193, 205, 193)),
('honeydew4', '#838b83', (131, 139, 131)),
('hot pink', '#ff69b4', (255, 105, 180)),
('HotPink', '#ff69b4', (255, 105, 180)),
('HotPink1', '#ff6eb4', (255, 110, 180)),
('HotPink2', '#ee6aa7', (238, 106, 167)),
('HotPink3', '#cd6090', (205, 96, 144)),
('HotPink4', '#8b3a62', (139, 58, 98)),
('indian red', '#cd5c5c', (205, 92, 92)),
('IndianRed', '#cd5c5c', (205, 92, 92)),
('IndianRed1', '#ff6a6a', (255, 106, 106)),
('IndianRed2', '#ee6363', (238, 99, 99)),
('IndianRed3', '#cd5555', (205, 85, 85)),
('IndianRed4', '#8b3a3a', (139, 58, 58)),
('indigo', '#4b0082', (75, 0, 130)),
('ivory', '#fffff0', (255, 255, 240)),
('ivory1', '#fffff0', (255, 255, 240)),
('ivory2', '#eeeee0', (238, 238, 224)),
('ivory3', '#cdcdc1', (205, 205, 193)),
('ivory4', '#8b8b83', (139, 139, 131)),
('khaki', '#f0e68c', (240, 230, 140)),
('khaki1', '#fff68f', (255, 246, 143)),
('khaki2', '#eee685', (238, 230, 133)),
('khaki3', '#cdc673', (205, 198, 115)),
('khaki4', '#8b864e', (139, 134, 78)),
('lavender', '#e6e6fa', (230, 230, 250)),
('lavender blush', '#fff0f5', (255, 240, 245)),
('LavenderBlush', '#fff0f5', (255, 240, 245)),
('LavenderBlush1', '#fff0f5', (255, 240, 245)),
('LavenderBlush2', '#eee0e5', (238, 224, 229)),
('LavenderBlush3', '#cdc1c5', (205, 193, 197)),
('LavenderBlush4', '#8b8386', (139, 131, 134)),
('lawn green', '#7cfc00', (124, 252, 0)),
('LawnGreen', '#7cfc00', (124, 252, 0)),
('lemon chiffon', '#fffacd', (255, 250, 205)),
('LemonChiffon', '#fffacd', (255, 250, 205)),
('LemonChiffon1', '#fffacd', (255, 250, 205)),
('LemonChiffon2', '#eee9bf', (238, 233, 191)),
('LemonChiffon3', '#cdc9a5', (205, 201, 165)),
('LemonChiffon4', '#8b8970', (139, 137, 112)),
('light blue', '#add8e6', (173, 216, 230)),
('light coral', '#f08080', (240, 128, 128)),
('light cyan', '#e0ffff', (224, 255, 255)),
('light goldenrod', '#eedd82', (238, 221, 130)),
('light goldenrod yellow', '#fafad2', (250, 250, 210)),
('light gray', '#d3d3d3', (211, 211, 211)),
('light green', '#90ee90', (144, 238, 144)),
('light grey', '#d3d3d3', (211, 211, 211)),
('light pink', '#ffb6c1', (255, 182, 193)),
('light salmon', '#ffa07a', (255, 160, 122)),
('light sea green', '#20b2aa', (32, 178, 170)),
('light sky blue', '#87cefa', (135, 206, 250)),
('light slate blue', '#8470ff', (132, 112, 255)),
('light slate gray', '#778899', (119, 136, 153)),
('light slate grey', '#778899', (119, 136, 153)),
('light steel blue', '#b0c4de', (176, 196, 222)),
('light yellow', '#ffffe0', (255, 255, 224)),
('LightBlue', '#add8e6', (173, 216, 230)),
('LightBlue1', '#bfefff', (191, 239, 255)),
('LightBlue2', '#b2dfee', (178, 223, 238)),
('LightBlue3', '#9ac0cd', (154, 192, 205)),
('LightBlue4', '#68838b', (104, 131, 139)),
('LightCoral', '#f08080', (240, 128, 128)),
('LightCyan', '#e0ffff', (224, 255, 255)),
('LightCyan1', '#e0ffff', (224, 255, 255)),
('LightCyan2', '#d1eeee', (209, 238, 238)),
('LightCyan3', '#b4cdcd', (180, 205, 205)),
('LightCyan4', '#7a8b8b', (122, 139, 139)),
('LightGoldenrod', '#eedd82', (238, 221, 130)),
('LightGoldenrod1', '#ffec8b', (255, 236, 139)),
('LightGoldenrod2', '#eedc82', (238, 220, 130)),
('LightGoldenrod3', '#cdbe70', (205, 190, 112)),
('LightGoldenrod4', '#8b814c', (139, 129, 76)),
('LightGoldenrodYellow', '#fafad2', (250, 250, 210)),
('LightGray', '#d3d3d3', (211, 211, 211)),
('LightGreen', '#90ee90', (144, 238, 144)),
('LightGrey', '#d3d3d3', (211, 211, 211)),
('LightPink', '#ffb6c1', (255, 182, 193)),
('LightPink1', '#ffaeb9', (255, 174, 185)),
('LightPink2', '#eea2ad', (238, 162, 173)),
('LightPink3', '#cd8c95', (205, 140, 149)),
('LightPink4', '#8b5f65', (139, 95, 101)),
('LightSalmon', '#ffa07a', (255, 160, 122)),
('LightSalmon1', '#ffa07a', (255, 160, 122)),
('LightSalmon2', '#ee9572', (238, 149, 114)),
('LightSalmon3', '#cd8162', (205, 129, 98)),
('LightSalmon4', '#8b5742', (139, 87, 66)),
('LightSeaGreen', '#20b2aa', (32, 178, 170)),
('LightSkyBlue', '#87cefa', (135, 206, 250)),
('LightSkyBlue1', '#b0e2ff', (176, 226, 255)),
('LightSkyBlue2', '#a4d3ee', (164, 211, 238)),
('LightSkyBlue3', '#8db6cd', (141, 182, 205)),
('LightSkyBlue4', '#607b8b', (96, 123, 139)),
('LightSlateBlue', '#8470ff', (132, 112, 255)),
('LightSlateGray', '#778899', (119, 136, 153)),
('LightSlateGrey', '#778899', (119, 136, 153)),
('LightSteelBlue', '#b0c4de', (176, 196, 222)),
('LightSteelBlue1', '#cae1ff', (202, 225, 255)),
('LightSteelBlue2', '#bcd2ee', (188, 210, 238)),
('LightSteelBlue3', '#a2b5cd', (162, 181, 205)),
('LightSteelBlue4', '#6e7b8b', (110, 123, 139)),
('LightYellow', '#ffffe0', (255, 255, 224)),
('LightYellow1', '#ffffe0', (255, 255, 224)),
('LightYellow2', '#eeeed1', (238, 238, 209)),
('LightYellow3', '#cdcdb4', (205, 205, 180)),
('LightYellow4', '#8b8b7a', (139, 139, 122)),
('lime', '#00ff00', (0, 255, 0)),
('lime green', '#32cd32', (50, 205, 50)),
('LimeGreen', '#32cd32', (50, 205, 50)),
('linen', '#faf0e6', (250, 240, 230)),
('magenta', '#ff00ff', (255, 0, 255)),
('magenta1', '#ff00ff', (255, 0, 255)),
('magenta2', '#ee00ee', (238, 0, 238)),
('magenta3', '#cd00cd', (205, 0, 205)),
('magenta4', '#8b008b', (139, 0, 139)),
('maroon', '#800000', (128, 0, 0)),
('maroon1', '#ff34b3', (255, 52, 179)),
('maroon2', '#ee30a7', (238, 48, 167)),
('maroon3', '#cd2990', (205, 41, 144)),
('maroon4', '#8b1c62', (139, 28, 98)),
('medium aquamarine', '#66cdaa', (102, 205, 170)),
('medium blue', '#0000cd', (0, 0, 205)),
('medium orchid', '#ba55d3', (186, 85, 211)),
('medium purple', '#9370db', (147, 112, 219)),
('medium sea green', '#3cb371', (60, 179, 113)),
('medium slate blue', '#7b68ee', (123, 104, 238)),
('medium spring green', '#00fa9a', (0, 250, 154)),
('medium turquoise', '#48d1cc', (72, 209, 204)),
('medium violet red', '#c71585', (199, 21, 133)),
('MediumAquamarine', '#66cdaa', (102, 205, 170)),
('MediumBlue', '#0000cd', (0, 0, 205)),
('MediumOrchid', '#ba55d3', (186, 85, 211)),
('MediumOrchid1', '#e066ff', (224, 102, 255)),
('MediumOrchid2', '#d15fee', (209, 95, 238)),
('MediumOrchid3', '#b452cd', (180, 82, 205)),
('MediumOrchid4', '#7a378b', (122, 55, 139)),
('MediumPurple', '#9370db', (147, 112, 219)),
('MediumPurple1', '#ab82ff', (171, 130, 255)),
('MediumPurple2', '#9f79ee', (159, 121, 238)),
('MediumPurple3', '#8968cd', (137, 104, 205)),
('MediumPurple4', '#5d478b', (93, 71, 139)),
('MediumSeaGreen', '#3cb371', (60, 179, 113)),
('MediumSlateBlue', '#7b68ee', (123, 104, 238)),
('MediumSpringGreen', '#00fa9a', (0, 250, 154)),
('MediumTurquoise', '#48d1cc', (72, 209, 204)),
('MediumVioletRed', '#c71585', (199, 21, 133)),
('midnight blue', '#191970', (25, 25, 112)),
('MidnightBlue', '#191970', (25, 25, 112)),
('mint cream', '#f5fffa', (245, 255, 250)),
('MintCream', '#f5fffa', (245, 255, 250)),
('misty rose', '#ffe4e1', (255, 228, 225)),
('MistyRose', '#ffe4e1', (255, 228, 225)),
('MistyRose1', '#ffe4e1', (255, 228, 225)),
('MistyRose2', '#eed5d2', (238, 213, 210)),
('MistyRose3', '#cdb7b5', (205, 183, 181)),
('MistyRose4', '#8b7d7b', (139, 125, 123)),
('moccasin', '#ffe4b5', (255, 228, 181)),
('navajo white', '#ffdead', (255, 222, 173)),
('NavajoWhite', '#ffdead', (255, 222, 173)),
('NavajoWhite1', '#ffdead', (255, 222, 173)),
('NavajoWhite2', '#eecfa1', (238, 207, 161)),
('NavajoWhite3', '#cdb38b', (205, 179, 139)),
('NavajoWhite4', '#8b795e', (139, 121, 94)),
('navy', '#000080', (0, 0, 128)),
('navy blue', '#000080', (0, 0, 128)),
('NavyBlue', '#000080', (0, 0, 128)),
('old lace', '#fdf5e6', (253, 245, 230)),
('OldLace', '#fdf5e6', (253, 245, 230)),
('olive', '#808000', (128, 128, 0)),
('olive drab', '#6b8e23', (107, 142, 35)),
('OliveDrab', '#6b8e23', (107, 142, 35)),
('OliveDrab1', '#c0ff3e', (192, 255, 62)),
('OliveDrab2', '#b3ee3a', (179, 238, 58)),
('OliveDrab3', '#9acd32', (154, 205, 50)),
('OliveDrab4', '#698b22', (105, 139, 34)),
('orange', '#ffa500', (255, 165, 0)),
('orange red', '#ff4500', (255, 69, 0)),
('orange1', '#ffa500', (255, 165, 0)),
('orange2', '#ee9a00', (238, 154, 0)),
('orange3', '#cd8500', (205, 133, 0)),
('orange4', '#8b5a00', (139, 90, 0)),
('OrangeRed', '#ff4500', (255, 69, 0)),
('OrangeRed1', '#ff4500', (255, 69, 0)),
('OrangeRed2', '#ee4000', (238, 64, 0)),
('OrangeRed3', '#cd3700', (205, 55, 0)),
('OrangeRed4', '#8b2500', (139, 37, 0)),
('orchid', '#da70d6', (218, 112, 214)),
('orchid1', '#ff83fa', (255, 131, 250)),
('orchid2', '#ee7ae9', (238, 122, 233)),
('orchid3', '#cd69c9', (205, 105, 201)),
('orchid4', '#8b4789', (139, 71, 137)),
('pale goldenrod', '#eee8aa', (238, 232, 170)),
('pale green', '#98fb98', (152, 251, 152)),
('pale turquoise', '#afeeee', (175, 238, 238)),
('pale violet red', '#db7093', (219, 112, 147)),
('PaleGoldenrod', '#eee8aa', (238, 232, 170)),
('PaleGreen', '#98fb98', (152, 251, 152)),
('PaleGreen1', '#9aff9a', (154, 255, 154)),
('PaleGreen2', '#90ee90', (144, 238, 144)),
('PaleGreen3', '#7ccd7c', (124, 205, 124)),
('PaleGreen4', '#548b54', (84, 139, 84)),
('PaleTurquoise', '#afeeee', (175, 238, 238)),
('PaleTurquoise1', '#bbffff', (187, 255, 255)),
('PaleTurquoise2', '#aeeeee', (174, 238, 238)),
('PaleTurquoise3', '#96cdcd', (150, 205, 205)),
('PaleTurquoise4', '#668b8b', (102, 139, 139)),
('PaleVioletRed', '#db7093', (219, 112, 147)),
('PaleVioletRed1', '#ff82ab', (255, 130, 171)),
('PaleVioletRed2', '#ee799f', (238, 121, 159)),
('PaleVioletRed3', '#cd6889', (205, 104, 137)),
('PaleVioletRed4', '#8b475d', (139, 71, 93)),
('papaya whip', '#ffefd5', (255, 239, 213)),
('PapayaWhip', '#ffefd5', (255, 239, 213)),
('peach puff', '#ffdab9', (255, 218, 185)),
('PeachPuff', '#ffdab9', (255, 218, 185)),
('PeachPuff1', '#ffdab9', (255, 218, 185)),
('PeachPuff2', '#eecbad', (238, 203, 173)),
('PeachPuff3', '#cdaf95', (205, 175, 149)),
('PeachPuff4', '#8b7765', (139, 119, 101)),
('peru', '#cd853f', (205, 133, 63)),
('pink', '#ffc0cb', (255, 192, 203)),
('pink1', '#ffb5c5', (255, 181, 197)),
('pink2', '#eea9b8', (238, 169, 184)),
('pink3', '#cd919e', (205, 145, 158)),
('pink4', '#8b636c', (139, 99, 108)),
('plum', '#dda0dd', (221, 160, 221)),
('plum1', '#ffbbff', (255, 187, 255)),
('plum2', '#eeaeee', (238, 174, 238)),
('plum3', '#cd96cd', (205, 150, 205)),
('plum4', '#8b668b', (139, 102, 139)),
('powder blue', '#b0e0e6', (176, 224, 230)),
('PowderBlue', '#b0e0e6', (176, 224, 230)),
('purple', '#800080', (128, 0, 128)),
('purple1', '#9b30ff', (155, 48, 255)),
('purple2', '#912cee', (145, 44, 238)),
('purple3', '#7d26cd', (125, 38, 205)),
('purple4', '#551a8b', (85, 26, 139)),
('red', '#ff0000', (255, 0, 0)),
('red1', '#ff0000', (255, 0, 0)),
('red2', '#ee0000', (238, 0, 0)),
('red3', '#cd0000', (205, 0, 0)),
('red4', '#8b0000', (139, 0, 0)),
('rosy brown', '#bc8f8f', (188, 143, 143)),
('RosyBrown', '#bc8f8f', (188, 143, 143)),
('RosyBrown1', '#ffc1c1', (255, 193, 193)),
('RosyBrown2', '#eeb4b4', (238, 180, 180)),
('RosyBrown3', '#cd9b9b', (205, 155, 155)),
('RosyBrown4', '#8b6969', (139, 105, 105)),
('royal blue', '#4169e1', (65, 105, 225)),
('RoyalBlue', '#4169e1', (65, 105, 225)),
('RoyalBlue1', '#4876ff', (72, 118, 255)),
('RoyalBlue2', '#436eee', (67, 110, 238)),
('RoyalBlue3', '#3a5fcd', (58, 95, 205)),
('RoyalBlue4', '#27408b', (39, 64, 139)),
('saddle brown', '#8b4513', (139, 69, 19)),
('SaddleBrown', '#8b4513', (139, 69, 19)),
('salmon', '#fa8072', (250, 128, 114)),
('salmon1', '#ff8c69', (255, 140, 105)),
('salmon2', '#ee8262', (238, 130, 98)),
('salmon3', '#cd7054', (205, 112, 84)),
('salmon4', '#8b4c39', (139, 76, 57)),
('sandy brown', '#f4a460', (244, 164, 96)),
('SandyBrown', '#f4a460', (244, 164, 96)),
('sea green', '#2e8b57', (46, 139, 87)),
('SeaGreen', '#2e8b57', (46, 139, 87)),
('SeaGreen1', '#54ff9f', (84, 255, 159)),
('SeaGreen2', '#4eee94', (78, 238, 148)),
('SeaGreen3', '#43cd80', (67, 205, 128)),
('SeaGreen4', '#2e8b57', (46, 139, 87)),
('seashell', '#fff5ee', (255, 245, 238)),
('seashell1', '#fff5ee', (255, 245, 238)),
('seashell2', '#eee5de', (238, 229, 222)),
('seashell3', '#cdc5bf', (205, 197, 191)),
('seashell4', '#8b8682', (139, 134, 130)),
('sienna', '#a0522d', (160, 82, 45)),
('sienna1', '#ff8247', (255, 130, 71)),
('sienna2', '#ee7942', (238, 121, 66)),
('sienna3', '#cd6839', (205, 104, 57)),
('sienna4', '#8b4726', (139, 71, 38)),
('silver', '#c0c0c0', (192, 192, 192)),
('sky blue', '#87ceeb', (135, 206, 235)),
('SkyBlue', '#87ceeb', (135, 206, 235)),
('SkyBlue1', '#87ceff', (135, 206, 255)),
('SkyBlue2', '#7ec0ee', (126, 192, 238)),
('SkyBlue3', '#6ca6cd', (108, 166, 205)),
('SkyBlue4', '#4a708b', (74, 112, 139)),
('slate blue', '#6a5acd', (106, 90, 205)),
('slate gray', '#708090', (112, 128, 144)),
('slate grey', '#708090', (112, 128, 144)),
('SlateBlue', '#6a5acd', (106, 90, 205)),
('SlateBlue1', '#836fff', (131, 111, 255)),
('SlateBlue2', '#7a67ee', (122, 103, 238)),
('SlateBlue3', '#6959cd', (105, 89, 205)),
('SlateBlue4', '#473c8b', (71, 60, 139)),
('SlateGray', '#708090', (112, 128, 144)),
('SlateGray1', '#c6e2ff', (198, 226, 255)),
('SlateGray2', '#b9d3ee', (185, 211, 238)),
('SlateGray3', '#9fb6cd', (159, 182, 205)),
('SlateGray4', '#6c7b8b', (108, 123, 139)),
('SlateGrey', '#708090', (112, 128, 144)),
('snow', '#fffafa', (255, 250, 250)),
('snow1', '#fffafa', (255, 250, 250)),
('snow2', '#eee9e9', (238, 233, 233)),
('snow3', '#cdc9c9', (205, 201, 201)),
('snow4', '#8b8989', (139, 137, 137)),
('spring green', '#00ff7f', (0, 255, 127)),
('SpringGreen', '#00ff7f', (0, 255, 127)),
('SpringGreen1', '#00ff7f', (0, 255, 127)),
('SpringGreen2', '#00ee76', (0, 238, 118)),
('SpringGreen3', '#00cd66', (0, 205, 102)),
('SpringGreen4', '#008b45', (0, 139, 69)),
('steel blue', '#4682b4', (70, 130, 180)),
('SteelBlue', '#4682b4', (70, 130, 180)),
('SteelBlue1', '#63b8ff', (99, 184, 255)),
('SteelBlue2', '#5cacee', (92, 172, 238)),
('SteelBlue3', '#4f94cd', (79, 148, 205)),
('SteelBlue4', '#36648b', (54, 100, 139)),
('tan', '#d2b48c', (210, 180, 140)),
('tan1', '#ffa54f', (255, 165, 79)),
('tan2', '#ee9a49', (238, 154, 73)),
('tan3', '#cd853f', (205, 133, 63)),
('tan4', '#8b5a2b', (139, 90, 43)),
('teal', '#008080', (0, 128, 128)),
('thistle', '#d8bfd8', (216, 191, 216)),
('thistle1', '#ffe1ff', (255, 225, 255)),
('thistle2', '#eed2ee', (238, 210, 238)),
('thistle3', '#cdb5cd', (205, 181, 205)),
('thistle4', '#8b7b8b', (139, 123, 139)),
('tomato', '#ff6347', (255, 99, 71)),
('tomato1', '#ff6347', (255, 99, 71)),
('tomato2', '#ee5c42', (238, 92, 66)),
('tomato3', '#cd4f39', (205, 79, 57)),
('tomato4', '#8b3626', (139, 54, 38)),
('turquoise', '#40e0d0', (64, 224, 208)),
('turquoise1', '#00f5ff', (0, 245, 255)),
('turquoise2', '#00e5ee', (0, 229, 238)),
('turquoise3', '#00c5cd', (0, 197, 205)),
('turquoise4', '#00868b', (0, 134, 139)),
('violet', '#ee82ee', (238, 130, 238)),
('violet red', '#d02090', (208, 32, 144)),
('VioletRed', '#d02090', (208, 32, 144)),
('VioletRed1', '#ff3e96', (255, 62, 150)),
('VioletRed2', '#ee3a8c', (238, 58, 140)),
('VioletRed3', '#cd3278', (205, 50, 120)),
('VioletRed4', '#8b2252', (139, 34, 82)),
('wheat', '#f5deb3', (245, 222, 179)),
('wheat1', '#ffe7ba', (255, 231, 186)),
('wheat2', '#eed8ae', (238, 216, 174)),
('wheat3', '#cdba96', (205, 186, 150)),
('wheat4', '#8b7e66', (139, 126, 102)),
('white', '#ffffff', (255, 255, 255)),
('white smoke', '#f5f5f5', (245, 245, 245)),
('WhiteSmoke', '#f5f5f5', (245, 245, 245)),
('yellow', '#ffff00', (255, 255, 0)),
('yellow green', '#9acd32', (154, 205, 50)),
('yellow1', '#ffff00', (255, 255, 0)),
('yellow2', '#eeee00', (238, 238, 0)),
('yellow3', '#cdcd00', (205, 205, 0)),
('yellow4', '#8b8b00', (139, 139, 0)),
('YellowGreen', '#9acd32', (154, 205, 50)),
)
&lt;/textarea&gt;

&lt;p&gt;Here's the table of Tkinter colors:&lt;/p&gt;
&lt;style&gt;
table { border-collapse: collapse; max-width: 100%; }
th, td { border: 1px solid #ccc; padding: 6px 8px; }
th { background: #f5f5f5; text-align: left; }
code { font-family: monospace; }


&lt;/style&gt;
&lt;script&gt;
function c(text, btn) {
  navigator.clipboard.writeText(text).then(() =&gt; {
    // save old text
    const oldHTML = btn.innerHTML;
    // set to green check
    btn.innerHTML = "&amp;#9989;";
    btn.style.color = "green";
    // restore after 1 second
    setTimeout(() =&gt; {
      btn.innerHTML = oldHTML;
      btn.style.color = "";
    }, 1000);
  }).catch(err =&gt; {
    alert("Copy failed: " + err);
  });
}
&lt;/script&gt;

&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Hex&lt;/th&gt;&lt;th&gt;RGB&lt;/th&gt;&lt;th&gt;Swatch&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('alice blue', this)"&gt;&amp;#128203;&lt;/button&gt; alice blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f0f8ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f0f8ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(240, 248, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(240, 248, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f0f8ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('AliceBlue', this)"&gt;&amp;#128203;&lt;/button&gt; AliceBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f0f8ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f0f8ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(240, 248, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(240, 248, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f0f8ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('antique white', this)"&gt;&amp;#128203;&lt;/button&gt; antique white&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#faebd7', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#faebd7&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(250, 235, 215)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(250, 235, 215)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#faebd7;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('AntiqueWhite', this)"&gt;&amp;#128203;&lt;/button&gt; AntiqueWhite&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#faebd7', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#faebd7&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(250, 235, 215)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(250, 235, 215)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#faebd7;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('AntiqueWhite1', this)"&gt;&amp;#128203;&lt;/button&gt; AntiqueWhite1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffefdb', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffefdb&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 239, 219)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 239, 219)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffefdb;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('AntiqueWhite2', this)"&gt;&amp;#128203;&lt;/button&gt; AntiqueWhite2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eedfcc', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eedfcc&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 223, 204)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 223, 204)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eedfcc;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('AntiqueWhite3', this)"&gt;&amp;#128203;&lt;/button&gt; AntiqueWhite3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdc0b0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdc0b0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 192, 176)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 192, 176)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdc0b0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('AntiqueWhite4', this)"&gt;&amp;#128203;&lt;/button&gt; AntiqueWhite4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b8378', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b8378&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 131, 120)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 131, 120)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b8378;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('aqua', this)"&gt;&amp;#128203;&lt;/button&gt; aqua&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00ffff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00ffff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 255, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 255, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00ffff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('aquamarine', this)"&gt;&amp;#128203;&lt;/button&gt; aquamarine&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7fffd4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7fffd4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(127, 255, 212)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(127, 255, 212)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7fffd4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('aquamarine1', this)"&gt;&amp;#128203;&lt;/button&gt; aquamarine1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7fffd4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7fffd4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(127, 255, 212)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(127, 255, 212)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7fffd4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('aquamarine2', this)"&gt;&amp;#128203;&lt;/button&gt; aquamarine2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#76eec6', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#76eec6&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(118, 238, 198)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(118, 238, 198)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#76eec6;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('aquamarine3', this)"&gt;&amp;#128203;&lt;/button&gt; aquamarine3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#66cdaa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#66cdaa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(102, 205, 170)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(102, 205, 170)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#66cdaa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('aquamarine4', this)"&gt;&amp;#128203;&lt;/button&gt; aquamarine4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#458b74', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#458b74&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(69, 139, 116)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(69, 139, 116)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#458b74;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('azure', this)"&gt;&amp;#128203;&lt;/button&gt; azure&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f0ffff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f0ffff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(240, 255, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(240, 255, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f0ffff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('azure1', this)"&gt;&amp;#128203;&lt;/button&gt; azure1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f0ffff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f0ffff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(240, 255, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(240, 255, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f0ffff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('azure2', this)"&gt;&amp;#128203;&lt;/button&gt; azure2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e0eeee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e0eeee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(224, 238, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(224, 238, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e0eeee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('azure3', this)"&gt;&amp;#128203;&lt;/button&gt; azure3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c1cdcd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c1cdcd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(193, 205, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(193, 205, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c1cdcd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('azure4', this)"&gt;&amp;#128203;&lt;/button&gt; azure4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#838b8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#838b8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(131, 139, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(131, 139, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#838b8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('beige', this)"&gt;&amp;#128203;&lt;/button&gt; beige&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f5f5dc', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f5f5dc&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(245, 245, 220)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(245, 245, 220)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f5f5dc;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('bisque', this)"&gt;&amp;#128203;&lt;/button&gt; bisque&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffe4c4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffe4c4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 228, 196)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 228, 196)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffe4c4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('bisque1', this)"&gt;&amp;#128203;&lt;/button&gt; bisque1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffe4c4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffe4c4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 228, 196)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 228, 196)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffe4c4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('bisque2', this)"&gt;&amp;#128203;&lt;/button&gt; bisque2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eed5b7', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eed5b7&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 213, 183)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 213, 183)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eed5b7;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('bisque3', this)"&gt;&amp;#128203;&lt;/button&gt; bisque3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdb79e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdb79e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 183, 158)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 183, 158)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdb79e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('bisque4', this)"&gt;&amp;#128203;&lt;/button&gt; bisque4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b7d6b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b7d6b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 125, 107)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 125, 107)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b7d6b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('black', this)"&gt;&amp;#128203;&lt;/button&gt; black&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#000000', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#000000&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#000000;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('blanched almond', this)"&gt;&amp;#128203;&lt;/button&gt; blanched almond&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffebcd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffebcd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 235, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 235, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffebcd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('BlanchedAlmond', this)"&gt;&amp;#128203;&lt;/button&gt; BlanchedAlmond&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffebcd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffebcd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 235, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 235, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffebcd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('blue', this)"&gt;&amp;#128203;&lt;/button&gt; blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#0000ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#0000ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#0000ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('blue violet', this)"&gt;&amp;#128203;&lt;/button&gt; blue violet&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8a2be2', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8a2be2&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(138, 43, 226)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(138, 43, 226)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8a2be2;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('blue1', this)"&gt;&amp;#128203;&lt;/button&gt; blue1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#0000ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#0000ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#0000ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('blue2', this)"&gt;&amp;#128203;&lt;/button&gt; blue2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#0000ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#0000ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#0000ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('blue3', this)"&gt;&amp;#128203;&lt;/button&gt; blue3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#0000cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#0000cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#0000cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('blue4', this)"&gt;&amp;#128203;&lt;/button&gt; blue4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00008b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00008b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00008b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('BlueViolet', this)"&gt;&amp;#128203;&lt;/button&gt; BlueViolet&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8a2be2', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8a2be2&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(138, 43, 226)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(138, 43, 226)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8a2be2;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('brown', this)"&gt;&amp;#128203;&lt;/button&gt; brown&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a52a2a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a52a2a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(165, 42, 42)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(165, 42, 42)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a52a2a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('brown1', this)"&gt;&amp;#128203;&lt;/button&gt; brown1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff4040', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff4040&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 64, 64)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 64, 64)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff4040;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('brown2', this)"&gt;&amp;#128203;&lt;/button&gt; brown2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee3b3b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee3b3b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 59, 59)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 59, 59)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee3b3b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('brown3', this)"&gt;&amp;#128203;&lt;/button&gt; brown3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd3333', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd3333&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 51, 51)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 51, 51)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd3333;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('brown4', this)"&gt;&amp;#128203;&lt;/button&gt; brown4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b2323', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b2323&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 35, 35)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 35, 35)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b2323;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('burlywood', this)"&gt;&amp;#128203;&lt;/button&gt; burlywood&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#deb887', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#deb887&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(222, 184, 135)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(222, 184, 135)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#deb887;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('burlywood1', this)"&gt;&amp;#128203;&lt;/button&gt; burlywood1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffd39b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffd39b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 211, 155)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 211, 155)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffd39b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('burlywood2', this)"&gt;&amp;#128203;&lt;/button&gt; burlywood2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eec591', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eec591&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 197, 145)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 197, 145)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eec591;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('burlywood3', this)"&gt;&amp;#128203;&lt;/button&gt; burlywood3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdaa7d', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdaa7d&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 170, 125)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 170, 125)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdaa7d;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('burlywood4', this)"&gt;&amp;#128203;&lt;/button&gt; burlywood4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b7355', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b7355&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 115, 85)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 115, 85)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b7355;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('cadet blue', this)"&gt;&amp;#128203;&lt;/button&gt; cadet blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#5f9ea0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#5f9ea0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(95, 158, 160)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(95, 158, 160)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#5f9ea0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('CadetBlue', this)"&gt;&amp;#128203;&lt;/button&gt; CadetBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#5f9ea0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#5f9ea0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(95, 158, 160)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(95, 158, 160)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#5f9ea0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('CadetBlue1', this)"&gt;&amp;#128203;&lt;/button&gt; CadetBlue1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#98f5ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#98f5ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(152, 245, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(152, 245, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#98f5ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('CadetBlue2', this)"&gt;&amp;#128203;&lt;/button&gt; CadetBlue2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8ee5ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8ee5ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(142, 229, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(142, 229, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8ee5ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('CadetBlue3', this)"&gt;&amp;#128203;&lt;/button&gt; CadetBlue3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7ac5cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7ac5cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(122, 197, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(122, 197, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7ac5cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('CadetBlue4', this)"&gt;&amp;#128203;&lt;/button&gt; CadetBlue4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#53868b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#53868b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(83, 134, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(83, 134, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#53868b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('chartreuse', this)"&gt;&amp;#128203;&lt;/button&gt; chartreuse&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7fff00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7fff00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(127, 255, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(127, 255, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7fff00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('chartreuse1', this)"&gt;&amp;#128203;&lt;/button&gt; chartreuse1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7fff00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7fff00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(127, 255, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(127, 255, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7fff00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('chartreuse2', this)"&gt;&amp;#128203;&lt;/button&gt; chartreuse2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#76ee00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#76ee00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(118, 238, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(118, 238, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#76ee00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('chartreuse3', this)"&gt;&amp;#128203;&lt;/button&gt; chartreuse3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#66cd00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#66cd00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(102, 205, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(102, 205, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#66cd00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('chartreuse4', this)"&gt;&amp;#128203;&lt;/button&gt; chartreuse4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#458b00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#458b00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(69, 139, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(69, 139, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#458b00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('chocolate', this)"&gt;&amp;#128203;&lt;/button&gt; chocolate&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d2691e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d2691e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(210, 105, 30)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(210, 105, 30)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d2691e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('chocolate1', this)"&gt;&amp;#128203;&lt;/button&gt; chocolate1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff7f24', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff7f24&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 127, 36)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 127, 36)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff7f24;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('chocolate2', this)"&gt;&amp;#128203;&lt;/button&gt; chocolate2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee7621', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee7621&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 118, 33)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 118, 33)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee7621;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('chocolate3', this)"&gt;&amp;#128203;&lt;/button&gt; chocolate3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd661d', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd661d&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 102, 29)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 102, 29)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd661d;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('chocolate4', this)"&gt;&amp;#128203;&lt;/button&gt; chocolate4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b4513', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b4513&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 69, 19)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 69, 19)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b4513;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('coral', this)"&gt;&amp;#128203;&lt;/button&gt; coral&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff7f50', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff7f50&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 127, 80)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 127, 80)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff7f50;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('coral1', this)"&gt;&amp;#128203;&lt;/button&gt; coral1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff7256', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff7256&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 114, 86)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 114, 86)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff7256;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('coral2', this)"&gt;&amp;#128203;&lt;/button&gt; coral2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee6a50', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee6a50&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 106, 80)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 106, 80)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee6a50;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('coral3', this)"&gt;&amp;#128203;&lt;/button&gt; coral3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd5b45', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd5b45&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 91, 69)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 91, 69)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd5b45;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('coral4', this)"&gt;&amp;#128203;&lt;/button&gt; coral4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b3e2f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b3e2f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 62, 47)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 62, 47)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b3e2f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('cornflower blue', this)"&gt;&amp;#128203;&lt;/button&gt; cornflower blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6495ed', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6495ed&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(100, 149, 237)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(100, 149, 237)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6495ed;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('CornflowerBlue', this)"&gt;&amp;#128203;&lt;/button&gt; CornflowerBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6495ed', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6495ed&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(100, 149, 237)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(100, 149, 237)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6495ed;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('cornsilk', this)"&gt;&amp;#128203;&lt;/button&gt; cornsilk&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fff8dc', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fff8dc&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 248, 220)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 248, 220)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fff8dc;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('cornsilk1', this)"&gt;&amp;#128203;&lt;/button&gt; cornsilk1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fff8dc', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fff8dc&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 248, 220)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 248, 220)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fff8dc;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('cornsilk2', this)"&gt;&amp;#128203;&lt;/button&gt; cornsilk2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eee8cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eee8cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 232, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 232, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eee8cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('cornsilk3', this)"&gt;&amp;#128203;&lt;/button&gt; cornsilk3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdc8b1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdc8b1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 200, 177)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 200, 177)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdc8b1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('cornsilk4', this)"&gt;&amp;#128203;&lt;/button&gt; cornsilk4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b8878', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b8878&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 136, 120)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 136, 120)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b8878;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('crimson', this)"&gt;&amp;#128203;&lt;/button&gt; crimson&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#dc143c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#dc143c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(220, 20, 60)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(220, 20, 60)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#dc143c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('cyan', this)"&gt;&amp;#128203;&lt;/button&gt; cyan&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00ffff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00ffff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 255, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 255, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00ffff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('cyan1', this)"&gt;&amp;#128203;&lt;/button&gt; cyan1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00ffff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00ffff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 255, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 255, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00ffff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('cyan2', this)"&gt;&amp;#128203;&lt;/button&gt; cyan2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00eeee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00eeee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 238, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 238, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00eeee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('cyan3', this)"&gt;&amp;#128203;&lt;/button&gt; cyan3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00cdcd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00cdcd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 205, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 205, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00cdcd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('cyan4', this)"&gt;&amp;#128203;&lt;/button&gt; cyan4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#008b8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#008b8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 139, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 139, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#008b8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark blue', this)"&gt;&amp;#128203;&lt;/button&gt; dark blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00008b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00008b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00008b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark cyan', this)"&gt;&amp;#128203;&lt;/button&gt; dark cyan&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#008b8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#008b8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 139, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 139, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#008b8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark goldenrod', this)"&gt;&amp;#128203;&lt;/button&gt; dark goldenrod&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b8860b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b8860b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(184, 134, 11)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(184, 134, 11)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b8860b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark gray', this)"&gt;&amp;#128203;&lt;/button&gt; dark gray&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a9a9a9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a9a9a9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(169, 169, 169)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(169, 169, 169)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a9a9a9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark green', this)"&gt;&amp;#128203;&lt;/button&gt; dark green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#006400', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#006400&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 100, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 100, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#006400;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark grey', this)"&gt;&amp;#128203;&lt;/button&gt; dark grey&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a9a9a9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a9a9a9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(169, 169, 169)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(169, 169, 169)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a9a9a9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark khaki', this)"&gt;&amp;#128203;&lt;/button&gt; dark khaki&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bdb76b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bdb76b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(189, 183, 107)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(189, 183, 107)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bdb76b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark magenta', this)"&gt;&amp;#128203;&lt;/button&gt; dark magenta&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b008b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b008b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 0, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 0, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b008b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark olive green', this)"&gt;&amp;#128203;&lt;/button&gt; dark olive green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#556b2f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#556b2f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(85, 107, 47)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(85, 107, 47)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#556b2f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark orange', this)"&gt;&amp;#128203;&lt;/button&gt; dark orange&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff8c00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff8c00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 140, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 140, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff8c00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark orchid', this)"&gt;&amp;#128203;&lt;/button&gt; dark orchid&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9932cc', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9932cc&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(153, 50, 204)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(153, 50, 204)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9932cc;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark red', this)"&gt;&amp;#128203;&lt;/button&gt; dark red&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b0000', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b0000&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 0, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 0, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b0000;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark salmon', this)"&gt;&amp;#128203;&lt;/button&gt; dark salmon&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e9967a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e9967a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(233, 150, 122)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(233, 150, 122)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e9967a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark sea green', this)"&gt;&amp;#128203;&lt;/button&gt; dark sea green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8fbc8f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8fbc8f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(143, 188, 143)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(143, 188, 143)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8fbc8f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark slate blue', this)"&gt;&amp;#128203;&lt;/button&gt; dark slate blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#483d8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#483d8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(72, 61, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(72, 61, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#483d8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark slate gray', this)"&gt;&amp;#128203;&lt;/button&gt; dark slate gray&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#2f4f4f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#2f4f4f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(47, 79, 79)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(47, 79, 79)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#2f4f4f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark slate grey', this)"&gt;&amp;#128203;&lt;/button&gt; dark slate grey&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#2f4f4f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#2f4f4f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(47, 79, 79)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(47, 79, 79)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#2f4f4f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark turquoise', this)"&gt;&amp;#128203;&lt;/button&gt; dark turquoise&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00ced1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00ced1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 206, 209)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 206, 209)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00ced1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dark violet', this)"&gt;&amp;#128203;&lt;/button&gt; dark violet&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9400d3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9400d3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(148, 0, 211)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(148, 0, 211)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9400d3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkBlue', this)"&gt;&amp;#128203;&lt;/button&gt; DarkBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00008b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00008b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00008b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkCyan', this)"&gt;&amp;#128203;&lt;/button&gt; DarkCyan&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#008b8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#008b8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 139, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 139, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#008b8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkGoldenrod', this)"&gt;&amp;#128203;&lt;/button&gt; DarkGoldenrod&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b8860b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b8860b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(184, 134, 11)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(184, 134, 11)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b8860b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkGoldenrod1', this)"&gt;&amp;#128203;&lt;/button&gt; DarkGoldenrod1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffb90f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffb90f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 185, 15)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 185, 15)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffb90f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkGoldenrod2', this)"&gt;&amp;#128203;&lt;/button&gt; DarkGoldenrod2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eead0e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eead0e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 173, 14)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 173, 14)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eead0e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkGoldenrod3', this)"&gt;&amp;#128203;&lt;/button&gt; DarkGoldenrod3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd950c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd950c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 149, 12)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 149, 12)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd950c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkGoldenrod4', this)"&gt;&amp;#128203;&lt;/button&gt; DarkGoldenrod4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b6508', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b6508&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 101, 8)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 101, 8)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b6508;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkGray', this)"&gt;&amp;#128203;&lt;/button&gt; DarkGray&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a9a9a9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a9a9a9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(169, 169, 169)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(169, 169, 169)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a9a9a9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkGreen', this)"&gt;&amp;#128203;&lt;/button&gt; DarkGreen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#006400', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#006400&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 100, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 100, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#006400;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkGrey', this)"&gt;&amp;#128203;&lt;/button&gt; DarkGrey&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a9a9a9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a9a9a9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(169, 169, 169)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(169, 169, 169)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a9a9a9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkKhaki', this)"&gt;&amp;#128203;&lt;/button&gt; DarkKhaki&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bdb76b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bdb76b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(189, 183, 107)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(189, 183, 107)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bdb76b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkMagenta', this)"&gt;&amp;#128203;&lt;/button&gt; DarkMagenta&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b008b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b008b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 0, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 0, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b008b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOliveGreen', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOliveGreen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#556b2f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#556b2f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(85, 107, 47)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(85, 107, 47)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#556b2f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOliveGreen1', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOliveGreen1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#caff70', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#caff70&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(202, 255, 112)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(202, 255, 112)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#caff70;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOliveGreen2', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOliveGreen2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bcee68', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bcee68&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(188, 238, 104)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(188, 238, 104)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bcee68;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOliveGreen3', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOliveGreen3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a2cd5a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a2cd5a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(162, 205, 90)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(162, 205, 90)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a2cd5a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOliveGreen4', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOliveGreen4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6e8b3d', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6e8b3d&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(110, 139, 61)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(110, 139, 61)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6e8b3d;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOrange', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOrange&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff8c00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff8c00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 140, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 140, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff8c00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOrange1', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOrange1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff7f00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff7f00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 127, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 127, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff7f00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOrange2', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOrange2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee7600', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee7600&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 118, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 118, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee7600;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOrange3', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOrange3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd6600', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd6600&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 102, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 102, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd6600;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOrange4', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOrange4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b4500', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b4500&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 69, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 69, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b4500;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOrchid', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOrchid&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9932cc', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9932cc&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(153, 50, 204)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(153, 50, 204)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9932cc;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOrchid1', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOrchid1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bf3eff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bf3eff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(191, 62, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(191, 62, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bf3eff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOrchid2', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOrchid2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b23aee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b23aee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(178, 58, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(178, 58, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b23aee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOrchid3', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOrchid3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9a32cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9a32cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(154, 50, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(154, 50, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9a32cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkOrchid4', this)"&gt;&amp;#128203;&lt;/button&gt; DarkOrchid4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#68228b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#68228b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(104, 34, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(104, 34, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#68228b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkRed', this)"&gt;&amp;#128203;&lt;/button&gt; DarkRed&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b0000', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b0000&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 0, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 0, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b0000;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkSalmon', this)"&gt;&amp;#128203;&lt;/button&gt; DarkSalmon&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e9967a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e9967a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(233, 150, 122)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(233, 150, 122)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e9967a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkSeaGreen', this)"&gt;&amp;#128203;&lt;/button&gt; DarkSeaGreen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8fbc8f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8fbc8f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(143, 188, 143)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(143, 188, 143)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8fbc8f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkSeaGreen1', this)"&gt;&amp;#128203;&lt;/button&gt; DarkSeaGreen1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c1ffc1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c1ffc1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(193, 255, 193)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(193, 255, 193)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c1ffc1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkSeaGreen2', this)"&gt;&amp;#128203;&lt;/button&gt; DarkSeaGreen2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b4eeb4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b4eeb4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(180, 238, 180)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(180, 238, 180)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b4eeb4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkSeaGreen3', this)"&gt;&amp;#128203;&lt;/button&gt; DarkSeaGreen3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9bcd9b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9bcd9b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(155, 205, 155)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(155, 205, 155)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9bcd9b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkSeaGreen4', this)"&gt;&amp;#128203;&lt;/button&gt; DarkSeaGreen4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#698b69', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#698b69&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(105, 139, 105)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(105, 139, 105)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#698b69;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkSlateBlue', this)"&gt;&amp;#128203;&lt;/button&gt; DarkSlateBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#483d8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#483d8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(72, 61, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(72, 61, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#483d8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkSlateGray', this)"&gt;&amp;#128203;&lt;/button&gt; DarkSlateGray&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#2f4f4f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#2f4f4f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(47, 79, 79)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(47, 79, 79)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#2f4f4f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkSlateGray1', this)"&gt;&amp;#128203;&lt;/button&gt; DarkSlateGray1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#97ffff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#97ffff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(151, 255, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(151, 255, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#97ffff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkSlateGray2', this)"&gt;&amp;#128203;&lt;/button&gt; DarkSlateGray2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8deeee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8deeee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(141, 238, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(141, 238, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8deeee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkSlateGray3', this)"&gt;&amp;#128203;&lt;/button&gt; DarkSlateGray3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#79cdcd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#79cdcd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(121, 205, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(121, 205, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#79cdcd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkSlateGray4', this)"&gt;&amp;#128203;&lt;/button&gt; DarkSlateGray4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#528b8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#528b8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(82, 139, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(82, 139, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#528b8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkSlateGrey', this)"&gt;&amp;#128203;&lt;/button&gt; DarkSlateGrey&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#2f4f4f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#2f4f4f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(47, 79, 79)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(47, 79, 79)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#2f4f4f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkTurquoise', this)"&gt;&amp;#128203;&lt;/button&gt; DarkTurquoise&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00ced1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00ced1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 206, 209)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 206, 209)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00ced1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DarkViolet', this)"&gt;&amp;#128203;&lt;/button&gt; DarkViolet&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9400d3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9400d3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(148, 0, 211)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(148, 0, 211)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9400d3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('deep pink', this)"&gt;&amp;#128203;&lt;/button&gt; deep pink&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff1493', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff1493&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 20, 147)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 20, 147)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff1493;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('deep sky blue', this)"&gt;&amp;#128203;&lt;/button&gt; deep sky blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00bfff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00bfff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 191, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 191, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00bfff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DeepPink', this)"&gt;&amp;#128203;&lt;/button&gt; DeepPink&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff1493', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff1493&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 20, 147)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 20, 147)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff1493;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DeepPink1', this)"&gt;&amp;#128203;&lt;/button&gt; DeepPink1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff1493', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff1493&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 20, 147)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 20, 147)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff1493;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DeepPink2', this)"&gt;&amp;#128203;&lt;/button&gt; DeepPink2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee1289', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee1289&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 18, 137)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 18, 137)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee1289;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DeepPink3', this)"&gt;&amp;#128203;&lt;/button&gt; DeepPink3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd1076', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd1076&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 16, 118)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 16, 118)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd1076;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DeepPink4', this)"&gt;&amp;#128203;&lt;/button&gt; DeepPink4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b0a50', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b0a50&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 10, 80)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 10, 80)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b0a50;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DeepSkyBlue', this)"&gt;&amp;#128203;&lt;/button&gt; DeepSkyBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00bfff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00bfff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 191, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 191, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00bfff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DeepSkyBlue1', this)"&gt;&amp;#128203;&lt;/button&gt; DeepSkyBlue1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00bfff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00bfff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 191, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 191, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00bfff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DeepSkyBlue2', this)"&gt;&amp;#128203;&lt;/button&gt; DeepSkyBlue2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00b2ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00b2ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 178, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 178, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00b2ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DeepSkyBlue3', this)"&gt;&amp;#128203;&lt;/button&gt; DeepSkyBlue3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#009acd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#009acd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 154, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 154, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#009acd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DeepSkyBlue4', this)"&gt;&amp;#128203;&lt;/button&gt; DeepSkyBlue4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00688b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00688b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 104, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 104, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00688b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dim gray', this)"&gt;&amp;#128203;&lt;/button&gt; dim gray&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#696969', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#696969&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(105, 105, 105)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(105, 105, 105)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#696969;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dim grey', this)"&gt;&amp;#128203;&lt;/button&gt; dim grey&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#696969', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#696969&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(105, 105, 105)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(105, 105, 105)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#696969;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DimGray', this)"&gt;&amp;#128203;&lt;/button&gt; DimGray&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#696969', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#696969&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(105, 105, 105)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(105, 105, 105)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#696969;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DimGrey', this)"&gt;&amp;#128203;&lt;/button&gt; DimGrey&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#696969', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#696969&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(105, 105, 105)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(105, 105, 105)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#696969;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('dodger blue', this)"&gt;&amp;#128203;&lt;/button&gt; dodger blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#1e90ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#1e90ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(30, 144, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(30, 144, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#1e90ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DodgerBlue', this)"&gt;&amp;#128203;&lt;/button&gt; DodgerBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#1e90ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#1e90ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(30, 144, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(30, 144, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#1e90ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DodgerBlue1', this)"&gt;&amp;#128203;&lt;/button&gt; DodgerBlue1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#1e90ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#1e90ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(30, 144, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(30, 144, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#1e90ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DodgerBlue2', this)"&gt;&amp;#128203;&lt;/button&gt; DodgerBlue2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#1c86ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#1c86ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(28, 134, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(28, 134, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#1c86ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DodgerBlue3', this)"&gt;&amp;#128203;&lt;/button&gt; DodgerBlue3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#1874cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#1874cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(24, 116, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(24, 116, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#1874cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('DodgerBlue4', this)"&gt;&amp;#128203;&lt;/button&gt; DodgerBlue4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#104e8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#104e8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(16, 78, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(16, 78, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#104e8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('firebrick', this)"&gt;&amp;#128203;&lt;/button&gt; firebrick&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b22222', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b22222&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(178, 34, 34)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(178, 34, 34)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b22222;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('firebrick1', this)"&gt;&amp;#128203;&lt;/button&gt; firebrick1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff3030', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff3030&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 48, 48)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 48, 48)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff3030;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('firebrick2', this)"&gt;&amp;#128203;&lt;/button&gt; firebrick2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee2c2c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee2c2c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 44, 44)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 44, 44)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee2c2c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('firebrick3', this)"&gt;&amp;#128203;&lt;/button&gt; firebrick3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd2626', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd2626&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 38, 38)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 38, 38)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd2626;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('firebrick4', this)"&gt;&amp;#128203;&lt;/button&gt; firebrick4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b1a1a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b1a1a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 26, 26)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 26, 26)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b1a1a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('floral white', this)"&gt;&amp;#128203;&lt;/button&gt; floral white&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fffaf0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fffaf0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 250, 240)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 250, 240)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fffaf0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('FloralWhite', this)"&gt;&amp;#128203;&lt;/button&gt; FloralWhite&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fffaf0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fffaf0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 250, 240)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 250, 240)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fffaf0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('forest green', this)"&gt;&amp;#128203;&lt;/button&gt; forest green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#228b22', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#228b22&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(34, 139, 34)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(34, 139, 34)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#228b22;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('ForestGreen', this)"&gt;&amp;#128203;&lt;/button&gt; ForestGreen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#228b22', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#228b22&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(34, 139, 34)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(34, 139, 34)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#228b22;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('fuchsia', this)"&gt;&amp;#128203;&lt;/button&gt; fuchsia&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff00ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff00ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 0, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 0, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff00ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gainsboro', this)"&gt;&amp;#128203;&lt;/button&gt; gainsboro&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#dcdcdc', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#dcdcdc&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(220, 220, 220)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(220, 220, 220)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#dcdcdc;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('ghost white', this)"&gt;&amp;#128203;&lt;/button&gt; ghost white&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f8f8ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f8f8ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(248, 248, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(248, 248, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f8f8ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('GhostWhite', this)"&gt;&amp;#128203;&lt;/button&gt; GhostWhite&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f8f8ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f8f8ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(248, 248, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(248, 248, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f8f8ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gold', this)"&gt;&amp;#128203;&lt;/button&gt; gold&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffd700', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffd700&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 215, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 215, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffd700;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gold1', this)"&gt;&amp;#128203;&lt;/button&gt; gold1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffd700', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffd700&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 215, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 215, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffd700;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gold2', this)"&gt;&amp;#128203;&lt;/button&gt; gold2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eec900', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eec900&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 201, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 201, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eec900;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gold3', this)"&gt;&amp;#128203;&lt;/button&gt; gold3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdad00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdad00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 173, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 173, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdad00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gold4', this)"&gt;&amp;#128203;&lt;/button&gt; gold4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b7500', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b7500&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 117, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 117, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b7500;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('goldenrod', this)"&gt;&amp;#128203;&lt;/button&gt; goldenrod&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#daa520', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#daa520&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(218, 165, 32)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(218, 165, 32)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#daa520;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('goldenrod1', this)"&gt;&amp;#128203;&lt;/button&gt; goldenrod1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffc125', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffc125&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 193, 37)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 193, 37)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffc125;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('goldenrod2', this)"&gt;&amp;#128203;&lt;/button&gt; goldenrod2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eeb422', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eeb422&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 180, 34)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 180, 34)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eeb422;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('goldenrod3', this)"&gt;&amp;#128203;&lt;/button&gt; goldenrod3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd9b1d', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd9b1d&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 155, 29)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 155, 29)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd9b1d;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('goldenrod4', this)"&gt;&amp;#128203;&lt;/button&gt; goldenrod4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b6914', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b6914&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 105, 20)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 105, 20)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b6914;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray', this)"&gt;&amp;#128203;&lt;/button&gt; gray&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#808080', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#808080&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(128, 128, 128)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(128, 128, 128)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#808080;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray0', this)"&gt;&amp;#128203;&lt;/button&gt; gray0&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#000000', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#000000&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#000000;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray1', this)"&gt;&amp;#128203;&lt;/button&gt; gray1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#030303', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#030303&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(3, 3, 3)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(3, 3, 3)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#030303;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray2', this)"&gt;&amp;#128203;&lt;/button&gt; gray2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#050505', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#050505&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(5, 5, 5)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(5, 5, 5)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#050505;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray3', this)"&gt;&amp;#128203;&lt;/button&gt; gray3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#080808', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#080808&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(8, 8, 8)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(8, 8, 8)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#080808;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray4', this)"&gt;&amp;#128203;&lt;/button&gt; gray4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#0a0a0a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#0a0a0a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(10, 10, 10)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(10, 10, 10)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#0a0a0a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray5', this)"&gt;&amp;#128203;&lt;/button&gt; gray5&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#0d0d0d', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#0d0d0d&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(13, 13, 13)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(13, 13, 13)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#0d0d0d;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray6', this)"&gt;&amp;#128203;&lt;/button&gt; gray6&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#0f0f0f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#0f0f0f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(15, 15, 15)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(15, 15, 15)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#0f0f0f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray7', this)"&gt;&amp;#128203;&lt;/button&gt; gray7&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#121212', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#121212&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(18, 18, 18)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(18, 18, 18)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#121212;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray8', this)"&gt;&amp;#128203;&lt;/button&gt; gray8&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#141414', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#141414&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(20, 20, 20)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(20, 20, 20)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#141414;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray9', this)"&gt;&amp;#128203;&lt;/button&gt; gray9&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#171717', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#171717&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(23, 23, 23)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(23, 23, 23)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#171717;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray10', this)"&gt;&amp;#128203;&lt;/button&gt; gray10&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#1a1a1a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#1a1a1a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(26, 26, 26)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(26, 26, 26)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#1a1a1a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray11', this)"&gt;&amp;#128203;&lt;/button&gt; gray11&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#1c1c1c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#1c1c1c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(28, 28, 28)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(28, 28, 28)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#1c1c1c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray12', this)"&gt;&amp;#128203;&lt;/button&gt; gray12&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#1f1f1f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#1f1f1f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(31, 31, 31)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(31, 31, 31)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#1f1f1f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray13', this)"&gt;&amp;#128203;&lt;/button&gt; gray13&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#212121', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#212121&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(33, 33, 33)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(33, 33, 33)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#212121;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray14', this)"&gt;&amp;#128203;&lt;/button&gt; gray14&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#242424', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#242424&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(36, 36, 36)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(36, 36, 36)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#242424;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray15', this)"&gt;&amp;#128203;&lt;/button&gt; gray15&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#262626', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#262626&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(38, 38, 38)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(38, 38, 38)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#262626;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray16', this)"&gt;&amp;#128203;&lt;/button&gt; gray16&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#292929', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#292929&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(41, 41, 41)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(41, 41, 41)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#292929;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray17', this)"&gt;&amp;#128203;&lt;/button&gt; gray17&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#2b2b2b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#2b2b2b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(43, 43, 43)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(43, 43, 43)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#2b2b2b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray18', this)"&gt;&amp;#128203;&lt;/button&gt; gray18&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#2e2e2e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#2e2e2e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(46, 46, 46)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(46, 46, 46)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#2e2e2e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray19', this)"&gt;&amp;#128203;&lt;/button&gt; gray19&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#303030', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#303030&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(48, 48, 48)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(48, 48, 48)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#303030;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray20', this)"&gt;&amp;#128203;&lt;/button&gt; gray20&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#333333', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#333333&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(51, 51, 51)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(51, 51, 51)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#333333;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray21', this)"&gt;&amp;#128203;&lt;/button&gt; gray21&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#363636', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#363636&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(54, 54, 54)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(54, 54, 54)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#363636;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray22', this)"&gt;&amp;#128203;&lt;/button&gt; gray22&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#383838', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#383838&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(56, 56, 56)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(56, 56, 56)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#383838;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray23', this)"&gt;&amp;#128203;&lt;/button&gt; gray23&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#3b3b3b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#3b3b3b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(59, 59, 59)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(59, 59, 59)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#3b3b3b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray24', this)"&gt;&amp;#128203;&lt;/button&gt; gray24&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#3d3d3d', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#3d3d3d&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(61, 61, 61)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(61, 61, 61)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#3d3d3d;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray25', this)"&gt;&amp;#128203;&lt;/button&gt; gray25&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#404040', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#404040&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(64, 64, 64)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(64, 64, 64)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#404040;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray26', this)"&gt;&amp;#128203;&lt;/button&gt; gray26&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#424242', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#424242&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(66, 66, 66)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(66, 66, 66)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#424242;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray27', this)"&gt;&amp;#128203;&lt;/button&gt; gray27&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#454545', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#454545&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(69, 69, 69)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(69, 69, 69)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#454545;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray28', this)"&gt;&amp;#128203;&lt;/button&gt; gray28&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#474747', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#474747&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(71, 71, 71)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(71, 71, 71)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#474747;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray29', this)"&gt;&amp;#128203;&lt;/button&gt; gray29&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4a4a4a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4a4a4a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(74, 74, 74)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(74, 74, 74)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4a4a4a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray30', this)"&gt;&amp;#128203;&lt;/button&gt; gray30&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4d4d4d', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4d4d4d&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(77, 77, 77)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(77, 77, 77)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4d4d4d;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray31', this)"&gt;&amp;#128203;&lt;/button&gt; gray31&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4f4f4f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4f4f4f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(79, 79, 79)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(79, 79, 79)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4f4f4f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray32', this)"&gt;&amp;#128203;&lt;/button&gt; gray32&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#525252', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#525252&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(82, 82, 82)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(82, 82, 82)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#525252;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray33', this)"&gt;&amp;#128203;&lt;/button&gt; gray33&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#545454', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#545454&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(84, 84, 84)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(84, 84, 84)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#545454;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray34', this)"&gt;&amp;#128203;&lt;/button&gt; gray34&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#575757', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#575757&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(87, 87, 87)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(87, 87, 87)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#575757;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray35', this)"&gt;&amp;#128203;&lt;/button&gt; gray35&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#595959', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#595959&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(89, 89, 89)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(89, 89, 89)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#595959;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray36', this)"&gt;&amp;#128203;&lt;/button&gt; gray36&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#5c5c5c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#5c5c5c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(92, 92, 92)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(92, 92, 92)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#5c5c5c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray37', this)"&gt;&amp;#128203;&lt;/button&gt; gray37&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#5e5e5e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#5e5e5e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(94, 94, 94)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(94, 94, 94)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#5e5e5e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray38', this)"&gt;&amp;#128203;&lt;/button&gt; gray38&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#616161', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#616161&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(97, 97, 97)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(97, 97, 97)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#616161;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray39', this)"&gt;&amp;#128203;&lt;/button&gt; gray39&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#636363', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#636363&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(99, 99, 99)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(99, 99, 99)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#636363;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray40', this)"&gt;&amp;#128203;&lt;/button&gt; gray40&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#666666', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#666666&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(102, 102, 102)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(102, 102, 102)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#666666;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray41', this)"&gt;&amp;#128203;&lt;/button&gt; gray41&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#696969', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#696969&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(105, 105, 105)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(105, 105, 105)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#696969;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray42', this)"&gt;&amp;#128203;&lt;/button&gt; gray42&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6b6b6b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6b6b6b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(107, 107, 107)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(107, 107, 107)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6b6b6b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray43', this)"&gt;&amp;#128203;&lt;/button&gt; gray43&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6e6e6e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6e6e6e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(110, 110, 110)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(110, 110, 110)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6e6e6e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray44', this)"&gt;&amp;#128203;&lt;/button&gt; gray44&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#707070', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#707070&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(112, 112, 112)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(112, 112, 112)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#707070;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray45', this)"&gt;&amp;#128203;&lt;/button&gt; gray45&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#737373', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#737373&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(115, 115, 115)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(115, 115, 115)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#737373;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray46', this)"&gt;&amp;#128203;&lt;/button&gt; gray46&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#757575', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#757575&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(117, 117, 117)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(117, 117, 117)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#757575;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray47', this)"&gt;&amp;#128203;&lt;/button&gt; gray47&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#787878', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#787878&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(120, 120, 120)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(120, 120, 120)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#787878;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray48', this)"&gt;&amp;#128203;&lt;/button&gt; gray48&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7a7a7a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7a7a7a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(122, 122, 122)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(122, 122, 122)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7a7a7a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray49', this)"&gt;&amp;#128203;&lt;/button&gt; gray49&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7d7d7d', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7d7d7d&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(125, 125, 125)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(125, 125, 125)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7d7d7d;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray50', this)"&gt;&amp;#128203;&lt;/button&gt; gray50&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7f7f7f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7f7f7f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(127, 127, 127)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(127, 127, 127)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7f7f7f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray51', this)"&gt;&amp;#128203;&lt;/button&gt; gray51&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#828282', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#828282&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(130, 130, 130)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(130, 130, 130)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#828282;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray52', this)"&gt;&amp;#128203;&lt;/button&gt; gray52&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#858585', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#858585&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(133, 133, 133)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(133, 133, 133)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#858585;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray53', this)"&gt;&amp;#128203;&lt;/button&gt; gray53&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#878787', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#878787&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(135, 135, 135)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(135, 135, 135)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#878787;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray54', this)"&gt;&amp;#128203;&lt;/button&gt; gray54&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8a8a8a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8a8a8a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(138, 138, 138)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(138, 138, 138)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8a8a8a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray55', this)"&gt;&amp;#128203;&lt;/button&gt; gray55&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8c8c8c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8c8c8c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(140, 140, 140)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(140, 140, 140)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8c8c8c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray56', this)"&gt;&amp;#128203;&lt;/button&gt; gray56&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8f8f8f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8f8f8f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(143, 143, 143)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(143, 143, 143)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8f8f8f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray57', this)"&gt;&amp;#128203;&lt;/button&gt; gray57&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#919191', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#919191&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(145, 145, 145)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(145, 145, 145)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#919191;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray58', this)"&gt;&amp;#128203;&lt;/button&gt; gray58&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#949494', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#949494&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(148, 148, 148)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(148, 148, 148)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#949494;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray59', this)"&gt;&amp;#128203;&lt;/button&gt; gray59&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#969696', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#969696&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(150, 150, 150)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(150, 150, 150)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#969696;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray60', this)"&gt;&amp;#128203;&lt;/button&gt; gray60&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#999999', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#999999&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(153, 153, 153)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(153, 153, 153)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#999999;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray61', this)"&gt;&amp;#128203;&lt;/button&gt; gray61&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9c9c9c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9c9c9c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(156, 156, 156)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(156, 156, 156)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9c9c9c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray62', this)"&gt;&amp;#128203;&lt;/button&gt; gray62&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9e9e9e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9e9e9e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(158, 158, 158)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(158, 158, 158)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9e9e9e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray63', this)"&gt;&amp;#128203;&lt;/button&gt; gray63&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a1a1a1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a1a1a1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(161, 161, 161)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(161, 161, 161)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a1a1a1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray64', this)"&gt;&amp;#128203;&lt;/button&gt; gray64&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a3a3a3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a3a3a3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(163, 163, 163)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(163, 163, 163)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a3a3a3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray65', this)"&gt;&amp;#128203;&lt;/button&gt; gray65&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a6a6a6', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a6a6a6&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(166, 166, 166)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(166, 166, 166)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a6a6a6;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray66', this)"&gt;&amp;#128203;&lt;/button&gt; gray66&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a8a8a8', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a8a8a8&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(168, 168, 168)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(168, 168, 168)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a8a8a8;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray67', this)"&gt;&amp;#128203;&lt;/button&gt; gray67&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ababab', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ababab&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(171, 171, 171)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(171, 171, 171)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ababab;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray68', this)"&gt;&amp;#128203;&lt;/button&gt; gray68&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#adadad', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#adadad&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(173, 173, 173)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(173, 173, 173)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#adadad;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray69', this)"&gt;&amp;#128203;&lt;/button&gt; gray69&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b0b0b0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b0b0b0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(176, 176, 176)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(176, 176, 176)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b0b0b0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray70', this)"&gt;&amp;#128203;&lt;/button&gt; gray70&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b3b3b3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b3b3b3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(179, 179, 179)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(179, 179, 179)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b3b3b3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray71', this)"&gt;&amp;#128203;&lt;/button&gt; gray71&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b5b5b5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b5b5b5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(181, 181, 181)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(181, 181, 181)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b5b5b5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray72', this)"&gt;&amp;#128203;&lt;/button&gt; gray72&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b8b8b8', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b8b8b8&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(184, 184, 184)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(184, 184, 184)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b8b8b8;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray73', this)"&gt;&amp;#128203;&lt;/button&gt; gray73&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bababa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bababa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(186, 186, 186)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(186, 186, 186)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bababa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray74', this)"&gt;&amp;#128203;&lt;/button&gt; gray74&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bdbdbd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bdbdbd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(189, 189, 189)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(189, 189, 189)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bdbdbd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray75', this)"&gt;&amp;#128203;&lt;/button&gt; gray75&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bfbfbf', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bfbfbf&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(191, 191, 191)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(191, 191, 191)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bfbfbf;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray76', this)"&gt;&amp;#128203;&lt;/button&gt; gray76&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c2c2c2', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c2c2c2&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(194, 194, 194)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(194, 194, 194)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c2c2c2;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray77', this)"&gt;&amp;#128203;&lt;/button&gt; gray77&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c4c4c4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c4c4c4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(196, 196, 196)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(196, 196, 196)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c4c4c4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray78', this)"&gt;&amp;#128203;&lt;/button&gt; gray78&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c7c7c7', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c7c7c7&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(199, 199, 199)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(199, 199, 199)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c7c7c7;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray79', this)"&gt;&amp;#128203;&lt;/button&gt; gray79&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c9c9c9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c9c9c9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(201, 201, 201)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(201, 201, 201)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c9c9c9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray80', this)"&gt;&amp;#128203;&lt;/button&gt; gray80&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cccccc', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cccccc&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(204, 204, 204)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(204, 204, 204)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cccccc;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray81', this)"&gt;&amp;#128203;&lt;/button&gt; gray81&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cfcfcf', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cfcfcf&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(207, 207, 207)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(207, 207, 207)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cfcfcf;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray82', this)"&gt;&amp;#128203;&lt;/button&gt; gray82&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d1d1d1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d1d1d1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(209, 209, 209)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(209, 209, 209)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d1d1d1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray83', this)"&gt;&amp;#128203;&lt;/button&gt; gray83&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d4d4d4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d4d4d4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(212, 212, 212)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(212, 212, 212)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d4d4d4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray84', this)"&gt;&amp;#128203;&lt;/button&gt; gray84&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d6d6d6', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d6d6d6&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(214, 214, 214)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(214, 214, 214)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d6d6d6;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray85', this)"&gt;&amp;#128203;&lt;/button&gt; gray85&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d9d9d9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d9d9d9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(217, 217, 217)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(217, 217, 217)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d9d9d9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray86', this)"&gt;&amp;#128203;&lt;/button&gt; gray86&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#dbdbdb', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#dbdbdb&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(219, 219, 219)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(219, 219, 219)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#dbdbdb;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray87', this)"&gt;&amp;#128203;&lt;/button&gt; gray87&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#dedede', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#dedede&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(222, 222, 222)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(222, 222, 222)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#dedede;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray88', this)"&gt;&amp;#128203;&lt;/button&gt; gray88&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e0e0e0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e0e0e0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(224, 224, 224)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(224, 224, 224)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e0e0e0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray89', this)"&gt;&amp;#128203;&lt;/button&gt; gray89&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e3e3e3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e3e3e3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(227, 227, 227)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(227, 227, 227)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e3e3e3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray90', this)"&gt;&amp;#128203;&lt;/button&gt; gray90&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e5e5e5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e5e5e5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(229, 229, 229)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(229, 229, 229)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e5e5e5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray91', this)"&gt;&amp;#128203;&lt;/button&gt; gray91&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e8e8e8', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e8e8e8&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(232, 232, 232)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(232, 232, 232)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e8e8e8;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray92', this)"&gt;&amp;#128203;&lt;/button&gt; gray92&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ebebeb', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ebebeb&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(235, 235, 235)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(235, 235, 235)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ebebeb;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray93', this)"&gt;&amp;#128203;&lt;/button&gt; gray93&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ededed', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ededed&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(237, 237, 237)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(237, 237, 237)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ededed;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray94', this)"&gt;&amp;#128203;&lt;/button&gt; gray94&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f0f0f0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f0f0f0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(240, 240, 240)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(240, 240, 240)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f0f0f0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray95', this)"&gt;&amp;#128203;&lt;/button&gt; gray95&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f2f2f2', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f2f2f2&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(242, 242, 242)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(242, 242, 242)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f2f2f2;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray96', this)"&gt;&amp;#128203;&lt;/button&gt; gray96&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f5f5f5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f5f5f5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(245, 245, 245)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(245, 245, 245)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f5f5f5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray97', this)"&gt;&amp;#128203;&lt;/button&gt; gray97&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f7f7f7', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f7f7f7&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(247, 247, 247)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(247, 247, 247)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f7f7f7;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray98', this)"&gt;&amp;#128203;&lt;/button&gt; gray98&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fafafa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fafafa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(250, 250, 250)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(250, 250, 250)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fafafa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray99', this)"&gt;&amp;#128203;&lt;/button&gt; gray99&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fcfcfc', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fcfcfc&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(252, 252, 252)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(252, 252, 252)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fcfcfc;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('gray100', this)"&gt;&amp;#128203;&lt;/button&gt; gray100&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffffff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffffff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 255, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 255, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffffff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('green', this)"&gt;&amp;#128203;&lt;/button&gt; green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#008000', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#008000&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 128, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 128, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#008000;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('green yellow', this)"&gt;&amp;#128203;&lt;/button&gt; green yellow&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#adff2f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#adff2f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(173, 255, 47)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(173, 255, 47)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#adff2f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('green1', this)"&gt;&amp;#128203;&lt;/button&gt; green1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00ff00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00ff00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 255, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 255, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00ff00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('green2', this)"&gt;&amp;#128203;&lt;/button&gt; green2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00ee00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00ee00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 238, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 238, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00ee00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('green3', this)"&gt;&amp;#128203;&lt;/button&gt; green3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00cd00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00cd00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 205, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 205, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00cd00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('green4', this)"&gt;&amp;#128203;&lt;/button&gt; green4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#008b00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#008b00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 139, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 139, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#008b00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('GreenYellow', this)"&gt;&amp;#128203;&lt;/button&gt; GreenYellow&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#adff2f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#adff2f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(173, 255, 47)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(173, 255, 47)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#adff2f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey', this)"&gt;&amp;#128203;&lt;/button&gt; grey&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#808080', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#808080&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(128, 128, 128)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(128, 128, 128)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#808080;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey0', this)"&gt;&amp;#128203;&lt;/button&gt; grey0&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#000000', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#000000&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#000000;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey1', this)"&gt;&amp;#128203;&lt;/button&gt; grey1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#030303', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#030303&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(3, 3, 3)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(3, 3, 3)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#030303;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey2', this)"&gt;&amp;#128203;&lt;/button&gt; grey2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#050505', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#050505&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(5, 5, 5)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(5, 5, 5)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#050505;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey3', this)"&gt;&amp;#128203;&lt;/button&gt; grey3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#080808', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#080808&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(8, 8, 8)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(8, 8, 8)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#080808;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey4', this)"&gt;&amp;#128203;&lt;/button&gt; grey4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#0a0a0a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#0a0a0a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(10, 10, 10)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(10, 10, 10)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#0a0a0a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey5', this)"&gt;&amp;#128203;&lt;/button&gt; grey5&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#0d0d0d', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#0d0d0d&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(13, 13, 13)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(13, 13, 13)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#0d0d0d;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey6', this)"&gt;&amp;#128203;&lt;/button&gt; grey6&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#0f0f0f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#0f0f0f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(15, 15, 15)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(15, 15, 15)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#0f0f0f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey7', this)"&gt;&amp;#128203;&lt;/button&gt; grey7&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#121212', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#121212&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(18, 18, 18)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(18, 18, 18)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#121212;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey8', this)"&gt;&amp;#128203;&lt;/button&gt; grey8&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#141414', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#141414&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(20, 20, 20)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(20, 20, 20)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#141414;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey9', this)"&gt;&amp;#128203;&lt;/button&gt; grey9&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#171717', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#171717&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(23, 23, 23)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(23, 23, 23)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#171717;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey10', this)"&gt;&amp;#128203;&lt;/button&gt; grey10&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#1a1a1a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#1a1a1a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(26, 26, 26)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(26, 26, 26)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#1a1a1a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey11', this)"&gt;&amp;#128203;&lt;/button&gt; grey11&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#1c1c1c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#1c1c1c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(28, 28, 28)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(28, 28, 28)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#1c1c1c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey12', this)"&gt;&amp;#128203;&lt;/button&gt; grey12&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#1f1f1f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#1f1f1f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(31, 31, 31)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(31, 31, 31)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#1f1f1f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey13', this)"&gt;&amp;#128203;&lt;/button&gt; grey13&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#212121', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#212121&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(33, 33, 33)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(33, 33, 33)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#212121;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey14', this)"&gt;&amp;#128203;&lt;/button&gt; grey14&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#242424', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#242424&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(36, 36, 36)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(36, 36, 36)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#242424;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey15', this)"&gt;&amp;#128203;&lt;/button&gt; grey15&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#262626', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#262626&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(38, 38, 38)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(38, 38, 38)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#262626;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey16', this)"&gt;&amp;#128203;&lt;/button&gt; grey16&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#292929', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#292929&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(41, 41, 41)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(41, 41, 41)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#292929;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey17', this)"&gt;&amp;#128203;&lt;/button&gt; grey17&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#2b2b2b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#2b2b2b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(43, 43, 43)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(43, 43, 43)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#2b2b2b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey18', this)"&gt;&amp;#128203;&lt;/button&gt; grey18&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#2e2e2e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#2e2e2e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(46, 46, 46)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(46, 46, 46)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#2e2e2e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey19', this)"&gt;&amp;#128203;&lt;/button&gt; grey19&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#303030', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#303030&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(48, 48, 48)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(48, 48, 48)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#303030;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey20', this)"&gt;&amp;#128203;&lt;/button&gt; grey20&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#333333', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#333333&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(51, 51, 51)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(51, 51, 51)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#333333;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey21', this)"&gt;&amp;#128203;&lt;/button&gt; grey21&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#363636', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#363636&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(54, 54, 54)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(54, 54, 54)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#363636;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey22', this)"&gt;&amp;#128203;&lt;/button&gt; grey22&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#383838', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#383838&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(56, 56, 56)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(56, 56, 56)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#383838;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey23', this)"&gt;&amp;#128203;&lt;/button&gt; grey23&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#3b3b3b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#3b3b3b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(59, 59, 59)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(59, 59, 59)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#3b3b3b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey24', this)"&gt;&amp;#128203;&lt;/button&gt; grey24&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#3d3d3d', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#3d3d3d&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(61, 61, 61)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(61, 61, 61)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#3d3d3d;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey25', this)"&gt;&amp;#128203;&lt;/button&gt; grey25&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#404040', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#404040&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(64, 64, 64)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(64, 64, 64)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#404040;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey26', this)"&gt;&amp;#128203;&lt;/button&gt; grey26&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#424242', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#424242&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(66, 66, 66)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(66, 66, 66)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#424242;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey27', this)"&gt;&amp;#128203;&lt;/button&gt; grey27&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#454545', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#454545&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(69, 69, 69)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(69, 69, 69)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#454545;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey28', this)"&gt;&amp;#128203;&lt;/button&gt; grey28&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#474747', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#474747&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(71, 71, 71)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(71, 71, 71)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#474747;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey29', this)"&gt;&amp;#128203;&lt;/button&gt; grey29&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4a4a4a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4a4a4a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(74, 74, 74)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(74, 74, 74)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4a4a4a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey30', this)"&gt;&amp;#128203;&lt;/button&gt; grey30&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4d4d4d', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4d4d4d&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(77, 77, 77)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(77, 77, 77)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4d4d4d;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey31', this)"&gt;&amp;#128203;&lt;/button&gt; grey31&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4f4f4f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4f4f4f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(79, 79, 79)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(79, 79, 79)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4f4f4f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey32', this)"&gt;&amp;#128203;&lt;/button&gt; grey32&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#525252', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#525252&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(82, 82, 82)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(82, 82, 82)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#525252;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey33', this)"&gt;&amp;#128203;&lt;/button&gt; grey33&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#545454', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#545454&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(84, 84, 84)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(84, 84, 84)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#545454;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey34', this)"&gt;&amp;#128203;&lt;/button&gt; grey34&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#575757', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#575757&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(87, 87, 87)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(87, 87, 87)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#575757;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey35', this)"&gt;&amp;#128203;&lt;/button&gt; grey35&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#595959', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#595959&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(89, 89, 89)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(89, 89, 89)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#595959;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey36', this)"&gt;&amp;#128203;&lt;/button&gt; grey36&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#5c5c5c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#5c5c5c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(92, 92, 92)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(92, 92, 92)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#5c5c5c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey37', this)"&gt;&amp;#128203;&lt;/button&gt; grey37&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#5e5e5e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#5e5e5e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(94, 94, 94)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(94, 94, 94)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#5e5e5e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey38', this)"&gt;&amp;#128203;&lt;/button&gt; grey38&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#616161', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#616161&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(97, 97, 97)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(97, 97, 97)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#616161;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey39', this)"&gt;&amp;#128203;&lt;/button&gt; grey39&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#636363', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#636363&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(99, 99, 99)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(99, 99, 99)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#636363;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey40', this)"&gt;&amp;#128203;&lt;/button&gt; grey40&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#666666', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#666666&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(102, 102, 102)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(102, 102, 102)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#666666;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey41', this)"&gt;&amp;#128203;&lt;/button&gt; grey41&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#696969', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#696969&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(105, 105, 105)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(105, 105, 105)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#696969;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey42', this)"&gt;&amp;#128203;&lt;/button&gt; grey42&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6b6b6b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6b6b6b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(107, 107, 107)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(107, 107, 107)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6b6b6b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey43', this)"&gt;&amp;#128203;&lt;/button&gt; grey43&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6e6e6e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6e6e6e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(110, 110, 110)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(110, 110, 110)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6e6e6e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey44', this)"&gt;&amp;#128203;&lt;/button&gt; grey44&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#707070', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#707070&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(112, 112, 112)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(112, 112, 112)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#707070;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey45', this)"&gt;&amp;#128203;&lt;/button&gt; grey45&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#737373', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#737373&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(115, 115, 115)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(115, 115, 115)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#737373;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey46', this)"&gt;&amp;#128203;&lt;/button&gt; grey46&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#757575', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#757575&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(117, 117, 117)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(117, 117, 117)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#757575;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey47', this)"&gt;&amp;#128203;&lt;/button&gt; grey47&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#787878', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#787878&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(120, 120, 120)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(120, 120, 120)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#787878;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey48', this)"&gt;&amp;#128203;&lt;/button&gt; grey48&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7a7a7a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7a7a7a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(122, 122, 122)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(122, 122, 122)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7a7a7a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey49', this)"&gt;&amp;#128203;&lt;/button&gt; grey49&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7d7d7d', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7d7d7d&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(125, 125, 125)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(125, 125, 125)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7d7d7d;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey50', this)"&gt;&amp;#128203;&lt;/button&gt; grey50&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7f7f7f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7f7f7f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(127, 127, 127)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(127, 127, 127)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7f7f7f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey51', this)"&gt;&amp;#128203;&lt;/button&gt; grey51&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#828282', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#828282&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(130, 130, 130)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(130, 130, 130)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#828282;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey52', this)"&gt;&amp;#128203;&lt;/button&gt; grey52&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#858585', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#858585&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(133, 133, 133)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(133, 133, 133)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#858585;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey53', this)"&gt;&amp;#128203;&lt;/button&gt; grey53&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#878787', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#878787&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(135, 135, 135)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(135, 135, 135)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#878787;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey54', this)"&gt;&amp;#128203;&lt;/button&gt; grey54&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8a8a8a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8a8a8a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(138, 138, 138)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(138, 138, 138)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8a8a8a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey55', this)"&gt;&amp;#128203;&lt;/button&gt; grey55&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8c8c8c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8c8c8c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(140, 140, 140)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(140, 140, 140)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8c8c8c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey56', this)"&gt;&amp;#128203;&lt;/button&gt; grey56&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8f8f8f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8f8f8f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(143, 143, 143)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(143, 143, 143)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8f8f8f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey57', this)"&gt;&amp;#128203;&lt;/button&gt; grey57&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#919191', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#919191&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(145, 145, 145)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(145, 145, 145)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#919191;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey58', this)"&gt;&amp;#128203;&lt;/button&gt; grey58&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#949494', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#949494&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(148, 148, 148)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(148, 148, 148)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#949494;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey59', this)"&gt;&amp;#128203;&lt;/button&gt; grey59&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#969696', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#969696&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(150, 150, 150)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(150, 150, 150)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#969696;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey60', this)"&gt;&amp;#128203;&lt;/button&gt; grey60&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#999999', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#999999&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(153, 153, 153)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(153, 153, 153)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#999999;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey61', this)"&gt;&amp;#128203;&lt;/button&gt; grey61&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9c9c9c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9c9c9c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(156, 156, 156)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(156, 156, 156)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9c9c9c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey62', this)"&gt;&amp;#128203;&lt;/button&gt; grey62&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9e9e9e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9e9e9e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(158, 158, 158)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(158, 158, 158)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9e9e9e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey63', this)"&gt;&amp;#128203;&lt;/button&gt; grey63&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a1a1a1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a1a1a1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(161, 161, 161)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(161, 161, 161)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a1a1a1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey64', this)"&gt;&amp;#128203;&lt;/button&gt; grey64&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a3a3a3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a3a3a3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(163, 163, 163)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(163, 163, 163)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a3a3a3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey65', this)"&gt;&amp;#128203;&lt;/button&gt; grey65&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a6a6a6', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a6a6a6&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(166, 166, 166)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(166, 166, 166)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a6a6a6;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey66', this)"&gt;&amp;#128203;&lt;/button&gt; grey66&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a8a8a8', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a8a8a8&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(168, 168, 168)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(168, 168, 168)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a8a8a8;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey67', this)"&gt;&amp;#128203;&lt;/button&gt; grey67&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ababab', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ababab&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(171, 171, 171)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(171, 171, 171)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ababab;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey68', this)"&gt;&amp;#128203;&lt;/button&gt; grey68&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#adadad', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#adadad&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(173, 173, 173)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(173, 173, 173)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#adadad;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey69', this)"&gt;&amp;#128203;&lt;/button&gt; grey69&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b0b0b0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b0b0b0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(176, 176, 176)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(176, 176, 176)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b0b0b0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey70', this)"&gt;&amp;#128203;&lt;/button&gt; grey70&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b3b3b3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b3b3b3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(179, 179, 179)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(179, 179, 179)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b3b3b3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey71', this)"&gt;&amp;#128203;&lt;/button&gt; grey71&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b5b5b5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b5b5b5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(181, 181, 181)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(181, 181, 181)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b5b5b5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey72', this)"&gt;&amp;#128203;&lt;/button&gt; grey72&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b8b8b8', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b8b8b8&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(184, 184, 184)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(184, 184, 184)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b8b8b8;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey73', this)"&gt;&amp;#128203;&lt;/button&gt; grey73&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bababa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bababa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(186, 186, 186)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(186, 186, 186)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bababa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey74', this)"&gt;&amp;#128203;&lt;/button&gt; grey74&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bdbdbd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bdbdbd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(189, 189, 189)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(189, 189, 189)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bdbdbd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey75', this)"&gt;&amp;#128203;&lt;/button&gt; grey75&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bfbfbf', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bfbfbf&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(191, 191, 191)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(191, 191, 191)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bfbfbf;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey76', this)"&gt;&amp;#128203;&lt;/button&gt; grey76&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c2c2c2', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c2c2c2&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(194, 194, 194)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(194, 194, 194)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c2c2c2;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey77', this)"&gt;&amp;#128203;&lt;/button&gt; grey77&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c4c4c4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c4c4c4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(196, 196, 196)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(196, 196, 196)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c4c4c4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey78', this)"&gt;&amp;#128203;&lt;/button&gt; grey78&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c7c7c7', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c7c7c7&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(199, 199, 199)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(199, 199, 199)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c7c7c7;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey79', this)"&gt;&amp;#128203;&lt;/button&gt; grey79&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c9c9c9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c9c9c9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(201, 201, 201)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(201, 201, 201)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c9c9c9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey80', this)"&gt;&amp;#128203;&lt;/button&gt; grey80&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cccccc', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cccccc&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(204, 204, 204)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(204, 204, 204)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cccccc;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey81', this)"&gt;&amp;#128203;&lt;/button&gt; grey81&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cfcfcf', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cfcfcf&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(207, 207, 207)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(207, 207, 207)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cfcfcf;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey82', this)"&gt;&amp;#128203;&lt;/button&gt; grey82&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d1d1d1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d1d1d1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(209, 209, 209)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(209, 209, 209)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d1d1d1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey83', this)"&gt;&amp;#128203;&lt;/button&gt; grey83&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d4d4d4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d4d4d4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(212, 212, 212)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(212, 212, 212)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d4d4d4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey84', this)"&gt;&amp;#128203;&lt;/button&gt; grey84&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d6d6d6', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d6d6d6&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(214, 214, 214)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(214, 214, 214)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d6d6d6;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey85', this)"&gt;&amp;#128203;&lt;/button&gt; grey85&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d9d9d9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d9d9d9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(217, 217, 217)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(217, 217, 217)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d9d9d9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey86', this)"&gt;&amp;#128203;&lt;/button&gt; grey86&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#dbdbdb', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#dbdbdb&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(219, 219, 219)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(219, 219, 219)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#dbdbdb;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey87', this)"&gt;&amp;#128203;&lt;/button&gt; grey87&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#dedede', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#dedede&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(222, 222, 222)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(222, 222, 222)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#dedede;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey88', this)"&gt;&amp;#128203;&lt;/button&gt; grey88&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e0e0e0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e0e0e0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(224, 224, 224)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(224, 224, 224)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e0e0e0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey89', this)"&gt;&amp;#128203;&lt;/button&gt; grey89&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e3e3e3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e3e3e3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(227, 227, 227)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(227, 227, 227)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e3e3e3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey90', this)"&gt;&amp;#128203;&lt;/button&gt; grey90&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e5e5e5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e5e5e5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(229, 229, 229)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(229, 229, 229)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e5e5e5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey91', this)"&gt;&amp;#128203;&lt;/button&gt; grey91&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e8e8e8', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e8e8e8&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(232, 232, 232)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(232, 232, 232)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e8e8e8;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey92', this)"&gt;&amp;#128203;&lt;/button&gt; grey92&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ebebeb', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ebebeb&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(235, 235, 235)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(235, 235, 235)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ebebeb;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey93', this)"&gt;&amp;#128203;&lt;/button&gt; grey93&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ededed', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ededed&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(237, 237, 237)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(237, 237, 237)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ededed;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey94', this)"&gt;&amp;#128203;&lt;/button&gt; grey94&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f0f0f0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f0f0f0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(240, 240, 240)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(240, 240, 240)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f0f0f0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey95', this)"&gt;&amp;#128203;&lt;/button&gt; grey95&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f2f2f2', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f2f2f2&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(242, 242, 242)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(242, 242, 242)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f2f2f2;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey96', this)"&gt;&amp;#128203;&lt;/button&gt; grey96&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f5f5f5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f5f5f5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(245, 245, 245)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(245, 245, 245)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f5f5f5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey97', this)"&gt;&amp;#128203;&lt;/button&gt; grey97&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f7f7f7', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f7f7f7&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(247, 247, 247)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(247, 247, 247)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f7f7f7;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey98', this)"&gt;&amp;#128203;&lt;/button&gt; grey98&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fafafa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fafafa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(250, 250, 250)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(250, 250, 250)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fafafa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey99', this)"&gt;&amp;#128203;&lt;/button&gt; grey99&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fcfcfc', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fcfcfc&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(252, 252, 252)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(252, 252, 252)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fcfcfc;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('grey100', this)"&gt;&amp;#128203;&lt;/button&gt; grey100&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffffff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffffff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 255, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 255, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffffff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('honeydew', this)"&gt;&amp;#128203;&lt;/button&gt; honeydew&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f0fff0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f0fff0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(240, 255, 240)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(240, 255, 240)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f0fff0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('honeydew1', this)"&gt;&amp;#128203;&lt;/button&gt; honeydew1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f0fff0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f0fff0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(240, 255, 240)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(240, 255, 240)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f0fff0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('honeydew2', this)"&gt;&amp;#128203;&lt;/button&gt; honeydew2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e0eee0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e0eee0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(224, 238, 224)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(224, 238, 224)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e0eee0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('honeydew3', this)"&gt;&amp;#128203;&lt;/button&gt; honeydew3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c1cdc1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c1cdc1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(193, 205, 193)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(193, 205, 193)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c1cdc1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('honeydew4', this)"&gt;&amp;#128203;&lt;/button&gt; honeydew4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#838b83', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#838b83&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(131, 139, 131)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(131, 139, 131)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#838b83;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('hot pink', this)"&gt;&amp;#128203;&lt;/button&gt; hot pink&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff69b4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff69b4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 105, 180)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 105, 180)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff69b4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('HotPink', this)"&gt;&amp;#128203;&lt;/button&gt; HotPink&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff69b4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff69b4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 105, 180)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 105, 180)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff69b4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('HotPink1', this)"&gt;&amp;#128203;&lt;/button&gt; HotPink1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff6eb4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff6eb4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 110, 180)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 110, 180)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff6eb4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('HotPink2', this)"&gt;&amp;#128203;&lt;/button&gt; HotPink2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee6aa7', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee6aa7&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 106, 167)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 106, 167)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee6aa7;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('HotPink3', this)"&gt;&amp;#128203;&lt;/button&gt; HotPink3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd6090', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd6090&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 96, 144)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 96, 144)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd6090;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('HotPink4', this)"&gt;&amp;#128203;&lt;/button&gt; HotPink4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b3a62', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b3a62&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 58, 98)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 58, 98)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b3a62;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('indian red', this)"&gt;&amp;#128203;&lt;/button&gt; indian red&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd5c5c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd5c5c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 92, 92)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 92, 92)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd5c5c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('IndianRed', this)"&gt;&amp;#128203;&lt;/button&gt; IndianRed&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd5c5c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd5c5c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 92, 92)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 92, 92)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd5c5c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('IndianRed1', this)"&gt;&amp;#128203;&lt;/button&gt; IndianRed1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff6a6a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff6a6a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 106, 106)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 106, 106)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff6a6a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('IndianRed2', this)"&gt;&amp;#128203;&lt;/button&gt; IndianRed2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee6363', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee6363&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 99, 99)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 99, 99)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee6363;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('IndianRed3', this)"&gt;&amp;#128203;&lt;/button&gt; IndianRed3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd5555', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd5555&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 85, 85)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 85, 85)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd5555;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('IndianRed4', this)"&gt;&amp;#128203;&lt;/button&gt; IndianRed4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b3a3a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b3a3a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 58, 58)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 58, 58)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b3a3a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('indigo', this)"&gt;&amp;#128203;&lt;/button&gt; indigo&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4b0082', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4b0082&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(75, 0, 130)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(75, 0, 130)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4b0082;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('ivory', this)"&gt;&amp;#128203;&lt;/button&gt; ivory&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fffff0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fffff0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 255, 240)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 255, 240)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fffff0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('ivory1', this)"&gt;&amp;#128203;&lt;/button&gt; ivory1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fffff0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fffff0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 255, 240)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 255, 240)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fffff0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('ivory2', this)"&gt;&amp;#128203;&lt;/button&gt; ivory2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eeeee0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eeeee0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 238, 224)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 238, 224)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eeeee0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('ivory3', this)"&gt;&amp;#128203;&lt;/button&gt; ivory3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdcdc1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdcdc1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 205, 193)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 205, 193)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdcdc1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('ivory4', this)"&gt;&amp;#128203;&lt;/button&gt; ivory4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b8b83', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b8b83&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 139, 131)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 139, 131)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b8b83;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('khaki', this)"&gt;&amp;#128203;&lt;/button&gt; khaki&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f0e68c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f0e68c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(240, 230, 140)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(240, 230, 140)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f0e68c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('khaki1', this)"&gt;&amp;#128203;&lt;/button&gt; khaki1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fff68f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fff68f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 246, 143)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 246, 143)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fff68f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('khaki2', this)"&gt;&amp;#128203;&lt;/button&gt; khaki2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eee685', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eee685&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 230, 133)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 230, 133)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eee685;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('khaki3', this)"&gt;&amp;#128203;&lt;/button&gt; khaki3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdc673', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdc673&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 198, 115)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 198, 115)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdc673;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('khaki4', this)"&gt;&amp;#128203;&lt;/button&gt; khaki4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b864e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b864e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 134, 78)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 134, 78)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b864e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('lavender', this)"&gt;&amp;#128203;&lt;/button&gt; lavender&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e6e6fa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e6e6fa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(230, 230, 250)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(230, 230, 250)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e6e6fa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('lavender blush', this)"&gt;&amp;#128203;&lt;/button&gt; lavender blush&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fff0f5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fff0f5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 240, 245)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 240, 245)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fff0f5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LavenderBlush', this)"&gt;&amp;#128203;&lt;/button&gt; LavenderBlush&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fff0f5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fff0f5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 240, 245)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 240, 245)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fff0f5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LavenderBlush1', this)"&gt;&amp;#128203;&lt;/button&gt; LavenderBlush1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fff0f5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fff0f5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 240, 245)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 240, 245)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fff0f5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LavenderBlush2', this)"&gt;&amp;#128203;&lt;/button&gt; LavenderBlush2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eee0e5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eee0e5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 224, 229)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 224, 229)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eee0e5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LavenderBlush3', this)"&gt;&amp;#128203;&lt;/button&gt; LavenderBlush3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdc1c5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdc1c5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 193, 197)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 193, 197)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdc1c5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LavenderBlush4', this)"&gt;&amp;#128203;&lt;/button&gt; LavenderBlush4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b8386', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b8386&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 131, 134)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 131, 134)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b8386;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('lawn green', this)"&gt;&amp;#128203;&lt;/button&gt; lawn green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7cfc00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7cfc00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(124, 252, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(124, 252, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7cfc00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LawnGreen', this)"&gt;&amp;#128203;&lt;/button&gt; LawnGreen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7cfc00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7cfc00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(124, 252, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(124, 252, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7cfc00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('lemon chiffon', this)"&gt;&amp;#128203;&lt;/button&gt; lemon chiffon&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fffacd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fffacd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 250, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 250, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fffacd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LemonChiffon', this)"&gt;&amp;#128203;&lt;/button&gt; LemonChiffon&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fffacd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fffacd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 250, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 250, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fffacd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LemonChiffon1', this)"&gt;&amp;#128203;&lt;/button&gt; LemonChiffon1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fffacd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fffacd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 250, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 250, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fffacd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LemonChiffon2', this)"&gt;&amp;#128203;&lt;/button&gt; LemonChiffon2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eee9bf', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eee9bf&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 233, 191)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 233, 191)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eee9bf;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LemonChiffon3', this)"&gt;&amp;#128203;&lt;/button&gt; LemonChiffon3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdc9a5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdc9a5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 201, 165)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 201, 165)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdc9a5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LemonChiffon4', this)"&gt;&amp;#128203;&lt;/button&gt; LemonChiffon4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b8970', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b8970&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 137, 112)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 137, 112)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b8970;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light blue', this)"&gt;&amp;#128203;&lt;/button&gt; light blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#add8e6', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#add8e6&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(173, 216, 230)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(173, 216, 230)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#add8e6;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light coral', this)"&gt;&amp;#128203;&lt;/button&gt; light coral&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f08080', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f08080&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(240, 128, 128)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(240, 128, 128)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f08080;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light cyan', this)"&gt;&amp;#128203;&lt;/button&gt; light cyan&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e0ffff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e0ffff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(224, 255, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(224, 255, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e0ffff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light goldenrod', this)"&gt;&amp;#128203;&lt;/button&gt; light goldenrod&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eedd82', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eedd82&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 221, 130)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 221, 130)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eedd82;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light goldenrod yellow', this)"&gt;&amp;#128203;&lt;/button&gt; light goldenrod yellow&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fafad2', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fafad2&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(250, 250, 210)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(250, 250, 210)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fafad2;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light gray', this)"&gt;&amp;#128203;&lt;/button&gt; light gray&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d3d3d3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d3d3d3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(211, 211, 211)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(211, 211, 211)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d3d3d3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light green', this)"&gt;&amp;#128203;&lt;/button&gt; light green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#90ee90', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#90ee90&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(144, 238, 144)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(144, 238, 144)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#90ee90;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light grey', this)"&gt;&amp;#128203;&lt;/button&gt; light grey&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d3d3d3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d3d3d3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(211, 211, 211)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(211, 211, 211)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d3d3d3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light pink', this)"&gt;&amp;#128203;&lt;/button&gt; light pink&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffb6c1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffb6c1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 182, 193)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 182, 193)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffb6c1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light salmon', this)"&gt;&amp;#128203;&lt;/button&gt; light salmon&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffa07a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffa07a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 160, 122)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 160, 122)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffa07a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light sea green', this)"&gt;&amp;#128203;&lt;/button&gt; light sea green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#20b2aa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#20b2aa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(32, 178, 170)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(32, 178, 170)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#20b2aa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light sky blue', this)"&gt;&amp;#128203;&lt;/button&gt; light sky blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#87cefa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#87cefa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(135, 206, 250)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(135, 206, 250)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#87cefa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light slate blue', this)"&gt;&amp;#128203;&lt;/button&gt; light slate blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8470ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8470ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(132, 112, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(132, 112, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8470ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light slate gray', this)"&gt;&amp;#128203;&lt;/button&gt; light slate gray&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#778899', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#778899&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(119, 136, 153)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(119, 136, 153)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#778899;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light slate grey', this)"&gt;&amp;#128203;&lt;/button&gt; light slate grey&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#778899', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#778899&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(119, 136, 153)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(119, 136, 153)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#778899;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light steel blue', this)"&gt;&amp;#128203;&lt;/button&gt; light steel blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b0c4de', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b0c4de&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(176, 196, 222)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(176, 196, 222)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b0c4de;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('light yellow', this)"&gt;&amp;#128203;&lt;/button&gt; light yellow&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffffe0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffffe0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 255, 224)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 255, 224)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffffe0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightBlue', this)"&gt;&amp;#128203;&lt;/button&gt; LightBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#add8e6', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#add8e6&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(173, 216, 230)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(173, 216, 230)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#add8e6;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightBlue1', this)"&gt;&amp;#128203;&lt;/button&gt; LightBlue1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bfefff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bfefff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(191, 239, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(191, 239, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bfefff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightBlue2', this)"&gt;&amp;#128203;&lt;/button&gt; LightBlue2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b2dfee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b2dfee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(178, 223, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(178, 223, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b2dfee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightBlue3', this)"&gt;&amp;#128203;&lt;/button&gt; LightBlue3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9ac0cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9ac0cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(154, 192, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(154, 192, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9ac0cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightBlue4', this)"&gt;&amp;#128203;&lt;/button&gt; LightBlue4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#68838b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#68838b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(104, 131, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(104, 131, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#68838b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightCoral', this)"&gt;&amp;#128203;&lt;/button&gt; LightCoral&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f08080', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f08080&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(240, 128, 128)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(240, 128, 128)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f08080;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightCyan', this)"&gt;&amp;#128203;&lt;/button&gt; LightCyan&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e0ffff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e0ffff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(224, 255, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(224, 255, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e0ffff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightCyan1', this)"&gt;&amp;#128203;&lt;/button&gt; LightCyan1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e0ffff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e0ffff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(224, 255, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(224, 255, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e0ffff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightCyan2', this)"&gt;&amp;#128203;&lt;/button&gt; LightCyan2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d1eeee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d1eeee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(209, 238, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(209, 238, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d1eeee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightCyan3', this)"&gt;&amp;#128203;&lt;/button&gt; LightCyan3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b4cdcd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b4cdcd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(180, 205, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(180, 205, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b4cdcd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightCyan4', this)"&gt;&amp;#128203;&lt;/button&gt; LightCyan4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7a8b8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7a8b8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(122, 139, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(122, 139, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7a8b8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightGoldenrod', this)"&gt;&amp;#128203;&lt;/button&gt; LightGoldenrod&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eedd82', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eedd82&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 221, 130)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 221, 130)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eedd82;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightGoldenrod1', this)"&gt;&amp;#128203;&lt;/button&gt; LightGoldenrod1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffec8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffec8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 236, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 236, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffec8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightGoldenrod2', this)"&gt;&amp;#128203;&lt;/button&gt; LightGoldenrod2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eedc82', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eedc82&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 220, 130)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 220, 130)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eedc82;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightGoldenrod3', this)"&gt;&amp;#128203;&lt;/button&gt; LightGoldenrod3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdbe70', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdbe70&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 190, 112)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 190, 112)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdbe70;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightGoldenrod4', this)"&gt;&amp;#128203;&lt;/button&gt; LightGoldenrod4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b814c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b814c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 129, 76)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 129, 76)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b814c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightGoldenrodYellow', this)"&gt;&amp;#128203;&lt;/button&gt; LightGoldenrodYellow&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fafad2', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fafad2&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(250, 250, 210)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(250, 250, 210)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fafad2;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightGray', this)"&gt;&amp;#128203;&lt;/button&gt; LightGray&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d3d3d3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d3d3d3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(211, 211, 211)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(211, 211, 211)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d3d3d3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightGreen', this)"&gt;&amp;#128203;&lt;/button&gt; LightGreen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#90ee90', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#90ee90&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(144, 238, 144)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(144, 238, 144)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#90ee90;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightGrey', this)"&gt;&amp;#128203;&lt;/button&gt; LightGrey&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d3d3d3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d3d3d3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(211, 211, 211)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(211, 211, 211)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d3d3d3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightPink', this)"&gt;&amp;#128203;&lt;/button&gt; LightPink&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffb6c1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffb6c1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 182, 193)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 182, 193)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffb6c1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightPink1', this)"&gt;&amp;#128203;&lt;/button&gt; LightPink1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffaeb9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffaeb9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 174, 185)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 174, 185)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffaeb9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightPink2', this)"&gt;&amp;#128203;&lt;/button&gt; LightPink2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eea2ad', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eea2ad&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 162, 173)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 162, 173)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eea2ad;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightPink3', this)"&gt;&amp;#128203;&lt;/button&gt; LightPink3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd8c95', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd8c95&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 140, 149)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 140, 149)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd8c95;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightPink4', this)"&gt;&amp;#128203;&lt;/button&gt; LightPink4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b5f65', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b5f65&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 95, 101)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 95, 101)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b5f65;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSalmon', this)"&gt;&amp;#128203;&lt;/button&gt; LightSalmon&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffa07a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffa07a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 160, 122)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 160, 122)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffa07a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSalmon1', this)"&gt;&amp;#128203;&lt;/button&gt; LightSalmon1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffa07a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffa07a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 160, 122)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 160, 122)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffa07a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSalmon2', this)"&gt;&amp;#128203;&lt;/button&gt; LightSalmon2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee9572', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee9572&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 149, 114)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 149, 114)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee9572;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSalmon3', this)"&gt;&amp;#128203;&lt;/button&gt; LightSalmon3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd8162', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd8162&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 129, 98)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 129, 98)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd8162;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSalmon4', this)"&gt;&amp;#128203;&lt;/button&gt; LightSalmon4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b5742', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b5742&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 87, 66)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 87, 66)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b5742;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSeaGreen', this)"&gt;&amp;#128203;&lt;/button&gt; LightSeaGreen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#20b2aa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#20b2aa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(32, 178, 170)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(32, 178, 170)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#20b2aa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSkyBlue', this)"&gt;&amp;#128203;&lt;/button&gt; LightSkyBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#87cefa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#87cefa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(135, 206, 250)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(135, 206, 250)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#87cefa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSkyBlue1', this)"&gt;&amp;#128203;&lt;/button&gt; LightSkyBlue1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b0e2ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b0e2ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(176, 226, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(176, 226, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b0e2ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSkyBlue2', this)"&gt;&amp;#128203;&lt;/button&gt; LightSkyBlue2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a4d3ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a4d3ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(164, 211, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(164, 211, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a4d3ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSkyBlue3', this)"&gt;&amp;#128203;&lt;/button&gt; LightSkyBlue3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8db6cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8db6cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(141, 182, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(141, 182, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8db6cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSkyBlue4', this)"&gt;&amp;#128203;&lt;/button&gt; LightSkyBlue4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#607b8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#607b8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(96, 123, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(96, 123, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#607b8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSlateBlue', this)"&gt;&amp;#128203;&lt;/button&gt; LightSlateBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8470ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8470ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(132, 112, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(132, 112, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8470ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSlateGray', this)"&gt;&amp;#128203;&lt;/button&gt; LightSlateGray&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#778899', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#778899&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(119, 136, 153)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(119, 136, 153)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#778899;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSlateGrey', this)"&gt;&amp;#128203;&lt;/button&gt; LightSlateGrey&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#778899', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#778899&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(119, 136, 153)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(119, 136, 153)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#778899;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSteelBlue', this)"&gt;&amp;#128203;&lt;/button&gt; LightSteelBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b0c4de', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b0c4de&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(176, 196, 222)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(176, 196, 222)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b0c4de;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSteelBlue1', this)"&gt;&amp;#128203;&lt;/button&gt; LightSteelBlue1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cae1ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cae1ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(202, 225, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(202, 225, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cae1ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSteelBlue2', this)"&gt;&amp;#128203;&lt;/button&gt; LightSteelBlue2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bcd2ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bcd2ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(188, 210, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(188, 210, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bcd2ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSteelBlue3', this)"&gt;&amp;#128203;&lt;/button&gt; LightSteelBlue3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a2b5cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a2b5cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(162, 181, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(162, 181, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a2b5cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightSteelBlue4', this)"&gt;&amp;#128203;&lt;/button&gt; LightSteelBlue4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6e7b8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6e7b8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(110, 123, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(110, 123, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6e7b8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightYellow', this)"&gt;&amp;#128203;&lt;/button&gt; LightYellow&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffffe0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffffe0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 255, 224)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 255, 224)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffffe0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightYellow1', this)"&gt;&amp;#128203;&lt;/button&gt; LightYellow1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffffe0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffffe0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 255, 224)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 255, 224)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffffe0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightYellow2', this)"&gt;&amp;#128203;&lt;/button&gt; LightYellow2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eeeed1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eeeed1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 238, 209)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 238, 209)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eeeed1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightYellow3', this)"&gt;&amp;#128203;&lt;/button&gt; LightYellow3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdcdb4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdcdb4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 205, 180)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 205, 180)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdcdb4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LightYellow4', this)"&gt;&amp;#128203;&lt;/button&gt; LightYellow4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b8b7a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b8b7a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 139, 122)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 139, 122)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b8b7a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('lime', this)"&gt;&amp;#128203;&lt;/button&gt; lime&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00ff00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00ff00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 255, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 255, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00ff00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('lime green', this)"&gt;&amp;#128203;&lt;/button&gt; lime green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#32cd32', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#32cd32&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(50, 205, 50)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(50, 205, 50)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#32cd32;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('LimeGreen', this)"&gt;&amp;#128203;&lt;/button&gt; LimeGreen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#32cd32', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#32cd32&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(50, 205, 50)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(50, 205, 50)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#32cd32;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('linen', this)"&gt;&amp;#128203;&lt;/button&gt; linen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#faf0e6', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#faf0e6&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(250, 240, 230)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(250, 240, 230)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#faf0e6;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('magenta', this)"&gt;&amp;#128203;&lt;/button&gt; magenta&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff00ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff00ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 0, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 0, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff00ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('magenta1', this)"&gt;&amp;#128203;&lt;/button&gt; magenta1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff00ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff00ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 0, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 0, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff00ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('magenta2', this)"&gt;&amp;#128203;&lt;/button&gt; magenta2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee00ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee00ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 0, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 0, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee00ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('magenta3', this)"&gt;&amp;#128203;&lt;/button&gt; magenta3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd00cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd00cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 0, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 0, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd00cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('magenta4', this)"&gt;&amp;#128203;&lt;/button&gt; magenta4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b008b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b008b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 0, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 0, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b008b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('maroon', this)"&gt;&amp;#128203;&lt;/button&gt; maroon&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#800000', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#800000&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(128, 0, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(128, 0, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#800000;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('maroon1', this)"&gt;&amp;#128203;&lt;/button&gt; maroon1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff34b3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff34b3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 52, 179)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 52, 179)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff34b3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('maroon2', this)"&gt;&amp;#128203;&lt;/button&gt; maroon2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee30a7', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee30a7&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 48, 167)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 48, 167)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee30a7;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('maroon3', this)"&gt;&amp;#128203;&lt;/button&gt; maroon3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd2990', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd2990&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 41, 144)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 41, 144)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd2990;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('maroon4', this)"&gt;&amp;#128203;&lt;/button&gt; maroon4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b1c62', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b1c62&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 28, 98)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 28, 98)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b1c62;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('medium aquamarine', this)"&gt;&amp;#128203;&lt;/button&gt; medium aquamarine&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#66cdaa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#66cdaa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(102, 205, 170)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(102, 205, 170)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#66cdaa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('medium blue', this)"&gt;&amp;#128203;&lt;/button&gt; medium blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#0000cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#0000cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#0000cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('medium orchid', this)"&gt;&amp;#128203;&lt;/button&gt; medium orchid&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ba55d3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ba55d3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(186, 85, 211)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(186, 85, 211)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ba55d3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('medium purple', this)"&gt;&amp;#128203;&lt;/button&gt; medium purple&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9370db', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9370db&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(147, 112, 219)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(147, 112, 219)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9370db;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('medium sea green', this)"&gt;&amp;#128203;&lt;/button&gt; medium sea green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#3cb371', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#3cb371&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(60, 179, 113)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(60, 179, 113)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#3cb371;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('medium slate blue', this)"&gt;&amp;#128203;&lt;/button&gt; medium slate blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7b68ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7b68ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(123, 104, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(123, 104, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7b68ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('medium spring green', this)"&gt;&amp;#128203;&lt;/button&gt; medium spring green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00fa9a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00fa9a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 250, 154)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 250, 154)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00fa9a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('medium turquoise', this)"&gt;&amp;#128203;&lt;/button&gt; medium turquoise&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#48d1cc', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#48d1cc&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(72, 209, 204)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(72, 209, 204)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#48d1cc;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('medium violet red', this)"&gt;&amp;#128203;&lt;/button&gt; medium violet red&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c71585', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c71585&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(199, 21, 133)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(199, 21, 133)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c71585;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumAquamarine', this)"&gt;&amp;#128203;&lt;/button&gt; MediumAquamarine&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#66cdaa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#66cdaa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(102, 205, 170)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(102, 205, 170)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#66cdaa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumBlue', this)"&gt;&amp;#128203;&lt;/button&gt; MediumBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#0000cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#0000cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#0000cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumOrchid', this)"&gt;&amp;#128203;&lt;/button&gt; MediumOrchid&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ba55d3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ba55d3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(186, 85, 211)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(186, 85, 211)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ba55d3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumOrchid1', this)"&gt;&amp;#128203;&lt;/button&gt; MediumOrchid1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#e066ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#e066ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(224, 102, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(224, 102, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#e066ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumOrchid2', this)"&gt;&amp;#128203;&lt;/button&gt; MediumOrchid2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d15fee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d15fee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(209, 95, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(209, 95, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d15fee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumOrchid3', this)"&gt;&amp;#128203;&lt;/button&gt; MediumOrchid3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b452cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b452cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(180, 82, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(180, 82, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b452cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumOrchid4', this)"&gt;&amp;#128203;&lt;/button&gt; MediumOrchid4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7a378b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7a378b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(122, 55, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(122, 55, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7a378b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumPurple', this)"&gt;&amp;#128203;&lt;/button&gt; MediumPurple&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9370db', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9370db&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(147, 112, 219)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(147, 112, 219)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9370db;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumPurple1', this)"&gt;&amp;#128203;&lt;/button&gt; MediumPurple1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ab82ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ab82ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(171, 130, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(171, 130, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ab82ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumPurple2', this)"&gt;&amp;#128203;&lt;/button&gt; MediumPurple2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9f79ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9f79ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(159, 121, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(159, 121, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9f79ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumPurple3', this)"&gt;&amp;#128203;&lt;/button&gt; MediumPurple3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8968cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8968cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(137, 104, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(137, 104, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8968cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumPurple4', this)"&gt;&amp;#128203;&lt;/button&gt; MediumPurple4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#5d478b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#5d478b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(93, 71, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(93, 71, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#5d478b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumSeaGreen', this)"&gt;&amp;#128203;&lt;/button&gt; MediumSeaGreen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#3cb371', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#3cb371&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(60, 179, 113)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(60, 179, 113)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#3cb371;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumSlateBlue', this)"&gt;&amp;#128203;&lt;/button&gt; MediumSlateBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7b68ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7b68ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(123, 104, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(123, 104, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7b68ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumSpringGreen', this)"&gt;&amp;#128203;&lt;/button&gt; MediumSpringGreen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00fa9a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00fa9a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 250, 154)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 250, 154)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00fa9a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumTurquoise', this)"&gt;&amp;#128203;&lt;/button&gt; MediumTurquoise&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#48d1cc', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#48d1cc&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(72, 209, 204)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(72, 209, 204)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#48d1cc;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MediumVioletRed', this)"&gt;&amp;#128203;&lt;/button&gt; MediumVioletRed&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c71585', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c71585&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(199, 21, 133)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(199, 21, 133)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c71585;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('midnight blue', this)"&gt;&amp;#128203;&lt;/button&gt; midnight blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#191970', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#191970&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(25, 25, 112)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(25, 25, 112)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#191970;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MidnightBlue', this)"&gt;&amp;#128203;&lt;/button&gt; MidnightBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#191970', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#191970&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(25, 25, 112)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(25, 25, 112)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#191970;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('mint cream', this)"&gt;&amp;#128203;&lt;/button&gt; mint cream&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f5fffa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f5fffa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(245, 255, 250)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(245, 255, 250)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f5fffa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MintCream', this)"&gt;&amp;#128203;&lt;/button&gt; MintCream&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f5fffa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f5fffa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(245, 255, 250)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(245, 255, 250)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f5fffa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('misty rose', this)"&gt;&amp;#128203;&lt;/button&gt; misty rose&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffe4e1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffe4e1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 228, 225)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 228, 225)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffe4e1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MistyRose', this)"&gt;&amp;#128203;&lt;/button&gt; MistyRose&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffe4e1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffe4e1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 228, 225)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 228, 225)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffe4e1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MistyRose1', this)"&gt;&amp;#128203;&lt;/button&gt; MistyRose1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffe4e1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffe4e1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 228, 225)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 228, 225)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffe4e1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MistyRose2', this)"&gt;&amp;#128203;&lt;/button&gt; MistyRose2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eed5d2', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eed5d2&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 213, 210)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 213, 210)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eed5d2;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MistyRose3', this)"&gt;&amp;#128203;&lt;/button&gt; MistyRose3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdb7b5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdb7b5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 183, 181)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 183, 181)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdb7b5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('MistyRose4', this)"&gt;&amp;#128203;&lt;/button&gt; MistyRose4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b7d7b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b7d7b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 125, 123)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 125, 123)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b7d7b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('moccasin', this)"&gt;&amp;#128203;&lt;/button&gt; moccasin&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffe4b5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffe4b5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 228, 181)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 228, 181)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffe4b5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('navajo white', this)"&gt;&amp;#128203;&lt;/button&gt; navajo white&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffdead', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffdead&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 222, 173)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 222, 173)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffdead;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('NavajoWhite', this)"&gt;&amp;#128203;&lt;/button&gt; NavajoWhite&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffdead', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffdead&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 222, 173)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 222, 173)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffdead;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('NavajoWhite1', this)"&gt;&amp;#128203;&lt;/button&gt; NavajoWhite1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffdead', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffdead&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 222, 173)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 222, 173)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffdead;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('NavajoWhite2', this)"&gt;&amp;#128203;&lt;/button&gt; NavajoWhite2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eecfa1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eecfa1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 207, 161)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 207, 161)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eecfa1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('NavajoWhite3', this)"&gt;&amp;#128203;&lt;/button&gt; NavajoWhite3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdb38b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdb38b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 179, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 179, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdb38b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('NavajoWhite4', this)"&gt;&amp;#128203;&lt;/button&gt; NavajoWhite4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b795e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b795e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 121, 94)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 121, 94)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b795e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('navy', this)"&gt;&amp;#128203;&lt;/button&gt; navy&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#000080', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#000080&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 128)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 128)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#000080;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('navy blue', this)"&gt;&amp;#128203;&lt;/button&gt; navy blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#000080', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#000080&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 128)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 128)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#000080;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('NavyBlue', this)"&gt;&amp;#128203;&lt;/button&gt; NavyBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#000080', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#000080&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 0, 128)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 0, 128)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#000080;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('old lace', this)"&gt;&amp;#128203;&lt;/button&gt; old lace&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fdf5e6', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fdf5e6&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(253, 245, 230)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(253, 245, 230)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fdf5e6;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('OldLace', this)"&gt;&amp;#128203;&lt;/button&gt; OldLace&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fdf5e6', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fdf5e6&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(253, 245, 230)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(253, 245, 230)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fdf5e6;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('olive', this)"&gt;&amp;#128203;&lt;/button&gt; olive&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#808000', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#808000&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(128, 128, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(128, 128, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#808000;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('olive drab', this)"&gt;&amp;#128203;&lt;/button&gt; olive drab&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6b8e23', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6b8e23&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(107, 142, 35)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(107, 142, 35)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6b8e23;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('OliveDrab', this)"&gt;&amp;#128203;&lt;/button&gt; OliveDrab&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6b8e23', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6b8e23&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(107, 142, 35)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(107, 142, 35)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6b8e23;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('OliveDrab1', this)"&gt;&amp;#128203;&lt;/button&gt; OliveDrab1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c0ff3e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c0ff3e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(192, 255, 62)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(192, 255, 62)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c0ff3e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('OliveDrab2', this)"&gt;&amp;#128203;&lt;/button&gt; OliveDrab2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b3ee3a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b3ee3a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(179, 238, 58)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(179, 238, 58)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b3ee3a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('OliveDrab3', this)"&gt;&amp;#128203;&lt;/button&gt; OliveDrab3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9acd32', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9acd32&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(154, 205, 50)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(154, 205, 50)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9acd32;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('OliveDrab4', this)"&gt;&amp;#128203;&lt;/button&gt; OliveDrab4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#698b22', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#698b22&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(105, 139, 34)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(105, 139, 34)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#698b22;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('orange', this)"&gt;&amp;#128203;&lt;/button&gt; orange&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffa500', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffa500&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 165, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 165, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffa500;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('orange red', this)"&gt;&amp;#128203;&lt;/button&gt; orange red&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff4500', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff4500&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 69, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 69, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff4500;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('orange1', this)"&gt;&amp;#128203;&lt;/button&gt; orange1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffa500', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffa500&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 165, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 165, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffa500;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('orange2', this)"&gt;&amp;#128203;&lt;/button&gt; orange2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee9a00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee9a00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 154, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 154, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee9a00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('orange3', this)"&gt;&amp;#128203;&lt;/button&gt; orange3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd8500', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd8500&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 133, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 133, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd8500;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('orange4', this)"&gt;&amp;#128203;&lt;/button&gt; orange4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b5a00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b5a00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 90, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 90, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b5a00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('OrangeRed', this)"&gt;&amp;#128203;&lt;/button&gt; OrangeRed&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff4500', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff4500&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 69, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 69, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff4500;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('OrangeRed1', this)"&gt;&amp;#128203;&lt;/button&gt; OrangeRed1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff4500', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff4500&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 69, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 69, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff4500;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('OrangeRed2', this)"&gt;&amp;#128203;&lt;/button&gt; OrangeRed2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee4000', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee4000&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 64, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 64, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee4000;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('OrangeRed3', this)"&gt;&amp;#128203;&lt;/button&gt; OrangeRed3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd3700', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd3700&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 55, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 55, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd3700;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('OrangeRed4', this)"&gt;&amp;#128203;&lt;/button&gt; OrangeRed4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b2500', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b2500&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 37, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 37, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b2500;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('orchid', this)"&gt;&amp;#128203;&lt;/button&gt; orchid&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#da70d6', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#da70d6&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(218, 112, 214)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(218, 112, 214)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#da70d6;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('orchid1', this)"&gt;&amp;#128203;&lt;/button&gt; orchid1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff83fa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff83fa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 131, 250)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 131, 250)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff83fa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('orchid2', this)"&gt;&amp;#128203;&lt;/button&gt; orchid2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee7ae9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee7ae9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 122, 233)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 122, 233)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee7ae9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('orchid3', this)"&gt;&amp;#128203;&lt;/button&gt; orchid3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd69c9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd69c9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 105, 201)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 105, 201)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd69c9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('orchid4', this)"&gt;&amp;#128203;&lt;/button&gt; orchid4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b4789', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b4789&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 71, 137)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 71, 137)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b4789;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('pale goldenrod', this)"&gt;&amp;#128203;&lt;/button&gt; pale goldenrod&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eee8aa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eee8aa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 232, 170)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 232, 170)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eee8aa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('pale green', this)"&gt;&amp;#128203;&lt;/button&gt; pale green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#98fb98', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#98fb98&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(152, 251, 152)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(152, 251, 152)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#98fb98;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('pale turquoise', this)"&gt;&amp;#128203;&lt;/button&gt; pale turquoise&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#afeeee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#afeeee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(175, 238, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(175, 238, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#afeeee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('pale violet red', this)"&gt;&amp;#128203;&lt;/button&gt; pale violet red&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#db7093', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#db7093&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(219, 112, 147)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(219, 112, 147)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#db7093;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleGoldenrod', this)"&gt;&amp;#128203;&lt;/button&gt; PaleGoldenrod&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eee8aa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eee8aa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 232, 170)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 232, 170)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eee8aa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleGreen', this)"&gt;&amp;#128203;&lt;/button&gt; PaleGreen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#98fb98', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#98fb98&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(152, 251, 152)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(152, 251, 152)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#98fb98;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleGreen1', this)"&gt;&amp;#128203;&lt;/button&gt; PaleGreen1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9aff9a', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9aff9a&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(154, 255, 154)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(154, 255, 154)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9aff9a;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleGreen2', this)"&gt;&amp;#128203;&lt;/button&gt; PaleGreen2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#90ee90', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#90ee90&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(144, 238, 144)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(144, 238, 144)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#90ee90;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleGreen3', this)"&gt;&amp;#128203;&lt;/button&gt; PaleGreen3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7ccd7c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7ccd7c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(124, 205, 124)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(124, 205, 124)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7ccd7c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleGreen4', this)"&gt;&amp;#128203;&lt;/button&gt; PaleGreen4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#548b54', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#548b54&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(84, 139, 84)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(84, 139, 84)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#548b54;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleTurquoise', this)"&gt;&amp;#128203;&lt;/button&gt; PaleTurquoise&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#afeeee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#afeeee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(175, 238, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(175, 238, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#afeeee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleTurquoise1', this)"&gt;&amp;#128203;&lt;/button&gt; PaleTurquoise1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bbffff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bbffff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(187, 255, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(187, 255, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bbffff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleTurquoise2', this)"&gt;&amp;#128203;&lt;/button&gt; PaleTurquoise2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#aeeeee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#aeeeee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(174, 238, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(174, 238, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#aeeeee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleTurquoise3', this)"&gt;&amp;#128203;&lt;/button&gt; PaleTurquoise3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#96cdcd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#96cdcd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(150, 205, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(150, 205, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#96cdcd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleTurquoise4', this)"&gt;&amp;#128203;&lt;/button&gt; PaleTurquoise4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#668b8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#668b8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(102, 139, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(102, 139, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#668b8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleVioletRed', this)"&gt;&amp;#128203;&lt;/button&gt; PaleVioletRed&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#db7093', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#db7093&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(219, 112, 147)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(219, 112, 147)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#db7093;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleVioletRed1', this)"&gt;&amp;#128203;&lt;/button&gt; PaleVioletRed1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff82ab', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff82ab&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 130, 171)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 130, 171)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff82ab;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleVioletRed2', this)"&gt;&amp;#128203;&lt;/button&gt; PaleVioletRed2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee799f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee799f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 121, 159)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 121, 159)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee799f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleVioletRed3', this)"&gt;&amp;#128203;&lt;/button&gt; PaleVioletRed3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd6889', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd6889&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 104, 137)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 104, 137)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd6889;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PaleVioletRed4', this)"&gt;&amp;#128203;&lt;/button&gt; PaleVioletRed4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b475d', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b475d&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 71, 93)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 71, 93)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b475d;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('papaya whip', this)"&gt;&amp;#128203;&lt;/button&gt; papaya whip&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffefd5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffefd5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 239, 213)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 239, 213)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffefd5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PapayaWhip', this)"&gt;&amp;#128203;&lt;/button&gt; PapayaWhip&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffefd5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffefd5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 239, 213)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 239, 213)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffefd5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('peach puff', this)"&gt;&amp;#128203;&lt;/button&gt; peach puff&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffdab9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffdab9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 218, 185)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 218, 185)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffdab9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PeachPuff', this)"&gt;&amp;#128203;&lt;/button&gt; PeachPuff&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffdab9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffdab9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 218, 185)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 218, 185)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffdab9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PeachPuff1', this)"&gt;&amp;#128203;&lt;/button&gt; PeachPuff1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffdab9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffdab9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 218, 185)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 218, 185)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffdab9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PeachPuff2', this)"&gt;&amp;#128203;&lt;/button&gt; PeachPuff2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eecbad', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eecbad&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 203, 173)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 203, 173)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eecbad;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PeachPuff3', this)"&gt;&amp;#128203;&lt;/button&gt; PeachPuff3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdaf95', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdaf95&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 175, 149)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 175, 149)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdaf95;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PeachPuff4', this)"&gt;&amp;#128203;&lt;/button&gt; PeachPuff4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b7765', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b7765&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 119, 101)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 119, 101)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b7765;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('peru', this)"&gt;&amp;#128203;&lt;/button&gt; peru&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd853f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd853f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 133, 63)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 133, 63)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd853f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('pink', this)"&gt;&amp;#128203;&lt;/button&gt; pink&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffc0cb', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffc0cb&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 192, 203)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 192, 203)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffc0cb;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('pink1', this)"&gt;&amp;#128203;&lt;/button&gt; pink1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffb5c5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffb5c5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 181, 197)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 181, 197)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffb5c5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('pink2', this)"&gt;&amp;#128203;&lt;/button&gt; pink2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eea9b8', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eea9b8&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 169, 184)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 169, 184)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eea9b8;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('pink3', this)"&gt;&amp;#128203;&lt;/button&gt; pink3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd919e', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd919e&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 145, 158)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 145, 158)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd919e;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('pink4', this)"&gt;&amp;#128203;&lt;/button&gt; pink4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b636c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b636c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 99, 108)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 99, 108)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b636c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('plum', this)"&gt;&amp;#128203;&lt;/button&gt; plum&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#dda0dd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#dda0dd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(221, 160, 221)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(221, 160, 221)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#dda0dd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('plum1', this)"&gt;&amp;#128203;&lt;/button&gt; plum1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffbbff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffbbff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 187, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 187, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffbbff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('plum2', this)"&gt;&amp;#128203;&lt;/button&gt; plum2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eeaeee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eeaeee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 174, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 174, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eeaeee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('plum3', this)"&gt;&amp;#128203;&lt;/button&gt; plum3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd96cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd96cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 150, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 150, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd96cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('plum4', this)"&gt;&amp;#128203;&lt;/button&gt; plum4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b668b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b668b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 102, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 102, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b668b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('powder blue', this)"&gt;&amp;#128203;&lt;/button&gt; powder blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b0e0e6', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b0e0e6&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(176, 224, 230)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(176, 224, 230)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b0e0e6;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('PowderBlue', this)"&gt;&amp;#128203;&lt;/button&gt; PowderBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b0e0e6', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b0e0e6&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(176, 224, 230)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(176, 224, 230)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b0e0e6;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('purple', this)"&gt;&amp;#128203;&lt;/button&gt; purple&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#800080', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#800080&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(128, 0, 128)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(128, 0, 128)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#800080;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('purple1', this)"&gt;&amp;#128203;&lt;/button&gt; purple1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9b30ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9b30ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(155, 48, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(155, 48, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9b30ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('purple2', this)"&gt;&amp;#128203;&lt;/button&gt; purple2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#912cee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#912cee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(145, 44, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(145, 44, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#912cee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('purple3', this)"&gt;&amp;#128203;&lt;/button&gt; purple3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7d26cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7d26cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(125, 38, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(125, 38, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7d26cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('purple4', this)"&gt;&amp;#128203;&lt;/button&gt; purple4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#551a8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#551a8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(85, 26, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(85, 26, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#551a8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('red', this)"&gt;&amp;#128203;&lt;/button&gt; red&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff0000', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff0000&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 0, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 0, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff0000;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('red1', this)"&gt;&amp;#128203;&lt;/button&gt; red1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff0000', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff0000&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 0, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 0, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff0000;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('red2', this)"&gt;&amp;#128203;&lt;/button&gt; red2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee0000', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee0000&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 0, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 0, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee0000;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('red3', this)"&gt;&amp;#128203;&lt;/button&gt; red3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd0000', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd0000&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 0, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 0, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd0000;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('red4', this)"&gt;&amp;#128203;&lt;/button&gt; red4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b0000', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b0000&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 0, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 0, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b0000;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('rosy brown', this)"&gt;&amp;#128203;&lt;/button&gt; rosy brown&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bc8f8f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bc8f8f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(188, 143, 143)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(188, 143, 143)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bc8f8f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('RosyBrown', this)"&gt;&amp;#128203;&lt;/button&gt; RosyBrown&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#bc8f8f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#bc8f8f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(188, 143, 143)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(188, 143, 143)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#bc8f8f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('RosyBrown1', this)"&gt;&amp;#128203;&lt;/button&gt; RosyBrown1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffc1c1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffc1c1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 193, 193)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 193, 193)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffc1c1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('RosyBrown2', this)"&gt;&amp;#128203;&lt;/button&gt; RosyBrown2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eeb4b4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eeb4b4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 180, 180)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 180, 180)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eeb4b4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('RosyBrown3', this)"&gt;&amp;#128203;&lt;/button&gt; RosyBrown3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd9b9b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd9b9b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 155, 155)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 155, 155)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd9b9b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('RosyBrown4', this)"&gt;&amp;#128203;&lt;/button&gt; RosyBrown4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b6969', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b6969&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 105, 105)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 105, 105)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b6969;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('royal blue', this)"&gt;&amp;#128203;&lt;/button&gt; royal blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4169e1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4169e1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(65, 105, 225)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(65, 105, 225)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4169e1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('RoyalBlue', this)"&gt;&amp;#128203;&lt;/button&gt; RoyalBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4169e1', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4169e1&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(65, 105, 225)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(65, 105, 225)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4169e1;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('RoyalBlue1', this)"&gt;&amp;#128203;&lt;/button&gt; RoyalBlue1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4876ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4876ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(72, 118, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(72, 118, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4876ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('RoyalBlue2', this)"&gt;&amp;#128203;&lt;/button&gt; RoyalBlue2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#436eee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#436eee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(67, 110, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(67, 110, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#436eee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('RoyalBlue3', this)"&gt;&amp;#128203;&lt;/button&gt; RoyalBlue3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#3a5fcd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#3a5fcd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(58, 95, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(58, 95, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#3a5fcd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('RoyalBlue4', this)"&gt;&amp;#128203;&lt;/button&gt; RoyalBlue4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#27408b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#27408b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(39, 64, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(39, 64, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#27408b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('saddle brown', this)"&gt;&amp;#128203;&lt;/button&gt; saddle brown&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b4513', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b4513&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 69, 19)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 69, 19)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b4513;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SaddleBrown', this)"&gt;&amp;#128203;&lt;/button&gt; SaddleBrown&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b4513', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b4513&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 69, 19)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 69, 19)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b4513;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('salmon', this)"&gt;&amp;#128203;&lt;/button&gt; salmon&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fa8072', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fa8072&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(250, 128, 114)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(250, 128, 114)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fa8072;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('salmon1', this)"&gt;&amp;#128203;&lt;/button&gt; salmon1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff8c69', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff8c69&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 140, 105)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 140, 105)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff8c69;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('salmon2', this)"&gt;&amp;#128203;&lt;/button&gt; salmon2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee8262', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee8262&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 130, 98)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 130, 98)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee8262;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('salmon3', this)"&gt;&amp;#128203;&lt;/button&gt; salmon3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd7054', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd7054&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 112, 84)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 112, 84)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd7054;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('salmon4', this)"&gt;&amp;#128203;&lt;/button&gt; salmon4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b4c39', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b4c39&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 76, 57)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 76, 57)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b4c39;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('sandy brown', this)"&gt;&amp;#128203;&lt;/button&gt; sandy brown&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f4a460', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f4a460&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(244, 164, 96)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(244, 164, 96)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f4a460;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SandyBrown', this)"&gt;&amp;#128203;&lt;/button&gt; SandyBrown&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f4a460', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f4a460&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(244, 164, 96)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(244, 164, 96)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f4a460;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('sea green', this)"&gt;&amp;#128203;&lt;/button&gt; sea green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#2e8b57', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#2e8b57&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(46, 139, 87)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(46, 139, 87)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#2e8b57;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SeaGreen', this)"&gt;&amp;#128203;&lt;/button&gt; SeaGreen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#2e8b57', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#2e8b57&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(46, 139, 87)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(46, 139, 87)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#2e8b57;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SeaGreen1', this)"&gt;&amp;#128203;&lt;/button&gt; SeaGreen1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#54ff9f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#54ff9f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(84, 255, 159)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(84, 255, 159)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#54ff9f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SeaGreen2', this)"&gt;&amp;#128203;&lt;/button&gt; SeaGreen2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4eee94', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4eee94&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(78, 238, 148)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(78, 238, 148)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4eee94;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SeaGreen3', this)"&gt;&amp;#128203;&lt;/button&gt; SeaGreen3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#43cd80', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#43cd80&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(67, 205, 128)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(67, 205, 128)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#43cd80;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SeaGreen4', this)"&gt;&amp;#128203;&lt;/button&gt; SeaGreen4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#2e8b57', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#2e8b57&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(46, 139, 87)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(46, 139, 87)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#2e8b57;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('seashell', this)"&gt;&amp;#128203;&lt;/button&gt; seashell&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fff5ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fff5ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 245, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 245, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fff5ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('seashell1', this)"&gt;&amp;#128203;&lt;/button&gt; seashell1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fff5ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fff5ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 245, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 245, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fff5ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('seashell2', this)"&gt;&amp;#128203;&lt;/button&gt; seashell2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eee5de', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eee5de&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 229, 222)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 229, 222)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eee5de;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('seashell3', this)"&gt;&amp;#128203;&lt;/button&gt; seashell3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdc5bf', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdc5bf&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 197, 191)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 197, 191)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdc5bf;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('seashell4', this)"&gt;&amp;#128203;&lt;/button&gt; seashell4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b8682', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b8682&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 134, 130)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 134, 130)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b8682;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('sienna', this)"&gt;&amp;#128203;&lt;/button&gt; sienna&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#a0522d', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#a0522d&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(160, 82, 45)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(160, 82, 45)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#a0522d;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('sienna1', this)"&gt;&amp;#128203;&lt;/button&gt; sienna1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff8247', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff8247&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 130, 71)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 130, 71)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff8247;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('sienna2', this)"&gt;&amp;#128203;&lt;/button&gt; sienna2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee7942', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee7942&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 121, 66)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 121, 66)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee7942;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('sienna3', this)"&gt;&amp;#128203;&lt;/button&gt; sienna3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd6839', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd6839&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 104, 57)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 104, 57)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd6839;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('sienna4', this)"&gt;&amp;#128203;&lt;/button&gt; sienna4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b4726', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b4726&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 71, 38)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 71, 38)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b4726;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('silver', this)"&gt;&amp;#128203;&lt;/button&gt; silver&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c0c0c0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c0c0c0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(192, 192, 192)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(192, 192, 192)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c0c0c0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('sky blue', this)"&gt;&amp;#128203;&lt;/button&gt; sky blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#87ceeb', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#87ceeb&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(135, 206, 235)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(135, 206, 235)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#87ceeb;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SkyBlue', this)"&gt;&amp;#128203;&lt;/button&gt; SkyBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#87ceeb', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#87ceeb&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(135, 206, 235)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(135, 206, 235)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#87ceeb;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SkyBlue1', this)"&gt;&amp;#128203;&lt;/button&gt; SkyBlue1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#87ceff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#87ceff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(135, 206, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(135, 206, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#87ceff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SkyBlue2', this)"&gt;&amp;#128203;&lt;/button&gt; SkyBlue2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7ec0ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7ec0ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(126, 192, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(126, 192, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7ec0ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SkyBlue3', this)"&gt;&amp;#128203;&lt;/button&gt; SkyBlue3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6ca6cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6ca6cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(108, 166, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(108, 166, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6ca6cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SkyBlue4', this)"&gt;&amp;#128203;&lt;/button&gt; SkyBlue4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4a708b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4a708b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(74, 112, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(74, 112, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4a708b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('slate blue', this)"&gt;&amp;#128203;&lt;/button&gt; slate blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6a5acd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6a5acd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(106, 90, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(106, 90, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6a5acd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('slate gray', this)"&gt;&amp;#128203;&lt;/button&gt; slate gray&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#708090', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#708090&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(112, 128, 144)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(112, 128, 144)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#708090;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('slate grey', this)"&gt;&amp;#128203;&lt;/button&gt; slate grey&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#708090', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#708090&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(112, 128, 144)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(112, 128, 144)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#708090;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SlateBlue', this)"&gt;&amp;#128203;&lt;/button&gt; SlateBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6a5acd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6a5acd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(106, 90, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(106, 90, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6a5acd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SlateBlue1', this)"&gt;&amp;#128203;&lt;/button&gt; SlateBlue1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#836fff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#836fff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(131, 111, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(131, 111, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#836fff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SlateBlue2', this)"&gt;&amp;#128203;&lt;/button&gt; SlateBlue2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#7a67ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#7a67ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(122, 103, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(122, 103, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#7a67ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SlateBlue3', this)"&gt;&amp;#128203;&lt;/button&gt; SlateBlue3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6959cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6959cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(105, 89, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(105, 89, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6959cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SlateBlue4', this)"&gt;&amp;#128203;&lt;/button&gt; SlateBlue4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#473c8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#473c8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(71, 60, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(71, 60, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#473c8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SlateGray', this)"&gt;&amp;#128203;&lt;/button&gt; SlateGray&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#708090', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#708090&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(112, 128, 144)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(112, 128, 144)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#708090;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SlateGray1', this)"&gt;&amp;#128203;&lt;/button&gt; SlateGray1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#c6e2ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#c6e2ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(198, 226, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(198, 226, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#c6e2ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SlateGray2', this)"&gt;&amp;#128203;&lt;/button&gt; SlateGray2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#b9d3ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#b9d3ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(185, 211, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(185, 211, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#b9d3ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SlateGray3', this)"&gt;&amp;#128203;&lt;/button&gt; SlateGray3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9fb6cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9fb6cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(159, 182, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(159, 182, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9fb6cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SlateGray4', this)"&gt;&amp;#128203;&lt;/button&gt; SlateGray4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#6c7b8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#6c7b8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(108, 123, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(108, 123, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#6c7b8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SlateGrey', this)"&gt;&amp;#128203;&lt;/button&gt; SlateGrey&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#708090', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#708090&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(112, 128, 144)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(112, 128, 144)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#708090;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('snow', this)"&gt;&amp;#128203;&lt;/button&gt; snow&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fffafa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fffafa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 250, 250)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 250, 250)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fffafa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('snow1', this)"&gt;&amp;#128203;&lt;/button&gt; snow1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#fffafa', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#fffafa&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 250, 250)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 250, 250)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#fffafa;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('snow2', this)"&gt;&amp;#128203;&lt;/button&gt; snow2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eee9e9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eee9e9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 233, 233)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 233, 233)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eee9e9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('snow3', this)"&gt;&amp;#128203;&lt;/button&gt; snow3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdc9c9', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdc9c9&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 201, 201)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 201, 201)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdc9c9;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('snow4', this)"&gt;&amp;#128203;&lt;/button&gt; snow4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b8989', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b8989&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 137, 137)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 137, 137)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b8989;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('spring green', this)"&gt;&amp;#128203;&lt;/button&gt; spring green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00ff7f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00ff7f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 255, 127)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 255, 127)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00ff7f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SpringGreen', this)"&gt;&amp;#128203;&lt;/button&gt; SpringGreen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00ff7f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00ff7f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 255, 127)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 255, 127)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00ff7f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SpringGreen1', this)"&gt;&amp;#128203;&lt;/button&gt; SpringGreen1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00ff7f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00ff7f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 255, 127)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 255, 127)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00ff7f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SpringGreen2', this)"&gt;&amp;#128203;&lt;/button&gt; SpringGreen2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00ee76', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00ee76&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 238, 118)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 238, 118)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00ee76;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SpringGreen3', this)"&gt;&amp;#128203;&lt;/button&gt; SpringGreen3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00cd66', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00cd66&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 205, 102)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 205, 102)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00cd66;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SpringGreen4', this)"&gt;&amp;#128203;&lt;/button&gt; SpringGreen4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#008b45', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#008b45&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 139, 69)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 139, 69)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#008b45;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('steel blue', this)"&gt;&amp;#128203;&lt;/button&gt; steel blue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4682b4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4682b4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(70, 130, 180)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(70, 130, 180)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4682b4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SteelBlue', this)"&gt;&amp;#128203;&lt;/button&gt; SteelBlue&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4682b4', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4682b4&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(70, 130, 180)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(70, 130, 180)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4682b4;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SteelBlue1', this)"&gt;&amp;#128203;&lt;/button&gt; SteelBlue1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#63b8ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#63b8ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(99, 184, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(99, 184, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#63b8ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SteelBlue2', this)"&gt;&amp;#128203;&lt;/button&gt; SteelBlue2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#5cacee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#5cacee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(92, 172, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(92, 172, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#5cacee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SteelBlue3', this)"&gt;&amp;#128203;&lt;/button&gt; SteelBlue3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#4f94cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#4f94cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(79, 148, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(79, 148, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#4f94cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('SteelBlue4', this)"&gt;&amp;#128203;&lt;/button&gt; SteelBlue4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#36648b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#36648b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(54, 100, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(54, 100, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#36648b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('tan', this)"&gt;&amp;#128203;&lt;/button&gt; tan&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d2b48c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d2b48c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(210, 180, 140)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(210, 180, 140)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d2b48c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('tan1', this)"&gt;&amp;#128203;&lt;/button&gt; tan1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffa54f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffa54f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 165, 79)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 165, 79)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffa54f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('tan2', this)"&gt;&amp;#128203;&lt;/button&gt; tan2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee9a49', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee9a49&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 154, 73)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 154, 73)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee9a49;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('tan3', this)"&gt;&amp;#128203;&lt;/button&gt; tan3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd853f', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd853f&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 133, 63)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 133, 63)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd853f;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('tan4', this)"&gt;&amp;#128203;&lt;/button&gt; tan4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b5a2b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b5a2b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 90, 43)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 90, 43)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b5a2b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('teal', this)"&gt;&amp;#128203;&lt;/button&gt; teal&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#008080', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#008080&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 128, 128)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 128, 128)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#008080;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('thistle', this)"&gt;&amp;#128203;&lt;/button&gt; thistle&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d8bfd8', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d8bfd8&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(216, 191, 216)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(216, 191, 216)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d8bfd8;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('thistle1', this)"&gt;&amp;#128203;&lt;/button&gt; thistle1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffe1ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffe1ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 225, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 225, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffe1ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('thistle2', this)"&gt;&amp;#128203;&lt;/button&gt; thistle2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eed2ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eed2ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 210, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 210, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eed2ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('thistle3', this)"&gt;&amp;#128203;&lt;/button&gt; thistle3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdb5cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdb5cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 181, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 181, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdb5cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('thistle4', this)"&gt;&amp;#128203;&lt;/button&gt; thistle4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b7b8b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b7b8b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 123, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 123, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b7b8b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('tomato', this)"&gt;&amp;#128203;&lt;/button&gt; tomato&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff6347', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff6347&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 99, 71)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 99, 71)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff6347;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('tomato1', this)"&gt;&amp;#128203;&lt;/button&gt; tomato1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff6347', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff6347&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 99, 71)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 99, 71)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff6347;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('tomato2', this)"&gt;&amp;#128203;&lt;/button&gt; tomato2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee5c42', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee5c42&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 92, 66)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 92, 66)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee5c42;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('tomato3', this)"&gt;&amp;#128203;&lt;/button&gt; tomato3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd4f39', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd4f39&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 79, 57)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 79, 57)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd4f39;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('tomato4', this)"&gt;&amp;#128203;&lt;/button&gt; tomato4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b3626', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b3626&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 54, 38)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 54, 38)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b3626;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('turquoise', this)"&gt;&amp;#128203;&lt;/button&gt; turquoise&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#40e0d0', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#40e0d0&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(64, 224, 208)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(64, 224, 208)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#40e0d0;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('turquoise1', this)"&gt;&amp;#128203;&lt;/button&gt; turquoise1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00f5ff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00f5ff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 245, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 245, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00f5ff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('turquoise2', this)"&gt;&amp;#128203;&lt;/button&gt; turquoise2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00e5ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00e5ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 229, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 229, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00e5ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('turquoise3', this)"&gt;&amp;#128203;&lt;/button&gt; turquoise3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00c5cd', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00c5cd&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 197, 205)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 197, 205)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00c5cd;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('turquoise4', this)"&gt;&amp;#128203;&lt;/button&gt; turquoise4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#00868b', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#00868b&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(0, 134, 139)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(0, 134, 139)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#00868b;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('violet', this)"&gt;&amp;#128203;&lt;/button&gt; violet&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee82ee', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee82ee&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 130, 238)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 130, 238)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee82ee;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('violet red', this)"&gt;&amp;#128203;&lt;/button&gt; violet red&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d02090', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d02090&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(208, 32, 144)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(208, 32, 144)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d02090;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('VioletRed', this)"&gt;&amp;#128203;&lt;/button&gt; VioletRed&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#d02090', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#d02090&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(208, 32, 144)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(208, 32, 144)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#d02090;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('VioletRed1', this)"&gt;&amp;#128203;&lt;/button&gt; VioletRed1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ff3e96', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ff3e96&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 62, 150)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 62, 150)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ff3e96;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('VioletRed2', this)"&gt;&amp;#128203;&lt;/button&gt; VioletRed2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ee3a8c', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ee3a8c&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 58, 140)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 58, 140)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ee3a8c;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('VioletRed3', this)"&gt;&amp;#128203;&lt;/button&gt; VioletRed3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cd3278', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cd3278&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 50, 120)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 50, 120)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cd3278;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('VioletRed4', this)"&gt;&amp;#128203;&lt;/button&gt; VioletRed4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b2252', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b2252&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 34, 82)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 34, 82)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b2252;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('wheat', this)"&gt;&amp;#128203;&lt;/button&gt; wheat&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f5deb3', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f5deb3&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(245, 222, 179)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(245, 222, 179)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f5deb3;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('wheat1', this)"&gt;&amp;#128203;&lt;/button&gt; wheat1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffe7ba', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffe7ba&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 231, 186)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 231, 186)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffe7ba;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('wheat2', this)"&gt;&amp;#128203;&lt;/button&gt; wheat2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eed8ae', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eed8ae&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 216, 174)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 216, 174)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eed8ae;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('wheat3', this)"&gt;&amp;#128203;&lt;/button&gt; wheat3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdba96', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdba96&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 186, 150)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 186, 150)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdba96;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('wheat4', this)"&gt;&amp;#128203;&lt;/button&gt; wheat4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b7e66', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b7e66&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 126, 102)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 126, 102)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b7e66;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('white', this)"&gt;&amp;#128203;&lt;/button&gt; white&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffffff', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffffff&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 255, 255)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 255, 255)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffffff;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('white smoke', this)"&gt;&amp;#128203;&lt;/button&gt; white smoke&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f5f5f5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f5f5f5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(245, 245, 245)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(245, 245, 245)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f5f5f5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('WhiteSmoke', this)"&gt;&amp;#128203;&lt;/button&gt; WhiteSmoke&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#f5f5f5', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#f5f5f5&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(245, 245, 245)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(245, 245, 245)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#f5f5f5;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('yellow', this)"&gt;&amp;#128203;&lt;/button&gt; yellow&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffff00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffff00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 255, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 255, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffff00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('yellow green', this)"&gt;&amp;#128203;&lt;/button&gt; yellow green&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9acd32', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9acd32&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(154, 205, 50)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(154, 205, 50)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9acd32;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('yellow1', this)"&gt;&amp;#128203;&lt;/button&gt; yellow1&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#ffff00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#ffff00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(255, 255, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(255, 255, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#ffff00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('yellow2', this)"&gt;&amp;#128203;&lt;/button&gt; yellow2&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#eeee00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#eeee00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(238, 238, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(238, 238, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#eeee00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('yellow3', this)"&gt;&amp;#128203;&lt;/button&gt; yellow3&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#cdcd00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#cdcd00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(205, 205, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(205, 205, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#cdcd00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('yellow4', this)"&gt;&amp;#128203;&lt;/button&gt; yellow4&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#8b8b00', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#8b8b00&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(139, 139, 0)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(139, 139, 0)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#8b8b00;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;button onclick="c('YellowGreen', this)"&gt;&amp;#128203;&lt;/button&gt; YellowGreen&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('#9acd32', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;#9acd32&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;&lt;button onclick="c('(154, 205, 50)', this)"&gt;&amp;#128203;&lt;/button&gt; &lt;code&gt;(154, 205, 50)&lt;/code&gt;&lt;/td&gt;
  &lt;td class='swatch' style='background:#9acd32;'&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</content><category term="misc"></category></entry><entry><title>Vibe Coding Experiment Failures</title><link href="https://inventwithpython.com/blog/vibe-coding-failures.html" rel="alternate"></link><published>2025-08-20T10:00:00-04:00</published><updated>2025-08-20T10:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-08-20:/blog/vibe-coding-failures.html</id><summary type="html">&lt;p&gt;Over the past week I've been experimenting with &lt;em&gt;vibe coding&lt;/em&gt;: asking LLMs such as ChatGPT, Claude, and Gemini write entire apps as if I had absolutely no programming ability at all. LLMs can easily solve programming challenges or interview questions. But I wanted to see how far the current LLMs can go when asked to make complete apps, and what kinds of failure patterns emerge. From the role of a non-programmer, I would only be able to fix bugs by describing them to the LLM. For simplicity, I choose small apps written in Python that use only the standard library and the tkinter package for the GUI. This blog post details the failures: the kinds of apps that AI just isn't capable of making.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Over the past week I've been experimenting with &lt;em&gt;vibe coding&lt;/em&gt;: asking LLMs such as ChatGPT, Claude, and Gemini write entire apps as if I had absolutely no programming ability at all. LLMs can easily solve programming challenges or interview questions. But I wanted to see how far the current LLMs can go when asked to make complete apps, and what kinds of failure patterns emerge. From the role of a non-programmer, I would only be able to fix bugs by describing them to the LLM. For simplicity, I choose small apps written in Python that use only the standard library and the tkinter package for the GUI. This blog post details the failures: the kinds of apps that AI just isn't capable of making.&lt;/p&gt;
&lt;p&gt;I'll update this blog post as I find new examples.&lt;/p&gt;
&lt;p&gt;I don't care about polished or beautiful user interfaces (they are restricted to using tkinter after all.) I want to know if the generated app actually works without significant errors. For these experiments I'm using ChatGPT 5, Gemini 2.5 Pro, and Claude Sonnet 4.&lt;/p&gt;
&lt;p&gt;I've included the source for some of the programs that the LLMs produced. If you do manage to get a working version of any of these ideas, I'd love to hear about the results: &lt;a href="mailto:al@inventwithpython.com"&gt;al@inventwithpython.com&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Failure Patterns in LLM-Generated Apps&lt;/h2&gt;
&lt;p&gt;LLMs tended to fail to create software with these qualities:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Slightly unusual. Any app that hasn't been implement hundreds of times before (Tetris, stopwatch, to-do list, etc.)&lt;/li&gt;
&lt;li&gt;Require spacial or visual qualities. LLMs generate text, but dealing with coordinates or drawing tended to fall apart.&lt;/li&gt;
&lt;li&gt;Similar but not identical to common apps. When asked to create pinball, it would create pong. When asked to create the amorphous blobs of a lava lamp, it draws perfect circles. LLMs would regress to common but inaccurate examples, sometimes even in spite of specifric instructions not to.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;List of Failed Vibe Coding Experiments&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#African-Countries-Geography-Quiz"&gt;African Countries Geography Quiz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Pinball-Game"&gt;Pinball Game&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Circular-Maze-Generator"&gt;Circular Maze Generator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Interactive-Chinese-Abacus"&gt;Interactive Chinese Abacus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Combination-Lock-Simulator"&gt;Combination Lock Simulator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Family-Tree-Diagram-Editor"&gt;Family Tree Diagram Editor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Lava-Lamp-Simulator"&gt;Lava Lamp Simulator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Snow-Globe-Simulator"&gt;Snow Globe Simulator&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a name="African-Countries-Geography-Quiz" /&gt;&lt;/p&gt;
&lt;h2&gt;African Countries Geography Quiz&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt; "Create a Python program using tkinter for the GUI and only uses the packages in the Python standard library. The app displays a simple map of Africa that doesn’t have country borders. It’s just the outline of Africa. It prompts the user with the name of a random African country, then user must click on the map. The app shows the outline of the country on the map for a few seconds, and then prompts the user with another random country name. To keep it simple, there is no score or points. The app doesn’t acknowledge if the user clicked within the borders of the country or not. (They can see this for themselves when the country outline appears.) To keep it simple, there is no stopping point. The geography quiz continues until the user closes the app’s window."&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; LLMs keep forgetting that Madagascar is part of Africa unless you remind them. Also, LLMs seem to think that Africa is shaped like a potato.&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/failed-africa1.webp" style="max-width: 100%; height: 400px;;"&gt; &lt;img src="/blogstatic/failed-africa2.webp" style="max-width: 100%; height: 400px;;"&gt; &lt;img src="/blogstatic/failed-africa3.webp" style="max-width: 100%; height: 400px;;"&gt;&lt;/p&gt;
&lt;p&gt;I was hoping for something similar to &lt;a href="https://www.sporcle.com/games/kfastic/countries-of-africa-without-outlines"&gt;this geography quiz&lt;/a&gt; that shows Africa without country borders, then asks the user to click on a given country.&lt;/p&gt;
&lt;p&gt;One of the LLMs got something approximate to Africa, and it also drew Madagascar (after I reminded it that Madagascar is part of Africa.) The shapes of countries are not... accurate. I instructed them to do web searches for SVG files of maps of Africa. They said they did, and then would draw another potato.&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_africa_1.py"&gt;fail_africa_1.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_africa_2.py"&gt;fail_africa_2.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_africa_3.py"&gt;fail_africa_3.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a name="Pinball-Game" /&gt;&lt;/p&gt;
&lt;h2&gt;Pinball Game&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt; "Create a simple pinball game in Python using tkinter or turtle and only the standard library."&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Invariably the LLMs wanted to give me a pong game with a ball that moved at a constant speed. I had to add additional details telling it to not make pong, to have two flippers, to have realistic gravity, and the sides should funnel the ball towards the flippers. The LLMs didn't implement collision detection and left the pinball field blank. I wasn't expecting &lt;a href="https://en.wikipedia.org/wiki/Full_Tilt!_Pinball"&gt;Space Cadet Pinball&lt;/a&gt;, but no amount of re-prompting created anything remotely acceptable. I also tried creating examples that use Pygame, just in case tkinter was too limiting of a factor.&lt;/p&gt;
&lt;p&gt;In one case, the left flipper was incorrectly positioned but flipped the correct way, while the right flipper was correctly positioned but flipped the wrong way.&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/failed-pinball1.webp" style="max-width: 100%; height: 400px;"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_pinball_1.py"&gt;fail_pinball_1.py&lt;/a&gt; (uses Pygame)&lt;/p&gt;
&lt;p&gt;&lt;a name="Circular-Maze-Generator" /&gt;&lt;/p&gt;
&lt;h2&gt;Circular Maze Generator&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt; "Write a Python program that only uses tkinter and the standard library to generate a picture of a circle-shaped maze. The walls and boundaries of the maze should not be straight lines or rectangles. The player starts in the center and must reach the exit at the top. The player uses the keyboard arrow keys to move, and must not be able to walk through walls."&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; LLMs can easily generate rectangular mazes programs. However, the &lt;a href="https://duckduckgo.com/?t=ffab&amp;amp;q=circular+maze&amp;amp;ia=images&amp;amp;iax=images"&gt;circular mazes&lt;/a&gt; they made were crude fascimiles: the walls were sprinkled randomly with unreachable areas and obvious, almost straight line paths. Sometimes the LLM would write a program that for all its code simply displayed a blank window. And the keyboard controls were completely broken and unfixable.&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/failed-circlemaze1.webp" style="max-width: 100%; height: 400px;;"&gt; &lt;img src="/blogstatic/failed-circlemaze2.webp" style="max-width: 100%; height: 400px;;"&gt; &lt;img src="/blogstatic/failed-circlemaze3.webp" style="max-width: 100%; height: 400px;;"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_circlemaze_1.py"&gt;fail_circlemaze_1.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_circlemaze_2.py"&gt;fail_circlemaze_2.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_circlemaze_3.py"&gt;fail_circlemaze_3.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_circlemaze_4.py"&gt;fail_circlemaze_4.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a name="Interactive-Chinese-Abacus" /&gt;&lt;/p&gt;
&lt;h2&gt;Interactive Chinese Abacus&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt; "Create a Python program that uses only tkinter and the standard library to create an interactive abacus. The user can click on beads to slide them around. There should be both "heaven" and "earth" beads. The number represented by the abacus's configuration should be displayed at the bottom of the window."&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; The LLMs made programs that displayed an &lt;a href="https://en.wikipedia.org/wiki/Abacus"&gt;abacus&lt;/a&gt;, but the sliding behavior of the beads would invariably be broken. The wrong beads would slide when clicked and couldn't slide back to their original position. The display number would be completely off and sometimes negative.&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/failed-abacus1.webp" style="max-width: 100%; height: 400px;;"&gt; &lt;img src="/blogstatic/failed-abacus2.webp" style="max-width: 100%; height: 400px;;"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_abacus_1.py"&gt;fail_abacus_1.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_abacus_2.py"&gt;fail_abacus_2.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_abacus_3.py"&gt;fail_abacus_3.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a name="Combination-Lock-Simulator" /&gt;&lt;/p&gt;
&lt;h2&gt;Combination Lock Simulator&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt; "Create a Python program that uses only tkinter and the standard library. The program is a combination lock simulator. The program displays a combination lock and and lets the user drag the mouse to spin the dial. The combination is displayed at the top. If the user has correctly entered the combination, when they click the latch the latch will pop open. The window should display instructions to the user on how to use the app to open the lock."&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; While the programs would display a rudimentary combination lock and dial with numbers, turning the dial was haphazard. The dial numbers would always be upright as the dial turned, but I didn't require that they be rotated correctly. Entering the combination was impossible, and even when I entered the correct numbers, clicking the latch button had no effect.&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/failed-combo1.webp" style="max-width: 100%; height: 400px;;"&gt; &lt;img src="/blogstatic/failed-combo2.webp" style="max-width: 100%; height: 400px;;"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_combo_1.py"&gt;fail_combo_1.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_combo_2.py"&gt;fail_combo_2.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_combo_3.py"&gt;fail_combo_3.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a name="Family-Tree-Diagram-Editor" /&gt;&lt;/p&gt;
&lt;h2&gt;Family Tree Diagram Editor&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt; "Create a Python program that uses only tkinter and the standard library. Create a family tree diagram app. The program uses squares with text names to represent people. The program begins with a single square. You can click on squares to edit the name, add a spouse, add a child, add a parent, or delete the person. The diagram automatically redraws itself as people are added or deleted. Use the standard lines of a family tree diagram."&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Completely broken. The window would display the starting person square and some of the generated apps let me edit the name. But adding any relations would either do nothing or completely break/make invisible the existing diagram. It was never able to draw a second square.&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/failed-familytree1.webp" style="max-width: 100%; height: 400px;;"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_familytree_1.py"&gt;fail_familytree_1.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_familytree_2.py"&gt;fail_familytree_2.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_familytree_3.py"&gt;fail_familytree_3.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a name="Lava-Lamp-Simulator" /&gt;&lt;/p&gt;
&lt;h2&gt;Lava Lamp Simulator&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt; "Create a Python program that only uses tkinter and the standard library. The program should be a lava lamp simulator that shows blobs gently floating around the window. Do not use circles, ellipses, or polygons for the blobs. Use bezier curves for the blob shapes. The blobs should slowly combine and separate like they do in a lava lamp."&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; These looked nothing like &lt;a href="https://en.wikipedia.org/wiki/Lava_lamp"&gt;lava lamps&lt;/a&gt;. The programs would display shapes that move, but that's all. They would bluntly combine together as they came close, with one blob disappearing and the other immediately increasing in size. The blobs would never separate; some apps had small blobs spontaneously generate out of thin air. The blobs tended to vibrate like nervous Chihuahuas.&lt;/p&gt;
&lt;p&gt;One of the LLMs just drew blob outlines.&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/failed-lavalamp1.webp" style="max-width: 100%; height: 400px;;"&gt; &lt;img src="/blogstatic/failed-lavalamp2.webp" style="max-width: 100%; height: 400px;;"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_lavalamp_1.py"&gt;fail_lavalamp_1.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_lavalamp_2.py"&gt;fail_lavalamp_2.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a name="Snow-Globe-Simulator" /&gt;&lt;/p&gt;
&lt;h2&gt;Snow Globe Simulator&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt; "Create a Python program that uses only tkinter and the standard library. The program is a "snow globe" with a blue background and white "snow" in it. When the window is moved around and "shook", the snow should also move around in it the way it would in a snow globe. Shaking the window faster causes the snow to move faster. The snow flakes should swirl inside the window rather than move like bouncing ping pong balls."&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Claude had some limited success; moving the window would "shake" the snow flakes but their behavior was more like ping pong balls in a cardboard box than snow flakes in a snow globe. The speed and vigor I shook the window seemed to have no difference; slow and fast shaking resulted in the same effect.&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/failed-snowglobe1.webp" style="max-width: 100%; height: 400px;;"&gt; &lt;img src="/blogstatic/failed-snowglobe2.webp" style="max-width: 100%; height: 400px;;"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_snowglobe_1.py"&gt;fail_snowglobe_1.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/fail_snowglobe_2.py"&gt;fail_snowglobe_2.py&lt;/a&gt;&lt;/p&gt;
&lt;!--

Here’s a list of algorithms that could be useful for a lava lamp simulator app:

Perlin Noise
Fluid Simulation (Navier-Stokes)
Voronoi Diagram
Marching Squares
Bézier Curves
Particle System
Lennard-Jones Potential (Molecular Dynamics)
Cellular Automata
Radial Gradient Blurring
Force-Directed Graph (for blob movement)
K-Means Clustering (for grouping blobs)
A Pathfinding (for smooth movement)*
Fluid Dynamics with Smoothed Particle Hydrodynamics (SPH)
Convection Diffusion Algorithm
Mesh Deformation



Here’s a list of algorithms and data structures that would be useful for a lava lamp simulator:
Algorithms:

Perlin Noise (for fluid-like motion and randomness)
Fluid Simulation (Navier-Stokes) (for realistic liquid movement)
Metaball Algorithm (for smooth blob merging and interaction)
Particle Systems (for managing individual blob particles)
Smoothed Particle Hydrodynamics (SPH) (for simulating liquid dynamics)
Marching Squares (for generating smooth surfaces)
Convection-Diffusion (for simulating heat or fluid flow in the liquid)
Lennard-Jones Potential (for simulating atomic interactions in blobs)
Voronoi Diagrams (for dividing space and simulating fluid interactions)
Bezier Curves (for smooth paths or blob movement)
K-Means Clustering (for grouping and simplifying blob behavior)
A Pathfinding* (for smooth blob motion and path adjustments)


--&gt;</content><category term="misc"></category></entry><entry><title>Al Sweigart's Endorsements for PSF Board 2025</title><link href="https://inventwithpython.com/blog/psf-candidate-endorsements-2025.html" rel="alternate"></link><published>2025-08-18T09:38:00-04:00</published><updated>2025-08-18T09:38:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-08-18:/blog/psf-candidate-endorsements-2025.html</id><summary type="html">&lt;p&gt;The &lt;a href="https://pyfound.blogspot.com/2025/08/announcing-psf-board-candidates-for-2025.html"&gt;nominees for the Python Software Foundation's Board of Directors 2025 election&lt;/a&gt; have been announced. You can find a full list on the &lt;a href="https://www.python.org/nominations/elections/2025-python-software-foundation-board/nominees/"&gt;nominees page&lt;/a&gt;. This year there are 4 seats open on the PSF Board. You can see who is currently on the board on the &lt;a href="https://www.python.org/psf/board/"&gt;PSF Officers &amp;amp; Directors page&lt;/a&gt;. I'm pleased to announce I'm endorsing the following nominees: &lt;strong&gt;Abigail Dogbe, Archana Vaidheeswaran, Daniele Procida, Jannis Leidel, Sheena O'Connell, Simon Willison&lt;/strong&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;The &lt;a href="https://pyfound.blogspot.com/2025/08/announcing-psf-board-candidates-for-2025.html"&gt;nominees for the Python Software Foundation's Board of Directors 2025 election&lt;/a&gt; have been announced. You can find a full list on the &lt;a href="https://www.python.org/nominations/elections/2025-python-software-foundation-board/nominees/"&gt;nominees page&lt;/a&gt;. This year there are 4 seats open on the PSF Board. You can see who is currently on the board on the &lt;a href="https://www.python.org/psf/board/"&gt;PSF Officers &amp;amp; Directors page&lt;/a&gt;. I'm pleased to announce I'm endorsing the following nominees: &lt;strong&gt;Abigail Dogbe, Archana Vaidheeswaran, Daniele Procida, Jannis Leidel, Sheena O'Connell, Simon Willison&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can become &lt;a href="https://www.python.org/psf/membership/"&gt;a member of the PSF&lt;/a&gt;. If you wish to vote in this year's election, you must &lt;a href="https://pyfound.blogspot.com/2025/07/affirm-your-psf-membership-voting-status.html"&gt;affirm your intention to vote&lt;/a&gt; no later than Tuesday, August 26th, 2:00 pm UTC, to participate in this year’s election. This year's Board Election vote begins Tuesday, September 2nd, 2:00 pm UTC, and closes on Tuesday, September 16th, 2:00 pm UTC.&lt;/p&gt;
&lt;h2&gt;Abigail Dogbe&lt;/h2&gt;
&lt;p&gt;Dogbe has a long history of involvement with various Python &amp;amp; Django conferences, PyLadies, and Django Girls. She is a PSF Fellow and PSF Community Service Award recipient. Dogbe is a personal acquaintance I met at PyOhio in 2024. I endorse Abigail Dogbe for the 2025 PSF Board election.&lt;/p&gt;
&lt;h2&gt;Archana Vaidheeswaran&lt;/h2&gt;
&lt;p&gt;Vaidheeswaran has extensive experience with fundraising, event organizing, non-profit organizations, teaching, and technical expertise. Vaidheeswaran is a PSF Fellow. I endorse Archana Vaidheeswaran for the 2025 PSF Board election.&lt;/p&gt;
&lt;h2&gt;Daniele Procida&lt;/h2&gt;
&lt;p&gt;Procida has a long history in technical and leadership roles. He is a PSF Fellow and PSF Community Service Award recipient. I endorse Daniele Procida for the 2025 PSF Board election.&lt;/p&gt;
&lt;h2&gt;Jannis Leidel&lt;/h2&gt;
&lt;p&gt;Leidel has previously served on the PSF board twice before and served on numerous committees and working groups. He shows a keen awareness of the role of the PSF and what it needs from the board. I endorse Jannis Leidel for the 2025 PSF Board election.&lt;/p&gt;
&lt;h2&gt;Sheena O'Connell&lt;/h2&gt;
&lt;p&gt;O'Connell has a long history of conference organizing and education programs in the Python and Django communities. She has a clear plan of action for how the board can help the global Python community. O'Connell is a personal acquaintance, and we met at PyCon US 2025. I endorse Sheena O'Connell for the 2025 PSF Board election.&lt;/p&gt;
&lt;h2&gt;Simon Willison&lt;/h2&gt;
&lt;p&gt;Willison is one of the co-creators of Django, the creator of Datasette, and has a widely-read blog covering LLM news and his own software experiments. In the current hype bubble, he is one of the only people I trust to be knowledgeable and sober about AI. He is a PSF Fellow and has previously served on the PSF board. I endorse Simon Willison for the 2025 PSF Board election.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>The Recursive Leap of Faith, Explained</title><link href="https://inventwithpython.com/blog/leap-of-faith.html" rel="alternate"></link><published>2025-08-09T10:01:00-04:00</published><updated>2025-08-09T10:01:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-08-09:/blog/leap-of-faith.html</id><summary type="html">&lt;p&gt;There is a phrase used when teaching recursion and recursive functions, &lt;em&gt;leap of faith&lt;/em&gt;. In researching my book, &lt;a href="https://inventwithpython.com/recursion/"&gt;The Recursive Book of Recursion&lt;/a&gt;, I found that most explanation of this technique were vague or even misleading. In this article, I'll go into what the leap of faith is and how it can help you write your recursive functions.&lt;br&gt;&lt;img src="/blogstatic/leap-of-faith.webp" style="max-width: 100%;"&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;There is a phrase used when teaching recursion and recursive functions, &lt;em&gt;leap of faith&lt;/em&gt;. In researching my book, &lt;a href="https://inventwithpython.com/recursion/"&gt;The Recursive Book of Recursion&lt;/a&gt;, I found that most explanation of this technique were vague or even misleading. In this article, I'll go into what the leap of faith is and how it can help you write your recursive functions.&lt;br&gt;&lt;img src="/blogstatic/leap-of-faith.webp" style="max-width: 100%;"&gt;&lt;/p&gt;
&lt;p&gt;TL;DR: See "The Five Guidelines for Taking a Leap of Faith" list.&lt;/p&gt;
&lt;p&gt;For brevity, I'll assume you have heard of recursive functions (i.e. functions that call themselves) and are familiar with the factorial function. Here's the Python code for it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# BASE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# RECURSIVE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The base case, where &lt;code&gt;number&lt;/code&gt; is passed the argument &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt;, returns &lt;code&gt;1&lt;/code&gt;. The recursive case makes the recursive call &lt;code&gt;factorial(number - 1)&lt;/code&gt;. Factorial only Since the &lt;code&gt;number&lt;/code&gt; argument must be an integer of &lt;code&gt;1&lt;/code&gt; or greater, the argument to the recursive case gets closer and closer to the base case: &lt;code&gt;1&lt;/code&gt;. In fact, in The Recursive Book of Recursion, I advise that you always ask yourself three questions when writing recursive functions (to which I've provided answers for our &lt;code&gt;factorial()&lt;/code&gt; function):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What is the base case?&lt;/strong&gt; - The base case for &lt;code&gt;factorial()&lt;/code&gt; is when &lt;code&gt;number&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt;, and 0! = 1 and 1! = 1 so the base case should evaluate to &lt;code&gt;1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What argument is passed to the recursive function call?&lt;/strong&gt; - In the recursive case, the argument is &lt;code&gt;number - 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How does this argument become closer to the base case?&lt;/strong&gt; - Since &lt;code&gt;number&lt;/code&gt; starts off larger than 1, &lt;code&gt;number - 1&lt;/code&gt; ensures it keeps getting smaller until it reaches &lt;code&gt;1&lt;/code&gt;, the base case.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But in case this isn't enough for you to figure out how to write the recursive function, let's use the &lt;em&gt;leap-of-faith&lt;/em&gt; technique. First, we start with a blank function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Uh... I don&amp;#39;t know what code goes here. Leave it blank for now.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then let's apply the following guidelines:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start by figuring out the data types of the parameters and return value.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is easy for factorial. 5! = 120. There is one integer parameter (&lt;code&gt;number&lt;/code&gt;) and the function returns an integer.&lt;/p&gt;
&lt;p&gt;This is general good advice for writing recursive functions, because &lt;em&gt;the parameters and return value must have consistent data types.&lt;/em&gt; You wouldn't want to, say, have &lt;code&gt;factorial()&lt;/code&gt; return an integer for the base case and a list of integers for the recursive case. If your recursive function has multiple recursive cases, you wouldn't want one recursive call to pass an integer argument and the other recursive call pass the &lt;code&gt;None&lt;/code&gt; value. Make sure you have a firm grasp on what the data types of the parameters and return value are, and make sure your code always sticks to it.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Next, implement the base case.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The base case is usually something small, simple, and has no recursive calls. 1! = 1 is such a base case:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# BASE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Take a leap of faith and assume your recursive function magically returns the correct value, and write your recursive case.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This means that, &lt;em&gt;before we're done writing our &lt;code&gt;factorial()&lt;/code&gt; function&lt;/em&gt;, we assume that &lt;code&gt;factorial()&lt;/code&gt; always returns the correct value. Wait... that makes it easy! We could just write our function like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Of course recursion couldn't be this simple. Indeed, if you run this program, you get a &lt;em&gt;stack overflow&lt;/em&gt; error:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;factorial&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;Traceback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;most&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;recent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;call&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;last&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;:
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;python-input-1&amp;gt;&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;factorial&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;~~~~~~~~~^^^&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;python-input-0&amp;gt;&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;factorial&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;factorial&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;python-input-0&amp;gt;&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;factorial&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;factorial&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;python-input-0&amp;gt;&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;factorial&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;factorial&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;[&lt;span class="nv"&gt;Previous&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;repeated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;988&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;more&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;times&lt;/span&gt;]
&lt;span class="nv"&gt;RecursionError&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;maximum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;recursion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;depth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;exceeded&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is because there are two important caveats to our magical function:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;First Caveat: The argument to the recursive function call cannot be the original argument.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;This is a fact that most leap-of-faith explanations fail to mention.&lt;/em&gt; This restricts us so that our &lt;code&gt;factorial()&lt;/code&gt; function can't simply call &lt;code&gt;factorial(number)&lt;/code&gt;. Instead, we have to pass &lt;em&gt;some other argument&lt;/em&gt; to the recursive &lt;code&gt;factorial()&lt;/code&gt; call. After all, "Ten factorial is ten factorial." may be true but it doesn't tell us much. We're forced to think about how &lt;code&gt;factorial(number)&lt;/code&gt; can be related to &lt;code&gt;factorial(*something_besides_just_number*)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Earlier, I pointed out that 5! = 5 x 4 x 3 x 2 x 1. If we sit and think for a bit (easier said than done, and this will be different for each recursive algorithm), we can notice that 4! = 4 x 3 x 2 x 1 and then, after more sitting and thinking, 5! = 5 x 4!. As code, this would look like &lt;code&gt;factorial(5) == 5 * factorial(4)&lt;/code&gt;. But we'd want to generalize this for &lt;em&gt;any&lt;/em&gt; argument for the &lt;code&gt;number&lt;/code&gt; parameter. We know that &lt;code&gt;factorial(5) == 5 * factorial(4)&lt;/code&gt; and &lt;code&gt;factorial(4) == 4 * factorial(3)&lt;/code&gt;, so it seems the &lt;em&gt;general pattern&lt;/em&gt; here is mathematically &lt;em&gt;N! = N x (N - 1)!&lt;/em&gt;. As code, this would be &lt;code&gt;return number * factorial(number - 1)&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# BASE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# RECURSIVE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ah, if only our magical recursive call to &lt;code&gt;factorial(number - 1)&lt;/code&gt; would give us the correct value, then we'd have our solution.&lt;/p&gt;
&lt;p&gt;But wait... it does! Or at least, we're taking a leap of faith that it does. But faith is rather useless, so let's try it out and see:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; factorial(5)
120
&amp;gt;&amp;gt;&amp;gt; factorial(6)
720
&amp;gt;&amp;gt;&amp;gt; factorial(10)
3628800
&amp;gt;&amp;gt;&amp;gt; factorial(1)
1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It works! Well done! But wait, didn't I say there was a &lt;em&gt;second&lt;/em&gt; caveat? There is, and it's one our &lt;code&gt;factorial()&lt;/code&gt; function avoided by pure luck:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Second Caveat: The argument to the recursive function call must ALWAYS get closer to the base case.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For the recursive &lt;code&gt;factorial()&lt;/code&gt; function we wrote, this is true. The &lt;code&gt;number - 1&lt;/code&gt; argument ensures that the next &lt;code&gt;number&lt;/code&gt; argument gets closer to the base case, &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This works. We have used the five guidelines of the leap-of-faith technique to write a recursive function. You could stop reading this blog post now.&lt;/p&gt;
&lt;p&gt;Or you could keep reading and follow me down a rather strange and demented turn.&lt;/p&gt;
&lt;p&gt;What if, instead of having the insight that 5! = 5 x 4!, we noticed that 5! = 6! / 6. Mathematically, this is a correct equation. This would make our recursive case &lt;code&gt;return factorial(number + 1) // (number + 1)&lt;/code&gt;. (If &lt;code&gt;5&lt;/code&gt; is our &lt;code&gt;number&lt;/code&gt;, we'd generalize this to &lt;code&gt;6&lt;/code&gt; being &lt;code&gt;number + 1&lt;/code&gt;. The integer division operator &lt;code&gt;//&lt;/code&gt; ensures we stick with integers and don't get floating-point rounding errors.) And &lt;code&gt;number + 1&lt;/code&gt; is not &lt;code&gt;number&lt;/code&gt;, so guideline 4 is being followed. However, by &lt;em&gt;adding&lt;/em&gt; to &lt;code&gt;number&lt;/code&gt; and making it bigger, we are actually getting &lt;em&gt;further away&lt;/em&gt; from the base case, &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If we tried to use this code, our &lt;code&gt;factorial()&lt;/code&gt; function would look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# BASE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# RECURSIVE CASE - The number + 1 argument gets further away from the base case 1.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When we run this code, we get a stack overflow:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; factorial(5)
Traceback (most recent call last):
  File &amp;quot;&amp;lt;python-input-13&amp;gt;&amp;quot;, line 1, in &amp;lt;module&amp;gt;
    factorial(5)
    ~~~~~~~~~^^^
  File &amp;quot;&amp;lt;python-input-12&amp;gt;&amp;quot;, line 10, in factorial
    return factorial(number + 1) // (number + 1)
           ~~~~~~~~~^^^^^^^^^^^^
  File &amp;quot;&amp;lt;python-input-12&amp;gt;&amp;quot;, line 10, in factorial
    return factorial(number + 1) // (number + 1)
           ~~~~~~~~~^^^^^^^^^^^^
  File &amp;quot;&amp;lt;python-input-12&amp;gt;&amp;quot;, line 10, in factorial
    return factorial(number + 1) // (number + 1)
           ~~~~~~~~~^^^^^^^^^^^^
  [Previous line repeated 988 more times]
RecursionError: maximum recursion depth exceeded
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Okay, so calling &lt;code&gt;factorial(5)&lt;/code&gt; failed. But what if we used a bigger base case? I can figure out 100! by entering the Python expression &lt;code&gt;100 * 99 * 98 * 97 ...&lt;/code&gt; and so on, and that tells me that 100! is 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000. (This is rather demented.) I'll make this my new base case:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# BASE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# RECURSIVE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And now &lt;code&gt;factorial(5)&lt;/code&gt; works, because the argument getting bigger makes it get closer to the base case, &lt;code&gt;100&lt;/code&gt;. It works with all the previous test cases we did:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; factorial(5)
120
&amp;gt;&amp;gt;&amp;gt; factorial(6)
720
&amp;gt;&amp;gt;&amp;gt; factorial(10)
3628800
&amp;gt;&amp;gt;&amp;gt; factorial(1)
1
&amp;gt;&amp;gt;&amp;gt; factorial(101)
Traceback (most recent call last):
  File &amp;quot;&amp;lt;python-input-5&amp;gt;&amp;quot;, line 1, in &amp;lt;module&amp;gt;
    factorial(101)
    ~~~~~~~~~^^^^^
  File &amp;quot;&amp;lt;python-input-4&amp;gt;&amp;quot;, line 10, in factorial
    return factorial(number + 1) // (number + 1)
           ~~~~~~~~~^^^^^^^^^^^^
  File &amp;quot;&amp;lt;python-input-4&amp;gt;&amp;quot;, line 10, in factorial
    return factorial(number + 1) // (number + 1)
           ~~~~~~~~~^^^^^^^^^^^^
  File &amp;quot;&amp;lt;python-input-4&amp;gt;&amp;quot;, line 10, in factorial
    return factorial(number + 1) // (number + 1)
           ~~~~~~~~~^^^^^^^^^^^^
  [Previous line repeated 988 more times]
RecursionError: maximum recursion depth exceeded
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This works as long as the argument passed to &lt;code&gt;factorial()&lt;/code&gt; is less or equal to than &lt;code&gt;100&lt;/code&gt;. Unfortunately, calling &lt;code&gt;factorial(101)&lt;/code&gt; (or any argument greater than &lt;code&gt;100&lt;/code&gt;) results in a stack overflow. You have to read that fifth guideline carefully, especially the word "always":&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The argument to the recursive function call must ALWAYS get closer to the base case.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;code&gt;factorial()&lt;/code&gt; function we wrote with a base case of &lt;code&gt;100&lt;/code&gt; doesn't &lt;strong&gt;ALWAYS&lt;/strong&gt; get closer to the base case. It only gets closer to the &lt;code&gt;100&lt;/code&gt; base case when the &lt;code&gt;number&lt;/code&gt; argument is less than &lt;code&gt;100&lt;/code&gt;. Well, we can fix this code by only doing &lt;code&gt;return factorial(number + 1) // (number + 1)&lt;/code&gt; if &lt;code&gt;number&lt;/code&gt; is less than &lt;code&gt;100&lt;/code&gt;. The rest of the time, we can do &lt;code&gt;return number * factorial(number - 1)&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# BASE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# RECURSIVE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# ANOTHER RECURSIVE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that our code handles &lt;code&gt;number&lt;/code&gt; arguments greater than &lt;code&gt;100&lt;/code&gt; and less than &lt;code&gt;100&lt;/code&gt;, the argument to the recursive function call ALWAYS gets closer to our base case of &lt;code&gt;100&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; factorial(101)
9425947759838359420851623124482936749562312794702543768327889353416977599316221476503087861591808346911623490003549599583369706302603264000000000000000000000000
&amp;gt;&amp;gt;&amp;gt; factorial(5)
120
&amp;gt;&amp;gt;&amp;gt; factorial(102) == factorial(100) &lt;span class="gs"&gt;* 101 *&lt;/span&gt; 102
True
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Though perhaps I took this a bit too far.&lt;/p&gt;
&lt;h1&gt;The Five Guidelines for Taking a Leap of Faith&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start by figuring out the data types of the parameters and return value.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Next, implement the base case.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Take a leap of faith and assume your recursive function magically returns the correct value, and write your recursive case.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First Caveat: The argument to the recursive function call cannot be the original argument.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Second Caveat: The argument to the recursive function call must ALWAYS get closer to the base case.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As long as you follow these five guidelines, you can write a correctly working recursive function. Even if it comes out as demented as that last &lt;code&gt;factorial()&lt;/code&gt; function I wrote, the code will work.&lt;/p&gt;
&lt;h1&gt;Another Example: Recursive Permutation&lt;/h1&gt;
&lt;p&gt;For all my expertise with recursion, I admit that I still have trouble writing a recursive permutation function. This is a function that is given a string and then returns a set of all permutations (that is, different orderings) of that string. For example, calling &lt;code&gt;permutations('ABC')&lt;/code&gt; should return the list &lt;code&gt;['ABC', 'ACB', 'BAC', 'BCA', 'CAB', 'CBA']&lt;/code&gt;. I can never seem to remember how to write this function. I'll follow my own advice and use the four guidelines of the leap-of-faith technique to create it here.&lt;/p&gt;
&lt;p&gt;(By the way, the number of permutations for a group of N objects is N!. This is why the factorial of zero is mathematically defined as one; if you have a set of zero objects, there is one way to order the objects in that set: a set of zero objects.)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start by figuring out the data types of the parameters and return value.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Okay. This is easy. For simplicity, our function will take a single string. The return value will always be a list of strings of the permutations of the string argument. So &lt;code&gt;permutations('ABC')&lt;/code&gt; returns &lt;code&gt;['ABC', 'ACB', 'BAC', 'BCA', 'CAB', 'CBA']&lt;/code&gt;, or &lt;code&gt;permutations('AB')&lt;/code&gt; returns &lt;code&gt;['AB', 'BA']&lt;/code&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Next, implement the base case.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Also simple: &lt;code&gt;permutations('')&lt;/code&gt; should return the list &lt;code&gt;['']&lt;/code&gt; and &lt;code&gt;permutations('A')&lt;/code&gt; should return &lt;code&gt;['A']&lt;/code&gt;. So if the length of the string argument is 0 or 1, the function should return a list of the argument. Remember the data types we set out: &lt;code&gt;permutations()&lt;/code&gt; should always return a list of strings like &lt;code&gt;['A']&lt;/code&gt; and not just a string like &lt;code&gt;'A'&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;permutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# BASE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Take a leap of faith and assume your recursive function magically returns the correct value, and write your recursive case.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Ah, if only we could do this and be done with our problem:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;permutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# BASE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;permutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But we have to keep in mind the fourth guideline:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;First Caveat: The argument to the recursive function call cannot be the original argument.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So what can we pass to &lt;code&gt;permutations()&lt;/code&gt; instead? Let's sit and think for a bit: To find the permutations of &lt;code&gt;'ABC'&lt;/code&gt;, we could split it into the first character (we'll call this the "head") and everything after the first character (the "tail"). We could get all the permutations of the tail, and then inject the head into each position of each permutation. We satisfy the first caveat: when &lt;code&gt;permutations(chars)&lt;/code&gt; is called, the recursive case is &lt;code&gt;permutations(chars[1:])&lt;/code&gt; and &lt;code&gt;chars[1:]&lt;/code&gt; is different from &lt;code&gt;chars&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let's think this through with an example. If &lt;code&gt;chars&lt;/code&gt; was &lt;code&gt;'ABC'&lt;/code&gt;, the head is &lt;code&gt;'A'&lt;/code&gt; and the tail is &lt;code&gt;'BC'&lt;/code&gt;. If we take a leap of faith and assume that &lt;code&gt;permutations('BC')&lt;/code&gt; works and returns &lt;code&gt;['BC', 'CB']&lt;/code&gt;, we could go through both these string permutations and make a list with &lt;code&gt;'A'&lt;/code&gt; at each position:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For &lt;code&gt;'BC'&lt;/code&gt;, we'd need to create &lt;code&gt;'ABC'&lt;/code&gt;, &lt;code&gt;'BAC'&lt;/code&gt;, and &lt;code&gt;'BCA'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;'CB'&lt;/code&gt;, we'd need to create &lt;code&gt;'ACB'&lt;/code&gt;, &lt;code&gt;'CAB'&lt;/code&gt;, and &lt;code&gt;'CBA'&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We'd combine this all into one list and return it: &lt;code&gt;['ABC', 'BAC', 'BCA', 'ACB', 'CAB', 'CBA']&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Okay, I think that would work as long as &lt;code&gt;permutations('BC')&lt;/code&gt; works. But does this pass the second caveat?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Second Caveat: The argument to the recursive function call must ALWAYS get closer to the base case.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Well, the tail of the argument to &lt;code&gt;permutations()&lt;/code&gt; is always smaller than the argument because it will have one less character. And our base case is when the argument has zero or one characters. So, yes, the argument would always get closer to the base case.&lt;/p&gt;
&lt;p&gt;Now we have to actually write the code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;permutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# BASE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# RECURSIVE CASE&lt;/span&gt;
        &lt;span class="n"&gt;all_perms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;  &lt;span class="c1"&gt;# Start with an empty list.&lt;/span&gt;
        &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;

        &lt;span class="n"&gt;tail_perms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;permutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;perm&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tail_perms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Go through each of the tail&amp;#39;s permutations.&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;perm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# Go through each position and inject the head.&lt;/span&gt;
                &lt;span class="n"&gt;new_perm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;perm&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;perm&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
                &lt;span class="n"&gt;all_perms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_perm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Add it to the complete list of permutations.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;all_perms&lt;/span&gt;  &lt;span class="c1"&gt;# Return all of the permutations.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let's take that leap of faith and try it out. Here goes nothing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; permutations(&amp;#39;ABC&amp;#39;)
[&amp;#39;ABC&amp;#39;, &amp;#39;BAC&amp;#39;, &amp;#39;BCA&amp;#39;, &amp;#39;ACB&amp;#39;, &amp;#39;CAB&amp;#39;, &amp;#39;CBA&amp;#39;]
&amp;gt;&amp;gt;&amp;gt; permutations(&amp;#39;Z&amp;#39;)
[&amp;#39;Z&amp;#39;]
&amp;gt;&amp;gt;&amp;gt; permutations(&amp;#39;ABCD&amp;#39;)
[&amp;#39;ABCD&amp;#39;, &amp;#39;BACD&amp;#39;, &amp;#39;BCAD&amp;#39;, &amp;#39;BCDA&amp;#39;, &amp;#39;ACBD&amp;#39;, &amp;#39;CABD&amp;#39;, &amp;#39;CBAD&amp;#39;, &amp;#39;CBDA&amp;#39;, &amp;#39;ACDB&amp;#39;, &amp;#39;CADB&amp;#39;, &amp;#39;CDAB&amp;#39;, &amp;#39;CDBA&amp;#39;, &amp;#39;ABDC&amp;#39;, &amp;#39;BADC&amp;#39;, &amp;#39;BDAC&amp;#39;, &amp;#39;BDCA&amp;#39;, &amp;#39;ADBC&amp;#39;, &amp;#39;DABC&amp;#39;, &amp;#39;DBAC&amp;#39;, &amp;#39;DBCA&amp;#39;, &amp;#39;ADCB&amp;#39;, &amp;#39;DACB&amp;#39;, &amp;#39;DCAB&amp;#39;, &amp;#39;DCBA&amp;#39;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Hey, it works!&lt;/p&gt;
&lt;h2&gt;How Does ChatGPT (Fail to) Teach the Leap of Faith Technique (Just Like Every Other Tutorial)?&lt;/h2&gt;
&lt;p&gt;AI hype continues to annoy me, but I keep an open mind and investigate how useful it is. I gave it the prompt, "Explain the "leap of faith" technique for recursive functions." to see how it would teach the concept. The output was the typical, disappointing tutorial. You can read it if you want, or give it a pass:&lt;/p&gt;
&lt;script&gt;
function toggleChatGPTOutput() {
  const chatgptDiv = document.getElementById("chatgptoutput");
  if (chatgptDiv.style.display === "none") {
    chatgptDiv.style.display = "block";
  } else {
    chatgptDiv.style.display = "none";
  }
}
&lt;/script&gt;

&lt;p&gt;&lt;button onclick="toggleChatGPTOutput()"&gt;Show/Hide ChatGPT Output&lt;/button&gt;&lt;/p&gt;
&lt;div id="chatgptoutput" style="display: none; border: 1px solid #ccc; padding: 10px; background-color: #f9f9f9;"&gt;

&lt;p&gt;The “leap of faith” technique is a way of reasoning about recursive functions that helps you &lt;em&gt;trust&lt;/em&gt; the recursion instead of getting tangled up trying to simulate every call in your head.&lt;/p&gt;
&lt;p&gt;Here’s how it works:&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;1. The idea&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;When you write a recursive function, there are usually two parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Base case&lt;/strong&gt; — a simple, direct answer for the smallest input (no recursion needed).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Recursive case&lt;/strong&gt; — solve the problem by calling the same function on a &lt;em&gt;smaller&lt;/em&gt; or &lt;em&gt;simpler&lt;/em&gt; input, then combine that result with some additional work.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The “leap of faith” is the mindset where, &lt;strong&gt;once you’ve ensured that the recursive call is on a simpler input and will eventually hit the base case&lt;/strong&gt;, you &lt;em&gt;assume&lt;/em&gt; that it works correctly and returns the right answer — without trying to trace through every smaller call.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;2. Why it’s useful&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;When learning recursion, beginners often get stuck simulating each step:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Okay, &lt;code&gt;factorial(4)&lt;/code&gt; calls &lt;code&gt;factorial(3)&lt;/code&gt;, which calls &lt;code&gt;factorial(2)&lt;/code&gt;, which calls… wait, what was I calculating again?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Instead, the leap of faith says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“I don’t need to track every call. I’ll assume &lt;code&gt;factorial(3)&lt;/code&gt; gives the right answer — because I’ve already made sure it works for smaller inputs — and then I’ll just use that answer to finish &lt;code&gt;factorial(4)&lt;/code&gt;.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;3. Example: factorial&lt;/strong&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;                     &lt;span class="c1"&gt;# base case&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# recursive case&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using the leap of faith for &lt;code&gt;factorial(4)&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Base case check:&lt;/strong&gt; eventually &lt;code&gt;n&lt;/code&gt; will be &lt;code&gt;0&lt;/code&gt;, so recursion stops.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Recursive case:&lt;/strong&gt; assume &lt;code&gt;factorial(3)&lt;/code&gt; correctly returns &lt;code&gt;6&lt;/code&gt; — don’t calculate it in your head — then &lt;code&gt;factorial(4)&lt;/code&gt; is &lt;code&gt;4 * 6 = 24&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;4. How to apply the leap of faith&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Verify&lt;/strong&gt; that your recursion moves toward the base case.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trust&lt;/strong&gt; that the recursive call returns the correct result for smaller inputs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Focus&lt;/strong&gt; only on combining that result to solve the current problem.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;5. Why it’s called a leap of faith&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;You’re “leaping” over the details of &lt;em&gt;how&lt;/em&gt; the recursive call works, and having “faith” that it returns the right answer, just like you trust a well-tested library function without reading its source code every time you use it.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you want, I can also explain &lt;strong&gt;how the leap of faith relates to induction proofs&lt;/strong&gt; — since the reasoning is very similar.&lt;/p&gt;

&lt;/div&gt;

&lt;p&gt;Here are the problems that this, and every other "leap of faith" tutorial has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No mention on keeping data types consistent.&lt;/strong&gt; - A common problem beginners have when writing recursive functions, especially for problems like permutations, is having the return value use different data types such as the strings/list of strings example I pointed to earlier. This needs to be made explicit to beginners.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Explicitly pointing out that the recursive call's argument can't just be the original argument.&lt;/strong&gt; - This is the first thought anyone has when we say, "Assume the recursive function already works." Keep in mind the first caveat (guideline 4): the recursive call argument must be different from the original argument.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Using vague words like "simpler" or "smaller" to describe arguments to the recursive function calls.&lt;/strong&gt; - What we &lt;em&gt;really&lt;/em&gt; mean is that, specifically, the argument in the recursive function call gets &lt;em&gt;closer to the base case&lt;/em&gt;. It's not enough to say "smaller", we need to connect it to base case, which also means explicitly writing the code for the base case first (guideline 2 in the list of guidelines I gave.) And keep in mind the second caveat (guideline 5): the argument must &lt;strong&gt;always&lt;/strong&gt; get closer to the base, otherwise you'll end up with a stack overflow.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There's nothing &lt;em&gt;wrong&lt;/em&gt; with ChatGPT's explanation; it's technically correct and similar to any other tutorial on the topic. But that's what's wrong with it: students read it, have trouble understanding the vaguely worded concepts, and then assume they just aren't smart enough to understand recursion.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I've always held that recursion isn't a difficult topic so much as it is poorly taught. Many tutorials have a lot of hand-waving and unspoken assumptions, especially for terms like "leap of faith". This tutorial tells you exactly what this term means, along with two caveats you need to keep in mind when using the leap-of-faith technique. If you're interested in learning more about recursion (including &lt;a href="https://inventwithpython.com/recursion/chapter9.html"&gt;drawing pretty fractals&lt;/a&gt; and &lt;a href="https://inventwithpython.com/recursion/chapter14.html"&gt;making recursive photos&lt;/a&gt;), you can read &lt;a href="https://inventwithpython.com/recursion/"&gt;my free book, The Recursive Book of Recursion&lt;/a&gt; or &lt;a href="https://nostarch.com/recursive-book-recursion"&gt;buy a print copy&lt;/a&gt;. Good luck!&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Recursive Functions To Anger Your CS Professor</title><link href="https://inventwithpython.com/blog/recursive-functions-to-piss-off-your-cs-prof.html" rel="alternate"></link><published>2025-07-18T10:00:00-04:00</published><updated>2025-07-18T10:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-07-18:/blog/recursive-functions-to-piss-off-your-cs-prof.html</id><summary type="html">&lt;p&gt;Here are some recursive functions that are technically correct and technically recursive.&lt;br&gt;&lt;img src="/blogstatic/droste.webp" style="max-width: 50%"&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;Here are some recursive functions that are technically correct and technically recursive.&lt;br&gt;&lt;img src="/blogstatic/droste.webp" style="max-width: 50%"&gt;&lt;/p&gt;
&lt;p&gt;The programs here are available in a &lt;a href="recusrive-functions-to-piss-off-your-cs-prof"&gt;git repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;(Explaining the joke kills the humor, but explanations have been given at the bottom if you aren't familiar with recursive programming. Input validation in these functions has been skipped for brevity.)&lt;/p&gt;
&lt;h2&gt;Factorial&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Base cases:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;720&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;5040&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;40320&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;362880&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;3628800&lt;/span&gt;
    &lt;span class="c1"&gt;# Skip the base case for 11.&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;479001600&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;6227020800&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;87178291200&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1307674368000&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;20922789888000&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;355687428096000&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;6402373705728000&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;121645100408832000&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;2432902008176640000&lt;/span&gt;

    &lt;span class="c1"&gt;# Recursive case:&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Fibonacci&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;fib_cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Memoization cache with base cases&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nth&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Check if nth fib number is in the cache:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fib_cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;nth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fib_cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;nth&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# If not, expand the cache:&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fib_cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fib_cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fib_cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fib_cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fib_cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;nth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
            &lt;span class="n"&gt;fib_cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Recursive case:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nth&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nth&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Even Odd Detection&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_odd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;originally_positive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Base case:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;

    &lt;span class="c1"&gt;# Determine argument for originally_positive:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;originally_positive&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;is_odd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;is_odd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Recursive cases:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;originally_positive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;is_odd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;is_odd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Depth First Search&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;branches&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
           &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;branches&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]},&lt;/span&gt;
           &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;z&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;branches&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
               &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;branches&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]}&lt;/span&gt;
           &lt;span class="p"&gt;]}&lt;/span&gt;
       &lt;span class="p"&gt;]}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Key found base case:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;branch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;branches&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="c1"&gt;# Recursively check each of the branches:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;KeyError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Check again sometimes, just to make sure:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;KeyError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="c1"&gt;# Key not found base case:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;KeyError&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Joke Explanations&lt;/h2&gt;
&lt;p&gt;Before you tell me some nitpick about why this code is wrong or a suggestion about how I should have written the code some other way, remember the &lt;a href="https://tvtropes.org/pmwiki/pmwiki.php/Main/MST3KMantra"&gt;MST3K mantra&lt;/a&gt;: "It's just a show, I should really just relax."&lt;/p&gt;
&lt;p&gt;And then you should keep the nitpick or suggestion to yourself.&lt;/p&gt;
&lt;h3&gt;Factorial Explanation&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Factorial"&gt;factorial of an integer&lt;/a&gt; is the product of all integers from 1 up the integer. For example, the factorial of 5 (written as 5! in mathematics) is 5 x 4 x 3 x 2 x 1 or 120.&lt;/p&gt;
&lt;p&gt;Recursive functions need to have at least one &lt;a href="https://en.wikipedia.org/wiki/Recursion#base_case"&gt;base case&lt;/a&gt; (or else it will eventually cause a stack overflow) and at least one &lt;a href="https://en.wikipedia.org/wiki/Recursion#recursive_step"&gt;recursive case&lt;/a&gt; (or else it isn't recursive). This can be done recursive because 5! is the same thing as 5 x 4!, and 4! is the same thing as 4 x 3!, and so on.&lt;/p&gt;
&lt;p&gt;Recursive factorial only needs one base case: 0! is 1. However, the gag here is that I supply several base cases, effectively getting rid of the need for any recursion for the integers 0 through 20. The function still uses recursion for integers larger than 21, but you could theoretically have millions or billions of base cases and still technically claim this is a recursive function. You are effectively &lt;a href="https://en.wikipedia.org/wiki/Hard_coding"&gt;hard-coding&lt;/a&gt; the recursion away.&lt;/p&gt;
&lt;p&gt;(This sort of pre-calculated caching is called &lt;a href="https://en.wikipedia.org/wiki/Memoization"&gt;memoization&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Bonus joke: I skipped the base case for 11 for absolutely no reason. The function still works for finding 11!.&lt;/p&gt;
&lt;h3&gt;Fibonacci Explanation&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Fibonacci_sequence"&gt;Fibonacci sequence&lt;/a&gt; starts with the numbers 0 and 1. Each subsequent number in the sequence is the sum of the previous two numbers: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, and so on forever. It's a pattern that comes up in nature.&lt;/p&gt;
&lt;p&gt;The base cases are for the 1st and 2nd Fibonacci numbers: 0 and 1, respectively. You can determine the Nth Fibonacci number by adding the Nth - 1 and Nth - 2 Fibonacci numbers. This lends itself well to recursion, however it results in a staggering amount of recalculation: Finding &lt;code&gt;fibonacci(5)&lt;/code&gt; means finding &lt;code&gt;fibonacci(4)&lt;/code&gt; and &lt;code&gt;fibonacci(3)&lt;/code&gt;, but finding &lt;code&gt;fibonacci(4)&lt;/code&gt; involves finding &lt;code&gt;fibonacci(3)&lt;/code&gt; and &lt;code&gt;fibonacci(2)&lt;/code&gt;. The &lt;code&gt;fibonacci(3)&lt;/code&gt; call is made multiple times, even though the 3rd Fibonacci number doesn't change and doesn't need to be recalculated.&lt;/p&gt;
&lt;p&gt;This &lt;a href="https://en.wikipedia.org/wiki/Overlapping_subproblems"&gt;issue of overlapping subproblems&lt;/a&gt; is solved by memoization, or the caching of results already calculated.&lt;/p&gt;
&lt;p&gt;The gag here is that when the given Fibonacci number hasn't been calculated, the cache is populated by &lt;em&gt;iteratively&lt;/em&gt; calculating the Fibonacci number. The two recursive calls are then made (making this a recursive function), even though the answer already exists in the cache. The recursion here is compeltely pointless.&lt;/p&gt;
&lt;h3&gt;Even Odd Detection Explanation&lt;/h3&gt;
&lt;p&gt;Determining if an integer is even or odd is straightforward in programming: &lt;a href="https://en.wikipedia.org/wiki/Modulo"&gt;mod&lt;/a&gt; the number by two and if the result is 0 it is even and if the result is 1 it is odd. The Python code is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;def is_odd(num):
    return num % 2

def is_even(num):
    return not (num % 2)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But this can also be done recursively. Is the integer 42 even or odd? Well, the answer is the opposite of whether 41 is even or odd. And 41's oddness is the opposite of 40's oddness, and so on. The base case is that &lt;a href="https://en.wikipedia.org/wiki/Parity_of_zero"&gt;0 is an even number&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The gag here is that the mod approach works in one step for any number, the recursive approach gets slower and slower the larger the number is: finding out if 1,000,000,000 requires a billion function calls to determine if it is odd or not.&lt;/p&gt;
&lt;p&gt;Let's make this even worse by unnecessarily splitting the recursive case into two recursive cases: whether the number argument was originally positive or negative by storing this information in a parameter. This is similar to the way certain recursive functions use an &lt;em&gt;accumulator&lt;/em&gt; parameter for their operation. (My opinion is that any recursive function has an accumulator, it can &lt;strong&gt;always&lt;/strong&gt; be done easier without using recursion.)&lt;/p&gt;
&lt;h3&gt;Depth First Search Explanation&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Depth-first_search"&gt;Depth first search&lt;/a&gt; is an algorithm for finding data in a tree data structure. Recursion is actually a suitable solution for this, as it involves 1) tree-like data structures and 2) backtracking. If your problem doesn't have both of these features, then recursion is an messy, overengineered approach.&lt;/p&gt;
&lt;p&gt;We have a tree data structure where each node has a key-value pair and branches to other nodes. The root node of the tree is passed to the &lt;code&gt;dfs()&lt;/code&gt; function along with the key to search for. If the root node's key matches the key we are searching for, it returns the value in that key-value pair. This is the first of two base cases.&lt;/p&gt;
&lt;p&gt;Otherwise, &lt;code&gt;dfs()&lt;/code&gt; is called on each of the branches of that node. This is the recursive case. In this way, the entire tree is searched. If the recursion stops at nodes that do not have any branches. If we return from all the recursive calls without finding the key, the &lt;code&gt;KeyError&lt;/code&gt; exception is raised to indicate that the key doesn't exist in the tree. This is the second base case.&lt;/p&gt;
&lt;p&gt;The gag here is that, randomly, the function will re-check the branches of the node. The function is still technically recursive, though it makes far more recursive function calls than needed.&lt;/p&gt;
&lt;p&gt;A similar joke is in my &lt;a href="https://github.com/asweigart/mergesort"&gt;Poisoned Merge Sort&lt;/a&gt; implementation, which contains a subtle bug for anyone who blindly copies and pastes the code into their programs.&lt;/p&gt;
&lt;h3&gt;Book Plug&lt;/h3&gt;
&lt;p&gt;My love-hate relationship with recursion led me to write The Recursive Book of Recursion. It's a wonderful but often overrated technique, and I am quite vocal that I think it's intimidating reputation comes from it being poorly taught more than an inherent difficulty.&lt;/p&gt;
&lt;p&gt;The book has code in Python and JavaScript, and is &lt;a href="https://inventwithpython.com/recursion/"&gt;free to read online&lt;/a&gt;. I also have a large collection of &lt;a href="https://github.com/asweigart/recursion_examples/"&gt;recursive code examples in Python&lt;/a&gt;, including Python programs that use the Turtle module to &lt;a href="https://inventwithpython.com/recursion/chapter9.html"&gt;draw beautiful fractals&lt;/a&gt; including your own &lt;a href="https://inventwithpython.com/recursion/chapter13.html"&gt;custom fractals&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I've given &lt;a href="https://www.youtube.com/watch?v=AfBqVVKg4GE"&gt;talks on recursion at PyCon conferences&lt;/a&gt; (also on &lt;a href="https://pyvideo.org/north-bay-python-2018/recursion-for-beginners-a-beginners-guide-to-recursion.html"&gt;pyvideo.org&lt;/a&gt;), if you want a 30 minute crash course on the subject.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Audio, Video, and Webcams in Python (Lost Chapter from Automate the Boring Stuff)</title><link href="https://inventwithpython.com/blog/lost-av-chapter.html" rel="alternate"></link><published>2025-07-07T10:00:00-04:00</published><updated>2025-07-07T10:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-07-07:/blog/lost-av-chapter.html</id><summary type="html">&lt;p&gt;The &lt;a href="https://automatetheboringstuff.com"&gt;third edition of Automate the Boring Stuff with Python is now available for purchase or to read for free online&lt;/a&gt;. It has updated content and several new chapters, but one chapter that was left on the cutting room floor was &lt;em&gt;"Working with Audio, Video, and Webcams"&lt;/em&gt;. I present the 26-page rough draft chapter in this blog, where you can learn how to write Python code that records and plays multimedia content.&lt;br&gt;&lt;img src="/blogstatic/lost-av-webcam-preview.webp" style="max-width: 100%; width: 400px    "&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;The &lt;a href="https://automatetheboringstuff.com"&gt;third edition of Automate the Boring Stuff with Python is now available for purchase or to read for free online&lt;/a&gt;. It has updated content and several new chapters, but one chapter that was left on the cutting room floor was &lt;em&gt;"Working with Audio, Video, and Webcams"&lt;/em&gt;. I present the 26-page rough draft chapter in this blog, where you can learn how to write Python code that records and plays multimedia content.&lt;br&gt;&lt;img src="/blogstatic/lost-av-webcam-preview.webp" style="max-width: 100%; width: 400px    "&gt;&lt;/p&gt;
&lt;h2&gt;Working with Audio, Video, and Webcams&lt;/h2&gt;
&lt;p&gt;These days a smartphone is a portable film studio that fits in your pocket. The ability to not just readily view but also produce video has led to an explosion of video content easily accessible over the internet. There’s plenty of video and audio editing software available for users of all levels of sophistication. But if you have, say, a thousand video files that you need to automate reducing their volume by 50% and then extract thumbnail images, you don’t want to have to edit each of those files individually yourself. Similarly, you may want to record video, audio, or photos from a laptop’s webcam for some automated process too specialized for off-the-shelf software. Or maybe you’d just like your program to play a funny sound effect. In this chapter, you’ll learn how to control your computer’s microphone, webcam, and speaker hardware from Python and play multimedia files.&lt;/p&gt;
&lt;p&gt;This chapter assumes you have already read Chapter 21 to learn the basics about how programs work with image data. Since video is just a series of images, many of the same concepts apply.&lt;/p&gt;
&lt;h2&gt;Audio and Video Data&lt;/h2&gt;
&lt;p&gt;First, you should have a reliable way to open and view video and audio files. Since its release over twenty years ago, the open source VLC Media Player application has been free of charge and the gold standard for multimedia players. Install the VLC Media Player from &lt;a href="https://www.videolan.org/vlc/"&gt;https://www.videolan.org/vlc/&lt;/a&gt;, which runs on Windows, macOS, and Linux. Mobile versions for Android and iOS are also freely available. VLC is far superior to any multimedia application that came with your computer.&lt;/p&gt;
&lt;p&gt;While you may be familiar with audio and video files by their file extension such as &lt;em&gt;.mp3&lt;/em&gt; or &lt;em&gt;.webm&lt;/em&gt;, audio and video formats are split into container formats and codecs. A container format is a wrapper that contains metadata and the encoded video or audio data. MP4, AVI, MKV, and MOV are popular container formats. A codec (or encoding) is the compression algorithm used for the video or audio data. MP3, H.264, VP9, and AAC are common video or audio codecs. Not all applications and devices support all codecs.&lt;/p&gt;
&lt;p&gt;The audio file formats we’ll be using in this chapter are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;.wav&lt;/em&gt; files - an uncompressed format that are widely supported by apps and libraries but tend to have large file sizes so they are often used for short sound effects.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;.mp3&lt;/em&gt; files - a popular compressed format often used for music and other lengthy audio.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;.ogg&lt;/em&gt; files - an open standard, compressed format that is less popular than &lt;em&gt;.mp3&lt;/em&gt; but provides better compression and quality.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The video file formats we’ll be using in this chapter are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;.avi&lt;/em&gt; files - an older format that tends to have larger file sizes.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;.mp4&lt;/em&gt; files - a popular format that is widely supported by different software&lt;/li&gt;
&lt;li&gt;&lt;em&gt;.mkv&lt;/em&gt; files - an open format that has many features but isn’t as widely supported as &lt;em&gt;.mp4&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;.webm&lt;/em&gt; files - a more recent format based on &lt;em&gt;.mkv&lt;/em&gt; that offers better compression and quality.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Common Video Sizes and Aspect Ratios&lt;/h3&gt;
&lt;p&gt;The aspect ratio is the relative size of a video or screen’s width to its height, often written as &lt;em&gt;width:height&lt;/em&gt;. For example, 4:3 (4 pixels of width for every 3 pixels of height) was a common aspect ratio for 20th century computer monitors and standard-definition televisions in the broadcast TV era. Today, 16:9 is common for most laptop screens and high-definition televisions. More recently, the wider 19.5:9 aspect ratio is becoming common for smart phones. An aspect ratio that is wider than it is tall has a &lt;em&gt;landscape orientation&lt;/em&gt;, while an upright aspect ratio that is taller than it is wide is called a &lt;em&gt;portrait orientation&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Screen resolution&lt;/em&gt; is the number of columns and rows of pixels that a laptop or mobile screen displays. It’s often written as &lt;em&gt;width&lt;/em&gt; x &lt;em&gt;height&lt;/em&gt;, such as 1280 x 720 or 1920 x 1080 (also written as 720p and 1080p, respectively). Your laptop’s screen often supports multiple resolutions that can be configured in your operating system’s settings. Videos have one size, though they can be scaled larger or smaller to fit a display or app window. While videos can have any size, they often match common screen resolutions so they can be viewed in &lt;em&gt;fullscreen mode&lt;/em&gt; without disproportionate scaling.&lt;/p&gt;
&lt;h2&gt;Recording from Webcams and Microphones&lt;/h2&gt;
&lt;p&gt;The webcam and microphone you use for video chats can be accessed by Python scripts through the OpenCV, sounddevice, and wavio Python packages. OpenCV, the open computer vision library, is an advanced open source imaging library. To use it you must first you must install the mathematics and data science package NumPy by running &lt;code&gt;pip install numpy&lt;/code&gt; on Windows and macOS or &lt;code&gt;sudo apt install python3-numpy&lt;/code&gt; on Ubuntu Linux. Then you can install OpenCV with &lt;code&gt;pip install opencv-python&lt;/code&gt; from the terminal. See Appendix A for full instructions. The sounddevice and wavio packages are installed with &lt;code&gt;pip install sounddevice&lt;/code&gt; and &lt;code&gt;pip install wavio&lt;/code&gt;, respectively.&lt;/p&gt;
&lt;p&gt;The following programs have been kept to a minimum so that you can easily insert their code into your own Python programs that record audio and video.&lt;/p&gt;
&lt;h3&gt;Taking a Photo from a Webcam&lt;/h3&gt;
&lt;p&gt;You can use your laptop’s webcam to take photos or record videos through a Python script. This has a few advantages over using a regular camera or photo app. Your Python script could:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Automatically take photos in response to certain events such as receiving emails.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Take time lapse photos over a long period of time at various intervals.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a photo booth feature to your program, using the Pillow module to edit the photos.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scan QR codes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OpenCV list your computer’s webcams with integer indexes that begin with &lt;code&gt;0&lt;/code&gt;. A laptop could easily have multiple webcams. For example, it could have a built-in camera at the top of the screen (perhaps at index &lt;code&gt;0&lt;/code&gt; in OpenCV) along with a separate USB webcam (perhaps at index 1). The following &lt;em&gt;webcamPreview.py&lt;/em&gt; program has a `list_webcam_indexes()`` function check which OpenCV webcam indexes exist on your computer. The program then takes repeated photos from the webcam to display in a preview window. When the user presses the space key, it saves the latest photo as &lt;em&gt;photo.png&lt;/em&gt; (though you can change the filename to &lt;em&gt;photo.jpg&lt;/em&gt; or any other image format.) Or the user can press the Esc key to quit without saving.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;cv2&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;list_webcam_indexes&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;detected_webcam_indexes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Test for up to 10 possible webcams.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VideoCapture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Connect to webcam.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;# A webcam exists at this index:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;detected_webcam_indexes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Disconnect from webcam.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;detected_webcam_indexes&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Returns a list of integer webcam indexes.&lt;/span&gt;

&lt;span class="n"&gt;first_available_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;list_webcam_indexes&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Fails if there are no webcams.&lt;/span&gt;
&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VideoCapture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first_available_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (1) Connect to webcam.&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;index:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;first_available_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;width:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CAP_PROP_FRAME_WIDTH&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;height:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CAP_PROP_FRAME_HEIGHT&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;fps:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CAP_PROP_FPS&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (2)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (3)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Could not capture frame.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Show the frame in a preview window:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;imshow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Press space to save photo or Esc to exit&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (4)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pressed_key_code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waitKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (5)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pressed_key_code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (6) 32 is the space key code.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;imwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;photo.png&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Save photo.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;elif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pressed_key_code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (7) 27 is the Esc key code.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Exit without saving photo.&lt;/span&gt;

&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (8) Disconnect from webcam.&lt;/span&gt;
&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;destroyAllWindows&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Close preview window.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On my laptop, this program makes a window with a live preview from the built-in webcam and outputs the following text:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;640.0&lt;/span&gt;
&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;480.0&lt;/span&gt;
&lt;span class="n"&gt;fps&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;30.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;From this output we can tell that the webcam produces 640 x 480 sized images and can record video up to 30 frames per second. The preview window closes when the user presses the space bar or Esc key. The live preview window looks like Figure 24-1.&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/lost-av-webcam-preview.webp" style="max-width: 100%"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The live preview window from webcamPreview.py.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;VideoCapture&lt;/code&gt; object that gets stored in the &lt;code&gt;webcam&lt;/code&gt; variable (1) represents the connected camera. The &lt;code&gt;webcam.read()&lt;/code&gt; method (2) returns a &lt;code&gt;numpy.ndarray&lt;/code&gt; data structure with all the photo pixel data in it. (NumPy is beyond the scope of this book, but an &lt;code&gt;ndarray&lt;/code&gt; is similar to a Python list.) This can be passed to the &lt;code&gt;cv2.imshow()`` function (3) to create a window with the webcam photo data. The&lt;/code&gt;while` loop runs fast enough to make the series of still photos look like live video in the preview window 4.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;cv2.waitKey()&lt;/code&gt; function (5) returns an integer key code of the keyboard key the user pressed while the preview window is focused. The integer argument passed to &lt;code&gt;cv2.waitKey()&lt;/code&gt; is how many milliseconds the function waits for the user to press a key, with &lt;code&gt;cv2.waitKey(0)&lt;/code&gt; meaning that it should wait forever. We call &lt;code&gt;cv2.waitKey(1)&lt;/code&gt; because the function should wait just 1 millisecond before returning so our program can continue to fetch new images from the webcam. If the user hasn’t pressed a key, &lt;code&gt;cv2.waitKey()&lt;/code&gt; returns &lt;code&gt;-1&lt;/code&gt;. The program checks if the key code is &lt;code&gt;32&lt;/code&gt; (for the space key) (6) and &lt;code&gt;27&lt;/code&gt; (for the Esc key) (7) lets the user choose whether to save a photo or not. Afterwards, the &lt;code&gt;VideoCapture&lt;/code&gt; object in &lt;code&gt;webcam&lt;/code&gt; is released (8) and the live preview window is closed so that the program can exit.&lt;/p&gt;
&lt;p&gt;To find other keyboard key codes, pass a single-character string to Python’s &lt;code&gt;ord()&lt;/code&gt; function. For example, &lt;code&gt;ord('q')&lt;/code&gt; returns the integer &lt;code&gt;113&lt;/code&gt;, and &lt;code&gt;113&lt;/code&gt; is the key code for the Q key.&lt;/p&gt;
&lt;h3&gt;Take a Photo without the Preview Window&lt;/h3&gt;
&lt;p&gt;If you want a program that simply saves a picture from the first available webcam as soon as possible, you can use this much simpler webcamPhoto.py program:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VideoCapture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# 0 is the index of the webcam to use.&lt;/span&gt;
&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Invalid webcam index.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;imwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;photo.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can incorporate this code into your own program that needs to take a photo from the webcam (though you may also wish to include the &lt;code&gt;list_webcam_indexes()&lt;/code&gt; from &lt;em&gt;webcamPreview.py&lt;/em&gt; instead of assuming index &lt;code&gt;0&lt;/code&gt; is correct.)&lt;/p&gt;
&lt;p&gt;You can also use the Pygame library to take photos from a webcam. While Pygame is designed for making 2D video games, it has several useful multimedia features as well. You can install it by running &lt;code&gt;pip install pygame&lt;/code&gt;. Appendix A has full instructions. The following &lt;em&gt;webcamPhotoPygame.py&lt;/em&gt; program takes a photo with the webcam and saves it as photo.png:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pygame.camera&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pygame.image&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="n"&gt;pygame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;all_webcams&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pygame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_cameras&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (1)&lt;/span&gt;
&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pygame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Camera&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_webcams&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (2) Use the first found webcam.&lt;/span&gt;
&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (3) Initialize the webcam.&lt;/span&gt;

&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (4) Wait for the camera to get ready.&lt;/span&gt;
&lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_image&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (5) Take a photo from the webcam.&lt;/span&gt;

&lt;span class="n"&gt;pygame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;photo.png&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (6) You can also use .jpg or .bmp.&lt;/span&gt;
&lt;span class="n"&gt;pygame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After importing the &lt;code&gt;pygame.camera&lt;/code&gt; and &lt;code&gt;pygame.image&lt;/code&gt; modules and initializing the &lt;code&gt;pygame.camera&lt;/code&gt; module by calling &lt;code&gt;pygame.camera.init()&lt;/code&gt;, you can obtain a list of the webcams connected to your computer by calling &lt;code&gt;pygame.camera.list_cameras()&lt;/code&gt; (1). For example, on my computer, this function call returned the list &lt;code&gt;['HD Pro Webcam C920', 'Integrated Camera']&lt;/code&gt; which represents an external USB webcam and the built-in webcam at the top of my laptop’s screen. You can then pass one of these strings to &lt;code&gt;pygame.camera.Camera()&lt;/code&gt; to obtain a new &lt;code&gt;Camera&lt;/code&gt; object to take pictures with.&lt;/p&gt;
&lt;p&gt;This &lt;code&gt;Camera&lt;/code&gt; object, which this program stores in a variable named &lt;code&gt;webcam&lt;/code&gt; (2), must also be initialized by calling its &lt;code&gt;start()&lt;/code&gt; method (3). You can take a picture by calling the &lt;code&gt;get_image()&lt;/code&gt; method (5), which returns a &lt;code&gt;Surface&lt;/code&gt; object that holds the image data.&lt;/p&gt;
&lt;p&gt;Unfortunately, the &lt;code&gt;start()&lt;/code&gt; method can take up to a full second before it’s ready to take photos. If you call &lt;code&gt;get_image()&lt;/code&gt; before it’s ready, you will get a blank, black image. In my testing, it only takes 0.6 or 0.7 seconds for the webcam to get ready, so a full second delay by calling &lt;code&gt;time.sleep(1)&lt;/code&gt; should be more than enough of a wait before calling &lt;code&gt;get_image()&lt;/code&gt; for any modern computer. Test this on your computer and increase the delay if you are getting an all-black image.&lt;/p&gt;
&lt;p&gt;After this, you can save the image to a file by passing the &lt;code&gt;Surface&lt;/code&gt; object and image filename to &lt;code&gt;pygame.image.save()&lt;/code&gt; (6). The file extension in your filename can either be &lt;em&gt;.bmp&lt;/em&gt;, &lt;em&gt;.jpg&lt;/em&gt;, or &lt;em&gt;.png&lt;/em&gt;. When your program is done using the webcam, it can call &lt;code&gt;pygame.camera.quit()&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Recording Video from a Webcam&lt;/h2&gt;
&lt;p&gt;OpenCV can also record video files. Open a new file editor window and enter the following code, saving it as &lt;em&gt;webcamVideo.py&lt;/em&gt;. This program begins recording immediately while showing a preview window of the video it’s recording to the file &lt;em&gt;video.avi&lt;/em&gt;. When the user presses the Esc key, the program stops recording.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;cv2&lt;/span&gt;

&lt;span class="c1"&gt;# NOTE: Using webcam index 0 by default:&lt;/span&gt;
&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VideoCapture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (1) Connect to webcam.&lt;/span&gt;

&lt;span class="c1"&gt;# Define the codec using VideoWriter_fourcc and create a VideoWriter object&lt;/span&gt;
&lt;span class="n"&gt;fourcc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VideoWriter_fourcc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;XVID&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (2)&lt;/span&gt;
&lt;span class="n"&gt;fps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CAP_PROP_FPS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CAP_PROP_FRAME_WIDTH&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CAP_PROP_FRAME_HEIGHT&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;video_file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VideoWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;video.avi&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fourcc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (3)&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (4)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Could not capture frame.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;imshow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Recording... (press Esc to stop)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;video_file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (5)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waitKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (6) 27 is the Esc key code.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Stop recording.&lt;/span&gt;

&lt;span class="n"&gt;webcam&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Disconnect from webcam.&lt;/span&gt;
&lt;span class="n"&gt;video_file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Disconnect from output video file.&lt;/span&gt;
&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;destroyAllWindows&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Close preview window.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While the &lt;code&gt;VideoCapture&lt;/code&gt; object stored in the &lt;code&gt;webcam&lt;/code&gt; variable (1) represents the camera hardware, the &lt;code&gt;VideoWriter&lt;/code&gt; object stored in the &lt;code&gt;video_file&lt;/code&gt; variable (3) represents the video file. The &lt;code&gt;VideoWriter_fourcc&lt;/code&gt; object (2) we create represents the &lt;em&gt;four-character code&lt;/em&gt; that identifies the video encoding. In our case, the four-character code is &lt;code&gt;'XVID'&lt;/code&gt; for the Xvid codec. The width, height, and frames per second settings are also passed to &lt;code&gt;cv2.VideoWriter()&lt;/code&gt;. Each frame of video from the &lt;code&gt;webcam.read()&lt;/code&gt; method call (4) is added to the video file by calling &lt;code&gt;video_file.write()&lt;/code&gt; (5) until the user presses the Esc key (6).&lt;/p&gt;
&lt;p&gt;Note that OpenCV can record video but not audio. We’ll use the sounddevice module in the next section to record audio, but unfortunately recording videos that include synced audio is complicated and beyond the scope of this book.&lt;/p&gt;
&lt;h2&gt;Recording Audio from a Webcam or Microphone&lt;/h2&gt;
&lt;p&gt;The sounddevice and wavio third-party modules can use your computer’s or external webcam’s microphones to record audio. These modules have several audio-related features, but the following &lt;em&gt;micRecord.py&lt;/em&gt; program has been stripped down to the basics. Still, you may want to review multithreaded programming in Chapter 19 to understand how the &lt;code&gt;record_audio_stream()&lt;/code&gt; function runs in the background while the rest of the Python program continues executing.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sounddevice&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;wavio&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;threading&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;

&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;list_microphone_devices&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Return a list of microphone indexes and names.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;microphones&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sounddevice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_devices&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# Check if the microphone has max input channels:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;max_input_channels&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (2)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;# Append i (the device id) and mic name:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;microphones&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;microphones&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;record_audio_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;44100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;channels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Create a list to store the audio data:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;recorded_data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sounddevice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;samplerate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;channels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (3)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;# Record audio in chunks and add it the recorded_data list:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;audio_chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;overflowed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;recorded_data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;audio_chunk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stop_recording_signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_set&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Save the recorded data to a WAV file&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;wavio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;recorded_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sampwidth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (4)&lt;/span&gt;

&lt;span class="c1"&gt;# Display information about audio/visual recording devices:&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Devices on this computer:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sounddevice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_devices&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Microphones connected to this computer:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_microphone_devices&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Create an event to signal the other thread to stop:&lt;/span&gt;
&lt;span class="n"&gt;stop_recording_signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Ready. Press Enter to start recording.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Start recording in a separate thread:&lt;/span&gt;
&lt;span class="n"&gt;recording_thread&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;record_audio_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;output.wav&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (6)&lt;/span&gt;
&lt;span class="n"&gt;recording_thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Wait for user to press Enter:&lt;/span&gt;
&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Now recording. Press Enter to stop.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (7)&lt;/span&gt;

&lt;span class="c1"&gt;# Replace the previous input() call with this code to record for a set time:&lt;/span&gt;
&lt;span class="c1"&gt;#time.sleep(1.0)  # Record for 1 second.&lt;/span&gt;

&lt;span class="c1"&gt;# Signal the recording thread to stop and wait for it to finish:&lt;/span&gt;
&lt;span class="n"&gt;stop_recording_signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (8)&lt;/span&gt;
&lt;span class="n"&gt;recording_thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# (9) Block until the other thread has stopped.&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Recording saved.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are several parts to this program aside from the actual recording of audio, so let’s explore those first. The &lt;code&gt;sounddevice.query_devices()&lt;/code&gt; function (5) returns an object that, when printed, displays a numbered list of multimedia input and output devices on the computer, such as the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sounddevice&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sounddevice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_devices&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Sound&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mapper&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Microphone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Realtek&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;High&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Sound&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mapper&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Speaker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;HP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Realtek&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;High&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Defini&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Primary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Sound&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Capture&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Driver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Windows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DirectSound&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Microphone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Realtek&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;High&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Definition&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Audio&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Windows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DirectSound&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This lists the various microphone input and speaker output devices on your computer, along with their integer &lt;code&gt;sounddevice&lt;/code&gt; index. The &lt;code&gt;&amp;gt;&lt;/code&gt; angle bracket marks the default input device and the &lt;code&gt;&amp;lt;&lt;/code&gt; angle bracket marks the default output device. This program also has a &lt;code&gt;list_microphone_devices()&lt;/code&gt; function (1) that calls &lt;code&gt;sounddevice.query_devices()&lt;/code&gt; and filters for only the microphone devices (which will have a &lt;code&gt;max_input_channels&lt;/code&gt; key with a value greater than &lt;code&gt;0&lt;/code&gt; (2)) to make it easy to find microphones. The &lt;code&gt;list_microphone_devices()&lt;/code&gt; returns a list of tuples, with each tuple having the device index and string description, like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;[(0, &amp;#39;Microsoft Sound Mapper - Input&amp;#39;), (1, &amp;#39;Microphone Array (Realtek High &amp;#39;), --snip—
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The program also has a &lt;code&gt;record_audio_stream()&lt;/code&gt; function, with the following parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;filename&lt;/code&gt;, a string of the filename (which should end with .wav) to save the audio.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;device&lt;/code&gt;, the integer &lt;code&gt;sounddevice&lt;/code&gt; index of the microphone to use to record the audio (the default value None indicates that the default input device should be used.)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rate&lt;/code&gt;, the number of samples to take per second. Higher sample rates provide higher quality audio, and 44.1k (or the integer &lt;code&gt;44100&lt;/code&gt;) is a standard sample rate for most microphones.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;channels&lt;/code&gt;, the number of channels to use. The default stereo audio uses 2 channels and mono audio uses 1.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These parameters are used to create the &lt;code&gt;InputStream&lt;/code&gt; object (3) that represents the recording from the given microphone. The &lt;code&gt;while&lt;/code&gt; loop that follows constantly reads audio data in from the &lt;code&gt;read()&lt;/code&gt; method call, which is a list of audio sample data that is added to the &lt;code&gt;recorded_data&lt;/code&gt; list. The recording stops and breaks out of the &lt;code&gt;while&lt;/code&gt; loop when the &lt;code&gt;stop_recorded_signal&lt;/code&gt; thread event is set (explained later). Then the &lt;code&gt;wavio.write()&lt;/code&gt; function (4) saves it to a &lt;em&gt;.wav&lt;/em&gt; audio file.&lt;/p&gt;
&lt;p&gt;We use a separate thread of execution to have our program run the code inside the &lt;code&gt;record_audio_stream()&lt;/code&gt; function while the rest of the code continues to also run. Multithreaded programming was explained in Chapter 19. First we create a &lt;code&gt;Thread&lt;/code&gt; object set with the &lt;code&gt;record_audio_stream&lt;/code&gt; (without the parentheses, since we want to refer to the function itself rather than call it) and the function arguments &lt;code&gt;'output.wav'&lt;/code&gt; for its filename parameter. For the sake of this explanation, we’ll call this the “recording thread” and the program’s original thread that continues in the main part of the program the “main thread.”&lt;/p&gt;
&lt;p&gt;Next, we don’t want to call &lt;code&gt;record_audio_stream()&lt;/code&gt; directly. Instead, we call the &lt;code&gt;start()&lt;/code&gt; method on this &lt;code&gt;Thread&lt;/code&gt; object so the recording thread runs the code inside &lt;code&gt;record_audio_stream()&lt;/code&gt; (6) while the main thread continues on to &lt;code&gt;input('Now recording. Press Enter to stop.')&lt;/code&gt; (7). This way, the user can choose when to stop the recording by pressing Enter. (Alternatively, we can replace the call to &lt;code&gt;input()&lt;/code&gt; with a call to &lt;code&gt;time.sleep()&lt;/code&gt; to record a set number of seconds instead of however long until the user presses Enter.) The user input thread then calls &lt;code&gt;stop_recording_signal.set()&lt;/code&gt; (8). The recording thread will notice this when &lt;code&gt;stop_recording_signal.is_set()&lt;/code&gt; returns &lt;code&gt;True&lt;/code&gt;, causing it to break out of the &lt;code&gt;while&lt;/code&gt; loop that continuously calls &lt;code&gt;stream.read()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, the user input thread calls &lt;code&gt;recording_thread.join()&lt;/code&gt; (9), making it pause until the recording thread has returned from the &lt;code&gt;record_audio_stream()&lt;/code&gt; function. Now there will be only the user input thread running, which then calls &lt;code&gt;print('Recording saved.')&lt;/code&gt; and exits the program.&lt;/p&gt;
&lt;p&gt;This program is rather complicated, but that’s because it has to handle so many small details: selecting which of possibly multiple microphones to record from, the sample rate and number of audio channels to use for the recording, and constantly gathering the audio data until the user directs the program to stop. The &lt;code&gt;sounddevice&lt;/code&gt; and &lt;code&gt;wavio&lt;/code&gt; modules themselves offer even more features, and you can find their full documentation at &lt;a href="https://python-sounddevice.readthedocs.io/"&gt;https://python-sounddevice.readthedocs.io/&lt;/a&gt; and &lt;a href="https://github.com/WarrenWeckesser/wavio"&gt;https://github.com/WarrenWeckesser/wavio&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Playing Audio Files&lt;/h2&gt;
&lt;p&gt;If you just need to play a simple audio file, you can use the &lt;code&gt;playsound3&lt;/code&gt; module. First, install it from the terminal by running &lt;code&gt;pip install playsound3&lt;/code&gt;. See Appendix A for full instructions. Once installed, you can play an audio file by calling the &lt;code&gt;playsound3&lt;/code&gt; module’s &lt;code&gt;playsound()&lt;/code&gt; function and passing the filename of the audio file. Download the &lt;em&gt;hello.mp3&lt;/em&gt; file from &lt;a href="https://autbor.com/hello.mp3"&gt;https://autbor.com/hello.mp3&lt;/a&gt; (or use your own .mp3 file) and enter the following into the interactive shell:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;playsound3&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;playsound3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;playsound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hello.mp3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;playsound()&lt;/code&gt; function won’t return until the audio file has finished playing, that is, the function blocks until the audio has finished. Keep in mind that this will halt your program for a while if you give it a long audio file to play. The &lt;code&gt;playsound3&lt;/code&gt; module can play MP3 and WAV files. If you cannot hear the sound, make sure your computer is unmuted. If &lt;code&gt;playsound()&lt;/code&gt; raises exceptions (which happens if the filename contains odd characters such as an equal sign), try passing a &lt;code&gt;Path&lt;/code&gt; object of the audio file instead of a string, as in the following example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;playsound3&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pathlib&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;audio_file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;C:\Users\Al\Desktop\hello.mp3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;playsound3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;playsound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;audio_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;playsound3&lt;/code&gt; module is best if you just need to play a simple sound effect and you want your program to block until the sound has finished. If you need to have better control over playback, such as changing volume, pausing and unpausing the audio file, seeking to a specific time in the audio, and other features, use the Pygame’s &lt;code&gt;mixer&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;For example, to play &lt;em&gt;hello.mp3&lt;/em&gt; with Pygame, enter the following into the interactive shell:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pygame.mixer&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pygame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pygame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;music&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hello.mp3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pygame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mixer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;music&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With Pygame, you must call the &lt;code&gt;pygame.mixer.init()&lt;/code&gt; function once after importing the &lt;code&gt;pygame.mixer&lt;/code&gt; module in order to play any audio files. Then load the audio file by calling &lt;code&gt;pygame.mixer.music.load()&lt;/code&gt; and pass either a string or &lt;code&gt;Path&lt;/code&gt; object of the audio file’s filename. Calling &lt;code&gt;pygame.mixer.music.play()&lt;/code&gt; starts playing the loaded audio file from the beginning. The &lt;code&gt;play()&lt;/code&gt; function doesn’t block; it returns immediately and the audio continues to play in the background while your Python program runs other lines of code.&lt;/p&gt;
&lt;p&gt;You can continue to affect the playing of the music with the other functions in the &lt;code&gt;pygame.mixer.music&lt;/code&gt; module. All of these functions are non-blocking and return immediately:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pygame.mixer.music.stop()&lt;/code&gt; stops playing the music immediately.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pygame.mixer.music.pause()&lt;/code&gt; stops playing the music, but will resume where it left off when unpaused.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pygame.mixer.music.unpause()&lt;/code&gt; resumes playing paused music.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pygame.mixer.music.fadeout(milliseconds)&lt;/code&gt; fades the volume of the music down to silence over the next &lt;em&gt;&lt;code&gt;milliseconds&lt;/code&gt;&lt;/em&gt; milliseconds. For example, passing &lt;code&gt;2000&lt;/code&gt; will fade out the music over the next 2 seconds after the function call.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pygame.mixer.music.get_volume()&lt;/code&gt; returns a float of the volume level for the &lt;code&gt;pygame.mixer&lt;/code&gt; music, with &lt;code&gt;1.0&lt;/code&gt; for full volume and &lt;code&gt;0.0&lt;/code&gt; for muted volume.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pygame.mixer.music.set_volume(level)&lt;/code&gt; lets you set the volume by passing a float between &lt;code&gt;1.0&lt;/code&gt; and &lt;code&gt;0.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pygame.mixer.music.get_busy()&lt;/code&gt; returns &lt;code&gt;True&lt;/code&gt; if music is currently playing and False if the music has finished, stopped, or paused.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pygame.mixer.music.get_pos()&lt;/code&gt; returns the position in the audio file Pygame's mixer module is playing it (or, if paused, would play it) in milliseconds. For example, returning 0 means the position is at the very start of the audio file and returning &lt;code&gt;5000&lt;/code&gt; means the position is 5 seconds into the audio file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pygame.mixer.music.set_pos(position)&lt;/code&gt; sets the position in the audio file to play. The position parameter is a float or int for the number of seconds into the audio. The audio must currently be playing to call set_pos().&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pygame.mixer.music.queue(audio_filename)&lt;/code&gt; lets you set another audio file to begin playing as soon as the current one has finished. The audio_filename parameter is either a string or a Path object of the audio file to queue.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pygame also has other audio features such as playing multiple sound files at the same time. The full documentation is at &lt;a href="https://www.pygame.org/docs/"&gt;https://www.pygame.org/docs/&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Working with Video Files&lt;/h2&gt;
&lt;p&gt;The programs you create from this book have text-based input and output with the &lt;code&gt;input()&lt;/code&gt; and &lt;code&gt;print()&lt;/code&gt; commands, so displaying images and videos in a conventional graphical user interface (GUI) is beyond the scope of this book. However, your Python programs can still download videos from the internet without a GUI and control the playback of the video in VLC Media Player, as you’ll learn in this section.&lt;/p&gt;
&lt;h3&gt;Downloading Videos from YouTube&lt;/h3&gt;
&lt;p&gt;The main limitation of YouTube and other video sites is that you can only view the videos in a browser while online. You can’t edit the videos to extract clips or make adjustments. The yt-dlp tool is a fork of the youtube-dl project, and can download videos as files from YouTube, Vimeo, Facebook, TikTok, Instagram, and hundreds of other video websites. You can install it by running &lt;code&gt;pip install yt-dlp&lt;/code&gt; from the terminal. Appendix A has full instructions.&lt;/p&gt;
&lt;p&gt;The yt-dlp project is both a terminal command and a Python module. We’ll focus on the terminal command first but also explore how to incorporate it in your Python programs. Both youtube-dl and yt-dlp are written in Python. Because video websites change their layout from time to time, you’ll need to always run the latest version of yt-dlp. You can make yt-dlp update itself by running &lt;code&gt;yt-dlp -U&lt;/code&gt;. The yt-dlp command uses &lt;em&gt;calver&lt;/em&gt; or &lt;em&gt;calendar versioning&lt;/em&gt;, so the version number is always the date of that version’s release. For example, version 2023.07.06 was released on July 6, 2023.&lt;/p&gt;
&lt;p&gt;To download a video, run the &lt;code&gt;yt-dlp&lt;/code&gt; command from a terminal with the video’s URL as the command-line argument:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nl"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Al&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Desktop&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;yt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dlp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;youtu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mDNa10gQTJA&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;youtube&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Extracting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;youtu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mDNa10gQTJA&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;youtube&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;mDNa10gQTJA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Downloading&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;webpage&lt;/span&gt;
&lt;span class="c1"&gt;--snip--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If the URL contains characters such as an ampersand or spaces, you may have to enclose them in double quotes. The filename is automatically made from the video description. To specify your own filename for the downloaded video file, pass the &lt;code&gt;-o&lt;/code&gt; argument:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;C:\Users\Al\Desktop&amp;gt;yt-dlp -o catvideo.webm https://youtu.be/mDNa10gQTJA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If the video is a music video and you only want the audio data, pass the &lt;code&gt;-x&lt;/code&gt; argument to extract the audio:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;C:\Users\Al\Desktop&amp;gt;yt-dlp -x https://youtu.be/9RHFFeQ2tu4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Depending on the video and video website, the audio format may be an &lt;em&gt;.mp3&lt;/em&gt;, &lt;em&gt;.ogg&lt;/em&gt;, or &lt;em&gt;.webm&lt;/em&gt; file. You can convert it to another audio format with ffmpeg, explained later in this chapter.&lt;/p&gt;
&lt;p&gt;If a video requires that you must be logged in to view it, log into the site in your browser and then run yt-dlp with the &lt;code&gt;--cookies-from-browser&lt;/code&gt; argument followed by the name of the browser (either &lt;code&gt;Firefox&lt;/code&gt;, &lt;code&gt;Chrome&lt;/code&gt;, &lt;code&gt;Edge&lt;/code&gt;, or &lt;code&gt;Safari&lt;/code&gt;). This makes yt-dlp examine your browser’s cookies to reuse the login session information there:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;C:\Users\Al\Desktop&amp;gt;yt-dlp --cookies-from-browser Firefox https://youtu.be/mDNa10gQTJA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can also specify the username and password of the account with the &lt;code&gt;-u&lt;/code&gt; and &lt;code&gt;-p&lt;/code&gt; command line arguments. You can leave out the &lt;code&gt;-p&lt;/code&gt; argument to have yt-dlp ask you to enter the password at the keyboard.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;C:\Users\Al\Desktop&amp;gt;yt-dlp -u my_username -p secret_passw0rd https://youtu.be/9RHFFeQ2tu4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are many features for downloading videos with yt-dlp, such as downloading entire playlists, using a VPN proxy, downloading subtitles, and others. To view all of them, run &lt;code&gt;yt-dlp --help&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Running yt-dlp from Python&lt;/h3&gt;
&lt;p&gt;You can also download videos using yt-dlp using Python code. The module name is &lt;code&gt;yt_dlp&lt;/code&gt;, which lets you create &lt;code&gt;YoutubeDL&lt;/code&gt; objects. Despite the name, the &lt;code&gt;YoutubeDL&lt;/code&gt; object can download videos from any video website that yt-dlp supports. Open a new file and add the following code for a minimal program that downloads videos with the `yt_dlp module. Name it &lt;em&gt;videoDownloader.py&lt;/em&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;yt_dlp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;YoutubeDL&lt;/span&gt;
&lt;span class="n"&gt;URLs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://www.youtube.com/watch?v=BaW_jenozKc&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Add video URLs here.&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;YoutubeDL&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ydl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;ydl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URLs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The yt-dlp documentation at &lt;a href="https://pypi.org/project/yt-dlp/"&gt;https://pypi.org/project/yt-dlp/&lt;/a&gt; has more information on supplying the download() function with more options.&lt;/p&gt;
&lt;p&gt;Alternatively, you can run the yt-dlp terminal command from a Python script using the &lt;code&gt;subprocess.run()&lt;/code&gt; function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;subprocess&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;yt-dlp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://www.youtube.com/watch?v=BaW_jenozKc&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you want the video’s metadata, such as duration, number of views, or description, you can download this as JSON data using the &lt;code&gt;-J&lt;/code&gt; argument. Chapter 19 covers using the &lt;code&gt;subprocess&lt;/code&gt; module to run terminal commands from a Python program and capture their output as strings. Chapter 18 covers the &lt;code&gt;json&lt;/code&gt; module and how to work with JSON data. Let’s look at the following example of parsing a video’s metadata as JSON from Python code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;json&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;subprocess&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;yt-dlp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;-J&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://youtu.be/mDNa10gQTJA&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python_data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;python_data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;formats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;thumbnails&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;thumbnail&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;description&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;channel_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;channel_url&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;duration&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;view_count&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;Sister cats Grace, Mercy, and Patience love each other very much.&amp;#39;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;description&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;These were kittens I rescued from a backyard cat colony in Houston in October 2021. Grace (gray), Mercy (brown tabby), and Patience (tortoiseshell) probably aren&amp;#39;t actually sisters, but they grew up together in my house and are the same age and--snip--&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;duration&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# In seconds.&lt;/span&gt;
&lt;span class="mi"&gt;116&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;view_count&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;1797&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;upload_date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# In YYYYMMDD format.&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;20220903&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Every video website returns different metadata, so the &lt;code&gt;list(python_data.keys())&lt;/code&gt; part of our example can show you the dictionary keys that are available.&lt;/p&gt;
&lt;h2&gt;Play a Video File&lt;/h2&gt;
&lt;p&gt;Playing a video file from your Python program is complicated. For the most part, this book covers Python programs that don’t use a graphical user interface. However, if you just need a video to play somewhere on your screen, you can install VLC Media Player and then use the third-party python-vlc library to display videos in their own window. After installing VLC Media Player, run pip install python-vlc to install the module that lets you control VLC from your Python programs. See Appendix A for full instructions.&lt;/p&gt;
&lt;p&gt;Once installed, you can then use the vlc module to create an Instance, MediaPlayer, and Media object. Just replace the 'video.mp4' string with the filename of the video file you want to play. Enter the following into the interactive shell with a video file named video.mp4 in the current working directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;vlc&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vlc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;--no-xlib&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;--quiet&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# These arguments stop debug output.&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;media_player_new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;media_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;video.mp4&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Replace with your filename.&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_media&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;play&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;play()&lt;/code&gt; method call causes the video to appear in a new window and start playing until it has finished. This method immediately returns; it doesn’t block until the video has finished playing. You can also call other &lt;code&gt;MediaPlayer&lt;/code&gt; methods:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pause()&lt;/code&gt; will pause the video, and unpauses when play() is called again.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stop()&lt;/code&gt; will immediately stop the video and close the video’s window.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;is_playing()&lt;/code&gt; returns 1 if the video is currently playing and returns 0 if the video is paused, stopped, or has finished. (In Python, True is equal to 1 and False is equal to 0.)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_length()&lt;/code&gt; returns an integer of the video length in milliseconds.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_fps()&lt;/code&gt; returns the frame rate of the video in frames per second.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_time()&lt;/code&gt; returns the position currently playing in the video, in milliseconds. Returns -1 if the video is stopped.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;set_time(seek)&lt;/code&gt; sets the position of the video to seek milliseconds. This can only be called while the video is playing or is paused.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;toggle_fullscreen()&lt;/code&gt; switches the video window between windowed and fullscreen mode.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;audio_get_volume()&lt;/code&gt; returns an integer of the MediaPlayer object’s volume: 100 is full volume and 0 is muted.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;audio_set_volume(volume)&lt;/code&gt; sets the MediaPlayer object’s volume to the volume integer, which can be greater than 100.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;video_get_size()&lt;/code&gt; returns a tuple of two integers of the width and height of the video in pixels.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;video_take_snapshot(0, '.', 0, 0)&lt;/code&gt; takes a snapshot of the current frame. The first &lt;code&gt;0&lt;/code&gt; argument is for the “video output number” which is usually zero, the &lt;code&gt;'.'&lt;/code&gt; argument tells VLC to save the snapshot image file in the current working directory, and the third and fourth arguments are the width and height of the snapshot (with the &lt;code&gt;0&lt;/code&gt; arguments meaning “use the original video’s width and height”). The snapshot image filename will be “vlcsnap” followed by a timestamp, such as &lt;em&gt;vlcsnap-2025-08-21-15h21m32s719.png&lt;/em&gt;. The method returns &lt;code&gt;0&lt;/code&gt; if the snapshot was successful and &lt;code&gt;-1&lt;/code&gt; if it failed for some reason.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While the &lt;code&gt;vlc&lt;/code&gt; module doesn’t provide much in the way a graphical user interface, the popup video window is enough for your basic Python programs to display a video clip as needed.&lt;/p&gt;
&lt;h2&gt;Editing Video and Audio with ffmpeg&lt;/h2&gt;
&lt;p&gt;One of the most powerful tools for editing video and audio content is the &lt;code&gt;ffmpeg&lt;/code&gt; command. While ffmpeg has no user-friendly graphical user interface like other apps (though ffmpeg GUI extensions have been separately made), it does have an extensive set of command line arguments. This makes it easy to automate from Python programs which is invaluable if you have a large number of multimedia files to process.&lt;/p&gt;
&lt;h3&gt;Downloading and Installing ffmpeg&lt;/h3&gt;
&lt;p&gt;The ffmpeg command is available for Windows, macOS, and Linux. You can download it from &lt;a href="https://ffmpeg.org/download.html"&gt;https://ffmpeg.org/download.html&lt;/a&gt;. The ffmpeg website also has an ffplay and ffprobe programs available for download. The ffplay is a terminal command that plays video and audio files in a barebones window while the ffprobe command extracts information about video and audio files. There is no installer for these commands. Instead, just place the ffmpeg, ffplay, and ffprobe executable files into a folder on your system PATH. (On Ubuntu Linux, you may be able to install them with the &lt;code&gt;sudo apt install ffmpeg&lt;/code&gt; command.) See Chapter 12 for an explanation of the PATH environment variable and working on the terminal command-line in general.&lt;/p&gt;
&lt;p&gt;On macOS, the ffmpeg, ffprobe, and ffplay programs might not run from the terminal since they don’t have a digital signature. Double-click on these commands in the Finder, and in the warning window about “unidentified developer,” click the Open button. This tells macOS that you consider these programs safe to run. You’ll only have to do this once for each program. &lt;/p&gt;
&lt;h3&gt;ffmpeg Recipes&lt;/h3&gt;
&lt;p&gt;The ffmpeg program’s set of command line arguments is far too complicated to cover in this book. Instead, this chapter features specific recipes for carrying out common editing tasks.&lt;/p&gt;
&lt;p&gt;The ffmpeg program produces a lot of output. You can hide all of this by passing the &lt;code&gt;-loglevel&lt;/code&gt; warning command-line arguments, which suppresses all logging output except for warnings and errors. For example, instead of running &lt;code&gt;ffmpeg -i input.mp4 output.avi&lt;/code&gt; to show you all the information associated with converting an &lt;em&gt;.mp4&lt;/em&gt; video file to an &lt;em&gt;.avi&lt;/em&gt; file, you can run &lt;code&gt;ffmpeg -loglevel warning -i input.mp4 output.avi&lt;/code&gt; to do the conversion with no output.&lt;/p&gt;
&lt;p&gt;In all of the ffmpeg recipes, the input file follows the &lt;code&gt;-i&lt;/code&gt; command line argument and the output file is placed at the very end of the command after all the other command line arguments.&lt;/p&gt;
&lt;p&gt;If a file already exists with the output filename you passed to ffmpeg, then ffmpeg pauses and asks for confirmation to overwrite this file by asking the user to press &lt;code&gt;y&lt;/code&gt; and Enter on the keyboard. This pause can interrupt your programs, so the &lt;code&gt;-y&lt;/code&gt; argument you see in the ffmpeg recipes causes ffmpeg to skip this confirmation step.&lt;/p&gt;
&lt;p&gt;You can run the ffmpeg command from your Python scripts using the &lt;code&gt;subprocess.run()&lt;/code&gt; function, as covered in Chapter 19. Remember to run &lt;code&gt;import subprocess&lt;/code&gt; first before calling &lt;code&gt;subprocess.run()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can transform the audio or video of your files with ffmpeg’s many filters. The recipes here explain some, but you can view the full list by running &lt;code&gt;ffmpeg -filters&lt;/code&gt; and then get help information about a specific filter by running &lt;code&gt;ffmpeg -h filter=&lt;/code&gt;&lt;em&gt;&lt;code&gt;filter_name&lt;/code&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The ffmpeg command has a vast set of command line arguments for doing almost any kind of editing you’d like from a terminal window or Python script. If you don’t have the time to read through its documentation, you can ask an AI large language model such as ChatGPT for the command. For example, you could give it the prompt: “What is the ffmpeg command for converting a video to black and white?” and receive the command along with an explanation of each command-line argument. Be sure to test the information the AI gives you, as it often makes mistakes.&lt;/p&gt;
&lt;p&gt;Alternatively, the website &lt;a href="https://ffmpeg.app/"&gt;https://ffmpeg.app/&lt;/a&gt; features an interactive prompt system that reliably guides you to an ffmpeg command with arguments to carry out your intended actions. The website looks like Figure 24-2:&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/lost-av-ffmpeg1.webp" style="max-width: 100%"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/lost-av-ffmpeg2.webp" style="max-width: 100%"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The website &lt;a href="https://ffmpeg.app"&gt;https://ffmpeg.app&lt;/a&gt; helps you find command line arguments.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Keep in mind that ffmpeg can not only edit video and audio, it can also edit image files as well. Instead of specifying a video file with &lt;code&gt;-i input.mp4&lt;/code&gt; or &lt;code&gt;output.mp4&lt;/code&gt; command line arguments, for images you can use &lt;code&gt;-i input.png&lt;/code&gt; or &lt;code&gt;output.webp&lt;/code&gt; If ffmpeg gives you an error message about how the output filename “does not contain an image sequence pattern” you can include the &lt;code&gt;-update 1&lt;/code&gt; argument. See the &lt;em&gt;Extract Frames from a Video&lt;/em&gt; section for more information about sequence patterns and the &lt;code&gt;-update&lt;/code&gt; argument.&lt;/p&gt;
&lt;h4&gt;View Video and Audio File Metadata with ffprobe&lt;/h4&gt;
&lt;p&gt;The ffprobe command (which comes with ffmpeg) can give you a large amount of information about a video or audio file. Its basic usage looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffprobe input.mp4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command produces a large amount of text output with details about the &lt;em&gt;input.mp4&lt;/em&gt; file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ffprobe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;109948&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ge026e29460&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;20230301&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Copyright&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2007&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FFmpeg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;developers&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;built&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gcc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;12.2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crosstool&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;NG&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.25&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.90&lt;/span&gt;&lt;span class="n"&gt;_cf9beb1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;#0:0[0x1](eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 191 kb/s (default)&lt;/span&gt;
&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;#0:1[0x2](eng): Video: h264 (High) (avc1 / 0x31637661), yuvj420p(pc, smpte170m/bt470bg/smpte170m, progressive), 1920x1080, 22582 kb/s, SAR 1:1 DAR 16:9, 30.01 fps, 30 tbr, 90k tbn (default)&lt;/span&gt;
&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can also run this command from the following Python instruction, which saves the output as a string in &lt;code&gt;proc.stderr&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;subprocess&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ffprobe&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;input.mp4&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ffprobe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;109948&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ge026e29460&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;20230301&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Copyright&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2007&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FFmpeg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;developers&lt;/span&gt;
&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Video and audio files contain streams of encoded video and audio data. For example, a movie video file may have a stream of video data and multiple audio streams for English, Spanish, and French dubs. The ffprobe command can show you these streams and their associated information. For example, this &lt;em&gt;input.mp4&lt;/em&gt; file has audio encoded with the AAC codec and video that has 30.01 frames per second (fps) and a size of 1920x1080. If you want your program to extract this information, you’ll likely need to use regular expressions (covered in Chapter 9).&lt;/p&gt;
&lt;p&gt;For example, you can enter this code into the interactive shell to use regular expressions to find the 1920 x 1080 width and height dimensions of an &lt;em&gt;input.mp4&lt;/em&gt; video file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;re&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ffprobe&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;input.mp4&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Video.*, (\d+)x(\d+),&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="mi"&gt;1920&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="mi"&gt;1080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using code like this, you could then find the dimensions of, say, each video file in a folder. This is similar to the &lt;code&gt;video_get_size()&lt;/code&gt; method in the &lt;code&gt;vlc&lt;/code&gt; module, though ffprobe can read far more detail than just video size.&lt;/p&gt;
&lt;p&gt;You can run &lt;code&gt;ffprobe --help&lt;/code&gt; to view the command-line options, but the output is over 2,400 lines long! It may be useful to ask a large language model AI for specific command line arguments to pass to ffprobe for a given task if you are overwhelmed by the documentation.&lt;/p&gt;
&lt;p&gt;The next several sections cover individual ffmpeg commands along with their command line arguments for performing a number of common video-related tasks. We’ll be using example filenames such as &lt;em&gt;input.mp4&lt;/em&gt; and &lt;em&gt;output.mp4&lt;/em&gt;, and keep in mind that ffmpeg won’t modify the input video and audio files; it only produces a new video or audio file with the changes as the output. You can download some short example files to practice with from &lt;a href="https://autbor.com/input_ffmpeg.zip"&gt;https://autbor.com/input_ffmpeg.zip&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Convert Video and Audio File Formats&lt;/h4&gt;
&lt;p&gt;You can convert video and audio files from one format to another with this terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 output.avi

&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;output.avi&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-i&lt;/code&gt; option indicates that the &lt;code&gt;input.mp4&lt;/code&gt; file is the input file, which is then converted to an &lt;em&gt;.avi&lt;/em&gt; file named &lt;em&gt;output.avi&lt;/em&gt;. You can specify any video file formats you want, including &lt;em&gt;.gif&lt;/em&gt; for an animated GIF. Remember that ffmpeg works with image files as well, so you can convert a &lt;em&gt;.png&lt;/em&gt; to a &lt;em&gt;.webp&lt;/em&gt; or any other format.&lt;/p&gt;
&lt;h4&gt;Extract Audio from a Video&lt;/h4&gt;
&lt;p&gt;You can extract the audio from a video file with this terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 output.mp3

&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;output.mp3&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the same set of arguments as converting formats. The ffmpeg command can understand that if the output file is an &lt;em&gt;.mp3&lt;/em&gt; audio file (or any other audio file format), the video data is dropped and only the audio data is used.&lt;/p&gt;
&lt;h4&gt;Mute or Change Volume in a Video File&lt;/h4&gt;
&lt;p&gt;You can increase, decrease, or mute the volume in a video file with the following terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -af volume=2.0 output.mp4

&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-af&amp;#39;, &amp;#39;volume=2.0&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-af&lt;/code&gt; argument specifies an audio filter named volume to change the volume by a factor of 2.0, or double the volume of the &lt;code&gt;input.mp4&lt;/code&gt; file. The video with the new volume is in &lt;code&gt;output.mp4&lt;/code&gt;. If you wanted to halve the volume, you could pass &lt;code&gt;volume=0.5&lt;/code&gt; instead. You could also change the volume by a number of decibels. For example, passing &lt;code&gt;volume=5dB&lt;/code&gt; increases the volume of the input file by 5 decibels. You could also pass &lt;code&gt;volume=-5dB&lt;/code&gt; to decrease the volume by 5 decibels.&lt;/p&gt;
&lt;p&gt;If you want to simply mute the volume by removing the audio data completely, pass the &lt;code&gt;-an&lt;/code&gt; argument:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -c:v copy -an output.mp4

&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-c:v&amp;#39;, &amp;#39;copy&amp;#39;, &amp;#39;-an&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-c:v&lt;/code&gt; copy argument means that ffmpeg will copy the video stream data from &lt;code&gt;input.mp4&lt;/code&gt; directly to &lt;code&gt;output.mp4&lt;/code&gt; without re-encoding it. The &lt;code&gt;-an&lt;/code&gt; argument removes the audio stream data altogether. Keep in mind that once you completely mute the volume on a video, you won’t be able to increase the volume later.&lt;/p&gt;
&lt;h4&gt;Combine a Video and Audio File&lt;/h4&gt;
&lt;p&gt;You can add an audio file to a silent video with the following terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input_video.mp4 -i input_audio.mp3 -c copy output.mp4

&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input_video.mp4&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input_audio.mp3&amp;#39;, &amp;#39;-c&amp;#39;, &amp;#39;copy&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-c copy&lt;/code&gt; arguments copy the video and audio stream data without re-encoding, respectively. If the audio has some problems because the video format doesn’t support it, try replacing &lt;code&gt;-c copy&lt;/code&gt; with &lt;code&gt;-c:v copy -c:a aac&lt;/code&gt; to copy the video but re-encode the audio with the widely-supported AAC audio codec.&lt;/p&gt;
&lt;p&gt;If the video already has audio, but you want to replace it with the audio in a separate audio file, run the following terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i video.mp4 -i audio.mp3 -c copy -map 0:v:0 -map 1:a:0 output.mp4

&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;video.mp4&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;audio.mp3&amp;#39;, &amp;#39;-c&amp;#39;, &amp;#39;copy&amp;#39;, &amp;#39;-map&amp;#39;, &amp;#39;0:v:0&amp;#39;, &amp;#39;-map&amp;#39;, &amp;#39;1:a:0&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-map 0:v:0 -map 1:a:0&lt;/code&gt; arguments direct ffmpeg to use the video stream from the first input, &lt;code&gt;video.mp4&lt;/code&gt;, and the audio from the second input, &lt;code&gt;audio.mp3&lt;/code&gt;, when creating the &lt;code&gt;output.mp4&lt;/code&gt; file. The original audio stream data in &lt;code&gt;video.mp4&lt;/code&gt; is dropped.&lt;/p&gt;
&lt;h4&gt;Resize a Video&lt;/h4&gt;
&lt;p&gt;You can resize a video with this terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -vf scale=1280:720 output.mp4
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-vf&amp;#39;, &amp;#39;scale=1280:720&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-vf&lt;/code&gt; indicates you are using a video filter, in this case a filter named &lt;code&gt;scale&lt;/code&gt; that scales the size of the video to 1280 x 720 resolution and outputs it to a video file named &lt;code&gt;output.mp4&lt;/code&gt;. You can change the resolution to any dimensions you want.&lt;/p&gt;
&lt;h4&gt;Extract a Video or Audio Segment&lt;/h4&gt;
&lt;p&gt;You can extract a segment from a video or audio file with the following terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -ss 00:01:30 -to 00:01:45 output.mp4
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-ss&amp;#39;, &amp;#39;00:01:30&amp;#39;, &amp;#39;-to&amp;#39;, &amp;#39;00:01:45&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-ss&lt;/code&gt; argument and &lt;code&gt;-to&lt;/code&gt; arguments mark the starting and ending times of the segment to extract, given in an &lt;em&gt;HH:MM:SS&lt;/em&gt; format. So &lt;code&gt;-ss 00:01:30 -to 00:01:45&lt;/code&gt; means extract the video or audio content from 1 minute, 30 seconds to 1 minute, 45 seconds from &lt;code&gt;input.mp4&lt;/code&gt; file. This will result in a 15 second segment placed in a file named &lt;code&gt;output.mp4&lt;/code&gt;. You can change the starting and ending timestamps as needed.
To extract an audio segment from an audio file, use the audio filenames for the input and output file. The rest of the command remains the same. You can use an ffprobe recipe described later in this chapter to find out the length of the video or audio content in a file.&lt;/p&gt;
&lt;h4&gt;Extract Frames from a Video&lt;/h4&gt;
&lt;p&gt;You can extract a single frame from a video with the following terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -ss 00:00:15 -vframes 12 output%04d.jpg
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-ss&amp;#39;, &amp;#39;00:00:15&amp;#39;, &amp;#39;-vframes&amp;#39;, &amp;#39;12&amp;#39;, &amp;#39;output%04d.jpg&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-vframes 12&lt;/code&gt; and &lt;code&gt;-ss 00:00:15&lt;/code&gt; arguments mean that 12 frames are extracted from the video starting at 15 seconds into the video is extracted and placed into an image file named &lt;em&gt;output0001.jpg&lt;/em&gt;. The &lt;code&gt;%04d&lt;/code&gt; in the &lt;code&gt;output%04d.jpg&lt;/code&gt; output name is a sequence pattern that means the 12 frames are saved to files with 4-digit numbers with leading zeros: &lt;em&gt;output0001.jpg&lt;/em&gt;, &lt;em&gt;output0002.jpg&lt;/em&gt;, &lt;em&gt;output0003.jpg&lt;/em&gt;, and so on up to &lt;em&gt;output0012.jpg&lt;/em&gt;. Also, you can use a wide range of image formats in addition to &lt;em&gt;.jpg&lt;/em&gt;, including &lt;em&gt;.png&lt;/em&gt;, &lt;em&gt;.bmp&lt;/em&gt;, or &lt;em&gt;.webp&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If you only want to extract a single frame from the video and don’t want to use a sequence pattern in the output filenames, use &lt;code&gt;-vframe 1&lt;/code&gt; and add the additional &lt;code&gt;-update 1&lt;/code&gt; argument:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -ss 00:00:15 -vframes 1 -update 1 output.jpg
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-ss&amp;#39;, &amp;#39;00:00:15&amp;#39;, &amp;#39;-vframes&amp;#39;, &amp;#39;1&amp;#39;, &amp;#39;-update&amp;#39;, &amp;#39;1&amp;#39;, &amp;#39;output.jpg&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you want to extract every frame from a video, you can run a much simpler terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 output%04d.jpg
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;output%04d.jpg&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Many videos are about 30 frames per second, so a single minute of video will produce about 1,800 image files.&lt;/p&gt;
&lt;h4&gt;Concatenate Multiple Video or Audio Files&lt;/h4&gt;
&lt;p&gt;You can concatenate a video or audio file to the end of another video or audio file with the following terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -f concat -i videolist.txt -c copy output.mp4
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-f&amp;#39;, &amp;#39;concat&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;mylist.txt&amp;#39;, &amp;#39;-c&amp;#39;, &amp;#39;copy&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command concatenates all the videos listed in the &lt;code&gt;videolist.txt&lt;/code&gt; text file together into a single video in a file named &lt;code&gt;output.mp4&lt;/code&gt;. The &lt;code&gt;videolist.txt&lt;/code&gt; file has the following format:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;file &amp;#39;input_spam.mp4&amp;#39;
file &amp;#39;input_eggs.mp4&amp;#39;
file &amp;#39;input_bacon.mp4&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command only works if the videos have the same codecs and size, otherwise you’ll need to consult the ffmpeg documentation for mixing videos of different formats.&lt;/p&gt;
&lt;h4&gt;Add Watermark or Overlay to a Video&lt;/h4&gt;
&lt;p&gt;You can add a watermark or overlay image to a video with the following terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -i logo.png -filter_complex overlay=10:50 output.mp4
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;logo.png&amp;#39;, &amp;#39;-filter_complex&amp;#39;, &amp;#39;overlay=10:50&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-i logo.png&lt;/code&gt; means that the logo.png file is also an input, and the &lt;code&gt;-filter_complex overlay=10:50&lt;/code&gt; means that it will be placed on top of the &lt;code&gt;input.mp4&lt;/code&gt; input video, 10 pixels from the left edge and 50 pixels from the top edge.&lt;/p&gt;
&lt;p&gt;You can also use a video as the overlay instead of a still image if you want a picture-in-picture effect.&lt;/p&gt;
&lt;h4&gt;Rotate a Video&lt;/h4&gt;
&lt;p&gt;If you have videos that are incorrectly rotated, you can correct this with the following terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -vf transpose=1 -c:a copy output.mp4
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-vf&amp;#39;, &amp;#39;transpose=1&amp;#39;, &amp;#39;-c:a&amp;#39;, &amp;#39;copy&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-vf&lt;/code&gt; argument means ffmpeg will apply a video filter, and the specific video filter is &lt;code&gt;transpose=1&lt;/code&gt;. The number &lt;code&gt;1&lt;/code&gt; means the video is rotated 90 degrees clockwise. You can also use any of the following numbers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0&lt;/code&gt;: Rotate by 90 degrees counterclockwise and flip vertically.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1&lt;/code&gt;: Rotate by 90 degrees clockwise.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2&lt;/code&gt;: Rotate by 90 degrees counterclockwise.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;3&lt;/code&gt;: Rotate by 90 degrees clockwise and flip vertically.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you would like to rotate the video 180 degrees clockwise, then apply the transpose video filter twice with the &lt;code&gt;-vf transpose=1,transpose=1&lt;/code&gt; arguments.&lt;/p&gt;
&lt;h4&gt;Normalizing audio in a video file&lt;/h4&gt;
&lt;p&gt;If you have audio with wild fluctuations in volume, you can normalize it with the following terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -af dynaudnorm output.mp4
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-af&amp;#39;, &amp;#39;dynaudnorm&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;dynaudnorm&lt;/code&gt; audio filter performs dynamic audio normalization and is good for evening out volume for, say, conversations where people speak at different volumes. There’s also the &lt;code&gt;loudnorm&lt;/code&gt; audio filter, which sets specific loudness standards commonly used in broadcast media:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -af loudnorm output.mp4
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-af&amp;#39;, &amp;#39;dynaudnorm&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The default settings for these filters should work well, but you can run &lt;code&gt;ffmpeg -h filter=dynaudnorm&lt;/code&gt; and &lt;code&gt;ffmpeg -h filter=loudnorm&lt;/code&gt; to view the help information for their various settings.&lt;/p&gt;
&lt;h4&gt;Compress a Video or Audio File&lt;/h4&gt;
&lt;p&gt;Video files, especially high-quality videos, can be quite large. You can re-encode a video with the following terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -vcodec libx265 -crf 28 output.mp4
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-vcodec&amp;#39;, &amp;#39;libx265&amp;#39;, &amp;#39;-crf&amp;#39;, &amp;#39;28&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command compresses a video file by re-encoding its video stream data with the H.265 (HEVC) codec and setting a higher CRF (Constant Rate Factor) value. This may slightly reduce the quality of the video, but it also reduces its disk size.&lt;/p&gt;
&lt;p&gt;The bitrate of an audio file is how many bits are used per unit of time. For example, 128kbps uses 128 kilobits (or 16 kilobytes) per second of audio, so a 60 second MP3 file at 128kbps would take up about 1.1 megabytes of disk space. In general, a higher bitrate produces higher quality audio.&lt;/p&gt;
&lt;p&gt;You can re-encode an audio file with a lower bitrate with the following terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp3 -b:a 32k output.mp3
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp3&amp;#39;, &amp;#39;-b:a&amp;#39;, &amp;#39;32k&amp;#39;, &amp;#39;output.mp3&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For MP3s, 32kbps is low quality while 256kbps is high quality and 320kbps is almost indistinguishable from a music CD. (CDs, or compact discs, were an audio storage media used by ancient civilizations.)&lt;/p&gt;
&lt;p&gt;While you can create smaller files by re-encoding a high quality audio file to a lower bitrate audio file, re-encoding a low bitrate audio file to a higher bitrate won’t magically improve the audio quality.&lt;/p&gt;
&lt;h4&gt;Video Fade In and Out&lt;/h4&gt;
&lt;p&gt;You can add a fade in from or fade out to black effect at the start or end of your video with the following terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -vf fade=in:0:30 output.mp4
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-vf&amp;#39;, &amp;#39;fade=in:0:30&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This recipe uses a video filter name &lt;code&gt;fade&lt;/code&gt;. The &lt;code&gt;-vf fade=in:0:30&lt;/code&gt; argument means that the fade in starts at frame 0 (the very start of the video) and ends at frame 30. For a video that is 30 frames per second, this creates a 1-second fade in at the start of the video. You can also have a fade out with this terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -vf fade=in:0:30,fade=out:150:60 output.mp4
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-vf&amp;#39;, &amp;#39;fade=in:0:30,fade=out:300:15&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The fade out starts at frame 150 (5 seconds into the video) and finishes in 60 frames, or two seconds.&lt;/p&gt;
&lt;h4&gt;Decrease Saturation or Convert to Black and White&lt;/h4&gt;
&lt;p&gt;You can decrease the saturation (how colorful the video is) with the following terminal commands or Python instructions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -vf eq=saturation=0.5 output.mp4
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-vf&amp;#39;, &amp;#39;eq=saturation=0.5&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The -vf argument applies a video filter, specifically the &lt;code&gt;eq=saturation=0.5&lt;/code&gt; video filter, which reduces the saturation by one half. To make the video completely black and white (that is, grayscale), use &lt;code&gt;eq=saturation=0.0&lt;/code&gt;. You can also increase the saturation to give the video a colorful, almost cartoon look by doubling the saturation with &lt;code&gt;eq=saturation=2.0&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;Adjust Video Brightness and Contrast&lt;/h4&gt;
&lt;p&gt;You can increase the brightness of a video by 6% with the following terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -vf eq=brightness=0.06 output.mp4
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-vf&amp;#39;, &amp;#39;eq=brightness=0.06&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-vf&lt;/code&gt; argument means you want to apply a video filter, specifically the &lt;code&gt;eq=brightness=0.06&lt;/code&gt; filter that increases brightness by 6%. Using &lt;code&gt;eq=brightness=0.5&lt;/code&gt; would increase brightness by 50%, while using &lt;code&gt;eq=brightness=-0.5&lt;/code&gt; would decrease brightness by 50%.&lt;/p&gt;
&lt;p&gt;On the other hand, the value for the contrast filter ranges from 0.0 (no contrast) to 1.0 (normal contrast) and up. You can set the contrast 30% higher than normal with the following terminal command or Python instruction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ffmpeg -y -i input.mp4 -vf eq=contrast=1.3 output.mp4
&amp;gt;&amp;gt;&amp;gt; subprocess.run([&amp;#39;ffmpeg&amp;#39;, &amp;#39;-y&amp;#39;, &amp;#39;-i&amp;#39;, &amp;#39;input.mp4&amp;#39;, &amp;#39;-vf&amp;#39;, &amp;#39;eq=contrast=1.3&amp;#39;, &amp;#39;output.mp4&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, you can make the contrast 30% lower than normal by using &lt;code&gt;eq=contrast=0.7&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Audio and video data make up a large part of how we interact with computers whether it’s podcasts, dance videos, security camera footage, or sound effects. Python has several third-party modules for downloading, playing, and editing audio and video data, and this chapter provides small code examples you can embed in your own programs.&lt;/p&gt;
&lt;p&gt;Python has are several multimedia libraries installable through pip. The OpenCV computer vision library can access your webcam to take photos and videos. The Pygame library, while designed for making video games, can record images from webcams as well as play sound files. The sounddevice and wavio modules for Python can record audio from your computer’s microphone.&lt;/p&gt;
&lt;p&gt;There are also several free, open source applications for dealing with multimedia data: yt-dlp for downloading videos, VLC Media Player for viewing videos, and ffmpeg for applying a wide range of filters and conversions to video and audio files.&lt;/p&gt;
&lt;p&gt;The topics touched on here could easily fill a book of their own, but this chapter provides a wide introduction and sample code for getting started. Python is much more than a programming language; it’s also a popular development community that has created many free third-party modules to bring new features to your programs.&lt;/p&gt;
&lt;h2&gt;Practice Questions&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Divide the following into audio or video file format: &lt;em&gt;.ogg&lt;/em&gt;, &lt;em&gt;.mp3&lt;/em&gt;, &lt;em&gt;.mp4&lt;/em&gt;, &lt;em&gt;.wav&lt;/em&gt;, &lt;em&gt;.avi&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Can the OpenCV library record audio for the video files it creates?&lt;/li&gt;
&lt;li&gt;Between the sounddevice and pygame modules, which would you use to play an audio file and block the execution of the Python program until the audio finishes playing? Which would you use to start playing an audio file and immediately continue with the rest of the Python program?&lt;/li&gt;
&lt;li&gt;What yt-dlp command line argument extracts the audio from a downloaded video file?&lt;/li&gt;
&lt;li&gt;How do you specify the input file for the ffmpeg command?&lt;/li&gt;
&lt;li&gt;How much does the VLC Media Player application cost?&lt;/li&gt;
&lt;li&gt;What does the ffprobe command do?&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Practice Projects&lt;/h2&gt;
&lt;p&gt;For practice, write programs that do the following.&lt;/p&gt;
&lt;h3&gt;Take Time Lapse Photos&lt;/h3&gt;
&lt;p&gt;Write a program that uses either OpenCV to take photos using a webcam once every 5 seconds, and then after a preset number of photos, assembles them into a time lapse video. Since time lapse videos can take hours to create, for testing purposes, it may help to find a lava lamp video online to play while the webcam on top of your screen is pointed at a mirror to view the video.&lt;/p&gt;
&lt;h3&gt;Add Fade In and Fade Out&lt;/h3&gt;
&lt;p&gt;Write a program that uses ffmpeg to add a two second fade in and fade out effect to every video file in a given folder. For testing purposes, you can make multiple copies of the same short video file. The program should also check the video length and offer a warning if the video is shorter than four seconds.&lt;/p&gt;
&lt;h3&gt;Brightness Preview&lt;/h3&gt;
&lt;p&gt;Write a program that uses ffmpeg to get a preview of various brightness changes. It can take quite a while to adjust the brightness of an entire video, and you may want to reprocess the video multiple times at varying brightness levels until you find one you like. It’s much faster to have ffmpeg change the brightness of a single image than an entire video.
To keep from having to re-run ffmpeg, your program should extract a frame from an input video, then run ffmpeg again on that frame image to produce other images at varying brightness levels. For example, in a loop you can pass ffmpeg the argument eq=brightness=0.01, eq=brightness=0.02, eq=brightness=0.03 and so on up to, say, eq=brightness=0.50. Be sure to give the output image files different names, such as brightness0.01.png, brightness0.02.png, and so on.
Then user will be free to view the different preview images and pick the brightness setting they’d like to later apply to the entire video.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>The TurtleSC Package for Python turtle.py Shortcuts</title><link href="https://inventwithpython.com/blog/turtlesc-package.html" rel="alternate"></link><published>2025-07-03T10:00:00-04:00</published><updated>2025-07-03T10:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-07-03:/blog/turtlesc-package.html</id><summary type="html">&lt;p&gt;TurtleSC provides a mini-language of shortcut instructions to carry out turtle.py function calls, like &lt;code&gt;'f 100'&lt;/code&gt; instead of &lt;code&gt;forward(100)&lt;/code&gt;. &lt;a href="https://github.com/asweigart/turtlesc"&gt;Git Repo&lt;/a&gt;, &lt;a href="https://pypi.org/project/TurtleSC/"&gt;PyPI Project Page&lt;/a&gt;.&lt;/p&gt;</summary><content type="html">&lt;p&gt;TurtleSC provides a mini-language of shortcut instructions to carry out turtle.py function calls, like &lt;code&gt;'f 100'&lt;/code&gt; instead of &lt;code&gt;forward(100)&lt;/code&gt;. &lt;a href="https://github.com/asweigart/turtlesc"&gt;Git Repo&lt;/a&gt;, &lt;a href="https://pypi.org/project/TurtleSC/"&gt;PyPI Project Page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These two examples do the same thing:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Original Turtle Code&lt;/th&gt;
&lt;th&gt;TurtleSC Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;from turtle import *&lt;/code&gt;&lt;br&gt;&lt;code&gt;from random import *&lt;/code&gt;&lt;br&gt;&lt;br&gt;&lt;code&gt;colors = ['red', 'orange', 'yellow', 'blue', 'green', 'purple']&lt;/code&gt;&lt;br&gt;&lt;br&gt;&lt;code&gt;speed('fastest')&lt;/code&gt;&lt;br&gt;&lt;code&gt;pensize(3)&lt;/code&gt;&lt;br&gt;&lt;code&gt;bgcolor('black')&lt;/code&gt;&lt;br&gt;&lt;code&gt;for i in range(300):&lt;/code&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;code&gt;pencolor(choice(colors))&lt;/code&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;code&gt;forward(i)&lt;/code&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;code&gt;left(91)&lt;/code&gt;&lt;br&gt;&lt;code&gt;hideturtle()&lt;/code&gt;&lt;br&gt;&lt;code&gt;done()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;from turtlesc import *&lt;/code&gt;&lt;br&gt;&lt;code&gt;from random import *&lt;/code&gt;&lt;br&gt;&lt;br&gt;&lt;code&gt;colors = ['red', 'orange', 'yellow', 'blue', 'green', 'purple']&lt;/code&gt;&lt;br&gt;&lt;br&gt;&lt;code&gt;sc('spd fastest, ps 3, bc black')&lt;/code&gt;&lt;br&gt;&lt;code&gt;for i in range(300):&lt;/code&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;code&gt;sc(f'pc {choice(colors)}, f {i}, l 91')&lt;/code&gt;&lt;br&gt;&lt;code&gt;sc('hide,done')&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These shortcuts are quicker to type, making them ideal for experimenting from the interactive shell. TurtleSC takes the idea of the existing &lt;code&gt;fd()&lt;/code&gt; and &lt;code&gt;rt()&lt;/code&gt; aliases for &lt;code&gt;forward()&lt;/code&gt; and &lt;code&gt;right()&lt;/code&gt; to the next level. All shortcuts are run from a string passed to the &lt;code&gt;turtlesc.sc()&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;I created this to help with some digital art projects, namely recreating the "impossible objects" of &lt;a href="https://en.wikipedia.org/wiki/Oscar_Reutersv%C3%A4rd"&gt;Swedish artist Oscar Reutersvärd&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/turtlesc-oscar-stamp.webp" style="width: 400px; max-width: 100%"&gt; &lt;img src="/blogstatic/turtlesc-oscar1.webp" style="width: 400px; max-width: 100%"&gt; &lt;img src="/blogstatic/turtlesc-oscar2.webp" style="width: 400px; max-width: 100%"&gt;&lt;/p&gt;
&lt;p&gt;The code for this is in my &lt;a href="https://github.com/asweigart/oscarsturtle"&gt;oscarsturtle repo&lt;/a&gt;, which I'll write a blog post on in the future.&lt;/p&gt;
&lt;p&gt;TurtleSC was created by me, Al Sweigart, author of &lt;a href="https://automatetheboringstuff.com/"&gt;Automate the Boring Stuff with Python&lt;/a&gt; and &lt;a href="https://inventwithpython.com/"&gt;other programming books&lt;/a&gt;. All of his books are available for free online under a &lt;a href="https://creativecommons.org/share-your-work/cclicenses/"&gt;Creative Commons license&lt;/a&gt;. If you'd like a tutorial specifically about Python's &lt;code&gt;turtle&lt;/code&gt; module, check out &lt;a href="https://github.com/asweigart/simple-turtle-tutorial-for-python/blob/master/simple_turtle_tutorial.md"&gt;The Simple Turtle Tutorial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can also write (less readable) turtle programs using shortcuts. For example, this program:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;turtle&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;fillcolor&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;

    &lt;span class="c1"&gt;# Set a random heading and draw several short lines with changing direction:&lt;/span&gt;
    &lt;span class="n"&gt;setheading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;begin_fill&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;end_fill&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;...could be written as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;turtlesc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;t 4 0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;fc &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;, sh &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;, bf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;f 1&lt;/span&gt;
&lt;span class="s1"&gt;               l &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;h,ef&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;u,done&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code isn't very readable, but it's quick to type. This is useful if you are making rapid prototypes of ideas.&lt;/p&gt;
&lt;p&gt;Shortcuts in the &lt;code&gt;sc()&lt;/code&gt; string argument are separated by a comma, with any shortcut arguments separated by spaces. Whitespace is insignificant (you can have one more spaces, and it's the same as a single space). Shortcut names and arguments are case-insensitive: &lt;code&gt;'f'&lt;/code&gt; and &lt;code&gt;'F'&lt;/code&gt; work the same. Newlines in the string argument are treated as shortcut-separating commas.&lt;/p&gt;
&lt;p&gt;Some less-common shortcuts (such as &lt;code&gt;'done'&lt;/code&gt;) only have their full function name. All shortcuts have their full function name as a shortcut name: you can use either &lt;code&gt;'f'&lt;/code&gt; or &lt;code&gt;'forward'&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;TurtleSC also adds cardinal movement shortcuts that move the turtle independent of it's current heading (and does not change the heading): &lt;code&gt;'n 100'&lt;/code&gt;, &lt;code&gt;'s 100'&lt;/code&gt;, &lt;code&gt;'e 100'&lt;/code&gt;, &lt;code&gt;'w 100'&lt;/code&gt; will move the turtle up, down, right, and left 100 steps, respectively. There are also full name shortcuts (&lt;code&gt;'north'&lt;/code&gt;, &lt;code&gt;'south'&lt;/code&gt;, &lt;code&gt;'east'&lt;/code&gt;, &lt;code&gt;'west'&lt;/code&gt;) and diagonal shortcuts (&lt;code&gt;'nw'&lt;/code&gt;, &lt;code&gt;'ne'&lt;/code&gt;, &lt;code&gt;'sw'&lt;/code&gt;, &lt;code&gt;'se'&lt;/code&gt;, &lt;code&gt;'northwest'&lt;/code&gt;, &lt;code&gt;'northeast'&lt;/code&gt;, &lt;code&gt;'southwest'&lt;/code&gt;, &lt;code&gt;'southeast'&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;By default, the &lt;code&gt;sc()&lt;/code&gt; function operates on the single, global turtle. You can also pass the &lt;code&gt;turtle_obj&lt;/code&gt; keyword argument to operate on different &lt;code&gt;Turtle&lt;/code&gt; objects:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;turtlesc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;turtle&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Turtle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Turtle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Make turtle 1 red and move up-right:&lt;/span&gt;
&lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pc red, l 45, f 100&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;turtle_obj&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Make turtle 2 blue and move down-right:&lt;/span&gt;
&lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pc blue, r 45, f 100&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;turtle_obj&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;done&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;turtlesc&lt;/code&gt; module also provides new &lt;code&gt;in_radians_mode()&lt;/code&gt; or &lt;code&gt;in_degrees_mode()&lt;/code&gt; functions that return a Boolean &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt; value depending on which mode the turtle is in. These features are missing in the original &lt;code&gt;turtle&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;If you want to get the original code for a shortcuts string (which can be helpful to print the shortcuts), pass it to the &lt;code&gt;scs()&lt;/code&gt; function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;turtlesc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;scs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;f 100, r 45, f 100&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;forward(100)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;right(45)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;forward(100)&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that the return value of &lt;code&gt;scs()&lt;/code&gt; is a string. If there are multiple shortcuts, they are separated by a newline and all lack the &lt;code&gt;turtle.&lt;/code&gt; prefix in case you want to add your own (either the &lt;code&gt;turtle&lt;/code&gt; module or a variable containing a &lt;code&gt;Turtle&lt;/code&gt; object.)&lt;/p&gt;
&lt;p&gt;To easily print out this shortcut string, call &lt;code&gt;psc()&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;turtlesc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;psc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;f 100, r 45, f 100&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Reference&lt;/h2&gt;
&lt;p&gt;Here is a complete reference of supported shortcuts:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Shortcut Call&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Turtle.py Equivalent&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('f 100')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.forward"&gt;&lt;code&gt;forward(100)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('b -100.5')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.backward"&gt;&lt;code&gt;backward(-100.5)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('l 45')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.left"&gt;&lt;code&gt;left(45)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('r 90')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.right"&gt;&lt;code&gt;right(90)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('h')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.home"&gt;&lt;code&gt;home()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('c')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.clear"&gt;&lt;code&gt;clear()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('g 15 40')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.goto"&gt;&lt;code&gt;goto(15 40)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('x 10')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.setx"&gt;&lt;code&gt;setx(10)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('y -20')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.sety"&gt;&lt;code&gt;sety(-20)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('st')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.stamp"&gt;&lt;code&gt;stamp()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('pd')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.pendown"&gt;&lt;code&gt;pendown()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('pu')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.penup"&gt;&lt;code&gt;penup()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('ps 4')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.pensize"&gt;&lt;code&gt;pensize(4)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('pc 1.0 0.0 0.5')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.pencolor"&gt;&lt;code&gt;pencolor(1.0, 0.0, 0.5)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('fc 255 0 128')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.fillcolor"&gt;&lt;code&gt;fillcolor(255, 0, 128)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('bc #FF00FF')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.bgcolor"&gt;&lt;code&gt;bgcolor('#FF00FF')&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('sh 90')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.setheading"&gt;&lt;code&gt;setheading(90)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('cir 10')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.circle"&gt;&lt;code&gt;circle(10)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('undo')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.undo"&gt;&lt;code&gt;undo()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('bf')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.begin_fill"&gt;&lt;code&gt;begin_fill()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('ef')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.end_fill"&gt;&lt;code&gt;end_fill()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('reset')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.reset"&gt;&lt;code&gt;reset()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('sleep 5')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/time.html#time.sleep"&gt;&lt;code&gt;time.sleep(5)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('n 10')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.setheading"&gt;&lt;code&gt;setheading(90)&lt;/code&gt;&lt;/a&gt; &lt;code&gt;;&lt;/code&gt; &lt;a href="https://docs.python.org/3/library/turtle.html#turtle.forward"&gt;&lt;code&gt;forward(10)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('s 10')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.setheading"&gt;&lt;code&gt;setheading(270)&lt;/code&gt;&lt;/a&gt; &lt;code&gt;;&lt;/code&gt; &lt;a href="https://docs.python.org/3/library/turtle.html#turtle.forward"&gt;&lt;code&gt;forward(10)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('e 10')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.setheading"&gt;&lt;code&gt;setheading(180)&lt;/code&gt;&lt;/a&gt; &lt;code&gt;;&lt;/code&gt; &lt;a href="https://docs.python.org/3/library/turtle.html#turtle.forward"&gt;&lt;code&gt;forward(10)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('w 10')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.setheading"&gt;&lt;code&gt;setheading(0)&lt;/code&gt;&lt;/a&gt; &lt;code&gt;;&lt;/code&gt; &lt;a href="https://docs.python.org/3/library/turtle.html#turtle.forward"&gt;&lt;code&gt;forward(10)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('nw 10')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.setheading"&gt;&lt;code&gt;setheading(135)&lt;/code&gt;&lt;/a&gt; &lt;code&gt;;&lt;/code&gt; &lt;a href="https://docs.python.org/3/library/turtle.html#turtle.forward"&gt;&lt;code&gt;forward(10)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('ne 10')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.setheading"&gt;&lt;code&gt;setheading(45)&lt;/code&gt;&lt;/a&gt; &lt;code&gt;;&lt;/code&gt; &lt;a href="https://docs.python.org/3/library/turtle.html#turtle.forward"&gt;&lt;code&gt;forward(10)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('sw 10')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.setheading"&gt;&lt;code&gt;setheading(225)&lt;/code&gt;&lt;/a&gt; &lt;code&gt;;&lt;/code&gt; &lt;a href="https://docs.python.org/3/library/turtle.html#turtle.forward"&gt;&lt;code&gt;forward(10)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('se 10')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.setheading"&gt;&lt;code&gt;setheading(315)&lt;/code&gt;&lt;/a&gt; &lt;code&gt;;&lt;/code&gt; &lt;a href="https://docs.python.org/3/library/turtle.html#turtle.forward"&gt;&lt;code&gt;forward(10)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('done')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.done"&gt;&lt;code&gt;done()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('bye')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.bye"&gt;&lt;code&gt;bye()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('exitonelick')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.exitonclick"&gt;&lt;code&gt;exitonclick()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('eoc')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.exitonclick"&gt;&lt;code&gt;exitonclick()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('t 100 0')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.tracer"&gt;&lt;code&gt;tracer(100, 0)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('u')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.update"&gt;&lt;code&gt;update()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('hide')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.hideturtle"&gt;&lt;code&gt;hide()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('show')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.showturtle"&gt;&lt;code&gt;show()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('dot 5')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.dot"&gt;&lt;code&gt;dot(5)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('cs 42')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.clearstamp"&gt;&lt;code&gt;clearstamp(42)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('css')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.clearstamps"&gt;&lt;code&gt;clearstamps()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('css 10')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.clearstamps"&gt;&lt;code&gt;clearstamps(10)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('degrees')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.degrees"&gt;&lt;code&gt;degrees()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('radians')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.radians"&gt;&lt;code&gt;radians()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('spd 5')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.speed"&gt;&lt;code&gt;speed(5)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('spd fastest')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/turtle.html#turtle.speed"&gt;&lt;code&gt;speed('fastest')&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sc('# this is a comment')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;# this is a comment&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;sc(skip=True)&lt;/code&gt; keyword argument skips all the shortcuts. Consider this the same as commenting out the &lt;code&gt;sc()&lt;/code&gt; call.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;sc('sleep 5')&lt;/code&gt; shortcut exists to call the &lt;code&gt;time.sleep()&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;You can pass multiple strings to &lt;code&gt;sc()&lt;/code&gt;. For example, &lt;code&gt;sc('f 100', 'r 45', 'f 100')&lt;/code&gt; is equivalent to &lt;code&gt;sc('f 100, r 45, f 100')&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;'pc'&lt;/code&gt;, &lt;code&gt;'fc'&lt;/code&gt;, and &lt;code&gt;'bc'&lt;/code&gt; shortcuts for pen color, fill color, and background color can take a color argument as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A color name, such as &lt;code&gt;'red'&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Three 0 to 255 integer values, such as &lt;code&gt;'255 0 0&lt;/code&gt; (turtle.py's &lt;a href="https://docs.python.org/3/library/turtle.html#turtle.colormode"&gt;color mode&lt;/a&gt; must be set to 255)&lt;/li&gt;
&lt;li&gt;Three 0.0 to 1.0 float values, such as &lt;code&gt;'1.0 0.0 0.0&lt;/code&gt; (turtle.py's &lt;a href="https://docs.python.org/3/library/turtle.html#turtle.colormode"&gt;color mode&lt;/a&gt; must be set to 255)&lt;/li&gt;
&lt;li&gt;A hexadecimal RGB code, such as &lt;code&gt;'#FF0000'&lt;/code&gt; (the leading # hashtag is required)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The cardinal directions shortcuts change &lt;em&gt;both&lt;/em&gt; the heading and position of the turtle.&lt;/p&gt;
&lt;h2&gt;scs (shortcut strings) and psc (print shortcuts)&lt;/h2&gt;
&lt;p&gt;If you pass the shortcut string to &lt;code&gt;scs()&lt;/code&gt;, instead of carrying out the turtle instructions, it will return a string of the original turtle.py function calls the &lt;code&gt;sc()&lt;/code&gt; function would make. For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;turtlesc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;scs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bf, f 100, r 90, f 100, r 90, ef&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;begin_fill()&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;forward(100)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;right(90)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;forward(100)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;right(90)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;end_fill()&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you'd like to print these out for debugging purposes, you can call &lt;code&gt;psc()&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;turtlesc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;psc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bf, f 100, r 90, f 100, r 90, ef&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;begin_fill&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;end_fill&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Comments&lt;/h2&gt;
&lt;p&gt;You can put comments inside the shortcut string. This is especially helpful when passing multi-line strings to &lt;code&gt;sc()&lt;/code&gt;. Comments begin with a # hashtag character and go up to the next comma (which marks the start of the next shortcut). For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;turtlesc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;f 100&lt;/span&gt;
&lt;span class="s1"&gt;          r 100&lt;/span&gt;
&lt;span class="s1"&gt;          f 100, # This is a comment&lt;/span&gt;
&lt;span class="s1"&gt;          # Another comment&lt;/span&gt;
&lt;span class="s1"&gt;          r 100, f 100&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Skipping Shortcuts&lt;/h2&gt;
&lt;p&gt;If you want to temporarily disable a call to &lt;code&gt;sc()&lt;/code&gt;, you could comment it out like any other Python instruction. However, this is difficult for &lt;code&gt;sc()&lt;/code&gt; calls that have multi-line string arguments. Instead, add the &lt;code&gt;skip=True&lt;/code&gt; keyword argument to the call. For example, this instruction does absolutely nothing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;turtlesc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;f 100&lt;/span&gt;
&lt;span class="s1"&gt;      r 100&lt;/span&gt;
&lt;span class="s1"&gt;      f 100,&lt;/span&gt;
&lt;span class="s1"&gt;      r 100, f 100&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;skip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you want to add these shortcuts back in, remove the &lt;code&gt;skip=True&lt;/code&gt; text.&lt;/p&gt;
&lt;h2&gt;Recording Turtle Function Calls&lt;/h2&gt;
&lt;p&gt;If you want to collect the turtle.py function calls for all your &lt;code&gt;sc()&lt;/code&gt; calls, add a call to &lt;code&gt;begin_recording()&lt;/code&gt; to the start of your program. When you call &lt;code&gt;end_recording()&lt;/code&gt; at the end, it returns a list of strings of turtle.py function calls. For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;turtlesc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;begin_recording&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;f 100, r 90, f 100&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; r 90,f 100, r 90, f 100&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;end_recording&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;f 100&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; r 90&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; f 100&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; r 90&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;f 100&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; r 90&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; f 100&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Interactive Drawing Mode&lt;/h2&gt;
&lt;p&gt;You can draw with the turtle like an &lt;a href="https://en.wikipedia.org/wiki/Etch_A_Sketch"&gt;etch a sketch&lt;/a&gt; by using TurtleSC's interactive mode. Make one of the following function calls:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;turtlesc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Use cardinal direction style (the default): WASD moves up/down, left/right&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;turn&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# WASD moves forward/backward, turn counterclockwise/clockwise&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;isometric&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# WASD moves up/down, and the AD, QE keys move along a 30 degree isometric plane&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For all styles, the O and L key move the pen up and down, respectively. The H key moves the cursor home (to 0, 0) and clears the window. The U key is an undo. A mouse click moves the turtle with the pen up (and puts the pen back down if it had been down before.)&lt;/p&gt;
&lt;p&gt;Details of each movement style:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;'cardinal'&lt;/strong&gt; style has the WASD and arrow keys move up/down/left/right. The QE, ZC keys also move diagonally. Pass the &lt;code&gt;length&lt;/code&gt; argument for how long each movement should be. Diagonal movements are shortened so that a diagonal up-right movement goes to the same position as an up movement and a right movement.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;'turn'&lt;/strong&gt; style has the WASD and arrow keys move forward/backward and &lt;em&gt;turn&lt;/em&gt; left and right relative to its current heading. Each turn is 90 degrees. However, you can also add a number like &lt;strong&gt;'turn45'&lt;/strong&gt; or &lt;strong&gt;'turn30'&lt;/strong&gt; to have it turn by a different amount for each key press.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;'isometric'&lt;/strong&gt; style has the WA keys move up/down, but the QE, AD keys move &lt;a href="https://duckduckgo.com/?q=isometric+tile+game&amp;amp;t=ffab&amp;amp;iar=images"&gt;diagonally along an isometric plane&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cardinal:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/turtlesc/refs/heads/main/style-cardinal.png" /&gt;&lt;/p&gt;
&lt;p&gt;Turn:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/turtlesc/refs/heads/main/style-turn.png" /&gt;&lt;/p&gt;
&lt;p&gt;Isometric:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/asweigart/turtlesc/refs/heads/main/style-isometric.png" /&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>What is the difference between GitHub vs. GitLab vs Alternatives?</title><link href="https://inventwithpython.com/blog/github-vs-gitlab.html" rel="alternate"></link><published>2025-06-29T10:01:00-04:00</published><updated>2025-06-29T10:01:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-06-29:/blog/github-vs-gitlab.html</id><summary type="html">&lt;p&gt;This article and its content were written, reviewed, and verified by a human, Al Sweigart. In my research I found that most "GitHub vs GitLab" articles are AI slop containing false or outdated information. I wrote this article with links to actual documentation pages, and not just copying what every other article on this topic says. For those who have heard of this "Git stuff," here's a summary of the differences and details. My hot take: &lt;strong&gt;For the needs of most individual or small group users, they're basically the same.&lt;/strong&gt; GitLab seems to be more oriented towards self-hosting and custom configuration. &lt;a href="https://en.wikipedia.org/wiki/Comparison_of_source-code-hosting_facilities"&gt;Wikipedia has an extensive comparison list of source code repo hosting servers.&lt;/a&gt;&lt;br&gt;&lt;img src="/blogstatic/github-vs-gitlab.webp"&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;This article and its content were written, reviewed, and verified by a human, Al Sweigart. In my research I found that most "GitHub vs GitLab" articles are AI slop containing false or outdated information. I wrote this article with links to actual documentation pages, and not just copying what every other article on this topic says. For those who have heard of this "Git stuff," here's a summary of the differences and details. My hot take: &lt;strong&gt;For the needs of most individual or small group users, they're basically the same.&lt;/strong&gt; GitLab seems to be more oriented towards self-hosting and custom configuration. &lt;a href="https://en.wikipedia.org/wiki/Comparison_of_source-code-hosting_facilities"&gt;Wikipedia has an extensive comparison list of source code repo hosting servers.&lt;/a&gt;&lt;br&gt;&lt;img src="/blogstatic/github-vs-gitlab.webp"&gt;&lt;/p&gt;
&lt;h2&gt;GitHub, GitLab, and Alternatives&lt;/h2&gt;
&lt;p&gt;While Git is the version control software you install on your computer to create and manage Git repositories, there are several websites that can host your Git repos online (i.e., "in the cloud."). There are several popular Git servers that are free or have free-tier options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/GitHub"&gt;GitHub&lt;/a&gt; (bought and owned by Microsoft)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/GitLab"&gt;GitLab&lt;/a&gt; (developed and owned by GitLab Inc.)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/GNU_Savannah"&gt;GNU Savannah&lt;/a&gt; (developed and owned by the Free Software Foundation)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Bitbucket"&gt;Bitbucket&lt;/a&gt; (bought and owned by Atlassian)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Gitea"&gt;Gitea&lt;/a&gt; (fork of Gogs, crowdfunded)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Codeberg"&gt;Codeberg&lt;/a&gt; (open source developed by German non-profit Codeberg e.V.)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/SourceForge"&gt;SourceForge&lt;/a&gt; (one of the most longstanding services, aimed at open source projects)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://azure.microsoft.com/en-us/products/devops/repos/"&gt;Azure Repos&lt;/a&gt; is also owned by Microsoft and is not free (you get $200 in credit for free to use in the first 30 days.)&lt;/p&gt;
&lt;p&gt;These are "cloud hosted" servers; you sign up for an account and they run the online Git server for you. There is also &lt;a href="https://en.wikipedia.org/wiki/Self-hosting_(web_services)"&gt;"self-hosted"&lt;/a&gt; or "self-managed", where you run the Git server software on an online host yourself and manage/admin it yourself. GitHub-like servers on your own internet-connected servers. You have more control with self-hosting options, but you must pay for hosting and admin the server yourself. (If something happens, you need to fix it.) This article only discusses cloud hosted Git servers.&lt;/p&gt;
&lt;h2&gt;Summary of GitHub and GitLab&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;GitHub&lt;/th&gt;
&lt;th&gt;GitLab&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pricing&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/pricing"&gt;Free and paid options&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://about.gitlab.com/pricing/"&gt;Free and paid options&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hosting&lt;/td&gt;
&lt;td&gt;Cloud with optionally &lt;a href="https://docs.github.com/en/actions/concepts/runners/communicating-with-self-hosted-runners"&gt;self-hosted GitHub Action runners&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Cloud &amp;amp; &lt;a href="https://docs.gitlab.com/install/"&gt;self-hosting option&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI/CD&lt;/td&gt;
&lt;td&gt;Yes, through &lt;a href="https://en.wikipedia.org/wiki/GitHub#GitHub_Actions"&gt;GitHub Actions&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Yes, through &lt;a href="https://docs.gitlab.com/runner/install/"&gt;GitLab Runners&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permissions&lt;/td&gt;
&lt;td&gt;Yes, with fine-grain permissions, roles, and &lt;a href="https://docs.github.com/en/organizations/managing-user-access-to-your-organizations-repositories/managing-repository-roles/repository-roles-for-an-organization"&gt;"teams"&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Yes, with fine-grained &lt;a href="https://docs.gitlab.com/user/permissions/"&gt;permissions, roles, and groups.&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project Management&lt;/td&gt;
&lt;td&gt;Issue tracker/discussion forums/etc&lt;/td&gt;
&lt;td&gt;Same&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-Hosted Licensing&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.github.com/en/enterprise-cloud@latest/billing/managing-your-license-for-github-enterprise/about-licenses-for-github-enterprise"&gt;Proprietary&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.gitlab.com/development/licensing/"&gt;MIT-licensed community edition and proprietary enterprise edition&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;What Reddit Says&lt;/h2&gt;
&lt;p&gt;There are several posts on the &lt;a href="https://www.reddit.com/r/devops/"&gt;r/DevOps subreddit&lt;/a&gt; where folks &lt;a href="https://www.reddit.com/r/devops/search?q=%22github%22+%22gitlab%22&amp;amp;restrict_sr=on"&gt;discuss GitHub vs GitLab&lt;/a&gt;. Here are the ones best conversations I found:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/devops/comments/13e4eoj/github_vs_gitlab/"&gt;GitHub vs Gitlab&lt;/a&gt; (May 2023)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/devops/comments/105a2bn/gitlab_ci_vs_jenkins_vs_github_actions/"&gt;Gitlab CI vs Jenkins vs GitHub Actions&lt;/a&gt; (Jan 2023)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/devops/comments/oy06no/full_suite_comparison_bitbucket_vs_gitlab_vs/"&gt;Full suite comparison: BitBucket vs Gitlab vs Github?&lt;/a&gt; (Aug 2021)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/devops/comments/l0nkiy/what_does_gitlab_offer_that_github_doesnt/"&gt;What does GitLab offer that GitHub doesn't? &lt;/a&gt; (Jan 2021)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have objections or corrections to the information in this article, contact &lt;a href="mailto:al@inventwithpython.com"&gt;al@inventwithpython.com&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Procedurally Generating a Tic-Tac-Toe Zine with Python</title><link href="https://inventwithpython.com/blog/tic-tac-toe-zine.html" rel="alternate"></link><published>2025-06-25T10:01:00-04:00</published><updated>2025-06-25T10:01:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2025-06-25:/blog/tic-tac-toe-zine.html</id><summary type="html">&lt;p&gt;At PyCon 2025, I handed out a pocket-sized zine that lets you play a procedurally generated choose-your-own-adventure version of tic-tac-toe. The zine itself is available as a &lt;a href="https://github.com/asweigart/tictactoe_zine/blob/main/tictactoe_zine_linear.pdf"&gt;PDF for viewing on your computer&lt;/a&gt; and a &lt;a href="https://github.com/asweigart/tictactoe_zine/blob/main/tictactoe_zine.pdf"&gt;PDF for double-sided printing&lt;/a&gt;. Here's how I made it using Python.&lt;br&gt;&lt;img src="/blogstatic/tictactoezine2.webp" style="max-width: 100%"&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;At PyCon 2025, I handed out a pocket-sized zine that lets you play a procedurally generated choose-your-own-adventure version of tic-tac-toe. The zine itself is available as a &lt;a href="https://github.com/asweigart/tictactoe_zine/blob/main/tictactoe_zine_linear.pdf"&gt;PDF for viewing on your computer&lt;/a&gt; and a &lt;a href="https://github.com/asweigart/tictactoe_zine/blob/main/tictactoe_zine.pdf"&gt;PDF for double-sided printing&lt;/a&gt;. Here's how I made it using Python.&lt;br&gt;&lt;img src="/blogstatic/tictactoezine2.webp" style="max-width: 100%"&gt;&lt;/p&gt;
&lt;h1&gt;What are Zines?&lt;/h1&gt;
&lt;p&gt;I never really liked zines.&lt;/p&gt;
&lt;p&gt;Zines (&lt;a href="https://en.wikipedia.org/wiki/Zine"&gt;pronounced "zeens", short for "magazines"&lt;/a&gt;) are small, self-published print booklets made by individuals or small groups, often about indie or non-mainstream topics. In the 80s and 90s when photocopying was accessible but the internet was not, so zines made sense as a way to do custom small-scale publishing. But once social media and smartphones took off, I thought of zines as an anachronism at best. Why not just have a website?&lt;/p&gt;
&lt;p&gt;In tech, &lt;a href="https://wizardzines.com/"&gt;Julia Evans' Wizard Zines&lt;/a&gt; do a great job of explaining various technical subjects. But I kept thinking, "Couldn't this just be a website?" What changed my mind and made me see the value in zines was:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Zines can be a physical gift or souvenir that don't take up much space.&lt;/li&gt;
&lt;li&gt;A zine will persist to be picked up later, unlike a forgotten browser tab or bookmark.&lt;/li&gt;
&lt;li&gt;There are so many possibilities for zines with the advent of laser printers, QR codes, and procedural-generated content.&lt;/li&gt;
&lt;li&gt;They're a fun, crafty little thing you can make for cheap.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the future I'll write a blog post about designing zines in the 2020s, but for now I'd like to show you how I made this zine.&lt;/p&gt;
&lt;h1&gt;Choose Your Own Adventure Books&lt;/h1&gt;
&lt;p&gt;If you're like me and... of a certain age... you may remember &lt;a href="https://en.wikipedia.org/wiki/Choose_Your_Own_Adventure"&gt;Choose Your Own Adventure books&lt;/a&gt;. These books would tell a story, then prompt the reader to choose what happens next based on what page they turn to. "If you want to explore the old cave, turn to page 37. If you want to go back to the motel, turn to page 45."&lt;/p&gt;
&lt;p&gt;You could think of a tic-tac-toe game as a Choose Your Own Adventure book with 362,880 (or 9 x 8 x 7 x 6 x 5 x 4 x 3 x 2 x 1) pages. You start on page 1 with a blank tic-tac-toe board and there are nine possible places the first player can move. Each of those nine boards in turn have eight possible moves for the second player to go to.&lt;/p&gt;
&lt;p&gt;362,880 is a lot, but much smaller than other games like chess. And the true number is much smaller: some boards are duplicates, games can end before the entire board is filled, and the human reader's move and the zine's responding move can be collapsed into one board. And you can fit multiple tables on each page.&lt;/p&gt;
&lt;p&gt;My zine was going to be folded such that each zine page was one-eighth of a US letter-sized page. (This is perfect for fitting into a pants pocket.) Printing duplex (i.e., on both sides of the sheet), I could get 32 zine pages for two sheets of paper. Experimenting with my &lt;a href="https://duckduckgo.com/?t=ffab&amp;amp;q=brother+HL-l2460dw&amp;amp;ia=web"&gt;Brother HL-l2460DW laser printer&lt;/a&gt;, I could fit 24 tic-tac-toe tables per zine page and still have it mostly readable. (Your phone's camera can be a magnifying lens if the tables are too small.)&lt;/p&gt;
&lt;p&gt;My zines could have 768 tic-tac-toe boards, but I'd want to use some of the pages for the front &amp;amp; back cover, instructions, and the centerfold. Really, I would have 25 pages available for 600 (24 x 25) tic-tac-toe boards.&lt;/p&gt;
&lt;p&gt;I wrote Python code to create the tic-tac-toe boards and then do the layout for the PDFs.&lt;/p&gt;
&lt;h1&gt;Procedurally Generated Tic-Tac-Toe&lt;/h1&gt;
&lt;p&gt;The code that generates the zine is &lt;a href="https://github.com/asweigart/tictactoe_zine/blob/main/make_ttt_zine.py"&gt;make_ttt_zine.py&lt;/a&gt; in my &lt;a href="https://github.com/asweigart/tictactoe_zine"&gt;tictactoe_zine&lt;/a&gt; repo.&lt;/p&gt;
&lt;p&gt;I've created simple tic-tac-toe programs for &lt;a href="https://inventwithpython.com/bigbookpython/project76.html"&gt;The Big Book of Small Python Projects&lt;/a&gt; and &lt;a href="https://inventwithpython.com/invent4thed/chapter10.html"&gt;Invent Your Own Computer Games with Python&lt;/a&gt; before. I also used tic-tac-toe &lt;a href="https://inventwithpython.com/beyond/chapter15.html"&gt;as a way to explain object-oriented programming in Beyond the Basic Stuff with Python&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/asweigart/tictactoe_zine/blob/main/make_ttt_zine.py"&gt;make_ttt_zine.py&lt;/a&gt; first creates the data structures for the tic-tac-toe boards (&lt;a href="https://github.com/asweigart/tictactoe_zine/blob/main/make_ttt_zine.py"&gt;Full code in the git repo&lt;/a&gt;):&lt;/p&gt;
&lt;textarea rows="10" style="width: 100%; font-family: monospace;"&gt;
"""
Tic Tac Toe Zine Maker by Al Sweigart 2025

This program creates a `boards` list of TTT board dictionaries. The dictionaries have keys of
space strings (e.g. 'TL' for top-left). The value is either 'X', 'O', or a number. The number
is an index in `boards` to the board if the human player moves on that space (along with the
zine's response). The `boards` list contains a sort of "multiverse" of all possible TTT games.

For example, run `python -i make_ttt_zine.py` to open an interactive shell after the program
finishes. The pb() function prints human-readable form of a TTT board dictionary. Call `pb(0)` to
look at the first board. Each space has an index to examine if you make that move. E.g. if `pb(0)`
shows a board with 204 in the top-left space, `pb(204)` shows the board after you move on that
space and the zine's response move.

Git repo of this code is at: https://github.com/asweigart/tictactoe_zine
Neat website on TTT strategy: https://tictactoefree.com/tips/how-to-win-in-tic-tac-toe
"""

import random, copy, io, os
from PIL import Image, ImageDraw, ImageFont
from pypdf import PdfWriter, PdfReader
random.seed(44)  # Change this to generate other games. 


OUTPUT_PDF_FILENAME = 'tictactoe_zine.pdf'
ZINE_BLUNDER_RATE = 0.25  # 0.0 makes best moves. 1.0 always makes random moves.

# Constants for the Tic Tac Toe board:
ALL_SPACES = 'TL TM TR ML MM MR BL BM BR'.split()  # Top-Left, Top-Middle, Top-Right, etc.
HUMAN_MARK = 'X'
ZINE_MARK = 'O'
EMPTY = ' '

def get_blank_board():
    """Return a new dictionary with all spaces set to None."""
    board = {}
    for move in ALL_SPACES:
        board[move] = None  # None means the space is empty.
    return board

def is_winner(player, board):
    """Returns True if `player` has three in a row on `board`."""
    b = board
    # Check all possible winning combinations:
    return (b['TL'] == b['TM'] == b['TR'] == player) or \
           (b['ML'] == b['MM'] == b['MR'] == player) or \
           (b['BL'] == b['BM'] == b['BR'] == player) or \
           (b['TL'] == b['ML'] == b['BL'] == player) or \
           (b['TM'] == b['MM'] == b['BM'] == player) or \
           (b['TR'] == b['MR'] == b['BR'] == player) or \
           (b['TL'] == b['MM'] == b['BR'] == player) or \
           (b['TR'] == b['MM'] == b['BL'] == player)

def is_full(board):
    """Returns True if the board is full with no winners."""
    for space in ALL_SPACES:
        if board[space] not in (HUMAN_MARK, ZINE_MARK):
            return False  # If any space is not occupied, the board is not full.
    # If all spaces are occupied, return True:
    return True

def get_zine_move(board):
    # If the zine is making a blunder, just make a random move instead of the best move:
    if random.random() &lt; ZINE_BLUNDER_RATE:
        free_spaces = []
        for space in ALL_SPACES:
            if board[space] is None:
                free_spaces.append(space)
        return random.choice(free_spaces)

    # Stop potential forking manuever by playing the opposite corner:
    forkable_board = get_blank_board()
    forkable_board['TL'] = HUMAN_MARK
    if board == forkable_board:
        return 'BR'
    forkable_board = get_blank_board()
    forkable_board['TR'] = HUMAN_MARK
    if board == forkable_board:
        return 'BL'
    forkable_board = get_blank_board()
    forkable_board['BL'] = HUMAN_MARK
    if board == forkable_board:
        return 'TR'
    forkable_board = get_blank_board()
    forkable_board['BR'] = HUMAN_MARK
    if board == forkable_board:
        return 'TL'
    forkable_board = get_blank_board()
    forkable_board['MM'] = HUMAN_MARK
    if board == forkable_board:
        return 'TL'  # Any corner will do to respond to the center-first move.

    # Make a winning move if possible:
    for space in ALL_SPACES:
        if board[space] is None:
            # Create a test board with the zine's mark in the current space:
            test_board = copy_marks(board)
            test_board[space] = ZINE_MARK
            # Check if this move would win the game for the zine:
            if is_winner(ZINE_MARK, test_board):
                return space

    # Make a blocking move if necessary:
    for space in ALL_SPACES:
        if board[space] is None:
            # Create a test board with the human's mark in the current space:
            test_board = copy_marks(board)
            test_board[space] = HUMAN_MARK
            # Check if this move would win the game for the human, and block it:
            if is_winner(HUMAN_MARK, test_board):
                return space

    # Make a center move if available:
    if board['MM'] is None:
        return 'MM'

    # Make a corner move if available:
    free_corners = []
    for space in ('TL', 'TR', 'BL', 'BR'):
        if board[space] is None:
            free_corners.append(space)
    # If there are any free corners, pick one at random:
    if len(free_corners) &gt; 0:
        return random.choice(free_corners)

    # Make a side move if available:
    free_sides = []
    for space in ('TM', 'BM', 'ML', 'MR'):
        if board[space] is None:
            free_sides.append(space)
    # If there are any free sides, pick one at random:
    if len(free_sides) &gt; 0:
        return random.choice(free_sides)

    assert False  # Something is wrong and `board` is full.

def copy_marks(board):
    """Return a new board that only has the X and O marks copied. Do not copy board indexes."""
    new_board = get_blank_board()  # Start with a blank board.
    for space in ALL_SPACES:
        # Copy only the X &amp; O marks, not the indexes:
        if board[space] == HUMAN_MARK or board[space] == ZINE_MARK:
            new_board[space] = board[space]
    return new_board

def get_board_text(idx):
    """Return a list of ASCII art strings that represent the board at index `idx`."""
    b = copy.copy(boards[idx])
    for space in ALL_SPACES:
        b[space] = str(b[space]).center(3)
    VERTICAL = '   |   |   '
    HORIZONTAL = '---+---+---'
    t = []
    #t.append([VERTICAL])
    t.append('%s|%s|%s' % (b['TL'], b['TM'], b['TR']))
    t.append(HORIZONTAL) #t.extend((VERTICAL, HORIZONTAL, VERTICAL))
    t.append('%s|%s|%s' % (b['ML'], b['MM'], b['MR']))
    if is_winner(HUMAN_MARK, boards[idx]):
        t.append('-YOU WIN!--') #t.extend((VERTICAL, '-YOU WIN!--', VERTICAL))
    elif is_winner(ZINE_MARK, boards[idx]):
        t.append('-ZINE WON!-') #t.extend((VERTICAL, '-ZINE WON!-', VERTICAL))
    elif is_full(boards[idx]):
        t.append('---+TIE+---') #t.extend((VERTICAL, '---+TIE+---', VERTICAL))
    else:
        t.append(HORIZONTAL) #t.extend((VERTICAL, HORIZONTAL, VERTICAL))
    t.append('%s|%s|%s' % (b['BL'], b['BM'], b['BR']))
    #t.append(VERTICAL)
    t.append('    #' + str(idx).ljust(3) + '   ')

    # Replace the characters with box-drawing characters:
    for i in range(len(t)):
        t[i] = t[i].replace('-', chr(9472)).replace('|', chr(9474)).replace('+', chr(9532))
    return t

def pb(idx):
    """Print the board at index `idx` in a human-readable format. This is for debugging."""
    print('\n'.join(get_board_text(idx)))

# Create the starting board and initialize the game state:
boards = [get_blank_board()]
now_working_on_idx = 0  # The index of the board we are currently working on.
# The number of wins/losses/ties is tracked for debugging purposes:
wins = 0
losses = 0
ties = 0

while now_working_on_idx &lt; len(boards):
    current_board = boards[now_working_on_idx]

    # Check if the current board is a winner:
    if is_winner(HUMAN_MARK, current_board) or is_winner(ZINE_MARK, current_board):
        if is_winner(HUMAN_MARK, current_board):
            wins += 1
        if is_winner(ZINE_MARK, current_board):
            losses += 1
        # To finish the current board, change all None spaces to EMPTY:
        for space in ALL_SPACES:
            if current_board[space] is None:
                current_board[space] = EMPTY
        now_working_on_idx += 1  # Start work on next tic tac toe board.
        continue

    # Check if the current board is full:
    if is_full(current_board):
        ties += 1
        now_working_on_idx += 1  # Start work on next tic tac toe board.
        continue

    # Add new boards with every possible move for the current board:
    for space in ALL_SPACES:
        if current_board[space] is None:
            new_board = copy_marks(current_board)
            new_board[space] = HUMAN_MARK

            if not (is_winner(HUMAN_MARK, new_board) or is_full(new_board)):
                # The zine makes a move:
                new_board[get_zine_move(new_board)] = ZINE_MARK

            boards.append(new_board)  # Add the new board to the list.
            current_board[space] = len(boards) - 1  # Set the space to point to the new board.
    now_working_on_idx += 1

# Print the summary of the initial set of tic-tac-toe boards:
print('Number of boards:', len(boards), 'Wins:', wins, 'Losses:', losses, 'Ties:', ties)
&lt;/textarea&gt;

&lt;p&gt;The program creates a list in &lt;code&gt;boards&lt;/code&gt; with a single blank starting board. Then for each board in the list, the main part of the program adds additional boards for each possible reader move followed by the zine's move. These new boards are appended to the end of the list. The index of the new board is added to the previous board that leads to it. Eventually, no new boards are appended as they are all finished with either a win, loss, or tie.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;get_zine_move()&lt;/code&gt; function that picks the zine's move, the &lt;code&gt;ZINE_BLUNDER_RATE&lt;/code&gt; value causes the zine to sometimes make a random move instead of the best move. Perfection is boring; it's more amusing for the zine to sometimes make mistakes.&lt;/p&gt;
&lt;p&gt;Next the boards are shuffled around randomly, updating their indexes to stay consistent. Then, duplicate boards are removed (&lt;a href="https://github.com/asweigart/tictactoe_zine/blob/main/make_ttt_zine.py"&gt;Full code in the git repo&lt;/a&gt;):&lt;/p&gt;
&lt;textarea rows="10" style="width: 100%; font-family: monospace;"&gt;
# Shuffle the boards into a random order, maintaining the consistency of the indexes:
for this_idx, this_board in enumerate(boards):
    if this_idx == 0: continue  # Skip the starting board. We always want to start at index 0.

    # Randomly pick another board:
    other_idx = this_idx
    while other_idx == this_idx:
        other_idx = random.randint(1, len(boards) - 1)

    # Swap the two boards:
    boards[this_idx], boards[other_idx] = boards[other_idx], boards[this_idx]

    # Update the indexes in all of the boards to keep the indexes consistent:
    for j in range(len(boards)):
        for space in ALL_SPACES:
            if boards[j][space] == this_idx:
                boards[j][space] = other_idx
            elif boards[j][space] == other_idx:
                boards[j][space] = this_idx

# Check for duplicate tic-tac-toe boards:
dupes = set()
for i in range(len(boards)):
    for j in range(i + 1, len(boards)):
        if boards[i] == boards[j] and i not in dupes:
            dupes.add(i)

# Remove duplicates from the boards list:
dupes_removed = 0
i = 0
while i &lt; len(boards):
    j = i + 1
    while j &lt; len(boards):
        # NOTE: j will always be larger than i
        if boards[i] == boards[j]:
            # Duplicate found. Delete the jth board:
            del boards[j]
            dupes_removed += 1

            # Re-number all indexes that pointed to the jth board to now point to the ith board:
            for k in range(len(boards)):
                for space in ALL_SPACES:
                    if boards[k][space] == j:
                        boards[k][space] = i

            # Shift all indexes greater than j down one on all boards:
            for k in range(len(boards)):
                for space in ALL_SPACES:
                    if str(boards[k][space]).isdigit() and boards[k][space] &gt; j:
                        boards[k][space] -= 1
        j += 1
    i += 1

print('Duplicates removed:', dupes_removed)
print('Number of boards after removing duplicates:', len(boards))

# If this random seed generated more than 600 boards, then the zine will not fit in 25 pages:
assert len(boards) &lt;= 600, 'ERROR: The number of boards must be under 600 to fit them in 25 zine pages.'

# Write the boards data to a text file for later reference/debugging if needed:
print('Writing `boards` data to ttt_data.txt...')
with open('ttt_data.txt', 'w') as fo:
    fo.write(str(boards))
print('Done.')
&lt;/textarea&gt;

&lt;p&gt;For random seed 44, these steps reduced the number of boards from 1,062 to 580. This fits within the number of pages I wanted for the zine.&lt;/p&gt;
&lt;p&gt;Finally, we need code that generates PNG images of each zine page, which then fits them into one US letter-sized image for each of the four printable pages. These are then put into a single PDF. We also create a PDF that is viewable on a computer instead of meant to be printed.  (&lt;a href="https://github.com/asweigart/tictactoe_zine/blob/main/make_ttt_zine.py"&gt;Full code in the git repo&lt;/a&gt;)&lt;/p&gt;
&lt;textarea rows="10" style="width: 100%; font-family: monospace;"&gt;
# CREATE THE ZINE PDFs:

# Check if the required files exist:
if not os.path.exists('zine_frontcover.png'):
    sys.exit('Missing zine_frontcover.png. Get from https://github.com/asweigart/tictactoe_zine')
if not os.path.exists('zine_backcover.png'):
    sys.exit('Missing zine_backcover.png. Get from https://github.com/asweigart/tictactoe_zine')
if not os.path.exists('zine_centerfold.png'):
    sys.exit('Missing zine_centerfold.png. Get from https://github.com/asweigart/tictactoe_zine')
if not os.path.exists('zine_smartphone.png'):
    sys.exit('Missing zine_smartphone.png. Get from https://github.com/asweigart/tictactoe_zine')
if not os.path.exists('zine_magnifyingglass.png'):
    sys.exit('Missing zine_magnifyingglass.png. Get from https://github.com/asweigart/tictactoe_zine')

# Create the multi-line strings of 4x6 boards for each page:
pages_text = []  # Contains the text for each page of the zine.
pages_im = []  # Contains the PIL Image objects for each page of the zine.
pages_text.append('\n'.join(get_board_text(0)))  # The first page is just the starting board.
SEP = ' '  # The separator between boards on a page.
BOARDS_PER_ROW = 4  # NOTE: If you change this, you must also change the code.
ROWS_PER_PAGE = 6  # NOTE: If you change this, you must also change the code.
ROWS_IN_PRINTED_BOARD = len(get_board_text(0))
FONT_SIZE = 24
LEFT_RIGHT_TRIM = 0
TOP_BOTTOM_TRIM = 0
IM_WIDTH = 825 + LEFT_RIGHT_TRIM  
IM_HEIGHT = 1275 + TOP_BOTTOM_TRIM
SEPARATOR_LINE_COLOR = (200, 200, 200)  # Light gray


# Create the instruction page with the first board:

# Adjust the page text to include instructions and a pointer to the board number:
pages_text[0] = """e.g. to move on top-
right, go to board #147
               %s
""" % (chr(9660)) + \
'\n'.join(['      ' + line for line in pages_text[0].splitlines()]) + \
"""
           %s
           Board number\n\n""" % (chr(9650))

with open('ttt_page_0.txt', 'w') as fo:
    fo.write(pages_text[0])
print('ttt_page_0.txt created.')

im = Image.new('RGB', (IM_WIDTH, IM_HEIGHT), (255, 255, 255))
draw = ImageDraw.Draw(im)
font = ImageFont.truetype('Courier New Bold.ttf', FONT_SIZE * 2)
bbox = draw.textbbox((0, 0), pages_text[0], font=font)
draw.multiline_text(((IM_WIDTH - bbox[2]) // 2, (IM_HEIGHT - bbox[3]) // 2), pages_text[0], fill=(0, 0, 0), font=font)

# Add instructions to the page:
instructions = 'You are X. The zine is O.\nStart on this page at\nboard #0. Pick a space &amp;\ngo to its numbered board.'
bbox = draw.textbbox((0, 0), instructions, font=font)
draw.multiline_text(((IM_WIDTH - bbox[2]) // 2, 40), instructions, fill=(0, 0, 0), font=font)

# Add magnifying lens advice to the page:
help = 'Text too small?\nUse your phone camera\nas a magnifying glass.'
bbox = draw.textbbox((0, 0), help, font=font)
draw.multiline_text(((IM_WIDTH - bbox[2]) // 2, (IM_HEIGHT - bbox[3] - 40)), help, fill=(0, 0, 0), font=font)

# Add the smartphone and magnifying glass images:
im.paste(Image.open('zine_smartphone.png'), (150, IM_HEIGHT - 400))
im.paste(Image.open('zine_magnifyingglass.png'), (350, IM_HEIGHT - 400))

im.save('ttt_page_0.png')
pages_im.append(im)
print('ttt_page_0.png created.')


# The remaining pages are 4x6 tic-tac-toe boards pages:
for i in range(1, len(boards), BOARDS_PER_ROW * ROWS_PER_PAGE):
    page = ''
    for y in range(ROWS_PER_PAGE):
        for row in range(ROWS_IN_PRINTED_BOARD):
            # Create a row of boards for the current page:
            page_row_text = []
            for x in range(BOARDS_PER_ROW):
                # Get the board text for the current board:
                if i + x + (y * BOARDS_PER_ROW) &lt; len(boards):
                    page_row_text.append(get_board_text(i + x + (y * BOARDS_PER_ROW))[row])
            page += SEP.join(page_row_text) + '\n'
        page += '\n'
    # Add the board numbers to the bottom of the board:
    page += ('#' + str(i) + ' to #' + str(i + BOARDS_PER_ROW * ROWS_PER_PAGE - 1)).center(BOARDS_PER_ROW * 12 - 1) + '\n'
    pages_text.append(page)

    # Write the page text to a file for later reference/debugging if needed:
    with open('ttt_page_%s.txt' % (len(pages_text) - 1), 'w') as fo:
        fo.write(page)
    print('ttt_page_%s.txt created.' % (len(pages_text) - 1))

    # Create the PNG image of the page:
    im = Image.new('RGB', (IM_WIDTH, IM_HEIGHT), (255, 255, 255))
    draw = ImageDraw.Draw(im)
    font = ImageFont.truetype('Courier New Bold.ttf', FONT_SIZE)
    bbox = draw.textbbox((0, 0), page, font=font)
    draw.multiline_text(((IM_WIDTH - bbox[2]) // 2, (IM_HEIGHT - bbox[3]) // 2), page, fill=(0, 0, 0), font=font)    
    im.save('ttt_page_%s.png' % (len(pages_text) - 1))
    pages_im.append(im)

    print('ttt_page_%s.png created.' % (len(pages_text) - 1))
print('Done writing page files..')

# Write the pages text to a file for later reference/debugging if needed:
with open('ttt_page_ALL.txt', 'w') as fo:
    fo.write('\n\n\n\n'.join(pages_text))

def add_separator_lines(im):
    """Add the separator lines to the zine sheet images."""
    draw = ImageDraw.Draw(im)
    draw.line([(0, IM_HEIGHT), (IM_WIDTH * 4, IM_HEIGHT)], fill=SEPARATOR_LINE_COLOR)
    draw.line([(IM_WIDTH, 0), (IM_WIDTH, IM_HEIGHT * 2)], fill=SEPARATOR_LINE_COLOR)
    #draw.line([(IM_WIDTH * 2, 0), (IM_WIDTH * 2, IM_HEIGHT * 2)], fill=SEPARATOR_LINE_COLOR)
    draw.line([(IM_WIDTH * 3, 0), (IM_WIDTH * 3, IM_HEIGHT * 2)], fill=SEPARATOR_LINE_COLOR)

# Create the sheet images, which are composed of 4x2 zine pages. Sheets are US letter landscape.

# Sheet 1 front:
im = Image.new('RGB', (IM_WIDTH * 4, IM_HEIGHT * 2), (255, 255, 255))
im.paste(pages_im[0], (0, 0))
im.paste(pages_im[25], (IM_WIDTH, 0))
im.paste(pages_im[2], (IM_WIDTH * 2, 0))
im.paste(pages_im[23], (IM_WIDTH * 3, 0))
im.paste(pages_im[4], (0, IM_HEIGHT))
im.paste(pages_im[21], (IM_WIDTH, IM_HEIGHT))
im.paste(pages_im[6], (IM_WIDTH * 2, IM_HEIGHT))
im.paste(pages_im[19], (IM_WIDTH * 3, IM_HEIGHT))
add_separator_lines(im)
im = im.transpose(Image.ROTATE_270)

im.save('sheet_1_front.png')
print('Sheet 1 front created.')

# Sheet 1 back:
im = Image.new('RGB', (IM_WIDTH * 4, IM_HEIGHT * 2), (255, 255, 255))
im.paste(pages_im[24], (0, 0))
im.paste(pages_im[1], (IM_WIDTH, 0))
im.paste(Image.open('zine_backcover.png'), (IM_WIDTH * 2, 0))
im.paste(Image.open('zine_frontcover.png'), (IM_WIDTH * 3, 0))
im.paste(pages_im[20], (0, IM_HEIGHT))
im.paste(pages_im[5], (IM_WIDTH, IM_HEIGHT))
im.paste(pages_im[22], (IM_WIDTH * 2, IM_HEIGHT))
im.paste(pages_im[3], (IM_WIDTH * 3, IM_HEIGHT))
add_separator_lines(im)
im = im.transpose(Image.ROTATE_90)
im.save('sheet_1_back.png')
print('Sheet 1 back created.')

# Sheet 2 front:
im = Image.new('RGB', (IM_WIDTH * 4, IM_HEIGHT * 2), (255, 255, 255))
im.paste(pages_im[18], (0, 0))
im.paste(pages_im[7], (IM_WIDTH, 0))
im.paste(pages_im[13].transpose(Image.ROTATE_180), (IM_WIDTH * 2, 0))
im.paste(pages_im[12].transpose(Image.ROTATE_180), (IM_WIDTH * 3, 0))
im.paste(pages_im[16], (0, IM_HEIGHT))
im.paste(pages_im[9], (IM_WIDTH, IM_HEIGHT))
im.paste(pages_im[14], (IM_WIDTH * 2, IM_HEIGHT))
im.paste(pages_im[11], (IM_WIDTH * 3, IM_HEIGHT))
add_separator_lines(im)
im = im.transpose(Image.ROTATE_270)
im.save('sheet_2_front.png')
print('Sheet 2 front created.')

# Sheet 2 back:
im = Image.new('RGB', (IM_WIDTH * 4, IM_HEIGHT * 2), (255, 255, 255))
im.paste(Image.open('zine_centerfold.png'), (0, 0))
im.paste(pages_im[8], (IM_WIDTH * 2, 0))
im.paste(pages_im[17], (IM_WIDTH * 3, 0))
im.paste(pages_im[10], (IM_WIDTH * 2, IM_HEIGHT))
im.paste(pages_im[15], (IM_WIDTH * 3, IM_HEIGHT))
add_separator_lines(im)
im = im.transpose(Image.ROTATE_90)
im.save('sheet_2_back.png')
print('Sheet 2 back created.')

# Create the PDF from the sheet images:
def image_to_pdf_bytes(image_path):
    with Image.open(image_path) as im:
        im = im.convert("RGB")  # PDF requires RGB
        buffer = io.BytesIO()
        im.save(buffer, format="PDF", resolution=300.0)  # 300 DPI for high quality
        buffer.seek(0)
        return buffer

def create_pdf_with_images(image_paths, output_pdf):
    writer = PdfWriter()

    for path in image_paths:
        pdf_buffer = image_to_pdf_bytes(path)
        reader = PdfReader(pdf_buffer)
        writer.add_page(reader.pages[0])

    with open(output_pdf, "wb") as f:
        writer.write(f)

create_pdf_with_images(['sheet_1_front.png', 'sheet_1_back.png', 'sheet_2_front.png', 'sheet_2_back.png'], OUTPUT_PDF_FILENAME)
print('Print PDF %s created.' % (OUTPUT_PDF_FILENAME))


# Create the PDF that can be read linearly on a computer:
writer = PdfWriter()
for page_im in [Image.open('zine_frontcover.png'), Image.open('zine_centerfold.png')] + pages_im + [Image.open('zine_backcover.png')]:
    page_im = page_im.convert('RGB')
    buffer = io.BytesIO()
    page_im.save(buffer, format="PDF", resolution=300.0) 
    buffer.seek(0)
    reader = PdfReader(buffer)
    writer.add_page(reader.pages[0])
with open('tictactoe_zine_linear.pdf', "wb") as f:
    writer.write(f)

print('Linear PDF tictactoe_zine_linear created.')
print('Done.')
&lt;/textarea&gt;

&lt;h1&gt;Zine Design and Layout&lt;/h1&gt;
&lt;p&gt;There was a lot of trial and error involved when designing the layout of the zine. I settled on two double-sided sheets, with eight zine pages per sheet side. This would make the zine a total of 32 pages (including the front and back cover.)&lt;/p&gt;
&lt;p&gt;I also chose to spend four zine pages for a centerfold that acts as an advertisement for the zine's git repo. This requires taking care to not cut the four zine pages of the centerfold or accidentally staple them down.&lt;/p&gt;
&lt;p&gt;In the future, I plan on making a Python app for creating these PDFs based on the number of pages and zine page size.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/asweigart/tictactoe_zine"&gt;Full details are available from in the git repo's README.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/tictactoezine3.webp"&gt; &lt;img src="/blogstatic/tictactoezine4.webp"&gt; &lt;img src="/blogstatic/tictactoezine5.webp"&gt; &lt;img src="/blogstatic/tictactoezine6.webp"&gt; &lt;img src="/blogstatic/tictactoezine7.webp"&gt;&lt;/p&gt;
&lt;p&gt;I forgot to bring business cards with me to PyCon, but the zines turned out to be a great substitute since they had my website and contact info on them. I ended up giving away all 150 of them by the third day of the conference. People liked them; they made for a nice souvenir that was different from the usual sticker or flyer (the centerfold was a neat kick, especially).&lt;/p&gt;
&lt;p&gt;This was a great project that ended up taking about four days. It's given me a new interest in making zines and software tools to help others make zines. Keep an eye out for a future blog post on the topic.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Fabric.js Tutorial Part 6</title><link href="https://inventwithpython.com/blog/fabric-js-tutorial-part-6.html" rel="alternate"></link><published>2024-05-04T10:06:00-04:00</published><updated>2024-05-04T10:06:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2024-05-04:/blog/fabric-js-tutorial-part-6.html</id><summary type="html">&lt;p&gt;Part 5 of a tutorial series on the Fabric.js canvas/graphics library for JavaScript, we'll learn how to change the size of shapes by scaling them with the scale() method. We'll also get some more practice drawing in general. We'll draw this forest:&lt;br&gt;&lt;img src="/blogstatic/fabric-forest.webp"&gt;&lt;/p&gt;</summary><content type="html">&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"&gt;&lt;/script&gt;

&lt;p&gt;Part 5 of a tutorial series on the Fabric.js canvas/graphics library for JavaScript, we'll learn how to change the size of shapes by scaling them with the scale() method. We'll also get some more practice drawing in general. We'll draw this forest:&lt;br&gt;&lt;img src="/blogstatic/fabric-forest.webp"&gt;&lt;/p&gt;
&lt;p&gt;This is Part 6 of the Fabric.js tutorial. You can see the previous &lt;a href=""&gt;Part 5&lt;/a&gt; or start at the beginning with &lt;a href=""&gt;Part 1&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this tutorial part, we'll learn how to change the size of shapes by scaling them with the &lt;code&gt;scale()&lt;/code&gt; method. We'll also get some more practice drawing in general. We'll draw this forest:&lt;/p&gt;
&lt;canvas id="forestCanvasId" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;
&lt;script&gt;
let canvasObj = new fabric.Canvas('forestCanvasId');
canvasObj.backgroundColor = '#F1DBC3';

let ground = new fabric.Circle({radius: 350, left: -250, top: 125, fill: '#F8F4E8'});
canvasObj.add(ground);

let branches = new fabric.Triangle({left: 10, top: 100, width: 30, height: 50, fill: '#38755b'});
let trunk = new fabric.Line([25, 140, 25, 160], {stroke: 'brown', strokeWidth: 6, originX: 'center'});
let treeGroup = new fabric.Group([branches, trunk]);

canvasObj.add(treeGroup);

treeGroup.clone(function(clonedObj) { 
  clonedObj.scale(0.8);
  clonedObj.left = 100;
  canvasObj.add(clonedObj);
});

treeGroup.clone(function(clonedObj) { 
  clonedObj.scale(1.1);
  clonedObj.left = 120;
  clonedObj.top = 100;
  canvasObj.add(clonedObj);
});

treeGroup.clone(function(clonedObj) { 
  clonedObj.scale(0.6);
  clonedObj.left = 170;
  clonedObj.top = 110;
  canvasObj.add(clonedObj);
});
&lt;/script&gt;

&lt;p&gt;The full code for this star is at at &lt;a href="https://inventwithpython.com/fabric-js-tutorial/fabric-js-forest.html"&gt;https://inventwithpython.com/fabric-js-tutorial/fabric-js-forest.html&lt;/a&gt; or on &lt;a href="https://jsfiddle.net/asweigart/mfoqvhd6/"&gt;JSFiddle&lt;/a&gt;. Let's examine each part individually:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Fabric.js Drawing a Forest&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;viewport&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cm"&gt;&amp;lt;!-- The HTML &amp;lt;canvas&amp;gt; element: --&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;forestCanvasId&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;border: 1px solid black;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the standard boilerplate HTML that we've used in previous tutorial parts. The &amp;lt;canvas&amp;gt; element's id is &lt;code&gt;"forestCanvasId"&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;canvasObj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Canvas&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;forestCanvasId&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;canvasObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;backgroundColor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#F1DBC3&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ground&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Circle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;125&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#F8F4E8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;canvasObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;ground&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We'll start by setting the background color with &lt;code&gt;canvasObj.backgroundColor = '#F1DBC3';&lt;/code&gt; and then use a large circle for the lighter color ground with &lt;code&gt;let ground = new fabric.Circle({radius: 350, left: -250, top: 125, fill: '#F8F4E8'});&lt;/code&gt; We'll need to add this circle immediately to the canvas object so that it is beneath all of the trees we'll create and add later.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;branches&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Triangle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#38755b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;trunk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Line&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;140&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="cp"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;brown&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strokeWidth&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originX&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;treeGroup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Group&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;trunk&lt;/span&gt;&lt;span class="cp"&gt;]&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;canvasObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;treeGroup&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The tree shape that we'll create is a simple green triangle and a wide brown line shape. To make it easier to clone, these shapes are in a single group and added the group to the canvas.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;treeGroup.clone(function(clonedObj) { 
  clonedObj.scale(0.8);
  clonedObj.left = 100;
  canvasObj.add(clonedObj);
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here we call &lt;code&gt;clone()&lt;/code&gt; to make a copy of the tree group, changing the size to 80% of and the left edge position of the clone before adding it to the canvas.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;treeGroup.clone(function(clonedObj)&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;clonedObj.scale(1.1);
&lt;span class="w"&gt;  &lt;/span&gt;clonedObj.left&lt;span class="w"&gt; &lt;/span&gt;=&lt;span class="w"&gt; &lt;/span&gt;120;
&lt;span class="w"&gt;  &lt;/span&gt;clonedObj.top&lt;span class="w"&gt; &lt;/span&gt;=&lt;span class="w"&gt; &lt;/span&gt;100;
&lt;span class="w"&gt;  &lt;/span&gt;canvasObj.add(clonedObj);
});

treeGroup.clone(function(clonedObj)&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;clonedObj.scale(0.6);
&lt;span class="w"&gt;  &lt;/span&gt;clonedObj.left&lt;span class="w"&gt; &lt;/span&gt;=&lt;span class="w"&gt; &lt;/span&gt;170;
&lt;span class="w"&gt;  &lt;/span&gt;clonedObj.top&lt;span class="w"&gt; &lt;/span&gt;=&lt;span class="w"&gt; &lt;/span&gt;110;
&lt;span class="w"&gt;  &lt;/span&gt;canvasObj.add(clonedObj);
});
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here we finish by making two more trees with the &lt;code&gt;clone()&lt;/code&gt; method and changing their position and size.&lt;/p&gt;
&lt;p&gt;This is the end of the Fabric.js tutorial for now. If you'd like to see more parts to this tutorial, send Al Sweigart a message at &lt;a href="https://mastodon.social/@AlSweigart"&gt;Mastodon&lt;/a&gt; or email at &lt;a href="mailto:al@inventwithpython.com"&gt;al@inventwithpython.com&lt;/a&gt; saying you found the first few parts helpful. Special thanks to &lt;a href="https://bio.link/hunor"&gt;Hunor Márton Borbély&lt;/a&gt; for the &lt;a href="https://svg-tutorial.com/svg/transform"&gt;SVG tutorial&lt;/a&gt; that inspired this Fabric.js tutorial.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Fabric.js Tutorial Part 5</title><link href="https://inventwithpython.com/blog/fabric-js-tutorial-part-5.html" rel="alternate"></link><published>2024-05-04T10:05:00-04:00</published><updated>2024-05-04T10:05:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2024-05-04:/blog/fabric-js-tutorial-part-5.html</id><summary type="html">&lt;p&gt;Part 5 of a tutorial series on the Fabric.js canvas/graphics library for JavaScript, where we learn about grouping, cloning, and rotating shapes.&lt;br&gt;&lt;img src="/blogstatic/fabric-star.webp"&gt;&lt;/p&gt;</summary><content type="html">&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"&gt;&lt;/script&gt;

&lt;p&gt;Part 5 of a tutorial series on the Fabric.js canvas/graphics library for JavaScript, where we learn about grouping, cloning, and rotating shapes.&lt;br&gt;&lt;img src="/blogstatic/fabric-star.webp"&gt;&lt;/p&gt;
&lt;p&gt;This is Part 5 of the Fabric.js tutorial. You can see the previous &lt;a href="https://inventwithpython.com/blog/fabric-js-tutorial-part-4.html"&gt;Part 4&lt;/a&gt; or start at the beginning with &lt;a href="https://inventwithpython.com/blog/fabric-js-tutorial-part-1.html"&gt;Part 1&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this tutorial, we'll draw a star shape. Instead of writing the code to draw every part of the star, we'll instead create one of the five "shards" of the star and then group, clone, and rotate it. The final image will look like this:&lt;/p&gt;
&lt;canvas id="starCanvasId" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;
&lt;script&gt;
let starCompleteCanvasObj = new fabric.Canvas('starCanvasId');

// Create two polygons for a star shard:
let shardHalf1 = new fabric.Polygon([
  {x: 0, y: 0}, {x: 36, y: -50}, {x: 0, y: -100}], {
  left: 100, top: 0, fill: "#EDD8B7"
});

let shardHalf2 = new fabric.Polygon([
  {x: 0, y: 0}, {x: -36, y: -50}, {x: 0, y: -100}], {
  left: 64, top: 0, fill: "#E5C39C"
});

// Create a group of the two shard halves:
let shardGroup = new fabric.Group([shardHalf1, shardHalf2], {
  originX: 'center', originY: 'bottom', centeredRotation: false
});
starCompleteCanvasObj.add(shardGroup);

// Create an array to hold the cloned groups:
let clonedGroups = [];
for (let rotAmount of [0, 72, -72, 144, -144]) {
  shardGroup.rotate(rotAmount);

  shardGroup.clone(function(clonedObj) { 
    starCompleteCanvasObj.add(clonedObj);
    clonedGroups.push(clonedObj);
  });
}
&lt;/script&gt;

&lt;p&gt;The full code for this star is at at &lt;a href="https://inventwithpython.com/fabric-js-tutorial/fabric-js-star-complete.html"&gt;https://inventwithpython.com/fabric-js-tutorial/fabric-js-star-complete.html&lt;/a&gt; or on &lt;a href="https://jsfiddle.net/asweigart/0hz67okx/"&gt;JSFiddle&lt;/a&gt;. Let's examine each part individually:&lt;/p&gt;
&lt;h1&gt;Drawing a Single Shard of a Star&lt;/h1&gt;
&lt;p&gt;Let's start by drawing a single shard out of two polygons:&lt;/p&gt;
&lt;canvas id="singleShardCanvasId" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;
&lt;script&gt;
let singleShardCanvasObj = new fabric.Canvas('singleShardCanvasId');

// Create two polygons for a star shard:
let singleShardHalf1 = new fabric.Polygon([
  {x: 0, y: 0}, {x: 36, y: -50}, {x: 0, y: -100}], {
  left: 100, top: 0, fill: "#EDD8B7"
});

let singleShardHalf2 = new fabric.Polygon([
  {x: 0, y: 0}, {x: -36, y: -50}, {x: 0, y: -100}], {
  left: 64, top: 0, fill: "#E5C39C"
});

singleShardCanvasObj.add(singleShardHalf1);
singleShardCanvasObj.add(singleShardHalf2);
&lt;/script&gt;

&lt;p&gt;Here is the code for drawing this shard:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Fabric.js Drawing Stars&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;viewport&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cm"&gt;&amp;lt;!-- The HTML &amp;lt;canvas&amp;gt; element: --&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;starCanvasId&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;border: 1px solid black;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the standard boilerplate HTML that we've used before, with a &amp;lt;canvas&amp;gt; element that has an id of &lt;code&gt;"starCanvasId"&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;starCanvasObj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Canvas&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;starCanvasId&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;two&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;polygons&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;star&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;shard&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;shardHalf1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Polygon&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="cp"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#EDD8B7&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the polygon for the right-side triangle. The X and Y coordinates I provide here are later shifted by the &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;top&lt;/code&gt; settings for the shape.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;let&lt;span class="w"&gt; &lt;/span&gt;shardHalf2&lt;span class="w"&gt; &lt;/span&gt;=&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;fabric.Polygon([
&lt;span class="w"&gt;  &lt;/span&gt;{x:&lt;span class="w"&gt; &lt;/span&gt;0,&lt;span class="w"&gt; &lt;/span&gt;y:&lt;span class="w"&gt; &lt;/span&gt;0},&lt;span class="w"&gt; &lt;/span&gt;{x:&lt;span class="w"&gt; &lt;/span&gt;-36,&lt;span class="w"&gt; &lt;/span&gt;y:&lt;span class="w"&gt; &lt;/span&gt;-50},&lt;span class="w"&gt; &lt;/span&gt;{x:&lt;span class="w"&gt; &lt;/span&gt;0,&lt;span class="w"&gt; &lt;/span&gt;y:&lt;span class="w"&gt; &lt;/span&gt;-100}],&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt;  &lt;/span&gt;left:&lt;span class="w"&gt; &lt;/span&gt;64,&lt;span class="w"&gt; &lt;/span&gt;top:&lt;span class="w"&gt; &lt;/span&gt;0,&lt;span class="w"&gt; &lt;/span&gt;fill:&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;#E5C39C&amp;quot;
});

starCanvasObj.add(shardHalf1);
starCanvasObj.add(shardHalf2);
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the polygon for the left-side triangle. Just to display these shards on the canvas, we call the &lt;code&gt;add()&lt;/code&gt; method. Notice that you can select these shards individually on the canvas. In the next section, we will group these two polygons into a single shape.&lt;/p&gt;
&lt;h1&gt;Putting the Star Shard Triangles into a Group&lt;/h1&gt;
&lt;p&gt;One way to make Fabric.js shapes easier to work with is by grouping them together. For a complex drawing, you can then rotate, scale, and transform it as a single group. Notice that in the following canvas, you can click on the unified group rather than the individual shards:&lt;/p&gt;
&lt;canvas id="starCanvasId3" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;
&lt;script&gt;
starCanvasObj = new fabric.Canvas('starCanvasId3');

// Create two polygons for a star shard:
shardHalf1 = new fabric.Polygon([
  {x: 0, y: 0}, {x: 36, y: -50}, {x: 0, y: -100}], {
  left: 100, top: 0, fill: "#EDD8B7"
});

shardHalf2 = new fabric.Polygon([
  {x: 0, y: 0}, {x: -36, y: -50}, {x: 0, y: -100}], {
  left: 64, top: 0, fill: "#E5C39C"
});

// Create a group of the two shard halves:
shardGroup = new fabric.Group([shardHalf1, shardHalf2], {
  originX: 'center', originY: 'bottom', centeredRotation: false
});
starCanvasObj.add(shardGroup);
&lt;/script&gt;

&lt;p&gt;Let's look at the parts of this code individually:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Fabric.js Putting Shards into a Group&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;viewport&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cm"&gt;&amp;lt;!-- The HTML &amp;lt;canvas&amp;gt; element: --&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;starCanvasId&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;border: 1px solid black;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;starCanvasObj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;starCanvasId&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

// Create two polygons for a star shard:
let shardHalf1 = new fabric.Polygon([
  {x: 0, y: 0}, {x: 36, y: -50}, {x: 0, y: -100}], {
  left: 100, top: 0, fill: &amp;quot;#EDD8B7&amp;quot;
});

let shardHalf2 = new fabric.Polygon([
  {x: 0, y: 0}, {x: -36, y: -50}, {x: 0, y: -100}], {
  left: 64, top: 0, fill: &amp;quot;#E5C39C&amp;quot;
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the standard boilerplate HTML along with the previous code creating two polygons for the star shard.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;//&lt;span class="w"&gt; &lt;/span&gt;Create&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;group&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;two&lt;span class="w"&gt; &lt;/span&gt;shard&lt;span class="w"&gt; &lt;/span&gt;halves:
let&lt;span class="w"&gt; &lt;/span&gt;shardGroup&lt;span class="w"&gt; &lt;/span&gt;=&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;fabric.Group([shardHalf1,&lt;span class="w"&gt; &lt;/span&gt;shardHalf2],&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt;  &lt;/span&gt;centeredRotation:&lt;span class="w"&gt; &lt;/span&gt;false,&lt;span class="w"&gt; &lt;/span&gt;originX:&lt;span class="w"&gt; &lt;/span&gt;&amp;#39;center&amp;#39;,&lt;span class="w"&gt; &lt;/span&gt;originY:&lt;span class="w"&gt; &lt;/span&gt;&amp;#39;bottom&amp;#39;
});
starCanvasObj.add(shardGroup);

&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To group multiple shapes, whether &lt;code&gt;Polygon&lt;/code&gt;, &lt;code&gt;Rect&lt;/code&gt;, &lt;code&gt;Circle&lt;/code&gt;, &lt;code&gt;Path&lt;/code&gt;, or others, you can pass them in an array as the first argument to &lt;code&gt;fabric.Group()&lt;/code&gt;. In our example, we are passing &lt;code&gt;[shardHalf1, shardHalf2]&lt;/code&gt; to &lt;code&gt;fabric.Group()&lt;/code&gt; to return a new &lt;code&gt;Group&lt;/code&gt; object that contains these two shapes. &lt;/p&gt;
&lt;p&gt;The second argument to this function is a JavaScript object of settings similar to the one you pass to shapes. In our example, we'll set &lt;code&gt;centeredRotation&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; because later when we rotate the group, we want it to rotate around the bottom-center point of the shape instead of the center. To specify the bottom-center point (as opposed to any other point or corner of the shape), we also set &lt;code&gt;originX&lt;/code&gt; to &lt;code&gt;'center'&lt;/code&gt; and &lt;code&gt;originY&lt;/code&gt; to &lt;code&gt;'bottom'&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This sets us up for cloning and rotating the star shard group in the next section.&lt;/p&gt;
&lt;h1&gt;Cloning and Rotating Star Shards&lt;/h1&gt;
&lt;p&gt;To create the complete star shape, we will clone the group we've created, then rotate the shard group in 72 degree increments and clone it again. The end result will be five star shard groups (you can select them in this canvas):&lt;/p&gt;
&lt;canvas id="starCanvasId4" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;
&lt;script&gt;
canvasObj = new fabric.Canvas('starCanvasId4');

// Create two polygons for a star shard:
shardHalf1 = new fabric.Polygon([
  {x: 0, y: 0}, {x: 36, y: -50}, {x: 0, y: -100}], {
  left: 100, top: 0, fill: "#EDD8B7"
});

shardHalf2 = new fabric.Polygon([
  {x: 0, y: 0}, {x: -36, y: -50}, {x: 0, y: -100}], {
  left: 64, top: 0, fill: "#E5C39C"
});

// Create a group of the two shard halves:
shardGroup = new fabric.Group([shardHalf1, shardHalf2], {
  originX: 'center', originY: 'bottom', centeredRotation: false
});
//canvasObj.add(shardGroup);

// Create an array to hold the cloned groups:
clonedGroups = [];
for (let rotAmount of [0, 72, -72, 144, -144]) {
  shardGroup.rotate(rotAmount);

  shardGroup.clone(function(clonedObj) { 
    canvasObj.add(clonedObj);
    clonedGroups.push(clonedObj);
  });
}
//canvasObj.remove(shardGroup);
&lt;/script&gt;

&lt;p&gt;Let's examine each part of this code individually:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Fabric.js Cloning Star Shards&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;viewport&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cm"&gt;&amp;lt;!-- The HTML &amp;lt;canvas&amp;gt; element: --&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;starCanvasId&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;border: 1px solid black;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;canvasObj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;starCanvasId&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

// Create two polygons for a star shard:
let shardHalf1 = new fabric.Polygon([
  {x: 0, y: 0}, {x: 36, y: -50}, {x: 0, y: -100}], {
  left: 100, top: 0, fill: &amp;quot;#EDD8B7&amp;quot;
});

let shardHalf2 = new fabric.Polygon([
  {x: 0, y: 0}, {x: -36, y: -50}, {x: 0, y: -100}], {
  left: 64, top: 0, fill: &amp;quot;#E5C39C&amp;quot;
});

// Create a group of the two shard halves:
let shardGroup = new fabric.Group([shardHalf1, shardHalf2], {
  originX: &amp;#39;center&amp;#39;, originY: &amp;#39;bottom&amp;#39;, centeredRotation: false
});
canvasObj.add(shardGroup);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here is the boilerplate HTML and JavaScript from the previous section. It creates the star shard and then groups it as a &lt;code&gt;Group&lt;/code&gt; object in the &lt;code&gt;shardGroup&lt;/code&gt; variable.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;//&lt;span class="w"&gt; &lt;/span&gt;Create&lt;span class="w"&gt; &lt;/span&gt;an&lt;span class="w"&gt; &lt;/span&gt;array&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;hold&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;cloned&lt;span class="w"&gt; &lt;/span&gt;groups:
let&lt;span class="w"&gt; &lt;/span&gt;clonedGroups&lt;span class="w"&gt; &lt;/span&gt;=&lt;span class="w"&gt; &lt;/span&gt;[];
for&lt;span class="w"&gt; &lt;/span&gt;(let&lt;span class="w"&gt; &lt;/span&gt;rotAmount&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;[0,&lt;span class="w"&gt; &lt;/span&gt;72,&lt;span class="w"&gt; &lt;/span&gt;-72,&lt;span class="w"&gt; &lt;/span&gt;144,&lt;span class="w"&gt; &lt;/span&gt;-144])&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt;  &lt;/span&gt;shardGroup.rotate(rotAmount);

&lt;span class="w"&gt;  &lt;/span&gt;shardGroup.clone(function(clonedObj)&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;canvasObj.add(clonedObj);
&lt;span class="w"&gt;    &lt;/span&gt;clonedGroups.push(clonedObj);
&lt;span class="w"&gt;  &lt;/span&gt;});
}
canvasObj.remove(shardGroup);
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Inside of a &lt;code&gt;for...of&lt;/code&gt; loop, we rotate the shard group by calling the &lt;code&gt;rotate()&lt;/code&gt; method at 0, 72, -72, 144, and -144 degrees. At each of these rotations, we create a duplicate of the shardGroup by calling the &lt;code&gt;clone()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;clone()&lt;/code&gt; method is rather frustrating. Instead of returning the cloned object, you must pass a callback function to the method which is then called with the newlycreated clone. In our callback function, we call &lt;code&gt;add()&lt;/code&gt; so that the cloned object (now in its rotated position) appears on the canvas.&lt;/p&gt;
&lt;p&gt;To ensure that we'll have access to this cloned object for later use, we also add it to the &lt;code&gt;clonedGroups&lt;/code&gt; array. This program doesn't make use of this array, but it could be handy if you wanted to later have access to the cloned shard groups.&lt;/p&gt;
&lt;p&gt;With five clones for the five shards of the star, we don't need the original group in &lt;code&gt;shardGroup&lt;/code&gt; anymore. We can remove it from the canvas by calling &lt;code&gt;canvasObj.remove(shardGroup)&lt;/code&gt;. This is sort of the opposite of &lt;code&gt;canvasObj.add(shardGroup)&lt;/code&gt;. (In fact, it was unnecessary to add the original shard group to the canvas in the first place. But I wanted to introduce the &lt;code&gt;remove()&lt;/code&gt; method to compliment the &lt;code&gt;add()&lt;/code&gt; method.)&lt;/p&gt;
&lt;p&gt;This tutorial continues on to &lt;a href="https://inventwithpython.com/blog/fabric-js-tutorial-part-6.html"&gt;Part 6&lt;/a&gt;. Special thanks to &lt;a href="https://bio.link/hunor"&gt;Hunor Márton Borbély&lt;/a&gt; for the &lt;a href="https://svg-tutorial.com/svg/transform"&gt;SVG tutorial&lt;/a&gt; that inspired this Fabric.js tutorial.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Fabric.js Tutorial Part 4</title><link href="https://inventwithpython.com/blog/fabric-js-tutorial-part-4.html" rel="alternate"></link><published>2024-05-04T10:04:00-04:00</published><updated>2024-05-04T10:04:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2024-05-04:/blog/fabric-js-tutorial-part-4.html</id><summary type="html">&lt;p&gt;Part 4 of a tutorial series on the Fabric.js canvas/graphics library for JavaScript, where we learn about drawing with paths.round color of a canvas as well as the shapes &lt;code&gt;fabric.Polygon&lt;/code&gt; and &lt;code&gt;fabric.Line&lt;/code&gt;.&lt;br&gt;&lt;img src="/blogstatic/fabric-heart.webp"&gt;&lt;/p&gt;</summary><content type="html">&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"&gt;&lt;/script&gt;

&lt;p&gt;Part 4 of a tutorial series on the Fabric.js canvas/graphics library for JavaScript, where we learn about drawing with paths.round color of a canvas as well as the shapes &lt;code&gt;fabric.Polygon&lt;/code&gt; and &lt;code&gt;fabric.Line&lt;/code&gt;.&lt;br&gt;&lt;img src="/blogstatic/fabric-heart.webp"&gt;&lt;/p&gt;
&lt;p&gt;You can see the previous &lt;a href="https://inventwithpython.com/blog/fabric-js-tutorial-part-3.html"&gt;Part 3&lt;/a&gt; or start at the beginning with &lt;a href="https://inventwithpython.com/blog/fabric-js-tutorial-part-1.html"&gt;Part 1&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this part, we'll draw shapes using SVG's language for paths. Paths are custom shapes that are ideal for creating complex vector shapes that the usual &lt;code&gt;Rect&lt;/code&gt;, &lt;code&gt;Circle&lt;/code&gt;, &lt;code&gt;Polyline&lt;/code&gt;, and other shape classes cannot easily construct. Fabric.js reuses SVG's path syntax, which is helpful considering how much overlap there is between the two graphics systems.&lt;/p&gt;
&lt;h1&gt;Drawing a Hamburger Icon&lt;/h1&gt;
&lt;p&gt;We'll begin this tutorial by using paths to draw the "hamburger icon" used by many mobile apps:&lt;/p&gt;
&lt;canvas id="hamburgerCanvasId" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;
&lt;script&gt;
let hamburgerCanvasObj = new fabric.Canvas('hamburgerCanvasId');
let hamburgerIconPath = new fabric.Path(`
  M -40 -40
  L  40 -40 
  M -40   0 
  L  40   0 
  M -40  40 
  L  40  40`, {
    stroke: '#333333', strokeWidth: 25, strokeLineCap: 'round', left: 40, top: 40
  });

hamburgerCanvasObj.add(hamburgerIconPath);
&lt;/script&gt;

&lt;p&gt;Unlike a &lt;code&gt;fabric.Polyline&lt;/code&gt; shape, paths don't have to be connected lines. And as we'll see in future tutorial parts, paths can also include curves in addition to lines.&lt;/p&gt;
&lt;p&gt;The full code for this hamburger icon is at at &lt;a href="https://inventwithpython.com/fabric-js-tutorial/fabric-js-path-hamburger.html"&gt;https://inventwithpython.com/fabric-js-tutorial/fabric-js-path-hamburger.html&lt;/a&gt; or on &lt;a href="https://jsfiddle.net/asweigart/xtz0y815/"&gt;JSFiddle&lt;/a&gt;. Let's examine each part individually:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Fabric.js Path - Hamburger Icon&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;viewport&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cm"&gt;&amp;lt;!-- The HTML &amp;lt;canvas&amp;gt; element: --&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hamburgerCanvasId&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;border: 1px solid black;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hamburgerCanvasObj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hamburgerCanvasId&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the HTML boilerplate that we've used in previous tutorial parts. The &amp;lt;canvas&amp;gt; element that we create has an id of &lt;code&gt;'hamburgerCanvasId'&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hamburgerIconPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;M&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-40&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-40&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;L&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;40&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-40&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;M&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-40&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nt"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;L&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;40&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nt"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;M&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-40&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;40&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;L&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;40&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;40&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#333333&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strokeWidth&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strokeLineCap&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;round&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The mini-language passed to &lt;code&gt;fabric.Path&lt;/code&gt; as a string value defines a shape. The two most common "commands" in this language are the &lt;em&gt;move-to&lt;/em&gt; and &lt;em&gt;line-to&lt;/em&gt; commands. For example, the &lt;code&gt;hamburgerIconPath&lt;/code&gt; we create starts by moving an imaginary cursor to the X and Y coordinates of -40, -40 with &lt;code&gt;M -40 -40&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Whitespace is flexible for path commands, so we'll use the ` backtick character to make a multi-line JavaScript string. We only need a single space to separate the parts of a command (such as the &lt;code&gt;M&lt;/code&gt; and &lt;code&gt;-40&lt;/code&gt;) but we can put in additional spaces, indentation, and newlines for readability.&lt;/p&gt;
&lt;p&gt;From the X, Y coordinates of -40, -40, we then have the path draw a visible line to the X, Y coordinates of 40, -40 with &lt;code&gt;L 40 -40&lt;/code&gt;. This draws the top line of the hamburger icon. The next four commands moves the imaginary cursor to the start of the middle line (&lt;code&gt;M -40 0&lt;/code&gt;) and draws it (&lt;code&gt;L 40 0&lt;/code&gt;), then moves the imaginary cursor to the start of the bottom line (&lt;code&gt;M -40 40&lt;/code&gt;) and draws it (&lt;code&gt;L 40 40&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;You can also use commas to separate the parts of commands: &lt;code&gt;M -40,-40&lt;/code&gt; instead of &lt;code&gt;M -40 -40&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The second argument is a JavaScript object that describes the style of the path's stroke color, width, and line cap style along with position the shape on the canvas. We've seen this before with the other Fabric.js shapes in previous tutorial parts.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;hamburgerCanvasObj.add(hamburgerIconPath);
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As in previous tutorial parts, the shape objects we created won't appear on the canvas until we call the &lt;code&gt;add()&lt;/code&gt; method for them.&lt;/p&gt;
&lt;h1&gt;Drawing a Heart&lt;/h1&gt;
&lt;p&gt;For our next example, let's draw a simple heart shape by setting a large stroke width and a round line cap style. We could create this with &lt;code&gt;fabric.Polyline&lt;/code&gt;, but let's see how a &lt;code&gt;fabric.Path&lt;/code&gt; shape can also make a heart.&lt;/p&gt;
&lt;canvas id="heartCanvasId" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;
&lt;script&gt;
let heartCanvasObj = new fabric.Canvas('heartCanvasId');
let heartPath = new fabric.Path(`
  M -30 -20 
  L   0  10 
  L  30 -20`, {
    stroke: 'black', strokeWidth: 80, strokeLineCap: 'round', left: 30, top: 40
  });

heartCanvasObj.add(heartPath);
&lt;/script&gt;

&lt;p&gt;The full code for this heart path is at at &lt;a href="https://inventwithpython.com/fabric-js-tutorial/fabric-js-path-heart.html"&gt;https://inventwithpython.com/fabric-js-tutorial/fabric-js-path-heart.html&lt;/a&gt; or on &lt;a href="https://jsfiddle.net/asweigart/k70nym9q/"&gt;JSFiddle&lt;/a&gt;. Let's examine each part individually:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Fabric.js Path - Heart&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;viewport&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cm"&gt;&amp;lt;!-- The HTML &amp;lt;canvas&amp;gt; element: --&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;heartCanvasId&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;border: 1px solid black;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the usual boilerplate HTML we've used before.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
let&lt;span class="w"&gt; &lt;/span&gt;canvasObj&lt;span class="w"&gt; &lt;/span&gt;=&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;fabric.Canvas(&amp;#39;heartCanvasId&amp;#39;);
let&lt;span class="w"&gt; &lt;/span&gt;heartPath&lt;span class="w"&gt; &lt;/span&gt;=&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;fabric.Path(`
&lt;span class="w"&gt;  &lt;/span&gt;M&lt;span class="w"&gt; &lt;/span&gt;-30&lt;span class="w"&gt; &lt;/span&gt;-20&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;L&lt;span class="w"&gt;   &lt;/span&gt;0&lt;span class="w"&gt;  &lt;/span&gt;10&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;L&lt;span class="w"&gt;  &lt;/span&gt;30&lt;span class="w"&gt; &lt;/span&gt;-20`,&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt;    &lt;/span&gt;stroke:&lt;span class="w"&gt; &lt;/span&gt;&amp;#39;black&amp;#39;,&lt;span class="w"&gt; &lt;/span&gt;strokeWidth:&lt;span class="w"&gt; &lt;/span&gt;80,&lt;span class="w"&gt; &lt;/span&gt;strokeLineCap:&lt;span class="w"&gt; &lt;/span&gt;&amp;#39;round&amp;#39;,&lt;span class="w"&gt; &lt;/span&gt;left:&lt;span class="w"&gt; &lt;/span&gt;30,&lt;span class="w"&gt; &lt;/span&gt;top:&lt;span class="w"&gt; &lt;/span&gt;40
&lt;span class="w"&gt;  &lt;/span&gt;});

heartCanvasObj.add(heartPath);
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After creating the &lt;code&gt;fabric.Canvas&lt;/code&gt; object from the &amp;lt;canvas&amp;gt; element with id &lt;code&gt;heartCanvasId&lt;/code&gt;. The three SVG path commands we give move the imaginary cursor the top left end of the heart (&lt;code&gt;M -30 -20&lt;/code&gt;), then draw a line to the bottom point of the heart (&lt;code&gt;L 0 10&lt;/code&gt;), then draw a second line segment to the top right end of the heart (&lt;code&gt;L 30 -20&lt;/code&gt;g)&lt;/p&gt;
&lt;h1&gt;Drawing an Arrow&lt;/h1&gt;
&lt;p&gt;Next, let's draw a simple red arrow using paths. We'll break up the construction of the arrow into three examples. The first is just half of an arrow:&lt;/p&gt;
&lt;canvas id="arrow1CanvasId" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;
&lt;script&gt;
let arrow1CanvasObj = new fabric.Canvas('arrow1CanvasId');
let arrow1Path = new fabric.Path(`
  M -70 0 
  L 70 0 
  L 30 -50`, {
    stroke: '#D1495B', strokeWidth: 25, strokeLineCap: 'round', strokeLineJoin: 'round', fill: 'transparent', left: 10, top: 40
  });

arrow1CanvasObj.add(arrow1Path);
&lt;/script&gt;

&lt;p&gt;The full code for this heart path is at at &lt;a href="https://inventwithpython.com/fabric-js-tutorial/fabric-js-path-heart.html"&gt;https://inventwithpython.com/fabric-js-tutorial/fabric-js-path-heart.html&lt;/a&gt; or on &lt;a href="https://jsfiddle.net/asweigart/1dx5r4pt/"&gt;JSFiddle&lt;/a&gt;. Let's examine each part individually:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Fabric.js Path - Arrow 1&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;viewport&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cm"&gt;&amp;lt;!-- The HTML &amp;lt;canvas&amp;gt; element: --&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;arrow1CanvasId&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;border: 1px solid black;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the usual HTML boilerplate we've been using. The &amp;lt;canvas&amp;gt; element has an id of &lt;code&gt;"Arrow1CanvasId"&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
let&lt;span class="w"&gt; &lt;/span&gt;arrow1CanvasObj&lt;span class="w"&gt; &lt;/span&gt;=&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;fabric.Canvas(&amp;#39;arrow1CanvasId&amp;#39;);
let&lt;span class="w"&gt; &lt;/span&gt;arrow1Path&lt;span class="w"&gt; &lt;/span&gt;=&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;fabric.Path(`
&lt;span class="w"&gt;  &lt;/span&gt;M&lt;span class="w"&gt; &lt;/span&gt;-70&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;L&lt;span class="w"&gt; &lt;/span&gt;70&lt;span class="w"&gt; &lt;/span&gt;0&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;L&lt;span class="w"&gt; &lt;/span&gt;30&lt;span class="w"&gt; &lt;/span&gt;-50`,&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt;    &lt;/span&gt;stroke:&lt;span class="w"&gt; &lt;/span&gt;&amp;#39;#D1495B&amp;#39;,&lt;span class="w"&gt; &lt;/span&gt;strokeWidth:&lt;span class="w"&gt; &lt;/span&gt;25,&lt;span class="w"&gt; &lt;/span&gt;strokeLineCap:&lt;span class="w"&gt; &lt;/span&gt;&amp;#39;round&amp;#39;,&lt;span class="w"&gt; &lt;/span&gt;strokeLineJoin:&lt;span class="w"&gt; &lt;/span&gt;&amp;#39;round&amp;#39;,&lt;span class="w"&gt; &lt;/span&gt;fill:&lt;span class="w"&gt; &lt;/span&gt;&amp;#39;transparent&amp;#39;,&lt;span class="w"&gt; &lt;/span&gt;left:&lt;span class="w"&gt; &lt;/span&gt;10,&lt;span class="w"&gt; &lt;/span&gt;top:&lt;span class="w"&gt; &lt;/span&gt;40
&lt;span class="w"&gt;  &lt;/span&gt;});

arrow1CanvasObj.add(arrow1Path);
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The path we draw is two simple line segments. We move the cursor into position with &lt;code&gt;M -70 0&lt;/code&gt; and draw the two line segments with &lt;code&gt;L 70 0&lt;/code&gt; and &lt;code&gt;L 30 -50&lt;/code&gt;. The end caps of the line are rounded due to the &lt;code&gt;strokeLineCap&lt;/code&gt; setting of &lt;code&gt;'round'&lt;/code&gt;, but notice that the middle point of the two segments is also rounded because of the &lt;code&gt;strokeLineJoin&lt;/code&gt; setting of &lt;code&gt;'round'&lt;/code&gt;. If we didn't specify the &lt;code&gt;strokeLineJoin&lt;/code&gt; setting, it would default to &lt;code&gt;'miter'&lt;/code&gt; (out of possible settings &lt;code&gt;'miter'&lt;/code&gt;, &lt;code&gt;'bevel'&lt;/code&gt;, and &lt;code&gt;'round'&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;Here is what the arrow with a &lt;code&gt;'miter'&lt;/code&gt; line join looks like:&lt;/p&gt;
&lt;canvas id="path4CanvasId" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;
&lt;script&gt;
canvasObj = new fabric.Canvas('path4CanvasId');
path = new fabric.Path(`
  M -70 0 
  L 70 0 
  L 30 -50`, {
    stroke: '#D1495B', strokeWidth: 25, strokeLineCap: 'round', fill: 'transparent', left: 10, top: 40
  });

canvasObj.add(path);
&lt;/script&gt;

&lt;p&gt;By adding &lt;code&gt;M 70 0 L 30 50&lt;/code&gt; to the &lt;code&gt;fabric.Path&lt;/code&gt; string, we can complete the arrow:&lt;/p&gt;
&lt;canvas id="path5CanvasId" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;
&lt;script&gt;
canvasObj = new fabric.Canvas('path5CanvasId');
path = new fabric.Path('M -70 0 L 70 0 L 30 -50 M 70 0 L 30 50', {
    stroke: '#D1495B', strokeWidth: 25, strokeLineCap: 'round', strokeLineJoin: 'round', fill: 'transparent', left: 10, top: 40
  });

canvasObj.add(path);
&lt;/script&gt;

&lt;p&gt;This tutorial continues on to &lt;a href="https://inventwithpython.com/blog/fabric-js-tutorial-part-5.html"&gt;Part 5&lt;/a&gt;. Special thanks to &lt;a href="https://bio.link/hunor"&gt;Hunor Márton Borbély&lt;/a&gt; for the &lt;a href="https://svg-tutorial.com/svg/basic-path"&gt;SVG tutorial&lt;/a&gt; that inspired this Fabric.js tutorial.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Fabric.js Tutorial Part 3</title><link href="https://inventwithpython.com/blog/fabric-js-tutorial-part-3.html" rel="alternate"></link><published>2024-05-04T10:03:00-04:00</published><updated>2024-05-04T10:03:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2024-05-04:/blog/fabric-js-tutorial-part-3.html</id><summary type="html">&lt;p&gt;Part 3 of a tutorial series on the Fabric.js canvas/graphics library for JavaScript, where we learn how to set the background color of a canvas as well as the shapes &lt;code&gt;fabric.Polygon&lt;/code&gt; and &lt;code&gt;fabric.Line&lt;/code&gt;.&lt;br&gt;&lt;img src="/blogstatic/fabric-house.webp"&gt;&lt;/p&gt;</summary><content type="html">&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"&gt;&lt;/script&gt;

&lt;p&gt;Part 3 of a tutorial series on the Fabric.js canvas/graphics library for JavaScript, where we learn how to set the background color of a canvas as well as the shapes &lt;code&gt;fabric.Polygon&lt;/code&gt; and &lt;code&gt;fabric.Line&lt;/code&gt;.&lt;br&gt;&lt;img src="/blogstatic/fabric-house.webp"&gt;&lt;/p&gt;
&lt;p&gt;This is Part 3 of the Fabric.js tutorial. You can see the previous &lt;a href="https://inventwithpython.com/blog/fabric-js-tutorial-part-2.html"&gt;Part 2&lt;/a&gt; or start at the beginning with &lt;a href="https://inventwithpython.com/blog/fabric-js-tutorial-part-1.html"&gt;Part 1&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Drawing a House&lt;/h1&gt;
&lt;p&gt;Let's get more practice by drawing this house:&lt;/p&gt;
&lt;canvas id="houseCanvasId" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;
&lt;script&gt;
let canvasObj = new fabric.Canvas('houseCanvasId');
canvasObj.backgroundColor = 'lightblue';

let frame = new fabric.Polygon([
  {x: -65, y: 80}, {x: -65, y: -10}, {x: 0, y: -70}, {x: 65, y: -10}, {x: 65, y:80}], {
  stroke: 'black', strokeWidth: 2, fill: 'white', left: 20, top: 40
});

let roof = new fabric.Polyline([
  {x: -75, y: -8}, {x: 0, y: -78}, {x: 75, y: -8}], {
  stroke: 'red', strokeWidth: 10, strokeLineCap: 'round', fill: 'transparent', left: 6, top: 32
});

let door = new fabric.Rect({
  left: 40, top: 130, width: 30, height: 60, stroke: 'black', fill: 'red'
});

let doorKnob = new fabric.Circle({
  radius: 3, stroke: 'black', fill: 'white', left: 45, top: 160
});

let windowFrame = new fabric.Rect({
  left: 90, top: 120, width: 40, height: 40, stroke: 'black', fill: '#fdea96'
});

let windowVerticalLine = new fabric.Line([110, 120, 110, 160], {stroke: 'black'});

let windowHorizontalLine = new fabric.Line([90, 140, 130, 140], {stroke: 'black'});

canvasObj.add(frame);
canvasObj.add(roof);
canvasObj.add(door);
canvasObj.add(doorKnob);
canvasObj.add(windowFrame);
canvasObj.add(windowVerticalLine);
canvasObj.add(windowHorizontalLine);
&lt;/script&gt;

&lt;p&gt;The full code is at &lt;a href="https://inventwithpython.com/fabric-js-tutorial/fabric-js-house.html"&gt;https://inventwithpython.com/fabric-js-tutorial/fabric-js-house.html&lt;/a&gt; or on &lt;a href="https://jsfiddle.net/asweigart/ntd2cau7/"&gt;JSFiddle&lt;/a&gt;. Let's examine each part individually:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Fabric.js House&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;viewport&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cm"&gt;&amp;lt;!-- The HTML &amp;lt;canvas&amp;gt; element: --&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;houseCanvasId&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;border: 1px solid black;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the boilerplate HTML that we've used before in previous tutorial parts. It has a &amp;lt;canvas&amp;gt; element with an id of &lt;code&gt;"houseCanvasId"&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;lt;script&amp;gt;
let canvasObj = new fabric.Canvas(&amp;#39;houseCanvasId&amp;#39;);
canvasObj.backgroundColor = &amp;#39;lightblue&amp;#39;;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To set the background color of the &amp;lt;canvas&amp;gt; element, set the &lt;code&gt;backgroundColor&lt;/code&gt; property to an HTML color name or RGB color code. The same color settings we've applied to stroke and fill in the previous tutorials can be used for &lt;code&gt;backgroundColor&lt;/code&gt; here.&lt;/p&gt;
&lt;p&gt;Avoid setting the CSS &lt;code&gt;background-color&lt;/code&gt; property of the &amp;lt;canvas&amp;gt; element when using Fabric.js, as this can causing weird rendering issues such as the background color covering up the entire canvas.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;frame&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Polygon&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="cp"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;black&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strokeWidth&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;white&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;roof&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Polyline&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="cp"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;red&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strokeWidth&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strokeLineCap&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;round&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;transparent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We'll create a new &lt;code&gt;fabric.Polygon&lt;/code&gt; shape for the white frame of the house and a red &lt;code&gt;fabric.Polyline&lt;/code&gt; for the roof. These shapes are similar: the difference between them is that a &lt;code&gt;fabric.Polygon&lt;/code&gt; shape automatically draws one more line from its last point to its first point. A &lt;code&gt;fabric.Polyline&lt;/code&gt; will not do this, although both shapes will fill their interior spaces with color. This is why the red roof needs to have a &lt;code&gt;fill&lt;/code&gt; property of &lt;code&gt;'transparent'&lt;/code&gt;, otherwise it draws a black (the default fill color in Fabric.js) triangle.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;door&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Rect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;130&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;black&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;red&amp;#39;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;doorKnob&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Circle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;black&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;white&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;windowFrame&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Rect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;black&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#fdea96&amp;#39;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The door, door knob, and window are made from the circle and rectangle shapes we've created in earlier parts of this tutorial.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;windowVerticalLine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Line&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;110&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;110&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="cp"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;black&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;windowHorizontalLine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Line&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="cp"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;140&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;130&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;140&lt;/span&gt;&lt;span class="cp"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;black&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The two lines across the yellow window are created with &lt;code&gt;fabric.Line&lt;/code&gt; shapes. The first argument to &lt;code&gt;fabric.Line()&lt;/code&gt; is an array of four numbers for the start and end points of the line: &lt;code&gt;[x1, y1, x2, y2]&lt;/code&gt; followed by the usual object of settings. As with the array-of-points argument to &lt;code&gt;fabric.Polyline()&lt;/code&gt; and &lt;code&gt;fabric.Polyon()&lt;/code&gt;, you can use the &amp;lt;canvas&amp;gt; element's coordinate system and leave the &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;top&lt;/code&gt; arguments at their default of &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;canvasObj.add(frame);
canvasObj.add(roof);
canvasObj.add(door);
canvasObj.add(doorKnob);
canvasObj.add(windowFrame);
canvasObj.add(windowVerticalLine);
canvasObj.add(windowHorizontalLine);
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As in previous tutorial parts, the shape objects we created won't appear on the canvas until we call the &lt;code&gt;add()&lt;/code&gt; method for them.&lt;/p&gt;
&lt;p&gt;This tutorial continues on to &lt;a href="https://inventwithpython.com/blog/fabric-js-tutorial-part-4.html"&gt;Part 4&lt;/a&gt;. Special thanks to &lt;a href="https://bio.link/hunor"&gt;Hunor Márton Borbély&lt;/a&gt; for the &lt;a href="https://svg-tutorial.com/svg/house"&gt;SVG tutorial&lt;/a&gt; that inspired this Fabric.js tutorial.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Fabric.js Tutorial Part 2</title><link href="https://inventwithpython.com/blog/fabric-js-tutorial-part-2.html" rel="alternate"></link><published>2024-05-04T10:02:00-04:00</published><updated>2024-05-04T10:02:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2024-05-04:/blog/fabric-js-tutorial-part-2.html</id><summary type="html">&lt;p&gt;This is Part 2 of the Fabric.js tutorial canvas/graphics library for JavaScript, where we learn about Polyline shapes and styling shapes by drawing a gingerbread figure.&lt;br&gt;&lt;img src="/blogstatic/fabric-gingerbread-figure.webp"&gt;&lt;/p&gt;</summary><content type="html">&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"&gt;&lt;/script&gt;

&lt;p&gt;This is Part 2 of the Fabric.js tutorial canvas/graphics library for JavaScript, where we learn about Polyline shapes and styling shapes by drawing a gingerbread figure.&lt;br&gt;&lt;img src="/blogstatic/fabric-gingerbread-figure.webp"&gt;&lt;/p&gt;
&lt;p&gt;You can start at the beginning with &lt;a href="https://inventwithpython.com/blog/fabric-js-tutorial-part-1.html"&gt;Part 1&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Drawing a Gingerbread Figure&lt;/h1&gt;
&lt;p&gt;Let's learn about a new shape, polyline, and how to add styling such as rounded corners and rotation by drawing this gingerbread figure:&lt;/p&gt;
&lt;canvas id="gingerbreadCanvasId" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;

&lt;script&gt;
// Create a Fabric.js "Canvas" object that is tied to the HTML &lt;canvas&gt; element:
let canvasObj = new fabric.Canvas('gingerbreadCanvasId');

const GINGERBREAD_COLOR = '#CD803D';

let head = new fabric.Circle({
  left: 100, top: 50, fill: GINGERBREAD_COLOR, radius: 40, originX: 'center', originY: 'center'
})


let arms = new fabric.Polyline(
  [{x: 0, y: 0}, {x: 100, y:0}], {
    stroke: GINGERBREAD_COLOR, strokeWidth: 40, strokeLineCap: 'round', fill: 'transparent', left: 100, top: 100, originX: 'center', originY: 'center'
  });

let legs = new fabric.Polyline(
  [{x: 0, y: 60}, {x: 30, y: 0}, {x: 60, y: 60}], {
    stroke: GINGERBREAD_COLOR, strokeWidth: 40, strokeLineCap: 'round', fill: 'transparent', left: 100, top: 140, originX: 'center', originY: 'center'
  });

let leftEye = new fabric.Circle({
  left: 90, top: 40, fill: 'transparent', stroke: 'white', radius: 10, originX: 'center', originY: 'center'
});

let rightEye = new fabric.Circle({
  left: 110, top: 40, fill: 'transparent', stroke: 'white', radius: 5, originX: 'center', originY: 'center'
});

let mouth = new fabric.Rect({
  left: 100, top: 70, originX: 'center', originY: 'center', fill: 'transparent', stroke: 'white', strokeWidth: 6, width: 40, height: 10, rx: 5, ry: 5, angle: 15
});

let topButton = new fabric.Circle({
  left: 100, top: 90, originX: 'center', originY: 'center', radius: 4, fill: 'black', stroke: 'black'
});

let bottomButton = new fabric.Circle({
  left: 100, top: 110, originX: 'center', originY: 'center', radius: 4
});

canvasObj.add(head);
canvasObj.add(arms);
canvasObj.add(legs);
canvasObj.add(leftEye);
canvasObj.add(rightEye);
canvasObj.add(mouth);
canvasObj.add(topButton);
canvasObj.add(bottomButton);
&lt;/script&gt;

&lt;p&gt;The full code is at &lt;a href="https://inventwithpython.com/fabric-js-tutorial/fabric-js-gingerbread-figure.html"&gt;https://inventwithpython.com/fabric-js-tutorial/fabric-js-gingerbread-figure.html&lt;/a&gt; or on &lt;a href="https://jsfiddle.net/asweigart/7kxv10f9/"&gt;JSFiddle&lt;/a&gt;. Let's examine each part individually:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Fabric.js Gingerbread Figure&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;viewport&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cm"&gt;&amp;lt;!-- The HTML &amp;lt;canvas&amp;gt; element: --&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;gingerbreadCanvasId&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;border: 1px solid black;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again, this is the standard HTML boilerplate that we have for our page but I include it to have a complete example that is viewable in an actual web browser. Note that the ID of the &amp;lt;canvas&amp;gt; element is &lt;code&gt;"gingerbreadCanvasId"&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Fabric&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Canvas&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tied&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HTML&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;canvasObj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fabric&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;gingerbreadCanvasId&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GINGERBREAD_COLOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#CD803D&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fabric&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GINGERBREAD_COLOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Multiple shapes use the color &lt;code&gt;#CD803D&lt;/code&gt; so let's save it to the constant &lt;code&gt;GINGERBREAD_COLOR&lt;/code&gt;. We'll create a new &lt;code&gt;fabric.Circle&lt;/code&gt; object for the gungerbread figure's head. Notice that because the &lt;code&gt;originX&lt;/code&gt; and &lt;code&gt;originY&lt;/code&gt; properties are set to &lt;code&gt;'center'&lt;/code&gt;, the &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;top&lt;/code&gt; settings refer to the X and Y coordinates of the shape's &lt;em&gt;center point&lt;/em&gt; and not the top-left corner. Confusingly, we still have to use the &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;top&lt;/code&gt; property names.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;arms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Polyline&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="cp"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="cp"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GINGERBREAD_COLOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strokeWidth&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strokeLineCap&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;round&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;transparent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originX&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;legs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Polyline&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="cp"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="cp"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GINGERBREAD_COLOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strokeWidth&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strokeLineCap&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;round&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;transparent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;140&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originX&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To create the arms and legs, we'll use a new shape called polyline which represents a connected series of lines. The first argument to the &lt;code&gt;fabric.Polyline&lt;/code&gt; is an array of objects with &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; properties.&lt;/p&gt;
&lt;p&gt;The arms only have two points: &lt;code&gt;[{x: 0, y: 0}, {x: 100, y:0}]&lt;/code&gt; These X and Y coordinates &lt;em&gt;can&lt;/em&gt; be relative to the shape's own Cartesian coordinate system, and then the shape is position on the &amp;lt;canvas&amp;gt; element with the &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;top&lt;/code&gt; properties.&lt;/p&gt;
&lt;p&gt;You &lt;em&gt;can alternatively&lt;/em&gt; use the &amp;lt;canvs&amp;gt; element's coordinate system (where the 0, 0 origin is the top-left corner of the &amp;lt;canvas&amp;gt; element) for the array of points and then leave the &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;top&lt;/code&gt; properties as their default &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;0&lt;/code&gt; values.&lt;/p&gt;
&lt;p&gt;The legs are made from a second polyline that has three points: &lt;code&gt;[{x: 0, y: 60}, {x: 30, y: 0}, {x: 60, y: 60}]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The color and thickness of the polyline is set by the &lt;code&gt;stroke&lt;/code&gt; and &lt;code&gt;strokeWidth&lt;/code&gt; properties in this object. Setting the stroke color to &lt;code&gt;GINGEBREAD_COLOR&lt;/code&gt; gives us the following shapes:&lt;/p&gt;
&lt;canvas id="gingerbreadCanvas2Id" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;

&lt;script&gt;
// Create a Fabric.js "Canvas" object that is tied to the HTML &lt;canvas&gt; element:
let canvasObj2 = new fabric.Canvas('gingerbreadCanvas2Id');

let arms2 = new fabric.Polyline(
  [{x: 0, y: 0}, {x: 100, y:0}], {
    stroke: GINGERBREAD_COLOR, strokeWidth: 1, fill: 'transparent', left: 100, top: 100, originX: 'center', originY: 'center'
  });

let legs2 = new fabric.Polyline(
  [{x: 0, y: 60}, {x: 30, y: 0}, {x: 60, y: 60}], {
    stroke: GINGERBREAD_COLOR, strokeWidth: 1, fill: 'transparent', left: 100, top: 140, originX: 'center', originY: 'center'
  });

canvasObj2.add(arms2);
canvasObj2.add(legs2);
&lt;/script&gt;

&lt;p&gt;Adding a &lt;code&gt;strokeWidth&lt;/code&gt; of &lt;code&gt;40&lt;/code&gt; makes these lines thicker:&lt;/p&gt;
&lt;canvas id="gingerbreadCanvas3Id" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;
&lt;script&gt;
// Create a Fabric.js "Canvas" object that is tied to the HTML &lt;canvas&gt; element:
let canvasObj3 = new fabric.Canvas('gingerbreadCanvas3Id');

let arms3 = new fabric.Polyline(
  [{x: 0, y: 0}, {x: 100, y:0}], {
    stroke: GINGERBREAD_COLOR, strokeWidth: 40, fill: 'transparent', left: 100, top: 100, originX: 'center', originY: 'center'
  });

let legs3 = new fabric.Polyline(
  [{x: 0, y: 60}, {x: 30, y: 0}, {x: 60, y: 60}], {
    stroke: GINGERBREAD_COLOR, strokeWidth: 40, fill: 'transparent', left: 100, top: 140, originX: 'center', originY: 'center'
  });

canvasObj3.add(arms3);
canvasObj3.add(legs3);
&lt;/script&gt;

&lt;p&gt;However, to give the gingerbread figure a more rounded look, we set the &lt;code&gt;strokeLineCap&lt;/code&gt; to &lt;code&gt;'round'&lt;/code&gt;. (It can also be set to &lt;code&gt;'square'&lt;/code&gt; or &lt;code&gt;'butt'&lt;/code&gt;, with the default being &lt;code&gt;'butt'&lt;/code&gt;.)&lt;/p&gt;
&lt;canvas id="gingerbreadCanvas4Id" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;

&lt;script&gt;
// Create a Fabric.js "Canvas" object that is tied to the HTML &lt;canvas&gt; element:
canvasObj = new fabric.Canvas('gingerbreadCanvas4Id');

arms = new fabric.Polyline(
  [{x: 0, y: 0}, {x: 100, y:0}], {
    stroke: GINGERBREAD_COLOR, strokeWidth: 40, fill: 'transparent', left: 100, top: 100, originX: 'center', originY: 'center', strokeLineCap: 'round'
  });

legs = new fabric.Polyline(
  [{x: 0, y: 60}, {x: 30, y: 0}, {x: 60, y: 60}], {
    stroke: GINGERBREAD_COLOR, strokeWidth: 40, fill: 'transparent', left: 100, top: 140, originX: 'center', originY: 'center', strokeLineCap: 'round'
  });

canvasObj.add(arms);
canvasObj.add(legs);
&lt;/script&gt;

&lt;p&gt;Note that setting &lt;code&gt;strokeLineCap&lt;/code&gt; only applies to the two ends and not for the middle connecting points of the polyline. The connection between the two line segments in the legs polyline can also be rounded if you set the &lt;code&gt;strokeLineJoin&lt;/code&gt; property to &lt;code&gt;'round'&lt;/code&gt;. (It can also be set to &lt;code&gt;'bevel'&lt;/code&gt; or &lt;code&gt;'miter'&lt;/code&gt;, with the default being &lt;code&gt;'miter'&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;It may seem odd that a polyline has a &lt;code&gt;fill&lt;/code&gt; property, but the area that is filled in is described by the &lt;em&gt;polygon shape&lt;/em&gt; that a polyline draws. The complete shape includes connecting the end point to the start point. By default, the fill color is black. To make the fill invisible and only draw a line, we must set &lt;code&gt;fill&lt;/code&gt; to &lt;code&gt;'transparent'&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Later we'll use a &lt;code&gt;fabric.Polygon&lt;/code&gt; shape, which is the same thing as &lt;code&gt;fabric.Polyline&lt;/code&gt; but its stroke connects the last point back to the first point.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mouth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Rect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originX&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;transparent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;white&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strokeWidth&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rx&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ry&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The mouth is a white rectangle outline, but we give it rounded corners by setting the horiztonal and vertical radius of the corners with &lt;code&gt;rx&lt;/code&gt; and &lt;code&gt;ry&lt;/code&gt; to 5 pixels. The rectangle is also rotated counter-clockwise by 15 degrees by setting &lt;code&gt;angle&lt;/code&gt; to &lt;code&gt;15&lt;/code&gt;. Note that this rotation is specified in number of degrees and not number of radians.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;leftEye&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Circle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;transparent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;white&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originX&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;rightEye&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Circle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;110&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;transparent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;white&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originX&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;topButton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Circle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originX&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;black&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;stroke&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;black&amp;#39;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;bottomButton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Circle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;110&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originX&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The last shapes are four circles for the eyes and buttons. These use the same concepts we have seen before.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;canvasObj.add(head);
canvasObj.add(arms);
canvasObj.add(legs);
canvasObj.add(leftEye);
canvasObj.add(rightEye);
canvasObj.add(mouth);
canvasObj.add(topButton);
canvasObj.add(bottomButton);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, none of these shapes will appear on the canvas until we call the &lt;code&gt;add()&lt;/code&gt; method for each of them.&lt;/p&gt;
&lt;p&gt;This tutorial continues on to &lt;a href="https://inventwithpython.com/blog/fabric-js-tutorial-part-3.html"&gt;Part 3&lt;/a&gt;. Special thanks to &lt;a href="https://bio.link/hunor"&gt;Hunor Márton Borbély&lt;/a&gt; for the &lt;a href="https://svg-tutorial.com/svg/gingerbread-figure"&gt;SVG tutorial&lt;/a&gt; that inspired this Fabric.js tutorial.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Fabric.js Tutorial Part 1</title><link href="https://inventwithpython.com/blog/fabric-js-tutorial-part-1.html" rel="alternate"></link><published>2024-05-04T10:01:00-04:00</published><updated>2024-05-04T10:01:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2024-05-04:/blog/fabric-js-tutorial-part-1.html</id><summary type="html">&lt;p&gt;This is part 1 my tutorial series on the Fabric.js canvas/graphics library for JavaScript, where we learn about drawing basic shapes like rectangles, circles, and triangles to draw pine trees.&lt;br&gt;&lt;img src="/blogstatic/fabric-pine-tree.png"&gt;&lt;/p&gt;</summary><content type="html">&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"&gt;&lt;/script&gt;

&lt;p&gt;This is part 1 my tutorial series on the Fabric.js canvas/graphics library for JavaScript, where we learn about drawing basic shapes like rectangles, circles, and triangles to draw pine trees.&lt;br&gt;&lt;img src="/blogstatic/fabric-pine-tree.png"&gt;&lt;/p&gt;
&lt;h1&gt;What is Fabric.js?&lt;/h1&gt;
&lt;p&gt;&lt;a href="http://fabricjs.com/"&gt;Fabric.js&lt;/a&gt; is a JavaScript library that makes drawing on HTML5 &amp;lt;canvas&amp;gt; elements easier than using the native Canvas API. This Fabric.js tutorial emulates the format of &lt;a href="https://bio.link/hunor"&gt;Hunor Márton Borbély&lt;/a&gt;'s SVG Tutorial at &lt;a href="https://svg-tutorial.com/"&gt;svg-tutorial.com&lt;/a&gt;: short demonstrations that you can enter into a text editor and view in your local browser.&lt;/p&gt;
&lt;p&gt;I dislike code snippets that you can't actually run yourself on your own computer with modification. I will always give you the complete HTML for each example that you can copy and open in your browser on your computer. There are no missing sections or "leave it to the reader" omissions. Furthermore, the Fabric.js examples on each page are live examples and not just screenshots. I also provide &lt;a href="https://jsfiddle.net/"&gt;JSFiddle&lt;/a&gt; links with the example code for you to modify.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://fabricjs.com/"&gt;Fabric.js website&lt;/a&gt; has tutorials, documentation, and a gallery of examples but you won't need to consult that site to follow this tutorial series. This tutorial does assume you have basic HTML and JavaScript knowledge.&lt;/p&gt;
&lt;h1&gt;Drawing Rectangles and Circles&lt;/h1&gt;
&lt;p&gt;Let's create a small example with red and blue rectangles on a &amp;lt;canvas&amp;gt; element that will look like this:&lt;/p&gt;
&lt;canvas id="mainCanvasId" width="400" height="400" style="border: 1px solid black;"&gt;&lt;/canvas&gt;

&lt;script&gt;
// Create a Fabric.js "Canvas" object that is tied to the HTML &lt;canvas&gt; element:
let canvasObj = new fabric.Canvas('mainCanvasId');

// Create a Fabric.js "Rect" object that represents a rectangle:
let rectObj = new fabric.Rect({
  left: 100, top: 50, fill: 'red', stroke: 'blue', width: 80, height: 30
});

// The rectangle won't appear on the canvas until it is added:
canvasObj.add(rectObj);

// Create a second rectangle:
let rectObj2 = new fabric.Rect({
  left: 50, top: 100, fill: 'green', width: 50, height: 50
});

canvasObj.add(rectObj2);

// Create a circle:
let circleObj = new fabric.Circle({
  // Note that the radius is half the width of the circle.
  radius: 100, fill: '#FF00FF', left: 200, top: 200
});

canvasObj.add(circleObj);
&lt;/script&gt;

&lt;p&gt;In Fabric.js, you draw basic shapes by creating shape objects with JavaScript code. You can also write text, load images, and apply image filters, but we'll explore that later. You can see the complete code for this example on its own page at &lt;a href="https://inventwithpython.com/fabric-js-tutorial/fabric-js-rects-and-circle.html"&gt;fabric-js-rects-and-circle.html&lt;/a&gt;. Right-click the page and select "View Source" to view the HTML and JavaScript code. You can also view this code on &lt;a href="https://jsfiddle.net/asweigart/azedfo2L/"&gt;JSFiddle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Notice that you can drag the objects around the canvas as well as scale and rotate them. This feature is automatically provided by Fabric.js. To disable this, replace the &lt;code&gt;fabric.Canvas()&lt;/code&gt; code (we'll see this in the next section) with &lt;code&gt;fabric.StaticCanvas()&lt;/code&gt;, as in this output where you can't move the shapes around:&lt;/p&gt;
&lt;canvas id="mainStaticCanvasId" width="400" height="400" style="border: 1px solid black;"&gt;&lt;/canvas&gt;

&lt;script&gt;
// Create a Fabric.js "Canvas" object that is tied to the HTML &lt;canvas&gt; element:
let staticCanvasObj = new fabric.StaticCanvas('mainStaticCanvasId');

// Create a Fabric.js "Rect" object that represents a rectangle:
let staticRectObj = new fabric.Rect({
  left: 100, top: 50, fill: 'red', stroke: 'blue', width: 80, height: 30
});

// The rectangle won't appear on the canvas until it is added:
staticCanvasObj.add(staticRectObj);

// Create a second rectangle:
let staticRectObj2 = new fabric.Rect({
  left: 50, top: 100, fill: 'green', width: 50, height: 50
});

staticCanvasObj.add(staticRectObj2);

// Create a circle:
let staticCircleObj = new fabric.Circle({
  // Note that the radius is half the width of the circle.
  radius: 100, fill: '#FF00FF', left: 200, top: 200
});

staticCanvasObj.add(staticCircleObj);
&lt;/script&gt;

&lt;p&gt;Notice that for objects added to the static canvas, you cannot select them and move them around.&lt;/p&gt;
&lt;p&gt;Let's look at each part of the HTML and JavaScript individually:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Fabric.js Rects and Circle&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;viewport&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is some standard HTML boilerplate for a basic web page header. I'm loading the Fabric.js library from a free &lt;a href="https://en.wikipedia.org/wiki/Content_delivery_network"&gt;CDN (Content Distribution Network)&lt;/a&gt; provided by Cloudflare, but you can put the &lt;em&gt;&lt;a href="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.js"&gt;fabric.js&lt;/a&gt;&lt;/em&gt; file on your own web host and link to it instead. This tutorial uses Fabric.js version 5.3.1. The CDN also provides a minified version for faster loading if you change the filename to &lt;em&gt;&lt;a href="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"&gt;fabric.min.js&lt;/a&gt;&lt;/em&gt; in this URL.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="cm"&gt;&amp;lt;!-- The HTML &amp;lt;canvas&amp;gt; element: --&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;mainCanvasId&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;400&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;400&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;border: 1px solid black;&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas"&gt;&amp;lt;canvas&amp;gt; element&lt;/a&gt; is the area where the drawing takes place. You can think of it as a &amp;lt;img&amp;gt; element but its contents come from JavaScript code instead of an image file. Instead of using the native Canvas API to do the drawing, you can use Fabric.js's API. I've given this &amp;lt;canvas&amp;gt; element a width and height of 400 pixels, as well as a thin black border for easier viewing.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;js&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Canvas&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;tied&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HTML&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;element&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;canvasObj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Canvas&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mainCanvasId&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First, create a new &lt;code&gt;Canvas&lt;/code&gt; object by passing &lt;code&gt;fabric.Canvas()&lt;/code&gt; the id of the &amp;lt;canvas&amp;gt; element, &lt;code&gt;'mainCanvasId'&lt;/code&gt;. This is necessary since there could be multiple &amp;lt;canvas&amp;gt; elements on the web page.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a Fabric.js &amp;quot;Rect&amp;quot; object that represents a rectangle:&lt;/span&gt;
&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rectObj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rect&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;red&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;blue&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// The rectangle won&amp;#39;t appear on the canvas until it is added:&lt;/span&gt;
&lt;span class="n"&gt;canvasObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rectObj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We'll draw a rectangle by creating a &lt;code&gt;Rect&lt;/code&gt; object. Pass a JS object with keys &lt;code&gt;'left'&lt;/code&gt; and &lt;code&gt;'top'&lt;/code&gt; for the X and Y coordinate of the left and top edge of the rectangle. The interior &lt;em&gt;fill&lt;/em&gt; color for this rectangle will be red and the outline &lt;em&gt;stroke&lt;/em&gt; will be blue. The size of the rectangle is 80 pixels wide and 30 pixels tall. (Remember that we made the total size of the &amp;lt;canvas&amp;gt; element 400 x 400.)&lt;/p&gt;
&lt;p&gt;You won't see this rectangle on the canvas until you call the &lt;code&gt;Canvas.add()&lt;/code&gt; method with the &lt;code&gt;Rect&lt;/code&gt; objects.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a second rectangle:&lt;/span&gt;
&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rectObj2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rect&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;green&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;canvasObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rectObj2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We'll create a second rectangle, this time with a green fill and size of 50 pixels by 50 pixels.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a circle:&lt;/span&gt;
&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;circleObj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// Note that the radius is half the width of the circle.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;#FF00FF&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;canvasObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;circleObj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;Circle&lt;/code&gt; object must be added to the &lt;code&gt;Canvas&lt;/code&gt; object the same way the &lt;code&gt;Rect&lt;/code&gt; objects are, or you will not see them on the canvas. Note that objects added to the canvas most recently are on top of any objects added earlier.&lt;/p&gt;
&lt;p&gt;Next, let's create a magenta circle with a radius of 100 pixels by calling the &lt;code&gt;fabric.Circle()&lt;/code&gt; function. The &lt;code&gt;fill&lt;/code&gt; property in the object passed to this function is set to &lt;code&gt;'#FF00FF'&lt;/code&gt;, an HTML RGB code for maximum red, no green, and maximum blue to produce magenta. Notice that the position of the circle is set by its left and top edge location. If you want to position the circle by supplying its &lt;em&gt;center&lt;/em&gt; X and Y coordinates, include &lt;code&gt;originX: 'center', originY: 'center'&lt;/code&gt; in the object passed to &lt;code&gt;fabric.Circle()&lt;/code&gt;. Somewhat confusingly, the &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;top&lt;/code&gt; parameter names will now set the center position of the shape instead of the left and top edge.&lt;/p&gt;
&lt;p&gt;For example, changing the code to &lt;code&gt;let circleObj = new fabric.Circle({radius: 100, fill: '#FF00FF', left: 200, top: 200, originX: 'center', originY: 'center'});&lt;/code&gt; will make the canvas look like this:&lt;/p&gt;
&lt;canvas id="mainCanvasCenterCircleId" width="400" height="400" style="border: 1px solid black;"&gt;&lt;/canvas&gt;

&lt;script&gt;
// Create a Fabric.js "Canvas" object that is tied to the HTML &lt;canvas&gt; element:
let centerCanvasObj = new fabric.Canvas('mainCanvasCenterCircleId');

// Create a Fabric.js "Rect" object that represents a rectangle:
let centerRectObj = new fabric.Rect({
  left: 100, top: 50, fill: 'red', stroke: 'blue', width: 80, height: 30
});

// The rectangle won't appear on the canvas until it is added:
centerCanvasObj.add(centerRectObj);

// Create a second rectangle:
let centerRectObj2 = new fabric.Rect({
  left: 50, top: 100, fill: 'green', width: 50, height: 50
});

centerCanvasObj.add(centerRectObj2);

// Create a circle:
centerCircleObj = new fabric.Circle({
  // Note that the radius is half the width of the circle.
  radius: 100, fill: '#FF00FF', left: 200, top: 200, originX: 'center', originY: 'center'
});

centerCanvasObj.add(centerCircleObj);
&lt;/script&gt;

&lt;p&gt;Note that forgetting to set either the &lt;code&gt;width&lt;/code&gt; or &lt;code&gt;height&lt;/code&gt; in &lt;code&gt;fabric.Rect()&lt;/code&gt; or the &lt;code&gt;radius&lt;/code&gt; in &lt;code&gt;fabric.Circle()&lt;/code&gt; will cause that shape to not render on the canvas. There will be no error in the browser's developer console to indicate that an error has occured; the shape will simply not appear.&lt;/p&gt;
&lt;p&gt;Review:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Put &lt;code&gt;&amp;lt;script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt; in your HTML file to load the Fabric.js library from a free CDN.&lt;/li&gt;
&lt;li&gt;Create the &amp;lt;canvas&amp;gt; element with &lt;code&gt;&amp;lt;canvas id="mainCanvasId" width="400" height="400" style="border: 1px solid black;"&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The code &lt;code&gt;let canvasObj = new fabric.Canvas('mainCanvasId');&lt;/code&gt; creates a &lt;code&gt;Canvas&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;The code &lt;code&gt;let rectObj = new fabric.Rect({
  left: 100, top: 50, fill: 'red', stroke: 'blue', width: 80, height: 30
});&lt;/code&gt; creates a rectangle.&lt;/li&gt;
&lt;li&gt;The code &lt;code&gt;let circleObj = new fabric.Circle({radius: 100, fill: '#FF00FF', left: 200, top: 200});&lt;/code&gt; creates a circle. Instead of &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt;, it has a &lt;code&gt;radius&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You must call &lt;code&gt;canvasObj.add(rectObj);&lt;/code&gt; to make a shape actually appear on the canvas; creating it is not enough.&lt;/li&gt;
&lt;li&gt;When creating a new shape, you can set &lt;code&gt;originX&lt;/code&gt; to &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;center&lt;/code&gt;, or &lt;code&gt;right&lt;/code&gt; and set &lt;code&gt;originY&lt;/code&gt; to &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;center&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt; to change which point on the shape the &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;top&lt;/code&gt; properties set. (However, these properties will still confusingly be named &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;top&lt;/code&gt;.)&lt;/li&gt;
&lt;li&gt;If a shape doesn't appear on the canvas, check that you remembered to supply a &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; property (for rectangles) or a &lt;code&gt;radius&lt;/code&gt; property (for circles). The &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;top&lt;/code&gt; properties may also have the position of the shape outside the view of the canvas.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Project: Draw a Pine Tree&lt;/h1&gt;
&lt;p&gt;Let's draw a pine tree with the &lt;code&gt;fabric.Triangle&lt;/code&gt; shape. We'll create this tree out of three triangles and one rectangle:&lt;/p&gt;
&lt;canvas id="treeCanvasId" width="200" height="200" style="border: 1px solid black;"&gt;&lt;/canvas&gt;

&lt;script&gt;
// Create a Fabric.js "Canvas" object that is tied to the HTML &lt;canvas&gt; element:
canvasObj = new fabric.Canvas('treeCanvasId');

let branches1 = new fabric.Triangle({
  width: 80, height: 80, fill: '#234236', left: 100, top: 20
});

let branches2 = new fabric.Triangle({
  width: 100, height: 60, fill: '#0C5C4C', left: 90, top: 70
});

let branches3 = new fabric.Triangle({
  width: 110, height: 50, fill: '#38755B', left: 85, top: 110
});

let stump = new fabric.Rect({
  width: 20, height: 30, fill: 'brown', left: 130, top: 150
});

canvasObj.add(branches3);
canvasObj.add(branches2);
canvasObj.add(branches1);
canvasObj.add(stump);
&lt;/script&gt;

&lt;p&gt;The full code is at &lt;a href="https://inventwithpython.com/fabric-js-tutorial/fabric-js-pine-tree.html"&gt;fabric-js-pine-tree.html&lt;/a&gt; or on &lt;a href="https://jsfiddle.net/asweigart/m6pdyrcx/"&gt;JSFiddle&lt;/a&gt;. Let's examine each part individually:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Fabric.js Pine Tree&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;viewport&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cm"&gt;&amp;lt;!-- The HTML &amp;lt;canvas&amp;gt; element: --&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;treeCanvasId&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;border: 1px solid black;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is the standard boilerplate HTML for a web page. Note that the ID we use for the &amp;lt;canvas&amp;gt; element is &lt;code&gt;treeCanvasId&lt;/code&gt; and it is a 200 x 200 pixels in size.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;js&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Canvas&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;tied&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HTML&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;element&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;canvasObj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Canvas&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;treeCanvasId&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;branches1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Triangle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#234236&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;branches2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Triangle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#0C5C4C&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;branches3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Triangle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;110&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#38755B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;110&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First, we need a &lt;code&gt;fabric.Canvas&lt;/code&gt; object to be the Fabric.js representation of the HTML &amp;lt;canvas&amp;gt; element. Then branches of our pine tree will be green triangles that we create by creating &lt;code&gt;fabric.Triangle&lt;/code&gt; objects. These isosceles triangles point upwards, but we'll learn how to rotate them in the next tutorial part.&lt;/p&gt;
&lt;p&gt;Note that the size and position are given the same way as &lt;code&gt;fabric.Rect&lt;/code&gt; rectangles: &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, &lt;code&gt;left&lt;/code&gt;, and &lt;code&gt;top&lt;/code&gt;. We don't specify a triangle's shape and size by specifying three pairs of X and Y coordinates. (For this kind of triangle, we'd use the &lt;code&gt;fabric.Polygon&lt;/code&gt; shape that we'll cover in later tutorial parts.) We color them with different shades of green: &lt;code&gt;#234236&lt;/code&gt;, &lt;code&gt;#0C5C4C&lt;/code&gt;, and &lt;code&gt;#38755B&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;stump&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fabric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Rect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;brown&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;130&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The brown stump will be a rectangle created with a &lt;code&gt;fabric.Rect&lt;/code&gt; object.&lt;/p&gt;
&lt;hr&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;canvasObj.add(branches3);
canvasObj.add(branches2);
canvasObj.add(branches1);
canvasObj.add(stump);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We've created these four shape objects, but they won't appear on the canvas until after we call the &lt;code&gt;add()&lt;/code&gt; method. I want the top branches (in &lt;code&gt;branches1&lt;/code&gt;) to appear on top of the other triangles, so I need to add it last.&lt;/p&gt;
&lt;p&gt;Review:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;fabric.Triangle&lt;/code&gt; shape draws isoceles triangles pointing upwards with &lt;code&gt;let branches1 = new fabric.Triangle({width: 80, height: 80, fill: '#234236', left: 100, top: 20});&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The order that the shapes are created doesn't matter, rather the first shapes added with &lt;code&gt;add()&lt;/code&gt; will be on the bottom of the canvas underneath shapes added later.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This tutorial continues on to &lt;a href="https://inventwithpython.com/blog/fabric-js-tutorial-part-2.html"&gt;Part 2&lt;/a&gt;. Special thanks to &lt;a href="https://bio.link/hunor"&gt;Hunor Márton Borbély&lt;/a&gt; for the &lt;a href="https://svg-tutorial.com/svg/polygon"&gt;SVG tutorial&lt;/a&gt; that inspired this Fabric.js tutorial.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Make Lively Movement Animation with PyTweening's Tweening Functions</title><link href="https://inventwithpython.com/blog/make-lively-movement-animation-with-pytweenings-tweening-functions.html" rel="alternate"></link><published>2024-02-20T12:00:00-05:00</published><updated>2024-02-20T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2024-02-20:/blog/make-lively-movement-animation-with-pytweenings-tweening-functions.html</id><summary type="html">&lt;p&gt;Tweening functions allow you to easily add many different styles of natural-seeming movement to the graphics in your program. In this blog post, you'll learn about how tweening functions can make more lively movement animations using Pygame and the PyTweening third-party library. Tweening functions apply to any programming language, but this tutorial has actual Python code for you to run and experiment with. Start by installing these libraries by running &lt;code&gt;pip install pygame&lt;/code&gt; and &lt;code&gt;pip install pytweening&lt;/code&gt; from the terminal. Then follow along with the code examples.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;Tweening functions allow you to easily add many different styles of natural-seeming movement to the graphics in your program. In this blog post, you'll learn about how tweening functions can make more lively movement animations using Pygame and the PyTweening third-party library. Tweening functions apply to any programming language, but this tutorial has actual Python code for you to run and experiment with. Start by installing these libraries by running &lt;code&gt;pip install pygame&lt;/code&gt; and &lt;code&gt;pip install pytweening&lt;/code&gt; from the terminal. Then follow along with the code examples.&lt;/p&gt;

&lt;p&gt;We won't be looking at "spite animation" where, say, you have several frames of a walking animation that you play in a loop. Instead, we'll focus on making static images move around their window. (Though 2D images are often called "sprites" in a video game context, so that is what this tutorial will call them.) To do this, we move the sprite over a few seconds, draw the sprite to the window, pause for a few milliseconds, and then repeat. This gives the illusion that the sprite is moving around the application's window.&lt;/p&gt;

&lt;h2&gt;Moving a Sprite&lt;/h2&gt;

&lt;p&gt;Here's a Python program using the Pygame library named &lt;a href="https://inventwithpython.com/blogstatic/tweening/tweening_demo_addition.py"&gt;tweening_demo_addition.py&lt;/a&gt; that moves a cat image across the window to the right. The cat starts with its left side at X coordinate 50 and top side at Y coordinate 100. The program keeps adding 10 to it, moving the cat to the right by 10 pixels, until the left side is at 400. Download the &lt;a href="https://inventwithpython.com/cat.png"&gt;cat.png&lt;/a&gt; file from &lt;a href="https://inventwithpython.com/cat.png"&gt;https://inventwithpython.com/cat.png&lt;/a&gt; and save it to the same folder as the following program, &lt;a href="https://inventwithpython.com/blogstatic/tweening/tweening_demo_addition.py"&gt;tweening_demo_addition.py&lt;/a&gt;:&lt;/p&gt;

&lt;textarea readonly rows="12" style="width: 100%; margin-bottom: 20px;"&gt;
# Basic Pygame setup stuff:
import pygame, sys, time
from pygame.locals import QUIT, KEYUP, MOUSEBUTTONUP

pygame.init()  # Initialize the Pygame library.
BG_COLOR = (0, 255, 220)  # The RGB color for the background.
DISPLAY_SURF = pygame.display.set_mode((600, 600))  # Create a 600x600 window.
fps_clock = pygame.time.Clock()  # Frames-per-second timer.

# Get the cat image and set its position:
cat_surface = pygame.Surface((80, 80))
cat_surface.fill((250, 0, 130))  # Use a pink 80x80 square for the "cat"
# If you don't have a cat.png file, comment out this line:
cat_surface = pygame.image.load('cat.png')  # Use a cat image for the cat sprite.
cat_rect = cat_surface.get_rect()
cat_rect.left = 50  # Set left edge of cat sprite at X coordinate 50.
cat_rect.top = 100  # Set top edge of cat sprite at Y coordinate 100.

# Draw the cat and pause for a couple seconds:
DISPLAY_SURF.fill(BG_COLOR)  # Clear the window.
DISPLAY_SURF.blit(cat_surface, cat_rect)  # Draw the cat.
pygame.display.update()  # Render the screen.
pygame.event.get()  # Required on macOS to display the window, for some reason.
time.sleep(2)

debug_output = True
print('DEBUG\tLEFT\tTOP')  # DEBUG OUTPUT
while True:
    # Terminate when the user closes the window or presses a key.
    for event in pygame.event.get():  # Event handling loop.
        if event.type in (QUIT, KEYUP, MOUSEBUTTONUP):
            pygame.quit()
            sys.exit()

    if cat_rect.left &lt; 400:
        # Move the cat to the right by 10 pixels:
        cat_rect.left += 10
        if debug_output:
            print('DEBUG\t', cat_rect.left, '\t', cat_rect.top)  # DEBUG OUTPUT
    else:
        time.sleep(1)  # Pause for a second after the animation is done. (User can't quit during this second. No biggie though.)
        cat_rect.left = 50  # Reset the cat to the start.
        debug_output = False  # Only show debug output for the first movement, then disable it.

    DISPLAY_SURF.fill(BG_COLOR)  # Clear the window.
    DISPLAY_SURF.blit(cat_surface, cat_rect)  # Draw the cat.
    pygame.display.update()  # Render the screen.
    fps_clock.tick(30)  # Run at 30 frames per second.

&lt;/textarea&gt;

&lt;p&gt;&lt;img src="tweenAddition.webp"&gt;&lt;/p&gt;

&lt;p&gt;(If you can't download the &lt;a href="https://inventwithpython.com/cat.png"&gt;cat.png&lt;/a&gt; image file and/or get the &lt;code&gt;FileNotFoundError: No file 'cat.png' found in working directory&lt;/code&gt; error message, just add a &lt;code&gt;#&lt;/code&gt; to the beginning of the &lt;code&gt;cat_surface = pygame.image.load('cat.png')&lt;/code&gt; line to comment it out. The program will use a generic pink square instead.)&lt;/p&gt;

&lt;p&gt;When you run this program, the cat sprite starts at an X coordinate of 50 (in the 600-pixel wide window) and moves to the right by 10 pixels each frame until it reaches the X coordinate 400. (The animation then resets and repeats.) The debug output from the &lt;code&gt;print()&lt;/code&gt; calls displays the &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;top&lt;/code&gt; attributes of the sprite's position:&lt;/p&gt;

&lt;textarea readonly rows="12" style="width: 100%; margin-bottom: 20px;"&gt;
DEBUG   LEFT    TOP
DEBUG    60      100
DEBUG    70      100
DEBUG    80      100
DEBUG    90      100
DEBUG    100     100
DEBUG    110     100
DEBUG    120     100
DEBUG    130     100
DEBUG    140     100
DEBUG    150     100
DEBUG    160     100
DEBUG    170     100
DEBUG    180     100
DEBUG    190     100
DEBUG    200     100
DEBUG    210     100
DEBUG    220     100
DEBUG    230     100
DEBUG    240     100
DEBUG    250     100
DEBUG    260     100
DEBUG    270     100
DEBUG    280     100
DEBUG    290     100
DEBUG    300     100
DEBUG    310     100
DEBUG    320     100
DEBUG    330     100
DEBUG    340     100
DEBUG    350     100
DEBUG    360     100
DEBUG    370     100
DEBUG    380     100
DEBUG    390     100
DEBUG    400     100
&lt;/textarea&gt;

&lt;p&gt;You can see that the cat's X coordinate increases by 10 for each frame while the Y coordinate stays the same. This is a straightforward way to code movement. But first, it can become tedious to program all sprite movements this way (especially when not moving perfectly horizontally or vertically). Second, the movement looks boring: there's no realistic acceleration or slow down, but rather a machine-like instant start, a constant speed, and an instant stop. We can handle the first case with a linear tweening function.&lt;/p&gt;

&lt;p&gt;A &lt;i&gt;tweening function&lt;/i&gt; (also called an &lt;i&gt;easing function&lt;/i&gt;) is used to give XY coordinates "in between" a start and end point. Originally it was used in &lt;a href="https://en.wikipedia.org/wiki/Inbetweening"&gt;traditional cartoon animation&lt;/a&gt;, where an animator would draw &lt;i&gt;key frames&lt;/i&gt; and pass them on to assistants who would draw the "in between" frames of animation. The tween function itself takes a floating-point number input between 0.0 (the start) and 1.0 (the end) called the &lt;i&gt;interpolation factor&lt;/i&gt; and returns a number between 0.0 and 1.0. This can be used to provide a variety of movement animation effects.&lt;/p&gt;

&lt;h2&gt;Interpolation Between a Start and End Point&lt;/h2&gt;

&lt;p&gt;Let's imagine the movement from start to finish as a journey of the value in a variable named &lt;code&gt;i&lt;/code&gt; (the interpolation factor) from &lt;code&gt;0.0&lt;/code&gt; (the start) to &lt;code&gt;1.0&lt;/code&gt; (the end). We can use a couple math equations to &lt;a href="https://en.wikipedia.org/wiki/Linear_interpolation"&gt;interpolate&lt;/a&gt; the X and Y coordinates between the start and end points:&lt;/p&gt;

&lt;p&gt;x = start_x + (i * (end_x - start_x))&lt;/p&gt;

&lt;p&gt;y = start_y + (i * (end_y - start_y))&lt;/p&gt;

&lt;p&gt;With these two equations, we can get the XY coordinate of the midpoint between the start and end XY coordinates by setting &lt;code&gt;i&lt;/code&gt; to 0.5. These equations work no matter what the start and end XY coordinates are. If you set &lt;code&gt;i&lt;/code&gt; to 0.1, then the XY coordinates they give you will be 10% from the starting XY coordinate towards the end XY coordinate. If &lt;code&gt;i&lt;/code&gt; is &lt;code&gt;0.0&lt;/code&gt;, the XY coordinates will be identical to the &lt;code&gt;start_x&lt;/code&gt; and &lt;code&gt;start_y&lt;/code&gt; coordinates. The same goes for setting &lt;code&gt;i&lt;/code&gt; to &lt;code&gt;1.0&lt;/code&gt;: you get the end XY coordinates.&lt;/p&gt;

&lt;p&gt;Now we don't have to worry about adding/subtracting the correct amount to the sprite's position. Instead, we can &lt;i&gt;calculate&lt;/i&gt; each position along the path.&lt;/p&gt;

&lt;p&gt;The following &lt;a href="https://inventwithpython.com/blogstatic/tweening/tweening_demo_interpolation.py"&gt;tweening_demo_interpolation.py&lt;/a&gt; program is similar to the previous program, except now we use the two interpolation equations and only increase the &lt;code&gt;i&lt;/code&gt; interpolation factor value:&lt;/p&gt;

&lt;textarea readonly rows="12" style="width: 100%; margin-bottom: 20px;"&gt;
# You can try changing the start/end positions:
START_X, START_Y = 50, 100
END_X, END_Y = 370, 250


# Basic Pygame setup stuff:
import pygame, sys, time, random
from pygame.locals import QUIT, KEYUP, MOUSEBUTTONUP

pygame.init()  # Initialize the Pygame library.
BG_COLOR = (0, 255, 220)  # The RGB color for the background.
DISPLAY_SURF = pygame.display.set_mode((600, 600))  # Create a 600x600 window.
fps_clock = pygame.time.Clock()  # Frames-per-second timer.

# Get the cat image and set its position:
cat_surface = pygame.Surface((80, 80))
cat_surface.fill((250, 0, 130))  # Use a pink 80x80 square for the "cat"
# If you don't have a cat.png file, comment out this line:
cat_surface = pygame.image.load('cat.png')
cat_rect = cat_surface.get_rect()
cat_rect.left = START_X
cat_rect.top = START_Y
i = 0.0  # The interpolation factor starts at 0.0.

# Draw the cat and pause for a second:
DISPLAY_SURF.fill(BG_COLOR)  # Clear the window.
pygame.draw.rect(DISPLAY_SURF, (255, 255, 255), (START_X, START_Y, cat_rect.width, cat_rect.height))  # Draw white rectangle at start.
pygame.draw.rect(DISPLAY_SURF, (0, 0, 0), (END_X, END_Y, cat_rect.width, cat_rect.height))  # Draw black rectangle at end.
DISPLAY_SURF.blit(cat_surface, cat_rect)  # Draw the cat.
pygame.display.update()  # Render the screen.
pygame.event.get()  # Required on macOS to display the window, for some reason.
time.sleep(1)  # Pause at the start before moving.

debug_output = True
print('DEBUG\ti\tLEFT\tTOP')  # DEBUG OUTPUT
while True:
    # Terminate when the user closes the window or presses a key.
    for event in pygame.event.get():  # Event handling loop.
        if event.type in (QUIT, KEYUP, MOUSEBUTTONUP):
            pygame.quit()
            sys.exit()

    if round(i, 5) &lt;= 1.0:  # round() here is necessary for a weird float rounding issue.
        # Set cat position based on the interpolation factor.
        cat_rect.left = START_X + (i * (END_X - START_X))
        cat_rect.top = START_Y + (i * (END_Y - START_Y))
        if debug_output:
            print('DEBUG\t', round(i, 3), '\t', cat_rect.left, '\t', cat_rect.top)  # DEBUG OUTPUT

        i += 0.02  # Increase the interpolation factor.
    else:
        time.sleep(1)  # Pause for a second after the animation is done. (User can't quit during this second. No biggie though.)
        i = 0.0  # Reset the interpolation factor to 0.0.
        debug_output = False

        # Uncomment the following to randomly change the start &amp; stop positions:
        # START_X = random.randint(0, 600 - cat_rect.width)
        # START_Y = random.randint(0, 600 - cat_rect.height)
        # END_X = random.randint(0, 600 - cat_rect.width)
        # END_Y = random.randint(0, 600 - cat_rect.height)

    DISPLAY_SURF.fill(BG_COLOR)  # Clear the window.
    pygame.draw.rect(DISPLAY_SURF, (255, 255, 255), (START_X, START_Y, cat_rect.width, cat_rect.height))  # Draw white rectangle at start.
    pygame.draw.rect(DISPLAY_SURF, (0, 0, 0), (END_X, END_Y, cat_rect.width, cat_rect.height))  # Draw black rectangle at end.
    DISPLAY_SURF.blit(cat_surface, cat_rect)  # Draw the cat.
    pygame.display.update()  # Render the screen.
    fps_clock.tick(30)  # Run at 30 frames per second.
&lt;/textarea&gt;

&lt;p&gt;&lt;img src="tweenInterpolation.webp"&gt;&lt;/p&gt;

&lt;p&gt;When you run this program, you'll see the same smooth, constant movement of the cat (though this time at an angle instead of perfectly horizontal.) Try editing the values of the &lt;code&gt;START_X&lt;/code&gt;, &lt;code&gt;START_Y&lt;/code&gt;, &lt;code&gt;END_X&lt;/code&gt;, and &lt;code&gt;END_Y&lt;/code&gt; variables to change the start and end position of the sprite. You won't need to change any other code; the same code works for the path from any start to any end. The program can set up random start and end points if you uncomment the code after &lt;code&gt;# Uncomment the following to randomly change the start &amp; stop positions:&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When you run the program with the start point as 50, 100 and end point as 370, 250, the debugging output is:&lt;/p&gt;

&lt;textarea readonly rows="12" style="width: 100%; margin-bottom: 20px;"&gt;
DEBUG   i   LEFT    TOP
DEBUG    0.0     50      100
DEBUG    0.02    56      103
DEBUG    0.04    63      106
DEBUG    0.06    69      109
DEBUG    0.08    76      112
DEBUG    0.1     82      115
DEBUG    0.12    88      118
DEBUG    0.14    95      121
DEBUG    0.16    101     124
DEBUG    0.18    108     127
DEBUG    0.2     114     130
DEBUG    0.22    120     133
DEBUG    0.24    127     136
DEBUG    0.26    133     139
DEBUG    0.28    140     142
DEBUG    0.3     146     145
DEBUG    0.32    152     148
DEBUG    0.34    159     151
DEBUG    0.36    165     154
DEBUG    0.38    172     157
DEBUG    0.4     178     160
DEBUG    0.42    184     163
DEBUG    0.44    191     166
DEBUG    0.46    197     169
DEBUG    0.48    204     172
DEBUG    0.5     210     175
DEBUG    0.52    216     178
DEBUG    0.54    223     181
DEBUG    0.56    229     184
DEBUG    0.58    236     187
DEBUG    0.6     242     190
DEBUG    0.62    248     193
DEBUG    0.64    255     196
DEBUG    0.66    261     199
DEBUG    0.68    268     202
DEBUG    0.7     274     205
DEBUG    0.72    280     208
DEBUG    0.74    287     211
DEBUG    0.76    293     214
DEBUG    0.78    300     217
DEBUG    0.8     306     220
DEBUG    0.82    312     223
DEBUG    0.84    319     226
DEBUG    0.86    325     229
DEBUG    0.88    332     232
DEBUG    0.9     338     235
DEBUG    0.92    344     238
DEBUG    0.94    351     241
DEBUG    0.96    357     244
DEBUG    0.98    364     247
DEBUG    1.0     370     250
&lt;/textarea&gt;

&lt;h2&gt;Linear Tween&lt;/h2&gt;

&lt;p&gt;We can enhance the interpolation equations into something a bit more exciting with tweening functions. We'll start with a boring example of the linear tween function that produces the same behavior as our interpolation code. The &lt;a href="https://pypi.org/project/pytweening/"&gt;PyTweening library&lt;/a&gt; It's boring because the returned output is the same as the input: &lt;code&gt;pytweening.linear(0.0)&lt;/code&gt; returns &lt;code&gt;0.0&lt;/code&gt; and &lt;code&gt;pytweening.linear(1.0)&lt;/code&gt; returns &lt;code&gt;1.0&lt;/code&gt;. Run the following code to verify this:&lt;/p&gt;

&lt;textarea readonly rows="12" style="width: 100%; margin-bottom: 20px;"&gt;
import pytweening
i = 0.0
while i &lt;= 1.0:
    print(round(i, 2), '\t', round(pytweening.linear(i), 2))
    i += 0.1
&lt;/textarea&gt;

&lt;p&gt;The output of this is:&lt;/p&gt;

&lt;textarea readonly rows="12" style="width: 100%; margin-bottom: 20px;"&gt;
0.0      0.0
0.1      0.1
0.2      0.2
0.3      0.3
0.4      0.4
0.5      0.5
0.6      0.6
0.7      0.7
0.8      0.8
0.9      0.9
1.0      1.0
&lt;/textarea&gt;

&lt;p&gt;As you can see, the output number is the same as the input. But this linear tweening function will make our animation program easy to understand at first. Later, all we need to do is swap out a different tweening function from PyTweening to get different effects.&lt;/p&gt;

&lt;p&gt;If you look at the &lt;a href="https://github.com/asweigart/pytweening/blob/master/pytweening/__init__.py"&gt;source of the Pytweening library&lt;/a&gt;, you'll notice the code for &lt;code&gt;pytweening.linear()&lt;/code&gt; (minus the docstring) is just one line code:&lt;/p&gt;

&lt;textarea readonly rows="12" style="width: 100%; margin-bottom: 20px;"&gt;
def linear(n):  # type: (Union[int, float]) -&gt; Union[int, float]
    return n
&lt;/textarea&gt;

&lt;p&gt;While the linear tween is the simplest of all tweening functions, the others aren't much more complicated:&lt;/p&gt;

&lt;textarea readonly rows="12" style="width: 100%; margin-bottom: 20px;"&gt;
def easeOutQuad(n):  # type: (Union[int, float]) -&gt; Union[int, float]
    return -n * (n - 2)
&lt;/textarea&gt;

&lt;p&gt;They are very math-y, but we'll look at them in detail later. Run the following code with &lt;code&gt;pytweening.easeOutQuad()&lt;/code&gt; instead of the previous example's &lt;code&gt;pytweening.linear()&lt;/code&gt;:&lt;/p&gt;

&lt;textarea readonly rows="12" style="width: 100%; margin-bottom: 20px;"&gt;
import pytweening
i = 0.0
while i &lt;= 1.0:
    print(round(i, 2), '\t', round(pytweening.easeOutQuad(i), 2))
    i += 0.1
&lt;/textarea&gt;

&lt;p&gt;The output of this is:&lt;/p&gt;

&lt;textarea readonly rows="12" style="width: 100%; margin-bottom: 20px;"&gt;
0.0      0.0
0.1      0.19
0.2      0.36
0.3      0.51
0.4      0.64
0.5      0.75
0.6      0.84
0.7      0.91
0.8      0.96
0.9      0.99
1.0      1.0
&lt;/textarea&gt;

&lt;p&gt;Notice that while &lt;code&gt;easeOutQuad()&lt;/code&gt;'s numbers are different from &lt;code&gt;linear()&lt;/code&gt;'s numbers, note that the outputs are both 0.0 when the interpolation factor (the input) is 0.0 and 1.0 when the interpolation factor is 1.0. This is common for almost every tweening function so that puts the sprite at the starting XY position at the start of the movement and the ending XY position at the end of the movement. It's the numbers in between that give different movement behavior.&lt;/p&gt;

&lt;p&gt;Let's swap out our interpolation code with code that uses PyTweening's tweening functions. The following is &lt;a href="https://inventwithpython.com/blogstatic/tweening/tweening_demo_pytweening.py"&gt;tweening_demo_pytweening.py&lt;/a&gt;, which has the same behavior as the previous &lt;a href="https://inventwithpython.com/blogstatic/tweening/tweening_demo_interpolation.py"&gt;tweening_demo_interpolation.py&lt;/a&gt; program but uses the PyTweening library instead:&lt;/p&gt;

&lt;textarea readonly rows="12" style="width: 100%; margin-bottom: 20px;"&gt;
# You can try changing the start/end positions:
START_X, START_Y = 50, 100
END_X, END_Y = 370, 250


# Basic Pygame setup stuff:
import pygame, sys, time, random, pytweening
from pygame.locals import QUIT, KEYUP, MOUSEBUTTONUP

pygame.init()  # Initialize the Pygame library.
BG_COLOR = (0, 255, 220)  # The RGB color for the background.
DISPLAY_SURF = pygame.display.set_mode((600, 600))  # Create a 600x600 window.
fps_clock = pygame.time.Clock()  # Frames-per-second timer.

# Get the cat image and set its position:
cat_surface = pygame.Surface((80, 80))
cat_surface.fill((250, 0, 130))  # Use a pink 80x80 square for the "cat"
# If you don't have a cat.png file, comment out this line:
cat_surface = pygame.image.load('cat.png')
cat_rect = cat_surface.get_rect()  # Get the position &amp; size rectangle of the cat.
i = 0.0  # The interpolation factor starts at 0.0.
cat_rect.left = START_X
cat_rect.top = START_Y

# Draw the cat for a couple seconds before starting the animation:
DISPLAY_SURF.fill(BG_COLOR)  # Clear the window.
pygame.draw.rect(DISPLAY_SURF, (255, 255, 255), (START_X, START_Y, cat_rect.width, cat_rect.height))  # Draw white rectangle at start.
pygame.draw.rect(DISPLAY_SURF, (0, 0, 0), (END_X, END_Y, cat_rect.width, cat_rect.height))  # Draw black rectangle at end.
DISPLAY_SURF.blit(cat_surface, cat_rect)  # Draw the cat.
pygame.display.update()  # Render the screen.
pygame.event.get()  # Needed on macOS to make the window appear for some reason.
time.sleep(1)  # Pause at the start before moving.

# Run the animation:
debug_output = True
print('DEBUG\ti\tti\tLEFT\tTOP')  # DEBUG OUTPUT
while True:
    # Terminate when the user closes the window, presses a key, or clicks the mouse:
    for event in pygame.event.get():  # Event handling loop.
        if event.type in (QUIT, KEYUP, MOUSEBUTTONUP):
            pygame.quit()
            sys.exit()

    if round(i, 5) &lt;= 1.0:  # round() here is necessary for a weird float rounding issue.
        # i is the input, ti is the tween output.
        # Uncomment one of these lines to select a different tweening function:
        ti = pytweening.linear(i)  # Same speed throughout
        # ti = pytweening.easeOutQuad(i)  # Starts fast, slows to stop
        # ti = pytweening.easeInQuad(i)  # Starts slow, accelerates, then decelerates
        # ti = pytweening.easeInOutQuad(i)  # Starts and ends slow
        # ti = pytweening.easeOutBounce(i)  # Ends with a bounce
        # ti = pytweening.easeInOutElastic(i)  # Start and end with a stretchy animation

        # Set cat position based on the tweening interpolation factor.
        cat_rect.left = START_X + (ti * (END_X - START_X))
        cat_rect.top = START_Y + (ti * (END_Y - START_Y))
        if debug_output:
            print('DEBUG\t', round(i, 3), '\t', round(ti, 3), '\t', cat_rect.left, '\t', cat_rect.top)  # DEBUG OUTPUT

        i += 0.02  # Increase the interpolation factor.
    else:
        time.sleep(1)  # Pause for a second after the animation is done. (User can't quit during this second. No biggie though.)
        i = 0.0  # Reset the interpolation factor to 0.0.
        debug_output = False  # Disable the debug output.
        # Uncomment the following to randomly change the start &amp; stop positions:
        # START_X = random.randint(0, 600 - cat_rect.width)
        # START_Y = random.randint(0, 600 - cat_rect.height)
        # END_X = random.randint(0, 600 - cat_rect.width)
        # END_Y = random.randint(0, 600 - cat_rect.height)

    DISPLAY_SURF.fill(BG_COLOR)  # Clear the window.
    pygame.draw.rect(DISPLAY_SURF, (255, 255, 255), (START_X, START_Y, cat_rect.width, cat_rect.height))  # Draw white rectangle at start.
    pygame.draw.rect(DISPLAY_SURF, (0, 0, 0), (END_X, END_Y, cat_rect.width, cat_rect.height))  # Draw black rectangle at end.
    DISPLAY_SURF.blit(cat_surface, cat_rect)  # Draw the cat.
    pygame.display.update()  # Render the screen.
    fps_clock.tick(30)  # Pause enough to run at 30 frames per second.
&lt;/textarea&gt;

&lt;p&gt;The debug output from this program looks like this:&lt;/p&gt;


&lt;textarea readonly rows="12" style="width: 100%; margin-bottom: 20px;"&gt;
DEBUG   i   ti  LEFT    TOP
DEBUG    0.0     0.0     50      100
DEBUG    0.02    0.02    56      103
DEBUG    0.04    0.04    63      106
DEBUG    0.06    0.06    69      109
DEBUG    0.08    0.08    76      112
DEBUG    0.1     0.1     82      115
DEBUG    0.12    0.12    88      118
DEBUG    0.14    0.14    95      121
DEBUG    0.16    0.16    101     124
DEBUG    0.18    0.18    108     127
DEBUG    0.2     0.2     114     130
DEBUG    0.22    0.22    120     133
DEBUG    0.24    0.24    127     136
DEBUG    0.26    0.26    133     139
DEBUG    0.28    0.28    140     142
DEBUG    0.3     0.3     146     145
DEBUG    0.32    0.32    152     148
DEBUG    0.34    0.34    159     151
DEBUG    0.36    0.36    165     154
DEBUG    0.38    0.38    172     157
DEBUG    0.4     0.4     178     160
DEBUG    0.42    0.42    184     163
DEBUG    0.44    0.44    191     166
DEBUG    0.46    0.46    197     169
DEBUG    0.48    0.48    204     172
DEBUG    0.5     0.5     210     175
DEBUG    0.52    0.52    216     178
DEBUG    0.54    0.54    223     181
DEBUG    0.56    0.56    229     184
DEBUG    0.58    0.58    236     187
DEBUG    0.6     0.6     242     190
DEBUG    0.62    0.62    248     193
DEBUG    0.64    0.64    255     196
DEBUG    0.66    0.66    261     199
DEBUG    0.68    0.68    268     202
DEBUG    0.7     0.7     274     205
DEBUG    0.72    0.72    280     208
DEBUG    0.74    0.74    287     211
DEBUG    0.76    0.76    293     214
DEBUG    0.78    0.78    300     217
DEBUG    0.8     0.8     306     220
DEBUG    0.82    0.82    312     223
DEBUG    0.84    0.84    319     226
DEBUG    0.86    0.86    325     229
DEBUG    0.88    0.88    332     232
DEBUG    0.9     0.9     338     235
DEBUG    0.92    0.92    344     238
DEBUG    0.94    0.94    351     241
DEBUG    0.96    0.96    357     244
DEBUG    0.98    0.98    364     247
DEBUG    1.0     1.0     370     250
&lt;/textarea&gt;

&lt;p&gt;Instead of using the interpolation factor &lt;code&gt;i&lt;/code&gt; variable directly in the interpolation code, this program passes &lt;code&gt;i&lt;/code&gt; to &lt;code&gt;pytweening.linear()&lt;/code&gt; to get the "tweened" interpolation factor for the &lt;code&gt;ti&lt;/code&gt; variable. This &lt;code&gt;ti&lt;/code&gt; variable is used in the familiar interpolation code instead of &lt;code&gt;i&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This doesn't make a difference for a linear tween because &lt;code&gt;ti&lt;/code&gt; is always the same as &lt;code&gt;i&lt;/code&gt;. But now we can swap out tweening functions in the PyTweening library to select what kind of movement behavior we want. For example, let's use the &lt;code&gt;pytweening.easeOutQuad()&lt;/code&gt; function by commenting out the &lt;code&gt;ti = pytweening.linear(i)&lt;/code&gt; and then uncommenting the &lt;code&gt;# ti = pytweening.easeOutQuad(i)&lt;/code&gt; line from this part of the &lt;a href="https://inventwithpython.com/blogstatic/tweening/tweening_demo_pytweening.py"&gt;tweening_demo_pytweening.py&lt;/a&gt; program:&lt;/p&gt;

&lt;pre&gt;# Uncomment one of these lines to select a different tweening function:
ti = pytweening.linear(i)  # Same speed throughout
# ti = pytweening.easeOutQuad(i)  # Starts fast, slows to stop
# ti = pytweening.easeInQuad(i)  # Starts slow, accelerates, then decelerates
# ti = pytweening.easeInOutQuad(i)  # Starts and ends slow
# ti = pytweening.easeOutBounce(i)  # Ends with a bounce
# ti = pytweening.easeInOutElastic(i)  # Start and end with a stretchy animation&lt;/pre&gt;

&lt;p&gt;With this one-line code change, you've changed the machine-like constant speed movement to a more natural, starts-fast-then-slows-to-a-stop movement. It's small details like this that can give the sprite movement a much more polished presentation. This concept is sometimes called "juice", and there is &lt;a href="https://www.youtube.com/watch?v=Fy0aCDmgnxg"&gt;a great talk by Martin Jonasson and Petri Purho from Nordic Game Jam called "Juice It or Lose It" that explains several simple ways (including tweening functions) to improve your video games and animation.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These short videos also have great tips for adding polish to your games and animations:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=cbZx2bSw4k4"&gt;Tips &amp; Tricks for Juicy Games by Skinner Space&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=WO_kBYqAQwU"&gt;BETTER 2D visuals in 7 EASY TIPS by MrEliptik&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=Bsy8pknHc0M"&gt;5 tips for better platformer controls by The Shaggy Dev&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=WwOhDkjL8EU"&gt;The Secrets That Make Platformers Feel Good || Metroidvania Devlog by nextProgram&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=wC9iu7cuQjI"&gt;Creating SMART enemies from scratch! | Devlog by Challacade&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try uncommenting other lines and re-running the program to see the effect of different tweens. Notice that the &lt;code&gt;easeOutBounce()&lt;/code&gt; tween produces output that reverses direction to give a bouncy effect, while the &lt;code&gt;easeInOutElastic()&lt;/code&gt; function produces output that is less than 0.0 and greater than 1.0 as the animation briefly shoots past the endpoint in an elastic rubber band effect.&lt;/p&gt;

&lt;p&gt;If you want to tightly control the value of the &lt;code&gt;i&lt;/code&gt; interpolation factor, try the following &lt;a href="https://inventwithpython.com/blogstatic/tweening/tweening_demo_all.py"&gt;tweening_demo_all.py&lt;/a&gt; program, which displays six cats using six different tweening functions from the PyTweening library. As you move the mouse cursor from the left edge of the window to the right edge, the interpolation factor goes from 0.0 to 1.0:&lt;/p&gt;


&lt;textarea readonly rows="12" style="width: 100%; margin-bottom: 20px;"&gt;
import pytweening

# Try changing the various tween functions here:
TWEENS = [pytweening.linear, pytweening.easeOutQuad, pytweening.easeInQuad, pytweening.easeInOutQuad, pytweening.easeOutBounce, pytweening.easeInOutElastic]


# Basic Pygame setup stuff:
import pygame, sys, time, random
from pygame.locals import QUIT, KEYUP, MOUSEBUTTONUP, MOUSEMOTION

pygame.init()  # Initialize the Pygame library.
BG_COLOR = (0, 255, 220)  # The RGB color for the background.
DISPLAY_SURF = pygame.display.set_mode((600, 600))  # Create a 600x600 window.
fps_clock = pygame.time.Clock()  # Frames-per-second timer.

# Don't change these:
START_X, START_Y = 50, 0
END_X, END_Y = 400, 0

# Get the cat image and set its position:
cats = []
for idx in range(6):  # There are six cats that will be animated:
    cat = {}
    cat['surface'] = pygame.Surface((80, 80))
    cat['surface'].fill((250, 0, 130))  # Use a pink 80x80 square for the "cat"
    # If you don't have a cat.png file, comment out this line:
    cat['surface'] = pygame.image.load('cat.png')
    cat['rect'] = cat['surface'].get_rect()  # Get the position &amp; size rectangle of the cat.
    cat['rect'].left = START_X
    cat['rect'].top = START_Y + (idx * 100)
    cat['tween'] = TWEENS[idx]
    cats.append(cat)

i = 0.0  # The interpolation factor starts at 0.0.

font = pygame.font.Font('freesansbold.ttf', 28)

# Run the animation:
while True:
    # Terminate when the user closes the window, presses a key, or clicks the mouse:
    for event in pygame.event.get():  # Event handling loop.
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == MOUSEMOTION:
            mouse_x, mouse_y = event.pos
            i = mouse_x / 600  # Set i based on mouse cursor position in the window.

    for idx in range(6):
        ti = cats[idx]['tween'](i)  # i is the input, ti is the tween output.

        # Set cat position based on the tweening interpolation factor.
        cats[idx]['rect'].left = START_X + (ti * (END_X - START_X))

    DISPLAY_SURF.fill(BG_COLOR)  # Clear the window.
    for idx in range(6):  # Draw all six cats.
        pygame.draw.rect(DISPLAY_SURF, (255, 255, 255), (START_X, START_Y + (idx * 100), cats[idx]['rect'].width, cats[idx]['rect'].height))  # Draw white rectangle at start.
        pygame.draw.rect(DISPLAY_SURF, (0, 0, 0), (END_X, END_Y + (idx * 100), cats[idx]['rect'].width, cats[idx]['rect'].height))  # Draw black rectangle at end.
        DISPLAY_SURF.blit(cats[idx]['surface'], cats[idx]['rect'])  # Draw the cat.

    # Set window caption to the current i value:
    pygame.display.set_caption('Move mouse to adjust: i = ' + str(round(i, 3)))

    pygame.display.update()  # Render the screen.
    fps_clock.tick(30)  # Pause enough to run at 30 frames per second.
&lt;/textarea&gt;

&lt;p&gt;&lt;img src="tweenAll.webp"&gt;&lt;/p&gt;

&lt;p&gt;You can use the &lt;a href="https://inventwithpython.com/blogstatic/tweening/tweening_demo_all.py"&gt;tweening_demo_all.py&lt;/a&gt; program to explore the different tweening functions in a way that you can physically control by moving the mouse.&lt;/p&gt;

&lt;p&gt;For further information check out &lt;a href="https://pypi.org/project/pytweening/"&gt;the PyPI page for PyTweening&lt;/a&gt; (which contains graphs of each tweening function) and &lt;a href="https://github.com/asweigart/pytweening/"&gt;the PyTweening source code on GitHub&lt;/a&gt;.&lt;/p&gt;


</content><category term="misc"></category></entry><entry><title>Book Review: Geometric Patterns with Creative Coding</title><link href="https://inventwithpython.com/blog/book-review-geometric-patterns-with-creative-coding.html" rel="alternate"></link><published>2024-01-15T12:00:00-05:00</published><updated>2024-01-15T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2024-01-15:/blog/book-review-geometric-patterns-with-creative-coding.html</id><summary type="html">&lt;p&gt;4 stars. I came across Selçuk Artut’s &lt;a target="_blank" href="https://www.amazon.com/Geometric-Patterns-Creative-Coding-Arts/dp/1484293886/?&amp;_encoding=UTF8&amp;tag=playwithpyth-20&amp;linkCode=ur2&amp;linkId=da4f7d52cca63c74b224bc92bc9bad2d&amp;camp=1789&amp;creative=9325"&gt;Geometric Patterns with Creative Coding&lt;/a&gt; while researching several topics on computer-generated art for beginners. I admit, the single 1-star review for this book annoyed me enough to read and review. I’ve come away believing that this book certainly deserves wider and better attention than that.&lt;/p&gt;</summary><content type="html">

&lt;blockquote&gt;&lt;a target="_blank" href="https://www.amazon.com/Geometric-Patterns-Creative-Coding-Arts/dp/1484293886/?&amp;amp;_encoding=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;linkCode=ur2&amp;amp;linkId=da4f7d52cca63c74b224bc92bc9bad2d&amp;amp;camp=1789&amp;amp;creative=9325"&gt;Geometric Patterns with Creative Coding: Coding for the Arts&lt;/a&gt; by Selçuk Artut. Published by Apress July 2023. 420 pages.&lt;/blockquote&gt;

&lt;h2&gt;4 stars. A fine introduction for programmers and non-programming artists alike.&lt;/h2&gt;

&lt;p&gt;I came across Selçuk Artut’s Geometric Patterns with Creative Coding while researching several topics on computer-generated art for beginners. I admit, the single 1-star review for this book annoyed me enough to read and review. I’ve come away believing that this book certainly deserves wider and better attention than that.&lt;/p&gt;

&lt;p&gt;This book is a decent starting point for anyone who wants to program computers to create generative art. The first several chapters of art history are slim summaries, but they give you plenty of things to look up later if you’re interested (though you can also skip these chapters entirely if you aren’t.) The real meat of the book starts about 60 pages in with the briefest introductions to programming concepts before the next 300 pages have example after example of interesting tessellations (23 in total) along with code and breakdowns of the pattern motifs.&lt;/p&gt;

&lt;p&gt;The primary downside of this book is that it doesn’t offer a lot of hand-holding. The explanation of the programming and math concepts is sufficient, but a bit more elaboration would help some readers with coding experience. But you certainly don’t need to be a programmer or “good at math” to follow along.&lt;/p&gt;

&lt;p&gt;This book gives back as much as you put in: you have to put aside any feelings of intimidation and be willing to dive into the examples. (You don’t even need to install anything; you can use the browser-based Processing interpreter at &lt;a href="https://editor.p5js.org/"&gt;https://editor.p5js.org/&lt;/a&gt; to run the example art programs.) The primary utility comes from the many tessellating pattern programs it offers. Take the time to understand them. Make your small modifications to the code and watch how they change the resulting artwork. They are examples to be studied rather than just read once.&lt;/p&gt;

&lt;p&gt;This book is rather raw and unpolished. The art history chapters are the briefest introduction and the programming and math sections make it hard for the non-programmer to figure out how to apply these ideas. I give this 4 stars because I feel there’s a lot that could be improved: comments in the source code could add more context, descriptive variable names instead of single letters, links to source code and other resources for further reading. All of the projects are basic tiled patterns of lines and arcs. It misses out on exciting patterns that could be demonstrated using Bézier curves, randomness, and recursion.&lt;/p&gt;

&lt;p&gt;However, I still think this is a good book as long as it isn’t your only resource on Processing and algorithmic art. It should be on your bookshelf whether you are a teen or an adult, a software engineer or a hobbyist. The diagrams for the patterns are clear and descriptive. The 23 patterns (along with the source code that generates them) make for great programming practice for anyone who wants to start into the world of computer art.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Additional: I highly recommend &lt;a href="https://www.youtube.com/watch?v=4Se0_w0ISYk"&gt;Tim Holman's CSSConf Australia talk, "Generative Art Speedrun"&lt;/a&gt; for a great and concise explanation on generative art concepts.&lt;/em&gt;&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>What's New in the 3rd Edition of "Automate the Boring Stuff with Python"</title><link href="https://inventwithpython.com/blog/whats-new-in-the-3rd-edition-of-automate-the-boring-stuff-with-python.html" rel="alternate"></link><published>2023-11-09T12:00:00-05:00</published><updated>2023-11-09T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2023-11-09:/blog/whats-new-in-the-3rd-edition-of-automate-the-boring-stuff-with-python.html</id><summary type="html">&lt;p&gt;The third edition is has new chapters, programming projects, and revised editing. The third party module coverage has been updated to the latest available version (including OpenPyXL). You can run &lt;code&gt;pip install automateboringstuff3&lt;/code&gt; to install the latest version of all modules covered in the book that are compatible with the book's code examples.&lt;br&gt;&lt;a href="https://nostarch.com/automate-boring-stuff-python-3rd-edition"&gt;&lt;img src="/images/cover-automate3-thumb.webp" /&gt;&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;The third edition is has new chapters, programming projects, and revised editing. The third party module coverage has been updated to the latest available version (including OpenPyXL). You can run &lt;code&gt;pip install automateboringstuff3&lt;/code&gt; to install the latest version of all modules covered in the book that are compatible with the book's code examples.&lt;br&gt;&lt;a href="https://nostarch.com/automate-boring-stuff-python-3rd-edition"&gt;&lt;img src="/images/cover-automate3-thumb.webp" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Updated June 20, 2025&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Like the previous editions, the third edition is released under a &lt;a href="https://creativecommons.org/"&gt;Creative Commons license&lt;/a&gt; and will be free to read online.&lt;/p&gt;
&lt;p&gt;Some of the new content includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Tips on how to use ChatGPT and other Large Language Model AIs to learn to code as well as common pitfalls to avoid.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Making your programs talk with text-to-speech libraries &lt;a href="https://pypi.org/project/pyttsx3/"&gt;pyttsx3&lt;/a&gt; and &lt;a href="https://pypi.org/project/gTTS/"&gt;gTTS&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Speech recognition with OpenAI's &lt;a href="https://pypi.org/project/openai-whisper/"&gt;Whisper&lt;/a&gt; library to create text transcriptions from audio and video files.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Doing OCR to extract text from images with &lt;a href="https://pypi.org/project/PyTesseract/"&gt;PyTesseract&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Using the &lt;a href=""&gt;NAPS2 application&lt;/a&gt; to create PDFs from images with embedded OCRed text.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;!--* Recording video, images, and audio from your computer's webcam and microphone.--&gt;

&lt;!--* Making programs that can play videos and sound files in your program.--&gt;

&lt;ul&gt;
&lt;li&gt;Downloading videos from YouTube and other video websites with &lt;a href="https://pypi.org/project/yt-dlp/"&gt;yt-dlp&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;!--* Edit, merge, split, and convert video and audio files using &lt;a href=""&gt;ffmpeg&lt;/a&gt;.--&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Updated PDF operations using &lt;a href="https://pypi.org/project/pypdf/"&gt;PyPDF&lt;/a&gt; and &lt;a href="https://pypi.org/project/pdfminer/"&gt;PdfMiner&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A complete introduction to &lt;a href="https://www.sqlite.org/index.html"&gt;SQLite&lt;/a&gt; and relational databases with Python's &lt;a href=""&gt;sqlite3&lt;/a&gt; module.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How to compile Python scripts into executable programs with &lt;a href="https://pypi.org/project/pyinstaller/"&gt;PyInstaller&lt;/a&gt; on Windows, macOS, and Linux.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Covering &lt;a href="https://pypi.org/project/playwright/"&gt;Playwright&lt;/a&gt; in addition to &lt;a href="https://pypi.org/project/Selenium/"&gt;Selenium&lt;/a&gt; for controlling web browsers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sending push notifications to your phone with &lt;a href="https://ntfy.sh"&gt;https://ntfy.sh&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In addition to JSON and CVS formatted data, how to work with XML&lt;!--, TOML, and YAML--&gt; files as well.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creating graphs with &lt;a href="https://pypi.org/project/matplotlib/"&gt;matplotlib&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copying and pasting images to the clipboard with &lt;a href="https://pypi.org/project/pyperclipimg/"&gt;pyperclipimg&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;a href="https://pypi.org/project/humre/"&gt;Humre&lt;/a&gt; module for making regular expressions easier to use.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have questions about the third edition or its content, you can post them on the &lt;a href="https://www.reddit.com/r/inventwithpython/"&gt;/r/inventwithpython&lt;/a&gt; subreddit.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Using ChatGPT-4 to Review My Recursion Programming Book</title><link href="https://inventwithpython.com/blog/using-chatgpt-4-to-review-my-recursion-programming-book.html" rel="alternate"></link><published>2023-06-15T12:00:00-04:00</published><updated>2023-06-15T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2023-06-15:/blog/using-chatgpt-4-to-review-my-recursion-programming-book.html</id><summary type="html">&lt;p&gt;My 2022 book, &lt;i&gt;The Recursive Book of Recursion&lt;/i&gt; (&lt;a href="https://inventwithpython.com/recursion/"&gt;read online for free&lt;/a&gt;, &lt;a href="https://nostarch.com/recursive-book-recursion"&gt;buy direct from the publisher&lt;/a&gt;) covers recursive algorithms, a notoriously tricky subject for programmers and computer science students. I feel like I did a good job writing it (and my editors at No Starch Press did an incredible job editing it), but I wondered how well Large Language Models (LLMs) like ChatGPT could understand it. I ran the entire book through to see what mistakes or changes ChatGPT would make. The results were disappointing in some places but pleasantly surprising in others, so I wrote this blog post about the role AI could play in editing technical books.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;My 2022 book, &lt;i&gt;The Recursive Book of Recursion&lt;/i&gt; (&lt;a href="https://inventwithpython.com/recursion/"&gt;read online for free&lt;/a&gt;, &lt;a href="https://nostarch.com/recursive-book-recursion"&gt;buy direct from the publisher&lt;/a&gt;) covers recursive algorithms, a notoriously tricky subject for programmers and computer science students. I feel like I did a good job writing it (and my editors at No Starch Press did an incredible job editing it), but I wondered how well Large Language Models (LLMs) like ChatGPT could understand it. I ran the entire book through to see what mistakes or changes ChatGPT would make. The results were disappointing in some places but pleasantly surprising in others, so I wrote this blog post about the role AI could play in editing technical books.&lt;/p&gt;

&lt;p&gt;Here's a summary of my findings:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;ChatGPT absolutely cannot replace humans or reduce headcount. It's just a tool.&lt;/li&gt;
  &lt;li&gt;The majority of ChatGPT's feedback is useless, if not wrong.&lt;/li&gt;
  &lt;li&gt;ChatGPT is a tool for writers/technical reviewers more than editors because you need subject matter expertise to know when it gives you wrong "corrections."&lt;/li&gt;
  &lt;li&gt;But ChatGPT can uncover conceptual and logical mistakes beyond spelling and grammar, including finding bugs in source code. This is the benefit that makes it worth sifting through the garbage feedback.&lt;/li&gt;
  &lt;li&gt;ChatGPT-3.5 is not good enough to work with code. ChatGPT-4 is sometimes good enough to work with code.&lt;/li&gt;
  &lt;li&gt;I recommend making AI review a standard part of the production process. It won't make the process more efficient (if anything, it slows down the process by adding this extra step), but it will improve the quality of the final product. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I got 185 items of feedback from ChatGPT, which I (very subjectively) categorized as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;i&gt;"Novel and useful feedback!"&lt;/i&gt; 28 items (15%) - These are actual mistakes that ChatGPT identified that require corrective action. This is the kind of feedback I want ChatGPT to give.&lt;/i&gt;&lt;/li&gt;

  &lt;li&gt;&lt;i&gt;"Slightly helpful but disregarded edit suggestion."&lt;/i&gt; 26 items (14%) - This feedback is correct, but not useful enough to actually follow. It may provide an interesting thought for a human writer/editor/reviewer, but ultimately isn't actionable.&lt;/i&gt;&lt;/li&gt;

  &lt;li&gt;&lt;i&gt;"Unhelpful feedback."&lt;/i&gt; 88 items (47%) - This feedback is not wrong but is meaningless or irrelevant, though it's easy to dismiss and wastes minimal time for a human editor.&lt;/i&gt;&lt;/li&gt;

  &lt;li&gt;&lt;i&gt;"False positive!"&lt;/i&gt; 43 items (23%) - This is factually incorrect feedback that will send you on a wild goose chase and wastes a human editor's time while they verify that it is indeed wrong.&lt;/i&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Further, I also have a &lt;i&gt;Formatting issue&lt;/i&gt; category where ChatGPT's feedback was incorrect because of formatting errors introduced by copy/pasting the book text into ChatGPT. These are not ChatGPT's fault, and easy to dismiss with little wasted time. There were 54 items in this category.&lt;/p&gt;


&lt;h2&gt;Background Information and ChatGPT Transcripts&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://inventwithpython.com/"&gt;All of my programming books&lt;/a&gt; are available for free online under a &lt;a href="https://creativecommons.org/licenses/by-nc-sa/3.0/"&gt;Creative Commons license&lt;/a&gt;, so anyone can repeat this experiment by copy/pasting the text into ChatGPT. I've used ChatGPT-4 for this task (ChatGPT-3.5 produces mediocre results regarding code.) I'm not looking for spelling or grammar mistakes but rather conceptual errors. My paid OpenAI account (about $20 a month) lets me make 25 ChatGPT-4 queries every 3 hours and I can only enter about 15,000 characters before it complains about the input being too long.&lt;/p&gt;

&lt;p&gt;Before each input, I prepend the following text: "The following text is from a book on Python programming. Please point out any technical errors or inaccuracies, along with any significant grammatical mistakes. Skip on summarizing the content and just list the errors you find:" I later changed this to "The following text is from a book on programming, specifically recursive functions. The book intentionally has at times a casual or conversational tone instead of a formal, academic one. Please point out any technical errors or inaccuracies, along with any significant grammatical mistakes. Skip on summarizing the content and just list the errors you find:"&lt;/p&gt;

&lt;p&gt;The training data for ChatGPT used in this review was from September 2021.&lt;/p&gt;

&lt;p&gt;To get the text of the book, I converted the book's .epub file to a .txt text file using &lt;a href=""&gt;Calibre&lt;/a&gt;, and then started copy/pasting text roughly 15,000 characters at a time. I'd always copy complete paragraphs, however the conversion to text does ruin the indentation of the code. For a more complete review, I recommend pasting the code with the original formatting. (I skipped this due to time constraints.)&lt;/p&gt;

&lt;p&gt;Here are links to the actual conversations I've had with ChatGPT so you can see the input I gave it and the complete output it returned:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://chat.openai.com/share/77b748b3-2399-41de-b085-781feb23476b"&gt;https://chat.openai.com/share/77b748b3-2399-41de-b085-781feb23476b&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://chat.openai.com/share/7346a091-70c1-4461-b69a-7abee6f6ad0d"&gt;https://chat.openai.com/share/7346a091-70c1-4461-b69a-7abee6f6ad0d&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://chat.openai.com/share/aa945b1b-1548-4645-af28-cc49b2c6342d"&gt;https://chat.openai.com/share/aa945b1b-1548-4645-af28-cc49b2c6342d&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://chat.openai.com/share/8765cb3b-9a9f-4914-b7f0-8b545e916ddd"&gt;https://chat.openai.com/share/8765cb3b-9a9f-4914-b7f0-8b545e916ddd&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://chat.openai.com/share/291387df-101b-401a-982c-07d99236b57a"&gt;https://chat.openai.com/share/291387df-101b-401a-982c-07d99236b57a&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://chat.openai.com/share/4e0728f7-2ea4-4ae2-8113-59a0c25370e3"&gt;https://chat.openai.com/share/4e0728f7-2ea4-4ae2-8113-59a0c25370e3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rest of this blog post are the 239 individual feedback items ChatGPT gave me, along with my assessment of them.&lt;/p&gt;

&lt;h2&gt;AI Feedback for Chapter 1 - What is Recursion?&lt;/h2&gt;


&lt;blockquote&gt;&lt;p&gt;In this sentence: The Definition of Recursion Before we begin...&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; This was caused by the copy/pasting from the text to the ChatGPT website, which put "The Definition of Recursion" on the same line as the first line of the paragraph. I won't count this for or against ChatGPT as a review tool.&lt;/p&gt;





&lt;blockquote&gt;In the Python and JavaScript programs where functions a(), b(), and c() are defined, there is a slight indentation problem...&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; This was caused by the indentation of the code being lost when converting from the .epub ebook to a .txt text file.&lt;/p&gt;

&lt;blockquote&gt;In the paragraph explaining recursive functions and stack overflows, there's a small grammatical error: "Functions and stacks are both simple concepts, and we can use them together to understand how recursion works." It would be better to say: "Functions and stacks are simple concepts in themselves, and we can use them together to understand how recursion works."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; A lot of the edit suggestions from ChatGPT are neither better or worse than the original text, but these end up being a lot of noise when trying to find problems in the text.&lt;/p&gt;

&lt;blockquote&gt;The explanation of the recursive function in Python and JavaScript could be misleading to beginners. Although technically accurate, it's important to note that this recursive function will lead to a "stack overflow" error because there is no base case to stop the recursion. Adding this information would provide a more complete understanding of recursion and its potential pitfalls.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; This suggestion was caused by the fact that I had to break up the book text into approximately 15,000 character chunks, so the previous explanation of stack overflows wasn't included in this chunk.&lt;/p&gt;

&lt;blockquote&gt;The phrase "Python uses the terms list and item, while JavaScript uses the terms array and element" could potentially be a source of confusion. While the terms 'list' and 'array' are used in Python and JavaScript respectively, the terms 'item' and 'element' are not language-specific and are often used interchangeably in both languages. A more accurate phrasing could be: "Python uses the term 'list' where JavaScript uses 'array' to refer to similar data structures".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Eh, you could make the argument that "item" and "element" are not &lt;i&gt;official&lt;/i&gt; terms in these languages, but documentation and developers do use these terms. Ultimately, this suggestion isn't useful enough and I disregarded it.&lt;/p&gt;

&lt;blockquote&gt;"But this “turtles all the way down” theory doesn’t do a good job of explaining cosmology, nor recursive functions." This should be corrected to: "But this “turtles all the way down” theory doesn’t do a good job of explaining either cosmology or recursive functions."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; I get the feeling if I had used ChatGPT's phrasing, then ChatGPT could have easily suggestion my original phrasing.&lt;/p&gt;

&lt;blockquote&gt;"The constant function calls with no returns grow the call stack until all the computer’s memory allocated for the call stack is used up." This could be rephrased for better clarity: "Constant function calls with no returns cause the call stack to grow until all the computer’s memory allocated for the call stack is used up."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Eh, either would be fine.&lt;/p&gt;

&lt;blockquote&gt;In the Python examples, the function calls "shortest()" and "shortestWithBaseCase(makeRecursiveCall)" would require the function definitions to be above these calls in the actual Python script. However, it's not explicitly mentioned in the text, which could potentially confuse a beginner programmer. This is not an error per se, but it's worth mentioning for clarity and completeness.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; This advice isn't incorrect, but not relevant to this book as the book assumes you have a basic level of programming knowledge and would already know this. (Not knowing this isn't ChatGPT's fault though.)&lt;/p&gt;


&lt;h2&gt;AI Feedback for Chapter 2 - Recursion vs Iteration&lt;/h2&gt;



&lt;blockquote&gt;The factorial of 0 is 1, not undefined or any other number. However, the text doesn't include this case in the factorial implementation in both the iterative and recursive methods. This is technically incorrect, as calling factorial(0) in the given code would not return the correct result, 1.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; This was an oversight on my part as the author. I wrote the functions based on my explanation that 5! was 5 x 4 x 3 x 2 x 1, but this explanation doesn't note that mathematics defines 0! as 1. While this is no great sin for a book on recursion and I don't want to change the code, I should add a sentence describing this. I think it's interesting that while ChatGPT noticed this, ChatGPT did not complain about other kinds of invalid input such as negative, floating point, or string arguments.&lt;/p&gt;

&lt;blockquote&gt;Similarly, the Fibonacci sequence usually starts with 0 and 1, not 1 and 1. The definition used in this text (1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, etc.) is a variant of the sequence and should be clearly stated as such. Also, the text doesn't consider the case when nthNumber is 0 in the Fibonacci implementation.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Yes, yes, I know that mathematicians use 0 and 1, but it's also valid to use 1 and 1, which is what I chose here. Heck, apparently Fibonacci himself &lt;a href="https://en.wikipedia.org/wiki/Fibonacci_sequence"&gt;used 1 and 2&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;In the explanation of the recursive Fibonacci function, the phrase "the fibonacci() function itself is simple" is a bit misleading. Recursive functions can often appear simple due to their mathematical elegance, but they can lead to complex behavior and performance issues, as is pointed out elsewhere in the text.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; While I try to avoid the word "simply" when writing about technical subjects that a beginner may not find so simple, I think it's use her is fitting because I'm comparing it to the more complicated code nearby.&lt;/p&gt;

&lt;blockquote&gt;There's a small grammatical error in the sentence "This involves calculating number - 1 and making a recursive function, causing a new frame object to be pushed to the call stack." It should be "making a recursive function call", not "making a recursive function".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; The phrasing I used is absolutely wrong and ChatGPT is correct: I meant to refer to a recursive function call and not a recursive function.&lt;/p&gt;

&lt;blockquote&gt;The text refers to .html programs. However, HTML is not a programming language. It would be more accurate to refer to these as scripts written in JavaScript, which are then embedded within an HTML document.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; This is interesting. ChatGPT is wrong in that I'm not claiming HTML is a programming language. When I say "This code is equivalent to the following functionCalls.html program:" it's obvious that I'm referring to the JavaScript program in the functionCalls.html file. An annoying, obtuse nitpicker would point this out, but many human readers and reviewers are annoying, obtuse nitpickers. To deflect this (irrelevant) criticism, I should change the wording to "This code is equivalent to the JavaScript code in the following functionCalls.html file:". This phrasing occurs several times in the book, so I'll have to replace them all by doing a search for ".html program".&lt;/p&gt;

&lt;blockquote&gt;The Python and JavaScript code blocks are not consistently formatted. For example, comments are not consistently capitalized and sometimes end with a period and sometimes do not. While this doesn't affect the functionality of the code, consistency is generally good practice.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;The last sentence is incomplete: "And don’t think that because a base case is reached, no more code remains to..." It should either be completed or removed.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Actually the sentence is fine: "And don’t think that because a base case is reached, no more code remains to run after either recursive call." Maybe you could make a nitpick about adding a "that there's" text in there.&lt;/p&gt;


&lt;blockquote&gt;The base cases for Fibonacci series mentioned in the first paragraph are wrong. The base case for Fibonacci is fibonacci(0) which returns 0, and fibonacci(1) which returns 1, not fibonacci(2) and fibonacci(1) which both return 1.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; It's common in programming to use zero-based indexing. But that's specifically not what I'm doing here according to the description in the text.&lt;/p&gt;

&lt;blockquote&gt;In the Python code for the recursive factorial algorithm, it doesn't handle the case where the input is 0. The factorial of 0 is 1, but the current code doesn't have a base case for that.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; This is the same issue as pointed out previously: Mathematics defines 0! as 1, though my program doesn't handle that case. But I don't do complete argument validation because that makes the code more complex while not helping the reader learn about recursion. Much of writing effective programming books is knowing what technically-correct-but-not-very-relevant information to skip.&lt;/p&gt;

&lt;blockquote&gt;In the Python and JavaScript code for the iterative factorial algorithm, if the input is 0, the code will result in an infinite loop. It doesn't have a condition to check for this base case.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; This is the same issue as the previously described one.&lt;/p&gt;

&lt;blockquote&gt;In the Python and JavaScript code for the recursive and iterative 'findSubstring' functions, they will return incorrect results if the 'needle' string is empty. They should return 0 in this case, but currently, they return -1.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; While it is a programming convention that searching for an empty string "finds" it at index 0, adding code here makes is a distraction that doesn't help the reader learn about recursion. I can see why ChatGPT made this recommendation, but it's not useful to the context of this book. I'm tempted to file this as a false positive, but it is slightly-slightly helpful.&lt;/p&gt;

&lt;blockquote&gt;In the Python and JavaScript code for the 'exponentByIteration' function, the code doesn't handle the case where the exponent 'n' is 0. The result of any number to the power of 0 is 1, but the current code will return the wrong result.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Though this seems to be the same error the ChatGPT reported previously, it is in a new context with the exponentByIteration() function.&lt;/p&gt;


&lt;blockquote&gt;The note about the modulus operator (%) for determining whether an integer is odd or even is incorrect. Any even integer mod 2 results in 0, and any odd integer mod 2 results in 1, not the other way around.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; ChatGPT is correct here. However, this is exactly what the book says too. Fortunately, the wild goose chase this suggestion led me on wasn't too long.&lt;/p&gt;

&lt;blockquote&gt;The sentence "The programs in this section demonstrate that it is always possible to turn any loop into an equivalent recursive function." is not grammatically incorrect, but it could be more precise. It should be "The programs in this section demonstrate that it is always possible to convert any iterative algorithm into an equivalent recursive function."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; ChatGPT's suggestion here is correct, but I'm trying to avoid going too hard on technical jargon. I can use the term "loop" here instead of "iterative algorithm" and the phrasing would be just a little simpler to understand.&lt;/p&gt;

&lt;blockquote&gt;The sentence "The recursive Fibonacci algorithm also suffers from a critical weakness: it repeats the same calculations over and over." could be improved to "The recursive Fibonacci algorithm also has a critical weakness: it performs the same calculations repeatedly."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Whatever. Skip the five dollar words.&lt;/p&gt;

&lt;blockquote&gt;The sentence "This slows the overall algorithm with unnecessarily repeated calculations." could be rewritten as "This slows down the overall algorithm due to unnecessary repeated calculations."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Yeah, actually my phrasing is a bit better. Keep it simple.&lt;/p&gt;

&lt;blockquote&gt;The sentence "A dictionary storing the return address information and nthNumber local variable emulates a frame object ❷." is a bit unclear. It would be better phrased as "A dictionary, which stores the return address information and the nthNumber local variable, emulates a frame object ❷."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; ChatGPT's phrasing kind of makes it sound like the dictionary's sole purpose is to store this info, but I'm talking about dictionary usage in a specific context.&lt;/p&gt;

&lt;blockquote&gt;The sentence "This is doing recursion for recursion’s sake, and since recursion is often harder to understand than iterative code, code readability deteriorates." could be improved for clarity: "This amounts to using recursion for its own sake, and since recursion is often harder to understand than iterative code, it can result in reduced code readability."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Yeah, I'm not saying my phrasing is simple, but it's simpler than ChatGPT's.&lt;/p&gt;

&lt;blockquote&gt;In the last sentence, "Those are the recursive cases, but what are the base cases?" the word "but" is not necessary. It can be rephrased as "Those are the recursive cases. What are the base cases?"&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Nah, I don't like ChatGPT's phrasing. On a side note, ChatGPT tends to produce the kind of wording leans more towards traditional technical and academic text (it is reviewing a programming book, after all) but that's exactly the kind of wording I'm trying to avoid. Perhaps I should change the prompt to make ChatGPT inclined to more casual phrasing?&lt;/p&gt;

&lt;blockquote&gt;In the Python and JavaScript functions exponentWithPowerRule(a, n), there is an issue when n is 0. The while loop while n &gt; 1: doesn't take into account the situation where the exponent n is 0. In mathematics, any non-zero number raised to the power of 0 is 1. The function as it currently stands would return a instead, which is incorrect. The function should be adjusted to return 1 when n is 0.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Yes, for completeness I probably should add code here. But then again, this exponent function doesn't work with (or claim to work with) negative exponents either.&lt;/p&gt;


&lt;blockquote&gt;"When we double-check our math, we find that 65 is indeed 7,776." There seems to be a typo in the representation of the operation. It should be "6^5" instead of "65".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; Copying and pasting the input text didn't capture the superscript of the "5".&lt;/p&gt;

&lt;blockquote&gt;"The iterative exponents function that uses the power rule has the improved performance of the recursive algorithm, while not suffering from the risk of a stack overflow." This sentence is a bit awkward. It could be rewritten for clarity, such as: "The iterative exponents function, which uses the power rule, improves performance over the recursive algorithm and avoids the risk of a stack overflow."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Actually, I like my phrasing a bit better.&lt;/p&gt;

&lt;blockquote&gt;"However, as the exponent functions we created in the previous section show, recursion can provide new insights into how to think about our programming problem." This sentence is also a bit awkward. It could be rewritten for clarity, such as: "However, as demonstrated by the exponent functions we created in the previous section, recursion can offer new insights into problem-solving in programming."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; I like my phrasing better.&lt;/p&gt;

&lt;blockquote&gt;"When Do You Need to Use Recursion?" This is a heading, not a sentence, but it's phrased as a question when it would be more standard as a statement. A better heading might be "Determining When to Use Recursion" or "The Appropriate Use of Recursion".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Is using a question for a heading so bad?&lt;/p&gt;

&lt;blockquote&gt;The references to "Figure 2-4", "Figure 2-5", and "Figure 2-6" are not meaningful in this text-only format. If this is meant to be a text-only summary, these references should be removed or replaced with textual descriptions of the images.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; The images weren't copy/pasted to the input.&lt;/p&gt;

&lt;blockquote&gt;There are instances of code being referred to as "Python" or "JavaScript" when it would be more accurate to refer to them as "Python code" or "JavaScript code". This is a minor issue, but improving this could make the text clearer and more accurate.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Nah, less is more. 'Nuf said.&lt;/p&gt;

&lt;blockquote&gt;There are numerous instances where the language could be simplified for ease of reading. For example, "This chapter featured both iterative and recursive implementations for these algorithms" could be simplified to "This chapter showed both iterative and recursive versions of these algorithms."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Eh, I think "implementations" is a better word (or at least, not worse) in this context.&lt;/p&gt;

&lt;blockquote&gt;There is a degree of repetition in the text, with recursion and its merits and drawbacks being discussed multiple times in similar ways. Consolidating these points could improve the flow of the text.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Nope. Strategically repeating information helps with understanding. Also, this advice is vague.&lt;/p&gt;

&lt;blockquote&gt;The writing occasionally shifts between different perspectives (we, you), which can be confusing. Consistency in perspective can make the text clearer and more engaging.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Eh.&lt;/p&gt;





&lt;h2&gt;AI Feedback for Chapter 3 - Classic Recursion Algorithms&lt;/h2&gt;

&lt;blockquote&gt;In the first paragraph, the phrase "Coding interviews (which, for lack of suitable ways to evaluate candidates, often crib notes from freshman computer science curricula) can touch upon them too." could be better written as, "Coding interviews, which often borrow from introductory computer science curriculum due to a lack of suitable ways to evaluate candidates, may also cover these topics."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; No, my phrasing is better.&lt;/p&gt;

&lt;blockquote&gt;In the section titled "Summing Numbers in an Array", the text says, "Still, summing numbers in an array (or some other calculation based on processing data in a linear data structure) is a common enough recursion problem in coding interviews that it deserves our attention." This could be written more clearly as, "Despite this, summing numbers in an array, or performing some other calculation based on processing data in a linear data structure, is a common enough problem in coding interviews to warrant our attention."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Eh.&lt;/p&gt;

&lt;blockquote&gt;In the section "Reversing a String", there's an inconsistency in font formatting: ′CAT′, ′X′, ′Y′, ′A′, ′T′ use a different font compared to the rest of the text. The correct formatting should be 'CAT', 'X', 'Y', 'A', 'T'.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Huh, I didn't notice this. A minor but real issue. Somehow "smart" single quotes got introduced here when I meant to use straight single quotes. it doesn't really matter to people reading the book, but copy/pasting this text would copy these incorrect quotes.&lt;/p&gt;

&lt;blockquote&gt;In the JavaScript code snippet for "Reversing a String", modern JavaScript conventions are not followed. Instead of using var, let or const should be used for variable declaration, since var is function-scoped and could lead to unexpected behavior.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; I did use "let" in most of the JS programs, but I did fall back on "var" for the reverse string, palindrome, Tower of Hanoi, and flood fill JS programs. I should probably also run the source code for these programs under a linter. Using "var" isn't wrong, but changing it to "let" will preemptively deflect any nitpickers.&lt;/p&gt;

&lt;blockquote&gt;In the phrase "Thinking about how to implement a recursive function like rev() can be difficult because it involves a chicken-and-egg problem," it would be more accurate to say "it presents a circular dependency problem" instead of "it involves a chicken-and-egg problem," which is more colloquial and may not be clear to all readers.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; I want the more colloquial phrasing.&lt;/p&gt;

&lt;blockquote&gt;The phrase "Taking a leap of faith in recursion is not a magical technique that guarantees your code works bug free" is a bit awkward. It would be clearer to say "Taking a leap of faith in recursion does not guarantee that your code will be bug-free."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; I like my phrasing better. "Leap of faith" is one of those terms that is often used in recursion lessons but is vague and magical-sounding.&lt;/p&gt;

&lt;blockquote&gt;In the section titled "Reversing a String", the author says, "You can’t simply pass the same argument that the recursive function received, like this:". This sentence could be clearer with some rewording, such as, "You cannot merely pass the same argument that the recursive function initially received, as shown here:"&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Eh.&lt;/p&gt;

&lt;blockquote&gt;In the phrase "The leap of faith requires you to have a firm understanding of your recursive function’s arguments and return value," the term "leap of faith" is a bit informal and could be confusing to some readers. It might be clearer to say, "This approach requires a firm understanding of the arguments and return value of your recursive function."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; No, "leap of faith" does have a particular meaning in the context of recursion.&lt;/p&gt;

&lt;blockquote&gt;The palindrome detection code won't handle spaces and capitalization properly. For example, 'race car' is a palindrome, but the code will report it as not being a palindrome because it doesn't handle spaces. Similarly, 'Racecar' is also a palindrome, but the code will report it as not being a palindrome because it doesn't handle capitalization. The same goes for the phrase 'A man, a plan, a canal . . . Panama'. In the real world, a string preprocessing step is required to handle spaces, punctuation, and capitalization.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; I purposefully made it not handle these cases so that the code can be simpler.&lt;/p&gt;

&lt;blockquote&gt;The JavaScript version of the Tower of Hanoi solution doesn't seem to print the initial state of the towers before the solving process starts. This could be a potential oversight.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; The JS code does in fact print the initial state.&lt;/p&gt;

&lt;blockquote&gt;In the sentence "The return statement in the recursive case ❹ makes use of Boolean short-circuiting, a feature of almost every programming language." it would be more accurate to say "many programming languages" instead of "almost every programming language".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; No.&lt;/p&gt;

&lt;blockquote&gt;In the sentence "For n disks, it takes a minimum of 2n – 1 moves to solve." it should be written as "For n disks, it takes a minimum of 2^n – 1 moves to solve." to correctly express the exponential notation.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; Copy/pasting the text removed the superscript of "n".&lt;/p&gt;

&lt;blockquote&gt;In the sentence "The series of operations for solving a four-disk Tower of Hanoi", the figure number following the sentence is "Figure 3-5". However, in the previous paragraph, the text refers to a Figure 3-6 which isn't mentioned again. This could be a numbering error.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; Copy/pasting the text doesn't copy the images.&lt;/p&gt;

&lt;blockquote&gt;The phrase "This towerOfHanoiSolver.html program contains the equivalent JavaScript code" might be misleading as the .html extension is for HTML files, not JavaScript. It should be clarified that this is an HTML file that contains JavaScript code.&lt;/blockquote&gt;

&lt;p&gt;This has been addressed previously.&lt;/p&gt;

&lt;blockquote&gt;In the sentence "When you run this code, the output shows each move of the disks until...", it seems like an incomplete sentence. The rest of the sentence is missing.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; What? The sentence is fine. This isn't even a formatting issue.&lt;/p&gt;

&lt;p&gt;ChatGPT had this to say about the flood fill source code in Python and JavaScript:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;In Python code:&lt;/p&gt;

&lt;p&gt;In line ❶, the comment says "Create the image (make sure it's rectangular!)" but it's actually creating a list of strings, not an image.&lt;/p&gt;
&lt;p&gt;In line ❷, there's an unnecessary indentation before the variable assignment.&lt;/p&gt;
&lt;p&gt;In lines ❸, ❹, ❺, and ❻, the comments say "RECURSIVE CASE" but they should say "BASE CASE" since they are the conditions for the base case.&lt;/p&gt;
&lt;p&gt;In line ❼, there's an extra indentation that shouldn't be there.&lt;/p&gt;

&lt;p&gt;In JavaScript code:&lt;/p&gt;

&lt;p&gt;In line ❶, the comment says "Create the image (make sure it's rectangular!)" but it's actually creating an array of arrays, not an image.&lt;/p&gt;
&lt;p&gt;In lines ❷, ❸, ❹, ❺, and ❻, the comments say "RECURSIVE CASE" but they should say "BASE CASE" since they are the conditions for the base case.&lt;/p&gt;
&lt;p&gt;In line ❼, there's a space before the semicolon that shouldn't be there.&lt;/p&gt;
&lt;p&gt;In line ❽, there's a space before the closing &amp;lt;/ pre&amp;gt; tag that shouldn't be there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Yes, it's made up of strings but this forms and ASCII-art image. This feedback is irrelevant.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; The "unnecessary indentation" was caused by copy/pasting the text to ChatGPT.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; ChatGPT is wrong. The recursive and base cases are correctly marked.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; The "extra indentation" was caused by copy/pasting the text to ChatGPT.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; I double-checked that this wasn't a formatting issue. There is no "space before the semicolon".&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; There was an extra space in the &amp;lt;/ pre&amp;gt; tag.&lt;/p&gt;

&lt;blockquote&gt;In the sentence "The following output shows the before and after images:", it should be "The following output shows the images before and after:" to correct the word order.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Eh.&lt;/p&gt;

&lt;blockquote&gt;In the sentence "If you want to see every step of the flood fill algorithm as it fills in the new character, uncomment the printImage(image) line ❶ in the floodFill() function and run the program again," the reference to line ❶ should be changed to line ❷.&lt;/blockquote&gt;

&lt;p&gt;Both &lt;i&gt;Novel and useful feedback!&lt;/i&gt; and &lt;i&gt;False positive!&lt;/i&gt;: The reference to 1 is wrong, but it should be removed entirely rather than changed to 2.&lt;/p&gt;

&lt;blockquote&gt;In the sentence "This means we could equally effectively use a set data structure that removes elements randomly," it should be "This means we could equally effectively use a data structure like a set that removes elements randomly."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; No.&lt;/p&gt;




&lt;h2&gt;AI Feedback for Chapter 4 - Backtracking and Tree Traversal Algorithms&lt;/h2&gt;

&lt;blockquote&gt;"A maze can be represented by a tree data structure, since mazes branch off into different paths, which in turn branch off into more paths." It might be worth clarifying that this representation applies to perfect mazes, where there is a single path between any two points without loops. Other types of mazes, which can include loops or cycles, may not be perfectly represented by a tree.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; The information here is correct, but I do go into the definition of perfect mazes later in the chapter. But this wasn't included in the 15,000 character chunk I sent to ChatGPT. This was a bit of a time waste to resolve.&lt;/p&gt;

&lt;blockquote&gt;"In mathematics and computer science, a graph is a collection of nodes and edges, and a tree is a kind of graph. The graph is directed because its edges have one direction: from parent to child node." This sentence might be confusing because it seems to imply that all graphs are directed. It would be better to specify that the graph we're talking about in this context (a tree) is directed.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; The "directed" was italicized but this was lost in the copy/paste, but I feel like this italicizing made it more obvious that it was not implying all graphs are directed. This was a bit of a time waste to resolve.&lt;/p&gt;

&lt;blockquote&gt;"Trees in general do not have this restriction and can have edges in both directions, including from a child node back to its parent node." This statement can be misunderstood, as trees are usually described as directed acyclic graphs, meaning they don't have edges going in both directions (from child to parent and vice versa). While it's true that trees can be represented with bidirectional edges for some algorithms, it might be worth noting that this is not the typical definition.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; I have this sentence specifically in contrast with the previous sentence that says DAGs are directed. It was a waste of time to investigate this feedback.&lt;/p&gt;

&lt;blockquote&gt;"You can think of lists, arrays, and strings as linear trees; the root is the first element, and the nodes have only one child node. This linear tree terminates at its one leaf node." This might be confusing as well. Lists, arrays, and strings are linear structures, but calling them "linear trees" might be misleading because they don't have the hierarchical structure typically associated with trees.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; Yeah, that's why I'm explaining how you can think of them as linear trees. That's the point of these sentences. It was a waste of time to investigate this feedback.&lt;/p&gt;

&lt;blockquote&gt;"The following preorderTraversal.py program has a preorderTraverse() function that traverses each child node first, before accessing the node’s data to print it to the screen:" The description of the preorderTraverse() function is incorrect. In a preorder traversal, the node's data is accessed first, then its child nodes are traversed, not the other way around. The actual code provided correctly implements a preorder traversal, so this seems to be a minor error in the accompanying text.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Yes, while the code is correct, I think there was a typo in the description that swapped these.&lt;/p&gt;

&lt;blockquote&gt;"When you look at the tree in Figure 4-1, notice that preorder traversal order displays the data in left nodes before right nodes, and bottom nodes before top nodes." This sentence may cause confusion, as there is no Figure 4-1 presented in the given text.&lt;/blockquote&gt;


&lt;blockquote&gt;Inorder Tree Traversal:
    The text and code provided are misleading as they suggest that inorder traversal can be used for general trees. Inorder traversal is specific to binary trees. The code provided treats the first child as a left node and the second child as a right node. However, this is not accurate for general trees where a node can have more than two children. In general trees, there is no concept of left and right children. The code and explanation should be corrected to reflect this.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; No, you can still do an inorder traversal with trees with more than two child nodes. If you access the data after traversing the first child and before traversing the last child, that's an inorder traversal.&lt;/p&gt;

&lt;blockquote&gt;Finding Eight-Letter Names in a Tree: The JavaScript function find8LetterName() has an extra parameter value that is not used within the function. This is a minor error and the value parameter should be removed.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Yep. That "value" parameter somehow got left in and should be removed.&lt;/p&gt;

&lt;blockquote&gt;Getting the Maximum Tree Depth: The text does not have any errors, but it ends abruptly without concluding the topic or providing any code examples. It might be useful to include an example here to properly illustrate this concept.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;Regarding the description of the getDepth() function, there's a discrepancy in the definition of the depth of a leaf node. It's stated that "A leaf node (the base case) simply returns 0," but later it's said that a leaf node "by its nature has a depth of one level." The first definition is in line with the code examples provided, where a leaf node indeed returns 0. So, the second statement is technically incorrect.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; and &lt;i&gt;False positive!&lt;/i&gt; Yes. There is a mistake, but it should be TODO&lt;/p&gt;

&lt;blockquote&gt;In the Python code example for the getDepth() function, the final print statement should follow Python 3's f-string formatting or the format method for clarity and consistency. Here's the suggested revision:

print(f'Depth of tree is {getDepth(root)}')

Instead of:

print('Depth of tree is ' + str(getDepth(root)))&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; I don't use f-strings in my Python code in order for it to be runnable on pre-3.6 versions.&lt;/p&gt;

&lt;blockquote&gt;The JavaScript example for getDepth(), the HTML element "script" has an incorrect closing tag. It should be &amp;lt;/script&amp;gt; but instead it is written as &amp;lt;/ script&amp;gt;.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; There was no space. I wasted quite a bit of time verifying this.&lt;/p&gt;

&lt;blockquote&gt;In the maze-solving section, the term "tree graph" is used, which could be confusing. The maze isn't strictly a tree because, unlike trees, mazes can have nodes with more than one parent. The term "graph" would be more accurate here.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; I've already explained in this chapter that the mazes are perfect mazes.&lt;/p&gt;

&lt;blockquote&gt;The last print statement printMaze(MAZE) needs an explanation or context, as it might confuse the reader. Is it meant to print the solved maze or the original one?&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; This is actually really good feedback, because looking at the code this is a perfect spot where just a couple simple comments will really improve the readability of the code.&lt;/p&gt;

&lt;blockquote&gt;There's an incorrect Unicode character in comments marked as ❶, ❷, ❸, and ❹. They should be replaced with proper numbering or bullets if they are meant to denote specific steps or points in the code.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; It's weird that ChatGPT hasn't complained about the use of the circle-numbers until now. These are fine.&lt;/p&gt;

&lt;blockquote&gt;In the JavaScript mazeSolver.html program, there's an incorrect closing tag &amp;lt;/ pre&amp;gt; for the "pre" element. It should be &amp;lt;/pre&amp;gt;.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Nice catch.&lt;/p&gt;

&lt;blockquote&gt;Grammatically, there are some long sentences that could be broken up for clarity. For example, "The nodes in this tree graph represent intersections where the maze solver could choose one of the north, south, east, or west paths to follow to the next intersection." could be restructured as "The nodes in this tree graph represent intersections. At each intersection, the maze solver could choose to follow one of the north, south, east, or west paths to reach the next intersection."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Yeah, actually this edit would be better.&lt;/p&gt;

&lt;blockquote&gt;The term "DAG" (Directed Acyclic Graph) is used without a proper introduction or definition. If it's the first time the term appears in the book, it should be introduced and defined.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;


&lt;blockquote&gt;In the paragraph discussing the "base case" in binary search, it might be helpful to clarify that the base case isn't only when the range of items is one item in length, but also when the search range is empty (left &gt; right).&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Yes, I should also add this bit of text.&lt;/p&gt;

&lt;blockquote&gt;For better understanding, it might be useful to clarify what the maze and visited variables are referring to in the context of the programming explanation. The paragraph seems to assume that the reader is familiar with these from a previous section or code sample.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; This is expained elsewhere.&lt;/p&gt;

&lt;blockquote&gt;The description of how recursion is used in backtracking algorithms could be explained more thoroughly. It is mentioned that "a recursive function call is analogous to traversing to a child node in a tree, while returning from a recursive function call is analogous to backtracking to a previous parent node," but it could be helpful to give a specific example of this for readers who are less familiar with these concepts.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; This is expained elsewhere.&lt;/p&gt;

&lt;blockquote&gt;In the explanation of binary search, it would be beneficial to note that binary search only works on sorted lists or arrays. This is implicit in the explanation, but could be explicitly stated for clarity.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; This is expained elsewhere.&lt;/p&gt;

&lt;blockquote&gt;In the JavaScript version of binarySearch, it uses "===" (strict equality operator) for comparison, which checks both the value and the type. This might lead to unintended results if the types are different. However, this depends on the expected types of the inputs in this specific application. In general, using "===" in JavaScript is considered good practice.&lt;/blockquote&gt;

&lt;li&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/li&gt;

&lt;blockquote&gt;When talking about the Big O Algorithm Analysis, it would be beneficial to explain what the Big O notation represents for readers unfamiliar with the concept.&lt;/blockquote&gt;

&lt;li&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; This explanation is in a box precisely because Big O notation is too complex of a topic to go into in this book.&lt;/li&gt;

&lt;blockquote&gt;It is stated that a simply connected maze has a tree-like structure, but it might be helpful to explain what "simply connected" means in this context. In graph theory, a simply connected graph is a tree, i.e., a connected graph without simple cycles.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; This is expained elsewhere.&lt;/p&gt;

&lt;blockquote&gt;The text is a little confusing when it discusses printMaze(MAZE) calls being uncommented to show how the maze data structure changes as it is processed. This might be clearer if there was a specific code sample provided that showed where these function calls were placed and how they could be uncommented.&lt;/blockquote&gt;

&lt;p&gt;ChatGPT already mentioned this issue.&lt;/p&gt;

&lt;blockquote&gt;The text could benefit from more concrete examples, particularly when discussing more abstract concepts like trees, DAGs, and recursion.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;



&lt;h2&gt;AI Feedback for Chapter 5 - Divide-and-Conquer Algorithms&lt;/h2&gt;





&lt;blockquote&gt;Technical Inaccuracy: The statement "The range halves in size for each recursive call, so it eventually becomes empty" is inaccurate. While the range does decrease in size after each recursive call, it doesn't necessarily halve. The partitioning is based on the pivot, so the size of the two resulting sub-ranges can vary.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; I should add a minor note that the range &lt;i&gt;roughly&lt;/i&gt; halves in size.&lt;/p&gt;

&lt;blockquote&gt;Typographical Error: In the Python script, the comment at line 8 refers to 'left side of items' whereas it should be 'right side of items'. The corrected line should be '# Put the pivot on the right side of items: items[i], items[right] = items[right], items[i]'&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; ChatGPT is completely wrong. The original text is correct in that it's the left side.&lt;/p&gt;

&lt;blockquote&gt;Consistency Issue: In the explanation of base case for the recursion, it's stated that "Being given a range to sort that contains zero or one item and that is already in sorted order." is the base case. However, quicksort doesn't check if a range of one item is already in sorted order because a single item is by definition sorted. This might lead to confusion and it would be better to say "Being given a range to sort that contains zero or one item."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Yeah, I can slightly change this wording to "and thus is already in sorted order."&lt;/p&gt;

&lt;blockquote&gt;Grammatical Error: In the following statement - "Someone else’s quicksort() implementation may instead use j and i, or even completely different variables", it is not clear what "j and i" refers to. If the author means to say that the positions of i and j may be swapped, then the statement could be clarified to say - "Someone else’s quicksort() implementation may instead swap the positions of i and j, or use completely different variables".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;Ambiguity: In the statement - "This results in no actual change since i and j are the same index", it's not clear why having i and j as the same index will result in no change. This can be rephrased for clarity - "This results in no actual change because swapping a value with itself does not alter the array, which is the case when i and j are at the same index."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; The other sentences clarify this, and ChatGPT's own wording is vague and unhelpful.&lt;/p&gt;


&lt;blockquote&gt;In the mergeSort() function, there is a bug that could cause an IndexError. The condition that checks whether an item should be appended from left or right doesn't ensure that iLeft and iRight are valid indices for their respective lists. This could lead to an IndexError if the left list is exhausted before the right list (or vice versa), but the exhausted list's index is still used to attempt to access an item. A condition to check whether iLeft or iRight have reached their list's length should be added before comparing left[iLeft] and right[iRight].&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; Yeah, I've done a thorough check and I think ChatGPT is just throwing stuff at the wall to see what sticks. This code can't cause an index error. That was a wild goose chase.&lt;/p&gt;

&lt;blockquote&gt;In the sumDivConq() function, numbers[mid:len(numbers) + 1] should be replaced with numbers[mid:]. This is because slicing in Python does not include the stop index, so adding 1 to len(numbers) does not have any effect. Additionally, if you leave out the stop index in a slice, Python will automatically include all elements until the end of the list, which is what we want here.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; In normal Python code this would be the conventional way to write it, but I'm trying to be more explicit so that it's in line with the equivalent JavaScript code I have. ChatGPT's suggestion is right, but isn't relevant to my particular case.&lt;/p&gt;

&lt;blockquote&gt;"Merging two sorted lists into a larger sorted list involves looking at the start of both smaller lists and appending the smaller value to the larger list." This sentence could be more accurately written as: "Merging two sorted lists into a larger sorted list involves looking at the start of both smaller lists and appending the smaller value to the new list."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Eh.&lt;/p&gt;

&lt;blockquote&gt;"The function divides the [8, 5] list into [8] and [5], reaches the base case, and then merges back into [5, 8]." This sentence would be clearer written as: "The function divides the [8, 5] list into [8] and [5], each of which is already sorted because it contains only one item (the base case), and then merges these back into the sorted list [5, 8]."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; I kind of like ChatGPT's wording, but I also kind of like how mine is shorter. Eh. I'm going to stick with mine.&lt;/p&gt;

&lt;blockquote&gt;"We already covered summing an array of integers in Chapter 3 with the head-tail technique." It's not an error, but this sentence would benefit from rephrasing for clarity: "In Chapter 3, we already covered the method of summing an array of integers using the head-tail technique."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Eh, I really don't think this suggestion is an improvement, and might even be worse.&lt;/p&gt;


&lt;blockquote&gt;&lt;p&gt;In the section "Here are the five steps of the Karatsuba algorithm:", the fourth step is described incorrectly. It should be:&lt;/p&gt;

&lt;p&gt;Calculate step 3 - step 2 - step 1 multiplied by 10^(n/2), where n is the number of digits.&lt;/p&gt;

&lt;p&gt;This ensures that the middle term in the final equation is properly accounted for. The resulting value is then used for padding in step 5.&lt;/p&gt;

&lt;p&gt;In the Python implementation, the padding in step 5 is incorrect. The lines:&lt;/p&gt;

&lt;pre&gt;step1Padding = (len(x) - halfOfDigits) + (len(x) - halfOfDigits)
step4Padding = (len(x) - halfOfDigits)&lt;/pre&gt;

&lt;p&gt;Should be replaced with:&lt;/p&gt;

&lt;pre&gt;step1Padding = 2 * (len(x) - halfOfDigits)
step4Padding = len(x) - halfOfDigits&lt;/pre&gt;

&lt;p&gt;Also, the 'left' padding in the 'padZeros' function is not necessary for the Karatsuba algorithm, as we only pad zeros to the right of the number in this case.&lt;/p&gt;

&lt;p&gt;In the JavaScript implementation, the same errors with padding exist. Replace:&lt;/p&gt;

&lt;pre&gt;let step1Padding = (x.length - halfOfDigits) + (x.length - halfOfDigits);
let step4Padding = (x.length - halfOfDigits);&lt;/pre&gt;

&lt;p&gt;With:&lt;/p&gt;

&lt;pre&gt;let step1Padding = 2 * (x.length - halfOfDigits);
let step4Padding = x.length - halfOfDigits;&lt;/pre&gt;&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; I'm really annoyed with this one because it took me a long time to verify that my code is correct. From what I can tell, ChatGPT isn't looking at my program so much as it's thinking of standard implementations of the Karatsuba algorithm and pointing out how mine is not like that. I've written my code more for simplicity though, rather than as professional code. This was a big waste of time.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Also, the description of the multiplication table generation is slightly incorrect. The text says:&lt;/p&gt;

&lt;p&gt;"Normally, lookup tables are hardcoded directly in the source code, from MULT_TABLE[[0, 0]] = 0 to MULT_TABLE[[9, 9]] = 81."&lt;/p&gt;

&lt;p&gt;However, in Python, accessing dictionaries with lists as keys (e.g., MULT_TABLE[[0, 0]]) will raise a TypeError because lists are mutable and cannot be hashed. This will not work unless you use a tuple (e.g., MULT_TABLE[(0, 0)]), which is the correct way to do it in Python and was done correctly in the code.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; ChatGPT is looking at the JavaScript code and confusing it with Python code. Disregard its advice here.&lt;/p&gt;

&lt;blockquote&gt;The concept of padding zeros in the Karatsuba algorithm is somewhat misrepresented. In the Karatsuba algorithm, padding zeros means multiplying a number by 10^k where k is a certain number of digits. It's not adding zeros to a string.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; Yeah, I know my code isn't standard. That's because I'm writing it for readability by students. Nobody would write a Karatsuba implementation in Python anyway because they could just use the * operator. Again, ChatGPT is complaining about how my code doesn't compare to a standard Karatsuba implementation rather than considering my code in its own context.&lt;/p&gt;

&lt;blockquote&gt;Finally, the paragraph mentioning HALF_TABLE is not technically incorrect, but the justification for the lookup table is somewhat misleading. While it is true that division operations might not be available in some very low-level programming or hardware situations, the vast majority of modern computing environments, including virtually all that use Python or JavaScript, support division operations.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Uhg.&lt;/p&gt;


&lt;blockquote&gt;Under "Practice Questions", the question "What is a benefit of the divide-and-conquer summing algorithm in this chapter compared to the head-tail summing algorithm in Chapter 3?" appears to refer to content that is not provided in the given text. It's not technically an error in this section, but the context is missing.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;In the "Practice Projects" section, the text suggests creating a version of the Karatsuba multiplication algorithm with a lookup table for products from 0 x 0 to 999 x 999. While this is technically possible, it may not be practical due to the large amount of memory such a lookup table would consume.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Yeah, it's not practical. It's meant to be an academic exercise. (I think at this point ChatGPT's advice is getting on my nerves.)&lt;/p&gt;

&lt;blockquote&gt;The question "How is the array [0, 3, 1, 2, 5, 4, 7, 6] not properly partitioned with a pivot value of 4?" suggests that there's a problem with an array partition, but no problem is evident from the given text.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; The answer is "there's a 5 before the 4, which should be to the right of the 4 if 4 was the pivot value."&lt;/p&gt;

&lt;blockquote&gt;The problem "Create a function that performs a linear search on a large array of integers 10,000 times." is more a computer science problem than specifically a Python problem.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; This is unhelpful feedback, but it's caused from the prompt I gave it. I need to change the prompt to not call this book a Python book.&lt;/p&gt;


&lt;h2&gt;AI Feedback for Chapter 6 - Permutations and Combinations&lt;/h2&gt;



&lt;blockquote&gt;In "The Terminology of Set Theory", when explaining n multichoose k, the concept is somewhat unclear. It might be better to explain that it refers to the number of ways to select k elements from a set of n elements where repetition is allowed and order doesn't matter.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Uh, I kind of do explain this?&lt;/p&gt;

&lt;blockquote&gt;In the sentence "This makes these calculations suitable for recursion." the word "these" is used twice in close proximity, which could be considered redundant. It could be rephrased as "This makes such calculations suitable for recursion."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;"For example, 4 choose 2 is 6, because there are six ways to choose two elements from a set of four elements like {A, B, C, D}: {A, B}, {A, C}, {A, D}, {B, C}, {B, D}, and {C, D}." The phrasing is slightly awkward. A possible improvement would be: "For example, in '4 choose 2', there are six possible ways to select two elements from a four-element set such as {A, B, C, D}: {A, B}, {A, C}, {A, D}, {B, C}, {B, D}, and {C, D}."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; I like my phrasing better.&lt;/p&gt;

&lt;blockquote&gt;"It’s surprising how quickly the number of permutations and combinations grows as we add elements to a set." This sentence is more of a personal observation than a factual statement. A more formal way to phrase this could be: "The number of permutations and combinations increases exponentially with the addition of elements to a set."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Wow, thanks. Any more advice on how to drain the last remnants of personality from the text?&lt;/p&gt;


&lt;blockquote&gt;The Python code has a lack of indentation which is crucial in Python. The if-else clauses, loops, and function definitions are not properly indented, making the code difficult to understand and likely to throw an error if run.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;In the "Getting Permutations with Nested Loops: A Less-Than-Ideal Approach" section, it says "We can calculate the number of possible combinations as 54, or 5 × 5 × 5 × 5, or 625." But later in the text, it shows a nested loop generating permutations with only 3 characters ('A', 'B', 'C', 'D', 'E'), which contradicts the previous statement that the permutations should be four characters long.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; ChatGPT has brain worms here. I don't even want to bother going into why this is so off the mark.&lt;/p&gt;

&lt;blockquote&gt;The for loop for generating permutations in Python in the "Getting Permutations with Nested Loops: A Less-Than-Ideal Approach" section is also incorrect. It says, "for a in ['A', 'B', 'C', 'D', 'E']: for b in ['A', 'B', 'C', 'D', 'E']: for c in ['A', 'B', 'C', 'D', 'E']: for d in ['A', 'B', 'C', 'D', 'E']:", which is generating 4-character permutations, but it's stated above that they are generating 3-character permutations.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; Same as previous.&lt;/p&gt;

&lt;blockquote&gt;In the "Permutations with Repetition: A Password Cracker" section, it says, "making 6 × 6 × 6 × 6, or 64, or 1,296 possible permutations." This is inaccurate as 6 × 6 × 6 × 6 equals to 1,296, not 64.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; The superscript for 4 didn't copy over.&lt;/p&gt;

&lt;blockquote&gt;In the JavaScript function getPermsWithRep(), when the base case is reached (permLength === 0), the function returns [prefix] which is an array. But prefix is a string. It's not clear what the purpose is of returning the string in an array.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; Brain worms response.&lt;/p&gt;

&lt;blockquote&gt;A minor grammatical error in "The function makes a recursive call to getPerms() to get all the permutations of the string in tail." It would be clearer if phrased as, "The function makes a recursive call to getPerms() to get all the permutations of the string from tail."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; No, my wording makes much more sense.&lt;/p&gt;

&lt;blockquote&gt;There is a contradiction in the phrase "Permutations with repetition go through all possible orderings of the elements in a set, like our wedding guest seating chart example." This should mention that repetition of elements is allowed in the set, unlike the wedding guest seating example where each guest can only be seated once (no repetition).&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; Wow. I checked and the text with the wedding guests uses the word "without" and not "with". ChatGPT is outright misquoting what the book says. This is a flagrantly bad failure of the LLM.&lt;/p&gt;

&lt;blockquote&gt;It's mentioned that the function getPermsWithRep('JPB123', 4) is being called but the string 'JPB123' is of length 6, not 4. The explanation for the permLength argument says that it begins as the length of chars, which is not the case here.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; ChatGPT is misunderstanding what the function requires. 4 is the length of the generated permutations, not the pool of characters to draw from (JPB123).&lt;/p&gt;

&lt;blockquote&gt;The algorithm for recursive permutations with repetition in the Python code doesn't explicitly handle the case when the permLength argument is greater than the length of chars, which might lead to unexpected results.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Input validation isn't a priority in this context.&lt;/p&gt;

&lt;blockquote&gt;The algorithm for recursive permutations with repetition in the JavaScript code doesn't check if permLength is greater than the length of chars, leading to the same potential problem as in the Python code.&lt;/blockquote&gt;

&lt;p&gt;This is a repeat of the previous issue.&lt;/p&gt;


&lt;blockquote&gt;Technical error: It is mentioned, "While sets don’t have an order, we need to be consistent in using the ABCD order of the set while generating this tree." This is misleading. In Python, sets are unordered and the order of items is not maintained. Therefore, if you need to maintain order, you might have to use a different data structure like a list.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; ChatGPT has misunderstood context. I'm talking about sets as in set theory, not as in the Python data structure.&lt;/p&gt;


&lt;blockquote&gt;Grammar error: The sentence, "The return value is an array of strings of combinations from the string chars, each of length k," uses the term "array" which is less common in Python context. It is better to use "list" instead, as that's the term used for this data structure in Python.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; In this book I'm using Python and JavaScript, and previously said I'd use "array" as a generic term.&lt;/p&gt;

&lt;blockquote&gt;Grammar error: "The second case occurs if chars is the blank string, which is an empty array since no possible combinations can be made from a blank string." Again, use the term "list" instead of "array".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; Same as previous.&lt;/p&gt;

&lt;blockquote&gt;The explanation in the paragraph about "2-combinations of 'ABC'" is a bit confusing. The explanation about 1-combination and the recursive call might need further clarification.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; I'll be generous and say this is because the accompanying figures aren't there, but I think ChatGPT is misreading this section.&lt;/p&gt;

&lt;blockquote&gt;In the Python code, 'getCombos' function is used, which uses recursion to find combinations. There's a point where it is mentioned that two arguments are passed to the recursive function call: the tail of chars and k-1 for the first call, and the tail of chars and k for the second call. This explanation may be confusing to some readers, as it's not immediately clear what 'tail' refers to. It might be helpful to explain that 'tail' refers to all characters in 'chars' except the first one (the 'head').&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; Head-tail was explained previously.&lt;/p&gt;

&lt;blockquote&gt;In the JavaScript code, you're using document.write(), which is generally considered a bad practice as it may lead to vulnerabilities such as Cross-Site Scripting (XSS) attacks. It is also not standard in modern web development where DOM manipulation is done using methods like innerHTML, textContent or better, via libraries like jQuery or frameworks like React, Angular, Vue.js, etc.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Uhg. This is the kind of irrelevant nonsense that gets tiresome to repeatedly read.&lt;/p&gt;

&lt;blockquote&gt;The JavaScript code uses the method .slice() on a string. Although this is not incorrect (as slice works on strings in JavaScript), it could be a little confusing because in Python, slicing is typically used on lists, not strings. It might be worth noting this difference for readers who are new to JavaScript.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; The reader knows that JavaScript and Python are different languages.&lt;/p&gt;

&lt;blockquote&gt;Technical error: In the section discussing balanced parentheses, the claim "A string has balanced parentheses if every opening parenthesis is followed by exactly one closing parenthesis." is incorrect. A string has balanced parentheses not only if every opening parenthesis is followed by a closing parenthesis, but also if every closing parenthesis is preceded by an opening parenthesis and the order of parentheses is correct, i.e., no closing parenthesis appears before an opening parenthesis corresponding to it. A more accurate definition might be: "A string has balanced parentheses if for every opening parenthesis there exists a corresponding closing parenthesis that appears later in the string, and vice versa, such that no closing parenthesis appears before an opening parenthesis corresponding to it."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; My wording is simpler, and the word "exactly" covers this extended discussion.&lt;/p&gt;

&lt;blockquote&gt;The explanation about the getBalancedParens() function might be improved by providing a code snippet. This would make it easier to understand the implementation of the function.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; No it wouldn't. And anyway, I provide the full code of the function already.&lt;/p&gt;

&lt;blockquote&gt;It is mentioned in the text that the getBalancedParens() function builds strings by adding either an opening or closing parenthesis. But it doesn't clarify what the criteria for adding the opening or closing parenthesis are.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Yes it does.&lt;/p&gt;

&lt;blockquote&gt;There's a small contradiction in the text. The author first writes that "the second base case happens after the two recursive cases have received the lists of balanced parentheses strings after adding an opening and/or&lt;/blockquote&gt;

&lt;p&gt;ChatGPT cut off it's own response here.&lt;/p&gt;


&lt;blockquote&gt;The document.write(&amp;lt;pre&amp;gt;"All combinations of 2 balanced parentheses:&amp;lt;br /&amp;gt;"); line in the JavaScript example is not syntactically correct. It should instead be document.write("&amp;lt;pre&amp;gt;All combinations of 2 balanced parentheses:&amp;lt;br /&amp;gt;");.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Good catch. The &amp;lt;pre&amp;gt; tag got moved outside of the double quotes somehow.&lt;/p&gt;

&lt;blockquote&gt;The statement that "The getBalancedParens() function ❶ requires one argument, the number of pairs of parentheses, when called by the user" is not entirely accurate. This function has default parameter values for its arguments, so technically it could be called without any arguments, and would default to pairs=undefined, openRem=undefined, closeRem=undefined, current=undefined, and indent=0.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; This is completely untrue. The Python version requires the pairs argument, and the JavaScript version breaks at runtime if you don't supply it.&lt;/p&gt;

&lt;blockquote&gt;The JavaScript code uses the Array.prototype.push.apply() method, which is outdated and has been replaced with the spread operator (...). It would be more current to use results.push(...getBalancedParens( pairs, openRem - 1, closeRem, current + '(', indent + 1)); instead of Array.prototype.push.apply(results, getBalancedParens( pairs, openRem - 1, closeRem, current + '(', indent + 1));.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; I'd rather use the older but simpler syntax.&lt;/p&gt;

&lt;blockquote&gt;The power set section describes an example of how the power set grows when new elements are added, but it mislabels the elements in bold as "new" elements. However, these are actually the subsets that include the new element, not the new elements themselves.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; The copied text doesn't retain the bold style.&lt;/p&gt;

&lt;blockquote&gt;In the Python code, powerSet = powerSet + tailPowerSet is inefficient. It would be better to use powerSet.extend(tailPowerSet) instead, to append the elements of tailPowerSet to powerSet directly, without creating a new list.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Simplicity of code is more important for the context of this book than performance.&lt;/p&gt;

&lt;blockquote&gt;The phrase "Let’s ask the three recursive algorithm questions about our power set algorithm" is awkward. It could be rewritten as "Let's consider the three questions about recursion as they apply to our power set algorithm."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;In "This is equivalent to the set of every possible k-combination of a set", it would be better to write "This is equivalent to the collection of all possible k-combinations of a set."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Eh, my wording is actually better.&lt;/p&gt;

&lt;blockquote&gt;The phrase "It is astronomically unlikely you’ll need to generate the power set of a set for any other reason, including the job you are interviewing for" is written in a tone that might come off as sarcastic or unprofessional. It could be rephrased more neutrally as "While generating the power set of a set is not a commonly required task in most jobs, it can still be a useful concept to understand."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Eh, my wording is actually better.&lt;/p&gt;

&lt;blockquote&gt;In the sentence "After these recursive cases is an unconditional base case that returns...", the grammar is off. It would be better to write "After these recursive cases, there is an unconditional base case that returns..."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Eh.&lt;/p&gt;

&lt;blockquote&gt;The phrase "The power sets of larger sets are similar to the power sets of smaller sets, hinting that we can create a recursive function to generate them" is a bit misleading. The similarity between power sets of larger and smaller sets doesn't hint at the need for a recursive function. Instead, it might be more accurate to say that "The structure of power sets of larger sets, which include the power sets of their smaller subsets, suggests a recursive approach to generating them."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Eh.&lt;/p&gt;

&lt;blockquote&gt;The phrase "Let’s ask the three recursive algorithm questions about our power set algorithm" is awkwardly worded and redundant. It could be rephrased as "Let's address the three key questions for our recursive power set algorithm."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; The "three questions" thing is something I introduce in this book, but ChatGPT doesn't pick up on this since the input is split into 15,000 character chunks.&lt;/p&gt;

&lt;blockquote&gt;The last sentence in the paragraph starting with "The getPower&lt;/blockquote&gt;

&lt;p&gt;ChatGTP's response got cut off here again, for some reason.&lt;/p&gt;



&lt;h2&gt;AI Feedback for Chapter 7 - Memoization&lt;/h2&gt;




&lt;blockquote&gt;The passage suggests that memoization increases memory usage, which might be misleading. While it's true that memoization involves storing results for later reference, this does not always mean that overall memory usage increases significantly. Often, the increased memory usage due to memoization is offset by the savings in computational resources.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; This is an eye-rolling level of irrelevant nitpicking.&lt;/p&gt;

&lt;blockquote&gt;In the section "Memoization in Functional Programming", it's stated that a function is nondeterministic if it returns different values when passed the same arguments. While this is correct, it might be useful to specify that such behavior is due to the function relying on external or unpredictable factors, not because the function itself is inherently random or unpredictable.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; I should probably add a sentence about this, but it doesn't strike me as too urgent.&lt;/p&gt;

&lt;blockquote&gt;The section "Python's functools Module" ends abruptly, not completing the explanation about @lru_cache().&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;Grammar: In the paragraph that begins with "Side effects are any changes...", the phrase "Any trace that a function call leaves behind after returning is a side effect" might be better stated as "Any change that a function call effects outside its own scope, which persists after the function has returned, is considered a side effect."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Uhg, I hate this new wording.&lt;/p&gt;

&lt;blockquote&gt;In the example Python and JavaScript code provided, the argument 'indent' isn't explained. A reader who's not familiar with this code might find it confusing. Its purpose (which appears to be to control indentation in printed output for clarity) should be explained.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; This was explained earlier in the book.&lt;/p&gt;

&lt;blockquote&gt;The python and JavaScript examples are a bit complicated for an introductory text and make the code look more complex than it needs to be, mostly due to the indent and indentation variables and the printing statements. These extra lines of code are unnecessary for the core function of calculating Fibonacci numbers and demonstrating memoization, and could be removed or separated out to focus on the memoization itself.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; Uhg. Actually these make the output so much easier to understand for a beginner, even though it's a departure from how most programming books present code like this. Then again, most programming books are kind of terrible. ChatGPT is trying to stamp out the unique things that make this book better than other sources.&lt;/p&gt;

&lt;blockquote&gt;The last line "The cache can have a memory size limit set" does not specify who or what sets the limit. The sentence could be clarified, for instance: "You can set a memory size limit for the cache."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;In the JavaScript example code, the HTML 'pre' tags are used, which might be unfamiliar to some readers. The text could explain their purpose.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Eh, this book isn't for people brand new to JavaScript.&lt;/p&gt;

&lt;blockquote&gt;It's said that "dynamic programming uses recursion with repeated recursive cases; these are the overlapping subproblems." This definition could be expanded to say that dynamic programming uses memoization to avoid recalculating the same subproblems, which is a key feature of the technique.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Yes, the book eventually covers this as well.&lt;/p&gt;

&lt;blockquote&gt;The python script shows the indent variable being initialized as an argument of the fibonacci function with indent=0. In the JavaScript code, indent is checked for undefined inside the function body. While these are two ways of accomplishing the same goal in two different languages, it could confuse beginners who are trying to understand the differences between Python and JavaScript.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; This is irrelevant. But kudos to ChatGPT for recognizing that the code in two different languages does the same thing.&lt;/p&gt;


&lt;blockquote&gt;In the first Python code snippet, the @functools.lru_cache() decorator should be placed immediately above the function it is meant to decorate. There should not be any other code (such as import statements) between the decorator and the function.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;The statement that "calculating fibonacci(99) with the recursive algorithm would take a few centuries" is an exaggeration. While calculating the 99th Fibonacci number without memoization would be very time-consuming due to the exponential number of function calls, it would not likely take centuries.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; No, it would literally take 600,000 years for my laptop to calculate fibonacci(99).&lt;/p&gt;

&lt;blockquote&gt;The printMessage() function in the second Python code snippet doesn't return a value. The lru_cache decorator won't have any effect on this function because it doesn't have a return value to cache.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; The entire point of this code example, as explained in the book, is that this is an inappropriate use of lru_cache.&lt;/p&gt;

&lt;blockquote&gt;The statement that "memoized functions must be pure" is not entirely accurate. While it's true that the results are most predictable when memoization is used with pure functions, it can be used with impure functions in some circumstances if the developer is aware of the potential side effects and considers them acceptable for their particular use case.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Uh, sure. This is an obnoxious and vague nitpick.&lt;/p&gt;

&lt;blockquote&gt;In the sentence "The additions to the original fibonacciByRecursion.py program from Chapter 2 have been marked in bold:", the text doesn't show any content marked in bold.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;The phrase "The doNotMemoize.py program demonstrates what happens when the @lru_cache function decorator (described in the previous section) memoizes these impure functions" is misleading because it is not the decorator that memoizes, but the decorator enables memoization. It could be rephrased as: "The doNotMemoize.py program demonstrates what happens when we use the @lru_cache decorator on these impure functions."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; This is such a nitpick that I'm pretty sure it's technically wrong.&lt;/p&gt;

&lt;blockquote&gt;The term "nondeterministic function" may confuse some readers. While technically accurate, the explanation might be clearer if it were phrased something like: "In functions whose output isn't predictable (such as a function that returns the current time)..."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; The definition of nondeterministic function was in a previous chunk.&lt;/p&gt;

&lt;blockquote&gt;The term "impure functions" might also be a bit confusing. It could be better defined as "functions that are non-deterministic or that have side effects, meaning they change the state outside of the function."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; The definition of impure function was in a previous chunk.&lt;/p&gt;

&lt;blockquote&gt;The phrase "Memoized functions must be pure—that is, they must be deterministic" may lead to some confusion. It could be more accurate to say, "For best results, functions to be memoized should ideally be pure—that is, they should be deterministic".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;The phrase "a programming paradigm that makes heavy use of recursion" can be misleading. While functional programming does often make use of recursion, this is not its defining characteristic. It could be better defined as "a programming paradigm that avoids changing state and mutable data."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; This is a nitpick, and also the suggested wording would make the book worse.&lt;/p&gt;


&lt;h2&gt;AI Feedback for Chapter 8 - Tail Call Optiization&lt;/h2&gt;



&lt;blockquote&gt;This statement "Many implementations of popular programming languages don’t even offer tail call optimization as a feature. These include interpreters and compilers for Python, JavaScript, and Java." is somewhat inaccurate. It's true for Python and Java, but not completely for JavaScript. ES6 does include proper tail call (PTC) semantics. As of my knowledge cutoff in 2021, however, its support across different JavaScript environments is uneven.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; From my actual testing, mainstream browsers do not support TCO.&lt;/p&gt;

&lt;blockquote&gt;In the JavaScript code for the factorial function, the syntax function factorial(number, accum=1) is incorrect. JavaScript does not support default parameter values like Python does. Instead, the proper way to define a default value for a function parameter in JavaScript is to use an if statement inside the function to check if the parameter is undefined and if so, assign a default value.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; JavaScript literally does support default parameters.&lt;/p&gt;

&lt;blockquote&gt;"The Java compiler since version 8 also doesn’t support tail call optimization." This implies that earlier versions of Java did support tail call optimization, which is not true. Java, regardless of the version, does not natively support tail call optimization.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Huh, I'm not sure where I heard that Java previosly supported TCO. But looking it up now, I think ChatGTP is right about this.&lt;/p&gt;

&lt;blockquote&gt;It would be useful to clarify the statement "The lack of tail call optimization is not unique to Python." While it is true that Python does not natively support tail call optimization, there are third-party libraries like recur.tco that provide support for this feature.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Sure, okay. But this isn't relevant to the discussion here.&lt;/p&gt;

&lt;blockquote&gt;The statement "Recall from Chapter 1 that all recursive algorithms can be implemented with a stack and a loop. Since tail call optimization removes the need for a call stack, we are effectively using recursion to simulate a loop’s iterative code." could be misleading for beginners. The statement seems to imply that tail call optimization is equivalent to iterative loops. However, it's not entirely accurate - tail call optimization allows a recursion to behave more like an iterative loop, but they are not equivalent.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; It's a metaphor.&lt;/p&gt;






&lt;h2&gt;AI Feedback for Chapter 9 - Drawing Fractals&lt;/h2&gt;




&lt;blockquote&gt;&lt;p&gt;Code execution errors in the JavaScript sections of the book. The JavaScript code is meant to execute the isOddTailCall function, but it mistakenly calls the isOdd function instead. The correct JavaScript code should look like:&lt;/p&gt;

&lt;pre&gt;document.write(isOddTailCall(42) + "&amp;lt;br /&amp;gt;");
document.write(isOddTailCall(99) + "&amp;lt;br /&amp;gt;");&lt;/pre&gt;&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Good catch here.&lt;/p&gt;

&lt;blockquote&gt;The function isOddTailCall in both Python and JavaScript versions does not handle negative inputs. While the text explicitly states that the functions are designed to handle only positive integers, it's typically good practice to include error handling for unexpected inputs.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; It's just an example.&lt;/p&gt;

&lt;blockquote&gt;The book describes the use of tail recursion to prevent stack overflows and then says that Python and JavaScript might optimize tail recursion to prevent stack overflows. As of my knowledge cutoff in September 2021, neither Python nor JavaScript optimizes tail recursion. The book should clarify that the availability of tail call optimization depends on the specific implementation of these languages and that the most common implementations (CPython for Python and V8 for JavaScript) do not support it.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; The book says this elsewhere.&lt;/p&gt;

&lt;blockquote&gt;There's an incomplete sentence at the end of the text, it reads "You’ll form a shape that, if you’re familiar with...". The sentence should be completed or removed.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;The book asserts that recursion can fail due to stack overflows while iterative solutions can't. This isn't always accurate. While it's true that recursion often presents a greater risk of stack overflows, iterative solutions can also cause stack overflows if they result in a call stack that exceeds its limits, such as with unbounded recursion.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Oh my god, this is technically true but is SUCH a nitpick.&lt;/p&gt;

&lt;blockquote&gt;While more of a matter of style, the sentence "Recursion can produce elaborate fractal art using surprisingly few lines of code." could be rephrased to avoid the use of the term "surprisingly" which could be considered subjective.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; I should update the prompt to tell ChatGPT that this book sometimes takes a casual conversational tone and that's fine.&lt;/p&gt;

&lt;blockquote&gt;There's a missing space in the sentence "As a technique, recursion is not automatically better or more sophisticated than iterative solutions." There should be a space between "iterative" and "solutions".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;The explanation of recursion in the context of isOdd() and isOddTailCall() could be made clearer. It's possible for a reader to misinterpret the alternating true/false nature of the functions.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; How can it be made clearer? This seems like ChatGPT is throwing stuff at the wall.&lt;/p&gt;



&lt;blockquote&gt;In the code section where the Sierpiński Triangle is drawn, there is a mistake in the import statement. It is written as "import turtle turtle.tracer(100, 0)". However...&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;In the same code block, the comment "# Return the x, y coordinate in the middle of the four given parameters." in the midpoint() function is technically inaccurate. It's not in the middle of the four parameters, but the midpoint between the two points defined by these four parameters.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Eh, if I caught this earlier in the writing process I'd go with this suggestion.&lt;/p&gt;

&lt;blockquote&gt;The recursive drawing of the Sierpiński Triangle is done by calling drawTriangle() recursively inside the same function. However, these recursive calls are made after the drawing of the initial triangle. Since the turtle operations are synchronous, the output will not fully align with the described behavior. The smaller triangles will be drawn first because of the depth-first nature of the recursion. It's not incorrect, but it might cause confusion to learners.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; I kind of see what ChatGPT is getting at, but it's really not a significant issue.&lt;/p&gt;

&lt;blockquote&gt;In the code snippet drawing the Sierpiński Carpet, the import statement is incorrect. It should be...&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;In the fractal tree section, the script calls variables LEFT_ANGLE, LEFT_DECREASE, RIGHT_ANGLE, RIGHT_DECREASE without defining them first, and later on, these variables are defined inside a while True loop, which is not recommended. They should be defined before calling drawBranch().&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Nah, this is a fake "best practice" but in ChatGPT's defense, is the same sort of bogus best practice an annoying human programmer would bring up in code review.&lt;/p&gt;

&lt;blockquote&gt;The random.seed(seed) call should be placed inside the while True loop, right before setting LEFT_ANGLE, LEFT_DECREASE, RIGHT_ANGLE, RIGHT_DECREASE. Otherwise, the random.seed(seed) call will only affect the random number generation once, instead of on each iteration of the loop.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; Uh, the random.seed(seed) call is, in fact, inside the while loop. ChatGPT's advice is right, but it misread the code.&lt;/p&gt;

&lt;blockquote&gt;In the line "You can also set the DRAW_SOLID constant to False and run the program.", it should be "You can also set the DRAW_SOLID constant to False and run the program." to properly highlight code syntax. This is not necessarily a grammatical error but improves readability for programmers.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; Weird. I think ChatGPT's output had the DRAW_SOLID name in monospace but that was lost when I copy/pasted this to this HTML file for this blog post. Anyway, the initial code was in monospace.&lt;/p&gt;




&lt;blockquote&gt;The python source code for kochSnowflake.py is missing the typical python executable script directive or shebang at the top (#!/usr/bin/env python3). While it's not strictly necessary, especially if you're running the code directly in an interactive Python environment, it's a good practice to include it for scripts meant to be run at the command line.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Weird that ChatGPT didn't complain about this for any of the other Python programs. It's not necessary and a distraction in this context.&lt;/p&gt;

&lt;blockquote&gt;The python code import turtle turtle.tracer(10, 0) in both kochSnowflake.py and hilbertCurve.py should be separated into two lines for correct syntax...&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;There is an incorrect statement in the book. It is mentioned, "Each time you create a new bump, you are increasing the curve’s length from three b / 3 lengths to four b / 3 lengths, or 4b / 3." This statement is technically incorrect because it implies that the length of the curve was initially 3 * b/3 which is b. After the transformation, it implies the length of the curve is 4b/3, but in reality, the length of the curve has increased to 4/3 * b.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; Uh, the initial length was 3 * b/3. And also, 4b/3 is the same as 4/3 * b. This advice was a waste of time.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;In the code for the Koch Snowflake, the function drawKochSnowflake is called only once at the end of the script with hardcoded parameters. This makes the code less reusable. It would be better to place the function call inside a main function, and then call that main function in a standard Python idiom:&lt;/p&gt;

&lt;pre&gt;    if __name__ == "__main__":
        main()&lt;/pre&gt;&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; It's weird that ChatGPT didn't complain about this for any other Python program. Anyway, it's not really a necessary best practice to follow for the context of this book.&lt;/p&gt;



&lt;blockquote&gt;There's a minor grammatical error in the sentence "A space-filling curve is a 1D line that curves around until it completely fills a 2D space without crossing over itself." It should be "A space-filling curve is a 1D line that curves around to completely fill a 2D space without crossing over itself."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;The phrase "To learn more about drawing with Python’s turtle module, I’ve written a simple tutorial at https://github.com/asweigart/simple-turtle-tutorial-for-python." should be rephrased for clarity and correctness. It should say, "For more information on drawing with Python's turtle module, refer to the simple tutorial I've written at https://github.com/asweigart/simple-turtle-tutorial-for-python."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;The sentence "The 3Blue1Brown YouTube channel has excellent animations of fractals, particularly the “Fractals Are Typically Not Self-Similar” video at https://youtu.be/gB9n2gHsHN4 and the “Fractal Charm: Space-Filling Curves” video at https://youtu.be/RU0wScIj36o." could be more concise. It should say, "For excellent animations of fractals, see the “Fractals Are Typically Not Self-Similar” and “Fractal Charm: Space-Filling Curves” videos on the 3Blue1Brown YouTube channel, available at https://youtu.be/gB9n2gHsHN4 and https://youtu.be/RU0wScIj36o respectively."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Meh.&lt;/p&gt;






&lt;h2&gt;AI Feedback for Chapter 10 - File Finder&lt;/h2&gt;






&lt;blockquote&gt;Technical Error: The hasEveryVowel function checks if the file name contains all the vowels (a, e, i, o, u), but the text states at one point that "We use a return statement with a lengthy expression that evaluates to True if name contains a, e, i, o, or u, indicating the file matches the search criteria." This should instead be "a, e, i, o, and u". The use of 'or' is incorrect in this context.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Actually, this is a good point.&lt;/p&gt;

&lt;blockquote&gt;Technical Suggestion: This is not necessarily an error, but the program could be improved for efficiency. Currently, the script is case insensitive only for vowel search (via the hasEveryVowel function) and not for even byte size search. Depending on the requirements, you might want to ensure case insensitivity across the board or at least mention this distinction in the text.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Yeah, but performance isn't a priority in the context of these teaching examples.&lt;/p&gt;

&lt;blockquote&gt;Grammar Error: In the section "Finding the Files with an Even Number of Bytes", there is an inconsistency in tense. The sentence "This gives you the powerful capability to write code for any search criteria you want." should be "This will give you the powerful capability to write code for any search criteria you want." to maintain future tense as in the preceding and following sentences.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; I'm not sure this is actually valid.&lt;/p&gt;

&lt;blockquote&gt;Grammar Error: In the section "The Recursive walk() Function", the text reads "The walk() function also recursively calls itself for each subfolder in the base folder it’s searching." This would read more clearly if it said "The walk() function also recursively calls itself for each subfolder in the base folder it is searching." This issue is about formality and readability, as "it's" can be less clear in formal writing.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; I should change the prompt to tell ChatGPT that this book isn't strictly written in a formal academic style.&lt;/p&gt;

&lt;blockquote&gt;Grammar Error: In the section "Finding Information About the File’s Name", the sentence "Enter the following into Python’s interactive shell." should end with a colon (:) instead of a period since it introduces a block of code.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Yes, but there's a sentence that follows it, which is why there's a period there. Maybe I should have added the followup sentence as a parenthetical instead? Either way, it'd be wrong to follow ChatGPT's advice here.&lt;/p&gt;

&lt;blockquote&gt;Grammar Suggestion: While not technically wrong, the phrase "since browser-based JavaScript has no way to access the folders on your computer" might be better worded as "because browser-based JavaScript cannot access the folders on your computer" for clarity.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Eh, I disagree.&lt;/p&gt;

&lt;blockquote&gt;Clarity Suggestion: In the section "The Complete File-Search Program", the text is unclear about what Figure 2-6 is. It's a minor point but could confuse readers who haven't seen this figure.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; The figures can't be included in the input to ChatGPT.&lt;/p&gt;







&lt;blockquote&gt;Python Code: In the 'visit' function, the 'hasVisited' variable is used, but it is not declared or passed as an argument. 'hasVisited' should be a global variable, or passed into 'visit' as an argument.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; hasVisited is, in fact, a global variable.&lt;/p&gt;

&lt;blockquote&gt;When adding the nextIntersection to hasVisited, it should be added after the visit(nextX, nextY) recursive call, to avoid marking locations as visited before actually visiting them.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; Making this change would introduce a bug in the program. ChatGPT is getting confused by the name of the variable.&lt;/p&gt;

&lt;blockquote&gt;JavaScript Code: The provided code snippet is incomplete, making it difficult to identify potential issues.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;JavaScript traditionally uses camelCase for variable names and function names, not snake_case as is more common in Python.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; The JS code here does use camelCase.&lt;/p&gt;

&lt;blockquote&gt;"While the time.localtime() function returns a struct_time object in the local time zone, the time.gmtime() function returns a struct_time object in the UTC or Greenwich Mean time zone." Here, "Greenwich Mean time" should be "Greenwich Mean Time."&lt;/blockquote&gt;



&lt;blockquote&gt;In the sentence "The algorithm works by visiting a starting space in the maze and then recursively visiting a neighboring space." a comma is required after "maze".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Good point. Counterpoint: no it doesn't.&lt;/p&gt;

&lt;blockquote&gt;"The interaction between these os.path functions (which return Unix epoch timestamps) and time functions (which return struct_time objects) can be confusing." The word "these" should be replaced with "the".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; No, "these" refers to the functions just used in the previous example.&lt;/p&gt;

&lt;blockquote&gt;In the sentence "After the walk() function returns a list of files matching your search criteria, you may want to rename, delete, or perform another operation on them." a comma is required after "criteria".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; There is literally a comma already there. ChatGPT even has the comma when it's quoting it.&lt;/p&gt;

&lt;blockquote&gt;"Calling os.unlink() permanently deletes the file, which can be dangerous if a bug in your program causes the function to delete the wrong file." This sentence is a bit awkward. It could be rephrased as "It can be dangerous to permanently delete a file using os.unlink() if a bug in your program causes the function to delete the wrong file."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;"To install this module, run run python -m pip install --user send2trash from the command prompt on Windows or run run python3 -m pip install from the terminal on macOS or Linux." The word "run" is unnecessarily repeated here.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Good catch.&lt;/p&gt;

&lt;blockquote&gt;"The search criteria are implemented as match functions, which are passed to the walk() function." The phrase "are implemented as match functions" should be replaced with "is implemented as a match function" for better clarity.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; "Criteria" is plural.&lt;/p&gt;

&lt;blockquote&gt;"Chapter 4 described a recursive algorithm that solves mazes, but another recursive algorithm generates mazes." A comma is needed after "mazes".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; Again, ChatGPT says a comma is necessary where there is already a comma and ChatGPT is quoting that comma.&lt;/p&gt;

&lt;blockquote&gt;"Let’s begin by taking a look at the complete Python and JavaScript source code for the program, which uses the recursive backtracking algorithm for maze generation." The phrase "Let's begin by taking a look" could be rephrased as "Let's start by examining".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;






&lt;blockquote&gt;In JavaScript, to check if an array contains another array, the provided code is using JSON.stringify(hasVisited).includes(JSON.stringify([x, y - 2])) etc. This is not the most efficient or reliable way to do it because it depends on the serialization of the arrays, and the order of the elements matters. A better approach would be to use a suitable data structure or function to do this operation.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; In the context of professional JS code, yes, but not for this book.&lt;/p&gt;

&lt;blockquote&gt;The statement "Unfortunately, JavaScript doesn’t have a way to set the seed value explicitly and will generate different mazes each time we run the program" is not accurate. While it's true that JavaScript's Math.random() does not support seeding directly, you can implement a pseudorandom number generator (PRNG) with a seed.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Cool. Yeah. Okay. I'll just reimplement my own PRNG. Thanks, ChatGPT. Great advice.&lt;/p&gt;

&lt;blockquote&gt;When explaining the maze data structure in JavaScript, the book mentioned that JavaScript objects are used as dictionaries. While this is generally correct, it's worth noting that JavaScript actually has a Map object that is more similar to Python's dictionaries, because it preserves the key insertion order.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Correct but not relevant.&lt;/p&gt;

&lt;blockquote&gt;In the sentence "If all the neighboring spaces have already been visited, the algorithm is at a dead end and backtracks to an earlier visited space to visit its unvisited neighbors," the phrase "an earlier visited space to visit its unvisited neighbors" is awkward and can be simplified for clarity. A better phrase might be "an earlier visited space to explore any unvisited neighbors."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Actually, yeah I like this phrasing.&lt;/p&gt;

&lt;blockquote&gt;In the sentence "The Python code continues by setting a few more constants," the word "constants" should be replaced with "variables" because in Python, there are no constants; the term "constant" is generally used for a variable whose value does not change. Although Python does not enforce this, it's just a naming convention in Python to have constants in all capital letters.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Correct but not relevant. Really not relevant.&lt;/p&gt;

&lt;blockquote&gt;In the sentence "This string notes whether this space is a blocking wall or a passable empty space in the maze," the term "notes" can be replaced with "indicates" or "denotes" for clarity.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;





&lt;blockquote&gt;"After this, we add the x, y coordinates of nextX and nextY to the hasVisited list or array before making the recursive call for this neighboring space." - This sentence lacks context for the variables nextX and nextY. If this is part of a longer passage, it may be fine, but standalone, it's confusing. A code snippet or a description about how nextX and nextY are calculated would be useful.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;

&lt;blockquote&gt;"When no neighbors exist, the base case simply returns to an earlier location." - This sentence is slightly misleading. A base case in recursion refers to a condition that stops the recursion, not an action that's taken when a condition is met. It would be better to say something like "When no unvisited neighbors exist, the recursive function returns, which effectively moves the algorithm back to a previous location."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; ChatGPT is correct in general, but is wrong here because this chunk of text didn't have the earlier context.&lt;/p&gt;

&lt;blockquote&gt;"We can represent a well-connected maze with no loops as a DAG—that is, a tree data structure." - This sentence is not technically correct. A DAG, or Directed Acyclic Graph, is not a tree. While all trees (where each node has at most one parent) are DAGs, not all DAGs are trees. A maze with no loops could be represented as a tree, but once you have multiple possible paths from the start to a specific point (which is common in many mazes), it is better represented as a DAG but not specifically a tree.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Okay, so ChatGPT is misunderstanding here, but only because it turns out I'm using the term "well-connected maze" which isn't a real term. I need to replace it with "simply connected maze" which is a real term. And I also need to search the rest of the book for "well-connected maze" as well. So ChatGPT did help me find a mistake in the text.&lt;/p&gt;

&lt;blockquote&gt;"Incidentally, mathematicians have proven that even the hardest 15-puzzle can be solved in 80 moves." - While this statement is true, it would be better if it included a reference to support this fact.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Huh, I thought I saw this on Wikipedia but it isn't there. I did find it in the paper &lt;a href="https://aaai.org/Papers/AAAI/2005/AAAI05-219.pdf"&gt;"Large-Scale Parallel Breadth-First Search" by Richard E. Korf and Peter Schultze&lt;/a&gt;. I'm not going to add this here, but I should probably update Wikipedia.&lt;/p&gt;

&lt;blockquote&gt;"Just as you can turn a maze into a DAG, you can convert a 15-puzzle into a tree graph" - The phrasing "turn a maze into a DAG" and "convert a 15-puzzle into a tree graph" could be confusing, because it could be interpreted as saying that the maze or puzzle is being altered in some way, rather than just being represented or modeled as a DAG or tree.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;In Python Code: The solve function contains the following code snippet:&lt;/p&gt;

&lt;pre&gt;if solved:
    displayBoard(board)
    for move in solutionMoves:
        print('Move', move)
        makeMove(board, move)
        print() # Print a newline.
        displayBoard(board)&lt;/pre&gt;

&lt;p&gt;The purpose of solving the puzzle is defeated here because we are making the moves again after we have already solved the puzzle. If we want to display the board after every move, we should maintain a list of board states and use that for printing. The list would contain the state of the board after every move made.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; Oh god, this is terrible advice. One: we don't want to display the board after move because that'd be a ton of useless output that would make hte program unworkably slow. It'd also eat up tons of memory for no reason required by this program.&lt;/p&gt;

&lt;blockquote&gt;"Clever algorithms are available for solving 15-puzzles, but we could also just recursively explore the entire tree graph until we find a path from the root node to the solution node." - The use of "just" here seems to downplay the complexity of the task. It might be better to say "alternatively" or "also".&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;"The root node is the starting state of the 15-puzzle. The solved-state node is the one in which the tiles are ordered correctly." - For clarity, it might be better to combine these two sentences: "The root node represents the starting state of the 15-puzzle, while the solved-state node corresponds to the correctly ordered tiles."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;"Because it is possible to&lt;/blockquote&gt;

&lt;p&gt;ChatGPT cut off it's output here for some reason.&lt;/p&gt;





&lt;h2&gt;AI Feedback for Chapter 13 - Fractal Art Maker&lt;/h2&gt;


&lt;blockquote&gt;In the drawFractal() function, there's a base case that stops recursion when the size of the shape is less than 1. This could potentially be misleading or confusing to some readers as in many graphics systems, you can draw shapes that are less than a pixel in size and they will simply not be visible. The base case could perhaps be better described as stopping when the shapes are too small to be visible, rather than when their size is less than 1.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;In the first Python code snippet of drawFilledSquare(size, depth): size = int(size), there's an error due to incorrect indentation. It should be...&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt;&lt;/p&gt;


&lt;blockquote&gt;&lt;p&gt;The description of the turtle's movement to the top-right corner of the square is incorrect. The code provided does not move the turtle to the top-right corner but to the top-left corner of the square. Here's the correct version:&lt;/p&gt;

&lt;pre&gt;# Move to the top-left corner before drawing: 
turtle.penup() 
turtle.backward(size // 2)  # Move to the left
turtle.left(90) 
turtle.forward(size // 2)  # Move upwards
turtle.right(90)  # Now facing the right direction to start drawing
turtle.pendown()&lt;/pre&gt;&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; This is weird. Both the code and the text talk about the top-right corner, but ChatGPT just decided this was wrong and that the book actually wants to use the top-left corner. The suggested code does work to move the turtle to the top-left corner, but I do not want to move it to the top-left corner. I want to move it to the top-right corner. What a weird suggestion to make.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;In the triangle drawing section, there's an incorrect statement that says "to begin drawing the line on the right side, the turtle must turn 90 degrees right to face rightward, then an additional 60 degrees to face the bottom-right corner of the triangle." This is wrong, as the turtle needs to turn 150 degrees right in total to face the bottom-left corner of the triangle, not the bottom-right corner. So, the corrected code and explanation should be:&lt;/p&gt;&lt;/blockquote&gt;

&lt;pre&gt;# Move the turtle to the top of the equilateral triangle:
height = size * math.sqrt(3) / 2
turtle.penup()
turtle.left(90) # Turn to face upward.
turtle.forward(height * (2/3)) # Move to the top corner.
turtle.right(150) # Turn to face the bottom-left corner.
turtle.pendown()&lt;/pre&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; Again, this is completely wrong.&lt;/p&gt;

&lt;blockquote&gt;Also, when talking about the angleChange value in the context of the fractal generation, the text doesn't mention that this value is in degrees, which might lead to confusion. This clarification could be added for completeness.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; I mention degrees later, but yeah I should have added this sooner too.&lt;/p&gt;

&lt;blockquote&gt;"The for loop assigns an individual specification dictionary in the specs list to the loop variable spec on each iteration of the loop." The sentence can be a bit clearer. Suggested change: "On each iteration of the loop, the for loop assigns an individual specification dictionary from the specs list to the loop variable, spec."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;"The get() dictionary method calls pull the values for the sizeChange, xChange, yChange, and angleChange keys from this dictionary..." The phrase "method calls pull" is a bit confusing. Suggested change: "The get() method calls extract the values for the sizeChange, xChange, yChange, and angleChange keys from this dictionary..."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;"The shapeDrawFunction, specs, and maxDepth arguments are passed to the recursive drawFractal() call unchanged." This sentence is grammatically correct, but could use some rewording for clarity. Suggested change: "The arguments: shapeDrawFunction, specs, and maxDepth, are passed unchanged to the recursive drawFractal() call."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;"Finally, with the turtle in the correct position and heading for the next shape, we make the recursive call to drawFractal():" The use of "we" in technical documents is often considered informal. Suggested change: "Finally, once the turtle is in the correct position and heading for the next shape, a recursive call to drawFractal() is made:"&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; This is actually worse, since it changes the text to passive voice.&lt;/p&gt;

&lt;blockquote&gt;"The Glider fractal here has additional Gliders drawn inside each of its five cells." The capitalization of "gliders" seems inconsistent with the rest of the text. Suggested change: "The glider fractal here has additional gliders drawn inside each of its five cells."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;"The squares are created slightly higher or lower than their previous square and rotated either 10 or -10 degrees." Suggested change for clarity: "The squares are created slightly higher or lower than the previous square and are rotated by either 10 or -10 degrees."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;"The center of these smaller triangles is size * math.sqrt(3) / 6 units from the center of the previous triangle." The phrase "size * math.sqrt(3) / 6 units" may be clearer with parentheses to indicate the intended order of operations. Suggested change: "The center of these smaller triangles is (size * math.sqrt(3) / 6) units from the center of the previous triangle."&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;"These shapes are drawn with a size of 600:" This sentence is a bit confusing as the code below it shows shapes drawn with a size of 400, not 600. If 400 is the correct size, the sentence should read: "These shapes are drawn with a size of 400:"&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Good catch. This should be 400 instead of 600. I think this was a copy/paste mistake when I was editing the text.&lt;/p&gt;



&lt;h2&gt;AI Feedback for Chapter 14 - Droste Maker&lt;/h2&gt;






&lt;blockquote&gt;A potential area of confusion might be the term 'base case' in the context of recursion. This term is correctly used but it might be helpful to define or explain what a 'base case' is at the beginning of the chapter for those who are not familiar with the term. The 'base case' in a recursive function is a condition that stops the function from calling itself ad infinitum.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; This was explained previously in the book.&lt;/p&gt;

&lt;blockquote&gt;As a minor point, you could consider clarifying that the magentaColor variable is a tuple, which is a type of collection in Python that is ordered and immutable. This could be a useful point of learning for readers who are less familiar with Python.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Slightly helpful but disregarded edit suggestion.&lt;/i&gt; Meh.&lt;/p&gt;


&lt;blockquote&gt;Misunderstanding of aspect ratio: The book states, "Because the aspect ratio is the width divided by the height, images with large aspect ratios are taller than they are wide, and those with small aspect ratios are wider than they are tall." This is incorrect. The aspect ratio is indeed the width divided by the height, but images with a larger aspect ratio are wider, not taller.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Novel and useful feedback!&lt;/i&gt; Good catch.&lt;/p&gt;

&lt;blockquote&gt;Code snippet about resizing the image: It seems you are resizing the image to be equal or larger in both dimensions (width and height) than the magenta area. This would potentially distort the image as you are not respecting the aspect ratio. You should keep the same aspect ratio while making sure that either the width or height is equal or larger than the magenta area, not both.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;False positive!&lt;/i&gt; The code snippet has an if-else statement, so it's doing one or the other, not both.&lt;/p&gt;

&lt;blockquote&gt;"We need to resize the base image to cover the entire magenta area and no more." – This sentence is awkward. "And no more" can be removed for clarity.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;"We call the resize() method once to produce a new Pillow Image object resized to match either the width of the base image or the height of the base image." – This sentence could be rephrased for clarity. It currently implies that the resized image is the same width or height as the base image, which could lead to confusion.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Unhelpful feedback.&lt;/i&gt; Meh.&lt;/p&gt;

&lt;blockquote&gt;"However, the resized image itself could have magenta pixels, and if so, these will now become part of the base image, as in the top-right image of Figure 14-2." – The reference to a figure (Figure 14-2) that hasn't been mentioned before could cause confusion. If this figure isn't relevant, the sentence can be rephrased to improve clarity.&lt;/blockquote&gt;

&lt;p&gt;&lt;i&gt;Formatting issue.&lt;/i&gt; The figure isn't included in ChatGPT's input.&lt;/p&gt;


</content><category term="misc"></category></entry><entry><title>Book Review: Python Distilled</title><link href="https://inventwithpython.com/blog/book-review-python-distilled.html" rel="alternate"></link><published>2023-03-28T12:00:00-04:00</published><updated>2023-03-28T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2023-03-28:/blog/book-review-python-distilled.html</id><summary type="html">&lt;p&gt;5 stars. &lt;a href="https://amzn.to/3FYkYdm"&gt;Python Distilled&lt;/a&gt; has a perfectly suited title: This book is for experienced programmers who want to learn Python and need the Python programming language's core syntax, standard library, and Pythonic idioms. However, if you're a developer who wants to add Python to your toolbox, I recommend Python Distilled as one of the first books you should read.&lt;/p&gt;</summary><content type="html">

&lt;blockquote&gt;&lt;a href="https://amzn.to/3FYkYdm"&gt;Python Distilled&lt;/a&gt; by David Beazley. Published by Pearson September 2021. 352 pages.&lt;/blockquote&gt;

&lt;p&gt;5 stars. &lt;a href="https://amzn.to/3FYkYdm"&gt;Python Distilled&lt;/a&gt; has a perfectly suited title: This book is for experienced programmers who want to learn Python and need the Python programming language's core syntax, standard library, and Pythonic idioms. However, if you're a developer who wants to add Python to your toolbox, I recommend Python Distilled as one of the first books you should read.&lt;/p&gt;

&lt;p&gt;Beazley endorses the view that programming should be kept simple by using simple syntax, and it shows in this book. But don't think that Python Distilled is afraid to dive into the advanced features of the Python language. And at 350 pages, the book strikes a good balance between giving you the information you need without overwhelming you with every nook and cranny of the language.&lt;/p&gt;

&lt;p&gt;While the title might make you think the information is densely packed, I didn't have any trouble reading through the chapters. The book is well-paced.&lt;/p&gt;

&lt;p&gt;If you're a complete beginner to programming, I'd recommend starting with a different book. But Python Distilled could easily be the second book on your list once you have some experience.
For a comparable book, Python Distilled is similar in its aim as &lt;a href="https://amzn.to/3nhdxYf"&gt;The Quick Python Book by Naomi Ceder&lt;/a&gt;. After reading this book, I'd recommend moving on to &lt;a href="https://amzn.to/3lWmu90"&gt;Fluent Python by Luciano Ramahlo&lt;/a&gt;, &lt;a href="https://amzn.to/3LTuanf"&gt;Effective Python by Brett Slatkin&lt;/a&gt;, &lt;a href="https://amzn.to/3ZpzoKC"&gt;Python in a Nutshell by Martelli, Ravenscroft, Holden, and McGuire&lt;/a&gt;, and &lt;a href="https://nostarch.com/seriouspython"&gt;Serious Python by Julien Danjou&lt;/a&gt;.&lt;/p&gt;


</content><category term="misc"></category></entry><entry><title>Book Review: Python in a Nutshell</title><link href="https://inventwithpython.com/blog/book-review-python-in-a-nutshell.html" rel="alternate"></link><published>2023-03-28T12:00:00-04:00</published><updated>2023-03-28T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2023-03-28:/blog/book-review-python-in-a-nutshell.html</id><summary type="html">&lt;p&gt;5 stars. &lt;a href="https://amzn.to/3ZpzoKC"&gt;Python in a Nutshell&lt;/a&gt; is an excellent book for professional developers who have some experience with Python but want to take the leap to being a senior Python developer, Python in a Nutshell is required reading.&lt;/p&gt;</summary><content type="html">

&lt;blockquote&gt;&lt;a href="https://amzn.to/3ZpzoKC"&gt;Python in a Nutshell, 4th Edition&lt;/a&gt; by Alex Martelli, Anna Ravenscroft, Steve Holden, Paul McGuire. Published by O'Reilly February 2023. 735 pages.&lt;/blockquote&gt;

&lt;p&gt;5 stars. &lt;a href="https://amzn.to/3ZpzoKC"&gt;Python in a Nutshell&lt;/a&gt; is an excellent book for professional developers who have some experience with Python but want to take the leap to being a senior Python developer, Python in a Nutshell is required reading.&lt;/p&gt;

&lt;p&gt;This is not a book for beginners new to programming, and I’d be hesitant to say that programmers experienced in other languages should use this book to learn Python (but if you’re the bold adventurous sort, have at it.) But if you want to fill out your Python knowledge and go beyond a developer who “kinda sorta” knows Python and copies and pastes Python code from Stack Overflow without much thought, this is a book you need to read.&lt;/p&gt;

&lt;p&gt;In a way, the title is a bit misleading. “In a nutshell” gives you the idea that this book is small, but it’s actually over 700 pages long. Rather, this book is compact: it doesn’t waste time covering the basics and is quite dense with useful information.&lt;/p&gt;

&lt;p&gt;The density of this book sometimes works against itself; there were quite a few times I had to carefully parse out the words and could have really used some example code to demonstrate the concepts. I understand why the authors skipped it: not everyone will need the additional explanation for every concept, and providing these examples would have doubled the page count. (For example, the object-oriented chapter led me to take quite a few breaks to test out what I had learned in the Python interactive shell just to make sure I understood what I read.) Suffice it to say, the information in the book is useful and accurate. It’s worth the time it sometimes takes to parse through the difficult concepts.&lt;/p&gt;

&lt;p&gt;Other times though, it’s a breeze. There’s quite a wide range of topics covered in this book, and I feel like they do a good job going deep enough into each one without losing the big picture. And quite frankly: a lot of the information in this book just plain isn’t easy to find online. Without this book, you’d be chasing endless search result links to blog posts of varying quality, all while still missing out of several topics. The real value of Python in a Nutshell is that it gathers all of this knowledge into one well-written book.&lt;/p&gt;

&lt;p&gt;Other books similar to Python in a Nutshell include &lt;a href="https://amzn.to/3lWmu90"&gt;Fluent Python by Luciano Ramahlo&lt;/a&gt;, &lt;a href="https://amzn.to/3LTuanf"&gt;Effective Python by Brett Slatkin&lt;/a&gt;, and &lt;a href="https://nostarch.com/seriouspython"&gt;Serious Python by Julien Danjou&lt;/a&gt;. I recommend reading all of these books though; while they have some overlap they complement rather than replace each other. If you’re experienced in a different language but want to learn Python, &lt;a href="https://amzn.to/3LZUAUm"&gt;Python Distilled by David Beazley&lt;/a&gt; is a good book to read first for general Python information, and then followed up by &lt;a href="https://amzn.to/3ZpzoKC"&gt;Python in a Nutshell&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Even as a software engineer with years of experience in Python myself, I kept finding little nuggets of information that has filled in gaps of my Python knowledge that I didn’t know I had. Again, it’s not a book for total beginners but don’t let that scare you off. As long as you have the basics of programming concepts and are generally comfortable writing Python code, go ahead and get started with Python in a Nutshell.&lt;/p&gt;


</content><category term="misc"></category></entry><entry><title>How to Install Pygame on Windows, macOS, and Linux</title><link href="https://inventwithpython.com/blog/install-pygame-on-windows-macos-linux.html" rel="alternate"></link><published>2023-01-27T12:00:00-05:00</published><updated>2023-01-27T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2023-01-27:/blog/install-pygame-on-windows-macos-linux.html</id><summary type="html">Installing Pygame on your computer is fairly straightforward, but there are a few potential bumps to be aware of.</summary><content type="html">

&lt;p&gt;If you know how to run pip (Python's package installer) from a terminal window, run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pip install pygame&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you don't know how to use pip in the terminal, then you can install in Python's interactive shell (or REPL) by running:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import subprocess, sys; subprocess.run([sys.executable, '-m', 'pip', 'install', 'pygame'])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(This is the way &lt;a href=""&gt;&lt;code&gt;pipfromrepl&lt;/code&gt;&lt;/a&gt; installs Python packages from the interactive shell.)&lt;/p&gt;

&lt;p&gt;This process works on Windows, macOS, and Linux for Python versions 3.6 to 3.10. Note that as of January 2023, the Pygame maintainers haven't updated Pygame to work on Python 3.11 or 3.12. However, there is a .whl "wheel" file available to install Pygame on Python 3.11 on Windows. Either run this from a Command Prompt or Windows Terminal window:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pip install https://inventwithpython.com/pygame/pygame-2.1.2-cp311-cp311-win_amd64.whl&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Or run this from the interactive shell:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import subprocess, sys; subprocess.run([sys.executable, '-m', 'pip', 'install', 'https://inventwithpython.com/pygame/pygame-2.1.2-cp311-cp311-win_amd64.whl'])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can test to see if Pygame was installed successfully by importing it from the interactive shell. If no error message appears, the installation was successful:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import pygame&lt;br /&gt;
  &amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This blog post will be updated as new versions of Python and Pygame are made available.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Python Linter Comparison 2022: Pylint vs Pyflakes vs Flake8 vs autopep8 vs Bandit vs Prospector vs Pylama vs Pyroma vs Black vs Mypy vs Radon vs mccabe</title><link href="https://inventwithpython.com/blog/comparing-python-linters-2022.html" rel="alternate"></link><published>2022-11-19T12:00:00-05:00</published><updated>2022-11-19T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-11-19:/blog/comparing-python-linters-2022.html</id><summary type="html">&lt;p&gt;As you can tell from the lengthy title, there are many linting tools for Python. Some of them have near-identical names as each other. In November 2022, I upgraded my text editor to Sublime Text 4 and then took the opportunity to spend a few hours reviewing all of the Python linters I could find. After personally reviewing all of them, I've selected the following as must-haves: Pyflakes, Mypy, and Black. If you'd like additional tools, I also liked: Radon, Pyroma, and docformatter. I'm using &lt;a href="https://docs.python.org/3.12/whatsnew/3.12.html"&gt;Python 3.12.0&lt;/a&gt;. I don't care for my linter to point out when I stray from the certain dictates in the &lt;a href="http://www.python.org/dev/peps/pep-0008/"&gt;PEP 8&lt;/a&gt; document, and my linter choices reflect that. You might have different needs and values than I, so in this blog post I give my reasoning and views for each linter.&lt;/p&gt;</summary><content type="html">

&lt;blockquote&gt;I'm giving a talk on static code analysis tools at PyCon 2023. &lt;a href="https://docs.google.com/presentation/d/1kHK5M4GpB_qSQO3aGbVzUtWaSwqsEaMqikuIeM4VDjk/edit?usp=sharing"&gt;Here's the slidedeck.&lt;/a&gt; I'll be updating this blog post as I learn more information.&lt;/blockquote&gt;

&lt;p&gt;As you can tell from the lengthy title, there are many linting tools for Python. Some of them have near-identical names as each other. In November 2022, I upgraded my text editor to Sublime Text 4 and then took the opportunity to spend a few hours reviewing all of the Python linters I could find. After personally reviewing all of them, I've selected the following as must-haves: Pyflakes, Mypy, and Black. If you'd like additional tools, I also liked: Radon, Pyroma, and docformatter. I'm using &lt;a href="https://docs.python.org/3.12/whatsnew/3.12.html"&gt;Python 3.12.0&lt;/a&gt;. I don't care for my linter to point out when I stray from the certain dictates in the &lt;a href="http://www.python.org/dev/peps/pep-0008/"&gt;PEP 8&lt;/a&gt; document, and my linter choices reflect that. You might have different needs and values than I, so in this blog post I give my reasoning and views for each linter.&lt;/p&gt;

&lt;p&gt;You can skip most of this discussion by using an IDE such as &lt;a href="https://www.jetbrains.com/pycharm/"&gt;PyCharm Community Edition&lt;/a&gt; and &lt;a href="https://code.visualstudio.com/"&gt;Microsoft Visual Studio Code&lt;/a&gt;, which already have integrated linters set up for you. But this blog post is good if you want to customize your development environment.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Linter&lt;/i&gt; is an overly broad term for a tool that examines your source code without running it (aka &lt;i&gt;static code analysis&lt;/i&gt;) in order to point out possible issues. This early warning system speeds up your development, as you can fix them early in the development process rather than wait for QA or users to point them out. The term linter comes from how dryer lint traps can remove small bits of fluff from clothing. I've broken up these tools into the following categories:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;i&gt;Type checkers&lt;/i&gt; verify that your program follows their own type annotations (aka type hints). (Mypy, Pyright, Pyre, Pytype) &lt;/li&gt;
    &lt;li&gt;&lt;i&gt;Error linters&lt;/i&gt; point out syntax errors or other code that will result in unhandled exceptions and crashes. (Pylint, Pyflakes, Flake8)&lt;/li&gt;
    &lt;li&gt;&lt;i&gt;Style linters&lt;/i&gt; point out issues that don't cause bugs but make the code less readable or are not in line with style guides such as Python's &lt;a href="http://www.python.org/dev/peps/pep-0008/"&gt;PEP 8&lt;/a&gt; document. (Pylint, Flake8)&lt;/li&gt;
    &lt;li&gt;&lt;i&gt;Packaging linters&lt;/i&gt; point out issues related to packaging your code for distribution on PyPI with properly formatted descriptions, versions, and meta data fields. (Pyroma)&lt;/li&gt;
    &lt;li&gt;&lt;i&gt;Security linters&lt;/i&gt; point out possible security vulnerabilities in your code. (Bandit, Dodgy)&lt;/li&gt;
    &lt;li&gt;&lt;i&gt;Code formatters&lt;/i&gt; change the style of your code (mostly revolving around proper whitespace) without affecting the behavior of the program. (Black)&lt;/li&gt;
    &lt;li&gt;&lt;i&gt;Dead code linters&lt;/i&gt; remove commented-out code from your program, since this practice should be skipped in favor of proper version control. (Vulture, eradicate) &lt;/li&gt;
    &lt;li&gt;&lt;i&gt;Docstring linters/formatters&lt;/i&gt; point out (and may correctly format) style issues in docstrings that aren't in line with Python's &lt;a href="https://peps.python.org/pep-0257/"&gt;PEP 257&lt;/a&gt; document. (pydocstringformatter, docformatter)&lt;/li&gt;
    &lt;li&gt;&lt;i&gt;Complexity analyzers&lt;/i&gt; point out code that is so complex that they can affect readability. (mccabe, Radon)&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Many of these Python packages include other Python packages, as explained in this table:&lt;/p&gt;

&lt;table class="table"&gt;
    &lt;tr&gt;&lt;td&gt;This Package...&lt;/td&gt;&lt;td&gt;...Includes These Packages&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;a href="https://pypi.org/project/flake8/"&gt;flake8&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://pypi.org/project/pyflakes/"&gt;Pyflakes&lt;/a&gt;, &lt;a href="https://pypi.org/project/pycodestyle/"&gt;pycodestyle&lt;/a&gt;, &lt;a href="https://pypi.org/project/mccabe/"&gt;mccabe&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;a href="https://pypi.org/project/autopep8/"&gt;autopep8&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://pypi.org/project/pycodestyle/"&gt;pycodestyle&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;a href="https://pypi.org/project/pylama/"&gt;Pylama&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://pypi.org/project/pycodestyle/"&gt;pycodestyle&lt;/a&gt;, &lt;a href="https://pypi.org/project/pydocstyle/"&gt;pydocstyle&lt;/a&gt;, &lt;a href="https://pypi.org/project/pyflakes/"&gt;Pyflakes&lt;/a&gt;, &lt;a href="https://pypi.org/project/mccabe/"&gt;mccabe&lt;/a&gt;, &lt;a href="https://pypi.org/project/pylint/"&gt;Pylint&lt;/a&gt;, &lt;a href="https://pypi.org/project/radon/"&gt;Radon&lt;/a&gt;, &lt;a href="https://pypi.org/project/eradicate/"&gt;eradicate&lt;/a&gt;, &lt;a href="https://pypi.org/project/mypy/"&gt;Mypy&lt;/a&gt;, &lt;a href="https://pypi.org/project/vulture/"&gt;Vulture&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;a href="https://pypi.org/project/prospector"&gt;Prospector&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href="https://pypi.org/project/pylint"&gt;Pylint&lt;/a&gt;, &lt;a href="https://pypi.org/project/flake8"&gt;flake8&lt;/a&gt; (including &lt;a href="https://pypi.org/project/pyflakes/"&gt;Pyflakes&lt;/a&gt;, &lt;a href="https://pypi.org/project/pycodestyle/"&gt;pycodestyle&lt;/a&gt;, &lt;a href="https://pypi.org/project/mccabe/"&gt;mccabe&lt;/a&gt;), &lt;a href="https://pypi.org/project/dodgy/"&gt;dodgy&lt;/a&gt;, &lt;a href="https://pypi.org/project/isort/"&gt;isort&lt;/a&gt;, &lt;a href="https://pypi.org/project/pydocstyle/"&gt;pydocstyle&lt;/a&gt;, &lt;a href="https://pypi.org/project/pep8-naming/"&gt;pep8-naming&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;




&lt;h2&gt;Disqualified Packages&lt;/h2&gt;

&lt;p&gt;Out of the running immediately is &lt;a href="https://pypi.org/project/PyChecker/"&gt;PyChecker&lt;/a&gt; because it hasn't been updated since 2003, so I skipped evaluating it. The same goes for &lt;a href="https://pypi.org/project/pylava/"&gt;Pylava&lt;/a&gt;, a fork of Pylama that hasn't been updated since September 2020. I'd just use the original Pylama instead.&lt;/p&gt;




&lt;h2&gt;Mypy (Type Checker)&lt;/h2&gt;

&lt;p&gt;Mypy is not a linter but rather a static type checking tool. You should install and use it no matter what Python linter you use. Mypy examines the type hints (aka type annotations) you put on your functions and variables and ensures the rest of the program correctly follows them. It's a great way to find bugs early. I wrote &lt;a href="https://inventwithpython.com/blog/2019/11/24/type-hints-for-busy-python-programmers/"&gt;a blog post on Python type hints&lt;/a&gt; if you need to learn more. Mypy was the first static type checker for Python and has been suitable for my needs; I haven't evaluated the others: &lt;a href="https://github.com/microsoft/pyright"&gt;Pyright&lt;/a&gt; from Microsoft, &lt;a href="https://pypi.org/project/pyre-check/"&gt;Pyre&lt;/a&gt; from Facebook (installed with &lt;code&gt;pip install pyre-check&lt;/code&gt; and not &lt;code&gt;pip install pyre&lt;/code&gt;), and &lt;a href="https://pypi.org/project/pytype/"&gt;Pytype&lt;/a&gt; from Google.&lt;/a&gt;




&lt;h2&gt;Pyflakes (Error Linter)&lt;/h2&gt;

&lt;p&gt;From the &lt;a href="https://pypi.org/project/pyflakes/"&gt;Pyflakes&lt;/a&gt; project page:&lt;/p&gt;

&lt;blockquote&gt;Pyflakes makes a simple promise: it will never complain about style, and it will try very, very hard to never emit false positives. Pyflakes is also faster than &lt;a href="https://pylint.pycqa.org/"&gt;Pylint&lt;/a&gt;. This is largely because Pyflakes only examines the syntax tree of each file individually. As a consequence, Pyflakes is more limited in the types of things it can check. If you like Pyflakes but also want stylistic checks, you want &lt;a href="https://pypi.org/project/flake8/"&gt;flake8&lt;/a&gt;, which combines Pyflakes with style checks against &lt;a href="https://www.python.org/dev/peps/pep-0008/"&gt;PEP 8&lt;/a&gt; and adds per-project configuration ability.&lt;/blockquote&gt;




&lt;h2&gt;Pylint (Error and Style Linter)&lt;/h2&gt;

&lt;p&gt;From the Pylint project page:&lt;/p&gt;

&lt;blockquote&gt;Pylint is a static code analyzer for Python 2 or 3. Pylint analyses your code without actually running it. It checks for errors, enforces a coding standard, looks for code smells, and can make suggestions about how the code could be refactored.&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://pylint.pycqa.org/"&gt;Pylint has extensive documentation.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see the kinds of code smells that Pylint points out in this example output:&lt;/p&gt;

&lt;pre&gt;example.py:609:35: C0103: Argument name "n" doesn't conform to snake_case naming style (invalid-name)
example.py:642:15: R1701: Consider merging these isinstance calls to isinstance(PAUSE, (float, int)) (consider-merging-isinstance)
example.py:655:4: R1705: Unnecessary "elif" after "return", remove the leading "el" from "elif" (no-else-return)
example.py:670:12: R1705: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it (no-else-return)
example.py:675:12: W0707: Consider explicitly re-raising using 'except Exception as exc' and 'raise ImageNotFoundException from exc' (raise-missing-from)
example.py:686:20: C0209: Formatting a regular string which could be a f-string (consider-using-f-string)
example.py:771:17: W0212: Access to a protected member _position of a client class (protected-access)
example.py:1038:0: R0913: Too many arguments (7/5) (too-many-arguments)
example.py:1168:47: E1306: Not enough arguments for format string (too-few-format-args)
example.py:1418:0: R0914: Too many local variables (17/15) (too-many-locals)
-----------------------------------
Your code has been rated at 3.42/10&lt;/pre&gt;

&lt;p&gt;I prefer Pyflakes to Pylint, though running &lt;code&gt;pylint --errors-only&lt;/code&gt; cuts out the style suggestions and gives me output similar to Pyflakes. Pylint tends to be slower than Pyflakes and flake8 as well as give more false positives. I'd use Pylint if you want to be especially thorough with your code.&lt;/p&gt;




&lt;h2&gt;pycodestyle (Style Linter)&lt;/h2&gt;

&lt;p&gt;From the &lt;a href="https://pypi.org/projects/pycodestyle"&gt;pycodestyle&lt;/a&gt; project page:&lt;/p&gt;

&lt;blockquote&gt;pycodestyle is a tool to check your Python code against some of the style conventions in &lt;a href="http://www.python.org/dev/peps/pep-0008/"&gt;PEP 8&lt;/a&gt;. This package used to be called pep8 but was renamed to pycodestyle to reduce confusion.&lt;/blockquote&gt;

&lt;p&gt;pycodestyle is included with &lt;a href="https://pypi.org/project/flake8/"&gt;flake8&lt;/a&gt;, &lt;a href="https://pypi.org/project/autopep8/"&gt;autopep8&lt;/a&gt;, &lt;a href="https://pypi.org/project/pylama/"&gt;Pylama&lt;/a&gt;, and &lt;a href="https://pypi.org/project/prospector"&gt;Prospector&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;Black (Code Formatter)&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/black/"&gt;Black&lt;/a&gt; is not a linter but rather a code formatter that changes spacing and other cosmetic aspects of your source code without changing the behavior of your program. You should install it and use it no matter what Python linter you use. I prefer to run it with the command-line arguments &lt;code&gt;black -l 120 -S yourScript.py&lt;/code&gt; so that it formats &lt;i&gt;yourScript.py&lt;/i&gt; with a line length of 120 (the default is 88) and doesn't change the quotes for your strings (I like using single quote string but Black formats them into double quote strings.) I cover it in &lt;a href="https://inventwithpython.com/beyond/chapter3.html"&gt;Chapter 3 of my free book, Beyond the Basic Stuff with Python&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;autopep8 (Style Linter and Formatter)&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pypi.org/projects/autopep8"&gt;autopep8&lt;/a&gt; uses &lt;a href="https://pypi.org/projects/pycodestyle"&gt;pycodestyle&lt;/a&gt; to detect style issues and then automatically correct them. I've personally preferred the way &lt;a href="https://pypi.org/project/black/"&gt;Black&lt;/a&gt; formats code over pycodestyle. &lt;/p&gt;





&lt;h2&gt;pyupgrade (Style Linter)&lt;/h2&gt;

&lt;p&gt;I'm wary of &lt;a href="https://pypi.org/project/pyupgrade/"&gt;pyupgrade&lt;/a&gt;. On the one hand, it seems to update Python syntax to more modern syntax that later versions of Python use. On the other hand, the documentation is spotty and there's no way to see what changes it would make before it makes them. The project does seem to be maintained with regular releases, though I'd want to carefully evaluate it before recommending it to others.&lt;/p&gt;





&lt;h2&gt;mccabe (Complexity Analysis)&lt;/h2&gt;

&lt;p&gt;mccabe is not a traditional Python linter, but rather measures cyclomatic complexity. &lt;a href="https://en.wikipedia.org/wiki/Cyclomatic_complexity"&gt;Cyclomatic complexity&lt;/a&gt; (also called McCabe complexity after its creator in 1976) measures the number of linearly independent paths through some source code. Functions and methods with a score greater than 10 are generally considered "too complicated" and should be simplified. In this case, simplification means using shorter functions and removing duplicate code. Take the cyclomatic complexity score with a grain of salt; if anything, just use the score as an indicator of what parts of your source code might need a second look at. Don't use the score as a hard rule for what needs to be simplified; you'll end up with a program with overloaded with tiny functions that are far more unreadable than what it was before.&lt;/p&gt;

&lt;p&gt;mccabe is installed with &lt;a href="https://pypi.org/project/flake8/"&gt;flake8&lt;/a&gt;, &lt;a href="https://pypi.org/project/pylama/"&gt;Pylama&lt;/a&gt;, and &lt;a href="https://pypi.org/project/prospector"&gt;Prospector&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After installing, run the program with &lt;code&gt;python -m --min 10 mccabe example.py&lt;/code&gt;. Some example output of functions and methods with a minimum score of 10 would look like the following:&lt;/p&gt;

&lt;pre&gt;TryExcept 182 10
646:0: '_normalizeXYArgs' 13
826:0: '_normalizeButton' 10
1418:0: '_mouseMoveDrag' 11
1899:0: '_tokenizeCommandStr' 19
2025:0: '_runCommandList' 21&lt;/pre&gt;

&lt;p&gt;Ooof, I should take a look at that &lt;code&gt;_runCommandList&lt;/code&gt; function I wrote and see if I can simplify it.&lt;/p&gt;




&lt;h2&gt;Radon (Complexity Analysis)&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/radon/"&gt;Radon&lt;/a&gt; includes the &lt;a href="https://pypi.org/project/mccabe/"&gt;mccabe&lt;/a&gt; package to perform cyclomatic complexity measurement, but also has options for measuring LOC ("lines of code"), the &lt;a href="https://en.wikipedia.org/wiki/Halstead_complexity_measures"&gt;Halstead metrics&lt;/a&gt; (based on number of operators and operands to calculate the "difficulty" of understanding the code), and a Maintainability Index (using the same metrics formula used by the Microsoft Visual Studio IDE). How these metrics are calculated is given in &lt;a href="https://radon.readthedocs.io/en/latest/intro.html"&gt;the documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, I ran these Radon measurements on some of my code, and the output looks like this:&lt;/p&gt;

&lt;pre&gt;C:\Users\Al\Desktop\lintercmp&gt;radon cc C:\github\pyautogui\pyautogui\__init__.py
\github\pyautogui\pyautogui\__init__.py
    F 2025:0 _runCommandList - D
    F 1418:0 _mouseMoveDrag - C
    F 1899:0 _tokenizeCommandStr - C
    F 646:0 _normalizeXYArgs - C
    F 826:0 _normalizeButton - B
    F 1733:0 displayMousePosition - B
--SNIP--
    F 2157:0 getInfo - A
    C 28:0 PyAutoGUIException - A
    C 38:0 FailSafeException - A
    C 48:0 ImageNotFoundException - A

C:\Users\Al\Desktop\lintercmp&gt;radon raw C:\github\pyautogui\pyautogui\__init__.py
\github\pyautogui\pyautogui\__init__.py
    LOC: 2163
    LLOC: 821
    SLOC: 1048
    Comments: 154
    Single comments: 117
    Multi: 593
    Blank: 405
    - Comment Stats
        (C % L): 7%
        (C % S): 15%
        (C + M % L): 35%

C:\Users\Al\Desktop\lintercmp&gt;radon hal C:\github\pyautogui\pyautogui\__init__.py
\github\pyautogui\pyautogui\__init__.py:
    h1: 19
    h2: 329
    N1: 268
    N2: 524
    vocabulary: 348
    length: 792
    calculated_length: 2831.7901243143224
    volume: 6686.811248712193
    difficulty: 15.130699088145896
    effort: 101176.1288634933
    time: 5620.89604797185
    bugs: 2.2289370829040647

C:\Users\Al\Desktop\lintercmp&gt;radon mi C:\github\pyautogui\pyautogui\__init__.py
\github\pyautogui\pyautogui\__init__.py - B&lt;/pre&gt;

&lt;p&gt;You'll need to peruse the documentation to figure out what exactly means, as it isn't very readable (funny enough). I wouldn't say Radon is as necessary as a type checker or error linter, but it can provide useful information on the general size of your code. Be sure not to confuse metrics with actual quality.&lt;/p&gt;




&lt;h2&gt;flake8 (Error &amp; Style Linter, Complexity Analysis)&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/flake8/"&gt;Flake8&lt;/a&gt; is a bundle of &lt;a href="https://pypi.org/project/pyflakes/"&gt;Pyflakes&lt;/a&gt;, &lt;a href="https://pypi.org/project/pycodestyle/"&gt;pycodestyle&lt;/a&gt;, and &lt;a href="https://pypi.org/project/mccabe/"&gt;mccabe&lt;/a&gt; and merges the output of these programs together. It's like &lt;a href="https://pypi.org/project/pylint/"&gt;Pylint&lt;/a&gt; but with the &lt;a href="https://pypi.org/project/mccabe/"&gt;mccabe&lt;/a&gt; package included as well. Use this if you want to run these several tools at the same time.&lt;/p&gt;




&lt;h2&gt;Pylama ("Kitchen Sink")&lt;/h2&gt;

&lt;p&gt;Pylama is the kitchen sink, containing several linters and other tools: &lt;a href="https://pypi.org/project/pycodestyle/"&gt;pycodestyle&lt;/a&gt; (style linter), &lt;a href="https://pypi.org/project/pydocstyle/"&gt;pydocstyle&lt;/a&gt; (docstring linter), &lt;a href="https://pypi.org/project/pyflakes/"&gt;Pyflakes&lt;/a&gt; (error linter), &lt;a href="https://pypi.org/project/mccabe/"&gt;mccabe&lt;/a&gt; (complexity analysis), &lt;a href="https://pypi.org/project/pylint/"&gt;Pylint&lt;/a&gt; (error and style linter), &lt;a href="https://pypi.org/project/radon/"&gt;Radon&lt;/a&gt; (complexity analysis), &lt;a href="https://pypi.org/project/eradicate/"&gt;eradicate&lt;/a&gt; (dead code linter), &lt;a href="https://pypi.org/project/mypy/"&gt;Mypy&lt;/a&gt; (type checker), &lt;a href="https://pypi.org/project/vulture/"&gt;Vulture&lt;/a&gt; (dead code linter).&lt;/p&gt;

&lt;p&gt;I'm not sure how practical it is to have all of these tools run on your source code, and it definitely needs fine tuning to limit the false positives. But if you looked at this blog post and wanted to download all of these tools in one package, Pylama does this. Pylama and Prospector are similar "kitchen sink" packages, but as of November 2022 seem to require different versions of their bundled packages and can't both be installed at the same time.&lt;/p&gt;




&lt;h2&gt;Prospector ("Kitchen Sink")&lt;/h2&gt;

&lt;p&gt;Prospector is also a bundle of several tools, &lt;a href="https://prospector.landscape.io/en/latest/supported_tools.html"&gt;outlined in their documentation&lt;/a&gt;. Installing Prospector installs &lt;a href="https://pypi.org/project/pylint/"&gt;Pylint&lt;/a&gt; (error and style linter), &lt;a href="https://pypi.org/project/pycodestyle/"&gt;pycodestyle&lt;/a&gt; (style linter), &lt;a href="https://pypi.org/project/pyflakes/"&gt;Pyflakes&lt;/a&gt; (error linter), &lt;a href="https://pypi.org/project/mccabe/"&gt;mccabe&lt;/a&gt; (complexity analysis), &lt;a href="https://pypi.org/project/dodgy/#history"&gt;dodgy&lt;/a&gt; (security linter), and &lt;a href="https://pypi.org/project/pydocstyle/"&gt;pydocstyle&lt;/a&gt; (docstring linter). There are also other packages you can optionally install as well. Like Pylama, Prospector is useful to install if you want several different tools to run with one command. Pylama and Prospector are similar "kitchen sink" packages, but as of November 2022 seem to require different versions of their bundled packages and can't both be installed at the same time.&lt;/p&gt;




&lt;h2&gt;Bandit (Security Linter)&lt;/h2&gt;

&lt;p&gt;Bandit can detect some security issues in your Python code, &lt;a href="https://bandit.readthedocs.io/en/latest/plugins/index.html#complete-test-plugin-listing"&gt;as listed on their website&lt;/a&gt;. In my limited testing, I found that it's mostly false positives but I can see this as a good sanity test. You'll want to create your own .bandit configuration file that skips the B601 test that alerts you to the use of &lt;code&gt;assert&lt;/code&gt; statements (by far the most common "security issue" that Bandit pointed out.) I haven't found any other such security linters for Python.&lt;/p&gt;




&lt;h2&gt;Dodgy (Security Linter)&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/dodgy/"&gt;Dodgy&lt;/a&gt; is "designed to detect things such as accidental SCM diff checkins, or passwords or secret keys hard coded into files." This might be nice to have, but the project hasn't been updated since December 2019 and I couldn't get it to trigger anything. I stored valid credit card numbers in variables, had a variable named "password", but nothing seemed to raise any warnings from Dodgy. &lt;a href="https://github.com/landscapeio/dodgy"&gt;The README also claims&lt;/a&gt; "This is a very early version with minimal functionality right now, but will be improved over time." I don't see Dodgy as a necessary tool.&lt;/p&gt;

&lt;p&gt;When my source code cleared with no issues, Dodgy still outputted the following:&lt;/p&gt;

&lt;pre&gt;{
  "warnings": []
}&lt;/pre&gt;

&lt;p&gt;Most tools output nothing when there are no issues to keep from polluting the output stream with unnecessary text.&lt;/p&gt;




&lt;h2&gt;Pyroma (Packaging Linter)&lt;/h2&gt;

&lt;p&gt;If you've seen projects on PyPI that say "The author of this package has not provided a project description" then you've seen a project that would benefit from &lt;a href="https://pypi.org/project/pyroma/"&gt;Pyroma&lt;/a&gt;. From the &lt;a href="https://pypi.org/project/pyroma/"&gt;Pyroma project description&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;Pyroma rhymes with aroma, and is a product aimed at giving a rating of how well a Python project complies with the best practices of the Python packaging ecosystem, primarily PyPI, pip, Distribute etc, as well as a list of issues that could be improved.&lt;/blockquote&gt;

&lt;p&gt;The project description lists what Pyroma checks for. While they are all fairly basic, they're easy to miss when putting together a package for uploading to PyPI. I haven't found any other "packaging linters," so Pyroma is a must-have for me when I intend distribute your code.&lt;/p&gt;




&lt;h2&gt;Vulture (Dead Code Linter)&lt;/h2&gt;

&lt;p&gt;Vulture detects unused variables, functions, and methods. It generated a high number of false positives for me, so I find its usefulness limited.&lt;/p&gt;




&lt;h2&gt;eradicate (Dead Code Linter/Remove)&lt;/h2&gt;

&lt;p&gt;eradicate can find (and also remove) commented out code. I had trouble getting it to run on Windows: the &lt;code&gt;-m&lt;/code&gt; doesn't work, forcing you to enter the full name of the eradicate Python script (which confusingly doesn't end in .py), and you can't run eradicate on a folder of files or pass a wildcard to have it run on multiple files. I also don't necessarily agree that commented-out code has no place if you use source control. Personally, I'd pass on eradicate.&lt;/p&gt;


&lt;h2&gt;autoflake (Dead Code Linter)&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pypi.org/pypi/autoflake"&gt;autoflake&lt;/a&gt; is extremely limited and has a poor choice of name: It removes unused standard library imports and unnecessary &lt;code&gt;pass&lt;/code&gt; statements. That's it. The name comes from the fact that it uses &lt;a href="https://pypi.org/pypi/pyflakes"&gt;Pyflakes&lt;/a&gt; to do this.&lt;/p&gt;

&lt;p&gt;I'd pass on this. Pyflakes, flake8, or the other linters can already point out unused imports, so a dedicated package to do this seems redundant to me.&lt;/p&gt;





&lt;h2&gt;docformatter (Docstring Formatter)&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pypi.org/pypi/docformatter"&gt;From the project description&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;docformatter automatically formats docstrings to follow a subset of the PEP 257 conventions. Below are the relevant items quoted from PEP 257.&lt;/blockquote&gt;

&lt;ul&gt;
    &lt;li&gt;For consistency, always use triple double quotes around docstrings.&lt;/li&gt;
    &lt;li&gt;Triple quotes are used even though the string fits on one line.&lt;/li&gt;
    &lt;li&gt;Multi-line docstrings consist of a summary line just like a one-line docstring, followed by a blank line, followed by a more elaborate description.&lt;/li&gt;
    &lt;li&gt;Unless the entire docstring fits on a line, place the closing quotes on a line by themselves.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;docformatter also handles some of the PEP 8 conventions.&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;Don’t write string literals that rely on significant trailing whitespace. Such trailing whitespace is visually indistinguishable and some editors (or more recently, reindent.py) will trim them.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;pydocstringformatter (Docstring Formatter)&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pypi.org/pypi/pydocstringformatter"&gt;From the project description&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;A tool to automatically format Python docstrings to follow recommendations from PEP 8 and PEP 257. This project is heavily inspired by &lt;a href="https://pypi.org/pypi/docformatter"&gt;docformatter&lt;/a&gt;.&lt;/blockquote&gt;

&lt;p&gt;It seems to be much less popular than docformatter, though both are currently maintained. Continued from the description:&lt;/p&gt;

&lt;blockquote&gt;As such, the biggest difference between the two is that pydocstringformatter fixes some of the open issues we found in docformatter. In general, the output of both formatters (and any other docstring formatter) should be relatively similar.&lt;/blockquote&gt;




&lt;h2&gt;pydocstyle (Docstring Linter)&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/pydocstyle/"&gt;pydocstyle&lt;/a&gt; seems to be another docstring linter.&lt;/p&gt;



&lt;h2&gt;isort (Style Linter for Import Statements)&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/isort/"&gt;isort&lt;/a&gt; is bundled with &lt;a href="https://pypi.org/project/prospector"&gt;Prospector&lt;/a&gt;. From the project description:&lt;/p&gt;

&lt;blockquote&gt;"isort is a Python utility / library to sort imports alphabetically, and automatically separated into sections and by type."&lt;/blockquote&gt;

&lt;p&gt;This is interesting, but I could also see it occassionally causing problems when the order of import statements does matter for some subtle reason. I'd use it as a linter, but not as something that automatically formats the order of &lt;code&gt;import&lt;/code&gt; statements.&lt;/p&gt;



&lt;!-- add pysa, which comes with pyre-check 

flakeheaven/fakehell
autopep8
pep8ify
--&gt;

</content><category term="misc"></category></entry><entry><title>I Want To Help Everyone And I Will Fail</title><link href="https://inventwithpython.com/blog/i-want-to-help-everyone-and-i-will-fail.html" rel="alternate"></link><published>2022-11-15T12:00:00-05:00</published><updated>2022-11-15T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-11-15:/blog/i-want-to-help-everyone-and-i-will-fail.html</id><summary type="html">&lt;p&gt;I like to program computers. An army of a million file clerks wouldn't be as productive as my laptop running the right code. And my house only has one bathroom so the line would be quite long too...&lt;/p&gt;</summary><content type="html">

&lt;p&gt;I like to program computers. An army of a million file clerks wouldn't be as productive as my laptop running the right code. And my house only has one bathroom so the line would be quite long too.&lt;/p&gt;

&lt;p&gt;I like to teach other people to program. As productive as I can be with a computer, I can write a book that enables thousands of others to become equally productive. It's a lot of work to write one of my books, but once done it costs me about $15 a month in web hosting fees to share it with thousands of people, anywhere in the world.&lt;/p&gt;

&lt;p&gt;And my readers can contact me. You can contact me. My email address is &lt;a href="mailto:al@inventwithpython.com"&gt;al@inventwithpython.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And readers do contact me. A lot. Currently I have 1,254 unread messages, some going back years. I get emails that only say, "My program doesn't work." I've written &lt;a href="https://inventwithpython.com/blog/2018/02/02/how-to-ask-for-programming-help/"&gt;a blog post on how to effectively ask for programming help&lt;/a&gt;, and sometimes I'm so busy all I can do is just reply with a link to it.&lt;/p&gt;

&lt;p&gt;Sometimes readers ask more than just a question, but are basically asking me to write software for them. Sometimes readers ask me how they can "learn programming" and if I could be their tutor. The answer can literally fill several books, and I have written these books. I reply telling them that I'm unfortunately too busy to provide individual help. (At least, unless they're willing to pay me several thousand U.S. dollars. I assume they aren't able to do that, so I don't mention it.)&lt;/p&gt;

&lt;p&gt;The thing is, though, I want to write that software with them. I want to provide them with individual tutoring over Zoom. I want to provide a five-page response to their question and give them advice. I want to help everyone and I will fail to do so.&lt;/p&gt;

&lt;p&gt;I'm getting older. I started writing programming books in 2009. There's no way I could reach as many people except by writing books and putting them for free online. I'm one person, and I make a much better author than classroom instructor or remote tutor. (I always support teachers and teachers unions because no book or online course could ever substitute them.)&lt;/p&gt;

&lt;p&gt;I can't even tell the readers who email me this; likely you've found this because I sent you a link to this instead of typing it up again. Or maybe you've emailed someone else entirely, and they've pointed you to this blog post. I've found that writing a public blog post instead of an individual reply email is much more effective. Still, my blog post drafts folder has over a hundred blog posts I've started but haven't finished.&lt;/p&gt;

&lt;p&gt;I hope you're not offended by my lack of individual response. I want to encourage you that, no matter what current frustration or confusion you're experiencing at this point in your programming journey, others have experienced it too and have successfully moved on to the next challenge. I may not have enough time to help you in particular, but I have an unlimited amount of sincerity in my belief in you.&lt;/p&gt;

&lt;p&gt;Thank you for your email. I'm sorry for the delay in my response. I'm afraid that my schedule is too busy to provide individual guidance, but I wish you good luck and hope you keep up your efforts.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Installing Brython to Get Python in Your Browser</title><link href="https://inventwithpython.com/blog/installing-brython-to-get-python-in-your-browser.html" rel="alternate"></link><published>2022-10-31T12:00:00-04:00</published><updated>2022-10-31T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-10-31:/blog/installing-brython-to-get-python-in-your-browser.html</id><summary type="html">&lt;p&gt;JavaScript is not the only programming language you can run in the browser. Brython is a Python interpreter implemented in JavaScript so you can run Python code in your browser. This lets you have a Python interactive shell without having to install Python. You can also write Python code to interact with the DOM and create browser apps just like you could with JavaScript. The primary downside is that a browser must download about 6 megabytes of JavaScript files before it can run, which can be a significant delay. This blog post guides you through setting up Brython.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;JavaScript is not the only programming language you can run in the browser. Brython is a Python interpreter implemented in JavaScript so you can run Python code in your browser. This lets you have a Python interactive shell without having to install Python. You can also write Python code to interact with the DOM and create browser apps just like you could with JavaScript. The primary downside is that a browser must download about 6 megabytes of JavaScript files before it can run, which can be a significant delay. This blog post guides you through setting up Brython.&lt;/p&gt;

&lt;p&gt;To "install" Brython, download the zip file from the Brython release page on GitHub: &lt;a href="https://github.com/brython-dev/brython/releases/"&gt;https://github.com/brython-dev/brython/releases/&lt;/a&gt;. (As of October 2022, this is &lt;a href="https://github.com/brython-dev/brython/releases/download/3.11/Brython-3.11.0.zip"&gt;Brython-3.11.0.zip&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Inside this zip file is &lt;i&gt;brython.js&lt;/i&gt; and &lt;i&gt;brython_stdlib.js&lt;/i&gt;. To get an interactive shell in a webpage, create a .html file with a text editor and copy/paste the following code:&lt;/p&gt;

&lt;pre&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;script type="text/javascript" src="brython.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type="text/javascript" src="brython_stdlib.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;style&amp;gt;
    .codearea {
        background-color:#000;
        color:#fff;
        font-family:'Oxygen Mono', Consolas, 'Liberation Mono', 'DejaVu Sans Mono', monospace;
        font-size:14px;
        overflow:auto
    }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body onload=brython({"debug":1}) &amp;gt;&amp;lt;!-- remove the 1 to leave debug mode --&amp;gt;
    &amp;lt;noscript&amp;gt;Please enable Javascript to view this page correctly&amp;lt;/noscript&amp;gt;

    &amp;lt;textarea id="code" class="codearea" rows="20" cols="100"&amp;gt;&amp;lt;/textarea&amp;gt;

    &amp;lt;script type="text/python3"&amp;gt;
        from interpreter import Interpreter

        # Start an interactive interpreter in textarea with id "code"
        Interpreter("code")
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;

&lt;p&gt;Open this .html in your browser to view the interactive shell:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/brython-repl.webp" /&gt;&lt;/p&gt;

&lt;p&gt;The Brython zip file you downloaded also has a &lt;i&gt;demo.html&lt;/i&gt; file that showcases many examples of Python code that can interact with the Document Object Model (DOM). Instead of putting JS code inside of a &lt;code&gt;&amp;lt;script type="text/python"&amp;gt;&lt;/code&gt; tag, you put Python code inside of a &lt;code&gt;&amp;lt;script type="text/python"&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;

&lt;p&gt;You can also install Brython as a module to your Python installation using pip. Run &lt;code&gt;pip install brython&lt;/code&gt;. This installs a &lt;i&gt;brython-cli&lt;/i&gt; script. When you run &lt;code&gt;brython-cli install&lt;/code&gt;, the script creates the following files in the current directory:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;README.txt&lt;/li&gt;
  &lt;li&gt;brython.js&lt;/li&gt;
  &lt;li&gt;brython_stdlib.js&lt;/li&gt;
  &lt;li&gt;demo.html&lt;/li&gt;
  &lt;li&gt;index.html&lt;/li&gt;
  &lt;li&gt;unicode.txt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you run &lt;code&gt;python -m http.server&lt;/code&gt; to run a web server on your computer, then open your browser to the address &lt;code&gt;http://localhost:8000&lt;/code&gt; you will see the contents of &lt;i&gt;index.html&lt;/i&gt; rendered in the browser. Right-click the web page and select View Source (or open &lt;i&gt;index.html&lt;/i&gt; in a text editor) to view the Python code being run by Brython:&lt;/p&gt;

&lt;pre&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html&amp;gt;

&amp;lt;head&amp;gt;
&amp;lt;meta charset="utf-8"&amp;gt;
&amp;lt;script type="text/javascript" src="brython.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script type="text/javascript" src="brython_stdlib.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body onload="brython(1)"&amp;gt;
&amp;lt;script type="text/python"&amp;gt;
from browser import document

document &amp;lt;= "Hello"
&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;

&amp;lt;/html&amp;gt;&lt;/pre&gt;

&lt;p&gt;To create client-side web apps that run in the browser, you'll have to learn Brython's API for interacting with the DOM. This is explained in detail in the &lt;a href="https://www.brython.info/static_doc/en/intro.html"&gt;Brython documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also check out this PyCon 2014 talk by Susan Tan, &lt;a href="https://www.youtube.com/watch?v=BcDV9QUium4"&gt;Python in the Browser: Intro to Brython&lt;/a&gt;&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>17 Online Python IDEs and Interactive Shells/REPLs</title><link href="https://inventwithpython.com/blog/list-of-online-python-interpreters-and-interactive-shell.html" rel="alternate"></link><published>2022-10-30T12:00:00-04:00</published><updated>2022-10-30T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-10-30:/blog/list-of-online-python-interpreters-and-interactive-shell.html</id><summary type="html">&lt;p&gt;Installing Python is easy, but maybe you're on a smartphone/tablet, are on a library computer that doesn't let you install software, or can't install Python for some other reason. This article has a list of 10 free Python interpreters and interactive shells (also called REPLs) that you can access from a web browser.&lt;/p&gt;</summary><content type="html">


&lt;p&gt;Installing Python is easy, but maybe you're on a smartphone/tablet, are on a library computer that doesn't let you install software, or can't install Python for some other reason. This article has a list of 17 free Python interpreters and interactive shells (also called REPLs) that you can access from a web browser.&lt;/p&gt;

&lt;p&gt;There are some downsides to using a browser-based Python interpreter. You won't be able to read or write files with the &lt;code&gt;open()&lt;/code&gt; function or make network connections. You might not be allowed to have long-running programs or programs that use a lot of memory. And you won't be able to install third-party modules such as NumPy, Pandas, Requests, PyAutoGUI, or Pygame (though some will have these modules pre-installed.) Some other Python features might be disabled; you'll have to check with them individually. Some of these require you to sign up for a free account. You can use &lt;a href="https://10minutemail.com/"&gt;10 Minute Email&lt;/a&gt; to create disposable email accounts if you don't want to deal with spam.&lt;/p&gt;

&lt;p&gt;However, you'll still be able to practice writing Python code. The game, simulation, and digital art projects in my book &lt;a href="https://inventwithpython.com/bigbookpython/"&gt;The Big Book of Small Python Projects&lt;/a&gt; will still work in these online interpreters. You'll also be able to enter the text-based game projects from &lt;a href="https://inventwithpython.com/invent4thed/"&gt;Invent Your Own Computer Games with Python&lt;/a&gt; and the encryption and code-breaking projects from &lt;a href="https://inventwithpython.com/cracking/"&gt;Cracking Codes with Python&lt;/a&gt;.These books are free to read online through a Creative Commons license.&lt;/p&gt;

&lt;p&gt;Here's a curated list of browser-based Python interpreters. This isn't a hastily put together clickbait blog post; I've examined each of these and listed them in order of my personal preference.&lt;/p&gt;



&lt;h2&gt;Python Tutor&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://pythontutor.com/"&gt;https://pythontutor.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My favorite browser-based Python file editor is &lt;a href="https://pythontutor.com/"&gt;Python Tutor&lt;/a&gt;. Python Tutor offers a built-in debugger that lets you execute one line at a time and inspect how the program executes. Not only that, but it also records the state of the program at each step, so you can also step backwards. This is a feature most debuggers don't have.&lt;/p&gt;

&lt;p&gt;The Python Tutor site also has Java 8, JavaScript ES6, and C/C++ (with the gcc 9.3 compiler). It also has Python 2.7, TypeScript 1.4, and Ruby 2.2, though these are unsupported. The main downside is that there is a limit to the runtime for your programs (since the programs are run on their servers before returning the output to your browser), however you won't hit this for most learning exercises when you're just starting.&lt;/p&gt;


&lt;h2&gt;Python Anywhere&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.pythonanywhere.com/"&gt;https://www.pythonanywhere.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PythonAnywhere is one of the most popular browser-based interpreters. You must sign up first, but free accounts are available. You'll be able t to store your Python scripts, or just use their &lt;a href="https://www.pythonanywhere.com/try-ipython/"&gt;IPython&lt;/a&gt; interactive shell without having to log in. Their service allows you to read and write files to a virtual hard drive, and they have many popular Python third-party modules already installed. They have several different Python versions available, and PythonAnywhere is also used for &lt;a href="https://www.python.org/shell/"&gt;the interactive shell on the Python.org website&lt;/a&gt;.&lt;/p&gt;



&lt;h2&gt;Replit&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://replit.com/"&gt;https://replit.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Replit requires you to sign up for a free account to use it. The user interface is a bit complicated, as they have several features including a help system to navigate the site. The site also has online compilers for C, C++, Java, JavaScript, and several other languages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.replit.com/mobile-app"&gt;Replit also has a mobile app for programming on smartphones and tablets.&lt;/a&gt;&lt;/p&gt;



&lt;h2&gt;Futurecoder.io&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://futurecoder.io/"&gt;https://futurecoder.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Futurecoder.io looks similar to Code Academy in that it has a curriculum of lessons along with a REPL. However, you can also use the REPL on its own and the site doesn't have ads or require signing up.&lt;/p&gt;


&lt;h2&gt;Brython&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://brython.info/tests/console.html?lang=en"&gt;https://brython.info/tests/console.html?lang=en&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Brython is Python that you can run in your browser the way web browsers run JavaScript. The normal client-side things you can do in a web page with JavaScript can be done with Brython. This means you can run programs without limits on memory or runtime since they'll be running on your own computer. The downside is that your browser must first download the 6 megabyte Brython code whenever it loads the web page.&lt;/p&gt;

&lt;p&gt;You can avoid this by downloading Brython and running it off of your computer. This way you won't need an internet connection after downloading Brython. First, download Brython-3.11.0.zip from &lt;a href="https://github.com/brython-dev/brython/releases"&gt;the Brython release page on their GitHub account&lt;/a&gt; (or whatever the latest version you see on that page). Unpack the Brython-3.11.0 folder inside the zip. Create a new text file named console.html (or any name with a .html extension you choose) and put the following text into it:&lt;/p&gt;

&lt;p&gt;&lt;textarea style="width: 100%;"&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;script type="text/javascript" src="brython.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type="text/javascript" src="brython_stdlib.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;style&amp;gt;
    .codearea {
        background-color:#000;
        color:#fff;
        font-family:'Oxygen Mono', Consolas, 'Liberation Mono', 'DejaVu Sans Mono', monospace;
        font-size:14px;
        overflow:auto
    }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body onload=brython({"debug":1}) &amp;gt;&amp;lt;!-- remove the 1 to leave debug mode --&amp;gt;
    &amp;lt;noscript&amp;gt;Please enable Javascript to view this page correctly&amp;lt;/noscript&amp;gt;

    &amp;lt;textarea id="code" class="codearea" rows="20" cols="100"&amp;gt;&amp;lt;/textarea&amp;gt;

    &amp;lt;script type="text/python3"&amp;gt;
        from interpreter import Interpreter

        # Start an interactive interpreter in textarea with id "code"
        Interpreter("code")
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;When you open this .html file on your computer, you'll have a working Python interactive shell.&lt;/p&gt;



&lt;h2&gt;Google Colab&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://colab.research.google.com/"&gt;https://colab.research.google.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Your Google or GMail account can give you access to a Jupyter Notebooks-style REPL with generous memory and runtime limits.&lt;/p&gt;



&lt;h2&gt;Trinket&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://trinket.io/"&gt;https://trinket.io/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Trinket is an education-focused site. You need to sign up for a free account to use it, but their interactive shell also supports Turtle, Matplotlib, and other modules that many online REPLs don't.&lt;/p&gt;



&lt;h2&gt;Python Fiddle&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://pythonfiddle.com/"&gt;http://pythonfiddle.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Python Fiddle is a bare bones editor that lets you create and run Python scripts. Their user interface is a bit unwieldy, but it is rather straightforward.&lt;/p&gt;



&lt;h2&gt;Programiz&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.programiz.com/python-programming/online-compiler/"&gt;https://www.programiz.com/python-programming/online-compiler/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Programiz has a simple file editor. It can't write files and the programs have a limited runtime. The website also has compilers for C, C++, Java, C#, JavaScript, and a sandbox for SQL databases and HTML/CSS.&lt;/p&gt;



&lt;h2&gt;Ideone&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ideone.com/"&gt;https://ideone.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ideaone has a rich editor and supports several programming languages besides Python (even older languages like Pascal, Fortran, Prolog, and Cobol, and esoteric programming languages like Brainfuck and Intercal). There's only the editor that lets you write and run programs (with a 5 second runtime limit), but no interactive shell. I think Ideaone is better if you want to practice in less popular languages, rather than use it on a common basis for Python.&lt;/p&gt;



&lt;h2&gt;SymPy Live Shell&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://live.sympy.org/"&gt;https://live.sympy.org/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A Jupyter Notebooks-style REPL with a small user interface that is fine for casual examples. It uses &lt;a href="https://jupyterlite.readthedocs.io/en/latest/"&gt;JupyterLite&lt;/a&gt; to run JupyterLab entirely in your browser rather on their servers.&lt;/p&gt;



&lt;h2&gt;OnlineGDB&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.onlinegdb.com/online_python_interpreter"&gt;https://www.onlinegdb.com/online_python_interpreter&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A decent editor that has a nice user interface without being too overwhelming. The runtime and memory limits seem quite generous too. I'd recommend this one.&lt;/p&gt;



&lt;h2&gt;W3Schools Python Shell&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.w3schools.com/python/python_compiler.asp"&gt;https://www.w3schools.com/python/python_compiler.asp&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;W3Schools has a straightforward Python editor with basic syntax highlighting. It has a runtime limit of about 10 seconds, though it doesn't report an error if your program goes over it. It's fine for simple examples.&lt;/p&gt;



&lt;h2&gt;Python Principles Online Python Interpreter&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://pythonprinciples.com/online-python-interpreter/"&gt;https://pythonprinciples.com/online-python-interpreter/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A simple file editor, but I had trouble getting it to run anything more than a toy example.&lt;/p&gt;



&lt;h2&gt;Online Python Beta&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.online-python.com/"&gt;https://www.online-python.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A simple and straightforward Python editor that runs the code on their back-end. Good for toy examples.&lt;/p&gt;



&lt;h2&gt;TutorialsPoint&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.tutorialspoint.com/execute_python_online.php"&gt;https://www.tutorialspoint.com/execute_python_online.php&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A simple and straightforward editor with generous runtime limits.&lt;/p&gt;



&lt;h2&gt;RexTester&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://rextester.com/l/python3_online_compiler"&gt;https://rextester.com/l/python3_online_compiler&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This looks like a hobbyist project with a minimal editor and 5 second runtime limit. There are compilers for other languages, but the website is hard to navigate.&lt;/p&gt;



&lt;h2&gt;Portable Python&lt;/h2&gt;
&lt;p&gt;https://portablepython.com/ (NOT RECOMMENDED&lt;/p&gt;
&lt;p&gt;This isn't a browser-based REPL, but rather a .exe file to run Python on Windows without having to install anything. However, the page hasn't been updated since 2013 (and Python 3.2), and the download pages don't use HTTPS. I recommend &lt;b&gt;against&lt;/b&gt; using this software due to the possibility of malware.&lt;/p&gt;


&lt;h2&gt;Visual Studio Code Online&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://vscode.dev/"&gt;https://vscode.dev/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Visual Studio Code is an &lt;a href="https://www.electronjs.org/"&gt;Electron&lt;/a&gt; app, meaning that it can run as a regular desktop app or a browser-based app. The browser version is a bit limited, and navigating this online IDE has a steeper learning curve than the other online interpreters. Still, if you're willing to dive into it or already have experience with Visual Studio Code, you can use this browser-based version.&lt;/p&gt;


&lt;h2&gt;SoloLearn&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://code.sololearn.com/#py"&gt;https://code.sololearn.com/#py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;SolorLearn has an editor (but not a REPL) for over a dozen languages including Python. Use the "Online Compiler" menu item at the top of the page. However, any inputs have to be specified in advanced, so you can't really use it to make interactive programs such as games. I don't really recommend it.&lt;/p&gt;


&lt;h2&gt;PixelPad&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://pixelpad.io/"&gt;https://pixelpad.io/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;PixelPad doesn't have a REPL but does have an editor along with a few example games. However, when I noticed that you can't call &lt;code&gt;time.sleep()&lt;/code&gt; so I get the feeling that you'll very quickly bump into walls when you try to do more than it's set of tutorials tells you. I don't really recommend it.&lt;/p&gt;

&lt;h2&gt;Computer Science Circles&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://cscircles.cemc.uwaterloo.ca/console/"&gt;https://cscircles.cemc.uwaterloo.ca/console/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A simple file editor with no REPL. However, the UI is awkward to use and you have to specify all inputs in advanced. I don't recommend it.&lt;/p&gt;


&lt;h2&gt;Reeborg's World&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://reeborg.ca/reeborg.html"&gt;http://reeborg.ca/reeborg.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A turtle-like, grid-based world where you move robots around in your browser using their Python library. This might be good for a beginner programmer to learn coding concepts, but it's not set up to run Python code in general.&lt;/p&gt;

&lt;h2&gt;61A Code&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://code.cs61a.org/"&gt;https://code.cs61a.org/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I couldn't get this to actually run a simple Hello, World program. I don't recommend it.&lt;/p&gt;


&lt;p&gt;&lt;i&gt;Edited Feb 5, 2023 to add Futurecoder.io, SoloLearn, PixelPad, Computer Science Circles, Reeborg's World, and 61A Code.&lt;/i&gt;&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>How Computers Store Data with Binary Numbers</title><link href="https://inventwithpython.com/blog/how-computers-store-data-with-binary-numbers.html" rel="alternate"></link><published>2022-10-29T12:00:00-04:00</published><updated>2022-10-29T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-10-29:/blog/how-computers-store-data-with-binary-numbers.html</id><summary type="html">&lt;p&gt;Programming and hacking in movies often involves streams of ones and zeros flowing across the screen. This looks mysterious and impressive, but what do these ones and zeros actually mean? You're probably aware that binary numbers (numbers written using only the two digits, zero and one) have something to do with computers but don't know why.&lt;/p&gt;</summary><content type="html">


&lt;p&gt;Programming and hacking in movies often involves streams of ones and zeros flowing across the screen. This looks mysterious and impressive, but what do these ones and zeros actually mean? You're probably aware that binary numbers (numbers written using only the two digits, zero and one) have something to do with computers but don't know why.&lt;/p&gt;

&lt;p&gt;The answer is economics: binary is the simplest number system and it can be implemented with relatively inexpensive components for computer hardware. Binary, also called the base-2 number system, can represent all of the same numbers that our more familiar base-10 decimal number system can. Decimal has ten digits, 0 through 9. The following table shows the first 24 numbers in decimal and binary:&lt;/p&gt;

&lt;table&gt;&lt;tr&gt;&lt;th&gt;Decimal&lt;/th&gt;&lt;th&gt;Binary&lt;/th&gt;&lt;th&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/th&gt;&lt;th&gt;Decimal&lt;/th&gt;&lt;th&gt;Binary&lt;/th&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;1100&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;1101&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;14&lt;/td&gt;&lt;td&gt;1110&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;1111&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;100&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;10000&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;101&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;17&lt;/td&gt;&lt;td&gt;10001&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;110&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;18&lt;/td&gt;&lt;td&gt;10010&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;111&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;19&lt;/td&gt;&lt;td&gt;10011&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;1000&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;td&gt;10100&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;1001&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;21&lt;/td&gt;&lt;td&gt;10101&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;1010&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;22&lt;/td&gt;&lt;td&gt;10110&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;1011&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;10111&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;Think of the number systems as a mechanical odometer: when you reach the last digit, it resets back to zero while incrementing the next digit. In decimal the last digit is 9 and in binary the last digit is 1. The decimal number after 9 is 10 and the decimal number after 999 is 1000. Similarly, the binary number after 1 is 10 and the binary number after 111 is 1000. Although "10" in binary doesn't mean the same quantity as "ten" in decimal, but rather two. And "1000" in binary doesn't mean the same quantity as "one thousand" in decimal, but rather eight. You can view an interactive binary and decimal odometer at &lt;a href="https://inventwithpython.com/odometer"&gt;https://inventwithpython.com/odometer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/odometer-binary.webp" /&gt;&lt;/p&gt;

&lt;p&gt;Representing binary numbers with computer hardware is simpler than decimal because there are only two states to represent. For example, spinning-disc hard drives have microscopic spots that can be magnetized or not magnetized. Blu-Ray discs and DVDs have smooth "lands" and indented "pits" etched on the surface of the disk that will or won't reflect the disc player's laser, respectively. Circuits can have electric current flowing through them or no electric current. Even the spaces on paper punch cards for mid-20th century computers have a hole punched in them or no hole. These various hardware standards all have ways of representing two different states. On the other hand, it'd be expensive to create high-quality electronic components that are sensitive enough to detect the difference between ten different voltage levels with reliable accuracy. It's more economical to use simple components, and two binary states is as simple as you can get.&lt;/p&gt;

&lt;p&gt;These binary digits are called bits for short. A single bit can represent two numbers, and eight bits (or one byte) can represent 2^8 or 256 numbers. This ranges from 0 to 255 in decimal and 0 to 11111111 in binary. This is similar to how a single decimal digit can represent ten numbers (0 to 9), an eight-digit decimal number can represent 10^8 or one hundred million numbers. Files on your computer are measured in how many bytes they take up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A kilobyte is 2^10 or 1,024 bytes.&lt;/li&gt;
&lt;li&gt;A megabyte is 2^20 or 1,048,576 bytes.&lt;/li&gt;
&lt;li&gt;A gigabyte is 2^30 or 1,073,741,824 bytes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The text of Shakespeare's Romeo and Juliet is about 135 kilobytes. A high-resolution photo is about 2 to 5 megabytes. A two-hour movie can be anywhere from 1 to 50 gigabytes depending on picture quality. Hard drive manufacturers and internet service providers will often use kilobyte, megabyte, and gigabyte to mean a flat one thousand, one million, or one billion because it allows them to overexaggerate how much capacity they provide. The reason corporations can blatantly lie about their numbers this way is because that's how the world works.&lt;/p&gt;

&lt;p&gt;Once there is a way to represent binary numbers, you can represent numbers in any number system. You don't need to know the math behind converting from base-2 to base-10 and vice versa to write Python scripts that automate boring stuff. Python has functions to perform this math for you: calling &lt;code&gt;bin(42)&lt;/code&gt; returns the string &lt;code&gt;'0b101010'&lt;/code&gt;. The &lt;code&gt;'0b'&lt;/code&gt; prefix is a convention for marking the number as in binary, instead of meaning "one hundred one thousand ten" in decimal. Meanwhile, calling &lt;code&gt;int('101010', 2)&lt;/code&gt; converts binary-to-decimal and returns the integer &lt;code&gt;42&lt;/code&gt;. The &lt;code&gt;2&lt;/code&gt; argument tells the &lt;code&gt;int()&lt;/code&gt; function that &lt;code&gt;'101010'&lt;/code&gt; is a base-2 binary number.&lt;/p&gt;

&lt;p&gt;The ones and zeros of binary can not only represent any integer but also any form of data. Text can be stored on computers as binary numbers by assigning each letter, punctuation mark, or symbol a unique number. One early such scheme for this was ASCII, the American Symbolic Code for Information-Interchange. In ASCII, a capital letter "A" is represent by the number 65 (or 1000001 in binary), a "?" question mark is represented by the number 63, and the numeral 7 is represented by the number 55. The string &lt;code&gt;'Hello'&lt;/code&gt; is stored as the numbers 72, 101, 108, 108, and 111. When combined one after the other, this appears as a stream of binary: 10010001100101110110011011001101111.&lt;/p&gt;

&lt;p&gt;Wow! Just like in those hacker movies!&lt;/p&gt;

&lt;p&gt;However, these don't make sense without knowing how many digits are in each number. Representing &lt;code&gt;'Hello'&lt;/code&gt; in decimal as 72101108108111 is confusing because you wouldn't know if the first letter was represented by 7, 72, or 721. We can solve this by always using three digits for each letter and adding the leading zeros where needed: 72 becomes 072. The fifteen digits of 072101108108111 can be evenly split up into five groups of three digits each to represent the five letters. The same can be done in binary, where one byte (or eight bits) represents each letter. With the leading zeros, &lt;code&gt;'Hello'&lt;/code&gt; becomes 0100100001100101011011000110110001101111, as shown in this figure:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/hello-to-binary.webp" /&gt;&lt;/p&gt;

&lt;p&gt;Large numbers can be tedious to write in binary, so programmers often view binary information in base-16 or hexadecimal. The numerals of hexadecimal (or simple, hex) range from 0 to 9, and then continue with the first six letters A to F like in the following table:&lt;/p&gt;

&lt;table&gt;&lt;tr&gt;&lt;th&gt;Decimal&lt;/th&gt;&lt;th&gt;Hexadecimal&lt;/th&gt;&lt;th&gt;Binary&lt;/th&gt;&lt;th&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/th&gt;&lt;th&gt;Decimal&lt;/th&gt;&lt;th&gt;Hexadecimal&lt;/th&gt;&lt;th&gt;Binary&lt;/th&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;C&lt;/td&gt;&lt;td&gt;1100&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;D&lt;/td&gt;&lt;td&gt;1101&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;14&lt;/td&gt;&lt;td&gt;E&lt;/td&gt;&lt;td&gt;1110&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;F&lt;/td&gt;&lt;td&gt;1111&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;100&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;10000&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;101&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;17&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;10001&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;110&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;18&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;10010&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;111&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;19&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;10011&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;1000&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;td&gt;14&lt;/td&gt;&lt;td&gt;10100&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;1001&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;21&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;10101&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;1010&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;22&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;10110&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;B&lt;/td&gt;&lt;td&gt;1011&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;17&lt;/td&gt;&lt;td&gt;10111&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;The single hex digit A represents the number ten, B represents eleven, and so on up to F, which represents 15. After this, we add another hexadecimal digit: the hexadecimal number 10 represents decimal 16. Python's &lt;code&gt;hex()&lt;/code&gt; and &lt;code&gt;int()&lt;/code&gt; functions convert between decimal and hexadecimal. Enter the following into the interactive shell:&lt;/p&gt;

&lt;pre&gt;
&gt;&gt;&gt; hex(42)  # Convert decimal to hex
'0x2a'
&gt;&gt;&gt; int('0x2a', 16)  # Convert hex to decimal
42
&gt;&gt;&gt; int('0x2A', 16)  # Convert hex to decimal
42
&gt;&gt;&gt; int('2A', 16)  # Convert hex to decimal
42&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;hex()&lt;/code&gt; function returns strings with a &lt;code&gt;'0x'&lt;/code&gt; prefix to mark it as a hexadecimal number. The &lt;code&gt;16&lt;/code&gt; argument tells the &lt;code&gt;int()&lt;/code&gt; function that &lt;code&gt;'2a'&lt;/code&gt; is written in base-16 hexadecimal.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;'Hello'&lt;/code&gt; string can be shown in 40 binary digits, 0100100001100101011011000110110001101111, or more compactly with 10 hexadecimal digits, 48656c6c6f. This form is more convenient for programmers to read, and software called hex editors can display the binary data of a file this way.&lt;/p&gt;

&lt;p&gt;ASCII was developed before the internet made international communication commonplace. It doesn't have numbers reserved for, say, Chinese characters. It's not just an encoding for English, but American English: the number 36 for the "$" dollar sign but no number for the British pound symbol. These issues were solved with Unicode Standard. Specifically, the UTF-8 encoding of the Unicode Standard uses one to four bytes to represent any possible character. UTF-8 is also backwards compatible with ASCII: 65 is a capital A in ASCII and UTF-8.&lt;/p&gt;

&lt;p&gt;Python's &lt;code&gt;ord()&lt;/code&gt; function takes a single-character string and returns the integer of its assigned Unicode code point. Python's &lt;code&gt;chr()&lt;/code&gt; function does the opposite, taking an integer and return a single-character string of that number's assigned Unicode character. In this way, all text can be represented as numbers, and all numbers can be stored on computer hardware in binary form.&lt;/p&gt;

&lt;p&gt;Engineers need to invent a way to encode each form of data as numbers. Photos and image are broken up into a two-dimensional grid of colored squares called pixels. Each pixel can use three bytes to represent how much red, green, and blue its color contains. &lt;a href="https://automatetheboringstuff.com/2e/chapter19/"&gt;Chapter 19 of Automate the Boring Stuff with Python&lt;/a&gt; covers image data in more detail. But for a short example, the numbers 255, 0, and 255 could represent a pixel with the maximum amount of red and blue but no green, resulting in a purple pixel.&lt;/p&gt;

&lt;p&gt;Sound is made up of waves of compressed air that reach our ears, which our brains interpret as audio sensation. We can graph the intensity and frequency of these waves over time. The numbers of this graph can then be converted to binary numbers and stored on a computer, which can later control speakers to reproduce the sound. This is a simplification, but this is how binary numbers are stored in audio files such MP3s.&lt;/p&gt;

&lt;p&gt;The data for several images combines with audio data to store videos. All forms of information can be encoded into numbers, converted into binary numbers, and then stored on computer hardware. There is, of course, a great deal more to it than this, but this is how ones and zeros represent the wide variety of data in our information age.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Is Python Compiled or Interpreted?</title><link href="https://inventwithpython.com/blog/is-python-compiled-or-interpreted.html" rel="alternate"></link><published>2022-10-10T12:00:00-04:00</published><updated>2022-10-10T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-10-10:/blog/is-python-compiled-or-interpreted.html</id><summary type="html">The short answer is: Python is interpreted. But it can also be considered compiled, both compiled and interpreted, and neither compiled nor interpreted. Read on to untangle these definitions.</summary><content type="html">

&lt;p&gt;The short answer is: Python is interpreted.&lt;/p&gt;

&lt;p&gt;There is no separate compile step after writing Python code and before running the .py file.&lt;/p&gt;

&lt;p&gt;The Python interpreter software you download from &lt;a href="https://python.org"&gt;python.org&lt;/a&gt; is called CPython because it's written in C.&lt;/p&gt;

&lt;p&gt;Python can be compiled into a binary executable with a tool like &lt;a href="https://www.py2exe.org/"&gt;Py2Exe&lt;/a&gt; (on Windows), &lt;a href="https://pypi.org/project/py2app/"&gt;py2app&lt;/a&gt; (on macOS), or &lt;a href="https://beeware.org/"&gt;BeeWare&lt;/a&gt; (all OSes including mobile).&lt;/p&gt;

&lt;p&gt;Funny enough, there's also &lt;a href="https://www.pypy.org/"&gt;PyPy&lt;/a&gt;, a Python interpreter written in Python.&lt;/p&gt;

&lt;p&gt;When you run a Python program, the interpreter first compiles it to bytecode and then runs the bytecode. So you could say that Python is compiled.&lt;/p&gt;

&lt;p&gt;Bytecode is a set of basic instructions that &lt;a href="https://en.wikipedia.org/wiki/Virtual_machine"&gt;virtual machine (VM)&lt;/a&gt; software runs just like machine code is a set of basic instructions that a CPU runs.&lt;/p&gt;

&lt;p&gt;The benefit of bytecode is that interpreter VMs on different operating systems can run the same programs. (Though operating systems were originally written to run the same programs on different computer hardware.)&lt;/p&gt;

&lt;p&gt;Python is technically not compiled nor interpreted, because Python is a language and you can write an interpreter or a compiler for any language.&lt;/p&gt;

&lt;p&gt;Still, most languages are considered either "compiled" or "interpreted" because they are most commonly implemented with a compiler or interpreter. In this sense, C++ is compiled and Python is interpreted.&lt;/p&gt;

&lt;p&gt;A programming language that is interpreted is often called a &lt;a href="https://en.wikipedia.org/wiki/Scripting_language"&gt;scripting language&lt;/a&gt;, but Java interprets bytecode and isn't considered a scripting language.&lt;/p&gt;

&lt;p&gt;A high-level language used to be any language with an abstraction layer over machine code or assembly. C used to be considered a high-level language, but is now considered a low-level or bare-metal language because it is less abstract than languages like Java, Python, or JavaScript.&lt;/p&gt;

&lt;p&gt;Transpilers compile source code in one programming language into source code in another programming language. &lt;a href="https://en.wikipedia.org/wiki/TypeScript"&gt;TypeScript&lt;/a&gt; is a popular transpiler that compiles source code written in the TypeScript language (which adds type safety features) into JavaScript source code so that it can be run by the JavaScript interpreter in web browsers.&lt;/p&gt;

&lt;p&gt;There's also &lt;a href="https://en.wikipedia.org/wiki/Just-in-time_compilation"&gt;Just-In-Time (JIT) compilation&lt;/a&gt;, where a bytecode interpreter analyzes which parts of the program run the most frequently and then compiles those parts to machine code for faster execution.&lt;/p&gt;

&lt;p&gt;Java code is compiled by the Java compiler. But it compiles to bytecode, which is interpreted by the Java VM. Still, Java is considered a compiled language rather than an interpreted language or scripting language.&lt;/p&gt;

&lt;p&gt;As software developers add more layers of abstraction, these definitions become looser and more ambiguous. It's probably not worth it to spend time arguing over them.&lt;/p&gt;

&lt;p&gt;If you'd like to learn how compilers work, I'd recommend first learning recursion by reading my free book &lt;a href="https://inventwithpython.com/recursion/"&gt;The Recursive Book of Recursion&lt;/a&gt; and then taking a compilers course such as &lt;a href="https://www.edx.org/course/compilers"&gt;Dr. Aiken's online compilers course&lt;/a&gt;.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>How to Run Pip From The Python Interactive Shell</title><link href="https://inventwithpython.com/blog/run-pip-from-the-interactive-shell.html" rel="alternate"></link><published>2022-08-25T12:00:00-04:00</published><updated>2022-08-25T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-08-25:/blog/run-pip-from-the-interactive-shell.html</id><summary type="html">&lt;p&gt;Installing Python modules with the pip tool is surprisingly hard to describe to beginners learning to code. There are several potential issues: multiple Python installations, virtual environments, PATH environment variable settings. You have to introduce command-line terminals and file system navigation, and the differences between Windows and Mac/Linux. However, there is a line of code you can run from the interactive shell to handle all this for you: &lt;code&gt;import pip; pip.main(['install', 'MODULE_NAME_HERE'])&lt;/code&gt;&lt;/p&gt;</summary><content type="html">

&lt;blockquote&gt;UPDATE March 22, 2023: I now recommend using the one-liner &lt;code&gt;import pip; pip.main(['install', 'MODULE_NAME_HERE'])&lt;/code&gt; module to install modules from the interactive shell rather than the instructions in this blog post. This code is easier to remember and works on Python versions 2.7 and 3.5+. The only downside is that it doesn't work on Python 3.4, in which case, use the instructions below. Here in 2023, however, the number of students working from 3.4 is near nonexistent so this is unlikely to be an issue.&lt;/blockquote&gt;

&lt;!--&lt;blockquote&gt;UPDATE November 20, 2022: I've created the pipfromrepl module to streamline this process.&lt;/blockquote&gt;--&gt;

&lt;p&gt;Installing Python modules with the pip tool is surprisingly hard to describe to beginners learning to code. There are several potential issues: multiple Python installations, virtual environments, PATH environment variable settings. You have to introduce command-line terminals and file system navigation, and the differences between Windows and Mac/Linux. However, there is a line of code you can run from the interactive shell to handle all this for you: &lt;code&gt;import pip; pip.main(['install', 'MODULE_NAME_HERE'])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Alternatively, you can run the following code from the interactive shell (that is, the thing with the &amp;gt;&amp;gt;&amp;gt; prompt):&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import sys, subprocess; subprocess.run([sys.executable, '-m', 'pip', 'install', '--user', 'MODULE_NAME_HERE'])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you're running the interactive shell from a virtual environment and you get an error like &lt;code&gt;ERROR: Can not perform a '--user' install. User site-packages are not visible in this virtualenv.&lt;/code&gt;, then remove the &lt;code&gt;--user&lt;/code&gt; option:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import sys, subprocess; subprocess.run([sys.executable, '-m', 'pip', 'install', 'MODULE_NAME'])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It's generally not advisable to run code that some website tells you to without understanding what it does, so let's break down each section.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import sys, subprocess;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This imports the &lt;code&gt;sys&lt;/code&gt; and &lt;code&gt;subprocess&lt;/code&gt; built-in modules so that we can access the &lt;code&gt;sys.executable&lt;/code&gt; variable and &lt;code&gt;subprocess.run()&lt;/code&gt; function. These modules are in Python's &lt;i&gt;standard library&lt;/i&gt;, so they are always already installed with Python.&lt;/p&gt;

&lt;p&gt;The semicolon makes Python consider it the "end of the line" so that we can fit two Python instructions on the same line.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;subprocess.run(&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This function call simulates running a command from the Terminal or Command Prompt window, except it's now done from Python code.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sys.executable, &lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;sys.executable&lt;/code&gt; variable contains the file path of the Python interpreter that is currently running the interactive shell. This is especially useful if you have multiple Python installations or virtual environments and want to run the current Python interpreter you are using.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;'-m', 'pip', 'install', 'MODULE_NAME'])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When you run the Python interpreter with the &lt;code&gt;-m&lt;/code&gt; option, you are running a module (in our case, the standard &lt;code&gt;pip&lt;/code&gt; module) as a script. Running &lt;code&gt;python -m pip&lt;/code&gt; is the same thing as running the &lt;code&gt;pip&lt;/code&gt; tool itself. The &lt;code&gt;'install', 'MODULE_NAME'&lt;/code&gt; options tells pip to download and install a module from the &lt;a href="https://pypi.org/"&gt;online Python Packaging Index&lt;/a&gt;. Your computer needs to be connected to the internet when you run these instructions.&lt;/p&gt;

&lt;p&gt;After running these instructions, you should see the output from pip in the window. For example, when I installed the &lt;a href="https://pypi.org/project/PyRect/"&gt;pyrect&lt;/a&gt; module the interactive shell looked like this:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; import sys, subprocess; subprocess.run([sys.executable, '-m', 'pip', 'install', 'pyrect'])
Collecting pyrect
  Using cached PyRect-0.2.0.tar.gz (17 kB)
  Preparing metadata (setup.py) ... done
Building wheels for collected packages: pyrect
  Building wheel for pyrect (setup.py) ... done
  Created wheel for pyrect: filename=PyRect-0.2.0-py2.py3-none-any.whl size=11196 sha256=5b7fb5f14167a70c955fff3bb14eecfa557f07747f27a03b5c63834173a58f49
  Stored in directory: c:\users\al\appdata\local\pip\cache\wheels\25\80\fa\27bb4a1c2e21f64ec71390489d52e57b7cc8afbe79bd595c5e
Successfully built pyrect
Installing collected packages: pyrect
Successfully installed pyrect-0.2.0
CompletedProcess(args=['C:\\Users\\Al\\.virtualenvs\\deleteme-NM1HlCaN\\Scripts\\python.exe', '-m', 'pip', 'install', 'pyrect'], returncode=0)
&lt;/pre&gt;

&lt;p&gt;To verify that the module installed correctly, run &lt;code&gt;import MODULE_NAME&lt;/code&gt;. If no error message appears, the module has been successfully installed.&lt;/p&gt;

&lt;p&gt;If you're teaching a Python class or running a Python workshop, using this Python code is a great way to avoid confusion when you need them to install a third-party module from &lt;a href="https://pypi.org/"&gt;PyPI&lt;/a&gt;. If you want to go into more detail about the command-line, environment variables, and similar topics, you can read &lt;a href="https://inventwithpython.com/beyond/chapter2.html"&gt;Chapter 2 of my free book, Beyond the Basic Stuff with Python&lt;/a&gt;.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Introducing Humre: Human-Readable Regular Expressions</title><link href="https://inventwithpython.com/blog/introducing-humre.html" rel="alternate"></link><published>2022-08-23T12:00:00-04:00</published><updated>2022-08-23T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-08-23:/blog/introducing-humre.html</id><summary type="html">&lt;p&gt;Regular expressions (aka regexes) are a mini-language to specify a pattern of text to look for. However, regex syntax is composed of various punctuation marks that can be hard to remember. Humre is a Python module that gives a more human-readable syntax that works better with code editing tools. You can install Humre just like any other Python module with &lt;code&gt;pip install humre&lt;/code&gt; and &lt;a href="https://github.com/asweigart/humre"&gt;the full documentation is available in the git repo's README file.&lt;/a&gt;&lt;/p&gt;</summary><content type="html">

&lt;p&gt;Regular expressions (aka regexes) are a mini-language to specify a pattern of text to look for. However, regex syntax is composed of various punctuation marks that can be hard to remember. &lt;a href="https://github.com/asweigart/humre"&gt;Humre&lt;/a&gt; is a Python module that gives a more human-readable syntax that works better with code editing tools. You can install Humre just like any other Python module with &lt;code&gt;pip install humre&lt;/code&gt; and &lt;a href="https://github.com/asweigart/humre"&gt;the full documentation is available in the git repo's README file.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, if you want to find an American phone number, you can use the regex string &lt;code&gt;r'\d{3}-\d{3}-\d{4}'&lt;/code&gt; (the &lt;code&gt;r&lt;/code&gt; makes it a &lt;a href="https://www.pythonmorsels.com/raw-strings/"&gt;raw string&lt;/a&gt; that automatically escapes the backslashes) to search for a 3 numeric digit characters for the area code (the &lt;code&gt;\d{3}&lt;/code&gt;), then a dash (the &lt;code&gt;-&lt;/code&gt;), followed by three digits, a dash, and four digits. But &lt;code&gt;r'\d{3}-\d{3}-\d{4}'&lt;/code&gt; is not an intuitive way to convey this. Humre fixes this by providing functions and constants with readable names. The equivalent Humre code for an American phone number is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;exactly(3, DIGIT) + '-' + exactly(3, DIGIT) + '-' exactly(4, DIGIT)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Humre's constants are strings and Humre's functions return strings, so the above expression evaluates to the same string: &lt;code&gt;r'\d{3}-\d{3}-\d{4}'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Consider this regex code using Python's &lt;code&gt;re&lt;/code&gt; module for an American phone number with optional parentheses around the area code:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; import re
&amp;gt;&amp;gt;&amp;gt; regexStr = r'(\d{3})|(\(\d{3}\))-\d{3}-\d{4}'
&amp;gt;&amp;gt;&amp;gt; patternObj = re.compile(regexStr)
&amp;gt;&amp;gt;&amp;gt; patternObj.search('My number is (415)-555-5555.')
&amp;lt;re.Match object; span=(13, 27), match='(415)-555-5555'&amp;gt;
&lt;/pre&gt;

&lt;p&gt;The regex string is hard to read, especially with those escaped parentheses mixed in with the&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; from humre import *
&amp;gt;&amp;gt;&amp;gt; regexStr = either(group(exactly(3, DIGIT)), group(OPEN_PAREN, exactly(3, DIGIT), CLOSE_PAREN)) + '-' + exactly(3, DIGIT) + '-' + exactly(4, DIGIT)
&amp;gt;&amp;gt;&amp;gt; regexStr
'(\\d{3})|(\\(\\d{3}\\))-\\d{3}-\\d{4}'
&amp;gt;&amp;gt;&amp;gt; patternObj = compile(regexStr)
&amp;gt;&amp;gt;&amp;gt; patternObj.search('My number is (415)-555-5555.')
&amp;lt;re.Match object; span=(13, 27), match='(415)-555-5555'&amp;gt;
&lt;/pre&gt;

&lt;p&gt;The Humre code produces the same regex string except using more readable code. Humre is not a reimplementation of a regular expression engine; it's a wrapper that adds readable names to standard regex syntax.&lt;/p&gt;

&lt;p&gt;Python's &lt;code&gt;re&lt;/code&gt; module has "verbose mode" which allows you to use multiline strings with comments. This can make your regex strings easier to read, but they are still strings and won't benefit from advanced features your IDE provides, such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Your editor's parentheses matching works.&lt;/li&gt;
  &lt;li&gt;Your editor's syntax highlight works.&lt;/li&gt;
  &lt;li&gt;Your editor's linter and type hints tool picks up typos.&lt;/li&gt;
  &lt;li&gt;Your editor's autocomplete works.&lt;/li&gt;
  &lt;li&gt;Auto-formatter tools like Black can automatically format your regex code.&lt;/li&gt;
  &lt;li&gt;Humre handles raw strings/string escaping for you.&lt;/li&gt;
  &lt;li&gt;You can put actual Python comments alongside your Humre code.&lt;/li&gt;
  &lt;li&gt;Better error messages for invalid regexes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://github.com/asweigart/humre"&gt;README on Humre's git repository has full documentation&lt;/a&gt;, but here's a general introduction. I still recommend all programmers learn standard regex syntax, as you'll need that knowledge to read other people's (non-Humre) code and diagnose any bugs you accidentally write with the Humre module.&lt;/p&gt;

&lt;h2&gt;Creating Basic Regex Strings with Humre&lt;/h2&gt;

&lt;p&gt;Humre functions return strings, so that instead of remembering the regex syntax for matching between 3 and 5 letter X's with &lt;code&gt;'X{3,5}'&lt;/code&gt;, you can write the code &lt;code&gt;between(3, 5, 'X')&lt;/code&gt;. To avoid having to prefix &lt;code&gt;humre.&lt;/code&gt; in front of every function call, I recommend importing the Humre module with &lt;code&gt;from humre import *&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Because Humre functions take string arguments and return strings, you can compose them together. For example, if you want to put the 3-to-5-letter-Xs inside of a regex group, you can write &lt;code&gt;group(between(3, 5, 'X'))&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Humre functions also accept a variable number of string arguments and concatenates them together for your convenience. The code &lt;code&gt;group('Z', between(3, 5, 'X'))&lt;/code&gt; adds a letter Z to the start of the group, and this is equivalent to the code &lt;code&gt;group('Z' + between(3, 5, 'X'))&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The exception to this is the &lt;code&gt;either()&lt;/code&gt; function, which is used for the regex alternation pipe character &lt;code&gt;'|'&lt;/code&gt;. If you wanted to match "cat" &lt;i&gt;or&lt;/i&gt; "dog" with the regex &lt;code&gt;'cat|dog'&lt;/code&gt;, you would need to call Humre's &lt;code&gt;either('cat', 'dog')&lt;/code&gt; function. The two string arguments are not concatenated together for &lt;code&gt;either()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Humre also provides constants for common regex strings, such as &lt;code&gt;DIGIT&lt;/code&gt; for &lt;code&gt;r'\d'&lt;/code&gt; and &lt;code&gt;OPEN_PAREN&lt;/code&gt; for &lt;code&gt;r'\('&lt;/code&gt;. These manage escaping for you.&lt;/p&gt;

&lt;p&gt;A full list of Humre functions and constants is available in the &lt;a href="https://github.com/asweigart/humre"&gt;README documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Experimenting with what strings Humre returns is easy to do in the interactive shell if you want to explore what Humre is doing:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; from humre import *
&amp;gt;&amp;gt;&amp;gt; exactly(3, 'X')
'X{3}'
&amp;gt;&amp;gt;&amp;gt; &gt;&gt;&gt; zero_or_more('X')
'X*'
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; group(DIGIT)
'(\\d)'
&amp;gt;&amp;gt;&amp;gt; group(either(exactly(3, DIGIT), 'cat'))
'(\\d{3}|cat)'
&amp;gt;&amp;gt;&amp;gt; either(exactly(3, DIGIT), group('cat'))
'\\d{3}|(cat)'
&lt;/pre&gt;

&lt;h2&gt;Humre's Convenience Functions&lt;/h2&gt;

&lt;p&gt;You don't need to keep typing out quotes and &lt;code&gt;+&lt;/code&gt; operators to join strings. Humre's &lt;code&gt;join()&lt;/code&gt; function offers an easier way to join strings together. The following &lt;code&gt;humre.join()&lt;/code&gt; function and &lt;code&gt;''.join()&lt;/code&gt; method calls are equivalent:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; ''.join(['Hello', 'World'])
'HelloWorld'
&amp;gt;&amp;gt;&amp;gt; from humre import *
&amp;gt;&amp;gt;&amp;gt; join('Hello', 'World')
'HelloWorld'
&lt;/pre&gt;

&lt;p&gt;Humre also provides a &lt;code&gt;compile()&lt;/code&gt; function which wraps the standard &lt;code&gt;re.compile()&lt;/code&gt; function. This is so that you don't need to mix your Humre and &lt;code&gt;re&lt;/code&gt; code. The following code produces equivalent pattern objects:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; import re
&amp;gt;&amp;gt;&amp;gt; re.compile(r'\d{3}')
re.compile('\\d{3}')
&amp;gt;&amp;gt;&amp;gt; from humre import *
&amp;gt;&amp;gt;&amp;gt; compile(exactly(3, DIGIT))
re.compile('\\d{3}')
&lt;/pre&gt;

&lt;p&gt;Because Humre functions concatenate multiple string arguments together, if you want to pass regex flags (such as &lt;code&gt;re.IGNORECASE&lt;/code&gt;) you need to use &lt;code&gt;humre.compile()&lt;/code&gt;'s &lt;code&gt;flags&lt;/code&gt; keyword argument instead. The following code produces equivalent pattern objects:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; import re
&amp;gt;&amp;gt;&amp;gt; re.compile('Hello', re.IGNORECASE)
re.compile('Hello', re.IGNORECASE)
&amp;gt;&amp;gt;&amp;gt; from humre import *
&amp;gt;&amp;gt;&amp;gt; compile('Hello', flags=re.IGNORECASE)
re.compile('Hello', re.IGNORECASE)
&lt;/pre&gt;

&lt;p&gt;Note that this means running &lt;code&gt;from humre import *&lt;/code&gt; overwrites Python's built-in &lt;code&gt;compile()&lt;/code&gt; function. However, most applications don't call this function. If yours does, you can import Humre with &lt;code&gt;import humre&lt;/code&gt; or import individual Humre functions and constants instead.&lt;/p&gt;

&lt;h2&gt;Human-Readable Error Messages&lt;/h2&gt;

&lt;p&gt;Typos in your Humre code give much better error messages than the standard &lt;code&gt;re&lt;/code&gt; module does. For example, if you make a typo and ask for between 5 and 3 occurrences of the letter X (instead of between 3 and 5), the &lt;code&gt;re&lt;/code&gt; module produces this unwieldy error:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; import re
&amp;gt;&amp;gt;&amp;gt; re.compile('X{5,3}')
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
  File "C:\Users\Al\AppData\Local\Programs\Python\Python311\Lib\re\__init__.py", line 227, in compile
    return _compile(pattern, flags)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Al\AppData\Local\Programs\Python\Python311\Lib\re\__init__.py", line 294, in _compile
    p = _compiler.compile(pattern, flags)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Al\AppData\Local\Programs\Python\Python311\Lib\re\_compiler.py", line 743, in compile
    p = _parser.parse(p, flags)
        ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Al\AppData\Local\Programs\Python\Python311\Lib\re\_parser.py", line 980, in parse
    p = _parse_sub(source, state, flags &amp; SRE_FLAG_VERBOSE, 0)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Al\AppData\Local\Programs\Python\Python311\Lib\re\_parser.py", line 455, in _parse_sub
    itemsappend(_parse(source, state, verbose, nested + 1,
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Al\AppData\Local\Programs\Python\Python311\Lib\re\_parser.py", line 672, in _parse
    raise source.error("min repeat greater than max repeat",
re.error: min repeat greater than max repeat at position 2
&lt;/pre&gt;

&lt;p&gt;That's quite a lot of text to read through. The same mistake in Humre provides a more direct, detailed error message:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; from humre import *
&amp;gt;&amp;gt;&amp;gt; between(5, 3, 'X')
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
  File "c:\github\humre\src\humre\__init__.py", line 439, in between
    raise ValueError('minimum argument ' + str(minimum) + ' must be less than maximum argument ' + str(maximum))
ValueError: minimum argument 5 must be less than maximum argument 3
&lt;/pre&gt;

&lt;h2&gt;Unicode Letter Support&lt;/h2&gt;

&lt;p&gt;It's common to write &lt;code&gt;[A-Za-z]&lt;/code&gt; for a character class to match all letters, but this suffers from the problem of not matching letters with accent marks or non-English letters. Humre's &lt;code&gt;LETTER&lt;/code&gt; character class looks like &lt;code&gt;'[A-Za-zªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶ-ͷͺ-ͽͿΆΈ-ΊΌΎ-Ρ...&lt;/code&gt; and matches all letters. The definition of "letter" in this case is based on Python's &lt;code&gt;isalpha()&lt;/code&gt; string method.&lt;/p&gt;

&lt;h2&gt;Massive Regexes Are Easier with Humre&lt;/h2&gt;

&lt;p&gt;The benefits of Humre compound as your regular expressions get larger. Let's compare a massive regex used in Python packaging code to its equivalent Humre code.&lt;/p&gt;

&lt;p&gt;Here's the &lt;a href="https://github.com/pypa/packaging/blob/213cb954e5fde8f6f14bc72c3a8e48d1884e37f8/packaging/specifiers.py#L106-L204"&gt;massive regular expression in verbose mode&lt;/a&gt;:&lt;/p&gt;

&lt;textarea style="width: 100%;"&gt;
  _version_regex_str = r"""
(?P&amp;lt;version&amp;gt;
    (?:
        # The identity operators allow for an escape hatch that will
        # do an exact string match of the version you wish to install.
        # This will not be parsed by PEP 440 and we cannot determine
        # any semantic meaning from it. This operator is discouraged
        # but included entirely as an escape hatch.
        (?&amp;lt;====)  # Only match for the identity operator
        \s*
        [^\s]*    # We just match everything, except for whitespace
                  # since we are only testing for strict identity.
    )
    |
    (?:
        # The (non)equality operators allow for wild card and local
        # versions to be specified so we have to define these two
        # operators separately to enable that.
        (?&amp;lt;===|!=)            # Only match for equals and not equals
        \s*
        v?
        (?:[0-9]+!)?          # epoch
        [0-9]+(?:\.[0-9]+)*   # release
        (?:                   # pre release
            [-_\.]?
            (a|b|c|rc|alpha|beta|pre|preview)
            [-_\.]?
            [0-9]*
        )?
        (?:                   # post release
            (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
        )?
        # You cannot use a wild card and a dev or local version
        # together so group them with a | and make them optional.
        (?:
            \.\*  # Wild card syntax of .*
            |
            (?:[-_\.]?dev[-_\.]?[0-9]*)?         # dev release
            (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local
        )?
    )
    |
    (?:
        # The compatible operator requires at least two digits in the
        # release segment.
        (?&amp;lt;=~=)               # Only match for the compatible operator
        \s*
        v?
        (?:[0-9]+!)?          # epoch
        [0-9]+(?:\.[0-9]+)+   # release  (We have a + instead of a *)
        (?:                   # pre release
            [-_\.]?
            (a|b|c|rc|alpha|beta|pre|preview)
            [-_\.]?
            [0-9]*
        )?
        (?:                                   # post release
            (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
        )?
        (?:[-_\.]?dev[-_\.]?[0-9]*)?          # dev release
    )
    |
    (?:
        # All other operators only allow a sub set of what the
        # (non)equality operators do. Specifically they do not allow
        # local versions to be specified nor do they allow the prefix
        # matching wild cards.
        (?&amp;lt;!==|!=|~=)         # We have special cases for these
                              # operators so we want to make sure they
                              # don't match here.
        \s*
        v?
        (?:[0-9]+!)?          # epoch
        [0-9]+(?:\.[0-9]+)*   # release
        (?:                   # pre release
            [-_\.]?
            (a|b|c|rc|alpha|beta|pre|preview)
            [-_\.]?
            [0-9]*
        )?
        (?:                                   # post release
            (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
        )?
        (?:[-_\.]?dev[-_\.]?[0-9]*)?          # dev release
    )
)
"""
&lt;/textarea&gt;

&lt;p&gt;And here's &lt;a href="https://pastebin.com/raw/CyQ5GXWP"&gt;the equivalent code with Humre&lt;/a&gt;:&lt;/p&gt;

&lt;textarea style="width: 100%;"&gt;
from humre import *

SEPARATOR = chars('-_' + PERIOD)
OPT_SEPARATOR = optional(SEPARATOR)


def version_template(fn):
    return ''.join([
    zero_or_more(WHITESPACE),
    optional('v'),
    optional(noncap_group(one_or_more(chars('0-9')), '!')), # epoch

    one_or_more(chars('0-9')), fn(noncap_group(PERIOD, one_or_more(chars('0-9')))), # release

    optional(noncap_group( # pre release
        OPT_SEPARATOR,
        group(either('a', 'b', 'c', 'rc', 'alpha', 'beta', 'pre', 'preview')),
        OPT_SEPARATOR,
        zero_or_more(chars('0-9')),
    )),
    optional(noncap_group( # post release
        either(
            noncap_group('-', one_or_more(chars('0-9'))),
            noncap_group(OPT_SEPARATOR, group_either('post', 'rev', 'r') + OPT_SEPARATOR + zero_or_more(chars('0-9')))
        )
    ))
])

EQ_NE_VERSION_TEMPLATE = version_template(zero_or_more)
COMPATIBILITY_VERSION_TEMPLATE = version_template(one_or_more)

DEV_RELEASE = optional(noncap_group(OPT_SEPARATOR, 'dev', OPT_SEPARATOR, zero_or_more(chars('0-9'))))  # dev release

_version_regex_str = named_group('version',
    either(
        noncap_group(
            # The identity operators allow for an escape hatch that will
            # do an exact string match of the version you wish to install.
            # This will not be parsed by PEP 440 and we cannot determine
            # any semantic meaning from it. This operator is discouraged
            # but included entirely as an escape hatch.
            positive_lookbehind('==='), # Only match for the identity operator
            zero_or_more(WHITESPACE),
            zero_or_more(nonchars(WHITESPACE)) # We just match everything, except for whitespace
                                               # since we are only testing for strict identity.
        ),
        noncap_group(
            # The (non)equality operators allow for wild card and local
            # versions to be specified so we have to define these two
            # operators separately to enable that.
            positive_lookbehind(either('==', '!=')), # Only match for equals and not equals

            EQ_NE_VERSION_TEMPLATE,

            # You cannot use a wild card and a dev or local version
            # together so group them with a | and make them optional.
            optional(noncap_group(
                either(
                    PERIOD + ASTERISK, # Wild card syntax of .*
                    DEV_RELEASE +
                    optional(noncap_group(PLUS, one_or_more(chars('a-z0-9')), zero_or_more(noncap_group(SEPARATOR, one_or_more(chars('a-z0-9')))))) # local
                )
            ))
        ),
        noncap_group(
            # The compatible operator requires at least two digits in the
            # release segment.
            positive_lookbehind('~='), # Only match for the compatible operator

            COMPATIBILITY_VERSION_TEMPLATE,

            DEV_RELEASE,
        ),
        noncap_group(
            # All other operators only allow a sub set of what the
            # (non)equality operators do. Specifically they do not allow
            # local versions to be specified nor do they allow the prefix
            # matching wild cards.
            negative_lookbehind(either('==', '!=', '~=')), # We have special cases for these
                                                           # operators so we want to make sure they
                                                           # don't match here.
            EQ_NE_VERSION_TEMPLATE,

            DEV_RELEASE,
        )
    )
)

print(_version_regex_str)
&lt;/textarea&gt;

&lt;p&gt;While both are a lot to take in, the Humre code benefits from not being in a multiline string. This means your IDE can match parentheses, your linter can spot typos, and your automatic code formatting tools can clean up your Humre code for you.&lt;/p&gt;

&lt;h2&gt;Full Documentation&lt;/h2&gt;

&lt;p&gt;This is a broad introduction to Humre. The &lt;a href="https://github.com/asweigart/humre"&gt;full documentation&lt;/a&gt; is available in the git repo's README file.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Which Al Sweigart Python Books Should I Start Reading?</title><link href="https://inventwithpython.com/blog/which-al-sweigart-python-books-should-i-start-reading.html" rel="alternate"></link><published>2022-08-22T12:00:00-04:00</published><updated>2022-08-22T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-08-22:/blog/which-al-sweigart-python-books-should-i-start-reading.html</id><summary type="html">&lt;p&gt;I've been writing programming books for beginners since 2009. All of them are free to read online at &lt;a href="https://inventwithpython.com"&gt;InventWithPython.com&lt;/a&gt; Many people email me asking which of my books they should read, so I wrote this guide to help beginners get started with my free learning materials.&lt;/p&gt;
</summary><content type="html">

&lt;p&gt;I've been writing programming books for beginners since 2009. All of them are free to read online at &lt;a href="https://inventwithpython.com"&gt;InventWithPython.com&lt;/a&gt; Many people email me asking which of my books they should read, so I wrote this guide to help beginners get started with my free learning materials.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="#just-get-started"&gt;"I am interested in coding and I just want to get started learning to code."&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#make-video-games"&gt;"I like playing or want to make video games."&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#hacking"&gt;"I'm interested in hacking and cybersecurity."&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#hello-world"&gt;"I'm a beginner but not a complete beginner and I'm tired of doing the same "Hello, world" tutorials."&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#minecraft"&gt;"I love Minecraft and want to get into coding."&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#kids"&gt;"I want to get my kids into programming."&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#all"&gt;"I'm super ambitious about learning to code and I want to read all of your books! What order should I read them?"&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#beginners"&gt;"What are your books for complete beginners?"&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;





&lt;p&gt;Before I dive into the details, another common question about learning to program is &lt;b&gt;"What programming language should I learn?" The answer is almost every case is &lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt;.&lt;/b&gt; Python has a gentle learning curve, readable syntax, but is also a serious language used by professionals. Many people say it doesn't matter which language you start with, and they are &lt;i&gt;sort of&lt;/i&gt; right. Picking another language to learn as your first programming language isn't a "wrong" choice, but Python is so versatile and free of common speed bumps in other languages that I strongly recommend it.&lt;/p&gt;

&lt;h2 name="just-get-started"&gt;"I am interested in coding and I just want to get started learning to code."&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://automatetheboringstuff.com/"&gt;&lt;img src="https://inventwithpython.com/images/cover_automate2_thumb.webp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Automate the Boring Stuff with Python, 2nd Edition (&lt;a href="https://automatetheboringstuff.com/"&gt;read free online&lt;/a&gt;, &lt;a href="https://www.nostarch.com/automatestuff2"&gt;buy print book&lt;/a&gt;) is my most popular programming book. The first half covers the basics of programming and the Python langauge, while the second half goes into software modules that help you with practical tasks such as updating Excel spreadsheets, sending text and email notifications, extracting text from PDFs, scraping data from web sites, and many other tasks. This book skips the technical details of computer science and algorithms and focuses on getting you to write practical programs to automate boring tasks on your computer.&lt;/p&gt;

&lt;p&gt;It's also a good general programming guide for complete beginners.&lt;/p&gt;

&lt;p&gt;Automate the Boring Stuff with Python is free to read on my website &lt;a href="https://automatetheboringstuff.com"&gt;AutomateTheBoringStuff.com&lt;/a&gt;. This book is ideal for:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Adults and teens who want to learn to code.&lt;/li&gt;
  &lt;li&gt;Hobbyists, office workers, academic researchers, and administrators, who use a computer as part of their job.&lt;/li&gt;
  &lt;li&gt;People who find themselves buried in spreadsheets and PDFs as part of their job.&lt;/li&gt;
  &lt;li&gt;Adults interested in changing careers to software engineering but want a small taste to start.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've also created a &lt;a href="https://inventwithpython.com/automateudemy"&gt;50-video Udemy course&lt;/a&gt; that follows most of the content in this book. The first &lt;a href="https://www.youtube.com/watch?v=1F_OgqRuSdI&amp;amp;list=PL0-84-yl1fUnRuXGFe_F7qSH1LEnn9LkW"&gt;15 videos&lt;/a&gt; are free to watch on YouTube.&lt;/p&gt;

&lt;p&gt;After finishing Automate, I recommend moving on to &lt;a href="https://inventwithpython.com/beyond/"&gt;Beyond the Basic Stuff with Python&lt;/a&gt; or reading Eric Matthes' &lt;a href="https://nostarch.com/pythoncrashcourse2e"&gt;Python Crash Course&lt;/a&gt;.&lt;/p&gt;

&lt;h2 name="make-video-games"&gt;"I like playing or want to make video games."&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://inventwithpython.com/invent4thed/"&gt;&lt;img src="https://inventwithpython.com/images/cover_invent4th_thumb.webp"&gt;&lt;/a&gt; &lt;a href="https://inventwithpython.com/pygame"&gt;&lt;img src="https://inventwithpython.com/images/cover_makinggames_thumb.webp"&gt;&lt;/a&gt; &lt;a href="https://inventwithpython.com/bigbookpython/"&gt;&lt;img src="https://inventwithpython.com/images/cover_bigbookpython_thumb.webp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Invent Your Own Computer Games with Python (&lt;a href="https://inventwithpython.com/invent4thed/"&gt;read free online&lt;/a&gt;, &lt;a href="https://www.nostarch.com/inventwithpython"&gt;buy print book&lt;/a&gt;) is for complete beginners and teaches programming by making simple text-based games (and, at the end, graphical games with the Pygame software library). Making Games with Python &amp; Pygame (&lt;a href="https://inventwithpython.com/pygame"&gt;read free online&lt;/a&gt;, &lt;a href="https://inventwithpython.com/amazon-pygame"&gt;buy print book&lt;/a&gt;) follows up with the source code to several graphical games such as Tetris and Snake and Connect Four. The Big Book of Small Python Projects (&lt;a href="https://inventwithpython.com/bigbookpython/"&gt;read free online&lt;/a&gt;, &lt;a href="https://nostarch.com/big-book-small-python-projects"&gt;buy print book&lt;/a&gt;) follows up with 81 text-based game, simulation, and animation projects.&lt;/p&gt;

&lt;p&gt;The game projects in these books aren't like modern video games with 3D graphics, but the simplified video games are fun projects while you learn the basics of programming.&lt;/p&gt;

&lt;p&gt;These books are ideal for:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Complete beginners who want to learn to program using fun video game projects.&lt;/li&gt;
  &lt;li&gt;People looking for small programming project ideas.&lt;/li&gt;
  &lt;li&gt;People who want to learn the Pygame 2D graphical game library.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 name="hacking"&gt;"I'm interested in hacking and cybersecurity."&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://inventwithpython.com/cracking/"&gt;&lt;img src="https://inventwithpython.com/images/cover_crackingcodes_thumb.webp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cracking Codes with Python (&lt;a href="https://inventwithpython.com/cracking/"&gt;read free online&lt;/a&gt;, &lt;a href="https://www.nostarch.com/crackingcodes"&gt;buy print book&lt;/a&gt;) covers classic ciphers and how to break them cryptanalysis techniques. You'll learn to write Python programs that implement various ciphers (Caesar cipher, transposition cipher, Vigenere cipher, the RSA algorithm, and more) and also write programs that can crack these codes.&lt;/p&gt;

&lt;p&gt;This book is good for:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;People new to hacking and cybersecurity.&lt;/li&gt;
  &lt;li&gt;People who want to get better at math and programming as part of their tech career.&lt;/li&gt;
  &lt;li&gt;Beginner cryptographers.&lt;/li&gt;
  &lt;li&gt;People who don't want a cybersecurity career, but want to learn Python and are intrigued by ciphers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 name="hello-world"&gt;"I'm a beginner but not a complete beginner and I'm tired of doing the same "Hello, world" tutorials."&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://inventwithpython.com/bigbookpython/"&gt;&lt;img src="https://inventwithpython.com/images/cover_bigbookpython_thumb.webp"&gt; &lt;img src="https://inventwithpython.com/images/cover_beyond_thumb.webp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some people say that to improve your coding skills, you should read the source code for open source projects. I disagree with this advice: open source code might not be well documented, could use advanced techniques, and is generally a "drinking from a fire hose" way to learn more about coding. I wrote The Big Book of Small Python Projects (&lt;a href="https://inventwithpython.com/bigbookpython/"&gt;read free online&lt;/a&gt;, &lt;a href="https://nostarch.com/big-book-small-python-projects"&gt;buy print book&lt;/a&gt;) to include the source code to projects that are short and simple, but still offer an example of how programming concepts are applied to actual programs.&lt;/p&gt;

&lt;p&gt;Beyond the Basic Stuff with Python (&lt;a href="https://inventwithpython.com/beyond/"&gt;read free online&lt;/a&gt;, &lt;a href="https://nostarch.com/beyond-basic-stuff-python"&gt;buy print book&lt;/a&gt;) covers some intermediate and advanced Python techniques, but also general best practices when it comes to software development such as how to write functions, how to name variables, programming jargon, and source control using Git and GitHub.&lt;/p&gt;

&lt;p&gt;These books are ideal for:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;People who feel "stuck" doing beginner tutorials they already understand but find advanced tutorials too difficult.&lt;/li&gt;
  &lt;li&gt;People who want ideas for small programming projects to work on.&lt;/li&gt;
  &lt;li&gt;People looking for the next step in learning to code with Python.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2 name="minecraft"&gt;"I love Minecraft and want to get into coding."&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://turtleappstore.com/book"&gt;&lt;img src="https://inventwithpython.com/images/cover_codingwithminecraft_thumb.webp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Coding with Minecraft (&lt;a href="https://turtleappstore.com/book"&gt;read free online&lt;/a&gt;, &lt;a href="https://www.nostarch.com/codingwithminecraft"&gt;buy print book&lt;/a&gt;) teaches you how to program using the ComputerCraft mod for Minecraft. ComputerCraft adds programmable robots to your Minecraft world, and you can program to mine, build, farm, craft, solve mazes, and more. ComputerCraft uses the &lt;a href="https://www.lua.org/"&gt;Lua&lt;/a&gt; scripting language rather than Python.&lt;/p&gt;

&lt;p&gt;This book is ideal for:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Kids (and parents) who like Minecraft and want to get into programming.&lt;/li&gt;
  &lt;li&gt;Teachers or instructors who run after school coding clubs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also check out &lt;a href="https://nostarch.com/programwithminecraft"&gt;Programming with Minecraft&lt;/a&gt;.&lt;/p&gt;

&lt;h2 name="kids"&gt;"I want to get my kids into programming."&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://inventwithscratch.com/"&gt;&lt;img src="https://inventwithpython.com/images/cover_scratch3programmingplayground_thumb.webp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scratch 3 Programming Playground (&lt;a href="https://inventwithscratch.com/"&gt;read free online&lt;/a&gt;, &lt;a href="https://www.nostarch.com/scratch3playground"&gt;buy print copy&lt;/a&gt;) cover's the Scratch programming environment tool. &lt;a href="https://scratch.mit.edu/"&gt;Scratch&lt;/a&gt; is a free, graphical programming environment from MIT. It teaches 8- to 16-year-olds programming by snapping code blocks together to form complete programs. You can &lt;a href="https://scratch.mit.edu/about/"&gt;learn more about what Scratch is&lt;/a&gt; and &lt;a href="https://scratch.mit.edu/parents/"&gt;read information for parents&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also check out Coding with Minecraft (&lt;a href="https://turtleappstore.com/book"&gt;read free online&lt;/a&gt;, &lt;a href="https://www.nostarch.com/codingwithminecraft"&gt;buy print book&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I also have &lt;a href="https://www.udemy.com/scratch-game-programming/"&gt;a free Udemy course that covers Scratch&lt;/a&gt;, although as of 2022 it still covers version 2 of the Scratch editor instead of the current version 3.&lt;/p&gt;


&lt;h2 name="all"&gt;"I'm super ambitious about learning to code and I want to read all of your books! What order should I read them?"&lt;/h2&gt;

&lt;p&gt;If you want to read all of these books, here's the order to tackle them:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/"&gt;Automate the Boring Stuff with Python&lt;/a&gt; for basic programming concepts.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://inventwithpython.com/invent4thed/"&gt;Invent Your Own Computer Games with Python&lt;/a&gt; for detailed tutorials for making game projects.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/"&gt;The Big Book of Small Python Projects&lt;/a&gt; for experience creating a wide variety of programming projects.&lt;/li&gt;
  &lt;li&gt;(Optionally) &lt;a href="https://inventwithpython.com/pygame"&gt;Making Games with Python &amp; Pygame&lt;/a&gt; if you want to continue making games.&lt;/li&gt;
  &lt;li&gt;(Optionally) &lt;a href="https://inventwithpython.com/cracking/"&gt;Cracking Codes with Python&lt;/a&gt; if you want to learn Python for cipher projects.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://inventwithpython.com/beyond/"&gt;Beyond the Basic Stuff with Python&lt;/a&gt; to learn intermediate and advanced software development skills.&lt;/li&gt;
  &lt;li&gt; &lt;a href="https://nostarch.com/recursive-book-recursion"&gt;The Recursive Book of Recursion&lt;/a&gt; to learn computer science by exploring recursive programming techniques.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2 name="beginners"&gt;"What are your books for complete beginners?"&lt;/h2&gt;

&lt;p&gt;To recap, here are my books for those with no programming experience:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/"&gt;Automate the Boring Stuff with Python&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://inventwithpython.com/cracking/"&gt;Cracking Codes with Python&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://inventwithpython.com/invent4thed/"&gt;Invent Your Own Computer Games with Python&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://turtleappstore.com/book"&gt;Coding with Minecraft&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://inventwithscratch.com/"&gt;Scratch 3 Programming Playground&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are more free resources than ever to learn to code. Good luck on your programming journey!&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>New Book: The Recursive Book of Recursion by Al Sweigart</title><link href="https://inventwithpython.com/blog/book-release-recursive-book-of-recursion-al-sweigart.html" rel="alternate"></link><published>2022-08-09T12:00:00-04:00</published><updated>2022-08-09T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-08-09:/blog/book-release-recursive-book-of-recursion-al-sweigart.html</id><summary type="html">&lt;p&gt;My new programming book, the Recursive Book of Recursion, is released in August 2022. The book covers several classic recursive algorithms and breaks down recursion's fearsome reputation as a programming technique. The book has the code for its numerous programs in both Python and JavaScript. When you &lt;a href="https://nostarch.com/recursive-book-recursion"&gt;buy it direct from the publisher, No Starch Press&lt;/a&gt;, you'll receive a DRM-free ebook copy with your print book order.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;&lt;img src="/blogstatic/new-book-recursion.webp" /&gt;&lt;/p&gt;

&lt;p&gt;My new programming book, the Recursive Book of Recursion, is released in August 2022. The book covers several classic recursive algorithms and breaks down recursion's fearsome reputation as a programming technique. The book has the code for its numerous programs in both Python and JavaScript. When you &lt;a href="https://nostarch.com/recursive-book-recursion"&gt;buy it direct from the publisher, No Starch Press&lt;/a&gt;, you'll receive a DRM-free ebook copy with your print book order.

&lt;!--The downloadable resources contains all of the programs in the book.--&gt;

&lt;p&gt;Here's the contents of the book:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Part 1: Understanding Recursion&lt;/b&gt;&lt;/p&gt;

&lt;b&gt;Chapter 1: What Is Recursion?&lt;/b&gt; - Explains recursion and how it is the natural result of the way programming languages implement functions and function &lt;p&gt;calls. This chapter also argues that recursion isn’t nearly the elegant, mystical concept many claim it is.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/stackoverflow-fig-1-8.webp" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Chapter 2: Recursion vs. Iteration&lt;/b&gt; - Dives into the differences (and many similarities) between recursive and iterative techniques.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/maze-tree-fig-2-5.webp" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Chapter 3: Classic Recursion Algorithms&lt;/b&gt; - Covers famous recursive programs such as the Tower of Hanoi, the flood fill algorithm, and others.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/tower-of-hanoi-fig-3-4.webp" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Chapter 4: Backtracking and Tree Traversal Algorithms&lt;/b&gt; - Discusses a problem for which recursion is particularly suited: traversing tree data structures, such as when solving mazes and navigating a directory.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/tree-and-not-tree-fig-4-1.webp" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Chapter 5: Divide-and-Conquer Algorithms&lt;/b&gt; - Discusses how recursion is useful for splitting large problems into smaller subproblems and covers &lt;/p&gt;several common divide-and-conquer algorithms.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/sort-books-fig-5-2.webp" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Chapter 6: Permutations and Combinations&lt;/b&gt; - Covers recursive algorithms involving ordering and matching, as well as the common programming problems to which these techniques are applied.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/wedding-guest-permutation-fig-6-2.webp" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Chapter 7: Memoization and Dynamic Programming&lt;/b&gt; - Explains some simple tricks to improve code efficiency when applying recursion in the real world.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/fib-fig-7-1.webp" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Chapter 8: Tail Call Optimization&lt;/b&gt; - Covers tail call optimization, a common technique used to improve the performance of recursive algorithms, and how it works.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/tco-factorial-fig-8-1.webp" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Chapter 9: Drawing Fractals&lt;/b&gt; - Tours the intriguing art that can be programmatically produced by recursive algorithms. This chapter makes use of turtle graphics for generating its images.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/sierpinsky-fig-9-3.webp" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Part 2: Projects&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Chapter 10: File Finder&lt;/b&gt; - Covers a project that searches through the files on your computer according to custom search parameters you provide.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/file-finder-fig-10-1.webp" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Chapter 11: Maze Generator&lt;/b&gt; - Covers a project that automatically generates mazes of any size using the recursive backtracker algorithm.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/maze-gen-fig-11-1.webp" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Chapter 12: Sliding-Tile Solver&lt;/b&gt; - Covers a project that solves sliding-tile puzzles, also called 15-puzzles.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/sliding-tile-solver-fig-12-2.webp" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Chapter 13: Fractal Art Maker&lt;/b&gt; - Explores a project that can produce custom fractal art of your own design.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/three-corners-fig-13-9.webp" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Chapter 14: Droste Maker&lt;/b&gt; - Explores a project that produces recursive, picture-in-picture images using the Pillow image-manipulation module.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/droste-maker-fig-14-2.webp" /&gt;&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>How Many Recursive Cases and Base Cases Does a Recursive Function Need?</title><link href="https://inventwithpython.com/blog/how-many-recursive-cases-and-base-cases-does-recursive-function-need.html" rel="alternate"></link><published>2022-03-20T12:00:00-04:00</published><updated>2022-03-20T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-03-20:/blog/how-many-recursive-cases-and-base-cases-does-recursive-function-need.html</id><summary type="html">&lt;p&gt;Let's look at a simple example of a recursive function to see what it needs at minimum.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;&lt;img src='"/blogstatic/how-many-recursive-base-cases.webp"' /&gt;&lt;/p&gt;

&lt;p&gt;All recursive functions need at least one recursive case and at least one base case. A recursive case is the set of circumstances where a recursive function calls itself. A base case is the set of circumstances where a recursive function returns without calling itself. Here's a simple recursive factorial function with one base case and one recursive case:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# BASE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# RECURSIVE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you call &lt;code&gt;factorial(5)&lt;/code&gt;, it returns

&lt;p&gt;If a recursive function has zero recursive cases, it never calls itself and is not a recursive function. The following buggy factorial function has a base case but no recursive case:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorialNoRecursiveCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# BASE CASE&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;When &lt;code&gt;factorialNoRecursiveCase(5)&lt;/code&gt; is called, it returns &lt;code&gt;1&lt;/code&gt;. The trouble is, it returns &lt;code&gt;1&lt;/code&gt; for every argument.&lt;/p&gt;

&lt;p&gt;If a recursive function has zero base ases, it never returns and only recurses. This will eventually cause a stack overflow (unless some other . The following buggy factorial function has a recursive case but no base case:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorialNoBaseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# RECURSIVE CASE&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;factorialNoBaseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;When &lt;code&gt;factorialNoBaseCase(5)&lt;/code&gt; is called, it causes a &lt;code&gt;RecursionError: maximum recursion depth exceeded&lt;/code&gt; error.&lt;/p&gt;

&lt;p&gt;There is one more requirement for a correctly working recursive function: the recursive function calls must eventually get closer to a base case. Otherwise, the base case is never reached and it will be as though no base case exists, causing a stack overflow. The following buggy factorial function has a recursive case and a base case but never reaches the base case:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorialNoReachBaseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# BASE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# RECURSIVE CASE&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;factorialNoReachBaseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Adds one instead of subtracts one!&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When &lt;code&gt;factorialNoReachBaseCase(5)&lt;/code&gt; is called, it never reaches the base case and causes a &lt;code&gt;RecursionError: maximum recursion depth exceeded&lt;/code&gt; error.&lt;/p&gt;

&lt;p&gt;When writing your own recursive functions, you should ask yourself three questions:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;What is the base case?&lt;/li&gt;
  &lt;li&gt;What arguments are passed to the recursive function call?&lt;/li&gt;
  &lt;li&gt;Does the argument get closer to the base case?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you'd like to learn more about recursion, check out my book, &lt;a href="https://nostarch.com/recursive-book-recursion"&gt;The Recursive Book of Recursion"&lt;/a&gt; from No Starch Press.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Bootstrap 5 Boilerplate Code for New Web Pages</title><link href="https://inventwithpython.com/blog/bootstrap-5-boilerplate.html" rel="alternate"></link><published>2022-03-11T12:00:00-05:00</published><updated>2022-03-11T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-03-11:/blog/bootstrap-5-boilerplate.html</id><summary type="html">&lt;p&gt;Here's the boilerplate that you can copy and paste when creating a new web page with Bootstrap 5.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;Here's the boilerplate that you can copy and paste when creating a new web page with &lt;a href="https://getbootstrap.com"&gt;Bootstrap 5.1.3&lt;/a&gt;. This comes from &lt;a href="https://getbootstrap.com/docs/5.1/getting-started/introduction/"&gt;the Bootstrap documentation&lt;/a&gt;, with some modification to load the icon library as well:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="cm"&gt;&amp;lt;!-- Required meta tags --&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;viewport&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width=device-width, initial-scale=1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="cm"&gt;&amp;lt;!-- Bootstrap CSS --&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;integrity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;crossorigin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;anonymous&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="cm"&gt;&amp;lt;!-- Bootstrap Icon library --&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;integrity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;sha384-He3RckdFB2wffiHOcESa3sf4Ida+ni/fw9SSzAcfY2EPnU1zkK/sLUzw2C5Tyuhj&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;crossorigin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;anonymous&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;TITLE_GOES_HERE&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;container&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;row&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;col&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
MAIN_CONTENT_GOES_HERE
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="cm"&gt;&amp;lt;!-- Bootstrap Bundle with Popper --&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;integrity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;crossorigin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;anonymous&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I created this for my own use whenever I start a new Bootstrap web page. Feel free to edit as needed. I'll also try to update this page as new versions of Bootstrap come out.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Make Fractals in Python Turtle with the FractalArtMaker Module</title><link href="https://inventwithpython.com/blog/fractalartmaker-python-turtle.html" rel="alternate"></link><published>2022-03-09T12:00:00-05:00</published><updated>2022-03-09T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-03-09:/blog/fractalartmaker-python-turtle.html</id><summary type="html">&lt;p&gt;&lt;a href="https://pypi.org/project/FractalArtMaker/"&gt;Fractal Art Maker&lt;/a&gt; has been upgraded and is now covered &lt;a href="/blog/fractal-art-maker-python-package.html"&gt;in this new blog post.&lt;/a&gt;&lt;/p&gt;</summary><content type="html">

&lt;a href="https://pypi.org/project/FractalArtMaker/"&gt;Fractal Art Maker&lt;/a&gt; has been upgraded and is now covered &lt;a href="/blog/fractal-art-maker-python-package.html"&gt;in this new blog post.&lt;/a&gt;

&lt;!--
&lt;p&gt;&lt;a href="https://pypi.org/project/FractalArtMaker/"&gt;FractalArtMaker&lt;/a&gt; is a module for creating fractal art in Python's &lt;code&gt;turtle&lt;/code&gt; module. You can install the module by running &lt;code&gt;pip3 install FractalArtMaker&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This module is explored in the book "The Recursive Book of Recursion" by Al Sweigart from No Starch Press. This blog post will show you how to make fractals like the following:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/fractalartmakerexamples.webp" /&gt;&lt;/p&gt;

&lt;p&gt;You can purchase this book directly from the publisher at &lt;a href="https://nostarch.com/recursive-book-recursion"&gt;https://nostarch.com/recursive-book-recursion&lt;/a&gt; or read it online at &lt;a href="https://inventwithpython.com/recursion/"&gt;https://inventwithpython.com/recursion/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Quickstart&lt;/h2&gt;

&lt;p&gt;To view some example fractals, run the following from the interactive shell:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;fractalartmaker&lt;/span&gt;
&lt;span class="n"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;fractalartmaker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fast&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# draw the fractals quickly&lt;/span&gt;
&lt;span class="n"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;fractalartmaker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# pass 1 to 9&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Making Fractals&lt;/h2&gt;

&lt;p&gt;The Fractal Art Maker's algorithm has two major components: a shape-drawing function and the recursive &lt;code&gt;drawFractal()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;The shape-drawing function draws a basic shape. The Fractal Art Maker program comes with the two shape-drawing functions, &lt;code&gt;fractalartmaker.drawFilledSquare()&lt;/code&gt; and &lt;code&gt;fractalartmaker.drawTriangleOutline()&lt;/code&gt;, but you can also create your own. We pass a shape-drawing function to the &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt; function as an argument.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt; function also has a parameter indicating changes to the size, position, and angle of the shapes between recursive calls to &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt; function uses a shape-drawing function passed to it to draw the individual parts of the fractal. This is usually a simple shape, such as a square or triangle. The beautiful complexity of the fractals emerges from &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt; recursively calling this function for each individual component of the whole fractal.&lt;/p&gt;

&lt;p&gt;Here's the two shape-drawing functions that come in the &lt;code&gt;fractalartmaker&lt;/code&gt; module:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drawFilledSquare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Move to the top-right corner before drawing:&lt;/span&gt;
    &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;penup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pendown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Alternate between white and gray (with black border):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pencolor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'black'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fillcolor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'white'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pencolor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'black'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fillcolor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gray'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Draw a square:&lt;/span&gt;
    &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin_fill&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# Draw four lines.&lt;/span&gt;
        &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end_fill&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drawTriangleOutline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Move the turtle to the top of the equilateral triangle:&lt;/span&gt;
    &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;penup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Turn to face upwards.&lt;/span&gt;
    &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Move to the top corner.&lt;/span&gt;
    &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Turn to face the bottom-right corner.&lt;/span&gt;
    &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pendown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Draw the three sides of the triangle:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;turtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The shape-drawing functions for the Fractal Art Maker have two parameters: &lt;code&gt;size&lt;/code&gt; and &lt;code&gt;depth&lt;/code&gt;. The size parameter is the length of the sides of the square or triangle it draws. The shape-drawing functions should always use arguments to &lt;code&gt;turtle.forward()&lt;/code&gt; that are based on &lt;code&gt;size&lt;/code&gt; so that the lengths will be proportionate to size at each level of recursion. Avoid code like &lt;code&gt;turtle.forward(100)&lt;/code&gt; or &lt;code&gt;turtle.forward(200)&lt;/code&gt;; instead, use code that is based on the &lt;code&gt;size&lt;/code&gt; parameter, like &lt;code&gt;turtle.forward(size)&lt;/code&gt; or &lt;code&gt;turtle.forward(size * 2)&lt;/code&gt;. In Python's &lt;code&gt;turtle&lt;/code&gt; module, &lt;code&gt;turtle.forward(1)&lt;/code&gt; moves the turtle by one unit, which is not necessarily the same as one pixel.&lt;/p&gt;

&lt;p&gt;The shape-drawing functions' second parameter is the recursive depth of &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt;. Your shape-drawing function can ignore this argument, but using it can cause interesting variations to the basic shape. For example, the &lt;code&gt;fractalartmaker.drawFilledSquare()&lt;/code&gt; shape-drawing function uses depth to alternate between drawing white squares and gray squares. Keep this in mind if you'd like to create your own shape-drawing functions for the Fractal Art Maker program, as they must accept a &lt;code&gt;size&lt;/code&gt; and &lt;code&gt;depth&lt;/code&gt; argument.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt; function has three required parameters and one optional one: &lt;code&gt;shapeDrawFunction&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, &lt;code&gt;specs&lt;/code&gt;, and optionally &lt;code&gt;maxDepth&lt;/code&gt;. The &lt;code&gt;shapeDrawFunction&lt;/code&gt; parameter expects a function, like &lt;code&gt;fractalartmaker.drawFilledSquare&lt;/code&gt; or &lt;code&gt;fractalartmaker.drawTriangleOutline&lt;/code&gt;. The &lt;code&gt;size&lt;/code&gt; parameter expects the starting size passed to the drawing function. Often, a value between &lt;code&gt;100&lt;/code&gt; and &lt;code&gt;500&lt;/code&gt; is a good starting size, though this depends on the code in your shape-drawing function, and finding the right value may require experimentation.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;specs&lt;/code&gt; parameter expects a list of dictionaries that specify how the recursive shapes should change their size, position, and angle as &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt; recursively calls itself. These specifications are described later in this section. To prevent &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt; from recursing until it causes a stack overflow, the &lt;code&gt;maxDepth&lt;/code&gt; parameter holds the number of times &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt; should recursively call itself. By default, &lt;code&gt;maxDepth&lt;/code&gt; has a value of &lt;code&gt;8&lt;/code&gt;, but you can provide a different value if you want more recursive shapes or fewer.&lt;/p&gt;

&lt;p&gt;The recursive calls to &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt; are based on the specification in the &lt;code&gt;specs&lt;/code&gt; list's dictionaries. For each dictionary, &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt; makes one recursive call to &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt;. If specs is a list with one dictionary, every call to &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt; results in only one recursive call to &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt;. If specs is a list with three dictionaries, every call to &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt; results in three recursive calls to &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The dictionaries in the specs parameter provide specifications for each recursive call. Each of these dictionaries has the keys &lt;code&gt;sizeChange&lt;/code&gt;, &lt;code&gt;xChange&lt;/code&gt;, &lt;code&gt;yChange&lt;/code&gt;, and &lt;code&gt;angleChange&lt;/code&gt;. These dictate how the size of the fractal, the position of the turtle, and the heading of the turtle change for a recursive &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt; call.&lt;/p&gt;

* &lt;code&gt;sizeChange&lt;/code&gt; (default is &lt;code&gt;1.0&lt;/code&gt;) - The next recursive shape's size value is the current size multiplied by this value.
* &lt;code&gt;xChange&lt;/code&gt; (default is &lt;code&gt;0.0&lt;/code&gt;) - The next recursive shape's x-coordinate is the current x-coordinate plus the current size multiplied by this value.
* &lt;code&gt;yChange&lt;/code&gt; (default is &lt;code&gt;0.0&lt;/code&gt;) - The next recursive shape's y-coordinate is the current y-coordinate plus the current size multiplied by this value.
* &lt;code&gt;angleChange&lt;/code&gt; (default is &lt;code&gt;0.0&lt;/code&gt;) - The next recursive shape's starting angle is the current starting angle plus this value.

&lt;p&gt;Let's take a look at the specification dictionary for the Four Corners fractal, which is drawn when you call &lt;code&gt;fractalartmaker.example(1)&lt;/code&gt;. The call to &lt;code&gt;fractalartmaker.drawFractal()&lt;/code&gt; for the Four Corners fractal passes the following list of dictionaries for the &lt;code&gt;specs&lt;/code&gt; parameter:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;fractalartmaker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drawFractal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fractalartmaker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drawFilledSquare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;'sizeChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'xChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'yChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'sizeChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'xChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'yChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'sizeChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'xChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'yChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'sizeChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'xChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'yChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;specs&lt;/code&gt; list has four dictionaries, so each call to &lt;code&gt;drawFractal()&lt;/code&gt; that draws a square will, in turn, recursively call &lt;code&gt;drawFractal()&lt;/code&gt; four more times to draw four more squares.&lt;/p&gt;

&lt;p&gt;To determine the size of the next square to be drawn, the value for the &lt;code&gt;sizeChange&lt;/code&gt; key is multiplied by the current size parameter. The first dictionary in the specs list has a &lt;code&gt;sizeChange&lt;/code&gt; value of &lt;code&gt;0.5&lt;/code&gt;, which makes the next recursive call have a size argument of &lt;code&gt;350 * 0.5&lt;/code&gt;, or &lt;code&gt;175&lt;/code&gt; units. This makes the next square half the size of the previous square. A &lt;code&gt;sizeChange&lt;/code&gt; value of &lt;code&gt;2.0&lt;/code&gt; would, for example, double the size of the next square. If the dictionary has no &lt;code&gt;sizeChange&lt;/code&gt; key, the value defaults to &lt;code&gt;1.0&lt;/code&gt; for no change to the size.&lt;/p&gt;

&lt;p&gt;If you look at the three other dictionaries in the &lt;code&gt;specs&lt;/code&gt; list, you'll notice they all have a &lt;code&gt;sizeChange&lt;/code&gt; value of &lt;code&gt;0.5&lt;/code&gt;. The difference between them is that their &lt;code&gt;xChange&lt;/code&gt; and &lt;code&gt;yChange&lt;/code&gt; values place them in the other three corners of the current square. As a result, the next four squares are drawn centered on the four corners of the current square.&lt;/p&gt;

&lt;p&gt;The dictionaries in the &lt;code&gt;specs&lt;/code&gt; list for this example don't have an &lt;code&gt;angleChange&lt;/code&gt; value, so this value defaults to &lt;code&gt;0.0&lt;/code&gt; degrees. A positive &lt;code&gt;angleChange&lt;/code&gt; value indicates a counterclockwise rotation, while a negative value indicates a clockwise rotation.&lt;/p&gt;

&lt;p&gt;Take a look at the code in the module's &lt;code&gt;example()&lt;/code&gt; function for more examples.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fractalartmaker&lt;/code&gt; module also has a &lt;code&gt;fractalartmaker.fast()&lt;/code&gt; function you can call to make the fractals draw quickly, and a &lt;code&gt;fractalartmaker.clear()&lt;/code&gt; to clear the turtle drawing window.&lt;/p&gt;

&lt;p&gt;Let's examine the code for each of the 9 example fractals that come with the module.&lt;/p&gt;

&lt;h2&gt;Example 1 - Four Corners&lt;/h2&gt;

&lt;p&gt;&lt;img src="/blogstatic/fractalartmaker-fourcorners.webp" /&gt;&lt;/p&gt;

&lt;p&gt;The first fractal is Four Corners, which begins as a large square. As the function calls itself, the fractal's specifications cause four smaller squares to be drawn in the four corners of the square:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Four Corners:&lt;/span&gt;
&lt;span class="n"&gt;drawFractal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drawFilledSquare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;'sizeChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'xChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'yChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'sizeChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'xChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'yChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'sizeChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'xChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'yChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'sizeChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'xChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'yChange'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The call to &lt;code&gt;drawFractal()&lt;/code&gt; here limits the maximum depth to &lt;code&gt;5&lt;/code&gt;, as any more tends to make the fractal so dense that the fine detail becomes hard to see.&lt;/p&gt;

&lt;h2&gt;Example 2 - Spiral Squares&lt;/h2&gt;

&lt;p&gt;&lt;img src="/blogstatic/fractalartmaker-spiralsquares.webp" /&gt;&lt;/p&gt;


&lt;p&gt;The Spiral Squares fractal also starts as a large square, but it creates just one new square on each recursive call:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Spiral Squares:&lt;/span&gt;
&lt;span class="n"&gt;drawFractal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drawFilledSquare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;angleChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This square is slightly smaller and rotated by 7 degrees. The centers of all the squares are unchanged, so there's no need to add &lt;code&gt;xChange&lt;/code&gt; and &lt;code&gt;&lt;code&lt;/&gt;yChange&lt;/code&gt; keys &lt;code&gt;to&lt;/code&gt; the specification. The default maximum depth of &lt;code&gt;8&lt;/code&gt; is &lt;code&gt;too&lt;/code&gt; small to get an interesting fractal, so we increase it to &lt;code&gt;50&lt;/code&gt; to &lt;code&gt;produce&lt;/code&gt; a hypnotic spiral pattern.&lt;/p&gt;

&lt;h2&gt;Example 3 - Double Spiral Squares&lt;/h2&gt;

&lt;p&gt;&lt;img src="/blogstatic/fractalartmaker-doublespiralsquares.webp" /&gt;&lt;/p&gt;


&lt;p&gt;The Double Spiral Squares fractal is similar to Spiral Squares, except each square creates two smaller squares. This creates an interesting fan effect, as the second square is drawn later and tends to cover up previously drawn squares:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Double Spiral Squares:&lt;/span&gt;
&lt;span class="n"&gt;drawFractal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drawFilledSquare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;angleChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;angleChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;}])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The squares are created slightly higher or lower than their previous square and rotated either &lt;code&gt;10&lt;/code&gt; or &lt;code&gt;-10&lt;/code&gt; degrees.&lt;/p&gt;

&lt;h2&gt;Example 4 - Triangle Spiral&lt;/h2&gt;

&lt;p&gt;&lt;img src="/blogstatic/fractalartmaker-trianglespiral.webp" /&gt;&lt;/p&gt;


&lt;p&gt;The Triangle Spiral fractal, another variation of the Spiral Square, uses the &lt;code&gt;drawTriangleOutline()&lt;/code&gt; shape-drawing function instead of &lt;code&gt;drawFilledSquare()&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Triangle Spiral:&lt;/span&gt;
&lt;span class="n"&gt;drawFractal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drawTriangleOutline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;angleChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Unlike the Spiral Square, the Triangle Spiral begins at the small size of &lt;code&gt;20&lt;/code&gt; units and slightly increases in size for each level of recursion. The &lt;code&gt;sizeChange&lt;/code&gt; key is greater than &lt;code&gt;1.0&lt;/code&gt;, so the shapes are always increasing in size.&lt;/p&gt;

&lt;p&gt;This means the base case occurs when the recursion reaches a depth of &lt;code&gt;80&lt;/code&gt;, because the base case of size becoming less than &lt;code&gt;1&lt;/code&gt; is never reached.&lt;/p&gt;

&lt;h2&gt;Example 5 - Conway's Game of Life Glider&lt;/h2&gt;

&lt;p&gt;&lt;img src="/blogstatic/fractalartmaker-glider.webp" /&gt;&lt;/p&gt;


&lt;p&gt;Conway's Game of Life is a famous example of cellular automata. The game's simple rules cause interesting and wildly chaotic patterns to emerge on a two-dimensional grid. One such pattern is a Glider consisting of five cells in a 3 × 3 space:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Conway&amp;#39;s Game of Life Glider:&lt;/span&gt;
&lt;span class="n"&gt;third&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;drawFractal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drawFilledSquare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;xChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;xChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;xChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;third&lt;/span&gt;&lt;span class="p"&gt;}])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The Glider fractal here has additional Gliders drawn inside each of its five cells. The third variable helps precisely set the position of the recursive shapes in the 3 × 3 space.&lt;/p&gt;

&lt;p&gt;You can find a Python implementation of Conway's Game of Life online at &lt;a href="https://inventwithpython.com/bigbookpython/project13.html"&gt;https://inventwithpython.com/bigbookpython/project13.html&lt;/a&gt;. Tragically, John Conway, the mathematician and professor who developed Conway's Game of Life, passed away of complications from COVID-19 in April 2020.&lt;/p&gt;

&lt;h2&gt;Example 6 - Sierpiński Triangle&lt;/h2&gt;

&lt;p&gt;&lt;img src="/blogstatic/fractalartmaker-sierpinskitriangle.webp" /&gt;&lt;/p&gt;


&lt;p&gt;We created the Sierpiński Triangle fractal in Chapter 9, but our Fractal Art Maker can re-create it as well by using the &lt;code&gt;drawTriangleOutline()&lt;/code&gt; shape function. After all, a Sierpiński triangle is an equilateral triangle with three smaller equilateral triangles drawn in its interior:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Sierpinski Triangle:&lt;/span&gt;
&lt;span class="n"&gt;toMid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="n"&gt;drawFractal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drawTriangleOutline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;toMid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;angleChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;toMid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;angleChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;toMid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;angleChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;}])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The center of these smaller triangles is &lt;code&gt;size * math.sqrt(3) / 6&lt;/code&gt; units from the center of the previous triangle. The three calls adjust the heading of the turtle to 0, 120, and 240 degrees before moving up on the turtle's relative y-axis.&lt;/p&gt;

&lt;h2&gt;Example 7 - Wave&lt;/h2&gt;

&lt;p&gt;&lt;img src="/blogstatic/fractalartmaker-wave.webp" /&gt;&lt;/p&gt;


&lt;p&gt;We discussed the Wave fractal at the start of this chapter. This relatively simple fractal creates three smaller and distinct recursive triangles:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Wave:&lt;/span&gt;
&lt;span class="n"&gt;drawFractal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drawTriangleOutline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;xChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;xChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;angleChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;}])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Example 8 - Horn&lt;/h2&gt;

&lt;p&gt;&lt;img src="/blogstatic/fractalartmaker-horn.webp" /&gt;&lt;/p&gt;


&lt;p&gt;The Horn fractal resembles a ram's horn:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Horn:&lt;/span&gt;
&lt;span class="n"&gt;drawFractal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drawFilledSquare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.96&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;angleChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This simple fractal is made up of squares, each of which is slightly smaller, moved up, and rotated 11 degrees from the previous square. We increase the maximum recursion depth to 100 to extend the horn into a tight spiral.&lt;/p&gt;

&lt;h2&gt;Example 9 - Snowflake&lt;/h2&gt;

&lt;p&gt;&lt;img src="/blogstatic/fractalartmaker-snowflake.webp" /&gt;&lt;/p&gt;


&lt;p&gt;The final fractal, Snowflake, is composed of squares laid out in a pentagon pattern. This is similar to the Four Corners fractal, but it uses five evenly spaced recursive squares instead of four:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Snowflake:&lt;/span&gt;
&lt;span class="n"&gt;drawFractal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drawFilledSquare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;144&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;144&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;216&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;216&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;288&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="s1"&gt;&amp;#39;yChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;288&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sizeChange&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;}])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This fractal uses the cosine and sine functions from trigonometry, implemented in Python's &lt;code&gt;math.cos()&lt;/code&gt; and &lt;code&gt;math.sin()&lt;/code&gt; functions, to determine how to shift the squares along the x-axis and y-axis. A full circle has 360 degrees, so to evenly space out the five recursive squares in this circle, we place them at intervals of 0, 72, 144, 216, and 288 degrees. The &lt;code&gt;math.cos()&lt;/code&gt; and &lt;code&gt;math.sin()&lt;/code&gt; functions expect the angle argument to be in radians instead of degrees, so we must multiply these numbers by &lt;code&gt;math.pi / 180.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The end result is that each square is surrounded by five other squares, which are surrounded by five other squares, and so on, to form a crystal-like fractal that resembles a snowflake.&lt;/p&gt;--&gt;
</content><category term="misc"></category></entry><entry><title>How to Use Python 3.11's New TOML Parser, tomllib</title><link href="https://inventwithpython.com/blog/toml-python-guide.html" rel="alternate"></link><published>2022-02-23T12:00:00-05:00</published><updated>2022-02-23T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-02-23:/blog/toml-python-guide.html</id><summary type="html">&lt;p&gt;Python 3.11 will include a TOML parsing module called &lt;code&gt;tomllib&lt;/code&gt;. Let's learn what TOML files are (they're similar to JSON), how they're useful for configuration files, and how you can write Python code to read them.&lt;/p&gt;</summary><content type="html">

&lt;h2&gt;TOML Parser To Be Included In Python 3.11&lt;/h2&gt;

&lt;p&gt;The Python language steering council has accepted PEP 680, which adds a TOML parser to the Python standard library. This means in Python 3.11, you'll be able to &lt;code&gt;import tomllib&lt;/code&gt; and have a module that can parse TOML files.&lt;/p&gt;

&lt;p&gt;But what are TOML files and why is a TOML parser being added to Python? This blog explains what TOML files look like, how they're used, and the Python code that lets you parse them.&lt;/p&gt;

&lt;p&gt;TOML stands for Tom's Obvious, Minimal Language. It is a way to structure data in a text file, much like what &lt;a href="https://en.wikipedia.org/wiki/JSON"&gt;JSON&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/XML"&gt;XML&lt;/a&gt;, and &lt;a href="https://en.wikipedia.org/wiki/YAML"&gt;YAML&lt;/a&gt; files do. The TOML standard is set up so that it's easy for humans to write TOML files in a text editor for use as configuration files. The syntax is very similar to .ini files on Windows. Here's an example TOML file:&lt;/p&gt;

&lt;pre&gt;# My Dungeons &amp; Dragons character.
name = "Mordnilap"
class = "Magic user"
maxHP = 14
currentHP = 2

inventory = ["apple", "enchanted staff", "healing potion"]

cursed = false

[spells]
# The integers represent how many times a day they can be cast.
fireball = 3
featherFall = 2
&lt;/pre&gt;

&lt;p&gt;TOML files are designed to easily and unambiguously be parsed into a dictionary data structure. With the TOML parser that will be in Python 3.11, you'll be able to parse this text and turn into the following Python dictionary:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'class'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Magic user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;'currentHP'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;'cursed'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;'inventory'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'apple'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'enchanted staff'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'healing potion'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="s1"&gt;'maxHP'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Mordnilap'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;'spells'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'featherFall'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'fireball'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Python already has standard library modules for parsing XML and JSON text (with the &lt;code&gt;xml&lt;/code&gt; and &lt;code&gt;json&lt;/code&gt; modules, respectively.) There are third-party modules for parsing TOML files, but Python 3.11 will include one in the standard library so that all Python programs can access it without the additional step of installing a third-party TOML parsing module.&lt;/p&gt;

&lt;p&gt;As you can see in the example TOML file above, TOML is a great format for structuring data for configuration files. It doesn't have same column-and-row format that an Excel spreadsheet or CSV file would use though.&lt;/p&gt;

&lt;p&gt;In Python 3.11, the TOML module will be named &lt;code&gt;tomllib&lt;/code&gt;, since a third-party module named &lt;code&gt;toml&lt;/code&gt; already exists. The TOML module in 3.11 will be based on &lt;a href="https://pypi.org/project/toml/"&gt;the existing &lt;code&gt;toml&lt;/code&gt; third-party module&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Writing Python Code with tomllib&lt;/h2&gt;

&lt;p&gt;You don't have to wait for Python 3.11 to be released to use it today. You can install the third-party &lt;code&gt;toml&lt;/code&gt; package with the pip tool by running &lt;code&gt;pip3 install --user toml&lt;/code&gt; in a Terminal window on macOS and Linux, or by running &lt;code&gt;pip install --user toml&lt;/code&gt; in a Command Prompt window on Windows. (Don't run this from the Python interactive shell; that doesn't work.)&lt;/p&gt;

&lt;p&gt;Much like how the &lt;code&gt;json&lt;/code&gt; module has functions named &lt;code&gt;load()&lt;/code&gt; and &lt;code&gt;loads()&lt;/code&gt; (which is pronounced "load s" or "load string", not "loads"), the &lt;code&gt;toml&lt;/code&gt; third-party library (and future &lt;code&gt;tomllib&lt;/code&gt; module in Python 3.11) has &lt;code&gt;load()&lt;/code&gt; and &lt;code&gt;loads()&lt;/code&gt; functions.&lt;/p&gt;

&lt;p&gt;For example, say we had the Dungeons &amp; Dragons text above in a TOML file named &lt;em&gt;dnd_character.toml&lt;/em&gt;. We could read into our Python program with the following code:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;tomllib&lt;/span&gt;  &lt;span class="c1"&gt;# import tomllib in Python 3.11&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pprint&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dnd_character.toml'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fileObj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileObj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;dnd_char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tomllib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pprint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dnd_char&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The output of this program from the &lt;code&gt;pprint&lt;/code&gt; "pretty print" module is as follows:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'class'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Magic user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;'currentHP'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;'cursed'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;'inventory'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'apple'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'enchanted staff'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'healing potion'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="s1"&gt;'maxHP'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Mordnilap'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;'spells'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'featherFall'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'fireball'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;toml.loads()&lt;/code&gt; function takes a string that we pulled out of a text file named &lt;em&gt;dnd_character.toml&lt;/em&gt;. This string could have come from anywhere (say, downloading it off the web with the &lt;a href="https://docs.python-requests.org/en/latest/"&gt;Requests&lt;/a&gt; module). But since reading it from a &lt;em&gt;.toml&lt;/em&gt; file is so common, the &lt;code&gt;tomllib.load()&lt;/code&gt; function can accept the file object directly:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;tomllib&lt;/span&gt;  &lt;span class="c1"&gt;# import tomllib in Python 3.11&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pprint&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dnd_character.toml'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'rb'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fileObj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;dnd_char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tomllib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileObj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pprint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dnd_char&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that the file object you pass to &lt;code&gt;tomllib.load()&lt;/code&gt; has to be open in "read binary" mode, and not just the default "read text" mode, so you need to pass &lt;code&gt;'rb'&lt;/code&gt; as the second argument to the &lt;code&gt;open()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Unlike the &lt;code&gt;json&lt;/code&gt; module's &lt;code&gt;dumps()&lt;/code&gt; function, there is no &lt;code&gt;tomllib.dumps()&lt;/code&gt; function that can &lt;em&gt;create&lt;/em&gt; text for TOML files. This is because while JSON is meant to be human-readable and read and written by programs, TOML files are meant to be used for configuration files. These files are often read and written by humans, but only read by software. There is a third-party module called &lt;code&gt;toml-w&lt;/code&gt; that has a &lt;code&gt;tomli-w.dumps()&lt;/code&gt; function for writing TOML-structured text. The &lt;code&gt;dumps()&lt;/code&gt; function won't be a part of the &lt;code&gt;tomllib&lt;/code&gt; module in the Python 3.11 standard library though.&lt;/p&gt;

&lt;h2&gt;Writing TOML Files in a Text Editor&lt;/h2&gt;

&lt;p&gt;We've covered how your Python programs can read in TOML files. But when you write TOML by hand, you'll need to know the syntax of the TOML format. Fortunately, Tom's Obvious, Minimal Language has an obvious, minimal format that's easy to remember. (For full details, you can read the &lt;a href="https://toml.io/en/v1.0.0"&gt;documentation on the TOML website&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;TOML files can have &lt;strong&gt;comments&lt;/strong&gt; which are ignored by parsers. They look like Python comments: beginning with a &lt;code&gt;#&lt;/code&gt; and extending to the end of the line. Blank lines are also allowed and ignored in TOML files.&lt;/p&gt;

&lt;p&gt;The basic &lt;strong&gt;key-value pairs&lt;/strong&gt; are written like Python assignment statements, with the key and value separated by an equal sign:&lt;/p&gt;

&lt;pre&gt;name = "Mordnilap"
class = "Magic user"
maxHP = 14
currentHP = 2&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;tomllib.loads()&lt;/code&gt; function takes the above TOML and returns the following Python dictionary:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Mordnilap'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'class'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Magic user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'maxHP'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'currentHP'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;TOML has a few standard &lt;strong&gt;data types&lt;/strong&gt; common to most programming languages, such as strings enclosed in single or double quotes (like &lt;code&gt;'Mordnilap'&lt;/code&gt;) and integers (like &lt;code&gt;14&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;TOML also has floating point numbers with decimal points (like &lt;code&gt;3.1415&lt;/code&gt; or &lt;code&gt;-42.0&lt;/code&gt;) and lowercase Boolean values &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt;. Note that &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; are written differently in TOML than Python's &lt;code&gt;True&lt;/code&gt; and &lt;code&gt;False&lt;/code&gt;. TOML isn't a Python-specific format, but a general format that can be parsed by many programming languages. In this case, the lowercase &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; is similar to JavaScript and JSON's way of writing Boolean values.&lt;/p&gt;

&lt;p&gt;Dates and times are also first-class values in TOML; they have a specific format (detailed in &lt;a href="https://tools.ietf.org/html/rfc3339"&gt;RFC 3339&lt;/a&gt;) and you don't need to write them as strings enclosed in quotes. There are a few different ways of writing them. For dat and timestamps that include the time zone, you can use the &lt;code&gt;Z&lt;/code&gt; suffix to mean UTC. Or you can specify an offset from UTC:&lt;/p&gt;

&lt;pre&gt;odt1 = 1979-05-27T07:32:00Z  # UTC time zone
odt2 = 1979-05-27T00:32:00-07:00  # UTC minus 7 hours&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;tomllib.loads()&lt;/code&gt; function takes the above TOML and returns the following Python dictionary:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'odt1'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1979&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tzinfo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="s1"&gt;'odt2'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1979&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tzinfo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;61200&lt;/span&gt;&lt;span class="p"&gt;)))}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can also have arrays in TOML, such as our example's &lt;code&gt;inventory = ["apple", "enchanted staff", "healing potion"]&lt;/code&gt; producing the Python dictionary &lt;code&gt;{"inventory": ["apple", "enchanted staff", "healing potion"]}&lt;/code&gt;. Arrays can also include other arrays.&lt;/p&gt;

&lt;p&gt;Instead of dictionaries, TOML has &lt;strong&gt;tables&lt;/strong&gt; which can be formed under a square-bracketed table headers:&lt;/p&gt;

&lt;pre&gt;[Mordnilap]
class = "Magic user"
maxHP = 14
currentHP = 2

[Bilbo]
class = "Fighter"
maxHP = 16
currentHP = 16&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;tomllib.loads()&lt;/code&gt; function takes the above TOML and returns the following Python dictionary:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'Bilbo'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'class'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Fighter'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'currentHP'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'maxHP'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="s1"&gt;'Mordnilap'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'class'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Magic user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'currentHP'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'maxHP'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can also use &lt;strong&gt;dotted notation&lt;/strong&gt; to create these dictionaries. The following TOML produces an identical dictionary to the previous example:&lt;/p&gt;

&lt;pre&gt;Mordnilap.class = "Magic user"
Mordnilap.maxHP = 14
Mordnilap.currentHP = 2

Bilbo.class = "Fighter"
Bilbo.maxHP = 16
Bilbo.currentHP = 16&lt;/pre&gt;

&lt;p&gt;But you can also use more compact &lt;strong&gt;inline tables&lt;/strong&gt; in TOML:&lt;/p&gt;

&lt;pre&gt;Mordnilap = { "class" = "Magic user", "maxHP" = 14, "currentHP" = 2 }
Bilbo = { "class" = "Fighter", "maxHP" = 16, "currentHP" = 16 }&lt;/pre&gt;

&lt;p&gt;There's several other aspects to the TOML syntax which you can read in the TOML documentation, but I hope this blog post has given you a good introduction to this helpful tool that will be part of the Python standard library as the &lt;code&gt;tomllib&lt;/code&gt; module as of version 3.11.&lt;/p&gt;


</content><category term="misc"></category></entry><entry><title>Downloading Web Pages and Files in Python 3 and 2 Without the Requests Module</title><link href="https://inventwithpython.com/blog/downloading-web-pages-without-requests.html" rel="alternate"></link><published>2022-02-21T12:00:00-05:00</published><updated>2022-02-21T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-02-21:/blog/downloading-web-pages-without-requests.html</id><summary type="html">How to download web pages and files off the internet only using Python 3 and 2's standard library.</summary><content type="html">

&lt;p&gt;I created a Python module called &lt;a href="https://pypi.org/project/WhatIsMyIP/"&gt;whatismyip&lt;/a&gt; that allows Python programs to easily figure out what their internet protocol (IP) address is. It works by connecting to one of several public websites that return this information, such as &lt;a href="https://icanhazip.com/"&gt;https://icanhazip.com/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Because it was a module to be included in other programs, I wanted it to have as few dependencies as possible. Normally I would use the &lt;a href="https://pypi.org/project/requests/"&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/a&gt; module to download these web pages, but I wanted to stick to just the Python standard library. Here's how I used only the standard library on Python 3 and 2 to download the HTML of a webpage:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Python 3&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;urllib.request&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urlopen&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Python 2&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;urllib2&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urlopen&lt;/span&gt;

&lt;span class="c1"&gt;# Supply a user-agent header of a common browser, since some web servers will refuse to reply to scripts without one.&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;#39;https://ifconfig.me&amp;#39; is a website that returns simple info about your request. Replace this with the page you want to download.&lt;/span&gt;
&lt;span class="n"&gt;requestObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://ifconfig.me/all&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;User-agent&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;responseObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestObj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# To figure out how to decode the downloaded binary data to text, we need to get the character set encoding:&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Python 3&lt;/span&gt;
    &lt;span class="n"&gt;charsets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;responseObj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_charsets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;charsets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;charsets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Character set encoding could not be determined.&lt;/span&gt;
        &lt;span class="n"&gt;charset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;  &lt;span class="c1"&gt;# Use the utf-8 encoding by default.&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Use the first character set encoding listed. (It&amp;#39;s often the only one.)&lt;/span&gt;
        &lt;span class="n"&gt;charset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;charsets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Python 2&lt;/span&gt;
    &lt;span class="n"&gt;charset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;responseObj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getencoding&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;charset&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;7bit&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Even though getencoding() returns &amp;#39;7bit&amp;#39;, this is an unknown encoding to decode(). &amp;#39;7bit&amp;#39; is the same as &amp;#39;ascii&amp;#39;&lt;/span&gt;
        &lt;span class="n"&gt;charset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ascii&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;responseObj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;charset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# The HTML of the web page.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And here is code for downloading a binary file (such as a &lt;em&gt;.png&lt;/em&gt; image or &lt;em&gt;.zip&lt;/em&gt; file) from a URL:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Python 3&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;urllib.request&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urlopen&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Python 2&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;urllib2&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urlopen&lt;/span&gt;

&lt;span class="c1"&gt;# Replace https://inventwithpython.com/images/cover_automate2_thumb.jpg with the file you want to download.&lt;/span&gt;
&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;https://inventwithpython.com/images/cover_automate2_thumb.jpg&amp;#39;&lt;/span&gt;

&lt;span class="c1"&gt;# Supply a user-agent header of a common browser, since some web servers will refuse to reply to scripts without one.&lt;/span&gt;
&lt;span class="n"&gt;requestObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;User-agent&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;responseObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestObj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;responseObj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Replace foo.jpg with the local filename you want to use:&lt;/span&gt;
&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Use the filename from the url.&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;wb&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fileObj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;fileObj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;urllib&lt;/code&gt; module in Python 2 was the original downloading module in the Python standard library added in Python 1.2. The &lt;code&gt;urllib2&lt;/code&gt; module in Python 2 had additional features and was added in Python 1.6. In Python 3, there is a new module just called &lt;code&gt;urllib&lt;/code&gt;. There are also third-party modules named &lt;a href="https://pypi.org/project/urllib3/"&gt;&lt;code&gt;urllib3&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://pypi.org/project/requests/"&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/a&gt; (which uses &lt;code&gt;urllib3&lt;/code&gt;) but these aren't in the Python standard library nor will they be added to it.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>I Like Al Sweigart's Books, What Can I Read to Get Into Game Programming?</title><link href="https://inventwithpython.com/blog/get-into-game-programming.html" rel="alternate"></link><published>2022-02-18T12:00:00-05:00</published><updated>2022-02-18T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-02-18:/blog/get-into-game-programming.html</id><summary type="html">If you like my programming books, here's a few more resources you can use to get into video game development.</summary><content type="html">

&lt;p&gt;I got an email from a reader:&lt;/p&gt;

&lt;blockquote&gt;Sir I am very much happy with your book. Can you please tell me other books so that I can learn how to make larger games in other programming languages?&lt;/blockquote&gt;

&lt;p&gt;First I recommend my own free books, since I've written a few different ones:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://inventwithpython.com/invent4thed/"&gt;Invent Your Own Computer Games with Python&lt;/a&gt; - For beginners with no programming experience. This book goes into simple text-based games and has a brief introduction to 2D graphics with the Pygame library.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://inventwithpython.com/pygame/"&gt;Make Your Own Games with Python &amp; Pygame&lt;/a&gt; - For those with basic Python experience, this book has several example game programs with 2D graphics.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/"&gt;The Big Book of Small Python Projects&lt;/a&gt; - For those with basic to no Python experience, this book has 81 small programming projects (mostly games, some simulation and digital art projects).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://www.reddit.com/r/learnprogramming/"&gt;/r/learnprogramming subreddit&lt;/a&gt; has an &lt;a href="https://www.reddit.com/r/learnprogramming/wiki/faq#wiki_how_can_i_get_started_making_video_games.3F"&gt;FAQ (Frequently Asked Questions) section on how to get started making video games&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Beyond these basic Python books, learn the Unity game engine (and the C# programming language) or the Godot game engine (and the Python-like GDScript language, though the engine also works with C#, C++, or Rust). Both of these game engines let you make games with 2D and 3D graphics for Windows, Linux, and macOS. But you can also make games for web browsers and mobile platforms like Android and iOS (iPhones). You can write your game once and create programs for all of these platforms. Unity also has the ability to make VR and AR games.&lt;/p&gt;

&lt;p&gt;Of the two, I recommend beginners look at Godot first because it is free and simpler than the Unity ecosystem. I've looked into both of these engines a little, but I don't do development in either of these engines, nor in C# or GDScript. But here are some resources I've found:&lt;/p&gt;

&lt;p&gt;For Unity, first watch the 18-minute video &lt;a href="https://www.youtube.com/watch?v=vFjXKOXdgGo"&gt;"How I learned Unity without following tutorials"&lt;/a&gt; from the excellent YouTube channel &lt;a href="https://www.youtube.com/channel/UCqJ-Xo29CKyLTjn6z2XwYAw"&gt;Game Maker's Toolkit&lt;/a&gt;. No matter what languages or engines you learn, you should watch the GMTK videos as they're great explanations of video game design principles.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://unity.com/download"&gt;download Unity for free&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For Godot, first read through the &lt;a href="https://docs.godotengine.org/en/latest/about/faq.html"&gt;Godot FAQ&lt;/a&gt;. The &lt;a href="https://www.reddit.com/r/godot/"&gt;/r/godot&lt;/a&gt; subreddit is also good place to ask questions.&lt;/p&gt;

&lt;p&gt;I recommend reading my Python books on simple projects and then working in Godot. It's easy to get carried away with ideas for a grand video game, but remember that the major professional video games can have budgets over $100 million. These video games are just like major movie productions. You should aim for making simple games first, even ones that you couldn't sell. It's much better to have a dozen completed small projects than one never-finished large project. Good luck!&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>What Kind of Computer Do I Need to Learn Programming?</title><link href="https://inventwithpython.com/blog/what-kind-of-computer-do-i-need-to-learn-programming.html" rel="alternate"></link><published>2022-02-03T12:00:00-05:00</published><updated>2022-02-03T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-02-03:/blog/what-kind-of-computer-do-i-need-to-learn-programming.html</id><summary type="html">&lt;p&gt;You don't need to buy a new computer to learn programming. It's a common misconception that you need a powerful computer to program. This guide will give you some hints to guide you in the right direction.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;You don't need to buy a new computer to learn programming. It's a common misconception that you need a powerful computer to program. This guide will give you some hints to guide you in the right direction.&lt;/p&gt;

&lt;h2&gt;Desktop, Laptop, or Mobile&lt;/h2&gt;

&lt;p&gt;Laptops are probably a better option, as you'll be able to take them with you to libraries, classes, or while you travel. Desktops are cheaper and less fragile in general. One thing to keep in mind if you want to learn to program is that mobile devices such as a tablet or especially a smartphone aren't ideal to learn to program on. I've never met a professional software developer who programs on a tablet or smartphone. The lack of a real keyboard and the small screen size is a large hinderance. Even developers who write mobile apps do so on a laptop or desktop.&lt;/p&gt;

&lt;h2&gt;Operating System&lt;/h2&gt;

&lt;p&gt;Windows, macOS, or Linux are fine options for developing software. Any of these platforms can be used for web app development. Though if you want to write desktop applications for a particular operating system, you should program on that operating system: write Windows apps on a Windows machine, write macOS apps on a macOS machine, write Linux apps on a Linux machine.&lt;/p&gt;

&lt;p&gt;If you want to do mobile app development, you can use any of these three operating systems for Android development, but it's best to use a macOS machine to write iOS apps for iPhones.&lt;/p&gt;

&lt;p&gt;If you want to create video games, any operating system is fine but Windows has the largest share of gamers (especially games from indie or large corporate game companies).&lt;/p&gt;

&lt;p&gt;Windows 7 or later is fine for programming. Windows XP is, at this point, may give you difficulty finding compatible software for.&lt;/p&gt;

&lt;h2&gt;Buying Used Computers&lt;/h2&gt;

&lt;p&gt;Here's a few used computers I found on &lt;a href="https://craigslist.org"&gt;craigslist&lt;/a&gt; or eBay with specs that are more than powerful enough for programming:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;"Dell OptiPlex 9010 i5 4GB 250GB Desktop SFF Tower PC w/Keyboard, Mouse" $99&lt;/li&gt;
    &lt;li&gt;"Old Win7 desktop (500gb hd/4gb ram/intel E5400 @2.7ghz x2 cpu)" $45&lt;/li&gt;
    &lt;li&gt;"HP Pavilion a6457c desktop with Acer monitor" $45&lt;/li&gt;
    &lt;li&gt;" HP 15 Laptop 250GB SSD Core i5 12GB RAM" $240&lt;/li&gt;
    &lt;li&gt;"Lenovo 15.6" Laptop Intel Core i3, 8GB RAM, 500GB SSD" $230&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any of these machines are more than powerful enough for programming. Working desktops can easily be found for $100 or less. Laptops are a bit more expensive, but workable laptops can be had for under $300.&lt;/p&gt;

&lt;p&gt;Keep in mind that you should buy working computers. If a computer is being sold for cheap because it's missing a hard drive, memory, or some other component, it's unlikely to be worth your time to find a compatible part for it. Make sure to test the computer before accepting it. The computer should be able to turn on and let you, say, open a web browser or some other basic task. The main component of a used laptop that is likely to be faulty is the battery, which might not be able to keep charge for very long. And ideally, you should reinstall the operating system on the computer to wipe any previous malware or viruses that are inadvertantly installed.&lt;/p&gt;

&lt;p&gt;For desktops, you'll need a monitor. Having a larger monitor makes a desktop far more useful than a slightly faster CPU or a little bit more memory. You can also get used monitors, but check for any "dead pixels" by opening a "dead pixel test" website &lt;a href="http://www.whitedisplay.com/"&gt;such as this one&lt;/a&gt; and put the browser into full screen mode (often by pressing F11). Having a second monitor for your desktop or laptop is also a great help to your productivity, but make sure there is a second monitor port on the desktop to plug the second monitor into. Almost every laptop will have an additional monitor port to plug a second monitor into while you use the laptop's connected screen as the first monitor.&lt;/p&gt;

&lt;p&gt;Every computer since around 2007 is a 64-bit computer. It's incredibly unlikely you'll find a still-functioning 32-bit computer, but before buying a very old machine make sure it is a 64-bit computer or else you may have problems running modern software on it.&lt;/p&gt;

&lt;h2&gt;More Information&lt;/h2&gt;

&lt;p&gt;The r/learnprogramming subreddit has an FAQ with additional information for &lt;a href="https://www.reddit.com/r/learnprogramming/wiki/faq#wiki_what_kind_of_operating_system_should_i_use.3F"&gt;what kind of operating system&lt;/a&gt; and &lt;a href="https://www.reddit.com/r/learnprogramming/wiki/faq#wiki_what_kind_of_computer.2Flaptop_should_i_get.3F"&gt;what kind of computer&lt;/a&gt; you should get for programming.&lt;/p&gt;

&lt;p&gt;If you are getting a Windows computer, I highly recommend checking out the &lt;a href="https://ninite.com/"&gt;Ninite.com website&lt;/a&gt;, which provides you with a no-hassle installer to install several common and free Windows applications.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Book Review: Make Python Talk</title><link href="https://inventwithpython.com/blog/book-review-make-python-talk.html" rel="alternate"></link><published>2022-01-13T12:02:00-05:00</published><updated>2022-01-13T12:02:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-01-13:/blog/book-review-make-python-talk.html</id><summary type="html">&lt;p&gt;Mark H. Liu’s “Make Python Talk” is a solid book for anyone who wants to leverage the power of the Python programming language to add speech capabilities to their programs. The chapters cover third-party libraries for speech recognition and text-to-speech in an engaging way.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;&lt;a href="https://nostarch.com/make-python-talk"&gt;Ebook free with print book purchase from No Starch Press.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mark H. Liu’s “Make Python Talk” is a solid book for anyone who wants to leverage the power of the Python programming language to add speech capabilities to their programs. The chapters cover third-party libraries for speech recognition and text-to-speech in an engaging way. This book is more than a dry reference for these libraries, but also contains several example projects demonstrating how you can use these speech features in a variety of contexts. You don’t have to have computer science expertise to add these advanced features to your own projects; Liu’s book presents these speech software libraries with clarity and ease.&lt;/p&gt;

&lt;p&gt;If you’ve stumbled through the documentation for Python’s speech libraries but are still unsure of how to actually make effective use of them, I recommend you give Make Python Talk a look.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.amazon.com/gp/customer-reviews/R3GRYC00PS1A1T"&gt;Amazon review link&lt;/a&gt;&lt;/p&gt;


</content><category term="misc"></category></entry><entry><title>Book Review: Serious Python</title><link href="https://inventwithpython.com/blog/book-review-serious-python.html" rel="alternate"></link><published>2022-01-13T12:01:00-05:00</published><updated>2022-01-13T12:01:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-01-13:/blog/book-review-serious-python.html</id><summary type="html">&lt;p&gt;Serious Python by Julien Danjou is an excellent book for those who have gone through the "hello world" tutorials or those coming to Python from other programming languages, and want a book that teaches you not just how to write code, but how to write code the pythonic way.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;&lt;a href="https://nostarch.com/seriouspython"&gt;Ebook free with print book purchase from No Starch Press.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Serious Python by Julien Danjou is an excellent book for those who have gone through the "hello world" tutorials or those coming to Python from other programming languages, and want a book that teaches you not just how to write code, but how to write code the pythonic way.&lt;/p&gt;

&lt;p&gt;You won't find chapters on mere syntax in this book: it expects that you've already covered the basics and want to move your Python skills to the next level. This covers a lot of ground that other Python books don't, and I'm glad I have it on my digital bookshelf.&lt;/p&gt;

&lt;p&gt;Chapter 1 covers laying out the files and folders for your Python project. This chapter was great, even for an experienced software developer like myself. I always threw everything into "src" and "docs" folders, but didn't quite understand how Python projects were formatted and, more importantly, why. And while I had heard about semantic versioning before, this book pointed out a couple things that I've been doing wrong with my version numbering for my entire career.&lt;/p&gt;

&lt;p&gt;Chapter 2 covers modules and importing, which is especially helpful from other languages that have similar, but distinct, ways of importing code.&lt;/p&gt;

&lt;p&gt;Chapter 3 covers not just documentation, but Python-specific documentation: the Sphinx tool, ReStructured Text, Sphinx extensions like autodoc and autosummary, and the warnings module. These are things I had been ignorant about for years while I was writing Python code, and it's great to find all this information in one place.&lt;/p&gt;

&lt;p&gt;Chapter 4 covers timestamps and time zones, and this is not just important Python information, but important programming information. Most programmers only learn about basic time/date-related functions and stop there. Time zones and the Olsen database and how to *properly* handle them are often ignored, resulting in subtle and hard-to-debug problems in software. Chapter 4 brings all of these issues to light, along with easy-to-miss Python modules that make working with dates and timezones manageable.&lt;/p&gt;

&lt;p&gt;Chapter 5 covers packaging and distributing Python code, which is especially complex given Python's history of various and overlapping package management tools. I wish there had been more information in this chapter, but it does act as a good introduction to a topic that you'd ordinarily only find out about piecemeal over a career.&lt;/p&gt;

&lt;p&gt;Chapter 6 covers unit testing (with pytest), fixtures, and mocks. It also covers code coverage.py tool for measuring the effectiveness of your unit tests.&lt;/p&gt;

&lt;p&gt;Chapter 7 covers decorators and static/class/abstract methods. This was a great chapter for me in particular, because it's a topic that I've been getting by on by copy/pasting decorator code from other projects and, while that worked, I didn't have a full understanding of *why* the code worked until I read this chapter. The same applies to static and class methods in Python, which are easy to misuse.&lt;/p&gt;

&lt;p&gt;Chapter 8 covers functional programming concepts. While Python isn't a functional programming language, it does have many features that support (and benefit from) writing code in a functional style. For anyone who doesn't know about functional programming, or was burned before when trying to learn the topic, this is a must-read chapter.&lt;/p&gt;

&lt;p&gt;Chapter 9 is the only chapter that I feel like isn't absolutely required reading. It's nice to have a chapter on abstract syntax trees (especially since Python comes with the ast module to help navigate Python code), but it isn't relevant to most programmers. Still, I'd rather read this chapter than try to find out about the topic from some random web tutoiral.&lt;/p&gt;

&lt;p&gt;Chapter 10 covers performance and optimization, explaining not just the cProfile module but also the dis module for disassembling Python bytecode for analysis. While this might not be directly necessary for most performance improvements, it does give a good introduction to the idea that Python bytecode isn't mystical at all; and you can directly compare how different bits of Python code are compiled to bytecode. There's also a hefty list of need-to-know, Python-specific tricks to writing faster code.&lt;/p&gt;

&lt;p&gt;Chapter 11 and 12 cover process and databases, respectively. I wish these chapters were longer, but like many of the short sections, they provide a good introduction to several concepts that many programmers are unaware of, so they are worth a read-through.&lt;/p&gt;

&lt;p&gt;Chapter 13 covers a few miscellaneous topics that don't quite fit in any other chapter, but I'm glad he covers them. Context managers, unicode, data classes (using the attrs module), and Python 2/3 support are definitely important to have in your Python toolbox.&lt;/p&gt;

&lt;p&gt;This book could easily have been ten times its current length, but I like that it is to the point and it does a great job guiding the reader through the most important topics that aren't always covered by other books. "Serious Python" is a book you need to read if you're serious about following mature processes and being a capable Python programmer. It's filled with practical information that's normally scattered across web tutorials, conference talks, and Stack Overflow posts, but has been compiled here into one an excellent book. 5 stars.&lt;/p&gt;

&lt;p&gt;(Full disclosure: I'm an author for No Starch Press, but No Starch Press didn't ask me to write this review and I purchased a copy on my own.) &lt;/p&gt;

&lt;a href="https://www.amazon.com/gp/customer-reviews/R3V7BB4QUGRAGT"&gt;Amazon review link&lt;/a&gt;

</content><category term="misc"></category></entry><entry><title>Book Review: Tiny Python Projects</title><link href="https://inventwithpython.com/blog/book-review-tiny-python-projects.html" rel="alternate"></link><published>2022-01-13T12:00:00-05:00</published><updated>2022-01-13T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2022-01-13:/blog/book-review-tiny-python-projects.html</id><summary type="html">&lt;p&gt;Writing for an intermediate audience is hard, but Ken Youens-Clark has nailed it with Tiny Python Projects. This is a great book for those who have dabbled with a few beginner Python tutorials but still feel like they don't "get" programming. The projects in this book are short and simple; perfect for beginners who want to get experience with actual programs instead of just code snippets.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;Writing for an intermediate audience is hard, but Ken Youens-Clark has nailed it with Tiny Python Projects. This is a great book for those who have dabbled with a few beginner Python tutorials but still feel like they don't "get" programming. The projects in this book are short and simple; perfect for beginners who want to get experience with actual programs instead of just code snippets.&lt;/p&gt;

&lt;p&gt;The book covers many classic programs: Tic Tac Toe, Mad Libs, 99 Bottles of Beer, a password generator, and more. Youens-Clark uses these projects to discuss several intermediate and advance topics such as regular expressions, file i/o, random number generation, unit tests, ASCII values, parsing CSV files, and several others. I recommend this book for anyone who has been wanting to level up their coding skills, or even for beginners who aren't afraid to dive right into programming.&lt;/p&gt;

&lt;a href="https://www.amazon.com/gp/customer-reviews/R33GC032PETWC4"&gt;Amazon review link&lt;/a&gt;


</content><category term="misc"></category></entry><entry><title>How to Represent a 2D Grid in Python Code</title><link href="https://inventwithpython.com/blog/represent-a-2d-grid-in-python.html" rel="alternate"></link><published>2021-12-28T12:00:00-05:00</published><updated>2021-12-28T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-12-28:/blog/represent-a-2d-grid-in-python.html</id><summary type="html">&lt;p&gt;This blog post examines different ways that Python lists and dictionaries can be used to represent a 2D data structure. I also write some test programs to measure the performance of each data structure.&lt;/p&gt;</summary><content type="html">

&lt;h2&gt;Introduction&lt;/h2&gt;

&lt;p&gt;This blog post examines different ways that Python lists and dictionaries can be used to represent a 2D data structure. I also write some test programs to measure the performance of each data structure.&lt;/p&gt;

&lt;p&gt;A two-dimensional or 2D grid is used in a variety of applications. Think of chess boards, top-down video games, spreadsheets, &lt;a href="https://inventwithpython.com/bigbookpython/project13.html"&gt;Conway's Game of Life simulation&lt;/a&gt; are all examples of data that is stored in a two-dimensional grid. We can use a Cartesian coordinate system to create unique "addresses" for each item in the grid. The &lt;em&gt;x coordinate&lt;/em&gt; is the horizontal address and the &lt;em&gt;y coordinate&lt;/em&gt; is the vertical address.&lt;/p&gt;

&lt;p&gt;The Cartesian coordinate system in programming is different from the one you may have learned about in math class. The origin (that is, the (0, 0) coordinate) is in the top-left corner of the screen, and while the &lt;em&gt;x&lt;/em&gt; coordinates increase going to the right as in mathematics, the &lt;em&gt;y&lt;/em&gt; coordinates increase going down rather than increase going up. In a spreadsheet program like Excel, the &lt;em&gt;x&lt;/em&gt; coordinates may be represented by letters instead of numbers, but we'll use numbers for both the &lt;em&gt;x&lt;/em&gt; and &lt;em&gt;y&lt;/em&gt; coordinates. When listed together, the &lt;em&gt;x&lt;/em&gt; coordinate comes first. In the coordinates (2, -5), 2 is the &lt;em&gt;x&lt;/em&gt; coordinate and -5 is the &lt;em&gt;y&lt;/em&gt; coordinate.&lt;/p&gt;

&lt;p&gt;As an aside, here's a list of Python projects that utilize a 2D data structure that come from my free book, &lt;a href=""&gt;The Big Book of Small Python Projects&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project13.html"&gt;Project #13 - Conway's Game of Life&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project23.html"&gt;Project #23 - Etching Drawer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project37.html"&gt;Project #37 - Hungry Robots&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project39.html"&gt;Project #39 - Langton's Ants&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project44.html"&gt;Project #44 - Maze Runner 2D&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project73.html"&gt;Project #73 - Sudoku Puzzle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project79.html"&gt;Project #79 - 2048&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;The 2D Data Structures&lt;/h2&gt;

&lt;p&gt;By "2D data structure" I mean a data structure that contains other values the way that lists and dictionaries contain other values. While the data in lists can be accessed by an integer index and the data in dictionaries can be accessed by a key value, the data in our 2D data structures will be accessed by two integers: the &lt;em&gt;x&lt;/em&gt; and &lt;em&gt;y&lt;/em&gt; coordinates. &lt;/p&gt;

&lt;p&gt;I'll be comparing three different data structures in this blog post:&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;A "1D list", where the data is stored in a Python list. The data at the coordinates (&lt;em&gt;x&lt;/em&gt;, &lt;em&gt;y&lt;/em&gt;) are stored at the index &lt;code&gt;grid[y * WIDTH + x]&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;A "2D list", where the data is stored in a Python list of lists. The data at the coordinates (&lt;em&gt;x&lt;/em&gt;, &lt;em&gt;y&lt;/em&gt;) are stored at &lt;code&gt;grid[x][y]&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;A dictionary, where the data is stored in... a Python dictioanry. The data at the coordiantes (&lt;em&gt;x&lt;/em&gt;, &lt;em&gt;y&lt;/em&gt;) are stored at &lt;code&gt;grid[(x, y)]&lt;/code&gt;. (We can just specify &lt;code&gt;grid[x, y]&lt;/code&gt; and Python will automaticall interpret this as a tuple containing &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are a few advantages and disadvantages that I can see off the top of my head:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;The 1D list and 2d list must have a fixed width and height. The dictionary can store data at any arbitrary coordinates.&lt;/li&gt;
    &lt;li&gt;The 1D list and 2d list use the same full amount of memory no matter how empty or full they are. The dictionary only uses up as much memory as it contains data.&lt;/li&gt;
    &lt;li&gt;The 2D lists can be tricky to work with, especially mixing the x and y coordinates with each other.&lt;/li&gt;
    &lt;li&gt;This is conjecture, but I think that as the dictionary becomes full, it uses up more memory than the 1D or 2D lists.&lt;/li&gt;
    &lt;li&gt;This is conjectue, but I think the dictionary might be slower than the lists at accessing and storing data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Performance Tests&lt;/p&gt;

&lt;p&gt;Without going into the specifics of Big O algorithm analysis (which you can learn about in &lt;a href="https://inventwithpython.com/beyond/chapter13.html"&gt;Chapter 13 of my free book, Beyond the Basic Stuff with Python&lt;/a&gt;), accessing and storing data is a &lt;em&gt;constant&lt;/em&gt; time operation for lists, lists of lists, and dictionaries. This means that it generally doesn't take longer to access or store data in lists or dictionaries as they fill up with data.&lt;/p&gt;

&lt;p&gt;However, I'm more interested in the specific performance metrics of these as well as the memory usage. I'm going to write tests to measure these for these three different approaches to storing data in a grid. You can download and run these tests yourself on your computer. I'm running them with Python 3.10.0 on my T480s Thinkpad laptop running Windows 10.&lt;/p&gt;

&lt;p&gt;I use Python's &lt;code&gt;timeit&lt;/code&gt; module to measure the performance of the test code. You can also learn about this module in &lt;a href="https://inventwithpython.com/beyond/chapter13.html"&gt;Beyond the Basic Stuff with Python&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's the &lt;a href="https://inventwithpython.com/blogstatic/gridtest.py"&gt;gridtest.py&lt;/a&gt; program I wrote to measure the runtime speed and memory usage of these three 2D grid data structures. I put the output I got next to its respective &lt;code&gt;print()&lt;/code&gt; call:&lt;/p&gt;

&lt;p&gt;&lt;textarea rows="20" style="width: 100%"&gt;
import timeit
from sys import getsizeof, stderr
from itertools import chain
from collections import deque

# These constants are the size of the grid used in the tests:
WIDTH = 150
HEIGHT = 50

# Function to determine memory usage from https://code.activestate.com/recipes/577504-compute-memory-footprint-of-an-object-and-its-cont/?in=user-178123
def memoryUsage(o, handlers={}, verbose=False):
    dict_handler = lambda d: chain.from_iterable(d.items())
    all_handlers = {tuple: iter,
                    list: iter,
                    deque: iter,
                    dict: dict_handler,
                    set: iter,
                    frozenset: iter,
                   }
    all_handlers.update(handlers)     # user handlers take precedence
    seen = set()                      # track which object id's have already been seen
    default_size = getsizeof(0)       # estimate sizeof object without __sizeof__

    def sizeof(o):
        if id(o) in seen:       # do not double count the same object
            return 0
        seen.add(id(o))
        s = getsizeof(o, default_size)

        for typ, handler in all_handlers.items():
            if isinstance(o, typ):
                s += sum(map(sizeof, handler(o)))
                break
        return s

    return sizeof(o)


def createAndFillDict():
    # Create a 2D grid from scratch using a dictionary and completely fill it with data.
    dictGrid = {}
    for x in range(WIDTH):
        for y in range(HEIGHT):
            dictGrid[(x, y)] = 'A'
    return dictGrid


def createAndFillDictComp():
    # Create a 2D grid from scratch using a dictionary comprehension and completely fill it with data.
    return {(x, y): 'A' for x in range(WIDTH) for y in range(HEIGHT)}


def createAndFill1DList():
    # Create a 2D grid from scratch using a list and completely fill it with data.
    list1DGrid = []
    for i in range(WIDTH * HEIGHT):
        list1DGrid.append('A')
    return list1DGrid


def createAndFill1DListComp():
    # Create a 2D grid from scratch using a list comprehension and completely fill it with data.
    return ['A' for i in range(WIDTH * HEIGHT)]


def createAndFill2DList():
    # Create a 2D grid from scratch using a list of lists and completely fill it with data.
    list2DGrid = []
    for x in range(WIDTH):
        list2DGrid.append([])
        for y in range(HEIGHT):
            list2DGrid[-1].append('A')
    return list2DGrid


def createAndFill2DListComp():
    # Create a 2D grid from scratch using a list comprehension of list comprehensions and completely fill it with data.
    list2DGrid = [['A' for y in range(HEIGHT)] for x in range(WIDTH)]
    return list2DGrid


def read1DList(grid):
    # Read every coordinate in the list 2D grid.
    for x in range(WIDTH):
        for y in range(HEIGHT):
            data = grid[y * WIDTH + x]


def read2DList(grid):
    # Read every coordinate in the list of lists 2D grid.
    for x in range(WIDTH):
        for y in range(HEIGHT):
            data = grid[x][y]


def readDict(grid):
    # Read every coordinate in the dictionary 2D grid.
    for x in range(WIDTH):
        for y in range(HEIGHT):
            data = grid[x, y]


def write1DList(grid):
    # Write to every coordinate in the list 2D grid.
    for x in range(WIDTH):
        for y in range(HEIGHT):
            grid[y * WIDTH + x] = 'A'


def write2DList(grid):
    # Write to every coordinate in the list to lists 2D grid.
    for x in range(WIDTH):
        for y in range(HEIGHT):
            grid[x][y] = 'A'


def writeDict(grid):
    # Write to every coordinate in the dictionary 2D grid.
    for x in range(WIDTH):
        for y in range(HEIGHT):
            grid[x, y] = 'A'


print('Compare the 1D list and 1D list comprehension creations:')
print(timeit.timeit('createAndFill1DList()', number=10000, globals=globals()))      # 5.796480499964673
print(timeit.timeit('createAndFill1DListComp()', number=10000, globals=globals()))  # 3.3725536999991164
# Conclusion: Using list comprehensions to create the list is faster than a for loop.

print('Compare the 2D list and 2D list comprehension creations:')
print(timeit.timeit('createAndFill2DList()', number=10000, globals=globals()))      # 7.913099199999124
print(timeit.timeit('createAndFill2DListComp()', number=10000, globals=globals()))  # 3.83729699999094
# Conclusion: Using list comprehensions to creat the 2D list is faster than nested for loops.

print('Compare the dictionary and dictionary comprehension creations:')
print(timeit.timeit('createAndFillDict()', number=10000, globals=globals()))      # 9.804479899990838
print(timeit.timeit('createAndFillDictComp()', number=10000, globals=globals()))  # 10.132151499972679
# Conclusion: With repeated trials, there isn't a significant difference.

print('Compare the 1D list, 2D list, and dictionary creations:')
print(timeit.timeit('createAndFill1DListComp()', number=10000, globals=globals()))  # 3.2536532999947667
print(timeit.timeit('createAndFill2DListComp()', number=10000, globals=globals()))  # 3.1561911000171676
print(timeit.timeit('createAndFillDict()', number=10000, globals=globals()))        # 9.759650700027123
# Conclusion: The dictionary is slowest to create, and the 1D and 2D lists are about the same.

print('Compare the memory usage of a full grid of each of the three approaches:')
print(memoryUsage(createAndFill1DListComp()))  # 67274
print(memoryUsage(createAndFill2DListComp()))  # 72282
print(memoryUsage(createAndFillDict()))        # 719246
# Conclusion: The 1D and 2D list use about the same amount, the 1D list less so. The dictionary uses 10x the memory though.

print('Compare the speed of reading grid data:')
list1dGrid = createAndFill1DListComp()
list2dGrid = createAndFill2DListComp()
dictGrid = createAndFillDict()
print(timeit.timeit('read1DList(list1dGrid)', number=10000, globals=globals()))  # 8.444686400005594
print(timeit.timeit('read2DList(list2dGrid)', number=10000, globals=globals()))  # 3.76759669999592
print(timeit.timeit('readDict(dictGrid)', number=10000, globals=globals()))        # 7.19706789997872
# Conclusion: The 2D list is twice as fast as the others at reading data. The 1D list is slower than the dictionary.

print('Compare the speed of writing grid data:')
print(timeit.timeit('write1DList(list1dGrid)', number=10000, globals=globals()))  # 8.487390499969479
print(timeit.timeit('write2DList(list2dGrid)', number=10000, globals=globals()))  # 4.278829399961978
print(timeit.timeit('writeDict(dictGrid)', number=10000, globals=globals()))        # 7.716881500033196
# Conclusion: As with the read test, the 2D list is twice as fast as the others. The 1D list is slower than the dictionary.

&lt;/textarea&gt;&lt;/p&gt;

&lt;h2&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;The 2D list approach was the fastest and the dictionary approach was the slowest and used 10x as much memory as the 1D and 2D lists. But the dictionary approach gives you the flexibility of unbounded grids while the 1D and 2D lists have fixed width and height. The 1D list's requirement to calculate the index actually made it slower than the dictionary.&lt;/p&gt;

&lt;p&gt;So if you need to have a 2D grid data structure, use the list-of-lists approach, unless you need an unbounded grid. In that case, the dictionary approach is significantly slower but offers this flexibility. Or, if performance isn't important, the dictionary approach has the easiest implementation.&lt;/p&gt;

&lt;p&gt;In my personal view, ease of implementation and debuggability are the most important factors and my use cases don't tend to be at large enough scales where the performance differences are significant. I'd go with the dictionary approach.&lt;/p&gt;










</content><category term="misc"></category></entry><entry><title>JavaScript Books That Misspell "JavaScript": How Amazon Became a Minefield of Low-Quality Programming Books</title><link href="https://inventwithpython.com/blog/javascript-books-that-misspell-javascript.html" rel="alternate"></link><published>2021-12-27T12:00:00-05:00</published><updated>2021-12-27T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-12-27:/blog/javascript-books-that-misspell-javascript.html</id><summary type="html">&lt;p&gt;Here's a hint for programmers writing their resumes: "JavaScript" is spelled as one word, capital-J, capital-S, and no hyphen. But this isn't a blog post about programmers who make typos on their resumes, but about what a minefield of low-quality programming books that Amazon has become.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;Here's a hint for programmers writing their resumes: "JavaScript" is spelled as one word, capital-J, capital-S, and no hyphen.&lt;/p&gt;

&lt;p&gt;But this isn't a blog post about programmers who make typos on their resumes, but about what a minefield of low-quality programming books that Amazon has become.&lt;/p&gt;

&lt;p&gt;Teaching people to code is important to me. I write &lt;a href="https://inventwithpython.com"&gt;books on programming and make them freely available online&lt;/a&gt; under a &lt;a href="https://creativecommons.org/"&gt;Creative Commons&lt;/a&gt; license. But there are many low-quality, self-published programming books on Amazon that you need to learn to avoid.&lt;/p&gt;

&lt;p&gt;Let's look at JavaScript books that misspell "JavaScript". I'm not linking the books here, because they don't deserve the traffic.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/misspelljavascript/sql-programming-guide.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;First up is &lt;em&gt;SQL Programming Guide&lt;/em&gt;. The subtitle is "Java Script and Coding Programming Guide: Learn In A Day!". If you look at the cover, it looks pretty nice except for the subtitle which was amateurishly added on after the fact. The subtitle is in a completely different font than the rest of the cover's text. It uses a serifed font instead of sans serif, likely because the serifed font was the default in whatever graphics program the "author" used.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/misspelljavascript/sql-programming-guide-inside.png" /&gt;&lt;/p&gt;

&lt;p&gt;It was odd that a SQL database book would also cover the JavaScript programming language. What's funny is that if you look at the table of contents, this book doesn't JavaScript &lt;em&gt;at all&lt;/em&gt;. Literally none. They're just trying to capitalize on the popularity of JavaScript.&lt;/p&gt;

&lt;p&gt;Suffice it to say, the positive reviews are probably all fake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Excerpt from a Negative Review:&lt;/strong&gt; "The only mention of Javascript is in the title." &lt;em&gt;(I liked this one because even the negative review misspells JavaScript.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/misspelljavascript/ajax-ajax-ajax-ajax-ajax.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;The next JavaScript book that misspells "JavaScript" is &lt;em&gt;Ajax books, ajax for beginners, Complete reference, Tips and tricks, ajax study, ajax for beginners, Step to step, ajax jquery, Jquery, Java script, PHP, Programming language&lt;/em&gt;. There's no subtitle, that's the entire title. The title contains the word "ajax" five times. They're clearly trying to cover their bases with that title. It's a title that has it all!&lt;/p&gt;

&lt;p&gt;The cover is... a mess. The background pattern makes it hard to read the text, and he puts his name in a larger font than the title. Yikes.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/misspelljavascript/ajax-ajax-ajax-ajax-ajax-inside.png" /&gt; &lt;img src="/blogstatic/misspelljavascript/ajax-ajax-ajax-ajax-ajax-wiki.png" /&gt;&lt;/p&gt;

&lt;p&gt;Also, the book's contents are plagarized from Wikipedia. Most of these books probably are.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Excerpt from a Negative Review:&lt;/strong&gt; "A book without a beginning or an end!"&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/misspelljavascript/software-development-for-engineers.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Software Development for Engineers, C/C++, Pascal, Assembly, Visual Basic, HTML, Java Script, Java DOS, Windows NT, UNIX&lt;/em&gt; is amazing because it's from 1997. There's only one copy for sale available, so Amazon has it priced at $143. I'm not sure if it's actually from 1997 because there's a Kindle version available, but the cover sure does look like late 90s tech books. It was doing the SEO title thing before SEO was a thing. I like how it covers everything from Assembly to Java to VB to Pascal, and also UNIX, Windows NT, and DOS. Remember DOS? I do, because I'm old.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/misspelljavascript/software-development-for-engineers-inside.png" /&gt;&lt;/p&gt;

&lt;p&gt;The book is over 600 pages, and from the table of contents it seems to actually cover the topics in its verbose title. It's just that the JavaScript chapter is 21 pages long. This was late 90s JavaScript, but still... I don't think 16 pages is enough to make a scrolling marquee in the status bar.&lt;/p&gt;

&lt;p&gt;Remember scrolling marquees in the status bar? I do, because I'm old.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Excerpt from a Negative Review:&lt;/strong&gt; There's only one review for this book, and it's just a wordless 3-star rating.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/misspelljavascript/smart-scripting.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SMART Scripting&lt;/em&gt; is a nice title. It's followed by a subtitle that seems to be the author's life story: &lt;em&gt;Scripting in more than one language - Learn mixing of VBScript, Java Script, HTML, Batch script and SQL Script. Write scripts for solving real daily life problems by mixing of scripts without having any expensive IDE compilers.&lt;/em&gt; That's multiple sentences with punctuation.&lt;/p&gt;

&lt;p&gt;The cover design isn't that bad: it uses a limit color palette and sticks to two fonts. The author's name is about the same size as the title. But then it has this college entrance essay for a subtitle in tiny tiny print. The book is only 100 pages, and I'm guessing forty of those is for the subtitle. The remaining sixty pages covers the entirety of VBScript, JavaScript, HTML, batch files, and SQL.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/misspelljavascript/smart-scripting-inside.png" /&gt;&lt;/p&gt;

&lt;p&gt;I read the first page, and it's incredible: There are only three sentences that don't have blatantly wrong information.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Best Excerpt from a Negative Review:&lt;/strong&gt; There's only one wordless 3-star review for this book too.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/misspelljavascript/java-script.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;I like the tasteful cover and title of &lt;em&gt;Java Script&lt;/em&gt;. The author didn't go for the 500-word subtitle approach of other books. It's too bad that the brief title gets the spelling of the language wrong. What's even more tragic is that the title &lt;em&gt;is the only place in the book where "JavaScript" is misspelled&lt;/em&gt;. The author and cover designer must be two different people, otherwise the book's creator snatch defeat from the jaws of victory.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/misspelljavascript/java-script-inside.png" /&gt;&lt;/p&gt;

&lt;p&gt;The interior of the book is the standard low quality writing and layout design. I can tell from the font and text color that this book was written in Word and directly exported to PDF from there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Excerpt from a Negative Review:&lt;/strong&gt; There are no reviews for this book, which makes me feel like this and the previous two books were actual earnest attempts to write books instead of making a cash grab that involved paying for positive reviews.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/misspelljavascript/using-java-script.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Using JavaScript&lt;/em&gt;'s cover is a masterpiece of ugly. The top half is a blue and white cloudscape, the bottom is a stone-gray table of sorts with an illustrated coffee cup on a saucer. A cursive font is an... interesting... choice for the title. Normally you'd want the title to be easy to read. The cursive font makes this book's title look like "Javal Cript".&lt;/p&gt;

&lt;p&gt;And the author forgot to put his name on the cover. He's modest.&lt;/p&gt;

&lt;p&gt;The author of this self-published book didn't do a good job with their Amazon listing. "JavaScript" is spelled correctly on the cover of the book, but not on the Amazon page. There's no "see inside" preview, which is alluring because apparently this book is &lt;strong&gt;896 pages&lt;/strong&gt;. But it also says this book was published on January 1, 2001, which is before the Pearson Custom Publishing company existed.&lt;/p&gt;

&lt;p&gt;And Amazon image of the cover... is literally a photo of the printed book resting on a table. The flash from the camera adds a bright spot that makes the title even harder to read.&lt;/p&gt;

&lt;p&gt;I almost want to buy it just because I'm so curious as to what could be in the pages of this massive tome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Excerpt fro-&lt;/strong&gt; of course there are no reviews for this book.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/misspelljavascript/sql-java.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;From the author of &lt;em&gt;SQL Programming Guide&lt;/em&gt;, the JavaScript book that literally has no JavaScript content, comes &lt;em&gt;JAVA, JAVA Script and SQL&lt;/em&gt;, another JavaScript book that literally has no JavaScript content. Or Swift content (the subtitle is "Programming Guide: Learn In A Day! (Java, Swift, Apps, Programming Language)"). Or &lt;em&gt;any&lt;/em&gt; content: the "look inside" preview on Amazon is literally 35 blank pages.&lt;/p&gt;

&lt;p&gt;The cover is simply two copies of the other book's cover copy and pasted together.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/misspelljavascript/sql-java-reviewer.png" /&gt;&lt;/p&gt;

&lt;p&gt;The book has only one 5-star review from Brianna, a nurse from Portland, Oregon. Her headshot is so conventionally attractive you'd think she's a stock photo model. She's written 540 reviews, all 5-star.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Self-publishing has created a new wave of tech books. While I got my own start through self-publishing programming books, I need to stress that you should cast a skeptical eye over these books before you buy them. Just being listed on Amazon is not an indicator of quality, and Amazon's review system is rife with paid-for positive reviews.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>How to Test Multiple Variables Against a Single Value and a Single Variable Against Multiple Values in Python</title><link href="https://inventwithpython.com/blog/how-to-test-multiple-variables-against-a-single-value-and-single-variables-against-multiple-values.html" rel="alternate"></link><published>2021-12-20T12:00:00-05:00</published><updated>2021-12-20T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-12-20:/blog/how-to-test-multiple-variables-against-a-single-value-and-single-variables-against-multiple-values.html</id><summary type="html">&lt;p&gt;Python has a more concise syntax that uses the &lt;code&gt;in&lt;/code&gt; operator and the multiple values packaged in a tuple. To check if one of multiple variables is one of many values, you can use a list comprehension and the &lt;code&gt;any()&lt;/code&gt; function.&lt;/p&gt;</summary><content type="html">

&lt;h2&gt;TL;DR: Version&lt;/h2&gt;

&lt;p&gt;To test if a variable is one of many values, use the &lt;code&gt;in&lt;/code&gt; operator:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; spam = 42
&amp;gt;&amp;gt;&amp;gt; if spam in (3.1415, 'hello', 42, False):
...   print("spam is either 3.1415, 'hello', 42, or False")
...
spam is either 3.1415, 'hello', 42, or False
&lt;/pre&gt;

&lt;p&gt;To test if one of multiple variables is one of many values, use a list comprehension and the &lt;code&gt;any()&lt;/code&gt; function:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; a, b, c = 1, 42, 3
&amp;gt;&amp;gt;&amp;gt; any([x in (100, 3.1415, 'Hello', 42, 'cheese') for x in (a, b, c)])
True
&lt;/pre&gt;

&lt;h2&gt;Testing Multiple Variables Against a Single Value And Vice Versa&lt;/h2&gt;

&lt;p&gt;In Python, if you want to see if a variable is one of many possible values, you could use the &lt;code&gt;or&lt;/code&gt; operator like this:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; spam = 42
&amp;gt;&amp;gt;&amp;gt; if spam == 3.1415 or spam == 'hello' or spam == 42 or spam == False:
...   print("spam is either 3.1415, 'hello', 42, or False")
...
spam is either 3.1415, 'hello', 42, or False
&lt;/pre&gt;

&lt;p&gt;Python has a more concise syntax that uses the &lt;code&gt;in&lt;/code&gt; operator and the multiple values are packaged in a tuple:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; spam = 42
&amp;gt;&amp;gt;&amp;gt; if spam in (3.1415, 'hello', 42, False):
...   print("spam is either 3.1415, 'hello', 42, or False")
...
spam is either 3.1415, 'hello', 42, or False
&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;in&lt;/code&gt; operator literally checks if the value in &lt;code&gt;spam&lt;/code&gt; is in the tuple on the right side of the &lt;code&gt;in&lt;/code&gt; operator. This code is more readable and in most cases code readability is more important than performance. But using the &lt;code&gt;in&lt;/code&gt; does perform slightly faster than the series of &lt;code&gt;or&lt;/code&gt; operators. We can use Python's built-in &lt;code&gt;timeit&lt;/code&gt; modules to see how many seconds running this code ten million times takes:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; timeit.timeit("spam = 42; spam == 3.1415 or spam == 'hello' or spam == 42 or spam == False", number=10000000)
1.527424699976109

&amp;gt;&amp;gt;&amp;gt; timeit.timeit("spam = 42; spam in (3.1415, 'hello', 42, False)", number=10000000)
0.8834100000094622
&lt;/pre&gt;

&lt;p&gt;If you have the opposite case and you have multiple variables you need to check against one value, you can swap the left and right sides of the &lt;code&gt;in&lt;/code&gt; operator. So instead of using &lt;code&gt;or&lt;/code&gt; operators like this:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; a, b, c = 3.1415, 'hello', 42
&amp;gt;&amp;gt;&amp;gt; if a == 'hello' or b == 'hello' or c == 'hello':
...   print("One of a, b, or c is equal to 'hello'.")
...
One of a, b, or c is equal to 'hello'.
&lt;/pre&gt;

&lt;p&gt;...you can write code using an &lt;code&gt;in&lt;/code&gt; operator like this:

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; if 'hello' in (a, b, c):
...   print("One of a, b, or c is equal to 'hello'.")
...
One of a, b, or c is equal to 'hello'.
&lt;/pre&gt;


&lt;h2&gt;Testing Multiple Variables Against Multiple Values&lt;/h2&gt;

&lt;p&gt;If you have multiple variables and you want to see if any of these variables matches one of several values, you can use a list comprehension and the built-in &lt;code&gt;any()&lt;/code&gt; function. Let's take a moment to learn about these two concepts first.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;any()&lt;/code&gt; function takes a list or tuple (or any other &lt;a href="https://treyhunner.com/2016/12/python-iterator-protocol-how-for-loops-work/"&gt;iterable value&lt;/a&gt;) and returns &lt;code&gt;True&lt;/code&gt; if &lt;em&gt;any&lt;/em&gt; of the values in that list are &lt;code&gt;True&lt;/code&gt;. If the list only has &lt;code&gt;False&lt;/code&gt; values or is empty, it returns &lt;code&gt;False&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; any([False, False, True, False])
True
&amp;gt;&amp;gt;&amp;gt; any([True, True])
True
&amp;gt;&amp;gt;&amp;gt; any([False, False, False, False, False])
False
&amp;gt;&amp;gt;&amp;gt; any([])
False
&lt;/pre&gt;

&lt;p&gt;There is also a built-in &lt;code&gt;all()&lt;/code&gt; function that returns &lt;code&gt;True&lt;/code&gt; if the list only contains &lt;code&gt;True&lt;/code&gt; values, or is an empty list. Otherwise, &lt;code&gt;all()&lt;/code&gt; returns &lt;code&gt;False&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;List comprehensions are a short Python syntax for creating list values based on another list or tuple (or any other &lt;a href="https://treyhunner.com/2016/12/python-iterator-protocol-how-for-loops-work/"&gt;iterable value&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Normally you would use a &lt;code&gt;for&lt;/code&gt; loop to create a new list based on another list's values. For example, I'm creating a list called &lt;code&gt;doubles&lt;/code&gt; which contains the integers in the &lt;code&gt;numbers&lt;/code&gt; list, but multiplied by &lt;code&gt;2&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; numbers = [3, 7, 15]
&amp;gt;&amp;gt;&amp;gt; doubles = []
&amp;gt;&amp;gt;&amp;gt; for number in numbers:
...   doubles.append(number * 2)
...
&amp;gt;&amp;gt;&amp;gt; doubles
[6, 14, 30]
&lt;/pre&gt;

&lt;p&gt;A more concise way to write this code in Python is with a list comprehension. Note the similarities between the single-line list comprehension and the multi-line &lt;code&gt;for&lt;/code&gt; loop code:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; numbers = [3, 7, 15]
&amp;gt;&amp;gt;&amp;gt; doubles = [number * 2 for number in numbers]
&amp;gt;&amp;gt;&amp;gt; doubles
[6, 14, 30]
&lt;/pre&gt;

&lt;p&gt;To check if any one of multiple variables contains any one of multiple values, we can use list comprehensions and the &lt;code&gt;in&lt;/code&gt; operator to create a list of Boolean &lt;code&gt;True&lt;/code&gt; and &lt;code&gt;False&lt;/code&gt; values. The list of Booleans created by the list comprehension are based on if the variables' value is in the tuple of values:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; a, b, c = 1, 2, 3
&amp;gt;&amp;gt;&amp;gt; [x in (100, 3.1415, 'Hello', 42, 'cheese') for x in (a, b, c)]
[False, False, False]
&amp;gt;&amp;gt;&amp;gt; b = 42
&amp;gt;&amp;gt;&amp;gt; [x in (100, 3.1415, 'Hello', 42, 'cheese') for x in (a, b, c)]
[False, True, False]
&lt;/pre&gt;

&lt;p&gt;We can pass this list of Booleans to the &lt;code&gt;any()&lt;/code&gt; function and determine if any of the variables matches any of the values:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; a, b, c = 1, 2, 3
&amp;gt;&amp;gt;&amp;gt; any([x in (100, 3.1415, 'Hello', 42, 'cheese') for x in (a, b, c)])
False
&amp;gt;&amp;gt;&amp;gt; b = 42
&amp;gt;&amp;gt;&amp;gt; any([x in (100, 3.1415, 'Hello', 42, 'cheese') for x in (a, b, c)])
True
&lt;/pre&gt;

&lt;p&gt;Putting this much code into a single line stretches the limits of code readability, but it is more concise than not using list comprehensions and &lt;code&gt;any()&lt;/code&gt;. Without them, you'd have to use nested &lt;code&gt;for&lt;/code&gt; loops and so many lines of code that it's not necessarily more

&lt;pre&gt;
&gt;&gt;&gt; a, b, c = 1, 2, 3
&gt;&gt;&gt; match = False
&gt;&gt;&gt; for variable in (a, b, c):
...   for value in (100, 3.1415, 'Hello', 42, 'cheese'):
...     if variable == value:
...       match = True
...
&gt;&gt;&gt; match
False
&lt;/pre&gt;

&lt;p&gt;That's a lot of code to do a simple task. The list comprehension and &lt;code&gt;any()&lt;/code&gt; approach is more concise and readable.&lt;/p&gt;

  </content><category term="misc"></category></entry><entry><title>How, As a Kid, I Taught Myself to Code</title><link href="https://inventwithpython.com/blog/how-as-a-kid-i-taught-myself-to-code.html" rel="alternate"></link><published>2021-11-07T12:00:00-05:00</published><updated>2021-11-07T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-11-07:/blog/how-as-a-kid-i-taught-myself-to-code.html</id><summary type="html">&lt;p&gt;I was one of those kids in the 90s who taught themselves how to code by making video games in the BASIC programming language. Later, I majored in computer science, moved to Silicon Valley, became a software engineer, started writing programming books including Automate the Boring Stuff with Python, and then quit my job to write full-time. I now wake up whenever I want and work from home or nearby cafes. I'm financially secure while being my own boss.&lt;/p&gt;

&lt;p&gt;This is a completely true story.&lt;sup&gt;*&lt;/sup&gt;&lt;/p&gt;</summary><content type="html">


&lt;p&gt;I was one of those kids in the 90s who taught themselves how to code by making video games in the BASIC programming language. Later, I majored in computer science, moved to Silicon Valley, became a software engineer, started writing programming books including &lt;a href="https://automatetheboringstuff.com"&gt;Automate the Boring Stuff with Python&lt;/a&gt;, and then quit my job to write full-time. I now wake up whenever I want and work from home or nearby cafes. I'm financially secure while being my own boss.&lt;/p&gt;

&lt;p&gt;This is a completely true story.&lt;sup&gt;*&lt;/sup&gt;&lt;/p&gt;


&lt;p&gt;Here's how I accomplished all that by, as a kid, teaching myself to code:&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;When I was five, my parents bought an 8-bit Nintendo Entertainment System which got me into video games.&lt;/li&gt;
    &lt;li&gt;In the 90s my parents bought a Compaq 386 PC with a 14 MHz CPU, 40 megabyte hard drive, and ran MS-DOS.&lt;/li&gt;
    &lt;li&gt;In the third grade, my friend Brian showed me a book from the school library about programming video games in BASIC. (His parents also bought a PC for their home.)&lt;/li&gt;
    &lt;li&gt;While I was in elementary school, my grandfather gave my a 5 1/4" floppy disk with some BASIC games. As an adult I found out that these games were commonly copied around on university computer networks (my grandfather was a professor). I didn't understand the source code to them, but I liked looking through them.&lt;/li&gt;
    &lt;li&gt;My parents bought a Compaq Presario desktop with a 100 Mhz CPU, 1.2 gigabyte hard drive, and ran Windows 95.&lt;/li&gt;
    &lt;li&gt;My parents bought me a 16-bit Super Nintendo and more games that inspired the games I programmed (or at least tried to).&lt;/li&gt;
    &lt;li&gt;In high school, I made friends with hacker kids (it turned out most of their "hacks" were made up) who showed me how to pirate Visual Basic 4.0. This was when developer tools cost hundreds of dollars, as opposed to today when they're given away for free. The hacker kids also grew up in houses with PCs.&lt;/li&gt;
    &lt;li&gt;I found a copy of the Borland Turbo C compiler for sale at Half Price Books for $15 (instead of the usual hundreds of dollars). My parents bought it for me.&lt;/li&gt;
    &lt;li&gt;I started going to the monthly 2600 hacker meetings. People there gave me advice and some direction. I once brought my girlfriend to a meeting, but she felt out place there and didn't come with me again.&lt;/li&gt;
    &lt;li&gt;I started going to Barnes and Noble after school, pulling thick $80 tech books off the shelf, reading them in the store for a few hours, then putting them back on the shelf. I did this for years. The employees started to recognize me, but they didn't hassle me. I looked like I fit in.&lt;/li&gt;
    &lt;li&gt;My parents signed up for CompuServ (back when America Online, Prodigy, and CompuServ where the three main online services). I could be online for ten hours a month on dialup, but my parents would also pay the extra $2.50 an hour when I exceeded that.&lt;/li&gt;
    &lt;li&gt;My parents later signed up for an unlimited dial up service. I spent a lot more time online.&lt;/li&gt;
    &lt;li&gt;My parents later signed up for broadband internet. They also bought an additional 7 gigabyte hard drive. My hacker friends showed me how to pirate Star Wars: The Phantom Menance.&lt;/li&gt;
    &lt;li&gt;Junior year of high school, my computer science teacher (my high school had computer science classes) selected me and two other students to represent our school at a citywide programming competition. 14 teams from 14 schools had 3 hours to solve programming problems. The easiest problem was worth one point, and the questions got harder and worth more points. My team scored one point. So did ten other teams.&lt;/li&gt;
    &lt;li&gt;The programming competition was hosted by a tech company, and they gave every participant and teacher a copy of Visual Studio 6.0. I was amazed, because this was several dozen copies of software that cost $600. (At the time I didn't realize that CDs cost pennies to create.)
    &lt;li&gt;Reading the books at Barnes and Noble for free, I studied for the A+ certification exam. My parents paid for the $125 exam fee (and later, the exam fees for the Network+ and i-Net+ certifications). I never did use those certifications to get a job. These days, I don't even bother putting them on my resume.&lt;/li&gt;
    &lt;li&gt;While I was in high school, my parents bought me a laptop. Because I asked them to.&lt;/li&gt;
    &lt;li&gt;My older sister worked in a neurology lab as a student. She introduced me to the professor who ran the lab. He hired me part time to create some software for him. The program I created was pretty bad and not all that useful, but he still paid me. He also wrote me a letter of recommendation for my college application.&lt;/li&gt;
    &lt;li&gt;When I was in college, my parents bought me a newer laptop. Later, they bought me a wifi card so I could get on the internet at cafes. They also paid for my tuition and housing while I majored in computer science.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that's the story of how I, on my own as a kid, taught myself to code.&lt;/p&gt;

&lt;p&gt;(Did I mention all of my hacker friends were boys?)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'm encountering some readers who aren't picking up on the facetious nature of this post. While everything in this post is factually true, it is satire. No child teaches themselves to code, though software engineers often tell and believe this story about themselves. I, myself, didn't realize how long this list would be until I wrote this post. We've had support, encouragement, and inspiration to follow this path. And we did not have discouragement, subtle or not, that convinced us we were out of place or not "programmer types." We would do well to remember this.&lt;/em&gt;&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>22 Examples of Recursive Functions in Python</title><link href="https://inventwithpython.com/blog/22-examples-of-recursive-functions.html" rel="alternate"></link><published>2021-10-04T12:00:00-04:00</published><updated>2021-10-04T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-10-04:/blog/22-examples-of-recursive-functions.html</id><summary type="html">&lt;p&gt;Here are 22 actual, runnable Python code for several recursive functions, written in a style to be understandable by beginners and produce debuggable output.&lt;/p&gt;</summary><content type="html">


&lt;blockquote&gt;&lt;em&gt;This blog post contains information from Al Sweigart's next book, &lt;a href="https://nostarch.com/book-recursion"&gt;The Recursive Book of Recurson&lt;/a&gt; from No Starch Press. The book will be released in June 2022. You can use the discount code PREORDERRECURSION to get 30% off your preorder, and receive the ebook free when you buy a print book. This book will also be freely available under a Creative Commons license on the &lt;a href="https://inventwithpython.com/recursion/"&gt;Invent with Python&lt;/a&gt; website. Al Sweigart also gave a conference talk on recursion at North Bay Python 2018 titled, &lt;a href="https://www.youtube.com/watch?v=AfBqVVKg4GE"&gt;Recursion for Beginners: A Beginner's Guide to Recursion&lt;/a&gt;.&lt;/em&gt;&lt;/blockquote&gt;

&lt;p&gt;Here are 22 actual, runnable Python code for several recursive functions, written in a style to be understandable by beginners and produce debuggable output.&lt;/p&gt;

&lt;p&gt;These are not code snippets; they are complete, runnable Python programs. The source code has been put inside a text field to make scrolling through this blog post easier. You can click on the text field, select all by pressing Ctrl-A, then copy the code to the clipboard by pressing Ctrl-C. Once on the clipboard, you can paste it into your editor or IDE. There are also download links for each of the programs as well.&lt;/p&gt;

&lt;p&gt;These recursive functions are also designed to produce helpful, teachable output. Some of the functions have an &lt;code&gt;indent&lt;/code&gt; parameter to aid in this output and isn't a part of the recursive algorithm itself.&lt;/p&gt;

&lt;h2&gt;Ackermann&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/ackermann.py"&gt;Download ackermann.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Ackermann function is named after its discoverer, Wilhelm Ackermann. Ackermann, a student of mathematician David Hilbert (creator of the Hilbert curve fractal), published his function in 1928. Mathematicians Rózsa Péter and Raphael Robinson later developed the version of the function featured in this section. While the Ackermann function has some application in advanced mathematics, it is mostly known for being an example of a highly recursive function. Even slight increases to its two integer arguments cause a large increase in the number of recursive calls it makes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/channel/UC9-y-6csu5WGm29I7JiwpnA"&gt;Computerphile&lt;/a&gt; YouTube channel has a video on it titled, &lt;a href="https://www.youtube.com/watch?v=i7sm9dzFtEI"&gt;"The Most Difficult Program to Compute?"&lt;/a&gt;&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;def ackermann(m, n, indentation=None):
    if indentation is None:
        indentation = 0
    print('%sackermann(%s, %s)' % (' ' * indentation, m, n))

    if m == 0:
        # BASE CASE
        return n + 1
    elif m &gt; 0 and n == 0:
        # RECURSIVE CASE
        return ackermann(m - 1, 1, indentation + 1)
    elif m &gt; 0 and n &gt; 0:
        # RECURSIVE CASE
        return ackermann(m - 1, ackermann(m, n - 1, indentation + 1), indentation + 1)

print('Starting with m = 1, n = 1:')
print(ackermann(1, 1))
print('Starting with m = 2, n = 3:')
print(ackermann(2, 3))
&lt;/textarea&gt;


&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;Starting with m = 1, n = 1:
ackermann(1, 1)
 ackermann(1, 0)
  ackermann(0, 1)
 ackermann(0, 2)
3
Starting with m = 2, n = 3:
ackermann(2, 3)
 ackermann(2, 2)
  ackermann(2, 1)
   ackermann(2, 0)
    ackermann(1, 1)
     ackermann(1, 0)
      ackermann(0, 1)
     ackermann(0, 2)
   ackermann(1, 3)
    ackermann(1, 2)
     ackermann(1, 1)
      ackermann(1, 0)
       ackermann(0, 1)
      ackermann(0, 2)
     ackermann(0, 3)
    ackermann(0, 4)
  ackermann(1, 5)
   ackermann(1, 4)
    ackermann(1, 3)
     ackermann(1, 2)
      ackermann(1, 1)
       ackermann(1, 0)
        ackermann(0, 1)
       ackermann(0, 2)
      ackermann(0, 3)
     ackermann(0, 4)
    ackermann(0, 5)
   ackermann(0, 6)
 ackermann(1, 7)
  ackermann(1, 6)
   ackermann(1, 5)
    ackermann(1, 4)
     ackermann(1, 3)
      ackermann(1, 2)
       ackermann(1, 1)
        ackermann(1, 0)
         ackermann(0, 1)
        ackermann(0, 2)
       ackermann(0, 3)
      ackermann(0, 4)
     ackermann(0, 5)
    ackermann(0, 6)
   ackermann(0, 7)
  ackermann(0, 8)
9&lt;/textarea&gt;




&lt;h2&gt;Binary Search&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/binarySearch.py"&gt;Download binarySearch.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Binary search is a technique for locating a target item in a sorted list by re-peatedly determining which half of the list the item is in. The most impartial way to search the bookshelf is to start with a book in the middle, then ascertain if the target book you’re looking for is in the left half or the right half.&lt;/p&gt;

&lt;p&gt;You can then repeat this process: look at the book in the middle of your chosen half, and determine if your target book is in the left-side quarter or the right-side quarter. You can do this until you either find the book or find the place where the book should be but isn’t and declare that the book doesn’t exist on the shelf.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;def binarySearch(needle, haystack, left=None, right=None):
    # By default, `left` and `right` are all of `haystack`:
    if left is None:
        left = 0  # `left` defaults to the 0 index.
    if right is None:
        right = len(haystack) - 1  # `right` defaults to the last index.

    print('Searching:', haystack[left:right + 1])

    if left &gt; right:  # BASE CASE
        return None  # The `needle` is not in `haystack`.

    mid = (left + right) // 2
    if needle == haystack[mid]:  # BASE CASE
        return mid  # The `needle` has been found in `haystack`
    elif needle &lt; haystack[mid]:  # RECURSIVE CASE
        return binarySearch(needle, haystack, left, mid - 1)
    elif needle &gt; haystack[mid]:  # RECURSIVE CASE
        return binarySearch(needle, haystack, mid + 1, right)

print(binarySearch(13, [1, 4, 8, 11, 13, 16, 19, 19]))
&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;Searching: [1, 4, 8, 11, 13, 16, 19, 19]
Searching: [13, 16, 19, 19]
Searching: [13]
4&lt;/textarea&gt;




&lt;h2&gt;Combinations&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/combinations.py"&gt;Download combinations.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Given a set of items, we can return a set of every combination of those items. For example, given a set of four letters, ABCD, the k-combinations are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The 0-combinations and 4-combinations have one combination: the empty string and ABCD respectively. &lt;/li&gt;
&lt;li&gt;The 1-combinations and 3-combinations have four combinations: A, B, C, D and ABC, ABD, ACD, BCD, respectively. &lt;/li&gt;
&lt;li&gt;The 2-combinations have the most combinations at six combinations: AB, AC, AD, BC, BD, and CD. &lt;/li&gt;
&lt;/ul&gt;


&lt;textarea cols="80" rows="10"&gt;def getCombos(chars, k, indent=0):
    debugMsg = '.' * indent + "In getCombos('" + chars + "', " + str(k) + ")"
    print(debugMsg + ', start.')
    if k == 0:
        # BASE CASE
        print(debugMsg + " base case returns ['']")
        return ['']  # If k asks for 0-combinations, return '' as the selection of zero letters from chars.
    elif chars == '':
        # BASE CASE
        print(debugMsg + ' base case returns []')
        return []  # A blank chars has no combinations, no matter what k is.

    # RECURSIVE CASE
    combinations = []
    # First part, get the combos that include the head:
    head = chars[:1]
    tail = chars[1:]
    print(debugMsg + " part 1, get combos with head '" + head + "'")
    tailCombos = getCombos(tail, k - 1, indent + 1)
    print('.' * indent + "Adding head '" + head + "' to tail combos:")
    for tailCombo in tailCombos:
        print('.' * indent + 'New combination', head + tailCombo)
        combinations.append(head + tailCombo)

    # Second part, get the combos that don't include the head:
    print(debugMsg + " part 2, get combos without head '" + head + "')")
    combinations.extend(getCombos(tail, k, indent + 1))

    print(debugMsg + ' results are', combinations)
    return combinations

print('2-combinations of "ABC":')
print('Results:', getCombos('ABC', 2))
&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;2-combinations of "ABC":
In getCombos('ABC', 2), start.
In getCombos('ABC', 2) part 1, get combos with head 'A'
.In getCombos('BC', 1), start.
.In getCombos('BC', 1) part 1, get combos with head 'B'
..In getCombos('C', 0), start.
..In getCombos('C', 0) base case returns ['']
.Adding head 'B' to tail combos:
.New combination B
.In getCombos('BC', 1) part 2, get combos without head 'B')
..In getCombos('C', 1), start.
..In getCombos('C', 1) part 1, get combos with head 'C'
...In getCombos('', 0), start.
...In getCombos('', 0) base case returns ['']
..Adding head 'C' to tail combos:
..New combination C
..In getCombos('C', 1) part 2, get combos without head 'C')
...In getCombos('', 1), start.
...In getCombos('', 1) base case returns []
..In getCombos('C', 1) results are ['C']
.In getCombos('BC', 1) results are ['B', 'C']
Adding head 'A' to tail combos:
New combination AB
New combination AC
In getCombos('ABC', 2) part 2, get combos without head 'A')
.In getCombos('BC', 2), start.
.In getCombos('BC', 2) part 1, get combos with head 'B'
..In getCombos('C', 1), start.
..In getCombos('C', 1) part 1, get combos with head 'C'
...In getCombos('', 0), start.
...In getCombos('', 0) base case returns ['']
..Adding head 'C' to tail combos:
..New combination C
..In getCombos('C', 1) part 2, get combos without head 'C')
...In getCombos('', 1), start.
...In getCombos('', 1) base case returns []
..In getCombos('C', 1) results are ['C']
.Adding head 'B' to tail combos:
.New combination BC
.In getCombos('BC', 2) part 2, get combos without head 'B')
..In getCombos('C', 2), start.
..In getCombos('C', 2) part 1, get combos with head 'C'
...In getCombos('', 1), start.
...In getCombos('', 1) base case returns []
..Adding head 'C' to tail combos:
..In getCombos('C', 2) part 2, get combos without head 'C')
...In getCombos('', 2), start.
...In getCombos('', 2) base case returns []
..In getCombos('C', 2) results are []
.In getCombos('BC', 2) results are ['BC']
In getCombos('ABC', 2) results are ['AB', 'AC', 'BC']
Results: ['AB', 'AC', 'BC']&lt;/textarea&gt;




&lt;h2&gt;Count Down and Up&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/countDownAndUp.py"&gt;Download countDownAndUp.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This function is a demonstration of a simple recursive function and shows the order that code runs before and after the recursive call.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;def countDownAndUp(number):
    print(number)
    if number == 0:
        # BASE CASE
        print('Reached the base case.')
        return
    else:
        # RECURSIVE CASE
        countDownAndUp(number - 1)
        print(number)
        return

countDownAndUp(3)
&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;3
2
1
0
Reached the base case.
1
2
3&lt;/textarea&gt;




&lt;h2&gt;Drunk Sierpinski Triangle&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/drunkSierpinskiTriangle.py"&gt;Download drunkSierpinskiTriangle.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This program uses the &lt;code&gt;turtle&lt;/code&gt; module to draw a sierpinski triangle fractal, except with slight errors to give the fractal a distorted look.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;import random
import turtle
turtle.tracer(1000, 0) # Increase the first argument to speed up the drawing.
turtle.setworldcoordinates(0, 0, 700, 700)
turtle.hideturtle()

MIN_SIZE = 4 # Try changing this to decrease/increase the amount of recursion.

DRUNKENESS = 0.001 # Increase to make more drunk. 0.01 is seriously drunk.

def midpoint(startx, starty, endx, endy):
    # Return the x, y coordinate in the middle of the four given parameters.
    xDiff = abs(startx - endx)
    yDiff = abs(starty - endy)
    return (min(startx, endx) + (xDiff / 2.0) + (random.random() * xDiff * (random.randint(-100, 100) * DRUNKENESS)),
            min(starty, endy) + (yDiff / 2.0) + (random.random() * xDiff * (random.randint(-100, 100) * DRUNKENESS)))

def isTooSmall(ax, ay, bx, by, cx, cy):
    # Determine if the triangle is too small to draw.
    width = max(ax, bx, cx) - min(ax, bx, cx)
    height = max(ay, by, cy) - min(ay, by, cy)
    return width &lt; MIN_SIZE or height &lt; MIN_SIZE

def drawTriangle(ax, ay, bx, by, cx, cy):
    if isTooSmall(ax, ay, bx, by, cx, cy):
        # BASE CASE
        return
    else:
        # RECURSIVE CASE
        # Draw the triangle.
        turtle.penup()
        turtle.goto(ax, ay)
        turtle.pendown()
        turtle.goto(bx, by)
        turtle.goto(cx, cy)
        turtle.goto(ax, ay)
        turtle.penup()

        # Calculate midpoints between points A, B, and C.
        mid_ab = midpoint(ax, ay, bx, by)
        mid_bc = midpoint(bx, by, cx, cy)
        mid_ca = midpoint(cx, cy, ax, ay)

        # Draw the three inner triangles.
        drawTriangle(ax, ay, mid_ab[0], mid_ab[1], mid_ca[0], mid_ca[1])
        drawTriangle(mid_ab[0], mid_ab[1], bx, by, mid_bc[0], mid_bc[1])
        drawTriangle(mid_ca[0], mid_ca[1], mid_bc[0], mid_bc[1], cx, cy)
        return

# Draw an equilateral Sierpinski triangle.
drawTriangle(50, 50, 350, 650, 650, 50)

# Draw a skewed Sierpinski triangle.
#drawTriangle(30, 250, 680, 600, 500, 80)

turtle.exitonclick()&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/drunksierpinski.png" /&gt;&lt;/p&gt;




&lt;h2&gt;Eight Queens&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/eightQueens.py"&gt;Download eightQueens.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Eight_queens_puzzle"&gt;eight queens puzzle&lt;/a&gt; is how to find an arrangement of eight chess queens on a board such that they cannot attack each other. This recursive program finds every possible solution on an 8 x 8 chess board.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;SIZE = 8

def getPermutations(items):
    if len(items) == 0:
        # BASE CASE
        return [[]]
    else:
        # RECURSIVE CASE
        permutations = []
        head = [items[0]]
        tail = items[1:]
        tailPermutations = getPermutations(tail)
        for tailPerm in tailPermutations:
            for i in range(len(tailPerm) + 1):
                newPermutation = tailPerm[0:i] + head + tailPerm[i:]
                permutations.append(newPermutation)
        return permutations

def printBoard(board):
    for row in range(SIZE):
        for column in range(SIZE):
            if board[row] == column:
                print('Q ', end='')  # Print queen.
            else:
                print('. ', end='')  # Print blank space.
        print()  # Print newline.
    print()

numSolutions = 0
# Go through all possible boards with one queen per column, and
# filter out the ones with queens that can attack each other diagonally:
for board in getPermutations(list(range(SIZE))):
    # check if any queens can diagonally attack each other
    isValidBoard = True
    for row in range(SIZE):
        column = board[row]
        for diagOffset in range(1, SIZE):
            if row + diagOffset &lt; SIZE:  # Bounds check.
                if (board[row + diagOffset] == column + diagOffset) or \
                   (board[row + diagOffset] == column - diagOffset):
                    isValidBoard = False
                    break

            if row - diagOffset &gt;= 0:  # Bounds check.
                if (board[row - diagOffset] == column - diagOffset) or \
                   (board[row - diagOffset] == column + diagOffset):
                    isValidBoard = False
                    break

    if isValidBoard:
        printBoard(board)
        numSolutions += 1
print('Number of solutions:', numSolutions)
&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;. . . . Q . . .
. Q . . . . . .
. . . . . Q . .
Q . . . . . . .
. . . . . . Q .
. . . Q . . . .
. . . . . . . Q
. . Q . . . . .

. . . . . Q . .
. . Q . . . . .
. . . . Q . . .
. . . . . . Q .
Q . . . . . . .
. . . Q . . . .
. Q . . . . . .
. . . . . . . Q

. . . . . Q . .
. . . Q . . . .
. . . . . . Q .
Q . . . . . . .
. . Q . . . . .
. . . . Q . . .
. Q . . . . . .
. . . . . . . Q

. . Q . . . . .
. . . . . Q . .
. Q . . . . . .
. . . . . . Q .
. . . . Q . . .
Q . . . . . . .
. . . . . . . Q
. . . Q . . . .

. . . . . Q . .
. . Q . . . . .
Q . . . . . . .
. . . . . . Q .
. . . . Q . . .
. . . . . . . Q
. Q . . . . . .
. . . Q . . . .

. . . . . Q . .
. Q . . . . . .
. . . . . . Q .
Q . . . . . . .
. . Q . . . . .
. . . . Q . . .
. . . . . . . Q
. . . Q . . . .

. . . . . Q . .
. . . Q . . . .
. . . . . . Q .
Q . . . . . . .
. . . . . . . Q
. Q . . . . . .
. . . . Q . . .
. . Q . . . . .

. . Q . . . . .
. . . . . Q . .
. Q . . . . . .
. . . . . . Q .
Q . . . . . . .
. . . Q . . . .
. . . . . . . Q
. . . . Q . . .

. . . . . Q . .
. . Q . . . . .
. . . . . . Q .
. Q . . . . . .
. . . Q . . . .
. . . . . . . Q
Q . . . . . . .
. . . . Q . . .

. . . . . Q . .
. . Q . . . . .
. . . . . . Q .
. . . Q . . . .
Q . . . . . . .
. . . . . . . Q
. Q . . . . . .
. . . . Q . . .

. Q . . . . . .
. . . . . Q . .
Q . . . . . . .
. . . . . . Q .
. . . Q . . . .
. . . . . . . Q
. . Q . . . . .
. . . . Q . . .

. . . . . Q . .
. Q . . . . . .
. . . . . . Q .
Q . . . . . . .
. . . Q . . . .
. . . . . . . Q
. . . . Q . . .
. . Q . . . . .

. . . . . Q . .
. . Q . . . . .
. . . . . . Q .
. Q . . . . . .
. . . . . . . Q
. . . . Q . . .
Q . . . . . . .
. . . Q . . . .

. . . . Q . . .
. . . . . . Q .
. Q . . . . . .
. . . . . Q . .
. . Q . . . . .
Q . . . . . . .
. . . Q . . . .
. . . . . . . Q

. . . . Q . . .
. . . . . . Q .
. Q . . . . . .
. . . . . Q . .
. . Q . . . . .
Q . . . . . . .
. . . . . . . Q
. . . Q . . . .

. . . Q . . . .
. . . . . . Q .
. . . . Q . . .
. . Q . . . . .
Q . . . . . . .
. . . . . Q . .
. . . . . . . Q
. Q . . . . . .

. . . Q . . . .
. . . . . . Q .
. . . . Q . . .
. Q . . . . . .
. . . . . Q . .
Q . . . . . . .
. . Q . . . . .
. . . . . . . Q

. . . . . . Q .
. . . . Q . . .
. . Q . . . . .
Q . . . . . . .
. . . . . Q . .
. . . . . . . Q
. Q . . . . . .
. . . Q . . . .

. . . Q . . . .
. Q . . . . . .
. . . . . . Q .
. . Q . . . . .
. . . . . Q . .
. . . . . . . Q
Q . . . . . . .
. . . . Q . . .

. . . Q . . . .
. Q . . . . . .
. . . . . . Q .
. . Q . . . . .
. . . . . Q . .
. . . . . . . Q
. . . . Q . . .
Q . . . . . . .

Q . . . . . . .
. . . . . . Q .
. . . Q . . . .
. . . . . Q . .
. . . . . . . Q
. Q . . . . . .
. . . . Q . . .
. . Q . . . . .

. . . . . . Q .
. Q . . . . . .
. . . . . Q . .
. . Q . . . . .
Q . . . . . . .
. . . Q . . . .
. . . . . . . Q
. . . . Q . . .

. Q . . . . . .
. . . . . . Q .
. . Q . . . . .
. . . . . Q . .
. . . . . . . Q
. . . . Q . . .
Q . . . . . . .
. . . Q . . . .

. . . . . . Q .
. . Q . . . . .
Q . . . . . . .
. . . . . Q . .
. . . . . . . Q
. . . . Q . . .
. Q . . . . . .
. . . Q . . . .

. . . . Q . . .
. Q . . . . . .
. . . Q . . . .
. . . . . . Q .
. . Q . . . . .
. . . . . . . Q
. . . . . Q . .
Q . . . . . . .

. . Q . . . . .
. . . . Q . . .
. . . . . . Q .
Q . . . . . . .
. . . Q . . . .
. Q . . . . . .
. . . . . . . Q
. . . . . Q . .

. . . . Q . . .
. . . . . . Q .
. . . Q . . . .
Q . . . . . . .
. . Q . . . . .
. . . . . . . Q
. . . . . Q . .
. Q . . . . . .

. . . . Q . . .
. . . . . . Q .
. Q . . . . . .
. . . Q . . . .
. . . . . . . Q
Q . . . . . . .
. . Q . . . . .
. . . . . Q . .

. Q . . . . . .
. . . . Q . . .
. . . . . . Q .
. . . Q . . . .
Q . . . . . . .
. . . . . . . Q
. . . . . Q . .
. . Q . . . . .

. . . . Q . . .
. . . . . . Q .
Q . . . . . . .
. . . Q . . . .
. Q . . . . . .
. . . . . . . Q
. . . . . Q . .
. . Q . . . . .

. . . . Q . . .
. . Q . . . . .
Q . . . . . . .
. . . . . . Q .
. Q . . . . . .
. . . . . . . Q
. . . . . Q . .
. . . Q . . . .

. Q . . . . . .
. . . . Q . . .
. . . . . . Q .
Q . . . . . . .
. . Q . . . . .
. . . . . . . Q
. . . . . Q . .
. . . Q . . . .

. . . . Q . . .
. . . . . . Q .
Q . . . . . . .
. . Q . . . . .
. . . . . . . Q
. . . . . Q . .
. . . Q . . . .
. Q . . . . . .

. . . Q . . . .
. Q . . . . . .
. . . . . . Q .
. . . . Q . . .
Q . . . . . . .
. . . . . . . Q
. . . . . Q . .
. . Q . . . . .

. . . . . . Q .
. . . Q . . . .
. Q . . . . . .
. . . . Q . . .
. . . . . . . Q
Q . . . . . . .
. . Q . . . . .
. . . . . Q . .

. . Q . . . . .
Q . . . . . . .
. . . . . . Q .
. . . . Q . . .
. . . . . . . Q
. Q . . . . . .
. . . Q . . . .
. . . . . Q . .

. Q . . . . . .
. . . . . . Q .
. . . . Q . . .
. . . . . . . Q
Q . . . . . . .
. . . Q . . . .
. . . . . Q . .
. . Q . . . . .

Q . . . . . . .
. . . . . . Q .
. . . . Q . . .
. . . . . . . Q
. Q . . . . . .
. . . Q . . . .
. . . . . Q . .
. . Q . . . . .

. . . Q . . . .
. . . . . . Q .
. . Q . . . . .
. . . . . . . Q
. Q . . . . . .
. . . . Q . . .
Q . . . . . . .
. . . . . Q . .

. . . Q . . . .
. . . . . . Q .
Q . . . . . . .
. . . . . . . Q
. . . . Q . . .
. Q . . . . . .
. . . . . Q . .
. . Q . . . . .

. . . . . . Q .
. Q . . . . . .
. . . Q . . . .
Q . . . . . . .
. . . . . . . Q
. . . . Q . . .
. . Q . . . . .
. . . . . Q . .

. . Q . . . . .
. . . . . . Q .
. Q . . . . . .
. . . . . . . Q
. . . . Q . . .
Q . . . . . . .
. . . Q . . . .
. . . . . Q . .

. . . . . . Q .
. . Q . . . . .
. . . . . . . Q
. Q . . . . . .
. . . . Q . . .
Q . . . . . . .
. . . . . Q . .
. . . Q . . . .

. . . . . . Q .
. . . Q . . . .
. Q . . . . . .
. . . . . . . Q
. . . . . Q . .
Q . . . . . . .
. . Q . . . . .
. . . . Q . . .

. . Q . . . . .
. . . . . . Q .
. Q . . . . . .
. . . . . . . Q
. . . . . Q . .
. . . Q . . . .
Q . . . . . . .
. . . . Q . . .

. . . . . . Q .
Q . . . . . . .
. . Q . . . . .
. . . . . . . Q
. . . . . Q . .
. . . Q . . . .
. Q . . . . . .
. . . . Q . . .

. . . . Q . . .
. Q . . . . . .
. . . Q . . . .
. . . . . Q . .
. . . . . . . Q
. . Q . . . . .
Q . . . . . . .
. . . . . . Q .

. . . . Q . . .
Q . . . . . . .
. . . Q . . . .
. . . . . Q . .
. . . . . . . Q
. Q . . . . . .
. . . . . . Q .
. . Q . . . . .

. . . . Q . . .
. . Q . . . . .
Q . . . . . . .
. . . . . Q . .
. . . . . . . Q
. Q . . . . . .
. . . Q . . . .
. . . . . . Q .

. . . Q . . . .
. . . . . Q . .
Q . . . . . . .
. . . . Q . . .
. Q . . . . . .
. . . . . . . Q
. . Q . . . . .
. . . . . . Q .

. . . . . Q . .
. . . Q . . . .
Q . . . . . . .
. . . . Q . . .
. . . . . . . Q
. Q . . . . . .
. . . . . . Q .
. . Q . . . . .

. . . . . Q . .
. . Q . . . . .
. . . . Q . . .
. . . . . . . Q
Q . . . . . . .
. . . Q . . . .
. Q . . . . . .
. . . . . . Q .

. . Q . . . . .
. . . . . Q . .
. Q . . . . . .
. . . . Q . . .
. . . . . . . Q
Q . . . . . . .
. . . . . . Q .
. . . Q . . . .

. . . . . Q . .
Q . . . . . . .
. . . . Q . . .
. Q . . . . . .
. . . . . . . Q
. . Q . . . . .
. . . . . . Q .
. . . Q . . . .

. . Q . . . . .
. . . . . Q . .
. . . Q . . . .
. Q . . . . . .
. . . . . . . Q
. . . . Q . . .
. . . . . . Q .
Q . . . . . . .

. . Q . . . . .
. . . . . Q . .
. . . Q . . . .
Q . . . . . . .
. . . . . . . Q
. . . . Q . . .
. . . . . . Q .
. Q . . . . . .

. . . . . Q . .
. . . Q . . . .
. Q . . . . . .
. . . . . . . Q
. . . . Q . . .
. . . . . . Q .
Q . . . . . . .
. . Q . . . . .

. . . . . Q . .
. . Q . . . . .
Q . . . . . . .
. . . . . . . Q
. . . . Q . . .
. Q . . . . . .
. . . Q . . . .
. . . . . . Q .

. . Q . . . . .
. . . . . Q . .
. . . . . . . Q
Q . . . . . . .
. . . . Q . . .
. . . . . . Q .
. Q . . . . . .
. . . Q . . . .

. Q . . . . . .
. . . Q . . . .
. . . . . Q . .
. . . . . . . Q
. . Q . . . . .
Q . . . . . . .
. . . . . . Q .
. . . . Q . . .

. . . Q . . . .
. . . . . Q . .
. . . . . . . Q
. . Q . . . . .
Q . . . . . . .
. . . . . . Q .
. . . . Q . . .
. Q . . . . . .

. . . Q . . . .
. . . . . Q . .
. . . . . . . Q
. Q . . . . . .
. . . . . . Q .
Q . . . . . . .
. . Q . . . . .
. . . . Q . . .

. . Q . . . . .
. . . . . Q . .
. . . . . . . Q
. Q . . . . . .
. . . Q . . . .
Q . . . . . . .
. . . . . . Q .
. . . . Q . . .

. . Q . . . . .
. . . . . Q . .
. . . . . . . Q
Q . . . . . . .
. . . Q . . . .
. . . . . . Q .
. . . . Q . . .
. Q . . . . . .

. . . . . Q . .
. . Q . . . . .
Q . . . . . . .
. . . . . . . Q
. . . Q . . . .
. Q . . . . . .
. . . . . . Q .
. . . . Q . . .

. Q . . . . . .
. . . . . Q . .
. . . . . . . Q
. . Q . . . . .
Q . . . . . . .
. . . Q . . . .
. . . . . . Q .
. . . . Q . . .

. . . . . Q . .
. . . . . . . Q
. Q . . . . . .
. . . Q . . . .
Q . . . . . . .
. . . . . . Q .
. . . . Q . . .
. . Q . . . . .

Q . . . . . . .
. . . . . Q . .
. . . . . . . Q
. . Q . . . . .
. . . . . . Q .
. . . Q . . . .
. Q . . . . . .
. . . . Q . . .

. . . Q . . . .
. Q . . . . . .
. . . . Q . . .
. . . . . . . Q
. . . . . Q . .
Q . . . . . . .
. . Q . . . . .
. . . . . . Q .

. . . Q . . . .
Q . . . . . . .
. . . . Q . . .
. . . . . . . Q
. . . . . Q . .
. . Q . . . . .
. . . . . . Q .
. Q . . . . . .

. . . . Q . . .
. . . . . . . Q
. . . Q . . . .
Q . . . . . . .
. . Q . . . . .
. . . . . Q . .
. Q . . . . . .
. . . . . . Q .

. . Q . . . . .
. . . . Q . . .
. Q . . . . . .
. . . . . . . Q
. . . . . Q . .
. . . Q . . . .
. . . . . . Q .
Q . . . . . . .

Q . . . . . . .
. . . . Q . . .
. . . . . . . Q
. . . . . Q . .
. . Q . . . . .
. . . . . . Q .
. Q . . . . . .
. . . Q . . . .

. . . . Q . . .
Q . . . . . . .
. . . . . . . Q
. . . . . Q . .
. . Q . . . . .
. . . . . . Q .
. Q . . . . . .
. . . Q . . . .

. . . Q . . . .
. Q . . . . . .
. . . . . . . Q
. . . . . Q . .
Q . . . . . . .
. . Q . . . . .
. . . . Q . . .
. . . . . . Q .

. . . . . . . Q
. . Q . . . . .
Q . . . . . . .
. . . . . Q . .
. Q . . . . . .
. . . . Q . . .
. . . . . . Q .
. . . Q . . . .

. Q . . . . . .
. . . . . . . Q
. . . . . Q . .
Q . . . . . . .
. . Q . . . . .
. . . . Q . . .
. . . . . . Q .
. . . Q . . . .

. . . Q . . . .
. . . . . . . Q
Q . . . . . . .
. . Q . . . . .
. . . . . Q . .
. Q . . . . . .
. . . . . . Q .
. . . . Q . . .

. . . . . . . Q
. . . Q . . . .
Q . . . . . . .
. . Q . . . . .
. . . . . Q . .
. Q . . . . . .
. . . . . . Q .
. . . . Q . . .

. . . Q . . . .
Q . . . . . . .
. . . . Q . . .
. . . . . . . Q
. Q . . . . . .
. . . . . . Q .
. . Q . . . . .
. . . . . Q . .

. . Q . . . . .
. . . . Q . . .
. . . . . . . Q
. . . Q . . . .
Q . . . . . . .
. . . . . . Q .
. Q . . . . . .
. . . . . Q . .

. . . . Q . . .
. . Q . . . . .
. . . . . . . Q
. . . Q . . . .
. . . . . . Q .
Q . . . . . . .
. . . . . Q . .
. Q . . . . . .

. . . . Q . . .
. Q . . . . . .
. . . . . . . Q
Q . . . . . . .
. . . Q . . . .
. . . . . . Q .
. . Q . . . . .
. . . . . Q . .

. . . . Q . . .
Q . . . . . . .
. . . . . . . Q
. . . Q . . . .
. Q . . . . . .
. . . . . . Q .
. . Q . . . . .
. . . . . Q . .

. . . . Q . . .
. . . . . . . Q
. . . Q . . . .
Q . . . . . . .
. . . . . . Q .
. Q . . . . . .
. . . . . Q . .
. . Q . . . . .

. . Q . . . . .
. . . . Q . . .
. Q . . . . . .
. . . . . . . Q
Q . . . . . . .
. . . . . . Q .
. . . Q . . . .
. . . . . Q . .

. . . Q . . . .
. . . . . . . Q
. . . . Q . . .
. . Q . . . . .
Q . . . . . . .
. . . . . . Q .
. Q . . . . . .
. . . . . Q . .

. . . Q . . . .
. Q . . . . . .
. . . . . . . Q
. . . . Q . . .
. . . . . . Q .
Q . . . . . . .
. . Q . . . . .
. . . . . Q . .

. . . Q . . . .
. . . . . . . Q
Q . . . . . . .
. . . . Q . . .
. . . . . . Q .
. Q . . . . . .
. . . . . Q . .
. . Q . . . . .

. . . . . . . Q
. Q . . . . . .
. . . . Q . . .
. . Q . . . . .
Q . . . . . . .
. . . . . . Q .
. . . Q . . . .
. . . . . Q . .

. . . . . . . Q
. Q . . . . . .
. . . Q . . . .
Q . . . . . . .
. . . . . . Q .
. . . . Q . . .
. . Q . . . . .
. . . . . Q . .

. . Q . . . . .
. . . . . . . Q
. . . Q . . . .
. . . . . . Q .
Q . . . . . . .
. . . . . Q . .
. Q . . . . . .
. . . . Q . . .

Number of solutions: 92&lt;/textarea&gt;




&lt;h2&gt;Exponentiation&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/exponentWithPowerRule.py"&gt;Download exponentWithPowerRule.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Calculating exponents such as 2^5 (or 2 x 2 x 2 x 2 x 2) can be done without using the ** operator by doing repeat multiplications. However, there's a shortcut in that 2^(m * 2) == 2^m * 2^m. You can use this trick to perform a^m in less than m multiplications.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;def exponentWithPowerRule(a, n):
    # Step 1: Determine the operations to be performed.
    opStack = []
    while n &gt; 1:
        if n % 2 == 0:
            # n is even
            opStack.append('square')
            n = n // 2
        elif n % 2 == 1:
            # n is odd
            n -= 1
            opStack.append('multiply')

    # Step 2: Perform the operations in reverse order.
    result = a # Start result at `a`
    while opStack:
        op = opStack.pop()

        if op == 'multiply':
            result *= a
        elif op == 'square':
            result *= result

    return result

print(exponentWithPowerRule(3, 6))
print(exponentWithPowerRule(10, 3))
print(exponentWithPowerRule(17, 10))
&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;729
1000
2015993900449&lt;/textarea&gt;




&lt;h2&gt;Factorial&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/factorialByRecursion.py"&gt;Download factorialByRecursion.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The factorial of 4 is 4 × 3 × 2 × 1 and the factorial of 5 is 5 × 4 × 3 × 2 × 1. So you could say that 5! = 5 × 4!. This is recursive because the definition of the factorial of 5 (or any number n) includes the definition of the factorial of 4 (the number n – 1). In turn, 4! = 4 × 3!, and so on, until you must calculate 1!, the base case, which is simply 1.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;def factorial(number):
    if number == 1:
        # BASE CASE
        return 1
    else:
        # RECURSIVE CASE
        return number * factorial(number - 1)
print(factorial(5))
&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;120&lt;/textarea&gt;




&lt;h2&gt;Fibonacci&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/fibonacciByRecursion"&gt;Download fibonacciByRecursion.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Fibonacci sequence is another classic example for introducing recursion. Mathematically, the Fibonacci sequence of integers begins with the numbers 1 and 1 (or sometimes, 0 and 1). The next number in the sequence is the sum of the previous two numbers. This creates the sequence 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, and so on, forever.&lt;/p&gt;

&lt;p&gt;There is a recursive property to calculating Fibonacci numbers. For example, if you want to calculate the 10th Fibonacci number, you add the 9th and 8th Fibonacci numbers together. To calculate those Fibonacci numbers, you add the 8th and 7th, then the 7th and 6th Fibonacci numbers. There’s going to be a lot of repeat calculations: notice that adding the 9th and 8th Fibonacci number involves calculating the 8th Fibonacci number again. You would continue this recursion until you reach the base case of the 1st or 2nd Fibonacci numbers, which are always 1.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;def fibonacci(nthNumber):
    print('fibonacci(%s) called.' % (nthNumber))
    if nthNumber == 1 or nthNumber == 2:
        # BASE CASE
        print('Base case fibonacci(%s) returning 1.' % (nthNumber))
        return 1
    else:
        # RECURSIVE CASE
        print('Calling fibonacci(%s) (nthNumber - 1).' % (nthNumber - 1))
        result = fibonacci(nthNumber - 1)

        print('Calling fibonacci(%s) (nthNumber - 2).' % (nthNumber - 2))
        result = result + fibonacci(nthNumber - 2)

        print('Call to fibonacci(%s) returning %s.' % (nthNumber, result))
        return result

print(fibonacci(10))&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;fibonacci(10) called.
Calling fibonacci(9) (nthNumber - 1).
fibonacci(9) called.
Calling fibonacci(8) (nthNumber - 1).
fibonacci(8) called.
Calling fibonacci(7) (nthNumber - 1).
fibonacci(7) called.
Calling fibonacci(6) (nthNumber - 1).
fibonacci(6) called.
Calling fibonacci(5) (nthNumber - 1).
fibonacci(5) called.
Calling fibonacci(4) (nthNumber - 1).
fibonacci(4) called.
Calling fibonacci(3) (nthNumber - 1).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Calling fibonacci(2) (nthNumber - 2).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Call to fibonacci(4) returning 3.
Calling fibonacci(3) (nthNumber - 2).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Call to fibonacci(5) returning 5.
Calling fibonacci(4) (nthNumber - 2).
fibonacci(4) called.
Calling fibonacci(3) (nthNumber - 1).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Calling fibonacci(2) (nthNumber - 2).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Call to fibonacci(4) returning 3.
Call to fibonacci(6) returning 8.
Calling fibonacci(5) (nthNumber - 2).
fibonacci(5) called.
Calling fibonacci(4) (nthNumber - 1).
fibonacci(4) called.
Calling fibonacci(3) (nthNumber - 1).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Calling fibonacci(2) (nthNumber - 2).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Call to fibonacci(4) returning 3.
Calling fibonacci(3) (nthNumber - 2).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Call to fibonacci(5) returning 5.
Call to fibonacci(7) returning 13.
Calling fibonacci(6) (nthNumber - 2).
fibonacci(6) called.
Calling fibonacci(5) (nthNumber - 1).
fibonacci(5) called.
Calling fibonacci(4) (nthNumber - 1).
fibonacci(4) called.
Calling fibonacci(3) (nthNumber - 1).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Calling fibonacci(2) (nthNumber - 2).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Call to fibonacci(4) returning 3.
Calling fibonacci(3) (nthNumber - 2).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Call to fibonacci(5) returning 5.
Calling fibonacci(4) (nthNumber - 2).
fibonacci(4) called.
Calling fibonacci(3) (nthNumber - 1).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Calling fibonacci(2) (nthNumber - 2).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Call to fibonacci(4) returning 3.
Call to fibonacci(6) returning 8.
Call to fibonacci(8) returning 21.
Calling fibonacci(7) (nthNumber - 2).
fibonacci(7) called.
Calling fibonacci(6) (nthNumber - 1).
fibonacci(6) called.
Calling fibonacci(5) (nthNumber - 1).
fibonacci(5) called.
Calling fibonacci(4) (nthNumber - 1).
fibonacci(4) called.
Calling fibonacci(3) (nthNumber - 1).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Calling fibonacci(2) (nthNumber - 2).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Call to fibonacci(4) returning 3.
Calling fibonacci(3) (nthNumber - 2).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Call to fibonacci(5) returning 5.
Calling fibonacci(4) (nthNumber - 2).
fibonacci(4) called.
Calling fibonacci(3) (nthNumber - 1).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Calling fibonacci(2) (nthNumber - 2).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Call to fibonacci(4) returning 3.
Call to fibonacci(6) returning 8.
Calling fibonacci(5) (nthNumber - 2).
fibonacci(5) called.
Calling fibonacci(4) (nthNumber - 1).
fibonacci(4) called.
Calling fibonacci(3) (nthNumber - 1).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Calling fibonacci(2) (nthNumber - 2).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Call to fibonacci(4) returning 3.
Calling fibonacci(3) (nthNumber - 2).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Call to fibonacci(5) returning 5.
Call to fibonacci(7) returning 13.
Call to fibonacci(9) returning 34.
Calling fibonacci(8) (nthNumber - 2).
fibonacci(8) called.
Calling fibonacci(7) (nthNumber - 1).
fibonacci(7) called.
Calling fibonacci(6) (nthNumber - 1).
fibonacci(6) called.
Calling fibonacci(5) (nthNumber - 1).
fibonacci(5) called.
Calling fibonacci(4) (nthNumber - 1).
fibonacci(4) called.
Calling fibonacci(3) (nthNumber - 1).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Calling fibonacci(2) (nthNumber - 2).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Call to fibonacci(4) returning 3.
Calling fibonacci(3) (nthNumber - 2).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Call to fibonacci(5) returning 5.
Calling fibonacci(4) (nthNumber - 2).
fibonacci(4) called.
Calling fibonacci(3) (nthNumber - 1).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Calling fibonacci(2) (nthNumber - 2).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Call to fibonacci(4) returning 3.
Call to fibonacci(6) returning 8.
Calling fibonacci(5) (nthNumber - 2).
fibonacci(5) called.
Calling fibonacci(4) (nthNumber - 1).
fibonacci(4) called.
Calling fibonacci(3) (nthNumber - 1).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Calling fibonacci(2) (nthNumber - 2).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Call to fibonacci(4) returning 3.
Calling fibonacci(3) (nthNumber - 2).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Call to fibonacci(5) returning 5.
Call to fibonacci(7) returning 13.
Calling fibonacci(6) (nthNumber - 2).
fibonacci(6) called.
Calling fibonacci(5) (nthNumber - 1).
fibonacci(5) called.
Calling fibonacci(4) (nthNumber - 1).
fibonacci(4) called.
Calling fibonacci(3) (nthNumber - 1).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Calling fibonacci(2) (nthNumber - 2).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Call to fibonacci(4) returning 3.
Calling fibonacci(3) (nthNumber - 2).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Call to fibonacci(5) returning 5.
Calling fibonacci(4) (nthNumber - 2).
fibonacci(4) called.
Calling fibonacci(3) (nthNumber - 1).
fibonacci(3) called.
Calling fibonacci(2) (nthNumber - 1).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Calling fibonacci(1) (nthNumber - 2).
fibonacci(1) called.
Base case fibonacci(1) returning 1.
Call to fibonacci(3) returning 2.
Calling fibonacci(2) (nthNumber - 2).
fibonacci(2) called.
Base case fibonacci(2) returning 1.
Call to fibonacci(4) returning 3.
Call to fibonacci(6) returning 8.
Call to fibonacci(8) returning 21.
Call to fibonacci(10) returning 55.
55&lt;/textarea&gt;




&lt;h2&gt;Floodfill&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/floodfill.py"&gt;Download floodfill.py&lt;/a&gt;&lt;/p&gt;


&lt;textarea cols="80" rows="10"&gt;import sys

# Create the image (make sure it's rectangular!)
im = [list('..########################...........'),
      list('..#......................#...#####...'),
      list('..#..........########....#####...#...'),
      list('..#..........#......#............#...'),
      list('..#..........########.........####...'),
      list('..######......................#......'),
      list('.......#..#####.....###########......'),
      list('.......####...#######................')]

HEIGHT = len(im)
WIDTH = len(im[0])

def floodFill(image, x, y, newChar, oldChar=None):
    if oldChar == None:
        # oldChar defaults to the character at x, y.
        oldChar = image[y][x]
    if oldChar == newChar or image[y][x] != oldChar:
        # BASE CASE
        return

    image[y][x] = newChar # Change the character.

    # Uncomment to view each step:
    #printImage(image)

    # Change the neighboring characters.
    if y + 1 &lt; HEIGHT and image[y + 1][x] == oldChar:
        # RECURSIVE CASE
        floodFill(image, x, y + 1, newChar, oldChar)
    if y - 1 &gt;= 0 and image[y - 1][x] == oldChar:
        # RECURSIVE CASE
        floodFill(image, x, y - 1, newChar, oldChar)
    if x + 1 &lt; WIDTH and image[y][x + 1] == oldChar:
        # RECURSIVE CASE
        floodFill(image, x + 1, y, newChar, oldChar)
    if x - 1 &gt;= 0 and image[y][x - 1] == oldChar:
        # RECURSIVE CASE
        floodFill(image, x - 1, y, newChar, oldChar)
    return # BASE CASE

def printImage(image):
    for y in range(HEIGHT):
        # Print each row.
        for x in range(WIDTH):
            # Print each column.
            sys.stdout.write(image[y][x])
        sys.stdout.write('\n')
    sys.stdout.write('\n')

printImage(im)
floodFill(im, 3, 3, 'o')
printImage(im)&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;..########################...........
..#......................#...#####...
..#..........########....#####...#...
..#..........#......#............#...
..#..........########.........####...
..######......................#......
.......#..#####.....###########......
.......####...#######................

..########################...........
..#oooooooooooooooooooooo#...#####...
..#oooooooooo########oooo#####ooo#...
..#oooooooooo#......#oooooooooooo#...
..#oooooooooo########ooooooooo####...
..######oooooooooooooooooooooo#......
.......#oo#####ooooo###########......
.......####...#######................&lt;/textarea&gt;




&lt;h2&gt;GCD, Greatest Common Denominator&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/gcdRecursive.py"&gt;Download gcdRecursive.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Factors are the numbers that are multiplied to produce a particular number.
Consider 4 × 6 = 24. In this equation, 4 and 6 are factors of 24. Because a
number’s factors can also be used to divide that number without leaving a
remainder, factors are also called divisors. &lt;/p&gt;

&lt;p&gt;The factors of 24 are 1, 2, 3, 4, 6, 8, 12, and 24. The factors of 30 are 1, 2, 3, 5, 6, 10, 15, and 30. Note that any number
will always have 1 and itself as its factors because 1 times a number is equal
to that number. Notice too that the list of factors for 24 and 30 have 1, 2,
3, and 6 in common. The greatest of these common factors is 6, so 6 is the
greatest common factor, more commonly known as the greatest common divisor
(GCD), of 24 and 30.&lt;/p&gt;

&lt;p&gt;Euclid, a mathematician who lived 2000 years ago, &lt;a href="https://en.wikipedia.org/wiki/Euclidean_algorithm"&gt;came up with a short
algorithm&lt;/a&gt; for finding the GCD of two numbers using modular arithmetic. His algorithm can be implemented with recursion:&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;def gcd(a, b):
    a, b = b % a, a
    if a == 0:
        # BASE CASE
        return b
    else:
        # RECURSIVE CASE
        return gcd(a, b)

print(gcd(42, 28))
print(gcd(28, 42))
print(gcd(345, 766))
&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;14
14
1&lt;/textarea&gt;




&lt;h2&gt;Hilbert Curve Fractal&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/hilbertCurve.py"&gt;Download hilbertCurve.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A space-filling curve is a one-dimensional line that curves around until it completely fills a two-dimensional space without crossing over itself. German mathematician David Hilbert described his space-filling Hilbert curve in 1891.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;import turtle
turtle.tracer(10, 0) # Increase the first argument to speed up the drawing.
turtle.setworldcoordinates(0, 0, 700, 700)
turtle.hideturtle()

LINE_LENGTH  = 5 # Try changing the line length by a litte.
ANGLE = 90 # Try changing the turning angle by a few degrees.
LEVELS = 6  # Try changing the recursive level by a litte.
DRAW_SOLID = False
#turtle.setheading(20) # Uncomment this line to draw the curve at an angle.

def hilbertCurveQuadrant(level, angle):
    if level == 0:
        # BASE CASE
        return
    else:
        # RECURSIVE CASE
        turtle.right(angle)
        hilbertCurveQuadrant(level - 1, -angle)
        turtle.forward(LINE_LENGTH)
        turtle.left(angle)
        hilbertCurveQuadrant(level - 1, angle)
        turtle.forward(LINE_LENGTH)
        hilbertCurveQuadrant(level - 1, angle)
        turtle.left(angle)
        turtle.forward(LINE_LENGTH)
        hilbertCurveQuadrant(level - 1, -angle)
        turtle.right(angle)
        return

def hilbertCurve(startingPosition):
    # Move to starting position.
    turtle.penup()
    turtle.goto(startingPosition)
    turtle.pendown()
    if DRAW_SOLID:
        turtle.begin_fill()

    hilbertCurveQuadrant(LEVELS, ANGLE) # Draw lower left quadrant.
    turtle.forward(LINE_LENGTH)

    hilbertCurveQuadrant(LEVELS, ANGLE) # Draw lower right quadrant.
    turtle.left(ANGLE)
    turtle.forward(LINE_LENGTH)
    turtle.left(ANGLE)

    hilbertCurveQuadrant(LEVELS, ANGLE) # Draw upper right quadrant.
    turtle.forward(LINE_LENGTH)

    hilbertCurveQuadrant(LEVELS, ANGLE) # Draw upper left quadrant.

    turtle.left(ANGLE)
    turtle.forward(LINE_LENGTH)
    turtle.left(ANGLE)
    if DRAW_SOLID:
        turtle.end_fill()

hilbertCurve((30, 350))
turtle.exitonclick()
&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/hilbertcurve.png" /&gt;&lt;/p&gt;




&lt;h2&gt;Karatsuba Multiplication&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/karatsubaMultiplication.py"&gt;Download karatsubaMultiplication.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Karatsuba multiplication is a fast, recursive algorithm discovered by Anatoly Karatsuba in 1960 that can multiply integers using addition, subtraction, and a pre-computed multiplication table of all products from single-digit numbers.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;import math

# Create a cache of all single-digit multiplication products:
MULT_TABLE = {}
for i in range(10):
    for j in range(10):
        MULT_TABLE[(i, j)] = i * j

def padZeros(numberString, numZeros, insertSide):
    """Return a string padded with zeros on the left or right side."""
    if insertSide == 'left':
        return '0' * numZeros + numberString
    elif insertSide == 'right':
        return numberString + '0' * numZeros

def karatsuba(x, y):
    """Multiply two integers with the Karatsuba algorithm. Note that
    the * operator isn't used anywhere in this function."""
    assert isinstance(x, int), 'x must be an integer'
    assert isinstance(y, int), 'y must be an integer'
    x = str(x)
    y = str(y)

    # At single-digits, look up the products in the multiplication table:
    if len(x) == 1 and len(y) == 1:  # BASE CASE
        print('Lookup', x, '*', y, '=', MULT_TABLE[(int(x), int(y))])
        return MULT_TABLE[(int(x), int(y))]

    # RECURSIVE CASE
    print('Multiplying', x, '*', y)

    # Pad with prepended zeros so that x and y are the same length:
    if len(x) &lt; len(y):
        # If x is shorter than y, pad x with zeros:
        x = padZeros(x, len(y) - len(x), 'left')
    elif len(y) &lt; len(x):
        # If y is shorter than x, pad y with zeros:
        y = padZeros(y, len(x) - len(y), 'left')
    # At this point, x and y have the same length.

    halfOfDigits = math.floor(len(x) / 2)

    # Split x into halves a &amp; b, split y into halves c &amp; d:
    a = int(x[:halfOfDigits])
    b = int(x[halfOfDigits:])
    c = int(y[:halfOfDigits])
    d = int(y[halfOfDigits:])

    # Make the recursive calls with these halves:
    step1Result = karatsuba(a, c) # Step 1: Multiply a &amp; c.
    step2Result = karatsuba(b, d) # Step 2: Multiply b &amp; d.
    step3Result = karatsuba(a + b, c + d) # Step 3: Multiply a + b &amp; c + d.

    # Step 4: Calculate Step 3 - Step 2 - Step 1:
    step4Result = step3Result - step2Result - step1Result

    # Step 5: Pad these numbers, then add them for the return value:
    step1Padding = (len(x) - halfOfDigits) + (len(x) - halfOfDigits)
    step1PaddedNum = int(padZeros(str(step1Result), step1Padding, 'right'))

    step4Padding = (len(x) - halfOfDigits)
    step4PaddedNum = int(padZeros(str(step4Result), step4Padding, 'right'))

    print('Solved', x, '*', y, '=', step1PaddedNum + step2Result + step4PaddedNum)

    return step1PaddedNum + step2Result + step4PaddedNum

# Example: 1357 x 2468 = 3349076
print('1357 * 2468 =', karatsuba(1357, 2468))
&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;Multiplying 1357 * 2468
Multiplying 13 * 24
Lookup 1 * 2 = 2
Lookup 3 * 4 = 12
Lookup 4 * 6 = 24
Solved 13 * 24 = 312
Multiplying 57 * 68
Lookup 5 * 6 = 30
Lookup 7 * 8 = 56
Multiplying 12 * 14
Lookup 1 * 1 = 1
Lookup 2 * 4 = 8
Lookup 3 * 5 = 15
Solved 12 * 14 = 168
Solved 57 * 68 = 3876
Multiplying 70 * 92
Lookup 7 * 9 = 63
Lookup 0 * 2 = 0
Multiplying 7 * 11
Lookup 0 * 1 = 0
Lookup 7 * 1 = 7
Lookup 7 * 2 = 14
Solved 07 * 11 = 77
Solved 70 * 92 = 6440
Solved 1357 * 2468 = 3349076
1357 * 2468 = 3349076&lt;/textarea&gt;




&lt;h2&gt;Koch Snowflake Fractal&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/kochSnowflake.py"&gt;Download kochSnowflake.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First introduced in 1902 by Swedish mathematician Helge von Koch, the Koch curve is one of the earliest fractals to be described mathematically. Its construction is simple: take a line of length b and divide it into three equal parts, each of length b / 3. Replace the middle section with a “bump” whose sides are also of length b / 3. This bump causes the Koch curve to be longer than the original line, since there are now four line segments of length b / 3. (We’ll exclude the original middle part of the line segment.) This bump-creation can be repeated on the new four line segments. The Koch Snowflake is three of these Koch curves connected together.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;import turtle
turtle.tracer(10, 0) # Increase the first argument to speed up the drawing.
turtle.setworldcoordinates(0, 0, 700, 700)
turtle.hideturtle()
turtle.pensize(2)

def drawKochCurve(startPosition, heading, length):
    if length &lt; 1:
        # BASE CASE
        return
    else:
        # RECURSIVE CASE
        # Move to the start position.
        recursiveArgs = []
        turtle.penup()
        turtle.goto(startPosition)
        turtle.setheading(heading)
        recursiveArgs.append({'position':turtle.position(),
                              'heading':turtle.heading()})

        # Erase the middle third.
        turtle.forward(length / 3)
        turtle.pencolor('white')
        turtle.pendown()
        turtle.forward(length / 3)

        # Draw the bump.
        turtle.backward(length / 3)
        turtle.left(60)
        recursiveArgs.append({'position':turtle.position(),
                              'heading':turtle.heading()})
        turtle.pencolor('black')
        turtle.forward(length / 3)
        turtle.right(120)
        recursiveArgs.append({'position':turtle.position(),
                              'heading':turtle.heading()})
        turtle.forward(length / 3)
        turtle.left(60)
        recursiveArgs.append({'position':turtle.position(),
                              'heading':turtle.heading()})

        for i in range(4):
            drawKochCurve(recursiveArgs[i]['position'],
                     recursiveArgs[i]['heading'],
                     length / 3)
        return

def drawKochSnowflake(startPosition, heading, length):
    # A Koch snowflake is three Koch curves in a triangle.

    # Move to the starting position
    turtle.penup()
    turtle.goto(startPosition)
    turtle.setheading(heading)

    for i in range(3):
        # Record the starting position and heading.
        curveStartingPosition = turtle.position()
        curveStartingHeading = turtle.heading()
        drawKochCurve(curveStartingPosition,
                      curveStartingHeading, length)

        # Move back to the start position for this side.
        turtle.penup()
        turtle.goto(curveStartingPosition)
        turtle.setheading(curveStartingHeading)

        # Move to the start position of the next side.
        turtle.forward(length)
        turtle.right(120)

drawKochSnowflake((100, 500), 0, 500)
turtle.exitonclick()
&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/kochsnowflake.png" /&gt;&lt;/p&gt;




&lt;h2&gt;Merge Sort&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/mergeSort.py"&gt;Download mergeSort.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Computer scientist John von Neumann developed merge sort in 1945 and it is an efficient, general-purpose sorting algorithm particularly used to sort linked list data structures.   It uses a divide-merge approach: each recursive call to mergeSort() divides the unsorted list into halves until they’ve been whittled down into lists with lengths of zero or one. Then, as the recursive calls return, these smaller lists are merged together into sorted order. When the last recursive call has returned, the entire list will have been sorted.&lt;/p&gt;

&lt;p&gt;For example, the divide step takes a list, such as [0, 7, 6, 3, 1, 2, 5, 4], and splits it into two lists, like [0, 7, 6, 3] and [1, 2, 5, 4], to pass to two recursive function calls. At the base case, the lists have been divided into lists of zero or one item. After the recursive calls return, the code merges the two lists into a larger list. If the two lists are in sorted order (and lists of zero or one item are sorted by their nature), merging will also produce a sorted list. The end result is that the original mergeSort() call returns the full list in sorted order.
&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;import math

def mergeSort(items):
    print('.....mergeSort() called on:', items)

    if len(items) == 0 or len(items) == 1:  # BASE CASE
        # With only zero or one items, `items` is already sorted.
        return items

    # RECURSIVE CASE
    # Round down if `items` doesn't divide in half evenly:
    iMiddle = math.floor(len(items) / 2)

    print('................Split into:', items[:iMiddle], 'and', items[iMiddle:])

    left = mergeSort(items[:iMiddle])
    right = mergeSort(items[iMiddle:])

    # At this point, `left` should be sorted and `right` should be
    # sorted. We can merge them into a single sorted list like a zipper.
    sortedResult = []
    iLeft = 0
    iRight = 0
    while (len(sortedResult) &lt; len(items)):
        # Append the smaller of left &amp; right's value to `sortedResult`.
        if left[iLeft] &lt; right[iRight]:
            sortedResult.append(left[iLeft])
            iLeft += 1
        else:
            sortedResult.append(right[iRight])
            iRight += 1

        # If one of the pointers has reached the end of its list,
        # put the rest of the other list into `sortedResult`.
        if iLeft == len(left):
            sortedResult.extend(right[iRight:])
            break
        elif iRight == len(right):
            sortedResult.extend(left[iLeft:])
            break

    print('The two halves merged into:', sortedResult)

    return sortedResult  # Returns a sorted version of `items`.

myList = [0, 7, 6, 3, 1, 2, 5, 4]
myList = mergeSort(myList)
print(myList)&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;.....mergeSort() called on: [0, 7, 6, 3, 1, 2, 5, 4]
................Split into: [0, 7, 6, 3] and [1, 2, 5, 4]
.....mergeSort() called on: [0, 7, 6, 3]
................Split into: [0, 7] and [6, 3]
.....mergeSort() called on: [0, 7]
................Split into: [0] and [7]
.....mergeSort() called on: [0]
.....mergeSort() called on: [7]
The two halves merged into: [0, 7]
.....mergeSort() called on: [6, 3]
................Split into: [6] and [3]
.....mergeSort() called on: [6]
.....mergeSort() called on: [3]
The two halves merged into: [3, 6]
The two halves merged into: [0, 3, 6, 7]
.....mergeSort() called on: [1, 2, 5, 4]
................Split into: [1, 2] and [5, 4]
.....mergeSort() called on: [1, 2]
................Split into: [1] and [2]
.....mergeSort() called on: [1]
.....mergeSort() called on: [2]
The two halves merged into: [1, 2]
.....mergeSort() called on: [5, 4]
................Split into: [5] and [4]
.....mergeSort() called on: [5]
.....mergeSort() called on: [4]
The two halves merged into: [4, 5]
The two halves merged into: [1, 2, 4, 5]
The two halves merged into: [0, 1, 2, 3, 4, 5, 6, 7]
[0, 1, 2, 3, 4, 5, 6, 7]
&lt;/textarea&gt;




&lt;h2&gt;Permutations&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/permutations.py"&gt;Download permutations.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A permutation of a set is a specific ordering of all elements in the set. For example, there are six permutations for the set {A, B, C}: ABC, ACB, BAC, BCA, CAB, and CBA. We call these permutations without repetition or permutations without replacement, because each element doesn’t appear in the permutation more than once.&lt;/p&gt;

&lt;p&gt;Imagine you must arrange the seating chart for a wedding reception with delicate social requirements. Some of the guests hate each other and while others demand to sit near an influential guest. The seats at the table form one long row, rather than a circle. It’d be helpful for your planning to see every possible ordering of guests, that is, every permutation without repetition of the set of guests. There is no repetition because each guest will only appear in the seating chart once. Let’s use a simple example of Alice, Bob, and Carol, or {A, B, C}.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;def getPerms(chars, indent=0):
    print('.' * indent + 'Start of getPerms("' + chars + '")')
    if len(chars) == 1:
        # BASE CASE
        print('.' * indent + 'When chars = "' + chars + '" base case returns', chars)
        return [chars]

    # RECURSIVE CASE
    permutations = []
    head = chars[0]
    tail = chars[1:]
    tailPermutations = getPerms(tail, indent + 1)
    for tailPerm in tailPermutations:
        print('.' * indent + 'When chars =', chars, 'putting head', head, 'in all places in', tailPerm)
        for i in range(len(tailPerm) + 1):
            newPerm = tailPerm[0:i] + head + tailPerm[i:]
            print('.' * indent + 'New permutation:', newPerm)
            permutations.append(newPerm)
    print('.' * indent + 'When chars =', chars, 'results are', permutations)
    return permutations

print('Permutations of "ABCD":')
print('Results:', ','.join(getPerms('ABCD')))
&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;Permutations of "ABCD":
Start of getPerms("ABCD")
.Start of getPerms("BCD")
..Start of getPerms("CD")
...Start of getPerms("D")
...When chars = "D" base case returns D
..When chars = CD putting head C in all places in D
..New permutation: CD
..New permutation: DC
..When chars = CD results are ['CD', 'DC']
.When chars = BCD putting head B in all places in CD
.New permutation: BCD
.New permutation: CBD
.New permutation: CDB
.When chars = BCD putting head B in all places in DC
.New permutation: BDC
.New permutation: DBC
.New permutation: DCB
.When chars = BCD results are ['BCD', 'CBD', 'CDB', 'BDC', 'DBC', 'DCB']
When chars = ABCD putting head A in all places in BCD
New permutation: ABCD
New permutation: BACD
New permutation: BCAD
New permutation: BCDA
When chars = ABCD putting head A in all places in CBD
New permutation: ACBD
New permutation: CABD
New permutation: CBAD
New permutation: CBDA
When chars = ABCD putting head A in all places in CDB
New permutation: ACDB
New permutation: CADB
New permutation: CDAB
New permutation: CDBA
When chars = ABCD putting head A in all places in BDC
New permutation: ABDC
New permutation: BADC
New permutation: BDAC
New permutation: BDCA
When chars = ABCD putting head A in all places in DBC
New permutation: ADBC
New permutation: DABC
New permutation: DBAC
New permutation: DBCA
When chars = ABCD putting head A in all places in DCB
New permutation: ADCB
New permutation: DACB
New permutation: DCAB
New permutation: DCBA
When chars = ABCD results are ['ABCD', 'BACD', 'BCAD', 'BCDA', 'ACBD', 'CABD', 'CBAD', 'CBDA', 'ACDB', 'CADB', 'CDAB', 'CDBA', 'ABDC', 'BADC', 'BDAC', 'BDCA', 'ADBC', 'DABC', 'DBAC', 'DBCA', 'ADCB', 'DACB', 'DCAB', 'DCBA']
Results: ABCD,BACD,BCAD,BCDA,ACBD,CABD,CBAD,CBDA,ACDB,CADB,CDAB,CDBA,ABDC,BADC,BDAC,BDCA,ADBC,DABC,DBAC,DBCA,ADCB,DACB,DCAB,DCBA&lt;/textarea&gt;




&lt;h2&gt;Quicksort&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/quicksort.py"&gt;Download quicksort.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quicksort uses a divide-and-conquer technique called partitioning. Think of partitioning this way: imagine you have a large pile of unalphabetized books. Grabbing one book and placing it in the right spot on the shelf means you’ll spend a lot of time rearranging the bookshelf as it gets full. It would help if you first turned the pile of books into two piles: an A to M pile and an N to Z pile. (In this example, M would be our pivot.)&lt;/p&gt;


&lt;p&gt;You haven’t sorted the pile, but you have partitioned it. And partitioning is easy: the book doesn’t have to go into the correct place in one of the two piles, it just has to go into the correct pile. Then you can further partition these two piles into four piles: A to G, H to M, N to T, and U to Z. This is shown in Figure 5-x. If you keep partitioning, you end up with piles that contain one book each (the base case), and the piles are now in sorted order. This repeated partitioning is how quicksort works. &lt;/p&gt;

&lt;p&gt;In our implementation, each call to quicksort() is given an array of items to sort. It is also given left and right arguments specifying the range of indexes in that array to sort, similar to binarySearch()’s left and right arguments. The algorithm selects a pivot value to compare with the other values in the range, then places the values to either the left side of the range (if they’re less than the pivot value) or the right side (if they’re greater than the pivot value). This is the partition step. Next, the quicksort() function is recursively called on these two, smaller ranges until a range has been reduced to zero. The list becomes more and more sorted as the recursive calls are made, until finally the entire list is in correct order.&lt;/p&gt;


&lt;textarea cols="80" rows="10"&gt;def quicksort(items, left=None, right=None):
    # By default, `left` and `right` span the entire range of `items`:
    if left is None:
        left = 0  # `left` defaults to the 0 index.
    if right is None:
        right = len(items) - 1  # `right` defaults to the last index.

    print('\nquicksort() called on this range:', items[left:right + 1])
    print('................The full list is:', items)

    if right &lt;= left:
        return  # BASE CASE

    # START OF THE PARTITIONING
    i = left  # i starts at the left end of the range.
    pivotValue = items[right]  # Select the last value for the pivot.

    print('....................The pivot is:', pivotValue)

    # Iterate up to, but not including, the pivot:
    for j in range(left, right):
        # If a value is less than the pivot, swap it so that it's on the
        # left side of `items`:
        if items[j] &lt;= pivotValue:
            # Swap these two values:
            items[i], items[j] = items[j], items[i]
            i += 1

    # Put the pivot on the left side of `items`:
    items[i], items[right] = items[right], items[i]
    # END OF THE PARTITIONING

    print('....After swapping, the range is:', items[left:right + 1])
    print('Recursively calling quicksort on:', items[left:i], 'and', items[i + 1:right + 1])

    # Call quicksort() on the two partitions:
    quicksort(items, left, i - 1)   # RECURSIVE CASE
    quicksort(items, i + 1, right)  # RECURSIVE CASE

myList = [0, 7, 6, 3, 1, 2, 5, 4]
quicksort(myList)
print('Sorted:', myList)
&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;
quicksort() called on this range: [0, 7, 6, 3, 1, 2, 5, 4]
................The full list is: [0, 7, 6, 3, 1, 2, 5, 4]
....................The pivot is: 4
....After swapping, the range is: [0, 3, 1, 2, 4, 7, 5, 6]
Recursively calling quicksort on: [0, 3, 1, 2] and [7, 5, 6]

quicksort() called on this range: [0, 3, 1, 2]
................The full list is: [0, 3, 1, 2, 4, 7, 5, 6]
....................The pivot is: 2
....After swapping, the range is: [0, 1, 2, 3]
Recursively calling quicksort on: [0, 1] and [3]

quicksort() called on this range: [0, 1]
................The full list is: [0, 1, 2, 3, 4, 7, 5, 6]
....................The pivot is: 1
....After swapping, the range is: [0, 1]
Recursively calling quicksort on: [0] and []

quicksort() called on this range: [0]
................The full list is: [0, 1, 2, 3, 4, 7, 5, 6]

quicksort() called on this range: []
................The full list is: [0, 1, 2, 3, 4, 7, 5, 6]

quicksort() called on this range: [3]
................The full list is: [0, 1, 2, 3, 4, 7, 5, 6]

quicksort() called on this range: [7, 5, 6]
................The full list is: [0, 1, 2, 3, 4, 7, 5, 6]
....................The pivot is: 6
....After swapping, the range is: [5, 6, 7]
Recursively calling quicksort on: [5] and [7]

quicksort() called on this range: [5]
................The full list is: [0, 1, 2, 3, 4, 5, 6, 7]

quicksort() called on this range: [7]
................The full list is: [0, 1, 2, 3, 4, 5, 6, 7]
Sorted: [0, 1, 2, 3, 4, 5, 6, 7]&lt;/textarea&gt;




&lt;h2&gt;Shortest Possible Recursive Function&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/shortest.py"&gt;Download shortest.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a demonstration, this is the shortest possible recursive function. It is merely a function that calls itself. It will result in a stack overflow error.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;def shortest():
    shortest()

shortest()&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;Traceback (most recent call last):
  File "C:\github\inventwithpythondotcom\static\blogstatic\recursion\shortest.py", line 4, in &amp;lt;module&amp;gt;
    shortest()
  File "C:\github\inventwithpythondotcom\static\blogstatic\recursion\shortest.py", line 2, in shortest
    shortest()
  File "C:\github\inventwithpythondotcom\static\blogstatic\recursion\shortest.py", line 2, in shortest
    shortest()
  File "C:\github\inventwithpythondotcom\static\blogstatic\recursion\shortest.py", line 2, in shortest
    shortest()
  [Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded&lt;/textarea&gt;




&lt;h2&gt;Sierpinski Carpet&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/sierpinskiCarpet.py"&gt;Download sierpinskiCarpet.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The sierpinski carpet is a square with an empty center and fractal sierpinski carpets in the eight perimeter areas of the square.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;import turtle
turtle.tracer(10, 0) # Increase the first argument to speed up the drawing.
turtle.setworldcoordinates(0, 0, 700, 700)
turtle.hideturtle()

MIN_SIZE = 6 # Try changing this to decrease/increase the amount of recursion.
DRAW_SOLID = True

def isTooSmall(width, height):
    # Determine if the rectangle is too small to draw.
    return width &lt; MIN_SIZE or height &lt; MIN_SIZE

def drawCarpet(x, y, width, height):
    # The x and y are the lower left corner of the carpet.

    # Move the pen into position.
    turtle.penup()
    turtle.goto(x, y)

    # Draw the outer rectangle.
    turtle.pendown()
    if DRAW_SOLID:
        turtle.fillcolor('black')
        turtle.begin_fill()
    turtle.goto(x, y + height)
    turtle.goto(x + width, y + height)
    turtle.goto(x + width, y)
    turtle.goto(x, y)
    if DRAW_SOLID:
        turtle.end_fill()
    turtle.penup()

    # Draw the inner rectangles.
    drawInnerRectangle(x, y, width, height)

def drawInnerRectangle(x, y, width, height):
    if isTooSmall(width, height):
        # BASE CASE
        return
    else:
        # RECURSIVE CASE

        oneThirdWidth = width / 3
        oneThirdHeight = height / 3
        twoThirdsWidth = 2 * (width / 3)
        twoThirdsHeight = 2 * (height / 3)

        # Move into position.
        turtle.penup()
        turtle.goto(x + oneThirdWidth, y + oneThirdHeight)

        # Draw the inner rectangle.
        if DRAW_SOLID:
            turtle.fillcolor('white')
            turtle.begin_fill()
        turtle.pendown()
        turtle.goto(x + oneThirdWidth, y + twoThirdsHeight)
        turtle.goto(x + twoThirdsWidth, y + twoThirdsHeight)
        turtle.goto(x + twoThirdsWidth, y + oneThirdHeight)
        turtle.goto(x + oneThirdWidth, y + oneThirdHeight)
        turtle.penup()
        if DRAW_SOLID:
            turtle.end_fill()

        # Draw the inner rectangles across the top.
        drawInnerRectangle(x, y + twoThirdsHeight, oneThirdWidth, oneThirdHeight)
        drawInnerRectangle(x + oneThirdWidth, y + twoThirdsHeight, oneThirdWidth, oneThirdHeight)
        drawInnerRectangle(x + twoThirdsWidth, y + twoThirdsHeight, oneThirdWidth, oneThirdHeight)

        # Draw the inner rectangles across the middle.
        drawInnerRectangle(x, y + oneThirdHeight, oneThirdWidth, oneThirdHeight)
        drawInnerRectangle(x + twoThirdsWidth, y + oneThirdHeight, oneThirdWidth, oneThirdHeight)

        # Draw the inner rectangles across the bottom.
        drawInnerRectangle(x, y, oneThirdWidth, oneThirdHeight)
        drawInnerRectangle(x + oneThirdWidth, y, oneThirdWidth, oneThirdHeight)
        drawInnerRectangle(x + twoThirdsWidth, y, oneThirdWidth, oneThirdHeight)

drawCarpet(50, 50, 600, 600)
turtle.exitonclick()
&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/sierpinskicarpet.png" /&gt;&lt;/p&gt;






&lt;h2&gt;Sierpinski Triangle&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/sierpinskiTriangle.py"&gt;Download sierpinskiTriangle.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The sierpinski triangle is an equilateral triangle that contains three sierpinski triangles. It looks like the triforce from Zelda video games.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;import turtle
turtle.tracer(100, 0) # Increase the first argument to speed up the drawing.
turtle.setworldcoordinates(0, 0, 700, 700)
turtle.hideturtle()

MIN_SIZE = 4 # Try changing this to decrease/increase the amount of recursion.

def midpoint(startx, starty, endx, endy):
    # Return the x, y coordinate in the middle of the four given parameters.
    xDiff = abs(startx - endx)
    yDiff = abs(starty - endy)
    return (min(startx, endx) + (xDiff / 2.0), min(starty, endy) + (yDiff / 2.0))

def isTooSmall(ax, ay, bx, by, cx, cy):
    # Determine if the triangle is too small to draw.
    width = max(ax, bx, cx) - min(ax, bx, cx)
    height = max(ay, by, cy) - min(ay, by, cy)
    return width &lt; MIN_SIZE or height &lt; MIN_SIZE

def drawTriangle(ax, ay, bx, by, cx, cy):
    if isTooSmall(ax, ay, bx, by, cx, cy):
        # BASE CASE
        return
    else:
        # RECURSIVE CASE
        # Draw the triangle.
        turtle.penup()
        turtle.goto(ax, ay)
        turtle.pendown()
        turtle.goto(bx, by)
        turtle.goto(cx, cy)
        turtle.goto(ax, ay)
        turtle.penup()

        # Calculate midpoints between points A, B, and C.
        mid_ab = midpoint(ax, ay, bx, by)
        mid_bc = midpoint(bx, by, cx, cy)
        mid_ca = midpoint(cx, cy, ax, ay)

        # Draw the three inner triangles.
        drawTriangle(ax, ay, mid_ab[0], mid_ab[1], mid_ca[0], mid_ca[1])
        drawTriangle(mid_ab[0], mid_ab[1], bx, by, mid_bc[0], mid_bc[1])
        drawTriangle(mid_ca[0], mid_ca[1], mid_bc[0], mid_bc[1], cx, cy)
        return

# Draw an equilateral Sierpinski triangle.
drawTriangle(50, 50, 350, 650, 650, 50)

# Draw a skewed Sierpinski triangle.
#drawTriangle(30, 250, 680, 600, 500, 80)

turtle.exitonclick()
&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/sierpinskitriangle.png" /&gt;&lt;/p&gt;




&lt;h2&gt;Towers of Hanoi&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/towerOfHanoiSolver.py"&gt;Download towerOfHanoiSolver.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Tower of Hanoi is a puzzle involving a tower of stacked disks. The puzzle begins with the largest disk on the bottom and the disk sizes decrease going up. The disks have a hole in their center so they can be stacked on top of each other through a pole. To solve the puzzle, the player must move the stack of disks from one pole to another while following three rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The player can move only one disk at a time.&lt;/li&gt;
&lt;li&gt;The player can only move disks to and from the top of a tower.&lt;/li&gt;
&lt;li&gt;The player can never place a larger disk on top of a smaller disk.&lt;/li&gt;
&lt;/ol&gt;

&lt;textarea cols="80" rows="10"&gt;# Set up towers A, B, and C. The end of the list is the top of the tower.
TOTAL_DISKS = 6

# Populate Tower A:
TOWERS = {'A': list(reversed(range(1, TOTAL_DISKS + 1))),
          'B': [],
          'C': []}

def printDisk(diskNum):
    # Print a single disk of width diskNum.
    emptySpace = ' ' * (TOTAL_DISKS - diskNum)
    if diskNum == 0:
        # Just draw the pole.
        print(emptySpace + '||' + emptySpace, end='')
    else:
        # Draw the disk.
        diskSpace = '@' * diskNum
        diskNumLabel = str(diskNum).rjust(2, '_')
        print(emptySpace + diskSpace + diskNumLabel + diskSpace + emptySpace, end='')

def printTowers():
    # Print all three towers.
    for level in range(TOTAL_DISKS, -1, -1):
        for tower in (TOWERS['A'], TOWERS['B'], TOWERS['C']):
            if level &gt;= len(tower):
                printDisk(0)
            else:
                printDisk(tower[level])
        print()
    # Print the tower labels A, B, and C.
    emptySpace = ' ' * (TOTAL_DISKS)
    print('%s A%s%s B%s%s C\n' % (emptySpace, emptySpace, emptySpace, emptySpace, emptySpace))

def moveOneDisk(startTower, endTower):
    # Move the top disk from startTower to endTower.
    disk = TOWERS[startTower].pop()
    TOWERS[endTower].append(disk)

def solve(numberOfDisks, startTower, endTower, tempTower):
    # Move the top numberOfDisks disks from startTower to endTower.
    if numberOfDisks == 1:
        # BASE CASE
        moveOneDisk(startTower, endTower)
        printTowers()
        return
    else:
        # RECURSIVE CASE
        solve(numberOfDisks - 1, startTower, tempTower, endTower)
        moveOneDisk(startTower, endTower)
        printTowers()
        solve(numberOfDisks - 1, tempTower, endTower, startTower)
        return


# Solve
printTowers()
solve(TOTAL_DISKS, 'A', 'B', 'C')

# Uncomment to enable interactive mode:
#while True:
#    printTowers()
#    print('Enter letter of start tower and the end tower. (A, B, C) Or Q to quit.')
#    move = input().upper()
#    if move == 'Q':
#        sys.exit()
#    elif move[0] in 'ABC' and move[1] in 'ABC' and move[0] != move[1]:
#        moveOneDisk(move[0], move[1])

&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;textarea cols="80" rows="5"&gt;      ||            ||            ||
     @_1@           ||            ||
    @@_2@@          ||            ||
   @@@_3@@@         ||            ||
  @@@@_4@@@@        ||            ||
 @@@@@_5@@@@@       ||            ||
@@@@@@_6@@@@@@      ||            ||
       A             B             C

      ||            ||            ||
      ||            ||            ||
    @@_2@@          ||            ||
   @@@_3@@@         ||            ||
  @@@@_4@@@@        ||            ||
 @@@@@_5@@@@@       ||            ||
@@@@@@_6@@@@@@      ||           @_1@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
   @@@_3@@@         ||            ||
  @@@@_4@@@@        ||            ||
 @@@@@_5@@@@@       ||            ||
@@@@@@_6@@@@@@    @@_2@@         @_1@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
   @@@_3@@@         ||            ||
  @@@@_4@@@@        ||            ||
 @@@@@_5@@@@@      @_1@           ||
@@@@@@_6@@@@@@    @@_2@@          ||
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
  @@@@_4@@@@        ||            ||
 @@@@@_5@@@@@      @_1@           ||
@@@@@@_6@@@@@@    @@_2@@       @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
     @_1@           ||            ||
  @@@@_4@@@@        ||            ||
 @@@@@_5@@@@@       ||            ||
@@@@@@_6@@@@@@    @@_2@@       @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
     @_1@           ||            ||
  @@@@_4@@@@        ||            ||
 @@@@@_5@@@@@       ||          @@_2@@
@@@@@@_6@@@@@@      ||         @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
  @@@@_4@@@@        ||           @_1@
 @@@@@_5@@@@@       ||          @@_2@@
@@@@@@_6@@@@@@      ||         @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||           @_1@
 @@@@@_5@@@@@       ||          @@_2@@
@@@@@@_6@@@@@@  @@@@_4@@@@     @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
 @@@@@_5@@@@@      @_1@         @@_2@@
@@@@@@_6@@@@@@  @@@@_4@@@@     @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
    @@_2@@          ||            ||
 @@@@@_5@@@@@      @_1@           ||
@@@@@@_6@@@@@@  @@@@_4@@@@     @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
     @_1@           ||            ||
    @@_2@@          ||            ||
 @@@@@_5@@@@@       ||            ||
@@@@@@_6@@@@@@  @@@@_4@@@@     @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
     @_1@           ||            ||
    @@_2@@          ||            ||
 @@@@@_5@@@@@    @@@_3@@@         ||
@@@@@@_6@@@@@@  @@@@_4@@@@        ||
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
    @@_2@@          ||            ||
 @@@@@_5@@@@@    @@@_3@@@         ||
@@@@@@_6@@@@@@  @@@@_4@@@@       @_1@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||          @@_2@@          ||
 @@@@@_5@@@@@    @@@_3@@@         ||
@@@@@@_6@@@@@@  @@@@_4@@@@       @_1@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||           @_1@           ||
      ||          @@_2@@          ||
 @@@@@_5@@@@@    @@@_3@@@         ||
@@@@@@_6@@@@@@  @@@@_4@@@@        ||
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||           @_1@           ||
      ||          @@_2@@          ||
      ||         @@@_3@@@         ||
@@@@@@_6@@@@@@  @@@@_4@@@@   @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||          @@_2@@          ||
     @_1@        @@@_3@@@         ||
@@@@@@_6@@@@@@  @@@@_4@@@@   @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
     @_1@        @@@_3@@@       @@_2@@
@@@@@@_6@@@@@@  @@@@_4@@@@   @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||           @_1@
      ||         @@@_3@@@       @@_2@@
@@@@@@_6@@@@@@  @@@@_4@@@@   @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||           @_1@
   @@@_3@@@         ||          @@_2@@
@@@@@@_6@@@@@@  @@@@_4@@@@   @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
   @@@_3@@@        @_1@         @@_2@@
@@@@@@_6@@@@@@  @@@@_4@@@@   @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
    @@_2@@          ||            ||
   @@@_3@@@        @_1@           ||
@@@@@@_6@@@@@@  @@@@_4@@@@   @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
     @_1@           ||            ||
    @@_2@@          ||            ||
   @@@_3@@@         ||            ||
@@@@@@_6@@@@@@  @@@@_4@@@@   @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
     @_1@           ||            ||
    @@_2@@          ||            ||
   @@@_3@@@         ||        @@@@_4@@@@
@@@@@@_6@@@@@@      ||       @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
    @@_2@@          ||           @_1@
   @@@_3@@@         ||        @@@@_4@@@@
@@@@@@_6@@@@@@      ||       @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||           @_1@
   @@@_3@@@         ||        @@@@_4@@@@
@@@@@@_6@@@@@@    @@_2@@     @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
   @@@_3@@@        @_1@       @@@@_4@@@@
@@@@@@_6@@@@@@    @@_2@@     @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||         @@@_3@@@
      ||           @_1@       @@@@_4@@@@
@@@@@@_6@@@@@@    @@_2@@     @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||         @@@_3@@@
     @_1@           ||        @@@@_4@@@@
@@@@@@_6@@@@@@    @@_2@@     @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||          @@_2@@
      ||            ||         @@@_3@@@
     @_1@           ||        @@@@_4@@@@
@@@@@@_6@@@@@@      ||       @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||           @_1@
      ||            ||          @@_2@@
      ||            ||         @@@_3@@@
      ||            ||        @@@@_4@@@@
@@@@@@_6@@@@@@      ||       @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||           @_1@
      ||            ||          @@_2@@
      ||            ||         @@@_3@@@
      ||            ||        @@@@_4@@@@
      ||      @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||          @@_2@@
      ||            ||         @@@_3@@@
      ||           @_1@       @@@@_4@@@@
      ||      @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||         @@@_3@@@
      ||           @_1@       @@@@_4@@@@
    @@_2@@    @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||         @@@_3@@@
     @_1@           ||        @@@@_4@@@@
    @@_2@@    @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
     @_1@        @@@_3@@@     @@@@_4@@@@
    @@_2@@    @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||           @_1@
      ||         @@@_3@@@     @@@@_4@@@@
    @@_2@@    @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||          @@_2@@         @_1@
      ||         @@@_3@@@     @@@@_4@@@@
      ||      @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||           @_1@           ||
      ||          @@_2@@          ||
      ||         @@@_3@@@     @@@@_4@@@@
      ||      @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||           @_1@           ||
      ||          @@_2@@          ||
      ||         @@@_3@@@         ||
  @@@@_4@@@@  @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||          @@_2@@          ||
     @_1@        @@@_3@@@         ||
  @@@@_4@@@@  @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
     @_1@        @@@_3@@@       @@_2@@
  @@@@_4@@@@  @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||           @_1@
      ||         @@@_3@@@       @@_2@@
  @@@@_4@@@@  @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||           @_1@
   @@@_3@@@         ||          @@_2@@
  @@@@_4@@@@  @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
   @@@_3@@@        @_1@         @@_2@@
  @@@@_4@@@@  @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
    @@_2@@          ||            ||
   @@@_3@@@        @_1@           ||
  @@@@_4@@@@  @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
     @_1@           ||            ||
    @@_2@@          ||            ||
   @@@_3@@@         ||            ||
  @@@@_4@@@@  @@@@@@_6@@@@@@ @@@@@_5@@@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
     @_1@           ||            ||
    @@_2@@          ||            ||
   @@@_3@@@    @@@@@_5@@@@@       ||
  @@@@_4@@@@  @@@@@@_6@@@@@@      ||
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
    @@_2@@          ||            ||
   @@@_3@@@    @@@@@_5@@@@@       ||
  @@@@_4@@@@  @@@@@@_6@@@@@@     @_1@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||          @@_2@@          ||
   @@@_3@@@    @@@@@_5@@@@@       ||
  @@@@_4@@@@  @@@@@@_6@@@@@@     @_1@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||           @_1@           ||
      ||          @@_2@@          ||
   @@@_3@@@    @@@@@_5@@@@@       ||
  @@@@_4@@@@  @@@@@@_6@@@@@@      ||
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||           @_1@           ||
      ||          @@_2@@          ||
      ||       @@@@@_5@@@@@       ||
  @@@@_4@@@@  @@@@@@_6@@@@@@   @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||          @@_2@@          ||
     @_1@      @@@@@_5@@@@@       ||
  @@@@_4@@@@  @@@@@@_6@@@@@@   @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
     @_1@      @@@@@_5@@@@@     @@_2@@
  @@@@_4@@@@  @@@@@@_6@@@@@@   @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||           @_1@
      ||       @@@@@_5@@@@@     @@_2@@
  @@@@_4@@@@  @@@@@@_6@@@@@@   @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||        @@@@_4@@@@       @_1@
      ||       @@@@@_5@@@@@     @@_2@@
      ||      @@@@@@_6@@@@@@   @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||           @_1@           ||
      ||        @@@@_4@@@@        ||
      ||       @@@@@_5@@@@@     @@_2@@
      ||      @@@@@@_6@@@@@@   @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||           @_1@           ||
      ||        @@@@_4@@@@        ||
      ||       @@@@@_5@@@@@       ||
    @@_2@@    @@@@@@_6@@@@@@   @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||        @@@@_4@@@@        ||
     @_1@      @@@@@_5@@@@@       ||
    @@_2@@    @@@@@@_6@@@@@@   @@@_3@@@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||         @@@_3@@@         ||
      ||        @@@@_4@@@@        ||
     @_1@      @@@@@_5@@@@@       ||
    @@_2@@    @@@@@@_6@@@@@@      ||
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||            ||            ||
      ||         @@@_3@@@         ||
      ||        @@@@_4@@@@        ||
      ||       @@@@@_5@@@@@       ||
    @@_2@@    @@@@@@_6@@@@@@     @_1@
       A             B             C

      ||            ||            ||
      ||            ||            ||
      ||          @@_2@@          ||
      ||         @@@_3@@@         ||
      ||        @@@@_4@@@@        ||
      ||       @@@@@_5@@@@@       ||
      ||      @@@@@@_6@@@@@@     @_1@
       A             B             C

      ||            ||            ||
      ||           @_1@           ||
      ||          @@_2@@          ||
      ||         @@@_3@@@         ||
      ||        @@@@_4@@@@        ||
      ||       @@@@@_5@@@@@       ||
      ||      @@@@@@_6@@@@@@      ||
       A             B             C
&lt;/textarea&gt;




&lt;h2&gt;Tree Fractal&lt;/h2&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion/fractalTree.py"&gt;Download fractalTree.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Trees can be algorithmically generated as a fractal. These trees are generated by having each branch produce two more similar but smaller branches.&lt;/p&gt;

&lt;textarea cols="80" rows="10"&gt;import random
import time
import turtle
turtle.tracer(1000, 0) # Increase the first argument to speed up the drawing.
turtle.setworldcoordinates(0, 0, 700, 700)
turtle.hideturtle()

def drawBranch(startPosition, direction, branchLength):
    if branchLength &lt; 5:
        # BASE CASE
        return

    # Go to the starting point &amp; direction.
    turtle.penup()
    turtle.goto(startPosition)
    turtle.setheading(direction)

    # Draw the branch (thickness is 1/7 the length).
    turtle.pendown()
    turtle.pensize(max(branchLength / 7.0, 1))
    turtle.forward(branchLength)

    # Record the position of the branch's end.
    endPosition = turtle.position()
    leftDirection = direction + LEFT_ANGLE
    leftBranchLength = branchLength - LEFT_DECREASE
    rightDirection = direction - RIGHT_ANGLE
    rightBranchLength = branchLength - RIGHT_DECREASE

    # RECURSIVE CASE
    drawBranch(endPosition, leftDirection, leftBranchLength)
    drawBranch(endPosition, rightDirection, rightBranchLength)

seed = 0
while True:
    # Get psuedorandom numbers for the branch properties.
    random.seed(seed)
    LEFT_ANGLE     = random.randint(10,  30)
    LEFT_DECREASE  = random.randint( 8,  15)
    RIGHT_ANGLE    = random.randint(10,  30)
    RIGHT_DECREASE = random.randint( 8,  15)
    START_LENGTH   = random.randint(80, 120)

    # Write out the seed number.
    turtle.clear()
    turtle.penup()
    turtle.goto(10, 10)
    turtle.write('Seed: %s' % (seed))

    # Draw the tree.
    drawBranch((350, 10), 90, START_LENGTH)
    turtle.update()
    time.sleep(2)

    seed = seed + 1

&lt;/textarea&gt;

&lt;p&gt;The output of this program looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/recursion/fractaltree.png" /&gt;&lt;/p&gt;


&lt;h2&gt;Learn More About Recursion&lt;/h2&gt;

&lt;p&gt;To learn how these programs work and learn more about recursion in general, you can check out my book, &lt;a href="https://inventwithpython.com/recursion/"&gt;The Recursive Book of Recursion&lt;/a&gt;. You can get an ebook copy for free when you &lt;a href="https://nostarch.com/recursive-book-recursion"&gt;buy a print book from the publisher, No Starch Press&lt;/a&gt;.&lt;/p&gt;







</content><category term="misc"></category><category term="recursion"></category></entry><entry><title>What is a Python Generator? (Implementing Your Own range() Function)</title><link href="https://inventwithpython.com/blog/what-is-a-generator.html" rel="alternate"></link><published>2021-09-24T12:00:00-04:00</published><updated>2021-09-24T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-09-24:/blog/what-is-a-generator.html</id><summary type="html">Generators in Python (also called generator functions) are used to create a series of values one at a time. Let's learn how they work by re-creating the built-in &lt;code&gt;range()&lt;/code&gt; function.</summary><content type="html">

&lt;p&gt;Generators in Python (also called generator functions) are used to create a series of values one at a time. Let's learn how they work by re-creating the built-in &lt;code&gt;range()&lt;/code&gt; function. This can keep your program from requiring the large amounts of memory needed if you generated all the values in the series at once. For example, in Python 2 running &lt;code&gt;for i in range(1000000):&lt;/code&gt; the &lt;code&gt;range()&lt;/code&gt; function would create and return a list with one million integers in it. This takes up a large amount of memory even though the for loop only needs one integer at a time. This was fixed in Python 3, where &lt;code&gt;range()&lt;/code&gt; now produces one integer at a time. Generator functions let you create one value at a time with any sort of data, not just ranges of integers.&lt;/p&gt;

&lt;p&gt;If you find yourself creating a large list of values with, say, a list comprehension or &lt;code&gt;for&lt;/code&gt; loop, but you only need one value at a time, you should consider using a generator instead. This is especially true if you are creating large lists with hundreds of thousands of items or more. Using a generator is like using the &lt;code&gt;readline()&lt;/code&gt; method to read a text file one line at a time, instead of using the &lt;code&gt;read()&lt;/code&gt; method to read in the entire file all at once.&lt;/p&gt;

&lt;p&gt;As a side note, generator functions return generator objects and generator objects are iterables. (Iterables are beyond the scope of this blog post, but see &lt;a href="https://treyhunner.com/2016/12/python-iterator-protocol-how-for-loops-work/"&gt;The Iterator Protocol: How "For Loops" Work in Python&lt;/a&gt; for more details.)&lt;/p&gt;

&lt;p&gt;You can download a single .py file of all the examples from &lt;a href="/blogstatic/whatIsAGenerator.py"&gt;whatIsAGenerator.py&lt;/a&gt;. This blog post assumes you have a basic beginner's level of understanding of Python.&lt;/p&gt;

&lt;p&gt;First, let's take a look at a regular function that you're already familiar with:&lt;/p&gt;

&lt;pre&gt;### A regular function:
def aRegularFunction(param):
    print('Hello', param)
    return 42

print("Calling aRegularFunction('Alice'):")
returnedValue = aRegularFunction('Alice')
print('Returned value:', returnedValue)&lt;/pre&gt;

&lt;p&gt;When you run this code, the output looks like this:&lt;/p&gt;

&lt;pre&gt;Calling aRegularFunction('Alice'):
Hello Alice
Returned value: 42&lt;/pre&gt;

&lt;p&gt;This is familiar and unsurprising. A function takes arguments for its parameters, runs some code, and then returns some return value.&lt;/p&gt;

&lt;p&gt;Now let's take a look at a generator function. You can tell a function is a generator function because it'll have the &lt;code&gt;yield&lt;/code&gt; keyword somewhere in the function body:&lt;/p&gt;

&lt;pre&gt;### A generator (aka generator function):
def aGeneratorFunction(param):
    print('Hello', param)
    yield 42
    print('How are you,', param)
    yield 99
    print('Goodbye', param)
    return 86  # Raises StopIteration with value 86.

print("Calling aGeneratorFunction('Bob'):")
generatorObj = aGeneratorFunction('Bob')  # Does not run the code, but returns a generator object.
print('Calling next():')
yieldedValue = next(generatorObj)
print('Yielded value:', yieldedValue)
print('Calling next():')
yieldedValue = next(generatorObj)
print('Yielded value:', yieldedValue)
print('Calling next():')
try:
    next(generatorObj)
except StopIteration as excObj:
    print('type(excObj) is', type(excObj))
    print('excObj.value is', excObj.value)
    print('type(excObj.value) is', type(excObj.value))&lt;/pre&gt;

&lt;p&gt;When you run this code, the output looks like this:&lt;/p&gt;

&lt;pre&gt;Calling aGeneratorFunction('Bob'):
Calling next():
Hello Bob
Yielded value: 42
Calling next():
How are you, Bob
Yielded value: 99
Calling next():
Goodbye Bob
type(excObj) is &amp;lt;class 'StopIteration'&amp;gt;
excObj.value is 86
type(excObj.value) is &amp;lt;class 'int'&amp;gt;&lt;/pre&gt;

&lt;p&gt;What you need to know is that &lt;strong&gt;one does not simply call a generator function&lt;/strong&gt;. Calling a generator function does not run it's code. Instead, calling a generator function returns a &lt;em&gt;generator object&lt;/em&gt;. You can see this in the interactive shell:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; aGeneratorFunction('Eve')
&amp;lt;generator object aGeneratorFunction at 0x0000013470179580&amp;gt;&lt;/pre&gt;

&lt;p&gt;To run the code inside a generator function, you must call Python's built-in &lt;code&gt;next()&lt;/code&gt; function and pass it the generator object. This will run the code up until a &lt;code&gt;yield&lt;/code&gt; statement, which acts like a &lt;code&gt;return&lt;/code&gt; statement and makes the &lt;code&gt;next()&lt;/code&gt; call return a value. The next time &lt;code&gt;next()&lt;/code&gt; is called, the execution resumes from the yield statement with all the same values for the local variables.&lt;/p&gt;

&lt;p&gt;When a &lt;code&gt;return&lt;/code&gt; statement is reached, the generator function raises a &lt;code&gt;StopIteration&lt;/code&gt; exception. Usually there's no value returned, but if so it is set to the exception object's &lt;code&gt;value&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;next()&lt;/code&gt; function usually isn't used with generators, but rather the generator function is used in a &lt;code&gt;for&lt;/code&gt; loop. On each iteration, the &lt;code&gt;for&lt;/code&gt; loop sets the loop variable to the yielded value. The &lt;code&gt;StopIteration&lt;/code&gt; exception tells the &lt;code&gt;for&lt;/code&gt; loop that the generator object has exhausted its values. For example:&lt;/p&gt;

&lt;pre&gt;### Using a generator function with a for loop or list() call:
for yieldedValue in aGeneratorFunction('Carol'):
    print('Yielded value:', yieldedValue)

listOfGeneratedValues = list(aGeneratorFunction('David'))
print('List of generated values:', listOfGeneratedValues)&lt;/pre&gt;

&lt;p&gt;When you run this code, the output looks like this:&lt;/p&gt;

&lt;pre&gt;Hello Carol
Yielded value: 42
How are you, Carol
Yielded value: 99
Goodbye Carol
Hello David
How are you, David
Goodbye David
List of generated values: [42, 99]&lt;/pre&gt;

&lt;p&gt;Rememebr that generators are mainly used to generate a series of values, but one at a time. Let's use an example of a Fibonacci sequence number generator. The Fibonacci sequence begins with 0 and 1, and then the next number in the sequence is the sum of the previous two numbers: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, and so on.&lt;/p&gt;

&lt;p&gt;A generator can produce these numbers (up to a given maximum) one at a time. This generator can be used with a &lt;code&gt;for&lt;/code&gt; loop or a &lt;code&gt;list()&lt;/code&gt; call:&lt;/p&gt;

&lt;pre&gt;### A practical example of a generator using the Fibonacci sequence:
def fibonacciSequence(maxNum=50):
    a = 0
    b = 1
    yield a
    yield b
    while True:
        a, b = b, b + a
        if b &gt;= maxNum:
            return b  # Raise StopIteration.
        else:
            yield b


for fibNum in fibonacciSequence():
    print('Next number in the Fibonacci sequence:', fibNum)


fibNumsUpTo500 = list(fibonacciSequence(500))
print('List of fib numbers up to 500:', fibNumsUpTo500)&lt;/pre&gt;

&lt;p&gt;When you run this code, the output looks like this:&lt;/p&gt;

&lt;pre&gt;Next number in the Fibonacci sequence: 0
Next number in the Fibonacci sequence: 1
Next number in the Fibonacci sequence: 1
Next number in the Fibonacci sequence: 2
Next number in the Fibonacci sequence: 3
Next number in the Fibonacci sequence: 5
Next number in the Fibonacci sequence: 8
Next number in the Fibonacci sequence: 13
Next number in the Fibonacci sequence: 21
Next number in the Fibonacci sequence: 34
List of fib numbers up to 500: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]&lt;/pre&gt;

&lt;p&gt;To summarize, Python generators look like functions that contain the &lt;code&gt;yield&lt;/code&gt; keyword. They're used for producing a series of values one at a time so that your program doesn't consume a lot of memory by producing all the values at once. When a generator function is called, it doesn't run the code inside it but rather returns a generator object. This generator object can be passed to the built-in &lt;code&gt;next()&lt;/code&gt; function. This runs the code in the generator function up until a &lt;code&gt;yield&lt;/code&gt; statement (which gives a value for &lt;code&gt;next()&lt;/code&gt; to return) or a &lt;code&gt;return&lt;/code&gt; statement (which raises the &lt;code&gt;StopIteration&lt;/code&gt; to signal that there are no more values to generate). However, generators are usually used in &lt;code&gt;for&lt;/code&gt; loops, which automatically handle them.&lt;/p&gt;

&lt;p&gt;That's it! Generators aren't all that complicated. I recommend you learn about iterables and iterators in general by checking out Trey Hunner's blog post, &lt;a href="https://treyhunner.com/2016/12/python-iterator-protocol-how-for-loops-work/"&gt;The Iterator Protocol: How "For Loops" Work in Python&lt;/a&gt;.&lt;/p&gt;


&lt;h2&gt;Implementing Your Own range() Function&lt;/h2&gt;

&lt;p&gt;Let's put our knowledge to the test by creating our own &lt;code&gt;range()&lt;/code&gt; function. Instead of &lt;code&gt;for i in range(10):&lt;/code&gt;, we'll be able to use &lt;code&gt;for i in mySimpleImplementationOfRange(10):&lt;/code&gt;. We'll use a generator function that takes a single integer argument, and yields values starting from &lt;code&gt;0&lt;/code&gt; up to but not including the argument:&lt;/p&gt;

&lt;pre&gt;### Using generator functions to re-implement range():
def mySimpleImplementationOfRange(stop):
    i = 0
    while i &lt; stop:
        yield i
        i += 1
    return  # Raise StopIteration.

for i in mySimpleImplementationOfRange(5):
    print(i)&lt;/pre&gt;

&lt;p&gt;When you run this code, the output looks like this:&lt;/p&gt;

&lt;pre&gt;0
1
2
3
4&lt;/pre&gt;

&lt;p&gt;Of course, the &lt;code&gt;range()&lt;/code&gt; function is a bit more complicated than that. It can take up to three arguments to specify a starting integer, stopping integer, and a "step" integer to indicate how much the yielded integers should change by. Also, &lt;code&gt;range()&lt;/code&gt; only accepts integer arguments and the step argument cannot be &lt;code&gt;0&lt;/code&gt;. A fuller implementation is below:&lt;/p&gt;

&lt;pre&gt;### Using generator functions for a more complete range() re-implementation:
def myImplementationOfRange(firstParam, secondParam=None, thirdParam=None):
    if secondParam is None:
        if not isinstance(firstParam, int):
            raise TypeError("'" + firstParam.__class__.__name__ + "' object cannot be interpreted as an integer")
        start = 0
        stop = firstParam
        step = 1
    elif thirdParam is None:
        if not isinstance(secondParam, int):
            raise TypeError("'" + secondParam.__class__.__name__ + "' object cannot be interpreted as an integer")
        start = firstParam
        stop = secondParam
        step = 1
    else:
        if not isinstance(thirdParam, int):
            raise TypeError("'" + thirdParam.__class__.__name__ + "' object cannot be interpreted as an integer")
        if thirdParam == 0:
            raise ValueError('myImplementationOfRange() arg 3 must not be zero')
        start = firstParam
        stop = secondParam
        step = thirdParam

    i = start
    if step &gt; 0:  # step arg is increasing
        while i &lt; stop:
            yield i
            i += step
        return i
    elif step &lt; 0:  # step arg is decreasing
        while i &gt; stop:
            yield i
            i += step  # Adding a negative to decrease
        return i

for i in myImplementationOfRange(4, 12, 2):
    print(i)&lt;/pre&gt;

&lt;p&gt;When you run this code, the output looks like this:&lt;/p&gt;

&lt;pre&gt;4
6
8
10&lt;/pre&gt;

&lt;p&gt;There's a bit more to it than that, as &lt;code&gt;range&lt;/code&gt; is actually a class which implements several dunder methods (which you can learn about from Nina Zakharenko's PyCon 2018 talk, &lt;a href="https://www.youtube.com/watch?v=WiQqqB9MlkA"&gt;Elegant Solutions for Everyday Python Problems&lt;/a&gt;. But now you can see how something like Python's built-in &lt;code&gt;range()&lt;/code&gt; is implemented.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Recursion Is Not A Superpower: An Iterative Ackermann</title><link href="https://inventwithpython.com/blog/recursion-is-not-a-superpower.html" rel="alternate"></link><published>2021-09-05T12:00:00-04:00</published><updated>2021-09-05T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-09-05:/blog/recursion-is-not-a-superpower.html</id><summary type="html">This post has the Python code for the infamously recursive Ackermann function, but without using recursion.</summary><content type="html">

&lt;p&gt;This post has the Python code for the infamously recursive Ackermann function, but without using recursion. Many people think that recursive algorithms hold some special power that can do computations beyond the reach of iterative algorithms. But recursion is not a superpower: there's nothing you can do with recurison that you can't do with a loop and a stack.&lt;/p&gt;

&lt;blockquote&gt;&lt;em&gt;This blog post contains information from Al Sweigart's next book, &lt;a href="https://nostarch.com/book-recursion"&gt;The Recursive Book of Recurson&lt;/a&gt; from No Starch Press. The book will be released in February 2022. You can use the discount code PREORDERRECURSION to get 30% off your preorder, and receive the ebook free when you buy a print book. This book will also be freely available under a Creative Commons license on the &lt;a href="https://inventwithpython.com/recursion/"&gt;Invent with Python&lt;/a&gt; website. Al Sweigart also gave a conference talk on recursion at North Bay Python 2018 titled, &lt;a href="https://www.youtube.com/watch?v=AfBqVVKg4GE"&gt;Recursion for Beginners: A Beginner's Guide to Recursion&lt;/a&gt;.&lt;/em&gt;&lt;/blockquote&gt;

&lt;p&gt;A &lt;em&gt;stack&lt;/em&gt; is one of the simplest data structures in computing. It stores multiple values like a list or an array does, but unlike lists or arrays, it limits you to adding to or removing values from the “top” of the stack only. Adding values is called pushing values onto the stack, while removing values is called popping values off the stack.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;call stack&lt;/em&gt; is a stack data structure your programming language's compiler or interpreter uses to keep track of where in your program the execution should return to when a function call finishes. When your program calls a function, this return address information is pushed onto the stack. When the function call returns, the information is popped off the stack. If your function call makes a function call which makes another function call, you'll have three things pushed onto the call stack.&lt;/p&gt;

&lt;p&gt;Recursion is confusing for beginner because instructors and tutorials often don't explicitly explain what the call stack is. The call stack is managed by the compiler or interpreter automatically in the background. You can't point to a place in your source code and say, "this is the call stack" like you can with other variables. This makes the call stack an invisible gap in the understanding of students.&lt;/p&gt;

&lt;p&gt;Recursive functions use the call stack as their stack data structure, but you can use a list or array as a stack data structure by restricting yourself to adding or removing things only from the end. For example, Python's &lt;code&gt;append()&lt;/code&gt; and &lt;code&gt;pop()&lt;/code&gt; list methods are effectively the "push" and "pop" operations to treat a Python list as a stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is nothing special about recursion. Anything you can do with recursion can be done with a loop and a stack. Further, recursion is not automatically more "elegant" than an iterative solution. Quite the opposite: recursion is often produces overengineered and less readable code than a straightforward iterative solution.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To prove this, I'll use the famous Ackermann recursive function as an example. The Ackermann function doesn't have much practical use aside from an example of a recursive function that is extremely recursive. Even small inputs to this function can result in an amount of computation that takes years or lifetimes to finish. &lt;a href="https://github.com/asweigart/recursion_examples/blob/master/ackermann.py"&gt;Here's the code to the recursive Ackermann function in Python:&lt;/a&gt;&lt;/p&gt;

&lt;pre&gt;def ackermann(m, n):
    if m == 0:
        # BASE CASE
        return n + 1
    elif m &gt; 0 and n == 0:
        # RECURSIVE CASE
        return ackermann(m - 1, 1)
    elif m &gt; 0 and n &gt; 0:
        # RECURSIVE CASE
        return ackermann(m - 1, ackermann(m, n - 1))&lt;/pre&gt;

&lt;p&gt;For example, calling &lt;code&gt;ackermann(1, 1)&lt;/code&gt; will take less than a second to complete. Calling &lt;code&gt;ackermann(4,1)&lt;/code&gt; will take a couple minutes. But calling &lt;code&gt;ackermann(15, 20)&lt;/code&gt; will take longer than the universe has existed to finish calculating. The Ackermann function becomes untennable very quickly.&lt;/p&gt;

&lt;p&gt;But recursion is not a superpower. Even Ackermann, one the most recursive of recursive functions, can be written with a loop and a stack. &lt;a href="https://github.com/asweigart/recursion_examples/blob/master/ackermann_iterative.py"&gt;Here's an iterative Ackermann implementation in Python:&lt;/a&gt;&lt;/p&gt;

&lt;pre&gt;print('Starting with m = 2, n = 3:')
callStack = [{'m': 2, 'n': 3, 'indentation': 0, 'instrPtr': 'start'}]
returnValue = None

while len(callStack) != 0:
    m = callStack[-1]['m']
    n = callStack[-1]['n']
    indentation = callStack[-1]['indentation']
    instrPtr = callStack[-1]['instrPtr']

    if instrPtr == 'start':
        print('%sackermann(%s, %s)' % (' ' * indentation, m, n))

        if m == 0:
            # BASE CASE
            returnValue = n + 1
            callStack.pop()
            continue
        elif m &gt; 0 and n == 0:
            # RECURSIVE CASE
            callStack[-1]['instrPtr'] = 'after first recursive case'
            callStack.append({'m': m - 1, 'n': 1, 'indentation': indentation + 1, 'instrPtr': 'start'})
            continue
        elif m &gt; 0 and n &gt; 0:
            # RECURSIVE CASE
            callStack[-1]['instrPtr'] = 'after second recursive case, inner call'
            callStack.append({'m': m, 'n': n - 1, 'indentation': indentation + 1, 'instrPtr': 'start'})
            continue
    elif instrPtr == 'after first recursive case':
        returnValue = returnValue
        callStack.pop()
        continue
    elif instrPtr == 'after second recursive case, inner call':
        callStack[-1]['innerCallResult'] = returnValue
        callStack[-1]['instrPtr'] = 'after second recursive case, outer call'
        callStack.append({'m': m - 1, 'n': returnValue, 'indentation': indentation + 1, 'instrPtr': 'start'})
        continue
    elif instrPtr == 'after second recursive case, outer call':
        returnValue = returnValue
        callStack.pop()
        continue
print(returnValue)&lt;/pre&gt;

&lt;p&gt;Try running both programs. You'll see that they produce identical output. But the iterative version doesn't use recursive functions. In fact, it doesn't use functions at all: there's no &lt;code&gt;def&lt;/code&gt; statement anywhere in the program.&lt;/p&gt;

&lt;p&gt;If you find recursion confusing or wonder why you "should" use recursion, don't despair. Recursion is an inappropriate approach just as often as it is appropriate (or even more). Just remember that recursion, while a useful technique that every programmer should understand, is not a superpower.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you'd like to learn how to use recursion, check out Al Sweigart's upcoming book, &lt;a href="https://nostarch.com/book-recursion"&gt;The Recursive Book of Recursion&lt;/a&gt; from No Starch Press. This book will also be freely available under a Creative Commons license on the &lt;a href="https://inventwithpython.com/recursion/"&gt;Invent with Python&lt;/a&gt; website. Al Sweigart also gave a conference talk on recursion at North Bay Python 2018 titled, &lt;a href="https://www.youtube.com/watch?v=AfBqVVKg4GE"&gt;Recursion for Beginners: A Beginner's Guide to Recursion&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</content><category term="misc"></category><category term="recursion"></category></entry><entry><title>ccwd - A Copy Current Working Directory Command</title><link href="https://inventwithpython.com/blog/ccwd-copy-current-working-directory.html" rel="alternate"></link><published>2021-08-28T12:01:00-04:00</published><updated>2021-08-28T12:01:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-08-28:/blog/ccwd-copy-current-working-directory.html</id><summary type="html">The source code and compiled binary for a Windows terminal command I created to copy the current working directory to the clipboard. I find myself using this all the time.</summary><content type="html">

&lt;p&gt;In 2008 I created a small program meant to be run from a terminal window called ccwd which copies the current working directory to the clipboard. While the standard Unix command pwd will print the working directory (that is, the current folder the terminal is in), this will copy it to the clipboard. I found myself using it all the time when I needed to get the directory that I was in.&lt;/p&gt;

&lt;p&gt;Example usage in the Windows Command Prompt:&lt;/p&gt;

&lt;pre&gt;Microsoft Windows [Version 10.0.19043.1165]
(c) Microsoft Corporation. All rights reserved.

C:\Users\Al&gt;cd Desktop

C:\Users\Al\Desktop&gt;ccwd

C:\Users\Al\Desktop&gt;&lt;/pre&gt;

&lt;p&gt;At this point, &lt;code&gt;C:\Users\Al\Desktop&lt;/code&gt; would be copied to the clipboard and could easily be pasted into other apps. I have the C source code and Windows executable here. ccwd.exe does not produce any output, and returns 0 on success and a nonzero value on failure.&lt;/p&gt;

&lt;p&gt;&lt;a href="/blogstatic/ccwd.cpp"&gt;C source code for Windows&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="/blogstatic/ccwd.exe"&gt;ccwd.exe executable program&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I recommend placing the ccwd.exe file in the C:\Windows folder or someplace on the system PATH (i.e. one of the folders listed in your &lt;a href="https://en.wikipedia.org/wiki/PATH_(variable)"&gt;PATH environment variable&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;UPDATE 2022/05/04: If you don't want to download the sketchy looking, unsigned .exe file from this page, you can download a batch file to use instead:&lt;/p&gt;

&lt;p&gt;&lt;a href="/blogstatic/ccwd.bat"&gt;ccwd.bat batch file&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code in this batch file is easy enough to understand. It uses the Get-Location cmdlet for Powershell (preinstalled on Windows 7 SP1 and later) to get the current working directory and stores it in a temporary variable. Then it uses Set-Clipboard to put the contents of this variable on the clipboard.&lt;/p&gt;

&lt;p&gt;TODO&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pwd | tr -d '\n' | pbcopy&lt;/code&gt;

</content><category term="misc"></category></entry><entry><title>Algorithmic Art with the BitFieldDraw Module</title><link href="https://inventwithpython.com/blog/bitfielddraw.html" rel="alternate"></link><published>2021-08-02T12:00:00-04:00</published><updated>2021-08-02T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-08-02:/blog/bitfielddraw.html</id><summary type="html">&lt;p&gt;I created the &lt;code&gt;bitfielddraw&lt;/code&gt; Python module so that you could turn math formulas into .png image files.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;A bit field is a 2D image of black and white pixels. It's the simplest possible image. The pixels don't necessarily have to be black and white, but a bit field is limited to two colors because each pixel is represented by a bit: a 0 or 1 number. You can install it by running &lt;code&gt;pip install --user bitfielddraw&lt;/code&gt; (on Windows) or &lt;code&gt;pip3 install --user bitfielddraw&lt;/code&gt; (on macOS and Linux). In the interactive shell, run &lt;code&gt;import bitfielddraw&lt;/code&gt; and &lt;code&gt;bitfielddraw.main()&lt;/code&gt; to see a demo. (Maximize your terminal window for best effect.)&lt;/p&gt;

&lt;p&gt;Despite this limitation, bit fields can be beautiful. I found &lt;a href="https://threadreaderapp.com/thread/1378106731386040322?refresh=1627428184"&gt;this twitter thread&lt;/a&gt; (and &lt;a href="https://www.metafilter.com/192164/Patterns"&gt;MetaFilter discussion&lt;/a&gt;) that had several bit fields generated by simple mathematical functions. The functions are fed the X and Y coordinate of a pixel, and if the function returns a zero the pixel is white and if the function returns a nonzero the pixel is black. There are several interesting patterns that can be generated from simple functions:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/bitfielddraw/terminalbitfield1.png" /&gt;&lt;/p&gt;

&lt;p&gt;Inspired by this, I've created a Python module called &lt;a href="https://pypi.org/project/BitFieldDraw/"&gt;BitFieldDraw&lt;/a&gt;, which makes it easy to see the bit fields generated from a given function. You can &lt;code&gt;from bitfielddraw import *&lt;/code&gt; and then call the &lt;code&gt;save()&lt;/code&gt; (to save it to an image or text file), &lt;code&gt;p()&lt;/code&gt; functions to print it to the terminal screen as block text characters, or &lt;code&gt;s()&lt;/code&gt; (to show the image using the Pillow module).&lt;/p&gt;

&lt;p&gt;The function that generates the art can be a Python lambda function such as &lt;code&gt;lambda x, y: (x ^ y) % 5&lt;/code&gt; or simply a string with the body of the lambda function such as &lt;code&gt;'(x ^ y) % 5'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The function &lt;code&gt;(x ^ y) % 5&lt;/code&gt; xor's (that is, exclusive or's) the x and y coordinates, then mods the result by 5. Let's use the coordinates (10, 19). This produces the operation &lt;code&gt;(10 ^ 19) % 5&lt;/code&gt;. 10 and 19 are 01010 and 10011 in binary. When we xor them, we get 11001 or 25. 25 mod 5 is 0 (that is, 25 divided by 5 has a remainder of 0). Because 0 is a zero value, this produces a white pixel at the coordinates (10, 19).&lt;/p&gt;

&lt;p&gt;The BitFieldDraw runs this calculation for every pixel in a 2D field to draw algorithmic art. Some examples:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; save('bitfield1.png', '(x ^ y) % 5', w=400, h=400)&lt;/code&gt;&lt;br /&gt;&lt;img src="/blogstatic/bitfielddraw/bitfield1.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; save('bitfield2.png', '(x * 64) % y', w=400, h=400)&lt;/code&gt;&lt;br /&gt;&lt;img src="/blogstatic/bitfielddraw/bitfield2.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; save('bitfield3.png', '(x % y) % 4', w=400, h=400)&lt;/code&gt;&lt;br /&gt;&lt;img src="/blogstatic/bitfielddraw/bitfield3.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; save('bitfield4.png', '(x &amp; y) &amp; (x ^ y) % 19', w=400, h=400)&lt;/code&gt;&lt;br /&gt;&lt;img src="/blogstatic/bitfielddraw/bitfield4.png" /&gt;&lt;/p&gt;

&lt;p&gt;When you save bit fields as image files, you can give them any two colors for the foreground and background colors:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; save('bitfield-color1.png', '(x &amp; y) &amp; (x ^ y) % 19', w=400, h=400, fg='red', bg='black')&lt;/code&gt;&lt;br /&gt;&lt;img src="/blogstatic/bitfielddraw/bitfield-color1.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; save('bitfield-color2.png', '(x &amp; y) &amp; (x ^ y) % 19', w=400, h=400, fg='yellow', bg='blue')&lt;/code&gt;&lt;br /&gt;&lt;img src="/blogstatic/bitfielddraw/bitfield-color2.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; save('bitfield-color3.png', '(x &amp; y) &amp; (x ^ y) % 19', w=400, h=400, fg='#004400', bg='green')&lt;/code&gt;&lt;br /&gt;&lt;img src="/blogstatic/bitfielddraw/bitfield-color3.png" /&gt;&lt;/p&gt;

&lt;p&gt;The first argument to &lt;code&gt;p()&lt;/code&gt; print-function and &lt;code&gt;s()&lt;/code&gt; save-function are an expression with &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; variables. The expression evaluates to the color for that pixel: black if zero and white if nonzero. The width and height of the bit field are automatically set to the size of the terminal window. Since text cells in terminal windows are twice as tall as they are wide, each text cell represents two pixel. However, other integers values can be passed for the &lt;code&gt;w&lt;/code&gt; and &lt;code&gt;h&lt;/code&gt; parameters to the functions to change the width and height.&lt;/p&gt;

&lt;p&gt;Since text cells in terminal windows are twice as tall as they are wide, each text cell actually represents two pixels in one column and two rows. The text characters used are code points 9600 (just the top pixel set &lt;code&gt;'▀'&lt;/code&gt;), 9604 (just the bottom pixel set &lt;code&gt;'▄'&lt;/code&gt;), and 9608 (both pixels set &lt;code&gt;'█'&lt;/code&gt;). For both pixels being clear, we use a space character &lt;code&gt;' '&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The 0, 0 origin of the bit field is set to the bottom left corner. The origin can be changed by passing integers to the &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; parameters.&lt;/p&gt;

&lt;pre&gt;
&gt;&gt;&gt; p('(x^y)%5', w=10, h=10)
██▀▄██▄▀▀▄
██▄▀██▀▄▄▀
▄▀██▀▄████
██▀▄██▄▀▀▄
▀▄██▄▀████
&gt;&gt;&gt; p('(x^y)%5', w=10, h=10, x=2)
▀▄██▄▀▀▄██
▄▀██▀▄▄▀██
██▀▄████▄▀
▀▄██▄▀▀▄██
██▄▀████▀▄
&gt;&gt;&gt; p('(x^y)%5', w=10, h=10, y=2)
▀▄██▄▀████
██▀▄██▄▀▀▄
██▄▀██▀▄▄▀
▄▀██▀▄████
██▀▄██▄▀▀▄
&lt;/pre&gt;

&lt;p&gt;Unlike computer screen coordinates, the &lt;code&gt;bitfielddraw&lt;/code&gt; module uses mathematics coordinates so that the Y coordinates increase going up, not down. You can change this by passing &lt;code&gt;flipy=True&lt;/code&gt; to any of the BitFieldDraw functions, in which case the y coordinates will increase going down. You can also pass &lt;code&gt;flipx=True&lt;/code&gt; to flip the x coordinates. (Currently there's no way to rotate the bit fields.)&lt;/p&gt;

&lt;pre&gt;
&gt;&gt;&gt; p('(x^y)%5', w=10, h=10)
██▀▄██▄▀▀▄
██▄▀██▀▄▄▀
▄▀██▀▄████
██▀▄██▄▀▀▄
▀▄██▄▀████
&gt;&gt;&gt; p('(x^y)%5', w=10, h=10, flipx=True)
▄▀▀▄██▄▀██
▀▄▄▀██▀▄██
████▄▀██▀▄
▄▀▀▄██▄▀██
████▀▄██▄▀
&gt;&gt;&gt; p('(x^y)%5', w=10, h=10, flipy=True)
▄▀██▀▄████
██▄▀██▀▄▄▀
▀▄██▄▀████
██▀▄██▄▀▀▄
██▄▀██▀▄▄▀
&gt;&gt;&gt; p('(x^y)%5', w=10, h=10, flipx=True, flipy=True)
████▄▀██▀▄
▀▄▄▀██▀▄██
████▀▄██▄▀
▄▀▀▄██▄▀██
▀▄▄▀██▀▄██
&lt;/pre&gt;

&lt;p&gt;If you'd like to swap the colors used, you can pass &lt;code&gt;True&lt;/code&gt; for the &lt;code&gt;invert&lt;/code&gt; parameter:&lt;/p&gt;

&lt;pre&gt;
&gt;&gt;&gt; p('(x^y)%5', w=10, h=10)
██▀▄██▄▀▀▄
██▄▀██▀▄▄▀
▄▀██▀▄████
██▀▄██▄▀▀▄
▀▄██▄▀████
&gt;&gt;&gt; p('(x^y)%5', w=10, h=10, invert=True)
  ▄▀  ▀▄▄▀
  ▀▄  ▄▀▀▄
▀▄  ▄▀
  ▄▀  ▀▄▄▀
▄▀  ▀▄
&lt;/pre&gt;

&lt;p&gt;You can also get the string of the bit field directly with &lt;code&gt;getBitFieldStr()&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; import bitfielddraw
&amp;gt;&amp;gt;&amp;gt; bitfielddraw.printBitFieldStr('(x ^ y) % 5', w=10, h=10)  # This is the same as the p() function.
██▀▄██▄▀▀▄
██▄▀██▀▄▄▀
▄▀██▀▄████
██▀▄██▄▀▀▄
▀▄██▄▀████
&amp;gt;&amp;gt;&amp;gt; bitfielddraw.getBitFieldStr('(x ^ y) % 5', w=10, h=10)
'██▀▄██▄▀▀▄\n██▄▀██▀▄▄▀\n▄▀██▀▄████\n██▀▄██▄▀▀▄\n▀▄██▄▀████'
&lt;/pre&gt;


&lt;h2&gt;getBitField(), getBitFieldStrFromSet(), and getBitFieldImgFromSet&lt;/h3&gt;

&lt;p&gt;If you'd like to get the raw data of the bit field, you can call &lt;code&gt;getBitField()&lt;/code&gt; which will return a frozen set of (x, y) tuples. From this, you can make changes to the data and then pass it to &lt;code&gt;getBitFieldStrFromSet()&lt;/code&gt; or &lt;code&gt;getBitFieldImgFromSet()&lt;/code&gt; to get a string or Pillow image object of the modified bit field.&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; import bitfielddraw
&amp;gt;&amp;gt;&amp;gt; bitFieldData = bitfielddraw.getBitField(lambda x, y: (x^y)%5, width=20, height=20)
&amp;gt;&amp;gt;&amp;gt; bitFieldData
frozenset({(7, 17), (18, 17), (8, 0), (19, 0), (8, 9), (19, 9), (11, 5), (8, 18), (19, 18), (0, 14), (4, 2), (3, 15), (14, 15), (15, 7), (7, 3), (18, 3), (15, 16), (7, 12), (8, 4), (19, 4), (11, 0), (0, 9), (11, 9), (3, 1), (3, 10), (14, 10), (3, 19), (14, 19), (15, 2), (15, 11), (18, 7), (7, 16), (18, 16), (10, 8), (10, 17), (3, 5), (14, 5), (3, 14), (15, 6), (18, 2), (7, 11), (6, 15), (10, 3), (10, 12), (2, 17), (3, 0), (14, 0), (14, 9), (3, 18), (14, 18), (6, 10), (6, 19), (10, 7), (2, 3), (10, 16), (2, 12), (3, 4), (17, 6), (9, 11), (6, 5), (6, 14), (10, 2), (10, 11), (2, 16), (16, 18), (17, 1), (5, 8), (17, 10), (17, 19), (9, 15), (6, 0), (10, 6), (2, 11), (1, 15), (13, 17), (16, 13), (5, 3), (9, 1), (5, 12), (17, 14), (9, 10), (9, 19), (6, 4), (6, 13), (13, 3), (1, 10), (13, 12), (16, 8), (1, 19), (16, 17), (17, 0), (5, 7), (17, 9), (9, 5), (5, 16), (9, 14), (12, 15), (1, 5), (16, 3), (13, 16), (16, 12), (5, 2), (17, 4), (9, 0), (5, 11), (0, 18), (12, 1), (12, 10), (4, 6), (12, 19), (4, 15), (1, 0), (1, 9), (13, 11), (16, 7), (1, 18), (5, 6), (19, 8), (0, 4), (19, 17), (0, 13), (11, 13), (12, 5), (12, 14), (4, 10), (4, 19), (16, 2), (1, 13), (8, 3), (19, 3), (19, 12), (8, 12), (0, 8), (11, 8), (0, 17), (11, 17), (12, 0), (4, 5), (15, 1), (7, 6), (15, 19), (7, 15), (18, 15), (0, 3), (11, 3), (19, 16), (8, 16), (0, 12), (11, 12), (12, 4), (4, 0), (4, 9), (3, 13), (14, 13), (7, 1), (18, 1), (15, 14), (7, 10), (18, 10), (18, 19), (19, 2), (8, 11), (19, 11), (0, 7), (11, 7), (0, 16), (11, 16), (3, 8), (14, 8), (3, 17), (14, 17), (15, 9), (7, 5), (18, 5), (15, 18), (7, 14), (18, 14), (19, 6), (0, 2), (11, 2), (14, 3), (14, 12), (15, 4), (7, 0), (18, 0), (15, 13), (7, 9), (18, 9), (10, 1), (2, 6), (2, 15), (3, 7), (14, 7), (3, 16), (17, 18), (7, 4), (18, 4), (6, 8), (6, 17), (2, 1), (10, 14), (2, 10), (2, 19), (3, 2), (14, 2), (3, 11), (17, 13), (9, 18), (10, 9), (2, 5), (10, 18), (2, 14), (14, 6), (9, 4), (9, 13), (6, 7), (6, 16), (10, 4), (2, 0), (10, 13), (2, 9), (13, 6), (2, 18), (13, 15), (16, 11), (5, 1), (17, 3), (17, 12), (9, 8), (5, 19), (9, 17), (6, 2), (6, 11), (2, 4), (13, 1), (1, 8), (13, 10), (16, 6), (1, 17), (16, 15), (17, 7), (5, 14), (17, 16), (12, 13), (4, 18), (1, 3), (13, 5), (16, 1), (1, 12), (13, 14), (16, 10), (16, 19), (17, 2), (5, 9), (17, 11), (9, 7), (5, 18), (6, 1), (12, 8), (12, 17), (4, 13), (13, 0), (1, 7), (13, 9), (16, 5), (1, 16), (13, 18), (5, 4), (9, 2), (5, 13), (8, 6), (8, 15), (19, 15), (0, 11), (4, 8), (4, 17), (1, 2), (13, 4), (16, 0), (7, 18), (8, 1), (19, 1), (8, 10), (0, 6), (11, 6), (8, 19), (11, 15), (12, 7), (4, 3), (12, 16), (4, 12), (1, 6), (15, 8), (18, 13), (8, 5), (19, 5), (0, 1), (8, 14), (19, 14), (11, 10), (0, 19), (11, 19), (12, 2), (12, 11), (4, 7), (15, 3), (15, 12), (18, 8)})
&lt;/pre&gt;

&lt;p&gt;(Currently, getBitFieldStrFromSet() isn't implemented.)&lt;/p&gt;


&lt;h2&gt;Examples&lt;/h3&gt;

&lt;p&gt;BitFieldDraw comes with several examples in its &lt;code&gt;EXAMPLES&lt;/code&gt; member:&lt;/p&gt;

&lt;pre&gt;
&amp;gt;&amp;gt;&amp;gt; import bitfielddraw
&amp;gt;&amp;gt;&amp;gt; import pprint
&amp;gt;&amp;gt;&amp;gt; pprint.pprint(bitfielddraw.EXAMPLES)
('(x ^ y) % 5',
 '(x ^ y) % 9',
 '(x ^ y) % 17',
 '(x ^ y) % 33',
 '(x ^ y) % 2',
 '(x ^ y) % 4',
 '(x ^ y) % 8',
 '(x | y) % 7',
 '(x | y) % 17',
 '(x | y) % 29',
 '(x * y) &amp; 64',
 '(x * y) &amp; 24',
 '(x * y) &amp; 47',
 '(x ^ y) &lt; 77',
...
&lt;/pre&gt;

&lt;p&gt;You can view a slideshow of these examples by running &lt;code&gt;bitfielddraw.main()&lt;/code&gt;. I recommend maximizing your terminal window for the largest view.&lt;/p&gt;



</content><category term="misc"></category><category term="review"></category></entry><entry><title>Book Review: The Charisma Machine by Morgan G. Ames</title><link href="https://inventwithpython.com/blog/book-review-the-charisma-machine.html" rel="alternate"></link><published>2021-07-10T12:01:00-04:00</published><updated>2021-07-10T12:01:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-07-10:/blog/book-review-the-charisma-machine.html</id><summary type="html">&lt;p&gt;&lt;em&gt;The Charisma Machine&lt;/em&gt; by Morgan G. Ames is the cautionary tale everyone in tech should read. Five stars.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;&lt;strong&gt;Five stars. Ames' The Charisma Machine covers the emperor-wears-no-clothes story of the One Laptop Per Child project, and how the technical gurus of MIT's famed Media Group set out to change the world and got it so wrong.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I remember the OLPC project. I followed news of it in the late 2000s and could get behind the idea; create a $100 laptop and give it to poor kids across the Global South and encourage them to learn to program. I loved the idea, because I had the same upbringing as the OLPC promoters who loved the idea. I was one of those kids who taught themselves how to program, making little video games in the BASIC programming language. This interest led me to major in computer science and then on to a successful career in Silicon Valley. So I thought these kids would teach themselves to program, express their creativity in code, develop a curiosity and an internal motivation to learn, become highly paid software engineers and tech entrepreneurs and Make The World A Better Place TM.&lt;/p&gt;

&lt;p&gt;OLPC is a classic story of Silicon Valley hubris by way of MIT's famed Media Lab, but Ames does a fantastic job of dissecting the missteps, grand delusions, and false presumptions that went into constructing this fiasco. This is a book for several audiences: educators, policy makers, social workers, or even just jaded techies like me who gasp with relief that someone else has pulled back the curtain on these so-called wizards.&lt;/p&gt;

&lt;p&gt;Ames uses the term "technically precocious boy" to identify the archetype that many in the OLPC project would identify with and, consciously or not, identify their targeted laptop-recipients as. I certainly was one of these "gifted" boys (and they were overwhelmingly boys) who taught himself to program at a young age by making little video games. I was independent, had a curiosity to learn outside of school, and had general anti-authority streak. Or at least I liked to think I did. Like the members behind OLPC, I was rather blind to the privileges I had: growing up with a PC and even internet access in the 90s and parents who encouraged my computing interest.&lt;/p&gt;

&lt;p&gt;So I am especially primed to fall under the charisma of the One Laptop Per Child's cheap XO laptops. All these poor kids need is a laptop, and they'll follow the same path that I did and have successful careers and bring economic prosperity to their home countries and the world at large, right? Socioeconomic problems like poverty and lack of opportunity are hard; a quick, cheap technical fix can seem like a miracle. If that sounds too good to be true, it's because it is.&lt;/p&gt;

&lt;p&gt;Not that the OLPC was a scam, but it did have some cult-like features. The charisma of the backers such as Nicholas Negroponte, who also had the connections to bring in funding and code contributions from open source volunteers, was overwhelming. The input of actual teachers, social workers, and local community organizers was glossed over, as was tracking the actual effectiveness of the program.&lt;/p&gt;

&lt;p&gt;Ames spent months in Paraguay in the actual communities that were recipients of OLPC laptops, cataloguing the experience of students and teachers. She also saw firsthand the many shortcomings of these laptops. Some of these were simple: classrooms often didn't have enough electric outlets or power strips to sustain everyone's laptop. But some were large: kids mainly used the laptops, not for creative expression, but just to consume media on the internet. They more often played video games than programmed them.&lt;/p&gt;

&lt;p&gt;I lived in Silicon Valley for over a decade and I've personally seen the strain of naivety of these tech-based utopian vision. Some guy will claim that he's going to teach JavaScript to homeless people and solve poverty. They never have any previous experience in education or social work, but this doesn't matter. After a few weeks, they, and the internet audience enrapture by their first blog post announcement, lose interest and quietly move on to their next venture. The One Laptop Per Child project was this, scaled up into millions of dollars spent by project funders and recipient governments, along with the countless time of open source volunteers, local teachers, parents, and the students themselves. In the end, the time and money would have been better spent on more conventional, but not-so-charismatic, educational programs.&lt;/p&gt;

&lt;p&gt;Ames' The Charisma Machine is a cautionary tale that is important to read for anyone who follows developments in the tech industry. These institutions and companies frequently make grand claims of innovation and salvation. I, for one, appreciate Ames' efforts to expose these hollow proclamations  and her guidance on how to avoid repeating their mistakes in the future.&lt;/p&gt;

</content><category term="misc"></category><category term="bookreview"></category></entry><entry><title>Combinations and Permutations in Python with itertools</title><link href="https://inventwithpython.com/blog/combinations-permutations-python-itertools.html" rel="alternate"></link><published>2021-07-03T12:01:00-04:00</published><updated>2021-07-03T12:01:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-07-03:/blog/combinations-permutations-python-itertools.html</id><summary type="html">Explores the difference between combinations and permutations, and why you don't need to write your own Python code for them because the built-in &lt;code&gt;itertools&lt;/code&gt; module already exists. Includes working code examples.</summary><content type="html">

&lt;p&gt;&lt;em&gt;(The following information and more can be found from my upcoming, to-be-announced book on recursive algorithms. I'll update this post when the book is released in 2022.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you a list, dictionary, or other iterable object of values you need to generate combinations and permutations from, Python has the built-in &lt;code&gt;itertools&lt;/code&gt; module as part of its standard library. The &lt;em&gt;permutations&lt;/em&gt; of an iterable are every possible &lt;strong&gt;ordering of all&lt;/strong&gt; of the values, while the &lt;em&gt;combinations&lt;/em&gt; are every possible &lt;strong&gt;selection of some, none, or all&lt;/strong&gt; of the values. For example, the permutations and combinations of the set &lt;code&gt;{'A', 'B', 'C'}&lt;/code&gt; are:&lt;/p&gt;

&lt;table style="border: 1px black solid"&gt;
    &lt;tr&gt;&lt;th&gt;Permutations&lt;/th&gt;&lt;th&gt;Combinations&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;ABC, ACB, BAC, BCA, CAB&lt;/td&gt;&lt;td&gt;(none), A, B, C, AB, AC, BC, ABC&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;You can also reuse the values multiple times, which is called &lt;em&gt;permutations with repetition&lt;/em&gt; and &lt;em&gt;combinations with repetition&lt;/em&gt; (also called replacement):&lt;/p&gt;

&lt;table&gt;
    &lt;tr&gt;&lt;th&gt;Permutations with Repetition&lt;/th&gt;&lt;th&gt;Combinations with Repetition&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;AAA, AAB, AAC, ABA, ABB, ABC, ACA, ACB, ACC, BAA, BAB, BAC, BBA, BBB, BBC, BCA, BCB, BCC, CAA, CAB, CAC, CBA, CBB, CBC, CCA, CCB, CCC&lt;/td&gt;&lt;td&gt;(none), A, B, C, AA, AB, AC, BB, BC, CC, AAA, AAB, AAC, ABB, ABC, ACC, BBB, BBC, BCC, CCC&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;(Note that permutations with repetition is effectively the same as a password cracker that tries every possible combination of characters to brute force a password.)&lt;/p&gt;

&lt;p&gt;The number of permutations and combinations quickly grows when more values are added to the iterable object. The total number of permutations and combinations is given in the following:&lt;/p&gt;

&lt;table&gt;
    &lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Permutations of n Values&lt;/td&gt;&lt;td&gt;Combinations of n Values&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Without Repetition&lt;/td&gt;&lt;td&gt;n!&lt;/td&gt;&lt;td&gt;2^n&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;With Repetition&lt;/td&gt;&lt;td&gt;n^n&lt;/td&gt;&lt;td&gt;"2n choose n", that is, (2n)! / (n!)^2&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;But to have Python generate permutations, you can use &lt;code&gt;itertools.permutations()&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
&gt;&gt;&gt; import itertools
&gt;&gt;&gt; for v in itertools.permutations(['A', 'B', 'C']):
...   print(v)
...
('A', 'B', 'C')
('A', 'C', 'B')
('B', 'A', 'C')
('B', 'C', 'A')
('C', 'A', 'B')
('C', 'B', 'A')
&gt;&gt;&gt;
&gt;&gt;&gt; list(itertools.permutations(['A', 'B', 'C']))
[('A', 'B', 'C'), ('A', 'C', 'B'), ('B', 'A', 'C'), ('B', 'C', 'A'), ('C', 'A', 'B'), ('C', 'B', 'A')]


&gt;&gt;&gt; list(itertools.permutations(['A', 'B', 'C'], 2))
[('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
&gt;&gt;&gt; list(itertools.permutations(['A', 'B', 'C'], 1))
[('A',), ('B',), ('C',)]
&lt;/pre&gt;

&lt;p&gt;To have Python generate combinations, you can use &lt;code&gt;itertools.combinations()&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
&gt;&gt;&gt; import itertools
&gt;&gt;&gt; for v in itertools.combinations(['A', 'B', 'C'], 2):
...   print(v)
...
('A', 'B')
('A', 'C')
('B', 'C')


&gt;&gt;&gt; list(itertools.combinations(['A', 'B', 'C'], 1))
[('A',), ('B',), ('C',)]
&gt;&gt;&gt; list(itertools.combinations(['A', 'B', 'C'], 2))
[('A', 'B'), ('A', 'C'), ('B', 'C')]
&gt;&gt;&gt; list(itertools.combinations(['A', 'B', 'C'], 3))
[('A', 'B', 'C')]
&lt;/pre&gt;

&lt;p&gt;Note that the &lt;code&gt;combinations()&lt;/code&gt; function takes a second argument for the number of values to select. To get &lt;strong&gt;all&lt;/strong&gt; combinations (also called the &lt;em&gt;power set&lt;/em&gt;), you'll need to make multiple calls to &lt;code&gt;combinations()&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
&gt;&gt;&gt; powerSet = []
&gt;&gt;&gt; import itertools
&gt;&gt;&gt; for k in range(4):
...   powerSet.extend(itertools.combinations(['A', 'B', 'C'], k))
...
&gt;&gt;&gt; powerSet
[(), ('A',), ('B',), ('C',), ('A', 'B'), ('A', 'C'), ('B', 'C'), ('A', 'B', 'C')]
&lt;/pre&gt;

&lt;p&gt;To get permutations with repetition/replacement, call &lt;code&gt;itertools.product()&lt;/code&gt; and pass the size of the iterable object for its &lt;code&gt;repeat&lt;/code&gt; argument:&lt;/p&gt;

&lt;pre&gt;
&gt;&gt;&gt; import itertools
&gt;&gt;&gt; for v in itertools.product(['A', 'B', 'C'], repeat=3):
...   print(v)
...
('A', 'A', 'A')
('A', 'A', 'B')
('A', 'A', 'C')
('A', 'B', 'A')
('A', 'B', 'B')
('A', 'B', 'C')
('A', 'C', 'A')
('A', 'C', 'B')
('A', 'C', 'C')
('B', 'A', 'A')
('B', 'A', 'B')
('B', 'A', 'C')
('B', 'B', 'A')
('B', 'B', 'B')
('B', 'B', 'C')
('B', 'C', 'A')
('B', 'C', 'B')
('B', 'C', 'C')
('C', 'A', 'A')
('C', 'A', 'B')
('C', 'A', 'C')
('C', 'B', 'A')
('C', 'B', 'B')
('C', 'B', 'C')
('C', 'C', 'A')
('C', 'C', 'B')
('C', 'C', 'C')
&gt;&gt;&gt; list(itertools.product(['A', 'B', 'C'], repeat=3))
[('A', 'A', 'A'), ('A', 'A', 'B'), ('A', 'A', 'C'), ('A', 'B', 'A'), ('A', 'B', 'B'), ('A', 'B', 'C'), ('A', 'C', 'A'), ('A', 'C', 'B'), ('A', 'C', 'C'), ('B', 'A', 'A'), ('B', 'A', 'B'), ('B', 'A', 'C'), ('B', 'B', 'A'), ('B', 'B', 'B'), ('B', 'B', 'C'), ('B', 'C', 'A'), ('B', 'C', 'B'), ('B', 'C', 'C'), ('C', 'A', 'A'), ('C', 'A', 'B'), ('C', 'A', 'C'), ('C', 'B', 'A'), ('C', 'B', 'B'), ('C', 'B', 'C'), ('C', 'C', 'A'), ('C', 'C', 'B'), ('C', 'C', 'C')]
&lt;/pre&gt;


&lt;p&gt;To get combinations with repetition/replacement, call &lt;code&gt;itertools.combinations_with_replacement()&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
&gt;&gt;&gt; import itertools
&gt;&gt;&gt; for v in itertools.combinations_with_replacement(['A', 'B', 'C'], 3):
...   print(v)
...
('A', 'A', 'A')
('A', 'A', 'B')
('A', 'A', 'C')
('A', 'B', 'B')
('A', 'B', 'C')
('A', 'C', 'C')
('B', 'B', 'B')
('B', 'B', 'C')
('B', 'C', 'C')
('C', 'C', 'C')
&gt;&gt;&gt; list(itertools.combinations_with_replacement(['A', 'B', 'C'], 3))
[('A', 'A', 'A'), ('A', 'A', 'B'), ('A', 'A', 'C'), ('A', 'B', 'B'), ('A', 'B', 'C'), ('A', 'C', 'C'), ('B', 'B', 'B'), ('B', 'B', 'C'), ('B', 'C', 'C'), ('C', 'C', 'C')]
&lt;/pre&gt;

&lt;p&gt;If you're like me and you had trouble remembering the differences between permutations and combinations, with and without repetition, and which Python functions implement them, bookmark this page to have easy access in the future.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>New Book: The Big Book of Small Python Projects</title><link href="https://inventwithpython.com/blog/new-book-big-book-python.html" rel="alternate"></link><published>2021-06-26T12:01:00-04:00</published><updated>2021-06-26T12:01:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-06-26:/blog/new-book-big-book-python.html</id><summary type="html">&lt;p&gt;My new book, The Big Book of Small Python Projects, is now available from No Starch Press. It's free to read online under a Creative Commons license. This book contains 81 small but complete programming projects written in Python. These projects are designed to help new programmers who have learned the basics see what real programs "look like". These aren't code snippets, but actual, full programs that you can run. At the same time, they are small enough so that their source code is easy to understand.&lt;/p&gt;&lt;p&gt;&lt;img src="https://inventwithpython.com/images/cover_bigbookpython_thumb.jpg" /&gt;&lt;/p&gt;</summary><content type="html">

&lt;p&gt;&lt;img src="https://inventwithpython.com/images/cover_bigbookpython_thumb.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;My new book, The Big Book of Small Python Projects, is now available from No Starch Press. You can get the ebook for free when you &lt;a href="https://nostarch.com/big-book-small-python-projects"&gt;purchase a print book from No Starch Press&lt;/a&gt;. Or you can buy the book off &lt;a href="https://www.amazon.com/Big-Book-Small-Python-Programming/dp/1718501242/"&gt;Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://inventwithpython.com/bigbookpython/"&gt;Read "The Big Book of Small Python Projects" online for free.&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This book contains 81 small but complete programming projects written in Python. These projects are designed to help new programmers who have learned the basics see what real programs "look like". These aren't code snippets, but actual, full programs that you can run. At the same time, they are small enough so that their source code is easy to understand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.youtube.com/watch?v=f9k6Q2yys0k&amp;amp;feature=emb_title"&gt;Watch this book's video trailer created by the author.&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.teachingpython.fm/64"&gt;Episode 64 of the Teaching Python Podcast discusses the book.&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you've followed some basic Python tutorials but feel like programming hasn't quite "clicked" yet, this is the book to read. Or, if you are new to programming but adventureous enough to just dive into code, these programs are simple enough to be understood to new programmers as well.&lt;/p&gt;

&lt;p&gt;Many of these projects come from &lt;a href=""&gt;my old "STDIO game jam" projects in this git repo on GitHub&lt;/a&gt;. You can also find the source code of the programs in the book in &lt;a href="https://nostarch.com/download/BigBookPythonResources.zip"&gt;the downoadable resources file&lt;/a&gt; on the &lt;a href="https://github.com/asweigart/pythonstdiogames"&gt;No Starch Press page for the book&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Praise for The Big Book of Small Python Projects&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"I've always been impressed by the variety of simple, but interesting and fun, projects Al can come up with and this collection takes that to the next level... Even experienced coders are likely to be pulled into browsing through the wide variety that Al has put into this book."&lt;br /&gt;
—Naomi Ceder, Fellow of the Python Software Foundation&lt;/p&gt;

&lt;p&gt;"Al Sweigart presents fun programs, inspiring learners to tweak them. That's how I learned to program: tinkering with examples from books and magazines. It works!"&lt;br /&gt;
–Luciano Ramalho, Technical Principal at ThoughtWorks and author of Fluent Python&lt;/p&gt;

&lt;p&gt;"Whether you're new to Python or want to exercise your coding brain, I recommend The Big Book of Small Python Projects to spark your thinking. Both the 'big' and the 'small' in the title are accurate. There are a lot of projects in this book, but most code takes up only a page or two. That makes it a fun reference book to get you into the flow or kick off a session in your terminal."&lt;br /&gt;
–Adam DuVander, Founder, EveryDeveloper&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project Descriptions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project1.html"&gt;Project 1&lt;/a&gt;, Bagels: Deduce a secret three-digit number based on clues.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project2.html"&gt;Project 2&lt;/a&gt;, Birthday Paradox: Determine the probability that two people share the same birthday in groups of different sizes.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project3.html"&gt;Project 3&lt;/a&gt;, Bitmap Message: Display a message on the screen configured by a 2D bitmap image.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project4.html"&gt;Project 4&lt;/a&gt;, Blackjack: A classic card game played against an AI dealer.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project5.html"&gt;Project 5&lt;/a&gt;, Bouncing DVD Logo: Simulates the colorful bouncing DVD logo of decades past.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project6.html"&gt;Project 6&lt;/a&gt;, Caesar Cipher: A simple encryption scheme used thousands of years ago.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project7.html"&gt;Project 7&lt;/a&gt;, Caesar Hacker: A program to decrypt Caesar cipher messages without the encryption key.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project8.html"&gt;Project 8&lt;/a&gt;, Calendar Maker: Create calendar pages for a given year and month.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project9.html"&gt;Project 9&lt;/a&gt;, Carrot in a Box: A silly bluffing game between two players.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project10.html"&gt;Project 10&lt;/a&gt;, Cho-Han: A gambling dice game from feudal Japan.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project11.html"&gt;Project 11&lt;/a&gt;, Clickbait Headline Generator: A humorous headline generator for your content farm.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project12.html"&gt;Project 12&lt;/a&gt;, Collatz Sequence: Explore the simplest impossible conjecture in mathematics.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project13.html"&gt;Project 13&lt;/a&gt;, Conway’s Game of Life: The classic cellular automata whose simple rules produce complex emergent behavior.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project14.html"&gt;Project 14&lt;/a&gt;, Countdown: A countdown timer with a seven-segment display.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project15.html"&gt;Project 15&lt;/a&gt;, Deep Cave: A tunnel animation that descends endlessly into the earth.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project16.html"&gt;Project 16&lt;/a&gt;, Diamonds: An algorithm for drawing diamonds of various sizes.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project17.html"&gt;Project 17&lt;/a&gt;, Dice Math: A visual dice-rolling math game.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project18.html"&gt;Project 18&lt;/a&gt;, Dice Roller: A tool for reading Dungeons &amp; Dragons dice notation to generate random numbers.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project19.html"&gt;Project 19&lt;/a&gt;, Digital Clock: A clock with a calculator-like display.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project20.html"&gt;Project 20&lt;/a&gt;, Digital Stream: A scrolling screensaver that resembles The Matrix.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project21.html"&gt;Project 21&lt;/a&gt;, DNA Visualization: An endless ASCII-art double helix that demonstrates the structure of DNA.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project22.html"&gt;Project 22&lt;/a&gt;, Ducklings: Mix and match strings to create a variety of ASCII-art ducks.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project23.html"&gt;Project 23&lt;/a&gt;, Etching Drawer: Move the cursor to create line drawings.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project24.html"&gt;Project 24&lt;/a&gt;, Factor Finder: Find all the multiplicative factors of a number.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project25.html"&gt;Project 25&lt;/a&gt;, Fast Draw: Test your reflexes to see if you’re the fastest keyboard in the West.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project26.html"&gt;Project 26&lt;/a&gt;, Fibonacci: Generate numbers in the famous Fibonacci sequence.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project27.html"&gt;Project 27&lt;/a&gt;, Fish Tank: A colorful, animated ASCII-art fish tank.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project28.html"&gt;Project 28&lt;/a&gt;, Flooder: Attempt to fill the entire puzzle board with one color.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project29.html"&gt;Project 29&lt;/a&gt;, Forest Fire Sim: Simulate the spread of wildfires through a forest.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project30.html"&gt;Project 30&lt;/a&gt;, Four in a Row: A board game where two players try to connect four tiles in a row.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project31.html"&gt;Project 31&lt;/a&gt;, Guess the Number: The classic number guessing game.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project32.html"&gt;Project 32&lt;/a&gt;, Gullible: A humorous program to keep gullible people busy for hours.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project33.html"&gt;Project 33&lt;/a&gt;, Hacking Minigame: Deduce a password based on clues.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project34.html"&gt;Project 34&lt;/a&gt;, Hangman and Guillotine: The classic word guessing game.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project35.html"&gt;Project 35&lt;/a&gt;, Hex Grid: Programmatically generate tiled ASCII art.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project36.html"&gt;Project 36&lt;/a&gt;, Hourglass: A simple physics engine for falling sand.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project37.html"&gt;Project 37&lt;/a&gt;, Hungry Robots: Avoid killer robots in a maze.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project38.html"&gt;Project 38&lt;/a&gt;, J’Accuse!: A detective game to determine liars and truth-tellers.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project39.html"&gt;Project 39&lt;/a&gt;, Langton’s Ant: A cellular automata whose ants move according to simple rules.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project40.html"&gt;Project 40&lt;/a&gt;, Leetspeak: Translate English messages into l33t5p34]&lt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project41.html"&gt;Project 41&lt;/a&gt;, Lucky Stars: A push-your-luck dice game.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project42.html"&gt;Project 42&lt;/a&gt;, Magic Fortune Ball: A program to answer your yes/no questions about the future.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project43.html"&gt;Project 43&lt;/a&gt;, Mancala: The ancient two-player board game from Mesopotamia.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project44.html"&gt;Project 44&lt;/a&gt;, Maze Runner 2D: Try to escape a maze.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project45.html"&gt;Project 45&lt;/a&gt;, Maze Runner 3D: Try to escape a maze... in 3D!&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project46.html"&gt;Project 46&lt;/a&gt;, Million Dice Roll Statistics Simulator: Explore the probability results of rolling a set of dice one million times.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project47.html"&gt;Project 47&lt;/a&gt;, Mondrian Art Generator: Create geometric drawings in the style of Piet Mondrian.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project48.html"&gt;Project 48&lt;/a&gt;, Monty Hall Problem: A simulation of the Monty Hall game show problem.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project49.html"&gt;Project 49&lt;/a&gt;, Multiplication Table: Display the multiplication table up to 12 × 12.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project50.html"&gt;Project 50&lt;/a&gt;, Ninety-Nine Bottles: Display the lyrics to a repetitive song.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project51.html"&gt;Project 51&lt;/a&gt;, niNety-nniinE BoOttels: Display the lyrics to a repetitive song that get more distorted with each verse.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project52.html"&gt;Project 52&lt;/a&gt;, Numeral Systems Counters: Examine binary and hexadecimal numbers.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project53.html"&gt;Project 53&lt;/a&gt;, Periodic Table of the Elements: An interactive database of chemical elements.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project54.html"&gt;Project 54&lt;/a&gt;, Pig Latin: Translates English messages into Igpay Atinlay.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project55.html"&gt;Project 55&lt;/a&gt;, Powerball Lottery: Simulate losing at the lottery thousands of times.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project56.html"&gt;Project 56&lt;/a&gt;, Prime Numbers: Calculate prime numbers.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project57.html"&gt;Project 57&lt;/a&gt;, Progress Bar: A sample progress bar animation to use in other programs.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project58.html"&gt;Project 58&lt;/a&gt;, Rainbow: A simple rainbow animation.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project59.html"&gt;Project 59&lt;/a&gt;, Rock Paper Scissors: The classic hand game for two players.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project60.html"&gt;Project 60&lt;/a&gt;, Rock Paper Scissors (Always-Win Version): A version of the game where the player cannot lose.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project61.html"&gt;Project 61&lt;/a&gt;, ROT13 Cipher: The simplest cipher for encrypting and decrypting text.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project62.html"&gt;Project 62&lt;/a&gt;, Rotating Cube: A rotating cube animation.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project63.html"&gt;Project 63&lt;/a&gt;, Royal Game of Ur: A 5,000-year-old game from Mesopotamia.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project64.html"&gt;Project 64&lt;/a&gt;, Seven-Segment Display Module: A display like those used in calculators and microwave ovens.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project65.html"&gt;Project 65&lt;/a&gt;, Shining Carpet: Programmatically generate the carpet from The Shining.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project66.html"&gt;Project 66&lt;/a&gt;, Simple Substitution Cipher: An encryption scheme more advanced than the Caesar cipher.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project67.html"&gt;Project 67&lt;/a&gt;, Sine Message: Display a scrolling wave message.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project68.html"&gt;Project 68&lt;/a&gt;, Sliding Tile Puzzle: The classic four-by-four tile puzzle.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project69.html"&gt;Project 69&lt;/a&gt;, Snail Race: Fast-paced snail racing action!&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project70.html"&gt;Project 70&lt;/a&gt;, Soroban Japanese Abacus: A computer simulation of a pre-computer calculating tool.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project71.html"&gt;Project 71&lt;/a&gt;, Sound Mimic: Memorize an increasingly long pattern of sounds.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project72.html"&gt;Project 72&lt;/a&gt;, sPoNgEcAsE: Translates English messages into sPOnGEcAsE.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project73.html"&gt;Project 73&lt;/a&gt;, Sudoku Puzzle: The classic nine-by-nine newspaper deduction puzzle.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project74.html"&gt;Project 74&lt;/a&gt;, Text-to-Speech Talker: Make your computer talk to you!&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project75.html"&gt;Project 75&lt;/a&gt;, Three-Card Monte: The tricky fast-swapping card game that scammers play on tourists.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project76.html"&gt;Project 76&lt;/a&gt;, Tic-Tac-Toe: The classic two-player board game of Xs and Os.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project77.html"&gt;Project 77&lt;/a&gt;, Tower of Hanoi: The classic disk- stacking puzzle.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project78.html"&gt;Project 78&lt;/a&gt;, Trick Questions: A quiz of simple questions with misleading answers.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project79.html"&gt;Project 79&lt;/a&gt;, Twenty Forty-Eight: A casual tile matching game.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project80.html"&gt;Project 80&lt;/a&gt;, Vigenère Cipher: An encryption scheme so advanced it remained unbreakable for hundreds of years until computers were invented.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inventwithpython.com/bigbookpython/project81.html"&gt;Project 81&lt;/a&gt;, Water Bucket Puzzle: Obtain exactly four liters of water by filling and emptying three buckets.&lt;/li&gt;
&lt;/ul&gt;

</content><category term="misc"></category></entry><entry><title>Al Sweigart's PyTennessee 2020 Opening Keynote</title><link href="https://inventwithpython.com/blog/pytennessee-2020-keynote.html" rel="alternate"></link><published>2021-03-07T12:00:00-05:00</published><updated>2021-03-07T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-03-07:/blog/pytennessee-2020-keynote.html</id><summary type="html">&lt;p&gt;In March 2020, just before the lockdowns would begin, I gave the opening keynote at PyTennessee 2020. Unfortunately, it wasn't recorded. But I still have the script I used and so I re-recorded the talk. I've posted it to my YouTube channel.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;In March 2020, just before the lockdowns would begin, I gave the opening keynote at PyTennessee 2020. Unfortunately, it wasn't recorded. But I still have the script I used and so I re-recorded the talk. I've posted it to my YouTube channel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=_XiG9m72_tc"&gt;VIDEO: Al Sweigart's PyTennessee 2020 Opening Keynote&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Below is a transcript from my speaker notes:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hello, Community. Hello PyTennessee. Thank you for having me, thank you to all the conference organizers. I really appreciate all the efforts the organizers put into this conference. And thank all of you for attending PyTennessee. Thank you. I’ve never given a 9am talk before. It’s a bit earlier than I’m used to. Also, I realized on the flight here from Seattle, that timezone change meant that this more like a 7am talk for me. And that registration was at 6am, and I had to get up around 4:30 in the morning. So this could be a very interesting talk.&lt;/p&gt;

&lt;p&gt;Hi. I’m Al Sweigart. I’m mostly known as the author of Automate the Boring Stuff with Python.&lt;/p&gt;

&lt;p&gt;Thank you. I've never given a 9am talk before. It's a bit earlier than I'm used to. Also, I realized on the flight here from Seattle, that timezone change meant that this more like a 7am talk for me. And that registration was at 6am, and I had to get up around 4:30 in the morning. So this could be a very interesting talk.Hi. I'm Al Sweigart. I'm mostly known as the author of Automate the Boring Stuff with Python, a book for complete beginners which is available for free online under a Creative Commons license. Let me tell you about myself. I like cats, I like origami, and I absolutely hate telling the story of how I learned to program. I hate it. I just... can't stand it.&lt;/p&gt;

&lt;p&gt;When I was in elementary school, a friend of mine found a book on programming games in a language called BASIC. I was pretty lucky that I grew up with a PC in the house.&lt;/p&gt;

&lt;p&gt;Of course, back then computers typically only had about 700 PB memory, a 12 gigahertz quantum CPU, and the hard drive was only big enough to store three, maybe four simulated universes. You know, typical Commodore 64 type of computer.&lt;/p&gt;

&lt;p&gt;I was really into Nintendo, so programming seemed really cool to me. After high school I majored in computer science, and then I became a software engineer, and I move to San Francisco, and then I started writing programming books and the fourth book I wrote was picked up by No Starch Press, and I now write books full time. I hate telling this story. It's a true story, but it's also an incredibly misleading one. I hate telling this story because people might hear it and think, "Well I didn't start programming in the third grade." or "I didn't have a computer growing up." or "I didn't major in computer science." or "I didn't go to college." or "I didn't do well at math". People get the misleading idea that in order to become a capable software developer, you need to have started young, born with a genius IQ, and had elite training. That's of course complete nonsense. &lt;/p&gt;

&lt;p&gt;The part of my story that doesn't get told, is that the games I made weren't really that good. They were all derivatives of games I had copied from other people. I really only had one book that I learned programming from, and all of the others were technical manuals that were way over my head. I didn't have Wikipedia or search engines or YouTube tutorials. I knew like three other kids who were into programming and they didn't know much more than I did.&lt;/p&gt;

&lt;p&gt;Programming is so much easier now. It's still hard, but there are more resources available, the languages are more readable, the tools and documentation and community are just so much better now compared to the 1990s. And we have the internet so we can reach all of these things. And so in the introduction of the second edition of my book, I wrote "Everything I learned about programming in the years between grade school and high school graduation could be learned today in about a dozen weekends. My head start wasn't really much of a head start."&lt;/p&gt;

&lt;p&gt;But the expectations today are so much higher. When you say programming, people today think of self-driving cars or triple-A video game titles or social media websites with millions of users. No one's really impressed by Guess The Number when billion dollar tech companies are household names.&lt;/p&gt;

&lt;p&gt;You've probably seen news stories that say something like, "These local teens may be the next Bill Gates, Mark Zuckerberg, Elon Musk, or some other white guy with rich parents." These stories are everywhere, you can find them by doing a news search for "whiz kid programmer". The articles never go into that much detail about the actual software these teenagers create. So I started digging and I noticed that the programs these whiz kids made... aren't great?&lt;/p&gt;

&lt;p&gt;I don't mean to take away from their accomplishments, they are way ahead of most other kids and certainly ahead of me when I was their age. But mostly their programs are nice demos or re-creations of existing apps. They aren't geniuses or self-made millionaires, because producing actual software is so much more than just writing code. But that gets lost in these news stories about "whiz kids".&lt;/p&gt;

&lt;p&gt;How about me? Was I one of these "whiz kids"? Well, I don't mean to brag but in high school my computer science teacher picked me and two other students to represent our high school at a city wide programming competition. Oooo.&lt;/p&gt;

&lt;p&gt;So there were 3 of us on our school's team. There were 14 teams representing 14 high schools. We had 3 hours to solve 10 problems. The easiest problems were worth 1 point, the harder problems were worth more. At the end of a grueling three hours, our team scored 1 point. Yeah the programming language was C. And all the problems, except the first one, required reading input from a text file, and I didn't know how to do that. And neither did the two other kids on my team. We spent three hours reading help menus and documentation to try to figure out how to read text from a text file. And we got nowhere. So we could only answer the first question. But the thing is, we tied with or did better than 10 of the other schools. 11 out of 14 teams at this competition of hand-picked computer nerds scored 0 or 1 point.&lt;/p&gt;

&lt;p&gt;(pause) How did I get here? How did I manage to become a software developer and write a popular programming book and get invited to give the opening keynote at a conference? How did I get here? Was I a bright and talented computer whiz kid? (Laughter) Sure I was.&lt;/p&gt;

&lt;p&gt;I was lucky. I was lucky to grow up in a house that had a personal computer. I was lucky to have a friend who also grew up in a house with a PC and he showed me that BASIC programming book that luckily the school library had. I was lucky I had free time and no pressure so I could just explore programming and have fun making mediocre video games. I was lucky that no one got in my way, no one ever took a look at me, at my skin and my face and said "Mmmm, I don't think you'd be interested in that nerd stuff." I was lucky, and luck can strike anyone, but it can't strike everyone.&lt;/p&gt;

&lt;p&gt;So, on a lighter note: I don't like putting pop culture references in my talks because, frankly, it gives away how old I am. But as a kid I had a VHS tape of, I mean, I had a blue ray disc, of this one science fiction movie from the 1970s. It's called, uh, Star Wars? (pause) Oh good, some of you have heard of it. It's a really quotable movie. "May the force be with you", if you've ever heard that, it's from this movie.&lt;/p&gt;

&lt;p&gt;Anyway, there's a scene where a space wizard is teaching his apprentice how to use a laser sword. And he puts this helmet on the apprentice so he can't see, and he tells him to block shots with the laser sword using his magic powers instead of his eyes. And he does! He pulls that off. But then this other character says, "I call it luck." And the space wizard replies, "In my experience there's no such thing as luck." And I thought, "That's cool." Yeah. That's like me. I make my own luck. I'm a ten year old watching Star Wars and I know how the world works. You have to have talent and work hard and the best people rise to the top of our meritocracy.&lt;/p&gt;

&lt;p&gt;(pause for laughter)&lt;/p&gt;

&lt;p&gt;There's a saying that "luck favors the prepared", which implies that random chance and systemic factors don't play a significant role and talented, hard-working people are rewarded. And that's kind of true. But I discovered another saying, "Luck favors the supported." I got where I am today because I had a lot of support. Not everyone can be lucky, but everyone can be supported. But in order to do that, we need to create inclusive communities and lower the barriers to entry. We can't just use the same excuses of "people need to have a thicker skin" and "people need to earn their place in our community". That's not high standards, that's just hazing. We need to lower the barriers to entry, and if we can't lower these barriers, we need to smash them. We can't just hope that people find the right book and then everything falls into place and they become software developers giving keynotes.&lt;/p&gt;

&lt;p&gt;Now, there's going to be some people watching this talk who'll think, "Oh brother. Al, this is programming language conference. Why are you talking about diversity and inclusion? Code doesn't care about your feelings, you SJW. Why can't you be objective and rational? You should be talking about algorithms or arguing why Python 3 sucks."&lt;/p&gt;

&lt;p&gt;Why is diversity important? Let me tell you a story that takes place before the story of how I learned to program.&lt;/p&gt;

&lt;p&gt;In 1985, Nintendo released the 8-bit Nintendo Entertainment System and Super Mario Brothers. This was the first video game I ever played and it directly lead to me wanting to learn how to program. But there were earlier video games.&lt;/p&gt;

&lt;p&gt;In 1972, Atari released Pong which became immensely popular and introduced a wide audience to video games. Lots of companies started making their own knock offs of Pong with their own hardware. Atari released their own home console which had hundreds of game titles.&lt;/p&gt;

&lt;p&gt;One of which was ET in 1983. This was based on the ET movie which was very popular at the time. But the game was rushed. The developers only had five and half weeks to make it so it could be released in time for Christmas. When it came out, people hated it. It was confusing, you weren't sure what you were supposed to do or where to go, the controls were bad, and it was just... boring. Compared to the beloved movie, the game was huge disappointment.&lt;/p&gt;

&lt;p&gt;Not only that, ET is often cited as one of the worst video games of all time and one of the biggest commercial failures in video game history. And this was at a bad time for video games as an industry.&lt;/p&gt;

&lt;p&gt;With so many companies making cheap knock offs, consumers were getting tired of expensive hardware and lousy games. Revenue for the industry fell from a peak of $3.2 billion in 1983, to $100 million two years later. That was a 97% drop for the entire industry. This was the Video Game Crash of 1983.&lt;/p&gt;

&lt;p&gt;Arguably, what saved video games in the 1980s was Nintendo. &lt;/p&gt;

&lt;p&gt;Microprocessors were more powerful by then and they could support better graphics, but Nintendo's success was so much more than that. It's a bit of a game design cliche to explain how the first level of Mario teaches you how to play Mario, but it's such a great story I have to go into it.&lt;/p&gt;

&lt;p&gt;Okay, when you start playing, Mario begins on the left facing right. This makes it obvious that you're suppose to go right. Every Mario level is designed to go from left to right, so you always know what you're supposed to do to make progress.&lt;/p&gt;

&lt;p&gt;Now you soon find a flashing question mark block, which grabs your interest. You want to go to it but then you encounter your first enemy, an angry-looking mushroom with feet. It's just one, this isn't a huge challenge. If you run into the angry mushroom, you die but that's fine because you start again at the beginning and haven't lost much ground.&lt;/p&gt;

&lt;p&gt;Shortly after you hit another question block and a mushroom pops out of it. This mushroom moves right, falls off the ledge, bounces off the green pipe and changes direction towards Mario. This means you're almost guaranteed to run into it, even if you confuse it for another enemy that you're supposed to avoid. When Mario touches it, he grows bigger and more powerful, so you can tell it's a powerup. This one event teaches you a lot about how objects in this world behave: there are power ups, they move on their own, they are affected by gravity, they can collide with objects and change direction.&lt;/p&gt;

&lt;p&gt;Next you jump over that green pipe, and then there's a slightly taller green pipe after it. In Super Mario Brothers, the longer you hold down the jump button, the higher Mario jumps. Then there's a third pipe that's even taller than the second pipe. It's fine if you mess up, the pipe isn't going anywhere. This lets you practice high jumping until you've mastered it and gotten over the third pipe. Which is good because right after the green pipe is a bottomless pit. You had practice, now comes the test. If you jump too early, you'll end up falling into the pit instead of landing on the other side.&lt;/p&gt;

&lt;p&gt;So to keep that from happening, there's a hidden powerup block that you run into which makes you fall back down immediately instead of into the bottomless pit. Out pops another powerup that gives you an extra life. This not only keeps you from making a bad jump, but it teaches you that there are secret hidden blocks that you can run into in this game. There's tons of stuff like this in the first level. You didn't see this kind of careful design in Atari games.&lt;/p&gt;

&lt;p&gt;The main game designer behind Mario and other classic Nintendo games is Shigeru Miyamoto. There's a great quote from an interview of his. He goes...&lt;/p&gt;

&lt;p&gt;"Well, early on, the people who made video games, they were technologists, they were programmers, they were hardware designers. But I wasn't. I was a designer, I studied industrial design. I was an artist, I drew pictures."&lt;/p&gt;

&lt;p&gt;The person who is arguably responsible for much of video gaming's comeback in the 1980s and made me and many others into programmers, wasn't a programmer. He didn't devise new algorithms or write a compiler or anything like that. This non-programmer's impact on the programming world is immeasurable.&lt;/p&gt;

&lt;p&gt;Diversity is important, because different people bring different skills and perspectives. They'll also have different needs which necessitate a variety of invention. The world itself is a diverse place, and if your community is a monoculture, then it's going to have blindspots and an existential danger of being irrelevant.&lt;/p&gt;

&lt;p&gt;The thing about barriers to entry is that they're often invisible. It's easy for us to look around this room and see the people who are here. But it's also easy to forget to look for the people who aren't here. Let me put this way: When Al Sweigart walks into a tech meetup, Al Sweigart see a lot of people who look like Al Sweigart. I'm biracial white and Asian, I'm a cisgender man, I'm straight, I'm able-bodied and I'm in my thirties and I speak English natively and I have a college degree and I have nerdy hobbies. When I tell people I'm a software developer, no one is surprised. I'm a quote-unquote "culture fit".&lt;/p&gt;

&lt;p&gt;Of course, there's no rule that explicitly states some kinds of people are welcome and some aren't. But there doesn't need to be one. Exclusion happens anyway.&lt;/p&gt;

&lt;p&gt;This is a problem that is both immense and subtle, and I'm sorry that I have to gloss over so many details for a one hour talk. But for folks who are sick of me rambling about wishy-washy feelings, let's go into some hardcore technical chops. Let's be objective and rational and logical,  you know like true programmers are.&lt;/p&gt;

&lt;p&gt;This is a computer simulation called the Parable of the Polygons by vi hart and nicky case. It's a story of how harmless choices can make a harmful world.&lt;/p&gt;

&lt;p&gt;In this simulation are triangles and squares. And triangles and squares don't *dislike* each other, they just have a slight preference for being around people like them. Nothing wrong with that, right? If a shape has less than a third of their neighbors like them, they'll move to a random new place. This doesn't seem so bad. They're not moving away from people who are different, they just have a slight preference to move closer to people like themselves.&lt;/p&gt;

&lt;p&gt;When we run this simulation, we end up with a society that is deeply segregated. Okay, at this point, we learn the error of our ways and we decrease that bias from 33% to 0%. Problem solved, right? Well no. When we run the simulation again, nobody moves. There's nothing that pushes for desegregation, so a segregated society stays segregated. If your solution is to say, "I don't even see differences in shapes. There's only one shape and that's polygon." That is not a neutral position to have. It's a position that maintains segregation. It's a pro-segregation stance.&lt;/p&gt;

&lt;p&gt;But is that so bad? So what if nerdy programmers just want to hang out with other nerdy programmers? What's the harm? What do we lose? I don't see anything missing.&lt;/p&gt;

&lt;p&gt;There is a way to turn a community from segregated to diverse. And that's to have just a slight bias against monoculture. To take active steps towards creating diversity. It doesn't have to be much. If we set the simulation so that shapes want to move if more than 80% of their neighbors are like them,&lt;/p&gt;

&lt;p&gt;We completely undo the segregation that previously took place. Now there's also issues of tokenism and assimilation but this is a short talk and I'm almost at the end of it.&lt;/p&gt;

&lt;p&gt;One of my nerdy interests is science fiction, specifically the cyberpunk genre of science fiction. Cyberpunk is usually about hackers, and "high tech, low life" kind of settings. Think about The Matrix, or Johnny Mnemonic. Actually that was a terrible movie, just think about The Matrix if you need an example of cyberpunk. (pause) Heh. Keanu Reeves has been in a lot of cyberpunk movies. Anyway.&lt;/p&gt;

&lt;p&gt;The cyberpunk genre arguably began with William Gibson's novel Neuromancer in 1984, but my favorite cyberpunk novel is&lt;/p&gt;

&lt;p&gt;Snow Crash by Neal Stephenson, published in 1992. Specifically I love this one scene that describes a programmer named Juanita working at a software company that's making a virtual reality version of the world wide web, and specifically she's working on making the faces of the avatars be able to convey emotion and expression. The book goes "But at this phase, the all-male society of bit-heads that made up the power structure of Black Sun Systems said that the face problem was trivial and superficial. It was, of course, nothing more than sexism, the especially virulent type espoused by male techies who sincerely believe that they are too smart to be sexists."&lt;/p&gt;

&lt;p&gt;Neal Stephenson was commenting on the real-world tech industry 30 years ago. I think about this line, and I think about this tweet from Jessica McKellar, who was the PyCon Diversity Chair for a number of years, and she shows how PyCon went from having 1% of its talks by women to 40% in five years. Think about how little diversity in the tech industry has changed in the 30 years since Snow Crash, and think of how much can it change in 5 years if we actually take diversity seriously.&lt;/p&gt;

&lt;p&gt;I'm just impatient. I take a look at the Mercury 7, the seven astronauts selected by NASA in the 1950s to be the first Americans in space. Notice something all seven have in common with each other? I mean, there's been plenty of astronauts who weren't white men. There's... no reason the Mercury 7 had to all be white men. They were all pilots but there were pilots who weren't white men back even before the 1950s. But I'm sure the American government had reasons for why 7 out of 7 of their astronauts white and male. It's just that those reasons were wrong. And dumb. So I'm impatient. There's no reason this disparity needed to exist in the 1950s in the space program and no reason it needs to exist today in tech. The excuses we give for it are wrong and dumb. &lt;/p&gt;

&lt;p&gt;Opportunity and obstacle are not randomly distributed. There is a pattern to privilege. We're not all playing on a level field. Ask anyone, anyone, to describe life and they will you that life is 1) tough and 2) not fair. That's why it falls on us to make our communities more equitable. And I'm impatient. I want to see results. I don't want another 30 years of it being enough to just say, "We're an equal opportunity employer" and have nothing actually change.&lt;/p&gt;

&lt;p&gt;Does this mean we have to lower our standards to increase diversity? No. PyCon continues to be awesome, it hasn't gotten worse, it sells out every year. The Python conference you are sitting in right now also sold out of tickets. We haven't lowered standards by making efforts to support and reach out to voices we haven't heard from before. Everyone should learn to code. It's awesome. It's fun as a hobby, you can write little scripts to automate stuff on your computer, and if you stick with it you can even make a career of it. That's something that should be available to everyone.&lt;/p&gt;

&lt;p&gt;And we can make it available to everyone.&lt;/p&gt;

&lt;p&gt;(breathe) So, that's big picture. Let me leave you with some smaller tips. My first PyCon was 2011 in Atlanta. Lovely town. They have an aquarium with a whale shark. I know, right? And since then I've learned a lot about how to make the most of PyCon.&lt;/p&gt;

&lt;p&gt;First, the point of PyCon is to meet people. The talks are great, thank you for coming to mine, but they're also being recorded. Don't feel like you have to cut conversations with people short to make it to every single talk.&lt;/p&gt;

&lt;p&gt;When you're chatting with a group of people, you naturally stand in a circle. Instead stand like pacman with an open space, so new people can join the discussion.&lt;/p&gt;

&lt;p&gt;I love going to pycon because I see my friends from across the country, but I have to remind myself that I also should spend time talking to new people. Don't spend the entire conference hanging out exclusively with your friends. Or go ahead and hang out with your friends, but also introduce new people to them.&lt;/p&gt;

&lt;p&gt;Don't eat alone. Conferences usually have breakfasts and lunches and dinners, go to those. Follow the pycon hashtag on twitter and ask if people are going out to eat somewhere at the end of the day, or organize your own group dinner plans with random strangers standing next to you. That happens all the time.&lt;/p&gt;

&lt;p&gt;Business cards! You remembered to bring business cards, right? That's fine if you didn't, just make one in a text file on your phone's screen and let the other person take a photo of it. But when do you get business cards, order the minimum amount. I know it's cheaper if you buy 50,000 cards, but trust me, just order the minimum amount. Make sure it's white and not glossy so people can write on it. And only have public information about yourself. Somebody should be able to take a photo of your business card and post it on the internet, and you're fine with it. I don't have my home address or even phone number on my card. So I don't care who ends up with it. After receiving business cards, write down the circumstances that you met them. And when you get a business card from someone, quickly write down the circumstances and what you talked about with the person, so you'll remember it later.&lt;/p&gt;

&lt;p&gt;And do follow up. Twitter is great for this. You don't have to post to Twitter, just sign up for an account and you can just follow people and casually reply to random stuff they talk about.&lt;/p&gt;

&lt;p&gt;This is a last minute slide addition: wash your hands from time to time throughout the day. No reason I'm bringing this up. This has always been good advice at all conferences. Just... wash your hands.&lt;/p&gt;

&lt;p&gt;And finally, there's the shirt color trick. You're in a room full of strangers, and you want to pick someone to talk to. Here's what you do. Think of a color. Find the closest person wearing a shirt of that color. Talk to that person.&lt;/p&gt;

&lt;p&gt;Don't think about it, just go by shirt color, the more you think about it, the more your internal biases will choose who you do and do not speak to. Don't think about who you might get along with or who you might have stuff in common with. No, talk to someone truly random. Because when we talk to everyone, we include everyone. I hope you enjoy PyTenneessee 2020. Thank you very much for having me.&lt;/p&gt;



</content><category term="misc"></category><category term="review"></category></entry><entry><title>Calm Programming Video Series</title><link href="https://inventwithpython.com/blog/calm-programming-video-series.html" rel="alternate"></link><published>2021-03-06T12:00:00-05:00</published><updated>2021-03-06T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-03-06:/blog/calm-programming-video-series.html</id><summary type="html">I've started a new video series of me silently coding a small game project from scratch. The Calm Programming series are meant to be coding videos that you can watch while playing the music of your choice. They aren't quite the same as tutorials: they don't have screen capture and aren't narrated, though you can turn on captions for some explanations.&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=tu0lnL0EbGE&amp;list=PL0-84-yl1fUm9CQDBaS1PUjrGBkGwCOFw"&gt;&lt;img src="/blogstatic/calm-programming-series.png" width=300 /&gt;&lt;/a&gt;&lt;/p&gt;</summary><content type="html">

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=tu0lnL0EbGE&amp;amp;list=PL0-84-yl1fUm9CQDBaS1PUjrGBkGwCOFw"&gt;&lt;img src="/blogstatic/calm-programming-series.png" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've started &lt;a href="https://www.youtube.com/watch?v=tu0lnL0EbGE&amp;amp;list=PL0-84-yl1fUm9CQDBaS1PUjrGBkGwCOFw"&gt;a new video series&lt;/a&gt; of me silently coding a small game project from scratch. The Calm Programming series are meant to be coding videos that you can watch while playing the music of your choice. They aren't quite the same as tutorials: they don't have screen capture and aren't narrated, though you can turn on captions for some explanations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=tu0lnL0EbGE"&gt;The premiere episode is creating a text-based blackjack card game from scratch.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm pulling the projects from my collection of short, text-based games &lt;a href="https://github.com/asweigart/gamesbyexample"&gt;at this repo&lt;/a&gt; which will be featured in my upcoming book, "The Big Book of Small Python Projects" from No Starch Press.&lt;/p&gt;

&lt;p&gt;I plan on making some improvements to the format based on my experience with the first episode: I'm going to lower the screen brightness to reduce glare, cover the phone camera's screen so it doesn't reflect off the laptop screen, changed the terminal windows font to be larger and a light gray, and show a demonstration of the completed game at the start of each episode. I'm welcome to new ideas or suggestions: &lt;a href="mailto:al@inventwithpython.com"&gt;al@inventwithpython.com&lt;/a&gt;.&lt;/p&gt;

</content><category term="misc"></category><category term="review"></category></entry><entry><title>Text Recognition in Python with pytesseract</title><link href="https://inventwithpython.com/blog/ocr-in-python.html" rel="alternate"></link><published>2021-01-11T12:01:00-05:00</published><updated>2021-01-11T12:01:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-01-11:/blog/ocr-in-python.html</id><summary type="html">&lt;p&gt;Extracting text as string values from images is called &lt;em&gt;optical character recognition&lt;/em&gt; (OCR) or simply &lt;em&gt;text recognition&lt;/em&gt;. This blog post tells you how to run the Tesseract OCR engine from Python. Includes working code examples.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;Extracting text as string values from images is called &lt;em&gt;optical character recognition&lt;/em&gt; (OCR) or simply &lt;em&gt;text recognition&lt;/em&gt;. This blog post tells you how to run the Tesseract OCR engine from Python. For example, if you have the following image stored in &lt;a href="/blogstatic/diploma_legal_notes.png"&gt;diploma_legal_notes.png&lt;/a&gt;, you can run OCR over it to extract the string of text.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/diploma_legal_notes.png" /&gt;&lt;/p&gt;

&lt;blockquote&gt;&lt;code&gt;' \n\n \n\nCLASS OF 2019!\n\nYOUR DIPLOMA GRANTS YOU MANY NEW\nPOWERS AND PRIVILEGES. THESE INCLUDE:\n\n+ YOU MAY NOW LEGALLY PERFORM\nMARRIAGES AND ARREST PEOPLE.\n\n+ IF YOU HAVE YOUR DIPLOMA WITH YOU,\nYOU CAN USE. GROCERY STORE EXPRESS\nLANES WITH ANY NUMBER OF ITEMS.\n\n+ ALL GRADUATES ARE ENTITLED To\nDELETE ONE WORD OF THEIR CHOICE.\nFROM THE OXFORD ENGUSH DICTIONARY.\n+ THE UNIVERSITY WILL MAIL YOU YOUR’\nWORKING LIGHTSABER WITHIN 6-8 WEEKS.\n* YOU CAN SEND MAIL WITHOUT STAMPS.\n+ YOU HAVE EARNED THE RIGHT TO\nCHALLENGE THE BRITISH ROYAL FAMILY\nTO TRIAL BY COMBAT. IF YOU DEFEAT\nTHEM ALL, THE THRONE |S YOURS.\n\n* YOU MAY NOL IGNORE. "DO NOT PET”\nWARNINGS ON AIRPORT SECURITY DOGS.\n\n= CONGRATULATIONS,\n\n \n\n \n\x0c'&lt;/code&gt;&lt;/blockquote&gt;

&lt;p&gt;Notice that the text recognition isn't quite perfect. The "CONGRATULATIONS" at the top is mistakenly placed at the bottom, the word "NOW" is recognized as "NOL", the word "IS" has a vertical pipe character "|" instead of a capital "I", and there seems to be some confusing punctuation marks added in. Cleaning up your OCR scans depends on your particular project. This blog post will simply show you the code to do OCR from Python.&lt;/p&gt;

&lt;h2&gt;Installing Tesseract&lt;/h2&gt;

&lt;p&gt;First, run &lt;code&gt;pip install pytesseract&lt;/code&gt;. The &lt;a href="https://pypi.org/project/pytesseract/"&gt;pytesseract&lt;/a&gt; package is a Python wrapper for the Tesseract OCR engine. If you need help running pip, see &lt;a href="https://inventwithpython.com/blog/2021/01/11/a-quick-pip-guide-how-to-install-python-modules/"&gt;A Quick Pip Guide&lt;/a&gt; or &lt;a href="https://realpython.com/what-is-pip/"&gt;What Is Pip? A Guide for New Pythonistas&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At this point, if you tried to use the &lt;code&gt;pytesseract&lt;/code&gt; module, you'd get a &lt;code&gt;TesseractNotFoundError&lt;/code&gt; message that says, &lt;code&gt;tesseract is not installed or it's not in your path&lt;/code&gt;. (You can learn about the PATH environment variable in &lt;a href=""&gt;Chapter 2 of my free book, Beyond the Basic Stuff with Python&lt;/a&gt;.) Let's install the Tesseract OCR engine itself next.&lt;/p&gt;

&lt;p&gt;On Windows, you can download &lt;a href="https://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-w64-setup-v5.0.0-alpha.20201127.exe"&gt;the installer for version 5.0.0 of Tesseract&lt;/a&gt; and run the installer. (To get the latest version of Tesseract, go to the &lt;a href="https://github.com/UB-Mannheim/tesseract/wiki"&gt;Tesseract at UB Mannheim web page&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;On macOS, according to &lt;a href="https://guides.library.illinois.edu/c.php?g=347520&amp;amp;p=4121425"&gt;this article&lt;/a&gt;, you can install Tesseract with Brew by opening a Terminal window and running &lt;code&gt;brew install tesseract --all-languages&lt;/code&gt;. You can also install Tesseract on macOS with MacPorts by running &lt;code&gt;sudo port install Tesseract&lt;/code&gt;, then run &lt;code&gt;sudo port install tesseract-eng&lt;/code&gt; to install the English language. For other languages, &lt;a href="https://ports.macports.org/?search=tesseract-&amp;amp;search_by=name"&gt;use the language codes listed in this link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On Linux, Tesseract may already be installed. If it isn't, according to &lt;a href="https://linuxhint.com/install-tesseract-ocr-linux/"&gt;this article&lt;/a&gt;, you can run the following:&lt;/p&gt;

&lt;p&gt;On Ubuntu, run &lt;code&gt;sudo apt-get install tesseract-ocr&lt;/code&gt; and then &lt;code&gt;sudo apt-get install tesseract-ocr-all&lt;/code&gt; to install all languages. You also need to install ImageMagick for its &lt;code&gt;convert&lt;/code&gt; program by running &lt;code&gt;sudo apt-get install imagemagick&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On Fedora, run &lt;code&gt;sudo dnf install tesseract&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On Manjaro, run &lt;code&gt;sudo pacman -Syu tesseract&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;Installing Pillow&lt;/h2&gt;

&lt;p&gt;The pytesseract module also requires the Pillow module for Python. You can install with pip by running &lt;code&gt;pip install pillow&lt;/code&gt; on Windows or &lt;code&gt;pip3 install pillow&lt;/code&gt; on macOS and Linux.&lt;/p&gt;

&lt;h2&gt;Running Tesseract from Python&lt;/h2&gt;

&lt;p&gt;To extract text from an image file named &lt;em&gt;image.png&lt;/em&gt;, run the following code:&lt;/p&gt;

&lt;pre&gt;import pytesseract as tess
from PIL import Image
img = Image.open('image.png')
text = tess.image_to_string(img)
print(text)&lt;/pre&gt;

&lt;p&gt;The recognized text in the image is returned as a string value from &lt;code&gt;image_to_string()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Additional Resources&lt;/h2&gt;

&lt;p&gt;To see an example of all of these steps, check out this YouTube video: &lt;a href="https://www.youtube.com/watch?v=4DrCIVS5U3Y"&gt;Image to Text with pytesseract&lt;/a&gt;&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>A Quick Pip Guide: How to Install Python Modules</title><link href="https://inventwithpython.com/blog/quick-pip-guide.html" rel="alternate"></link><published>2021-01-11T12:00:00-05:00</published><updated>2021-01-11T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2021-01-11:/blog/quick-pip-guide.html</id><summary type="html">&lt;p&gt;This tutorial teaches you about Pip, a &lt;em&gt;package manager&lt;/em&gt; that installs third-party packages for Python.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;Pip is a &lt;em&gt;package manager&lt;/em&gt; that installs third-party packages (such as &lt;a href="https://pypi.org/project/PyAutoGUI/"&gt;PyAutoGUI&lt;/a&gt;, &lt;a href="https://pypi.org/project/matplotlib/"&gt;Matplotlib&lt;/a&gt;, or &lt;a href="https://pypi.org/project/pygame/"&gt;Pygame&lt;/a&gt;) from &lt;a href="https://pypi.org/"&gt;https://pypi.org/&lt;/a&gt;, the Python Package Index. For a more detailed blog post on how pip works, see &lt;a href="https://realpython.com/what-is-pip/"&gt;What Is Pip? A Guide for New Pythonistas&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On Windows, to install a module named &lt;em&gt;module_name&lt;/em&gt; open a Command Prompt window and run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;strong&gt;pip install --user &lt;em&gt;module_name&lt;/em&gt;&lt;/strong&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On macOS and Linux, &lt;code&gt;pip&lt;/code&gt; is for the older Python 2 version, so you need to open a Terminal window and run &lt;code&gt;pip3&lt;/code&gt; instead. On Ubuntu Linux, pip doesn't come with the pre-installed version of Python. You'll need to install it by opening a Terminal window and running &lt;code&gt;sudo apt-get install python3-pip&lt;/code&gt;. (This &lt;a href="https://askubuntu.com/questions/603892/pip-is-not-included-in-python3"&gt;AskUbuntu&lt;/a&gt; question has more information.)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;strong&gt;pip3 install --user &lt;em&gt;module_name&lt;/em&gt;&lt;/strong&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(If you get a &lt;code&gt;'pip' is not recognized as an internal or external command, operable program or batch file.&lt;/code&gt; or &lt;code&gt;pip3: command not found&lt;/code&gt; error message, then the folder that the pip tool is listed in must be added to the PATH environment variable. You can find out about hte command line and environment variables in &lt;a href="https://inventwithpython.com/beyond/chapter2.html"&gt;Chapter 2: Environment Setup and the Command Line in my free book, Beyond the Basic Stuff with Python&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you can run &lt;code&gt;python&lt;/code&gt; or &lt;code&gt;python3&lt;/code&gt; from the terminal/command prompt window without errors, you can use Python itself to run pip by entering the following on Windows:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;strong&gt;python -m pip install --user &lt;em&gt;module_name&lt;/em&gt;&lt;/strong&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On macOS and Linux, run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;strong&gt;python3 -m pip install --user &lt;em&gt;module_name&lt;/em&gt;&lt;/strong&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Remember to run &lt;code&gt;pip&lt;/code&gt; or &lt;code&gt;python&lt;/code&gt; from the &lt;em&gt;command line&lt;/em&gt; (either Terminal or Command Prompt) and not from the Python interactive shell. The Python interactive shell will have the &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; prompt. However, you can run pip from the Python interactive shell by running the following Python code:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;strong&gt;import sys, os; os.system(sys.executable + ' -m pip install --user &lt;em&gt;module_name&lt;/em&gt;')&lt;/strong&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Note the space before the &lt;code&gt;-m&lt;/code&gt; is required. Replace &lt;em&gt;module_name&lt;/em&gt; with the name of the module you wish to install, but type everything else exactly as shown.&lt;/p&gt;

&lt;p&gt;You can find packages to install by browsing &lt;a href="https://pypi.org/"&gt;PyPI, the Python Package Index, at https://pypi.org/&lt;/a&gt;&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Installing the ComputerCraft Mod for Minecraft 1.16 (and later)</title><link href="https://inventwithpython.com/blog/installing-computercraft-for-minecraft.html" rel="alternate"></link><published>2020-12-24T12:00:00-05:00</published><updated>2020-12-24T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2020-12-24:/blog/installing-computercraft-for-minecraft.html</id><summary type="html">&lt;p&gt;How to install the ComputerCraft mod, which adds Lua-programmable robots to Minecraft.&lt;/p&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=6-hyqrwBEq0"&gt;&lt;img src="/blogstatic/install-computercraft-youtube-video.png" width=300 /&gt;&lt;/a&gt;&lt;/p&gt;</summary><content type="html">

&lt;p&gt;My book, &lt;a href="https://turtleappstore.com/book/"&gt;Coding with Minecraft&lt;/a&gt; covers the ComputerCraft mod for Minecraft and is free to read online. ComputerCraft adds programmable robot "turtles" to the Minecraft world, and you can write &lt;a href="https://www.lua.org/"&gt;Lua&lt;/a&gt; programs to have the robots mine, build, farm, and craft automatically.&lt;/p&gt;

&lt;p&gt;As of 2020, the original ComputerCraft mod only supports up to Minecraft 1.12. However, the &lt;a href="https://computercraft.cc/"&gt;ComputerCraft Tweaked&lt;/a&gt; mod is a fork of the original ComputerCraft project and supports later versions of Minecraft (up to version 1.16 as of 2020).&lt;/p&gt;

&lt;p&gt;The Coding with Minecraft book's instructions for installing this mod are out of date, but this blog post explains how to install the ComputerCraft Tweaked mod. &lt;strong&gt;After installing this new mod, the rest of the Coding with Minecraft book is still valid and applicable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=6-hyqrwBEq0"&gt;You can also watch this video of the ComputerCraft installation process.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=6-hyqrwBEq0"&gt;&lt;img src="/blogstatic/install-computercraft-youtube-video.png" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The steps are:&lt;/p&gt;

&lt;p&gt;&lt;ol&gt;
    &lt;li&gt;Download and run the &lt;a href="https://files.minecraftforge.net/"&gt;Forge mod installer&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;Download the &lt;a href="https://www.curseforge.com/minecraft/mc-mods/cc-tweaked"&gt;ComputerCraft: Tweaked mod&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;From the Minecraft launcher, create a new installation that uses the "release 1.16.4-forge-35.1.4" mod. (Or whichever version of Forge you downloaded.)&lt;/li&gt;
    &lt;li&gt;Play the new installation of Minecraft.&lt;/li&gt;
    &lt;li&gt;From the Minecraft start sceen, click Mods, then click Open Mods Folder.&lt;/li&gt;
    &lt;li&gt;Copy the CC: Tweaked jar file to this folder.&lt;/li&gt;
    &lt;li&gt;Restart Minecraft. Check that "3 mods loaded" appears in the lower left.&lt;/li&gt;
    &lt;li&gt;You are now ready to play Minecraft with the CC mod.&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;

&lt;p&gt;First, you need to download Forge (a mod that runs other mods, such as CC Tweaked). Open a web browser to &lt;a href="https://files.minecraftforge.net/"&gt;https://files.minecraftforge.net/&lt;/a&gt; and select the version of Minecraft you have (as of December 2020, I have 1.16.4) and click the &lt;strong&gt;Installer&lt;/strong&gt; link underneath "Download Recommended". You can also click on &lt;strong&gt;Installer&lt;/strong&gt; underneath "Download Latest". This version will have the latest bug fixes (but also might have new bugs). The Forge file will be named something like &lt;em&gt;forge-1.16.4-35.1.4-installer.jar&lt;/em&gt;. (The 1.16.4 refers to that this is for Minecraft version 1.16.4, and the 35.1.4 refers to the version of Forge.)&lt;/p&gt;

&lt;p&gt;The minecraftforge.net website looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/cc-screenshots/download-forge.png" /&gt;&lt;/p&gt;


&lt;p&gt;Double-click the &lt;em&gt;forge-1.16.4-35.1.4-installer.jar&lt;/em&gt; file to run it. It will look like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/cc-screenshots/run-forge.png" /&gt;&lt;/p&gt;

&lt;p&gt;Make sure "Install Client" is checked, and click OK. When the Forge installer is done, the window will tell you the installation was successful:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/cc-screenshots/install-forge-success.png" /&gt;&lt;/p&gt;

&lt;p&gt;Next, go to &lt;a href="https://www.curseforge.com/minecraft/mc-mods/cc-tweaked"&gt;the CC: Tweaked page on the CurseForge.com website&lt;/a&gt;, click the &lt;storng&gt;Files&lt;/storng&gt; tab, and download the "Main File". As of December 2020, this is &lt;em&gt;cc-tweaked-1.16.4-1.94.0.jar&lt;/em&gt;. (The 1.16.4 refers to that this is for Minecraft version 1.16.4, and the 1.94.0 refers to the version of CC: Tweaked.)&lt;/p&gt;


&lt;p&gt;Instead of using the &lt;a href="https://atlauncher.com/"&gt;ATLauncher&lt;/a&gt; program, you can now install mods using the regular Minecraft Launcher that comes with Minecraft. Start the Minecraft Launcher. It will look like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/cc-screenshots/install-cc-1-launcher.png" /&gt;&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;Installations&lt;/strong&gt; tab. We will create a new installation for ComputerCraft Tweaked. Click the &lt;strong&gt;New&lt;/strong&gt; button and the screen will look like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/cc-screenshots/create-new-install-cc.png" /&gt;&lt;/p&gt;

&lt;p&gt;name your installation something like, "ComputerCraft Install" and for the version select "release 1.16.4-forge-35.1.4" (or whatever Forge download you installed). Finally, click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Back on the &lt;strong&gt;Play&lt;/strong&gt; tab, click on the dropdown in the lower left corner and select the new "ComputerCraft Install" install, then click the &lt;strong&gt;Play&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/cc-screenshots/select-cc-install.png" /&gt;&lt;/p&gt;

&lt;p&gt;You may get a warning window telling you that modded Minecraft isn't officially supported. You can check the "I understand the risks. Don't warn me again about this installation." and then click Play.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/cc-screenshots/mod-warning.png" /&gt;&lt;/p&gt;

&lt;p&gt;Minecraft's loading screen will look different when you run the Forge installation:&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/cc-screenshots/minecraft-forge-load-screen.png" /&gt;&lt;/p&gt;

&lt;p&gt;When Minecraft has finished loading, note that it says in the lower left that there are "2 mods loaded". These are the original Minecraft application and Forge. Click the &lt;strong&gt;Mods&lt;/strong&gt; button. You should see two mods in the list: "Minecraft" and "Forge".&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/cc-screenshots/mods-screen-before-cc.png" /&gt;&lt;/p&gt;

&lt;p&gt;Click the "Open mods folder" button to open the mods folder and copy the cc-tweaked-1.16.4-1.94.0.jar there.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/cc-screenshots/mods-folder.png" /&gt;&lt;/p&gt;

&lt;p&gt;Exit out of Minecraft, then restart it. The start screen should now say "3 mods loaded" in the lower left.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/cc-screenshots/minecraft-start-screen-3-mods.png" /&gt;&lt;/p&gt;

&lt;p&gt;If you click the Mods button, you can see the "CC: Tweaked" mod is now loaded.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/cc-screenshots/mods-screen-after-cc.png" /&gt;&lt;/p&gt;

&lt;p&gt;You can now play Minecraft with the ComputerCraft mod. If you want to learn how to program Minecraft robots, you can read the full text of the Coding with Minecraft book at &lt;a href="https://turtleappstore.com/book/"&gt;https://turtleappstore.com/book/&lt;/a&gt;&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Two Weird Tricks to Help Learn the Git Command Line</title><link href="https://inventwithpython.com/blog/two-weird-tricks-to-learn-git-command-line.html" rel="alternate"></link><published>2020-09-18T12:00:00-04:00</published><updated>2020-09-18T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2020-09-18:/blog/two-weird-tricks-to-learn-git-command-line.html</id><summary type="html">Open two terminal windows and run &lt;code&gt;watch "git status"&lt;/code&gt; in one and &lt;code&gt;watch "git log -oneline"&lt;/code&gt; in the other so you can get live feedback as you enter Git commands.</summary><content type="html">

&lt;p&gt;TL;DR: Open two terminal windows and run &lt;code&gt;watch "git status"&lt;/code&gt; in one and &lt;code&gt;watch "git log -oneline"&lt;/code&gt; in the other so you can get live feedback as you enter Git commands.&lt;/p&gt;

&lt;p&gt;Version control is a necessary skill for all software developers, and GUI tools for a version control system like Git make Git easy to use. But you'll often only have access to a command line, so it's still important to learn Git's command line commands. Here's two tricks (invent by a programmer!) you can do to make learning and using the Git command line easier.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;git status&lt;/code&gt; command shows which files are unmodified, modified, or untracked while the &lt;code&gt;git log --oneline&lt;/code&gt; command shows the first line of every commit message. This is useful information to have, and you'll constantly run these commands as you use Git.&lt;/p&gt;

&lt;p&gt;To make this easier, you can have the &lt;code&gt;watch&lt;/code&gt; command run these automatically. The &lt;code&gt;watch&lt;/code&gt; command repeatedly runs a command you give it, refreshing the screen with the command's output. Open a new terminal window, and run &lt;code&gt;watch "git status"&lt;/code&gt; to have an automatically updated view of the Git repo's status. In another terminal window, run &lt;code&gt;watch "git log --oneline"&lt;/code&gt; to see a log that is automatically updated every time you commit changes.&lt;/p&gt;

&lt;p&gt;(These tricks aren't weird, but I wanted to follow the clickbait headline template in an attempt at humor that will surely date this article.)&lt;/p&gt;

&lt;p&gt;&lt;img src="https://inventwithpython.com/blogstatic/watch-git.png" /&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;watch&lt;/code&gt; command comes standard with macOS and Linux. However, &lt;a href="https://github.com/asweigart/watchpython"&gt;you'll have to download &lt;code&gt;watch.exe&lt;/code&gt; separately on Windows&lt;/a&gt; and place it in a folder in the system PATH. (If you don't know what that is, you can always place it in C:\Windows if you have an admin account.)&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Free Coupon Codes for My Online Courses</title><link href="https://inventwithpython.com/blog/free-coupon-codes-for-my-course.html" rel="alternate"></link><published>2020-03-26T12:00:00-04:00</published><updated>2020-03-26T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2020-03-26:/blog/free-coupon-codes-for-my-course.html</id><summary type="html">&lt;p&gt;Information about how to sign up for my Python course on Udemy for free or at a discounted rate.&lt;/p&gt;&lt;p&gt;&lt;img src="https://inventwithpython.com/images/automate_udemy_course_logo.jpg" /&gt;&lt;/p&gt;</summary><content type="html">

&lt;p&gt;&lt;img src="https://inventwithpython.com/images/automate_udemy_course_logo.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;Hello! I've probably sent you this link after you've asked about getting a discount or free code to my online Udemy course, "Automate the Boring Stuff with Python". Unfortunately, in 2019 Udemy changed their promo code policy so that I'm severely limited in how often I can give codes out. In response, I've made the following link:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://inventwithpython.com/automateudemy"&gt;https://inventwithpython.com/automateudemy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This URL will always redirect you to the lowest price possible. For the first six days of each month, it redirects to the free codes that I make for the course. The rest of the month it redirects to a 70% discount code that lowers the price from $50 to about $14. This is the maximum discount I'm allowed to provide.&lt;/p&gt;

&lt;p&gt;You can also view &lt;a href="https://www.youtube.com/watch?v=1F_OgqRuSdI&amp;amp;list=PL0-84-yl1fUnRuXGFe_F7qSH1LEnn9LkW"&gt;the first 15 of the 50 videos from the course on my YouTube channel with this link.&lt;/a&gt; If you like the course, please use the above link to purchase it.&lt;/p&gt;

&lt;p&gt;Please do use the &lt;a href="https://inventwithpython.com/automateudemy"&gt;https://inventwithpython.com/automateudemy&lt;/a&gt;; I get a much higher percentage of the course fee when people use my link as opposed to finding the course on their own through the Udemy site.&lt;/p&gt;

&lt;p&gt;If this link doesn't work for some reason, you can try to manually enter the code. Each month's $14 code is the month and year. For example, in 2020 the codes are &lt;code&gt;JAN2020&lt;/code&gt;, &lt;code&gt;FEB2020&lt;/code&gt;, &lt;code&gt;MAR2020&lt;/code&gt;, &lt;code&gt;APR2020&lt;/code&gt;, &lt;code&gt;MAY2020&lt;/code&gt;, &lt;code&gt;JUN2020&lt;/code&gt;, &lt;code&gt;JUL2020&lt;/code&gt;, &lt;code&gt;AUG2020&lt;/code&gt;, &lt;code&gt;SEP2020&lt;/code&gt;, &lt;code&gt;OCT2020&lt;/code&gt;, &lt;code&gt;NOV2020&lt;/code&gt;, &lt;code&gt;DEC2020&lt;/code&gt;. The free code (which only works from the 1st to the 3rd of each month) has the suffix "FREE": &lt;code&gt;JAN2020FREE&lt;/code&gt;, &lt;code&gt;FEB2020FREE&lt;/code&gt;, and so on. These free codes expire after 3 days, so from the 4th to the 6th the code is &lt;code&gt;JAN2020FREE2&lt;/code&gt;, &lt;code&gt;FEB2020FREE2&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;p&gt;Thanks for your interest in my course. However, I'm unable to make additional free codes aside from the ones outlined in this post. Please don't contact me repeatedly asking for free codes; you can watch &lt;a href="https://www.youtube.com/watch?v=1F_OgqRuSdI&amp;amp;list=PL0-84-yl1fUnRuXGFe_F7qSH1LEnn9LkW"&gt;the first 15 videos for free on YouTube&lt;/a&gt; and wait for the next month's free code period.&lt;/p&gt;

&lt;p&gt;You can always read the book that the course is based on, as well as all of my programming books, for free on &lt;a href="https://inventwithpython.com"&gt;https://inventwithpython.com&lt;/a&gt;.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Reading Guide to Automate the Boring Stuff, Second Edition, for Readers of the First Edition</title><link href="https://inventwithpython.com/blog/automate-second-edition-reading-guide.html" rel="alternate"></link><published>2019-12-31T12:00:00-05:00</published><updated>2019-12-31T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2019-12-31:/blog/automate-second-edition-reading-guide.html</id><summary type="html">&lt;a href="https://automatetheboringstuff.com/2e/"&gt;The second edition of Automate the Boring Stuff with Python&lt;/a&gt; is now freely available. This blog post outlines the new content in the second edition with links to the content online so that readers of the first edition can immediately find the new content.</summary><content type="html">

&lt;p&gt;&lt;a href="https://automatetheboringstuff.com/2e/"&gt;The second edition of Automate the Boring Stuff with Python is now available online&lt;/a&gt; under a &lt;a href="https://creativecommons.org/licenses/by-nc-sa/3.0/"&gt;Creative Commons license&lt;/a&gt;. This book teaches programming to complete beginners with no previous experience. The &lt;a href="https://automatetheboringstuff.com/chapter0/"&gt;first edition&lt;/a&gt; was published in 2015. This blog post outlines the new content in the second edition with links to the content online so that readers of the first edition can immediately find the new content.&lt;/p&gt;

&lt;p&gt;You can also buy a print book from the publisher, &lt;a href="https://nostarch.com/automatestuff2"&gt;No Starch Press, and receive PDF/Kindle/epub ebooks for free with purchase&lt;/a&gt; (something you won't get from Amazon).&lt;/p&gt;

&lt;p&gt;There are many small edit throughout the book, but here are the new sections:&lt;/p&gt;

&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter0/#calibre_link-76"&gt;You Are Not Too Old to Learn Programming&lt;/a&gt; - A short section to remind you that you're not too old to learn to program, just like you don't need to be good at math to program.&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter0/#calibre_link-80"&gt;Downloading and Installing Mu&lt;/a&gt; - You can use IDLE (which comes with Python) to type your code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Chapter 1 - Python Basics&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;No new sections.&lt;/li&gt;&lt;/ul&gt;

&lt;h2&gt;Chapter 2 - Flow Control&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter2/#calibre_link-128"&gt;A Short Program: Guess the Number&lt;/a&gt; - Not new, but refined and moved from Chapter 3.&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter2/#calibre_link-129"&gt;A Short Program: Rock, Paper, Scissors&lt;/a&gt; - An example rock, paper, scissors game.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Chapter 3 - Functions&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter3/#calibre_link-138"&gt;The Call Stack&lt;/a&gt; - Understanding the call stack helps you develop a mental model for function calls and scopes.&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter3/#calibre_link-146"&gt;A Short Program: Zigzag&lt;/a&gt; - A fun animation program.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Chapter 4 - Lists&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter4/#calibre_link-180"&gt;Identity and the id() Function&lt;/a&gt; - The &lt;code&gt;id()&lt;/code&gt; function can help develop your mental model of objects and references in Python.&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter4/#calibre_link-183"&gt;A Short Program: Conway's Game of Life&lt;/a&gt; - The classic simple cellular automata program.&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter4/#calibre_link-188"&gt;Coin Flip Streaks&lt;/a&gt; - A practice project to collect coin flip statistics.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Chapter 5 - Dictionaries and Structuring Data&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter5/#calibre_link-204"&gt;Chess Dictionary Validator&lt;/a&gt; - A practice project to write code to ensure a dictionary for a chess board is well-formed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Chapter 6 - Manipulating Strings&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter6/#calibre_link-223"&gt;Project: Multi-Clipboard Automatic Messages&lt;/a&gt; - The "password locker" project has been changed to a similar "multi-clipboard" project, since I don't want to encourage readers to keep their passwords in plain text on their hard drives.&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter6/#calibre_link-231"&gt;A Short Program: Pig Latin&lt;/a&gt; - A fun program to turn English into Pig Latin.&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter6/#calibre_link-236"&gt;Zombie Dice Bots&lt;/a&gt; - A practice project to write bots to play a game called, "Zombie Dice"&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;Chapter 7 - Pattern Matching with Regular Expressions&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter7/#calibre_link-274"&gt;Date Detection&lt;/a&gt; - A practice project to use regex to make sure date strings are properly formatted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Chapter 8 - Input Validation&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter8/"&gt;This entire chapter is new&lt;/a&gt;, and covers the &lt;a href="https://pypi.org/project/PyInputPlus/"&gt;PyInputPlus&lt;/a&gt; module.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Chapter 9 - Reading and Writing Files&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter9/#calibre_link-290"&gt;Files and File Paths&lt;/a&gt; - This entire chapter has been rewritten to use the new &lt;code&gt;pathlib&lt;/code&gt; module and &lt;code&gt;Path&lt;/code&gt; objects for file paths.&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter9/#calibre_link-292"&gt;Using the / Operator to Join Paths&lt;/a&gt; - The &lt;code&gt;/&lt;/code&gt; operator can join &lt;code&gt;Path&lt;/code&gt; objects.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Chapter 10 - Organizing Files&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;No new sections.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Chapter 11 - Debugging&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter11/#calibre_link-360"&gt;Mu's Debugger&lt;/a&gt; - Since the second edition uses the Mu editor instead of IDLE, the debugging chapter covers Mu's debugger.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Chapter 12 - Web Scraping&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter12/#calibre_link-392"&gt;Project: Opening All Search Results&lt;/a&gt; - A project to search the Python Package Index and automatically open each search result link.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Chapter 13 - Working with Excel Spreadsheets&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;No new sections.&lt;/li&gt;&lt;/ul&gt;


&lt;h2&gt;Chapter 14 - Working with Google Sheets&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter14/"&gt;This entire chapter is new&lt;/a&gt;, and covers the &lt;a href="https://pypi.org/project/EZSheets/"&gt;EZSheets&lt;/a&gt; module.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Chapter 15 - Working with PDF and Word Documents&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;No new sections.&lt;/li&gt;&lt;/ul&gt;


&lt;h2&gt;Chapter 16 - Working with CSV Files and JSON Data&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;No new sections.&lt;/li&gt;&lt;/ul&gt;


&lt;h2&gt;Chapter 17 - Keeping Time, Scheduling Tasks, and Launching Programs&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;No new sections.&lt;/li&gt;&lt;/ul&gt;

&lt;h2&gt;Chapter 18 - Sending Email and Text Messages&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter18/#calibre_link-568"&gt;Sending and Receiving Email with the Gmail API&lt;/a&gt; - Using the &lt;a href="https://pypi.org/project/EZGmail/"&gt;EZGmail&lt;/a&gt; module to access Gmail accounts.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Chapter 19 - Manipulating Images&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;No new sections.&lt;/li&gt;&lt;/ul&gt;


&lt;h2&gt;Chapter 20 - Controlling the Keyboard and Mouse with GUI Automation&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter20/#calibre_link-636"&gt;Setting Up Accessibility Apps on macOS&lt;/a&gt; - Setting up PyAutoGUI to work on macOS.&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter20/#calibre_link-647"&gt;Planning Your Mouse Movements&lt;/a&gt; - Covers the MouseInfo features of PyAutoGUI to log mouse coordinates.&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter20/#calibre_link-652"&gt;Getting Window Information&lt;/a&gt; - Covers the PyGetWindow features of PyAutoGUI to obtain window information.&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter20/#calibre_link-669"&gt;Displaying Message Boxes&lt;/a&gt; - Covers the PyMsgBox features of PyAutoGUI to display alert boxes.&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://automatetheboringstuff.com/2e/chapter20/#calibre_link-674"&gt;Using the Clipboard to Read a Text Field&lt;/a&gt; - A practice project to read text from other applications using PyAutoGUI and Pyperclip.&lt;/li&gt;
&lt;/ul&gt;


</content><category term="misc"></category></entry><entry><title>Type Hints for Busy Python Programmers</title><link href="https://inventwithpython.com/blog/type-hints-for-busy-python-programmers.html" rel="alternate"></link><published>2019-11-24T12:00:00-05:00</published><updated>2019-11-24T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2019-11-24:/blog/type-hints-for-busy-python-programmers.html</id><summary type="html">&lt;p&gt;A quick guide to using type hints in Python.&lt;/p&gt;</summary><content type="html">


&lt;p&gt;&lt;strong&gt;"Okay, I know that type annotations or type hints or whatever-"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, "type annotation" and "type hint" mean the same thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Right. I know what they are: they let you specify data types for your variables and function parameters &amp; return values, which lets you catch certain kinds of bugs early and save you from lots of debugging and headache. I'm in. How do I use them?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There's two parts. The first part is you add type hints to your source code. This involves the &lt;code&gt;typing&lt;/code&gt; module that's a part of the Python Standard Library as of verison 3.5. The second part is you install the &lt;code&gt;mypy&lt;/code&gt; module to do the actual checking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Wait, why are there two modules? That sounds complicated."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first module, &lt;code&gt;typing&lt;/code&gt;, lets you specify different data types. But the Python interpreter doesn't care if they're correct. The variable's actual value doesn't have to match the variable's type; the code will still run just fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Doesn't that defeat the purpose?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nah, remember that in Python type hints are optional. If you're writing a quick script and don't care, you don't have to use type hints. The Python interpreter itself completely ignores type hints. &lt;em&gt;No type checking happens at runtime.&lt;/em&gt; So, technically, type hints aren't "optional static typing" because "static typing" implies that variables always store values of a certain type. Python is still a dynamically typed language even with type hints. Type hints are just &lt;em&gt;hints&lt;/em&gt; that third-party tools like Mypy can use to point out potential bugs.&lt;/p&gt;

&lt;p&gt;It's the second module, &lt;code&gt;mypy&lt;/code&gt;, that is the tool that actually checks that your source code is valid and all the types match. Mypy is made by a third-party, which is why we use two separate modules. You don't have to use Mypy. You could instead use any third-party module that does the checking that Mypy does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"So what could I use instead of Mypy?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Microsoft/pyright"&gt;Pyright&lt;/a&gt;, from Microsoft. &lt;a href="https://github.com/facebook/pyre-check"&gt;Pyre&lt;/a&gt;, from Facebook. &lt;a href="https://github.com/google/pytype"&gt;Pytype&lt;/a&gt;, from Google. They're all free and should work the same, but most people seem to use Mypy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"So &lt;code&gt;typing&lt;/code&gt; comes with Python 3.5 and later. How do I install Mypy?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;python -m pip install mypy&lt;/code&gt;. (Use &lt;code&gt;python3&lt;/code&gt; on macOS and Linux.) Add &lt;code&gt;--user&lt;/code&gt; if you get error messages about permissions: &lt;code&gt;python -m pip install --user mypy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If the Python interpreter doesn't care about type hints, then how do I check if my source code matches the declared types?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mypy installs a command line tool in Python's Scripts folder when you install Mypy. So you run &lt;code&gt;mypy yourProgram.py&lt;/code&gt; from the command line (not from the Python interactive shell) and if your code is valid, it prints nothing. If it's not, it will display errors. I generally use the &lt;code&gt;python -m mypy yourProgram.py&lt;/code&gt; style of running it.&lt;/p&gt;

&lt;p&gt;But even easier is to get a plugin for whatever editor or IDE you use. This way, your editor runs Mypy in the background as you type code and can near-instantly show you any mistakes you make. For example, for Sublime Text, you just install the &lt;a href="http://www.sublimelinter.com/en/stable/installation.html"&gt;SublimeLinter&lt;/a&gt; and &lt;a href="https://github.com/fredcallaway/SublimeLinter-contrib-mypy"&gt;SublimeLinter-contrib-mypy&lt;/a&gt; using &lt;a href="https://packagecontrol.io/installation"&gt;Package Control&lt;/a&gt;. Then, as you type Python code in Sublime Text, it displays any errors that Mypy finds automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"When you say type hints in Python are optional, do you mean half my variables could use type hints and the other half doesn't have to?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Python calls this &lt;em&gt;gradual typing&lt;/em&gt;. If you want to add type hints to your Python code, you don't have do it all at once. It's like unit test coverage: you could just test some of your functions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Okay, I'll install Mypy and set up my editor to use it. Done. So what are all the types I use when writing type hints in my code?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html"&gt;The Mypy site has a great cheat sheet.&lt;/a&gt; Basically, you use the same names as those type conversion functions, like &lt;code&gt;int()&lt;/code&gt;, &lt;code&gt;str()&lt;/code&gt;, and so on. That's because those are technically the class names for integers and strings. Just like you call &lt;code&gt;datetime.date(2025, 10, 31)&lt;/code&gt; to create an object of the &lt;code&gt;date&lt;/code&gt; class, &lt;code&gt;int("42")&lt;/code&gt; creates an integer object of the &lt;code&gt;int&lt;/code&gt; class. In Python, "class" and "type" and "data type" are the exact same thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Okay. So how do I write type hints in my code?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After the variable name, add a colon and the type name. Here's an example, with comments explaining each one:&lt;/p&gt;

&lt;pre&gt;numberOfTacos: int = 42  # This variable's type int declares it to be an int variable.
tacosEaten: int  # This declares tacosEaten to be an int variable, but doesn't set an initial value. It's not set to None: using it will result in NameError.

# Type hints without an initial value are handy when there's no one place where an initial value is set, like here:
if numberOfTacos &lt; 10:
    tacosEaten = numberOfTacos
else:
    tacosEaten = 10

piesEaten: float = 3.14
myCatsName: str = 'Zophie'
isBestCat: bool = True
someBytes: bytes = b'HELLO'
randomValues: list = ['Zophie', 99, 3.1415]  # Note that the list can contain values of any type.
goodCats: tuple = ('Zophie', 'Pooka', 'Simon', 'Fat-tail', 'Footfoot')
fruitCount: dict = {'apples': 5, 'tomatoes': 7}
barbers: set = set(['Alice', 'Bob', 'Carol'])
shavers: frozenset = frozenset(['Alice', 'Bob', 'Carol'])&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;"Cool. So what happens when I write code that sets a variable to the wrong type of value?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Python, nothing. When you run the Python interpreter to run your program, it doesn't care about type hints. It just sets the variable to the value. But, before that point, when you're writing the source code in an editor that runs Mypy in the background, Mypy will report to the editor that there's a problem and the editor will show it to you as you're typing.&lt;/p&gt;

&lt;pre&gt;numberOfTacos: int = 42  # The numberOfTacos variable should only have ints.
numberOfTacos = 'bleventeen'  # Your editor will display a warning on this line. But Pyhton will still run this code just fine.&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;"What about the values &lt;em&gt;inside of&lt;/em&gt; the list, tuple, dictionary, and other container types? Can I specify type hints for those?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, and you do this adding square brackets with the item types inside. But &lt;code&gt;cats: list[str] = ['Zophie', 'Pooka']&lt;/code&gt; isn't valid Python code. (The reasons why this style couldn't work is too long to go into here.) The solution that the Python core devs came up with is to have analogous classes in the &lt;code&gt;typing&lt;/code&gt; module. Here's an example:&lt;/p&gt;

&lt;pre&gt;from typing import List, Tuple, Dict, Set, Frozenset

groceryList: List[str] = ['bread', 'eggs', 'tofu']
cityHall: Tuple[float, float, str] = (37.779, -122.419, 'San Francisco')
fruitCount: Dict[str, int] = {'apples': 5, 'tomatoes': 7}
barbers: Set[str] = set(['Alice', 'Bob', 'Carol'])
shavers: Frozenset[str] = frozenset(['Alice', 'Bob', 'Carol'])&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;"What if I want to have a list that can contain multiple types of values for its items?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use the &lt;code&gt;Union&lt;/code&gt; type from the &lt;code&gt;typing&lt;/code&gt; module. For example:&lt;/p&gt;

&lt;pre&gt;from typing import Union
favoriteLettersAndNumbers: List[Union[int, str]] = [42, 'x', 86]&lt;/pre&gt;

&lt;p&gt;You can also use &lt;code&gt;Union&lt;/code&gt; to specify variables that can have more than one type:&lt;/p&gt;

&lt;pre&gt;from typing import Union
serialNumber: Union[int, str] = 42
serialNumber = '42b'&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;"Okay. So if I have a variable that contains integers, but could also contain &lt;code&gt;None&lt;/code&gt;, should I use, uh, &lt;code&gt;Union[int, NoneType]&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;Optional&lt;/code&gt; instead, which means the same thing. And technically, &lt;code&gt;NoneType&lt;/code&gt; isn't a defined type the way &lt;code&gt;int&lt;/code&gt; or &lt;code&gt;str&lt;/code&gt; are. So you can't say &lt;code&gt;type(None) == NoneType&lt;/code&gt; the way you can say &lt;code&gt;type(42) == int&lt;/code&gt;. Instead, we'd just use &lt;code&gt;None&lt;/code&gt; like in this example:&lt;/p&gt;

&lt;pre&gt;from typing import Union, Optional

# These two have the same type hint, but `Optional[int]` is easier to write and read:
eggs: Union[int, None] = 42
bacon: Optional[int] = 42&lt;/pre&gt;

&lt;p&gt;But here's the thing: you should avoid using "null" values like &lt;code&gt;None&lt;/code&gt; at all. Computer scientist and inventer of the null value Tony Hoare called it his &lt;a href="https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions"&gt;Billion Dollar Mistake&lt;/a&gt; because it's often the cause of bugs. I vaguely remember some study that said NullReferenceException was by itself the cause of something like 30% of all Java application crashes. &lt;a href="https://kotlinlang.org/docs/reference/null-safety.html"&gt;Kotlin, a language that is a sort of modernized Java, by default doesn't let you set variables to null.&lt;/a&gt; You can still use &lt;code&gt;None&lt;/code&gt; if you need to, but don't use it without forethought.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What if I don't care what type of a value a variable has. Should I just not use type hints?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No, remember that "explicit is better than implicit." You should explicitly use the &lt;code&gt;Any&lt;/code&gt; type for variables that can be of any type:&lt;/p&gt;

&lt;pre&gt;from typing import Any
spam: Any = 42
spam = 'hello'  # This is fine. `spam` can be set to any type.
&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Can I do more specific checks? Like, specify that a variable should be an int, but never a negative integer?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nope. Type hints are only for types, not for values. I'd recommend subclass the class or, since you can't subclass &lt;code&gt;int&lt;/code&gt;, you create your own non-negative integer class. Anyway, remember that type hints only work before runtime. They don't do runtime checking of values. You should use &lt;code&gt;assert&lt;/code&gt; or even just &lt;code&gt;if&lt;/code&gt; statements for that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What about classes I write or other types like &lt;code&gt;datetime.date&lt;/code&gt; or the match objects returned from &lt;code&gt;re.search('[aeiou]', 'hello')&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You just use the class name as the type. Remember, in Python the terms type, data type, and class all mean the same thing. Also note that &lt;code&gt;self&lt;/code&gt; isn't written with a type hint:&lt;/p&gt;

&lt;pre&gt;class Cat:
    def __init__(self, name: str, color: str, weightkg: float) -&gt; None:
        self.name = name
        self.color = color
        self.weightkg = weightkg

def getZophieClone() -&gt; Cat:
    return Cat('Zophie', 'gray', 4.9)&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;"What about weird types, like functions or sequences or mappings or iterables?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;typing&lt;/code&gt; module has &lt;code&gt;Callable&lt;/code&gt;, &lt;code&gt;Sequence&lt;/code&gt;, &lt;code&gt;Mapping&lt;/code&gt;, &lt;code&gt;Iterable&lt;/code&gt;, and others. You can find them in the &lt;a href="https://docs.python.org/3/library/typing.html"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Alright, cool. So how about functions? How do I write type hints for parameters and return values?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These are all done in the &lt;code&gt;def&lt;/code&gt; statement. The return value's type hint is specified after a &lt;code&gt;-&gt;&lt;/code&gt; arrow. For example:&lt;/p&gt;

&lt;pre&gt;from typing import Optional, List

def getVowelsFromWord(word: str, maxVowels: Optional[int] = None) -&gt; List[str]:
    vowels: List[str] = []
    for letter in word:
        if maxVowels is not None and len(vowels) &gt;= maxVowels:
            break  # Break early since we've reached the maxmimum.

        if letter.upper() in 'AEIOU':
            vowels.append(letter) # Record this vowel letter.
    return vowels  # Return all the vowels fown in word.&lt;/pre&gt;

&lt;p&gt;In our &lt;code&gt;getVowels()&lt;/code&gt; function, it has two parameters, &lt;code&gt;word&lt;/code&gt; (which can be a string) and &lt;code&gt;maxVowels&lt;/code&gt; (which can be an integer or &lt;code&gt;None&lt;/code&gt;). The function itself returns a list of strings. A tool like Mypy can verify that the &lt;code&gt;return&lt;/code&gt; statement returns the &lt;code&gt;vowels&lt;/code&gt; variable, which has the type hint &lt;code&gt;List[str]&lt;/code&gt;, which in turn matches the function's return type hint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What about type hints in Python 2? Won't those colons cause syntax errors?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, but you can put type hints inside comments if you're writing Python 2 (or pre-Python 3.5) compatible code:&lt;/p&gt;

&lt;pre&gt;from typing import List # Required even for comment-style type hints.

spam = 42 # type: int
def sayHello():
    # type: () -&gt; None
    """The docstring comes after the type hint comment."""
    print('Hello!')

def addTwoNumbers(listOfNumbers):
    # type: (List[float]) -&gt; None
    listOfNumbers[0] += listOfNumbers.pop()&lt;/pre&gt;

&lt;p&gt;Note that you'll still need to import objects from &lt;code&gt;typing&lt;/code&gt; even with the comment-style of type hints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"I've started using type hints, but I've come across a warning that doesn't really apply to my code. How do I get Mypy to ignore that one line?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can add a comment &lt;code&gt;# type: ignore&lt;/code&gt; to the end of that line:&lt;/p&gt;

&lt;pre&gt;from typing import Any

def raiseMyCustomException(val: Any):
    # Some code here...
    raise Exception('message here: ' + str(val))

def getLengthIfString(val: Any) -&gt; int:
    # Some code here...

    # This function call always raises an exception, but mypy thinks
    # gives the error "missing return statement", so we tell mypy to ignore it:
    raiseMyCustomException(val)  # type: ignore&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;"Cool! Now I know enough to get started. Where can I learn more about type hints?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://docs.python.org/3/library/typing.html"&gt;official Python documentation&lt;/a&gt; or the &lt;a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html"&gt;Mypy documentation, starting with the cheat sheet&lt;/a&gt;. There was also &lt;a href="https://www.youtube.com/watch?v=pMgmKJyWKn8"&gt;a well-received talk at PyCon 2019 on type hints by Carl Meyer&lt;/a&gt;.&lt;/p&gt;

</content><category term="misc"></category><category term="python"></category></entry><entry><title>Book Review: Code This Game! by Meg Ray (Odd Dot 2019)</title><link href="https://inventwithpython.com/blog/book-review-code-this-game.html" rel="alternate"></link><published>2019-11-12T12:01:00-05:00</published><updated>2019-11-12T12:01:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2019-11-12:/blog/book-review-code-this-game.html</id><summary type="html">&lt;p&gt;5 Stars: An excellent project-based book for young readers who want to move beyond Scratch&lt;/p&gt;&lt;p&gt;Code This Game is an excellent book that teaches Python programming by guiding the reader through making a tower-defense game from scratch. This is a book that doesn't shy away from programming detail, but manages to remain fun and engaging. If you have a student or child who wants to move beyond MIT's Scratch programming tool and do "real" programming, this is a great book.&lt;/p&gt;</summary><content type="html">


&lt;h2&gt;5 Stars: An excellent project-based book for young readers who want to move beyond Scratch.&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.amazon.com/gp/product/1250306698/ref=as_li_qf_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=1250306698&amp;amp;linkId=f277e476ba0d56c120f8ea71b897bab0"&gt;&lt;img src="/blogstatic/codethisgame_cover.jpg" /&gt;&lt;br /&gt;Amazon link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Code This Game is an excellent book that teaches Python programming by guiding the reader through making a tower-defense game from scratch. This is a book that doesn't shy away from programming detail, but manages to remain fun and engaging. If you have a student or child who wants to move beyond MIT's Scratch programming tool and do "real" programming, this is a great book.&lt;/p&gt;

&lt;p&gt;The book features a tower defense game called Attack of the Pizza Vampires, and anyone who's ever played the popular Plants vs. Zombie games will recognize its influence here. The graphics are provided from a free website download, but the reader copies the code from the book, accompanied by explanations of the relevant programming concepts.&lt;/p&gt;

&lt;p&gt;Don't let the 300+ page count intimidate you: The pages are short and colorful, and the chapters are well-paced and let you see the progress you've made as you go from absolutely nothing to a full game. The book covers Pygame, a free software library for Python that has been used in some commercial video games, but is still simple enough to be approachable by young adults. The book itself includes a small stand to prop the pages up, making it easy to read as the reader enters the Python code into their computer.&lt;/p&gt;

&lt;p&gt;From my experience teaching with after school coding clubs, I agree with the stated 10 to 14 age range. This book would be suitable for motivated 9 or maybe even 8 year olds and for older teenagers if they look past the colorful cartoony presentation. The code and teaching itself is solid. Your student or child might be able to read this book on their own, but I think a parent or teacher (even one without programming experience) could be a great help to follow along with. I also think this book could be useful for an after school coding club or intro to programming class: the game project is split up in such a way that it makes for a good resource to work through over multiple sessions.&lt;/p&gt;

&lt;p&gt;I like how the book doesn't mind going into technical topics, but at the same a hard understanding of these terms isn't necessary to create the Attack of the Pizza Vampires game and get a sense of accomplishment. The introductory chapter has lots of useful tips for setting up your computer (whether Windows, macOS , or another operating system) to get Python installed and running. These "environment setup" steps are often a large stumbling block in getting a single student or entire class started on programming games, so the presence of this information shows good foresight on behalf of the authors.&lt;/p&gt;

&lt;p&gt;Python is excellent programming language for beginners. If you're looking for a resource that teaches actual programming skills while still be approachable, I recommend Code This Game.&lt;/p&gt;

</content><category term="misc"></category><category term="bookreview"></category></entry><entry><title>Live Online Python Class on Twitch on October 21, 2019</title><link href="https://inventwithpython.com/blog/twitch-class-oct21-2019.html" rel="alternate"></link><published>2019-10-20T12:00:00-04:00</published><updated>2019-10-20T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2019-10-20:/blog/twitch-class-oct21-2019.html</id><summary type="html">I'll be doing a live online Python class for beginner- and intermediate-level programmers on Twitch on Monday evening, October 21, 2019.</summary><content type="html">

&lt;p&gt;Hello, I'm Al Sweigart. I'm the author of &lt;a href="https://nostarch.com/automatestuff2"&gt;Automate the Boring Stuff with Python&lt;/a&gt;, and I'll be doing a live online Python class for beginner- and intermediate-level programmers on Twitch on Monday evening, October 21, 2019. The URL is &lt;a href="https://www.twitch.tv/alsweigart/"&gt;https://www.twitch.tv/alsweigart/&lt;/a&gt; and the stream will begin at &lt;strong&gt;6pm Pacific&lt;/strong&gt;. If you want to join in the chat, you'll need to sign up for a (free) account on Twitch. However, you'll be able to watch the stream without an account. I'll post a link to the video if you're reading this after the stream.&lt;/p&gt;

&lt;p&gt;I'll also be releasing a free code for &lt;a href="https://www.udemy.com/automate/"&gt;my online course that follows the "Automate the Boring Stuff with Python"&lt;/a&gt; on the stream (and on this page after the stream). Unfortunately, due to changes in Udemy's policies, this code will only work for three days and will stop working after Wednesday, October 23. Udemy has limited how often I can make free codes, so I won't be able to create new ones for those who miss this one. &lt;a href="https://www.youtube.com/watch?v=1F_OgqRuSdI&amp;amp;list=PL0-84-yl1fUnRuXGFe_F7qSH1LEnn9LkW"&gt;You'll be still be able to watch the first 15 videos of the 50-video course at anytime on YouTube.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;EDIT: The coupon code is OCT2019FREE, but is only valid until Wednesday, October 23, 2019.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class Format:&lt;/strong&gt; The format for the class is that I'll be working through several beginner level coding projects. I'll take a look at these programs, and show you how I write them step by step. You can follow along, or simply re-type the source code yourself and try running the programs. The programs are from &lt;a href="https://github.com/asweigart/pythonstdiogames"&gt;a repo of small, simple Python games I've been developing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The programs we'll be covering, in order starting at 6pm, are:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;a href="https://github.com/asweigart/PythonStdioGames/blob/master/src/zigzag.py"&gt;Zig Zag animation&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://github.com/asweigart/PythonStdioGames/blob/master/src/rockpaperscissors.py"&gt;Rock, Paper, Scissors&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://github.com/asweigart/PythonStdioGames/blob/master/src/rockpaperscissorsalwayswin.py"&gt;Rock, Paper, Scissors but the Player Always Wins&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://github.com/asweigart/PythonStdioGames/blob/master/src/game2048.py"&gt;2048&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://github.com/asweigart/PythonStdioGames/blob/master/src/mancala.py"&gt;Mancala&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can read through the code to familiarize yourself with them beforehand, but it isn't required. I expect the process for each of these programs will be:&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;Explaining what the game is or what the program does.&lt;/li&gt;
    &lt;li&gt;A demonstration of running the completed program.&lt;/li&gt;
    &lt;li&gt;Starting with a blank file, and stubbing out the general parts of the program with comments.&lt;/li&gt;
    &lt;li&gt;Filling in the code while describing the data structures/algorithms used.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'll be copying the code from GitHub exactly, which is a bit artificial in the way that I normally write code, but I'm doing it this way so that the audience doesn't have to follow exactly along with code as I type. Instead, you can move slower or faster by copying the code from the repo. I recommend having a browser window open with the code and copying it by typing it yourself (as opposed to copy/pasting it).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; You'll be expected to have Python 3 installed on your computer along with an IDE or text editor for writing Python programs, such as IDLE or PyCharm or Visual Studio Code. This class is for beginners, but not complete beginners: I'll expect that you've written a "Hello world" program and gotten it to run on your computer. However, even if you're a complete beginner, feel free to watch along to get an idea for how programs are written by programmers.&lt;/p&gt;

&lt;p&gt;I offer a blog post I wrote, &lt;a href="https://inventwithpython.com/blog/2018/02/02/how-to-ask-for-programming-help/"&gt;"How to Ask for Programming Help"&lt;/a&gt; to read before the live stream. There are several techniques that make it much easier for folks to answer your questions (copy/pasting your code to a pastebin, stating the exact error message, describing what you've already tried, describing your OS and Python versions, etc.) that can make it much easier to answer your questions.&lt;/p&gt;

&lt;p&gt;Questions are welcome, but unfortunately &lt;em&gt;I cannot help you with environment setup on your computer&lt;/em&gt;. This tends to hold up the class for hours, and it'd mean I couldn't progress with the class. Please make sure you are able to run a &lt;code&gt;print('Hello, world!')&lt;/code&gt; program on your computer. The projects are designed to not require any third-party modules or additional libraries that need to be installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Streaming Schedule:&lt;/strong&gt; I hope to make this a regular occurring stream, and I'll be taking notes on the format and what works and what doesn't. I hope to make this a weekly or twice-weekly event. This is a casual streaming event on the internet, but you should treat it as a professional event with mixed company. The general rules for chat are stated as, "No harassment, offensive/edgy/"ironic" jokes, or complaining about moderators. Keep it classy and on topic. Young adults should be able to watch and participate without their parents cringing."&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>What's New in the 2nd Edition of "Automate the Boring Stuff with Python"</title><link href="https://inventwithpython.com/blog/whats-new-in-the-2nd-edition-of-automate-the-boring-stuff-with-python.html" rel="alternate"></link><published>2019-10-07T12:00:00-04:00</published><updated>2019-10-07T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2019-10-07:/blog/whats-new-in-the-2nd-edition-of-automate-the-boring-stuff-with-python.html</id><summary type="html">&lt;p&gt;Here are all the new additions and updates in the second edition.</summary><content type="html">

&lt;p&gt;The second edition of "Automate the Boring Stuff with Python" is almost out! &lt;a href="https://nostarch.com/automatestuff2"&gt;You can preorder it on the No Starch Press website&lt;/a&gt;, where buying the print book will get you DRM-free ebooks in PDF, epub, and Kindle formats for free.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nostarch.com/automatestuff2"&gt;&lt;img src="/blogstatic/automate_2e_cover.png" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second edition is about 100 pages longer than the first, with new chapters, programming projects, and revised editing. The third party module coverage has been updated to the latest available version (including OpenPyXL).&lt;/p&gt;

&lt;p&gt;Like the first edition, the second edition will be released under a &lt;a href="https://creativecommons.org/"&gt;Creative Commons license&lt;/a&gt; and will be free to read online. The estimated online release is early November on the &lt;a href="https://automatetheboringstuff.com"&gt;https://automatetheboringstuff.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some of the new content includes:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;The Mu editor&lt;/strong&gt; - The second edition switches from IDLE to &lt;a href="https://codewith.mu/"&gt;Mu&lt;/a&gt;, an editor specifically designed for education and beginners. &lt;a href="https://www.youtube.com/watch?v=T5IAf5vGGSk"&gt;Watch Nicholas Tollervey present Mu at PyCon 2018.&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Explanation of the call stack&lt;/strong&gt; - A description of the call stack, which helps the reader understand global and local scopes.&lt;/li&gt;

    &lt;li&gt;&lt;strong&gt;The &lt;code&gt;enumerate()&lt;/code&gt; function&lt;/strong&gt; - Using &lt;code&gt;enumarate()&lt;/code&gt; in &lt;code&gt;for&lt;/code&gt; loops and its advantages over using &lt;code&gt;range()&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The &lt;code&gt;random.choice()&lt;/code&gt; and &lt;code&gt;random.shuffle()&lt;/code&gt; functions&lt;/strong&gt; - These functions in the &lt;code&gt;random&lt;/code&gt; module weren't covered before.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Identity and the &lt;code&gt;id()&lt;/code&gt; function&lt;/strong&gt; - Added to help explain references and in-place modification.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The &lt;code&gt;partition()&lt;/code&gt; method&lt;/strong&gt; - A useful string method similar to &lt;code&gt;join()&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;New chapter: PyInputPlus and input validation&lt;/strong&gt; - Covers a third party module, PyInputPlus, for input validation and why input validation is important.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The &lt;code&gt;pathlib&lt;/code&gt; module and &lt;code&gt;Path&lt;/code&gt; objects&lt;/strong&gt; - Explores a new, versatile way to write code that interacts with the file system.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;OpenPyXL updates&lt;/strong&gt; - This third party module made several backwards-incompatible changes, confusing some readers who installed the latest version instead of the version in the book. This chapter has been updated to the latest version.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;New chapter: Working with Google Sheets&lt;/strong&gt; - A new chapter covering the EZSheets module, allowing your Python scripts to read and update spreadsheets on Google Sheets.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;New module: EZGmail&lt;/strong&gt; - The email/SMS chapter now features the EZGmail module, which allows Python scripts to interact with Gmail accounts. Previously this was much more difficult to do via the &lt;code&gt;smtplib&lt;/code&gt; and &lt;code&gt;imapclient&lt;/code&gt; modules.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;SMS email gateways&lt;/strong&gt; - A new section on how to send SMS text messages via the phone carrier's email gateways. Now your Python scripts won't need to use a service such as Twilio to send occasional text messages.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;MouseInfo app&lt;/strong&gt; - A small GUI application that is a part of PyAutoGUI which reveals the current coordinates of the mouse. This information is useful for planning out GUI automation tasks.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The PyGetWindow module&lt;/strong&gt; - A new module included in PyAutoGUI that retrieves the coordinates, size, and status of the desktop windows for running programs.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;The PyMsgBox module&lt;/strong&gt; - A new module included in PyAutoGUI that provides popup message boxes for your program when it needs to get the attention of the user.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Requirements.txt files&lt;/strong&gt; - Available text files that make it easy to install the exact versions of the modules that the book covers. (See below.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;New projects and programs include:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;&lt;a href="https://github.com/asweigart/PythonStdioGames/blob/master/src/rockpaperscissors.py"&gt;"Rock, Paper, Scissors"&lt;/a&gt; game&lt;/strong&gt; - The source code for a simple rock, paper, scissors game.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;&lt;a href="https://github.com/asweigart/PythonStdioGames/blob/master/src/zigzag.py"&gt;"Zig Zag" program&lt;/a&gt;&lt;/strong&gt; - A small animation program to produce a zig zag line.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;&lt;a href="https://github.com/asweigart/PythonStdioGames/blob/master/src/conway.py"&gt;"Conway's Game of Life" program&lt;/a&gt;&lt;/strong&gt; - The source code to &lt;a href="https://www.youtube.com/watch?v=R9Plq-D1gEk"&gt;the classic cellular automata program.&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;&lt;a href="https://github.com/asweigart/PythonStdioGames/blob/master/src/piglatin.py"&gt;"Pig Latin" program&lt;/a&gt;&lt;/strong&gt; - The source code for an "igpay atinlay" text converter.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;&lt;a href="https://github.com/asweigart/zombiedice"&gt;"Zombie Dice" simulation&lt;/a&gt;&lt;/strong&gt; - Instructions how to write your own bots that play the game &lt;a href="http://www.sjgames.com/dice/zombiedice/"&gt;Zombie Dice&lt;/a&gt; against each other.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Date Detection practice project&lt;/strong&gt; - An exercise for using regular expressions to parse dates.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;&lt;a href="https://github.com/asweigart/PythonStdioGames/blob/master/src/idiot.py"&gt;"How to Keep an Idiot Busy for Hours" program&lt;/a&gt;&lt;/strong&gt; - The source code for a joke program.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;"Multiplication Quiz" program&lt;/strong&gt; - The source code for a math quiz program.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Practice projects: "Sandwich maker" and "Write your own multiplication quiz"&lt;/strong&gt; - Exercises for using the PyInputPlus module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To avoid installing third-party module versions that the book doesn't cover, I'm making available several &lt;em&gt;requirements.txt&lt;/em&gt; files so that &lt;code&gt;pip&lt;/code&gt; can easily install the exact versions that the book covers. Download the file for your operating system:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;code&gt;&lt;a href="https://automatetheboringstuff.com/files/automate-win-requirements.txt"&gt;automate-win-requirements.txt&lt;/a&gt;&lt;/code&gt; for Windows&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;&lt;a href="https://automatetheboringstuff.com/files/automate-mac-requirements.txt"&gt;automate-mac-requirements.txt&lt;/a&gt;&lt;/code&gt; for macOS&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;&lt;a href="https://automatetheboringstuff.com/files/automate-linux-requirements.txt"&gt;automate-linux-requirements.txt&lt;/a&gt;&lt;/code&gt; for Linux&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To install, download the file for your operating system. Then open a terminal or command prompt window and run &lt;code&gt;pip install -r automate-&amp;lt;your os&amp;gt;-requirements.txt&lt;/code&gt;. If you're using pipenv to create a virtual environment, you'll need to run &lt;code&gt;pipenv install -r automate-&amp;lt;your os&amp;gt;-requirements.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note that PyAutoGUI requires Pillow, which currently doesn't support Python 3.8 yet.&lt;/p&gt;

&lt;p&gt;If you have questions about the second edition or its content, you can post them on the &lt;a href="https://www.reddit.com/r/inventwithpython/"&gt;/r/inventwithpython&lt;/a&gt; subreddit.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Python Error Messages: Gotta Catch Em All</title><link href="https://inventwithpython.com/blog/gotta-catch-em-all.html" rel="alternate"></link><published>2019-08-15T12:00:00-04:00</published><updated>2019-08-15T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2019-08-15:/blog/gotta-catch-em-all.html</id><summary type="html">To make errors less intimidating, I've created a "Gotta Catch 'em All" worksheet that lists several common Python error messages. Instead of trying to avoid errors, beginners can try to hunt and collect them all.</summary><content type="html">

&lt;p&gt;Error messages can be intimidating for beginners to programming. They're often shown in red text, and may give the sense that the computer has been damaged or broken. Experienced programmers take them in stride; errors are just a casual fact of life, and correcting them is just a part of the programming process.&lt;/p&gt;

&lt;p&gt;To make errors less intimidating, I've created a "Gotta Catch 'em All" worksheet that lists several common Python error messages. Instead of trying to avoid errors, beginners can try to hunt and collect them all. This makes errors less scary and gives an incentive for beginners to carefully read the error messages and figure out what causes them.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/gottacatchemallworksheet.png" /&gt;&lt;/p&gt;

&lt;p&gt;This worksheet can be useful for self-taught individuals or classroom and workshop instructors. As students encounter error messages, they can look them up on the worksheet to see if they've encountered them before. There's a space to write down the line of code that caused the error message, encouragement to do a web search for the error message, and a space to write down the solution they found.&lt;/p&gt;

&lt;p&gt;&lt;a href="/blogstatic/pythonerrorsgottacatchemall.pdf"&gt;PDF of the "Gotta Catch 'em All" worksheet (letter-sized)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="/blogstatic/pythonerrorsgottacatchemall.docx"&gt;Microsoft Word document of the worksheet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This worksheet could use some improvement.&lt;/strong&gt; It'd be nice if the list of errors was expanded, or even if each kind of error (or error message) had a small Pokemon-like mascot. Perhaps putting these errors and mascots on cards instead of a worksheet would be good. I'm open to anyone who wants to take this idea and expand upon it.&lt;/p&gt;

&lt;p&gt;If you have other ideas for how the worksheet could be improved or more error messages to add, email &lt;a href="mailto:al@inventwithpython.com"&gt;al@inventwithpython.com&lt;/a&gt; or contact me on Twitter at &lt;a href="https://twitter.com/AlSweigart"&gt;@AlSweigart&lt;/a&gt;.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Pythonic Ways to Use Dictionaries</title><link href="https://inventwithpython.com/blog/pythonic-ways-to-use-dictionaries.html" rel="alternate"></link><published>2019-06-05T12:00:00-04:00</published><updated>2019-06-05T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2019-06-05:/blog/pythonic-ways-to-use-dictionaries.html</id><summary type="html">Python dictionaries are a useful part of the language. In addition to having the ability to store keys and values, you can also use dictionary methods to manipulate those values, and you can use dictionaries to write more concise code.</summary><content type="html">


&lt;p&gt;Python dictionaries are a useful part of the language. In addition to having the ability to store keys and values, you can also use dictionary methods to manipulate those values, and you can use dictionaries to write more concise code.&lt;/p&gt;

&lt;h2&gt;Use &lt;code&gt;get()&lt;/code&gt; and &lt;code&gt;setdefault()&lt;/code&gt; with Dictionaries&lt;/h2&gt;

&lt;p&gt;Trying to access a dictionary key that doesn’t exist will result in a KeyError error, so programmers often will write unpythonic code to avoid the situation like this:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; # Unpythonic Example
&gt;&gt;&gt; workDetails = {}
&gt;&gt;&gt; if 'hours' in workDetails:
...     hoursWorked = workDetails['hours']
... else:
...     hoursWorked = 0 # Default to 0 if the 'hours' key doesn't exist.
...
&gt;&gt;&gt; hoursWorked
0&lt;/pre&gt;

&lt;p&gt;This pattern happens so often that dictionaries have a &lt;code&gt;get()&lt;/code&gt; method that allows you specify a default value to return when a key doesn’t exist in the dictionary. The following code is equivalent to the previous example, but is pythonic:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; # Pythonic Example
&gt;&gt;&gt; workDetails = {}
&gt;&gt;&gt; hoursWorked = workDetails.get('hours', 0)
&gt;&gt;&gt; hoursWorked
0&lt;/pre&gt;

&lt;p&gt;If you find yourself writing if statements that check whether a key exists and provide a default value when the key is absent, use the &lt;code&gt;get()&lt;/code&gt; method instead.&lt;/p&gt;

&lt;p&gt;Conversely, you may want to set a default value if a key doesn’t exist. For example, if the dictionary in &lt;code&gt;workDetails&lt;/code&gt; doesn’t have an &lt;code&gt;'hours'&lt;/code&gt; key, then the instruction &lt;code&gt;workDetails['hours'] += 10&lt;/code&gt; would result in a &lt;code&gt;KeyError&lt;/code&gt; error. You may want to add code that checks for the key’s absence and sets a default value:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; # Unpythonic Example
&gt;&gt;&gt; workDetails = {}
&gt;&gt;&gt; if 'hoursWorked' not in workDetails:
...     workDetails['hours'] = 0
...
&gt;&gt;&gt; workDetails['hours'] += 10
&gt;&gt;&gt; workDetails['hours']
10&lt;/pre&gt;

&lt;p&gt;The previous example is unpythonic. Because this pattern is also common, dictionaries have a more pythonic &lt;code&gt;setdefault()&lt;/code&gt; method. The following code is equivalent to the previous example:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; # Pythonic Example
&gt;&gt;&gt; workDetails = {}
&gt;&gt;&gt; workDetails.setdefault('hours', 0) # Does nothing if 'hours' exists.
0
&gt;&gt;&gt; workDetails['hours'] += 10
&gt;&gt;&gt; workDetails['hours']
10&lt;/pre&gt;

&lt;p&gt;If you find yourself writing &lt;code&gt;if&lt;/code&gt; statements that check if a key exists in a dictionary and sets a default value if the key is absent, use the &lt;code&gt;setdefault()&lt;/code&gt; method instead.&lt;/p&gt;

&lt;h2&gt;Python Uses Dictionaries Instead of a Switch Statement&lt;/h2&gt;

&lt;p&gt;Languages such as Java have a &lt;code&gt;switch&lt;/code&gt; statement, which is a kind of &lt;code&gt;if-elif-else&lt;/code&gt; statement that runs code based on the values of a single variable or expression. Python doesn’t have a &lt;code&gt;switch&lt;/code&gt; statement, but a &lt;code&gt;switch&lt;/code&gt; statement is similar to the following Python code, which sets the &lt;code&gt;holiday&lt;/code&gt; variable based on the value of the &lt;code&gt;month&lt;/code&gt; variable:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; # Unpythonic Example
if season == 'Winter':
    holiday = 'Christmas'
elif season == 'Spring':
    holiday = 'Easter'
elif season == 'Summer':
    holiday = 'American Independence Day'
elif season == 'Fall':
    holiday = 'Halloween'
else:
    holiday = 'Personal day off'&lt;/pre&gt;

&lt;p&gt;Handling code by using &lt;code&gt;if&lt;/code&gt; statements to mimic &lt;code&gt;switch&lt;/code&gt; statements is unpythonic. By default, Java &lt;code&gt;switch&lt;/code&gt; statements have “fall-through” that requires each block end with a &lt;code&gt;break&lt;/code&gt; statement. Otherwise, the execution continues on the next block. Forgetting to add this &lt;code&gt;break&lt;/code&gt; statement is a common source of bugs. However, all the &lt;code&gt;if-elif&lt;/code&gt; statements in our Python example are not much better since they can be overly verbose. Some Python programmers prefer to set up a dictionary value instead of using &lt;code&gt;if-elif&lt;/code&gt; statements. The following concise and pythonic code is equivalent to the previous example:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; # Pythonic Example
holiday = {'Winter': 'Christmas',
           'Spring': 'Easter',
           'Summer': 'American Independence Day',
           'Fall':   'Halloween'}.get(season, 'Personal day off')&lt;/pre&gt;

&lt;p&gt;This code is just a single assignment statement. The value stored in &lt;code&gt;holiday&lt;/code&gt; is the return value of the &lt;code&gt;get()&lt;/code&gt; method call, which returns the value for the key that season is set to. If the season key doesn’t exist, then &lt;code&gt;get()&lt;/code&gt; returns &lt;code&gt;'Personal day off'&lt;/code&gt;. Using a dictionary will result in more concise code, but it can also make your code harder to read. It’s up to you whether to use this convention or not.&lt;/p&gt;


</content><category term="misc"></category></entry><entry><title>Hashable Objects Must Be Immutable</title><link href="https://inventwithpython.com/blog/hashable-objects-must-be-immutable.html" rel="alternate"></link><published>2019-02-01T12:00:00-05:00</published><updated>2019-02-01T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2019-02-01:/blog/hashable-objects-must-be-immutable.html</id><summary type="html">&lt;p&gt;A recent post to Reddit sparked some comments, so I wanted to clarify: &lt;strong&gt;In Python, hashable objects must be immutable and mutable objects cannot be hashable.&lt;/strong&gt; &lt;small&gt;(With one exception.)&lt;/small&gt;&lt;/p&gt;</summary><content type="html">

&lt;p&gt;A recent post to Reddit sparked some comments, so I wanted to clarify: &lt;strong&gt;In Python, hashable objects must be immutable and mutable objects cannot be hashable.&lt;/strong&gt; &lt;small&gt;(With one exception.)&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Before the torches and pitchforks are gathered, let me explain some background.&lt;/p&gt;

&lt;!--
&lt;p&gt;Dictionaries work by creating (effectively) a list, where each position in the list is called a &lt;em&gt;bucket&lt;/em&gt;. When a key-value pair is stored in the dictionary, they key and value are stored at a particular index. This index is decided based on the hash of the key.&lt;/p&gt;

&lt;p&gt;You can find the hash of objects from Python's built-in types with the &lt;code&gt;hash()&lt;/code&gt; function. The hash of an object will be different each time Python runs, but the hash of the object won't change for the lifetime of the object:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; hash('hello')
-6243439111058758738
&gt;&gt;&gt; hash('Hello')
-3096249431554254785
&gt;&gt;&gt; hash(42)
42
&gt;&gt;&gt; hash(3.14)
322818021289917443&lt;/pre&gt;

&lt;p&gt;Note that two different objects might happen to return the same hash by coincidence. This is necessary evil, given that there are more possible values than hashes (which is 2^64, or 18,446,744,073,709,551,616) but is fine.&lt;/p&gt;
--&gt;

&lt;p&gt;If you want to make your classes hashable, you must follow two rules outlined in the &lt;a href="https://docs.python.org/3/glossary.html#term-hashable"&gt;Python Glossary for the entry for "hashable"&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;An object is hashable if [1] it has a hash value which never changes during its lifetime (it needs a &lt;code&gt;__hash__()&lt;/code&gt; method), and can be compared to other objects (it needs an &lt;code&gt;__eq__()&lt;/code&gt; method). [2] Hashable objects which compare equal must have the same hash value.&lt;/blockquote&gt;

&lt;p&gt;In Python, integers, floats, and bools are all immutable. And because &lt;code&gt;1 == 1.0 == True&lt;/code&gt;, then &lt;code&gt;hash(1) == hash(1.0) == hash(True)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's create an immutable Point class, which has read-only &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; attributes, and it reuses the hashes for tuples:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; class ImmutablePoint:
...   def __init__(self, x, y):
...     self._x = x
...     self._y = y
...   def __eq__(self, other):
...     if not isinstance(other, ImmutablePoint):
...       return False
...     return self._x == other._x and self._y == other._y
...   def __hash__(self):
...     return hash((self._x, self._y))
...   @property
...   def x(self):
...     return self._x
...   @property
...   def y(self):
...     return self._y&lt;/pre&gt;

&lt;p&gt;We can create &lt;code&gt;ImmutablePoint&lt;/code&gt; objects, and they are immutable: we can't change their &lt;code&gt;x&lt;/code&gt; or &lt;code&gt;y&lt;/code&gt; attributes:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; ip = ImmutablePoint(10, 20)

&gt;&gt;&gt; ip.x, ip.y
(10, 20)

&gt;&gt;&gt; ip.x = 'changed' # x and y are read-only
Traceback (most recent call last):
  File "stdin", line 1, in module
AttributeError: can't set attribute

&gt;&gt;&gt; ip2 = ImmutablePoint(10, 20)

&gt;&gt;&gt; ip == ip2
True&lt;/pre&gt;

&lt;p&gt;Since they are hashable, we can also use these objects as keys in a dictionary:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; d = {ip: 'hello'}

&gt;&gt;&gt; d[ip]
'hello'

&gt;&gt;&gt; d[ip2]
'hello'&lt;/pre&gt;

&lt;p&gt;Now, since hashes are based on an object's value and an object's hash never changes (according to the rule from the Python Glossary), this necessarily means that &lt;strong&gt;only immutable objects can be hashable&lt;/strong&gt;. Notice that you can't use a mutable list or dictionary as a key in a dictionary, nor do they return anything from &lt;code&gt;hash()&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; {[1, 2, 3]: 'hello'}
Traceback (most recent call last):
  File "stdin", line 1, in module
TypeError: unhashable type: 'list'

&gt;&gt;&gt; hash([1, 2, 3])
Traceback (most recent call last):
  File "stdin", line 1, in module
TypeError: unhashable type: 'list'

&gt;&gt;&gt; hash({1:2, 3:4})
Traceback (most recent call last):
  File "stdin", line 1, in module
TypeError: unhashable type: 'dict'&lt;/pre&gt;

&lt;p&gt;But why is this so? What's so bad about making a mutable object hashable? We can just add a &lt;code&gt;__hash__()&lt;/code&gt; method to our mutable class, right?&lt;/p&gt;

&lt;p&gt;The reason has to do with how hashes are used in dictionaries. This will require some CS background on how dictionaries/hashmaps work (but you can watch Brandon Rhode's PyCon 2010 talk, &lt;a href="https://www.youtube.com/watch?v=C4Kc8xzcA68"&gt;The Mighty Dictionary&lt;/a&gt;). But the short answer is that if the object's value changes, then the hash must also change since hashes are based on values. However, if the object's value changes after it's used as a key in a dictionary, the hash will no longer refer to the correct bucket in the dictionary for that key. Let's use an example.&lt;/p&gt;


&lt;p&gt;Here's a subclass of Python's built-in list data type (which is mutable) that we've added a &lt;code&gt;__hash__()&lt;/code&gt; function to:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; import collections

&gt;&gt;&gt; class HashableList(collections.UserList):
...     def __hash__(self):
...         return hash(tuple(self))&lt;/pre&gt;

&lt;p&gt;We can create a hashable list object and put it in a dictionary:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; h = HashableList([1, 2, 3])

&gt;&gt;&gt; d = {h: 'hello'}

&gt;&gt;&gt; d
{[1, 2, 3]: 'hello'}&lt;/pre&gt;

&lt;p&gt;It seems to work. We even make another hashable list with the same value and it seems to work too:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; d[h]
'hello'

&gt;&gt;&gt; h2 = HashableList([1, 2, 3])

&gt;&gt;&gt; d[h2]
'hello'&lt;/pre&gt;

&lt;p&gt;Now we change &lt;code&gt;h&lt;/code&gt;. As expected, it creates a &lt;code&gt;KeyError&lt;/code&gt; because &lt;code&gt;[1, 2, 100]&lt;/code&gt; isn't a key in the dictionary, only &lt;code&gt;[1, 2, 3]&lt;/code&gt; is (this turns out to be wrong, but ignore that for now):&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; h[2] = 100

&gt;&gt;&gt; d[h]
Traceback (most recent call last):
  File "stdin", line 1, in module
KeyError: [1, 2, 100]&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;But here's the thing.&lt;/strong&gt; &lt;code&gt;h2&lt;/code&gt; no longer works as a key, even though it is &lt;code&gt;[1, 2, 3]&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; d[h2]
Traceback (most recent call last):
  File "stdin", line 1, in module
KeyError: [1, 2, 3]&lt;/pre&gt;

&lt;p&gt;Why is this? Because the key in &lt;code&gt;d&lt;/code&gt; &lt;em&gt;isn't a copy of the &lt;code&gt;[1, 2, 3]&lt;/code&gt; object, it's a copy of the reference&lt;/em&gt;. When we changed &lt;code&gt;h&lt;/code&gt;, &lt;strong&gt;we also changed the dictionary key&lt;/strong&gt;:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; d
{[1, 2, 100]: 'hello'}&lt;/pre&gt;

&lt;p&gt;So this means the key is now &lt;code&gt;[1, 2, 100]&lt;/code&gt;, but it's in the bucket/slot for &lt;code&gt;[1, 2, 3]&lt;/code&gt;. But &lt;code&gt;h2&lt;/code&gt;'s &lt;code&gt;[1, 2, 3]&lt;/code&gt; won't work because the key's value is now &lt;code&gt;[1, 2, 100]&lt;/code&gt; and Python just assumes it happens to be a hash collision.&lt;/p&gt;

&lt;p&gt;To make things even worse, try setting &lt;code&gt;h[2]&lt;/code&gt; to &lt;code&gt;99&lt;/code&gt;. By sheer coincidence, &lt;code&gt;[1, 2, 99]&lt;/code&gt; hashes to the same bucket/slot as &lt;code&gt;[1, 2, 3]&lt;/code&gt;. And the values are the same (because the key has also changed to &lt;code&gt;[1, 2, 99]&lt;/code&gt;) so it works perfectly fine even though it should cause a &lt;code&gt;KeyError&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; h[2] = 99 # You might need to find another integer to reproduce this, depending on your Python version.

&gt;&gt;&gt; d[h]
'hello'&lt;/pre&gt;

&lt;p&gt;So this is why it's a Bad Thing to have mutable items be hashable: changing them also changes the key in the dictionary. Sometimes this works even though it should cause a &lt;code&gt;KeyError&lt;/code&gt; (when there's a hash collision by coincidence) and other times it'll cause a &lt;code&gt;KeyError&lt;/code&gt; when it should work (because the dictionary key has changed when the mutable object has changed).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BUT WAIT, WHAT WAS THE ONE EXCEPTION?&lt;/strong&gt; Well, a mutable object can be hashable and used as a dictionary key as long as it's hash and value are the same as its identity (or some other unique, unchanging integer). That is, it must have the following &lt;code&gt;__eq__()&lt;/code&gt; and &lt;code&gt;__hash__()&lt;/code&gt; implementations:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; class SomeClass:
...   def __eq__(self, other):
...     return other is self
...   def __hash__(self):
...     return id(self)&lt;/pre&gt;

&lt;p&gt;This also happens to be the default implementations for Python classes, so we can shorten our class to just the following:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; class SomeClass:
...   pass&lt;/pre&gt;

&lt;p&gt;In this case, we uphold the two rules for hashability. But we don't really have a useful class, because every object of type &lt;code&gt;SomeClass&lt;/code&gt; is only equal to itself, and will always be &lt;code&gt;False&lt;/code&gt; when compared to any other object, no matter what its value is.&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; sc = SomeClass()

&gt;&gt;&gt; {sc: 'hello'}
{&lt;__main__.SomeClass object at 0x000001C6CEA57748&gt;: 'hello'}

&gt;&gt;&gt; sc2 = SomeClass()

&gt;&gt;&gt; sc == sc2
False&lt;/pre&gt;

&lt;!--&lt;p&gt;If you try to consider attributes in it's &lt;code&gt;__eq__()&lt;/code&gt; method but leave the &lt;code&gt;__hash__()&lt;/code&gt; as being based off the object's identity, you'll end up with dictionary keys that don't work:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; class MutablePoint:
...   def __init__(self, x, y):
...     self.x = x
...     self.y = y
...   def __eq__(self, other):
...     if not isinstance(other, MutablePoint):
...       return False
...     return self.x == other.x and self.y == other.y
...   def __hash__(self):
...     return id(self)
...

&gt;&gt;&gt; mp = MutablePoint(10, 20)

&gt;&gt;&gt; d = {mp: 'hello'} # You can use mp as a key.

&gt;&gt;&gt; d
{&lt;__main__.MutablePoint object at 0x000001C6CEA57940&gt;: 'hello'}

&gt;&gt;&gt; d[mp] # This works.
'hello'

&gt;&gt;&gt; mp.x = 9999 # Let's change mp's x attribute.

&gt;&gt;&gt; d[mp] # This should cause a KeyError, but doesn't because the hash is based on the mp's identity, not mp's value, and the identity hasn't changed.
'hello'

&gt;&gt;&gt; mp2 = MutablePoint(9999, 20)

&gt;&gt;&gt; mp == mp2 # mp and mp2 have the same value.
True

&gt;&gt;&gt; d[mp2] # But this doesn't work, because the hash is tied to mp's identity, not value, and mp and mp2 have different identities because they are different objects.
Traceback (most recent call last):
  File "stdin", line 1, in module
KeyError: __main__.MutablePoint object at 0x000001C6CEA579E8&lt;/pre&gt;
--&gt;
&lt;p&gt;So either you can follow Python's two hashability rules for your class, or you can create mutable, hashable objects that don't actually work in dictionaries. The only exception when you can have a mutable, hashable class is when the hash is based on the identity and not the value, which severely restricts its usefulness as a dictionary key. Which means that, effectively, only immutable objects can be hashable and mutable objects can't be hashable.&lt;/p&gt;

&lt;p&gt;Fin.&lt;/p&gt;


</content><category term="misc"></category></entry><entry><title>Book Review: The Little Schemer</title><link href="https://inventwithpython.com/blog/book-review-the-little-schemer.html" rel="alternate"></link><published>2018-12-09T12:01:00-05:00</published><updated>2018-12-09T12:01:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2018-12-09:/blog/book-review-the-little-schemer.html</id><summary type="html">&lt;p&gt;Alternative Title: The Little Schemer Wears No Clothes&lt;/p&gt;&lt;p&gt;Perhaps a software engineer or MIT professor friend of yours recommended this book to you. “Oh, you want to learn how to code? You should read The Little Schemer,” they’ll say. Your friend means well, but is completely wrong to recommend this book.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;&lt;em&gt;&lt;a href="https://www.amazon.com/gp/customer-reviews/R27B76VV7ZWU2S/"&gt;(Also posted to Amazon.com.)&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alternative Title: The Little Schemer Wears No Clothes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Perhaps a software engineer or MIT professor friend of yours recommended this book to you. “Oh, you want to learn how to code? You should read The Little Schemer,” they’ll say. Your friend means well, but is completely wrong to recommend this book.&lt;/p&gt;

&lt;p&gt;I really wanted to like this book. I wanted to be charmed by its Socratic, question-and-answer-format presentation. I have a computer science degree. I’ve taught kids and adults how to program. I have (full disclosure) written popular books teaching folks how to code. I have taught and given talks on recursion. I have read Godel Escher Bach by Douglas Hofstadter, which seems to have started the “whimsical Lewis Carroll” trope among computer-scientists-turned-writers. If anyone should be a person who likes this book, it should be me.&lt;/p&gt;

&lt;p&gt;I don’t like this book. The problems are legion.&lt;/p&gt;

&lt;p&gt;First: If you are in the, like, 5% of the population who likes manipulating abstract symbols according to arbitrary logical rules just for the sake of manipulating abstract symbols according to arbitrary logical rules, you’ll love this book. This is the book for you. Five-stars, disregard this review and send me a nasty email telling me I “just don’t get it” and “it’s not supposed to teach programming but a way of thinking”. You should read this book. You’ll get a lot out of it.&lt;/p&gt;

&lt;p&gt;If you’re someone who wants to learn to code, or is a freshmen computer science student, or someone who likes popular science and wants to learn more about computing, put this book back on the shelf and back away. When you’ve got enough distance, turn and run.&lt;/p&gt;

&lt;p&gt;Before I go into the problems, I think this book would have been great if it had a heavy-handed editor. The approach Friedman and Felleisen take is novel, and experimentation should be encouraged. But that doesn’t mean every experiment is a success. A good editor would have pointed out all the problems that I’ll bring up in this review, sent them back to the drawing board/writing desk, and they would’ve come back with something better. A few rounds of this, and they’d without a doubt have a great book. Instead, they don’t have a great book for beginners, they have a book that computer science professors with years of familiarity and experience think is a great book for beginners.&lt;/p&gt;

&lt;p&gt;One of the largest problems is the question-and-answer format. Like alcohol, it’s great in moderation but several issues come up when you rely on literally nothing but it. This book literally never explains anything directly. Nothing is ever given a solid definition; you are left to infer it for yourself. Can’t figure it out, or worst, think you’ve figured it out but are actually working under the wrong assumption? Too bad. The book is moving on, and all the concepts in this book build on top of the previous work. A glossary is a standard part to have in any textbook and would be incredibly helpful in this one, but sadly the authors considered themselves above having one. In the preface, the authors state “We believe that you can form your own definitions and will thus remember them and understand them better than if we had written each one for you.” This is an utterly, completely erroneous belief. And conveniently it frees the authors from having to, you know, explain in simple language what exactly they mean.&lt;/p&gt;

&lt;p&gt;Relying solely on a Socratic approach simply isn’t practical. And what exactly is going on in this book? Are we watching two people having a question-and-answer dialogue? Is the book asking the reader the question? (In which case, there is no possible way the reader could know the answer for most of them, which is hair-pulling levels of stressful the hundredth time you encounter it.) Are these questions the reader is posing to the book? It actually changes page to page. You’ll see the impracticality of the Socratic-only approach in some of the “questions” (which are really answers) and “answers” (which are really questions), especially in the later chapters. This book needs to stop being chained to the question-and-answer format; a few explanatory paragraphs would have done a world of good.&lt;/p&gt;

&lt;p&gt;There were “The Ten Commandments” and “The Five Rules” at the start of the book, but they aren’t the most lucid statements: “When recurring on a list of atoms, lat, ask two questions about it: (null? lat) and else.” What? The word “else” is a question? How is the reader supposed to derive knowledge from a sentence that barely parses as English? It’d be nice if the listings of commandments had page number references to where they’re explained in the book, instead you’re left on your own. And by the way, the First Commandment is split up across three different places (the final one listed after the Fifth Commandment). Any editor would have told them “split this up into three commandments”. There’s a multitude of places where the simple advice from an editor would have saved the reader so much headache.&lt;/p&gt;

&lt;p&gt;There are no objectives stated in the chapters. You’ll find yourself asking “what’s the point of this?” and “where are they going with this?” constantly. Constantly. The chapters launch into the content cold turkey and always end abruptly. Really, the dividing line between where one chapter ends and the next one starts could be made anywhere. And the chapter names don’t offer any hints either: “Toys”, “Cons the Magnificent”, “Shadows”. That last one has the especially infuriating questions: “What is (lat? ls) where ls is ((( )) (() ( )) (() ( )()))” answered with “It is very false.” and “Is that bad?” answered with “You must beware of shadows.” and then the chapter immediately ends. This is literally the only time the word “shadows” appears in the entire book. Oh yes, very mysterious. But they aren’t writing a mystery novel, they’re writing a textbook that should explain what the hell they mean. To someone with a CS degree it’s a bad metaphor, but to the beginner it’s simple inscrutable.&lt;/p&gt;

&lt;p&gt;Because the book is a steady stream of question-and-answer, it forms a trail of breadcrumbs: lose your way once and you’re completely lost. You can’t skim this book, and when you pick it up to start where you left off, it’s hard to remember what you were looking at and trying to understand. Everything is built on the previous content, so this book requires you to load everything into your short-term memory. Some will say this is a “feature”, but I say it’s a staggering bug.&lt;/p&gt;

&lt;p&gt;That guy who loved this book and is recommending it to you is well-intended, but clueless as to what makes for an effective teaching tool. And that’s the reason I wrote this scathing review (which I’ve sat on for a year, wondering if I should actually post it). My prime worry, nay, chilling fear, is that a lot of people will be handed this book, read it, not get it at all, and conclude that they are simply too stupid to learn computer science. "If I can't understand the book with the cute elephant drawings, how am I suppose to understand the harder computer science textbooks?"&lt;/p&gt;

&lt;p&gt;This book needs better titles for its chapters. It needs a glossary and explanatory paragraphs. It needs objectives and summaries in each chapter. It needs to drop the cutesy metaphors and coy figure-out-for-yourself-what-we-mean approach (or at least add straightforward explanation afterwards). It needs an editor. But all of these things make for a book that isn’t as fun to write as a whimsical Socratic book of recursive algorithms. The Little Schemer is simply not a good guide for beginners; it’s a vanity project.&lt;/p&gt;


</content><category term="misc"></category><category term="bookreview"></category></entry><entry><title>A Curriculum for Python Packaging</title><link href="https://inventwithpython.com/blog/curriculum-for-python-packaging.html" rel="alternate"></link><published>2018-10-22T12:00:00-04:00</published><updated>2018-10-22T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2018-10-22:/blog/curriculum-for-python-packaging.html</id><summary type="html">&lt;p&gt;Python's packaging ecosystem contains multitudes. It can be intimidating for new Python developers to try to crack into, especially given the rapid evolution of Python packaging. Writing a *helloworld.py* file and running it on your computer is simple, but getting it to run on someone else's computer (and doing this the "right" way) involves a tangle of terms, tools, and techniques. What are wheel files? What is distutils? Do I use distutils or easy_install or pip?&lt;/p&gt;&lt;p&gt;To get to the bottom of this myself, I've compiled a curriculum of PyCon talks, online documentation, and my own personal notes to finally get a complete handle on Python packaging.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;Python's packaging ecosystem contains multitudes. It can be intimidating for new Python developers to try to crack into, especially given the rapid evolution of Python packaging. Writing a *helloworld.py* file and running it on your computer is simple, but getting it to run on someone else's computer (and doing this the "right" way) involves a tangle of terms, tools, and techniques. What are wheel files? What is distutils? Do I use distutils or easy_install or pip?&lt;/p&gt;

&lt;p&gt;To get to the bottom of this myself, I've compiled a curriculum of PyCon talks, online documentation, and my own personal notes to finally get a complete handle on Python packaging. &lt;strong&gt;The following list is my recommended order&lt;/strong&gt;: start from the beginning, and continue until whatever level of satisfaction you require. I've tried to put the easy/simple/quick resources first, and the rest become progressively more detailed and specialized.&lt;/p&gt;

&lt;p&gt;I've also added my own summary and notes, which you may or may not find useful. If you have any comments about this curriculum, please &lt;a href="mailto:al@inventwithpython.com"&gt;email me&lt;/a&gt; or &lt;a href="https://twitter.com/AlSweigart"&gt;message me (and follow me!) on Twitter&lt;/a&gt;. (Note that this blog post was written in October 2018. I might update this every few months (or years).)&lt;/p&gt;

&lt;p&gt;NOTE: The information in the &lt;a href="https://docs.python.org/3/distutils/introduction.html"&gt;official Python documentation for the distutils module&lt;/a&gt; is out of date (note that it says "Legacy version" in the title), as well as the &lt;a href="https://docs.python.org/3/install/index.html"&gt;Installing Python Modules (Legacy version)&lt;/a&gt; documentation. The latter was replaced by &lt;a href="https://docs.python.org/3/installing/index.html"&gt;Installing Python Modules&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Python Packaging Curriculum&lt;/h2&gt;

&lt;p&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=AQsZsgJ30AE"&gt;Dustin Ingram - Inside the Cheeseshop: How Python Packaging Works - PyCon 2018&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=qOH-h-EKKac"&gt;Dave Forgac Share Your Code! Python Packaging Without Complication PyCon 2017&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://blog.ionelmc.ro/2014/05/25/python-packaging/"&gt;Packaging a python library - Ionel Cristian Mărieș's Blog&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://hynek.me/articles/testing-packaging/"&gt;Testing &amp; Packaging - Hynek Schlawack's Blog&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=UtFHIpNPMPA"&gt;Grug make fire! Grug make wheel! by Russell Keith-Magee - PyCon Australia 2014&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=5BqAeN-F9Qs"&gt;Glyph - Shipping Software To Users With Python - PyCon 2016&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=Q3LyPTTb81w"&gt;Mahmoud Hashemi - BayPiggies September 2017 at LinkedIn: The Packaging Gradient&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=GBQAKldqgZs"&gt;Kenneth Reitz - Pipenv: The Future of Python Dependency Management - PyCon 2018&lt;/a&gt;&lt;/li&gt;
  &lt;/ol&gt;
&lt;/p&gt;

&lt;p&gt;The following are also packaging-related, but I haven't had time to organize them into this curriculum:&lt;/p&gt;

&lt;p&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=5BqAeN-F9Qs"&gt;Glyph - Shipping Software To Users With Python - PyCon 2016&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=02aAZ8u3wEQ"&gt;Elana Hashman - The Black Magic of Python Wheels - PyCon 2019&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://youtu.be/4fzAMdLKC5k"&gt;Dave Forgac - Python Packaging from Init to Deploy - PyOhio 2015&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://youtu.be/QX_Nhu1zhlg"&gt;Samuel Roeca - Poetry: "dependency management and packaging made easy" - PyGotham 2019&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=vGphzPLemZE"&gt;Andrew T Baker 5 ways to deploy your Python web app in 2017 PyCon 2017&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=rdp8o0nkXQ4"&gt;Katie McLaughlin - What is deployment, anyway? - DjangoCon 2021&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=P3dY3uDmnkU"&gt;Chris Wilcox - Shipping your first Python package and automating future publishing - PyCon 2019&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=AD8LSdy7b2s"&gt;Caitlin Rubin - Intentional Deployment: Best Practices for Feature Flag Management - PyCon 2019&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=wuCpCkrfeMs"&gt;Hynek Schlawack - How to Write Deployment-friendly Applications - PyCon 2018&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=eLPiPHr6TVI"&gt;Asheesh Laroia: Python packaging simplified, for end users, app developers - PyCon 2014&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=W8A2bOKPtJU"&gt;Domen Kožar - Rethinking packaging, development and deployment - PyCon 2015&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/p&gt;

&lt;!--
&lt;p&gt;These videos were interesting, but are either out of date or advanced topics not relevant to a beginner's curriculum:&lt;/p&gt;

&lt;p&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=wuCpCkrfeMs"&gt;Hynek Schlawack - How to Write Deployment-friendly Applications - PyCon 2018&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=eLPiPHr6TVI"&gt;Asheesh Laroia: Python packaging simplified, for end users, app developers - PyCon 2014&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=W8A2bOKPtJU"&gt;Domen Kožar - Rethinking packaging, development and deployment - PyCon 2015&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/p&gt;
--&gt;


&lt;h3&gt;1 - Dustin Ingram - Inside the Cheeseshop: How Python Packaging Works - PyCon 2018&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=AQsZsgJ30AE"&gt;Link to YouTube video.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My summary of this talk:&lt;/strong&gt; A great history lesson on the Python Package Index (PyPI), it’s previous incarnations, and the distutils/setuptools/easy_install/twine tools for getting your packages on it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;History: The first package index was a web site called The Vaults of Parnassus, which was a simple, fairly unstructured, GeoCities-looking website.&lt;/li&gt;
&lt;li&gt;Then came distutils (“distribution utilities”) in Python 1.6 gave us &lt;code&gt;python setup.py ...&lt;/code&gt; Had a “just use Python code instead of a domain-specific language or anything fancy” attitude; very barebones.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;python setup.py build&lt;/code&gt; became the standard, makefile-like way to build packages. To package source code for sharing, we used the now-familiar &lt;code&gt;python setup.py sdist&lt;/code&gt; (“source distributions”)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;python setup.py bdist&lt;/code&gt; (“build distributions”) would package up pre-built code for your OS/platform for you.&lt;/li&gt;
&lt;li&gt;The problem was, before running &lt;code&gt;python setup.py bdist&lt;/code&gt;, how do you get everything together for the bdist part? This was solved on Linux by package managers like RPM, but not on Windows/macOS/obscure platforms.&lt;/li&gt;
&lt;li&gt;To solve this, the Python Package Index was made as a general Python package manager for all platforms.&lt;/li&gt;
&lt;li&gt;PyPI is “Pie-pea-eye”, not “Pie-pie”. “Cheese shop” was the old name for PyPI.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setuptools&lt;/code&gt; was a monkey-patch (bad idea) to distutils to install package dependencies.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;easy_install&lt;/code&gt; intrduced the “Egg Distribution”. Egg files are just zip files with some metadata files included in them. (Pythons, the snake, lay eggs.)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;easy_install&lt;/code&gt; couldn’t uninstall stuff or tell you the packages you had installed.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pyinstall&lt;/code&gt; was created to solve these problems. You’ve probably never heard of this, because it was soon renamed to &lt;code&gt;pip&lt;/code&gt;, which stands for “PIP Installs Packages”.&lt;/li&gt;
&lt;li&gt;Pip ignores eggs, and only handles source distributions. Pip begins to be used to install applications, not just library modules.&lt;/li&gt;
&lt;li&gt;Pip introduces requirements.txt, which contains a list of depended-on packages (and the specific version of the package) that the package depends on: &lt;code&gt;pip install -r requirements.txt&lt;/code&gt; is used to install all these dependencies listed in the file.&lt;/li&gt;
&lt;li&gt;Pip mainly depended on installing from whoever was hosting the package from their website. This was a problem; this random person’s website could be slow, or could be hacked so that it spread malware when you installed from it. So PyPI begins hosting modules on its own website.&lt;/li&gt;
&lt;li&gt;There are still problems with source distributions that were solved with built distributions, but the Egg format was poorly defined, so Wheel files becomes the new Egg file: a way to distribute built modules. Wheel files (named after cheese wheels, a reference to cheese shop) are also like Egg files in that they are just zip files but they are more well-defined (see PEP 427) that learned from the mistakes of easy_install and Egg files.&lt;/li&gt;
&lt;li&gt;Twine came out to solve the problem that &lt;code&gt;python setup.py upload&lt;/code&gt; doesn’t use HTTPS. The name comes from using twine to bundle up boxes &amp;amp; packages. The name doesn’t really make a lot of sense. Twine doesn’t bundle stuff, so much as just upload already-bundled packages using encrypted HTTPS.&lt;/li&gt;
&lt;li&gt;PyPI was showing its age, and so it was rewritten from scratch. This was the “warehouse” project, started in 2011 and became the new PyPI in 2018. Yay!&lt;/li&gt;
&lt;li&gt;Current problems: Packaging is still kinda hard. There’s tons of tools and history. Read the Python Packaging Guide at &lt;a href="https://packaging.python.org"&gt;https://packaging.python.org&lt;/a&gt;, and sampleproject at &lt;a href="https://github.com/pypa/sampleproject"&gt;https://github.com/pypa/sampleproject&lt;/a&gt; (a nice skeleton project that follows best practices).&lt;/li&gt;
&lt;li&gt;Current problems: Packaging is a little too easy; typo squatting and spamming PyPI. Solution: conda, a Python-agnostic packaging tool.&lt;/li&gt;
&lt;li&gt;Current problems: Reproducible environments. Solution: pipfile and pipfile.lock from &lt;a href="https://github.com/pypa/pipfile"&gt;https://github.com/pypa/pipfile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Current problem: &lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt; executes arbitrary code, which is a security/standards/maintenance concern.&lt;/li&gt;
&lt;li&gt;Current problem: The “distutils/setuptools” dance; these are old/hard-to-maintain standard library modules. Updating them is hard to do. PEP 517 and PEP 518 detail solutions, which is where pyproject.toml files come in. These are files that specify dependencies, etc. in an agnostic way: distutils &amp;amp; setuptools can use pyproject.toml files to install dependencies, or some other tool can use them.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;2 - Dave Forgac Share Your Code! Python Packaging Without Complication PyCon 2017&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=qOH-h-EKKac"&gt;Link to YouTube video.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My summary of this talk:&lt;/strong&gt; This video goes into detail about &lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt; files, but otherwise it’s great in listing things that you should go off and learn more about. It also touches on the associated things you need to get together for a package (docs, tests, continuous integration, etc). The slides for this talk are available at &lt;a href="http://daveops.com/pycon2017"&gt;daveops.com/pycon2017&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The things you need to share your code with other people:&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Package the code&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Source Hosting (GitHub, etc.)&lt;/li&gt;
&lt;li&gt;Tests&lt;/li&gt;
&lt;li&gt;Continuous Integration (CI)&lt;/li&gt;
&lt;li&gt;License&lt;/li&gt;
&lt;li&gt;Contributing guide&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;This is a lot stuff, but you can just use cookiecutter to make all of this stuff for you.&lt;/li&gt;
&lt;li&gt;Terminology:&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Module (Python code saved in a file)&lt;/li&gt;
&lt;li&gt;Import Package (a folder that stores Python modules)&lt;/li&gt;
&lt;li&gt;Distribution Package (a file that has bundled up your code into a shareable/installable file)&lt;/li&gt;
&lt;li&gt;Source Distribution (source code that is shared with other people, include C source code for C extensions, these are built/compiled when the package is downloaded/installed)&lt;/li&gt;
&lt;li&gt;Built Distribution (eggs and wheel files, wheel is the modern current one, these are pre-compiled so they only need to be unpacked when downloaded/installed)&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Types of wheel files:&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Universal wheels (contains only Python code that works with Python 2 &lt;em&gt;and&lt;/em&gt; 3 and can be installed anywhere.)&lt;/li&gt;
&lt;li&gt;Pure Python wheels (contains only Python code, but only for Python 2 &lt;em&gt;or&lt;/em&gt; Python 3.)&lt;/li&gt;
&lt;li&gt;Platform wheels (files that contain compiled code for a target platform/OS)&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;History (this was covered more in Dustin Ingram’s talk).&lt;/li&gt;
&lt;li&gt;PyPA (Python Packaging Authority) starts creating standards for packaging in the Python ecosystem. PyPUG is the Python Packaging User Guide at &lt;a href="https://packaging.python.org/"&gt;https://packaging.python.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt; is just a Python file, but don’t do anything clever or add custom logic to it.&lt;/li&gt;
&lt;li&gt;setup.cfg holds wheel settings and various other settings&lt;/li&gt;
&lt;li&gt;&lt;a href="http://MANIFEST.in"&gt;MANIFEST.in&lt;/a&gt; will list any non-Python files (data files, configuration files, documentation, etc.) that need to be packaged up too.&lt;/li&gt;
&lt;li&gt;README.rst is the Restructured Text file (note, not Markdown) that will be used for the package’s PyPI page.&lt;/li&gt;
&lt;li&gt;PEP 440 covers the format to use for version strings.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt; is a file basically just calls &lt;code&gt;setuptools.setup()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;(There’s a lot of details about the various keyword arguments in &lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt;'s setuptools.setup() function call.)&lt;/li&gt;
&lt;li&gt;Use piptools to manage the requirements.txt file.&lt;/li&gt;
&lt;li&gt;Don’t use &lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt;'s own upload feature, use twine instead.&lt;/li&gt;
&lt;li&gt;Develop Mode (any file changes made to your source won’t have to be reinstalled with pip): &lt;code&gt;pip install -e .&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;h3&gt;3 - Packaging a python library - Ionel Cristian Mărieș's Blog&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://blog.ionelmc.ro/2014/05/25/python-packaging/"&gt;Link to the blog post.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My summary of this blog post:&lt;/strong&gt; This is about packaging libraries for other Python software developers to use in their applications, not on packaging applications. It’s a good list of how to layout all the different parts of a library.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Put your Python module in &lt;em&gt;/src/packagename&lt;/em&gt;, not in &lt;em&gt;/packagename&lt;/em&gt;. This gives you “import parity”: it forces you to install the package just like your users would have to. Otherwise, if you run Python from the root of your repo, &lt;code&gt;import packagename&lt;/code&gt; would import &lt;em&gt;/src/packagename&lt;/em&gt; just fine, but this means you don’t have to go through the install process that your users would and you could miss potential errors they’d face.&lt;/li&gt;
&lt;li&gt;Don’t import your package from your &lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt; file, i.e. your &lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt; shouldn’t have &lt;code&gt;import packagename&lt;/code&gt; in it. You don’t want to do this, because if your package imports other dependencies, those dependencies might not be installed yet and this causes distribution installation errors.&lt;/li&gt;
&lt;li&gt;Having a &lt;em&gt;/src&lt;/em&gt; folder lets you just add &lt;code&gt;graft src&lt;/code&gt; to your &lt;a href="http://MANIFEST.in"&gt;MANIFEST.in&lt;/a&gt; file, which is simple.&lt;/li&gt;
&lt;li&gt;“Flat is better than nested” &lt;strong&gt;but not for data&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Don’t put your &lt;em&gt;tests&lt;/em&gt; folder inside your &lt;em&gt;src&lt;/em&gt; or &lt;em&gt;packagename&lt;/em&gt; folder.&lt;/li&gt;
&lt;li&gt;Whenever the &lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt; file opens a file, it specifies an encoding. Note that for python 2/3 compatibility, it uses &lt;code&gt;io.open()&lt;/code&gt; instead of &lt;code&gt;open()&lt;/code&gt;, since Python 2’s &lt;code&gt;open()&lt;/code&gt; doesn’t have an &lt;code&gt;encoding&lt;/code&gt; keyword argument.&lt;/li&gt;
&lt;li&gt;Don’t use &lt;code&gt;python setup.py test&lt;/code&gt; to run your tests; that’s outdated. Travis-CI has become the de factor standard for running tests after checking in code. Tox is a good way to run tests locally.&lt;/li&gt;
&lt;li&gt;Ionel has a sample project layout here: &lt;a href="https://github.com/ionelmc/python-nameless"&gt;https://github.com/ionelmc/python-nameless&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;cookiecutter&lt;/code&gt; to generate these files (tox.ini, MANIFEST.in, etc.)&lt;/li&gt;
&lt;/ul&gt;






&lt;h3&gt;4 - Testing &amp; Packaging - Hynek Schlawack's Blog&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://hynek.me/articles/testing-packaging/"&gt;Link to the blog post.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My summary of this blog post:&lt;/strong&gt; This blog article sort of assumes you are already familiar with tox and coverage.py. It doesn't really hold your hand with its examples. Though it did introduce me to detox, which is a drop-in replacement (rather, an addition) to tox that lets you run the different tox environment tests in parallel.&lt;/p&gt;


&lt;p&gt;Put your modules in a separate src folder. This simplifies what you need in your setup.py:&lt;/p&gt;

&lt;pre&gt;    setup(
        ...
        packages=find_packages(where='src'),
        package_dir={'': 'src'},
    )&lt;/pre&gt;

&lt;p&gt;"Combined coverage" means not just measuring your code coverage with one version of Python, but across all the versions you test with tox. If you have 100% coverage in Python 3, but less with Python 2, then that affects your overall combined coverage percentage.&lt;/p&gt;








&lt;h3&gt;5 - Grug make fire! Grug make wheel! by Russell Keith-Magee - PyCon Australia 2014&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=UtFHIpNPMPA"&gt;Link to YouTube video.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My summary of this talk:&lt;/strong&gt; Kind of old, being from 2014, but has some good basic information about &lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt;. He also covers the difference between universal wheels and platform wheels. A good, basic overview of Python packaging, and I think this talk touched on things the other basic Python packaging talks didn’t touch on, so it makes sense to watch this along with other basic packaging talks.&lt;/p&gt;


&lt;ul&gt;
&lt;li&gt;Wheel files - Contains binary files &amp;amp; compiled code. This is much more ready-to-use than a source distribution (sdist).&lt;/li&gt;
&lt;li&gt;Eggs were intended to be an executable file, not just a distribution file. Wheels are just for distribution, you don’t “execute a wheel file” or “run a wheel file”.&lt;/li&gt;
&lt;li&gt;Russ goes into a project folder layout, which includes: README, LICENSE, package folder, docs folder, tests folder.&lt;/li&gt;
&lt;li&gt;Other tools you might find useful: tox (for running unit tests on different versions of Python all at once), sphinx (for creating documentation, which can then be uploaded to &lt;a href="http://readthedocs.org"&gt;readthedocs.org&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;To put your module into a package, you need: &lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt;, setup.cfg, &lt;a href="http://MANIFEST.in"&gt;MANIFEST.in&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Note: &lt;code&gt;python setup.py bdist_wheel&lt;/code&gt; might come up with an error because the bdist_wheel command was used in older versions of pip. I believe &lt;code&gt;python setup.py bdist&lt;/code&gt; replaces it, or you can run &lt;code&gt;pip install wheel&lt;/code&gt; to get the bdis_wheel command just in case older scripts require it.&lt;/li&gt;
&lt;li&gt;setup.cfg is a config file is optional, but includes options for &lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt; so you don’t need to specify them as command line arguments.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://MANIFEST.in"&gt;MANIFEST.in&lt;/a&gt; describes what files need to be in the distributable that isn’t a .py file (docs, tests, etc.)&lt;/li&gt;
&lt;li&gt;Use check-manifest can check the &lt;a href="http://MANIFEST.in"&gt;MANIFEST.in&lt;/a&gt; file.&lt;/li&gt;
&lt;li&gt;Use bumpversion to be sure to update the version number in your file.&lt;/li&gt;
&lt;li&gt;If your code is pure-Python, only runs on Python 2 &amp;amp; 3 and has no C extensions, you can create a “universal wheel” with &lt;code&gt;python setup.py bdist_wheel --universal&lt;/code&gt; (or you can have a setup.cfg with universal set to 1 there).&lt;/li&gt;
&lt;li&gt;If you do have C extensions, you need to create a wheel file.&lt;/li&gt;
&lt;li&gt;If all else fails, you can use a source distribution &lt;code&gt;python setup.py sdist&lt;/code&gt;, but any C extensions will have be compiled by the user.&lt;/li&gt;
&lt;li&gt;Don’t use &lt;code&gt;python setup.py register&lt;/code&gt; because it passes in your username/password unencrypted. Instead, log in to PyPI with your web browser and create it through the website.&lt;/li&gt;
&lt;li&gt;Twine uses encryption, but also lets you test your package that you created with &lt;code&gt;python setup.py&lt;/code&gt; before uploading it with Twine.&lt;/li&gt;
&lt;li&gt;Bootstrapping pip: If you don’t have it, then (for pre Python 3.4) you can run &lt;code&gt;python get-pip.py&lt;/code&gt; or (for Python 3.4 and later) you can run &lt;code&gt;python -m ensurepip&lt;/code&gt;. But pip should automatically be included with Python 3.4 and later.&lt;/li&gt;
&lt;li&gt;Python 3.4 also includes virtualenv.&lt;/li&gt;
&lt;/ul&gt;





&lt;h3&gt;6 - Glyph - Shipping Software To Users With Python - PyCon 2016&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=5BqAeN-F9Qs"&gt;Link to YouTube video.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My summary of this talk:&lt;/strong&gt; Getting software onto a production server or running on end user’s desktop still requires a few different steps, but the tools for doing this are out there and exist. This talk touches on many of those tools and the issues in using them. This talk also has a great set of “bad practices” that can help you avoid making some common mistakes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Package” in most software senses (like a Redhat Package Manager package) is called a “distribution” in Python (specifically a “distutils distribution”. “Package” in Python refers to those folders that contain a &lt;strong&gt;init&lt;/strong&gt;.py and &lt;strong&gt;main&lt;/strong&gt;.py and so on. They’re more of a namespace.&lt;/li&gt;
&lt;li&gt;A distutils distribution comes, for example, in a wheel file.&lt;/li&gt;
&lt;li&gt;PYTHONPATH was used in the past, but don’t use it today.&lt;/li&gt;
&lt;li&gt;Don’t use &lt;code&gt;sudo pip install&lt;/code&gt;. Don’t install stuff using your operating system’s Python installation (i.e. “System Python”).&lt;/li&gt;
&lt;li&gt;Don’t use &lt;code&gt;python setup.py install&lt;/code&gt;, instead use pip. For one, there’s no &lt;code&gt;python setup.py uninstall&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Virtualenv creates lightweight Python environments. They’re about a 1/10 of the full Python environment but safely isolated (for the most part).&lt;/li&gt;
&lt;li&gt;Don’t have a C compiler on the server machine, since it’ll compile to different compiled bits in each deployment. The “Ana Karenina Principal” comes from a line in Tolstoy’s novel: “All happy families are alike, but all unhappy families are unhappy in different ways.” Also true of unhappy servers. Having a C compiler can also slow down your deployments. Don’t have build tools on your production server, which means you don’t want to ship source distributions to your servers.&lt;/li&gt;
&lt;li&gt;For library module developers Requirements.txt should have exact versions specified.&lt;/li&gt;
&lt;li&gt;Shipping software to end users is more rocky. (Let’s assume that getting Python is easy to install, and we can get pip. )&lt;/li&gt;
&lt;li&gt;pip install --user can install stuff to the user’s home directory, but now they also have to configure their shell so that Python can find it. This is less than ideal for deploying applications.&lt;/li&gt;
&lt;li&gt;GUI applications in Python: using py2app to get Python GUI applications on Macs. Py2App builds effectively a self-contained Python environment that is an application by itself.&lt;/li&gt;
&lt;li&gt;py2exe and py2installer does the same thing on Windows.&lt;/li&gt;
&lt;li&gt;cx_Freeze is cross-platformed.&lt;/li&gt;
&lt;li&gt;These tools that can distribute GUI desktop applications to users tend to over-optimize: they read the source for import statements so they only include them and not everything. This goes against Python’s dynamic nature; namespace packages (import zope.interface, import flufl.enum) will likely break.&lt;/li&gt;
&lt;li&gt;You &lt;em&gt;can&lt;/em&gt; just import all of these modules in your initial script. It’s kind of tedious.&lt;/li&gt;
&lt;li&gt;PyBee’s Briefcase project is good for packaging up stuff for various platforms.&lt;/li&gt;
&lt;li&gt;pynsist is great for making Windows installers.&lt;/li&gt;
&lt;li&gt;My takeaway: The Python packaging ecosystem is large and intimidating, but there’s a lot of really cool tools that have been made for taking on this problem from a variety of angles. It can be hard to know where to start, and there seems to be a “last mile” problem: it’s still not obvious to non-developers how to install Python software.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3&gt;7 - Python Packaging User Guide&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://packaging.python.org/"&gt;Link to documentation.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My summary of this website:&lt;/strong&gt; This is the official Python packaging documentation, so it has complete information but may be a bit much all at once. I'd start here: &lt;a href="https://packaging.python.org/tutorials/installing-packages/"&gt;Installing Packages&lt;/a&gt;&lt;/p&gt;





&lt;h3&gt;8 - Mahmoud Hashemi - BayPiggies September 2017 at LinkedIn: The Packaging Gradient&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=Q3LyPTTb81w"&gt;Link to YouTube video.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My summary of this talk:&lt;/strong&gt; With some humor, Mahmoud covers a lot of the issues devs face when they don’t know about Python’s packaging ecosystem and start reinventing things from scratch. “The Packing Gradient” is his concept of how deep into the packaging ecosystem you need to get depending on how complicated your software is. There’s info about Anaconda &amp;amp; packaging that I didn’t see anywhere else. But in general, not directly related to how to do Python packaging.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://speakerdeck.com/mhashemi/the-packaging-gradient-extended-edition"&gt;https://speakerdeck.com/mhashemi/the-packaging-gradient-extended-edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;“Packaging” is getting your code into some kind of file to send to other people.&lt;/li&gt;
&lt;li&gt;Packaging is something developers don’t consider until the end of development, but this is a mistake and leads to reinventing existing tools.&lt;/li&gt;
&lt;li&gt;Packaging a standalone Python module (one .py file). “Standalone” means it only imports from the standard library. For example, &lt;a href="http://bottle.py"&gt;bottle.py&lt;/a&gt; (which inspired Flask (and the name “Flask”)) is a single file. Standalone modules are easy to distribute and integrate, you basically just copy the file.&lt;/li&gt;
&lt;li&gt;“Vendoring” is including software you didn’t write in your software.&lt;/li&gt;
&lt;li&gt;“Artifacts” are the files you want your build process to produce: .dll or .exe for compiled stuff, or a .zip file or .whl file for a Python distribution.&lt;/li&gt;
&lt;li&gt;Packaging a pure-Python package: Note that “package” in Python means a folder with a &lt;em&gt;&lt;strong&gt;init&lt;/strong&gt;.py&lt;/em&gt; file in it. They only have Python files in them. For example: Django, requests. These are easy to install with pip.&lt;/li&gt;
&lt;li&gt;PIP stands for “PIP Installs Packages”&lt;/li&gt;
&lt;li&gt;“Distribution” is a file (like a zip file) that contains zero or more Python packages. (“Distribution” is what Python calls what most package managers like RPM would call a “package”.)&lt;/li&gt;
&lt;li&gt;Distributions are built by setuptools with a &lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt; file. Making a distribution file is great for pure-Python modules.&lt;/li&gt;
&lt;li&gt;“You can have multiple distributions providing the same package”, for example, PIL and Pillow where someone forked PIL to make Pillow. PIL wasn’t updated and Pillow has PIL’s code but with a new distribution name on PyPI but the same package names. That is, you can run &lt;code&gt;pip install pillow&lt;/code&gt; but still use the old &lt;code&gt;from PIL import Image&lt;/code&gt; code you’ve always been using before Pillow.&lt;/li&gt;
&lt;li&gt;He mentions the left-pad incident which is a good case study to read on: &lt;a href="https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/"&gt;https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;“Python is slow” is misleading: One of Python’s strengths is its interoperability: after all, numpy isn’t slow.&lt;/li&gt;
&lt;li&gt;If you distribute sdist (“source distributions”) you’ll be sending out uncompiled C code, which isn’t ready to use on the platform you’re installing to. We have wheel files: they have binary distributions (bdist) for compiled C code. Supports &lt;em&gt;most&lt;/em&gt; Windows, Mac, and Linux. The “manylinux 1” tag is used as for generic Linux platform that wheel files target.&lt;/li&gt;
&lt;li&gt;(Wheel files replace egg files, so you can forget about egg files. Egg files are obsolete.)&lt;/li&gt;
&lt;li&gt;Note: He has &lt;code&gt;python setup.py sdist bdist_wheel upload&lt;/code&gt; as the modern way to build and upload a Python package, but these days Twine replaces the upload command. (This talk was in September 2017.)&lt;/li&gt;
&lt;li&gt;But wheel files aren’t what you use to install, say, applications on your phone or whatever.&lt;/li&gt;
&lt;li&gt;“PyPI is not an app store” (it’s free for one thing, but also Python is too general purpose for just apps). Installing by pip from PyPI requires a working Python &amp;amp; pip installation, an internet connection, preinstalled system libraries (like lxml requires libxml2), and build tools for target packages (like gcc/clang). But more than anything, pip requires a knowledgeable software developer. Pip isn’t that great for end users who don’t know how to debug pip’s install error messages.&lt;/li&gt;
&lt;li&gt;So how do we ship &lt;strong&gt;applications&lt;/strong&gt; like Sublime Text or EVE Online or whatever?&lt;/li&gt;
&lt;li&gt;PEX (Python EXecutable) results in a single runnable file. No setup or install step. You can wget this file and then just run it. It uses Python’s zipimport module. (zipapp is more or less PEX.) PEX is good for distributing standalone Python scripts. A 15-minute lightning talk on PEX: “WTF is PEX?” &lt;a href="https://www.youtube.com/watch?v=NmpnGhRwsu0"&gt;https://www.youtube.com/watch?v=NmpnGhRwsu0&lt;/a&gt; (The lightning talk is a bit hard to follow, but I did learn about &lt;code&gt;python .&lt;/code&gt; runs the &lt;strong&gt;main&lt;/strong&gt;.py file in the current folder)&lt;/li&gt;
&lt;li&gt;Anaconda has &lt;code&gt;conda install your_application&lt;/code&gt;, for example, you can &lt;code&gt;conda install postgres&lt;/code&gt; and &lt;code&gt;conda install nginx&lt;/code&gt; or even install other versions of Python! Anaconda is a whole new ecosystem. It’s not just Python-specific.&lt;/li&gt;
&lt;li&gt;Anaconda is a cross-platform, Python-first package management and social app store. It’s like a Python version of Steam, pkgsrc, and Nix/Enlambda.&lt;/li&gt;
&lt;li&gt;Mahmoud likes to recommend &lt;code&gt;conda install&lt;/code&gt; over &lt;code&gt;pip install&lt;/code&gt; for non-developers.&lt;/li&gt;
&lt;li&gt;Freezers: Provide installers, include Python. Dropbox, EVE Online, Civilization IV, any Kivy apps have a full runtime Python included with the app. Examples: cx_Freeze, PyInstaller, osnap, bbFreee, py2exe, py2app, pynsist, nuitka.&lt;/li&gt;
&lt;li&gt;Freezers represent the best option for consumer software.&lt;/li&gt;
&lt;li&gt;“Enterprise” Freezer like Omnibus. Example: GitLab. Best for shipping applications with multiple components (services, queues, etc)&lt;/li&gt;
&lt;li&gt;Userspace images - include their own environment.&lt;/li&gt;
&lt;li&gt;Containers - like userspace images but also with sandboxing. Example: Flatpak/Snappy, Docker. These only work on Linux, reuire a container runtime, has a “pull” step. Can easily be gigabytes in size.&lt;/li&gt;
&lt;li&gt;Virtual machine - includes an entire OS kernel, along with everything containers have. Can work on any OS, is gigabytes in size. There is no “VM Image” app store, users have to download it directly from your site.&lt;/li&gt;
&lt;li&gt;Hardware - ha! They just ship you the computer: they’re basically appliances that sit on a server rack. Think: routers that let you include your own Python code.&lt;/li&gt;
&lt;/ul&gt;






&lt;h3&gt;9 - Kenneth Reitz - Pipenv: The Future of Python Dependency Management - PyCon 2018&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=GBQAKldqgZs"&gt;Link to YouTube video.&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Packaging history: We had the Cheeseshop, which was only an index of packages; you had to host your packages on your own website. Packages were installed with &lt;code&gt;python setup.py install&lt;/code&gt; but you couldn’t uninstall them. Easy_install made this process easier, but there was still no uninstall. From 2010 onward, pip &amp;amp; virtualenv &amp;amp; requirements.txt files were the norm.&lt;/li&gt;
&lt;li&gt;Ruby doesn’t have virtual environments because they can have multiple versions of a package installed at the same time, unlike Python.&lt;/li&gt;
&lt;li&gt;Other communities (Node.js’s yarn &amp;amp; npm, PHP’s Composer, Rust’s Cargo, Ruby’s Bundler) all use a lockfile.&lt;/li&gt;
&lt;li&gt;Venv has downsides: it’s hard for new-comers to understand, its a manual process (though virtualenv-wrapper helps with this).&lt;/li&gt;
&lt;li&gt;The requirements.txt file has a problem: it can represent what you want installed, and it can represent what you need installed. (pip-tools was created to ease the pain of installing packages you need.)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pip freeze&lt;/code&gt; shows you a pre-flattened list of required packages: it includes the project’s dependencies, but also the dependencies’ dependencies. So it can be hard to see which packages are really required for your project and what packages are installed because they are dependencies of those required packages. (This is what we mean by “pre-flattened”; you can’t tell which is which between dependencies and dependencies’ dependencies.)&lt;/li&gt;
&lt;li&gt;You could just specify the few packages you need, but then you have &lt;strong&gt;non-deterministic builds&lt;/strong&gt;: the subdependencies will be whatever latest version is out there. So building one day may have different (that is, newer) subdependency packages installed. This uncertainty and different behavior can cause hard-to-debug bugs.&lt;/li&gt;
&lt;li&gt;The requirements.txt file can be a lockfile and not a lockfile depending on how you use it. We need to split this up into two files.&lt;/li&gt;
&lt;li&gt;The pipfile is a new standard from the PyPA (Python Packaging Authority) (specifically from Donald Stufft) that is replacing the requirements.txt file. It’s written in TOML (an .ini-like language)&lt;/li&gt;
&lt;li&gt;Example pipfile:&lt;/li&gt;
&lt;pre&gt;[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"

[packages]
flask = "*"

[dev-packages]
pytest = "*"
&lt;/pre&gt;
&lt;li&gt;Pipenv lets you use pipfile &amp;amp; pipfile.lock, it uses virtualenv but hides all the details of it from you. Pipenv mostly replaces virtualenvwrapper. The [dev-packages] section specifies what will be installed when &lt;code&gt;pipenv install --dev&lt;/code&gt; is run.&lt;/li&gt;
&lt;li&gt;Demo:&lt;/li&gt;
&lt;pre&gt;&lt;code&gt;pipenv install requests (Creates the virtual environment, the pipfile, and the pipfile.lock file.)
&lt;/code&gt;&lt;/pre&gt;
&lt;li&gt;Once this virtual environment is created by pipenv, you can run pipenv shell to activate it. The virutal environment exists in HOME/.virtualenvs. Running &lt;code&gt;pipenv -- venv&lt;/code&gt; will show you where the virtual environment files are stored.&lt;/li&gt;
&lt;li&gt;The created pipfile file only has the requests library in it, while the pipfile.lock file has all of its subdependencies with exact version numbers (and hashes, in case the package maintainer changes it but reuses the version number) for each one.&lt;/li&gt;
&lt;li&gt;According to &lt;a href="https://github.com/pypa/pipenv/issues/598"&gt;https://github.com/pypa/pipenv/issues/598&lt;/a&gt; you should commit pipfile.lock to source control.&lt;/li&gt;
&lt;li&gt;Pipenv is very folder-specific. If you move the project folder to a new location, you’ll lose the virtual environment. But that’s okay, because you can just run &lt;code&gt;pipenv install&lt;/code&gt; and it will use the existing Pipfile.lock file to install all the modules again.&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;pipenv graph&lt;/code&gt; will give a nice dependency tree.&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;pipenv check&lt;/code&gt; will check for security vulnerabilities.&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;pipenv install --deploy&lt;/code&gt; will do ???. It also checks if Pipfile nad Pipfile.lock are in sync (they must be kept in sync with each other).&lt;/li&gt;
&lt;li&gt;If you have an existing &lt;code&gt;.venv&lt;/code&gt; folder in your project folder, than &lt;code&gt;pipenv install&lt;/code&gt; will use it instead of the central folder for all virtual environments.&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;pipenv --three&lt;/code&gt; creates a virtual environment with Python 3. You can later run &lt;code&gt;pipenv --two&lt;/code&gt; and it destroys the old virtual environment and creates a new one with Python 2.&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;pipenv lock -r&lt;/code&gt; will output a requirements.txt file.&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;pipenv sync&lt;/code&gt; will uninstall any packages you no longer need.&lt;/li&gt;
&lt;li&gt;Pipenv does NOT replace &lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt;. Pipenv is for applications, while &lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt; is used for libraries. Kenneth would not check in the lockfile for libraries that could be targetting multiple versions of Python.&lt;/li&gt;
&lt;li&gt;Don’t get pipenv confused with pyenv.&lt;/li&gt;
&lt;/ul&gt;




&lt;!--
&lt;h2&gt;Interesting Resources, but Out of Date or Only Tangentially Related&lt;/h2&gt;

&lt;h3&gt;Hynek Schlawack - How to Write Deployment-friendly Applications - PyCon 2018&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=wuCpCkrfeMs"&gt;Link to YouTube video.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My summary of this talk:&lt;/strong&gt; A good talk on how to arrange your project for creating service apps that run on production servers, but this talk isn't about PyPI or packaging per se.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This talk is on making your Python applications play nicer with other Python code. It’s about platform agility; making your app run the same on your laptop and production servers.&lt;/li&gt;
&lt;li&gt;Write log info to stdout, and let logging software handle it. Don’t log to files directly.&lt;/li&gt;
&lt;li&gt;Use environment variables to pass key-value pairs to other applications. Direnv is a good tool for dealing with this. Envconsul and Etcdenv and Confd, and Consul-Template are other tools.&lt;/li&gt;
&lt;li&gt;Generic network applications should have “HOST” and “PORT” command line args for running the application.&lt;/li&gt;
&lt;li&gt;Don’t use environment variables to store sensitive data like passwords, auth tokens, and “secret” values (ignore The 12 Factor App’s advice on this). It’s really easy for the environment variables to get dumped to log files which are then made public.&lt;/li&gt;
&lt;li&gt;Encode credentials as URLs: “postgresql://user@localhost/db”&lt;/li&gt;
&lt;li&gt;Be sure to handle “sigterm”. Many Python frameworks don’t really handle this. (Really, this is the difference between network applications you make with flask/django and host on heroku or whatever, and my “browser apps” idea.)&lt;/li&gt;
&lt;li&gt;/healthz (Google) and /&lt;strong&gt;heartbeat&lt;/strong&gt; (Mozilla) and /-/ready and /-/readiness (gitlab and other people) are used to give application status info about whether it is up and running or not. General heartbeat stuff.&lt;/li&gt;
&lt;li&gt;/&lt;strong&gt;version&lt;/strong&gt; and /-/metrics and /-/log-level exposes other information. (The /-/ namespacing is kind of nice.)&lt;/li&gt;
&lt;li&gt;Looking at the source of PyPI is a great example of these best practices.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;Asheesh Laroia: Python packaging simplified, for end users, app developers - PyCon 2014&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=eLPiPHr6TVI"&gt;Link to YouTube video.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My summary of this talk:&lt;/strong&gt; Since this talk was made in 2014, a number of things are out of date (egg files instead of wheel files, disutils instead of setuptools, &lt;a href="http://setup.py"&gt;setup.py&lt;/a&gt; upload instead of Twine, etc.). It’s a good talk, but I’d recommend skipping it.&lt;/p&gt;
&lt;p&gt;Asheesh brings up a good point about putting the call to &lt;code&gt;setup()&lt;/code&gt; inside a &lt;code&gt;if __name__ == '__main__':&lt;/code&gt; wrapper.&lt;/p&gt;



&lt;h3&gt;Domen Kožar - Rethinking packaging, development and deployment - PyCon 2015&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=W8A2bOKPtJU"&gt;Link to YouTube video.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My summary of this talk:&lt;/strong&gt; This talk is about the Nix project, which is a build tool that uses a functional programming approach. Since this talk doesn't cover existing standard tools, I'm not sure I'd include it as necessary to learn about the Python packaging ecosystem.&lt;/p&gt;

&lt;p&gt;Nix Package Manager, Nix is a minimalistic functional language and associated tools for building distributions.&lt;/p&gt;
--&gt;

&lt;p&gt;&lt;em&gt;Updated 2021/12/12: New recorded talks added.&lt;/em&gt;&lt;/p&gt;

  </content><category term="misc"></category></entry><entry><title>Writing Bots to Play Zombie Dice</title><link href="https://inventwithpython.com/blog/writing-bots-to-play-zombie-dice.html" rel="alternate"></link><published>2018-10-17T12:00:00-04:00</published><updated>2018-10-17T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2018-10-17:/blog/writing-bots-to-play-zombie-dice.html</id><summary type="html">&lt;p&gt;&lt;em&gt;Programming games&lt;/em&gt; are a games genre where instead of playing the game directly, players write bot programs to play the game autonomously. I've created a Zombie Dice simulator, allowing programmers to practice their skills while having fun making game-playing AIs. Zombie Dice bots can be simple or incredibly complex, and are great for a class exercise or an individual programming challenge.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;&lt;em&gt;Note: I'm looking for feedback for my Zombie Dice simulator, as well as for this set of instructions. If there's any part that you feel could be improved, please email me at &lt;a href="mailto:al@inventwithpython.com"&gt;al@inventwithpython.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Programming games&lt;/em&gt; are a games genre where instead of playing the game directly, players write bot programs to play the game autonomously. I've created a Zombie Dice simulator, allowing programmers to practice their skills while having fun making game-playing AIs. Zombie Dice bots can be simple or incredibly complex, and are great for a class exercise or an individual programming challenge.&lt;/p&gt;

&lt;h2&gt;How to Play Zombie Dice&lt;/h2&gt;

&lt;p&gt;Zombie Dice is a quick, fun dice game from Steve Jackson Games. The players are zombies trying to eat as many human brains as possible without getting "shotgunned". There is a cup of 13 dice with icons of brains, footsteps, and shotguns on their faces. The dice are colored green (brains are more likely), red (shotguns are more likely), and yellow (evenly split between brains and shotguns).&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/zombiedice_dice.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;On their turn:&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;The player randomly pulls dice from the cup until they have three in their hand, and rolls them. Players always roll exactly three dice.&lt;/li&gt;
    &lt;li&gt;They set aside any brains (humans whose brains were eaten) and shotguns (humans who fought back).&lt;/li&gt;
    &lt;li&gt;If the player has set aside three shotguns on this turn, their turn ends with zero points.&lt;/li&gt;
    &lt;li&gt;The player keeps any dice that came up footsteps in their hand if they decide to roll again.&lt;/li&gt;
    &lt;li&gt;Otherwise, the player can choose to end their turn, where they'll collect one point per brain and return all dice to the cup for the next player.&lt;/li&gt;
    &lt;li&gt;Or the player can choose to continue their turn and roll again. Go back to step 1, noting any dice already in hand that came up footsteps will be reused.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Zombie Dice has a "push your luck" game mechanic: the more you re-roll the dice, the more brains you can get but the more likely you'll eventually accrue three shotguns and lose everything. Once a player reaches 13 points, the rest of the players get one more turn (to potentially catch up) and the game ends. The player with the most points wins.&lt;/p&gt;

&lt;p&gt;More complete rules for Zombie Dice can be found here:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;a href="http://www.sjgames.com/dice/zombiedice/img/ZDRules_English.pdf"&gt;PDF of the rules in English&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://www.sjgames.com/dice/zombiedice/demo.html"&gt;Animated Flash demo of how to play&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.instructables.com/id/How-to-play-Zombie-Dice/"&gt;Instructables article with the rules&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=xodehimqCVs"&gt;YouTube video of someone explaining the rules&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Bot Programming Challenge&lt;/h2&gt;

&lt;p&gt;To install the simulator with pip, run &lt;code&gt;pip install zombiedice&lt;/code&gt; (on Windows) or &lt;code&gt;pip3 install zombiedice&lt;/code&gt; (on macOS and Linux). To run a demo with some pre-made bots, run &lt;code&gt;python -m zombiedice&lt;/code&gt; (on Windows) or &lt;code&gt;python3 -m zombiedice&lt;/code&gt; (on macOS and Linux) to see an example of the simulator's web gui:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://github.com/asweigart/zombiedice/raw/master/screenshot.png" /&gt;&lt;/p&gt;

&lt;p&gt;The simulator can quickly run hundreds of games, noting how many wins and ties each bot got.&lt;/p&gt;

&lt;p&gt;You'll create bots by writing a class with a &lt;code&gt;turn()&lt;/code&gt; method, which is called by the simulator when it's your bot's turn. Inside this &lt;code&gt;turn()&lt;/code&gt; method, you'll call the &lt;code&gt;zombiedice.roll()&lt;/code&gt; function as often as you want to roll the dice (though you should call it at least once on your turn, of course). Classes are beyond the scope of this article, but you can simply change the &lt;code&gt;turn()&lt;/code&gt; code in &lt;em&gt;myzombie.py&lt;/em&gt;:&lt;/p&gt;

&lt;pre&gt;import zombiedice

class MyZombie:
    def __init__(self, name):
        self.name = name

    def turn(self, gameState):
        # gameState is a dict with info about the current state of the game.
        # You can choose to ignore it in your code.

        diceRollResults = zombiedice.roll() # first roll
        # roll() returns a dictionary with keys 'brains', 'shotgun', and
        # 'footsteps' with how many rolls of each type there were.
        # The 'rolls' key is a list of (color, icon) tuples with the
        # exact roll result information.
        # Example of a roll() return value:
        # {'brains': 1, 'footsteps': 1, 'shotgun': 1,
        #  'rolls': [('yellow', 'brains'), ('red', 'footsteps'),
        #            ('green', 'shotgun')]}

        # The code logic for your zombie goes here:
        brains = 0
        while diceRollResults is not None:
            brains += diceRollResults['brains']

            if brains &lt; 2:
                diceRollResults = zombiedice.roll() # roll again
            else:
                break

class AlwaysRollsTwicePerTurn:
    def __init__(self, name):
        self.name = name

    def turn(self, gameState):
        zombiedice.roll()
        zombiedice.roll()


zombies = (
    zombiedice.examples.RandomCoinFlipZombie(name='Random'),
    zombiedice.examples.RollsUntilInTheLeadZombie(name='Until Leading'),
    zombiedice.examples.MinNumShotgunsThenStopsZombie(name='Stop at 2 Shotguns', minShotguns=2),
    zombiedice.examples.MinNumShotgunsThenStopsZombie(name='Stop at 1 Shotgun', minShotguns=1),
    MyZombie(name='My Zombie Bot'),
    AlwaysRollsTwicePerTurn(name='Rolls Twice'),
    # Add any other zombie players here.
)

# Uncomment one of the following lines to run in CLI or Web GUI mode:
#zombiedice.runTournament(zombies=zombies, numGames=1000)
zombiedice.runWebGui(zombies=zombies, numGames=1000)&lt;/pre&gt;

&lt;p&gt;The code in the &lt;code&gt;MyZombie&lt;/code&gt; class's &lt;code&gt;turn()&lt;/code&gt; method keeps calling &lt;code&gt;zombiedice.roll()&lt;/code&gt; until it has rolled at least two brains. A second bot is implemented in the &lt;code&gt;AlwaysRollsTwicePerTurn&lt;/code&gt; class, which has a &lt;code&gt;turn()&lt;/code&gt; method that simply rolls the dice twice on every turn. Running this program calls &lt;code&gt;runWebGui()&lt;/code&gt; and brings up the web browser as shown in Figure 6-1, ready to run the simulation. You can examine the source code in the zombiedice package's &lt;em&gt;&lt;a href="https://github.com/asweigart/zombiedice/blob/master/src/zombiedice/examples.py"&gt;examples.py&lt;/a&gt;&lt;/em&gt; file to see how the other bots work.&lt;/p&gt;

&lt;p&gt;Try writing some of your own bots to play Zombie Dice, and see how they compare against the other bots. And if you find yourself playing this game in the real world, you'll have the benefit of thousands of simulated games telling you that one of the best strategies is to simply stop once you've rolled two shotguns. But you could always try pressing your luck...&lt;/p&gt;

&lt;h2&gt;Constructing MyZombie, Step by Step&lt;/h2&gt;

&lt;p&gt;I'll describe the though process of creating the &lt;code&gt;MyZombie&lt;/code&gt; class in the previous section. Start with this basic template that imports the &lt;code&gt;zombiedice&lt;/code&gt; module, creates a class with a &lt;code&gt;turn()&lt;/code&gt; method that calls &lt;code&gt;zombiedice.roll()&lt;/code&gt;, and then runs a tournament:&lt;/p&gt;

&lt;pre&gt;import zombiedice

class MyZombie:
    def __init__(self, name):
        self.name = name

    def turn(self, gameState):
        zombiedice.roll()

zombies = (
    zombiedice.examples.RandomCoinFlipZombie(name='Random'),
    MyZombie(name='My Zombie Bot'),
)

# Uncomment one of the following lines to run in CLI or Web GUI mode:
zombiedice.runWebGui(zombies=zombies, numGames=1000)&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;MyZombie&lt;/code&gt; so far only rolls the dice one, which is a very conservative strategy that is unlikely to get many points. To decide if it should roll again, we'll need to examine the return value of &lt;code&gt;zombiedice.roll()&lt;/code&gt;, which is a dictionary that shows the results of the roll of the three dice. It would look something like this:&lt;/p&gt;

&lt;pre&gt;{'brains': 1, 'footsteps': 1, 'shotgun': 1,
 'rolls': [('yellow', 'brains'), ('red', 'footsteps'),
           ('green', 'shotgun')]}&lt;/pre&gt;

&lt;p&gt;This dictionary has a &lt;code&gt;'brains'&lt;/code&gt;, &lt;code&gt;'footsteps'&lt;/code&gt;, and &lt;code&gt;'shotgun'&lt;/code&gt; key with values that are the number of that kind of roll. The exact rolls are in the &lt;code&gt;'rolls'&lt;/code&gt; key's value: a list of tuples. Each tuple represent a die, composed of a color and the icon that came up.&lt;/p&gt;

&lt;p&gt;Let's say we want to program the strategy that our zombie will keep rolling until it has at least two brains rolled. We would need to keep track of how many brains have been rolled so far on this turn (in a &lt;code&gt;brains&lt;/code&gt; variable), which starts at &lt;code&gt;0&lt;/code&gt;. If a future call to &lt;code&gt;zombiedice.roll()&lt;/code&gt; ever returns &lt;code&gt;None&lt;/code&gt;, then we know that we've reached or exceeded three shotguns on this turn and will get zero points. Update &lt;code&gt;turn()&lt;/code&gt; so that it looks like this:&lt;/p&gt;

&lt;pre&gt;    def turn(self, gameState):
        diceRollResults = zombiedice.roll()
        brains = 0
        while diceRollResults is not None:
            diceRollResults = zombiedice.roll()&lt;/pre&gt;

&lt;p&gt;Now the code will keep rolling the dice until it returns &lt;code&gt;None&lt;/code&gt;. But at that point, we've reached three shotguns and lost the turn. We need to add in logic so that at some point the method returns before &lt;code&gt;zombiedice.roll()&lt;/code&gt; starts returning &lt;code&gt;None&lt;/code&gt;. Let's examine &lt;code&gt;diceRollResults&lt;/code&gt; to see how many brains were rolled:&lt;/p&gt;

&lt;pre&gt;    def turn(self, gameState):
        diceRollResults = zombiedice.roll()
        brains = 0
        while diceRollResults is not None:
            brains += diceRollResults['brains']

            if brains &lt; 2:
                diceRollResults = zombiedice.roll()&lt;/pre&gt;

&lt;p&gt;That's better, now &lt;code&gt;zombiedice.roll()&lt;/code&gt; is only called as long as we've yet to reach two brain rolls on our turn. However, there's no condition where this method changes. It'd just loop forever! Let's add a bit more code:&lt;/p&gt;

&lt;pre&gt;    def turn(self, gameState):
        diceRollResults = zombiedice.roll()
        brains = 0
        while diceRollResults is not None:
            brains += diceRollResults['brains']

            if brains &lt; 2:
                diceRollResults = zombiedice.roll()
            else:
                break&lt;/pre&gt;

&lt;p&gt;This new code says that if we've rolled at least two brains, go ahead and break out of the loop and eventually return from the method, which tells the simulator that the bot is done with its turn.&lt;/p&gt;

&lt;p&gt;The value passed for the &lt;code&gt;gameState&lt;/code&gt; parameter is a dictionary with the following keys:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;code&gt;'CURRENT_ZOMBIE'&lt;/code&gt; - string of the zombie whose turn it is currently&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;'CURRENT_CUP'&lt;/code&gt; - list of dice strings (i.e. 'red', 'yellow', 'green') in the cup&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;'CURRENT_HAND'&lt;/code&gt; - list of dice being rolled (should always be length of three)&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;'SHOTGUNS_ROLLED'&lt;/code&gt; - number of shotguns rolled so far on this turn&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;'BRAINS_ROLLED'&lt;/code&gt; - number of brains rolled so far on this turn&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;'ROLLED_BRAINS_DETAILS'&lt;/code&gt; - list of dice strings for each brain rolled, used in the rare event we run out of brain dice&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;'TURN_START_TIME'&lt;/code&gt; - time since unix epoch that the current turn began&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Ideas for Other Bots&lt;/h2&gt;

&lt;p&gt;There's plenty of strategies you can try in Zombie Dice. Try writing bots that do the following:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;A bot that has a 50% chance of choosing to re-roll. See how this bot compares against a bot with a 25% or 75% chance to re-roll.&lt;/li&gt;
    &lt;li&gt;A bot that stops re-rolling when it reaches two shotguns.&lt;/li&gt;
    &lt;li&gt;A bot that stops re-rolling when it reaches two shotguns, unless it isn't in the lead. (Check the &lt;code&gt;gameState&lt;/code&gt; dictionary to find this information.)&lt;/li&gt;
    &lt;li&gt;A bot that rolls as long as there are green dice left in the cup. (Check the &lt;code&gt;gameState&lt;/code&gt; dictionary to find this information.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Be sure to check out the &lt;em&gt;&lt;a href="https://github.com/asweigart/zombiedice/blob/master/src/zombiedice/examples.py"&gt;examples.py&lt;/a&gt;&lt;/em&gt; file in the module for the code to some bots that have already been made. Then load them up in the simulator and see how they perform!&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>PyOhio 2018 Recursion Tutorial</title><link href="https://inventwithpython.com/blog/pyohio-2018-recursion-tutorial.html" rel="alternate"></link><published>2018-08-24T12:00:00-04:00</published><updated>2018-08-24T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2018-08-24:/blog/pyohio-2018-recursion-tutorial.html</id><summary type="html">&lt;p&gt;This page has materials for folks taking my two-hour tutorial, &lt;a href="https://www.pyohio.org/2018/schedule/presentation/40/"&gt;A Beginner's Guide to Tackling Recursion&lt;/a&gt; at PyOhio 2018 or following its video recording.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;This page has materials for folks taking my two-hour tutorial, &lt;a href="https://www.pyohio.org/2018/schedule/presentation/40/"&gt;A Beginner's Guide to Tackling Recursion&lt;/a&gt; at PyOhio 2018 or following its video recording.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=L7x0OUN1Aws"&gt;You can watch the recorded video of this tutorial on YouTube.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: I don't think I did a particularly good job with this tutorial. &lt;a href="https://www.youtube.com/watch?v=AfBqVVKg4GE"&gt;You may want to watch my North Bay Python talk on recursion instead.&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;Pre-Tutorial Setup&lt;/h1&gt;

&lt;p&gt;To take this tutorial, you'll need:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;A computer with Python installed.&lt;/li&gt;
    &lt;li&gt;To know how to launch the interactive shell, so you can follow along with the examples.&lt;/li&gt;
    &lt;li&gt;To know how to launch a text editor, so you can save and run .py files.&lt;/li&gt;
    &lt;li&gt;A beginner's level understanding of Python, including variables, loops, defining functions, lists, and dictionaries. That's it, really.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's strongly recommended that you type the code as you follow along with this tutorial. This helps build the "muscle memory" of coding, and is much more effective for learning than simple watching the tutorial.&lt;/p&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion-handout.html"&gt;Handout form&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="/blogstatic/recursion-handout2.html"&gt;(Filled out handout form)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="/blogstatic/pyohio-recursion.odp"&gt;Slides&lt;/a&gt;&lt;/p&gt;

&lt;!--
&lt;p&gt;Here are the handouts for the class. They'll be available at the tutorial, but if you are watching the video its recommended that you print them out so you can fill them out with pen or pencil. Writing down the answers is more effective than simply reading the questions and thinking about the answers.&lt;/p&gt;

&lt;p&gt;(Link will be posted when the video is uploaded.)&lt;a href=""&gt;Download Handouts&lt;/a&gt;--&gt;&lt;/p&gt;

&lt;h1&gt;Objectives&lt;/h1&gt;

&lt;p&gt;By the end of this tutorial, you'll learn:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;What recursion is, in terms of actual code.&lt;/li&gt;
    &lt;li&gt;How recursion isn't a difficult so much as a poorly taught one.&lt;/li&gt;
    &lt;li&gt;What are stacks and the callstack, and how does recursion use them?&lt;/li&gt;
    &lt;li&gt;What is a stack overflow, and how does a base case prevent it?&lt;/li&gt;
    &lt;li&gt;What are recursive and base cases?&lt;/li&gt;
    &lt;li&gt;How recursive algorithms can't do anything that an iterative solution can do.&lt;/li&gt;
    &lt;li&gt;How caching/memoization and tail call elimination prevent common problems in recursive algorithms.&lt;/li&gt;
    &lt;li&gt;What are the types of problems where recursion is better than iteration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;Code Examples&lt;/h1&gt;

&lt;p&gt;Not all of the programs in this git repo are featured in the tutorial, but you can find code examples here: &lt;a href="https://github.com/asweigart/recursion_examples"&gt;https://github.com/asweigart/recursion_examples&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It isn't necessary to review this code before the tutorial.&lt;/p&gt;

&lt;p&gt;The visualization tool used to explore these programs is &lt;a href="http://pythontutor.com/"&gt;http://pythontutor.com/&lt;/a&gt;&lt;/p&gt;



</content><category term="misc"></category></entry><entry><title>The Zen of Python, Explained</title><link href="https://inventwithpython.com/blog/zen-of-python-explained.html" rel="alternate"></link><published>2018-08-17T12:00:00-04:00</published><updated>2018-08-17T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2018-08-17:/blog/zen-of-python-explained.html</id><summary type="html">&lt;p&gt;&lt;em&gt;The Zen of Python&lt;/em&gt; by Tim Peters are 20 guidelines for the design of the Python language. Your Python code doesn’t necessarily have to follow these guidelines, but they’re good to keep in mind. The Zen of Python is an &lt;em&gt;Easter egg&lt;/em&gt;, or hidden joke, that appears if you run &lt;code&gt;import this&lt;/code&gt;.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;&lt;em&gt;The Zen of Python&lt;/em&gt; by Tim Peters are 20 guidelines for the design of the Python language. Your Python code doesn’t necessarily have to follow these guidelines, but they’re good to keep in mind. The Zen of Python is an &lt;em&gt;Easter egg&lt;/em&gt;, or hidden joke, that appears if you run &lt;code&gt;import this&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&gt;&gt;&gt; import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!&lt;/pre&gt;

&lt;blockquote&gt;&lt;em&gt;NOTE: Mysteriously, only 19 of the guidelines are written down. Guido van Rosum reportedly said that the missing 20th aphorism is “some bizarre Tim Peters in-joke.”&lt;/em&gt;&lt;/blockquote&gt;

&lt;p&gt;In the end, these guidelines are opinions that can be argued for or against. Like all good sets of moral codes, they sometimes contradict themselves to provide the most flexibility. Here’s my own interpretation of these aphorisms:&lt;/p&gt;

&lt;h3&gt;Beautiful is better than ugly.&lt;/h3&gt;

&lt;p&gt;Programmers often write code quickly without concern for readability. While code doesn’t have to be readable, the code of the Python language itself must be thought out, consistent, and a joy to use. Of course, not every script needs to be beautiful, and beauty is subjective, but much of Python’s popularity is a result of being so easy to work with.&lt;/p&gt;

&lt;h3&gt;Explicit is better than implicit.&lt;/h3&gt;

&lt;p&gt;“This aphorism is self-explanatory,” that would be a terrible explanation for any aphorism. Similarly, it’s better for code to be verbose and explicit. You should avoid hiding code’s functionality behind obscure language features that require familiarity to fully understand.&lt;/p&gt;

&lt;h3&gt;Simple is better than complex. Complex is better than complicated.&lt;/h3&gt;

&lt;p&gt;These two aphorisms remind us that building anything can be done using simple or complex techniques. With a simple problem, such as building a birdhouse, a simple solution is better. Building a diesel train engine, on the other hand, is a complex problem that requires complex techniques. Even if you could technically make a diesel train engine using birdhouse techniques, you probably would end up with a complicated, Rube Goldberg arrangement of birdhouse parts that wouldn’t be an ideal solution. Prefer simplicity to complexity, but know the limits of simplicity.&lt;/p&gt;

&lt;h3&gt;Flat is better than nested.&lt;/h3&gt;

&lt;p&gt;Programmers love to organize things into categories, especially categories that contain subcategories which contain other sub-subcategories. These hierarchies often don’t add &lt;em&gt;organization&lt;/em&gt; so much as they add &lt;em&gt;bureaucracy&lt;/em&gt;. It’s okay to have code in just one top-layer module or class instead of split up across multiple submodules or subclasses. If you make packages and modules that require code like &lt;code&gt;import spam.eggs.bacon.ham.foo.bar&lt;/code&gt;, then you’re making your code too complicated.&lt;/p&gt;

&lt;h3&gt;Sparse is better than dense.&lt;/h3&gt;

&lt;p&gt;Programmers often like to cram as much functionality into as little code as possible, such as one-liners like the following: &lt;code&gt;print('\n'.join("%i bytes = %i bits which has %i possible values." % (j, j*8, 256**j-1) for j in (1 &lt;&lt; i for i in range(8))))&lt;/code&gt;. While code like this may impress their friends, it’ll infuriate their coworkers who have to try to understand it. Code that is spread out over multiple lines is often easier to read than dense one-liners.&lt;/p&gt;

&lt;h3&gt;Readability counts.&lt;/h3&gt;

&lt;p&gt;While &lt;code&gt;strcmp()&lt;/code&gt; may obviously mean the “string compare” function to someone who has been programming in C since the 1970s, modern computers have enough memory to write out the full function name. Don’t drop vowels from your names or write overly terse code. Code is read more often than it’s written, so explicit, readable code is more important than terse, undocumented code.

&lt;h3&gt;Special cases aren’t special enough to break the rules. Although practicality beats purity.&lt;/h3&gt;

&lt;p&gt;These two aphorisms, which come as a set, contradict each other. Programming is full of “best practices” that programmers should strive for in their code. Skirting these practices for a quick hack may be tempting, but can lead to a rat’s nest of inconsistent, unreadable code. However, bending over backwards to adhere to rules can result in highly-abstract, unreadable code. The Java programming language’s attempt to fit all code to its object-oriented paradigm often results in lots of boilerplate code for even the smallest program. Walking the line between these two aphorisms becomes easier with experience. And in time, you’ll not only learn the rules, but also learn when to break them.&lt;/p&gt;

&lt;h3&gt;Errors should never pass silently. Unless explicitly silenced.&lt;/h3&gt;

&lt;p&gt;Just because programmers often ignore error messages doesn’t mean the program should stop emitting them. Silent errors can happen when functions return error codes or &lt;code&gt;None&lt;/code&gt; instead of raising exceptions. These two aphorisms tell us that it’s better for a program to &lt;code&gt;fail fast&lt;/code&gt; and crash than to silence the error and continue running the program. The bugs that inevitably happen later on will be harder to debug since they are far removed from the original cause. Though you can always choose to explicitly ignore the errors your programs cause, just be sure you are making the conscious choice to do so.&lt;/p&gt;

&lt;h3&gt;In the face of ambiguity, refuse the temptation to guess.&lt;/h3&gt;

&lt;p&gt;Computers have made humans superstitious: To exorcise the demons in our computers we perform the sacred ritual of turning them off and then on. Supposedly this will fix any mysterious problem. However, computers are not magic. If your code isn’t working, there is a reason and only careful, critical thinking will solve it. Refuse the temptation to blindly try solutions until something seems to work; often you have merely masked the problem rather than solved it.

&lt;h3&gt;There should be one—and preferably only one—obvious way to do it.&lt;/h3&gt;

&lt;p&gt;This is a broadside against the Perl programming language’s motto, “There’s more than one way to do it!” It turns out that having three or four different ways to write code that does the same thing is a double-edged sword: you have flexibility in how you write code, but now you have to learn every possible way it could have been written in order to read it. This flexibility isn’t worth the 3x or 4x effort needed to learn a programming language.&lt;/p&gt;

&lt;h3&gt;Although that way may not be obvious at first unless you’re Dutch.&lt;/h3&gt;

&lt;p&gt;This line is a joke. Guido van Rossum, the creator and &lt;em&gt;BDFL&lt;/em&gt; (Benevolent Dictator for Life) of Python, is Dutch. However, not even this aphorism prevented Python from incorporating three different ways of formatting strings.&lt;/p&gt;

&lt;h3&gt;Now is better than never. Although never is often better than *right* now.&lt;/h3&gt;

&lt;p&gt;These two aphorisms tell us that code that hangs or gets caught in infinite loops is obviously worse than code that doesn’t. However, it’s almost certainly better to wait for your program to finish than to have it finish too early with incorrect results.&lt;/p&gt;

&lt;h3&gt;If the implementation is hard to explain, it’s a bad idea. If the implementation is easy to explain, it may be a good idea.&lt;/h3&gt;

&lt;p&gt;Python strives to make the programmer’s job easier rather than accommodate the computer so a program runs faster. And programs need to be understandable not just by the programmer who wrote it, but also by other programmers who maintain the code. These two aphorisms remind us that if “high-performance” code is so complicated as to be impossible for programmers to understand and debug, then it’s bad code. But alas, just because it’s easy to explain a program’s code to someone else doesn’t mean it isn’t bad code. Programming is hard.&lt;/p&gt;

&lt;h3&gt;Namespaces are one honking great idea—let’s do more of those!&lt;/h3&gt;

&lt;p&gt;Namespaces (and also global and local scopes) are key for preventing names in one module or scope from conflicting with names in another. But also remember that &lt;em&gt;flat is better than nested&lt;/em&gt;: As great as they are, namespaces should be made only to prevent naming conflicts, and not to add needless categorization.&lt;/p&gt;

&lt;p&gt;Like all opinions about programming, the ones I’ve written here can be argued against or may simply be irrelevant to your situation. Arguing over how code should be written is rarely as productive as you think it is. (Unless you’re writing an entire book full of programming opinions.)&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Python's Fake Increment and Decrement Operators</title><link href="https://inventwithpython.com/blog/pythons-fake-increment-and-decrement-operators.html" rel="alternate"></link><published>2018-05-21T12:00:00-04:00</published><updated>2018-05-21T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2018-05-21:/blog/pythons-fake-increment-and-decrement-operators.html</id><summary type="html">&lt;p&gt;In Python, you can increase the value of a variable by &lt;code&gt;1&lt;/code&gt; or reduce it by &lt;code&gt;1&lt;/code&gt; using the augmented assignment operators. The code &lt;code&gt;spam += 1&lt;/code&gt; and &lt;code&gt;spam -= 1&lt;/code&gt; increments and decrements the numeric values in &lt;code&gt;spam&lt;/code&gt; by &lt;code&gt;1&lt;/code&gt;, respectively.&lt;/p&gt;

&lt;p&gt;Other languages such as C++ and Java have the &lt;code&gt;++&lt;/code&gt; and &lt;code&gt;--&lt;/code&gt; operators for incrementing and decrementing variables. (The name of C++ itself reflects this; the name is a tongue-in-cheek joke that indicates it's an enhanced form of the C language.) Code in C++ and Java could have &lt;code&gt;++spam&lt;/code&gt; or &lt;code&gt;spam++&lt;/code&gt;. Python wisely doesn't include these operators; &lt;a href="https://softwareengineering.stackexchange.com/q/59880"&gt;they are notoriously susceptible to subtle bugs.&lt;/a&gt;&lt;/p&gt;</summary><content type="html">


&lt;p&gt;In Python, you can increase the value of a variable by &lt;code&gt;1&lt;/code&gt; or reduce it by &lt;code&gt;1&lt;/code&gt; using the augmented assignment operators. The code &lt;code&gt;spam += 1&lt;/code&gt; and &lt;code&gt;spam -= 1&lt;/code&gt; increments and decrements the numeric values in &lt;code&gt;spam&lt;/code&gt; by &lt;code&gt;1&lt;/code&gt;, respectively.&lt;/p&gt;

&lt;p&gt;Other languages such as C++ and Java have the &lt;code&gt;++&lt;/code&gt; and &lt;code&gt;--&lt;/code&gt; operators for incrementing and decrementing variables. (The name of C++ itself reflects this; the name is a tongue-in-cheek joke that indicates it's an enhanced form of the C language.) Code in C++ and Java could have &lt;code&gt;++spam&lt;/code&gt; or &lt;code&gt;spam++&lt;/code&gt;. Python wisely doesn't include these operators; &lt;a href="https://softwareengineering.stackexchange.com/q/59880"&gt;they are notoriously susceptible to subtle bugs.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, it is perfectly legal to have the following Python code:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; spam = 42
&amp;gt;&amp;gt;&amp;gt; spam = ++spam
&amp;gt;&amp;gt;&amp;gt; spam
42
&amp;gt;&amp;gt;&amp;gt; spam = --spam
&amp;gt;&amp;gt;&amp;gt; spam
42&lt;/pre&gt;

&lt;p&gt;The first thing you notice is that the &lt;code&gt;++&lt;/code&gt; and &lt;code&gt;--&lt;/code&gt; "operators" in Python don't actually increment or decrement the value in &lt;code&gt;spam&lt;/code&gt;. Rather, the leading &lt;code&gt;-&lt;/code&gt; is Python's unary negation operator. It allows you to have code like this:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; spam = 42
&amp;gt;&amp;gt;&amp;gt; print(-spam)
-42&lt;/pre&gt;

It's legal to have multiple unary negative operators in front of a value. With two of them, you'd get the negative of the negative of the value, which for integer values just evaluates to the original value:

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; print(--spam)
42
&amp;gt;&amp;gt;&amp;gt; --42
42&lt;/pre&gt;

&lt;p&gt;This is a quite silly thing to do, and you won't ever see a unary negation operator used twice in real-world code. (Though if you did, it's probably because the programmer learned to program in another language has just written buggy Python code!) There is also a &lt;code&gt;+&lt;/code&gt; unary operator. It evaluates an integer value to the same sign as the original value, which is to say, it does absolutely nothing:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; +42
42
&amp;gt;&amp;gt;&amp;gt; spam = -42
&amp;gt;&amp;gt;&amp;gt; print(+spam) # +spam is not the same as abs(spam)
-42&lt;/pre&gt;

&lt;p&gt;Being able to write &lt;code&gt;+42&lt;/code&gt; (or &lt;code&gt;++42&lt;/code&gt;) seems just as silly as &lt;code&gt;--42&lt;/code&gt;, so why does Python even have this unary operator? It exists only to complement the &lt;code&gt;–&lt;/code&gt; operator if you need to overload these operators for your own classes. (See the documentation for the &lt;code&gt;__pos__&lt;/code&gt; special method.)&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;+&lt;/code&gt; and &lt;code&gt;–&lt;/code&gt; unary operators are only valid when in front of a Python value, not after it. While &lt;code&gt;spam++&lt;/code&gt; and &lt;code&gt;spam--&lt;/code&gt; might be legal code in C++ or Java, they produce syntax errors in Python:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; spam++
  File "&amp;lt;stdin&amp;gt;", line 1
    spam++
         ^
SyntaxError: invalid syntax&lt;/pre&gt;

&lt;p&gt;Python doesn't have increment and decrement operators, it's only a quirk of the langauge syntax that can make it seem like it does.&lt;/p&gt;


</content><category term="misc"></category></entry><entry><title>Python Tuples are Immutable, Except When They're Mutable</title><link href="https://inventwithpython.com/blog/python-tuples-are-immutable-except-when-theyre-mutable.html" rel="alternate"></link><published>2018-02-05T12:00:00-05:00</published><updated>2018-02-05T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2018-02-05:/blog/python-tuples-are-immutable-except-when-theyre-mutable.html</id><summary type="html">&lt;p&gt;Before we can get a nuanced answer to &lt;em&gt;"Are tuples mutable or immutable?"&lt;/em&gt;, we need some background information.&lt;/p&gt;</summary><content type="html">

    &lt;h2&gt;Prologue&lt;/h2&gt;

    &lt;blockquote&gt;&lt;p&gt;'When I use a word,' Humpty Dumpty said, in rather a scornful tone, 'it means just what I choose it to mean — neither more nor less.'&lt;/p&gt;

    &lt;p&gt;'The question is,' said Alice, 'whether you can make words mean so many different things.'&lt;/p&gt;

    &lt;p&gt;'The question is,' said Humpty Dumpty, 'which is to be master — that's all.'&lt;/p&gt;&lt;/blockquote&gt;

    &lt;h2&gt;Are Tuples Mutable or Immutable?&lt;/h2&gt;

    &lt;p&gt;In Python, tuples are immutable, and "immutable" means the value cannot change. These are well-known, basic facts of Python. While tuples are more than just immutable lists (as Chapter 2 of Luciano Ramalho's excellent &lt;a href="https://www.amazon.com/gp/product/1491946008/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1491946008&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=f03271c8ba828c5896fa0c029f680117"&gt;"Fluent Python"&lt;/a&gt; explains), there is a bit of ambiguity here. &lt;a href="https://standupdev.com/wiki/doku.php?id=python_tuples_are_immutable_but_may_change"&gt;Luciano has written up a great blog post&lt;/a&gt; on this topic as well.&lt;/p&gt;

    &lt;p&gt;Before we can get a nuanced answer to &lt;em&gt;"Are tuples mutable or immutable?"&lt;/em&gt;, we need some background information.&lt;/p&gt;

    &lt;h2&gt;Some Background Information&lt;/h2&gt;

    &lt;p&gt;According to the &lt;a href="https://docs.python.org/3/reference/datamodel.html"&gt;Python data model&lt;/a&gt;, "objects are Python's abstraction for data, and all data in a Python program is represented by objects or by relations between objects". Every value in Python is an object, including integers, floats, and Booleans. In Java, these are "primitive data types" and considered separate from "objects". Not so, in Python. Every value in Python is an object, and Ned Batchelder's fantastic PyCon 2015 talk &lt;a href="https://www.youtube.com/watch?v=_AEJHKGk9ns"&gt;Facts and Myths About Python Names and Values"&lt;/a&gt; goes into more detail about this.&lt;/p&gt;

    &lt;p&gt;So not only is the &lt;code&gt;datetime.datetime(2018, 2, 4, 19, 38, 54, 798338)&lt;/code&gt; datetime object an object, but the integer &lt;code&gt;42&lt;/code&gt; is an object and the Boolean &lt;code&gt;True&lt;/code&gt; is an object.&lt;/p&gt;

    &lt;p&gt;All Python objects have three things: a value, a type, and an identity. This is a bit confusing, because we often casually say, for example, "the value &lt;code&gt;42&lt;/code&gt;", although &lt;code&gt;42&lt;/code&gt; is also called an object which itself has a value. But never mind that, let's continue with our &lt;code&gt;42&lt;/code&gt; example. Enter the following into the interactive shell:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; spam = 42
&amp;gt;&amp;gt;&amp;gt; spam
42
&amp;gt;&amp;gt;&amp;gt; type(spam)
&amp;lt;class 'int'&amp;gt;
&amp;gt;&amp;gt;&amp;gt; id(spam)
1594282736&lt;/pre&gt;

    &lt;p&gt;The variable &lt;code&gt;spam&lt;/code&gt; refers to an object that has a value of &lt;code&gt;42&lt;/code&gt;, a type of &lt;code&gt;int&lt;/code&gt;, and an identity of &lt;code&gt;1594282736&lt;/code&gt;. An identity is a unique integer, created when the object is created, and never changes for the lifetime of the object. An object's type also cannot change. Only the value of an object may change.&lt;/p&gt;

    &lt;p&gt;Let's try changing an object's value by entering the following into the interactive shell:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; spam = 42
&amp;gt;&amp;gt;&amp;gt; spam = 99&lt;/pre&gt;

    &lt;p&gt;You may think you've changed the object's value from &lt;code&gt;42&lt;/code&gt; to &lt;code&gt;99&lt;/code&gt;, but you haven't. All you've done is made &lt;code&gt;spam&lt;/code&gt; refer to a new object. You can confirm this by calling the &lt;code&gt;id()&lt;/code&gt; function and noticing &lt;code&gt;spam&lt;/code&gt; refers to a completely new object:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; spam = 42
&amp;gt;&amp;gt;&amp;gt; id(spam)
1594282736
&amp;gt;&amp;gt;&amp;gt; spam = 99
&amp;gt;&amp;gt;&amp;gt; id(spam)
1594284560&lt;/pre&gt;

    &lt;p&gt;Integers (and floats, Booleans, strings, frozen sets, and bytes) are immutable; their value doesn't change. Lists (and dictionaries, sets, arrays, and bytearrays), on the other hand, are mutable. This can lead to a common Python gotcha:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; spam = ['dogs', 'cats']
&amp;gt;&amp;gt;&amp;gt; eggs = spam  # copies the reference, not the list

&amp;gt;&amp;gt;&amp;gt; spam
['dogs', 'cats']
&amp;gt;&amp;gt;&amp;gt; eggs
['dogs', 'cats']

&amp;gt;&amp;gt;&amp;gt; spam.append('moose') # modifies the list referred by spam
&amp;gt;&amp;gt;&amp;gt; spam
['dogs', 'cats', 'moose']
&amp;gt;&amp;gt;&amp;gt; eggs
['dogs', 'cats', 'moose']&lt;/pre&gt;

    &lt;p&gt;The reason &lt;code&gt;eggs&lt;/code&gt; has changed even though we only appended a value to &lt;code&gt;spam&lt;/code&gt; is because &lt;code&gt;spam&lt;/code&gt; and &lt;code&gt;eggs&lt;/code&gt; refer to the same object. The &lt;code&gt;eggs = spam&lt;/code&gt; line made a copy of the reference, not the object. (You can use the &lt;a href="https://docs.python.org/3/library/copy.html"&gt;&lt;code&gt;copy&lt;/code&gt; module's &lt;code&gt;copy()&lt;/code&gt; or &lt;code&gt;deepcopy()&lt;/code&gt; functions&lt;/a&gt; if you want to copy a list object.)&lt;/p&gt;

    &lt;p&gt;The &lt;a href="https://docs.python.org/3/glossary.html#term-immutable"&gt;glossary in Python's official documentation says this about "immutable"&lt;/a&gt; (emphasis mine):&lt;/p&gt;

    &lt;blockquote&gt;"An object with a fixed value. Immutable objects include numbers, strings and &lt;strong&gt;tuples&lt;/strong&gt;. Such an object cannot be altered. &lt;strong&gt;A new object has to be created if a different value has to be stored.&lt;/strong&gt;"&lt;/blockquote&gt;

    &lt;p&gt;The official Python documentation (and every other book, tutorial, and StackOverflow answer I've found) describes tuples as immutable. Conversely, &lt;a href="https://docs.python.org/3/glossary.html#term-mutable"&gt;the glossary says this about "mutable"&lt;/a&gt;:&lt;/p&gt;

    &lt;blockquote&gt;"Mutable objects can change their value but keep their &lt;code&gt;id()&lt;/code&gt;. See also &lt;a href="https://docs.python.org/3/glossary.html#term-immutable"&gt;immutable&lt;/a&gt;."&lt;/blockquote&gt;

    &lt;p&gt;Let's move on to discuss how value and identity affect the &lt;code&gt;==&lt;/code&gt; and &lt;code&gt;is&lt;/code&gt; operators.&lt;/p&gt;

    &lt;h2&gt;&lt;code&gt;==&lt;/code&gt; vs &lt;code&gt;is&lt;/code&gt;&lt;/h2&gt;

    &lt;p&gt;The &lt;code&gt;==&lt;/code&gt; equality operator compares values, while the &lt;code&gt;is&lt;/code&gt; operator compares identities. You can consider &lt;code&gt;x is y&lt;/code&gt; to be shorthand for &lt;code&gt;id(x) == id(y)&lt;/code&gt;. Enter the following into the interactive shell:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; spam = ['dogs', 'cats']
&amp;gt;&amp;gt;&amp;gt; id(spam)
41335048
&amp;gt;&amp;gt;&amp;gt; eggs = spam
&amp;gt;&amp;gt;&amp;gt; id(eggs)
41335048
&amp;gt;&amp;gt;&amp;gt; id(spam) == id(eggs)
True
&amp;gt;&amp;gt;&amp;gt; spam is eggs  # spam and eggs are the same object
True
&amp;gt;&amp;gt;&amp;gt; spam == eggs  # spam and eggs have the same value, naturally
True
&amp;gt;&amp;gt;&amp;gt; spam == spam  # Just like spam and spam are the same object and have the same value, naturally
True

&amp;gt;&amp;gt;&amp;gt; bacon = ['dogs', 'cats']
&amp;gt;&amp;gt;&amp;gt; spam == bacon  # spam and bacon have the same value
True
&amp;gt;&amp;gt;&amp;gt; id(bacon)
40654152
&amp;gt;&amp;gt;&amp;gt; id(spam) == id(bacon)
False
&amp;gt;&amp;gt;&amp;gt; spam is bacon  # spam and bacon are different objects
False&lt;/pre&gt;

    &lt;p&gt;Two different objects may share the same value, but they will never share the same identity.&lt;/p&gt;

    &lt;h2&gt;Hashability&lt;/h2&gt;

    &lt;p&gt;According to the glossary in the official Python documentation, &lt;a href="https://docs.python.org/3/glossary.html#term-hashable"&gt;&lt;em&gt;"An object is hashable if it has a hash value which never changes during its lifetime"&lt;/em&gt;&lt;/a&gt;, that is, if the object is immutable. (There are a couple other requirements concerning the &lt;code&gt;__hash__()&lt;/code&gt; and &lt;code&gt;__eq__()&lt;/code&gt; special methods, but that's beyond the scope of this post.)&lt;/p&gt;

    &lt;p&gt;A hash is an integer that depends on an object's value, and objects with the same value &lt;strong&gt;always&lt;/strong&gt; have the same hash. (Objects with different values will occasionally have the same hash too. This is called a &lt;a href="https://stackoverflow.com/a/17586126/1893164"&gt;&lt;em&gt;hash collision&lt;/em&gt;&lt;/a&gt;.) While &lt;code&gt;id()&lt;/code&gt; will return an integer based on an object's identity, the &lt;code&gt;hash()&lt;/code&gt; function will return an integer (the object's hash) based on the hashable object's value:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; hash('dogs')
-4064183437113369969

&amp;gt;&amp;gt;&amp;gt; hash(True)
1

&amp;gt;&amp;gt;&amp;gt; spam = ('hello', 'goodbye')
&amp;gt;&amp;gt;&amp;gt; eggs = ('hello', 'goodbye')
&amp;gt;&amp;gt;&amp;gt; spam == eggs  # spam and eggs have the same value
True
&amp;gt;&amp;gt;&amp;gt; spam is eggs  # spam and eggs are different objects with different identities
False
&amp;gt;&amp;gt;&amp;gt; hash(spam)
3746884561951861327
&amp;gt;&amp;gt;&amp;gt; hash(eggs)
3746884561951861327
&amp;gt;&amp;gt;&amp;gt; hash(spam) == hash(eggs)  # spam and eggs have the same hash
True&lt;/pre&gt;


        &lt;p&gt;&lt;strong&gt;Immutable objects can be hashable, mutable objects can't be hashable.&lt;/strong&gt; This is important to know, because (for reasons beyond the scope of this post) &lt;a href="https://stackoverflow.com/questions/17585730/what-does-hash-do-in-python"&gt;only hashable objects can be used as keys in a dictionary or as items in a set&lt;/a&gt;. Since hashes are based on values and only immutable objects can be hashable, this means that hashes will never change during the object's lifetime.&lt;/p&gt;

        &lt;p&gt;In the interactive shell, try to create a dictionary with immutable objects for keys by entering the following:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; spam = {'dogs': 42, True: 'hello', ('a', 'b', 'c'): ['hello']}
&amp;gt;&amp;gt;&amp;gt; spam.keys()
dict_keys(['dogs', True, ('a', 'b', 'c')])&lt;/pre&gt;

        &lt;p&gt;All the keys in &lt;code&gt;spam&lt;/code&gt; are immutable, hashable objects. If you try to call &lt;code&gt;hash()&lt;/code&gt; on a mutable object (such as a list), or try to use a mutable object for a dictionary key, you'll get an error:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; spam = {['hello', 'world']: 42}
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
TypeError: unhashable type: 'list'

&amp;gt;&amp;gt;&amp;gt; d = {'a': 1}
&amp;gt;&amp;gt;&amp;gt; spam = {d: 42}
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
TypeError: unhashable type: 'dict'&lt;/pre&gt;


    &lt;p&gt;Tuples, being immutable objects, can be used as dictionary keys:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; spam = {('a', 'b', 'c'): 'hello'}&lt;/pre&gt;

    &lt;p&gt;...or can they?:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; spam = {('a', 'b', [1, 2, 3]): 'hello'}
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
TypeError: unhashable type: 'list'&lt;/pre&gt;

    &lt;p&gt;It seems that if a tuple contains a mutable object, it can't be hashed. (&lt;a href="https://stackoverflow.com/a/9756028/1893164"&gt;Raymond Hettinger explains, with lots of context, why immutable tuples can contain mutable values.&lt;/a&gt;) This fits with what we know: immutable objects &lt;em&gt;can&lt;/em&gt; be hashable, but this doesn't necessarily mean they're &lt;em&gt;always&lt;/em&gt; hashable. And remember, the hash is derived from the object's value.&lt;/p&gt;

    &lt;p&gt;This is an interesting corner case: a tuple (which should be immutable) that contains a mutable list cannot be hashed. This is because the hash of the tuple depends on the tuple's value, but if that list's value can change, that means the tuple's value can change and therefore the hash can change during the tuple's lifetime. But if we go back to &lt;a href="https://stackoverflow.com/questions/17585730/what-does-hash-do-in-python"&gt;the glossary definition of hashable&lt;/a&gt;, a hash is never supposed to change during the object's lifetime.&lt;/p&gt;

    &lt;p&gt;Does this mean that the value of tuples can change? Are tuples mutable?&lt;/p&gt;


    &lt;h2&gt;So, Are Tuples Mutable or Immutable?&lt;/h2&gt;

    &lt;p&gt;Before we can finally answer this question, we should ask, "Is mutability a property of data types or of objects?"&lt;/p&gt;

    &lt;p&gt;(Update: &lt;a href="https://mail.python.org/pipermail/python-dev/2018-February/152117.html"&gt;Our BDFL generally thinks it's of data types.&lt;/a&gt;)&lt;/p&gt;

    &lt;p&gt;Python programmers often say things like "strings are immutable, lists are mutable", which makes us think that mutability is a property of types: all string objects are immutable, all list objects are mutable, etc. And in general, I agree.&lt;/p&gt;

    &lt;p&gt;But in the previous section, we saw how &lt;em&gt;some&lt;/em&gt; tuples are hashable (implying they're immutable) but &lt;em&gt;some other&lt;/em&gt; tuples aren't hashable (implying they're mutable).&lt;/p&gt;

    &lt;p&gt;Let's go back to the official Python documentation's definition of immutable and mutable: &lt;a href="https://docs.python.org/3/glossary.html#term-immutable"&gt;"An object with a fixed value"&lt;/a&gt; and &lt;a href="https://docs.python.org/3/glossary.html#term-mutable"&gt;"Mutable objects can change their value"&lt;/a&gt;, respectively.&lt;/p&gt;

    &lt;p&gt;So perhaps mutability is a property of objects, and some tuples (that contain only immutable objects) are immutable and some other tuples (that contain one or more mutable objects) are mutable. But every Pythonista I've run into says and continues to say that tuples are immutable, even if they're unhashable. Why?&lt;/p&gt;

    &lt;p&gt;&lt;strong&gt;In one sense,&lt;/strong&gt; tuples are immutable because the objects in a tuple cannot be deleted or replaced by new objects. Just like &lt;code&gt;spam = 42; spam = 99&lt;/code&gt; doesn't change the &lt;code&gt;42&lt;/code&gt; object in spam; it replaces it with an entirely new object, &lt;code&gt;99&lt;/code&gt;. If we use the interactive shell to look at a tuple that contains a list:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; spam = ('dogs', 'cats', [1, 2, 3])
&amp;gt;&amp;gt;&amp;gt; id(spam[0]), id(spam[1]), id(spam[2])
(41506216, 41355896, 40740488)&lt;/pre&gt;

    &lt;p&gt;The same objects will always be in this tuple, and they will always have the same identities in the same order: 41506216, 41355896, and 40740488. &lt;strong&gt;&lt;em&gt;Tuples are immutable.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;&lt;strong&gt;However in another sense,&lt;/strong&gt; tuples are mutable because their values can be changed. Enter the following into the interactive shell:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; a = ('dogs', 'cats', [1, 2, 3])
&amp;gt;&amp;gt;&amp;gt; b = ('dogs', 'cats', [1, 2, 3])
&amp;gt;&amp;gt;&amp;gt; a == b
True
&amp;gt;&amp;gt;&amp;gt; a is b
False&lt;/pre&gt;

    &lt;p&gt;In this example, the tuples referred to by &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; have equal values (according to &lt;code&gt;==&lt;/code&gt;) but are different objects (according to &lt;code&gt;is&lt;/code&gt;). Let's change the list in &lt;code&gt;a&lt;/code&gt;'s tuple:&lt;/p&gt;

&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; a[2].append(99)
&amp;gt;&amp;gt;&amp;gt; a
('dogs', 'cats', [1, 2, 3, 99])
&amp;gt;&amp;gt;&amp;gt; a == b
False&lt;/pre&gt;

    &lt;p&gt;We have changed the &lt;code&gt;a&lt;/code&gt;'s value. We must have, because &lt;code&gt;a&lt;/code&gt; is no longer equal to &lt;code&gt;b&lt;/code&gt; and we didn't change &lt;code&gt;b&lt;/code&gt;'s value. &lt;strong&gt;&lt;em&gt;Tuples are mutable.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

    &lt;h2&gt;Epilogue&lt;/h2&gt;

    &lt;blockquote&gt;&lt;p&gt;'The question is,' said Humpty Dumpty, 'which is to be master — that's all.'&lt;/p&gt;&lt;/blockquote&gt;

    &lt;p&gt;Humans are the masters of words, not the other way around. Words are invented by humans to convey ideas to other humans. Personally, I'm going to continue to say that tuples are immutable, because this is more accurate than not and the most helpful term in most contexts.&lt;/p&gt;

    &lt;p&gt;However, at the same time we should also be aware that from our definitions of "mutable" and "value", we can see that tuples' values can sometimes change, i.e. mutate. It isn't technically wrong to say that tuples can be mutable, though it will certainly raise eyebrows and require more explanation.&lt;/p&gt;

    &lt;p&gt;Thank you for sitting through my, what turned out to be lengthy, explanation on tuples and mutability. I certainly learned a lot in writing this post, and hope I conveyed it to you as well.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>How To Ask For Programming Help</title><link href="https://inventwithpython.com/blog/how-to-ask-for-programming-help.html" rel="alternate"></link><published>2018-02-02T12:00:00-05:00</published><updated>2018-02-02T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2018-02-02:/blog/how-to-ask-for-programming-help.html</id><summary type="html">&lt;p&gt;I often get emails say little more than &lt;strong&gt;"Why doesn't my program work?"&lt;/strong&gt; Often I can't answer them; either I don't have enough information about your problem or you're asking me about something I don't know anything about.&lt;/p&gt;</summary><content type="html">

&lt;p&gt;Hello! If you've asked for programming help and then someone gave you a link to this page, you probably need to give more information about your problem. In the next section I explain why I'm so slow to reply (or often don't reply at all) but you can &lt;a href="#howtoaskprogrammingquestions"&gt;skip directly to instructions for how to ask programming questions.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Why I Didn't Reply To Your Email Yet (or Ever)&lt;/h2&gt;

&lt;p&gt;I often get emails say little more than &lt;strong&gt;"Why doesn't my program work?"&lt;/strong&gt; Often I can't answer them; either I don't have enough information about your problem or you're asking me about something I don't know anything about.&lt;/p&gt;

&lt;p&gt;I like helping people learn to code. My books, videos, and online courses (most of which are free) have taught tens of thousands of people how to program. This is great! I'm amazed that the internet lets me cheaply share knowledge. I can reach more people in a few years online than a lifetime at the front of a classroom. But when people send me emails, &lt;em&gt;I can only help one person at a time.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This article teaches you how to ask &lt;em&gt;effective&lt;/em&gt; programming questions.&lt;/p&gt;

&lt;h2&gt;&lt;a name="howtoaskprogrammingquestions"&gt;&lt;/a&gt;Is Email the Best Way to Ask Your Programming Question?&lt;/h2&gt;

&lt;p&gt;These are bad programming questions:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;"Why doesn't my program work? I tried so many ways but no luck. Can you please help me out?" &lt;em&gt;(What is your program? What's it supposed to do, and what's it doing instead?)&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;"Sometimes my program works, but sometimes I get an error when I run it." &lt;em&gt;(What error? What's different between when you get it and when you don't?)&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;"My program can't find the file." &lt;em&gt;(What file? What does your code look like?)&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;"My program doesn't work. Can you help me?" &lt;em&gt;(Don't ask to ask, just ask.)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They're bad because they don't have enough information for your helper to answer them in one reply. In email conversations, there's a lot of waiting in between emails. &lt;strong&gt;To answer your question effectively, we need to cut down the amount of back-and-forth.&lt;/strong&gt; The best way to do that is to not use email at all! Run through this checklist &lt;em&gt;before&lt;/em&gt; clicking Send:&lt;/p&gt;

&lt;blockquote&gt;Before you email, consider posting the problem to &lt;a href="https://www.reddit.com/r/inventwithpython"&gt;the /r/inventwithpython subreddit&lt;/a&gt; or &lt;a href="https://stackoverflow.com"&gt;StackOverflow&lt;/a&gt;.&lt;/blockquote&gt;

&lt;p&gt;The &lt;a href="https://www.reddit.com/r/inventwithpython"&gt;/r/inventwithpython&lt;/a&gt; subreddit can answer your questions about code in my books faster than I can reply to your email.&lt;/p&gt;

&lt;p&gt;StackOverflow is good for general programming questions, but read the short &lt;a href="https://stackoverflow.com/help/how-to-ask"&gt;How do I ask a good question?&lt;/a&gt; page first. (This page is also good to read for anyone asking for programming help in general.)&lt;/p&gt;

&lt;blockquote&gt;Before you email, google your question. If you got an error message, copy and paste the exact error message into google.&lt;/blockquote&gt;

&lt;p&gt;You're not the first person to encounter this error, and someone else has probably answered your questions already. In this case, you'll get an answer much faster by searching the web for it.&lt;/p&gt;

&lt;blockquote&gt;Before you email, learn to use the debugger, and use the debugger.&lt;/blockquote&gt;

&lt;p&gt;A debugger lets you run through your code one step at a time, and examine what values are in your variables. &lt;a href="https://automatetheboringstuff.com/chapter10/"&gt;IDLE comes with a debugger&lt;/a&gt;, and you can also always use the pdb debugger module. A debugger saves you much more time than it takes to learn how to use a debugger.&lt;/p&gt;

&lt;h2&gt;Put As Much Informataion in Your First Email As Possible&lt;/h2&gt;

&lt;p&gt;The next best way to cut down on the back-and-forth is to &lt;strong&gt;put as much information as possible in your first email.&lt;/strong&gt; Go through the following checklist:&lt;/p&gt;

&lt;blockquote&gt;In your email, don't ask to ask. Just ask.&lt;/blockquote&gt;

&lt;p&gt;You don't have to say "I have a question about (something). Can you help me with this?" It isn't rude to just ask your programming question if you've taken the time to include all the information your helper needs to answer it.&lt;/p&gt;

&lt;blockquote&gt;In your email, use a descriptive subject line.&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/help/how-to-ask"&gt;&lt;strong&gt;Pretend you're talking to a busy colleague&lt;/strong&gt; and have to sum up your entire question in one sentence.&lt;/a&gt; Don't use "I need help" or "Can I ask you a question?" These subjects don't describe your question. I have dozens of emails like these, and it's hard to re-find an email later on.

&lt;p&gt;If you are posting your question somewhere online, descriptive post titles make it easier for search engines to guide future people with your same question to your post (and the followup answers!)&lt;/p&gt;

&lt;p&gt;It's rude to use a vague, demanding headline like, "HELP!!! My program keeps CRASHING. WHY DOESN'T ANYONE ANSWER ME?!?!?!1" Please don't do that.&lt;/p&gt;

&lt;blockquote&gt;In your email, show that you've done research.&lt;/blockquote&gt;

&lt;p&gt;Add something like, "I googled for (terms) and found a StackOverflow post at (url), but I don't understand what this person means when they say (something)." This shows that you respect your helper's time and tried to answer it yourself first. It also gives your helpers some leads to go off of.&lt;/p&gt;

&lt;blockquote&gt;In your email, don't just say what your program is doing, but &lt;em&gt;also&lt;/em&gt; what the overall goal of your program. Make sure your questions have a question mark!&lt;/blockquote&gt;

&lt;p&gt;Emails like "I'm trying download a web page using Requets, but I'm getting stuck while return json format." aren't actually asking a question: There's no question mark! Your helper can't assume what the problem is or what you want your program to do: What do you mean by "getting stuck?" What web page are you downloading and why? What would your program specifically be doing if it was working properly?&lt;/p&gt;

&lt;p&gt;With context, your helper can figure out if you're on the wrong path entirely. Maybe this is a problem you don't even need to solve!&lt;/p&gt;

&lt;blockquote&gt;In your email, copy and paste the &lt;strong&gt;full&lt;/strong&gt; error message. If there is no error message, explicitly say that there was no error message.&lt;/blockquote&gt;

&lt;p&gt;Python error messages will look something like this:&lt;/p&gt;

&lt;pre&gt;Traceback (most recent call last):
File "test1.py", line 18, in &amp;lt;module&amp;gt;
spam()
File "test1.py", line 16, in spam
bar()
File "test1.py", line 13, in bar
foo()
File "test1.py", line 4, in foo
int(x)
ValueError: invalid literal for int() with base 10: 'twelve'&lt;/pre&gt;

&lt;p&gt;Your helper needs &lt;strong&gt;all&lt;/strong&gt; of this information, especially the line numbers! The line numbers tell your helper exactly where the error is happening. Just telling your helper, "I got SyntaxError" doesn't tell them enough to know what's wrong.&lt;/p&gt;

&lt;blockquote&gt;In your email, send a pastebin link to the full source code of your program.&lt;/blockquote&gt;

&lt;p&gt;Your helper needs the &lt;strong&gt;complete, full&lt;/strong&gt; source code to figure out what's wrong. Go to a "pastebin" site like &lt;a href="https://pastebin.com/"&gt;https://pastebin.com/&lt;/a&gt; or &lt;a href="https://gist.github.com/"&gt;https://gist.github.com/&lt;/a&gt;. This way, your helper can run your program themselves.&lt;/p&gt;

&lt;p&gt;Do &lt;strong&gt;not&lt;/strong&gt; copy and paste the source code into the email itself. Blank lines at the top of the code will throw off the line numbers in the error message, and many email programs will get rid of the indentation, which renders the code unrunnable.&lt;/p&gt;

&lt;p&gt;Do &lt;strong&gt;not&lt;/strong&gt; send a screenshot of the code (or take a photo of your screen with your smartphone). Your helper often needs to run the code to figure out what's wrong, and they can't copy and paste the code from the image. It's rude to expect them to retype all of your code because you didn't want to.&lt;/p&gt;

&lt;blockquote&gt;In your email, say what operating system, operating system version, and version of Python you are using.&lt;/blockquote&gt;

&lt;p&gt;There are minor but possibly important differences between operating systems and Python versions. Telling me, "I'm running Python 3.6.2 on Windows 10" helps narrow down what could be causing your problem.&lt;/p&gt;

&lt;blockquote&gt;In your email, if you have a question about something in one of my books, tell me which book, chapter, and page number you were reading.&lt;/blockquote&gt;

&lt;p&gt;I've written several books, so "I can't get the program in your book to work" doesn't tell me which book and which program.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Going through this checklist makes it much more likely that you'll get a quick answer. &lt;strong&gt;It's always better to include too much information than not enough.&lt;/strong&gt; Providing more information helps me help you. And posting to an online forum like &lt;a href="https://www.reddit.com/r/inventwithpython"&gt;the /r/inventwithpython subreddit&lt;/a&gt; allows many people to answer your question while creating a page that people in the future with a similar problem can find.&lt;/p&gt;

&lt;p&gt;Good luck, and keep coding!&lt;/p&gt;

&lt;h3&gt;PS&lt;/h3&gt;

&lt;p&gt;A common question I get from readers is "what should I study/read/learn next?" If you've read &lt;a href="https://automatetheboringstuff.com"&gt;Automate the Boring Stuff with Python&lt;/a&gt;, you might want to read &lt;a href="https://inventwithpython.com"&gt;my other books for free online&lt;/a&gt;. I also recommend &lt;a href="https://nostarch.com/pythoncrashcourse"&gt;Python Crash Course&lt;/a&gt; (you can skip directly to the second half if you're comfortable with Python) and &lt;a href="https://www.amazon.com/gp/product/1491946008/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1491946008&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=f03271c8ba828c5896fa0c029f680117"&gt;Fluent Python&lt;/a&gt; and &lt;a href="https://www.amazon.com/gp/product/0134034287/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0134034287&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=40b1da5c8883167fab51a2706373562d"&gt;Effective Python&lt;/a&gt;. If you want practice writing simple programs, try the practice problems on &lt;a href="http://exercism.io/languages/python/about"&gt;Exercism.io&lt;/a&gt; or the &lt;a href="https://www.reddit.com/r/dailyprogrammer/"&gt;/r/dailyprogrammer subreddit.&lt;/p&gt;
</content><category term="misc"></category></entry><entry><title>Lessons Learned from Creating a Udemy Course</title><link href="https://inventwithpython.com/blog/lessons-learned-from-creating-a-udemy-course.html" rel="alternate"></link><published>2017-04-05T12:00:00-04:00</published><updated>2017-04-05T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2017-04-05:/blog/lessons-learned-from-creating-a-udemy-course.html</id><summary type="html">&lt;p&gt;This post contains the lessons I've learned and my process for creating video tutorials, and I hope others who want to do the same find it useful. While this post is specific to Udemy and programming courses, the same lessons probably apply to all online courses.&lt;/p&gt;</summary><content type="html">

    &lt;p&gt;I've created two online courses through &lt;a href="https://udemy.com"&gt;Udemy.com&lt;/a&gt; for my books. I initially created them to promote my books, but have actually made significant income from the courses themselves:&lt;/p&gt;

    &lt;ul&gt;
        &lt;li&gt;&lt;a href="https://www.udemy.com/automate/?couponCode=FOR_LIKE_TEN_BUCKS"&gt;Automate the Boring Stuff with Python (use this link for 80% off)&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href="https://www.udemy.com/scratch-game-programming/?couponCode=INVENT_WITH_SCRATCH"&gt;Scratch Programming Playground (use this link to sign up for free)&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;This post contains the lessons I've learned and my process for creating video tutorials, and I hope others who want to do the same find it useful. While this post is specific to Udemy and programming courses, the same lessons probably apply to all online courses.&lt;/p&gt;

    &lt;h2&gt;The TL;DR&lt;/h2&gt;

    &lt;ul&gt;
    &lt;li&gt;If you want to sell your course, you're limited to Udemy.&lt;/li&gt;
    &lt;li&gt;If you want to make a free course, post it to Udemy as well as YouTube.&lt;/li&gt;
    &lt;li&gt;Don't expect to make enough money to justify it as your job.&lt;/li&gt;
    &lt;li&gt;Don't expect to make any significant amount of money at all.&lt;/li&gt;
    &lt;li&gt;Almost all of your paying students bought your course through a huge discount, not the original price.&lt;/li&gt;
    &lt;li&gt;Your primary way of pushing your course is offering large discount codes that reduce the price to $10 or $15.&lt;/li&gt;
    &lt;li&gt;It'll take on the order of months to create a course.&lt;/li&gt;
    &lt;li&gt;Do several takes for each video, but record each of them as though they were the final take.&lt;/li&gt;
    &lt;li&gt;I prefer practicing the lines doing multiple takes over writing and reading off of a script.&lt;/li&gt;
    &lt;li&gt;I use &lt;a href="https://www.techsmith.com/camtasia.html"&gt;Camtasia Studio&lt;/a&gt; ($200) to record my screen and edit the video.&lt;/li&gt;
    &lt;li&gt;I use &lt;a href="http://www.audacityteam.org/download/"&gt;Audacity&lt;/a&gt; (free) and the &lt;a href="http://wiki.audacityteam.org/wiki/Nyquist_Effect_Plug-ins#Noise_Gate"&gt;Noise Gate plugin&lt;/a&gt; (free) for post-production of the audio.&lt;/li&gt;
    &lt;li&gt;I use a &lt;a href="https://www.amazon.com/gp/product/B002VA464S/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B002VA464S&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=085ab00e80f7e01c4d50fd568eb46f8c"&gt;Yeti USB microphone&lt;/a&gt; ($130) and &lt;a href="https://www.amazon.com/gp/product/B006JH8T3S/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B006JH8T3S&amp;amp;linkId=111e96e83deadf1a29f2c8f5b1ffe587"&gt;Logitech c920 webcam&lt;/a&gt; ($65) because they're the best tradeoff between quality and cost.&lt;/li&gt;
    &lt;li&gt;You don't need to use a webcam to add a "talking head" to your videos.&lt;/li&gt;
    &lt;li&gt;You don't need to spend much money on your bedroom recording studio. Thick blankets hanging from ceiling hooks work well enough for sound-proofing.&lt;/li&gt;
    &lt;li&gt;Lower your screen resolution to 1280 x 720 so that video is quicker to stream and the text isn't too small to read.&lt;/li&gt;
    &lt;li&gt;Do a quick test video before each recording session. Make sure your mute button isn't on!&lt;/li&gt;
    &lt;li&gt;For the first couple of weeks, make your course free with discount codes. This builds the initial audience and reviews, which gives your course credibility.&lt;/li&gt;
    &lt;li&gt;Don't worry about people pirating your course to watch it for free.&lt;/li&gt;
    &lt;li&gt;Worry about people pirating your course and selling it themselves.&lt;/li&gt;
    &lt;/ul&gt;

    &lt;h2&gt;Which Site to Choose for Your Course&lt;/h2&gt;

    &lt;p&gt;You're pretty much limited to &lt;a href="https://udemy.com"&gt;Udemy.com&lt;/a&gt; if you want to charge for your course. &lt;a href="https://www.edx.org/"&gt;EdX&lt;/a&gt;, &lt;a href="https://www.coursera.org/"&gt;Coursera&lt;/a&gt;, and most online course sites unless you're a professor affiliated with a university. Even if you're creating a free course to host on &lt;a href="https://youtube.com"&gt;YouTube&lt;/a&gt; or &lt;a href="https://vimeo.com"&gt;Vimeo&lt;/a&gt;, Udemy can be an additional place to put your videos. The Udemy content requirements are good guidelines for YouTube and Vimeo videos anyway.&lt;/p&gt;

    &lt;p&gt;&lt;img src="/blogstatic/udemytips/automate_udemy_course_page.png" alt="Automate course on Udemy" /&gt;&lt;/p&gt;

    &lt;p&gt;You could host the videos yourself, though you'd have to create the website, handle payments, and do all your own promotion. I recommend against this, especially for your first course, unless you know what you're doing.&lt;/p&gt;

    &lt;h2&gt;How Much Money You'll Make and How&lt;/h2&gt;

    &lt;p&gt;The &lt;em&gt;Automate&lt;/em&gt; course, as of April 2017, has over 46,000 students signed up. Many of these students signed up for free with discount codes I provided. Most paying students will buy the course after being referred by a Udemy promotion with a substantial discount code. Through this promotion, no matter if you set your course for $50 or $500, these students probably paid $10 or $15 for the course. &lt;strong&gt;This is normal.&lt;/strong&gt; Udemy will pay you about $1 or $2 for the students it signs up for your course. I didn't realize Udemy was selling my course at a discount at first. But I don't complain because most of my course's income comes from Udemy discount promotions. Don't worry about the steep discount and (generally) trust that Udemy knows how to sell your course.&lt;/p&gt;

    &lt;p&gt;Udemy, like a department store, is always running sales. I've paid for courses on the site myself, and can tell you that you should never pay full price for a course. &lt;strong&gt;Almost no one ends up buying your course at the original full price.&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;You can (and most definitely should) create discount codes. Udemy pays you almost the full price (less a dollar or two) for students who sign up using your discount code. If a student signs up for your $100 course using a 90% discount code you provided, Udemy will pay you about $8 or $9 of that $10. Again, this is normal. But it's also a great incentive to advertise your own course. I always push the discount code that lowers the price of my $50 course to $10, hence the &lt;a href="https://www.udemy.com/automate/?couponCode=FOR_LIKE_TEN_BUCKS"&gt;FOR_LIKE_TEN_BUCKS&lt;/a&gt; discount code.&lt;/p&gt;

    &lt;p&gt;Again, even though I could make a lot more money per student by referring them myself, most of my Udemy income comes from students that Udemy found just because they find so many for me.&lt;/p&gt;

    &lt;p&gt;I'm really pleased that my Automate course has done so well: most courses get very few students to the point where it's really not worth the time to create the course. It'll get you beer money, but not much else. My course gets me a 4-figure sum per month (before taxes). Not nearly as much as I could make as a software developer, but enough to keep me from living entirely off savings.&lt;/p&gt;

    &lt;p&gt;I put a lot of hard work into my Automate course, but also I got lucky with it. On the other hand, my &lt;a href="https://www.udemy.com/scratch-game-programming/?couponCode=INVENT_WITH_SCRATCH"&gt;Scratch Programming Playground&lt;/a&gt; course only got me about $170 last month. And there's no telling what the popularity of my courses will be next year, or two years from now, or five years from now.&lt;/p&gt;

    &lt;h2&gt;The Course's Videos&lt;/h2&gt;

    &lt;p&gt;The Automate course took about four months to create its 50 videos totaling 9.5 hours. (Factor in that I originally wrote the course's content for the &lt;a href="https://automatetheboringstuff.com"&gt;Automate book&lt;/a&gt;.) But it has definitely paid off considering that I now have it as a passive income stream and only need to answer maybe a half dozen student questions each week. This was my full-time job, but I had other projects too. If I focused only on creating the course, it could probably have been done in two or three months.&lt;/p&gt;

    &lt;p&gt;My videos are screencasts, where I use software to record the screen while I do a voice-over explanation. This format works well for programming courses: The students can see the code that I type into an interactive shell and hear me explain how it works.&lt;/p&gt;

    &lt;p&gt;&lt;img src="/blogstatic/udemytips/screencast_screen.png" alt="Screenshot of Automate course on Udemy" /&gt;&lt;/p&gt;

    &lt;p&gt;You don't necessarily need a script, but:&lt;/p&gt;

    &lt;ul&gt;
        &lt;li&gt;Write down a &lt;strong&gt;detailed&lt;/strong&gt; outline that you can consult while recording.&lt;/li&gt;
        &lt;li&gt;Expect to do three or four takes for each video. I'm almost never satisfied with the first take.&lt;/li&gt;
        &lt;li&gt;If you do practice takes, record them anyway as if you were doing a real take. There's something about having it recorded that makes me treat the take more seriously.&lt;/li&gt;
        &lt;li&gt;When you do a practice take, do the entire video from start to finish just as you would do for a real take.&lt;/li&gt;
        &lt;li&gt;Rather than immediately edit the video after recording it, I record several videos at a time, then edit several videos at time. It takes me a while to get comfortable recording, and I like to keep the ball rolling once I'm warmed up.&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;The most important thing about your videos is that they are concise and get to the point. The optimal video length is apparently &lt;a href="http://blog.edx.org/optimal-video-length-student-engagement"&gt;&lt;strong&gt;6 minutes&lt;/strong&gt;&lt;/a&gt;. Udemy caps individual videos at 20 minutes. Most of my Automate course videos were between 9 and 13 minutes.&lt;/p&gt;

    &lt;p&gt;For my Python course, I also sometimes made use of &lt;a href="http://pythontutor.com/"&gt;pythontutor.com&lt;/a&gt;'s visualization tool. Upload a program (it can't have third-party modules, text input, or file access) and it will create a debugger-like trace for each step of your program. To visualize the evaluation of individual expressions, I use my own &lt;a href="https://github.com/asweigart/showeval"&gt;ShowEval&lt;/a&gt; tool.&lt;/p&gt;

    &lt;h2&gt;Recording Software&lt;/h2&gt;

    &lt;p&gt;I'm on Windows 7 and I use &lt;a href="https://www.techsmith.com/camtasia.html"&gt;Camtasia Studio&lt;/a&gt; (available for Windows and Mac) to record the screen and edit the videos. It can also record from a webcam and place it in your videos. &lt;a href="http://shop.techsmith.com/store/techsm/en_US/buy/productID.289432000"&gt;Camtasia costs $200&lt;/a&gt; ($170 for students and educators), though if leave your order in your shopping cart for a few days, they'll email you a discount to encourage you to complete the purchase. (This trick works on other websites, though not Amazon.)&lt;/p&gt;

    &lt;p&gt;&lt;a href="http://camstudio.org/"&gt;CamStudio&lt;/a&gt; for Windows is free but fairly bare bones. It only records the screen so you'll need separate video editing software.&lt;/p&gt;

    &lt;p&gt;You'll also want to download the free &lt;a href="http://www.audacityteam.org/download/"&gt;Audacity&lt;/a&gt; software and the &lt;a href="http://wiki.audacityteam.org/wiki/Nyquist_Effect_Plug-ins#Noise_Gate"&gt;Noise Gate plugin&lt;/a&gt;. While Camtasia's noise reduction is okay, the noise gate plugin will detect periods where there is minimal sound (i.e. when you pause your speaking) and replace it with dead silence. This is a large improvement over just the noise reduction algorithm.&lt;/p&gt;

    &lt;h2&gt;Recording Hardware&lt;/h2&gt;

    &lt;p&gt;I have the following in my amateur bedroom recording studio:&lt;/p&gt;

    &lt;ul&gt;
    &lt;li&gt;&lt;a href="https://www.amazon.com/gp/product/B002VA464S/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B002VA464S&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=085ab00e80f7e01c4d50fd568eb46f8c"&gt;Yeti USB microphone&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.amazon.com/gp/product/B00ACFAULC/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B00ACFAULC&amp;amp;linkId=48b72637ff4daeb77ea9c148bbc472e8"&gt;Pop filter&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.amazon.com/gp/product/B01MUL9ZC6/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B01MUL9ZC6&amp;amp;linkId=fe494241840a7eb78133ff4427c533f1"&gt;Mic stand that attaches to the desk&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.amazon.com/gp/product/B006JH8T3S/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B006JH8T3S&amp;amp;linkId=111e96e83deadf1a29f2c8f5b1ffe587"&gt;Logitech c920 webcam&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.amazon.com/gp/product/B00YEMKJE2/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B00YEMKJE2&amp;amp;linkId=b5a29c51d558360266f8f4505c6b30bf"&gt;100w equivalent cool light bulbs&lt;/a&gt; in cheap &lt;a href="https://www.amazon.com/gp/product/B01E9IY6US/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B01E9IY6US&amp;amp;linkId=ffc026f73d730b8e5e470a35b042fcfb"&gt;clamp lamps&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.amazon.com/gp/product/B001PCQTDO/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B001PCQTDO&amp;amp;linkId=ae52e9e07f23bb0039a99ca179179eba"&gt;A cheap green screen&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;I record with a &lt;a href="https://www.amazon.com/gp/product/B002VA464S/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B002VA464S&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=085ab00e80f7e01c4d50fd568eb46f8c"&gt;Blue Yeti USB Microphone&lt;/a&gt; and a &lt;a href="https://www.amazon.com/gp/product/B00ACFAULC/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B00ACFAULC&amp;amp;linkId=48b72637ff4daeb77ea9c148bbc472e8"&gt;pop filter&lt;/a&gt; over it. For convenience, I bought a &lt;a href="https://www.amazon.com/gp/product/B01MUL9ZC6/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B01MUL9ZC6&amp;amp;linkId=fe494241840a7eb78133ff4427c533f1"&gt;microphone stand&lt;/a&gt; that attaches to my desk. It frees me from having to awkwardly stack books to lift the microphone closer to my mouth. Don't record from your laptop's built-in microphone; it picks up the vibrations from your CPU fan and the audio quality is simply unacceptable for an online course.&lt;/p&gt;

    &lt;p&gt;If you want to do a talking-head recording with a webcam, your laptop's built-in webcam is not high-quality enough (and the low camera angle isn't flattering). The standard USB webcam that streamers use is the &lt;a href="https://www.amazon.com/gp/product/B006JH8T3S/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B006JH8T3S&amp;amp;linkId=111e96e83deadf1a29f2c8f5b1ffe587"&gt;Logitech c920&lt;/a&gt; which runs about $65. Cheaper webcams tend to be no better than your built-in webcam, more expensive webcams are more quality than you need. Although the newer &lt;a href="https://www.amazon.com/gp/product/B01LXCDPPK/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B01LXCDPPK&amp;amp;linkId=88106271908612080ea0e8c63c65f48c"&gt;Logitech c922x&lt;/a&gt; is nicer for $95 (they're compared in &lt;a href="http://www.pcworld.com/article/3126008/consumer-electronics/logitechs-c922-webcam-is-the-revered-c920s-vastly-upgraded-successor.html"&gt;this PC World article&lt;/a&gt;).&lt;/p&gt;

    &lt;p&gt;You'll also need lighting and a green screen. You can spend a lot of time researching these topics, but I've found that getting 100 watt equivalent, "cool" bulbs (with blue/white-ish light) such as &lt;a href="https://www.amazon.com/gp/product/B00YEMKJE2/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B00YEMKJE2&amp;amp;linkId=b5a29c51d558360266f8f4505c6b30bf"&gt;this 4-pack from Phillips&lt;/a&gt; works for me. You don't want to use "warm" yellow/orange-ish light bulbs.&lt;/p&gt;

    &lt;p&gt;A standing lamp or cheap &lt;a href="https://www.amazon.com/gp/product/B01E9IY6US/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B01E9IY6US&amp;amp;linkId=ffc026f73d730b8e5e470a35b042fcfb"&gt;clamp lamps with reflectors&lt;/a&gt; are all you need. There's no such thing as a high-quality clamp lamp: the cheap ones will do fine. Also, don't bother buying a photography light reflector: &lt;a href="http://www.diyphotography.net/1-5-5-minutes-reflector-use/"&gt;you can make one out of cardboard, aluminum foil, and tape that works just as well&lt;/a&gt;. Photo reflectors are nice if you don't want to shine the lamps directly on yourself but instead shine the lamp on the reflector and catch the reflected light.&lt;/p&gt;

    &lt;p&gt;Remember to shave and wash your face before recording it.&lt;/p&gt;

    &lt;p&gt;For a green screen, I bought a &lt;a href="https://www.amazon.com/gp/product/B001PCQTDO/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B001PCQTDO&amp;amp;linkId=ae52e9e07f23bb0039a99ca179179eba"&gt;cheap one off Amazon that is little more than a green bed sheet&lt;/a&gt;. The creases need to be ironed out, but your video editor's green screen feature will work well enough even if it isn't perfect. You'll need a few more lights to prevent shadows from falling on the green screen.&lt;/p&gt;

    &lt;p&gt;Unless you're setting up a dedicated room to act as a studio, you don't necessarily need to buy &lt;a href="https://www.amazon.com/gp/product/B00TP7C9YY/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=as2&amp;amp;creativeASIN=B00TP7C9YY&amp;amp;linkId=4de685571bdf36c0f7d485b371963eae"&gt;foam sound-proofing tiles&lt;/a&gt;. My solution was to get some used duvets from the thrift store, poke some zip ties through one edge and form a loop, and then hang them from ceiling hooks. I have two duvets to form a sort of cylindrical blanket fort around my bedroom desk that I can take down when I'm done recording.&lt;/p&gt;

    &lt;p&gt;&lt;img src="/blogstatic/udemytips/blanket_fort.jpg" /&gt;&lt;/p&gt;

    &lt;p&gt;I didn't really notice the echo when before I had this setup, but I do notice a slight improvement when I compare recordings with and without them. I'd recommend at least some kind of sound proofing for everyone.&lt;/p&gt;

    &lt;p&gt;Connect your laptop to a monitor. This second screen won't be recorded for the screencast, but can hold notes or web pages you want to look at while recording.&lt;/p&gt;

    &lt;h2&gt;Scripts, Speaking, and Recording&lt;/h2&gt;

    &lt;p&gt;I favor doing multiple repeat takes to practice my delivery, as opposed to writing a script. Scripts take time to write, and while you might be able to do the recording in one take you could end up sounding robotic by reading off a script. Multiple takes can be tiresome, but the final take usually sounds more natural. I almost never pull footage from different takes for the single final video.&lt;/p&gt;

    &lt;p&gt;You'll come up with ideas and rewordings while doing a take, so a script would need rewrites anyway. Think of each video as a short speech you'll give: you don't need to memorize every word, but you should have all the topics you want to hit memorized.&lt;/p&gt;

    &lt;p&gt;Position the mic close to your mouth. The Yeti mic is a side address microphone, so have it facing you rather than pointed at you.&lt;/p&gt;

    &lt;p&gt;&lt;img src="/blogstatic/udemytips/do-desktop-large.jpg" /&gt;&lt;/p&gt;

    &lt;p&gt;Read the rest of Blue's &lt;a href="http://www.bluemic.com/yeti-support/"&gt;Yeti microphone FAQ&lt;/a&gt;. Do a quick test recording to make sure it sounds okay and is not muted. (More than once I've finished an entire take, then realized my laptop's mute setting was on the whole time.) Also, between your laptop's built-in microphone, the webcam's microphone, and the Yeti microphone, make sure that you have the Yeti microphone selected as the audio source before you record.&lt;/p&gt;

    &lt;p&gt;It's better to speak slowly than quickly. Udemy, YouTube, and other video sites often have video speed-up options. A normal conversation speed is best for videos. Keep in mind that many of your students won't speak English as their first language.&lt;/p&gt;

    &lt;p&gt;Do a few tongue twisters before recording to get into a presenter mindset. You don't want to be low-energy sounding. I find it helps to smile while speaking. If you aren't recording your face, open your mouth wider than usual to keep yourself from muttering. Keep a glass of water nearby, but not on the desk. Make sure it won't spill on anything if you accidentally knock it over.&lt;/p&gt;

    &lt;p&gt;I lower my screen resolution to 1280 x 720 (720p) to record. Compared to my laptop's normal 1920 x 1080 resolution, this makes the video files smaller, quicker to download, and fit on more screens (including phones). People watching on 1920 x 1080 screens can have your video open but also fit other windows on their screen simultaneously. But most important for programming screencasts, it makes text larger and easier to read. Sometimes I'll increase the text editor's font size as well.&lt;/p&gt;

    &lt;p&gt;Don't make videos composed of just a PowerPoint presentation. A screencast is much more information-rich. It's one thing to say "compile the program", and another to actually see someone navigate menus and click buttons to start the compilation process.&lt;/p&gt;

    &lt;p&gt;If I get flustered while recording, I just take a quick 10 or 20 seconds to compose myself. It's easy to find these silent pauses in the video editor and cut them.&lt;/p&gt;

    &lt;p&gt;Here's the pre-recording checklist I run through:&lt;/p&gt;

    &lt;ul&gt;
    &lt;li&gt;Close instant messaging programs that could have pop-ups while recording.&lt;/li&gt;
    &lt;li&gt;Make sure the Yeti microphone is selected as the audio source.&lt;/li&gt;
    &lt;li&gt;Have a glass of water nearby.&lt;/li&gt;
    &lt;li&gt;If I'm recording my face: shave and wash my face.&lt;/li&gt;
    &lt;li&gt;Do some tongue twister warm ups.&lt;/li&gt;
    &lt;li&gt;Make sure the mic is unmuted.&lt;/li&gt;
    &lt;li&gt;Position the mic close to your mouth.&lt;/li&gt;
    &lt;li&gt;Set screen resolution to 1280 x 720.&lt;/li&gt;
    &lt;li&gt;Do a quick test recording to make sure the audio sounds okay.&lt;/li&gt;
    &lt;li&gt;Smile!&lt;/li&gt;
    &lt;/ul&gt;

    &lt;h2&gt;Promotion&lt;/h2&gt;

    &lt;p&gt;The worst approach to take is protectively caging up your course. &lt;strong&gt;A paywall is biggest wall to getting paid.&lt;/strong&gt; If you want to make money, you need to start by giving your course out for free to build up a respectable number of students and reviews. I'm always hesitant to buy courses that only have a couple hundred students and a dozen reviews. You won't get paying students until a lot of students have vetted your course.&lt;/p&gt;

    &lt;p&gt;Announce your course on social media and Reddit with a free code to generate initial interest. &lt;strong&gt;Don't offer just a discount for this promotion: make it entirely free.&lt;/strong&gt; A 90%-off discount still looks like spam. Be sure to note that this free code is for a limited time and make it sound personal like, "I've finished my new Python course and made it free this week with code THANKS_REDDIT". You'll probably get people messaging you after the period asking if they could also get a coupon. Be generous and generate a coupon for them, along with something like "If you like the course, please do rate it." Don't make it sound like you're demanding a positive review in exchange for the code.&lt;/p&gt;

    &lt;p&gt;Be generous with how many videos you make available for free previews. Of the 50 videos in my Automate course, I have the first 15 free to view (along with some other random videos later in the course). Also make these videos available on a YouTube channel, with links back to the Udemy course page. Better yet, have links using a discount code you made for YouTube viewers (this will also let you track how many students YouTube is bringing you.)&lt;/p&gt;

    &lt;p&gt;Udemy also allows you to make as many or as few of your videos available for students to download. I go ahead and make all of my videos available for download. It's a large convenience for students. And anyway it's fairly easy to google for instructions on how to download them regardless.&lt;/p&gt;

    &lt;p&gt;&lt;strong&gt;Don't worry about people pirating your course and watching it for free.&lt;/strong&gt; The only way your course won't be put on torrent sites is if no one likes it to begin with. The kind of piracy you do need to worry about are other people taking your videos and selling them on Udemy or other sites. These sites will have a DMCA or infringement notice form that you can fill out. It's good practice to do searches for your course every now and then.&lt;/p&gt;

    &lt;p&gt;The promo video that people will see on your course page when they're deciding to buy it or not is incredibly important. Spend as much time as you can and &lt;a href="https://info.udemy.com/perfect-promo"&gt;follow Udemy's promo video guidelines&lt;/a&gt; &lt;strong&gt;religiously&lt;/strong&gt;. You may want to consider spending money on background music or intro/extro video clips. I like using &lt;a href="https://www.pond5.com/"&gt;Pond5.com&lt;/a&gt; for buying sound clips.&lt;/p&gt;

    &lt;p&gt;You might not want to use Udemy's free service to generate a logo. The quality is okay but they tend to reuse them for other courses with just a color palette change. You may want to consider commissioning a logo from a graphic designer. Think of it like a getting a tattoo: keep it simple and spend &lt;em&gt;more&lt;/em&gt; than $5 on it.&lt;/p&gt;

    &lt;h2&gt;Good luck!&lt;/h2&gt;

    &lt;p&gt;These are the tips I figured out about my process over a long time, and I wanted to compile them all into a blog post to give others a head start. If you want to learn more, here are some topics to google:&lt;/p&gt;

    &lt;ul&gt;
    &lt;li&gt;&lt;a href="https://www.google.com/search?q=void+acting+tutorial"&gt;voice acting tutorial&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.google.com/search?q=DIY+sound+proofing"&gt;DIY sound proofing&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.google.com/search?q=amateur+recording+studio"&gt;amateur recording studio&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.google.com/search?q=DIY+lighting"&gt;DIY lighting&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.google.com/search?q=screencast+tips"&gt;screencast tips&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.google.com/search?q=Twitch+streaming+tips"&gt;Twitch streaming tips&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="https://www.google.com/search?q=video+editing+tips"&gt;video editing tips&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;

</content><category term="misc"></category></entry><entry><title>How to Install Django 1.10.6 and Python 3.6.0 on Dreamhost Shared Hosting</title><link href="https://inventwithpython.com/blog/how-to-install-django-1-10-6-and-python-3-6-0-on-dreamhost-shared-hosting.html" rel="alternate"></link><published>2017-03-14T12:00:00-04:00</published><updated>2017-03-14T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2017-03-14:/blog/how-to-install-django-1-10-6-and-python-3-6-0-on-dreamhost-shared-hosting.html</id><summary type="html">&lt;p&gt;&lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt; is a web app framework for &lt;a href="https://python.org"&gt;Python&lt;/a&gt; (similar to Rails for Ruby). It implements common web app features so you don't have to write that code. &lt;a href="https://www.dreamhost.com/"&gt;Dreamhost&lt;/a&gt; is a web host that offers cheap but reliable web servers. Their &lt;a href="https://www.dreamhost.com/hosting/shared/"&gt;shared hosting plan&lt;/a&gt; is $11 a month, and provides more than enough disk space and bandwidth for personal sites.&lt;/p&gt;</summary><content type="html">


&lt;p&gt;&lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt; is a web app framework for &lt;a href="https://python.org"&gt;Python&lt;/a&gt; (similar to Rails for Ruby). It implements common web app features so you don't have to write that code. &lt;a href="https://www.dreamhost.com/"&gt;Dreamhost&lt;/a&gt; is a web host that offers cheap but reliable web servers. Their &lt;a href="https://www.dreamhost.com/hosting/shared/"&gt;shared hosting plan&lt;/a&gt; is $11 a month, and provides more than enough disk space and bandwidth for personal sites.&lt;/p&gt;

&lt;p&gt;(If you'd like to help Invent with Python, &lt;a href="https://www.dreamhost.com/r.cgi?239190"&gt;use this link to sign up for Dreamhost&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;I wanted to create a Django web app for my &lt;a href="https://turtleappstore.com"&gt;https://turtleappstore.com&lt;/a&gt; site to let others share Minecraft code projects for ComputerCraft. The process took some time to figure out, so I documented it here. Note that this tutorial is specific to Dreamhost, Django 1.10.6, and Python 3.6. The steps may vary if you use different versions or web hosts.&lt;/p&gt;

&lt;h2&gt;Setup&lt;/h2&gt;

&lt;p&gt;After you have purchased a shared hosting plan from Dreamhost, go to &lt;a href="https://panel.dreamhost.com/"&gt;https://panel.dreamhost.com/&lt;/a&gt; and log into your Dreamhost account. This tutorial assumes you have also registered for a domain name. From the sidebar, click Domains &gt; Manage Domains, then click the Edit link next to the domain name you want to host the Django app. In this tutorial, I will be using &lt;strong&gt;example.turtleappstore.com&lt;/strong&gt; for the domain name. Replace this with your domain name in any commands in this tutorial.&lt;/p&gt;

&lt;p&gt;You will see the configuration details for this domain name. Make sure that the &lt;strong&gt;Passenger (Ruby/NodeJS/Python apps only)&lt;/strong&gt; checkbox is checked. This will also make the web directory end with &lt;code&gt;public&lt;/code&gt;, such as &lt;code&gt;/home/username/example.turtleappstore.com/public&lt;/code&gt;. The page will look like this:&lt;br /&gt;&lt;img src="/blogstatic/djangodreamhost/enable_passenger.png" alt="Screenshot of enabling Passenger in the Dreamhost control panel" /&gt;&lt;/p&gt;

&lt;p&gt;Aside: If you have multiple domain names with Dreamhost, I recommend creating a separate user for each one. (From the sidebar, click Users &gt; Manage Users.) If the account or site is hacked, it will only affect that single domain instead of possibly all of your domains.&lt;/p&gt;

&lt;p&gt;The user account that manages this domain name must have "shell access". From the sidebar, click Users &gt; Manage Users, then click the Edit link next to the user. Next to &lt;strong&gt;User Type&lt;/strong&gt;, select "Shell user". This will allow you to SSH into the shared hosting server that the web server is running on.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/djangodreamhost/shell_user.png" alt="Screenshot of Dreamhost's user control panel" /&gt;&lt;/p&gt;

&lt;h2&gt;SSH into the Server&lt;/h2&gt;

&lt;p&gt;Using an SSH program (such as &lt;a href="http://www.putty.org/"&gt;Putty&lt;/a&gt; on Windows, or the SSH program on Mac/Linux). Connect to your server (in my case, &lt;code&gt;example.turtleappstore.com&lt;/code&gt;) using your shell user's username and password (not the Dreamhost username/password you used for &lt;a href="https://panel.dreamhost.com/"&gt;https://panel.dreamhost.com/&lt;/a&gt;.)&lt;/p&gt;

&lt;h2&gt;Download and Install Python 3.6.0&lt;/h2&gt;

&lt;p&gt;Enter the following commands (not the &lt;code&gt;[server]$&lt;/code&gt; part, that's the shell prompt):&lt;/p&gt;

&lt;pre&gt;[server]$ cd ~
[server]$ cd example.turtleappstore.com/
[server]$ mkdir tmp
[server]$ cd tmp
[server]$ wget https://www.python.org/ftp/python/3.6.0/Python-3.6.0.tgz
[server]$ tar zxvf Python-3.6.0.tgz
[server]$ cd Python-3.6.0
[server]$ ./configure --prefix=$HOME/opt/python-3.6.0
[server]$ make
[server]$ make install&lt;/pre&gt;

&lt;p&gt;Some of these commands will take a few minutes to run and output a lot of text. You shouldn't encounter any error messages, but if you do you'll need to figure out what is causing them. Try googling the error message to see if others have encountered it and described the solution.&lt;/p&gt;

&lt;p&gt;Next, add &lt;code&gt;export PATH=$HOME/opt/python-3.6.0/bin:$PATH&lt;/code&gt; to the bottom of your &lt;code&gt;~/.bashrc&lt;/code&gt; and &lt;code&gt;~/.bash_profile&lt;/code&gt; files so that when your web server runs Python, it runs the Python 3.6.0 program you just installed and not Dreamhost's default Python (which is 2.7, as of 2017/03/14). Use a text editor such as &lt;a href="http://www.openvim.com/"&gt;vi or vim&lt;/a&gt; to do this.&lt;/p&gt;

&lt;p&gt;Then run the following to set the system path in your current SSH session (or logout and log back in):&lt;/p&gt;

&lt;pre&gt;[server]$ source ~/.bash_profile&lt;/pre&gt;

&lt;p&gt;Confirm that &lt;code&gt;python3&lt;/code&gt; and &lt;code&gt;pip3&lt;/code&gt; will use the Python you just installed by running the following and checking their output:&lt;/p&gt;

&lt;pre&gt;[server]$ which python3
/home/your_username/opt/python-3.6.0/bin/python3
[server]$ which pip3
/home/your_username/opt/python-3.6.0/bin/pip3&lt;/pre&gt;

&lt;p&gt;With Python installed, you can now delete the temporary install files by running the following:&lt;/p&gt;

&lt;pre&gt;[server]]$ rm ~/example.turtleappstore.com/tmp/* -rf&lt;/pre&gt;

&lt;h2&gt;Download and Install Virtualenv&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://docs.python-guide.org/en/latest/dev/virtualenvs/"&gt;Virtualenv&lt;/a&gt; is a tool to keep installed modules separate for separate projects. This way, if you are working on one Python project that requires Django 1.10.6 and another that requires Django 1.3, you won't be constantly installing and uninstalling Django modules as you work on them. Virtual environments are a good idea to have.&lt;/p&gt;

&lt;p&gt;Run the following to install the virtualenv Python module:&lt;/p&gt;

&lt;pre&gt;[server]$ pip3 install virtualenv&lt;/pre&gt;

&lt;p&gt;Confirm that virtualenv was installed by running the following and checking the output:&lt;/p&gt;

&lt;pre&gt;[server]$ which virtualenv
/home/[username]/opt/python-3.6.0/bin/virtualenv&lt;/pre&gt;

&lt;p&gt;According to the &lt;a href="https://docs.python.org/3/library/venv.html"&gt;venv documentation&lt;/a&gt;, in Python 3.6 you don't use &lt;code&gt;virtualenv&lt;/code&gt; or &lt;code&gt;pyenv&lt;/code&gt; but instead &lt;code&gt;python3 -m venv&lt;/code&gt; to prevent confusion as to which Python interpreter the virtual environment will be based on.&lt;/p&gt;

&lt;p&gt;Next, create a virtual environment for your project. I'll call mine &lt;code&gt;tas_venv&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;[server]$ python3 -m venv ~/tas_venv&lt;/pre&gt;


&lt;p&gt;From now on, before you install/unintall Python modules or run any Django scripts such as manage.py, you will need to make sure the virtual environment for your web app project is activated. Run the following:&lt;/p&gt;

&lt;pre&gt;[server]$ source ~/tas_venv/bin/activate
(tas_venv) [server]$&lt;/pre&gt;

&lt;p&gt;You'll see the &lt;code&gt;(tas_venv)&lt;/code&gt; prompt come up. To exit the virtual environment, just run &lt;code&gt;deactivate&lt;/code&gt;.&lt;/p&gt;


&lt;h2&gt;Download and Install Django&lt;/h2&gt;

&lt;p&gt;With the &lt;code&gt;tas_venv&lt;/code&gt; (or whatever you named it) virtual environment set up, install Django by running this:&lt;/p&gt;

&lt;pre&gt;(tas_venv) [server]$ pip3 install django==1.10.6&lt;/pre&gt;

&lt;p&gt;You can verify Django installed properly from the Python interactive shell:&lt;/p&gt;

&lt;pre&gt;(tas_venv) [server]$ python3
Python 3.6.0 (default, Mar 14 2017, 18:13:54)
[GCC 4.6.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
&gt;&gt;&gt; import django
&gt;&gt;&gt; django.VERSION
(1, 10, 6, 'final', 0)&lt;/pre&gt;

&lt;p&gt;Create the Django project for your site. I'm going to name mine tasproject:&lt;/p&gt;

&lt;pre&gt;(tas_venv) [server]$ cd ~/example.turtleappstore.com/
(tas_venv) [server]$ python3 ~/tas_venv/bin/django-admin.py startproject tasproject&lt;/pre&gt;

&lt;h2&gt;Setup Passenger &amp;amp; Django&lt;/h2&gt;

&lt;p&gt;To finish setting up your site, create a file passenger_wsgi.py in the root of your website directory using a text editor. Add the following to it:&lt;/p&gt;

&lt;pre&gt;import sys, os
cwd = os.getcwd()
sys.path.append(cwd)
sys.path.append(cwd + '/tasproject')

INTERP = os.path.expanduser("~/tas_venv/bin/python3")

if sys.executable != INTERP: os.execl(INTERP, INTERP, *sys.argv)

sys.path.insert(0,'$HOME/tas_venv/bin')
sys.path.insert(0,'$HOME/tas_venv/lib/python3.6/site-packages/django')
sys.path.insert(0,'$HOME/tas_venv/lib/python3.6/site-packages')

os.environ['DJANGO_SETTINGS_MODULE'] = "tasproject.settings"
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()&lt;/pre&gt;

&lt;p&gt;(tas_venv is the name of the virtual environment, while tasproject is the name of the Django project.)&lt;/p&gt;

&lt;p&gt;Also add the following line to the ~/example.turtleappstore.com/tasproject/tasproject/settings.py file:&lt;/p&gt;

&lt;pre&gt;STATIC_ROOT = os.path.dirname(BASE_DIR) + '/static/'&lt;/pre&gt;

&lt;p&gt;Passenger causes Django to handle web server requests. However, I've been having problems getting static files to work correctly on Dreamhost, so I use the &lt;code&gt;public&lt;/code&gt; folder to server all static files by placing a &lt;code&gt;static&lt;/code&gt; subfolder under the &lt;code&gt;public&lt;/code&gt; folder. I'll update this blog post if I figure out a way to get static files to work properly through Django. Note that Dreamhost will check the &lt;code&gt;public&lt;/code&gt; folder first for URL requests, and then check Django's urls.py file next.&lt;/p&gt;

&lt;p&gt;Finally, create a restart.txt file to cause Passenger to reload your passenger_wsgi.py script:&lt;/p&gt;

&lt;pre&gt;[server]$ touch ~/example.turtleappstore.com/tmp/restart.txt&lt;/pre&gt;

&lt;p&gt;At this point, you should be able to open example.turtleappstore.com in a web browser and see Django's default "It worked!" page. Whenever you make a change, you need to re-touch the restart.txt file for Passenger to pick up the changes. Note that Passenger will handle &lt;em&gt;all&lt;/em&gt; requests made to the example.turtleappstore.com. You can now start creating apps and working on your Django project as you normally would.&lt;/p&gt;

&lt;p&gt;&lt;img src="/blogstatic/djangodreamhost/it_worked.png" alt="Screenshot of Django's It Worked page" /&gt;&lt;/p&gt;

</content><category term="misc"></category><category term="django"></category></entry><entry><title>"Automate the Boring Stuff with Python" online course now available!</title><link href="https://inventwithpython.com/blog/automate-the-boring-stuff-with-python-online-course-now-available.html" rel="alternate"></link><published>2015-09-21T12:00:00-04:00</published><updated>2015-09-21T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2015-09-21:/blog/automate-the-boring-stuff-with-python-online-course-now-available.html</id><summary type="html">&lt;p&gt;For the last few months I've been putting together an online course that follows my latest (and free) book, "Automate the Boring Stuff with Python". It is now available.&lt;/p&gt;</summary><content type="html">
        &lt;p&gt;For the last few months I've been putting together an online course that follows my latest (and free) book, "Automate the Boring Stuff with Python".&lt;/p&gt;

        &lt;p&gt;&lt;a href="https://www.udemy.com/automate/?couponCode=FOR_LIKE_TEN_BUCKS"&gt;Sign up for the Udemy course for 80% off.&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;The first 15 videos are also &lt;a href="https://www.youtube.com/watch?v=1F_OgqRuSdI&amp;amp;list=PL0-84-yl1fUnRuXGFe_F7qSH1LEnn9LkW"&gt;freely available on YouTube.&lt;/a&gt;&lt;/p&gt;
</content><category term="misc"></category></entry><entry><title>APIs, not apps: What the future will be like when everyone can code</title><link href="https://inventwithpython.com/blog/apis-not-apps-what-the-future-will-be-like-when-everyone-can-code.html" rel="alternate"></link><published>2015-09-15T12:00:00-04:00</published><updated>2015-09-15T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2015-09-15:/blog/apis-not-apps-what-the-future-will-be-like-when-everyone-can-code.html</id><summary type="html">&lt;p&gt;I've written an article for &lt;a href="https://opensource.com/"&gt;OpenSource.com&lt;/a&gt; called &lt;a href="https://opensource.com/education/15/9/apis-not-apps"&gt;APIs, not apps: What the future will be like when everyone can code&lt;/a&gt;, where I write about a coming future where programming ability is in the hands of everyone.&lt;/p&gt;</summary><content type="html">
        &lt;p&gt;I've written an article for &lt;a href="https://opensource.com/"&gt;OpenSource.com&lt;/a&gt; called &lt;a href="https://opensource.com/education/15/9/apis-not-apps"&gt;APIs, not apps: What the future will be like when everyone can code&lt;/a&gt;, where I write about a coming future where programming ability is in the hands of everyone.&lt;/p&gt;

        &lt;p&gt;Excerpt:&lt;/p&gt;

        &lt;blockquote&gt;
            &lt;p&gt;Despite this hype, I do think that coding will become a more widespread and routine skill in the years to come. Programmable technology will continue to pervade more parts of our life, computers will continue to become more accessible to a wider population, and the world will continue to become more complex. Understanding coding (and debugging) will naturally go with it.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;These are areas where non-programmers can significantly boost their productivity by learning to code. This is different from everyone becoming a software engineer. When I say learn to code, I don't mean develop software professionally. Almost every adult has a driver's license, but only a minority are cab drivers or NASCAR racers.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;Universal coding literacy doesn't increase the supply of web services so much as increase the sophistication in how web services are used. Programming—by which I mean being able to direct a computer to access data, organize it, and then make decisions based on it— will open up not only a popular ability to make more of online services, but also to demand more.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Further Reading: Intermediate Python Resources</title><link href="https://inventwithpython.com/blog/further-reading-intermediate-python-resources.html" rel="alternate"></link><published>2015-09-01T12:00:00-04:00</published><updated>2015-09-01T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2015-09-01:/blog/further-reading-intermediate-python-resources.html</id><summary type="html">You're no longer a complete beginner and would like to know where to go next.</summary><content type="html">

&lt;p&gt;So after reading one of my Python books (available free online &lt;a href="//inventwithpython.com"&gt;here&lt;/a&gt; and &lt;a href="//automatetheboringstuff.com"&gt;here&lt;/a&gt;), you're no longer a complete beginner and would like to know where to go next. It can be hard to find intermediate-level material: stuff that isn't for total beginners or advanced computer scientists. The topics that you should google for are &lt;a href="https://www.google.com/search?q=Python+standard+library"&gt;Python standard library&lt;/a&gt;, &lt;a href="https://www.google.com/search?q=Python+object+oriented+programming"&gt;Python object oriented programming&lt;/a&gt;, &lt;a href="https://www.google.com/search?q=Python+idioms"&gt;Python idioms&lt;/a&gt;, and &lt;a href="https://www.google.com/search?q=popular+Python+modules"&gt;popular Python modules&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For a more concrete list of resources, here's my list of recommendations.&lt;/p&gt;
&lt;h2&gt;Continuing with Python&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="//pymotw.com"&gt;The Python Module of the Week Blog&lt;/a&gt; covers many of the modules in Python's standard library with practical examples. The Python standard library has a wide range of handy functions ("Guido's Time Machine" refers to how requests for features in Python would often be met by Guido van Rosum mentioning he had added it the night before.)&lt;/li&gt;
&lt;li&gt;&lt;a href="//www.amazon.com/gp/product/1449357016/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1449357016&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=XAJJULGVP4SGTPY4"&gt;Python Pocket Reference &lt;img src="http://ecx.images-amazon.com/images/I/517k0XB9ogL._SX302_BO1,204,203,200_.jpg" width="200" style="float: right;"&gt;&lt;/a&gt; is a short book intended for programmers who want to learn Python quickly. Now that you know basic programming concepts, this short book is a great way to fill out your Python knowledge and explore some more modules without spending a lot of time.&lt;/li&gt;
&lt;li&gt;&lt;a href="//www.amazon.com/gp/product/1784398780/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1784398780&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=VUQ2ITHFPYTMXZAX"&gt;Python 3 Object-oriented Programming&lt;/a&gt; is a great resource to learn specifically about classes, objects, and other OOP concepts. My books skip OOP since it isn't necessary to get started coding, but once you've been programming for a while it's a must to become familiar with these topics.&lt;/li&gt;
&lt;li&gt;Data science and machine learning are hot topics in the job market. &lt;a href="//www.amazon.com/gp/product/149190142X/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=149190142X&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=OKGQY5ENRNETZFGD"&gt;Data Science from Scratch&lt;/a&gt; and &lt;a href="//www.amazon.com/gp/product/0596529325/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0596529325&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=NWUYDUX6FVH7TOLN"&gt;Programming Collective Intelligence&lt;/a&gt; are both great introductions to these topics.&lt;/li&gt;
&lt;li&gt;If you'd like to learn Python well enough to become a software engineer, &lt;a href="//www.amazon.com/gp/product/0134034287/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0134034287&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=3GN5BLFUM2A25MEQ"&gt;Effective Python: 59 Specific Ways to Write Better Python&lt;/a&gt; provides a nice list of advanced (but effective) topics to read up on.&lt;/a&gt;
&lt;li&gt;&lt;a href="//www.amazon.com/gp/product/1449340377/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1449340377&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=QDGZAUT37OXSKO4L"&gt;The Python Cookbook&lt;/a&gt; has several recipes for getting stuff done in Python. Reformatting text to fixed columns, determining last Friday's date, or using callback functions are all things that are &lt;em&gt;possible&lt;/em&gt; with Python, but you don't want to waste time figuring out how to do them on your own.
&lt;li&gt;&lt;a href="//docs.python-guide.org/en/latest/"&gt;The Hitchhiker's Guide to Python&lt;/a&gt; is a brilliant summary of many Python gotchas and idioms specific to the Python language.&lt;/li&gt;
&lt;li&gt;&lt;a href="//interactivepython.org/runestone/static/pythonds/index.html"&gt;Problem Solving with Algorithms and Data Structures Using Python&lt;/a&gt; is a good computer science textbook to exploring algorithms in a direct, readable text. It's free to read online.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Practicing Your Code-Fu&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="//projecteuler.net/"&gt;Project Euler &lt;img src="http://projecteuler.net/images/euler_portrait.png" style="float: right;"&gt;&lt;/a&gt; is a classic practice programming site, with mostly math-related problems that can be solved with code.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/dailyprogrammer"&gt;The dailyprogrammer subreddit&lt;/a&gt; has beginner, intermediate, and advanced programming problems post every day. (Reddit also has an &lt;a href="https://www.reddit.com/r/learnprogramming/wiki/faq#wiki_where_can_i_find_practice_exercises_and_project_ideas.3F"&gt;FAQ&lt;/a&gt; with links to other resources.&lt;/li&gt;
&lt;li&gt;&lt;a href="//inventwithpython.com/blog/2012/02/20/i-need-practice-programming-49-ideas-for-game-clones-to-code/"&gt;A list of 49 games to clone&lt;/a&gt; is a great source of ideas. The games were selected based on their simple mechanics and not requiring a lot of artwork or level designing. (You can read &lt;a href="//inventwithpython.com/pygame"&gt;a free book on Pygame here&lt;/a&gt;.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Moving On to Other Languages&lt;/h2&gt;
&lt;p&gt;Python is versatile and you can keep going down that path if you choose, but don't feel that you're somehow "not ready" to tackle a new language. If you do want to move on, here's some resources for the next step.&lt;/p&gt;
&lt;li&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;: All dynamic behavior that happens in the browser is from JavaScript code. If you want to learn to create web apps, getting a basic understanding of HTML and CSS is recommended. The &lt;a href=""&gt;jQuery module&lt;/a&gt; is also standard for doing any web app development. My favorite JavaScript books are: &lt;a href="http://eloquentjavascript.net/"&gt;Eloquent JavaScript&lt;/a&gt; (free), &lt;a href="http://www.amazon.com/gp/product/1118531647/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1118531647&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=QHMDCWWVVGOEX5UP"&gt;JavaScript and JQuery: Interactive Front-End Web Development&lt;/a&gt;, and (once you have some JS experience) Crockford's &lt;a href="http://www.amazon.com/gp/product/0596517742/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0596517742&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=MUMYA5HJ7SZHYHWA"&gt;JavaScript: The Good Parts&lt;/a&gt; is the key book to read to understand the language.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ruby&lt;/strong&gt;: Ruby is a scripting language and similar to Python. It's noteworthy for the Ruby on Rails web app framework. You can try it out online at &lt;a href="http://tryruby.org/levels/1/challenges/0"&gt;tryruby.org&lt;/a&gt;, and Codecademy has tracks for both &lt;a href="https://www.codecademy.com/tracks/ruby"&gt;Ruby&lt;/a&gt; and &lt;a href="https://www.codecademy.com/courses/learn-rails"&gt;Ruby on Rails&lt;/a&gt;. But since you already know how to code, I'd suggest the &lt;a href="https://www.ruby-lang.org/en/documentation/quickstart/"&gt;Learn Ruby in 20 Minutes&lt;/a&gt; page on the official Ruby site.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Java&lt;/strong&gt; or &lt;strong&gt;C#&lt;/strong&gt;: &lt;a href="http://www.amazon.com/gp/product/0071809252/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0071809252&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=5B7BQXCCCCYYZLRU"&gt;&lt;img src="http://ecx.images-amazon.com/images/I/51bSQY6AZhL._SX404_BO1,204,203,200_.jpg" width="200" style="float: right;"&gt;&lt;/a&gt; These languages are fairly similar to each other. Java is the more popular one and a mainstay of software engineering jobs. C# is (essentially) Microsoft's version of Java, meant to create Windows applications. I don't have any recommendations as far as C# books, but &lt;a href="http://www.amazon.com/gp/product/0071809252/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0071809252&amp;amp;linkCode=as2&amp;amp;tag=playwithpyth-20&amp;amp;linkId=5B7BQXCCCCYYZLRU"&gt;Java: A Beginner's Guide&lt;/a&gt; is a decent intro. There have been plenty of slight changes to the Java language over the years, so you don't want to get a book that's more than a decade old or so.&lt;/li&gt;
&lt;/ul&gt;

</content><category term="misc"></category></entry><entry><title>3rd Edition of "Invent Your Own Computer Games with Python" Released</title><link href="https://inventwithpython.com/blog/3rd-edition-of-invent-your-own-computer-games-with-python-released.html" rel="alternate"></link><published>2015-03-10T12:00:00-04:00</published><updated>2015-03-10T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2015-03-10:/blog/3rd-edition-of-invent-your-own-computer-games-with-python-released.html</id><summary type="html">&lt;p&gt;The 3rd edition of "Invent Your Own Computer Games with Python" is now available.&lt;/p&gt;</summary><content type="html">

            &lt;p&gt;&lt;a href="/inventwithpython_3rd.pdf?27f655"&gt;&lt;img src="http://inventwithpython.com/images/cover_inventwithpython3_thumb.jpg" alt="Invent with Python book cover for 3rd edition" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="/inventwithpython_3rd.pdf?27f655"&gt;Download the PDF&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The 3rd edition of "Invent Your Own Computer Games with Python" is now available. The 3rd edition has been streamlined to provide the same content but in 50 fewer pages, making it an easier read. If you've already read the 2nd edition, there's no need to read the 3rd (unless you want to sharpen your Python skills). But for new readers, Invent with Python should be even easier to pick up.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.amazon.com/gp/product/1503212300?ie=UTF8&amp;amp;tag=playwithpyth-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1503212300"&gt;Buy the 3rd Edition on Amazon&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/chapters/"&gt;Read the 3rd Edition online for free&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/2/"&gt;Read the 2nd Edition&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The 3rd edition is also being used for the translations. Currently the &lt;a href="http://inventwithpython.com/es/"&gt;Spanish translation&lt;/a&gt; is online (though incomplete). The German and Dutch versions are nearing completion as well.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Translate Your Python 3 Program with the gettext Module</title><link href="https://inventwithpython.com/blog/translate-your-python-3-program-with-the-gettext-module.html" rel="alternate"></link><published>2014-12-20T12:00:00-05:00</published><updated>2014-12-20T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2014-12-20:/blog/translate-your-python-3-program-with-the-gettext-module.html</id><summary type="html">&lt;p&gt;You've written a Python 3 program and want to make it available in other languages. You &lt;em&gt;could&lt;/em&gt; duplicate the entire code-base, then go painstakingly through each .py file and replace any text strings you find. Fortunately, Python provides a solution with the &lt;code&gt;gettext&lt;/code&gt; module.&lt;/p&gt;</summary><content type="html">

&lt;!-- Check out these links and maybe add them to this blog post:
https://phrase.com/blog/posts/translate-python-gnu-gettext/
https://lingohub.com/blog/2013/07/php-internationalization-with-gettext-tutorial
https://www.gnu.org/software/libc/manual/html_node/Translation-with-gettext.html
https://www.gnu.org/software/gawk/manual/html_node/Explaining-gettext.html
--&gt;

&lt;!-- TODO
&lt;p&gt;UPDATED: &lt;/p&gt;
--&gt;

&lt;p&gt;You've written a Python 3 program and want to make it available in other languages. You &lt;em&gt;could&lt;/em&gt; duplicate the entire code-base, then go painstakingly through each .py file and replace any text strings you find. But this would mean you have two separate copies of your code, which doubles your workload every time you need to make a change or fix a bug. And if you want your program in other languages, it gets even worse.&lt;/p&gt;
&lt;p&gt;Fortunately, Python provides a solution with the &lt;code&gt;gettext&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;&lt;span id="more-1893"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;A Hack Solution&lt;/h3&gt;
&lt;p&gt;You could hack together your own solution. For example, you could replace every string in your program with a function call (with the function name being something simple, like &lt;code&gt;_()&lt;/code&gt;)) which will return the string translated into the correct language. For example, if your program was:&lt;/p&gt;
&lt;pre&gt;print('Hello world!')&lt;/pre&gt;
&lt;p&gt;...you could change this to:&lt;/p&gt;
&lt;pre&gt;print(_('Hello world!'))&lt;/pre&gt;
&lt;p&gt;...and the &lt;code&gt;_()&lt;/code&gt; function could return the translation for &lt;code&gt;'Hello world!'&lt;/code&gt; based on what language setting the program had. For example, if the language setting was stored in a global variable named &lt;code&gt;LANGUAGE&lt;/code&gt;, the &lt;code&gt;_()&lt;/code&gt; function could look like this:&lt;/p&gt;
&lt;pre&gt;def _(s):
    spanishStrings = {'Hello world!': 'Hola Mundo!'}
    frenchStrings = {'Hello world!': 'Bonjour le monde!'}
    germanStrings = {'Hello world!': 'Hallo Welt!'}

    if LANGUAGE == 'English':
        return s
    if LANGUAGE == 'Spanish':
        return spanishStrings[s]
    if LANGUAGE == 'French':
        return frenchStrings[s]
    if LANGUAGE == 'German':
        return germanStrings[s]&lt;/pre&gt;
&lt;p&gt;This would work, but you'd be reinventing the wheel. This is pretty much what Python's &lt;code&gt;gettext&lt;/code&gt; module does. &lt;a href="https://en.wikipedia.org/wiki/Gettext"&gt;gettext&lt;/a&gt; is a set of tools and file formats created in the early 1990s to standardize software internationalization (also called I18N). gettext was designed as a system for all programming languages, but we'll focus on Python in this article.&lt;/p&gt;
&lt;h3&gt;The Example Program&lt;/h3&gt;
&lt;p&gt;Say you have a simple "Guess the Number" game written in Python 3 that you want to translate. The &lt;a href="/blogstatic/gettext/guess_original.py"&gt;source code to this program is here&lt;/a&gt;. There are four steps to internationalizing this program:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Modify the .py file's source code so that the strings are passed to a function named &lt;code&gt;_()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use the &lt;em&gt;pygettext.py&lt;/em&gt; script that comes installed with Python to create a "pot" file from the source code.&lt;/li&gt;
&lt;li&gt;Use the free cross-platform &lt;a href="http://poedit.net/"&gt;Poedit&lt;/a&gt; software to create the .po and .mo files from the pot file.&lt;/li&gt;
&lt;li&gt;Modify your .py file's source code again to import the &lt;code&gt;gettext&lt;/code&gt; module and set up the language setting.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Step 1: Add the _() Function&lt;/h3&gt;
&lt;p&gt;First, go through all of the strings in your program that will need to be translated and replace them with &lt;code&gt;_()&lt;/code&gt; calls. The gettext system for Python uses &lt;code&gt;_()&lt;/code&gt; as the generic name for getting the translated string since it is a short name.&lt;/p&gt;
&lt;p&gt;Note that using string formatting instead of string concatenation will make your program easier to translate. For example, using string concatenation your program would have to look like this:&lt;/p&gt;
&lt;pre&gt;print('Good job, ' + myName + '! You guessed my number in ' + guessesTaken + ' guesses!')

print(_('Good job, ') + myName + _('! You guessed my number in ') + guessesTaken + _(' guesses!'))&lt;/pre&gt;
&lt;p&gt;This results in three separate strings that need to be translated, as opposed to the single string needed in the string formatting approach:&lt;/p&gt;
&lt;pre&gt;print('Good job, %s! You guessed my number in %s guesses!' % (myName, guessesTaken))

print(_('Good job, %s! You guessed my number in %s guesses!') % (myName, guessesTaken))&lt;/pre&gt;
&lt;p&gt;When you've gone through the "Guess the Number" source code, &lt;a href="/blogstatic/gettext/guess.py"&gt;it will look like this&lt;/a&gt;. You won't be able to run this program since the &lt;code&gt;_()&lt;/code&gt; function is undefined. This change is just so that the &lt;em&gt;pygettext.py&lt;/em&gt; script can find all the strings that need to be translated.&lt;/p&gt;
&lt;h3&gt;Step 2: Extract the Strings Using pygettext.py&lt;/h3&gt;
&lt;p&gt;In the &lt;em&gt;Tools/i18n&lt;/em&gt; of your Python installation (&lt;em&gt;C:\Python34\Tools\i18n&lt;/em&gt; on Windows) is the &lt;em&gt;pygettext.py&lt;/em&gt; script. While the normal gettext unix command parse C/C++ source code for translatable strings and the xgettext unix command can parse other languages, &lt;em&gt;pygettext.py&lt;/em&gt; knows how to parse Python source code. It will find all of these strings and produce a "pot" file.&lt;/p&gt;
&lt;p&gt;On Windows, I've run this script like so:&lt;/p&gt;
&lt;pre&gt;C:\&gt;py -3.4 C:\Python34\Tools\i18n\pygettext.py -d guess guess.py&lt;/pre&gt;
&lt;p&gt;&lt;!-- TODO explain the -d and domains --&gt;This creates a pot file named &lt;em&gt;guess.pot&lt;/em&gt;. This is just a normal plaintext file that lists all the translated strings it found in the source code by search for &lt;code&gt;_()&lt;/code&gt; calls. You can &lt;a href="/blogstatic/gettext/guess.pot?27f655"&gt;view the &lt;em&gt;guess.pot&lt;/em&gt; file here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Step 3: Translate the Strings using Poedit&lt;/h3&gt;
&lt;p&gt;You could fill in the translation using a text editor, but the free Poedit software makes it easier. &lt;a href="http://poedit.net"&gt;Download it from http://poedit.net&lt;/a&gt;. Select &lt;strong&gt;File &gt; New from POT/PO file...&lt;/strong&gt; and select your &lt;em&gt;guess.po&lt;/em&gt; file.&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/gettext/poedit_new.jpg?27f655" /&gt;&lt;/p&gt;
&lt;p&gt;Poedit will ask what language you want to translate the strings to. For this example, we'll use Spanish:&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/gettext/poedit_translate.jpg?27f655" /&gt;&lt;/p&gt;
&lt;p&gt;Then fill in the translations. (I'm using &lt;a href="http://translate.google.com"&gt;http://translate.google.com&lt;/a&gt;, so it probably sounds a bit odd to actual Spanish-speakers.)&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/gettext/poedit_filledin.jpg?27f655" /&gt;&lt;/p&gt;
&lt;p&gt;And now save the file in it's gettext-formatted folder. Saving will create the .po file (a human-readable text file identical to the original .pot file, except with the Spanish translations) and a .mo file (a machine-readable version which the &lt;code&gt;gettext&lt;/code&gt; module will read. These files have to be saved in a certain folder structure for &lt;code&gt;gettext&lt;/code&gt; to be able to find them. They look like this (say I have "es" Spanish files and "de" German files):&lt;/p&gt;
&lt;pre&gt;./guess.py
./guess.pot
./locale/es/LC_MESSAGES/guess.mo
./locale/es/LC_MESSAGES/guess.po
./locale/de/LC_MESSAGES/guess.mo
./locale/de/LC_MESSAGES/guess.po&lt;/pre&gt;
&lt;p&gt;&lt;!-- TODO Explain that this folder structure is specified in the gettext.translation() function call --&gt;These two-character language names like "es" for Spanish and "de" for German are called &lt;a href="https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes"&gt;ISO 639-1 codes&lt;/a&gt; and are standard abbreviations for languages. You don't &lt;em&gt;have&lt;/em&gt; to use them, but it makes sense to follow that naming standard.&lt;/p&gt;
&lt;h3&gt;Step 4: Add gettext Code to Your Program&lt;/h3&gt;
&lt;p&gt;Now that you have the .mo file that contains the translations, modify your Python script to use it. Add the following to your program:&lt;/p&gt;
&lt;pre&gt;import gettext
es = gettext.translation('guess', localedir='locale', languages=['es'])
es.install()&lt;/pre&gt;
&lt;p&gt;The first argument &lt;code&gt;'guess'&lt;/code&gt; is the "domain", which basically means the "guess" part of the &lt;em&gt;guess.mo&lt;/em&gt; filename. The &lt;code&gt;localedir&lt;/code&gt; is the directory location of the &lt;em&gt;locale&lt;/em&gt; folder you created. This can be either a relative or absolute path. The &lt;code&gt;'es'&lt;/code&gt; string describes the folder under the &lt;em&gt;locale&lt;/em&gt; folder. The &lt;em&gt;LC_MESSAGES&lt;/em&gt; folder is a standard name&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;install()&lt;/code&gt; method will cause all the &lt;code&gt;_()&lt;/code&gt; calls to return the Spanish translated string. If you want to go back to the original English, just assign a lambda function value to &lt;code&gt;_&lt;/code&gt; that returns the string it was passed:&lt;/p&gt;
&lt;pre&gt;import gettext, os
FOLDER_OF_THIS_FILE = os.path.dirname(os.path.abspath(__file__))
esLang = gettext.translation('guess', localedir=os.path.join(FOLDER_OF_THIS_FILE, 'locale'), languages=['es'])
esLang.install()  # All _() calls return Spanish strings after this point.
print(_('Hello! What is your name?'))  # prints Spanish
&lt;/pre&gt;

&lt;p&gt;You can &lt;a href="/blogstatic/gettext/guess.py"&gt;view the translation-ready source code for the "Guess the Number"&lt;/a&gt;. If you want to run this program, &lt;a href="/blogstatic/gettext/guess_gettext.zip?27f655"&gt;download and unzip this zip file&lt;/a&gt; with it's &lt;em&gt;locale&lt;/em&gt; folders and .mo file set up.&lt;/p&gt;
&lt;h3&gt;Further Reading&lt;/h3&gt;
&lt;p&gt;I am by no means an expert on I18N or gettext, and please leave comments if I'm breaking any best practices in this tutorial. Most of the time your software will not switch languages while it's running, and instead read one of the &lt;code&gt;LANGUAGE&lt;/code&gt;, &lt;code&gt;LC_ALL&lt;/code&gt;, &lt;code&gt;LC_MESSAGES&lt;/code&gt;, and &lt;code&gt;LANG&lt;/code&gt; environment variables to figure out the locale of the computer it's running on. I'll update this tutorial as I learn more.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Programming a Bot to Play the "Sushi Go Round" Flash Game</title><link href="https://inventwithpython.com/blog/programming-a-bot-to-play-the-sushi-go-round-flash-game.html" rel="alternate"></link><published>2014-12-17T12:00:00-05:00</published><updated>2014-12-17T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2014-12-17:/blog/programming-a-bot-to-play-the-sushi-go-round-flash-game.html</id><summary type="html">&lt;p&gt;This tutorial teaches how to write a bot that can automatically play the Flash game Sushi Go Round. The concepts in this tutorial can be applied to make bots that play other games as well.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;&lt;strong&gt;Update 2015/01/08:&lt;/strong&gt; If you attended my NCSS class on making a bot for this game, you can download the &lt;a href="/sushi.zip?27f655"&gt;sushi.zip&lt;/a&gt; file which has all the code in bot5.py.&lt;/p&gt;
&lt;p&gt;This tutorial teaches how to write a bot that can automatically play the Flash game Sushi Go Round. The concepts in this tutorial can be applied to make bots that play other games as well. It's inspired by the &lt;a href="http://code.tutsplus.com/tutorials/how-to-build-a-python-bot-that-can-play-web-games--active-11117"&gt;How to Build a Python Bot That Can Play Web Games&lt;/a&gt; by Chris Kiehl. The primary improvement of this tutorial is it uses the cross-platform &lt;a href="https://pypi.python.org/pypi/PyAutoGUI"&gt;PyAutoGUI&lt;/a&gt; module to control the mouse and take screenshots. It is &lt;a href="http://pyautogui.readthedocs.org/en/latest/"&gt;documented on its ReadTheDocs page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/sushigoroundbot.jpg?27f655"&gt;&lt;img src="/blogstatic/sushigoroundbot.jpg?27f655" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Sushi Go Round is a resource management game similar to "Dine N Dash". You fill customer orders for different types of sushi and place them on the conveyor belt. Incoming customers may take other customer's sushi orders, forcing you to remake orders. Customers who wait too long to get orders end up leaving, costing you reputation. Angry customers can be placated with saki, but this bot does not make use of that feature. Ingredients will have to be ordered as they get low.&lt;/p&gt;
&lt;p&gt;I've refined this bot so that it can successfully play all the way through the game, ending up with a score of about 38,000. The top scores are a little over 100,000, so there is still room for improvement with this bot. You can watch a &lt;a href="http://youtu.be/lfk_T6VKhTE"&gt;YouTube video of the bot playing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;iframe width="560" height="315" src="//www.youtube.com/embed/lfk_T6VKhTE" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p&gt;The source code and images for the bot can be &lt;a href="/blogstatic/sushigoround.zip?27f655"&gt;downloaded here&lt;/a&gt; or &lt;a href="https://github.com/asweigart/sushigoroundbot"&gt;viewed on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span id="more-1865"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Step 1 - Install PyAutoGUI and Download Images&lt;/h3&gt;
&lt;p&gt;First, install PyAutoGUI by &lt;a href="https://pypi.python.org/pypi/PyAutoGUI"&gt;downloading it from PyPI&lt;/a&gt; or &lt;a href="https://docs.python.org/3/installing/index.html"&gt;installing it through the pip program that comes with Python 3&lt;/a&gt;. If you've installed PyAutoGUI before, use the -U option to update it to the latest version. This tutorial uses Python 3, though PyAutoGUI and the bot code work with either Python 2 or 3.&lt;/p&gt;
&lt;p&gt;You can check if PyAutoGUI has been installed correctly by running &lt;code&gt;import pyautogui&lt;/code&gt; in the interactive shell. Full documentation for PyAutoGUI is available at &lt;a href="http://pyautogui.readthedocs.org/en/latest/"&gt;http://pyautogui.readthedocs.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;PyAutoGUI provides basic image recognition by finding the screen coordinates of a provided image. To save yourself time of taking screenshots and making these images yourself, &lt;a href="/blogstatic/sushigoround.zip?27f655"&gt;download them from the zip file here&lt;/a&gt;. All of the images should be stored in an &lt;code&gt;/images&lt;/code&gt; folder:&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/sushi_california_roll_order.png?27f655" /&gt; &lt;img src="/blogstatic/sushi_clear_mat.png?27f655" /&gt; &lt;img src="/blogstatic/sushi_gunkan_maki_order.png?27f655" /&gt; &lt;img src="/blogstatic/sushi_nori_order_button.png?27f655" /&gt; &lt;img src="/blogstatic/sushi_normal_delivery_button.png?27f655" /&gt; &lt;img src="/blogstatic/sushi_onigiri_order.png?27f655" /&gt; &lt;img src="/blogstatic/sushi_skip_button.png?27f655" /&gt; &lt;img src="/blogstatic/sushi_cant_afford_rice.png?27f655" /&gt;&lt;/p&gt;
&lt;h3&gt;Step 2 - Basic Setup&lt;/h3&gt;
&lt;p&gt;Create a &lt;em&gt;sushigoround.py&lt;/em&gt; file and enter the following code:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="c"&gt;#! python3&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Sushi Go Round Bot&lt;/span&gt;
&lt;span class="sd"&gt;Al Sweigart &lt;a class="__cf_email__" href="/cdn-cgi/l/email-protection" data-cfemail="16777a567f7860737862617f627e666f627e79783875797b"&gt;[email&amp;#160;protected]&lt;/a&gt;&lt;script data-cfhash="f9e31" type="text/javascript"&gt;/* &lt;![CDATA[ */!function(t,e,r,n,c,a,p){try{t=document.currentScript||function(){for(t=document.getElementsByTagName('script'),e=t.length;e--;)if(t[e].getAttribute('data-cfhash'))return t[e]}();if(t&amp;&amp;(c=t.previousSibling)){p=t.parentNode;if(a=c.getAttribute('data-cfemail')){for(e='',r='0x'+a.substr(0,2)|0,n=2;a.length-n;n+=2)e+='%'+('0'+('0x'+a.substr(n,2)^r).toString(16)).slice(-2);p.replaceChild(document.createTextNode(decodeURIComponent(e)),c)}p.removeChild(t)}}catch(u){}}()/* ]]&gt; */&lt;/script&gt; @AlSweigart&lt;/span&gt;

&lt;span class="sd"&gt;A bot program to automatically play the Sushi Go Round flash game at http://miniclip.com/games/sushi-go-round/en/&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;copy&lt;/span&gt;

&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%(asctime)s&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="si"&gt;%(msecs)03d&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;%(message)s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;datefmt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;%H:%M:%S&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;#logging.disable(logging.DEBUG) # uncomment to block debug log messages&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This generic code sets up a &lt;a href="https://stackoverflow.com/questions/2429511/why-do-people-write-usr-bin-env-python-on-the-first-line-of-a-python-script"&gt;shebang line&lt;/a&gt;, some comments to describe the program, imports for several modules, and setting up logging so that the &lt;code&gt;logging.debug()&lt;/code&gt; function will output debug messages. (I explain why logging is preferable to &lt;code&gt;print()&lt;/code&gt; calls &lt;a href="http://inventwithpython.com/blog/2012/04/06/stop-using-print-for-debugging-a-5-minute-quickstart-guide-to-pythons-logging-module/"&gt;in this blog post&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Step 3 - Constants Setup&lt;/h3&gt;
&lt;p&gt;Next, there are several variable constants set up for this program. I use constants as a way to detect typos; mistyping a string value as &lt;code&gt;'califrnia_roll'&lt;/code&gt; will cause bugs but Python won't directly point out the typo. Mistyping a variable name as &lt;code&gt;CALIFRNIA_ROLL&lt;/code&gt; will cause Python to raise a &lt;code&gt;NameError&lt;/code&gt; exception, letting you quickly fix it.&lt;/p&gt;
&lt;p&gt;First, set up constants for each type of order in the game, as well as a tuple of all the orders:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="c"&gt;# Food order constants (don&amp;#39;t change these: the image filenames depend on these specific values)&lt;/span&gt;
&lt;span class="n"&gt;ONIGIRI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;onigiri&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;GUNKAN_MAKI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;gunkan_maki&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;CALIFORNIA_ROLL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;california_roll&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;SALMON_ROLL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;salmon_roll&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;SHRIMP_SUSHI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;shrimp_sushi&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;UNAGI_ROLL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;unagi_roll&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;DRAGON_ROLL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;dragon_roll&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;COMBO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;combo&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;ALL_ORDER_TYPES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ONIGIRI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GUNKAN_MAKI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CALIFORNIA_ROLL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SALMON_ROLL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SHRIMP_SUSHI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UNAGI_ROLL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DRAGON_ROLL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COMBO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Next, set up constants for each of the ingredients and the recipes for each order. (This tutorial saves you from having to look up the recipe information in the game yourself.)&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="c"&gt;# Ingredient constants (don&amp;#39;t change these: the image filenames depend on these specific values)&lt;/span&gt;
&lt;span class="n"&gt;SHRIMP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;shrimp&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;RICE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;rice&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;NORI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;nori&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;ROE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;roe&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;SALMON&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;salmon&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;UNAGI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;unagi&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;RECIPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ONIGIRI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="n"&gt;CALIFORNIA_ROLL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ROE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="n"&gt;GUNKAN_MAKI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ROE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="n"&gt;SALMON_ROLL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SALMON&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="n"&gt;SHRIMP_SUSHI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SHRIMP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="n"&gt;UNAGI_ROLL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UNAGI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="n"&gt;DRAGON_ROLL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ROE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UNAGI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="n"&gt;COMBO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;           &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ROE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SALMON&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UNAGI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SHRIMP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},}&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Next are several constants and global variables used in the program. Although using global variables is generally a bad idea for making maintainable programs, this Python script is just a one off and I didn't want to make it complicated for newer programmers who don't yet know object-oriented programming concepts. The variables are described in the comments:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;LEVEL_WIN_MESSAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;win&amp;#39;&lt;/span&gt; &lt;span class="c"&gt;# checkForGameOver() returns this value if the level has been won&lt;/span&gt;

&lt;span class="c"&gt;# Settings&lt;/span&gt;
&lt;span class="n"&gt;MIN_INGREDIENTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="c"&gt;# if an ingredient gets below this value, order more&lt;/span&gt;
&lt;span class="n"&gt;PLATE_CLEARING_FREQ&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="c"&gt;# plates are cleared every this number of seconds, roughly&lt;/span&gt;
&lt;span class="n"&gt;NORMAL_RESTOCK_TIME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="c"&gt;# the number of seconds it takes to restock inventory after ordering it (at normal speed, not express)&lt;/span&gt;
&lt;span class="n"&gt;TIME_TO_REMAKE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="c"&gt;# if an order goes unfilled for this number of seconds, remake it&lt;/span&gt;

&lt;span class="c"&gt;# Global variables&lt;/span&gt;
&lt;span class="n"&gt;LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c"&gt;# current level being played&lt;/span&gt;
&lt;span class="n"&gt;INVENTORY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SHRIMP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;ROE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;SALMON&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UNAGI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;GAME_REGION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# (left, top, width, height) values coordinates of the game window&lt;/span&gt;
&lt;span class="n"&gt;ORDERING_COMPLETE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SHRIMP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ROE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SALMON&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UNAGI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c"&gt;# unix timestamp when an ordered ingredient will have arrived&lt;/span&gt;
&lt;span class="n"&gt;ROLLING_COMPLETE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c"&gt;# unix timestamp of when the rolling of the mat will have completed&lt;/span&gt;
&lt;span class="n"&gt;LAST_PLATE_CLEARING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c"&gt;# unix timestamp of the last time the plates were cleared&lt;/span&gt;
&lt;span class="n"&gt;LAST_GAME_OVER_CHECK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c"&gt;# unix timestamp when we last checked for the Game Over or You Win messages&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Note: a "unix timestamp" is the number of seconds since midnight, January 1, 1970. It is returned by the &lt;code&gt;time.time()&lt;/code&gt; function. For example, if &lt;code&gt;time.time()&lt;/code&gt; is called at 10:36:54.392 PM on December 16, 2014, it will return the value &lt;code&gt;1418798214.392&lt;/code&gt;. Ten seconds later it will return the value &lt;code&gt;1418798224.392&lt;/code&gt;. Subtracting these values is how the bot can measure how much time has passed.&lt;/p&gt;
&lt;p&gt;And finally set up some global variables which will store coordinates for various things in the game window. The &lt;code&gt;GAME_REGION&lt;/code&gt; variable stores the position of the Sushi Go Round game itself. Once that value has been acquired, the &lt;code&gt;setupCoordinates()&lt;/code&gt; function (described later) will populate the values in these variables:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="c"&gt;# various coordinates of objects in the game&lt;/span&gt;
&lt;span class="n"&gt;GAME_REGION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# (left, top, width, height) values coordinates of the entire game window&lt;/span&gt;
&lt;span class="n"&gt;INGRED_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="n"&gt;PHONE_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="n"&gt;TOPPING_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="n"&gt;ORDER_BUTTON_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="n"&gt;RICE1_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="n"&gt;RICE2_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="n"&gt;NORMAL_DELIVERY_BUTTON_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="n"&gt;MAT_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Step 4 - The main() Function&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;main()&lt;/code&gt; ties together the code that runs from the very start of the bot program. It assumes that the Sushi Go Round game is visible on the screen and at the opening start screen (which has the flashing "PLAY" button).&lt;/p&gt;
&lt;p&gt;Because PyAutoGUI takes control of the mouse, it can be hard to get the program's terminal window back in focus to shut it down. &lt;a href="http://pyautogui.readthedocs.org/en/latest/introduction.html#fail-safes"&gt;PyAutoGUI has a fail-safe feature;&lt;/a&gt; if you move the mouse cursor to the top-left corner of the screen PyAutoGUI will raise an exception and terminate the program.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Runs the entire program. The Sushi Go Round game must be visible on the screen and the PLAY button visible.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Program Started. Press Ctrl-C to abort at any time.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;To interrupt mouse movement, move mouse to upper left corner.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;getGameRegion&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;navigateStartGameMenu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;setupCoordinates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;startServing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Step 5 - Image Recognition and Finding the Game Screen&lt;/h3&gt;
&lt;p&gt;Computer vision and optical character recognition (OCR) are major topics of computer science. The good thing about Sushi Go Round is that you don't need to know any of that because the game uses static 2D sprites. A california roll in the game will always look the same down to the pixel. So we can use PyAutoGUI's pixel-recognition functions to "see" where on the screen different objects are.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;pyautogui.locateOnScreen()&lt;/code&gt; function takes a string filename argument and returns a (left, top, width, height) tuple of integer coordinates for where the image is found on the screen. (The "left" value is the X-coordinate of the left edge, the "top" value is the Y-coordinate of the top edge. With the XY coordinates of the top-left corner and the width and height, you can completely describe the region.)&lt;/p&gt;
&lt;p&gt;If the image isn't found on the screen, the function returns &lt;code&gt;None&lt;/code&gt;. An optional &lt;code&gt;region&lt;/code&gt; keyword argument can specify a smaller region of the screen to search (a smaller region means faster detection). Without a region specified, the entire screen is searched.&lt;/p&gt;
&lt;p&gt;The location of the game window itself will be stored later in the &lt;code&gt;GAME_REGION&lt;/code&gt; variable, which holds a (left, top, width, height) tuple of integers. (The (left, top, width, height) tuples are used throughout PyAutoGUI.) These values are visualized in the following graphic:&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/sushigoroundcoordinates.png?27f655"&gt;&lt;img src="/blogstatic/sushigoroundcoordinates.png?27f655" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To find the game window, we search for the top-right corner image, which is stored in &lt;em&gt;top_right_corner.png&lt;/em&gt;. (These image files are included in the &lt;a href="/blogstatic/sushigoround.zip?27f655"&gt;zip file download&lt;/a&gt;.) Once this image is located on the screen, the &lt;code&gt;GAME_REGION&lt;/code&gt; variable can be populated.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;A shortcut for joining the &amp;#39;images/&amp;#39;&amp;#39; file path, since it is used so often. Returns the filename with &amp;#39;images/&amp;#39; prepended.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;images&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getGameRegion&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Obtains the region that the Sushi Go Round game is on the screen and assigns it to GAME_REGION. The game must be at the start screen (where the PLAY button is visible).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;

    &lt;span class="c"&gt;# identify the top-left corner&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Finding game region...&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locateOnScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;top_right_corner.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Could not find game on screen. Is the game visible?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# calculate the region of the entire game&lt;/span&gt;
    &lt;span class="n"&gt;topRightX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# left + width&lt;/span&gt;
    &lt;span class="n"&gt;topRightY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# top&lt;/span&gt;
    &lt;span class="n"&gt;GAME_REGION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topRightX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;topRightY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;480&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# the game screen is always 640 x 480&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Game region found: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Note that running PyAutoGUI's image recongition takes a relatively long time to execute (several hundred milliseconds). This can be mitigated by searching a smaller region instead of the entire screen. And since some objects will always appear in the same place in the game window, you can rely on using static integer coordinates instead of image recognition to find them.&lt;/p&gt;
&lt;h3&gt;Step 6 - Dealing with Coordinates&lt;/h3&gt;
&lt;p&gt;Many of the buttons in the game's user interface will always be in the same location. The coordinates for these buttons can be pre-programmed ahead of time. You could take a screenshot, paste the image into a graphics program such as Photoshop or MS Paint, and find all of the XY coordinates yourself. But this tutorial has done this step for you. The other coordinate global variables are populated in the &lt;code&gt;setupCoordinates()&lt;/code&gt; function. Note that they are all relative to where the game window (whose coordinates are in &lt;code&gt;GAME_REGION&lt;/code&gt;) is.&lt;/p&gt;
&lt;p&gt;For example, the coordinates for the Salmon ordering button are set to &lt;code&gt;(GAME_REGION[0] + 496, GAME_REGION[1] + 329)&lt;/code&gt;. This is visualized in the following graphic:&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/sushigoroundcoordinates2.png?27f655"&gt;&lt;img src="/blogstatic/sushigoroundcoordinates2.png?27f655" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setupCoordinates&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Sets several of the coordinate-related global variables, after acquiring the value for GAME_REGION.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;INGRED_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PHONE_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TOPPING_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ORDER_BUTTON_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RICE1_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RICE2_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NORMAL_DELIVERY_BUTTON_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MAT_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LEVEL&lt;/span&gt;
    &lt;span class="n"&gt;INGRED_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SHRIMP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;335&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                     &lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;335&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                     &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;385&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                     &lt;span class="n"&gt;ROE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;385&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                     &lt;span class="n"&gt;SALMON&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;425&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                     &lt;span class="n"&gt;UNAGI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;425&lt;/span&gt;&lt;span class="p"&gt;),}&lt;/span&gt;
    &lt;span class="n"&gt;PHONE_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;560&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;TOPPING_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;513&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;269&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ORDER_BUTTON_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SHRIMP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;496&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;222&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                           &lt;span class="n"&gt;UNAGI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;578&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;222&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                           &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;496&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;281&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                           &lt;span class="n"&gt;ROE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;578&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;281&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                           &lt;span class="n"&gt;SALMON&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;496&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;329&lt;/span&gt;&lt;span class="p"&gt;),}&lt;/span&gt;
    &lt;span class="n"&gt;RICE1_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;543&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;294&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;RICE2_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;545&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;269&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;NORMAL_DELIVERY_BUTTON_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;495&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;293&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;MAT_COORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;190&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;375&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This tutorial does the tedious coordinate-finding work for you, but if you ever need to do it yourself you can use the &lt;code&gt;pyautogui.displayMousePosition()&lt;/code&gt; function to display the XY coordinates of the mouse cursor. The coordinates update as you move the mouse, so you can move the mouse to the desired location on the screen, then view its current coordinates. The &lt;code&gt;displayMousePosition()&lt;/code&gt; function also shows that pixel's RGB color value.&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/displaymouseposition.png?27f655"&gt;&lt;img src="/blogstatic/displaymouseposition.png?27f655" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can also pass X and Y offsets to the function to set the (0, 0) origin somewhere besides the top-left corner of the screen. When you are done, press Ctrl-C to return from the function.&lt;/p&gt;
&lt;h3&gt;Step 7 - Controlling the Mouse&lt;/h3&gt;
&lt;p&gt;PyAutogui has a &lt;code&gt;pyautogui.click()&lt;/code&gt; function that can be passed a (x, y) tuple argument of the coordinates on the screen to click on. Often you can use the return value of &lt;code&gt;pyautogui.locateCenterOnScreen()&lt;/code&gt; for this argument. The click is done immediately, but the optional &lt;code&gt;duration&lt;/code&gt; keyword argument will specify the number of seconds PyAutoGUI spends moving to the (x, y) coordinate. A small delay makes it easier to visually follow along with the bot's clicking.&lt;/p&gt;
&lt;p&gt;Since the SKIP button in the game is flashing, the &lt;code&gt;pyautogui.locateCenterOnScreen(imPath('skip_button.png'), region=GAME_REGION)&lt;/code&gt; call might not be able to find it if it is flashing in the other color than &lt;em&gt;skip_button.png&lt;/em&gt; has. This is why there is a &lt;code&gt;while&lt;/code&gt; loop that keeps searching until it finds it. Remember, &lt;code&gt;locateCenterOnScreen()&lt;/code&gt; will return &lt;code&gt;None&lt;/code&gt; if it can't find the image on the screen.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;navigateStartGameMenu&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Performs the clicks to navigate form the start screen (where the PLAY button is visible) to the beginning of the first level.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="c"&gt;# Click on everything needed to get past the menus at the start of the game.&lt;/span&gt;

    &lt;span class="c"&gt;# click on Play&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Looking for Play button...&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c"&gt;# loop because it could be the blue or pink Play button displayed at the moment.&lt;/span&gt;
        &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locateCenterOnScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;play_button.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Clicked on Play button.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# click on Continue&lt;/span&gt;
    &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locateCenterOnScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;continue_button.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Clicked on Continue button.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# click on Skip&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Looking for Skip button...&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c"&gt;# loop because it could be the yellow or red Skip button displayed at the moment.&lt;/span&gt;
        &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locateCenterOnScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;skip_button.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Clicked on Skip button.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# click on Continue&lt;/span&gt;
    &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locateCenterOnScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;continue_button.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Clicked on Continue button.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Once the buttons at the start of the game have been navigated past, the main part of the bot code can begin.&lt;/p&gt;
&lt;h3&gt;Step 8 - The startServing() Function&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;startServing()&lt;/code&gt; function handles all of the main game play. This includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Seeing which orders are being requested from customers.&lt;/li&gt;
&lt;li&gt;Creating dishes to fill orders.&lt;/li&gt;
&lt;li&gt;Ordering more ingredients when running low on them.&lt;/li&gt;
&lt;li&gt;Checking if customers haven't received their orders in a long time and remaking those orders.&lt;/li&gt;
&lt;li&gt;Clearing finished plates.&lt;/li&gt;
&lt;li&gt;Checking if ordered ingredients have arrived.&lt;/li&gt;
&lt;li&gt;Checking if the game has been lost or if the level has been won.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Much of this functionality is passed on to other functions, which are explained later. The first part of &lt;code&gt;startServing()&lt;/code&gt; sets up all the variables for a new game:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;startServing&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;The main game playing function. This function handles all aspects of game play, including identifying orders, making orders, buying ingredients and other features.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;LAST_GAME_OVER_CHECK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INVENTORY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ORDERING_COMPLETE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LEVEL&lt;/span&gt;

    &lt;span class="c"&gt;# Reset all game state variables.&lt;/span&gt;
    &lt;span class="n"&gt;oldOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;backOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;remakeOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;remakeTimes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;LAST_GAME_OVER_CHECK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;ORDERING_COMPLETE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SHRIMP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="n"&gt;ROE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SALMON&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UNAGI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The next part scans the region where customers orders are displayed:&lt;/p&gt;
&lt;p&gt;&lt;a href="/blogstatic/sushigoround_scanning.png?27f655"&gt;&lt;img src="/blogstatic/sushigoround_scanning.png?27f655" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is handled by the &lt;code&gt;getOrders()&lt;/code&gt; function. Since you'll be repeatedly scanning for the orders but don't want to remake orders each time you scan them, you need to find out which new orders have appeared since the last scan, and which orders have disappeared since the last scan (either because the customer is eating or has left). This is handled by the &lt;code&gt;getOrdersDifference()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The keys in these "orders dictionaries" will be the (left, top, width, height) tuple of where the order image was identified. This is just to keep the customers distinct. The values will be the strings in one of the ingredient constants like &lt;code&gt;CALIFORNIA_ROLL&lt;/code&gt; or &lt;code&gt;ONIGIRI&lt;/code&gt;. The &lt;code&gt;remakeTimes&lt;/code&gt; dictionary has similar keys, but it's values are unix timestamps (returned from &lt;code&gt;time.time()&lt;/code&gt;) for when the dish should be remade if it is still being requested by the customer.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c"&gt;# Check for orders, see which are new and which are gone since last time.&lt;/span&gt;
        &lt;span class="n"&gt;currentOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getOrders&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;added&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;removed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getOrdersDifference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentOrders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oldOrders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;added&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="p"&gt;{}:&lt;/span&gt;
            &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;New orders: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;added&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())))&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;added&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;remakeTimes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;TIME_TO_REMAKE&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;removed&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="p"&gt;{}:&lt;/span&gt;
            &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Removed orders: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;removed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())))&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;removed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;remakeTimes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The next part goes through the &lt;code&gt;remakeTimes&lt;/code&gt; dictionary and checks if any of those timestamps are before the current time (as returned by &lt;code&gt;time.time()&lt;/code&gt;). In that case, add this order to the &lt;code&gt;remakeOrders&lt;/code&gt; dictionary (which is similar but separate to the &lt;code&gt;added&lt;/code&gt; orders dictionary of new orders.)&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;        &lt;span class="c"&gt;# Check if the remake times have past, and add those to the remakeOrders dictionary.&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;remakeTime&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;remakeTimes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;remakeTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;remakeTimes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;TIME_TO_REMAKE&lt;/span&gt; &lt;span class="c"&gt;# reset remake time&lt;/span&gt;
                &lt;span class="n"&gt;remakeOrders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentOrders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; added to remake orders.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentOrders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Next, the program loops through all the newly requested orders in the &lt;code&gt;added&lt;/code&gt; dictionary and attempts to make them. The &lt;code&gt;makeOrder()&lt;/code&gt; function (described later) will return &lt;code&gt;None&lt;/code&gt; if the order was successfully made. Otherwise, it will return a string of the ingredient it doesn't have enough of. In that case, &lt;code&gt;orderIngredient()&lt;/code&gt; (described later) is called and the order is placed on the &lt;code&gt;backOrders&lt;/code&gt; dictionary.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;        &lt;span class="c"&gt;# Attempt to make the order.&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;added&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;makeOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;orderIngredient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;backOrders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;
                &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Ingredients for &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; not available. Putting on back order.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When a customer picks up their meal, they'll spend a few seconds eating and then leave behind a dirty dish. Click this dirty dish so that a new customer will take that seat. Instead of using image recognition (which is slow), the bot can just occassionally click on all six plates. There's no penalty to clicking on the plate when the customer is eating or if there is no plate at that seat.&lt;/p&gt;
&lt;p&gt;Since this doesn't need to be done frequently, the &lt;code&gt;if random.randint(1, 10) == 1 or time.time() - PLATE_CLEARING_FREQ &gt; LAST_PLATE_CLEARING:&lt;/code&gt; statement will only do this roughly once every 10 iterations, or if more than 6 seconds (the value in &lt;code&gt;PLATE_CLEARING_FREQ&lt;/code&gt;) has passed since the last plate clearing (according to the timestamp in &lt;code&gt;LAST_PLATE_CLEARING&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The actual clicking is done by the &lt;code&gt;clickOnPlates()&lt;/code&gt; function, described later.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;        &lt;span class="c"&gt;# Clear any finished plates.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;PLATE_CLEARING_FREQ&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;LAST_PLATE_CLEARING&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;clickOnPlates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;It's easy to keep track of ingredient quanties as you make dishes. Each level starts with a set quantity of ingredients, encoded in the &lt;code&gt;INVENTORY = {SHRIMP: 5, RICE: 10, NORI: 10, ROE: 10, SALMON: 5, UNAGI: 5}&lt;/code&gt; lines. You can subtract values in the &lt;code&gt;INVENTORY&lt;/code&gt; dictionary as you use ingredients.&lt;/p&gt;
&lt;p&gt;When you order ingredients they don't arrive immediately. If you used image recognition for the numbers in the lower left corner of the game window it would require grabbing tons of screenshots and slow down the bot. Instead, since orders take less than 7 seconds to arrive (which is why the &lt;code&gt;NORMAL_RESTOCK_TIME&lt;/code&gt; is set to 7), the &lt;code&gt;ORDERING_COMPLETE&lt;/code&gt; can store unix timestamps for 7 seconds in the future when &lt;code&gt;INVENTORY&lt;/code&gt; can be updated.&lt;/p&gt;
&lt;p&gt;The values in &lt;code&gt;ORDERING_COMPLETE&lt;/code&gt; are &lt;code&gt;None&lt;/code&gt; if they are not currently being ordered and delivered.&lt;/p&gt;
&lt;p&gt;The next part of code checks if the current time (as returned by &lt;code&gt;time.time()&lt;/code&gt;) has passed. If so, the &lt;code&gt;INVENTORY&lt;/code&gt; dictionary's values are updated.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;        &lt;span class="c"&gt;# Check if ingredient orders have arrived.&lt;/span&gt;
        &lt;span class="n"&gt;updateInventory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c"&gt;# Go through and see if any back orders can be filled.&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backOrders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;makeOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;backOrders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# remove from back orders&lt;/span&gt;
                &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Filled back order for &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;remakeOrders&lt;/code&gt; dictionary contains orders that (for whatever reason) didn't make it to the customer. This code is similar to the previous dish-making code:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;        &lt;span class="c"&gt;# Go through and see if any remake orders can be filled.&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;remakeOrders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;currentOrders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;remakeOrders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Canceled remake order for &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;makeOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;remakeOrders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# remove from remake orders&lt;/span&gt;
                &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Filled remake order for &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The next part checks the &lt;code&gt;INVENTORY&lt;/code&gt; dictionary to see if there is less than 4 (the value stored in the &lt;code&gt;MIN_INGREDIENTS&lt;/code&gt; constant) of any ingredient. If so, more is ordered by calling the &lt;code&gt;orderIngredient()&lt;/code&gt; function (described later). This isn't something that needs to be checkeded frequently, so it is enclosed in an &lt;code&gt;if&lt;/code&gt; statement that runs 1 in 5 times:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c"&gt;# order any ingredients that are below the minimum amount&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;INVENTORY&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;MIN_INGREDIENTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;orderIngredient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The next part checks for the "You Win" or "You Fail" message. Since this check doesn't need to happen frequently at all, the &lt;code&gt;if time.time() - 12 &gt; LAST_GAME_OVER_CHECK:&lt;/code&gt; statement only runs it once every 12 seconds. &lt;code&gt;LAST_GAME_OVER_CHECK&lt;/code&gt; is updated with the current time from &lt;code&gt;time.time()&lt;/code&gt; every time the check is performed. The &lt;code&gt;checkForGameOver()&lt;/code&gt; function (described later) terminates the program if the player has lost, or returns the string in the &lt;code&gt;LEVEL_WIN_MESSAGE&lt;/code&gt; constant if the level has been beaten.&lt;/p&gt;
&lt;p&gt;If the level has been beaten, the code resets many of the variables:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;        &lt;span class="c"&gt;# check for the &amp;quot;You Win&amp;quot; or &amp;quot;You Fail&amp;quot; messages&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;LAST_GAME_OVER_CHECK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkForGameOver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;LEVEL_WIN_MESSAGE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c"&gt;# player has completed the level&lt;/span&gt;

                &lt;span class="c"&gt;# Reset inventory and orders.&lt;/span&gt;
                &lt;span class="n"&gt;INVENTORY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SHRIMP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                             &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ROE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                             &lt;span class="n"&gt;SALMON&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UNAGI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;ORDERING_COMPLETE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SHRIMP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                     &lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ROE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                     &lt;span class="n"&gt;SALMON&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UNAGI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;backOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
                &lt;span class="n"&gt;remakeOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
                &lt;span class="n"&gt;currentOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
                &lt;span class="n"&gt;oldOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Also, the bot will let the user view the end-level stats for 5 seconds before clicking Continue to move on to the next level.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;                &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Level &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; complete.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LEVEL&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;LEVEL&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# give another 5 seconds to tally score&lt;/span&gt;

                &lt;span class="c"&gt;# Click buttons to continue to next level.&lt;/span&gt;
                &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locateCenterOnScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;continue_button.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Clicked on Continue button.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locateCenterOnScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;continue_button.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For every level except for the last, there will be a second Continue button to click on. On the last level, the bot doesn't do this so the user can view the game ending.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;LEVEL&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c"&gt;# click the second continue if the game isn&amp;#39;t finished.&lt;/span&gt;
                    &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Clicked on Continue button.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;oldOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentOrders&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Step 9 - Clearing the Plates&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;clickOnPlates()&lt;/code&gt; function blindly clicks on all six places where there could be a dirty plate to clean up. This works because there is no penalty for clicking when there isn't a dirty plate, freeing the bot from having to use performance-costly image recognition to first identify the dirty plate.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;LAST_PLATE_CLEARING&lt;/code&gt; global variable contains the timestamp of the last time the plates were cleared. This is used to determine if a long time has passed since the plates have been cleared.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clickOnPlates&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Clicks the mouse on the six places where finished plates will be flashing. This function does not check for flashing plates, but simply clicks on all six places.&lt;/span&gt;

&lt;span class="sd"&gt;    Sets LAST_PLATE_CLEARING to the current time.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;LAST_PLATE_CLEARING&lt;/span&gt;

    &lt;span class="c"&gt;# just blindly click on all the places where a plate should be&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;83&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;203&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;LAST_PLATE_CLEARING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Step 10 - Getting the Orders from Customers&lt;/h3&gt;
&lt;p&gt;Customer orders appear as dish images in word bubbles above their heads. PyAutoGUI's image recognition can be used to scan this region of the game screen and match them to the images in &lt;em&gt;california_roll_order.png&lt;/em&gt;, &lt;em&gt;gunkan_maki_order.png&lt;/em&gt;, &lt;em&gt;onigiri_order.png&lt;/em&gt;, and so on:&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/sushi_california_roll_order.png?27f655" /&gt; &lt;img src="/blogstatic/sushi_gunkan_maki_order.png?27f655" /&gt; &lt;img src="/blogstatic/sushi_onigiri_order.png?27f655" /&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;getOrders()&lt;/code&gt; function finds all the current orders being requested. Figuring out which of these orders have been seen before (and possible already fulfilled) is done by the &lt;code&gt;getOrdersDifference()&lt;/code&gt; function, explained in the next section.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getOrders&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Scans the screen for orders being made. Returns a dictionary with a (left, top, width, height) tuple of integers for keys and the order constant for a value.&lt;/span&gt;

&lt;span class="sd"&gt;    The order constants are ONIGIRI, GUNKAN_MAKI, CALIFORNIA_ROLL, SALMON_ROLL, SHRIMP_SUSHI, UNAGI_ROLL, DRAGON_ROLL, COMBO.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;orderType&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ALL_ORDER_TYPES&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;allOrders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locateAllOnScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;_order.png&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;orderType&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;558&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;allOrders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orderType&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;

The &lt;code&gt;newOrders&lt;/code&gt; and &lt;code&gt;oldOrders&lt;/code&gt; parameters have values that were returned from different calls to &lt;code&gt;getOrders()&lt;/code&gt;. The orders that are new to newOrders are returned in the &lt;code&gt;added&lt;/code&gt; dictionaries and the orders that have since been removed are returned in the &lt;code&gt;removed&lt;/code&gt; dictionary.

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getOrdersDifference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newOrders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oldOrders&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Finds the differences between the orders dictionaries passed. Return value is a tuple of two dictionaries.&lt;/span&gt;

&lt;span class="sd"&gt;    The first dictionary is the &amp;quot;added&amp;quot; dictionary of orders added to newOrders since oldOrders. The second dictionary is the &amp;quot;removed&amp;quot; dictionary of orders in oldOrders but removed in newOrders.&lt;/span&gt;

&lt;span class="sd"&gt;    Each dictionary has (left, top, width, height) for keys and an order constant for a value.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;added&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;removed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c"&gt;# find all orders in newOrders that are new and not found in oldOrders&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;newOrders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;oldOrders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;added&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newOrders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c"&gt;# find all orders in oldOrders that were removed and not found in newOrders&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;oldOrders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;newOrders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;removed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oldOrders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;added&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;removed&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Step 11 - Making the Orders&lt;/h3&gt;
&lt;p&gt;Creating a dish involves clicking on the correct ingredients and then clicking on the rolling mat. The dish is place on a conveyor belt and eventually makes its way to a customer. The first part of the function is just the docstring and a few &lt;code&gt;global&lt;/code&gt; statements.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;makeOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderType&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Does the mouse clicks needed to create an order.&lt;/span&gt;

&lt;span class="sd"&gt;    The orderType parameter has the value of one of the ONIGIRI, GUNKAN_MAKI, CALIFORNIA_ROLL, SALMON_ROLL, SHRIMP_SUSHI, UNAGI_ROLL, DRAGON_ROLL, COMBO constants.&lt;/span&gt;

&lt;span class="sd"&gt;    The INVENTORY global variable is updated in this function for orders made.&lt;/span&gt;

&lt;span class="sd"&gt;    The return value is None for a successfully made order, or the string of an ingredient constant if that needed ingredient is missing.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;ROLLING_COMPLETE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INGRED_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INVENTORY&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The next part of the code ensures that the bot doesn't try to make a dish when it's not possible. One of the reasons is that the mat is still in the process of rolling the previous dish. Also, a dish will stay on the mat after being rolled if there isn't space on the conveyor belt. (This happens when orders aren't picked up by a customer.)&lt;/p&gt;
&lt;p&gt;This is an important check: if the bot starts clicking on ingredients when the rolling mat is occupied, then the bot's &lt;code&gt;INVENTORY&lt;/code&gt; dictionary will contain an inaccurate count of ingredients, leading to other mistakes and eventually losing the game.&lt;/p&gt;
&lt;p&gt;The mat takes about 1.5 seconds to complete, which is why the &lt;code&gt;ROLLING_COMPLETE = time.time() + 1.5&lt;/code&gt; is run after clicking the mat to begin rolling it. This is a simple check that doesn't take long to run.&lt;/p&gt;
&lt;p&gt;But if the conveyor belt is occupied, then the previous order could still be on the mat waiting for a clear space on the belt. After performing the cheap &lt;code&gt;ROLLING_TIME&lt;/code&gt; check, the bot then uses &lt;code&gt;pyautogui.locateOnScreen()&lt;/code&gt; to see if the mat is clear. This guarantees that when the bot starts clicking on ingredients they will be moved to the mat.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="c"&gt;# wait until the mat is clear. The previous order could still be there if the conveyor belt has been full or the mat is currently rolling.&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ROLLING_COMPLETE&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locateOnScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;clear_mat.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;115&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;295&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;220&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;175&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;But before the bot begins making a dish, it consults the &lt;code&gt;INVENTORY&lt;/code&gt; dictionary to make sure it has enough of each of the ingredients in the dish's recipe. The recipes for each dish are held in the &lt;code&gt;RECIPES&lt;/code&gt; constant.&lt;/p&gt;
&lt;p&gt;If there isn't enough of an ingredient, the string in the ingredient's constant is returned. This information will be used to place an order for more of that ingredient.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="c"&gt;# check that all ingredients are available in the inventory.&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;RECIPE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;orderType&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;INVENTORY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;More &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; is needed to make &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orderType&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Once it has been confirmed there is enough of each ingredient, calls to &lt;code&gt;pyautogui.click()&lt;/code&gt; cause the bot to click on the appropriate ingredients, then click on the mat to roll it. The &lt;code&gt;ROLLING_COMPLETE&lt;/code&gt; global variable is updated with the timestamp of when the mat rolling will be finished (1.5 seconds in the future) so the next dish can be prepared.&lt;/p&gt;
&lt;p&gt;Just before the rolling mat is clicked though, the bot will check the conveyor belt for any excess dishes that weren't picked up by customers. While this is a waste of ingredients, these excess dishes tend to pile up (especially in the later levels), and slow down the bot from serving dishes to the point that it could lose the game. The call to &lt;code&gt;findAndClickPlatesOnBelt()&lt;/code&gt; (described next) gets rid of any excess dishes that happen to be on the conveyor belt.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="c"&gt;# click on each of the ingredients&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;RECIPE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;orderType&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INGRED_COORDS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;INVENTORY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;findAndClickPlatesOnBelt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# get rid of any left over meals on the conveyor belt, which may stall this meal from being loaded on the belt&lt;/span&gt;
    &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAT_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# click the rolling mat to make the order&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Made a &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; order.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderType&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;ROLLING_COMPLETE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt; &lt;span class="c"&gt;# give the mat enough time (1.5 seconds) to finish rolling before being used again&lt;/span&gt;

Sometimes other customers will swoop in and grab dishes that were intended for other customers. To prevent the customers from leaving, the &lt;code&gt;remakeTimes&lt;/code&gt; dictionary keeps track of when orders will need to be remade. However, if the customer leaves before picking up this remade dish, then it will circle endlessly on the conveyor belt. Eventually these excess dishes can pile up and stall new dishes from being added. The &lt;code&gt;findAndClickPlatesOnBelt()&lt;/code&gt; will scan the conveyor belt next to the rolling mat for any dishes and click on them to remove them.

It does this by identifying the pink, blue, or red coloring of the plates. These colors are stored in the &lt;em&gt;pink_plate_color.png&lt;/em&gt;, &lt;em&gt;blue_plate_color.png&lt;/em&gt;, &lt;em&gt;red_plate_color.png&lt;/em&gt; images.

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;findAndClickPlatesOnBelt&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Find any plates on the conveyor belt that can be removed and click on them to remove them. This will get rid of excess orders.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;pink&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;blue&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;red&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locateCenterOnScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;_plate_color.png&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;343&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Clicked on &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; plate on belt at X: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; Y: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Step 12 - Ordering More Ingredients&lt;/h3&gt;
&lt;p&gt;When the bot needs more ingredients, it must navigate the menu buttons on the phone in the lower right corner of the game window. This tutorial has already figured out these coordinates for you, but you could use the &lt;code&gt;pyautogui.displayMousePosition()&lt;/code&gt; function to find coordinates for any pixel on the screen.&lt;/p&gt;
&lt;p&gt;The first step is to click on the phone:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;orderIngredient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Do the clicks to purchase an ingredient. If successful, the ORDERING_COMPLETE dictionary is updated for when the ingredients will arive and INVENTORY can be updated. (This is handled in the updateInventory() function.)&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Ordering more &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; (inventory says &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; left)...&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INVENTORY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PHONE_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The next part of the code handles pressing the buttons to order rice. The timestamp in &lt;code&gt;ORDERING_COMPLETE&lt;/code&gt; is checked to make sure a previous rice order hasn't been made. Then the screen is scanned for the &lt;em&gt;cant_afford_rice.png&lt;/em&gt; image. The rice ordering button takes on a dimmed appearance if the player does not have enough money to order rice. In this case, the function returns.&lt;/p&gt;
&lt;p&gt;&lt;img src="/blogstatic/sushi_cant_afford_rice.png?27f655" /&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ingredient&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;RICE&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;ORDERING_COMPLETE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c"&gt;# Order rice.&lt;/span&gt;
        &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RICE1_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# Check if we can&amp;#39;t afford the rice&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locateOnScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cant_afford_rice.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;498&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;242&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Can&amp;#39;t afford rice. Canceling.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;585&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;335&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# click cancel phone button&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Otherwise, the bot will click on the rice button. The code doesn't update &lt;code&gt;INVENTORY&lt;/code&gt; quite yet, as it will take time for the rice to be delivered. (For simplicity, the bot will never use the Express Delivery option, though you could certainly build in the intelligence to do that.) The expected delivery time is set in the &lt;code&gt;ORDERING_COMPLETE&lt;/code&gt; dictionary.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;INVENTORY&lt;/code&gt; will be updated with the new quantity in the &lt;code&gt;updateInventory()&lt;/code&gt; function.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;        &lt;span class="c"&gt;# Purchase the rice&lt;/span&gt;
        &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RICE2_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NORMAL_DELIVERY_BUTTON_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ORDERING_COMPLETE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;NORMAL_RESTOCK_TIME&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Ordered more &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The next part does something similar except it navigates the phone menu for the non-rice ingredients. The same general logic is used; if an ingredient isn't affordable, the dimmed button image will inform the bot that it can't be purchased.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;ORDERING_COMPLETE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c"&gt;# Order non-rice ingredient.&lt;/span&gt;
        &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TOPPING_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# Check if we can&amp;#39;t afford the ingredient&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locateOnScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cant_afford_&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;.png&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;446&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;187&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Can&amp;#39;t afford &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;. Canceling.&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;597&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;337&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# click cancel phone button&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Note that the bot doesn't have to keep track of how much money it has. It only needs to know if it can afford an ingredient or not. For simplicity, the "normal delivery" option is always used instead of the costly "express delivery" option:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;        &lt;span class="c"&gt;# Order the ingredient&lt;/span&gt;
        &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ORDER_BUTTON_COORDS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NORMAL_DELIVERY_BUTTON_COORDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ORDERING_COMPLETE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;NORMAL_RESTOCK_TIME&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Ordered more &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If it turns out the bot couldn't afford the desired ingredient, the next part will close the phone menu by clicking on the coordinates for the "hang up" button:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="c"&gt;# The ingredient has already been ordered, so close the phone menu.&lt;/span&gt;
    &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;589&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;341&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# click cancel phone button&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Already ordered &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;updateInventory()&lt;/code&gt; function is frequently called to check if the timestamps in &lt;code&gt;ORDERING_COMPLETE&lt;/code&gt; indicate that the ordered ingredients have arrived. In that case, it updates &lt;code&gt;INVENTORY&lt;/code&gt; with the new values. Shrimp, unagi, and salmon deliveries always add 5. Nori, roe, and rice deliveries always add 10.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;updateInventory&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Check if any ordered ingredients have arrived by looking at the timestamps in ORDERING_COMPLETE.&lt;/span&gt;
&lt;span class="sd"&gt;    Update INVENTORY global variable with the new quantities.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ingredient&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;INVENTORY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ORDERING_COMPLETE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ORDERING_COMPLETE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;ORDERING_COMPLETE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ingredient&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SHRIMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UNAGI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SALMON&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;INVENTORY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;ingredient&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NORI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ROE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RICE&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;INVENTORY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
            &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Updated inventory with added &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;:&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INVENTORY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Step 13 - Checking for the End of the Level&lt;/h3&gt;
&lt;p&gt;The bot can tell when the level is over by doing image recognition for the &lt;em&gt;you_win.png&lt;/em&gt; or &lt;em&gt;you_fail.png&lt;/em&gt; images. If the game is over, the program terminates by calling &lt;code&gt;sys.exit()&lt;/code&gt;. If the level has been beaten, the bot clicks on the "You Win" window so that it immediately tallies the stats and returns the string in the &lt;code&gt;LEVEL_WIN_MESSAGE&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;checkForGameOver&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Checks the screen for the &amp;quot;You Win&amp;quot; or &amp;quot;You Fail&amp;quot; message.&lt;/span&gt;

&lt;span class="sd"&gt;    On winning, returns the string in LEVEL_WIN_MESSAGE.&lt;/span&gt;

&lt;span class="sd"&gt;    On losing, the program terminates.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="c"&gt;# check for &amp;quot;You Win&amp;quot; message&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locateOnScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;you_win.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;188&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;94&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;262&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;LEVEL_WIN_MESSAGE&lt;/span&gt;

    &lt;span class="c"&gt;# check for &amp;quot;You Fail&amp;quot; message&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pyautogui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locateOnScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;you_failed.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;167&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GAME_REGION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;133&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;314&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Game over. Quitting.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Finally, the &lt;code&gt;main()&lt;/code&gt; function is called if this script is being run (as opposed to imported as a module):&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;That's it!&lt;/h3&gt;
&lt;p&gt;If you download and run the bot, it doesn't have much problem beating all 7 levels of Sushi Go Round. But as you can tell from the leaderboard, if doesn't compare to the best human players. Feel free to modify the code to try to improve the performance of the bot.&lt;/p&gt;
&lt;p&gt;Sushi Go Round is a good candidate for creating a bot since it's 2D sprite graphics are static and easy to identify from screenshots. If you find any other similar such games, please list them in the comments below! I've found that the &lt;a href="http://www.newgrounds.com/games/browse/genre/skill/interval/year/sort/score/min_score/0"&gt;"Skill" genre on Newgrounds&lt;/a&gt;, "puzzle", "gathering", and "rhythm" games are usually bot-friendly since their images are easier to identify. (There's a &lt;a href="http://newgrounds.wikia.com/wiki/List_of_Flash_Game_Genres"&gt;list of Flash game genres here&lt;/a&gt;.) Look for ones that have retro pixel graphics. In particular, these games seem like good candidates for making bots:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/642967"&gt;Color Finger&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/634256"&gt;Cardinal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/635494"&gt;Sagacious Saga&lt;/a&gt; (helps if you know &lt;a href="http://inventwithpython.com/blog/2011/08/11/recursion-explained-with-the-flood-fill-algorithm-and-zombies-and-cats/"&gt;the floodfill algorithm&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/643915"&gt;Apple Catcher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/637291"&gt;Cold Runner&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/633907"&gt;GatherX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/633175"&gt;Next Avoider&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/634591"&gt;Dodge Copter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/631796"&gt;Rockocroco&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/634395"&gt;Holy Stomping&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And some trickier games, but still possible to make bots for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/636211"&gt;1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/639339"&gt;Golden Duel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.homestarrunner.com/weengame.html"&gt;Homestar Runner: Marshie's Malloween Mix-Up&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/645866"&gt;Twin Cube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/630835"&gt;Rollasaurus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.newgrounds.com/portal/view/649860"&gt;Hyperwall&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Update: &lt;a href="https://www.reddit.com/r/Python/comments/2pm3hj/programming_a_bot_in_python_to_play_the_sushi_go/cmy69zn"&gt;roddds on Reddit&lt;/a&gt; posted a link to his own &lt;a href="https://github.com/roddds/sushibot"&gt;Sushi Go Round bot he had made previously&lt;/a&gt;. He also has &lt;a href="https://github.com/roddds/DiamondDashBot"&gt;code&lt;/a&gt; and a &lt;a href="https://www.youtube.com/watch?v=AQv-z2LBu6w"&gt;video&lt;/a&gt; for a &lt;a href="http://www.wooga.com/games/diamonddash/"&gt;Diamond Dash&lt;/a&gt;-playing bot.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Making a Text Adventure Game with the cmd and textwrap Python Modules</title><link href="https://inventwithpython.com/blog/making-a-text-adventure-game-with-the-cmd-and-textwrap-python-modules.html" rel="alternate"></link><published>2014-12-11T12:00:00-05:00</published><updated>2014-12-11T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2014-12-11:/blog/making-a-text-adventure-game-with-the-cmd-and-textwrap-python-modules.html</id><summary type="html">&lt;p&gt;Text adventures are an old game genre where the entire world is textually described as a series of rooms. Play involves entering simple text commands such as "move north" or "eat pie". Each room in the game world has its own text description, items on the ground, and exits to adjacent rooms.&lt;/p&gt;</summary><content type="html">

            &lt;p&gt;Text adventures are an old game genre where the entire world is textually described as a series of rooms. Play involves entering simple text commands such as "move north" or "eat pie". Each room in the game world has its own text description, items on the ground, and exits to adjacent rooms. "Room" is a general name for a single area in the game world: a room can be a large open canyon or the inside of a wardrobe. Multi-user text adventures, called MUDs or Multi-User Dungeons, were the precursor to modern MMORPGs. You can still play MUDs today by finding them on &lt;a href="http://www.mudconnect.com/"&gt;The Mud Connector&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://inventwithpython.com/blogstatic/esotera.png?27f655" alt="Screenshot of an old text adventure game." /&gt;&lt;/p&gt;
&lt;p&gt;Text adventures are easyto make because they don't require graphics. This tutorial uses two Python modules, &lt;code&gt;cmd&lt;/code&gt; and &lt;code&gt;textwrap&lt;/code&gt; and makes minimal use of object-oriented programming, but you don't have to know OOP concepts to follow. (But in general, text adventures would do very well with an object-oriented approach.) This tutorial is for beginner Python 3 programmers.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/asweigart/textadventuredemo"&gt;The code for Text Adventure Demo is available on GitHub.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span id="more-1623"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;cmd&lt;/code&gt; module provides a generic command-line interface that has several useful features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tab-completion&lt;/strong&gt; - Type a partial command and press tab, and the game can guess the rest of the command.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;History browsing&lt;/strong&gt; - Press the up arrow key to bring up the previously entered commands.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automatic help&lt;/strong&gt; - A help system for commands is automatically generated for your commands.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Python Debugger (&lt;a href="http://pythonconquerstheuniverse.wordpress.com/2009/09/10/debugging-in-python/"&gt;tutorial here&lt;/a&gt;) in the &lt;code&gt;pdb&lt;/code&gt; module makes use of the &lt;code&gt;cmd&lt;/code&gt; module for its command line interface.&lt;/p&gt;
&lt;p&gt;To get tab-completion to work on Windows, download and install this module: &lt;a href="https://pypi.python.org/pypi/pyreadline/2.0"&gt;https://pypi.python.org/pypi/pyreadline/2.0&lt;/a&gt; (On Windows with Python 3, open a command window and type &lt;code&gt;cd c:\Python34\Scripts&lt;/code&gt; and then &lt;code&gt;pip install pyreadline&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Our program will be about turn-based, single-player, and about 800 lines long. You can download the complete version from here: &lt;a href="https://raw.githubusercontent.com/asweigart/textadventuredemo/master/textadventuredemo.py"&gt;Download textadventuredemo.py&lt;/a&gt; or look at the &lt;a href="https://raw.githubusercontent.com/asweigart/textadventuredemo"&gt;GitHub page&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Demo of Game Play&lt;/h3&gt;
&lt;p&gt;Here's what our text adventure game will look like. It won't have combat, but will have a small town that you can navigate while picking up and looking at different things. Money isn't implemented, but shops to buy and sell at are.&lt;/p&gt;
&lt;blockquote style="font-family: monospace;"&gt;&lt;p&gt;Text Adventure Demo!&lt;br /&gt;
====================&lt;/p&gt;
&lt;p&gt;(Type "help" for commands.)&lt;/p&gt;
&lt;p&gt;Town Square&lt;br /&gt;
===========&lt;br /&gt;
The town square is a large open space with a fountain in the center. Streets&lt;br /&gt;
lead in all directions.&lt;/p&gt;
&lt;p&gt;A welcome sign stands here.&lt;br /&gt;
A bubbling fountain of green water.&lt;/p&gt;
&lt;p&gt;North: North Y Street&lt;br /&gt;
South: South Y Street&lt;br /&gt;
East: East X Street&lt;br /&gt;
West: West X Street&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;look sign&lt;/strong&gt;&lt;br /&gt;
The welcome sign reads, "Welcome to this text adventure demo. You can type&lt;br /&gt;
"help" for a list of commands to use. Be sure to check out Al's cool programming&lt;br /&gt;
books at http://inventwithpython.com"&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;look fountain&lt;/strong&gt;&lt;br /&gt;
The water in the fountain is a bright green color. Is that... gatorade?&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;north&lt;/strong&gt;&lt;br /&gt;
You move to the north.&lt;br /&gt;
North Y Street&lt;br /&gt;
==============&lt;br /&gt;
The northern end of Y Street has really gone down hill. Pot holes are everywhere, as are stray cats, rats, and wombats.&lt;/p&gt;
&lt;p&gt;A sign stands here, not bolted to the ground.&lt;/p&gt;
&lt;p&gt;South: Town Square&lt;br /&gt;
East: Bakery&lt;br /&gt;
West: Thief Guild&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;look sign&lt;/strong&gt;&lt;br /&gt;
The sign reads, "Do Not Take This Sign"&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;take sign&lt;/strong&gt;&lt;br /&gt;
You take a sign.&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;inventory&lt;/strong&gt;&lt;br /&gt;
Inventory:&lt;br /&gt;
Do Not Take Sign Sign&lt;br /&gt;
Donut&lt;br /&gt;
Sword&lt;br /&gt;
README Note&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;look readme&lt;/strong&gt;&lt;br /&gt;
The README note reads, "Welcome to the text adventure demo. Be sure to check out the source code to see how this game is put together."&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;eat donut&lt;/strong&gt;&lt;br /&gt;
You eat a donut&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;eat readme&lt;/strong&gt;&lt;br /&gt;
You eat a README note&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;eat sword&lt;/strong&gt;&lt;br /&gt;
You cannot eat that.&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;inventory&lt;/strong&gt;&lt;br /&gt;
Inventory:&lt;br /&gt;
Sword&lt;br /&gt;
Do Not Take Sign Sign&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;e&lt;/strong&gt;&lt;br /&gt;
You move to the east.&lt;br /&gt;
Bakery&lt;br /&gt;
======&lt;br /&gt;
The delightful smell of meat pies fills the air, making you hungry. The baker flashes a grin, as he slides a box marked "Not Human Organs" under a table with his foot.&lt;/p&gt;
&lt;p&gt;A "Shopping HOWTO" note rests on the ground.&lt;/p&gt;
&lt;p&gt;South: East X Street&lt;br /&gt;
West: North Y Street&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;list&lt;/strong&gt;&lt;br /&gt;
For sale:&lt;br /&gt;
- Meat Pie&lt;br /&gt;
- Donut&lt;br /&gt;
- Bagel&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;buy pie&lt;/strong&gt;&lt;br /&gt;
You have purchased a meat pie&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;buy pie&lt;/strong&gt;&lt;br /&gt;
You have purchased a meat pie&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;buy pie&lt;/strong&gt;&lt;br /&gt;
You have purchased a meat pie&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;inventory&lt;/strong&gt;&lt;br /&gt;
Inventory:&lt;br /&gt;
Meat Pie (3)&lt;br /&gt;
Sword&lt;br /&gt;
Do Not Take Sign Sign&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;quit&lt;/strong&gt;&lt;br /&gt;
Thanks for playing!&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3&gt;The "Choose Your Own Adventure" Mistake&lt;/h3&gt;
&lt;p&gt;Beginners tend to write code that looks like a "Choose Your Own Adventure" book: the execution starts at the beginning of the program, then it jumps to another part of the code (just like the reader would turn to some page at a branching point in the book). &lt;a href="http://inventwithpython.com/extra/dragon2.py"&gt;I have an example of this type of program in my dragon2.py game.&lt;/a&gt; This works, but the program becomes unwieldy as it gets larger.&lt;/p&gt;
&lt;p&gt;You won't have a &lt;code&gt;townSquare()&lt;/code&gt; function that displays the Town Square text when called like so:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;townSquare&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;The town square is a large open space with a fountain in the center. Streets lead in all directions.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;TOWN_SQUARE_HAS_SIGN&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;A welcome sign stands here.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;TOWN_SQUARE_HAS_FOUNTAIN&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getFountainDescription&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;North: North Y Street&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;South: South Y Street&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;East: East X Street&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;West: West X Street&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;A better way to program a game is a more &lt;strong&gt;data structure-centric&lt;/strong&gt; approach. This sounds very abstract. Here's an example of the data structure you'll have for locations in your game:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;Town Square&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The town square is a large open space with a fountain in the center. Streets lead in all directions.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;NORTH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;North Y Street&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;EAST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;East X Street&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SOUTH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;South Y Street&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;WEST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;West X Street&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;GROUND&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Welcome Sign&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Fountain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;North Y Street&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The northern end of Y Street has really gone down hill. Pot holes are everywhere, as are stray cats, rats, and wombats.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;WEST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Thief Guild&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;EAST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Bakery&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SOUTH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Town Square&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;GROUND&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Do Not Take Sign Sign&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can see that the general structure of the &lt;code&gt;worldRooms&lt;/code&gt; dictionary is:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;Room Name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Description of room.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;NORTH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Name of room to the north.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;GROUND&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Name of an item on the ground in this room&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Another item name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And here's a data structure to hold the different types of items in the game:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;worldItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;Welcome Sign&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;GROUNDDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;A welcome sign stands here.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SHORTDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a welcome sign&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;LONGDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The welcome sign reads, &amp;quot;Welcome to this text adventure demo. You can type &amp;quot;help&amp;quot; for a list of commands to use. Be sure to check out Al&lt;/span&gt;&lt;span class="se"&gt;\&amp;#39;&lt;/span&gt;&lt;span class="s"&gt;s cool programming books at http://inventwithpython.com&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;TAKEABLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;welcome&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;sign&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;Fountain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;GROUNDDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;A bubbling fountain of green water.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SHORTDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a fountain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;LONGDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The water in the fountain is a bright green color. Is that... gatorade?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;TAKEABLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;fountain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can see that the general structure of the &lt;code&gt;worldRooms&lt;/code&gt; dictionary is:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;worldItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;Item Name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;GROUNDDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;How this item is described when on the ground.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SHORTDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;A short description of this item.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;LONGDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;A long description of this item, used when the player looks at it.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;TAKEABLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;# whether this item can be taken and put in your inventory&lt;/span&gt;
        &lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a word the player can use to refer to this item&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;another word&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;By putting the rooms and items into data structures, now you can write a &lt;strong&gt;single&lt;/strong&gt; &lt;code&gt;displayLocation()&lt;/code&gt; function that can display &lt;em&gt;any&lt;/em&gt; room in the game because it works off of the values in the &lt;code&gt;worldRooms&lt;/code&gt; data structure. You can write code that deals with rooms and items generically. Writing a big game world will be much faster, just like how interchangeable parts speed up manufacturing and factories.&lt;/p&gt;
&lt;p&gt;A map of the full world looks something like this:&lt;/p&gt;
&lt;pre&gt;        +---------+    +---------+
        | Thief   O    | Bakery  |
        | Guild   |    |         |
+------++------O--+    +----O----+
| Used |
|Anvils|        Town Square     +--------+
|      O                        |Obs Deck|
+------++----O----+    +----O----/  /
        | Black-  O    | Wizard /  /
        | smith   |    | Tower    /
        +---------+    +---------+&lt;/pre&gt;
&lt;p&gt;(Data structures like these are normally put into classes in objet-oriented programming. To keep this text adventure demo simple, I'm using dictionaries and lists.)&lt;/p&gt;
&lt;h3&gt;Constant Variables and Global Variables&lt;/h3&gt;
&lt;p&gt;Start from scratch with a blank &lt;code&gt;textadventuredemo.py&lt;/code&gt; file. Add a &lt;code&gt;#! python3&lt;/code&gt; &lt;a href="https://stackoverflow.com/questions/2429511/why-do-people-write-usr-bin-env-python-on-the-first-line-of-a-python-script"&gt;shebang line&lt;/a&gt;, some comments describing the program, and the &lt;code&gt;worldRooms&lt;/code&gt; and &lt;code&gt;worldItems&lt;/code&gt; data structures. Since this is mostly text and not code, you might want to just copy and paste the code from &lt;a href="https://raw.githubusercontent.com/asweigart/textadventuredemo/master/snippets/datastructures.py"&gt;the snippet on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The keys in the above data structures are constant variables:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;DESC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;desc&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;NORTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;north&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;SOUTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;south&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;EAST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;east&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;WEST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;west&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;UP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;up&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;DOWN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;down&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;GROUND&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;ground&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;SHOP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;shop&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;GROUNDDESC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;grounddesc&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;SHORTDESC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;shortdesc&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;LONGDESC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;longdesc&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;TAKEABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;takeable&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;EDIBLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;edible&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;DESCWORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;descwords&amp;#39;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The reason that constant variables are used instead of typing the string directly is to minimize the chance of errors. If you mistype the string &lt;code&gt;'desc'&lt;/code&gt; as &lt;code&gt;'dsec'&lt;/code&gt;, Python will not immediately complain because since &lt;code&gt;'dsec'&lt;/code&gt; is a valid string. Later there will be a KeyError when you try to use &lt;code&gt;'dsec'&lt;/code&gt; as a key, but this is a vague error message that could have many causes.&lt;/p&gt;
&lt;p&gt;But if you are using constant variables instead, mistyping &lt;code&gt;DESC&lt;/code&gt; as &lt;code&gt;DSEC&lt;/code&gt; will immediately crash the program with a NameError exception since &lt;code&gt;DSEC&lt;/code&gt; isn't a variable name. &lt;strong&gt;The sooner your program crashes, the easier it is to find the crash-causing bug.&lt;/strong&gt; If your program silently continued to work, it will take longer to trace the original bug.&lt;/p&gt;
&lt;p&gt;There is also the &lt;code&gt;SCREEN_WIDTH&lt;/code&gt; constant, which will be used by the &lt;code&gt;textwrap&lt;/code&gt; module to determine how wide the program should assume the screen is:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;SCREEN_WIDTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Th &lt;code&gt;SCREEN_WIDTH&lt;/code&gt; constant will be explained in the next section.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;location&lt;/code&gt; and &lt;code&gt;inventory&lt;/code&gt; global variables will keep track of the player's position and inventory. The &lt;code&gt;showFullExits&lt;/code&gt; variable tracks whether to show full or brief versions of the current room's exits (this is explained later in the &lt;code&gt;displayLocation()&lt;/code&gt; function). Add the following code to your program:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Town Square&amp;#39;&lt;/span&gt; &lt;span class="c"&gt;# start in town square&lt;/span&gt;
&lt;span class="n"&gt;inventory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;README Note&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Sword&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Donut&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# start with blank inventory&lt;/span&gt;
&lt;span class="n"&gt;showFullExits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;(Normally your programs should avoid using global variables, but they're used here to keep the text adventure demo code simple.)&lt;/p&gt;
&lt;p&gt;Also, import the following modules into you program:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;textwrap&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;The textwrap Module&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;textwrap&lt;/code&gt; module can intelligently break up a string into multiple lines of a given width. You might think this is easily done with code such as the following, which breaks the &lt;code&gt;desc&lt;/code&gt; string up into lines 80 characters wide:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The town square is a large open space with a fountain in the center. Streets lead in all directions.&amp;#39;&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;But this can cut off a line right in the middle of words, like this:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;The town square is a large open space with a fountain in the center. Streets lea
d in all directions.&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;textwrap.wrap()&lt;/code&gt; function is smart enough to avoid chopping lines in the middle of a word. It returns a list, with each line as a string in the list:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;textwrap&lt;/span&gt;
&lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The town square is a large open space with a fountain in the center. Streets lead in all directions.&amp;#39;&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;textwrap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;The town square is a large open space with a fountain in the center. Streets
lead in all directions.&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To make it easy to change the screen width that &lt;code&gt;textwrap.wrap()&lt;/code&gt; uses, pass the &lt;code&gt;SCREEN_WIDTH&lt;/code&gt; constant for the second argument. That way, if you want to change the screen width you only need to change the &lt;code&gt;SCREEN_WIDTH&lt;/code&gt; value.&lt;/p&gt;
&lt;h3&gt;Location and Movement Functions&lt;/h3&gt;
&lt;p&gt;When the player walks into a room, the displayed text looks something like this:&lt;/p&gt;
&lt;blockquote style="font-family: monospace;"&gt;&lt;p&gt;Town Square&lt;br /&gt;
===========&lt;br /&gt;
The town square is a large open space with a fountain in the center. Streets&lt;br /&gt;
lead in all directions.&lt;/p&gt;
&lt;p&gt;A welcome sign stands here.&lt;br /&gt;
A bubbling fountain of green water.&lt;/p&gt;
&lt;p&gt;North: North Y Street&lt;br /&gt;
South: South Y Street&lt;br /&gt;
East: East X Street&lt;br /&gt;
West: West X Street
&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The data for each room is tied up in the &lt;code&gt;worldRooms&lt;/code&gt; variable. You need a function that, given the name of the room as a string, will print out text like the above. This will need to print out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The room's name&lt;/li&gt;
&lt;li&gt;The room's description&lt;/li&gt;
&lt;li&gt;A list of the items on the ground in this room&lt;/li&gt;
&lt;li&gt;A list of all the available exits&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Add the following code to your program:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;displayLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;A helper function for displaying an area&amp;#39;s description and exits.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="c"&gt;# Print the room name.&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;=&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;# Print the room&amp;#39;s description (using textwrap.wrap())&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textwrap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;SCREEN_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="c"&gt;# Print all the items on the ground.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;GROUND&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;GROUND&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;GROUNDDESC&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c"&gt;# Print all the exits.&lt;/span&gt;
    &lt;span class="n"&gt;exits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NORTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SOUTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EAST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DOWN&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;exits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;showFullExits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NORTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SOUTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EAST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DOWN&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Exits: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="s"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exits&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There will also be a &lt;code&gt;moveDirection()&lt;/code&gt; function that, given a north/south/east/west/up/down string argument, will change the &lt;code&gt;location&lt;/code&gt; global variable to the new location.&lt;/p&gt;
&lt;p&gt;But it will only do this if the direction was a valid one to make. If the player is able to move in the given direction, the new room's description will be displayed on the screen. Add the following code to your program:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;moveDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;A helper function that changes the location of the player.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;You move to the &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;displayLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;You cannot move in that direction&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You'll see how these functions are used by the command line interface in a bit. But if you want to see how the code you have so far works, add the following temporary code:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="c"&gt;# TEMPORARY CODE:&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;displayLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;quit&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NORTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SOUTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EAST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DOWN&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;moveDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Your program will now look like &lt;a href="https://raw.githubusercontent.com/asweigart/textadventuredemo/master/snippets/movement.py"&gt;this source code on GitHub&lt;/a&gt;. When you run this program, you will be able to move around the game world by typing &lt;code&gt;north&lt;/code&gt;, &lt;code&gt;south&lt;/code&gt;, &lt;code&gt;east&lt;/code&gt;, &lt;code&gt;west&lt;/code&gt;, &lt;code&gt;up&lt;/code&gt;, and &lt;code&gt;down&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Notice a few things about this game world:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Moving in an invalid direction results in &lt;code&gt;'You cannot move in that direction'&lt;/code&gt; printed to the screen.&lt;/li&gt;
&lt;li&gt;At the top of the wizard's tower, the "Magical Escalator to Nowhere" room's up exit &lt;em&gt;leads to itself&lt;/em&gt;. This means if you exit up from this room, you will arrive in the same room. You can effectively go up an infinite amount of times. This is a side effect of the way the room data structures are set up.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are just a few more helper functions you'll need.&lt;/p&gt;
&lt;h3&gt;Item Helper Functions&lt;/h3&gt;
&lt;p&gt;These functions all work with the item data structure you have already set up. You won't quite see how these functions are needed right now, but they will be explained later.&lt;/p&gt;
&lt;p&gt;Remember that the &lt;code&gt;worldItems&lt;/code&gt; variable is a dictionary with key-value pairs that look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="s"&gt;&amp;#39;Fountain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;GROUNDDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;A bubbling fountain of green water.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SHORTDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a fountain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;LONGDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The water in the fountain is a bright green color. Is that... gatorade?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;TAKEABLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;fountain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The helper functions for items are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;getAllDescWords()&lt;/code&gt; - Given a list of strings of item names, return a list with all of these items' DESCWORDS strings.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getAllFirstDescWords()&lt;/code&gt; - Given a list of strings of item names, return a list with all of these items' first DESCWORDS strings.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getFirstItemMatchingDesc()&lt;/code&gt; - Given a string and a list of item names, return a string of the name of the first item that has a DESCWORD string that matches the given string argument.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getAllItemsMatchingDesc()&lt;/code&gt; - Given a string and a list of item names, return a list of strings of item names for items that have the given string argument as one of their DESCWORD strings.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Add the following code to your programs:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getAllDescWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Returns a list of &amp;quot;description words&amp;quot; for each item named in itemList.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;itemList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# make itemList unique&lt;/span&gt;
    &lt;span class="n"&gt;descWords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;descWords&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descWords&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getAllFirstDescWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Returns a list of the first &amp;quot;description word&amp;quot; in the list of&lt;/span&gt;
&lt;span class="sd"&gt;    description words for each item named in itemList.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;itemList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# make itemList unique&lt;/span&gt;
    &lt;span class="n"&gt;descWords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;descWords&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descWords&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getFirstItemMatchingDesc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;itemList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# make itemList unique&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getAllItemsMatchingDesc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;itemList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# make itemList unique&lt;/span&gt;
    &lt;span class="n"&gt;matchingItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;matchingItems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;matchingItems&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;list(set(itemList))&lt;/code&gt; code is a Pythonic way to get rid of duplicate values in a list by coverting the list to a set and back to a list. Note that this might change the order of the values in the list, since the set data type is unordered.&lt;/p&gt;
&lt;h3&gt;Creating a Command Line Interface with the cmd Module&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;cmd&lt;/code&gt; module frees us from reinventing the wheel when creating the command line interface for the text adventure. First, create a class that subclasses &lt;code&gt;cmd.Cmd&lt;/code&gt;. The &lt;code&gt;cmd&lt;/code&gt; module will read any methods that begin with &lt;code&gt;do_&lt;/code&gt;, &lt;code&gt;complete_&lt;/code&gt; and &lt;code&gt;help_&lt;/code&gt; for it's command, tab-completion, and help system features, respectively.&lt;/p&gt;
&lt;p&gt;Here's a short example. Add the following code to your program:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TextAdventureCmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cmd&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;gt; &amp;#39;&lt;/span&gt;

    &lt;span class="c"&gt;# The default() method is called when none of the other do_*() command methods match.&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;I do not understand that command. Type &amp;quot;help&amp;quot; for a list of commands.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# A very simple &amp;quot;quit&amp;quot; command to terminate the program:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_quit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Quit the game.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt; &lt;span class="c"&gt;# this exits the Cmd application loop in TextAdventureCmd.cmdloop()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;help_combat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Combat is not implemented in this program.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There are several cmd-specific things about this code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The string in the &lt;code&gt;prompt&lt;/code&gt; member variable is printed when the player is expected to begin typing in a command. In your text adventure's case, this will be the &lt;code&gt;'\n&gt;'&lt;/code&gt; string: a newline followed by a &amp;gt; character.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;default()&lt;/code&gt; method will be called by the command line interface when it cannot understand the command the player typed. In this case, a "I do not understand" message is printed to the screen.&lt;/li&gt;
&lt;li&gt;When the player types in a command, the command line interface checks if there are any methods of the same name as the command to handle it. For example, "quit" would be handled by &lt;code&gt;do_quit()&lt;/code&gt;. If the player entered the "qwerty" command but there is no &lt;code&gt;do_qwerty()&lt;/code&gt; method, the &lt;code&gt;default()&lt;/code&gt; method is called instead.&lt;/li&gt;
&lt;li&gt;When a &lt;code&gt;do_&amp;lt;command&amp;gt;()&lt;/code&gt; returns, the command line interface will let the player type in another command. If a &lt;code&gt;do_&amp;lt;command&amp;gt;()&lt;/code&gt; returns the value &lt;code&gt;True&lt;/code&gt;, the command line interface will stop asking for commands.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;do_quit()&lt;/code&gt; method implements the "quit" command. Since it returns &lt;code&gt;True&lt;/code&gt;, the command line interface will stop asking the player for commands.&lt;/li&gt;
&lt;li&gt;The docstring at the top of each &lt;code&gt;do_&amp;lt;command&amp;gt;()&lt;/code&gt; function will automatically be used for the command line interface's help system.
&lt;li&gt;The &lt;code&gt;help_combat()&lt;/code&gt; method implements the "help combat" command. You can add as many help topics as you want: typing "help" by itself lists all of them. Since there is no combat in this text adventure, this function prints a message telling the player there is no combat.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;arg&lt;/code&gt; parameter in &lt;code&gt;default()&lt;/code&gt; and &lt;code&gt;do_quit()&lt;/code&gt; will be explained later.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To kick off the command line interface code, you will need to call the &lt;code&gt;cmd.Cmd&lt;/code&gt; object's &lt;code&gt;cmdloop()&lt;/code&gt; method. Take out the temporary code in the &lt;code&gt;while True:&lt;/code&gt; loop and add the following to your program:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Text Adventure Demo!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;====================&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;(Type &amp;quot;help&amp;quot; for commands.)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;displayLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;TextAdventureCmd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmdloop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Thanks for playing!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Your program should now look like &lt;a href="https://raw.githubusercontent.com/asweigart/textadventuredemo/master/snippets/basiccmdloop.py"&gt;this file on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;The cmd Help System&lt;/h3&gt;
&lt;p&gt;When you run this program, the movement commands won't work (they were implemented in the temporary &lt;code&gt;while&lt;/code&gt; loop you removed) but you can run the "help", "help quit", and "help combat" commands:&lt;/p&gt;
&lt;blockquote style="font-family: monospace;"&gt;&lt;p&gt;&amp;gt; &lt;strong&gt;help&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Documented commands (type help &lt;topic&gt;):&lt;br /&gt;
========================================&lt;br /&gt;
help  quit&lt;/p&gt;
&lt;p&gt;Miscellaneous help topics:&lt;br /&gt;
==========================&lt;br /&gt;
combat&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;help quit&lt;/strong&gt;&lt;br /&gt;
Quit the game.&lt;/p&gt;
&lt;p&gt;&amp;gt; &lt;strong&gt;help combat&lt;/strong&gt;&lt;br /&gt;
Combat is not implemented in this program.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Notice that the "quit" command's docstring has been used for the "help quit" command. You can end this program by running "quit", which makes the command line interface call the &lt;code&gt;do_quit()&lt;/code&gt; method. Since it is a &lt;code&gt;do_&amp;lt;command&amp;gt;()&lt;/code&gt; method that returns &lt;code&gt;True&lt;/code&gt;, it causes the &lt;code&gt;TextAdventureCmd().cmdloop()&lt;/code&gt; function return and the execution reaches the end of the source code.&lt;/p&gt;
&lt;p&gt;As a free feature provided by the command line interface, pressing the up arrow key will cycle through the command history. This provides an easy way to re-enter commands you previously typed.&lt;/p&gt;
&lt;h3&gt;The Move Commands and Tab Completion&lt;/h3&gt;
&lt;p&gt;In order to move the player around, you'll need a &lt;code&gt;do_north()&lt;/code&gt; method for the "north" command, a &lt;code&gt;do_south()&lt;/code&gt; method for the "south" command, and so on. All of these methods will simply call the &lt;code&gt;moveDirection()&lt;/code&gt; function with the appropriate argument. Add the following to your program:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="c"&gt;# These direction commands have a long (i.e. north) and show (i.e. n) form.&lt;/span&gt;
    &lt;span class="c"&gt;# Since the code is basically the same, I put it in the moveDirection()&lt;/span&gt;
    &lt;span class="c"&gt;# function.&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_north&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Go to the area to the north, if possible.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;moveDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;north&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_south&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Go to the area to the south, if possible.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;moveDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;south&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_east&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Go to the area to the east, if possible.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;moveDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;east&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_west&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Go to the area to the west, if possible.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;moveDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;west&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Go to the area upwards, if possible.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;moveDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;up&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_down&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Go to the area downwards, if possible.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;moveDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;down&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Since the code is the exact same, we can just copy the&lt;/span&gt;
    &lt;span class="c"&gt;# methods with shortened names:&lt;/span&gt;
    &lt;span class="n"&gt;do_n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;do_north&lt;/span&gt;
    &lt;span class="n"&gt;do_s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;do_south&lt;/span&gt;
    &lt;span class="n"&gt;do_e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;do_east&lt;/span&gt;
    &lt;span class="n"&gt;do_w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;do_west&lt;/span&gt;
    &lt;span class="n"&gt;do_u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;do_up&lt;/span&gt;
    &lt;span class="n"&gt;do_d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;do_down&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Because the "n" and "north" commands would do the same thing, we can create another function with the same code by assigning &lt;code&gt;do_n = do_north&lt;/code&gt;, and so on.&lt;/p&gt;
&lt;p&gt;To toggle the Boolean value in the global &lt;code&gt;showFullExits&lt;/code&gt; variable, an "exits" command can be implemented in &lt;code&gt;do_exits()&lt;/code&gt;. Add the following to your program:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_exits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Toggle showing full exit descriptions or brief exit descriptions.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;showFullExits&lt;/span&gt;
        &lt;span class="n"&gt;showFullExits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;showFullExits&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;showFullExits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Showing full exit descriptions.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Showing brief exit descriptions.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With this code, your program should look like &lt;a href="https://raw.githubusercontent.com/asweigart/textadventuredemo/master/snippets/cmdmovement.py"&gt;this file on GitHub&lt;/a&gt;. When you run this program, the north/south/east/west/up/down commands will all work.&lt;/p&gt;
&lt;h3&gt;Inventory&lt;/h3&gt;
&lt;p&gt;The "inventory" command will display the contents of the &lt;code&gt;inventory&lt;/code&gt; global variable. Rather than just a &lt;code&gt;do_inventory()&lt;/code&gt; method that runs &lt;code&gt;print(inventory)&lt;/code&gt;, there's additional code that checks if the player has more than one of a type of item. So an &lt;code&gt;inventory&lt;/code&gt; value of &lt;code&gt;['Donut', 'Donut', 'Donut']&lt;/code&gt; will display a single line: &lt;code&gt;Donut (3)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Add the following code to your program:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_inventory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Display a list of the items in your possession.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Inventory:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;  (nothing)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="c"&gt;# first get a count of each distinct item in the inventory&lt;/span&gt;
        &lt;span class="n"&gt;itemCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;itemCount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="n"&gt;itemCount&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;itemCount&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="c"&gt;# get a list of inventory items with duplicates removed:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Inventory:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;itemCount&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;  &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;)&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;itemCount&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;  &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;do_inv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;do_inventory&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To add an identical "inv" shortcut command, the &lt;code&gt;do_inv = do_inventory&lt;/code&gt; line is added after the &lt;code&gt;do_inventory()&lt;/code&gt; function.&lt;/p&gt;
&lt;h3&gt;Taking and Dropping Items&lt;/h3&gt;
&lt;p&gt;Every room in the &lt;code&gt;worldRooms&lt;/code&gt; data structure has a &lt;code&gt;GROUND&lt;/code&gt; key whose value is a list of items on the ground of that room. By removing items from the &lt;code&gt;inventory&lt;/code&gt; list and adding them to the &lt;code&gt;GROUND&lt;/code&gt; list, the player can "drop" the item in a certain room. The items will continue to be on the ground even if the player leaves the room and comes back. Similarly, by removing a value from the &lt;code&gt;GROUND&lt;/code&gt; list and adding it to the &lt;code&gt;inventory&lt;/code&gt; list, the player can "take" the item.&lt;/p&gt;
&lt;p&gt;The "take" and "drop" commands are a bit more complicated: There is text that follows the "take" and "drop" words such as in "take sign" or "drop sword". The additional text is passed to the &lt;code&gt;do_&amp;lt;command&amp;gt;()&lt;/code&gt; function for its &lt;code&gt;arg&lt;/code&gt; parameter. So if the player enters the command "drop sword", the &lt;code&gt;do_drop()&lt;/code&gt; method is called with &lt;code&gt;'sword'&lt;/code&gt; passed for its &lt;code&gt;arg&lt;/code&gt; parameter.&lt;/p&gt;
&lt;p&gt;The drop and take methods will have to do more than modify &lt;code&gt;inventory&lt;/code&gt; and the &lt;code&gt;GROUND&lt;/code&gt; key's value. They must handle the player forgetting to specify what to take/drop, check that the item is actually on the ground/inventory, and if the object is "takeable" (that is, it's &lt;code&gt;TAKEABLE&lt;/code&gt; key's value is &lt;code&gt;True&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Also, items can be referred to by any of it's &lt;code&gt;DESCWORDS&lt;/code&gt; from the &lt;code&gt;worldItems&lt;/code&gt; data structure. For example, the sword item:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="s"&gt;&amp;#39;Sword&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;GROUNDDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;A sword lies on the ground.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SHORTDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a sword&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;LONGDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;A longsword, engraved with the word, &amp;quot;Exkaleber&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;sword&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;exkaleber&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;longsword&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]},}&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;...has a &lt;code&gt;DESCWORDS&lt;/code&gt; value of &lt;code&gt;['sword', 'exkaleber', 'longsword']&lt;/code&gt;. So the "drop sword", "drop exkaleer", and "drop longsword" would all refer to the same sword to drop.&lt;/p&gt;
&lt;p&gt;Add the following code to your program:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&amp;quot;take &amp;lt;item&amp;gt; - Take an item on the ground.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="c"&gt;# put this value in a more suitably named variable&lt;/span&gt;
        &lt;span class="n"&gt;itemToTake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;itemToTake&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Take what? Type &amp;quot;look&amp;quot; the items on the ground here.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="n"&gt;cantTake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

        &lt;span class="c"&gt;# get the item name that the player&amp;#39;s command describes&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;getAllItemsMatchingDesc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemToTake&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;GROUND&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAKEABLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;cantTake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt; &lt;span class="c"&gt;# there may be other items named this that you can take, so we continue checking&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;You take &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;SHORTDESC&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
            &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;GROUND&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# remove from the ground&lt;/span&gt;
            &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# add to inventory&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cantTake&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;You cannot take &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemToTake&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;That is not on the ground.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&amp;quot;drop &amp;lt;item&amp;gt; - Drop an item from your inventory onto the ground.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="c"&gt;# put this value in a more suitably named variable&lt;/span&gt;
        &lt;span class="n"&gt;itemToDrop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c"&gt;# get a list of all &amp;quot;description words&amp;quot; for each item in the inventory&lt;/span&gt;
        &lt;span class="n"&gt;invDescWords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getAllDescWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# find out if the player doesn&amp;#39;t have that item&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;itemToDrop&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;invDescWords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;You do not have &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; in your inventory.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemToDrop&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="c"&gt;# get the item name that the player&amp;#39;s command describes&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getFirstItemMatchingDesc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemToDrop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;You drop &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;SHORTDESC&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
            &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# remove from inventory&lt;/span&gt;
            &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;GROUND&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# add to the ground&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After you are finished, your program will look like &lt;a href="https://raw.githubusercontent.com/asweigart/textadventuredemo/master/snippets/takedrop.py"&gt;this file on GitHub&lt;/a&gt;. When you run the program, you will be able to drop and take items (provided they have the &lt;code&gt;TAKEABLE&lt;/code&gt; setting).&lt;/p&gt;
&lt;h3&gt;Tab Completion&lt;/h3&gt;
&lt;p&gt;if the player types a partial command, such as "nor", they can press the Tab key and the command line interface will complete the command: "north". The command line  interface knows this because "north" is the only command that begins with "nor". If there were other commands that began with "nor", pressing Tab would bring up a list of possible complete commands.&lt;/p&gt;
&lt;p&gt;However, say that the player has an &lt;code&gt;inventory&lt;/code&gt; value of &lt;code&gt;['sword', 'swingset', 'swampwater']&lt;/code&gt; and had entered "drop sw" and pressed Tab. The command line interface knows all the commands because it can see what &lt;code&gt;do_%lt;command&amp;gt;()&lt;/code&gt; functions there are. But items on the ground or in the inventory are specific to your program, so you will need a way to tell the command line interface what it should return for possible tab completions.&lt;/p&gt;
&lt;p&gt;This is done by the &lt;code&gt;complete_&amp;lt;command&amp;gt;()&lt;/code&gt; methods. These methods have the following parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;text&lt;/code&gt; is the part after the command. If "drop sw" were being completed, &lt;code&gt;text&lt;/code&gt; would be set to &lt;code&gt;'sw'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;line&lt;/code&gt; is the entire command that was entered. If "drop sw" were being completed, &lt;code&gt;line&lt;/code&gt; would be set to &lt;code&gt;'drop sw'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;begidx&lt;/code&gt; is index in &lt;code&gt;line&lt;/code&gt; where the last word begins. If "drop sw" were being completed, &lt;code&gt;begidx&lt;/code&gt; would be &lt;code&gt;5&lt;/code&gt;, which is where &lt;code&gt;'sw'&lt;/code&gt; begins.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;endidx&lt;/code&gt; is index in &lt;code&gt;line&lt;/code&gt; where the last word begins. If "drop sw" were being completed, &lt;code&gt;endidx&lt;/code&gt; would be &lt;code&gt;7&lt;/code&gt;, which is where &lt;code&gt;'sw'&lt;/code&gt; ends.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When the Tab key is pressed, the command's &lt;code&gt;complete_&amp;lt;command&amp;gt;()&lt;/code&gt; method is called and passed the command typed in so far. For example, typing "drop sw"-Tab will call the &lt;code&gt;complete_drop()&lt;/code&gt; method and typing "take sw"-Tab will call the &lt;code&gt;complete_take()&lt;/code&gt; method. If a &lt;code&gt;complete_&amp;lt;command&amp;gt;()&lt;/code&gt; method command doesn't exist, such as not &lt;code&gt;complete_qwerty()&lt;/code&gt; method for "qwerty sw", then the command line interface does nothing.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;getAllFirstDescWords()&lt;/code&gt; helper function, you can add &lt;code&gt;complete_take()&lt;/code&gt; and &lt;code&gt;complete_drop()&lt;/code&gt; methods to determine what item the player is trying to take or drop. Add the following to your program:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complete_take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;begidx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endidx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;possibleItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c"&gt;# if the user has only typed &amp;quot;take&amp;quot; but no item name:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;getAllFirstDescWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;GROUND&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="c"&gt;# otherwise, get a list of all &amp;quot;description words&amp;quot; for ground items matching the command text so far:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;GROUND&lt;/span&gt;&lt;span class="p"&gt;])):&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TAKEABLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# make list unique&lt;/span&gt;


    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complete_drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;begidx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endidx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;possibleItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;itemToDrop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c"&gt;# get a list of all &amp;quot;description words&amp;quot; for each item in the inventory&lt;/span&gt;
        &lt;span class="n"&gt;invDescWords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getAllDescWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;invDescWords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;drop &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="c"&gt;# command is complete&lt;/span&gt;

        &lt;span class="c"&gt;# if the user has only typed &amp;quot;drop&amp;quot; but no item name:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;itemToDrop&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;getAllFirstDescWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# otherwise, get a list of all &amp;quot;description words&amp;quot; for inventory items matching the command text so far:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;invDescWords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# make list unique&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Your program will look like &lt;a href="https://raw.githubusercontent.com/asweigart/textadventuredemo/master/snippets/takedrop_complete.py"&gt;this file on GitHub&lt;/a&gt;. When you run the program, you will be able to type "drop sw", then press Tab, and the command will complete to "drop sword".&lt;/p&gt;
&lt;h3&gt;Looking at Things&lt;/h3&gt;
&lt;p&gt;The "look" command will have similar &lt;code&gt;do_look()&lt;/code&gt; and &lt;code&gt;complete_look()&lt;/code&gt; methods. Typing "look" will print the current room's &lt;code&gt;DEC&lt;/code&gt; key's value. Typing "look exits" will print the names of all the adjacent rooms, while "look &lt;direction&gt;" will print the name of the room in that direction. The player can also type "look &lt;item&gt;" to look at a item that is either on the ground or in their inventory.&lt;/p&gt;
&lt;p&gt;Enter the following code for the "look" command:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_look&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Look at an item, direction, or the area:&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;look&amp;quot; - display the current area&amp;#39;s description&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;look &amp;lt;direction&amp;gt;&amp;quot; - display the description of the area in that direction&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;look exits&amp;quot; - display the description of all adjacent areas&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;look &amp;lt;item&amp;gt;&amp;quot; - display the description of an item on the ground or in your inventory&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="n"&gt;lookingAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lookingAt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c"&gt;# &amp;quot;look&amp;quot; will re-print the area description&lt;/span&gt;
            &lt;span class="n"&gt;displayLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lookingAt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;exits&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NORTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SOUTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EAST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DOWN&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lookingAt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;north&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;west&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;east&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;south&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;up&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;down&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;e&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lookingAt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;NORTH&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;NORTH&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;lookingAt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;WEST&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;WEST&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;lookingAt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;e&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;EAST&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;EAST&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;lookingAt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;SOUTH&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;SOUTH&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;lookingAt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;UP&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;UP&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;lookingAt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;DOWN&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;DOWN&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;There is nothing in that direction.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="c"&gt;# see if the item being looked at is on the ground at this location&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getFirstItemMatchingDesc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lookingAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;GROUND&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textwrap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;LONGDESC&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;SCREEN_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="c"&gt;# see if the item being looked at is in the inventory&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getFirstItemMatchingDesc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lookingAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textwrap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;LONGDESC&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;SCREEN_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;You do not see that nearby.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complete_look&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;begidx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endidx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;possibleItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;lookingAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c"&gt;# get a list of all &amp;quot;description words&amp;quot; for each item in the inventory&lt;/span&gt;
        &lt;span class="n"&gt;invDescWords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getAllDescWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;groundDescWords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getAllDescWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;GROUND&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;shopDescWords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getAllDescWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SHOP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]))&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;invDescWords&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;groundDescWords&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;shopDescWords&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;NORTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SOUTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EAST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DOWN&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;look &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="c"&gt;# command is complete&lt;/span&gt;

        &lt;span class="c"&gt;# if the user has only typed &amp;quot;look&amp;quot; but no item name, show all items on ground, shop and directions:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lookingAt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getAllFirstDescWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;GROUND&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
            &lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getAllFirstDescWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SHOP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])))&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NORTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SOUTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EAST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DOWN&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# make list unique&lt;/span&gt;

        &lt;span class="c"&gt;# otherwise, get a list of all &amp;quot;description words&amp;quot; for ground items matching the command text so far:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;groundDescWords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lookingAt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# otherwise, get a list of all &amp;quot;description words&amp;quot; for items for sale at the shop (if this is one):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;shopDescWords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lookingAt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# check for matching directions&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NORTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SOUTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EAST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DOWN&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lookingAt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# get a list of all &amp;quot;description words&amp;quot; for inventory items matching the command text so far:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;invDescWords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lookingAt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# make list unique&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The program will now look like &lt;a href="https://raw.githubusercontent.com/asweigart/textadventuredemo/master/snippets/look.py"&gt;this file on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Shops&lt;/h3&gt;
&lt;p&gt;Shops are rooms that have a &lt;code&gt;SHOP&lt;/code&gt; key, such as the Bakery room:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="s"&gt;&amp;#39;Bakery&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The delightful smell of meat pies fills the air, making you hungry. The baker flashes a grin, as he slides a box marked &amp;quot;Not Human Organs&amp;quot; under a table with his foot.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;WEST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;North Y Street&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SOUTH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;East X Street&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SHOP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Meat Pie&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Donut&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Bagel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;GROUND&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Shop Howto&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The value for the &lt;code&gt;SHOP&lt;/code&gt; is a list of items that the shop sells. Inside these rooms, the player can run the "list" command (to see what is for sale), the "buy" command (to purchase an item), and the "sell" command (to pawn an item from the player's inventory). For simplicity, any item can be sold to any shop. Also, money is currently not implemented in this game, so items in the shop are free.&lt;/p&gt;
&lt;p&gt;Several things must be checked when the player tries to run these commands: Is the player currently in a shop room? Did the player forget to specify what they want to buy or sell? Does the shop sell what the player wants to buy? Does the player have the item they want to sell to the shop? The following code implements the "list", "buy", and "sell" commands and addresses all these issues. Add the following code to your program:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;List the items for sale at the current location&amp;#39;s shop. &amp;quot;list full&amp;quot; will show details of the items.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;SHOP&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;This is not a shop.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;For sale:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;SHOP&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;  - &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;full&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textwrap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;LONGDESC&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;SCREEN_WIDTH&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;


    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_buy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&amp;quot;buy &amp;lt;item&amp;gt;&amp;quot; - buy an item at the current location&amp;#39;s shop.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;SHOP&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;This is not a shop.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="n"&gt;itemToBuy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;itemToBuy&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Buy what? Type &amp;quot;list&amp;quot; or &amp;quot;list full&amp;quot; to see a list of items for sale.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getFirstItemMatchingDesc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemToBuy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;SHOP&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c"&gt;# NOTE - If you wanted to implement money, here is where you would add&lt;/span&gt;
            &lt;span class="c"&gt;# code that checks if the player has enough, then deducts the price&lt;/span&gt;
            &lt;span class="c"&gt;# from their money.&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;You have purchased &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;SHORTDESC&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
            &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; is not sold here. Type &amp;quot;list&amp;quot; or &amp;quot;list full&amp;quot; to see a list of items for sale.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemToBuy&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complete_buy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;begidx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endidx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;SHOP&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

        &lt;span class="n"&gt;itemToBuy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;possibleItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

        &lt;span class="c"&gt;# if the user has only typed &amp;quot;buy&amp;quot; but no item name:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;itemToBuy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;getAllFirstDescWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;SHOP&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="c"&gt;# otherwise, get a list of all &amp;quot;description words&amp;quot; for shop items matching the command text so far:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;SHOP&lt;/span&gt;&lt;span class="p"&gt;])):&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# make list unique&lt;/span&gt;


    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_sell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&amp;quot;sell &amp;lt;item&amp;gt;&amp;quot; - sell an item at the current location&amp;#39;s shop.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;SHOP&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;This is not a shop.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="n"&gt;itemToSell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;itemToSell&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Sell what? Type &amp;quot;inventory&amp;quot; or &amp;quot;inv&amp;quot; to see your inventory.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;itemToSell&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="c"&gt;# NOTE - If you wanted to implement money, here is where you would add&lt;/span&gt;
                &lt;span class="c"&gt;# code that gives the player money for selling the item.&lt;/span&gt;
                &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;You have sold &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;SHORTDESC&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
                &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;You do not have &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;. Type &amp;quot;inventory&amp;quot; or &amp;quot;inv&amp;quot; to see your inventory.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemToSell&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complete_sell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;begidx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endidx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;SHOP&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldRooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

        &lt;span class="n"&gt;itemToSell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;possibleItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

        &lt;span class="c"&gt;# if the user has only typed &amp;quot;sell&amp;quot; but no item name:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;itemToSell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;getAllFirstDescWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# otherwise, get a list of all &amp;quot;description words&amp;quot; for inventory items matching the command text so far:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# make list unique&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When you are finished typing in this code, your program will look like &lt;a href="https://raw.githubusercontent.com/asweigart/textadventuredemo/master/snippets/shops.py"&gt;this file on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Eating&lt;/h3&gt;
&lt;p&gt;Some items are marked as edible by having an &lt;code&gt;EDIBLE&lt;/code&gt; key set to &lt;code&gt;True&lt;/code&gt; For example, Meat Pies, Bagels, and Donuts are all edible items:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="s"&gt;&amp;#39;Meat Pie&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;GROUNDDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;A suspicious meat pie rests on the ground.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SHORTDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a meat pie&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;LONGDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;A meat pie. It tastes like chicken.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;EDIBLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;pie&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;meat&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;Bagel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;GROUNDDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;A bagel rests on the ground. (Gross.)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SHORTDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a bagel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;LONGDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;It is a donut-shaped bagel.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;EDIBLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;bagel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;Donut&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;GROUNDDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;A donut rests on the ground. (Gross.)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SHORTDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a donut&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;LONGDESC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;It is a bagel-shaped donut.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;EDIBLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;donut&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For simplicity, eating things doesn't do anything other than remove them from your inventory. But you could implement health or hunger levels in your text adventure, and need eat (or drink) items or else have adverse effects. There's nothing new about how the &lt;code&gt;do_eat()&lt;/code&gt; and &lt;code&gt;complete_eat()&lt;/code&gt; functions work. Add the following code to your program:&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;pre&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&amp;quot;eat &amp;lt;item&amp;gt;&amp;quot; - eat an item in your inventory.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;itemToEat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;itemToEat&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Eat what? Type &amp;quot;inventory&amp;quot; or &amp;quot;inv&amp;quot; to see your inventory.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="n"&gt;cantEat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;getAllItemsMatchingDesc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemToEat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EDIBLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;cantEat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt; &lt;span class="c"&gt;# there may be other items named this that you can eat, so we continue checking&lt;/span&gt;
            &lt;span class="c"&gt;# NOTE - If you wanted to implement hunger levels, here is where&lt;/span&gt;
            &lt;span class="c"&gt;# you would add code that changes the player&amp;#39;s hunger level.&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;You eat &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;SHORTDESC&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
            &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cantEat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;You cannot eat that.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;You do not have &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;. Type &amp;quot;inventory&amp;quot; or &amp;quot;inv&amp;quot; to see your inventory.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemToEat&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complete_eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;begidx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endidx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;itemToEat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;possibleItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

        &lt;span class="c"&gt;# if the user has only typed &amp;quot;eat&amp;quot; but no item name:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;itemToEat&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;getAllFirstDescWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# otherwise, get a list of all &amp;quot;description words&amp;quot; for edible inventory items matching the command text so far:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;DESCWORDS&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;worldItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EDIBLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;descWord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;possibleItems&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# make list unique&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With the above code added, you will have finished the entire text adventure program. This program can be downloaded from GitHub: &lt;a href="https://raw.githubusercontent.com/asweigart/textadventuredemo/master/textadventuredemo.py"&gt;https://raw.githubusercontent.com/asweigart/textadventuredemo/master/textadventuredemo.py&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Ideas for New Features&lt;/h3&gt;
&lt;p&gt;That's it for this text adventure tutorial. Because of the &lt;code&gt;cmd&lt;/code&gt; module's command line interface features, it is fairly easy to add new commands to your game. From here, there are several things you could add to your game:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add HP, hunger/thirst levels, and status effects. (These are common to RPG-like games.)&lt;/li&gt;
&lt;li&gt;Combat with randomly wandering monsters.&lt;/li&gt;
&lt;li&gt;Casting magic spells, or learning new spells from spellbook items.&lt;/li&gt;
&lt;li&gt;Drinking items, including potions which can have magical effects.&lt;/li&gt;
&lt;li&gt;Equipping items, such as wearing helmets or wielding swords.&lt;/li&gt;
&lt;li&gt;Money, including different types of currencies that shops can accept or deny.&lt;/li&gt;
&lt;li&gt;Several new rooms to expand the world.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are interested in creating a &lt;a href="http://www.roguebasin.com/index.php?title=Main_Page"&gt;roguelike&lt;/a&gt; (a genre that is sort of like the Diablo games except with ASCII-art graphics), the libtcod module will be very helpful. There are tutorials &lt;a href="http://www.roguebasin.com/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod"&gt;here&lt;/a&gt; and &lt;a href="http://kooneiform.wordpress.com/2009/03/29/241/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I detail the differences between these genre of text-based games in my blog post, &lt;a href="http://inventwithpython.com/blog/2013/06/05/text-adventure-vs-mud-vs-roguelike-vs-dwarf-fortress/"&gt;Text Adventure vs. MUD vs. Roguelike vs. Dwarf Fortress&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can try playing MUDs you find through &lt;a href="http://www.mudconnect.com/"&gt;The Mud Connector&lt;/a&gt; to get new ideas for additional features you'd like to add. Good luck, and have fun!&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Announcing the "Invent with Python" Bookshelf</title><link href="https://inventwithpython.com/blog/announcing-the-invent-with-python-bookshelf.html" rel="alternate"></link><published>2014-12-06T12:00:00-05:00</published><updated>2014-12-06T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2014-12-06:/blog/announcing-the-invent-with-python-bookshelf.html</id><summary type="html">&lt;p&gt;I've organized a large collection of free and non-free books on a new part of this site: &lt;a href="/bookshelf"&gt;The Invent with Python Bookshelf&lt;/a&gt;.&lt;/p&gt;</summary><content type="html">

            &lt;p&gt;I've organized a large collection of free and non-free books on a new part of this site: &lt;a href="/bookshelf"&gt;The Invent with Python Bookshelf&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="/bookshelf"&gt;&lt;img src="/images/bookshelf_thumb.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Feel free to send any suggested titles I should add to it.&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>Why is Object-Oriented Programming Useful? (With a Role Playing Game Example)</title><link href="https://inventwithpython.com/blog/why-is-object-oriented-programming-useful-with-an-role-playing-game-example.html" rel="alternate"></link><published>2014-12-02T12:00:00-05:00</published><updated>2014-12-02T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2014-12-02:/blog/why-is-object-oriented-programming-useful-with-an-role-playing-game-example.html</id><summary type="html">&lt;p&gt;This blog post is those still new to programming and have probably heard about "object-oriented programming", "OOP", "classes", "inheritance/encapsulation/polymorphism", and other computer science terms but still don't get what exactly OOP is used for. In this post I'll explain &lt;em&gt;why&lt;/em&gt; OOP is used and how it makes coding easier.&lt;/p&gt;</summary><content type="html">

            &lt;p&gt;This blog post is those still new to programming and have probably heard about "object-oriented programming", "OOP", "classes", "inheritance/encapsulation/polymorphism", and other computer science terms but still don't get what exactly OOP is used for. In this post I'll explain &lt;em&gt;why&lt;/em&gt; OOP is used and how it makes coding easier. This post uses Python 3 code, but the concepts apply to any programming language.&lt;/p&gt;

&lt;p&gt;There are two key non-OOP concepts to understand right off the bat:&lt;br /&gt;
&lt;ol&gt;
    &lt;li&gt;&lt;strong&gt;Duplicate code is (mostly) a Bad Thing.&lt;/strong&gt;&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Code will always be changed.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;


&lt;span id="more-1717"&gt;&lt;/span&gt;

&lt;p&gt;Aside from small "throw-away" programs that are written for some single task and only run once, you'll almost always need to update your code to either &lt;strong&gt;fix bugs&lt;/strong&gt; or &lt;strong&gt;add new features&lt;/strong&gt;. A large part of writing good software is writing software that is readable and easy to change.&lt;/p&gt;

&lt;p&gt;If you have copy/pasted code in your program, changing it requires making the same change to multiple places in your program. This is problematic. If you miss applying the change to some places, you'll fail to fix the bug everywhere or implement the new feature inconsistently. &lt;em&gt;Duplicate code is a Bad Thing.&lt;/em&gt; Having duplicate code in your program is setting yourself up for bugs and headache.&lt;/p&gt;

&lt;p&gt;Functions let you get rid of duplicate code. You write the code in a function once, and just call the function everywhere your program needs that code run. Updating the function's code updates it everywhere the function is called automatically. Just as functions makes updating code easier, using object-oriented programming techniques also organizes your code to make changes easier. &lt;em&gt;And code will always be changed.&lt;/em&gt;&lt;/p&gt;

&lt;strong&gt;The Role Playing Game Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most OOP tutorials stink. They have "Car" classes with "Honk" methods and other examples that are unrelatable to the actual programs that new coders write or have used before. So this blog will use an RPG-style video game (think World of Warcraft, Pokemon, or Dungeons &amp; Dragons). We're used to thinking of things in these games as a collection of integers and strings. Check out all the numbers on this Diablo status screen or D &amp; D character sheet:&lt;/p&gt;

&lt;p&gt;&lt;a href="/blogstatic/oop_stats1.jpg?27f655"&gt;&lt;img src="/blogstatic/oop_stats1.jpg?27f655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="/blogstatic/oop_stats2.jpg?27f655"&gt;&lt;img src="/blogstatic/oop_stats2.jpg?27f655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Taking out the graphics from these RPG video games, the characters, armor, and other objects are just a bunch of integer or string values in variables. Without using object-oriented concepts, you could implement these things in Python code like this:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 150px; overflow: auto; font-family: monospace; color: black;"&gt;name = 'Elsa'
health = 50
magicPoints = 80
inventory = {'gold': 40, 'healing potion': 2, 'key': 1}

print('The hero %s has %s health.' % (name, health))&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;The above variable names are pretty generic. To add monsters in this program, you'll need to rename the player character's variables and make new ones for the monsters:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 150px; overflow: auto; font-family: monospace; color: black;"&gt;heroName = 'Elsa'
heroHealth = 50
heroMagicPoints = 80
heroInventory = {'gold': 40, 'healing potion': 2, 'key': 1}
monsterName = 'Goblin'
monsterHealth = 20
monsterMagicPoints = 0
monsterInventory = {'gold': 12, 'dagger': 1}

print('The hero %s has %s health.' % (heroName, heroHealth))&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;But of course you'll want to have more than one monster, but then you'll have variables like &lt;code&gt;monster1Name&lt;/code&gt;, &lt;code&gt;monster2Name&lt;/code&gt;, etc. That's a poor way to code, so maybe you'll make the monster variables into lists:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 150px; overflow: auto; font-family: monospace; color: black;"&gt;monsterName = ['Goblin', 'Dragon', 'Goblin']
monsterHealth = [20, 300, 18]
monsterMagicPoints = [0, 200, 0]
monsterInventory = [{'gold': 12, 'dagger': 1}, {'gold': 890, 'magic amulet': 1}, {'gold': 15, 'dagger': 1}]&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;Then you could just have the first goblin's stats be the values at index &lt;code&gt;0&lt;/code&gt; in the lists, the dragon's stats are at index &lt;code&gt;1&lt;/code&gt;, and the other goblin's stats are at index &lt;code&gt;2&lt;/code&gt;. This way you could have multiple monsters contained in these variables.&lt;/p&gt;

&lt;p&gt;But this kind of code easily leads to bugs. If your lists get out of sync, the program will no longer work correctly. Say the player vanquishes the goblin at index &lt;code&gt;0&lt;/code&gt; and the program calls a vanquishMonster() function. But the function has a bug and deletes the values from all the lists except (accidentally) &lt;code&gt;monsterInventory&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 180px; overflow: auto; font-family: monospace; color: black;"&gt;def vanquishMonster(monsterIndex):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;del monsterName[monsterIndex]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;del monsterHealth[monsterIndex]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;del monsterMagicPoints[monsterIndex]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# Note there is no del for monsterInventory

vanquishMonster(0)&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;The monster lists end up looking like:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 150px; overflow: auto; font-family: monospace; color: black;"&gt;monsterName = ['Dragon', 'Goblin']
monsterHealth = [300, 18]
monsterMagicPoints = [200, 0]
monsterInventory = [{'gold': 12, 'dagger': 1}, {'gold': 890, 'magic amulet': 1}, {'gold': 15, 'dagger': 1}]&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;Now it looks like the dragon's inventory is the same as the old goblin's inventory. And the second goblin now has the inventory the dragon had. &lt;em&gt;This is quickly getting out of hand.&lt;/em&gt;  The problem is that you have &lt;em&gt;one&lt;/em&gt; monster's data spread out over multiple variables. You could fix this by putting a single monster's data into a dictionary, and then have a list of dictionaries:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 180px; overflow: auto; font-family: monospace; color: black;"&gt;monsters = [{'name': 'Goblin', 'health': 20, 'magic points': 0, 'inventory': {'gold': 12, 'dagger': 1}},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{'name': 'Dragon', 'health': 300, 'magic points': 200, 'inventory': {'gold': 890, 'magic amulet': 1}},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{'name': 'Goblin', 'health': 18, 'magic points': 0, 'inventory': {'gold': 15, 'dagger': 1}}]&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;G'ah. This code is getting complex. For example, the inventory of a monster is a dictionary-in-a-dictionary-in-a-list. What if things like spells or inventory items also have their own attributes and need to be dictionaries? What if an inventory item could be a sack, which itself contains other inventory items? This monsters dictionary would get intense.&lt;/p&gt;

&lt;p&gt;This is something that object-oriented programming can solve by creating a new data type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating Classes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Classes are the blueprints for a new data type in your program.&lt;/strong&gt; Object-oriented programming provides a new approach for modeling the armor, monsters, etc. much better than a hodge-podge of lists and dictionaries, even though OOP concepts it take some getting used to.&lt;/p&gt;

&lt;p&gt;In fact, since the hero will have all the same features of monsters (health, inventory, etc.) we can just have a generic &lt;code&gt;LivingThing&lt;/code&gt; class that the hero and monsters share. Your code can be changed to look like this:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 300px; overflow: auto; font-family: monospace; color: black;"&gt;class LivingThing():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def __init__(self, name, health, magicPoints, inventory):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.name = name
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.health = health
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.magicPoints = magicPoints
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.inventory = inventory

# Create the LivingThing object for the hero.
hero = LivingThing('Elsa', 50, 80, {})
monsters = []
monsters.append(LivingThing('Goblin', 20, 0, {'gold': 12, 'dagger': 1}))
monsters.append(LivingThing('Dragon', 300, 200, {'gold': 890, 'magic amulet': 1}))

print('The hero %s has %s health.' % (hero.name, hero.health))&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;Hey look, using classes has already cut down our code in half since we can use the same code for both player characters and monsters.&lt;/p&gt;

&lt;p&gt;In the above code you are defining a new data type/class (pedantics aside, but the two terms are basically the same. See also: &lt;a href="https://stackoverflow.com/questions/468145/what-is-the-difference-between-type-and-class"&gt;Stack Overflow - What's the difference between a type and a class?&lt;/a&gt;) called &lt;code&gt;LivingThing&lt;/code&gt;. You can have &lt;code&gt;LivingThing&lt;/code&gt; values/objects (again, two terms for basically the same thing) just like you can have integer values, string values, or boolean values.&lt;/p&gt;

&lt;p&gt;Some Python-specific details about the above code:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 150px; overflow: auto; font-family: monospace; color: black;"&gt;class LivingThing():&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;The above is a &lt;code&gt;class&lt;/code&gt; statement that defines a new class, just like &lt;code&gt;def&lt;/code&gt; statements define a new function. The class's name is &lt;code&gt;LivingThing&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 150px; overflow: auto; font-family: monospace; color: black;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def __init__(self, name, health, magicPoints, inventory):&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;The above defines a method for the &lt;code&gt;LivingThing&lt;/code&gt; class. &lt;strong&gt;"Method"&lt;/strong&gt; is just the name used for functions that belong to a class. (See also: &lt;a href="https://stackoverflow.com/questions/155609/what-is-the-difference-between-a-method-and-a-function"&gt;Stack Overflow - What is the difference between a method and a function?&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This particular method is special. The name &lt;code&gt;__init__()&lt;/code&gt; is used for the "constructor function" (also called a "constructor method", or "constructor" or "ctor" for short) for the class. While a class is a blue print for a new data type, you still need to create values of this data type in order to have something that you can store in variables or pass to functions.&lt;/p&gt;

&lt;p&gt;When called, the constructor creates the new object, runs the code in the constructor, and returns the new object. This is what the &lt;code&gt;hero = LivingThing('Elsa', 50, 80, {})&lt;/code&gt; line is. No matter what the class name is, the constructor is always named &lt;code&gt;__init__&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If a class has no &lt;code&gt;__init__()&lt;/code&gt; method, Python supplies a generic constructor method for your class that doesn't do anything. But the &lt;code&gt;__init__()&lt;/code&gt; method is a good place for code that does the initial setup of a new object.&lt;/p&gt;

&lt;p&gt;In Python, the first parameter for methods &lt;code&gt;self&lt;/code&gt;. The &lt;code&gt;self&lt;/code&gt; parameter is used to create member variables, explained next.&lt;/p&gt;

&lt;p&gt;The body of the constructor function is this:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 150px; overflow: auto; font-family: monospace; color: black;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.name = name
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.health = health
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.magicPoints = magicPoints
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.inventory = inventory&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;This seems a bit repetitive, but what this code does is create member variables for the object being created by the constructor. Member variables will begin with &lt;code&gt;self.&lt;/code&gt; to show that they are member variables belonging to the object, and not just regular local variables in the method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Calling the Constructor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 150px; overflow: auto; font-family: monospace; color: black;"&gt;# Create the LivingThing object for the hero.
hero = LivingThing('Elsa', 50, 80, {})
monsters = [LivingThing('Goblin', 20, 0, {'gold': 12, 'dagger': 1}),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LivingThing('Dragon', 300, 200, {'gold': 890, 'magic amulet': 1}),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LivingThing('Goblin', 18, 0, {'gold': 15, 'dagger': 1})]

print('The hero %s has %s health.' % (hero.name, hero.health))&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;The constructor call in Python will just look like a function call using the the class's name. So &lt;code&gt;LivingThing()&lt;/code&gt; is the call to the &lt;code&gt;LivingThing&lt;/code&gt; class's &lt;code&gt;__init__()&lt;/code&gt; constructor. The above &lt;code&gt;LivingThing('Elsa', 50, 80, {})&lt;/code&gt; call creates a new &lt;code&gt;LivingThing&lt;/code&gt; object is created and stored in the &lt;code&gt;hero&lt;/code&gt; variable. The above code also creates &lt;code&gt;LivingThing&lt;/code&gt; objects for the three monsters and stores them in the &lt;code&gt;monsters&lt;/code&gt; list.&lt;/p&gt;

&lt;p&gt;And here's where we begin to see the benefits of object-oriented programming. If another programmer is reading your code, when they see the &lt;code&gt;LivingThing()&lt;/code&gt; call they know they can just look for "class LivingThing" and find out the details of everything they need to know about what a &lt;code&gt;LivingThing&lt;/code&gt; is.&lt;/p&gt;

&lt;p&gt;But an even bigger benefit comes when you try to update your &lt;code&gt;LivingThing&lt;/code&gt; class.&lt;/p&gt;

&lt;strong&gt;Updating Classes&lt;/strong&gt;&lt;/p&gt;

Say you wanted to add "hunger" levels to your RPG. If a hero or monster has a &lt;code&gt;hunger&lt;/code&gt; level of &lt;code&gt;0&lt;/code&gt;, they are not at all hungry. But if their &lt;code&gt;hunger&lt;/code&gt; level is above &lt;code&gt;100&lt;/code&gt;, then they take damage and their &lt;code&gt;health&lt;/code&gt; value decreases each day. You could change your &lt;code&gt;__init__()&lt;/code&gt; method to this:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 150px; overflow: auto; font-family: monospace; color: black;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def __init__(self, name, health, magicPoints, inventory):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.name = name
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.health = health
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.magicPoints = magicPoints
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.inventory = inventory
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.hunger = 0  # all living things start with hunger level 0&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;Without changing &lt;em&gt;any&lt;/em&gt; other line of code, &lt;em&gt;every&lt;/em&gt; &lt;code&gt;LivingThing&lt;/code&gt; object in your game now has a hunger level! You don't have to worry about some &lt;code&gt;LivingThing&lt;/code&gt; objects having the &lt;code&gt;hunger&lt;/code&gt; member variable and some not: the very definition of what a &lt;code&gt;LivingThing&lt;/code&gt; is has been updated.&lt;/p&gt;

&lt;p&gt;You also don't have to change any of the constructor calls since you didn't add a new hunger level parameter to the &lt;code&gt;__init__()&lt;/code&gt; method. That's because &lt;code&gt;0&lt;/code&gt; is a good common-sense default value for a new &lt;code&gt;LivingThing&lt;/code&gt; object's hunger level. If you do add a new parameter to &lt;code&gt;__init__()&lt;/code&gt; for the hunger levels, you'll have to update all the code that calls the constructor. But this is true for any function anyway.&lt;/p&gt;

&lt;p&gt;If your RPG has a lot of default values for things, you can avoid a lot of "boilerplate" code by using classes with a constructor that sets the default for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Methods&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Methods are useful for running code that affect the object itself. For example, you could just have code that changes the health of a LivingThing object directly:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 150px; overflow: auto; font-family: monospace; color: black;"&gt;hero = LivingThing('Elsa', 50, {})
hero.health -= 10  # Elsa takes 10 points of damage&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;But this isn't a very robust way to handle taking damage. Lots of other game logic needs to be checked whenever something takes damage. For example, say you want to check if a character dies each time they take damage. You would need code like this:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 150px; overflow: auto; font-family: monospace; color: black;"&gt;hero = LivingThing('Elsa', 50, {})
hero.health -= 10  # Elsa takes 10 points of damage
if hero.health &lt; 0:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(hero.name + ' has died!')&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;The problem with the above approach is that you would need that check everywhere your code decreases the health of a &lt;code&gt;LivingThing&lt;/code&gt; object. But duplicating code is a Bad Thing. The non-OOP way to prevent duplicate code would be to put it in a function:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 200px; overflow: auto; font-family: monospace; color: black;"&gt;def takeDamage(livingThingObject, dmgAmount):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;livingThingObject.health = self.health - dmgAmount
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if livingThingObject.health &lt; 0:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(livingThingObject.name + ' is dead!')

hero = LivingThing('Elsa', 50, {})
takeDamage(hero, 10)  # Elsa takes 10 points of damage&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;This is a better solution because any updates to &lt;code&gt;takeDamage()&lt;/code&gt; (such as factoring in armor, protective spells, bonuses, etc.) just have to be added to the &lt;code&gt;takeDamage()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;However, the downside is that when your program grows in size, it's easy for &lt;code&gt;takeDamage()&lt;/code&gt; to get lost in among them. It isn't so clear that &lt;code&gt;takeDamage()&lt;/code&gt; is related to the &lt;code&gt;LivingThing&lt;/code&gt; class. If you have hundreds of functions in your program, it will be hard to figure out which ones are related to the &lt;code&gt;LivingThing&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;The solution is to turn this function into a &lt;code&gt;LivingThing&lt;/code&gt; method:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 300px; overflow: auto; font-family: monospace; color: black;"&gt;class LivingThing():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# ...other code in the class...

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def takeDamage(self, dmgAmount):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.health = self.health - dmgAmount
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if self.health == 0:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(self.name + ' is dead!')

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# ...other code in the class...

hero = LivingThing('Elsa', 50, {})
hero.takeDamage(10)  # Elsa takes 10 points of damage&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;Once your program has many classes, each with many methods and member variables, you will begin to see how OOP can help organize your programs to be more manageable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Public and Private Methods&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Methods and member variables can be marked as public or private. Public methods can be called and public member variables can be set by any code, inside or outside of the class. Private methods can be called and private member variables can be set only by code inside the object's own class.&lt;/p&gt;

&lt;p&gt;In some languages such as Java, this "can be called/set" is strictly enforced by the compiler. In Python, there's no such concept as "private" and "public". All methods and member variables are "public". However, the convention is that if a method name begins with an underscore, it is marked as private. This is why you'll see methods such as &lt;code&gt;_takeDamage()&lt;/code&gt;. Nothing prevents you from writing code that calls private methods or set private member variables from outside the object's class, but you've been fairly warned not to do that.&lt;/p&gt;

&lt;p&gt;The reason for having this public/private distinction is to set expectations for how the class interacts with outside code. (See also: &lt;a href="https://stackoverflow.com/questions/2620699/why-private-methods-in-the-object-oriented"&gt;Stack Overflow - Why "private" methods in the object oriented?&lt;/a&gt;) The programmer of the class expects that other people won't write code that calls the private methods or sets the private member variables.&lt;/p&gt;

&lt;p&gt;For example, the &lt;code&gt;takeDamage()&lt;/code&gt; method factors in checking for death if health falls below 0. You may want to add all sorts of other checks in the code. Things like armor, agility, and protective spells could factor in to reduce the damage taken. The &lt;code&gt;LivingThing&lt;/code&gt; object might be wearing an enchanted cape that &lt;em&gt;heals&lt;/em&gt; them by adding the damage amount to their &lt;code&gt;health&lt;/code&gt; instead of subtracting. All of this game logic can go in the &lt;code&gt;takeDamage()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;But all of OOP organizing is for nothing if you accidentally put this code in there:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 150px; overflow: auto; font-family: monospace; color: black;"&gt;class LivingThing():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# ...code in the class...

hero = LivingThing('Elsa', 50, {})

# ...some more code...

if someCondition:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;hero.health -= 50&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;That &lt;code&gt;hero.health -= 50&lt;/code&gt; will subtract 50 points of health, without taking into any consideration what armor Elsa is wearing, if she has protective spells, or is wearing that enchanted healing cape. This code bluntly decrements &lt;code&gt;health&lt;/code&gt; by &lt;code&gt;50&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's easy to forget about the &lt;code&gt;takeDamage()&lt;/code&gt; method and accidentally write code like this. This doesn't check if the &lt;code&gt;hero&lt;/code&gt; object's &lt;code&gt;health&lt;/code&gt; member variable has dropped below &lt;code&gt;0&lt;/code&gt;. The program continues as though Elsa is alive even if she has negative health! This is a bug we can avoid with public/private members and methods.&lt;/p&gt;

&lt;p&gt;If you rename the &lt;code&gt;health&lt;/code&gt; member variable to &lt;code&gt;_health&lt;/code&gt; and mark it private, then it's easy to catch this bug when you write it:&lt;/p&gt;

&lt;p&gt;&lt;textarea style="width: 100%; height: 150px; overflow: auto; font-family: monospace; color: black;"&gt;hero = LivingThing('Elsa', 50, {})

# ...some more code...

if someCondition:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;hero._health -= 50 # WAIT! This code is outside the hero object's class but modifying a private member variable! This must be a bug!&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;In a language like Java where the compiler enforces private/public access, it would be impossible to write a program that illegally accesses a private member or method. Object-oriented programming helps prevent these kinds of bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inheritance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using the LivingThing class for dragons is nice, but dragons have a lot of other qualities in addition to the ones provided by &lt;code&gt;LivingThing&lt;/code&gt;. So you want to create a new &lt;code&gt;Dragon&lt;/code&gt; class that will have member variables like &lt;code&gt;airSpeed&lt;/code&gt; and &lt;code&gt;breathType&lt;/code&gt; (which can be string such as &lt;code&gt;'fire'&lt;/code&gt;, &lt;code&gt;'blizzard'&lt;/code&gt;, &lt;code&gt;'lightning'&lt;/code&gt;, &lt;code&gt;'poison gas'&lt;/code&gt;, etc).&lt;/p&gt;

&lt;p&gt;Since &lt;code&gt;Dragon&lt;/code&gt; objects will also have &lt;code&gt;health&lt;/code&gt;, &lt;code&gt;magicPoints&lt;/code&gt;, &lt;code&gt;inventory&lt;/code&gt;, and all the other things that &lt;code&gt;LivingThing&lt;/code&gt; objects have, you &lt;em&gt;could&lt;/em&gt; just create a new &lt;code&gt;Dragon&lt;/code&gt; class and copy/paste all the code from your &lt;code&gt;LivingThing&lt;/code&gt; class. But this would result in duplicate code, which is a Bad Thing.&lt;/p&gt;

&lt;p&gt;Instead, make a &lt;code&gt;Dragon&lt;/code&gt; class that is a &lt;strong&gt;subclass&lt;/strong&gt; of the &lt;code&gt;LivingThing&lt;/code&gt; class:&lt;/p&gt;

&lt;p&gt;&lt;textarea wrap="off" style="width: 100%; height: 150px; overflow: auto; font-family: monospace; color: black;"&gt;class LivingThing():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# ...other code in the class...

class Dragon(LivingThing):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# ...Dragon-specific code in the class...&lt;/textarea&gt;&lt;/p&gt;

&lt;p&gt;This is effectively saying, "A &lt;code&gt;Dragon&lt;/code&gt; is the same as a &lt;code&gt;LivingThing&lt;/code&gt;, with some additional methods and member variables". When you create a &lt;code&gt;Dragon&lt;/code&gt; object, it will automatically have all the same methods and member variables as &lt;code&gt;LivingThing&lt;/code&gt; (saving us from duplicating the code). But it could also have dragon-specific methods and member variables to it. Further, any code that deals with a &lt;code&gt;LivingThing&lt;/code&gt; object can automatically handle a &lt;code&gt;Dragon&lt;/code&gt; object because &lt;code&gt;Dragon&lt;/code&gt; objects already have the &lt;code&gt;LivingThing&lt;/code&gt; members and methods. This principle is called &lt;strong&gt;subtype polymorphism&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In practice, inheritance is easy to abuse though. You must be certain that any conceivable change or update you make to the &lt;code&gt;LivingThing&lt;/code&gt; class would also be something you would want the &lt;code&gt;Dragon&lt;/code&gt; class &lt;em&gt;and every other subclass of &lt;code&gt;LivingThing&lt;/code&gt;&lt;/em&gt; to also have. This might not always be so straightforward.&lt;/p&gt;

&lt;p&gt;For example, what if you created &lt;code&gt;Monster&lt;/code&gt; and &lt;code&gt;Hero&lt;/code&gt; subclasses of the &lt;code&gt;LivingThing&lt;/code&gt; class, and then created &lt;code&gt;FlyingMonster&lt;/code&gt; and &lt;code&gt;MagicalMonster&lt;/code&gt; subclasses from &lt;code&gt;Monster&lt;/code&gt;. Would the new &lt;code&gt;Dragon&lt;/code&gt; class be a subclass of &lt;code&gt;FlyingMonster&lt;/code&gt; or &lt;code&gt;MagicalMonster&lt;/code&gt;? Or maybe just its own subclass of &lt;code&gt;Monster&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;This is where inheritance and OOP start to get tricky and religious arguments over the "correct" way to design classes come about. I want to keep this blog post short and simple, so I'll leave these topics as exercises for the reader to look into. (See also &lt;a href="https://stackoverflow.com/questions/49002/prefer-composition-over-inheritance"&gt;Stack Overflow - Prefer composition over inheritance?&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Composition_over_inheritance"&gt;Wikipedia - Composition over inheritance&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I hate programming tutorials for beginners that start with object-oriented programming. OOP is a very abstract concept. Until have some experience and are writing large programs, you won't understand why using classes and objects makes programming easier. Instead, the neophyte is left with a steep learning curve to climb and no idea why they're climbing it. I hope the RPG example has at least given you a glimpse as to why OOP can be helpful. There is MUCH more to OOP. If you want to learn more, try googling for &lt;a href="https://www.google.com/search?q=python%20object%20oriented%20design"&gt;"python object oriented design"&lt;/a&gt; or &lt;a href="https://www.google.com/search?q=python+design+patterns"&gt;"python design patterns"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are still confused by OOP concepts, feel free to write programs without classes. You don't &lt;strong&gt;need&lt;/strong&gt; them and they can result in over-engineered code. But once you have some coding experience under your belt, the benefits of object-oriented programming will become apparent. Good luck!&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>IDLE Reimagined</title><link href="https://inventwithpython.com/blog/idle-reimagined.html" rel="alternate"></link><published>2014-11-20T12:00:00-05:00</published><updated>2014-11-20T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2014-11-20:/blog/idle-reimagined.html</id><summary type="html">&lt;p&gt;IDLE Reimagined is the project name for a redesign for Python's default IDLE editor with focus as an educational tool. IDLE's chief utility is that it comes installed with Python, making it simple for newbies to start programming. But professional software developers don't use IDLE as their IDE. Instead of turning IDLE into a sophisticated IDE for professional software developers, it can be tooled with features specifically to make it friendly to those learning to program.&lt;/p&gt;</summary><content type="html">


			&lt;p&gt;(Update: &lt;a href="https://www.youtube.com/watch?v=u58DiW_t2lM"&gt;I've recorded a copy of a talk I gave at PyCon 2015's Education Summit about IDLE Reimagined and posted it to YouTube&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;I've started a wiki for an IDLE redesign project: &lt;a href="https://github.com/asweigart/idle-reimagined/wiki"&gt;https://github.com/asweigart/idle-reimagined/wiki&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you would like to help, please join the mailing list: &lt;a href="https://groups.google.com/forum/#!forum/idle-reimagined/"&gt;https://groups.google.com/forum/#!forum/idle-reimagined/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="http://inventwithpython.com/blogstatic/idlereimagined.png?27f655" alt="IDLE Reimagined mockup screenshot" /&gt;&lt;/p&gt;
&lt;p&gt;From the wiki:&lt;/p&gt;
&lt;p&gt;IDLE Reimagined is the project name for a redesign for Python's default IDLE editor with focus as an educational tool. IDLE's chief utility is that it comes installed with Python, making it simple for newbies to start programming. But professional software developers don't use IDLE as their IDE. Instead of turning IDLE into a sophisticated IDE for professional software developers, it can be tooled with features specifically to make it friendly to those learning to program.&lt;/p&gt;
&lt;p&gt;Prime Directives for the new design:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;IR is designed not for experienced developers or those new to Python, but specifically for those new to programming.&lt;/li&gt;
&lt;li&gt;IR is meant to be a drop-in replacement of IDLE, and be installed with the default Python installer.&lt;/li&gt;
&lt;li&gt;IR's code will use the tkinter GUI toolkit (unless a better GUI toolkit is bundled with Python).&lt;/li&gt;
&lt;li&gt;IR is fully-featured offline, but also has features for finding help or sharing code online.&lt;/li&gt;
&lt;li&gt;"Simple is better than complex."&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These are the features that will distinguish IR and make it a good candidate to replace IDLE:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Single window design, with file editor on the upper pane and interactive shell on the lower pane. (No more confusing separate windows for shell &amp; file editor.)&lt;/li&gt;
&lt;li&gt;Tabbed file editor.&lt;/li&gt;
&lt;li&gt;Foreign language support. (Though Python's keywords and standard library will still be in English, IDLE itself can be multi-lingual.)&lt;/li&gt;
&lt;li&gt;Tutorial plugin system for Codecademy-like tutorials.&lt;/li&gt;
&lt;li&gt;Integrated pip installer.&lt;/li&gt;
&lt;li&gt;Integrated pastebin feature. (Easily share code with those who can help you.)&lt;/li&gt;
&lt;li&gt;"Plain English" error message translations. Instant Google-search for error messages.&lt;/li&gt;
&lt;li&gt;Detects and warns if you are trying to run Python 2 code on Python 3.&lt;/li&gt;
&lt;li&gt;Lightweight real-time lint tool that will point out missing variables and syntax errors. (Checks for errors, but does not check for style or PEP8.)&lt;/li&gt;
&lt;/ol&gt;


</content><category term="misc"></category></entry><entry><title>No, Seriously, Why Should I Learn to Code?</title><link href="https://inventwithpython.com/blog/no-seriously-why-should-i-learn-to-code.html" rel="alternate"></link><published>2014-09-30T12:00:00-04:00</published><updated>2014-09-30T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2014-09-30:/blog/no-seriously-why-should-i-learn-to-code.html</id><summary type="html">&lt;p&gt;The geeks have inherited the earth. A couple decades ago if you talked with friends in a chat room, you were cast as a socially-awkward nerd. Doing the same today just means you're a typical Facebook user. Broadband Internet connections and smartphones have taken the Information Age that technically started in the 1970s with PCs and pushed it into daily mainstream life.&lt;/p&gt;&lt;p&gt;Don't get me wrong: &lt;em&gt;You should learn to program.&lt;/em&gt; But lost in this hype is a plainspoken reason: NO, SERIOUSLY, WHY SHOULD I LEARN TO CODE?&lt;/p&gt;</summary><content type="html">


&lt;p&gt;The geeks have inherited the earth. A couple decades ago if you talked with friends in a chat room, you were cast as a socially-awkward nerd. Doing the same today just means you're a typical Facebook user. Broadband Internet connections and smartphones have taken the Information Age that technically started in the 1970s with PCs and pushed it into daily mainstream life.&lt;/p&gt;
&lt;p&gt;Along with this culture change is a social anxiety: Should I learn to code? Is coding the new literacy? Will I program or be programmed? This is not a new anxiety. Here's a New York Times article, &lt;a href="http://www.nytimes.com/1984/01/17/science/personal-computers-does-everyone-need-to-learn-programming.html"&gt;Personal Computers; Does Everyone Need to Learn Programming?&lt;/a&gt; It was written in 1984.&lt;/p&gt;
&lt;p&gt;Like with all panics, entrepreneurs and pundits (including myself) come out of the woodwork to offer their opinions or push their products. Software developer Jeff Atwood gives &lt;a href="http://blog.codinghorror.com/please-dont-learn-to-code/"&gt;a plea to not learn how to code&lt;/a&gt; (a position &lt;a href="http://inventwithpython.com/blog/2012/05/16/a-modest-proposal-please-dont-learn-to-code-because-it-will-damage-your-tiny-brain/"&gt;I sharply disagree with&lt;/a&gt;). There are many websites and bootcamps (ranging from free to overpriced) to teach beginners programming. Most of them (in my opinion) are &lt;a href="http://inventwithpython.com"&gt;terrible&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Don't get me wrong: &lt;em&gt;You should learn to program.&lt;/em&gt; But lost in this hype is a plainspoken reason: NO, SERIOUSLY, WHY SHOULD I LEARN TO CODE?&lt;/p&gt;
&lt;p&gt;Here are bogus reason you should learn to code that you'll find in most everyone-should-learn-to-code rhetoric:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;To become a software engineer and make six-figures.&lt;/strong&gt; If you are interested in becoming a developer, by all means follow up on it. But if you enjoy your current career, you don't have to leave it. The world needs more than just software developers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;To make the next Facebook or Flappy-Bird and become a millionaire.&lt;/strong&gt; Most startups fail. Software startups require much more programming skill than a run through of Codecademy can provide, and programming is not the most important skill to have when starting a business.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;To build critical-thinking and problem-solving skills.&lt;/strong&gt; Ehhh... sort of. But people who give this reason often leave out what exactly that means or why coding provides it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You can put in on your resume.&lt;/strong&gt; Ehhh... again, true, but how coding applies to most non-developer jobs is left unstated.
&lt;li&gt;&lt;strong&gt;To build a website.&lt;/strong&gt; &lt;a href=""&gt;HTML is not a programming language&lt;/a&gt;, and web design is a different skill set to coding. But anyway, if you just want a website presence,  sign up for a free WordPress blog.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Because other people are saying you should.&lt;/strong&gt; This is a terrible reason to do anything.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before I go into the real reasons you, your friends, your neighbors, your cat, and everyone should indeed learn to code, consider this anecdote:&lt;/p&gt;
&lt;p&gt;A math teacher is giving a lesson on logarithms or the quadratic equation or whatever and is asked by a student, "When will I ever need to know this?"&lt;/p&gt;
&lt;p&gt;"Most likely never," replied the teacher without hesitation. "Most jobs and even a lot of professions won't require you to know any math beyond basic arithmetic or a little algebra."&lt;/p&gt;
&lt;p&gt;"But," the teacher continued, "let me ask you this. Why do people go to the gym and lift weights? Do they all plan on becoming Olympic weight lifters, or professional body builders? Do they think they'll one day find an old lady trapped under a 200 pound bar bell and say, 'This is what I've been training for.'"&lt;/p&gt;
&lt;p&gt;"No, they lift weights because it makes them stronger. Learning math is important because because it makes you smarter. It forces your brain to think in a way that normally it wouldn't think: a way that requires precision, discipline, and abstract thought.  It's more than rote memorization, or making beautiful things, or figuring out someone's expectations and how to appease them. Doing your math homework is practice for the kind of disciplined thinking where there are objective right and wrong answers. And math is ubiquitous: it comes up in a lot of other subjects and is universal across cultures. And all this is practice for thinking in a new way. And being able to think in new ways, more than anything, is what will prepare you for an unpredictable, even dangerous, future."&lt;/p&gt;
&lt;p&gt;Learning to program a computer, even if you don't plan on becoming a software engineer, has three similar reasons to pursue it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Learning to code is also great practice for precise, disciplined, and abstract thinking.&lt;/strong&gt; A program either works, or it doesn't. It could have bugs, straightforward or subtle, that require analysis and concentration. But unlike math, a program does something immediately practical. (Mathematicians, please don't shoot me for saying this.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Programming transforms your computer from a home appliance to a power tool.&lt;/strong&gt; Whether you have an office job, are a student, or just go online a lot, people use computers to get stuff done. But some tedious, lengthy computer tasks are so specific or context-dependent that no one has produced software to do it. (&lt;a href="http://automatetheboringstuff.com"&gt;My next programming book addresses exactly these kinds of problems.&lt;/a&gt;) Even without deep programming knowledge, coding becomes a large productivity multiplier. Computers are everywhere; they should be tools, not appliances. &lt;em&gt;A tool is something you sharpen, an appliance is something you replace.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Learning to program is great practice for learning itself.&lt;/strong&gt; Professional software developers use Google &lt;strong&gt;daily&lt;/strong&gt; to answer questions or find documentation for their work. During the course of coding you will come up with several questions that will require you to seek out an answer. Googling is a skill in itself: figuring out which keywords to use, identifying useless advice and generic platitudes on content-farm sites, knowing how to ask a question. (The last one is so complex it has its own &lt;a href="http://www.catb.org/esr/faqs/smart-questions.html"&gt;24-page FAQ&lt;/a&gt;.) These aren't arbitrary word problems or Sudoku puzzles you are answering. Learning to code involves learning how to find real help for yourself.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href="http://www.theguardian.com/technology/2012/dec/04/ict-teach-kids-regular-expressions"&gt;Cory Doctorow wrote an article&lt;/a&gt; that even before learning to code, we should be teaching kids how to use regular expressions. "Regexes" are ways to specify a &lt;em&gt;pattern&lt;/em&gt; of text. Your word processing software most likely lets you use regular expressions for its find-and-replace feature. Regular expressions can be used for an app's search feature. This is a skill that magnifies your power as a user. And once a populace is literate in regular expressions, software developers will increasing make use of them in more capable and powerful apps.&lt;/p&gt;
&lt;p&gt;The same can be true with programming. &lt;strong&gt;Imagine what software could be like if the populace were literate in basic programming.&lt;/strong&gt; Instead of apps, we could have APIs. Users could tie together the different services and programs they use. It would tear down the walled-gardens that Facebook, Apple, Twitter, and Google have erected to maintain their monopolies. It would pave the roads for improving the Information Age the same way the printing press improved literacy.&lt;/p&gt;
&lt;p&gt;There are plenty of skills people can learn to become well-rounded human beings: Playing an instrument, public speaking, budgeting, first aid, growing plants, persuasive writing, babysitting, identifying scams, et cetera, et cetera.&lt;/p&gt;
&lt;p&gt;But programming is unique among these skills. Programming is a practical skill on a tool you already use every day. Learning it provides abundant, but not pointless, intellectual hurdles to train your brain. It can save you time on computer-based tasks while opening your eyes to accomplishments that were previously unfeasible. That is why you should seriously learn to program.&lt;/p&gt;


</content><category term="misc"></category></entry><entry><title>"Let's Create Software" Tutorial: Bus Routes Overlaid on Google Maps</title><link href="https://inventwithpython.com/blog/lets-create-software-bus-routes-overlaid-on-google-maps.html" rel="alternate"></link><published>2014-02-11T12:00:00-05:00</published><updated>2014-02-11T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2014-02-11:/blog/lets-create-software-bus-routes-overlaid-on-google-maps.html</id><summary type="html">&lt;p&gt;I had the idea to use Google Maps' API to draw out a map of all the Muni routes. This would have the benefit of the system map PDF but also be easier to load and navigate. I have no experience using the Google Maps API and am vaguely aware that there's public data about Muni bus routes, but I'll learn along the way.&lt;/p&gt;</summary><content type="html">


			&lt;p&gt;&lt;em&gt;The "Let's Create Software" series covers the creation of small, complete software projects from start to finish. The entire process and code is explained to give beginner and intermediate programmers an idea of what it is like to not just program, but create an entire piece of software from scratch.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There's a &lt;a href="http://www.sfmta.com/maps/muni-system-map"&gt;large PDF system map&lt;/a&gt; of all the Muni bus routes in San Francisco (as well as &lt;a href="http://www.sfmta.com/maps"&gt;maps of the individual Muni routes&lt;/a&gt;). If I need to find directions, Google Maps' mass transit instructions are fine. But sometimes I'd like to just browse all the routes throughout the city. However the large PDF is slow to render on my laptop (moreso on my phone). Scrolling the PDF also results in a lengthy wait for redrawing.&lt;/p&gt;
&lt;p&gt;So I had the idea to use Google Maps' API to draw out a map of all the Muni routes. This would have the benefit of the system map PDF but also be easier to load and navigate. I have no experience using the Google Maps API and am vaguely aware that there's public data about Muni bus routes, but I'll learn along the way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="/sfmuni"&gt;The completed SF Muni Google Map.&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/sfmuni/sfmuni_screenshot1.png?27f655" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/sfmuni/sfmuni_screenshot2.png?27f655" /&gt;&lt;/p&gt;
&lt;p&gt;To follow along with this tutorial, it helps to have a bit of Python or JavaScript knowledge, or know a little bit about programming. I've tried to keep this as straightforward as possible, even for non-programmers to roughly follow.&lt;/p&gt;
&lt;p&gt;This project will involve writing a few "throw-away" scripts to parse and extract data, and my preferred language is Python. While I do all the scripts in Python 3 (note: Python 3 has some backwards incompatibilities with Python 2. My scripts might not run on Python 2), you can use whatever language you like. The final software will be done with JavaScript since it is a web page that uses the Google Maps API. (The final result is here: &lt;a href="http://inventwithpython.com/sfmuni/"&gt;http://inventwithpython.com/sfmuni/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In general, by "throw-away" script I mean a script that is written in order to be run once to perform a task. Software such as Firefox or Angry Birds I want to keep around to run again and again, but a throw-away script I just need to automate a certain task. I'll still keep this script; I won't actually delete this script once I've run it, but I most likely won't ever need it again.&lt;/p&gt;
&lt;p&gt;A note to beginning programmers who might feel like this project might be too advanced for them: even though I have years of experience programming, I still made many mistakes and went down a few blind alleys while putting this code together. Although it looks like I just sat down and typed out this code, just remember that this tutorial is the polished outcome of quite a bit of tenacious work. Don't be discouraged or intimidated, just keep at it!&lt;/p&gt;
&lt;p&gt;&lt;span id="more-1626"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Prior Art Search&lt;/h2&gt;
&lt;p&gt;The first part before we do any programming project is to see what else is already out there. It might be fun to reimplement something that's already out there, but by looking at what other people have done, we can get ideas for what we might want to do. Or you might find the exact software you wanted is already made, and can save yourself the trouble.&lt;/p&gt;
&lt;p&gt;Doing a few Google Searches, I see that there's &lt;a href="http://www.sflivebus.com/"&gt;SF Live Bus&lt;/a&gt;, which gives a live feed of every bus's position using the Google Maps API and the NextBus API. But it doesn't show the routes on the maps like the system map does.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.nextbus.com/"&gt;NextBus&lt;/a&gt; has outfitted the buses and trains of many municipal transit systems with GPS locators and provides a data feed of them. They also have an API that is well documented.&lt;/p&gt;
&lt;p&gt;Other than that, I haven't really found anything like what I want to do specifically.&lt;/p&gt;
&lt;h2&gt;Decide on Features (and Non-Features)&lt;/h2&gt;
&lt;p&gt;Here's what I want the software to do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use Google Maps with all the usual navigation controls that it provides, but all the Muni routes are overlaid on top of it.&lt;/li&gt;
&lt;li&gt;Also, have all the stops marked on the map.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, it's important to come up with a list of features you will &lt;strong&gt;not&lt;/strong&gt; have. It's really easy to say, "Hey it'd be cool if it could do this..." and keep accumulating features and making the software more complicated. While good learning experiences for the programmer, half-finished projects are almost always useless for other people. Our system map software will &lt;strong&gt;not&lt;/strong&gt; have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Display current position of the buses.&lt;/li&gt;
&lt;li&gt;Have bus predictions for the stops.&lt;/li&gt;
&lt;li&gt;Offer directions between locations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I'll come up with new features as I make progress, but keeping the scope of the project from constantly expanding will prevent me from ending up with another half-finished project.&lt;/p&gt;
&lt;h2&gt;Research&lt;/h2&gt;
&lt;p&gt;There are four things I'll have to learn before I can create this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get a list of all the bus routes for San Francisco Muni .&lt;/li&gt;
&lt;li&gt;Figure out the coordinates of the routes so that I can draw them on the map.&lt;/li&gt;
&lt;li&gt;Figure out the coordinates of all the stops in the city.&lt;/li&gt;
&lt;li&gt;Learn enough of the Google Maps API to create a map and draw lines and markers on it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The NextBus site has &lt;a href="http://www.nextbus.com/xmlFeedDocs/NextBusXMLFeed.pdf‎"&gt;a PDF that explains their public data feed&lt;/a&gt;. We can use that to figure out the coordinates for routes and stops. You don't have to read it, I'll be explaining what I do as I do it.&lt;/p&gt;
&lt;h2&gt;Getting a List of Routes&lt;/h2&gt;
&lt;p&gt;According to the &lt;a href="http://www.nextbus.com/xmlFeedDocs/NextBusXMLFeed.pdf‎"&gt;NextBus PDF&lt;/a&gt;, &lt;code&gt;sf-muni&lt;/code&gt; is the "agency name" (NextBus provides info for many different cities' bus systems) that we plug into the URL for the XML data feed. You can access &lt;a href="http://webservices.nextbus.com/service/publicXMLFeed?command=routeList&amp;amp;a=sf-muni"&gt;http://webservices.nextbus.com/service/publicXMLFeed?command=routeList&amp;a;=sf-muni&lt;/a&gt; to get XML text with all the routes. The XML looks like this:&lt;/p&gt;
&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;

&amp;lt;body copyright="All data copyright San Francisco Muni 2014."&amp;gt;

&amp;lt;route tag="F" title="F-Market &amp; Wharves"/&amp;gt;

&amp;lt;route tag="J" title="J-Church"/&amp;gt;

&amp;lt;route tag="KT" title="KT-Ingleside/Third Street"/&amp;gt;

&amp;lt;route tag="L" title="L-Taraval"/&amp;gt;

...&lt;/pre&gt;
&lt;p&gt;I need to extract the information in the &lt;code&gt;tag&lt;/code&gt; and &lt;code&gt;title&lt;/code&gt; attributes. I &lt;em&gt;could&lt;/em&gt; just spend time copying &amp; pasting this information, but instead I'll write a throw-away Python script (called &lt;a href="/sfmuni/getroutes.py"&gt;getroutes.py&lt;/a&gt; to do this for me.&lt;/p&gt;
&lt;p&gt;Download &lt;a href="/sfmuni/getroutes.py"&gt;getroutes.py&lt;/a&gt; or &lt;a href="http://pastebin.com/xn4msVJu"&gt;view it online&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I've installed the &lt;a href="http://docs.python-requests.org/en/latest/"&gt;Requests&lt;/a&gt; module to make downloading the XML data easier. The &lt;a href="http://docs.python-requests.org/en/latest/user/quickstart/"&gt;quick start&lt;/a&gt; page has a good intro in how to use it. To download a web page, you &lt;code&gt;import requests&lt;/code&gt; and then call &lt;code&gt;requests.get()&lt;/code&gt; to download the page. The &lt;code&gt;requests.get()&lt;/code&gt; function returns a "request object" which has the webpage as a string in the &lt;code&gt;text&lt;/code&gt; attribute. (The Requests module is &lt;strong&gt;much&lt;/strong&gt; easier to use than Python's urllib2 module.)&lt;/p&gt;
&lt;p&gt;I'll use the &lt;code&gt;xml&lt;/code&gt; and &lt;code&gt;pprint&lt;/code&gt; modules to turn the XML text file into a Python dictionary value that is saved to a file named &lt;a href="/sfmuni/routedata.py"&gt;routedata.py&lt;/a&gt;. I can pass a dictionary value to the &lt;code&gt;pprint.pformat()&lt;/code&gt; function, and it will return a string of what that dictionary would look like typed as code. &lt;/p&gt;
&lt;p&gt;I'm using Python code to generate Python code! This sounds complicated, but just keep in mind that a Python script is just a plain text file: Technically this is Python code that generates Python code: &lt;code&gt;print("print('Hello world!')")&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Download &lt;a href="/sfmuni/routedata.py"&gt;routedata.py&lt;/a&gt; or &lt;a href="http://pastebin.com/jYiBDUk6"&gt;view it online&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I now have a &lt;a href="/sfmuni/routedata.py"&gt;routedata.py&lt;/a&gt; file that looks like this:&lt;/p&gt;
&lt;pre&gt;allRoutes = {'1': '1-California',

 '10': '10-Townsend',

 '108': '108-Treasure Island',

 '12': '12-Folsom/Pacific',

...&lt;/pre&gt;
&lt;p&gt;I won't actually run this script, but I'll import it for the &lt;code&gt;allRoutes&lt;/code&gt; variable in future scripts. There are 81 routes listed in this dictionary.&lt;/p&gt;
&lt;p&gt;The entire process has looked like this:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NextBus XML feed URL &gt; getroutes.py processing &gt; Dictionary in routedata.py&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Getting Route Path Data&lt;/h2&gt;
&lt;p&gt;The &lt;a href="http://www.nextbus.com/xmlFeedDocs/NextBusXMLFeed.pdf‎"&gt;NextBus PDF&lt;/a&gt; explaining their API says I can access a URL like &lt;a href="http://webservices.nextbus.com/service/publicXMLFeed?command=routeConfig&amp;amp;a=sf-muni&amp;amp;r=N"&gt;http://webservices.nextbus.com/service/publicXMLFeed?command=routeConfig&amp;a;=sf-muni&amp;r;=N&lt;/a&gt; to get all the information about the N route. I just have to replace "N" with the other route names in the URL to get info for the other routes. Part of the XML at this URL looks like this:&lt;/p&gt;
&lt;pre&gt;&amp;lt;path&amp;gt;

&amp;lt;point lat="37.76017" lon="-122.50878"/&amp;gt;

&amp;lt;point lat="37.7603" lon="-122.50812"/&amp;gt;

&amp;lt;point lat="37.76039" lon="-122.50606"/&amp;gt;

&amp;lt;point lat="37.76052" lon="-122.50284"/&amp;gt;

&amp;lt;point lat="37.76068" lon="-122.49915"/&amp;gt;

&amp;lt;point lat="37.76083" lon="-122.49596"/&amp;gt;

&amp;lt;/path&amp;gt;

&amp;lt;path&amp;gt;

&amp;lt;point lat="37.76493" lon="-122.45651"/&amp;gt;

&amp;lt;point lat="37.76537" lon="-122.45293"/&amp;gt;&lt;/pre&gt;
&lt;p&gt;Each &amp;lt;path&amp;gt; element has the &amp;lt;point&amp;gt; elements which have latitude and longitude data "that can be used to draw a route on a map." (according to the &lt;a href="http://www.nextbus.com/xmlFeedDocs/NextBusXMLFeed.pdf‎"&gt;NextBus PDF&lt;/a&gt;.) The reason there are multiple &amp;lt;path&amp;gt; elements instead of one giant &amp;lt;path&amp;gt; element is also explained in the PDF: &lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;"Due to the nature of the configuration there can be many separate paths, some of them overlapping. A map client should simply draw all of the paths. The paths are not necessarily in any kind of order so you should only connect the points within a path. You should &lt;strong&gt;not&lt;/strong&gt; connect the points between two separate paths though."&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So we'll need to write another script that downloads the XML for each of the 81 routes, parses it to get the path information, and then put it in a big Python dictionary. I want the Python dictionary to look something like this:&lt;/p&gt;
&lt;pre&gt;allRoutesPathData = {'N': [ [{'lon': XXX, 'lat': XXX}, {'lon': XXX, 'lat': XXX}, ...], ... ],

                     '1': [ [{'lon': XXX, 'lat': XXX}, {'lon': XXX, 'lat': XXX}, ...], ... ],

...&lt;/pre&gt;
&lt;p&gt;The dictionary's keys will be strings of the route name. The values will be lists of lists which represent each &amp;lt;path&amp;gt; element. This list will contain a series of dictionaries with keys &lt;code&gt;'lon'&lt;/code&gt; and &lt;code&gt;lat&lt;/code&gt; for the coordinate points.&lt;/p&gt;
&lt;p&gt;My &lt;a href="/sfmuni/getpathdata.py"&gt;getpathdata.py&lt;/a&gt; script will create the URL for each route, download the XML, parse the longitude and latitude data in the XML, and then produce a giant Python dictionary with this data in a file named &lt;a href="/sfmuni/munipaths.py"&gt;munipaths.py&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Download &lt;a href="/sfmuni/getpathdata.py"&gt;getpathdata.py&lt;/a&gt; or &lt;a href="http://pastebin.com/Qw9KrxZ8"&gt;view it online&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Download &lt;a href="/sfmuni/munipaths.py"&gt;munipaths.py&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The output of this program is in &lt;a href="/sfmuni/munipaths.py"&gt;munipaths.py&lt;/a&gt; and looks like this:&lt;/p&gt;
&lt;pre&gt;munipaths = {'1': [[{'lat': '37.78984', 'lon': '-122.43367'},

        {'lat': '37.78999', 'lon': '-122.4325'},

        {'lat': '37.79019', 'lon': '-122.43085'},

        {'lat': '37.79036', 'lon': '-122.42918'},

        {'lat': '37.79068', 'lon': '-122.42714'},

...&lt;/pre&gt;
&lt;p&gt;This process can be summarized as:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Each NextBus route URL &gt; getpathdata.py processing &gt; Dictionary in munipaths.py&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Getting Route Stop Data&lt;/h2&gt;
&lt;p&gt;We'll also collect the stop information from the same XML feed that had the path information. According to the &lt;a href="http://www.nextbus.com/xmlFeedDocs/NextBusXMLFeed.pdf‎"&gt;NextBus PDF&lt;/a&gt;, the attributes we'll be interested in are "title", "lat", "lon", and "stopId". I'll write a script to download and extract this data just like I did with the path data. I'll call this script &lt;a href="/sfmuni/getstopdata.py"&gt;getstopdata.py&lt;/a&gt; and make it from a modified &lt;a href="/sfmuni/getpathdata.py"&gt;getpathdata.py&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Download &lt;a href="/sfmuni/getstopdata.py"&gt;getstopdata.py&lt;/a&gt; or &lt;a href="http://pastebin.com/eVeLNspA"&gt;view it online&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Download &lt;a href="/sfmuni/munistops.py"&gt;munistops.py&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It outputs the giant Python dictionary to &lt;a href="/sfmuni/munistops.py"&gt;munistops.py&lt;/a&gt;, which looks like this:&lt;/p&gt;
&lt;pre&gt;munistops = {'1': [{'lat': '37.7954199',

        'lon': '-122.397',

        'stopId': '14015',

        'title': 'Clay St &amp; Drumm St'},

       {'lat': '37.7945',

        'lon': '-122.3976099',

        'stopId': '16294',

        'title': 'Sacramento St &amp; Davis St'},

...&lt;/pre&gt;
&lt;p&gt;This process can be summarized as:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Each NextBus route URL &gt; getstopdata.py processing &gt; Dictionary in munistops.py&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Figure Out How Google Maps API Works&lt;/h2&gt;
&lt;p&gt;Okay! Now that we have all our data, we need to figure out how to draw it on Google Maps using their API. First, I'm going to &lt;a href="https://developers.google.com/maps/documentation/javascript/"&gt;start reading through the Google Maps API documentation&lt;/a&gt;. (You don't have to read it, I'll summarize.)&lt;/p&gt;
&lt;p&gt;It starts off "This documentation is designed for people familiar with JavaScript programming and object-oriented programming concepts." So you'll have to know a bit of JavaScript to make use of it.&lt;/p&gt;
&lt;p&gt;First, &lt;a href="https://developers.google.com/maps/documentation/javascript/tutorial#api_key"&gt;according to the guide&lt;/a&gt;, we have to &lt;a href="https://code.google.com/apis/console/?noredirect"&gt;sign up for a Google API key&lt;/a&gt;. This will require having a Google account. The instructions seem a bit out of date (Google has a new Dev Console site). From the APIs menu item on the left, I had to turn on "Google Maps API v3". Then I went to the Credentials menu item on the left, and clicked the "Create New Key" button and typed in "*.inventwithpython.com/*" and "inventwithpython.com/*" as the HTTP referers since this is where I'll host the map. (My inventwithpython.com site can optionally be www.inventwithpython.com, hence the two different referers.)&lt;/p&gt;
&lt;p&gt;This gave me my API key, which for me was "AIzaSyAq1luszpXp057mCW_qifsvM-_fzCOf2Ok" (This key will only work on maps on the inventwithpython.com site, so you can't copy and paste it into your own apps. But getting your own API key is pretty easy.)&lt;/p&gt;
&lt;p&gt;I'll host the map at &lt;a href="http://inventwithpython.com/sfmuni"&gt;http://inventwithpython.com/sfmuni&lt;/a&gt; in the index.html file (that way, the user won't have to type the "index.html" part of the URL).&lt;/p&gt;
&lt;p&gt;You can &lt;a href="http://pastebin.com/7HWNKLan"&gt;view the final source HTML of index.html online&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I've copied their &lt;a href="https://developers.google.com/maps/documentation/javascript/tutorial#HelloWorld"&gt;"Hello World"&lt;/a&gt; example, entering in my own API key and setting the "sensor" parameter to "false" (since this site won't use GPS to find the user's position).&lt;/p&gt;
&lt;p&gt;I browsed to &lt;a href="http://maps.google.com"&gt;http://maps.google.com&lt;/a&gt; and did a search for "San Francisco, CA", and then clicking the "link" icon I got this URL: &lt;a href="https://maps.google.com/?ll=37.7577,-122.4376&amp;amp;spn=0.15906,0.308647&amp;amp;t=m&amp;amp;z=12"&gt;https://maps.google.com/?ll=37.7577,-122.4376&amp;spn;=0.15906,0.308647&amp;t;=m&amp;z;=12&lt;/a&gt;. This gives a nice full view of San Francisco, and the &lt;code&gt;ll=37.7577,-122.4376&lt;/code&gt; part of the URL tells me the latitude and longitude values of the current map. I set the initial latitude and longitude of my Google Map to 37.7577 and -122.4376, and the initial zoom level to 12.&lt;/p&gt;
&lt;p&gt;I've posted this first map at this URL: &lt;a href="http://inventwithpython.com/sfmuni/firstmap.html"&gt;http://inventwithpython.com/sfmuni/firstmap.html&lt;/a&gt;. You can &lt;a href="http://pastebin.com/2WjAsKjg"&gt;view the source HTML of this webpage online&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Drawing Lines on the Google Map&lt;/h2&gt;
&lt;p&gt;Next, I need to read up some more to figure out how to draw what Google Maps calls "polylines" on the map. I'll use polylines to draw out the routes. There is &lt;a href="https://developers.google.com/maps/documentation/javascript/shapes#polylines"&gt;documentation for drawing shapes&lt;/a&gt; as well as &lt;a href="https://developers.google.com/maps/documentation/javascript/examples/polyline-simple"&gt;an example using polylines&lt;/a&gt; on the Google site.&lt;/p&gt;
&lt;p&gt;From the documentation, it looks like I have to have JavaScript code that creates a series of "google.maps.LatLng" objects which are used to create a "google.maps.Polyline" object. I can also set the color, opacity, and width (in pixels) of the polyline with the strokeColor, strokeOpacity, and strokeWeight parameters for the Polyline constructor.&lt;/p&gt;
&lt;p&gt;After all that, I call &lt;code&gt;setMap(map)&lt;/code&gt; on the Polyline object, where &lt;code&gt;map&lt;/code&gt; is the Google map object that was used in the "Hello World" example and is in firstmap.html page&lt;/p&gt;
&lt;p&gt;So I'm going to have to write Python code to generate the text of the JavaScript code. (Once again, I could do all this typing by hand, but that would take hours and hours.) But first, while I'll use the same width and opacity for each route, I don't want to use the same color for each route. I could just use the colors from the original &lt;a href="http://www.sfmta.com/maps/muni-system-map"&gt;Muni System Map PDF&lt;/a&gt;, but there's a lot of color reuse. Instead I'll use some standard RGB colors: #FF0000, #00FF00, #0000FF, #FFFF00, #00FFFF, #FF00FF, #800000, #008000, #000080, #808000, #008080, #800080&lt;/p&gt;
&lt;p&gt;The JavaScript code I have to generate for each Polyline object looks something like this:&lt;/p&gt;
&lt;pre&gt;var routePath = new google.maps.Polyline({

    path: [new google.maps.LatLng(37.78984, -122.43367), ... ],

    strokeColor: '#FF0000',

    strokeOpacity: 1.0,

    strokeWeight: 2

  });

routePath.setMap(map);&lt;/pre&gt;
&lt;p&gt;I wrote &lt;a href="/sfmuni/generatePolylineJS.py"&gt;generatePolylineJS.py&lt;/a&gt; to create this JavaScript code, which is outputted to a text file named &lt;a href="/sfmuni/polylineJS.js?27f655"&gt;polylineJS.js&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Download &lt;a href="/sfmuni/generatePolylineJS.py"&gt;generatePolylineJS.py&lt;/a&gt; or &lt;a href="http://pastebin.com/e6vsaaYb"&gt;view it online&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Download &lt;a href="/sfmuni/polylineJS.js?27f655"&gt;polylineJS.js&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The JavaScript code output is ready to be copied and pasted into our maps web page, and it looks like this:&lt;/p&gt;
&lt;pre&gt;var routePath = new google.maps.Polyline({

    path: [new google.maps.LatLng(37.77774, -122.43817), new google.maps.LatLng( ... ],

    strokeColor: '#00FF00',

    strokeOpacity: 1.0,

    strokeWeight: 2

  });

routePath.setMap(map);



var routePath = new google.maps.Polyline({

    path: [new google.maps.LatLng(37.77999, -122.42014) ...

...&lt;/pre&gt;
&lt;p&gt;I've copied and pasted all this text into my maps web page, index.html, at the end of the &lt;code&gt;initialize()&lt;/code&gt; function. You can view what the file looks like at this point here: &lt;a href="http://inventwithpython.com/sfmuni/secondmap.html"&gt;http://inventwithpython.com/sfmuni/secondmap.html&lt;/a&gt; (Right-click this link to save the HTML file.)&lt;/p&gt;
&lt;p&gt;Look at how huge this html file is! Over 800 kb! I'm glad I wrote a script that could automatically grab the route coordinates from the NextBus feed and then generate the necessary JavaScript for me. If I had to somehow manually mark each of the latitude and longitude points for each of the routes, that would have taken weeks!&lt;/p&gt;
&lt;h2&gt;Reducing the File Size&lt;/h2&gt;
&lt;p&gt;There are 14,266 points in the web page now for all the routes. The web page file is about 800 kb in size, which will be slow to load for smart phones. Let's take out all the redundant code by simply placing the latitude and longitude numbers in a JavaScript object, and then have JavaScript code go through this object to create the Polyline objects. We'll have to write a Python script to generate this JavaScript code for us.&lt;/p&gt;
&lt;p&gt;We want to go from this:&lt;/p&gt;
&lt;pre&gt;var routePath = new google.maps.Polyline({

    path: [new google.maps.LatLng(37.77774, -122.43817), new google.maps.LatLng( ... ],

    strokeColor: '#00FF00',

    strokeOpacity: 1.0,

    strokeWeight: 2

  });

routePath.setMap(map);



var routePath = new google.maps.Polyline({

    path: [new google.maps.LatLng(37.77999, -122.42014) ...

...&lt;/pre&gt;
&lt;p&gt;...to something like this:&lt;/p&gt;
&lt;pre&gt;var allData = {"38BX": [[37.78271,-122.4458,37.78537,-122.44643,37.79107,-122.40095,37.79117, ... (a lot more data here)

var routeColors = {"38BX": "#00FF00","9": "#008000", ...

var routeNames = ["38BX","9","8X", ...





for (var i = 0; i &lt; routeNames.length; i++) { // loop over each route

    var routeName = routeNames[i];

    for (var j = 0; j &lt; allData[routeName].length; j++) { // loop over each path on the route

        var curPath = allData[routeName][j];

        var polylinePoints = [];

        for (var k = 0; k &lt; curPath.length; k += 2) { // loop over each point in the path

            polylinePoints.push(new google.maps.LatLng(curPath[k], curPath[k+1]));

        }

        var routePath = new google.maps.Polyline({

            path: polylinePoints,

            strokeColor: routeColors[routeName],

            strokeOpacity: 1.0,

            strokeWeight: 2

            });

        routePath.setMap(map);

    }

}&lt;/pre&gt;
&lt;p&gt;This will be much smaller since we only have the &lt;code&gt;new google.maps.LatLng(...)&lt;/code&gt; and &lt;code&gt;var routePath = new google.maps.Polyline(...)&lt;/code&gt; code typed once. The JavaScript code creates an array named &lt;code&gt;polylinePoints&lt;/code&gt; that has &lt;code&gt;google.maps.LatLng&lt;/code&gt; objects in it for the points on each path on each route. The &lt;code&gt;routeColors&lt;/code&gt; JavaScript array will store the randomly-determined color to use for each route.&lt;/p&gt;
&lt;p&gt;Also, I noticed that my data currently has a lot of redundant routes on it. For example, the 14 Mission bus route is the same as the 14L "Local" Mission and 14X "Express" Mission routes. I'll set up my new script to not include the following routes: 1AX, 1BX, 5L, 8AX, 8BX, 9L, 14L, 14X, 28L, 31AX, 31BX, 38L, 38AX, 38BX, 71L, K OWL, L OWL, M OWL, N OWL, T OWL. (Routes like the 82X stay because the original route 82 no longer exists.) Also, I later found out that routes 59, 60, 61, 30X, and NX are listed in the NextBus XML feed but are no longer in service.&lt;/p&gt;
&lt;p&gt;This JavaScript code will be generated by our Python script (named &lt;a href="/sfmuni/generatePolylineJS2.py"&gt;generatePolylineJS2.py&lt;/a&gt;) and outputted to a file named &lt;a href="/sfmuni/polylineJS2.py"&gt;polylineJS2.js&lt;/a&gt;. We can copy and paste this JavaScript code into our map web page.&lt;/p&gt;
&lt;p&gt;Download &lt;a href="/sfmuni/generatePolylineJS2.py"&gt;generatePolylineJS2.py&lt;/a&gt; or &lt;a href="http://pastebin.com/PW0njP6n"&gt;view it online&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Download the output file, &lt;a href="/sfmuni/polylineJS2.js?27f655"&gt;polylineJS2.js&lt;/a&gt;, or &lt;a href="http://pastebin.com/Ap6YVUGT"&gt;view it online&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The new map web page is at &lt;a href="/sfmuni/thirdmap.html"&gt;thirdmap.html&lt;/a&gt;. It's the same as &lt;a href="/sfmuni/secondmap.html"&gt;secondmap.html&lt;/a&gt;, except at 226 kb it's about 70% smaller, making it much faster to load.&lt;/p&gt;
&lt;h2&gt;Adding Mouseover Events to the Lines&lt;/h2&gt;
&lt;p&gt;The different colors don't tell us what the route numbers are. It'd be nice if this information showed up whenever we hovered the mouse over it. According to the &lt;a href="https://developers.google.com/maps/documentation/javascript/events"&gt;Events documentation for Google Maps&lt;/a&gt;, we can just call &lt;code&gt;addListener()&lt;/code&gt; on the &lt;code&gt;google.maps.Polyline&lt;/code&gt; and pass them the string &lt;code&gt;'mouseover'&lt;/code&gt; and a JavaScript function to call when the mouse hovers over the polyline.&lt;/p&gt;
&lt;p&gt;To display the route name, we can use an &lt;a href="https://developers.google.com/maps/documentation/javascript/infowindows"&gt;InfoWindow object&lt;/a&gt; that Google Maps provides. When the mouse hovers over a line, move the InfoWindow to the mouse cursor, and then open it and display the route name. When the mouse leaves a line (the &lt;code&gt;'mouseout'&lt;/code&gt; event) we want to close the InfoWindow.&lt;/p&gt;
&lt;p&gt;When the event handling function is called, Google Maps will pass it a &lt;a href="https://developers.google.com/maps/documentation/javascript/reference#MouseEvent"&gt;MouseEvent&lt;/a&gt; object with has a &lt;code&gt;latLng&lt;/code&gt; property of where the mouse event happened. We can use that location to set the position of the InfoWindow.&lt;/p&gt;
&lt;p&gt;After poking around the Google Maps documentation, here's the code I came up with to do this:&lt;/p&gt;
&lt;pre&gt;var routeNames = ["38BX", ... (a bunch more code)



var routeInfoWindow = new google.maps.InfoWindow({ // this info window shows the route name when the mouse hovers over a route line

  disableAutoPan: true

});



...(other code)...



        var routePath = new google.maps.Polyline({

            path: polylinePoints,

            strokeColor: routeColors[routeName],

            strokeOpacity: 1.0,

            strokeWeight: 2

            });



        (function(thisRouteName) { // making this into a closure so that "thisRouteName" keeps its value

          google.maps.event.addListener(routePath, 'mouseover', function(moevent) {

            routeInfoWindow.setPosition(moevent.latLng); // set the info window where the mouse is

            routeInfoWindow.setContent(thisRouteName);

            routeInfoWindow.open(map);

          });

        })(routeName);



        routePath.setMap(map);

...(rest of the map web page)...&lt;/pre&gt;
&lt;p&gt;I decided not to have code that closes the window on the mouseout event because then you have to get the mouse precisely on the super skinny line, which was less than ideal. Also, I realized that I could make these info windows have not just the text of the route name, but also make them into a link to the &lt;a href="http://www.sfmta.com/maps/categories/muni-route-maps"&gt;individual route maps on the Muni website&lt;/a&gt;. (Although since the URLs for these maps are named like &lt;a href="http://www.sfmta.com/node/11329"&gt;http://www.sfmta.com/node/11329&lt;/a&gt; instead of having names based off the route name, I had to copy and paste the links from my browser and into my code. The JavaScript I added looks like this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;var routeMapLinks = {"1":"11329", "10":"11341", "108":"11406", "12":"11342", "14":"11343", "16X":"11344", "17":"76586", "18":"11346", "19":"11347", "2":"11330", "21":"11348", "22":"11349", "23":"11350", "24":"11351", "27":"11352", "28":"11353", "29":"11355", "3":"11331", "30":"11356", "31":"11358", "33":"11359", "35":"11360", "36":"11361", "37":"11362", "38":"11363", "39":"11364", "41":"11365", "43":"11366", "44":"11367", "45":"11357", "47":"11368", "48":"11369", "49":"87351", "5":"11332", "52":"11371", "54":"11372", "56":"11373", "6":"11333", "66":"11377", "67":"11378", "71":"88001", "76X":"11379", "81X":"11381", "82X":"11382", "83X":"11383", "88":"73391", "8X":"72596", "9":"11339", "90":"11385", "91":"11386", "F":"11399", "J":"11387", "K":"71901", "KT":"69176", "L":"11398", "M":"11397", "N":"11388"};&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I've made the code adjustments to the map web page and you can view it here: &lt;a href="http://inventwithpython.com/sfmuni/fourthmap.html"&gt;http://inventwithpython.com/sfmuni/fourthmap.html&lt;/a&gt;. You can &lt;a href="http://pastebin.com/eHtuL53k"&gt;view the source HTML online&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Adding Bus Stop Markers to the Map&lt;/h2&gt;
&lt;p&gt;Next, we want to add Google Map's markers for each of the stops. One slight problem with the data in munistops.py though: it has a list of stops for each route, but because some stops are used by multiple routes there's a lot of redundant information there. We need to make a dictionary where the keys are stops and the values hold the information for the stop (including a list of routes that the stop serves).&lt;/p&gt;
&lt;p&gt;According to the &lt;a href="https://developers.google.com/maps/documentation/javascript/reference#Marker"&gt;Google Maps API documentation for markers&lt;/a&gt;, adding a marker to the map looks like this:&lt;/p&gt;
&lt;pre&gt;var marker = new google.maps.Marker({

    position: new google.maps.LatLng(&lt;lat number&gt;, &lt;lng number&gt;),

    map: map,

    title: "Some Title Text"

    });&lt;/pre&gt;
&lt;p&gt;There's a slight problem. We have our bus stop data in a format where the keys are route names and the values are lists of stops. Since multiple routes use the same stops, we will end up with redundant bus stop markers on our map. We need to invert it so we have a Python dictionary of bus stops as keys and route names (and other info) as the values. I've written &lt;a href="/sfmunicleanup_stops.py/"&gt;cleanup_stops.py&lt;/a&gt; to generate the JavaScript I need (it is outputted to &lt;a href="/sfmuni/stops_cleanedup.js?27f655"&gt;stops_cleanedup.js&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Download &lt;a href="/sfmuni/cleanup_stops.py"&gt;cleanup_stops.py&lt;/a&gt; or &lt;a href="http://pastebin.com/aqJPqm7W"&gt;view it online&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Download &lt;a href="/sfmuni/stops_cleanedup.js?27f655"&gt;stops_cleanedup.js&lt;/a&gt; or &lt;a href="http://pastebin.com/QGXxiyrE"&gt;view it online&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The map web page with stop markers is here: &lt;a href="http://inventwithpython.com/sfmuni/fifthmap.html"&gt;http://inventwithpython.com/sfmuni/fifthmap.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/sfmuni/sfmuni_screenshot3.png?27f655" /&gt;&lt;/p&gt;
&lt;p&gt;Wow. That looks horrible. I realized that I don't need another InfoWindow to appear when you click on the marker, because hovering the mouse over the marker will show a tooltip with the stop's name. But even after commenting that out, the sheer number of stops makes the map look terrible.&lt;/p&gt;
&lt;p&gt;I could add code that makes a single route's stop markers appear when the mouse is over the route polyline. Or maybe the markers only show up once you are zoomed in enough. But I like the fourth map that didn't have the markers, so I'll just go with that. (Making bus stop markers appear and disappear on the map as the mouse hovers over the route polyline is left as an exercise for the reader.)&lt;/p&gt;
&lt;p&gt;The final product is hosted at &lt;a href="http://inventwithpython.com/sfmuni/"&gt;http://inventwithpython.com/sfmuni/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can &lt;a href="http://pastebin.com/7HWNKLan"&gt;view the final source HTML online&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;That's the entire product: a Google Map with the San Francisco Muni routes drawn over it. At the start of this project I had no experience with the Google Maps API and didn't know where I'd get the route info. I spent much more time reading documentation and experimenting than I did writing code, which is usually how projects go.&lt;/p&gt;
&lt;p&gt;Here are the files in this tutorial:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/sfmuni/getroutes.py"&gt;getroutes.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/sfmuni/routedata.py"&gt;routedata.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/sfmuni/getpathdata.py"&gt;getpathdata.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/sfmuni/munipaths.py"&gt;munipaths.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/sfmuni/getstopdata.py"&gt;getstopdata.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/sfmuni/munistops.py"&gt;munistops.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/sfmuni/generatePolylineJS.py"&gt;generatePolylineJS.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/sfmuni/polylineJS.js?27f655"&gt;polylineJS.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/sfmuni/generatePolylineJS2.py"&gt;generatePolylineJS2.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/sfmuni/polylineJS2.js?27f655"&gt;polylineJS2.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/sfmuni/cleanup_stops.py"&gt;cleanup_stops.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/sfmuni/stops_cleanedup.js?27f655"&gt;stops_cleanedup.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/sfmuni/index.html"&gt;index.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My final secret: I play the &lt;a href="http://en.wikipedia.org/wiki/Augmented_reality"&gt;AR game&lt;/a&gt; &lt;a href="http://ingress.com"&gt;Ingress&lt;/a&gt; and the reason I wanted a better Muni system map was so that I could compare it to the Ingress map of portals in the game. The 71 bus through the Haight goes by a ton of portals, and it's nice to hit them all while sitting in a bus seat, and I was wondering if there were other routes similarly packed with portals. To do that, I needed a Muni system map that was easy to navigate. :)&lt;/p&gt;
&lt;p&gt;I hope you enjoyed this tutorial. Please leave comments for any parts you have trouble understanding or you think need more explanation. Have fun and keep coding!&lt;/p&gt;


</content><category term="misc"></category></entry><entry><title>Why Is HTML Not A Programming Language?</title><link href="https://inventwithpython.com/blog/why-is-html-not-a-programming-language.html" rel="alternate"></link><published>2013-12-15T12:00:00-05:00</published><updated>2013-12-15T12:00:00-05:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2013-12-15:/blog/why-is-html-not-a-programming-language.html</id><summary type="html">&lt;p&gt;HTML is not a programming language. You can't write a "program" that, say, calculates the average of ten numbers with HTML. While some may point out that HTML is a "markup language", this doesn't clarify how that is different from a programming language. This isn't meant to be a thorough argument, but rather a brief explanation that goes just a bit more in detail than "HTML isn't a programming language but instead a markup language."&lt;/p&gt;</summary><content type="html">

&lt;p&gt;HTML is not a programming language. You can't write a "program" that, say, calculates the average of ten numbers with HTML. While some may point out that HTML is a "markup language", this doesn't clarify how that is different from a programming language. This isn't meant to be a thorough argument, but rather a brief explanation that goes just a bit more in detail than "HTML isn't a programming language but instead a markup language."&lt;/p&gt;

&lt;p&gt;But first, an important note: The fact that HTML is not a programming language is often used as a way to devalue it, along with web design or front-end engineering in general. HTML and CSS are valuable skills and like many skills, they are easy to learn at a rudimentary level but take a large effort to master. Many techies argue about the "merits" of their favorite programming language, operating system, or whatever and nine out of ten times they're actually expressing their own opinion or a fact that is largely irrelevant. The point of this blog post isn't denigrate HTML.&lt;/p&gt;

&lt;p&gt;Here is a point I'd like to make: &lt;strong&gt;HTML is no more a programming language than Microsoft Word is.&lt;/strong&gt; With Word (or any other word processor software), you can write out text and format it with different fonts, sizes, and colors. Images and tables and bullet points can be added as well. This is all done through the word processor's graphical user interface.&lt;/p&gt;

&lt;p&gt;An HTML file is similar, except it does not have a GUI. Instead, it is entirely written out in plaintext, so all the formatting instructions must be typed out as HTML tags (for example, &amp;lt;b&amp;gt; or &amp;lt;center&amp;gt;.) This "language" of tags is how we can format the appearance of a webpage.&lt;/p&gt;

&lt;p&gt;Programming languages, on the other hand, can &lt;strong&gt;process data&lt;/strong&gt; and &lt;strong&gt;make decisions&lt;/strong&gt;. You can store data such as text strings and integers, and then manipulate these values to perform calculations. For example, doing some math or sorting text alphabetically are kinds of &lt;em&gt;data processing&lt;/em&gt; that you cannot do in HTML.&lt;/p&gt;

&lt;p&gt;Programming languages can also make decisions about what instructions they should execute. Depending on if a certain condition is true or false, a set of instructions may be executed or skipped by a program. Here is a Python example of such code:&lt;/p&gt;

&lt;pre&gt;if password == 'rosebud':

    print('Access granted.')

else:

    print('Access denied.')&lt;/pre&gt;

&lt;p&gt;Programming languages also have ways of executing instructions over and over again in loops. Loops, if-else statements, and other such instructions are called &lt;em&gt;flow control&lt;/em&gt; statements. &lt;strong&gt;All programming languages have these flow control statements, but HTML (and Microsoft Word) do not.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;JavaScript is a programming language. It has all these features of flow control and data processing. While JavaScript is used in many web pages, it is a distinctly separate thing from HTML. You can write HTML without JavaScript, and you can write JavaScript code without HTML.&lt;/p&gt;

&lt;p&gt;Because HTML lacks these features, it isn't a programming language. &lt;strong&gt;One does not "program in HTML".&lt;/strong&gt; You shouldn't list HTML on your resume under "programming languages" anymore (a broader title such as "skills" would be more suitable).&lt;/p&gt;

</content><category term="misc"></category></entry><entry><title>"Invent with Scratch" Screencast Series</title><link href="https://inventwithpython.com/blog/invent-with-scratch-screencast-series.html" rel="alternate"></link><published>2013-10-02T12:00:00-04:00</published><updated>2013-10-02T12:00:00-04:00</updated><author><name>Al Sweigart</name></author><id>tag:inventwithpython.com,2013-10-02:/blog/invent-with-scratch-screencast-series.html</id><summary type="html">&lt;p&gt;I've created a series of video screencast tutorials for Scratch. Scratch is a block-based programming environment from MIT. It is a programming education toy that is made for kids between the ages of 8 and 16. The screencasts can be found at &lt;a href="https://inventwithscratch.com"&gt;InventWithScratch.com&lt;/a&gt;&lt;/p&gt;</summary><content type="html">


			&lt;p&gt;I've created a series of video screencast tutorials for Scratch. Scratch is a block-based programming environment from MIT. It is a programming education toy that is made for kids between the ages of 8 and 16. The screencasts can be found at:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="http://inventwithscratch.com"&gt;"Invent with Scratch" Screencast at http://inventwithscratch.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Scratch itself is hosted at &lt;a href="http://scratch.mit.edu"&gt;http://scratch.mit.edu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I highly recommend Scratch as a teaching tool for younger kids who may not be ready for Python programming or are frustrated by their slow typing. Scratch is a drag-and-drop environment with code "blocks" that snap together.&lt;/p&gt;


</content><category term="misc"></category></entry></feed>