python challenge 答案

YQYI發表於2017-05-21

Level 0 [1]

../_images/calc.jpg

Hint: try to change the URL address.

第零關主要讓人熟悉遊戲規則和形式。首先觀察標籤欄主題顯示的是warming up,表明這關的主題是熱身。頁面圖片下標註一行小提示嘗試改變url的地址。在沒有別的提示下,明顯是改成圖片內的內容, 2^38。開啟Python直譯器,輸入”2**38”,顯示答案”274877906944L”。去掉L替換URL中的0得到下一關地址。

http://www.pythonchallenge.com/pc/def/map.html

這一關想表明的是Python中的整數的儲存。Python中的整數相當於C中的長整型(long), 32位的機器上整型取值範圍為 -2147483648至2147483647, 64位機器上為-9223372036854775808到9223372036854775807。Python的長整型是無限制的,只要記憶體允許。很相似的是Python裡的無限list。一個很著名的例子是使用生成器(generator),就可以生成一個無限長的Fibonacci數列:

def fib():
    a = b = 1
    while True:
        yield a
        a,b = b,a+b


Level 1

../_images/map.jpg

Hint1: K -> M, O -> Q, E -> G

Hint2: 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.

同樣的首先觀察標籤欄主題, “what about making trans”。這個提示很有啟發性。然後觀察圖片, K->M, O->Q, E->G, 每個字母對應其後的兩個字母, 很顯然的一個 凱撒密碼 。最後觀察圖下的提示, “everybody think twice before solving this”和一堆亂碼。按照圖片中的方法, 把亂碼還原。可以通過ASCII碼與字元的轉換來做(ord, chr);或者直接使用string模組(table = string.maketrancs(from,to), string.translate(raw_str, table))。將轉換關係運用到Hint2的字串即可。

In [22]: import string

In [23]: table = string.maketrans( string.ascii_lowercase, string.ascii_lowercase[2:]+string.ascii_lowercase[:2])

In [24]: code = "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"

In [25]: code.translate(table)
Out[25]: "i hope you didnt translate it by hand. thats what computers are for. doing it in by hand is inefficient and that's why this text is so long. using string.maketrans() is recommended. now apply on the url"

In [26]: "map".translate(table)
Out[26]: 'ocr'

得到下一關地址 http://www.pythonchallenge.com/pc/def/ocr.html

Level 2

../_images/ocr.jpg

Hint1: recognize the characters. maybe they are in the book, but MAYBE they are in the page source.

這裡提示檢視網頁的原始碼,然後得到提示2

Hint2: 網頁原始碼的註釋中有: find rare characters in the mess below;下面是一堆字元。顯然是從這對字元中找出現次數最少的;注意忽略空白符,出現次數同樣多的字元按出現次序排序。拷貝亂碼字元到檔案level2code.txt,處理程式碼如下:

char_count = {}
for line in open("level2code.txt"):
    for c in line:
        char_count.setdefault(c, 0)
        char_count[c] += 1

sorted_chars = sorted(char_count.items(), key=lambda x: x[1])
print sorted_chars

得到結果:

[('a', 1), ('e', 1), ('i', 1), ('l', 1), ('q', 1), ('u', 1), ('t', 1), ('y', 1), ('\n', 1220), ('^', 6030), ('*', 6034), ('&', 6043), ('$', 6046), ('{', 6046), ('+', 6066), ('!', 6079), ('%', 6104), ('}', 6105), ('[', 6108), ('_', 6112), ('#', 6115), (']', 6152), ('(', 6154), ('@', 6157), (')', 6186)]

可以看出最少的字母是 ‘a’, ‘e’, ‘i’, ‘l’, ‘q’, ‘u’, ‘t’, ‘y’, 按照出現次數同樣多的字元按出現次序排序

chars = ['a', 'e', 'i', 'l', 'q', 'u', 't', 'y']
all_str = "".join(open("level2code.txt"))
chars_ind = [(c, all_str.find(c)) for c in chars]
print ''.join([c[0] for c in sorted(chars_ind, key=lambda x: x[1])])

結果是 equality 下一關地址是 http://www.pythonchallenge.com/pc/def/equality.html

Level 3

../_images/bodyguard.jpg

Hint1:One small letter, surrounded by EXACTLY three big bodyguards on each of its sides.

Hint2: 網頁原始碼中又是一堆字元。

用正規表示式, 找到這樣的“小寫字元”:其兩側恰好都被3個大寫字母佔據。

import re
all_str = "".join(open("level3code.txt"))
chars = re.findall(r'[^A-Z][A-Z]{3}([a-z])[A-Z]{3}[^A-Z]', all_str)
print "".join(chars)

結果是 linkedlist 下一關地址是 http://www.pythonchallenge.com/pc/def/linkedlist.php

Level 4

../_images/chainsaw.jpg

Hint1: <!– urllib may help. DON’T TRY ALL NOTHINGS, since it will never end. 400 times is more than enough. –>

Hint2:<a href=”linkedlist.PHP?nothing=12345”>

開啟http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=12345,結果是 and the next nothing is 44827

用urllib 和 re 模組繼續處理下去,直到出現 Yes. Divide by two and keep going. 此時所在頁面是:http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=16044

16044/2=8022,從http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=8022 繼續處理。

最後出現 peak.html 下一關地址是 http://www.pythonchallenge.com/pc/def/peak.html

import re
import urllib

next = "8022"
url = ""
response = ""
while next:
    url = "http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing="+next
    res = urllib.urlopen(url)
    response = res.read()

    # handle the solution (last) line
    if re.findall(r'\.html$', response):
        break

    code = re.findall(r'\d+$', response)

    if(code):
        next = code[0]
    else:
        # handle the divide by two line
        next = str (int (next) / 2 )

    print url
    print response


Level 5

../_images/peakhell.jpg

Hint1: pronounce it

Hint2: 圖片中是一座小山 (hill)

Hint3: 網頁原始碼中:<!– peak hell sounds familiar ? –>

Hint4: 網頁原始碼中:<peakhell src=”banner.p”/>, banner.p 可以下載。

“peak hell” 發音類似 “pickle”,用pickle 處理banner.p。

>>> import pickle
>>> banner = pickle.load(open("banner.p", 'r'))
>>> banner
[[(' ', 95)], [(' ', 14), ... (omitted)
>>> len(banner)
23

可以看到是一個巢狀list,總共23個,每個list裡是一些字元-數字對,猜測數字是字元重複的次數,試著列印這些字元:

for linelist in banner:
    print "".join(ch * count for ch, count in linelist)

出現 channel 下一關地址是 http://www.pythonchallenge.com/pc/def/channel.html

Level 6 [2]

../_images/peakhell.jpg

Hint1:<!– <– zip –> 根據提示,應該和zip檔案有關(Python有zipfile模組)。

將channel.html 改為 channel.zip,發現可以下載。channel.zip解壓後,channel資料夾中是一系列txt檔案。其中一個檔名是”readme”,其餘是數字名。開啟readme.txt:

welcome to my zipped list.
hint1: start from 90052
hint2: answer is inside the zip

好吧,從90052.txt 開始處理。直到出現:Collect the comments. 哪裡有comments? 沒辦法,學習zipfile模組吧,果然有comment屬性。 上述過程再重複一遍(還是從90052.txt)開始,記錄每次遇到的檔案的comment(如:z=zipfile.ZipFile(‘channel.zip’);z.getinfo(‘90052.txt’).comment )

列印記錄的comments,形如”hockey”。

import zipfile, re
findnothing = re.compile(r"Next nothing is (\d+)").match
comments = []
z = zipfile.ZipFile("channel.zip", "r")
seed = "90052"
while True:
    fname = seed + ".txt"
    comments.append(z.getinfo(fname).comment)
    guts = z.read(fname)
    m = findnothing(guts)
    if m:
        seed = m.group(1)
    else:
        break
print "".join(comments)

開啟 http://www.pythonchallenge.com/pc/def/hockey.html:

it's in the air. look at the letters.

表示還沒完,要從letters中找答案。組成”hockey”形狀的單詞分別是O、X、Y、G、E、N,即”oxygen”:

****************************************************************
****************************************************************
**                                                            **
**   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      **
**                                                            **
****************************************************************
 **************************************************************

下一關地址是 http://www.pythonchallenge.com/pc/def/oxygen.html

Level 7

../_images/oxygen.png

Hint1:圖片中有一道灰度條。

沒有其他提示,必然要從圖片下手了。用到PIL。該影象模式是RGBA。每個畫素是個四元組,前3個分量分別代表紅(R)、綠(G)、藍(B),對於灰度影象,R=B=G。這樣,我們可以找到這段灰度條。使用 getpixel((width, height)) 得到畫素點的RGB值,將其數值按照ASCII碼對映為字元(chr 函式)。

import Image

im = Image.open("../images/oxygen.png")
print "Image info:",im.format, im.size, im.mode

height = 0
for h in range(im.size[1]):
    pixel = im.getpixel((0, h))
    if(pixel[0]==pixel[1] and pixel[1]==pixel[2]):
        height = h
        break

ascii = [-1]
for w in range(0, im.size[0], 7):
    pixel = im.getpixel((w, height))
    if(pixel[0]==pixel[1] and pixel[1]==pixel[2]):
        ascii.append(pixel[0])
    else:
        break

ascii.remove(-1)
print "".join([chr(asc) for asc in ascii])

列印出來的資訊是:

smart guy, you made it. the next level is [105, 110, 116, 101, 103, 114, 105, 116, 121]

表示還沒完,繼續用chr轉換後面的數值得到下一關的地址 integrity 即 http://www.pythonchallenge.com/pc/def/integrity.html

Level 8

../_images/integrity.jpg

Hint1:Where is the missing link?

發現圖片是有連結的,點選後彈出登入框,需要使用者名稱和密碼,開啟網頁原始碼,看到:

<!--
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'
-->

其中un、pw 都是”BZh”開頭,應該是bz2編碼,於是用python的bz2庫來解碼,結果是:

In [40]: import bz2

In [41]: bz2.
bz2.BZ2Compressor    bz2.BZ2Decompressor  bz2.BZ2File          bz2.compress         bz2.decompress

In [41]: bz2.decompress('BZh91AY&SYA\xaf\x82\r\x00\x00\x01\x01\x80\x02\xc0\x02\x00 \x00!\x9ah3M\x07<]\xc9\x14\xe1BA\x06\xbe\x084')
Out[41]: 'huge'

In [42]: bz2.decompress('BZh91AY&SY\x94$|\x0e\x00\x00\x00\x81\x00\x03$ \x00!\x9ah3M\x13<]\xc9\x14\xe1BBP\x91\xf08')
Out[42]: 'file'

這就是”../return/good.html”頁面需要的使用者名稱密碼了。下一關地址 http://www.pythonchallenge.com/pc/return/good.html

Level 9

../_images/good.jpg

開啟原始碼,看到提示: first+second=? 然後是兩大塊數字,根據圖片中的黑點的意思,猜測是要通過描點畫出一個輪廓圖來,這兩塊數字矩陣估計是畫素值。

import Image,ImageDraw
im = Image.new('RGB', (500,500))
draw = ImageDraw.Draw(im)
first=[146,399,163,403, ...]  # chunked
second=[156,141,165,135, ...] # chunked
draw.polygon(first,fill='white')
draw.polygon(second,fill='white')
im.save('09.jpg')

這裡用到了 ImageDraw 庫,其中的 draw.polygon(first,fill=’white’) 用來畫多邊形的函式:

.polygon(L, fill=None, outline=None)
    Works like the .line() method, but after drawing all the specified line segments, it draws one more that connects the last point back to the first. The interior displays the fill, transparent by default. The border is drawn in the outline color, defaulting to white.

    For example, draw.polygon([(60,60), (90,60), (90,90), (60,90)], fill="red", outline="green") would draw a square box with a green outline, filled with red.

畫出來的圖片是

../_images/09.jpg

……這是頭公牛,試試英文單詞bull,OK!下一關地址是 http://www.pythonchallenge.com/pc/return/bull.html

Level 10

../_images/bull.jpg

Hint1: len(a[30]) = ?

開啟原始碼,看到有個 “sequence.txt” 連結,裡面是一個數列:

a = [1, 11, 21, 1211, 111221,

google後知道這是一個 Look-and-say sequence 數列,就是說下一個數是前一個數讀出來。 程式碼生成這個數列,然後列印a[30]的長度。

def readNum(num_seq):
    next_num = ""
    index = 0
    cur = num_seq[index]
    index += 1
    while index < len(num_seq):
        while index < len(num_seq) and num_seq[index] == cur[0]:
            cur += num_seq[index]
            index += 1
        else:
            if index < len(num_seq):
                next_num += str(len(cur))+cur[0]
                cur = num_seq[index]
                index += 1
    else:
        next_num += str(len(cur))+cur[0]

    return next_num

a = ['1']
for i in range(30):
    a.append(readNum(a[i]))

print len(a[30])

結果是5808,下一關地址是 http://www.pythonchallenge.com/pc/return/5808.html

Level 11

../_images/cave.jpg

開啟原始碼,沒有任何提示,觀察圖片,明顯是兩張圖片重疊在一起的,根據網頁標題odd even, 猜測可以按奇偶把圖片分解為兩張圖片。程式碼如下:

import Image
src = Image.open("cave.jpg")
w,h = src.size[0], src.size[1]
print "Image info:",src.format, src.size, src.mode
new = Image.new(src.mode,(w/2, h))

for i in range(w*h):
    y, x = divmod(i, w)
    p = src.getpixel((x,y))
    if i%2:
        new.putpixel((x/2,y/2+h/2),p)
    else:
        new.putpixel((x/2,y/2),p)
new.save('11.jpg')


../_images/11.jpg

可以看到 evil ,下一關地址是 http://www.pythonchallenge.com/pc/return/evil.html

Level 12 [3]

../_images/evil1.jpg

第一眼看到這道題的時候,還以為又是圖片處理,但是又沒有發現這張圖到底有哪裡需要處理的, 於是就放棄了這個想法,然後看了看title和頁面原始碼。原始碼是相當的乾淨簡潔,什麼註釋都沒有, 唯一有的提示就只剩下title了——dealing evil。在整個原始碼中能夠找到於evil有關的就只有img標籤的src了。 仔細觀察會發現src是 evil1.jpg ,既然有1,那麼是否會有2、3、4甚至更多的圖呢? 於是接下來就是更改url來檢視圖片。將url改成evil2.jpg會看到提示:不是jpg,是gfx。 那麼就將字尾名改為gfx,這樣就可以得到一個不知道是幹嗎的二進位制檔案。繼續更改url看看能不能得到更多的資訊, 在evil3.jpg上會看到“no more evils…”。也就是說沒有evil可以處理了。

對於這個不知所謂的二進位制檔案完全不知道怎麼下手,思考了很長時間也不得要領,出於無奈只好百度答案了, 後來發現原來在evil3後面還有4,不過4只有在ie中才能看到(我嘗試過在chrom、firefox、opera中檢視,都看不到)。 但是我覺得沒有什麼有價值的資訊,果然還是到3就可以結束了(可能是因為我對外國文化不是很瞭解, 有一個外國人的blog說以後會用到這個資訊)。到目前為止所有資訊到被找到了,可是唯獨有一樣資訊沒有被用上, 那就是第一張圖,在這張圖上是一個人在分牌,牌被分為了5份,這是在暗示我們, 將得到的gfx檔案也按照發牌的方式分成五分,於是就可以得到下面的程式碼:

#! /usr/bin/env python
'''python challenge level 12
question url: http://www.pythonchallenge.com/pc/return/evil.html
answer url: http://www.pythonchallenge.com/pcc/return/.html
'''

f = open('evil2.gfx','rb')
content = f.read()
f.close()

for i in xrange(5):
    f = open('level12_%d.jpg' % i, 'wb')
    f.write(content[i::5])
    f.close()

這樣就得到了0-4,一共5張圖,可以直接從資料夾中看出圖片的內容:

../_images/level12.gif

最後一張圖由於字母被劃去了,所以忽略,最後就得到結果disproportional。因此,下一題的連結為:

http://www.pythonchallenge.com/pc/return/disproportional.html

在做題的時候,會得到各種各樣的資訊,有些資訊是有用的有些資訊是沒用的,有些資訊是需要自己分析出來的,但是這些資訊是比較雜亂的,不便於記憶,有時會忽略掉一些資訊。因此要在身邊準備一個本子和一支筆,要把自己得到的資訊記錄下來,俗話說好記性不如爛筆頭。當前進受到阻礙的時候就看一看這些記錄,再將思路整理一下,也許會有意想不到的收穫。也正如《程式設計師的思維修煉》中所說,不是每個人都能成為偉大的人,但是每個人都會有偉大的想法。身邊隨時要攜帶紙筆或者其他可以替代的東西,靈感稍縱即逝,這樣才能記錄下你的每一個靈感。另外,不要過於關注細節,不要太過追求完美,美國作家安妮·拉莫特在她的《Bird by Bird: Some Instructions on Writing and Life》一書中解釋了完美主義的危害:

完美主義是壓迫者的聲音,是人們的敵人。它會束縛你的想法,毀掉你的生命,同時它也會妨礙你建立較差的草稿初案。我認為完美主義基於一種強迫性的想法:如果你足夠細緻,沒見事情都做得很好,那你就不會失敗。但事實是,無論怎麼做你都有可能失敗,可是很多人即使不太仔細也會做的比你好,而且其間也會擁有更多的快樂

相關文章