Compare commits

...

49 Commits

Author SHA1 Message Date
github-actions[bot]
05edde6c33 docs: auto-update README.md via pytudes.py 2026-06-17 21:51:48 +00:00
Peter Norvig
ceb79dd67f Add tutorial on Lisp interpreter to pytudes 2026-06-17 14:51:26 -07:00
Peter Norvig
76e35e2cd3 Add files via upload 2026-06-17 14:19:39 -07:00
Peter Norvig
a2f6772b66 Fix typo in colab link 2026-06-11 13:12:06 -07:00
Peter Norvig
8fbf7b4a19 Fix typo in README.md for 'colab' 2026-06-11 13:11:00 -07:00
github-actions[bot]
8a5a7fd4f8 docs: auto-update README.md via pytudes.py 2026-06-11 20:09:29 +00:00
Peter Norvig
af3fb185ef Add files via upload 2026-06-11 13:09:15 -07:00
Peter Norvig
3db6443929 Delete py/README.md 2026-06-11 13:00:45 -07:00
github-actions[bot]
c9c763f97f docs: auto-update README.md via pytudes.py 2026-06-11 19:58:57 +00:00
Peter Norvig
9ec85ae31e Add files via upload 2026-06-11 12:58:37 -07:00
Peter Norvig
59007a6145 Convert year comparison to integer for accuracy 2026-06-11 12:45:06 -07:00
Peter Norvig
84616c6a9b Fix formatting in pytudes.py 2026-06-11 12:37:05 -07:00
Peter Norvig
2d8286014f Add environment variable for Node.js version
Set environment variable to force Node.js version 24 for actions.
2026-06-11 12:36:26 -07:00
Peter Norvig
10e2880c9b Add new programming example for approximating Pi 2026-06-11 12:32:38 -07:00
Peter Norvig
dba5328b8a Add files via upload 2026-06-11 12:27:51 -07:00
Peter Norvig
57fc06e78c Update concordance description formatting in Snobol notebook 2026-06-11 11:23:34 -07:00
Peter Norvig
cbe3c7a9b3 Merge pull request #146 from JEHoctor/patch-1
Fix return type and docstring in guess_row function
2026-06-03 13:46:17 -07:00
Peter Norvig
7b24391b7b Add files via upload 2026-05-29 16:39:21 -07:00
Peter Norvig
d67caa2820 Add files via upload 2026-05-29 13:59:54 -07:00
Peter Norvig
7ac2359e88 Add files via upload 2026-05-29 13:29:30 -07:00
Peter Norvig
9e14d0c5ac Fix table formatting in Pickleball.ipynb 2026-05-16 11:17:07 -07:00
Peter Norvig
759bc14257 Fix table formatting in Pickleball.ipynb 2026-05-16 11:08:07 -07:00
Peter Norvig
6e45084acb Add files via upload 2026-04-25 09:54:04 -07:00
Peter Norvig
ce990774bc Add files via upload 2026-04-25 09:50:53 -07:00
Peter Norvig
721a7954b4 Remove year highlighting for current year 2026-04-25 09:44:39 -07:00
Peter Norvig
7cebfa6149 Add new puzzles and games to pytudes.py 2026-04-25 09:28:35 -07:00
github-actions[bot]
9b965630ac docs: auto-update README.md via pytudes.py 2026-04-25 16:23:16 +00:00
Peter Norvig
1bc1ce5cb9 Update Sudoku Java puzzle speed from 100k to 200k 2026-04-25 09:23:02 -07:00
Peter Norvig
ec3c52f43a Add files via upload 2026-04-24 22:10:43 -07:00
Peter Norvig
5907ec9bbf Add files via upload 2026-04-23 09:09:36 -07:00
Peter Norvig
2e8234232d Update explanation of modular exponentiation 2026-03-29 19:44:23 -07:00
Peter Norvig
be1195dfa6 Fix formatting in TruncatablePrimes.ipynb 2026-03-29 18:50:31 -07:00
Peter Norvig
6712e8f626 Fix wording in TruncatablePrimes.ipynb 2026-03-29 18:50:08 -07:00
Peter Norvig
9ec6d0565e Add files via upload 2026-03-29 18:32:37 -07:00
Peter Norvig
5b9b2c2a11 Add files via upload 2026-03-25 17:16:09 -07:00
Peter Norvig
32dda84d1a Fix formatting issues in xkcd1313 notebook 2026-03-25 11:11:41 -07:00
Peter Norvig
1011a6c263 Remove unused code cells from xkcd1313 notebook 2026-03-25 11:10:19 -07:00
Peter Norvig
b499e06b97 Fix formatting of update message in xkcd1313 notebook 2026-03-25 11:08:28 -07:00
Peter Norvig
5d8afafae0 Fix formatting issue in Orderable Cards notebook 2026-03-24 14:37:41 -07:00
github-actions[bot]
7dd20e0759 docs: auto-update README.md via pytudes.py 2026-03-24 21:34:45 +00:00
Peter Norvig
345f95b1b1 Rename card hand description for clarity 2026-03-24 14:34:31 -07:00
Peter Norvig
e9f863ca75 Add files via upload 2026-03-23 15:36:44 -07:00
Peter Norvig
edc8549237 Fix typo and update condition in simulation function 2026-03-19 14:39:54 -07:00
Peter Norvig
a8d49f0e9f Add files via upload 2026-03-18 11:26:16 -07:00
Peter Norvig
ce4f285150 Add files via upload 2026-03-16 21:23:58 -07:00
Peter Norvig
d4cfefac7c Fix assert in missing_bridges 2026-03-13 12:55:25 -07:00
Peter Norvig
932dcac87f Fix punctuation in markdown cell text 2026-03-13 12:37:10 -07:00
Peter Norvig
3789bd415d Fix formatting issues in Portmantout.ipynb 2026-03-13 12:32:43 -07:00
James Hoctor
8e4b98eba4 Fix return type and docstring in guess_row function 2026-03-11 14:58:31 -04:00
25 changed files with 354364 additions and 503519 deletions

View File

@@ -5,6 +5,9 @@ on:
paths:
- 'py/pytudes.py'
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
build-and-update:
runs-on: ubuntu-latest

238
README.md
View File

@@ -15,151 +15,149 @@ To continue the musical analogy, some people think of programming like [Spotify]
# Index of Jupyter (IPython) Notebooks
For each notebook you can hover on the title to see a description, or click the title to view on github, or click one of the letters in the left column to launch the notebook on
[**C**olab](https://colab.research.google.com),
[**D**eepnote](https://deepnote.com),
[**M**ybinder](https://mybinder.org),
[**S**agemaker](https://studiolab.sagemaker.aws/), or
[**N**BViewer](https://nbviewer.jupyter.org/).
For each notebook you can hover on the title to see a description, or click the title to view on github, or click one of the letters in the left column to open the notebook on
[**co**lab](https://colab.research.google.com) or
[**nb**viewer](https://nbviewer.jupyter.org/).
|Run|Year|New|
|Open|Year|New|
|---|---|---|
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/TruncatablePrimes.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FTruncatablePrimes.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FTruncatablePrimes.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/TruncatablePrimes.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/TruncatablePrimes.ipynb) | <u>2026</u> | <a href="ipynb/TruncatablePrimes.ipynb" title="Prime numbers where you can remove digits and still get primes.">Truncatable Primes</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2025.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAdvent-2025.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAdvent-2025.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2025.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Advent-2025.ipynb) | 2025 | <a href="ipynb/Advent-2025.ipynb" title="Puzzle site with a coding puzzle for 12 days of Christmas, December 2025">Advent of Code 2025</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAdvent-2025-AI.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAdvent-2025-AI.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) | 2025 | <a href="ipynb/Advent-2025-AI.ipynb" title="Using large language coding models to solve AoC puzzles">Advent of Code 2025: AI LLM Edition</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2024.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAdvent-2024.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAdvent-2024.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2024.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Advent-2024.ipynb) | 2024 | <a href="ipynb/Advent-2024.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2024">Advent of Code 2024</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Paint.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FPaint.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FPaint.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Paint.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Paint.ipynb) | 2024 | <a href="ipynb/Paint.ipynb" title="What is the average cluster size of a random grid of colored squares?">Counting Cluster Sizes in Paint by Numbers</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/CherylMind.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FCherylMind.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FCherylMind.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/CherylMind.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/CherylMind.ipynb) | 2024 | <a href="ipynb/CherylMind.ipynb" title="Do LLMs have enough theory of mind to solve the Cheryl's Birthday puzzle?">LLMs, Theory of Mind, and Cheryl's Birthday</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/NumberBracelets.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FNumberBracelets.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FNumberBracelets.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/NumberBracelets.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/NumberBracelets.ipynb) | 2024 | <a href="ipynb/NumberBracelets.ipynb" title="A game involving numbered beads on a circular bracelet.">Number Bracelets Game</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Overtime.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FOvertime.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FOvertime.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Overtime.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Overtime.ipynb) | 2024 | <a href="ipynb/Overtime.ipynb" title="In American Football, which team has the advantage in overtime?">Overtime in American Football</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Stubborn.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FStubborn.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FStubborn.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Stubborn.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Stubborn.ipynb) | 2024 | <a href="ipynb/Stubborn.ipynb" title="Any number ending in 5 has a square that also ends in 5. What other endings are like this?">Stubborn number endings</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Triplets.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FTriplets.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FTriplets.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Triplets.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Triplets.ipynb) | 2024 | <a href="ipynb/Triplets.ipynb" title="LLMs do better at producing a program to solve this puzzle than they do at solving it directly">The Languages of English, Math, and Programming</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/lispy.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/lispy.ipynb) | 2026 | <a href="ipynb/lispy.ipynb" title="Tutorial on interpreters.">(How to Write a (Lisp) Interpreter (in Python))</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/RationalPi.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/RationalPi.ipynb) | 2026 | <a href="ipynb/RationalPi.ipynb" title="Using continued fractions to find fractions (like 22/7) that are close to pi.">Approximating Pi with a Fraction</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/clvr.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/clvr.ipynb) | 2026 | <a href="ipynb/clvr.ipynb" title="Guess famous phrases from letter shapes. Beginner level.">Did you solve it? R y clvr ngh t rd ths sntnc?</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler3.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler3.ipynb) | 2026 | <a href="ipynb/Euler3.ipynb" title="Find the largest prime factor of a number">Project Euler #3: Largest prime factor</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/TruncatablePrimes.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/TruncatablePrimes.ipynb) | 2026 | <a href="ipynb/TruncatablePrimes.ipynb" title="Prime numbers where you can remove digits and still get primes.">Truncatable Primes</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2025.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2025.ipynb) | 2025 | <a href="ipynb/Advent-2025.ipynb" title="Puzzle site with a coding puzzle for 12 days of Christmas, December 2025">Advent of Code 2025</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) | 2025 | <a href="ipynb/Advent-2025-AI.ipynb" title="Using large language coding models to solve AoC puzzles">Advent of Code 2025: AI LLM Edition</a> |
|Run|Year|Programming Examples|
|Open|Year|Programming Examples|
|---|---|---|
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/AlphaCode.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAlphaCode.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAlphaCode.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/AlphaCode.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/AlphaCode.ipynb) | 2022 | <a href="ipynb/AlphaCode.ipynb" title="Analysis of AlphaCode's automated solution to a coding problem">AlphaCode Automated Programming</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Babylonian%20digits.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FBabylonian%20digits.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FBabylonian%20digits.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Babylonian%20digits.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Babylonian%20digits.ipynb) | 2022 | <a href="ipynb/Babylonian%20digits.ipynb" title="Translating between Babylonian and traditional number systems.">The Babylonian Number System</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Beal.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FBeal.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FBeal.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Beal.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Beal.ipynb) | 2018 | <a href="ipynb/Beal.ipynb" title="A search for counterexamples to Beal's Conjecture">Beal's Conjecture Revisited</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Bike-Stats.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FBike-Stats.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FBike-Stats.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Bike-Stats.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Bike-Stats.ipynb) | 2020 | <a href="ipynb/Bike-Stats.ipynb" title="Visualizing statistics about bike routes">Bicycling Statistics</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Cant-Stop.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FCant-Stop.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FCant-Stop.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Cant-Stop.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Cant-Stop.ipynb) | 2018 | <a href="ipynb/Cant-Stop.ipynb" title="Optimal play in a dice board game">Can't Stop</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Sierpinski.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FSierpinski.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FSierpinski.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Sierpinski.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Sierpinski.ipynb) | 2019 | <a href="ipynb/Sierpinski.ipynb" title="A surprising appearance of the Sierpinski triangle in a random walk">Chaos with Triangles</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Life.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FLife.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FLife.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Life.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Life.ipynb) | 2017 | <a href="ipynb/Life.ipynb" title="The cellular automata zero-player game">Conway's Game of Life</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Maze.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FMaze.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FMaze.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Maze.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Maze.ipynb) | 2020 | <a href="ipynb/Maze.ipynb" title="Make a maze by generating a random tree superimposed on a grid and solve it">Generating and Solving Mazes</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Triplets.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FTriplets.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FTriplets.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Triplets.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Triplets.ipynb) | 2024 | <a href="ipynb/Triplets.ipynb" title="LLMs do better at producing a program to solve this puzzle than they do at solving it directly">The Languages of English, Math, and Programming</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Konane.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FKonane.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FKonane.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Konane.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Konane.ipynb) | 2021 | <a href="ipynb/Konane.ipynb" title="Solving the game of Konane (Hawaiian checkers).">Mel's Konane Board</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/PhotoFocalLengths.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FPhotoFocalLengths.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FPhotoFocalLengths.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/PhotoFocalLengths.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/PhotoFocalLengths.ipynb) | 2020 | <a href="ipynb/PhotoFocalLengths.ipynb" title="Generate charts of what focal lengths were used on a photo trip">Photo Focal Lengths</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Pickleball.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FPickleball.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FPickleball.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Pickleball.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Pickleball.ipynb) | 2018 | <a href="ipynb/Pickleball.ipynb" title="Scheduling a doubles tournament fairly and efficiently">Pickleball Tournament</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Project%20Euler%20Utils.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FProject%20Euler%20Utils.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FProject%20Euler%20Utils.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Project%20Euler%20Utils.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Project%20Euler%20Utils.ipynb) | 2017 | <a href="ipynb/Project%20Euler%20Utils.ipynb" title="My utility functions for the Project Euler problems, including `Primes` and `Factors`">Project Euler Utilities</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Menu.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FMenu.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FMenu.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Menu.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Menu.ipynb) | 2022 | <a href="ipynb/Menu.ipynb" title="Efficiently Selecting Names from a Menu, by typing characters and arrows">Selecting Names from a Menu</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Electoral%20Votes.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FElectoral%20Votes.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FElectoral%20Votes.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Electoral%20Votes.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Electoral%20Votes.ipynb) | 2020 | <a href="ipynb/Electoral%20Votes.ipynb" title="How many electoral votes would Trump get if he wins the state where he has positive net approval?">Tracking Polls: Electoral Votes</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/AlphaCode.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/AlphaCode.ipynb) | 2022 | <a href="ipynb/AlphaCode.ipynb" title="Analysis of AlphaCode's automated solution to a coding problem">AlphaCode Automated Programming</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/RationalPi.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/RationalPi.ipynb) | 2026 | <a href="ipynb/RationalPi.ipynb" title="Using continued fractions to find fractions (like 22/7) that are close to pi.">Approximating Pi with a Fraction</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Babylonian%20digits.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Babylonian%20digits.ipynb) | 2022 | <a href="ipynb/Babylonian%20digits.ipynb" title="Translating between Babylonian and traditional number systems.">The Babylonian Number System</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Beal.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Beal.ipynb) | 2018 | <a href="ipynb/Beal.ipynb" title="A search for counterexamples to Beal's Conjecture">Beal's Conjecture Revisited</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Bike-Stats.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Bike-Stats.ipynb) | 2020 | <a href="ipynb/Bike-Stats.ipynb" title="Visualizing statistics about bike routes">Bicycling Statistics</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Cant-Stop.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Cant-Stop.ipynb) | 2018 | <a href="ipynb/Cant-Stop.ipynb" title="Optimal play in a dice board game">Can't Stop</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Sierpinski.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Sierpinski.ipynb) | 2019 | <a href="ipynb/Sierpinski.ipynb" title="A surprising appearance of the Sierpinski triangle in a random walk">Chaos with Triangles</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Life.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Life.ipynb) | 2017 | <a href="ipynb/Life.ipynb" title="The cellular automata zero-player game">Conway's Game of Life</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Maze.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Maze.ipynb) | 2020 | <a href="ipynb/Maze.ipynb" title="Make a maze by generating a random tree superimposed on a grid and solve it">Generating and Solving Mazes</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Triplets.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Triplets.ipynb) | 2024 | <a href="ipynb/Triplets.ipynb" title="LLMs do better at producing a program to solve this puzzle than they do at solving it directly">The Languages of English, Math, and Programming</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Konane.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Konane.ipynb) | 2021 | <a href="ipynb/Konane.ipynb" title="Solving the game of Konane (Hawaiian checkers).">Mel's Konane Board</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/PhotoFocalLengths.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/PhotoFocalLengths.ipynb) | 2020 | <a href="ipynb/PhotoFocalLengths.ipynb" title="Generate charts of what focal lengths were used on a photo trip">Photo Focal Lengths</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Pickleball.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Pickleball.ipynb) | 2018 | <a href="ipynb/Pickleball.ipynb" title="Scheduling a doubles tournament fairly and efficiently">Pickleball Tournament</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Project%20Euler%20Utils.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Project%20Euler%20Utils.ipynb) | 2017 | <a href="ipynb/Project%20Euler%20Utils.ipynb" title="My utility functions for the Project Euler problems, including `Primes` and `Factors`">Project Euler Utilities</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Menu.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Menu.ipynb) | 2022 | <a href="ipynb/Menu.ipynb" title="Efficiently Selecting Names from a Menu, by typing characters and arrows">Selecting Names from a Menu</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Electoral%20Votes.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Electoral%20Votes.ipynb) | 2020 | <a href="ipynb/Electoral%20Votes.ipynb" title="How many electoral votes would Trump get if he wins the state where he has positive net approval?">Tracking Polls: Electoral Votes</a> |
|Run|Year|Advent of Code|
|Open|Year|Advent of Code|
|---|---|---|
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAdvent-2025-AI.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAdvent-2025-AI.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) | 2025 | <a href="ipynb/Advent-2025-AI.ipynb" title="Using large language coding models to solve AoC puzzles">Advent of Code 2025: AI LLM Edition</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2025.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAdvent-2025.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAdvent-2025.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2025.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Advent-2025.ipynb) | 2025 | <a href="ipynb/Advent-2025.ipynb" title="Puzzle site with a coding puzzle for 12 days of Christmas, December 2025">Advent of Code 2025</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2024.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAdvent-2024.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAdvent-2024.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2024.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Advent-2024.ipynb) | 2024 | <a href="ipynb/Advent-2024.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2024">Advent of Code 2024</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2023.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAdvent-2023.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAdvent-2023.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2023.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Advent-2023.ipynb) | 2023 | <a href="ipynb/Advent-2023.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2023">Advent of Code 2023</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2022.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAdvent-2022.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAdvent-2022.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2022.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Advent-2022.ipynb) | 2022 | <a href="ipynb/Advent-2022.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2022">Advent of Code 2022</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2021.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAdvent-2021.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAdvent-2021.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2021.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Advent-2021.ipynb) | 2021 | <a href="ipynb/Advent-2021.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2021">Advent of Code 2021</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2020.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAdvent-2020.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAdvent-2020.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2020.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Advent-2020.ipynb) | 2020 | <a href="ipynb/Advent-2020.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2020">Advent of Code 2020</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2018.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAdvent-2018.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAdvent-2018.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2018.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Advent-2018.ipynb) | 2018 | <a href="ipynb/Advent-2018.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2018">Advent of Code 2018</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2017.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAdvent-2017.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAdvent-2017.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2017.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Advent-2017.ipynb) | 2017 | <a href="ipynb/Advent-2017.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2017">Advent of Code 2017</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2016.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAdvent-2016.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAdvent-2016.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2016.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Advent-2016.ipynb) | 2016 | <a href="ipynb/Advent-2016.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2016">Advent of Code 2016</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/AdventUtils.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAdventUtils.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAdventUtils.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/AdventUtils.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/AdventUtils.ipynb) | 2022 | <a href="ipynb/AdventUtils.ipynb" title="Utility functions for Advent of Code puzzles">Advent of Code Utilities</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) | 2025 | <a href="ipynb/Advent-2025-AI.ipynb" title="Using large language coding models to solve AoC puzzles">Advent of Code 2025: AI LLM Edition</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2025.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2025.ipynb) | 2025 | <a href="ipynb/Advent-2025.ipynb" title="Puzzle site with a coding puzzle for 12 days of Christmas, December 2025">Advent of Code 2025</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2024.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2024.ipynb) | 2024 | <a href="ipynb/Advent-2024.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2024">Advent of Code 2024</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2023.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2023.ipynb) | 2023 | <a href="ipynb/Advent-2023.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2023">Advent of Code 2023</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2022.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2022.ipynb) | 2022 | <a href="ipynb/Advent-2022.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2022">Advent of Code 2022</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2021.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2021.ipynb) | 2021 | <a href="ipynb/Advent-2021.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2021">Advent of Code 2021</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2020.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2020.ipynb) | 2020 | <a href="ipynb/Advent-2020.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2020">Advent of Code 2020</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2018.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2018.ipynb) | 2018 | <a href="ipynb/Advent-2018.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2018">Advent of Code 2018</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2017.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2017.ipynb) | 2017 | <a href="ipynb/Advent-2017.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2017">Advent of Code 2017</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2016.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2016.ipynb) | 2016 | <a href="ipynb/Advent-2016.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2016">Advent of Code 2016</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/AdventUtils.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/AdventUtils.ipynb) | 2022 | <a href="ipynb/AdventUtils.ipynb" title="Utility functions for Advent of Code puzzles">Advent of Code Utilities</a> |
|Run|Year|Probability and Uncertainty|
|Open|Year|Probability and Uncertainty|
|---|---|---|
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Goldberg.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FGoldberg.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FGoldberg.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Goldberg.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Goldberg.ipynb) | 2019 | <a href="ipynb/Goldberg.ipynb" title="A re-implementation in Python 3 of Yoav Goldberg's unreasonably effective character-level n-gram language model.">Effectiveness of Language Models</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Probability.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FProbability.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FProbability.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Probability.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Probability.ipynb) | 2018 | <a href="ipynb/Probability.ipynb" title="Code and examples of the basic principles of Probability Theory">A Concrete Introduction to Probability</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/ProbabilityParadox.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FProbabilityParadox.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FProbabilityParadox.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/ProbabilityParadox.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/ProbabilityParadox.ipynb) | 2016 | <a href="ipynb/ProbabilityParadox.ipynb" title="Some classic paradoxes in Probability Theory, and how to think about disagreements">Probability, Paradox, and the Reasonable Person Principle</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/ProbabilitySimulation.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FProbabilitySimulation.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FProbabilitySimulation.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/ProbabilitySimulation.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/ProbabilitySimulation.ipynb) | 2020 | <a href="ipynb/ProbabilitySimulation.ipynb" title="When the sample space is too complex, simulations can estimate probabilities">Estimating Probabilities with Simulations</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Diamonds.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FDiamonds.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FDiamonds.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Diamonds.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Diamonds.ipynb) | 2023 | <a href="ipynb/Diamonds.ipynb" title="Finding an optimal strategy for buying bags with unknown numbers of diamonds.">The Diamond Game: A Probability Puzzle</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Coin%20Flip.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FCoin%20Flip.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FCoin%20Flip.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Coin%20Flip.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Coin%20Flip.ipynb) | 2019 | <a href="ipynb/Coin%20Flip.ipynb" title="How to beat the Devil at his own game">The Devil and the Coin Flip Game</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Dice%20Baseball.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FDice%20Baseball.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FDice%20Baseball.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Dice%20Baseball.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Dice%20Baseball.ipynb) | 2020 | <a href="ipynb/Dice%20Baseball.ipynb" title="Simulating baseball games">Dice Baseball</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Economics.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FEconomics.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FEconomics.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Economics.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Economics.ipynb) | 2018 | <a href="ipynb/Economics.ipynb" title="A simulation of a simple economic game">Economics Simulation</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Overtime.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FOvertime.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FOvertime.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Overtime.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Overtime.ipynb) | 2024 | <a href="ipynb/Overtime.ipynb" title="In American Football, which team has the advantage in overtime?">Overtime in American Football</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/poker.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2Fpoker.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2Fpoker.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/poker.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/poker.ipynb) | 2012 | <a href="ipynb/poker.ipynb" title="How do we decide which poker hand wins? Several variants of poker are considered">Poker Hand Ranking</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/risk.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2Frisk.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2Frisk.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/risk.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/risk.ipynb) | 2020 | <a href="ipynb/risk.ipynb" title="Determining who is likely to win an interminably long game of Risk">The Unfinished Game .... of Risk</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/WWW.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FWWW.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FWWW.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/WWW.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/WWW.ipynb) | 2019 | <a href="ipynb/WWW.ipynb" title="Computing the probability of winning the NBA title, for my home town Warriors, or any other team">WWW: Who Will Win (NBA Title)?</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Goldberg.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Goldberg.ipynb) | 2019 | <a href="ipynb/Goldberg.ipynb" title="A re-implementation in Python 3 of Yoav Goldberg's unreasonably effective character-level n-gram language model.">Effectiveness of Language Models</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Probability.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Probability.ipynb) | 2018 | <a href="ipynb/Probability.ipynb" title="Code and examples of the basic principles of Probability Theory">A Concrete Introduction to Probability</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/ProbabilityParadox.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/ProbabilityParadox.ipynb) | 2016 | <a href="ipynb/ProbabilityParadox.ipynb" title="Some classic paradoxes in Probability Theory, and how to think about disagreements">Probability, Paradox, and the Reasonable Person Principle</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/ProbabilitySimulation.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/ProbabilitySimulation.ipynb) | 2020 | <a href="ipynb/ProbabilitySimulation.ipynb" title="When the sample space is too complex, simulations can estimate probabilities">Estimating Probabilities with Simulations</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Diamonds.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Diamonds.ipynb) | 2023 | <a href="ipynb/Diamonds.ipynb" title="Finding an optimal strategy for buying bags with unknown numbers of diamonds.">The Diamond Game: A Probability Puzzle</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Coin%20Flip.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Coin%20Flip.ipynb) | 2019 | <a href="ipynb/Coin%20Flip.ipynb" title="How to beat the Devil at his own game">The Devil and the Coin Flip Game</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Dice%20Baseball.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Dice%20Baseball.ipynb) | 2020 | <a href="ipynb/Dice%20Baseball.ipynb" title="Simulating baseball games">Dice Baseball</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Economics.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Economics.ipynb) | 2018 | <a href="ipynb/Economics.ipynb" title="A simulation of a simple economic game">Economics Simulation</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Overtime.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Overtime.ipynb) | 2024 | <a href="ipynb/Overtime.ipynb" title="In American Football, which team has the advantage in overtime?">Overtime in American Football</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/poker.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/poker.ipynb) | 2012 | <a href="ipynb/poker.ipynb" title="How do we decide which poker hand wins? Several variants of poker are considered">Poker Hand Ranking</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/risk.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/risk.ipynb) | 2020 | <a href="ipynb/risk.ipynb" title="Determining who is likely to win an interminably long game of Risk">The Unfinished Game .... of Risk</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/WWW.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/WWW.ipynb) | 2019 | <a href="ipynb/WWW.ipynb" title="Computing the probability of winning the NBA title, for my home town Warriors, or any other team">WWW: Who Will Win (NBA Title)?</a> |
|Run|Year|Logic and Number/Counting Puzzles|
|Open|Year|Logic and Number/Counting Puzzles|
|---|---|---|
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Paint.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FPaint.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FPaint.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Paint.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Paint.ipynb) | 2024 | <a href="ipynb/Paint.ipynb" title="What is the average cluster size of a random grid of colored squares?">Counting Cluster Sizes in Paint by Numbers</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Cryptarithmetic.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FCryptarithmetic.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FCryptarithmetic.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Cryptarithmetic.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Cryptarithmetic.ipynb) | 2014 | <a href="ipynb/Cryptarithmetic.ipynb" title="Substitute digits for letters and make NUM + BER = PLAY">Cryptarithmetic</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler's%20Conjecture.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FEuler's%20Conjecture.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FEuler's%20Conjecture.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler's%20Conjecture.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Euler's%20Conjecture.ipynb) | 2018 | <a href="ipynb/Euler's%20Conjecture.ipynb" title="Solving a 200-year-old puzzle by finding integers that satisfy a<sup>5</sup> + b<sup>5</sup> + c<sup>5</sup> + d<sup>5</sup> = e<sup>5</sup>">Euler's Sum of Powers Conjecture</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Countdown.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FCountdown.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FCountdown.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Countdown.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Countdown.ipynb) | 2020 | <a href="ipynb/Countdown.ipynb" title="Solving the equation 10 _ 9 _ 8 _ 7 _ 6 _ 5 _ 4 _ 3 _ 2 _ 1 = 2016. Originally from an Alex Bellos puzzle">Four 4s, Five 5s, and Countdowns</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/How%20To%20Count%20Things.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FHow%20To%20Count%20Things.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FHow%20To%20Count%20Things.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/How%20To%20Count%20Things.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/How%20To%20Count%20Things.ipynb) | 2020 | <a href="ipynb/How%20To%20Count%20Things.ipynb" title="Combinatorial math: how to count how many things there are, when there are a lot of them">How to Count Things</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/KenKen.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FKenKen.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FKenKen.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/KenKen.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/KenKen.ipynb) | 2021 | <a href="ipynb/KenKen.ipynb" title="A Sudoku-like puzzle, but with arithmetic.">KenKen (Sudoku-like Puzzle)</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/NumberBracelets.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FNumberBracelets.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FNumberBracelets.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/NumberBracelets.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/NumberBracelets.ipynb) | 2024 | <a href="ipynb/NumberBracelets.ipynb" title="A game involving numbered beads on a circular bracelet.">Number Bracelets Game</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Socks.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FSocks.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FSocks.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Socks.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Socks.ipynb) | 2019 | <a href="ipynb/Socks.ipynb" title="What is the probability that you will be able to pair up socks as you randomly pull them out of the dryer?">Pairing Socks</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Sicherman%20Dice.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FSicherman%20Dice.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FSicherman%20Dice.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Sicherman%20Dice.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Sicherman%20Dice.ipynb) | 2018 | <a href="ipynb/Sicherman%20Dice.ipynb" title="Find a pair of dice that is like a regular pair of dice, only different">Sicherman Dice</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Golomb-Puzzle.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FGolomb-Puzzle.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FGolomb-Puzzle.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Golomb-Puzzle.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Golomb-Puzzle.ipynb) | 2014 | <a href="ipynb/Golomb-Puzzle.ipynb" title="A Puzzle involving placing rectangles of different sizes inside a square">Sol Golomb's Rectangle Puzzle</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Stubborn.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FStubborn.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FStubborn.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Stubborn.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Stubborn.ipynb) | 2024 | <a href="ipynb/Stubborn.ipynb" title="Any number ending in 5 has a square that also ends in 5. What other endings are like this?">Stubborn number endings</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/StarBattle.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FStarBattle.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FStarBattle.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/StarBattle.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/StarBattle.ipynb) | 2021 | <a href="ipynb/StarBattle.ipynb" title="Fill-in-the-grid puzzle similar to Sudoku">Star Battle (Sudoku-like Puzzle)</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Sudoku.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FSudoku.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FSudoku.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Sudoku.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Sudoku.ipynb) | 2006 | <a href="ipynb/Sudoku.ipynb" title="Classic fill-in-the-grid puzzle">Sudoku</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/SudokuJava.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FSudokuJava.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FSudokuJava.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/SudokuJava.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/SudokuJava.ipynb) | 2021 | <a href="ipynb/SudokuJava.ipynb" title="A version of the Sudoku solver using parallel threads and other optimizations">Sudoku: 100,000 puzzles/second in Java</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/SquareSum.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FSquareSum.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FSquareSum.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/SquareSum.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/SquareSum.ipynb) | 2020 | <a href="ipynb/SquareSum.ipynb" title="Place the numbers from 1 to n in a chain (or a circle) such that adjacent pairs sum to a perfect square">Square Sum Puzzle</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Cheryl.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FCheryl.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FCheryl.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Cheryl.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Cheryl.ipynb) | 2020 | <a href="ipynb/Cheryl.ipynb" title="Solving the *Cheryl's Birthday* logic puzzle">When is Cheryl's Birthday?</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Cheryl-and-Eve.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FCheryl-and-Eve.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FCheryl-and-Eve.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Cheryl-and-Eve.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Cheryl-and-Eve.ipynb) | 2015 | <a href="ipynb/Cheryl-and-Eve.ipynb" title="Inventing new puzzles in the Style of Cheryl's Birthday">When Cheryl Met Eve: A Birthday Story</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/CherylMind.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FCherylMind.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FCherylMind.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/CherylMind.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/CherylMind.ipynb) | 2024 | <a href="ipynb/CherylMind.ipynb" title="Do LLMs have enough theory of mind to solve the Cheryl's Birthday puzzle?">LLMs, Theory of Mind, and Cheryl's Birthday</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/xkcd1313.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2Fxkcd1313.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2Fxkcd1313.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/xkcd1313.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/xkcd1313.ipynb) | 2015 | <a href="ipynb/xkcd1313.ipynb" title="Find the smallest regular expression; inspired by Randall Munroe">xkcd 1313: Regex Golf</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/xkcd1313-part2.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2Fxkcd1313-part2.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2Fxkcd1313-part2.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/xkcd1313-part2.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/xkcd1313-part2.ipynb) | 2015 | <a href="ipynb/xkcd1313-part2.ipynb" title="Regex Golf: better, faster, funner (with Stefan Pochmann)">xkcd 1313: Regex Golf (Part 2: Infinite Problems)</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Paint.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Paint.ipynb) | 2024 | <a href="ipynb/Paint.ipynb" title="What is the average cluster size of a random grid of colored squares?">Counting Cluster Sizes in Paint by Numbers</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Cryptarithmetic.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Cryptarithmetic.ipynb) | 2014 | <a href="ipynb/Cryptarithmetic.ipynb" title="Substitute digits for letters and make NUM + BER = PLAY">Cryptarithmetic</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler's%20Conjecture.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler's%20Conjecture.ipynb) | 2018 | <a href="ipynb/Euler's%20Conjecture.ipynb" title="Solving a 200-year-old puzzle by finding integers that satisfy a<sup>5</sup> + b<sup>5</sup> + c<sup>5</sup> + d<sup>5</sup> = e<sup>5</sup>">Euler's Sum of Powers Conjecture</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Countdown.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Countdown.ipynb) | 2020 | <a href="ipynb/Countdown.ipynb" title="Solving the equation 10 _ 9 _ 8 _ 7 _ 6 _ 5 _ 4 _ 3 _ 2 _ 1 = 2016. Originally from an Alex Bellos puzzle">Four 4s, Five 5s, and Countdowns</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/How%20To%20Count%20Things.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/How%20To%20Count%20Things.ipynb) | 2020 | <a href="ipynb/How%20To%20Count%20Things.ipynb" title="Combinatorial math: how to count how many things there are, when there are a lot of them">How to Count Things</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/KenKen.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/KenKen.ipynb) | 2021 | <a href="ipynb/KenKen.ipynb" title="A Sudoku-like puzzle, but with arithmetic.">KenKen (Sudoku-like Puzzle)</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/NumberBracelets.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/NumberBracelets.ipynb) | 2024 | <a href="ipynb/NumberBracelets.ipynb" title="A game involving numbered beads on a circular bracelet.">Number Bracelets Game</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler3.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler3.ipynb) | 2026 | <a href="ipynb/Euler3.ipynb" title="Find the largest prime factor of a number">Project Euler #3: Largest prime factor</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Socks.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Socks.ipynb) | 2019 | <a href="ipynb/Socks.ipynb" title="What is the probability that you will be able to pair up socks as you randomly pull them out of the dryer?">Pairing Socks</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Sicherman%20Dice.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Sicherman%20Dice.ipynb) | 2018 | <a href="ipynb/Sicherman%20Dice.ipynb" title="Find a pair of dice that is like a regular pair of dice, only different">Sicherman Dice</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Golomb-Puzzle.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Golomb-Puzzle.ipynb) | 2014 | <a href="ipynb/Golomb-Puzzle.ipynb" title="A Puzzle involving placing rectangles of different sizes inside a square">Sol Golomb's Rectangle Puzzle</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Stubborn.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Stubborn.ipynb) | 2024 | <a href="ipynb/Stubborn.ipynb" title="Any number ending in 5 has a square that also ends in 5. What other endings are like this?">Stubborn number endings</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/StarBattle.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/StarBattle.ipynb) | 2021 | <a href="ipynb/StarBattle.ipynb" title="Fill-in-the-grid puzzle similar to Sudoku">Star Battle (Sudoku-like Puzzle)</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Sudoku.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Sudoku.ipynb) | 2006 | <a href="ipynb/Sudoku.ipynb" title="Classic fill-in-the-grid puzzle">Sudoku</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/SudokuJava.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/SudokuJava.ipynb) | 2021 | <a href="ipynb/SudokuJava.ipynb" title="A version of the Sudoku solver using parallel threads and other optimizations">Sudoku: 200,000 puzzles/second in Java</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/SquareSum.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/SquareSum.ipynb) | 2020 | <a href="ipynb/SquareSum.ipynb" title="Place the numbers from 1 to n in a chain (or a circle) such that adjacent pairs sum to a perfect square">Square Sum Puzzle</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Cheryl.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Cheryl.ipynb) | 2020 | <a href="ipynb/Cheryl.ipynb" title="Solving the *Cheryl's Birthday* logic puzzle">When is Cheryl's Birthday?</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Cheryl-and-Eve.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Cheryl-and-Eve.ipynb) | 2015 | <a href="ipynb/Cheryl-and-Eve.ipynb" title="Inventing new puzzles in the Style of Cheryl's Birthday">When Cheryl Met Eve: A Birthday Story</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/CherylMind.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/CherylMind.ipynb) | 2024 | <a href="ipynb/CherylMind.ipynb" title="Do LLMs have enough theory of mind to solve the Cheryl's Birthday puzzle?">LLMs, Theory of Mind, and Cheryl's Birthday</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/xkcd1313.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/xkcd1313.ipynb) | 2015 | <a href="ipynb/xkcd1313.ipynb" title="Find the smallest regular expression; inspired by Randall Munroe">xkcd 1313: Regex Golf</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/xkcd1313-part2.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/xkcd1313-part2.ipynb) | 2015 | <a href="ipynb/xkcd1313-part2.ipynb" title="Regex Golf: better, faster, funner (with Stefan Pochmann)">xkcd 1313: Regex Golf (Part 2: Infinite Problems)</a> |
|Run|Year|Word Puzzles|
|Open|Year|Word Puzzles|
|---|---|---|
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Boggle.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FBoggle.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FBoggle.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Boggle.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Boggle.ipynb) | 2020 | <a href="ipynb/Boggle.ipynb" title="Find all the words on a Boggle board; then find a board with a lot of words">Boggle / Inverse Boggle</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/ElementSpelling.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FElementSpelling.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FElementSpelling.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/ElementSpelling.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/ElementSpelling.ipynb) | 2020 | <a href="ipynb/ElementSpelling.ipynb" title="Spelling words using the chemical element symbols, like CoIn">Chemical Element Spelling</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/equilength-numbers.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2Fequilength-numbers.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2Fequilength-numbers.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/equilength-numbers.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/equilength-numbers.ipynb) | 2020 | <a href="ipynb/equilength-numbers.ipynb" title="What number names have the same letter count as the number they name (such as FOUR)?">Equilength Numbers: FOUR = 4</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Gesture%20Typing.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FGesture%20Typing.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FGesture%20Typing.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Gesture%20Typing.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Gesture%20Typing.ipynb) | 2017 | <a href="ipynb/Gesture%20Typing.ipynb" title="What word has the longest path on a gesture-typing smartphone keyboard?">Gesture Typing</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Ghost.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FGhost.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FGhost.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Ghost.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Ghost.ipynb) | 2017 | <a href="ipynb/Ghost.ipynb" title="The word game Ghost (add letters, try to avoid making a word)">Ghost: A Word game</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/How%20to%20Do%20Things%20with%20Words.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FHow%20to%20Do%20Things%20with%20Words.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FHow%20to%20Do%20Things%20with%20Words.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/How%20to%20Do%20Things%20with%20Words.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/How%20to%20Do%20Things%20with%20Words.ipynb) | 2018 | <a href="ipynb/How%20to%20Do%20Things%20with%20Words.ipynb" title="Spelling Correction, Secret Codes, Word Segmentation, and more">How to Do Things with Words: NLP in Python</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Fred%20Buns.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FFred%20Buns.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FFred%20Buns.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Fred%20Buns.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Fred%20Buns.ipynb) | 2015 | <a href="ipynb/Fred%20Buns.ipynb" title="A tale of a bicycle combination lock that uses letters instead of digits. Inspired by Bike Snob NYC">Let's Code About Bike Locks</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/OneLetterOff.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FOneLetterOff.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FOneLetterOff.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/OneLetterOff.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/OneLetterOff.ipynb) | 2023 | <a href="ipynb/OneLetterOff.ipynb" title="Word game; use of a large language model to generate clues.">One Letter Off</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Scrabble.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FScrabble.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FScrabble.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Scrabble.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Scrabble.ipynb) | 2017 | <a href="ipynb/Scrabble.ipynb" title="Refactoring the Scrabble / Word with Friends game from Udacity 212">Scrabble: Refactoring a Crossword Game Program</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/SpellingBee.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FSpellingBee.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FSpellingBee.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/SpellingBee.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/SpellingBee.ipynb) | 2020 | <a href="ipynb/SpellingBee.ipynb" title="Find the highest-scoring board for the NY Times Spelling Bee puzzle">Spelling Bee</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/PropositionalLogic.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FPropositionalLogic.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FPropositionalLogic.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/PropositionalLogic.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/PropositionalLogic.ipynb) | 2017 | <a href="ipynb/PropositionalLogic.ipynb" title="Automatically convert informal English sentences into formal Propositional Logic">Translating English into Propositional Logic</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Jotto.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FJotto.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FJotto.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Jotto.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Jotto.ipynb) | 2020 | <a href="ipynb/Jotto.ipynb" title="The word guessing games Wordle, Evil Wordle, Antiwordle, and Jotto">Wordle, Evil Wordle, Antiwordle, and Jotto</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Wordle.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FWordle.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FWordle.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Wordle.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Wordle.ipynb) | 2022 | <a href="ipynb/Wordle.ipynb" title="A simple human-usable strategy to always win at Wordle. And an analysis of 2-guess wins">Winning Wordle</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/pal3.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2Fpal3.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2Fpal3.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/pal3.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/pal3.ipynb) | 2017 | <a href="ipynb/pal3.ipynb" title="Searching for a long Panama-style palindrome, this time letter-by-letter">World's Longest Palindrome</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Portmantout.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FPortmantout.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FPortmantout.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Portmantout.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Portmantout.ipynb) | 2020 | <a href="ipynb/Portmantout.ipynb" title="Find a word that squishes together a bunch of words">World's Shortest Portmantout Word</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/xkcd-Name-Dominoes.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2Fxkcd-Name-Dominoes.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2Fxkcd-Name-Dominoes.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/xkcd-Name-Dominoes.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/xkcd-Name-Dominoes.ipynb) | 2018 | <a href="ipynb/xkcd-Name-Dominoes.ipynb" title="Lay out dominoes legally; the dominoes have people names, not numbers">xkcd 1970: Name Dominoes</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Boggle.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Boggle.ipynb) | 2020 | <a href="ipynb/Boggle.ipynb" title="Find all the words on a Boggle board; then find a board with a lot of words">Boggle / Inverse Boggle</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/ElementSpelling.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/ElementSpelling.ipynb) | 2020 | <a href="ipynb/ElementSpelling.ipynb" title="Spelling words using the chemical element symbols, like CoIn">Chemical Element Spelling</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/clvr.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/clvr.ipynb) | 2026 | <a href="ipynb/clvr.ipynb" title="Guess famous phrases from letter shapes. Beginner level.">Did you solve it? R y clvr ngh t rd ths sntnc?</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/equilength-numbers.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/equilength-numbers.ipynb) | 2020 | <a href="ipynb/equilength-numbers.ipynb" title="What number names have the same letter count as the number they name (such as FOUR)?">Equilength Numbers: FOUR = 4</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Gesture%20Typing.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Gesture%20Typing.ipynb) | 2017 | <a href="ipynb/Gesture%20Typing.ipynb" title="What word has the longest path on a gesture-typing smartphone keyboard?">Gesture Typing</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Ghost.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Ghost.ipynb) | 2017 | <a href="ipynb/Ghost.ipynb" title="The word game Ghost (add letters, try to avoid making a word)">Ghost: A Word game</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/How%20to%20Do%20Things%20with%20Words.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/How%20to%20Do%20Things%20with%20Words.ipynb) | 2018 | <a href="ipynb/How%20to%20Do%20Things%20with%20Words.ipynb" title="Spelling Correction, Secret Codes, Word Segmentation, and more">How to Do Things with Words: NLP in Python</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Fred%20Buns.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Fred%20Buns.ipynb) | 2015 | <a href="ipynb/Fred%20Buns.ipynb" title="A tale of a bicycle combination lock that uses letters instead of digits. Inspired by Bike Snob NYC">Let's Code About Bike Locks</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/OneLetterOff.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/OneLetterOff.ipynb) | 2023 | <a href="ipynb/OneLetterOff.ipynb" title="Word game; use of a large language model to generate clues.">One Letter Off</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Scrabble.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Scrabble.ipynb) | 2017 | <a href="ipynb/Scrabble.ipynb" title="Refactoring the Scrabble / Word with Friends game from Udacity 212">Scrabble: Refactoring a Crossword Game Program</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/SpellingBee.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/SpellingBee.ipynb) | 2020 | <a href="ipynb/SpellingBee.ipynb" title="Find the highest-scoring board for the NY Times Spelling Bee puzzle">Spelling Bee</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/PropositionalLogic.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/PropositionalLogic.ipynb) | 2017 | <a href="ipynb/PropositionalLogic.ipynb" title="Automatically convert informal English sentences into formal Propositional Logic">Translating English into Propositional Logic</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Jotto.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Jotto.ipynb) | 2020 | <a href="ipynb/Jotto.ipynb" title="The word guessing games Wordle, Evil Wordle, Antiwordle, and Jotto">Wordle, Evil Wordle, Antiwordle, and Jotto</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Wordle.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Wordle.ipynb) | 2022 | <a href="ipynb/Wordle.ipynb" title="A simple human-usable strategy to always win at Wordle. And an analysis of 2-guess wins">Winning Wordle</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/pal3.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/pal3.ipynb) | 2017 | <a href="ipynb/pal3.ipynb" title="Searching for a long Panama-style palindrome, this time letter-by-letter">World's Longest Palindrome</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Portmantout.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Portmantout.ipynb) | 2020 | <a href="ipynb/Portmantout.ipynb" title="Find a word that squishes together a bunch of words">World's Shortest Portmantout Word</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/xkcd-Name-Dominoes.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/xkcd-Name-Dominoes.ipynb) | 2018 | <a href="ipynb/xkcd-Name-Dominoes.ipynb" title="Lay out dominoes legally; the dominoes have people names, not numbers">xkcd 1970: Name Dominoes</a> |
|Run|Year|The Riddler (from 538)|
|Open|Year|The Riddler (from 538)|
|---|---|---|
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Anigrams.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FAnigrams.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FAnigrams.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Anigrams.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Anigrams.ipynb) | 2022 | <a href="ipynb/Anigrams.ipynb" title="Finding the longest chain of anagrams that each add one letter">Anigrams: Word Chains</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Riddler%20Battle%20Royale.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FRiddler%20Battle%20Royale.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FRiddler%20Battle%20Royale.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Riddler%20Battle%20Royale.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Riddler%20Battle%20Royale.ipynb) | 2017 | <a href="ipynb/Riddler%20Battle%20Royale.ipynb" title="A puzzle involving allocating your troops and going up against an opponent">Battle Royale</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/ClimbingWall.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FClimbingWall.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FClimbingWall.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/ClimbingWall.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/ClimbingWall.ipynb) | 2021 | <a href="ipynb/ClimbingWall.ipynb" title="How many holds have to be placed to make a path in each climbing event">Climbing Wall</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/CrossProduct.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FCrossProduct.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FCrossProduct.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/CrossProduct.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/CrossProduct.ipynb) | 2021 | <a href="ipynb/CrossProduct.ipynb" title="A puzzle where digits fill a table, subject to constraints on their products">CrossProduct</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/flipping.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2Fflipping.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2Fflipping.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/flipping.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/flipping.ipynb) | 2020 | <a href="ipynb/flipping.ipynb" title="Can you go through a deck of cards, guessing higher or lower correctly for each card?">Flipping Cards; A Guessing Game</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/RiddlerLottery.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FRiddlerLottery.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FRiddlerLottery.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/RiddlerLottery.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/RiddlerLottery.ipynb) | 2019 | <a href="ipynb/RiddlerLottery.ipynb" title="Can you find what lottery number tickets these five friends picked?">Lottery</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/NightKing.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FNightKing.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FNightKing.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/NightKing.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/NightKing.ipynb) | 2019 | <a href="ipynb/NightKing.ipynb" title="A battle between the army of the dead and the army of the living">How Many Soldiers to Beat the Night King?</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Mean%20Misanthrope%20Density.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FMean%20Misanthrope%20Density.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FMean%20Misanthrope%20Density.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Mean%20Misanthrope%20Density.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Mean%20Misanthrope%20Density.ipynb) | 2017 | <a href="ipynb/Mean%20Misanthrope%20Density.ipynb" title="How crowded will this neighborhood be, if nobody wants to live next door to anyone else?">Misanthropic Neighbors</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Orderable%20Cards.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FOrderable%20Cards.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FOrderable%20Cards.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Orderable%20Cards.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Orderable%20Cards.ipynb) | 2018 | <a href="ipynb/Orderable%20Cards.ipynb" title="Can you get your hand of cards into a nice order with just one move?">Properly Ordered Card Hands</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/RaceTrack.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FRaceTrack.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FRaceTrack.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/RaceTrack.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/RaceTrack.ipynb) | 2021 | <a href="ipynb/RaceTrack.ipynb" title="Race virtual cars around a circular track defined on a grid of points.">Race Track</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/SplitStates.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FSplitStates.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FSplitStates.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/SplitStates.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/SplitStates.ipynb) | 2021 | <a href="ipynb/SplitStates.ipynb" title="Split the US states into two near-halves by area.">Split the States</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/TourDe538.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FTourDe538.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FTourDe538.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/TourDe538.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/TourDe538.ipynb) | 2020 | <a href="ipynb/TourDe538.ipynb" title="Solve a puzzle involving the best pace for a bicycle race.">Tour de 538</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/TwelveBalls.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FTwelveBalls.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FTwelveBalls.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/TwelveBalls.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/TwelveBalls.ipynb) | 2020 | <a href="ipynb/TwelveBalls.ipynb" title="A puzzle where you are given some billiard balls and a balance scale, and asked to find the one ball that is heavier or lighter, in a limited number of weighings">Weighing Twelve Balls</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/war.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2Fwar.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2Fwar.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/war.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/war.ipynb) | 2020 | <a href="ipynb/war.ipynb" title="How likely is it to win a game of war in 26 turns?">War. What is it Good For?</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Anigrams.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Anigrams.ipynb) | 2022 | <a href="ipynb/Anigrams.ipynb" title="Finding the longest chain of anagrams that each add one letter">Anigrams: Word Chains</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Riddler%20Battle%20Royale.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Riddler%20Battle%20Royale.ipynb) | 2017 | <a href="ipynb/Riddler%20Battle%20Royale.ipynb" title="A puzzle involving allocating your troops and going up against an opponent">Battle Royale</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/ClimbingWall.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/ClimbingWall.ipynb) | 2021 | <a href="ipynb/ClimbingWall.ipynb" title="How many holds have to be placed to make a path in each climbing event">Climbing Wall</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/CrossProduct.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/CrossProduct.ipynb) | 2021 | <a href="ipynb/CrossProduct.ipynb" title="A puzzle where digits fill a table, subject to constraints on their products">CrossProduct</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/flipping.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/flipping.ipynb) | 2020 | <a href="ipynb/flipping.ipynb" title="Can you go through a deck of cards, guessing higher or lower correctly for each card?">Flipping Cards; A Guessing Game</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/RiddlerLottery.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/RiddlerLottery.ipynb) | 2019 | <a href="ipynb/RiddlerLottery.ipynb" title="Can you find what lottery number tickets these five friends picked?">Lottery</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/NightKing.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/NightKing.ipynb) | 2019 | <a href="ipynb/NightKing.ipynb" title="A battle between the army of the dead and the army of the living">How Many Soldiers to Beat the Night King?</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Mean%20Misanthrope%20Density.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Mean%20Misanthrope%20Density.ipynb) | 2017 | <a href="ipynb/Mean%20Misanthrope%20Density.ipynb" title="How crowded will this neighborhood be, if nobody wants to live next door to anyone else?">Misanthropic Neighbors</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Orderable%20Cards.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Orderable%20Cards.ipynb) | 2018 | <a href="ipynb/Orderable%20Cards.ipynb" title="Can you get your hand of cards into a nice order with just one move?">Properly Organized Card Hands</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/RaceTrack.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/RaceTrack.ipynb) | 2021 | <a href="ipynb/RaceTrack.ipynb" title="Race virtual cars around a circular track defined on a grid of points.">Race Track</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/SplitStates.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/SplitStates.ipynb) | 2021 | <a href="ipynb/SplitStates.ipynb" title="Split the US states into two near-halves by area.">Split the States</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/TourDe538.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/TourDe538.ipynb) | 2020 | <a href="ipynb/TourDe538.ipynb" title="Solve a puzzle involving the best pace for a bicycle race.">Tour de 538</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/TwelveBalls.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/TwelveBalls.ipynb) | 2020 | <a href="ipynb/TwelveBalls.ipynb" title="A puzzle where you are given some billiard balls and a balance scale, and asked to find the one ball that is heavier or lighter, in a limited number of weighings">Weighing Twelve Balls</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/war.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/war.ipynb) | 2020 | <a href="ipynb/war.ipynb" title="How likely is it to win a game of war in 26 turns?">War. What is it Good For?</a> |
|Run|Year|Computer Science Algorithms and Concepts|
|Open|Year|Computer Science Algorithms and Concepts|
|---|---|---|
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/BASIC.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FBASIC.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FBASIC.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/BASIC.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/BASIC.ipynb) | 2017 | <a href="ipynb/BASIC.ipynb" title="How to write an interpreter for the BASIC programming language">BASIC Interpreter</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Convex%20Hull.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FConvex%20Hull.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FConvex%20Hull.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Convex%20Hull.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Convex%20Hull.ipynb) | 2017 | <a href="ipynb/Convex%20Hull.ipynb" title="A classic Computer Science Algorithm">Convex Hull Problem</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/DocstringFixpoint.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FDocstringFixpoint.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FDocstringFixpoint.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/DocstringFixpoint.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/DocstringFixpoint.ipynb) | 2023 | <a href="ipynb/DocstringFixpoint.ipynb" title="An approach to writing code and docstrings that go together.">Docstring Fixpoint Theory</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/StableMatching.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FStableMatching.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FStableMatching.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/StableMatching.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/StableMatching.ipynb) | 2020 | <a href="ipynb/StableMatching.ipynb" title="What is the best way to pair up two groups with each other, obeying preferences?">Stable Matching Problem</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Differentiation.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FDifferentiation.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FDifferentiation.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Differentiation.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Differentiation.ipynb) | 2017 | <a href="ipynb/Differentiation.ipynb" title="A computer algebra system, including symbolic differentiation">Symbolic Algebra, Simplification, and Differentiation</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Snobol.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FSnobol.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FSnobol.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Snobol.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/Snobol.ipynb) | 2017 | <a href="ipynb/Snobol.ipynb" title="As a student, did you ever get a bad grade on a programming assignment?">Snobol: Bad Grade, Good Experience</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/TSP.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FTSP.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FTSP.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/TSP.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/TSP.ipynb) | 2018 | <a href="ipynb/TSP.ipynb" title="Another of the classics">Traveling Salesperson Problem</a> |
| [C](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/TruncatablePrimes.ipynb) [D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2FTruncatablePrimes.ipynb) [M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2FTruncatablePrimes.ipynb) [N](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/TruncatablePrimes.ipynb) [S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes/blob/main/ipynb/TruncatablePrimes.ipynb) | <u>2026</u> | <a href="ipynb/TruncatablePrimes.ipynb" title="Prime numbers where you can remove digits and still get primes.">Truncatable Primes</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/BASIC.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/BASIC.ipynb) | 2017 | <a href="ipynb/BASIC.ipynb" title="How to write an interpreter for the BASIC programming language">BASIC Interpreter</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Convex%20Hull.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Convex%20Hull.ipynb) | 2017 | <a href="ipynb/Convex%20Hull.ipynb" title="A classic Computer Science Algorithm">Convex Hull Problem</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/lispy.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/lispy.ipynb) | 2026 | <a href="ipynb/lispy.ipynb" title="Tutorial on interpreters.">(How to Write a (Lisp) Interpreter (in Python))</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/DocstringFixpoint.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/DocstringFixpoint.ipynb) | 2023 | <a href="ipynb/DocstringFixpoint.ipynb" title="An approach to writing code and docstrings that go together.">Docstring Fixpoint Theory</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/StableMatching.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/StableMatching.ipynb) | 2020 | <a href="ipynb/StableMatching.ipynb" title="What is the best way to pair up two groups with each other, obeying preferences?">Stable Matching Problem</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Differentiation.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Differentiation.ipynb) | 2017 | <a href="ipynb/Differentiation.ipynb" title="A computer algebra system, including symbolic differentiation">Symbolic Algebra, Simplification, and Differentiation</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Snobol.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Snobol.ipynb) | 2017 | <a href="ipynb/Snobol.ipynb" title="As a student, did you ever get a bad grade on a programming assignment?">Snobol: Bad Grade, Good Experience</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/TSP.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/TSP.ipynb) | 2018 | <a href="ipynb/TSP.ipynb" title="Another of the classics">Traveling Salesperson Problem</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/TruncatablePrimes.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/TruncatablePrimes.ipynb) | 2026 | <a href="ipynb/TruncatablePrimes.ipynb" title="Prime numbers where you can remove digits and still get primes.">Truncatable Primes</a> |
# Index of Python Files

513
ipynb/Euler3.ipynb Normal file
View File

@@ -0,0 +1,513 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "01a279af-6dd7-4385-a355-a1a61d31abfa",
"metadata": {},
"source": [
"<div align=\"right\" style=\"text-align: right\"><i>Peter Norvig<br>April 2026</i></div>\n",
"\n",
"# Project Euler Problem 3\n",
"\n",
"Project Euler's [Problem #3](https://projecteuler.net/problem=3) states:\n",
"\n",
"\n",
" The prime factors of 13195 are 5, 7, 13 and 29.\n",
" What is the largest prime factor of the number 600851475143?\n",
"\n",
"I'll describe here how I think about this problem, at alevel suitable for a novice programmer. There are two things to get right: the general plan of how to attack this problem, and the details of how to say it in Python.\n",
"\n",
"## The General Plan\n",
"\n",
"I would like to be able to find the largest prime factor of *any* integer, not just 600851475143; I want `largest_prime_factor(n)`. Here's how I think about it:\n",
"\n",
"- I don't know how to immediately find the *largest* prime factor of *n*. \n",
"- I do know how to find the *smallest* prime factor: just go through the integers from 2 to *n*, in order. The first integer that evenly divides *n* must be the smallest prime factor. It is a factor because it evenly divides, it is smallest because we didn't find a smaller one first, and it is prime because if it were a composite number (like 4 or 9), then we would have found one of its factors (like 2 or 3) first.\n",
"- Once I have the smallest prime factor ***p*** then I know: **the largest prime factor of *n* is the maximum of *p* and the largest prime factor of *n*/*p*.**\n",
"- If *n* is prime, then the largest prime factor is *n*.\n",
"- If *n* is 1, then [by convention](https://oeis.org/wiki/Greatest_prime_factor_of_n) we say the largest prime factor is 1, even though 1 is usually not considered a prime."
]
},
{
"cell_type": "markdown",
"id": "f722a30c-fbae-4c60-8327-22f188c6c1c4",
"metadata": {},
"source": [
"## How to Say It in Python\n",
"\n",
"There are a few things to remember (or look up):\n",
"- `def` is the keyword for [defining a function](https://k21academy.com/ai-ml/functions/) in Python.\n",
" - `def` is followed by the function name, the function parameter(s), and the function body.\n",
" - It is good practice to include a [documentation string](https://www.educative.io/answers/what-is-a-python-docstring) as the first thing in the function body, for clarification. \n",
"- `range(2, n)` means all the integers starting at 2 and stopping just before *n*. E.g., `range(2, 6)` is [2, 3, 4, 5].\n",
"- `n % p` means \"the remainder of *n* divided by *p*\"; e.g., `13 % 10` is `3`.\n",
"- `n % p == 0` means \"is the remainder of *n* divided by *p* equal to zero?\" or \"does *p* evenly divide *n*?\" or \"is *p* a factor of *n*\"?\n",
"- `n // p` means \"integer division\"; `30 // 2` is the integer 15, while `30 / 2` is the decimal number 15.0. We want integers.\n",
"- `return` means to immediately return a value from a function; don't do any more statements in the function.\n",
"\n",
"\n",
"## The Implementation and the Solution\n",
"\n",
"The **implementation**:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "249c7f13-cdf9-41a2-b0e7-8a3dec15f74b",
"metadata": {},
"outputs": [],
"source": [
"def largest_prime_factor(n):\n",
" \"\"\"The largest prime that evenly divides n.\n",
" Find the smallest prime p that evenly divides n, \n",
" and return the maximum of p and the largest prime factor of n/p.\n",
" If n is prime or if n = 1, this will return n.\"\"\"\n",
" for p in range(2, n):\n",
" if n % p == 0: # n is composite\n",
" return max(p, largest_prime_factor(n // p))\n",
" return n # n is prime or 1"
]
},
{
"cell_type": "markdown",
"id": "1643caa2-97d6-4ff8-8487-3285a5a42182",
"metadata": {},
"source": [
"\n",
"\n",
"The **solution**:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "734de0e3-aafc-438a-8332-09741eef39aa",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"6857"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"largest_prime_factor(600851475143)"
]
},
{
"cell_type": "markdown",
"id": "8bf8dd51-8e06-4572-85fa-fe42875e0305",
"metadata": {},
"source": [
"We can represent the execution of the program as a series of equations, one per line. Using `F` as an abbreviation for `largest_prime_factor`, here is hjow we find the largest prime factor of 360:\n",
"\n",
" F(360) = max(2, F(180))\n",
" F(180) = max(2, F(90))\n",
" F(90) = max(2, F(45))\n",
" F(45) = max(3, F(15))\n",
" F(15) = max(3, F(5))\n",
" F(5) = 5\n",
" F(36) = max(2, max(2, max(2, 3, 3, 5))) = 5"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "a4aaf952-bf6a-4dec-a950-5c573e1fbef0",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"360"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"2 * 2 * 2 * 3 * 3 * 5"
]
},
{
"cell_type": "markdown",
"id": "893a4079-d73d-47c9-970f-f886f4761580",
"metadata": {},
"source": [
"## Some Tests\n",
"\n",
"Are we sure our function is correct? It is good idea to define some **tests** to gain confidence. "
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "9bedb1c9-4138-4250-90e6-77b10ba01586",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[11, 'out of', 11, 'tests pass']"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def tests():\n",
" \"\"\"Test largest_prime_factor.\"\"\"\n",
" cases = {1: 1, # by convention, 1\n",
" 2: 2, # even prime\n",
" 3: 3, # odd prime\n",
" 6: 3, # composite\n",
" 32: 2, # power of 2\n",
" 49: 7, # square of a prime\n",
" 97: 97, # bigger prime\n",
" 99991: 99991, # even bigger prime\n",
" 97**9: 97, # even bigger power of a prime\n",
" 360: 5, # test case for equations above\n",
" 600851475143: 6857 # Project Euler #3\n",
" }\n",
" correct = sum(largest_prime_factor(n) == cases[n] for n in cases)\n",
" return [correct, 'out of', len(cases), 'tests pass']\n",
"\n",
"tests()"
]
},
{
"cell_type": "markdown",
"id": "c51ab097-5ba7-4fdf-9725-944e31aa2336",
"metadata": {},
"source": [
"## Efficiency\n",
"\n",
"How long does it take to get our answer? We can use `%time` to see it is just a few hundred microseconds (μs):"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "0e4a1dc6-5969-49fd-9d65-11d9801cbea2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 181 μs, sys: 0 ns, total: 181 μs\n",
"Wall time: 183 μs\n"
]
},
{
"data": {
"text/plain": [
"6857"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"%time largest_prime_factor(600851475143)"
]
},
{
"cell_type": "markdown",
"id": "c8ead8e9-199f-47c6-ad76-360b1ebdaa02",
"metadata": {},
"source": [
"The algorithm is slowest when *n* is prime, because the `for` loop has to go all the way up to *n*. How long would it take for the largest 8-digit prime, 99,999,989?"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "7323d528-96d7-4c05-a05d-125e99605443",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 1.9 s, sys: 18.4 ms, total: 1.92 s\n",
"Wall time: 1.92 s\n"
]
},
{
"data": {
"text/plain": [
"99999989"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"n8 = 99999989 # An 8-digit number that happens to be prime \n",
"%time largest_prime_factor(n8)"
]
},
{
"cell_type": "markdown",
"id": "9e894dc7-01ca-48fe-8de9-fa8791ff2af8",
"metadata": {},
"source": [
"About 2 seconds. Maybe that's good enough. But could we speed things up?\n",
"\n",
"In trying to find a *p* that evenly divides *n*, the algorithm tests all the integers from 2 to *n*. But do we really have to test all those potential factors? No! Either *n* is prime, or it has two factors *p* and *q* such that *p* × *q* = *n*. Of those two factors, one must be less than or equal to the square root of *n*. So to determine if *n* has a prime factor other than itself, we only have to check integers up to √*n*, not all the way up to *n*. That's a big difference! for an 8-digit prime it is the difference between roughly 100 million steps versus a mere 10 thousand steps.\n",
"\n",
"Let's change the definition of `largest_prime_factor` to incorporate this new trick. (We will `import` the square root function, `sqrt`, from the `math` module.)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "b90b5407-4666-4925-99d2-6a3a6b192fac",
"metadata": {},
"outputs": [],
"source": [
"from math import sqrt\n",
"\n",
"def largest_prime_factor(n):\n",
" \"\"\"The largest prime that evenly divides n.\n",
" Find the smallest prime p that evenly divides n, \n",
" and return the maximum of p and the largest prime factor of n/p.\n",
" If n is prime or if n = 1, this will return n.\"\"\"\n",
" for p in range(2, int(sqrt(n) + 1)): # <<<< only need to go up to √n\n",
" if n % p == 0: # n is composite\n",
" return max(p, largest_prime_factor(n // p))\n",
" return n # n is prime or 1"
]
},
{
"cell_type": "markdown",
"id": "c39b08b9-06ed-49c0-a277-db248de909fd",
"metadata": {},
"source": [
"Any time you change a function, you should re-run the tests to give you some confidence that you didn't introduce a bug:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "4026dc87-a0aa-4c75-b92a-96ec24cda1b7",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[11, 'out of', 11, 'tests pass']"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tests()"
]
},
{
"cell_type": "markdown",
"id": "6cddf393-a0fd-4cc2-9eef-3341339106bd",
"metadata": {},
"source": [
"Now we can see how fast the new function is on the 8-digit prime:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "d3d0c13e-5c01-4112-b372-60f7eb302d25",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 196 μs, sys: 0 ns, total: 196 μs\n",
"Wall time: 196 μs\n"
]
},
{
"data": {
"text/plain": [
"99999989"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"%time largest_prime_factor(n8)"
]
},
{
"cell_type": "markdown",
"id": "82b008ea-f6b5-4bcd-8768-09c8dab089cb",
"metadata": {},
"source": [
"As predicted, this is about 10,000 times faster.\n",
"\n",
"We should be able to handle a 16-digit prime in about 2 seconds:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "9b6030ef-626a-4195-aedb-ef2edac65da4",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 1.96 s, sys: 19.3 ms, total: 1.97 s\n",
"Wall time: 1.97 s\n"
]
},
{
"data": {
"text/plain": [
"9927935178558959"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"n16 = 9927935178558959 # Largest 16-digit prime\n",
"%time largest_prime_factor(n16)"
]
},
{
"cell_type": "markdown",
"id": "d72764be-0bde-4f2a-ad09-9d0b815a3ffe",
"metadata": {},
"source": [
"## Imperative versus Declarative (or Functional) Style\n",
"\n",
"Our definition of largest_prime_factor mixed paradigms, using some [imperative](https://en.wikipedia.org/wiki/Imperative_programming) features (a for loop with a return in the middle) and some [functional](https://en.wikipedia.org/wiki/Functional_programming) features (an implementation of the equation `largest_prime_factor(n) = max(p, largest_prime_factor(n // p))`).\n",
"\n",
"Here's what it might look like if we leaned into the functional style more, making the equation more explicit:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "0308612c-6860-49b0-bcd6-856fa08133b1",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[11, 'out of', 11, 'tests pass']"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def largest_prime_factor(n):\n",
" \"\"\"The largest prime that evenly divides n.\n",
" Find the smallest prime p that evenly divides n, \n",
" and return the maximum of p and the largest prime factor of n/p.\n",
" If n is prime or if n = 1, this will return n.\"\"\"\n",
" p = smallest_prime_factor(n)\n",
" return 1 if n == 1 else max(p, largest_prime_factor(n // p))\n",
"\n",
"def smallest_prime_factor(n):\n",
" \"\"\"The smallest prime that evenly divides n (or n itself if no prime divisors).\"\"\"\n",
" return next((p for p in range(2, int(sqrt(n) + 1)) if n % p == 0), n)\n",
"\n",
"tests()"
]
},
{
"cell_type": "markdown",
"id": "00508ec5-85db-4329-a50d-7c3176bff203",
"metadata": {},
"source": [
"And here's what it looks like in an imperative style, updating `n`, `p`, and `largest` within a `while` loop that contains another `while` loop to take care of the case where *n* has *p* as a repeated factor."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "b8907a8f-872f-4531-825c-fae9c70211c1",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[11, 'out of', 11, 'tests pass']"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def largest_prime_factor(n):\n",
" \"\"\"The largest prime that evenly divides n.\n",
" Find the smallest prime p that evenly divides n, \n",
" and return the maximum of p and the largest prime factor of n/p.\n",
" If n is prime or if n = 1, this will return n.\"\"\"\n",
" largest = 1\n",
" p = 2\n",
" while p * p <= n:\n",
" while n % p == 0:\n",
" largest = p\n",
" n = n // p\n",
" p = p + 1\n",
" return max(n, largest)\n",
" \n",
"tests()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -229,7 +229,7 @@
"|11|2,5 vs 4,11|1,3 vs 6,12|7,9 vs 8,10|\n",
"\n",
"\n",
"||1|2|3|4|5|6|7|8|9|10|11|12|Total|\n",
"| |1|2|3|4|5|6|7|8|9|10|11|12|Total|\n",
"|--|--|--|--|--|--|--|--|--|--|--|--|--|--|\n",
"|**1**|-|3|1|3|1|2|2|1|2|1|3|3|11|\n",
"|**2**|3|-|2|2|3|2|1|2|2|2|2|1|11|\n",
@@ -255,16 +255,16 @@
"|1|1,2 vs 3,4|5,6 vs 11,12|9,10 vs 7,8|\n",
"|2|9,11 vs 4,12|8,10 vs 2,6|1,3 vs 5,7|\n",
"|3|7,12 vs 2,3|5,9 vs 4,6|8,11 vs 1,10|\n",
"|4|2,4 vs 6,11|5,12 vs 7,10|\n",
"|5|4,7 vs 2,9|6,12 vs 3,8|\n",
"|4|2,4 vs 6,11|5,12 vs 7,10||\n",
"|5|4,7 vs 2,9|6,12 vs 3,8||\n",
"|6|1,9 vs 2,10|3,6 vs 7,11|4,5 vs 8,12|\n",
"|7|5,8 vs 2,12|6,7 vs 4,10|1,11 vs 3,9|\n",
"|8|1,12 vs 6,9|3,7 vs 4,8|5,10 vs 2,11|\n",
"|9|2,5 vs 1,6|3,10 vs 4,9|\n",
"|10|3,11 vs 2,8|1,5 vs 7,9|\n",
"|11|3,12 vs 10,11|6,8 vs 1,7|\n",
"|9|2,5 vs 1,6|3,10 vs 4,9||\n",
"|10|3,11 vs 2,8|1,5 vs 7,9||\n",
"|11|3,12 vs 10,11|6,8 vs 1,7||\n",
"|12|3,5 vs 6,10|9,12 vs 2,7|1,8 vs 4,11|\n",
"|13|5,11 vs 8,9|1,4 vs 10,12|\n",
"|13|5,11 vs 8,9|1,4 vs 10,12||\n",
"\n",
"\n",
"||1|2|3|4|5|6|7|8|9|10|11|12|Total|\n",

File diff suppressed because one or more lines are too long

259
ipynb/RationalPi.ipynb Normal file
View File

@@ -0,0 +1,259 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "5b025dd9-9345-402d-9be0-dd0c17842915",
"metadata": {},
"source": [
"<div align=\"right\" style=\"text-align: right\"><i>Peter Norvig<br>May 2026</i></div>\n",
"\n",
"# Fractions approximating π\n",
"\n",
"The number π is irrational, which means that it cannot be represented exactly by a fraction. But some fractions, such as 22/7, are known to come close to π. In this notebook, we look for fractions that approximate π even more closely than 22/7.\n",
"\n",
"If we want to know *what's the best fraction approximation to π with a denominator no more than a given number of digits?* we can try every possible denominator up to the given number of digits, compute the best numerator (and hence the best fraction) for each denominator, and choose the resulting fraction that minimizes the distance to the target, π (or if we want, any target):"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "00c9ebd9-ee55-4dcb-917a-9c7a098d3816",
"metadata": {},
"outputs": [],
"source": [
"from fractions import Fraction\n",
"from math import pi\n",
"\n",
"def approximation(target=pi, digits=6) -> Fraction:\n",
" \"\"\"The fraction best approximating `target` whose denominator has no more than `digits` digits.\"\"\"\n",
" denominators = range(1, 10 ** digits)\n",
" fractions = (Fraction(round(target * d), d) for d in denominators)\n",
" return min(fractions, key=lambda x: abs(target - x))"
]
},
{
"cell_type": "markdown",
"id": "49db7449-1f81-4a7f-842a-84348dc5f16a",
"metadata": {},
"source": [
"For example, here are the best approximations with one- and three-digit denominators:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "de87500e-62cf-48fe-adee-3625f4cf6742",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Fraction(22, 7)"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"approximation(pi, 1)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "3e9e799a-437a-45ff-8182-3d8d3342fe3a",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Fraction(355, 113)"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"approximation(pi, 3)"
]
},
{
"cell_type": "markdown",
"id": "4408e472-e7e6-4cc7-8a6b-790a55860bd3",
"metadata": {},
"source": [
"We can make a pretty report showing the best approximations to π with 1, 2, 3, 4, and 5-digit denominators:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "59faedd9-4899-4ccd-858b-fb5e4032a0cd",
"metadata": {},
"outputs": [],
"source": [
"def report(approximator=approximation, target=pi, d_range=range(1, 6)) -> str:\n",
" \"\"\"For each `d` in `d_range`, print a description of how close \n",
" the fraction `approximator(target, d)` is to `target`.\"\"\"\n",
" for d in d_range:\n",
" r = approximator(target, d)\n",
" print(f'{r:^22} = {r:.25f} (error {r-target:+6.0e})')"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "24447e10-a58f-43f2-93e5-3b058c098096",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 22/7 = 3.1428571428571428571428571 (error +1e-03)\n",
" 311/99 = 3.1414141414141414141414141 (error -2e-04)\n",
" 355/113 = 3.1415929203539823008849558 (error +3e-07)\n",
" 355/113 = 3.1415929203539823008849558 (error +3e-07)\n",
" 312689/99532 = 3.1415926536189366233975003 (error +3e-11)\n"
]
}
],
"source": [
"report()"
]
},
{
"cell_type": "markdown",
"id": "5f21ad7f-193b-412e-b175-4cf9975cffd2",
"metadata": {},
"source": [
"(The approximation 355/113 is so good that no 4-digit-denominator is better. You can think of 3550/1130 as the best 4-digit approximation.)"
]
},
{
"cell_type": "markdown",
"id": "9c035f0d-5cb8-4092-8ed1-883f9a9dd30d",
"metadata": {},
"source": [
"# Faster, Better Approximations\n",
"\n",
"A 5-digit denominator is the most that `approximation` can handle in under a second of run time. \n",
"\n",
"We can get better approximations much faster by iteratively improving a guess:\n",
"- The zeroth approximation to π is formed by rounding to the nearest integer, **3**.\n",
"- The first approximation to π is formed by adding 3 to an approximation of the remainder after rounding, 0.14159...\n",
" - Rounding the remainder would give us 0, so that doesn't help.\n",
" - Instead of rounding the remainder, use (1 / approximation(1 / remainder)).\n",
" - 1 / 0.14159... rounds to 7 (with remainder 0.06251...) so the first approximation is **3 + (1/7) = 22/7**.\n",
"- The second approximation to π is formed by adding in an approximation of 0.06251...\n",
" - 1 / 0.06251... rounds to 16, so the second approximation is **3 + (1/(7 + 1/16)) = 355/113**.\n",
" - You can repeat the approximation process to any depth.\n",
" \n",
"We can implement it like this:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "f81761ad-1b21-4dce-9fba-9621447617d1",
"metadata": {},
"outputs": [],
"source": [
"PI = Fraction(\"3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803\")\n",
"\n",
"def fast_approximation(target: Fraction, depth: int) -> Fraction:\n",
" \"\"\"Approximate `target` by taking the whole part plus an approximation to the remainder.\n",
" Repeat `depth` times (or stop when there is no remainder).\n",
" Use (1 / fast_approximation(1 / remainder)), because remainder < 1 and (1 / remainder) > 1.\"\"\"\n",
" whole = round(target)\n",
" remainder = target - whole\n",
" if depth == 0 or remainder == 0:\n",
" return Fraction(whole)\n",
" else:\n",
" return whole + (1 / fast_approximation(1 / remainder, depth - 1))"
]
},
{
"cell_type": "markdown",
"id": "7e3c32d2-66f7-450f-9dc3-e5034dfd09c5",
"metadata": {},
"source": [
"This gives an approximation accurate to 22 digits after 14 iterations, and takes just milliseconds to run:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "ba7e0c79-515e-48c8-9f7b-a4a99734b9be",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 3 = 3.0000000000000000000000000 (error -1e-01)\n",
" 22/7 = 3.1428571428571428571428571 (error +1e-03)\n",
" 355/113 = 3.1415929203539823008849558 (error +3e-07)\n",
" 104348/33215 = 3.1415926539214210447087159 (error +3e-10)\n",
" 312689/99532 = 3.1415926536189366233975003 (error +3e-11)\n",
" 1146408/364913 = 3.1415926535914039784825424 (error +2e-12)\n",
" 5419351/1725033 = 3.1415926535898153832419438 (error +2e-14)\n",
" 80143857/25510582 = 3.1415926535897926593756269 (error -6e-16)\n",
" 245850922/78256779 = 3.1415926535897931602832772 (error -8e-17)\n",
" 411557987/131002976 = 3.1415926535897932578264482 (error +2e-17)\n",
" 1068966896/340262731 = 3.1415926535897932353925649 (error -3e-18)\n",
" 2549491779/811528438 = 3.1415926535897932390140098 (error +6e-19)\n",
"6167950454/1963319607 = 3.1415926535897932383863775 (error -8e-20)\n",
"21053343141/6701487259 = 3.1415926535897932384623817 (error -3e-22)\n"
]
}
],
"source": [
"report(fast_approximation, PI, range(14))"
]
},
{
"cell_type": "markdown",
"id": "c4f1404b-a9b2-4aaa-be83-d34bab26d734",
"metadata": {},
"source": [
"It turns out there is a name for this approach: it is the [continued fraction approximation to π](https://en.wikipedia.org/wiki/Pi#Continued_fractions). \n",
"\n",
"Note: it is more traditional to use `whole = int(target)` rather than `whole = round(target)` to generate the continued fractions. Using `round` gives better fractions in fewer itera, but using `int` has the nice property that all the bits we add are positive."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4ea1db23-b668-4854-86ea-337462a18a8b",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python [conda env:base] *",
"language": "python",
"name": "conda-base-py"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -16,7 +16,7 @@
"\n",
"# The Concordance Assignment\n",
"\n",
"> *Using the [`Snobol`](http://www.snobol4.org/) language, read lines of text from the standard input and print a *concordance*, which is an alphabetized list of words in the text, with the line number(s) where each word appears. Words with different capitalization (like \"A\" and \"a\") should be merged into one entry.*\n",
"> *Using the [`Snobol`](http://www.snobol4.org/) language, read lines of text from the standard input and print a **concordance**, which is an alphabetized list of words in the text, with the line number(s) where each word appears. Words with different capitalization (like \"A\" and \"a\") should be merged into one entry.*\n",
"\n",
"After studying Snobol a bit, I realized that the expected solution was along these lines:\n",
"\n",

View File

@@ -1,18 +1,22 @@
import java.io.*;
import java.lang.Integer.*;
import java.util.*;
import java.util.stream.*;
import java.lang.StringBuilder;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
//////////////////////////////// Solve Sudoku Puzzles ////////////////////////////////
//////////////////////////////// @author Peter Norvig ////////////////////////////////
//////////////////////////////// 2007, 2021 ////////////////////////////////
/** There are two representations of puzzles that we will use:
/** Solve Sudoku Puzzles
** @author Peter Norvig
** Mostly 2007, som2 2021, 2026
**
** There are two representations of puzzles that we will use:
** 1. A gridstring is 81 chars, with characters '0' or '.' for blank and '1' to '9' for digits.
** 2. A puzzle grid is an int[81] with a digit d (1-9) represented by the integer (1 << (d - 1));
** that is, a bit pattern that has a single 1 bit representing the digit.
** A blank is represented by the OR of all the digits 1-9, meaning that any digit is possible.
** A blank is represented by the OR of all the digits 1-9, meaning any digit is possible.
** While solving the puzzle, some of these digits are eliminated, leaving fewer possibilities.
** The puzzle is solved when every square has only a single possibility.
**
@@ -29,60 +33,59 @@ import java.util.concurrent.CountDownLatch;
public class Sudoku {
//////////////////////////////// main; command line options //////////////////////////////
static final String usage = String.join("\n",
"usage: java Sudoku -(no)[fghnprstuv] | -[RT]<number> | <filename> ...",
"E.g., -v turns verify flag on, -nov turns it off. -R and -T require a number. The options:\n",
" -f(ile) Print summary stats for each file (default on)",
" -g(rid) Print each puzzle grid and solution grid (default off)",
" -h(elp) Print this usage message",
" -n(aked) Run naked pairs (default on)",
" -p(uzzle) Print summary stats for each puzzle (default off)",
" -r(everse) Solve the reverse of each puzzle as well as each puzzle itself (default off)",
" -s(earch) Run search (default on, but some puzzles can be solved with CSP methods alone)",
" -t(hread) Print summary stats for each thread (default off)",
" -u(nitTest)Run a suite of unit tests (default off)",
" -v(erify) Verify each solution is valid (default on)",
" -T<number> Concurrently run <number> threads (default 26)",
" -R<number> Repeat each puzzle <number> times (default 1)",
" <filename> Solve all puzzles in filename, which has one puzzle per line");
//////////////////////////////// main; command line options //////////////////////////////
static final String USAGE = String.join("\n",
"usage: java Sudoku -(no)[fghnprstuv] | -[RT]<number> | <filename> ...",
"Options and filenames are processed left-to-right. Use '-no' to turn an option off\n",
"E.g.: -v turns verify flag on, -nov turns it off. -R and -T require a number. The options:\n",
" -g(rid) Print each puzzle grid and solution grid (default off)",
" -h(elp) Print this usage message",
" -n(aked) Run the naked pairs strategy (default on)",
" -p(uzzle) Print summary stats for each puzzle (default off)",
" -r(everse) Solve the reverse of each puzzle as well as each puzzle itself (default off)",
" -s(ummary) Print per-file summary stats (default on)",
" -t(hread) Print summary stats for each thread (default off)",
" -u(nitTest) Run a suite of unit tests (default off)",
" -v(erify) Verify each solution is valid (default on)",
" -T<number> Concurrently run <number> threads (default 25)",
" -R<number> Repeat the solving of each puzzle <number> times (default 1)",
" <filename> Solve all puzzles in filename, which has one puzzle per line");
boolean printFileStats = true; // -f
boolean printGrid = false; // -g
boolean runNakedPairs = true; // -n
boolean printPuzzleStats = false; // -p
boolean reversePuzzle = false; // -r
boolean runSearch = true; // -s
boolean printFileStats = true; // -s
boolean printThreadStats = false; // -t
boolean verifySolution = true; // -v
int nThreads = 26; // -T
int nThreads = 25; // -T
int repeat = 1; // -R
int backtracks = 0; // count total backtracks
private final AtomicInteger backtracks = new AtomicInteger(0);
private volatile boolean headerPrinted = false;
/** Parse command line args and solve puzzles in files. **/
public static void main(String[] args) throws IOException {
Sudoku s = new Sudoku();
for (String arg: args) {
for (String arg : args) {
if (!arg.startsWith("-")) {
s.solveFile(arg);
} else {
boolean value = !arg.startsWith("-no");
switch(arg.charAt(value ? 1 : 3)) {
case 'f': s.printFileStats = value; break;
case 'g': s.printGrid = value; break;
case 'h': System.out.println(usage); break;
case 'n': s.runNakedPairs = value; break;
case 'p': s.printPuzzleStats = value; break;
case 'r': s.reversePuzzle = value; break;
case 's': s.runSearch = value; break;
case 't': s.printThreadStats = value; break;
case 'u': s.runUnitTests(); break;
case 'v': s.verifySolution = value; break;
case 'T': s.nThreads = Integer.parseInt(arg.substring(2)); break;
case 'R': s.repeat = Integer.parseInt(arg.substring(2)); break;
default: System.out.println("Unrecognized option: " + arg + "\n" + usage);
switch (arg.charAt(value ? 1 : 3)) {
case 'g' -> s.printGrid = value;
case 'h' -> System.out.println(USAGE);
case 'n' -> s.runNakedPairs = value;
case 'p' -> s.printPuzzleStats = value;
case 'r' -> s.reversePuzzle = value;
case 's' -> s.printFileStats = value;
case 't' -> s.printThreadStats = value;
case 'u' -> s.runUnitTests();
case 'v' -> s.verifySolution = value;
case 'T' -> s.nThreads = Integer.parseInt(arg.substring(2));
case 'R' -> s.repeat = Integer.parseInt(arg.substring(2));
default -> System.out.println("Unrecognized option: " + arg + "\n" + USAGE);
}
}
}
@@ -91,30 +94,31 @@ public class Sudoku {
//////////////////////////////// Handling Lists of Puzzles ////////////////////////////////
/** Solve all the puzzles in a file. Report timing statistics. **/
/** Solve all the puzzles in a file. Report timing statistics. **/
void solveFile(String filename) throws IOException {
List<int[]> grids = readFile(filename);
List<int[]> grids = readPuzzlesFromFile(filename);
long startFileTime = System.nanoTime();
switch(nThreads) {
case 1: solveList(grids); break;
default: solveListThreaded(grids, nThreads); break;
if (nThreads == 1) {
solveList(grids);
} else {
solveListThreaded(grids, nThreads);
}
if (printFileStats) printStats(grids.size() * repeat, startFileTime, filename);
}
/** Solve a list of puzzles in a single thread.
/** Solve a list of puzzles in a single thread.
** repeat -R<number> times; print each puzzle's stats if -p; print grid if -g; verify if -v. **/
void solveList(List<int[]> grids) {
int[] puzzle = new int[N * N]; // Used to save a copy of the original grid
int[][] gridpool = new int[N * N][N * N]; // Reuse grids during the search
for (int g=0; g<grids.size(); ++g) {
int grid[] = grids.get(g);
int[] puzzle = new int[N * N];
int[][] gridpool = new int[N * N][N * N];
for (int g = 0; g < grids.size(); ++g) {
int[] grid = grids.get(g);
System.arraycopy(grid, 0, puzzle, 0, grid.length);
for (int i = 0; i < repeat; ++i) {
long startTime = printPuzzleStats ? System.nanoTime() : 0;
int[] solution = initialize(grid); // All the real work is
if (runSearch) solution = search(solution, gridpool, 0); // on these 2 lines.
int[] solution = initialize(grid);
solution = search(solution, gridpool, 0);
if (printPuzzleStats) {
printStats(1, startTime, "Puzzle " + (g + 1));
}
@@ -129,26 +133,25 @@ public class Sudoku {
/** Break a list of puzzles into nThreads sublists and solve each sublist in a separate thread. **/
void solveListThreaded(List<int[]> grids, int nThreads) {
try {
final long startTime = System.nanoTime();
int nGrids = grids.size();
final long startTime = System.nanoTime();
int nGrids = grids.size();
final CountDownLatch latch = new CountDownLatch(nThreads);
int size = nGrids / nThreads;
for (int c = 0; c < nThreads; ++c) {
int end = c == nThreads - 1 ? nGrids : (c + 1) * size;
int end = (c == nThreads - 1) ? nGrids : (c + 1) * size;
final List<int[]> sublist = grids.subList(c * size, end);
new Thread() {
public void run() {
solveList(sublist);
latch.countDown();
if (printThreadStats) {
printStats(repeat * sublist.size(), startTime, "Thread");
}
new Thread(() -> {
solveList(sublist);
latch.countDown();
if (printThreadStats) {
printStats(repeat * sublist.size(), startTime, "Thread");
}
}.start();
}).start();
}
latch.await(); // Wait for all threads to finish
latch.await();
} catch (InterruptedException e) {
System.err.println("And you may ask yourself, 'Well, how did I get here?'");
Thread.currentThread().interrupt();
System.err.println("Solver thread was interrupted.");
}
}
@@ -159,56 +162,60 @@ public class Sudoku {
int[] cross(int[] rows, int[] cols) {
int[] result = new int[rows.length * cols.length];
int i = 0;
for (int r: rows) { for (int c: cols) { result[i++] = N * r + c; } }
for (int r : rows) {
for (int c : cols) {
result[i++] = N * r + c;
}
}
return result;
}
/** Return true iff item is an element of array, or of array[0:end]. **/
/** Return true iff item is an element of array. **/
boolean member(int item, int[] array) { return member(item, array, array.length); }
/** Return true iff item appears within array[0..end). **/
boolean member(int item, int[] array, int end) {
for (int i = 0; i<end; ++i) {
if (array[i] == item) { return true; }
for (int i = 0; i < end; ++i) {
if (array[i] == item) return true;
}
return false;
}
//////////////////////////////// Constants ////////////////////////////////
final int N = 9; // Number of cells on a side of grid.
final int[] DIGITS = {1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 1<<8};
final int ALL_DIGITS = Integer.parseInt("111111111", 2);
final int[] ROWS = IntStream.range(0, N).toArray();
final int[] COLS = ROWS;
final int[] SQUARES = IntStream.range(0, N * N).toArray();
final int[][] BLOCKS = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
final int[][] ALL_UNITS = new int[3 * N][];
final int[][][] UNITS = new int[N * N][3][N];
final int[][] PEERS = new int[N * N][20];
final int[] NUM_DIGITS = new int[ALL_DIGITS + 1];
final int[] HIGHEST_DIGIT = new int[ALL_DIGITS + 1];
final int N = 9;
final int[] DIGITS = {1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 1<<8};
final int ALL_DIGITS = Integer.parseInt("111111111", 2);
final int[] ROWS = IntStream.range(0, N).toArray();
final int[] COLS = ROWS;
final int[] SQUARES = IntStream.range(0, N * N).toArray();
final int[][] BLOCKS = {{0,1,2},{3,4,5},{6,7,8}};
final int[][] ALL_UNITS = new int[3 * N][];
final int[][][] UNITS = new int[N * N][3][N];
final int[][] PEERS = new int[N * N][20];
final int[] NUM_DIGITS = new int[ALL_DIGITS + 1];
final int[] HIGHEST_DIGIT = new int[ALL_DIGITS + 1];
{
// Initialize ALL_UNITS to be an array of the 27 units: rows, columns, and blocks
int i = 0;
for (int r: ROWS) {ALL_UNITS[i++] = cross(new int[] {r}, COLS); }
for (int c: COLS) {ALL_UNITS[i++] = cross(ROWS, new int[] {c}); }
for (int[] rb: BLOCKS) {for (int[] cb: BLOCKS) {ALL_UNITS[i++] = cross(rb, cb); } }
for (int r : ROWS) { ALL_UNITS[i++] = cross(new int[]{r}, COLS); }
for (int c : COLS) { ALL_UNITS[i++] = cross(ROWS, new int[]{c}); }
for (int[] rb : BLOCKS) {
for (int[] cb : BLOCKS) { ALL_UNITS[i++] = cross(rb, cb); }
}
// Initialize each UNITS[s] to be an array of the 3 units for square s.
for (int s: SQUARES) {
for (int s : SQUARES) {
i = 0;
for (int[] u: ALL_UNITS) {
for (int[] u : ALL_UNITS) {
if (member(s, u)) UNITS[s][i++] = u;
}
}
// Initialize each PEERS[s] to be an array of the 20 squares that are peers of square s.
for (int s: SQUARES) {
for (int s : SQUARES) {
i = 0;
for (int[] u: UNITS[s]) {
for (int s2: u) {
for (int[] u : UNITS[s]) {
for (int s2 : u) {
if (s2 != s && !member(s2, PEERS[s], i)) {
PEERS[s][i++] = s2;
}
@@ -216,10 +223,8 @@ public class Sudoku {
}
}
// Initialize NUM_DIGITS[val] to be the number of 1 bits in the bitset val
// and HIGHEST_DIGIT[val] to the highest bit set in the bitset val
for (int val = 0; val <= ALL_DIGITS; val++) {
NUM_DIGITS[val] = Integer.bitCount(val);
NUM_DIGITS[val] = Integer.bitCount(val);
HIGHEST_DIGIT[val] = Integer.highestOneBit(val);
}
}
@@ -230,61 +235,48 @@ public class Sudoku {
/** Search for a solution to grid. If there is an unfilled square, select one
** and try--that is, search recursively--every possible digit for the square. **/
int[] search(int[] grid, int[][] gridpool, int level) {
if (grid == null) {
return null;
}
if (grid == null) return null;
int s = select_square(grid);
if (s == -1) {
return grid; // No squares to select means we are done!
}
for (int d: DIGITS) {
// For each possible digit d that could fill square s, try it
if (s == -1) return grid; // All squares filled — puzzle is solved.
for (int d : DIGITS) {
if ((d & grid[s]) > 0) {
// Copy grid's contents into gridpool[level], and use that at the next level
System.arraycopy(grid, 0, gridpool[level], 0, grid.length);
int[] result = search(fill(gridpool[level], s, d), gridpool, level + 1);
if (result != null) {
return result;
}
backtracks += 1;
if (result != null) return result;
backtracks.incrementAndGet(); // thread-safe
}
}
return null;
}
/** Verify that grid is a solution to the puzzle. **/
/** Verify that grid is a valid solution to puzzle. **/
boolean verify(int[] grid, int[] puzzle) {
if (grid == null) { return false; }
// Check that all squares have a single digit, and
// no filled square in the puzzle was changed in the solution.
for (int s: SQUARES) {
if ((NUM_DIGITS[grid[s]] != 1) || (NUM_DIGITS[puzzle[s]] == 1 && grid[s] != puzzle[s])) {
if (grid == null) return false;
for (int s : SQUARES) {
if (NUM_DIGITS[grid[s]] != 1
|| (NUM_DIGITS[puzzle[s]] == 1 && grid[s] != puzzle[s])) {
return false;
}
}
// Check that each unit is a permutation of digits
for (int[] u: ALL_UNITS) {
int unit_digits = 0; // All the digits in a unit.
for (int s : u) {unit_digits |= grid[s]; }
if (unit_digits != ALL_DIGITS) {
return false;
}
for (int[] u : ALL_UNITS) {
int unitDigits = 0;
for (int s : u) { unitDigits |= grid[s]; }
if (unitDigits != ALL_DIGITS) return false;
}
return true;
}
/** Choose an unfilled square with the minimum number of possible values.
** If all squares are filled, return -1 (which means the puzzle is complete). **/
/** Choose the unfilled square with the fewest possible values.
** Return -1 if all squares are filled (puzzle complete). **/
int select_square(int[] grid) {
int square = -1;
int min = N + 1;
for (int s: SQUARES) {
for (int s : SQUARES) {
int c = NUM_DIGITS[grid[s]];
if (c == 2) {
return s; // Can't get fewer than 2 possible digits
} else if (c > 1 && c < min) {
if (c == 2) return s; // Can't do better than 2
if (c > 1 && c < min) {
square = s;
min = c;
}
@@ -293,24 +285,22 @@ public class Sudoku {
}
/** fill grid[s] = d. If this leads to contradiction, return null. **/
/** Fill grid[s] = d. Return null if this creates a contradiction. **/
int[] fill(int[] grid, int s, int d) {
if ((grid == null) || ((grid[s] & d) == 0)) { return null; } // d not possible for grid[s]
if (grid == null || (grid[s] & d) == 0) return null;
grid[s] = d;
for (int p: PEERS[s]) {
if (!eliminate(grid, p, d)) { // If we can't eliminate d from all peers of s, then fail
return null;
}
for (int p : PEERS[s]) {
if (!eliminate(grid, p, d)) return null;
}
return grid;
}
/** Eliminate digit d as a possibility for grid[s].
** Run the 3 constraint propagation routines.
** If constraint propagation detects a contradiction, return false. **/
/** Eliminate digit d as a possibility for grid[s].
** Run all three constraint-propagation routines.
** Return false if a contradiction is detected. **/
boolean eliminate(int[] grid, int s, int d) {
if ((grid[s] & d) == 0) { return true; } // d already eliminated from grid[s]
if ((grid[s] & d) == 0) return true; // Already eliminated
grid[s] -= d;
return arc_consistent(grid, s) && dual_consistent(grid, s, d) && naked_pairs(grid, s);
}
@@ -318,29 +308,27 @@ public class Sudoku {
//////////////////////////////// Constraint Propagation ////////////////////////////////
/** Check if square s is consistent: that is, it has multiple possible values, or it has
** one possible value which we can consistently fill. **/
/** Check arc consistency: either s has multiple possibilities, or its single
** remaining value can be filled without contradiction. **/
boolean arc_consistent(int[] grid, int s) {
int count = NUM_DIGITS[grid[s]];
return count >= 2 || (count == 1 && (fill(grid, s, grid[s]) != null));
return count >= 2 || (count == 1 && fill(grid, s, grid[s]) != null);
}
/** After we eliminate d from possibilities for grid[s], check each unit of s
** and make sure there is some position in the unit where d can go.
** If there is only one possible place for d, fill it with d. **/
/** After eliminating d from grid[s], ensure d still has at least one valid
** position in each of s's units. If exactly one remains, fill it. **/
boolean dual_consistent(int[] grid, int s, int d) {
for (int[] u: UNITS[s]) {
int dPlaces = 0; // The number of possible places for d within unit u
int dplace = -1; // Try to find a place in the unit where d can go
for (int s2: u) {
if ((grid[s2] & d) > 0) { // s2 is a possible place for d
dPlaces++;
if (dPlaces > 1) break;
dplace = s2;
for (int[] u : UNITS[s]) {
int dPlaces = 0;
int dPlace = -1;
for (int s2 : u) {
if ((grid[s2] & d) > 0) {
if (++dPlaces > 1) break;
dPlace = s2;
}
}
if (dPlaces == 0 || (dPlaces == 1 && (fill(grid, dplace, d) == null))) {
if (dPlaces == 0 || (dPlaces == 1 && fill(grid, dPlace, d) == null)) {
return false;
}
}
@@ -348,23 +336,19 @@ public class Sudoku {
}
/** Look for two squares in a unit with the same two possible values, and no other values.
** For example, if s and s2 both have the possible values 8|9, then we know that 8 and 9
** must go in those two squares. We don't know which is which, but we can eliminate
** 8 and 9 from any other square s3 that is in the unit. **/
/** If two squares in a unit share exactly the same two possible values, eliminate
** those values from every other square in that unit (naked pairs strategy). **/
boolean naked_pairs(int[] grid, int s) {
if (!runNakedPairs) { return true; }
if (!runNakedPairs || NUM_DIGITS[grid[s]] != 2) return true;
int val = grid[s];
if (NUM_DIGITS[val] != 2) { return true; } // Doesn't apply
for (int s2: PEERS[s]) {
for (int s2 : PEERS[s]) {
if (grid[s2] == val) {
// s and s2 are a naked pair; find what unit(s) they share
for (int[] u: UNITS[s]) {
for (int[] u : UNITS[s]) {
if (member(s2, u)) {
for (int s3: u) { // s3 can't have either of the values in val (e.g. 8|9)
int d = HIGHEST_DIGIT[val];
int d2 = val - d;
for (int s3 : u) {
if (s3 != s && s3 != s2) {
int d = HIGHEST_DIGIT[val];
int d2 = val - d;
if (!eliminate(grid, s3, d) || !eliminate(grid, s3, d2)) {
return false;
}
@@ -379,110 +363,114 @@ public class Sudoku {
//////////////////////////////// Input ////////////////////////////////
/** The method `readFile` reads one puzzle per file line and returns a List of puzzle grids. **/
List<int[]> readFile(String filename) throws IOException {
BufferedReader in = new BufferedReader(new FileReader(filename));
List<int[]> grids = new ArrayList<int[]>(1000);
String gridstring;
while ((gridstring = in.readLine()) != null) {
grids.add(parseGrid(gridstring));
if (reversePuzzle) {
grids.add(parseGrid(new StringBuilder(gridstring).reverse().toString()));
/** Read one puzzle per line from filename and return a list of puzzle grids. **/
List<int[]> readPuzzlesFromFile(String filename) throws IOException {
try (BufferedReader in = new BufferedReader(new FileReader(filename))) {
List<int[]> grids = new ArrayList<>(1000);
String gridstring;
while ((gridstring = in.readLine()) != null) {
grids.add(parseGrid(gridstring));
if (reversePuzzle) {
grids.add(parseGrid(new StringBuilder(gridstring).reverse().toString()));
}
}
return grids;
}
return grids;
}
/** Parse a gridstring into a puzzle grid: an int[] with values DIGITS[0-9] or ALL_DIGITS. **/
/** Parse a gridstring into a puzzle grid. **/
int[] parseGrid(String gridstring) {
int[] grid = new int[N * N];
int s = 0;
for (int i = 0; i<gridstring.length(); ++i) {
for (int i = 0; i < gridstring.length(); ++i) {
char c = gridstring.charAt(i);
if ('1' <= c && c <= '9') {
grid[s++] = DIGITS[c - '1']; // A single-bit set to represent a digit
grid[s++] = DIGITS[c - '1'];
} else if (c == '0' || c == '.') {
grid[s++] = ALL_DIGITS; // Any digit is possible
grid[s++] = ALL_DIGITS;
}
}
assert s == N * N;
if (s != N * N) {
throw new IllegalArgumentException(
"Grid string yielded " + s + " squares; expected " + (N * N) + ": \"" + gridstring + "\"");
}
return grid;
}
/** Initialize a grid from a puzzle.
** First initialize every square in the new grid to ALL_DIGITS, meaning any value is possible.
** Then, call `fill` on the puzzle's filled squares to initiate constraint propagation. **/
/** Initialize a fresh grid from puzzle, then fill known squares to trigger constraint propagation. **/
int[] initialize(int[] puzzle) {
int[] grid = new int[N * N]; Arrays.fill(grid, ALL_DIGITS);
for (int s: SQUARES) { if (puzzle[s] != ALL_DIGITS) { fill(grid, s, puzzle[s]); } }
int[] grid = new int[N * N];
Arrays.fill(grid, ALL_DIGITS);
for (int s : SQUARES) {
if (puzzle[s] != ALL_DIGITS) fill(grid, s, puzzle[s]);
}
return grid;
}
//////////////////////////////// Output and Tests ////////////////////////////////
boolean headerPrinted = false;
/** Print stats on puzzles solved, average time, frequency, threads used, and name. **/
/** Print stats: puzzles solved, average µs, KHz, threads, backtracks, and name. **/
void printStats(int nGrids, long startTime, String name) {
double usecs = (System.nanoTime() - startTime) / 1000.;
String line = String.format("%7d %6.1f %7.3f %7d %10.1f %s",
nGrids, usecs / nGrids, 1000 * nGrids / usecs, nThreads, backtracks * 1. / nGrids, name);
synchronized (this) { // So that printing from different threads doesn't get garbled
double usecs = (System.nanoTime() - startTime) / 1_000.0;
int bt = backtracks.getAndSet(0); // thread-safe
String line = String.format("%7d %6.1f %7.3f %7d %10.1f %s",
nGrids, usecs / nGrids, 1_000 * nGrids / usecs, nThreads, bt * 1.0 / nGrids, name);
synchronized (this) {
if (!headerPrinted) {
System.out.println("Puzzles μsec KHz Threads Backtracks Name\n"
+ "======= ====== ======= ======= ========== ====");
headerPrinted = true;
}
System.out.println(line);
backtracks = 0;
}
}
/** Print the original puzzle grid and the solution grid. **/
/** Print the original puzzle grid alongside the solution grid. **/
void printGrids(String name, int[] puzzle, int[] solution) {
String bar = "------+-------+------";
String gap = " "; // Space between the puzzle grid and solution grid
final String BAR = "------+-------+------";
final String GAP = " ";
if (solution == null) solution = new int[N * N];
synchronized (this) { // So that printing from different threads doesn't get garbled
System.out.format("\n%-22s%s%s\n", name + ":", gap,
(verify(solution, puzzle) ? "Solution:" : "FAILED:"));
synchronized (this) {
System.out.format("\n%-22s%s%s\n", name + ":", GAP,
verify(solution, puzzle) ? "Solution:" : "FAILED:");
for (int r = 0; r < N; ++r) {
System.out.println(rowString(puzzle, r) + gap + rowString(solution, r));
if (r == 2 || r == 5) System.out.println(bar + gap + " " + bar);
System.out.println(rowString(puzzle, r) + GAP + rowString(solution, r));
if (r == 2 || r == 5) System.out.println(BAR + GAP + " " + BAR);
}
}
}
/** Return a String representing a row of this puzzle. **/
/** Return a String representing one row of the grid. **/
String rowString(int[] grid, int r) {
String row = "";
StringBuilder row = new StringBuilder(30);
for (int s = r * 9; s < (r + 1) * 9; ++s) {
row += (char) ((NUM_DIGITS[grid[s]] == 9) ? '.' : (NUM_DIGITS[grid[s]] != 1) ? '?' :
('1' + Integer.numberOfTrailingZeros(grid[s])));
row += (s % 9 == 2 || s % 9 == 5 ? " | " : " ");
int nd = NUM_DIGITS[grid[s]];
char cell = nd == 9 ? '.' : nd != 1 ? '?' : (char)('1' + Integer.numberOfTrailingZeros(grid[s]));
row.append(cell);
row.append(s % 9 == 2 || s % 9 == 5 ? " | " : " ");
}
return row;
return row.toString();
}
/** Unit Tests. Just getting started with these. **/
/** Unit Tests. **/
void runUnitTests() {
assert N == 9;
assert SQUARES.length == 81;
for (int s: SQUARES) {
for (int s : SQUARES) {
assert UNITS[s].length == 3;
assert PEERS[s].length == 20;
}
assert Arrays.equals(PEERS[19],
new int[] {18, 20, 21, 22, 23, 24, 25, 26, 1, 10, 28, 37, 46, 55, 64, 73, 0, 2, 9, 11});
assert Arrays.equals(PEERS[19],
new int[]{18,20,21,22,23,24,25,26,1,10,28,37,46,55,64,73,0,2,9,11});
assert Arrays.deepToString(UNITS[19]).equals(
"[[18, 19, 20, 21, 22, 23, 24, 25, 26], [1, 10, 19, 28, 37, 46, 55, 64, 73], [0, 1, 2, 9, 10, 11, 18, 19, 20]]");
"[[18, 19, 20, 21, 22, 23, 24, 25, 26], [1, 10, 19, 28, 37, 46, 55, 64, 73], [0, 1, 2, 9, 10, 11, 18, 19, 20]]");
System.out.println("Unit tests pass.");
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -13,11 +13,11 @@
"\n",
"[![](https://community.wolfram.com//c/portal/getImageAttachment?filename=IMG_20181212_120939.jpg&userId=143131)](https://community.wolfram.com/groups/-/m/t/1569707)\n",
"\n",
"The 24-digit number printed on it is a prime, and as you sharpen the pencil and remove digits one at a time from the left, the resulting numbers are all primes:\n",
"The 24-digit number printed on this pencil is a prime, and as you sharpen the pencil and remove digits one at a time from the left, the resulting numbers are all primes:\n",
"\n",
" 357686312646216567629137 is prime\n",
" 57686312646216567629137 is prime\n",
" 7686312646216567629137 is prime|\n",
" 7686312646216567629137 is prime\n",
" ...\n",
" 137 is prime\n",
" 37 is prime\n",
@@ -1127,7 +1127,7 @@
"\n",
"# Note on Modular Exponentiation\n",
"\n",
"Just one more thing: none of this would work unless we can efficiently compute *a*<sup>(*n* - 1)</sup> (mod *n*). How does the `pow` builtin function do it? When *a* and *n* are 24-digit numbers, if we naively tried to compute `a ** (n - 1)`, we'd have two problems: we'd need nearly a billion petabytes to store the result, and we'd need centuries to compute it. The way around these problems is to use [modular exponentiation](https://en.wikipedia.org/wiki/Modular_exponentiation) where we apply the modulus to each intermediate result, and cut the exponent in half each iteration, so we need only do *O*(log *n*) multiplications, not *O*(*n*). That's a big difference: 10<sup>24</sup> is a trillion trillion, and log<sub>2</sub>(10<sup>24</sup>) is only 80. \n",
"Just one more thing: none of this would work unless we can efficiently compute *a*<sup>(*n* - 1)</sup> (mod *n*). How does the `pow` builtin function do it? When *a* and *n* are 24-digit numbers, if we naively tried to compute `a ** (n - 1)` and then apply (mod *n*), we'd have two problems: we'd need nearly a billion petabytes to store the result, and we'd need centuries to compute it. The way around these problems is to use [modular exponentiation](https://en.wikipedia.org/wiki/Modular_exponentiation) where we apply the modulus to each intermediate result, and cut the exponent in half each iteration, so we need only do *O*(log *n*) multiplications, not *O*(*n*). That's a big difference: 10<sup>24</sup> is a trillion trillion, and log<sub>2</sub>(10<sup>24</sup>) is only 80. \n",
"\n",
"There are two key ides that make this work:\n",
"1) *b*<sup>2*e*</sup> = (*b* × *b*)<sup>*e*</sup>\n",

View File

@@ -294,8 +294,8 @@
"metadata": {},
"outputs": [],
"source": [
"def guess_row(guess) -> Tuple[Word, int, float, int]:\n",
" \"\"\"A tuple of a (guess word, nuber of guaranteed wins, expected wins, maximum bin size).\"\"\"\n",
"def guess_row(guess) -> Tuple[Word, int, float]:\n",
" \"\"\"A tuple of a (guess word, number of guaranteed wins, expected wins).\"\"\"\n",
" B = bins([guess], words)\n",
" return (guess, sum(len(bin) == 1 for bin in B), sum(1 / len(bin) for bin in B))\n",
"\n",

715
ipynb/clvr.ipynb Normal file
View File

@@ -0,0 +1,715 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "9e54c615-5dab-4642-924f-f2840558ff9e",
"metadata": {},
"source": [
"<div align=\"right\" style=\"text-align: right\"><i>Peter Norvig<br>April 2026</i></div>\n",
"\n",
"# Did you solve it? R y clvr ngh t rd ths sntnc?\n",
"\n",
"Alex Bellos's [30 March 2026 column](https://www.theguardian.com/science/2026/mar/30/did-you-solve-it-r-y-clvr-ngh-t-rd-ths-sntnc) asks us to guess famous phrases or sayings, given the shapes of the rectangles that bound the letters, and the clue that vowels are <span style=\"color:green\">**green**</span> and consonants are <span style=\"color:blue\">**blue**</span>. \n",
"\n",
"Here's one of the puzzles:\n",
"\n",
"![](https://i.guim.co.uk/img/media/dd62fe8dfdc6eb9d98815a2e14791ae268aa4d46/0_0_580_75/master/580.jpg?width=310&dpr=2&s=none&crop=none)"
]
},
{
"cell_type": "markdown",
"id": "6a2c1014-5894-4bf0-b4b3-5f1d1561fa2a",
"metadata": {},
"source": [
"I can help solve this problem by using code to constrain what each letter and each word might be.\n",
"\n",
"I'll start by defining different subsets of letters: `v` for the vowels, `c` for the consonants, `a` for the letters whose shape ascends above the norm, etc.:"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "628d38c1-0671-49dd-9897-7bd5eff196ee",
"metadata": {},
"outputs": [],
"source": [
"letters = set('abcdefghijklmnopqrstuvwxyz')\n",
"v = set('aeiou') # vowels (green)\n",
"c = letters - v # consonants (blue)\n",
"\n",
"a = set('bdfhijlt') # ascending\n",
"d = set('gjpqy') # descending\n",
"b = letters - a - d # block: neither ascending nor descending\n",
"\n",
"t = set('li') # thin\n",
"w = set('mw') # wide\n",
"n = letters - t - w # normal: neither wide nor thin"
]
},
{
"cell_type": "markdown",
"id": "f51d68da-7569-4bdd-97a2-3c245e143c70",
"metadata": {},
"source": [
"Now I can say that the first letter of the first word above (the green rectangle) is a block-shaped vowel; the intersection of the **b**lock and **v**owel sets, denoted in Python with the `&` operator:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "1f17f9ce-0e88-464d-80e6-53a458b3594b",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'a', 'e', 'o', 'u'}"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"b&v"
]
},
{
"cell_type": "markdown",
"id": "de4886ea-0244-4582-8a43-9fae9daceb9a",
"metadata": {},
"source": [
"The first word has three letters, `b&v` followed by two **a**scending **t**hin **c**onsonants, `a&t&c`:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "df3a9acf-e4df-42eb-8eb0-61fbdda7b598",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[{'a', 'e', 'o', 'u'}, {'l'}, {'l'}]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"[b&v, a&t&c, a&t&c]"
]
},
{
"cell_type": "markdown",
"id": "b3945fa3-587c-4155-b201-ed7aa55510e5",
"metadata": {},
"source": [
"Neat! There is only one **a**scending **t**hin **c**onsonant, `'l'`.\n",
"\n",
"The whole puzzle is as follows (I made the apostrophe be a word of its own):"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "b4b5bb61-08f6-4815-9f46-ff1dd8e5816c",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[[{'a', 'e', 'o', 'u'}, {'l'}, {'l'}],\n",
" [{'b', 'd', 'f', 'h', 'j', 'l', 't'},\n",
" {'b', 'd', 'f', 'h', 'j', 'l', 't'},\n",
" {'a', 'e', 'o', 'u'}],\n",
" [{'m', 'w'},\n",
" {'a', 'e', 'o', 'u'},\n",
" {'c', 'k', 'm', 'n', 'r', 's', 'v', 'w', 'x', 'z'},\n",
" {'b', 'd', 'f', 'h', 'j', 'l', 't'},\n",
" {'b', 'd', 'f', 'h', 'j', 'l', 't'}],\n",
" [{''}],\n",
" [{'c', 'k', 'm', 'n', 'r', 's', 'v', 'w', 'x', 'z'}],\n",
" [{'a', 'e', 'o', 'u'}],\n",
" [{'c', 'k', 'm', 'n', 'r', 's', 'v', 'w', 'x', 'z'},\n",
" {'b', 'd', 'f', 'h', 'j', 'l', 't'},\n",
" {'a', 'e', 'o', 'u'},\n",
" {'g', 'j', 'p', 'q', 'y'},\n",
" {'a', 'e', 'o', 'u'}]]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"puzzle3 = [[b&v, a&t&c, a&t&c], [a&c, a&c, b&v], [w&c, b&v, b&c, a&c, a&c], [{\"\"}], [b&c], [b&v], [b&c, a&c, b&v, d&c, b&v]]\n",
"puzzle3"
]
},
{
"cell_type": "markdown",
"id": "50586a9e-2a63-4c43-858e-509e783aa99f",
"metadata": {},
"source": [
"## Possible Words\n",
"\n",
"What combinations of letters can each word pattern make? The function `possible_words` goes through the pattern one letter set at a time and builds up all possible ways of adding each possible letter to each possible partial word string:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "664292e4-416c-4408-bc9a-a5df0f2ede08",
"metadata": {},
"outputs": [],
"source": [
"def possible_words(pattern: list[set[str]]) -> set[str]:\n",
" \"\"\"All ways of choosing one letter from each of the possible letters in the word pattern.\"\"\"\n",
" words = {''} # To start there is one possible partial word, with no letters\n",
" for letter_set in pattern:\n",
" # On each turn, add each possible letter to each possible partial word\n",
" words = {word + letter for word in words for letter in letter_set}\n",
" return words"
]
},
{
"cell_type": "markdown",
"id": "c3de036e-7ca3-45f5-99ca-ead670ced8a0",
"metadata": {},
"source": [
"For example,"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "2743ba3a-0474-4f04-91cf-7ea57550b74c",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'all', 'ell', 'oll', 'ull'}"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"possible_words([b&v, a&t&c, a&t&c])"
]
},
{
"cell_type": "markdown",
"id": "d213da56-3d0b-4344-ab09-6a676b11a1f2",
"metadata": {},
"source": [
"Another example:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "0a8cb3da-3d3d-4241-9d22-8b50c16aeb8d",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'ban', 'bat', 'bon', 'bot', 'can', 'cat', 'con', 'cot'}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"possible_words([{'b', 'c'}, {'a', 'o'}, {'n', 't'}])"
]
},
{
"cell_type": "markdown",
"id": "799abea2-83a4-4d71-bd04-270ff056249a",
"metadata": {},
"source": [
"Let's trace through how `possible_words` works on this example. It starts with one possible partial word, the empty string:\n",
"\n",
" words = {''}\n",
"\n",
"Then it enterts the `for` loop and looks at the first letter set, `{'b', 'c'}`, and adds each letter to each partial word (just one of them: the empty string) to get a set of two partial words:\n",
"\n",
" words = {'b', 'c'}\n",
"\n",
"It does the same thing with the second letter set, `{'a', 'o'}`, to get a set of four partial words:\n",
"\n",
" words = {'ba', 'bo', 'ca', 'co'}\n",
"\n",
"Finally, it considers the third letter set, `{'n', 't'}`, and gets the final answer, a set of eight words:\n",
"\n",
" words = {'ban', 'bat', 'bon', 'bot', 'can', 'cat', 'con', 'cot'}\n",
"\n",
"## Dictionary Words\n",
"\n",
"What actual dictionary words could a pattern stand for? To answer that I'll need a list of actual dictionary words. Furthermore, when `possible_words(pattern)` returns more than one word, I'll have to pick one. To facilitate that, I will use a word list that includes the frequency of each word so that I can pick the word with the highest frequency. \n",
"\n",
"I'll download the word list file \"[count_big.txt](count_big.txt)\" which has the format: \n",
"\n",
" a 21160\n",
" aah 1\n",
" aaron 5\n",
" ab 2\n",
" aback 3\n",
" abacus 1\n",
" abandon 32\n",
" abandoned 72\n",
" abandoning 27\n",
" abandonment 15\n",
"\n",
"A few things to note about the following command (but you don't need to memorize):\n",
"- The `!` at the start of a line means to do an operating system command, not a Python command.\n",
"- The `[ -e count_big.txt ] ||` part says to skip downloading the file if it already exists.\n",
"- The `curl` command downloads the file\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "4b0b245b-5085-492e-b3c3-902f2d8ae723",
"metadata": {},
"outputs": [],
"source": [
"! [ -e count_big.txt ] || curl -O https://norvig.com/ngrams/count_big.txt"
]
},
{
"cell_type": "markdown",
"id": "65006749-5475-434c-a6bb-aab8c81e6e0b",
"metadata": {},
"source": [
"I'll read the contents of the file into a Python dictionary, which will have the form `{'a': 21160, 'aah': 1, ...}`. "
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "c863b2e8-4e81-4c13-bd62-2aa7be38ad92",
"metadata": {},
"outputs": [],
"source": [
"def make_dictionary(lines) -> dict:\n",
" \"\"\"The lines are strings with a word and a frequency count; make it into a dict.\"\"\"\n",
" counts = {} # Start with an emoty dict\n",
" for line in lines:\n",
" word, count = line.split()\n",
" counts[word] = int(count)\n",
" return counts\n",
"\n",
"dictionary = make_dictionary(open('count_big.txt'))"
]
},
{
"cell_type": "markdown",
"id": "a5939c3b-b99a-4e8d-9cf5-9f4e8eb1533f",
"metadata": {},
"source": [
"Here are some things you can do with the dictionary:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "9edff66d-8668-4408-b636-a2aec0614fcb",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"'aback' in dictionary"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "cae82016-a5d1-4fac-9e4d-75029e506f67",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"'the' in dictionary"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "8bf5eb96-7502-4568-bba0-71313f481e99",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"'xyzzy!@#$' in dictionary"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "5cbaf8af-a4eb-4390-b0f2-0bb0e32426ca",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"80030"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dictionary['the'] # get the frequency count"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "d1919007-f2e6-484a-a078-c9dd805a17c2",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"80030"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dictionary.get('the', 0) # Get the count, with a default of 0 if word is not in dictionary"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "b137e272-9f34-4ecc-9131-934304a4f5b2",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dictionary.get('xyzzy!@#$', 0) # Get the count, with a default of 0 if word is not in dictionary"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "5e10a770-0945-48c7-ad37-826f2e42a2b1",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"29136"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(dictionary) # the number of words in the dictionary"
]
},
{
"cell_type": "markdown",
"id": "3662284a-b83e-4122-9896-831fc8580a87",
"metadata": {},
"source": [
"Now I want to take a pattern and figure out the most likely word (which I will guess is the most frequent one):"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "fea1a704-ab79-4204-bd89-506e66c12042",
"metadata": {},
"outputs": [],
"source": [
"def most_likely_word(pattern: list[set[str]]) -> str:\n",
" \"\"\"Out of all the possible words the pattern can make, pick the most frequent one.\"\"\"\n",
" return max(possible_words(pattern), key=frequency)\n",
"\n",
"def frequency(word) -> int: \n",
" \"\"\"The frequency count of the word in the dictionary, or 0 by default.\"\"\"\n",
" return dictionary.get(word, 0) "
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "a7cbb472-ce03-4239-b64f-ee1f8cbe02e3",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'all'"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"most_likely_word([b&v, a&t&c, a&t&c])"
]
},
{
"cell_type": "markdown",
"id": "d78f5702-9064-4a50-822e-f6ad89a16497",
"metadata": {},
"source": [
"So far, so good!\n",
"\n",
"A puzzle consists of a list of word patterns, and we can generate a best guess at solving the puzzle by finding the `most_likely_word` for each word pattern, and then joining them into a big string."
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "b6f70f1d-5f16-4d80-bb95-fc8b01ab57df",
"metadata": {},
"outputs": [],
"source": [
"def solve(puzzle: list[list[set[str]]]) -> str:\n",
" \"\"\"Given a puzzle (a list of word patterns), return a string formed from the most likely matching words.\"\"\"\n",
" return ' '.join(most_likely_word(pattern) for pattern in puzzle)"
]
},
{
"cell_type": "markdown",
"id": "316692e0-244f-4715-bec9-f6e7babcd036",
"metadata": {},
"source": [
"## Puzzle #3\n",
"\n",
"We're ready to see if our program can solve the puzzle:\n",
"\n",
"![](https://i.guim.co.uk/img/media/dd62fe8dfdc6eb9d98815a2e14791ae268aa4d46/0_0_580_75/master/580.jpg?width=310&dpr=2&s=none&crop=none)\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "42054f45-a8af-4513-a5d0-f097b2acda02",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'all the world s a stage'"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"solve(puzzle3) "
]
},
{
"cell_type": "markdown",
"id": "8e3b7a2a-259c-463f-b542-5c095c559516",
"metadata": {},
"source": [
"It worked! Let's do another one:\n",
"\n",
"## Puzzle #1\n",
"\n",
"![](https://i.guim.co.uk/img/media/a54abfc70c03cf9bc40e178c4e6186915b97ea1e/0_0_580_75/master/580.jpg?width=310&dpr=2&s=none&crop=none)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "688d1a5c-2770-4dd1-b8db-5d535949f909",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'all s well that ends well'"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"solve([[b&v, a&t&c, a&t&c], [{\"\"}], [b&c], [w&c, b&v, t&c, t&c], [a&c, a&c, b&v, a&c], [b&v, b&c, a&c, b&c], [w&c, b&v, t&c, t&c]])"
]
},
{
"cell_type": "markdown",
"id": "47db30ff-a96a-47d7-a4a0-34fe29dad4db",
"metadata": {},
"source": [
"That's correct!\n",
"\n",
"## Puzzle #8\n",
"\n",
" ![](https://i.guim.co.uk/img/media/3eba3f724bda55abf76c8bca57f645cd5a669aef/0_0_580_75/master/580.jpg?width=310&dpr=2&s=none&crop=none)"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "599ece2f-88a2-4818-83f9-0d0e478986ab",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'all roads lead to some'"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"solve([[v, a&c, a&c], [b&n&c, b&n&v, b&n&v, a&c, b&c], [a&t&c, b&v, b&v, a&c],[a&c, b&v], [c, b&v, w&b&c, b&v]])"
]
},
{
"cell_type": "markdown",
"id": "bfd1dc29-5958-4c51-a124-b39d633ea60f",
"metadata": {},
"source": [
"OK, not quite right, but a good hint.\n",
"\n",
"One more:\n",
"\n",
"## Puzzle #10\n",
"\n",
"\n",
"![](https://i.guim.co.uk/img/media/c358352e9f14f1fb0d0f458f034bb86777efcc83/0_0_464_75/master/464.jpg?width=310&dpr=2&s=none&crop=none)"
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "68d34678-7688-49c4-94b6-5ba207497866",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'have in blind'"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"solve([[a&c, b&v, b&c, b&v], [a&v, b&c], [a&c, a&t&c, a&v, b&c, a&c]])"
]
},
{
"cell_type": "markdown",
"id": "87eb56d5-9a6b-41f0-8eec-b4c8f63dbdbd",
"metadata": {},
"source": [
"That's not right either, but again it is a good clue to the right answer. (One reason I didn't get this one right is that I didn't consider capital letters, and a Capital \"L\" has a different shape than a lowercase \"l\".)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "747c3d08-c98a-497d-9430-f6a69dbd70cc",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python [conda env:base] *",
"language": "python",
"name": "conda-base-py"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

BIN
ipynb/houses4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

938
ipynb/lispy.ipynb Normal file
View File

@@ -0,0 +1,938 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "828d21f7-0a3b-4024-b3d5-306ea56a3214",
"metadata": {},
"source": [
"<div align=\"right\" style=\"text-align: right\"><i>Peter Norvig</i></div>\n",
"\n",
"# (How to Write a (Lisp) Interpreter (in Python))\n",
"\n",
"This notebook describes how (and why) to implement computer language interpreters in general, and in particular an interpreter for most of the [**Scheme**](https://www.scheme.org/) dialect of [**Lisp**](https://en.wikipedia.org/wiki/Lisp_(programming_language%29). I call my language and interpreter **Lispy** because it is Lisp implemented in Python. \n",
"\n",
"Why should interpreters and compilers matter to you? As [Steve Yegge said](https://steve-yegge.blogspot.com/2007/06/rich-programmer-food.html?), \"If you don't know how compilers work, then you don't know how computers work.\" Yegge describes 8 problems that can be solved with compilers (or equally well with interpreters, or with Yegge's typical heavy dosage of cynicism).\n",
"\n",
"## Syntax and Semantics of Programs\n",
"\n",
"The **syntax** of a language is the arrangement of characters to form correct statements or expressions. For example, in the language of mathematical expressions (and in many programming languages and handheld calculators), the syntax for computing one plus two is \"1 + 2\". The **semantics** of a language determines what it means: what computations it describes, and ultimately what answer(s) it computes. We say that \"1 + 2\" *evaluates* to 3, and write that as \"1 + 2\" ⇒ 3. \n",
"\n",
"If you are familiar with languages such as Python or Java, you may find Scheme syntax to be unusual. Consider:\n",
"\n",
"<table style=\"border-collapse: collapse; border: 2px solid black;\">\n",
" <thead>\n",
" <tr>\n",
" <th style=\"border: 1px solid black; padding: 10px; text-align: left;\">Java</th>\n",
" <th style=\"border: 1px solid black; padding: 10px; text-align: left;\">Lisp</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <td style=\"border: 1px solid black; padding: 10px; vertical-align: top;\">\n",
"<pre style=\"margin: 0; font-family: monospace;\"><code>if (x.val() &gt; 0) {\n",
" return fn(A[i] + 3 * i,\n",
" new String[] {\"one\", \"two\"});\n",
"}</code></pre>\n",
" </td>\n",
" <td style=\"border: 1px solid black; padding: 10px; vertical-align: top;\">\n",
"<pre style=\"margin: 0; font-family: monospace;\"><code>(if (&gt; (val x) 0)\n",
" (fn (+ (aref A i) (* 3 i))\n",
" (quote (one two))))</code></pre>\n",
" </td>\n",
" </tr>\n",
" </tbody>\n",
"</table> \n",
"\n",
"Java has a wide variety of syntactic conventions (keywords, infix operators, four kinds of brackets, operator precedence, dot notation, quotes, commas, semicolons), but Scheme syntax is much simpler:\n",
"Scheme programs consist solely of expressions; there is no statement/expression distinction.\n",
"Numbers (e.g. 1) and symbols (e.g. A) are called **atomic expressions**; they cannot be broken into pieces. These are similar to their Java counterparts, except that in Scheme, operators such as `+` and `>` are symbols too, and are treated the same way as `A` and `fn`.\n",
"Everything else is a **list expression**: a \"(\", followed by zero or more expressions, followed by a \")\". The first element of the list expression determines what it means:\n",
"- A list starting with a keyword, e.g. `(if ...)`, is a **special form**; the meaning depends on the keyword.\n",
"- A list starting with a non-keyword, e.g. `(max x y)`, is a function call: the function `max` is applied to the arguments `x` and `y` to compute a value.\n",
"\n",
"The beauty of Scheme is that the full language only needs 5 keywords and 8 syntactic forms. In comparison, Python has 35 keywords and 110 syntactic forms, and Java has 50 keywords and 133 syntactic forms. All those parentheses may seem intimidating, but Scheme syntax has the virtues of simplicity and consistency. (Some have joked that \"Lisp\" stands for \"**L**ots of **I**rritating **S**illy **P**arentheses\"; I think it stand for \"**L**isp **I**s **S**yntactically **P**ure\".)\n",
"\n",
"\n",
"# Language 1: Lispy Calculator\n",
"\n",
"We won't tackle all of Scheme right away; instead we'll start with a subset of Scheme I call **Lispy Calculator**. Lispy Calculator lets you do any computation you could do on a typical calculator—as long as you are comfortable with prefix notation. And while many calculators let you store and retrieve a value from a fixed set of registers (e.g. A, B, or C), Lispy Calculator let's you define and use any number of variables with any names you choose. Here's an example program that computes the area of a circle of radius 10, using the formula π r<sup>2</sup>\n",
"\n",
" (begin\n",
" (define r 10)\n",
" (* pi (* r r)))\n",
"\n",
"\n",
"Here is a table of all the allowable expressions in the Lispy Calculator Language. In the Syntax column of this table, *symbol* must be a symbol, *number* must be an integer or floating point number, and the other italicized words can be any expression. The notation *exp...* means zero or more repetitions of *exp*.\n",
"\n",
"|Expression\t|Syntax\t|Example|Semantics|\n",
"|-----------|-------|-------|-------------|\n",
"|constant \t|*number*\t|`12` or `-3.45e+6`|A number evaluates to itself.|\n",
"|variable |\t*symbol*\t|`r`|A symbol is interpreted as a variable name; its value is the variable's value.|\n",
"|definition\t|`(define` *symbol exp*`}`\t|`(define r 10)`|Define a new variable and give it the value of the expression *exp*.|\n",
"|procedure call\t|`(`*proc exp*...`)`\t|`(sqrt (* 2 8))` ⇒ 4.0|Proc's value (a function) is applied to the argument values.|\n",
"\n",
"\n",
"Let's get some imports out of the way, and be explicit about how Scheme objects are represented in Python:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "e1c3cef0-d091-433e-a2c0-0959df3cee0d",
"metadata": {},
"outputs": [],
"source": [
"from numbers import Number\n",
"import math\n",
"import operator as op\n",
"\n",
"Symbol = str # A Scheme symbol is implemented as a Python str\n",
"Atom = Symbol | Number # A Scheme atom is a Symbol or Number\n",
"List = list # A Scheme list is implemented as a Python list\n",
"Exp = Atom | List # A Scheme expression is an Atom or List\n",
"Env = dict # A Scheme environment is a dictionary mapping of {variable: value}"
]
},
{
"cell_type": "markdown",
"id": "b5c1ff1c-15e1-47d2-bfc8-a008ecc5ff13",
"metadata": {},
"source": [
"## The core of Lisp: eval \n",
"\n",
"Here is the core of the interpreter, `eval`. It takes as input an expression, `exp`, and an **environment** that specifies the values of variables. It returns the value of the expression. These few lines are what what [Alan Kay called](https://queue.acm.org/detail.cfm?id=1039523) \"the Maxwell's Equations of Software.\"\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "3b081873-6ae7-4d73-830d-c441cd196cbc",
"metadata": {},
"outputs": [],
"source": [
"def eval(exp: Exp, env: Env) -> object:\n",
" \"\"\"Evaluate an expression in an environment.\"\"\"\n",
" match exp:\n",
" case Number(): # number evaluates to itself \n",
" return exp\n",
" case Symbol(): # variable evaluates to its value in environment\n",
" return env[exp]\n",
" case ('define', Symbol(name), val): # definition adds name to the environment\n",
" env[name] = eval(val, env)\n",
" return name\n",
" case (proc, *args): # procedure call\n",
" func = eval(proc, env)\n",
" vals = [eval(arg, env) for arg in args]\n",
" return func(*vals)"
]
},
{
"cell_type": "markdown",
"id": "e9ae14da-9668-405a-8c5c-2a7186e98cb4",
"metadata": {},
"source": [
"## Global Environment\n",
"\n",
"We mentioned that an **environment** is a mapping from variable names to their values. We will define a default global environment. `ENV`, containing values for the names of a bunch of standard functions like `sqrt` and `max`, as well as operators like `+` and `>`, which are implemented as procedures in Lisp. (Scheme's name for `print` is `display`; the function `unparse` will be defined later.)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "ce21a511-1089-4d70-9c9a-8825c3d63b17",
"metadata": {},
"outputs": [],
"source": [
"ENV = {\n",
" **vars(math), # sqrt, sin, cos, etc.\n",
" '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, \n",
" '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, \n",
" 'eq?': op.is_, 'equal?': op.eq,\n",
" 'abs': abs,\n",
" 'append': op.add, \n",
" 'apply': lambda proc, args: proc(*args),\n",
" 'begin': lambda *x: x[-1],\n",
" 'cons': lambda x,y: [x] + y,\n",
" 'display': lambda x: print(unparse(x)),\n",
" 'expt': pow,\n",
" 'first': lambda x: x[0],\n",
" 'length': len, \n",
" 'list': lambda *x: List(x), \n",
" 'list?': lambda x: isinstance(x, list), \n",
" 'map': lambda f, *args: list(map(f, *args)),\n",
" 'max': max, \n",
" 'min': min,\n",
" 'not': op.not_,\n",
" 'null?': lambda x: x == [], \n",
" 'number?': lambda x: isinstance(x, Number), \n",
" 'procedure?': callable,\n",
" 'rest': lambda x: x[1:], \n",
" 'round': round,\n",
" 'symbol?': lambda x: isinstance(x, Symbol),\n",
" }"
]
},
{
"cell_type": "markdown",
"id": "edafbbc3-ae0b-4705-859e-3fcf2f10fdad",
"metadata": {},
"source": [
"(*Note:* because I am not implementing all the features of Scheme (such as [continuations](https://groups.csail.mit.edu/mac/projects/info/schemedocs/ref-manual/html/scheme_122.html)), I can get away with defining `begin` as a function rather than a special form.)\n",
"\n",
"## Parsing\n",
"\n",
"How do we get from a sequence of characters to the abstract syntax tree that `eval` expects? The function `parse` does the job, in two steps: \n",
"1. **Lexical analysis**: the function `tokenize` breaks the characters into tokens (such as the keyword `\"if\"` or the number `\"10\"`).\n",
"2. **Syntactic analysis**: the function `parse_tokens` converts the tokens into an expression."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "209b7574-1cad-469e-b735-06f8e18d699d",
"metadata": {},
"outputs": [],
"source": [
"def parse(program: str) -> Exp:\n",
" \"\"\"Read a Scheme expression from a string.\n",
" First split the program into tokens, then read from the token list.\"\"\"\n",
" return parse_tokens(tokenize(program))"
]
},
{
"cell_type": "markdown",
"id": "1b6a1fb3-4765-42c2-9278-a374fa30f5af",
"metadata": {},
"source": [
"There are many tools for lexical analysis (such as Mike Lesk and Eric Schmidt's [lex](https://en.wikipedia.org/wiki/Lex_%28software%29)), most of which define tokens as a class containing a token kind and a token string. But Lisp is so simple that there are really only three types of tokens: left paren, right paren, and everything else. So `str.split` can do the job (with a little help):"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "48875a7a-3c86-4322-9307-3592ab327924",
"metadata": {},
"outputs": [],
"source": [
"def tokenize(chars: str) -> list[str]:\n",
" \"\"\"Convert a string of characters into a list of tokens.\n",
" (Put spaces around parens, then split on spaces.)\"\"\"\n",
" return chars.replace('(', ' ( ').replace(')', ' ) ').split()"
]
},
{
"cell_type": "markdown",
"id": "64d2de32-6e7f-43ba-b6db-eb1eff397ff2",
"metadata": {},
"source": [
"`parse_tokens` looks at the first token; if it is a `)` that's a syntax error. If it is a `(`, then we start building up a list of sub-expressions until we hit a matching `)`. Any non-parenthesis token must be an atom: first try to interpret it as a number, and failing that, it must be a symbol. "
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "c2677c70-f08f-49ff-b167-0cb492ec124f",
"metadata": {},
"outputs": [],
"source": [
"def parse_tokens(tokens: list[str]) -> Exp:\n",
" \"\"\"Read an expression from a list of tokens, mutating the list.\"\"\"\n",
" if not tokens:\n",
" raise SyntaxError('unexpected end of expression')\n",
" match (token := tokens.pop(0)):\n",
" case ')':\n",
" raise SyntaxError('unexpected \")\"')\n",
" case '(':\n",
" result = []\n",
" while tokens[0] != ')':\n",
" result.append(parse_tokens(tokens))\n",
" tokens.pop(0) # pop off the closing ')'\n",
" return result\n",
" case _:\n",
" try:\n",
" n = float(token)\n",
" return int(n) if n.is_integer() else n\n",
" except ValueError:\n",
" return token # symbol"
]
},
{
"cell_type": "markdown",
"id": "a8191faa-a8ad-41dc-a1fa-a3e805b24a28",
"metadata": {},
"source": [
"## Homoiconicity\n",
"\n",
"One of the defining features of Lisp is [**homoiconicity**](https://en.wikipedia.org/wiki/Homoiconicity), a fancy word derived from the Greek for \"same representation\" that refers to a language in which the internal representation of a program is the same as the external representation. In other words, a Lisp program is just a list, so all the functions for reading, manipulating and printing lists apply equally to programs. We've already defined `parse` to convert a string into an internal representation; now we'll define `unparse` to reverse the process:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "62c5a9f4-5969-4646-ac64-fbc34027cadc",
"metadata": {},
"outputs": [],
"source": [
"def unparse(exp: Exp) -> str:\n",
" \"Convert a Python object back into a Scheme-readable string.\"\n",
" match exp:\n",
" case List(): return '(' + ' '.join(map(unparse, exp)) + ')'\n",
" case _: return str(exp)"
]
},
{
"cell_type": "markdown",
"id": "69764e26-0c13-4b05-86aa-cec9605865b6",
"metadata": {},
"source": [
"We can see that parse and unparse are inverses:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "9c5d310b-832e-494e-a141-b8eeaa221206",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'(lambda (A i) (fn (+ (aref A i) (* 3 i))))'"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"unparse(parse(\"(lambda (A i) (fn (+ (aref A i) (* 3 i))))\"))"
]
},
{
"cell_type": "markdown",
"id": "cf6b5b61-f523-486a-b600-7a9aadc4c824",
"metadata": {},
"source": [
"Python has a module, `ast` (for \"abstract syntax tree\") that makes it possible to manipulate programs, but Python is not homoiconic. The internal representation of a program is quite different from the external representation:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "0617fa61-7f37-4623-b431-fa44a3edb5e4",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Module(\n",
" body=[\n",
" Expr(\n",
" value=Lambda(\n",
" args=arguments(\n",
" args=[\n",
" arg(arg='A'),\n",
" arg(arg='i')]),\n",
" body=Call(\n",
" func=Name(id='fn', ctx=Load()),\n",
" args=[\n",
" BinOp(\n",
" left=Subscript(\n",
" value=Name(id='A', ctx=Load()),\n",
" slice=Name(id='i', ctx=Load()),\n",
" ctx=Load()),\n",
" op=Add(),\n",
" right=BinOp(\n",
" left=Constant(value=3),\n",
" op=Mult(),\n",
" right=Name(id='i', ctx=Load())))])))])\n"
]
}
],
"source": [
"import ast\n",
"print(ast.dump(ast.parse(\"lambda A, i: fn(A[i] + 3 * i)\"), indent=4))"
]
},
{
"cell_type": "markdown",
"id": "c568f646-4f7e-4c13-806d-5bfc7fc98933",
"metadata": {},
"source": [
"## Batch Processing\n",
"\n",
"In **batch processing** an entire program is read, parsed, and evaluated in one step, with no human in the loop."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "3a96b511-30da-407b-9137-4b79bc40c367",
"metadata": {},
"outputs": [],
"source": [
"def batch(program: str) -> None:\n",
" \"\"\"Parse the program, evaluate it, and print the result.\"\"\"\n",
" print(unparse(eval(parse(program), ENV)))"
]
},
{
"cell_type": "markdown",
"id": "8aa89ec1-7f62-4797-8e70-da97e74a7b88",
"metadata": {},
"source": [
"Here is a sample program and the result of running it using `batch`:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "69509c3a-2c1e-435e-8563-9a9a82cfc75f",
"metadata": {},
"outputs": [],
"source": [
"program1 = \"\"\"\n",
"(begin \n",
" (define r 10)\n",
" (* pi (* r r))) \"\"\""
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "6447f9b9-a3f8-4465-a735-1be81217b94d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"314.1592653589793\n"
]
}
],
"source": [
"batch(program1)"
]
},
{
"cell_type": "markdown",
"id": "3ec8b0e9-e384-48fa-8539-2794114f2275",
"metadata": {},
"source": [
"We can also see the intermediate steps of tokenizing and parsing the program:"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "75485bb1-9625-470e-bfa2-3e3c39cb43c0",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['(',\n",
" 'begin',\n",
" '(',\n",
" 'define',\n",
" 'r',\n",
" '10',\n",
" ')',\n",
" '(',\n",
" '*',\n",
" 'pi',\n",
" '(',\n",
" '*',\n",
" 'r',\n",
" 'r',\n",
" ')',\n",
" ')',\n",
" ')']"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenize(program1)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "1fba56d9-e97b-4919-9a51-31e94c535361",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['begin', ['define', 'r', 10], ['*', 'pi', ['*', 'r', 'r']]]"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"parse(program1)"
]
},
{
"cell_type": "markdown",
"id": "ffe3866c-76ee-4a6e-b0d2-efb18b544c1e",
"metadata": {},
"source": [
"## Interactive Processing\n",
"\n",
"One of Lisp's great legacies is the notion of an interactive loop: a way for a programmer to enter an expression, see the results, and then think of something new to try. This facilitates exploratory programming: instead of having to design every aspect of a complete program ahead of time, the programmer can experiment, learning as they go, step by step. So let's define the function `repl` (which stands for read-eval-print-loop):"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "037afa47-c35e-416f-affb-aa219cfff935",
"metadata": {},
"outputs": [],
"source": [
"def repl(prompt='\\nlispy> ') -> None:\n",
" \"\"\"A read-eval-print loop.\"\"\"\n",
" print('lispy> read-eval-print loop Type exit to exit')\n",
" while (expr := input(prompt)) != 'exit':\n",
" batch(expr)"
]
},
{
"cell_type": "markdown",
"id": "28cccf9e-0cf3-426b-9f12-61ac165f1e31",
"metadata": {},
"source": [
"Here is an example run of `repl()`:\n",
" \n",
" lispy> read-eval-print loop Type exit to exit\n",
" lispy> (define r 10)\n",
" r\n",
" \n",
" lispy> (* pi (* r r))\n",
" 314.159265359\n",
" \n",
" lispy> (if (> (* 11 11) 120) (* 7 6) oops)\n",
" 42\n",
" \n",
" lispy> (list (+ 1 1) (+ 2 2) (* 2 3) (expt 2 3) (expt 2 (expt 2 2)))\n",
" (2 4 6 8 16\n",
"\n",
" lispy> exit\n",
"\n",
"You can experiment with `repl()` yourself by deleting the `#` and running the following cell. I left it commented out so that when you do the \"Run All Cells\" command on this notebook, it runs all the way through, without pausing to ask you to type in some Scheme expressions."
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "5286fcff-0152-4da7-8b70-d5ba1883ac4d",
"metadata": {},
"outputs": [],
"source": [
"#repl()"
]
},
{
"cell_type": "markdown",
"id": "ed98aea1-201a-4532-9cf4-56aff0ba74e6",
"metadata": {},
"source": [
"# Language 2: Full Lispy\n",
"\n",
"We will now extend our language with four new special forms, and one variant of the old `define` special form. This gived us a much more nearly-complete Scheme subset:\n",
"\n",
"|Expression\t|Syntax| Example|\tSemantics|\n",
"|-----------|------|--------|------------|\n",
"|conditional\t|(`if` *test then_part else_part*`)`|`(if (< x 0) (- x) x)`|\tEvaluate *test*; if true, evaluate and return *then part*; otherwise *else part*.|\n",
"|quotation\t|`(quote` *exp*`)`| `(quote (+ 1 2))` ⇒ `(+ 1 2)`|\tReturn the exp literally; do not evaluate it.|\n",
"|assignment\t|`(set!` *symbol exp*`)`| `(set! r2 (* r r))`|\tEvaluate *exp* and assign that value to *symbol*.|\n",
"|procedure\t|`(lambda (`*symbol...*`)` *exp*`)`|`(lambda (r) (* pi (* r r)))`|\tCreate an anonymous procedure.|\n",
"|definition | `(define (`*symbol*...`)` *body*`)`|`(defun (add1 x) (+ x 1)`| Define a named procedure.|\n",
"\n",
"- The `if` special form is similar to the `(x if test else y)` syntax in Python, although only `False` counts as false in Scheme.\n",
"- The `quote` special form allows you to build a literal data structure.\n",
"- The `set!` special form allows you to update the value of a previously-defined variable. This is different from `define`, which introduces a new variable in the curent environment (and sets its initial value).\n",
"- The `lambda` special form (an obscure name from Alonzo Church's [lambda calculus](https://en.wikipedia.org/wiki/Lambda_calculus)) creates a procedure (without giving it a name).\n",
"- The new option for `define` is just a shortcut for a regular `define` of a `lambda` expression.\n",
"\n",
"There are two equivalent ways of defining a procedure and giving it a name. Consider:\n",
"\n",
" (define (circle-area r) (* pi (* r r)))\n",
"\n",
" (define circle-area (lambda (r) (* pi (* r r)))\n",
"\n",
"Either way, `circle-area` is defined to take as its value a procedure that refers to the global variables `pi` and `*`, and takes a single parameter, `r`. Now we can call the procedure like this:\n",
"\n",
" (circle-area (+ 5 5))\n",
"\n",
"We want this call to return the value of `(* pi (* r r))` with `r` set to 10. But it wouldn't do to set `r` to 10 in the global environment. What if we were using `r` for some other purpose? Instead, we want to arrange for there to be a **local variable** named `r` that is only used during this call to `circle-area`. The process for calling a procedure introduces these new local variable(s), binding each symbol in the parameter list of the function to the corresponding value in the argument list of the function call. In this case, the result of the call is 314.159265359.\n",
" \n",
"\n",
"## Local Variables and Procedures\n",
"\n",
"To handle local variables, we will **nest** envronments. Local variables are defined in an environment that is \"inside\" another environment. We will use the convention: `env['_outer']` to refer to the outer environment of the nested environment `env`. When we evaluate (`circle-area (+ 5 5))`, we will fetch the procedure body, `(* pi (* r r))`, and evaluate it in an environment that has `r` as the sole local variable (with value 10), but also has the global environment as the `_outer` environment; it is there that we will find the values of `*` and `pi`. In the diagram, the inner environment is blue and the outer red:\n",
"\n",
"<p><table style=\"border: 3px solid red\" cellspacing=1 cellpadding=5><tr><td>\n",
"<tt>pi: 3.141592653589793\n",
"<br>*: &lt;built-in function mul&gt;\n",
"<br>...\n",
"<br>\n",
"<table style=\"border: 3px solid blue\" cellspacing=1 cellpadding=5>\n",
"<tr><td>r: 10\n",
"</table>\n",
"</table>\n",
"\n",
"When we look up a variable in such a nested environment, we look first at the innermost level, but if we don't find the variable name there, we move to the next outer level. \n",
"\n",
"Here is the definition of the Procedure class:"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "1d9cc9fb-32df-41dc-9c6c-863c7a5ba5a6",
"metadata": {},
"outputs": [],
"source": [
"from dataclasses import dataclass\n",
"\n",
"@dataclass\n",
"class Procedure(object):\n",
" \"\"\"A user-defined Scheme procedure.\"\"\"\n",
" parms: list[Symbol]\n",
" body: Exp\n",
" env: Env\n",
" def __call__(self, *args) -> object: \n",
" env = Env(zip(self.parms, args), _outer=self.env)\n",
" return eval(self.body, env)\n",
" def __repr__(self) -> str: \n",
" return f'<Procedure{unparse(self.parms)}>'"
]
},
{
"cell_type": "markdown",
"id": "7734c900-7d9c-4770-987a-30830989175d",
"metadata": {},
"source": [
"We see that a procedure has three components: a list of parameter names, a body expression, and an environment that tells us what other variables are accessible from the body. For a procedure defined at the top level this will be the global environment, but it is also possible for a procedure to refer to the local variables of the environment in which it was defined (**not** the environment in which it is called).\n",
"\n",
"The function `find` is used to find the right environment for a variable: starting with the inner one and going out, find the first environment that mentions the variable name."
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "22fd9960-b288-4a01-94fe-faab8e8da8bf",
"metadata": {},
"outputs": [],
"source": [
"def find(var: Symbol, env: Env) -> Env:\n",
" \"\"\"Find the environment that contains the variable `var`.\"\"\"\n",
" return env if (var in env) else find(var, env['_outer'])"
]
},
{
"cell_type": "markdown",
"id": "3d0599c6-3a35-4fd8-b179-c970e8a36c4d",
"metadata": {},
"source": [
"To see how these all go together, here is the new definition of `eval`:"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "afa69b35-bac3-459c-a0cc-b94656ab474c",
"metadata": {},
"outputs": [],
"source": [
"def eval(exp: Exp, env) -> object:\n",
" \"\"\"Evaluate an expression in an environment.\"\"\"\n",
" match exp:\n",
" case Symbol(): # variable reference\n",
" return find(exp, env)[exp]\n",
" case Number(): # constant \n",
" return exp\n",
" case ('if', test, then_, else_): # conditional evaluates one branch or the other\n",
" branch = (else_ if eval(test, env) is False else then_)\n",
" return eval(branch, env)\n",
" case ('define', (name, *parms), body): # procedure definition\n",
" env[name] = eval(['lambda', parms, body], env)\n",
" return name\n",
" case ('define', Symbol(name), val): # regular definition\n",
" env[name] = eval(val, env)\n",
" return name\n",
" case ('quote', constant): # constant expression\n",
" return constant\n",
" case ('set!', symbol, val): # variable assignment\n",
" find(symbol, env)[symbol] = eval(val, env)\n",
" return None\n",
" case ('lambda', parms, body): # create an anonymous procedure\n",
" return Procedure(parms, body, env)\n",
" case (proc, *args): # procedure call\n",
" func = eval(proc, env)\n",
" vals = [eval(arg, env) for arg in args]\n",
" return func(*vals)"
]
},
{
"cell_type": "markdown",
"id": "73750dec-a8e0-41fb-83ae-3c7740103659",
"metadata": {},
"source": [
"For example:"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "fb398ac0-cd7f-45e2-9a8c-1ea15bc7b4e6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"314.1592653589793\n"
]
}
],
"source": [
"batch(\"\"\"\n",
"(begin \n",
" (define (circle-area r) (* pi (* r r)))\n",
" (circle-area (+ 5 5)))\"\"\")"
]
},
{
"cell_type": "markdown",
"id": "1a2ccfcd-326b-44f0-b041-da657dc042a0",
"metadata": {},
"source": [
"We now have a language with variables, conditionals, sequential execution, and procedures with recursive calls. That makes our language Turing-complete. If you are familiar with other languages, you might think that a `while` or `for` loop would be needed, but Scheme manages to do without these just fine. In Scheme you iterate by defining recursive functions. The Scheme report says \"Scheme demonstrates that a very small number of rules for forming expressions, with no restrictions on how they are composed, suffice to form a practical and efficient programming language.\" \n",
"\n",
"## How Small/Complete/Good/Fast is Lispy?\n",
" \n",
"In which we judge Lispy on several criteria:\n",
"- **Small**: Lispy is *very* small: about 120 lines or 4K of source code. (An earlier version was just 90 lines, but was perhaps a bit too terse.) The smallest version of my Scheme in Java, [Jscheme](http://norvig.com/jscheme.html) was 1664 lines and 57K of source. Jscheme was originally called SILK (Scheme in Fifty Kilobytes), but I only kept under that limit by counting bytecode rather than source code. Lispy does much better; I think it meets Alan Kay's 1972 [claim](http://gagne.homedns.org/~tgagne/contrib/EarlyHistoryST.html) that *you could define the \"most powerful language in the world\" in \"a page of code.\"* (if you use a small font). However, I think Alan would disagree, because he would count the Python compiler as part of the code, putting me <i>well</i> over a page.\n",
"- **Complete**: Lispy is not very complete compared to the [Scheme standard](https://standards.scheme.org/). Some major shortcomings:\n",
" - **Syntax**: Missing comments, quote and quasiquote notation, # literals, the derived\n",
" expression types (such as `cond` and `let`), and dotted list notation.\n",
" - **Semantics**: Missing `call/cc` and tail recursion. \n",
" - **Data Types**: Missing strings, characters, booleans, ports,\n",
" vectors, exact/inexact numbers. A Scheme list should actually be a custom data class, not a Python list.\n",
" - **Procedures**: Missing over 100 primitive procedures.\n",
" - **Error recovery**: Lispy does not attempt to detect,\n",
" reasonably report, or recover from errors. Lispy expects the\n",
" programmer to be perfect. \n",
"- **Good**: That's up to the readers to decide. I think that Lispy is good for my purpose of explaining Lisp interpreters. It is not a viable choice for serious software development.\n",
"- **Fast**: Lispy computes <tt>(factorial 100)</tt> in less than a millisecond. That's fast enough for me. <p>"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "5bcc6a7f-261b-4156-92d3-c771ecfcd109",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000\n",
"CPU times: user 395 μs, sys: 200 μs, total: 595 μs\n",
"Wall time: 521 μs\n"
]
}
],
"source": [
"%%time\n",
"batch(\"\"\"\n",
"(begin \n",
" (define (factorial n) \n",
" (if (<= n 1) \n",
" 1 \n",
" (* n (factorial (- n 1)))))\n",
" (factorial 100))\"\"\")"
]
},
{
"cell_type": "markdown",
"id": "802789ab-2f1c-4fb0-a79b-c7621a851e81",
"metadata": {},
"source": [
"## More Example Lisp Programs"
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "f8728be6-2423-487f-a44a-49a50411d363",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(count 3 4)\n"
]
}
],
"source": [
"batch(\"\"\"\n",
"(list \n",
" (define (count item L) \n",
" (if (null? L)\n",
" 0\n",
" (+ (equal? item (first L)) (count item (rest L)))))\n",
" (count 0 (list 0 1 2 3 0 0))\n",
" (count (quote the) (quote (the more the merrier the bigger the better))))\"\"\")"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "af58a444-c684-481c-a74b-1ad6bb49459b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(square compose repeat 10 81 65536 2.0)\n"
]
}
],
"source": [
"batch(\"\"\"\n",
"(list\n",
" (define (square x) (* x x))\n",
" (define (compose f g) (lambda (x) (f (g x))))\n",
" (define (repeat f) (compose f f))\n",
" ((compose round sqrt) 101)\n",
" ((repeat square) 3)\n",
" ((repeat (repeat square)) 2)\n",
" ((repeat (repeat sqrt)) (pow 2 16)))\"\"\")"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "5dba7073-33d3-4f39-a192-5e0ccdc42f8a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765)\n"
]
}
],
"source": [
"batch(\"\"\"\n",
"(begin\n",
" (define (fib n) \n",
" (if (< n 2) \n",
" 1 \n",
" (+ (fib (- n 1)) (fib (- n 2)))))\n",
" (define (range start stop)\n",
" (if (= start stop) \n",
" (quote ()) \n",
" (cons start (range (+ start 1) stop))))\n",
" (map fib (range 0 20)))\"\"\")"
]
},
{
"cell_type": "markdown",
"id": "5f7fcfb8-ef93-4dfb-af8b-43030ab74eaa",
"metadata": {},
"source": [
"## True Story\n",
"\n",
"To back up the idea that it can be helpful to know how\n",
"interpreters work, here's a story. Way back in 1984 I was writing a\n",
"Ph.D. thesis. This was before LaTeX, before Microsoft Word for Windowswe used\n",
"[troff](https://en.wikipedia.org/wiki/Troff). Unfortunately, troff had no facility for forward references\n",
"to symbolic labels: I wanted to be able to write \"As we will see on\n",
"page @theorem-x\" and then write something like \"@(set theorem-x \\n%)\" in\n",
"the appropriate place (the troff register \\n% holds the page number). My\n",
"fellow grad student Tony DeRose felt the same need, and together we\n",
"sketched out a simple Lisp program that would handle this as a preprocessor. However,\n",
"it turned out that the Lisp we had at the time was good at reading\n",
"Lisp expressions, but slow at reading 100 KB of characters one character at a time.\n",
"\n",
"From there Tony and I split paths. He reasoned that the hard part was\n",
"the interpreter for expressions; he needed Lisp for that, but he knew\n",
"how to write a tiny C routine\n",
"for reading the characters one at a time, and how to link it into the Lisp\n",
"program. I didn't know how to do that linking, but I reasoned that writing an\n",
"interpreter for this trivial language (all it had was set variable,\n",
"fetch variable, and string concatenate) was easy, so I wrote an\n",
"interpreter in C. So, ironically, Tony wrote a Lisp program (with one small routine in C) because he was a\n",
"C programmer, and I wrote a C program (that implements a hand-coded mini-interpreter) because I was a Lisp programmer.\n",
"\n",
"In the end, we both got our theses done (<a href=\"http://www.eecs.berkeley.edu/Pubs/TechRpts/1985/6081.html\">Tony</a>, <a href=\"http://www.eecs.berkeley.edu/Pubs/TechRpts/1987/5995.html\">Peter</a>).\n",
"\n",
"<h2>Further Reading</h2>\n",
"\n",
"Years ago, I showed how to write a semi-practical near-complete Scheme interpreter (one in [Java](https://norvig.com/jscheme.html) and one in [Common Lisp](https://github.com/norvig/paip-lisp/blob/main/docs/chapter22.md)). I also have another page describing a <a href=\"http://norvig.com/lispy2.html\">more advanced version of Lispy</a>.\n",
" \n",
"To learn more about Scheme consult some of the fine books (by\n",
" <a\n",
" href=\"http://books.google.com/books?id=xyO-KLexVnMC&lpg=PP1&dq=scheme%20programming%20book&pg=PP1#v=onepage&q&f=false\">Friedman\n",
" and Fellesein</a>,\n",
" <a href=\"http://books.google.com/books?id=wftS4tj4XFMC&lpg=PA300&dq=scheme%20programming%20book&pg=PP1#v=onepage&q&f=false\">Dybvig</a>,\n",
" <a\n",
" href=\"http://books.google.com/books?id=81mFK8pqh5EC&lpg=PP1&dq=scheme%20programming%20book&pg=PP1#v=onepage&q&f=false\">Queinnec</a>,\n",
" <a href=\"http://www.eecs.berkeley.edu/~bh/ss-toc2.html\">Harvey and\n",
" Wright</a> or\n",
" <a href=\"https://www.researchgate.net/profile/Gerald-Sussman-2/publication/37597721_Structure_and_Interpretation_of_Computer_Programs_H_Abelson_GJ_Sussman_colaboracion_de_J_Sussman_prol_de_Alan_J_Perlis/links/53d141450cf220632f392bf7/Structure-and-Interpretation-of-Computer-Programs-H-Abelson-GJ-Sussman-colaboracion-de-J-Sussman-prol-de-Alan-J-Perlis.pdf\">Sussman and Abelson</a>),\n",
" videos (by <a\n",
" href=\"http://groups.csail.mit.edu/mac/classes/6.001/abelson-sussman-lectures/\">Abelson\n",
" and Sussman</a>),\n",
" tutorials (by\n",
" <a\n",
" href=\"http://www.ccs.neu.edu/home/dorai/t-y-scheme/t-y-scheme.html\">Dorai</a>,\n",
" <a href=\"http://docs.racket-lang.org/quick/index.html\">PLT</a>, or\n",
" <a href=\"http://cs.gettysburg.edu/~tneller/cs341/scheme-intro/index.html\">Neller</a>),\n",
" or the\n",
" <a\n",
" href=\"http://www.schemers.org/Documents/Standards/R5RS/HTML\">reference\n",
" manual</a>.\n",
"\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

250000
ipynb/sudokus.txt Normal file

File diff suppressed because it is too large Load Diff

49151
ipynb/sudokus_17.txt Normal file

File diff suppressed because it is too large Load Diff

375
ipynb/sudokus_hard.txt Normal file
View File

@@ -0,0 +1,375 @@
........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3
........2..8.1.9..5....3.4....1.93...6..3..8...37......4......53.1.7.8..2........
..2...7...1.....6.5......18....37.......49.....41.23....3.2.9...8.....5.6.......2
........7..4.2.6..8.....31......29...4..9..3...95.6....1......8..6.5.2..7......6.
..4..3....7..8....2.81....6..3....9..8..2....1..7....3......45....8..9....9..5..8
..6..1....5..3....9..4....7..1....2..3..9....4..5....13.....68....3..2....2..8..3
........3..1..9.6..5..8.4.....9...8...867.....1....2....6..7.2..3.8..5..4.......8
........5..6..87..3......9....1.7.4...7...8...4...6....9..8...3..16..4..5...2....
.....5..3..9....4..81.4.......7.......4..2..68...14.3.......2...4...6..79...5..1.
.....5..4.9.....2...6.7.3.....7..8....86.....13..8......3.1.6...2......54......9.
........6..5..18...9...8.7....8.2.....3.1.2..4..5.3....6.....9...83..1..7.......4
........9..6.1.7.24......3......12...6..2..5...28.7....3......4..8.7.6..9..1.....
..6.....1.8...7.5.9...5.8.......4....4..3..2....2.8.....3.7...9.7.....8.1.....6.3
........2..8..91..5......4....9.7.....7.3.8.....8.1.3..4..6...5..97..3..2........
........7..2..96..8...6...3....92.....46..5...1..54.....5.4.9...3.....7.1.......8
.......24..3...7..41..7...6.....3.....5.9.8.....8.5...2...4...9..7...4..96.....1.
.......58..6...7..84..7...3.....1.....9.2.1.....6.9...5...8...2..7...8..23.....4.
..6.......3...7..11.7.............9.3....6..5.5.8..2....3..5..6...2...4..4..9.8..
........5..8..79...6..1..4....1.2.7.4...7...3.7.6......3..2..6...5...8..9.......7
........6..2....4..1...798....79.....8..5......3..8.1...6....2..9...51..4.......8
....6...9..6..35...4..5..7......2......8.......2.1.8....5...3...9..3..1.71......4
........7..2...6...8..1..9....9.1.3.3...8...4.9...5....3..9..4...7...2..6..5.....
........2..7...1...3..9..4......9.6.6...3.4.5.4...8....6..4..5...2...7..1..8.....
.....6......5.8.....8.9.5......7...4..76..2...1..2..3...2...6...4..6..9.39......1
..1.......5...6..16.7...........1..3...4.5.8..9..2..5...5..3..7....8..4..3.5..2..
........6..9...4...1..7..8....7.1.3.1.8.3.5...3...2....8..2..1...6...9..4..3.....
........7..9.5.2..1....6.8....5..6...9.....3...64.2....8.6.......4.2.9..7......61
........7..1..9.8..3.6..5.9.9..25.......6....3..9...4...7....91.2.5..3..8........
........6..5..8.9..3.4..7....491........8..4.5....42....1..9.5..6....4.37........
.....7..8.3..2..9...84..3....6.......8.5..6..5.4..........9.8.2.1.....7.8..3..4..
..9.......6...3...1...7...54...52..7.....6.4..3.9.....2......1.....2.57...48....2
.....6..5.1.....2...9.3.4......7......48.9...8..4.39....7.8.3...5.....1.2.......6
.....4.....2.3.....5.7....9..4..2.8..985....63.........8....79....8...6...5.1...8
2.......6.5..8..1...4...9...7.3.1......82.......7.5.3...9...4...8..1..5.6.......2
........6..5.3.7..2....8.1....9..8...5..8..4...87.3....1.3.......7.9.5..6.......2
.....1..7....6..2.8..9..3...954....3..3...4..4......8......7..6.1..2....5..3..9..
........8..3.9.4..6....7.1....5.97...3.....2...74........7....6..4.5.3..81.......
.....1.....7.2.....9.6.8..5..1..7....6.9....42.9.........8...59.8....4....6.3...8
........9..1...6...5..7..4....4.7.8...3.8...5.8...2....78.2..5..39...1..6........
........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........
..4.....9.1...3.7.6.....2.......8....5..3..8....1.5.....2.....4.7.3...1.9...7.6..
.....6.....1.2...3.3.8...7...6.....5.5.3..7..2....1.......9..4...5...98..9.4....7
..8...5...4...2.9.7.......3.2...4......93.......2.1.6.5.......8.1.6...2...3...7..
.....5.....3.7...8.9.8...4...5.....2.8.6..4..7...3......2...69...4...1...1.9....4
..3...5...7...4.6.8.......9.4...7......45.......2.1.7.5.......3.1.7...2...9...8..
........2..1...7...3..5..9......6.4...3.4.8...4.5.9....9..6..3...2...1..7....3...
..8.......5...8..11.9.4........3......5..9..4.4.6...7.......6..5....4.29.2..7..3.
..5...7...8...6.4.9.......2.4...3......65.......4.8.1.2.......9.1.8...3...7...5..
..8...3...7...9.4.1.......2.6...4......98.......7.6.5.2.......1.9.4...6...3...8..
..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7..
1.......9.4...3.8...2...6...7..58.......2.......7.4.5...6...2...3.8...7.9.......1
..2...6...4...5.1.3.......8.9...4......52.......1.7.4.8.......3.5.7...9...6...2..
..9...7...1...6.8.3.......4.2...5......89.......6.2.1.4.......3.8.2...5...7...9..
5.......9.2.1...7...8...3...4.6.........5.......2.7.1...3...8...6...4.2.9.......5
3.......8.7.5...1...6...4...9.2.1.......4.......97..2.4.....3...5...2.7...8.....6
9.......5.4.3...6...2...1...8.74........2.......8.6.7.1.....9...3...7.4...5.....2
.......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7.....
3.......2.8..7..1...69......5.7.4...........8...51..7...9...3...1..4..8.2.......6
3.......5.7...8.6...2...9...1...6......82.......1.7.4...9...2...8.6...1.5.......3
3.......1.4...2.7...5...8......76.4.....5.....6.2.9...1.....5...7.6...9...8.....3
5.......7.8...9.3...4...2...1..36.......4.......1.8.6...2...4...9.3...1.7.......5
....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7
1.......2....1..3...5..34....2..1..4....8.7..6..9.......1..5.4.8.....5..9...6....
1.........2.4...6...3...5.1.4..86.......49.8....2.....5.7...3...6.9...4.........7
........91......35..9.3.8....3.5...67....2......4.......6.8..9..2.7..6..4.....1..
1.......5.2.4...6...3...7...9...4........9.8.8.26.........5.1...6.9...2...7.....3
.......39.8......5..9.6.8....5.9...67....2......4.......3.8..5..2.7..6..4.....1..
1.......7.2.4...6...3...5...9..4........62.4....9..8....5.....3.6.2...8.7....1...
1.......2.2.....6...34..5.....8.5.....8.3.9.....9.4.....5..34...7.....1.6.......7
.2.4..7.........32.......94.9.2...7...6..5...8...1....5.1..8....3.9....7......6..
1.......2.2.....6...34..5.....8.5.....8.3.9.....9.4.....53..4...6......77......1.
....567.....1.....6..37......4.....13...6.9...8.....2.5....73....2.....8.1.....4.
.......93.8......5..3.6.8....5.9...67....2......4.......9.8..5..2.7..6..4.....1..
1.......7.2.4...6...3...5...9..82.......9..8....6..4....5...1...6.8...2.7....3...
.......1...6....23.2..3.4..8....5....3..1...4..96........9..7...1..2..4.5....8...
.......89.....1.35..3.5......5.6...8.7...2...1..4.......6.9..5..2.7..6..4........
.......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6...
...1....87.......9..3.9.5....9.3...5.....23.....4..6....6.8..5.47...1...2........
.....1.39........5..3.5.6....8.9...67....28..1..4.......9.8..5..2.......4..7.....
........7.2.4...6.1.....5...9...2.4....8..6..6..9.......5..3....3..8..2.7....4..1
...2....87.......9..3.9.5....8.3...51.....3.....4..6....6.8..5..7...1...2....4...
...1....8.7......9..3.9.5....8.3...57....23.....4..6....6.8..5.4....1...2........
.1......8.7...4..9..3.9.5....8.3...5.2....3.....4..6....6.8..5.2....1......7.....
5.......9.2.1...7...8...3...4...2.......5.......7.6.1...3...8...6...4.2.9.......5
9....4....3..6..7...5...8...2.1.6.......7...3...2...1...8...9...7..1..6.4.......5
1...5...9.......3..79........7.1...65....42..6..8.......5.6...7.....2.4....3..8..
...1....87.......9..6.9.5....8.6...5.....23...7.4..6....3.8..5..2...1....4.......
.......1......4.32.2..3.5.......7....4..2...5..89..4....78..6...3..1..5.9........
7....4..8.1......9..6.9.5....8.6...5.....23.....4..6....3.8..5.2....1......7.....
1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1
12.3.....4...5......6..17..5..2......3..1..4...1..68....9....7....1..9.......9.68
12.3.....3...4......5..23..5....61.........9...67....8.5...16.....9...4.....8...7
..3.......5.4...8.1.......7.9..8........94.6.5.62.............3.6.9...4...7.2.1..
...2....871......9..6.9.5....8.6...5.....23...4....6....9.3..5.4....1........7...
12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8
.2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9..
..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5.
1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6.
...4...8...7..92......3...526...1.....19......7....1..5......4..1.8....3..6..29..
..34...8.......1.37........2...9.......5..8...6...7.4...51....8.7...5...9...62.5.
1...5...9..7...2...69..3.4....8........91..7.9....5..83..5....1..2...6...4.......
....56.8..5.7....3..8......2.....9...4.5....7....92.6.3.4.....15..1..4...1.....7.
1...56.....71.......9....1..4..6.3.......8..29..7...6......24..5..6...9.....3...8
...4...8...7..92......3...526...1.....19.....7.....1..5......4...6..29..9..8....3
1....6....571......9..7..1...8...........89.29..5...6.....42..3.3....4..7..6...9.
.2.4.....4...8...6..9..31..2..5...7..95.......7....3....29...4......1..3....6.8..
..3..6.8.4..1..2...9......4.7...8....35......9...3...5..9.4...7.....2.1....8..6..
.2.4..7...5...9.3.6...7......5..8.9.7...2.....4.6..3.......1..85......1...1...9.3
...4....9....8.2..6....3.1..1...7...7..6..5...3.....7.3....5.2...29..8......2...4
1....67...5.7....3..8.....4.4.5..3......2..9........45.7.9...3.6...1......2..8...
1.3.....9..71.9....9.....4..1...7..3....2.8.....5...6.....6.5..6..8...2..7...4...
..3..67...5..8.....9.1.........9..1...7..32..8..5...4.37....4.......4..6..4....72
1.......2..94...5..6....7.....89..4....3.6.....8.4.....2....1..7.......6..5.8..3.
6.......2.9.4...5...1...7...5..84.......2.......3.5.4.2.....6...3...9.8...7.....1
1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1
1.......2.3.4...5...6...7...5.9.4.......23......8...9...2...6...9...8.4.7.......1
1.......2.3.4...5...6...7...5.8.4.......29......3...9...7.....1.9...8.4.2.....6..
1.......2.9.4...5...6...7...5.3.4......96.........8.4...2...6...3...9.8.7.......1
1.......2.3.4...5...6...7...5.8.3.......7.......95..8.7.....6...9...8.3...2.....1
1.......2..34...5..6....7.....85..9....3.6.....8.9.....2....1..7.......6..9.8..3.
1.......2.3.4...5...6...7...5.8.4.......73......9...8.7.....6...4...8.9...2.....1
1.......2..34...5..6....7......3..4....8.6.....954.....2....1..7.......6..5.9..8.
..1...5...2.4...6.3....7....6.28........9..2.......4.65.....1...9.8...4...7.....3
1.......2.3.4...5...6...7...5.8.3.......74......9...8.7.....6...9...8.3...2.....1
...1....9.......85..9.5..6...5.3...6.7...2...1..4.......3.8..9..2...73..4........
.......35.....2.6...3.5..8...5.9...6.7....9..1..4.......6.8..9..2.1.....4....7...
1......8......913.......5.6..7.4....3....1.5..6.2.....5....89...4..7......26.....
3....97...6..4......18.....5......93.....327.........8..4.1....2....7.5..8.6.....
.......8...6...12....2..6.5..15..9..8....3....4..7....3....8.....21....6.7..4....
1.......2..34...5..6....7......3..4....5.6.....894.....2....1..7.......6..5.8..9.
12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8
........94....92......7..45..1.3.....7.6..9..8....7..2.3.7..8....6.1....9....5.2.
...4....9.5...9.3.......1.52..8.......6.4.....1...53....42...7.8...67....7......3
1...5..8...7.....6.8.3..5..2......1...6.....7.9...38.....9.4....4..3.9.....8...2.
..3.5.7..4....9....8.2.....2......4..9.8.......1.6.5.....5.16......7...3..6....57
..34..7.......9..6....2..1...8..4..13.....8..57.8..........8.9.8...6...2..53..1..
..3..6.8....1....6.6.72.....1...7..8..4...6..5..3..9...3...2..7..1...4..9......5.
.....6......7...2...9.3.5....5.9.84.8..2....6.......7.39..4....5.....4....4..1..8
..3...7..4...8...6.9.....1.......6..6....4..8..16..39..7...2...5..8....2....45...
1..4.......7.8...6.9....41......3....3.8..9....5.6...7..2......5....8..2.7..2.65.
..34......5...9...6...2.1..2...6...1.4.5...6...6..3.....9...6.7......8.28...7..1.
.2...67..4...8..3....2.............53...4..1...5..76...34..8.9..81.9.4..9........
......7.94....9.3..8....4.5..18......6..2....7....53..5....7..3.7.6.......2.1..7.
1....67...5.1....6..9....1...5..83..7..5...9..3...........4...25..6...7...4.2.8..
1....6....5......6..9.7..1...4.1..7.3..8......8...53.......4.2.....2.9.7..27..1..
1.....7...5..8...37..2.1....6...5...3.58....6....6.4....29..3.........9..3..4...8
....5.78...6..9..27.........3.....5....83.2....1..2..6.6......4..41..92.9....4...
.2..5..8...71.9...6...........5...4..4......8....4.3.2..1..7..6.6..2..3.9..6..8..
1.....7...5..8...37..2.1....6...5.3...58....6....6.4....29..3.........9..3..4...8
1..4..7....7..9..6.8.....5.....1....83..9......5..7.6.3.......5....4..92..2..56..
1...567.....1....6..9....1...5..83..7..5...9..3...........4...25..6...7...4.2.8..
.2......9..6.8.1..7.......5..46....8....4..1....3.84...9.....2.5....7..1..1.3.8..
....56.8..5.7....3..8......2.....9...4.5....7....92.6.3.......15..1..4...1..4..7.
..3...7..4...8..2...9.....6.1...43......2...79..8...5....5..6..5....8.9...129....
..34....9.5.......6....7.5.2..8..3....8..49...4..2...8..29....4.7..61.........1..
.2....7..4....9.3.6..723.4...85.........1....9....4.6..94....5.5.....6.3.....5...
1...5..8.......1...8...3..6..7..4.6.3...7...4.4....8..5.2....9....29.....6...7..8
..3.5...9...1..23.6..3..5....5.3.9.........2.8....7..4..2.4..9..6...1....7.8.....
..3..6...4.7....6..6....1...7...1.9....59.81.9.......2....2.9...4...3.7....8....5
..345....4....9.3..8..3....2..9...4.....2...1..7...6..3...9..5...1..28...6......7
...4.67....7..........7..15.94.6.5.........98..591.6..3.............2.6...6.4.1..
...4.67....7..........7..15.94.6.5.........98..5.1.6..3....1........2.6...6.4.1..
...4.67....71.........7..15.94.6.5.........98..5.1.6..3.............2.6...6.4.1..
..3.5.7.....7......8...2..6..4.....1.1....86.8....12..5.239...........9..4...8..2
..3.5.7.....7......8...2..62.4.....1.1....86......12..5..39......8....9..4...8..2
1.....7....71.9..6.8..7......8.9.6..5.......2...2.3.5...6.1.9...4.......9....4..3
.....67....71.9...68..7......6.1.8...3.9..........4.23..1.9.6..5..2....4.......5.
...4...8...7..9....8..3.1..2.....9..7....53...3..6..1..6..9...15....46....2....9.
..3.5.7.....7......8...2..62.4.....1.1....86.8....12..5..39...........9..4...8..2
.2.....8...71....6.6..3........4.9.......38..7..5....2..5.9....6.1..54...7.6....5
.2.4...8...6.8....7....3..6...9.....6....7..1..4.2.9...67.....55.....31..1...5...
1......8..5.1....6..9.3.4....6.........9.36......6..24.7...5...8..3....7..2.4.3..
1.3.567......8...36....74.......5.715...........64.5.........2.7....41...9......7
12....7....7....36....7..1..1...5...5..9....8..6.2.1...4..........8.4.9...1.3.6..
1....67.....18..3...9.7......4.....3.9.....2.7..6..5....2..1...8...6.1.....8.5.4.
...4.......7.8...66..7...5...1.6.3.839.........6.7...1........271..2......26.8...
...4.......7.8...66......5...1.6.3.839.........6.7...1.....7..271..2......26.8...
..3..6.8....1....6.6..7.1..2...4.5...3...1..8......34....9......8...7..39.5....2.
.2....7..4....9.3.6..2.3.4.....1......85.....9....4.6..9.....5.5...4.6.3.....54..
1....6.8....7..1....9.....4.......5..18..5...5..36.8..6.5..8.3.8....3.1.....2....
1....6.8....7..1....9.....4.......5..18..5...5..36....6.5..8.3.8....3.1.....2...8
........94....9.2..8.1..5....5..86..3.86......6..15.....2....47.3.5..8......7....
......7....71.9...68..7......1.9.8..8.......4.4.....2...8.1.6..5..2....3...9.3.5.
..3.....94...8.1...9.....4.2...1.4.......7.5..753.........6..146.......8...2.86..
.2.4..7.......9..3....3..462.7...8...8.....64...8......7.6..4....1..5...9...1..5.
1....6....5..8..3...9..2....7..4.3.....6....79....7..5..1...4...4..7...3..2..48..
1...5.7....7..9.3......7..4.8......3..4....2.6..5..9....8.1.....1..6...29....51..
1...5.7....7.....6..97...4......8..4.1..3.9.....6...2.39......25..8.......1.9.5..
1...5.7....71......6...2..4.....834..8..........62...8..52...9..3...4..29...6....
.234.......6.8....7....35....4....7....8...9.9....7..53....1.5....2....3.6....9.1
12....7....7....366...7..1......4.9.54.9.......1.3.6.......8......5....8..2.1.3..
...4.......678....7....2..4.3....6....1..5.9....2....5.9...7.1..6....3..8...2...7
1...5..8......9..6..92..5.......3..8.7.9.......4.2.3..6...4....7.......1..5..24..
....567....71....6......41.2...9.....3...5.....67...4...8...1..5...3...8.9...2...
......7....71.9...68..7......1.9.6.....3...2..4......3..8.6.1..5......4..1...2..5
...4......5...9..6..9.2..1..3......5..1..2.6.7.....8......1...78...6.3....62...4.
1.....7....71.9...68..7......1.9.6.....3...2..4......3..8.6.1..5......4......2..5
.2...6...4...8......97..1........3.5.6....9.1..13...7...59....38....2..7....4....
.2....7..4....9..3....2......1...6..8....3..4...67..1..8...5...5...3..4...98....5
.2.4...8......9..66.....5....87.....3....8.7..7..2.4....1..5.......1.9...3.8...4.
1....6..9..67...2..8..3.5.....5..3...1...8..6....2......1..4..884.......9......7.
.2....7.........3...91....4..49....83.....6...7..6..2...5.98........1..5.4.5..8..
.2.4....9......1..7...3..4..1.6..9..5....8.....7..3.1......5....4.2...9.8.....6.2
...4...8...7..92......3...526...1.....19.....7...2.1..5......4...6..29.....8....3
...4...8.4....9..2...23.....7...56..3..9....5.......1.5...9...4.6.5...7...1...8..
...4.67....7..9..6....7..5.2......1..8......3..6.4.5..3.......8..5.9.4...1.2.....
.2......9..6.8.1..7......5...46.8....6.3..8......4...1.9.....2......5..7..1.6.3..
1.....7....71.9...68..7......1.9.8...4.2....5.......2...8.6.9..5....3.4.........3
1..4...8..5...9..6....3.1...9...5..7..8.2.4.........2.7.5..3....3.69............3
.2...6..94..18......8...4....1....5....5......6...3..2.....7.9..7..9.3....26....7
.2...6..9..71........23..1....5..3..5...7.....8...2..6.....4...6.8.....4.4....92.
...4.....45.....6...8..7..1..9..8..73.........6.5..3....2.7..9......217....8....2
.2...6..9..71.........3..1....5..3..5...7.....8...2..6.....4...6.8.2...4.4....92.
1...5...9..7...2...6.....4...6..1........84..9..5....83....5..1..13...2..7.9.....
1...5.7...5...9.3..8...7....4..1........6...29....51....4.....87..5..6...3.....2.
1..4...8..5...9..2..9...4.....6..8.36............41.6...2.7.5...7.......8..3...4.
1..4.......7.8...6.9....41..3............39....5.6...7..2..8....7..2..6.8....5..2
1..4..7....6....2.....3...5.....5..6....24.3.8..1..4....7...9...149.....9....8...
........9.5.1...3...9.2.4...3.7...5.......8..8....4..2.....1.6.5.16......6..73...
........94....9.2..8.1..5.......56..3.56......6..18.....2....47.3.5..8......7....
......7...5.1....6..9.7..4.2....49...1.6....8.......2.5.13.......8.....3.3..68...
.....6....56...1..7...3..4.2............2.9.4.4.3.7.2.3...4..9...8.......1.8....5
...4.67....7.....6....7..512..........4.1.5...9.2....8.3.......8....3.9...6.4.1..
..3.5..8.4....91...8.......2...........64.2.......2.91.753...4.........79....46..
..34..7...5...9.3.6..3.....2.....8...4..9..5.7.....6.......1..8.1.9...4...2..5...
.2....7..4....9.3.6..2.3.4.....1......85.....9....4.6..94....5.5.....6.3.....5...
.2.4....9....8....6.8...1.....7..39..7.....2.....24..75.........4.9....3..1..5.6.
1.....7...5...9.3.....2...1..6....4.3.5..4....4.96......87....2.3...5.6.......8..
1....6..9.5.7...2.......5...3......8.91..8...8..36........2.47...4......9....3..1
1....6.8.....8.2...89.7.........53....4..1...7...6..1.5..6...7..4....9....2.....3
1...5.7....7..9.3..8...7....4..1........6...29....51....4.....87..5..6...3.....2.
1..4...8..5...9..2..9.........6..8.36.......4....41.6...2.7.5...7.......8..3...4.
1..4...8..5...9..2..9..........48.6....6..3.16.........72..15..8..3...1.....7....
12....7....7....36....7....2..9......4...5.9...1.2.6..........85..8.4.....2.6.1..
12....7....7....366...7...........9..4.9.5.....6.2.3..5....4..8...8.......2.6.1..
12....7....7....366...7.........8.5..4.9....8..1.2.3.....5.......2.6.1..9....4...
12....7....7....366...7.....4..........8.4.9...1.3.6.......5...5..9....8..2.1.3..
....5..8...6..91.....2....4.17.......3..4....6....73....1....5..6...39..9..8....2
..3..67..45...............4..8...........8.26...37.8....2..76...1..9..7.9..5...1.
..3..67..45...............4..8.....7.....8.26...37.8....2..76...1..9....9..5...1.
..3......4...8..36..8...1...4..6..73...9..........2..5....7..686....4...7.....54.
..3......4...8..36..8...1...4..6..73...9...1......2.....4.7..686........7....35..
..34.........8..36..8...1.4.4..6..73...9..........2..5..4.7..686........7.....5..
.2......945...9.2....1....4....6....34...52....8......59...3..2...5...3.....7..5.
.2......945...9.2....1....4..5.6....34...52....8.......9...3..2...5...3.....7..5.
.2...6.8.4.....2.6...3.......59...1..6...28..........7.7...1..85...3......9.4..7.
.2.4.......6.8.1..7....2.......4..96...8.14....4......53.........8.1.9.....5...73
1......8...67....3.8....5....53....6.4...19..9...4.......2..65...2..........73..2
1..4..7....71.9...6...7....2......5...1.6.4.....3....8..6.4.9..5......2..8......3
1.34......5...9.2.........4.....19.5.1..........27..1..7...2.9.6..3..8..8...6....
1....67......8......92...5.....4...85..3...1......76..3.2.......1.9...2.9.5.....4
......7.94...8..3......2..5.4..3..6.3.8.....7..69......1.2......3...5.....4.6..1.
......7....71.9...68..7......1.9.6..8.......4.4.....2...8.6.1..5..3..........2.53
......7....71.9...68..7......1.9.6.........2484.........8.6.1..5.......3...3.2.5.
...4.67....7..........7..152.........9.2....8..4.6.1..38.....9....6.3.....6.1.5..
..3......4...8..36..8...1...4..6..73...9..........2..5..4.7..686........7..6..5..
..3......4...8..36..8...1...4..6..73...9..........2..5..4.7..686....4...7.....5..
..3......4...8..36..8...1...4..6..73...9...1......2.....4.7..686........7..6..5..
..34......5....1..7....3..52.7....6..3...7..88..9.........4..9...8..2..7...6...1.
..34.....4...8..36..8...1...4..6..73...9..........2..5..4.7..686........7.....5..
.2.4....9..7..9.....9..31.....6...5....2..8...9...4..2.6..4...37.....5....8....1.
1.....7....71.9...68..7......8.6.9..3..2...5.........2..6.1.8...4......3.....5.4.
1.....7....6.8.1...89.....6...5.....6...9...1.3...2.5.......4....23.4...8...6...7
1....6.8....7......9..3.......56..3.3.............38.15....1.6.....2.4..8.2..5.1.
1....6.8..5.1..2....9...1...9..3......45.....6....2..8.4.....6.7....18.....3...7.
1....67...57.8......6.....4...9...4.....2...8.1...36..3.......25...9......1..75..
1....67...5.....3...8....4.....9...3..5.12...9..6..1...4.7....8.....1...7...6.9..
1....6...4....9.3..8.2..6....4.....7.9..2.8..5......1....3...7..3...29....58.....
12....7....7....36....7......6.1.3...4.8.....9....4..8..2.6.1.....9...5......5..2
.2.4.......6.8....7....35....4....7....8..39.9....7..53....1.5....2....3.6......1
.2.4.......7.8...6.....3.5...9.6...1.....23.....5...4...1...8..6...1...797.......
...4.67....7..........7..1529......8..6.1.5.....2......3.......8....3.9...5.4.6..
1...5...9......12...8...5...3...7...5...6..1...48......7...4...6...2.9.....3....6
.....6.8.4....9....6..7.1...9...73......1..2...5.3.....1.7..6....4.....2..8.....5
....5.7..4....9.3..8....4......1..9.7....23....6....2...85.......1.6....9....4..3
......7....71.9...68..7......8.6.1..5.............3.52..1.9.8...4.2....3.......4.
......7....71.9...68..7......8.6.1..5..3...2.........3..1.9.8.......2..4.4.....5.
........94....9.2..8.1..5.......56..3..6......6..18..5..2....47.3.5..8......7....
......7....71.9...68..7......1.9.6...4...2..58......2...8.6.9..5......3....3....4
......7....71.9...68..7......1.6.8...6.....42.....2.....6.9.1..54......3.1.3...5.
....5....4....92....9....1.2..7.39....6.....79....43...94...8.......8.9.8......23
...4.......6.8.1..7....3..4.3.5.......9.6.8.......7.5...2....166...2....98....2..
...4.......6.8.1..7....3..4.3.5.......9.6.8...6...7.5...2....16....2....98....2..
...45.7...5...9.3.6..3.....2....1....3.9...5.9....5..8.1..9..4...2...8..7.....6..
..34....94...8.1...9.......2...1.8.....3.7.5..75.........2.86......6..146.......8
.2...6.8....7..1....92..........83....5..4....4.6...7..7..6..2.5.......3..1...9..
.2...6.8....7..1....92..........43....5..8....4.6...7..7..6..2.5.......3..1...9..
1.....7....71.9...68..7......1.6.8...6.....42.....2.....6.9.1..54......3...3...5.
1....6.8..5.1....6..9.3.5...7.8.......5.2.9.......7...39....4......4......4....52
1....67.....18..3..9..7......4.1...3..9....2.8..6..1....2......5...6.8.......5.4.
12....7....7....36....7.........5..85..9.......6.2.3...4..........8.4.9...2.6.1..
......7....71.9...68..7......1.9.8..8.......4.4.....2...8.6.1..5..2....3...9.3.5.
......7....71.9...68..7......1.9.6...4......28......4...8.6.1..5.......3...2.3.5.
......7....71.9...68..7......1.6.9..84.....2.........43..2...5...8.1.6.......5..3
......7....71.9...68..7......1.6785.5....3.....8.1.9....6.9.1...4.....9.........2
...4.......6.8.1..7....3..4.3.5.......9.6.8.......7.5...2....16....2...898....2..
...4.67....7..........7..512..8...9...1.4.6...9........3.......8....3..2..6.1.5..
..3.....94.....2...6..7..1..1...7.5...46........5....2.8.7...6......8..39....18..
..3......4...8..36..83..1...4..6..73...9..........2..5..4.7..686........7.....5..
.2...6.8.4.......2..9...4...7............76.1...82..7.3...9...5..53......6...8.1.
.2.4...8.....8....7....2..6..49..1..6....7..3.9..1.....62.....55.....37......5...
1....6....5..8..3.7..2......4..9...3.....79..9..6..5.......2..8.9..4.3..8.1.....4
12....7....7....36....7..1..4...5.9....9.......1.3.6..5....4..8.1.8.......6.2.1..
.......8...6.8.1..7....3..4.47..5...5..32............53....4..7......9...1.9...6.
1......8....18...6..9..35.......4.....4....2593....4...7..........76......2..59..
1....6.8..5.1..2.6...3..1....9.4..7.........26....18...4.9.....7....2..8..5.3....
1..........7.8...6.9.3...1...46........7.8..4....4.62.5.........3...59....2.6...7
1.....7...5...91...89.....5......6.....26....9....1..8..234.....3.....4.8....5..7
12....7....7....36....7.........4..8.4.9.......6.2.3...7.5.8.9.5..........2.3.1..
.2..5......6..9..17..3.....2...6.8....8..4.1.....3.6....4..81..5......9..7.....4.
1....6.8.....8.2...9..7......4.....35..6...7...9.....2..5..1..48...6..5......53..
1..4......5...92......7..14..6.9.....3...59.........67......8..5.2..8....8.93....
.2......9.5.1..2..6..7...5.....1......68.5.2.....2...3.6.2.8.7.7...6.4..8........
.2......9.5.1..2..6..7.2.5.....1......68.5.2.........3.6.2.8.7.7...6.4..8........
.2...6.8....1....66..73.......3...5...9...4..7....1..8..58..9..8....3..1.4..1....
......7....71.9...68..7......1.6.85.5....3...7.8.1.9....6.9.1...4.....9.........2
..34.6....5.1..3..6...7..4.2...6..7.........8.18..5.......9.2.4...6.7.9.9........
.2......9..6.8.1..7......4.2....1.5......5.9...58..6..37.6......4.3.......1..83..
.2...6.8....7..1..7.9.1.........45.8.4..........62..4.3..9.............3.6...5.2.
1....67....71.9...68..7......8.6.9..5....2.4.........2..1.9.8...4......3...3...5.
12.3.....34....1....5......6.24..5......6..7......8..6..42..3......7...9.....9.8.
...4......5..8.2.6.....7...2...4....3......1...5.3.8.25...6.3.8..6....95..8......
1....6.8...71....66.....15..3.9.....7....184.....2........9.41.5....4..8...8..5..
.2....7..4....9.3.6..2.3.4.....1......89.....9....4.6..94....5.5.....6.3.....5...
1...5.7.9..7.......6.......2...........5.1..2....2.39.3.4.9...15...1...3...8...4.
..3......4...8..36..83..1...4..6..73...9...1......2.....4.7..686........7.....5..
...4......5...9...6...2..1.2...7.9.1..5....7......8.3...6....9.7...3.1......9.327
...4......5..8.2.6.....71..2...4....3......1...5.3.8.25...6.3.8..6....9...8......
1...5.7.9..71......6.......2...........5.1..2....2.39.3...9...15...1...3...8...4.
1...56....5718...66...7.....9.3............9.8....5..35...17..8..2....1.......4..
1....6.8...7.....66.....15..3.9.........2....7....854.....9.81.8...41..5.1....4..
...4......5..8.2.6.....7...2...4....3......1...5.3.8.25...6.3.8..6..8.9...8......
...4......5..8.2.6.....7...2........3.....51...5.3.8.25...6.3.8..6....9...8.7....
12..5...9.57...2...9..2..1....8..9.47...6.1.......4............57..9.6....6..3...
.2.4...8...7.....3.8.237.1.2.1....9..9....8.4...9......1.8...4.5............6...8
.2.4...8...7.....3...237.1.2.1....9..9....8.48..9......1.8...4.5.8..........6....
.2.4...8...7.....3.8.237.1.2.1....9..9....8.4...9......1.8...4.5.8..........6....
..3..67.........2.79.2......3....6..5....4..76.7..3.453.5..74...............1...8
..3.5.....5.1..2.66...2..4....8...9..8..1.6.5...6.....7.......4..........6...18.2
..3.56....5.1..2.6....2..4...68...9..8..1.6.5...6.....7.......4..........6...18.2
..3.....945...92....9..3.54....6....6..9..8....5..8.2..1.7................4..5.92
.....6..9..67...3.79...3....1...7.5...752......5.....2..167..2......14..8........
...4......5..8.2.6.....71..2........3.....51...5.3.8.25...6.3.8..6..8.9...8......
.2.4...8......9..2..9.3............5..8..7....4.5..82...46..21.6.21..4...1......8
........9.5.7...2.7.9..2....1.67..5.......4..8....5....7.31....6....7.3..3..6...1
....5.7..4.7..9.3..8....1..2.8..7.....4..8....7..9...3......6...4...2.97..2.....1
.2.4.6..9..7......6...2..5......15...1.64...28......1..3.26.....6..1...39....3...
1....67.9.5.........9.....4....9..3.....1....9..6..8.1..27.....7..8...4.8...6.1.7
.2..5...9...7.....7.....5..2..........4..8....6..2..91.3.2..9..6...9..13..1.6..2.
.......8...67......8...36.5.4..3..5......4..66....83.4..1.9..2..3...25..9........
..3.9....4...8..36..8...1...4..6..73...9..........2.....4.7..686........7.....5.4
..39.....4...8..36..8...1...4..6..738......1......2.....4.7..686........7.....5..
..3......4...8..36..8...1...4..6..73...9..........2.....4.7..686........7..6..59.
..39.....4...8..36..8...1...4..6..73...3...1......2.....4.7..686........7.....5..
..3......4...8..36..83..1...4..6..73...9..........2.....4.7..686........7.....59.
..3......4...8..36..8...1...4..6..73...9..........2.....4.7..686...2....7..6..5..
..3..2...4...8..36..8...1...4..6..73...9.......6........4.7..686........7.....59.
..3......4...8..36..8...1...4..6..73...9...1......2.....4.7..686....4...7.....5..
..34.....4...8..36..8...1...4..6..73...9..........2.....4.7..686........7.....59.
..3..2...4...8..36..8...1...4..6..73...9..........4.....4.7..686........7.....59.
..3.9....1...4..36..8..21...4..8..73...9................4.7..686........7.....5.4
..3......4...8..36..8...1...4..6..73...9.........42..5..4.7..686........7.....5..
..3.9....4...8..36..8...1...4..6..73...9..........2.....4.7..686..4.....7.....5..
..3......4...8..36..8...1...4..6..73...9........7.2..5..4.7..686........7.....5..
12.4..3..3...1..5...6...1..7...9.....4.6.3.....3..2...5...8.7....7.....5.......98
1...5......7..9.3...9..754...4..3.7..6........9.8........79..2......24.3..2......
.23.....94.....1...9..3..4.2..81...4.....78..9...4...23...9...1.6..........5.....
1..4.6..........2..8..3.5.6.6...48.5............5..2.......3.9...7..8....4.6..3.8
..3.5.....567....27..2...4......18..3...2...66...7...453..4...7.......9.......4..
...4.67....7.....6....7..512......9...5.6.1...91.426..3...........8.......4.1.5..
1..4....9.56..9.......1..6..6....8..5....4.9.9....5.1..7....2..6....1.5....3.....
1.....7....6.8.1...89.....6.....4.5.6..59...19...1...8..2.........3.....8...6...7
1.....78...6.8.1....9.....6.....4.5.6..59...19...1...8..2.........3.....8...6...7
12.3.....4.5...6...7.....2.6..1..3....453.........8..9...45.1.........8......2..7
5..6......2.....4...1.2.3..9..8.......7.4.1.......9..6..4.7.2...3.....1......5..8

10
ipynb/sudokus_hard10.txt Normal file
View File

@@ -0,0 +1,10 @@
........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3
........2..8.1.9..5....3.4....1.93...6..3..8...37......4......53.1.7.8..2........
..2...7...1.....6.5......18....37.......49.....41.23....3.2.9...8.....5.6.......2
........7..4.2.6..8.....31......29...4..9..3...95.6....1......8..6.5.2..7......6.
..4..3....7..8....2.81....6..3....9..8..2....1..7....3......45....8..9....9..5..8
..6..1....5..3....9..4....7..1....2..3..9....4..5....13.....68....3..2....2..8..3
........3..1..9.6..5..8.4.....9...8...867.....1....2....6..7.2..3.8..5..4.......8
........5..6..87..3......9....1.7.4...7...8...4...6....9..8...3..16..4..5...2....
.....5..3..9....4..81.4.......7.......4..2..68...14.3.......2...4...6..79...5..1.
.....5..4.9.....2...6.7.3.....7..8....86.....13..8......3.1.6...2......54......9.

File diff suppressed because it is too large Load Diff

View File

@@ -2,10 +2,11 @@
notebooks = {
'New': [], # Gets updated automatically
'New': [], # Gets updated automatically
'Programming Examples': [
("AlphaCode Automated Programming", 2022, 'AlphaCode.ipynb', "Analysis of AlphaCode's automated solution to a coding problem"),
("Approximating Pi with a Fraction", 2026, 'RationalPi.ipynb', "Using continued fractions to find fractions (like 22/7) that are close to pi."),
('The Babylonian Number System', 2022, 'Babylonian%20digits.ipynb', 'Translating between Babylonian and traditional number systems.'),
("Beal's Conjecture Revisited", 2018, 'Beal.ipynb', "A search for counterexamples to Beal's Conjecture"),
('Bicycling Statistics', 2020, 'Bike-Stats.ipynb', 'Visualizing statistics about bike routes'),
@@ -57,13 +58,14 @@ notebooks = {
('How to Count Things', 2020, 'How%20To%20Count%20Things.ipynb', 'Combinatorial math: how to count how many things there are, when there are a lot of them'),
('KenKen (Sudoku-like Puzzle)', 2021, 'KenKen.ipynb', 'A Sudoku-like puzzle, but with arithmetic.'),
('Number Bracelets Game', 2024, 'NumberBracelets.ipynb', 'A game involving numbered beads on a circular bracelet.'),
('Project Euler #3: Largest prime factor', 2026, 'Euler3.ipynb', 'Find the largest prime factor of a number'),
('Pairing Socks', 2019, 'Socks.ipynb', 'What is the probability that you will be able to pair up socks as you randomly pull them out of the dryer?'),
('Sicherman Dice', 2018, 'Sicherman%20Dice.ipynb', 'Find a pair of dice that is like a regular pair of dice, only different'),
("Sol Golomb's Rectangle Puzzle", 2014, 'Golomb-Puzzle.ipynb', 'A Puzzle involving placing rectangles of different sizes inside a square'),
("Stubborn number endings", 2024, 'Stubborn.ipynb', 'Any number ending in 5 has a square that also ends in 5. What other endings are like this?'),
('Star Battle (Sudoku-like Puzzle)', 2021, 'StarBattle.ipynb', 'Fill-in-the-grid puzzle similar to Sudoku'),
('Sudoku', 2006, 'Sudoku.ipynb', 'Classic fill-in-the-grid puzzle'),
('Sudoku: 100,000 puzzles/second in Java', 2021, 'SudokuJava.ipynb', 'A version of the Sudoku solver using parallel threads and other optimizations'),
('Sudoku: 200,000 puzzles/second in Java', 2021, 'SudokuJava.ipynb', 'A version of the Sudoku solver using parallel threads and other optimizations'),
('Square Sum Puzzle', 2020, 'SquareSum.ipynb', 'Place the numbers from 1 to n in a chain (or a circle) such that adjacent pairs sum to a perfect square'),
("When is Cheryl's Birthday?", 2020, 'Cheryl.ipynb', "Solving the *Cheryl's Birthday* logic puzzle"),
('When Cheryl Met Eve: A Birthday Story', 2015, 'Cheryl-and-Eve.ipynb', "Inventing new puzzles in the Style of Cheryl's Birthday"),
@@ -74,6 +76,7 @@ notebooks = {
'Word Puzzles': [
('Boggle / Inverse Boggle', 2020, 'Boggle.ipynb', 'Find all the words on a Boggle board; then find a board with a lot of words'),
('Chemical Element Spelling', 2020, 'ElementSpelling.ipynb', 'Spelling words using the chemical element symbols, like CoIn'),
('Did you solve it? R y clvr ngh t rd ths sntnc?', 2026, 'clvr.ipynb', 'Guess famous phrases from letter shapes. Beginner level.'),
('Equilength Numbers: FOUR = 4', 2020, 'equilength-numbers.ipynb', 'What number names have the same letter count as the number they name (such as FOUR)?'),
('Gesture Typing', 2017, 'Gesture%20Typing.ipynb', 'What word has the longest path on a gesture-typing smartphone keyboard?'),
('Ghost: A Word game', 2017, 'Ghost.ipynb', 'The word game Ghost (add letters, try to avoid making a word)'),
@@ -98,7 +101,7 @@ notebooks = {
('Lottery', 2019, 'RiddlerLottery.ipynb', 'Can you find what lottery number tickets these five friends picked?'),
('How Many Soldiers to Beat the Night King?', 2019, 'NightKing.ipynb', 'A battle between the army of the dead and the army of the living'),
('Misanthropic Neighbors', 2017, 'Mean%20Misanthrope%20Density.ipynb', 'How crowded will this neighborhood be, if nobody wants to live next door to anyone else?'),
('Properly Ordered Card Hands', 2018, 'Orderable%20Cards.ipynb', 'Can you get your hand of cards into a nice order with just one move?'),
('Properly Organized Card Hands', 2018, 'Orderable%20Cards.ipynb', 'Can you get your hand of cards into a nice order with just one move?'),
('Race Track', 2021, 'RaceTrack.ipynb', 'Race virtual cars around a circular track defined on a grid of points.'),
('Split the States', 2021, 'SplitStates.ipynb', 'Split the US states into two near-halves by area.'),
('Tour de 538', 2020, 'TourDe538.ipynb', 'Solve a puzzle involving the best pace for a bicycle race.'),
@@ -109,6 +112,7 @@ notebooks = {
'Computer Science Algorithms and Concepts': [
('BASIC Interpreter', 2017, 'BASIC.ipynb', 'How to write an interpreter for the BASIC programming language'),
('Convex Hull Problem', 2017, 'Convex%20Hull.ipynb', 'A classic Computer Science Algorithm'),
('(How to Write a (Lisp) Interpreter (in Python))', 2026, 'lispy.ipynb', 'Tutorial on interpreters.'),
('Docstring Fixpoint Theory', 2023, 'DocstringFixpoint.ipynb', 'An approach to writing code and docstrings that go together.'),
('Stable Matching Problem', 2020, 'StableMatching.ipynb', 'What is the best way to pair up two groups with each other, obeying preferences?'),
('Symbolic Algebra, Simplification, and Differentiation', 2017, 'Differentiation.ipynb', 'A computer algebra system, including symbolic differentiation'),
@@ -143,7 +147,7 @@ import re
import urllib.request
current_year = 2026
new_year = current_year - 2 # What `find_newest` looks for
new_year = current_year - 1 # What `find_newest` looks for
def read_url(url: str) -> str:
"""Reads a file from the specified URL and returns its content as a string."""
@@ -190,21 +194,20 @@ def find_newest(notebooks, label='New', year=new_year) -> None:
def format_category(category) -> str:
"""Make a table of multiple jupyter/ipython notebooks, under a header."""
print(f'{len(notebooks[category]):2d} notebooks in {category}')
return table(('Run', 'Year', category),
return table(('Open', 'Year', category),
[format_notebook(*line) for line in notebooks[category]])
def format_notebook(title, year, url, comment=''):
"""Make a markdown table entry for a jupyter/ipython notebook."""
urlb = f'/blob/main/ipynb/{url}'
co = f'[C](https://colab.research.google.com/github/norvig/pytudes{urlb})'
co = f'[co](https://colab.research.google.com/github/norvig/pytudes{urlb})'
gh = f'[G](ipynb/{url})'
dn = f'[D](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmain%2Fipynb%2F{url})'
my = f'[M](https://mybinder.org/v2/gh/norvig/pytudes/main?filepath=ipynb%2F{url})'
nb = f'[N](https://nbviewer.jupyter.org/github/norvig/pytudes{urlb})'
nb = f'[nb](https://nbviewer.jupyter.org/github/norvig/pytudes{urlb})'
sm = f'[S](https://studiolab.sagemaker.aws/import/github/norvig/pytudes{urlb})'
ti = f'<a href="{gh[4:-1]}" title="{comment}">{title}</a>'
if year == current_year: year = f'<u>{year}</u>'
return f'| {co} {dn} {my} {nb} {sm} | {year} | {ti} |'
return f'| {co} {nb} | {year} | {ti} |'
def format_pythons() -> str:
"""Make a table of multiple python programs."""
@@ -236,12 +239,9 @@ To continue the musical analogy, some people think of programming like [Spotify]
# Index of Jupyter (IPython) Notebooks
For each notebook you can hover on the title to see a description, or click the title to view on github, or click one of the letters in the left column to launch the notebook on
[**C**olab](https://colab.research.google.com),
[**D**eepnote](https://deepnote.com),
[**M**ybinder](https://mybinder.org),
[**S**agemaker](https://studiolab.sagemaker.aws/), or
[**N**BViewer](https://nbviewer.jupyter.org/).
For each notebook you can hover on the title to see a description, or click the title to view on github, or click one of the letters in the left column to open the notebook on
[**co**lab](https://colab.research.google.com) or
[**nb**viewer](https://nbviewer.jupyter.org/).
{format_notebooks()}