Sunday, 24 April 2011

Python Challenge #008

Python Challenge #007 - oxygen

Have to use command line tool Image Magick.

>>> import subprocess
>>> subprocess.check_call(['convert', 'oxygen.png', 'oxy.txt'])
0

Read in the Image Magick text file.
>>> with open('oxy.txt') as f:
...   lines = f.readlines()
... 


Parse file header.
>>> m = re.match(r'# ImageMagick pixel enumeration:\s*?(\d+),(\d+),.*', lines.pop(0))
>>> w, h = [int(g) for g in m.groups()]
>>> 
>>> print('Dims: ', w, 'x', h)
Dims:  629 x 95


Read in pixel data.
>>> 
>>> p = re.compile(r'(\d+),(\d+): \(\s*?(\d+),\s*?(\d+),\s*?(\d+),.*')
>>> pixdata = []
>>> for i in range(h):
...   pixdata.append([0] * w)
...   for j in range(w):
...     m = p.match(lines[i * w + j])
...     x, y, r, g, b = [int(g) for g in m.groups()]
...     pixdata[i][j] = (r, g, b)
...
>>> s = ''.join(chr(pixdata[48][j][0]) for j in range(0, 608, 7))
>>> print(s)
smart guy, you made it. the next level is [105, 110, 116, 101, 103, 114, 105, 116, 121]
>>> print(''.join(chr(int(n)) for n in s[43:-1].replace(',', '').split()))
integrity


Python Challenge #006 - channel

The zip is a clue. The file can be opened with the zipfiles library.
>>> import re
>>> import sys
>>> import zipfile
>>> n = '90052'
>>> history = []
>>> f = zipfile.ZipFile("channel.zip")
>>> print(f.read(n + '.txt'))
b'Next nothing is 94191'
>>> print(f.getinfo(n + '.txt').comment)
b'*'

This function follows the nothings through the zipped files.
>>> def next_zip(n):
...   history.append(n)
...   text = f.read(n + '.txt').decode()
...   m = re.search('\d+', text)
...   if m is None: return m
...   n = m.group()
...   return n
...

Now collate the file comments in the zip archive in the order of the nothings.
>>> while n: n = next_zip(n)
... 
>>> print(''.join(f.getinfo(n + '.txt').comment.decode() for n in history))
****************************************************************
****************************************************************
**                                                            **
**   OO    OO    XX      YYYY    GG    GG  EEEEEE NN      NN  **
**   OO    OO  XXXXXX   YYYYYY   GG   GG   EEEEEE  NN    NN   **
**   OO    OO XXX  XXX YYY   YY  GG GG     EE       NN  NN    **
**   OOOOOOOO XX    XX YY        GGG       EEEEE     NNNN     **
**   OOOOOOOO XX    XX YY        GGG       EEEEE      NN      **
**   OO    OO XXX  XXX YYY   YY  GG GG     EE         NN      **
**   OO    OO  XXXXXX   YYYYYY   GG   GG   EEEEEE     NN      **
**   OO    OO    XX      YYYY    GG    GG  EEEEEE     NN      **
**                                                            **
****************************************************************
 **************************************************************

Python Challenge #005 - peak

Peak hell? What does that sound like? The page source links to banner.p, so let's have a look at it.
>>> from urllib.request import urlopen
>>> url = 'http://www.pythonchallenge.com/pc/def/banner.p'
>>> text = urlopen(url).read()
>>> text[:20]
b"(lp0\n(lp1\n(S' '\np2\nI"

This is a streamed encoding called Pickle.
>>> import pickle
>>> data = pickle.loads(text)
>>> len(data)
23
>>> from pprint import pprint
>>> pprint(data[:5])
[[(' ', 95)],
 [(' ', 14), ('#', 5), (' ', 70), ('#', 5), (' ', 1)],
 [(' ', 15), ('#', 4), (' ', 71), ('#', 4), (' ', 1)],
 [(' ', 15), ('#', 4), (' ', 71), ('#', 4), (' ', 1)],
 [(' ', 15), ('#', 4), (' ', 71), ('#', 4), (' ', 1)]]


This is numbers and types of characters to print on each row like a Unix Banner.
>>> for row in data:
...   print(''.join(pair[0] * pair[1] for pair in row))
... 
                                                                                               
              #####                                                                      ##### 
               ####                                                                       #### 
               ####                                                                       #### 
               ####                                                                       #### 
               ####                                                                       #### 
               ####                                                                       #### 
               ####                                                                       #### 
               ####                                                                       #### 
      ###      ####   ###         ###       #####   ###    #####   ###          ###       #### 
   ###   ##    #### #######     ##  ###      #### #######   #### #######     ###  ###     #### 
  ###     ###  #####    ####   ###   ####    #####    ####  #####    ####   ###     ###   #### 
 ###           ####     ####   ###    ###    ####     ####  ####     ####  ###      ####  #### 
 ###           ####     ####          ###    ####     ####  ####     ####  ###       ###  #### 
####           ####     ####     ##   ###    ####     ####  ####     #### ####       ###  #### 
####           ####     ####   ##########    ####     ####  ####     #### ##############  #### 
####           ####     ####  ###    ####    ####     ####  ####     #### ####            #### 
####           ####     #### ####     ###    ####     ####  ####     #### ####            #### 
 ###           ####     #### ####     ###    ####     ####  ####     ####  ###            #### 
  ###      ##  ####     ####  ###    ####    ####     ####  ####     ####   ###      ##   #### 
   ###    ##   ####     ####   ###########   ####     ####  ####     ####    ###    ##    #### 
      ###     ######    #####    ##    #### ######    ###########    #####      ###      ######
                                                                                               
>>> 


Python Challenge #004 - linkedlist

Clicking the image leads to linkedlist.php?nothing=12345, which suggests replacing the value at the end of the URL. Let's define a function to get the next value.
>>> import sys
>>> from urllib.request import urlopen
>>> BASE_URL = 'http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing='
>>> def next_nothing(n):
...   text = urlopen(BASE_URL + n).read().decode()
...   m = re.match('and the next nothing is ([0-9]+)', text)
...   if not m: print('\n\n' + text + '\n')
...   n = m.group(1)
...   sys.stdout.write(n + ', '); sys.stdout.flush()
...   return n
... 

Note that this will raise an AttributeError when the regular expression fails to match and m.group is called when m is None.
>>> n = '12345'
>>> while True: n = next_nothing(n)
... 
92512, 64505, ...

Feed in the next value and repeat as necessary.
>>> n = '50010'
>>> while True: n = next_nothing(n)
... 
29193, 89924, ...

Saturday, 23 April 2011

Python Challenge #003 - equality

The bodyguards seem to refer to capital letters. Use regular expressions to parse.
>>> import re
>>> text = urlopen('http://www.pythonchallenge.com/pc/def/ocr.html').read()
>>> text = text.decode()
>>> print(''.join(re.findall('[^A-Z][A-Z]{3}([a-z])[A-Z]{3}[^A-Z]', text)))
linkedlist

Python Challenge #002 - ocr

If urlopen is already imported, the first statement won't do much.
>>> from urllib.request import urlopen
>>> url = 'http://www.pythonchallenge.com/pc/def/ocr.html'
>>> text = urlopen(url).read()
Chop the interesting characters from the html.
>>> start = text.index(b'%%')
>>> stop = start + text[start:].index(b'-->') 
>>> chars = text[start:stop]

Find the histogram of letter counts and any lower case ASCII characters.
>>> hist = {}
>>> t = []
>>> for c in chars:
...   if c in hist:
...     hist[c] += 1
...   else:
...     hist[c] = 1
...   if c > 96 and c < 123:
...     t.append(c)
... 
Use pprint to format standard output.
>>> from pprint import pprint
>>> pprint(hist)
{10: 1220,
 33: 6079,
 35: 6115,
 36: 6046,
 37: 6104,
 38: 6043,
 40: 6154,
 41: 6186,
 42: 6034,
 43: 6066,
 64: 6157,
 91: 6108,
 93: 6152,
 94: 6030,
 95: 6112,
 97: 1,
 101: 1,
 105: 1,
 108: 1,
 113: 1,
 116: 1,
 117: 1,
 121: 1,
 123: 6046,
 125: 6105}
>>> t
[101, 113, 117, 97, 108, 105, 116, 121]
>>> ''.join(chr(c) for c in t)
'equality'