Hacking Pi with PowerShell
A Facebook friend recently posted a cool picture from the Pi chain that California Institute of Technology created on Pi Day, 2013:
After seeing that, you might wonder – “Where in Pi is that?” And, “What number does each colour represent?” PowerShell can help here – its support for regular expressions let you find all kinds of stuff in text. But where do you find the text of Pi? Bing, of course. After copy + pasting the first 100,000 digits of Pi from into a text file, you now have some text to work with.
If you want to see some of the things you can do with Regular Expressions, check out this quick reference in the PowerShell Cookbook: http://www.powershellcookbook.com/recipe/qAxK/appendix-b-regular-expression-reference.
If you assume that the chain links are 4 inches wide, 100,000 digits will go for almost 7 miles. It’s doubtful that they made a chain longer than that, so using 100,000 digits is pretty safe.
PS C:\Users\Lee> $page = iwr http://www.geom.uiuc.edu/~huberty/math5337/groupe/digits.html
PS C:\Users\Lee> $null = "$page" -match '(\d|\.|\n){5,}'
PS C:\Users\Lee> $digits = $matches[0] -replace "\s",""
PS C:\Users\Lee> -join $digits[0..100]
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067
Now, since we know that each colour represents a different number, and those colours appear in a certain pattern, we can look in $digits for that pattern. What we see – 15 colours: Yellow, Yellow, Blue, (Orange?) Green, Blue, Yellow, (Red?), Purple, Purple, Orange, Pink, Red, Yellow, Yellow Now, here’s the regular expression to match 15 different characters (15 dots):
$digits –match "................"
It does, of course (I hear Pi goes on for a while), so let’s get more specific. Regular expressions let you group characters by surrounding them in parenthesis:
$digits –match "(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)"
And also give them names. Let’s name the first group:
$digits –match "(?<Yellow>.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)"
And rather than using the “dot” to match a specific character, you can refer to a previous group – by either name or number. Here’s the chunk that names the first character “Yellow”, and then says that the second character must be the same as the first:
$regex = "(?<Yellow>.)(\k<Yellow>)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)"
$digits | Select-String $regex -AllMatches | % { $_.Matches.Value } | Select -First 1
33832795028841
PS >
This is how we’ll “crack the code” – find patterns where the digits are the same at the same place the colours are the same. Since most of the colours aren’t repeated, they don’t help us much and we can ignore them for now. Let’s expand our example:
$regex = "(?<Yellow>.)(\k<Yellow>)" +
"(?<Blue>.)(?<Orangey>.)(<?Green>.)(\k<Blue>)(\k<Yellow>)" +
"(?<Pinkish>.)(?<Purple>.)(\k<Purple>)(?<Orange>.)(?<Pink>.)" +
"(?<Red>.)(\k<Yellow>)(\k<Yellow>)"
$digits | Select-String $regex -AllMatches | % { $_.Matches.Value } | sort
PS >
Hrrm. No results. Let’s relax some constraints. Maybe some colours we thought matched really didn’t? With my man eyes to the rescue, those blues look subtly different.
$regex = "(?<Yellow>.)(\k<Yellow>)" +
"(?<Blue>.)(?<Orangey>.)(<?Green>.)(?<Blue2>.)(\k<Yellow>)" +
"(?<Pinkish>.)(?<Purple>.)(\k<Purple>)(?<Orange>.)(?<Pink>.)" +
"(?<Red>.)(\k<Yellow>)(\k<Yellow>)"
$digits | Select-String $regex -AllMatches | % { $_.Matches.Value } | sort
PS >
Grr! No results again. Maybe the yellows are different? There are three groups. The first group of two is certainly the same colour. The last group of two is certainly the same colour. But maybe those groups are different yellows. Lets see – call the other ones Yellow2 and Yellow3:
$regex = "(?<Yellow>.)(\k<Yellow>)" +
"(?<Blue>.)(?<Orangey>.)(<?Green>.)(?<Blue2>.)(?<Yellow2>.)" +
"(?<Pinkish>.)(?<Purple>.)(\k<Purple>)(?<Orange>.)(?<Pink>.)" +
"(?<Red>.)(?<Yellow3>.)(\k<Yellow3>)"
$digits | Select-String $regex -AllMatches | % { $_.Matches.Value } | sort
PS >
Hmm. This doesn’t seem to be working out so well.
Brain flash.
Oh wait, this is just a picture. What if it was taken from the other side? The colours would be reversed. Maybe we’re looking at the digits of Pi in the wrong order? Let’s rewrite the regex since we might actually be looking at: Yellow, Yellow, Red, Pink, Orange, Purple, Purple, (Red?), Yellow, Blue, (Orange?) Green, Blue, Yellow,Yellow
$regex = "(?<Yellow3>.)(\k<Yellow3>)" +
"(?<Red>.)(?<Pink>.)(?<Orange>.)(?<Purple>.)(\k<Purple>)" +
"(?<Pinkish>.)(?<Yellow2>.)(?<Blue2>.)(?<Green>.)(?<Orangey>.)" +
"(?<Blue>.)(?<Yellow>.)(\k<Yellow>)"
$digits | Select-String $regex -AllMatches | % { $_.Matches.Value } | sort
000139962344455
004489917210022
006079992795411
006447771837022
006734420647477
006758838043211
008368842671022
009318804549333
112821107094422
(…)
Bingo! That’s a lot of options, so let’s add back in some things that we are more sure of – specifically, those yellows:
$regex = "(?<Yellow3>.)(\k<Yellow3>)" +
"(?<Red>.)(?<Pink>.)(?<Orange>.)(?<Purple>.)(\k<Purple>)" +
"(?<Pinkish>.)(?<Yellow3>.)(?<Blue2>.)(?<Green>.)(?<Orangey>.)" +
"(?<Blue>.)(?<Yellow2>.)(\k<Yellow2>)"
$digits | Select-String $regex -AllMatches | % { $_.Matches.Value } | sort
000139962344455
004489917210022
006079992795411
006447771837022
006734420647477
006758838043211
008368842671022
009318804549333
112821107094422
113829928776911
(…)
But that first yellow has a zero. Maybe that’s a hint?
$regex = "(?<Yellow3>0)(\k<Yellow3>)" +
"(?<Red>.)(?<Pink>.)(?<Orange>.)(?<Purple>.)(\k<Purple>)" +
"(?<Pinkish>.)(?<Yellow3>.)(?<Blue2>.)(?<Green>.)(?<Orangey>.)" +
"(?<Blue>.)(?<Yellow2>.)(\k<Yellow2>)"
$digits | Select-String $regex -AllMatches | % { $_.Matches.Value } | sort
000139962344455
004489917210022
006079992795411
006447771837022
006734420647477
006758838043211
008368842671022
009318804549333
Not very many, so we’re getting closer. Let’s take a look at the patterns and rule out the ones that don’t make sense:
000139962344455 # No, triple start digit
004489917210022 # No, second digits doubled
006079992795411 # No, has another zero ("Yellow") on the fourth digit
006447771837022 # No, has another doubling right after the red
006734420647477 # No, has colour four of colour #4, four of colour #7
006758838043211 # No, 8 means purple, and has another purple in the middle
008368842671022 # No, has another zero ("Yellow") where we thought Blue was
009318804549333 # No, has a tripled final colour
Well, evidently none of them make sense. This is truly confusing. Maybe the numbers happen after 100,000 digits of Pi? Taking a different approach, it turns out that the original California Institute of Technology page shows a few pictures where there are numbers drawn on the links. Could we use those for a key?
Let’s try this again from the start. The pictures tell us:
0 = Yellow 1 = Pink 2 = Goldenrod 3 = Green 4 = ? 5 = Light Blue 6 = Orange 7 = Purple 8 = ? 9 = Dark Blue
Wait. There’s a Goldenrod? This might help. Let’s do this match against the left-to-right version:
Yellow, Yellow, Blue, (Orange?) Green, Blue, Yellow, (Red?), Purple, Purple, Orange, Pink, Red, Yellow, Yellow
We can now substitute some numbers into the regex. Yellow and Goldenrod are pretty close, so let’s use Regex Alteration (a|b) to let it select either. The light blue is pretty clear, so let’s call that out specifically:
$regex = "(0|2)(0|2)5..5.......(0|2)(0|2)"
$digits | Select-String $regex -AllMatches | % { $_.Matches.Value } | sort
225695968815920
Wow! It seems like we have a match! Godenrod, Goldenrod, Light Blue, Orange, Dark Blue, Blue, Dark Blue, … But there are two problems:
- It implies that either 9 is Green, or that the picture showing Green is actually Dark Blue
- It says that the colour before the second blue is the same as the colour after it. That’s not the case.
Maybe they messed up assigning colours? If you follow the colour chart through further, there are almost a dozen more mistakes. Let’s go back to the big brain flash, and reverse the regex.
$regex = ".........5..5(0|2)(0|2)"
$digits | Select-String $regex -AllMatches | % { $_.Matches.Value } | sort
005476022525520
008169980536520
020695124539500
036407573587502
048466277504500
068006422512520
090988702550520
101024365553522
(…)
Tons of matches again. Let’s get more specific with those yellows:
$regex = "(0|2)(0|2)......(0|2)5..5(0|2)(0|2)"
$digits | Select-String $regex -AllMatches | % { $_.Matches.Value } | sort
005476022525520
008169980536520
Much better. Apply the process of elimination again:
005476022525520 ## Says that the red is colour 5, but also that orange and light blue are too.
008169980536520 ## Seems legit
One was way off, the other one seemed legit. If we apply our colour key, we get:
Yellow, Yellow, ?, Pink, Orange, Dark Blue, Dark Blue, Red, Yellow, Light Blue, Green, Orange, Light Blue, Goldenrod, Yellow
That seems very reasonable. The unknown colour (8) is evidently red. The two that we thought were purple were either actually dark blue, or the assembly person made a mistake. “Pinkish” was red in the sunlight. The last two yellows were actually not two yellows – they were one Goldenrod, and then one Yellow. Or the person putting them together made a mistake.
Now, we can get smart. How far along were they on the chain?
PS > $digits.IndexOf("008169980536520")
12309
Going back to our 4-inch estimate (3 of those in a foot), that’s 4103 feet:
PS > 12309/3
4103
Nearly a mile in, or perhaps near the end of the chain (which was evidently about 15,000 links).
What went wrong in the pattern-based regex?
If you take a look at the final regex we played with, we assumed that the last two were the same colour:
$regex = "(?<Yellow3>0)(\k<Yellow3>)" +
"(?<Red>.)(?<Pink>.)(?<Orange>.)(?<Purple>.)(\k<Purple>)" +
"(?<Pinkish>.)(?<Yellow3>.)(?<Blue2>.)(?<Green>.)(?<Orangey>.)" +
"(?<Blue>.)(?<Yellow2>.)(\k<Yellow2>)"
$digits | Select-String $regex -AllMatches | % { $_.Matches.Value } | sort
If we assume that “Orangey” is actually “Orange”, and say that the final two digits are either Yellow or Goldenrod, then we get the proper result after the process of elimination:
$regex = "(?<Yellow3>0)(\k<Yellow3>)" +
"(?<Red>.)(?<Pink>.)(?<Orange>.)(?<Purple>.)(\k<Purple>)" +
"(?<Pinkish>.)(?<Yellow3>.)(?<Blue2>.)(?<Green>.)(\k<Orange>)" +
"(?<Blue>.)(?<Yellow2>.)(\k<Yellow2>|\k<Yellow3>)"
$digits | Select-String $regex -AllMatches | % { $_.Matches.Value } | sort
001502298330798
>> 008169980536520 <<
009743300884990
Why do I care?
Why not spend some time messing around in Pi?