Posts Tagged ‘python challenge’

Python Challenge level 23: “what is this module?”

As I understand it, this was the former last level of the game. Level 23. In time 10 more levels were added.

Hint 1: it can’t find it. this is an undocumented module.
Hint 2: va gur snpr bs jung?

One needs Python to solve this one. No other language will do. There’s no real need to use Python, but you doo need it.
After having spent some time in the forums for hints for some of the previous level, one gets to see that ‘encrypting’ things with ROT13 is a tradition over there. And the second hint is ROT13 for ‘in the face of what?’

I didn’t think about it at first, but the author said there that the solution was to take the hints literally. The answer to the riddle can be found in “Gur Mra bs Clguba, ol Gvz Crgref”. It looks like a Czech proverb, but it isn’t. At least that I konw of. After poking into every module, the only undocoumented module -as in ‘no a single comment in it’, not even oneliners!- is the answer to our prayers. It can be found a lot faster by just grepping for ‘the face of’:

taher@borg:/usr/lib/python2.5$ grep -i "va gur snpr bs" *
this.py:Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.

Heh! ‘this module is undocumented’. Nice one. Try importing module ‘this’ in your Python interpreter. It’s amusing. The famous “proverb” is attributed to Tim Peters, who went by the name of UncleTimmy in the forums.

You’ll find the key to the next level either in the previous link or in the allready mentioned this.py.
Next is Level 24.

There’s something left in this level:

TODO: do you owe someone an apology? now it is a good time to tell him that you are sorry. Please show good manners although it has nothing to do with this level.

The only one ever insulted would have been Leopold, and since he doesn’t like cookies one would figure the second best thing is to simply drop him a line. If you apologize and he’s in good mood, you’ll get a response like this one:

From: Leopold Mozart <leopold.moz@pythonchall… >
Subject: Re: sorry
Never mind that.

Have you found my broken zip?
md5: bbb8b499a0eef99b52c7f13f4e78c24b

Can you believe what one mistake can lead to?

I guess it’ll be useful in the future. This was one easy and really nice level. If it’s the first time you hear of The Zen of Python, you should take a look. You’ll like it!

Read the rest of this entry »

Python Challenge level 22: “emulate”

Level 22 got me stuck for a while.

Hint 1: or maybe white.gif would be more bright
Hint 2: image of a joystick

I didn’t know what to do with this one untill I notices it was a GIF89, which reminded me of animated GIFs. After opening it with Gimp I saw that it had 133 frames. Now I had something. It turns out one of the pixels is slightly lighter in each frame in one of several locations. Only 9 of them, actually. Just as the number of possible positions for a joystick. The code bellow is anything but pretty. Beware!

Fact 1: We’re looking for color 8, as shown below. This can be found using im.getcolors() in every frame

#!/usr/bin/env python
import Image,ImageDraw
def get_vectors():
    # Return a list of movement vectors extracted from wiggling pixels
    im = Image.open("white.gif")
    vectors=[]
    try:
        while True:
            pix=list(im.getdata()).index(8)
            y,x=divmod(pix,200)
            v=(x-100,y-100)
            vectors.append(v)
            im.seek(im.tell()+1)
    except EOFError:
        pass # end of sequence
    return vectors

max_x, h, w = 0, 50, 250
im = Image.new('RGB', (w,h))
draw = ImageDraw.Draw(im)
src = (max_x,h//2) # (x,y)
for v in get_vectors():
    if v==(0,0):
        max_x+=30
        src = (max_x,h//2)
        continue
    dst=(src[0]+v[0],src[1]+v[1])
    max_x=max(max_x,dst[0])
    draw.line([src, dst], fill='white')
    src=dst
im.save('22.jpg')

This renders the following image:

bonus

Thats our link for next level: Level 23.

Read the rest of this entry »

Python Challenge level 21: “hidden pack”

There’s no page for this challenge. From the previous level we get a hidden file that was “hiding at 1152983631″. It’s a zipfile with a password. Luckily back in level 20 we also got a tip “esrever ni emankcin wen ruoy si drowssap eht”, and a nickname: invader.

After extracting the archive, we get a text file with the hints, and a file named package.pack

Hint 1: We used to play this game when we were kids.
Hint 2: When I had no idea what to do, I looked backwards.
Hint 3: package.pack

The .pack extension suggested something, so after trying several compression modules the winner is zlib . Once extracted the file’s size is more or less the same. Suspicious. Still unreadable. The second hint comes in handy, and it looks like the tail of the file is a reversed bzip2 header. We already have experience with that thanks to previous levels, isn’t it? Again unreadable. Again reversible. Again compressed. And the show goes on. We’ll have to go back and forth several times to extract the content.

import zlib,bz2
st=open('21_package.pack').read()
log=''
log_len=len(log)
while True:
    try: #zlib
        st=zlib.decompress(st)
        log+=' '
    except:
        try: #bzip2
            st=bz2.decompress(st)
            log+='#'
        except: #reverse
            if log_len==len(log): break
            st=st[::-1]
            print log[log_len:]
            log_len=len(log)
open('21_package.unpack','wb').write(st)

After the whole run we get to read what’s in package.pack:

look at your logs

Originaly I kept a list with the number of zlib and bzip2 cycles en each run, expecting a banner(ish) run length encoding. I noticed that the sum of cycles needed to complete each direction was allways roughly the same. That is, before I had to reverse the string to start over, the sum of bzip+zip+bzip+… was more or less 73. And that meant that the output was meant to be directly graphical, so I sticked with a simpler log version that simplifies the code in turn.

The logs in question look like:

      ###          ###      ########    ########    ##########  ########
    #######      #######    #########   #########   #########   #########
   ##     ##    ##     ##   ##      ##  ##      ##  ##          ##      ##
  ##           ##       ##  ##      ##  ##      ##  ##          ##      ##
  ##           ##       ##  #########   #########   ########    #########
  ##           ##       ##  ########    ########    ########    ########
  ##           ##       ##  ##          ##          ##          ##   ##
   ##     ##    ##     ##   ##          ##          ##          ##    ##
    #######      #######    ##          ##          #########   ##     ##
      ###          ###      ##          ##          ##########  ##      ##

Will wonders never cease? Very nice level. It took me a while to figure out the representation for the logs and I never made sense of the first hint -something cultural I suppose-, but an excellent level nevertheless.

Copper is the next level. Level 22.

Read the rest of this entry »

Python Challenge level 20: “go away!”

Level 20 meant Chaos.

Hint 1: ‘unreal.jpg’, an image of a fence with the message “private property beyond this fence”
Hint 2: but inspecting it carefully is allowed.

As I said, this riddle was chaotic. Completely. Very interesting indeed, but it took some serious trial and error. From deeper inspection of the headers I got the following facts:

Fact 1: One of the headers is ‘Content-Range: bytes 0-30202/2123456789‘. I had to look for some info on this subject because I wasn’t familiar with it. Found some enlightenment in the appropriate RFC2616.

Fact 2: beyond the fence would be in the range 30203 – 2123456789. Such huge size is suspicous, so after trying a retrieval of the whole range at once, and after not getting any meanigful results, I stuck to getting some chunks along the stream. Some at the beginning of the fence boundary, some at the end of the range. There could be more interesting places between the values mentioned, by I do believe the most significant ones are retrieved in the following script.

Obviously I didn’t start with these values. I merely requested fragments from the beginning and the end of the stream, and selected the ones interesting for your convinience. I know, I know! I’m a nice guy, don’t mention it :-D

import urllib
url = 'http://butter:fly@www.pythonchallenge.com/pc/hex/unreal.jpg'

##Private info found
for i in [(30237,30337), (30284,30384), (30295,30395), (30313,30413),
            (2123456744,2123456788), (2123456712,2123456743)]:
    opener = urllib.FancyURLopener({})
    opener.addheader("range", "bytes=%d-%d" % i)
    f = opener.open(url)
    print f.read()

##Something hidden in this particular range.
opener = urllib.FancyURLopener({})
opener.addheader("range", "bytes=%d-%d" % (1152983631,1152983671))
f = opener.open(url)
print f.info()
open("20.zip", "w").write(f.read())

The relevant pieces of information I could come up with were:

Content-Range: bytes 30237-30283/2123456789
we can go on in this way for really long time.
Content-Range: bytes 30284-30294/2123456789
stop this!
Content-Range: bytes 30295-30312/2123456789
invader! invader!
Content-Range: bytes 30313-30346/2123456789
ok, invader. you are inside now.
Content-Range: bytes 2123456712-2123456743/2123456789
and it is hiding at 1152983631
Content-Range: bytes 2123456744-2123456788/2123456789
esrever ni emankcin wen ruoy si drowssap eht

To ‘be in’ must mean that this level has been solved. The catch is we don’t have an URL to refer to for level 21. Fortunately we do have a ZIP file. Discovering it’s misteries will be the next riddle, Level 21.

Read the rest of this entry »

Python Challenge level 19: “please!”

For level 19 I used two scripts.

Hint 1: there is an email in the source with an attached file

#!/usr/bin/env python
import email
mail=email.message_from_file(open('19.txt'))
for part in mail.walk():
    if part.get_content_maintype()=='audio':
        audio=part.get_payload(decode=1)
        open('19_indian.wav', 'wb').write(audio)

The only understandable thing in the file was the word “sorry”, and that page is not really useful. Theres more to the riddle than meets the ear.

Hint 2: the colors of the map seem to be inverted.

After trying reversing the info (utter jibberish), the hint is to be reinterpreted as “inverted India”, which I take as “inverted endian”. Nice longshot. And correct, for what it’s worth.

#!/usr/bin/env python
import array,wave
wi = wave.open('19_indian.wav','rb')
wo = wave.open('19_indian_inv.wav', 'wb')
wo.setparams(wi.getparams())
a = array.array('i')
a.fromstring(wi.readframes(wi.getnframes()))
a.byteswap()
wo.writeframes(a.tostring())
wi.close(),wo.close()

This part of the challenge was completely unexpected. I hadn’t even used the wave module untill today! And we get a nice file with these lyrics:

you are an idiot

And with a message from Leopold, we go to Level 20.

Read the rest of this entry »

Python Challenge level 18: “can you tell the difference?”

Level 18 is imaginative again.

Hint 1: it is more obvious that what you might think.

Fact 1: the main difference between images is brightness, we’ll go to brightness.html which is an almost twin page except for a new hint.

Hint 2: maybe consider deltas.gz.

After retrieving deltas.gz, we can go on with the script:

import gzip,difflib
deltas=gzip.GzipFile('deltas.gz', 'rb').read()
deltas=deltas.splitlines()
left,right,png=[],[],['','','']
for row in deltas:
    left.append(row[:53])
    right.append(row[56:])
diff = list(difflib.ndiff(left,right))

for row in diff:
    bytes = [chr(int(byte,16)) for byte in row[2:].split()]
    if row[0]=='-': png[0]+=''.join(bytes)
    elif row[0]=='+': png[1]+=''.join(bytes)
    elif row[0]==' ': png[2]+=''.join(bytes)

for i in range(3):
    open('18_%d.png' %i,'wb').write(png[i])

The images created show the following texts:

18_0.png: fly
18_1.png: butter
18_2.png: ../hex/bin.html

That’s that. As allways, once you figure out the module you should be using makes the solution pretty straight forward. Level 19 is next.

Read the rest of this entry »

Python Challenge level 17: “eat?”

Level 17 is cookie related, so urllib usage is needed once again.

Hint 1: cookies in the photo. Unambiguous!
Hint 2: there’s a thumbnail of the image from level 4

So, we’ll have to investiagate the http headers. Interestingly, we find out something in level 4.

Fact 1:info=you+should+have+followed+busynothing…;” is a cookie set by level 4.
Fact 2: busynothing is another linkedlist, and has a chinging cookie!
Fact 3: once obtained every piece, the info seems to be a quote_plus string from a BZ2 chunk of data

The code for this part of the riddle would be:

import urllib,re,bz2
url='http://www.pythonchallenge.com/pc/def/linkedlist.php?busynothing='
seed="12345"
info=''
while True:
    req=urllib.urlopen(url+seed)
    text=req.read()
    seed=''.join(re.findall(r"busynothing is (\d+)",text))
    cookies=req.info().getheaders('Set-Cookie')[0]
    byte=cookies.split(';')[0].split('=')[1]
    info+=byte
    try :
        int(seed)
        print "   Info:",byte,"t   Next:",seed
    except :
        print "   Info:",byte,"t   Last:",text
        break
print "info:",bz2.decompress(urllib.unquote_plus(info))

is it the 26th already? call his father and inform him that “the flowers are on their way”. he’ll understand.

This one is intricate. Lets use level 13′s phonebook to find Mozart’s father’s phone. His name was Leopold by the way:

violin

From there to here, and stuck again. The only hint now is the page title: “it’s me. what do you want?”
Now it’s time to give him the message. After some trial and error and no RPC errors seen, setting a cookie with the message will show itself as the way to go.

from urllib2 import Request,urlopen
from urllib import quote_plus
info='the flowers are on their way'
url='http://www.pythonchallenge.com/pc/stuff/violin.php'
req = Request(url, headers={'Cookie': 'info=' + quote_plus(info)})
print urlopen(req).read()

oh well, don’t you dare to forget the balloons

Balloons! Finally Level 18.

Read the rest of this entry »

Python Challenge level 16: “let me get this straight”

In level 16 the title says everything.

Hint 1: there are 5 pink pixels in each row
Fact 1: pink is color 195 in the image’s color index (says The Gimp).

import urllib, Image,cStringIO
def straighten(line): # five equal consecutive pink pixels are the clue
    idx=0
    while line[idx]!=195:
        idx+=1
    return line[idx:]+line[:idx]

url = 'http://huge:file@www.pythonchallenge.com/pc/return/mozart.gif'
im = Image.open(cStringIO.StringIO(urllib.urlopen(url).read()))
for y in range(im.size[1]):
    line=[im.getpixel((x, y)) for x in range(im.size[0])]
    line=straighten(line)
    [im.putpixel((x, y),line[x]) for x in range(len(line))]
im.save('16.gif')

We don’t really need “Fact 1” to solve this problem, but it simplifies the code a bit. Instead of looking for pink pixels my first version looked for five equal pixels, but the result was slightly distorted in the lines where the level number is drawn.

The resulting image shows the word “romance”, and that’s where Level 17 awaits. I must say number 17 looks good. The most promising I’ve seen until now, and I’ve loved almost every single one of them. We’ll see if it can be as easily solved as this one.

Read the rest of this entry »

Python Challenge level 15: “whom?”

Level 15. I didn’t like this one. Not a bit. I think it’s the only level not as well designed as the rest. At least up untill now. Either that or my mindset doesn’t like some types of riddle as much as the rest. We are given a date in a callendar and a couple of hints.

Hint 1: he ain’t the youngest, he is the second
Hint 2: todo: buy flowers for tomorrow
Hint 3: whoever it is has something to do with a leap year, between 1006 and 1996 in which Jan 26 was Monday

I could easily generate some dates with the appropiate constraints, but the problem didn’t seem narrower. I had to read the forums for ideas. At first I was tricked by the image because it seemed to have just one digit missing in the year. I believe this was intentional. The hint about the flowers could mean many things: a death, a marriage, a birth…
So to the forums it is. These are excellent by the way, although by the absence of recent posts I think I’m tha last guy on Earth playing the game. Everybody seems to have finished long ago. The threads are classified by challenge level, no spoilers are allowed and there’s a lot of valuable hints. After reading the forums, I got some things straight.

Fact 1: the year is between 1006 and 1996. This narrows the thing a lot.
Fact 2: birth day is Jan 27. Better go to Wikipedia to get a list of events that happened that day.

#!/usr/bin/env python
import datetime,calendar
for year in range(1006,1996,10):
    d=datetime.date(year, 1, 26)
    if d.isoweekday() == 1 & calendar.isleap(year):
        print d

1176-01-26
1356-01-26
1576-01-26
1756-01-26
1976-01-26

The second youngest would have been born on 1756. Since it’s the next day, 1756-01-27. After going to Wikipedia there is finally an answer, Mozart. And that’s that. Im glad it’s over. Now to Level 16.

Read the rest of this entry »

Python Challenge level 14: “walk around”

Level 14 is really short once again.

Hint 1: image is a spiral. It’s direction could be meaningful
Hint 2: remember: 100*100 = (100+99+99+98) + …. (3+2+2+1)+1
Fact 1: wire.jpg is 1×10000 pixels = 100*100

I especially liked this level because it was straight forward. You didn’t have to imagine obscure meanings hidden in the hints. The wire.jpg is so streched that it resembles a barcode, so you can automatically know that there is something encoded somehow. Also the 100*100 sequence hint, and the 100*100 pixels couldn’t be clearer. I think it is the first level I solved in under 5 minutes since the easy ones at the beginning of the game.

#!/usr/bin/env python
import Image
im = Image.open("wire.png")
new = Image.new(im.mode,[100,100])
doubled_steps=200
directions=[(1,0), (0,1), (-1,0), (0,-1)] # vectors in [x,y] format
x,y,p=-1,0,0
while doubled_steps//2 > 0:
    for v in directions: # we will be taking steps in 4 directions
        steps=doubled_steps//2
        for s in range(steps):
            x,y=x+v[0],y+v[1]
            new.putpixel((x,y),im.getpixel((p,0)))
            p+=1
        doubled_steps-=1
new.save('14.jpg')

It’s a cat. We’ll learn its name in cat.html as well as the place to go if we’re looking for Level 15.

Read the rest of this entry »