Solutions for the Python Challenge

Show possible solutions for: Level: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24

Possible solutions for all challenges

Jump directly to: level 0, level 1, level 2, level 3, level 4, level 5, level 6, level 7, level 8, level 9, level 10, level 11, level 12, level 13, level 14, level 15, level 16, level 17, level 18, level 19, level 20, level 21, level 22, level 23, level 24

  1. Level 0

    URI:
    http://www.pythonchallenge.com/pc/def/0.html
    Log-in data:
    Username: ; password:
    Comment:
    Possible Solution:
    print 2**38

    (Download this source code.)

    Code word for next challenge:
    274877906944
  2. Level 1

    URI:
    http://www.pythonchallenge.com/pc/def/map.html
    Log-in data:
    Username: ; password:
    Comment:
    Possible Solution:
    import string
    
    original = "g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc " \
        "dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq " \
        "rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu " \
        "ynnjw ml rfc spj."
    
    table = string.maketrans(
        "abcdefghijklmnopqrstuvwxyz", "cdefghijklmnopqrstuvwxyzab"
    )
    
    print original.translate(table)

    (Download this source code.)

    Code word for next challenge:
    ocr
  3. Level 2

    URI:
    http://www.pythonchallenge.com/pc/def/ocr.html
    Log-in data:
    Username: ; password:
    Comment:
    Possible Solution:
    import re
    
    data = "" # Copy-and-paste or extract otherwise from the challenge page's
              # HTML-source; I'll leave it out here for clarity reasons.
    
    print "".join(re.findall("[A-Za-z]", data))

    (Download this source code.)

    Code word for next challenge:
    equality
  4. Level 3

    URI:
    http://www.pythonchallenge.com/pc/def/equality.html
    Log-in data:
    Username: ; password:
    Comment:
    Possible Solution:
    import re
    
    data = "" # Again left out for clarity reasons. Get the data from
              # http://www.pythonchallenge.com/pc/def/equality.html
    
    print "".join(re.findall("[^A-Z]+[A-Z]{3}([a-z])[A-Z]{3}[^A-Z]+", data))

    (Download this source code.)

    Code word for next challenge:
    linkedlist
  5. Level 4

    URI:
    http://www.pythonchallenge.com/pc/def/linkedlist.html
    Log-in data:
    Username: ; password:
    Comment:
    Possible Solution:
    import urllib, re, time
    
    uri = "http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=%s"
    nothing_rep = "and the next nothing is (\d+)"
    nothing = "12345" # You'll later be asked to change this
                      # to "46059" and re-run the script.
    
    while True:
        try:
            source = urllib.urlopen(uri % nothing).read()
            nothing = re.search(nothing_rep, source).group(1)
        except:
            break
    
        print nothing

    (Download this source code.)

    Code word for next challenge:
    peak
  6. Level 5

    URI:
    http://www.pythonchallenge.com/pc/def/peak.html
    Log-in data:
    Username: ; password:
    Comment:
    Possible Solution:
    import pickle, urllib
    
    handle = urllib.urlopen("http://www.pythonchallenge.com/pc/def/banner.p")
    data = pickle.load(handle)
    handle.close()
    
    for elt in data:
        print "".join([e[1] * e[0] for e in elt])

    (Download this source code.)

    Code word for next challenge:
    channel
  7. Level 6

    URI:
    http://www.pythonchallenge.com/pc/def/channel.html
    Log-in data:
    Username: ; password:
    Comment:
    Possible Solution:
    import urllib, zipfile, re, collections
    
    o, n, f = [], "90052", "%s.txt"
    nnr = "Next nothing is (\d+)"
    
    # Download the ZIP file from http://www.pythonchallenge.com/pc/def/channel.zip
    
    file = zipfile.ZipFile("channel.zip")
    
    while True:
        try:
            n = re.search(nnr, file.read(f % n)).group(1)
        except:
            print file.read(f % n)
            break
    
        o.append(file.getinfo(f % n).comment)
    
    print "".join(o)

    (Download this source code.)

    Code word for next challenge:
    oxygen
  8. Level 7

    URI:
    http://www.pythonchallenge.com/pc/def/oxygen.html
    Log-in data:
    Username: ; password:
    Comment:
    This challenge is the first in a row of (way too many, in my opinion) challenges that require the Python Imaging Library (PIL). If you chose to solve these challenges with Python, that is. I suppose even ImageMagick and a shell script would do the job in some cases.
    Possible Solution:
    import re, Image
    
    i = Image.open("oxygen.png") # http://www.pythonchallenge.com/pc/def/oxygen.png
    row = [i.getpixel((x, 45)) for x in range(0, i.size[0], 7)]
    ords = [r for r, g, b, a in row if r == g == b]
    
    print "".join(map(chr, map(int, re.findall("\d+", "".join(map(chr, ords))))))

    (Download this source code.)

    Code word for next challenge:
    integrity
  9. Level 8

    URI:
    http://www.pythonchallenge.com/pc/def/integrity.html
    Log-in data:
    Username: ; password:
    Comment:
    Possible Solution:
    import bz2
    
    un = "BZh91AY&SYA\xaf\x82\r\x00\x00\x01\x01\x80\x02\xc0\x02\x00 \x00!" \
         "\x9ah3M\x07<]\xc9\x14\xe1BA\x06\xbe\x084"
    pw = "BZh91AY&SY\x94$|\x0e\x00\x00\x00\x81\x00\x03$ \x00!" \
         "\x9ah3M\x13<]\xc9\x14\xe1BBP\x91\xf08"
    
    print [bz2.decompress(elt) for elt in (un, pw)]

    (Download this source code.)

    Code word for next challenge:
  10. Level 9

    URI:
    http://www.pythonchallenge.com/pc/return/good.html
    Log-in data:
    Username: huge; password: file
    Comment:
    Possible Solution:
    import Image
    
    # Get this from the challenge page's HTML source:
    
    first = (
        146, 399, 163, 403, 170, 393, 169, 391, 166, 386, 170, 381, 170, 371, 170,
        355, 169, 346, 167, 335, 170, 329, 170, 320, 170, 310, 171, 301, 173, 290,
        178, 289, 182, 287, 188, 286, 190, 286, 192, 291, 194, 296, 195, 305, 194,
        307, 191, 312, 190, 316, 190, 321, 192, 331, 193, 338, 196, 341, 197, 346,
        199, 352, 198, 360, 197, 366, 197, 373, 196, 380, 197, 383, 196, 387, 192,
        389, 191, 392, 190, 396, 189, 400, 194, 401, 201, 402, 208, 403, 213, 402,
        216, 401, 219, 397, 219, 393, 216, 390, 215, 385, 215, 379, 213, 373, 213,
        365, 212, 360, 210, 353, 210, 347, 212, 338, 213, 329, 214, 319, 215, 311,
        215, 306, 216, 296, 218, 290, 221, 283, 225, 282, 233, 284, 238, 287, 243,
        290, 250, 291, 255, 294, 261, 293, 265, 291, 271, 291, 273, 289, 278, 287,
        279, 285, 281, 280, 284, 278, 284, 276, 287, 277, 289, 283, 291, 286, 294,
        291, 296, 295, 299, 300, 301, 304, 304, 320, 305, 327, 306, 332, 307, 341,
        306, 349, 303, 354, 301, 364, 301, 371, 297, 375, 292, 384, 291, 386, 302,
        393, 324, 391, 333, 387, 328, 375, 329, 367, 329, 353, 330, 341, 331, 328,
        336, 319, 338, 310, 341, 304, 341, 285, 341, 278, 343, 269, 344, 262, 346,
        259, 346, 251, 349, 259, 349, 264, 349, 273, 349, 280, 349, 288, 349, 295,
        349, 298, 354, 293, 356, 286, 354, 279, 352, 268, 352, 257, 351, 249, 350,
        234, 351, 211, 352, 197, 354, 185, 353, 171, 351, 154, 348, 147, 342, 137,
        339, 132, 330, 122, 327, 120, 314, 116, 304, 117, 293, 118, 284, 118, 281,
        122, 275, 128, 265, 129, 257, 131, 244, 133, 239, 134, 228, 136, 221, 137,
        214, 138, 209, 135, 201, 132, 192, 130, 184, 131, 175, 129, 170, 131, 159,
        134, 157, 134, 160, 130, 170, 125, 176, 114, 176, 102, 173, 103, 172, 108,
        171, 111, 163, 115, 156, 116, 149, 117, 142, 116, 136, 115, 129, 115, 124,
        115, 120, 115, 115, 117, 113, 120, 109, 122, 102, 122, 100, 121,  95, 121,
         89, 115,  87, 110,  82, 109,  84, 118,  89, 123,  93, 129, 100, 130, 108,
        132, 110, 133, 110, 136, 107, 138, 105, 140,  95, 138,  86, 141,  79, 149,
         77, 155,  81, 162,  90, 165,  97, 167,  99, 171, 109, 171, 107, 161, 111,
        156, 113, 170, 115, 185, 118, 208, 117, 223, 121, 239, 128, 251, 133, 259,
        136, 266, 139, 276, 143, 290, 148, 310, 151, 332, 155, 348, 156, 353, 153,
        366, 149, 379, 147, 394, 146, 399
    )
    
    second = (
        156, 141, 165, 135, 169, 131, 176, 130, 187, 134, 191, 140, 191, 146, 186,
        150, 179, 155, 175, 157, 168, 157, 163, 157, 159, 157, 158, 164, 159, 175,
        159, 181, 157, 191, 154, 197, 153, 205, 153, 210, 152, 212, 147, 215, 146,
        218, 143, 220, 132, 220, 125, 217, 119, 209, 116, 196, 115, 185, 114, 172,
        114, 167, 112, 161, 109, 165, 107, 170,  99, 171,  97, 167,  89, 164,  81,
        162,  77, 155,  81, 148,  87, 140,  96, 138, 105, 141, 110, 136, 111, 126,
        113, 129, 118, 117, 128, 114, 137, 115, 146, 114, 155, 115, 158, 121, 157,
        128, 156, 134, 157, 136, 156, 136
    )
    
    first, second = map(
        lambda seq: [seq[i:i + 2] for i in range(0, len(seq), 2)],
        (first, second)
    )
    
    i = Image.open("good.jpg") # http://www.pythonchallenge.com/pc/return/good.jpg
    new = Image.new(i.mode, i.size)
    
    for elt in (first, second):
        for x, y in elt:
            new.putpixel((x, y), (255, 255, 255))
        
    new.save("new.jpg")

    (Download this source code.)

    Code word for next challenge:
    bull
  11. Level 10

    URI:
    http://www.pythonchallenge.com/pc/return/bull.html
    Log-in data:
    Username: huge; password: file
    Comment:
    Possible Solution:
    import itertools
    
    def look_and_say (length):
        table = {
            ("1", "1", "1"): "31",
            ("1", "1"): "21",
            ("1", ): "11",
            ("2", "2", "2"): "32",
            ("2", "2"): "22",
            ("2", ): "12",
            ("3", "3", "3"): "33",
            ("3", "3"): "23",
            ("3", ): "13"
        }
    
        prec, result = "1", [1]
    
        for i in xrange(length - 1):
            prec = "".join(table[tuple(l)] for e, l in itertools.groupby(prec))
            result.append(int(prec))
    
        return result
    
    print len(str(look_and_say(31)[30]))

    (Download this source code.)

    Code word for next challenge:
    5808
  12. Level 11

    URI:
    http://www.pythonchallenge.com/pc/return/5808.html
    Log-in data:
    Username: huge; password: file
    Comment:
    Possible Solution:
    import Image
    
    # download the image from: http://www.pythonchallenge.com/pc/return/cave.jpg
    
    image = Image.open("cave.jpg")
    nsize = tuple([x / 2 for x in image.size])
    odd = even = Image.new(image.mode, nsize)
    
    for x in range(image.size[0]):
        for y in range(image.size[1]):
            if x % 2 == 0 and y % 2 == 0:
                even.putpixel((x / 2, y / 2), image.getpixel((x, y)))
            elif x % 2 == 0 and y % 2 == 1:
                odd.putpixel((x / 2, (y - 1) / 2), image.getpixel((x, y)))
            elif x % 2 == 1 and y % 2 == 0:
                even.putpixel(((x - 1) / 2, y / 2), image.getpixel((x, y)))
            else:
                odd.putpixel(((x - 1) / 2, (y - 1) / 2), image.getpixel((x, y)))
    
    even.save("even.jpg")
    odd.save("odd.jpg")

    (Download this source code.)

    Code word for next challenge:
    evil
  13. Level 12

    URI:
    http://www.pythonchallenge.com/pc/return/evil.html
    Log-in data:
    Username: huge; password: file
    Comment:
    Possible Solution:
    # Get the data from: http://www.pythonchallenge.com/pc/return/evil2.gfx
    
    h = open("evil2.gfx", "rb")
    data = h.read()
    h.close()
    
    new_data = [[], [], [], [], []]
    n = 0
    
    for byte in range(len(data) - 1):
        new_data[n].append(data[byte])
        n = 0 if n == 4 else n + 1
    
    for n, elt in enumerate(new_data):
        h = open(str(n + 1), "wb")
        h.write("".join(elt))
        h.close()

    (Download this source code.)

    Code word for next challenge:
    disproportional
  14. Level 13

    URI:
    http://www.pythonchallenge.com/pc/return/disproportional.html
    Log-in data:
    Username: huge; password: file
    Comment:
    Possible Solution:
    import xmlrpclib
    
    conn = xmlrpclib.ServerProxy("http://www.pythonchallenge.com/pc/phonebook.php")
    
    print conn.phone("Bert")

    (Download this source code.)

    Code word for next challenge:
    italy
  15. Level 14

    URI:
    http://www.pythonchallenge.com/pc/return/italy.html
    Log-in data:
    Username: huge; password: file
    Comment:
    Possible Solution:
    import Image, math
    
    def get_pixel(t):
        t = (100 * 100 - 1) - t
        shell = int((math.sqrt(t) + 1) / 2)
        leg = 0 if (shell == 0) else int((t - (2 * shell - 1) ** 2) / 2 / shell)
        elt = t - (2 * shell - 1) ** 2 - 2 * shell * leg - shell + 1
        
        if leg == 0:
            x, y = shell, elt
        elif leg == 1:
            x, y = -elt, shell
        elif leg == 2:
            x, y = -shell, -elt
        else:
            x, y = elt, -shell
    
        return (49 + x, 50 - y)
    
    # Download the image from: http://www.pythonchallenge.com/pc/return/wire.png
    
    input = Image.open("wire.png")
    output = Image.new("RGB", (100, 100))
    
    for i, px in enumerate(input.getdata()):
        output.putpixel(get_pixel(i), px)
    
    output.save("new.jpg")

    (Download this source code.)

    Code word for next challenge:
    cat
  16. Level 15

    URI:
    http://www.pythonchallenge.com/pc/return/uzi.html
    Log-in data:
    Username: huge; password: file
    Comment:
    This one’s rather a Google/Wikipedia-research challenge than it is one in programming. However, January 27th, 1756, is the birthday of Mozart, and this is how you get to the year in the first place:
    Possible Solution:
    import calendar
    
    print [i for i in range(1006, 2006, 10) if calendar.weekday(i, 1, 1) == 3][-3]

    (Download this source code.)

    Code word for next challenge:
    mozart
  17. Level 16

    URI:
    http://www.pythonchallenge.com/pc/return/mozart.html
    Log-in data:
    Username: huge; password: file
    Comment:
    Possible Solution:
    import Image, ImageChops
    
    # Download the image from: http://www.pythonchallenge.com/pc/return/mozart.gif
    
    image = Image.open("mozart.gif")
    magic = chr(195)
    
    for y in range(image.size[1]):
        box = 0, y, image.size[0], y + 1
        row = image.crop(box)
        bytes = row.tostring()
        i = bytes.index(magic)
        row = ImageChops.offset(row, -i)
        image.paste(row, box)
        
    image.save("new.gif")

    (Download this source code.)

    Code word for next challenge:
    romance
  18. Level 17

    URI:
    http://www.pythonchallenge.com/pc/return/romance.html
    Log-in data:
    Username: huge; password: file
    Comment:
    Possible Solution:
    import re, urllib, urllib2, bz2, xmlrpclib
    
    # First of all, get the data that is hidden within the "Set-Cookie" headers:
    
    uri = "http://www.pythonchallenge.com/pc/def/linkedlist.php?busynothing=%s"
    nn_rep = re.compile("the next busynothing is (\d+)")
    cookie_val = re.compile("info=([^;]+);")
    result = []
    n = "12345"
    
    while True:
        h = urllib.urlopen(uri % n)
        next = h.read()
        cookie = h.info().getheader("Set-Cookie")
        h.close()
    
        cval = cookie_val.search(cookie)
    
        if cookie and cval:
            result.append(urllib.unquote_plus(cval.group(1)))
    
        try:
            n = nn_rep.search(next).group(1)
        except:
            break
    
    print bz2.decompress("".join(result)
    
    # Get Leopold's phone numer using the code for level 13:
    
    conn = xmlrpclib.ServerProxy("http://www.pythonchallenge.com/pc/phonebook.php")
    
    print conn.phone("Leopold")
    
    # Now phone Mozart's father and tell him that "the flowers are on their way":
    
    uri = "http://www.pythonchallenge.com/pc/stuff/violin.php"
    msg = "the flowers are on their way"
    req = urllib2.Request(
        uri, headers = { "Cookie": "info=" + urllib.quote_plus(msg)}
    )
    
    print urllib2.urlopen(req).read()

    (Download this source code.)

    Code word for next challenge:
    balloons
  19. Level 18

    URI:
    http://www.pythonchallenge.com/pc/return/balloons.html
    Log-in data:
    Username: huge; password: file
    Comment:
    Possible Solution:
    import gzip, difflib
    
    # Download the GNU zip file from:
    # http://www.pythonchallenge.com/pc/return/deltas.gz
    
    h = gzip.open("deltas.gz")
    d = difflib.Differ()
    
    part_1, part_2 = [], []
    file_1, file_2, file_3 = [], [], []
    
    for line in h:
        part_1.append(line[0:53])
        part_2.append(line[56:-1])
    
    h.close()
    
    for line in list(d.compare(part_1, part_2)):
        if line[0] == "+":
            file_1.append(line[2:])
        elif line[0] == "-":
            file_2.append(line[2:])
        else:
            file_3.append(line[2:])
    
    for n, data in enumerate((file_1, file_2, file_3)):
        temp = []
    
        for line in data:
            temp.extend([chr(int(o, 16)) for o in line.strip().split(" ") if o])
    
        h = open("%s.png" % (n + 1), "wb")
        h.writelines(temp)
        h.close()
    
        # The third file is corrupt and won't be properly displayed by most image
        # viewers. It should work in Gecko-based browsers such as Firefox, though.

    (Download this source code.)

    Code word for next challenge:
    ../hex/bin
  20. Level 19

    URI:
    http://www.pythonchallenge.com/pc/hex/bin.html
    Log-in data:
    Username: butter; password: fly
    Comment:
    Possible Solution:
    import base64, wave, array
    
    # Input ...
    
    h = open("data")
    a = array.array("c", base64.b64decode(h.read()))
    a.byteswap()
    h.close()
    
    # ... Processing ...
    
    h = wave.open("modified.wav", "wb")
    h.setnchannels(1)
    h.setsampwidth(1)
    h.setframerate(22050)
    
    # ... Output
    
    h.writeframes(a.tostring())
    h.close()

    (Download this source code.)

    Code word for next challenge:
    idiot
  21. Level 20

    URI:
    http://www.pythonchallenge.com/pc/hex/idiot2.html
    Log-in data:
    Username: butter; password: fly
    Comment:
    Yes, the ZIP file you eventually get actually is level 21.
    Possible Solution:
    import httplib, base64
    
    base64_login = base64.encodestring('%s:%s' % ("butter", "fly"))[:-1]
    headers = {"Authorization": "Basic %s" % base64_login}
    conn = httplib.HTTPConnection("www.pythonchallenge.com")
    
    # Needless to say that normally we wouldn't know about the exact byte
    # ranges yet and thus probably use infinite loops instead ...
    
    for n in range(30203, 30314):
        headers["Range"] = "bytes=%s-%s" % (n, n + 1)
        conn.request("GET", "/pc/hex/unreal.jpg", "", headers)
        response = conn.getresponse()
        data = response.read()
    
        if data:
            print data
    
    # We now know that our username is "invader".
    
    for n in (2123456744, 2123456743):
        headers["Range"] = "bytes=%s-%s" % (n, n + 1)
        conn.request("GET", "/pc/hex/unreal.jpg", "", headers)
        response = conn.getresponse()
    
        print response.read()
    
    # We learned that "the password is your new nickname in reverse", thus:
    # "redavni". Further, that "it is hiding at 1152983631".
    
    headers["Range"] = "bytes=1152983631-1152983632"
    conn.request("GET", "/pc/hex/unreal.jpg", "", headers)
    response = conn.getresponse()
    
    h = open("data.zip", "wb")
    h.write(response.read())
    h.close()
    
    # Unzip the file and read the "readme.txt" it contains.

    (Download this source code.)

    Code word for next challenge:
  22. Level 21

    URI:
    Log-in data:
    Username: butter; password: fly
    Comment:
    My first grey hair I will name “level 21”. Hated it.
    Possible Solution:
    import zlib, bz2
    
    h = open("package.pack") # Hides within the ZIP file we got at the
    data = h.read()          # end of level 20.
    h.close()
    
    output = []
    
    while True:
        if data.startswith("BZh"):
            data = bz2.decompress(data)
            output.append("#")
        elif data.startswith("x\x9c"):
            data = zlib.decompress(data)
            output.append(" ")
        elif data.endswith("\x9cx"):
            data = data[::-1]
            output.append("\n")
        else:
            break
    
    print "".join(output)

    (Download this source code.)

    Code word for next challenge:
    copper
  23. Level 22

    URI:
    http://www.pythonchallenge.com/pc/hex/copper.html
    Log-in data:
    Username: butter; password: fly
    Comment:
    Possible Solution:
    import Image, ImageSequence
    
    img = Image.open("white.gif") # http://www.pythonchallenge.com/pc/hex/white.gif
    out = Image.new("P", (125, 125))
    pix = out.load()
    pos = [25, 25]
    
    for x in [list(f.getdata()).index(8) for f in ImageSequence.Iterator(img)]:
        if x == 19698:
            pos[0] -= 1
            pos[1] -= 1
        elif x == 19700:
            pos[1] -= 1
        elif x == 19702:
            pos[0] += 1
            pos[1] -= 1
        elif x == 20098:
            pos[0] -= 1
        elif x == 20100:
            pos = [pos[0] + 10, pos[1] + 10]
        elif x == 20102:
            pos[0] += 1
        elif x == 20498:
            pos[0] -= 1
            pos[1] += 1
        elif x == 20500:
            pos[1] += 1
        elif x == 20502:
            pos[0] += 1
            pos[1] += 1
    
        pix[pos[0], pos[1]] = 200
    
    out.save("solution.png")

    (Download this source code.)

    Code word for next challenge:
    bonus
  24. Level 23

    URI:
    http://www.pythonchallenge.com/pc/hex/bonus.html
    Log-in data:
    Username: butter; password: fly
    Comment:
    Obviously, not much of a programming challenge again.
    Possible Solution:
    import this

    (Download this source code.)

    Code word for next challenge:
    ambiguity
  25. Level 24

    URI:
    http://www.pythonchallenge.com/pc/hex/ambiguity.html
    Log-in data:
    Username: butter; password: fly
    Comment:
    Instead of inventing the wheel twice, I decided to use the A* algorithm as implemented by John Eriksson for the pygame AStar library. Naturally, there are much shorter ways to solve this challenge.
    Possible Solution:
    # Copyright for the following five classes by John Eriksson
    # <http://arainyday.se/>, 2006. Originally written for the AStar
    # library <http://www.pygame.org/projects/9/195/> and released into the
    # public domain. Thanks a lot!
    
    
    import Image
    
    
    class Path:
        def __init__ (self, nodes, totalCost):
            self.nodes = nodes
            self.totalCost = totalCost
    
        def getNodes (self):
            return self.nodes
    
        def getTotalMoveCost (self):
            return self.totalCost
    
    
    class Node:
        def __init__ (self, location, mCost, lid, parent = None):
            self.location = location
            self.mCost = mCost
            self.parent = parent
            self.score = 0
            self.lid = lid
    
        def __eq__ (self, n):
            if n.lid == self.lid:
                return 1
            else:
                return 0
    
    
    class AStar:
        def __init__ (self, maphandler):
            self.mh = maphandler
    
        def _getBestOpenNode (self):
            bestNode = None
    
            for n in self.on:
                if not bestNode:
                    bestNode = n
                elif n.score <= bestNode.score:
                    bestNode = n
    
            return bestNode
    
        def _tracePath (self, n):
            nodes = []
            totalCost = n.mCost
            p = n.parent
            nodes.insert(0, n)
    
            while True:
                if p.parent is None:
                    break
    
                nodes.insert(0, p)
                p = p.parent
    
            return Path(nodes, totalCost)
    
        def _handleNode (self, node, end):
            i = self.o.index(node.lid)
            self.on.pop(i)
            self.o.pop(i)
            self.c.append(node.lid)
    
            nodes = self.mh.getAdjacentNodes(node, end)
    
            for n in nodes:
                if n.location == end:
                    return n
                elif n.lid in self.c:
                    continue
                elif n.lid in self.o:
                    i = self.o.index(n.lid)
                    on = self.on[i];
                    if n.mCost < on.mCost:
                        self.on.pop(i);
                        self.o.pop(i);
                        self.on.append(n);
                        self.o.append(n.lid);
                else:
                    self.on.append(n);
                    self.o.append(n.lid);
    
            return None
    
        def findPath (self, fromlocation, tolocation):
            self.o = []
            self.on = []
            self.c = []
    
            end = tolocation
            fnode = self.mh.getNode(fromlocation)
            self.on.append(fnode)
            self.o.append(fnode.lid)
            nextNode = fnode
    
            while nextNode is not None:
                finish = self._handleNode(nextNode, end)
    
                if finish:
                    return self._tracePath(finish)
    
                nextNode=self._getBestOpenNode()
    
            return None
    
    
    class SQ_Location:
        def __init__ (self, x, y):
            self.x = x
            self.y = y
    
        def __eq__ (self, l):
            if l.x == self.x and l.y == self.y:
                return 1
            else:
                return 0
    
    
    class SQ_MapHandler:
        def __init__ (self, mapdata, width, height):
            self.m = mapdata
            self.w = width
            self.h = height
    
        def getNode (self, location):
            x = location.x
            y = location.y
    
            if x < 0 or x >= self.w or y < 0 or y >= self.h:
                return None
    
            d = self.m[(y * self.w) + x]
    
            if d == -1:
                return None
    
            return Node(location, d, ((y * self.w) + x));
    
        def getAdjacentNodes (self, curnode, dest):
            result = []
            cl = curnode.location
            dl = dest
            n = self._handleNode(cl.x + 1, cl.y, curnode, dl.x, dl.y)
    
            if n:
                result.append(n)
    
            n = self._handleNode(cl.x - 1, cl.y, curnode, dl.x, dl.y)
    
            if n:
                result.append(n)
    
            n = self._handleNode(cl.x, cl.y + 1, curnode, dl.x, dl.y)
    
            if n:
                result.append(n)
    
            n = self._handleNode(cl.x, cl.y - 1, curnode, dl.x, dl.y)
    
            if n:
                result.append(n)
    
            return result
    
        def _handleNode (self, x, y, fromnode, destx, desty):
            n = self.getNode(SQ_Location(x, y))
    
            if n is not None:
                dx = max(x, destx) - min(x, destx)
                dy = max(y, desty) - min(y, desty)
                emCost = dx+dy
                n.mCost += fromnode.mCost
                n.score = n.mCost + emCost
                n.parent = fromnode
    
                return n
    
            return None
    
    
    def main ():
        img = Image.open("maze.png")
        maze = img.load()
        mapdata = []
    
        # Translate pixel data into something that AStar understands.
    
        for elt in img.getdata():
            if elt == (255, 255, 255, 255):
                mapdata.append(-1)
            else:
                mapdata.append(1)
    
        # Define start and destination points.
    
        mapdata[639] = 5
        mapdata[410241] = 6
    
        astar = AStar(SQ_MapHandler(mapdata, 641, 641))
        start = SQ_Location(639, 0)
        end = SQ_Location(1, 640)
        p = astar.findPath(start, end)
        data = []
    
        # Extract data from "logs".
    
        for node in p.nodes:
            if node.location.x % 2 and node.location.y % 2:
                data.append(chr(maze[node.location.x, node.location.y][0]))
    
        h = open("unzip-me.zip", "wb")
        h.write("".join(data))
        h.close()
    
    
    if __name__ == "__main__":
        main()

    (Download this source code.)

    Code word for next challenge:
    lake

By Holger Thölking, May 29th, 2007.