网络上的第一个 Python 编程难题挑战 (完)

建立日期: 2020/03/28
更新日期: 2020/04/22 第33关 (完)
个人常用集 Tool.py
网址: http://www.pythonchallenge.com/
说明:本文请随意引用或更改,只须标示出处及作者,作者不保证内容絶对正确无误,如造成任何后果,请自行负责.

标题:网络上的第一个Python编程难题挑战

找到一网站, 专门来挑战你对Python的了解程度以及个人智商, 以下是个人对每个问题详解, 不会一次解完所有的问题, 阶段性的补上内容.
(问题内容以英文为主)

第0关 http://www.pythonchallenge.com/pc/def/0.ht...

网络上的第一个Python编程难题挑战
Hint: try to change the URL address.
提示: 试着改变URL地址

[解]

  1. 因为这是第0关, 要找到第1关, 所以把网址中的0改成1
    http://www.pythonchallenge.com/pc/def/1.ht...
    2**38 is much much larger.
    2**38 是非常非常的大.
  2. 意思是让我们算一下2**38有多大
    >>> print(2**38)
    274877906944
  3. 把这个数代入网址, 结果进入第1关了
    http://www.pythonchallenge.com/pc/def/2748...
    而且, 网址后面被改为map, 表示这个map可能有坑.

第1关 http://www.pythonchallenge.com/pc/def/map....

网络上的第一个Python编程难题挑战

everybody thinks twice before solving this.

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.

[解] 图下面有一段看起来像乱码的内容, 而图中把K, O, E转换为M, Q, G, 是一个转码题

  1. 直觉的想法, 当然就是把那一段像乱码的内容中, 把K, O, E转换为M, Q, G, 结果还是乱码
text = ("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 rcv"
        "r gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnj"
        "w ml rfc spj.")
text = text.replace('k', 'm').replace('o', 'q').replace('e', 'g')
print(text)
"""
g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr ammnsrcpq ypcdmp. bmglg gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmlg. sqglg qrpglg.myicrpylq() gq pcammmclbcb. lmu ynnjw ml rfc spj.
"""
  1. 观察一下, 每个转换都是字母向后移2位, 假设到了Y, Z就移A, B, 这里都是小写, 就不管大写了.
text = ("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 rcv"
        "r gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnj"
        "w ml rfc spj.")

# 使用字串的maketrans以及translate来作转换
table1 = ''.join([chr(ord('a')+i) for i in range(26)]) # abcd...xyz
table2 = table1[2:]+table1[:2]                         # cdef...zab
trans = table1.maketrans(table1, table2)
result = text.translate(trans)
print(result)
"""
i hope you didnt translate it by hand. thats what computers arefor. 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.
"""
  1. 嗯, 看到正常的英文句子了, 但是怎么套上网址 ? 改为maketrans ?
    http://www.pythonchallenge.com/pc/def/make...
    that's a handy little function. isn't it?
    还是不对, 没进入第2关…
  2. 对了, 把网址上的map可能有坑, 试转换一下
text = "map"
...

"""
ocr
"""
  1. 改网址为 ocr, 果然进入第2关了

第2关 http://www.pythonchallenge.com/pc/def/ocr....

网络上的第一个Python编程难题挑战

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

[解] 书中的字, 太过模糊, 不太可能看清楚, 所以答案应该在page source, 意思就是在源代码中, 按右键检视网页原始源代码.

  1. 看了下源代码
    里面有一个特殊的地方, 让我们在那一长串的字符中找出少出现的字.
<!--
find rare characters in the mess below:
-->

<!--
%%$@_$^__#)^)&!_+]!*@&^}@[@%]()%+$&[(_@%+%$*^@$^!+] !&_#)_*}{}}!}_]$[%}@[{_@#_^{*
@##&{#&{&)*%(]{{([*}@[@&]+!!*{)!}{%+{))])[!^})+)$] #{*+^((@^@}$[**$&^{$!@#$%)!@(&
...
}!)$]&($)@](+(#{$)_%^%_^^#][{*[)%}+[##(##^{$}^]#&( &*{)%)&][&{]&#]}[[^^&[!#}${@_( |
#@}&$[[%]_&$+)$!%{(}$^$}* |
-->
  1. 为了不复制一大段的字符, 打开网页撷取, 这就开始爬虫了…因为简单就不用bs4或其它的包来解析html了, 建立字典记录所有字出现的次数. (这里有个缺点, 就是字典不会按顺序列出其中的元素, 刚好我的结果就是对的, 所以就不再试其他方法了)
from urllib import request

url = "http://www.pythonchallenge.com/pc/def/ocr.html"
with request.urlopen(url) as response:
    data = response.read()

html = data.decode('utf-8')
start_string = "<!--\n"
length = len(start_string)
stop_string = "-->\n"

# 有两个comment, 要找后面那一个, 所以使用rfind, 不用find.
start = html.rfind(start_string)
stop = html.rfind(stop_string)
text = html[start+length:stop].replace('\n', '')

chars = {}
for char in text:
    chars[char] = chars[char] + 1 if char in chars else 1
avg = sum(chars.values())/len(chars)
result = ''.join([char for char in chars if chars[char]<avg])
print(result)
"""
equality
"""
  1. 更改网址为equality, 嗯果然看到第3关了.
  2. 这里也可以使用正则式, 不过这个是已知结果都是各一次出现的a-z
import re
print(''.join(re.findall('[a-z]', text)))
"""
equality
"""

<下回继续>

本作品采用《CC 协议》,转载必须注明作者和本文链接
Jason Yang
本帖由系统于 2年前 自动加精
讨论数量: 36
Jason990420

第3关 http://www.pythonchallenge.com/pc/def/equa...

img

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

[解] 一个’小’字母, “刚好”被前后各三个”大”保镳围着

  1. 小写字母, 前后各自只有三个大写字母包围, 这是我的理解. 但是字串哪? 还是看一下网页源代码, 嗯… 又是一大串字符, 二话不说, 先捞出来再说.
from urllib import request

url = "http://www.pythonchallenge.com/pc/def/equality.html"
with request.urlopen(url) as response:
    data = response.read()

html = data.decode('utf-8')
start_string = "<!--"
length = len(start_string)
stop_string = "-->"

start = html.find(start_string)
stop = html.rfind(stop_string)
text = html[start+length:stop].replace('\n', '')

# 这里我们不用正则式来处理
uppercase = ''.join([chr(ord('A')+i) for i in range(26)])
lowercase = uppercase.lower()

result = ''.join([text[i] for i in range(3, len(text)-3)
            if text[i-3] in uppercase and text[i-2] in uppercase and
               text[i-1] in uppercase and text[i+1] in uppercase and
               text[i+2] in uppercase and text[i+3] in uppercase and
               text[i] in lowercase])
print(result)
"""
jfeiauzroivgzbmpszazlutnwsdofdbiwqidjbzshfrblqgsbyfdajygcblwjcwggtdmfjetobhcmdlzxajvithekcgpkcfwqqbvkoixetpiiljanvvqjjgtctpcadzjkgucbmluaidgumlcdskunujfcjhfmbzpkzxsasdxsqqdqlaeisjezfjfdaolkjapkypwxjtlhqjknnednxunsahxqedoeqsdccmmhltcqsnwakjxdteytfaalhlgabekfmyimwrkffydghiunlroiwgkudzsqljjbsixguytfsatejmdwkbfbzifdknpacqimvehxujkszmbuyutmsompijcjojspbywlroefiwmqrsjstdjhgfwxhcnthsoosmjoqtmufoxvpvpjpkgiaqgofrhyufxxdnjiwtfqusbkdeakunqjegdkfndpwibulklgjougnivhgixsnekxjgrirbnsllpuaouvhzabilbjimrmqqxtktgcnkdljoashnekxwtsgvzwjaehgurngksokjtroovpcmygkzgeolwynsyfiodeomflmkwmj
"""
  1. 哇 ~ 这是什么东西 ? 原来我忘了一个条件, 要刚好三个大写字母
from urllib import request

url = "http://www.pythonchallenge.com/pc/def/equality.html"
with request.urlopen(url) as response:
    data = response.read()

html = data.decode('utf-8')
start_string = "<!--"
length = len(start_string)
stop_string = "-->"

start = html.find(start_string)
stop = html.rfind(stop_string)
text = html[start+length:stop].replace('\n', '')

# 这里我们不用正则式来处理
uppercase = ''.join([chr(ord('A')+i) for i in range(26)])
chars = []
for i in range(3, len(text)-3):

    if (text[i-3] in uppercase and text[i-2] in uppercase and
        text[i-1] in uppercase and text[i+1] in uppercase and
        text[i+2] in uppercase and text[i+3] in uppercase and
        text[i] in lowercase):

        if i == 3 and text[i+4] not in uppercase:
            chars.append(text[i])
        elif i == len(text)-4 and text[i-4] not in uppercase:
            chars.append(text[i])
        elif text[i-4] not in uppercase and text[i+4] not in uppercase:
            chars.append(text[i])

print(''.join(chars))
"""
linkedlist
"""
  1. 使用正则式来找, 可能容易多了
import re
print(''.join(re.findall('[^A-Z][A-Z]{3}([a-z])[A-Z]{3}[^A-Z]', text)))
"""
linkedlist
"""
  1. 更改网址为linkedlist, 嗯还有东西…"linkedlist.php"
  2. 再更改网址为linkedlist.html为linkedlist.php, 哈, 看到第4关了.
4年前 评论
Jason990420

第4关 http://www.pythonchallenge.com/pc/def/link...

img

[解] 咦, 没有文字提示?

  1. 可以点图进入, 网变成

    http://www.pythonchallenge.com/pc/def/link...

    文字提示 and the next nothing is 44827, 再改网址为44827,

    文字提示 and the next nothing is 45439, 再改网址为45439,

    ..... 天啊要多少次 ?

  2. 还是看网页源代码, 看到一行注解 , 让我们使用urllib, 它说了也许改了400次就可以了, 也许 ??

    urllib may help. DON'T TRY ALL NOTHINGS, since it will never end. 400 times is more than enough.

  3. 还是用urllib来代理吧

from urllib import request
import datetime
now = datetime.datetime.now()
url = "http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing="
nothing = "12345"
text = "and the next nothing is"
count = 0
while True:
    count += 1
    with request.urlopen(url+nothing) as response:
        html = response.read().decode('utf-8')
    if text not in html:
        print(f"After {count} times and {datetime.datetime.now()-now}")
        print(f"We got nothing was {nothing} and different result as\n{html}")
        break
    nothing = html[html.rfind(' ')+1:].strip()
"""
After 86 times and 0:00:52.528705
We got nothing was 16044 and different result as
Yes. Divide by two and keep going.
"""
  1. 啊, 结果16044除以2, 8022, 先看一下, 内容是否一致, 嗯, 是的, 再继续...
nothing = "8022"
"""
After 165 times and 0:01:39.050807
we got nothing is 66831 and different result as
peak.html
"""
  1. 更改网址为peak.html, 哈, 看到第5关了.
4年前 评论
Jason990420

第5关 http://www.pythonchallenge.com/pc/def/peak...

img

pronounce it

[解] 发音 ?? 念看看 ?? 还是看网页源代码

  1. 有一行注解 peak hell sounds familiar ? peak hall 听起来熟悉吗?

    pickall ? pikcle ? pickle, 嗯python中有一个模组就叫pickle, 可以用来转换资料结构为字节流, 比如字典转换成bytes. 但哪来的资料结构 ?

  2. 再检查一下网页源代码, 除了格式格style.css, 还有显示的图片peakhell.jpg, 咦, 还有一个 banner.p 档没用到, 下载存档再说.

  3. 用pickle.load载入banner.p

import pickle

with open("D:/banner.p", 'br') as f:
    data = pickle.load(f, encoding='ascii')
print(data)
"""
[[(' ', 95)],
 [(' ', 14), ('#', 5), (' ', 70), ('#', 5), (' ', 1)],
 [(' ', 15), ('#', 4), (' ', 71), ('#', 4), (' ', 1)],
...
 [(' ', 6), ('#', 3), ... , (' ', 6), ('#', 6)],
 [(' ', 95)]]
"""
  1. 三维阵列, 最低一维是个文字及数字, 看起来是个重复字串, 再上一个维度可能是个分行.
for line in data:
    text = ''
    for element in line:
        text += element[0]*element[1]
    print(text)

file

  1. 更改网址为channel.html, 哈, 看到第6关了.
4年前 评论

费脑子。。不过挺有意思的。。发出来。有空可以玩玩。。

4年前 评论
Jason990420

第6关 http://www.pythonchallenge.com/pc/def/chan...

img

img

[解] 唯一可动的就是捐款按键, 进去就是捐款, 没助解题, 还是网页源代码

  1. 第一行就看到个注解<html> <!-- <-- zip -->, 这是要把网址中的html改成zip ?? 一改完就下载了一个channel .zip, 用winrar打开看, 910个文字档案, 除了一个readme.txt, 其他都是以数字为档名.
readme.txt
    welcome to my zipped list.

    hint1: start from 90052
    hint2: answer is inside the zip

90052.txt
    Next nothing is 94191
  1. 看来又是档案内容不一样的
file = '90052'
string = 'Next nothing is'
while True:
    path = f'd:/channel/{file}.txt'
    with open(path, 'rt') as f:
        text = f.read()
    if string in text:
        file = text[text.rfind(' ')+1:].strip()
    else:
        print(f"'{text}' in file {path}")
        break
"""
'Collect the comments.' in file d:/channel/46145.txt
"""
  1. Comments ? 注解在哪 ? 这可能与压缩档 zip有关, Python zip ? Python zipfile ?

    https://docs.python.org/3.3/library/functi... 不是这个

    https://docs.python.org/3/library/zipfile.... 就是这个

import zipfile
info = zipfile.ZipInfo('d:/channel.zip')
print(info.comment)
"""
b''
"""
  1. zip档的注解没有, 只好找Zip档中每个档案的注解, 发现都很短, 所以全部合在一起
import zipfile

file = '90052'
string = 'Next nothing is'
zip = zipfile.ZipFile('d:/channel.zip')
information = []
while True:
    path = f'{file}.txt'
    with zip.open(path) as f:
        text = f.read().decode('ascii')
    information.append(zip.getinfo(path).comment.decode('ascii'))
    if string in text:
        file = text[text.rfind(' ')+1:].strip()
    else:
        print(f"'{text}' in file {path}")
        break
print(''.join(information))
"""
'Collect the comments.' in file 46145.txt
****************************************************************
****************************************************************
**                                                            **
**   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      **
**                                                            **
****************************************************************
 **************************************************************

"""
  1. 看到hockey, 改了网址, 这里都是用小写的网址, 结果

    it's in the air. look at the letters. 在空中, 看下字母....按每个字的字母应该是oxygen

  2. 更改网址为oxygen.html, 哈, 看到第7关了.

4年前 评论
Jason990420

第7关 http://www.pythonchallenge.com/pc/def/oxyg...

img

[解] 没有提示, 直觉图片中隐藏有密码, 要把图片中那一片异常的点值找出来, 图片先存档

  1. 假设原图邻近的点在X方向不会有超过三个以上相同RGBA的值, 如果是的话, 应该就是密码区. 先取出密码区的点值及重复次数.
from PIL import Image

im = Image.open('D:\oxygen.png')
print(f'Image mode, size: {im.mode}, {im.width}x{im.height}')

result = []
for y in range(im.height):
    old_value = None
    count = 1
    line = []
    for x in range(im.width):
        point = im.getpixel((x, y))
        if point == old_value:
            count += 1
        else:
            if count > 3:
                line.append([old_value, count])
            count = 1
            old_value = point
    if count > 3:
        line.append([old_value, count])
    result.append(line)
  1. 从结果中发现, R,G,B值都一样, 而且值在32~127以内, 这正好符合ASCII码可印出字符范围, 去掉不符合的, 而且只留R值当字元码, 长度为重复次数除以7, 余数进位.
for line in result:
    flag = False
    string = ''
    if line == []:
        continue
    for p in line:
        if p[0][0]==p[0][1] and p[0][1]==p[0][2] and 31<p[0][0]<128:
            string += chr(p[0][0])*(int(p[1]/7+0.5))
            flag = True
        else:
            break
    if flag:
        print(string)
"""
smart guy, you made it. the next level is [105, 110, 116, 101, 103, 114, 105, 116, 121]
smart guy, you made it. the next level is [105, 110, 116, 101, 103, 114, 105, 116, 121]
...
smart guy, you made it. the next level is [105, 110, 116, 101, 103, 114, 105, 116, 121]
"""
  1. 再把 [105, 110, 116, 101, 103, 114, 105, 116, 121] 转换成字串
print(''.join(map(chr, [105, 110, 116, 101, 103, 114, 105, 116, 121])))
"""
integrity
"""
  1. 更改网址为 integrity.html, 哈, 看到第8关了.
4年前 评论
Jason990420

只有33关, 网址本身没有答案吧, 但有很多人提出自己的答案, 网上能搜到的, 直接看答案就没意思了

3年前 评论
Coolest 3年前
Jason990420

第8关 http://www.pythonchallenge.com/pc/def/inte...

img

Where is the missing link?

[解] 遗失的链结在哪? 图中的蜜蜂可以点击, 然后要输入用户名以及密码, 老样子还是看网页源代码, 最后面的注解中的un/pw应该就是用户名以及密码, 看来要解码.

<!--
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
-->
  1. 这些码, 一看就不是简单的ascii code, 但我相信最后的结果一定会是ascii code
>>> un = ('BZh91AY&SYA\xaf\x82\r\x00\x00\x01\x01\x80\x02\xc0\x02\x00 \x00!'
... '\x9ah3M\x07<]\xc9\x14\xe1BA\x06\xbe\x084')
>>> byte_un = un.encode('utf-8')
>>> byte_un.decode('utf-8')
('BZh91AY&SYA¯\x82\r'
 '\x00\x00\x01\x01\x80\x02À\x02\x00 \x00!\x9ah3M\x07<]É\x14áBA\x06¾\x084')
>>> byte_un.decode('utf-16')
'婂㥨䄱♙奓쉁슯ං\x00ā胂쌂ʀ\u2000℀髂㍨ݍ崼觃쌔䊡ف뻂
  1. 看起来没那么简单? 肯定又是某种编码转换. 在登入时应该有一个提示The site says: "inflate", 我就是看不到. 找下Python 的inflate, "inflate is a Python built command line interface", 这也不对. 要辨识其压缩的方法, 最重要的是前两码的header, 这里是'BZ', 在谷歌上搜索python deflat header 'BZ' , 找到了

    https://docs.python.org/3/library/bz2.html

  2. 编码之间的转换, 在Python 2与Python 3还是不一样的.

import bz2

un = (b'BZh91AY&SYA\xaf\x82\r\x00\x00\x01\x01\x80\x02\xc0\x02\x00 \x00!'
      b'\x9ah3M\x07<]\xc9\x14\xe1BA\x06\xbe\x084')
pw = (b'BZh91AY&SY\x94$|\x0e\x00\x00\x00\x81\x00\x03$ \x00!\x9ah3M\x13<'
      b']\xc9\x14\xe1BBP\x91\xf08')
print(bz2.BZ2Decompressor().decompress(un).decode('ascii'))
print(bz2.BZ2Decompressor().decompress(pw).decode('ascii'))
"""
huge
file
"""
  1. 在输入用户名 huge 和密码 file, 终于进到第9关了.
3年前 评论
Jason990420

第9关 www.pythonchallenge.com/pc/return/g...

Python

[解] 没提示, 直接看网页源代码, 注解 first+second=? 长度不一样不能相加

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,
...
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,
...
158,121,157,128,156,134,157,136,156,136
  1. 先载入这些数字, 因为要密码及用户名, 复杂一点
from urllib import request, response, parse

url = 'http://www.pythonchallenge.com/pc/return/good.html'
data = parse.urlencode({'Username':'huge', 'Password':'file'}).encode('utf-8')
header = {
    "Accept": ("text/html,application/xhtml+xml,application/xml;q=0.9,image"
               "/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3"
               ";q=0.9"),
    "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "zh-CN,zh-TW;q=0.9,zh;q=0.8,en;q=0.7",
    "Authorization": "Basic aHVnZTpmaWxl",
    "Connection": "keep-alive",
    "Host": "www.pythonchallenge.com",
    "User-Agent": ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/5"
                   "37.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/5"
                   "37.36")}

req = request.Request(url, data, header)
with request.urlopen(req) as response:
    data = response.read()

for key in response.headers.keys():
    print(f'{key}:{response.headers[key]}')
"""
Vary:Accept-Encoding
Content-Encoding:gzip
Last-Modified:Sat, 12 Mar 2016 19:38:46 GMT
ETag:"153910772"
Content-Type:text/html
Accept-Ranges:bytes
Content-Length:1298
Connection:close
Date:Sat, 28 Mar 2020 17:09:13 GMT
Server:lighttpd/1.4.35
"""
  1. 这里使用了gzip编码, 所以在使用unicode解码前, 要先使用gzip解压缩
import gzip
html = gzip.decompress(data).decode('utf-8')

first_start = html.find('first:')
len_start = len('first:')
second_start = html.find('second:')
len_second = len('second:')
second_stop = html.rfind('-->')

first_text = html[first_start+len_start:second_start].strip()
second_text = html[second_start+len_second:second_stop].strip()

first = list(map(int, map(str.strip, first_text.split(','))))
second = list(map(int, map(str.strip, second_text.split(','))))

print(len(first), len(second))
"""
442 112
"""
  1. 这些数字能作什么? 图片上面还有一些黑点, 而网页标题为connect the dots, 把点连在一起? 怎样连? 不能把一组当x, 另外一组当y. 嗯, 有可能所谓的点不是图中的黑点, 假设(x, y)都在同一组, 试看看…
import matplotlib.pyplot as plt

plt.plot(first[0::2], first[1::2])
plt.plot(second[0::2], second[1::2])

plt.show()

Python

  1. 这是一头牛吧, 牛的英文有哪些…cow, ox, bull, neat, bossy, cattle, 这又考英文? 全部试了一遍, 就是 bull, 就这样来到第10关.
3年前 评论
Jason990420

第10关 www.pythonchallenge.com/pc/return/b...

file

len(a[30]) = ?

[解] 又是牛, len(a[30]) 得看a[30]是什么东西, 图中站着的牛是可以点击的, 然后出现了下面的内容, 有头没尾的, 还是看一下网页源代码. 标题是what are you looking at ? 意思是那头牛问你说你在看什么? 看了一下, 没有别的提示了.

a = [1, 11, 21, 1211, 111221,
  1. 好吧, 就来看看a[10]有多长, 基本上不算, 长度就是1, 但是在python中len函数的参数只能是container, 整数不是container, 会产生TypeError. 不管先算一下a[10]是什么? 再检查一下该整数的长度.
def sequence(i):
    n = i+1
    p = "1"
    seq = [1]
    while (n > 1):
        text = ''
        index = 0
        length = len(p)
        while index < length:
            start = index
            index += 1
            while index < length and p[index] == p[start]:
                index += 1
            text += str(index-start) + p[start]
        n, p = n - 1, text
        seq.append(int(p))
    return seq

seq = sequence(30)
print(len(str(seq[30])))
"""
5808
"""
  1. 在更改网址为5808, 终于进到第10关了.
  2. 找序列数的算法在网路上可以找到很多, 不想算的也能找个网站, 自动帮找你出来.
3年前 评论
Jason990420

第11关 http://www.pythonchallenge.com/pc/return/5...

file

[解] 直接看网页源代码, title - odd even 就这地方不太一样, 奇偶能出什么牌? 看了好久, 得的这图片好像有网格, 放大看一下. 试着处理一下这些网格 file

  1. 把奇偶点的点RGB取出当文字, 全部打印出来, 看了一遍, 什么也没有.
from PIL import Image

im = Image.open('D:/cave.jpg')
for x in range(0, im.width, 2):
    chars = []
    for y in range(1, im.height, 2):
        chars.append(chr(im.getpixel((x, 1))[0]))
    print(''.join(chars))
  1. 把黑点组合成一新图, 真的有货.
from PIL import Image

im = Image.open('D:/cave.jpg')
new_im = Image.new('RGB', (im.width//2, im.height//2))
for x in range(0, im.width, 2):
    for y in range(1, im.height, 2):
        point = im.getpixel((x+y%2, y))
        new_im.putpixel((x//2, y//2), point)
new_im.show()

file

  1. 在更改网址为evil, 进到第12关了.
3年前 评论
Jason990420

第12关 http://www.pythonchallenge.com/pc/return/e...

file

[解] 标题是 dealing evil, 处理恶魔...看了网页源代码, 没什么提示, 就是标题和上一面这张图.

  1. 图片上又有网格, 那就先来处理吧

file

  1. 假设图像条是错开的, 把偶数点放到左边, 奇数点放到右边, 结果只是一张图变两张图.
from PIL import Image

im = Image.open('D:/evil1.jpg')
new_im = Image.new('RGB', (im.width, im.height))
for x in range(0, im.width, 2):
    for y in range(0, im.height):
        point1 = im.getpixel((x, y))
        point2 = im.getpixel((x+1, y))
        new_im.putpixel((x//2, y), point1)
        new_im.putpixel((x//2+im.width//2, y), point2)
new_im.show()

file

  1. 上面这一步骤后, 倒觉得这像是某种图片压缩技术下的结果. 试着把所有颜色找出来, 307200个点竟然还有92199种颜色, 哎...
from PIL import Image
import numpy as np

im = Image.open('D:/evil1.jpg')
im_np = np.array(im).reshape((-1, 3))
result = np.unique(im_np, axis=0)
print(len(result))
"""
92199
"""
  1. 对了, 这个图档叫evil1.jpg, 会不会还有别的evil2.jpg, ...., 哈, 总共有四张图片, 这第四张无法显示, 虽然是.jpg, 估计这不是jpg档, 而是某种资料. 第二张图片说了, 不是jpg, 是gfx. 虽然第三张说没有了, 就别相信它.

file

  1. 改了附属档名为gfx, 看图软件还是开不了, 搜索下Python gfx, 有个包叫gfx, 但是却安装不了... 最后然用文本阅读器就打开了, 试了bert, 不行.
Bert is evil! go back!
  1. 回到第二张图片, "不是jpg, 是gfx"会不会说的是网址, 试试...咦, 下载了一个evil2.gfx, 这回文本阅读器打不开了.

.GFX File Format Description 不是这个3D模型描述档, 这是文本内容的

  1. 找维基百科, 没有, 各种图片格式也没有

GFX may refer to:

  • Ganesh GFX, A Video & Photo Editior
  • Graphics
  • Mangetti Dune !Kung, spoken in Namibia and South Africa
  • Scaleform GFx, a game development middleware package

8 . 自己试着解码吧, 把evil2.gfx数据画成一张2D图, 看到结果似乎是五个点为单位.

import math
from PIL import Image

with open('D:/evil2.gfx', 'br') as f:
    data = f.read()

N = len(data) # 67575

factor = [] # 找出因数
for i in range(2, int(math.sqrt(N)+1)):
    if N%i == 0:
        factor.append(i)
print(factor)
# [3, 5, 15, 17, 25, 51, 53, 75, 85, 159, 255]

height = 85 # 随意定义个图片高度, 把所有的点画出来
width = N//height
im = Image.new('RGB', (width, height))
for x in range(width):
    for y in range(height):
        im.putpixel((x, y), data[x+y*height])
im.show()

file

  1. 每五个点取一个点, 组图, 因为图的大小是任意取的, 结果看图的细部是偏移了18个点, 目前寛度是159, 159-18=141, 67575/141 =479.25531914893617, 这不是常见的图片寛度 !
from PIL import Image

N = 67575//5 # 5个点取一点
height = 85
width = 159-18
im = Image.new('RGB', (width, height))
for x in range(width):
    for y in range(height):
        im.putpixel((x, y), data[x*5+y*height])
im.save('D:/result.jpg')
  1. 图寛480, 图高呢? 67575/480/5 ~ 28, 试试, 结果输出还是不对, 如果不是点的颜色呢? 如果分成不同的档案呢? 再试试...嗯, 出来了,不过第四个档案有问题.
from PIL import Image
for i in range(5):
    with open(f'D:/file{i}', 'wb') as f:
        f.write(data[i::5])
for i in range(5):
    try:
        im = Image.open(f'D:/file{i}')
        im.show()
    except:
        print(i)
  1. 看一下每个档案的前16个码, 再找一下各类签章, 原来各个档为jpg, png, gif, png, jpg
PNG - 89 50(P) 4E(N) 47(G) 0D 0A 1A 0A
GIF87a - 47(G) 49(I) 46(F) 38(8) 37(7) 61(a)
JFIF - FF D8 FF E0 00 10 4A(J) 46(F) 49(I) 46(F) 00 01
for i in range(5):
    with open(f'D:/file{i}.jpg', 'rb') as f:
        data = f.read()
    print(data[:16])
"""
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x00\xb4'
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR'
b'GIF87a@\x01\xf0\x00\xe7\x00\x00\x00\x01\x00'
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR'
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x00\xb4'
"""
  1. 第四个档案还是不齐全, 只能看到一部份

file

  1. 再来又要考英文了, dis-pro-port-...-ity, 最后ity应该是不要的, 不过不算太难, 就是disproportionality, 进到第13关, 越来越难.
3年前 评论
Jason990420

第13关 http://www.pythonchallenge.com/pc/return/d...

Python

phone that evil

[解] 线索

1. 标题: call him, 文字提示: phone that evil 打电话给恶魔
2. 号码键5可以点击进到 http://www.pythonchallenge.com/pc/phonebook.php
<methodResponse>
    <fault>
        <value>
            <struct>
                <member>
                    <name>faultCode</name>
                    <value>
                        <int>105</int>
                    </value>
                </member>
                <member>
                    <name>faultString</name>
                    <value>
                        <string>
                            XML error: Invalid document end at line 1, column 1
                        </string>
                    </value>
                </member>
            </struct>
        </value>
    </fault>
</methodResponse>
  1. 网址改为faultcode, 105, faultstring都没货. 我又把XML的内容转成字典, 还是没看到什么. 错误讯息说第一行第一个字的起头不对, 查了一下XML的架构.
methodResponse = {
    'fault': {
        'faultcode':105,
        'faultstring':'XML error: Invalid document end at line 1, column 1'}
        }
  1. 再次看了网页源代码, phone that <remote /> evil 中间插了一个remote, 改下网址, 出来一串"the phone is remote”, 应该和Python remote 有关, 却google不出什么东西, 后来又查了一下Python XML remote, 还是没有, 再查了一下Python remote module XML, 看到一个module xmlrpc
xmlrpc — XMLRPC server and client modules
XML-RPC是一种远程过程调用方法,它使用通过HTTP传递的XML作为传输方式。有了它,客户端可以在远程服务器(该服务器由URI命名)上调用带有参数的方法,并获取结构化数据。
  1. 使用xmlrpc库查遍所有出现的字眼, 后来又把html, XML的内容都进去搜索, 也都没有
from xmlrpc.client import ServerProxy
def call(text):
    print(client.phone(text))
server_name = 'http://www.pythonchallenge.com/pc/phonebook.php'
client = ServerProxy(server_name)
phone_list = ['phone', '105', 'remote', 'evil', 'faultcode',
              'faultstring', 'phonebook', 'dispro', 'XML', 'him',
              'xmlrpc']
for phone_no in phone_list:
    call(phone_no)
"""
He is not the evil
....
"""
  1. 对了, 第12关说了一句话, 差点忘了, "Bert is evil! go back !", 终于有点结果, 不输入以后还是不对, 估计电话号码要输码, 在美国就是这样在广告中打出电话号码的, 比较好记, 试了半天也不对.
>>> call('Bert')
555-ITALY
"""
The Google Ads Phone Number is 1-866-2GOOGLE (1-866-246-6453) for United States callers.
"""
  1. 又试了半天, 哎~ 原来是要小写的义大利啊, 就这样进了第14关.
3年前 评论
Jason990420

第14關 http://www.pythonchallenge.com/pc/return/i...

file

[解] 還有一張圖wire.png, 不管怎麼樣打開來都是一條長線, 而不是像網頁源代碼中所示, 再存檔右鍵看一下內容, 是一個寛度10000, 高度1的PNG檔.網頁標題walk around, 還有一個數學式子.

<!-- remember: 100*100 = (100+99+99+98) + (... -->
<img src="wire.png" width="100" height="100">
  1. 100*100 = (100+99+99+98) + (… 還有10000個點, 先看一下png內容格式
from Tool import Read_File # Tool is my own library for general functions
im = Read_File('D:/wire.png')
if im == None:
    quit()
print(im.mode, im.width, im.height)
"""
RGB 10000 1
"""
  1. 先轉作100x100的圖片, 出現bit, 輸入網址後出現 you took the wrong curve. 假設錯了, 先找別的方向.
import numpy as np
from PIL import Image
np_im = np.array(im).reshape(100, 100, 3)
new_im = Image.fromarray(np_im, 'RGB')
new_im.show()

file

  1. 這麼花的圖內容, 应该只是順序不對, 100+98+98+97…還有一個標題walk around, 麵包又是螺旋狀的, 不會要一條10000個的點, 順時針盤成一張圖吧? 向右的第一條水平線就是100個點, 再向下就是99個點, 向左又是99個點, 再向上, 沒錯就是98個點, 嗯試試.
new_im = Image.new('RGB', (100,100))
data = [[0 for y in range(100)] for x in range(100)]
x, y = 0, 0
dx, dy = 1, 0
for i in range(im.width):
    new_im.putpixel((x, y), im.getpixel((i, 0)))
    data[x][y] = 1
    if not((0 <= x+dx <= 99) and (0 <= y+dy <= 99) and data[x+dx][y+dy] != 1):
        dx, dy = -dy if dy != 0 else 0, dx if dx !=0 else 0
    x += dx
    y += dy
Save_File('D:/new_image.jpg', new_im)

file

  1. 這裡的作法就當作遊戲一樣, 不要出界, 走過的地方不再走, 從(0, 0) 逆時針方向出發, 出來的結果是一隻貓, 那就輸入cat 吧, 又出來兩隻貓的圖片, 不過有一行文字串and its name is uzi. you’ll hear from him later. 再改為uzi, 嗯, 看到第15關了.
3年前 评论
Jason990420

第15关 http://www.pythonchallenge.com/pc/return/u...

file

  • whom?
  • screen15.jpg
  • he ain't the youngest, he is the second
  • todo: buy flowers for tomorrow

[解] 谁? 他不是最小的,他是第二? 为明天买花? 一月1?6 上面有个大黑点? 上面一月26日打了个圏? 和上关中出现的小黑有关? 一时想不到什么... 最右下角应该是二月, 二月有29天, 而且那一天是星期日, 先找一下那是哪一年?

  1. 2/29 闰年, 周日, 年份要从1开始, 29日可能会出错, 改为2/28周六, 在ISO的标准, 周一为0, 周日为6, 不是我们一般认知的周日为第一天, 所以我们要找的是weekday=5, 再判断一下年份1开头, 6结尾.
from datetime import date

years = [year for year in range(1, 2021)
            if date(year, 2, 28).weekday()==5 and
               year%4==0 and
               str(year)[0]=='1' and
               str(year)[-1]=='6']
print(years)
"""
[156, 1176, 1356, 1576, 1756, 1976]
"""
  1. 再上网查一下, 那几年的01/26是哪个名人的生日 ?

    http://www.birthdaytalker.com/content/2496

    结果没有, 不会吧... 第二天该不会是01/27, 而不是01/26, 再查一下,嗯有两人, 1756年的莫扎特和1976年的林心如, 一定不会是后者, 那就是Wolfgang Amadeus Mozart, 每个字都试一下.

  2. 答案就是小写的 mozart, 这么简单就进到第16关了.

  3. 莫札特家中共七位兄弟姐妹, 他排老二, 也只有他姐姐和他活了下来, 其他的早逝, 所以说他不是最小的,他是第二 !

3年前 评论
Jason990420

第16关 http://www.pythonchallenge.com/pc/return/m...

file

  • 图片档案 mozart.gif 中都是乱纹, 里面还有一些洋红色的小横纹(1白5洋红1白, 7个点)
  • 标题 let me get this straight 让我搞清楚? 把这给弄直?
  • 把 mozart.gif 转成莫札特的图片?

[解] 洋红纹都是一样的, 一样的东西就代表没有资讯.

  1. 先看下图片的结构, 640x480图, mode为P, 也就是说是彩色图像1层(L) , 1字节/像素, 使用256色的调色板
from PIL import Image

filename = 'D:/mozart.gif'
im = Image.open(filename)
print(im.mode, im.size)
"""
P (640, 480)
"""
  1. 转换成numpy, 检查像素值有没有1白5洋红1白, 7个点的. 从数据中看出来, 前后两个白点颜色值还不一样, 主要是其中那五个点颜色值为155.
im_np = np.array(im)
print(im_np[0])
"""
[ 24 16 22 59 59 82 88 100 95 94 16 23 47 47 90 96 88 54
  94 83 23 23 82 47 95 17 95 60 59 22 125 18 22 90 23 60
  58 95 46 11 60 95 11 17 18 60 16 59 60 60
  ...
 249 195 195 195 195 195 252 <=========
 ...
  88 48 96 47 88 24 17 96 94 60 96 89 16 54 17
    48 88 18 82 88 24 100 96 94 24 60 12 60 16 95 24 58 18
  95 23 84 47 88 60 17 59 16 16]
"""
  1. 确认一下洋红纹的分布, 每一行出现195的次数都是5, 也就是说每一行只有一个洋红纹
print(np.count_nonzero(im_np==195, axis=1))
  1. 将洋红纹去掉再拼成新图. 结果看来和原图差不多不多, 就是少了那些洋红纹.
result = []
for y in range(im.height):
    line = list(im_np[y])
    where = line.index(195)
    result.append(line[:where-1]+line[where+6:])
new_im_np = np.array(result)
new_im = Image.fromarray(new_im_np, mode='P')
new_im.palette = im.palette
new_im.show()
  1. 新图中还可以看到一些杂乱的近似白条纹, 每一行的数量还不一样, 不能再用上一方式移除, 因为这样一来每一行的长短就不一样了. 对了, 提示还有一个, 标题let me get this straight 让我搞清楚? 把这给弄直? 这意思应该是把纵向弄直, 不移除白线, 移到边上就长度不变了, 但是白线的内容又不一定, 根本没去判断白线的位置及长短.
  2. 如果要对齐的不是那些白线, 而是洋红线....试试, 全部移到左边.
result = []
for y in range(im.height):
    line = list(im_np[y])
    where = line.index(195)
    result.append(line[where-1:]+line[:where-1]) # 改变这一行
new_im_np = np.array(result)
new_im = Image.fromarray(new_im_np, mode='P')
new_im.palette = im.palette
new_im.show()

file

  1. 看起来没错, romance, 哈, 就是你, 第17关了.
3年前 评论
Jason990420

第17关 http://www.pythonchallenge.com/pc/return/r...

file

[提示]

  • 图片 cookies.jpg 左下角有张小图, 中间有个折线
  • 标题 eat ? 吃吗?

[解] 这个折线看了半天, 也不知道如何精确的取出, 也许有什么意义, 先不管. 不对, 这张见过, 在第4关, 回去比对了一下, 这图原来就长这样.那这小图有何用意呢? 再看别的线索吧.

吃? 饼干? 肯定不是, 这对解题一点帮助都没有, 但肯定是有种意义.

  1. cookie ? Http 1.1 is a generic, stateless, protocol (通用的无状态协议), 属于没有记忆保留的连网, 为了解决持续连网的问题, 其中有一个方式就是使用cookie.
  2. 难道第4关有cookie的存取? 先看一下第4关的 response.headers
from Tool import Read_URL
url = 'http://www.pythonchallenge.com/pc/def/linkedlist.php'
response, html = Read_URL(url)
print(response.headers.keys())
print(response.headers['Set-Cookie'])
  1. 第4关是重复取得"and the next nothing is xxxx", 最后取得不同的讯息. 难道要重复读取第4关的cookie 直到有不同的讯息. 试试...响应中有没有cookie, 结果没有.
from Tool import Read_URL
base_url = 'http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing='
nothing = '12345'
response, html = Read_URL(base_url+nothing)
print(response.headers.keys())
  1. 第2点中的 Cookie 中有一项 info=you+should+have+followed+busynothing..., 把原来的网址中的 nothing 要改成 busynothing ? 读出来 info=B ?
from Tool import Read_URL
base_url = 'http://www.pythonchallenge.com/pc/def/linkedlist.php?busynothing='
nothing = '12345'
response, html = Read_URL(base_url+nothing)
print(response.headers.keys())
print(response.headers['Set-Cookie'])
  1. 那就重作第四关, 但记录下每一次重读网页headrs中的info.
from Tool import Read_URL

url = "http://www.pythonchallenge.com/pc/def/linkedlist.php?busynothing="
nothing = "12345"
text = "and the next busynothing is "
string = ''
while True:
    response, html = Read_URL(url+nothing)
    if not html:
        break
    if 'Set-Cookie' in response.headers:
        cookie = response.headers['Set-Cookie']
        start = cookie.find('info=')
        stop = cookie.find(';', start+5)
        string += cookie[start+5:stop].strip()
    if text not in html:
        print(f"We got nothing was {nothing} and different result as\n{html}")
        print(f'Cookie info get a string:\n{string}')
        break
    nothing = html[html.find(text)+len(text):].strip()
  1. 找到一个字符串, 又看到BZ起头, 不会又是压缩吧... 这个字串又是 unicode-escape, 先转成二位元位组, 再解压缩
from urllib import parse
import bz2
string = ('BZh91AY%26SY%94%3A%E2I%00%00%21%19%80P%81%11%00%AFg%9E%A0+%00hE'
          '%3DM%B5%23%D0%D4%D1%E2%8D%06%A9%FA%26S%D4%D3%21%A1%EAi7h%9B%9A'
          '%2B%BF%60%22%C5WX%E1%ADL%80%E8V%3C%C6%A8%DBH%2632%18%A8x%01%08'
          '%21%8DS%0B%C8%AF%96KO%CA2%B0%F1%BD%1Du%A0%86%05%92s%B0%92%C4Bc'
          '%F1w%24S%85%09%09C%AE%24%90').replace('+', ' ')
tmp = parse.unquote_to_bytes(string)
result = bz2.BZ2Decompressor().decompress(tmp).decode('utf-8')
  1. 又打电话? 他父亲? 又得回第13关打电话, 送花, 那莫札特他父亲是Johann Georg Leopold Mozart
from xmlrpc.client import ServerProxy
def call(text):
    print(client.phone(text))
server_name = 'http://www.pythonchallenge.com/pc/phonebook.php'
client = ServerProxy(server_name)
call('Johann')
call('Georg')
call('Leopold')
  1. 代入网址, 最后看到莫札特父亲的照片..., 网页源代码来一句"是我, 有什事吗?", 要通知他"the flowers are on their way" 花已经在路上了, 用打电话的方式, 不行. 看了一下, 在Request.Headers 中Cookie: info=you+should+have+followed+busynothing...

file

  1. 用 Cookie 的方式来送看看, Request.Headers 中 Cookie: info=the flowers are on their way
from Tool import Read_URL

url = 'http://www.pythonchallenge.com/pc/stuff/violin.php'

headers = {
    'Accept': ('text/html,application/xhtml+xml,application/xml;q=0.9,'
               'image/webp,image/apng,*/*;q=0.8,application/signed-exc'
               'hange;v=b3;q=0.9'),
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh-TW;q=0.9,zh;q=0.8,en;q=0.7',
    'Connection': 'keep-alive',
    'Host': 'www.pythonchallenge.com',
    'Cookie': 'info=the flowers are on their way;',
    'Sec-Fetch-Dest': 'document',
    'Sec-Fetch-Mode': 'navigate',
    'Sec-Fetch-Site': 'none',
    'Upgrade-Insecure-Requests': '1',
    'User-Agent': ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWeb'
                   'Kit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.14'
                   '9 Safari/537.36')}

response, html = Read_URL(url, headers=headers)
print(response.headers.keys())
# ['Content-type', 'Connection', 'Transfer-Encoding', 'Date', 'Server']
print(html)
  1. 没有Cookie 回来, 再看一下收到的网页源代码, 比前面没送Cookie 时, 多了一句话"oh well, don't you dare to forget the balloons.", 再改网址为balloons , 到站了, 这就是第18关.
3年前 评论
Jason990420

第18关 http://www.pythonchallenge.com/pc/return/b...

file

[提示]

  • 标题: can you tell the difference? 你可以看出来有何不同?
  • 图档 balloons.jpg
  • balloons 气球
  • 注解: it is more obvious that what you might think 你可能会认为这太明显了

[解] 一亮一暗两张图, 应该是亮度不一样, 亮度的变化都一样吗? 如果一样就代表没有资讯, 这和气球有啥关系?

  1. 先检查两张图的亮度差吧, 结果没什么明显的数字, 把图片放大看了一遍, 也没什么特别的
from Tool import Read_File
import numpy as np
im = Read_File('D:/balloons.jpg')
half_width = im.width//2
im_np = np.array(im)
im_diff = im_np[:,:half_width]/im_np[:,half_width:]
  1. 一亮一暗的差别在数字上看不出什么, 但从语言一来看, 两张图片的差异就是亮度brightness, 输入新的网址, 还是一样的图, 但是有图就表示这个网址是OK的.

http://www.pythonchallenge.com/pc/return/b...

  1. 网页源代码中多了一句注解maybe consider deltas.gz, deltas ? gz ? gzip ? 没有档案, 解压缩? deltas 差异, 两张图片的差异, 如果以亮度来说, 应该相除, 但会有除零出错的问题, 所以有可能是相减. 相减还是一张类似的图片.
  2. 没有档案, 网址试试deltas.gz, 还真的下载了这个档案. 解压缩得到delta.txt, 内容就是一大堆十六进位码, 每个byte之间空至少一格, 有的更多....先读进来看一下. 看起来基本的格式就像是一般HEX editor的格式. 但是两都是HEX, 而且内容几乎一样, 除了左边少了某些内容.
  3. 先把少的内容找出来
from Tool import Read_File
text = Read_File('D:/delta.txt').replace(' ', '').replace('\n', '')
left = ''.join([text[i:i+36] for i in range( 0, len(text), 72)])
right = ''.join([text[i:i+36] for i in range(36, len(text), 72)])

i = j = 0
missed = []
while i<len(left) and j<len(right):
    if left[i:i+2] == right[j:j+2]:
        j += 2
    else:
        missed.append(left[i:i+2])
    i += 2
miss = ''.join(missed)
# '89504e470d0a1a0a0000000d4 ... 7e31fc426082'
  1. 这结果会不会是张图片, 直接显示肯定不行, 又不知道格式, 也不知道长宽. 先换成二进位元组, 再存档, 喔对了, 查一下图片类型的Header. 在第12关我们查过, PNG - 89 50(P) 4E(N) 47(G) 0D 0A 1A 0A, 就是PNG档. 但是打开却出错了, 好像资料有错.
result = bytearray.fromhex(miss)
with open('D:/result.png', 'wb') as f:
    f.write(result)
  1. 先前假设都是左边多出了一些行来, 但是如果右边有一些行是左边没有的, 结果就是后面都会被当作不存, 全部加了进来. 这就来到文件比对的问题了, 还是用Python difflib 库吧.
from Tool import Read_File, Any
lines = Read_File('D:/delta.txt').splitlines()
left = [line[:53] for line in lines]
right = [line[56:] for line in lines]

import difflib

more = less = origin = ''
for line in difflib.Differ().compare(left, right):
    if line.startswith('+ '):
        more += line[2:]
    elif line.startswith('- '):
        less += line[2:]
    elif line.startswith(' '):
        origin += line[2:]
table = {'Origin.png':origin, 'Less.png':less, 'More.png':more}
for key, value in table.items():
    with open('D:/'+key, 'wb') as f:
        f.write(bytearray.fromhex(value.replace(' ', '')))

Origin.png

More.png

Less.png

  1. 更新网址 /hex/bin.html 后, 要求输入用户名 butter 与密码 fly, OK, 第19关我来了 !
3年前 评论
Jason990420

第19关 http://www.pythonchallenge.com/pc/hex/bin....

file

[提示]

  • 标题: please! 麻烦你了
  • 有一封信在网页源代码中, 主要内容是附件内容主题为what do you mean by "open the attachment?", 还有一附件, base64编码的音乐档indian.wav, 除了一大段字符串, 还有一个数字1295515792.

[解] 先提出那一大串的字符串吧

  1. 内容太长, 下载网原内容再取出字符串, 再存为 indian.wav, 播放后就只听到一句 Sorry. 输入当网址, 得到 - "what are you apologizing for?" 为何要道歉 ?
from Tool import Read_URL
from base64 import b64encode, b64decode
auth = str.encode("%s:%s" % ('butter', 'fly'))
user_and_pass = b64encode(auth).decode("ascii")
headers = {'Authorization': 'Basic %s' % user_and_pass, "Accept": 'application/json'}
response, html = Read_URL('http://www.pythonchallenge.com/pc/hex/bin.html', headers=headers)
start = html.find('base64')+6
stop = html.find('--===============1295515792==--')
text = html[start:stop].strip().replace('\n', '')
data = b64decode(text)
with open('D:/indian.wav', 'wb') as f:
    f.write(data)
  1. 在网页源代码邮件中, 有一句话Maybe my computer is out of order. 按英文的意思来说是电脑坏了, 但是按字面上来解译可说是顺序乱了, 而图片或档名是印度Indian, 这可能讲的是数据储存是MSB和LSB的顺序, 也就是little endian (LSB+MSB) 和big endian (MSB+LSB), endian念起来又很像indian, 试着反转资料的顺序.
>>> new_text = ''.join([text[i+1]+text[i] for i in range(0, len(text), 2)])
>>> new_data = b64decode(new_text+'=')
b'\x91A\xa5\xbd\x1c\xf2@\x05\xc1U\x01Y\x99\x9d5\x04'
  1. 上面的结果, 不再是个.wav 档, 不可以直接转, 得保留.wav 的格式, 因此从indian.wav 读进声音的数据, 再存成新的big endian档案, 格式就不会跑掉.
import soundfile
f1 = soundfile.SoundFile('D:/indian.wav', 'rb')
soundfile.write('D:/new.wav', f1.read(), f1.samplerate, subtype=f1.subtype,
    endian='BIG', format='WAV', closefd=True)
f1.close()
  1. 有了you are an idiot ? idiot ? 没错, 修改网址, 又看到莫札特他爹, 再点击就就到第20关了. 这里别忘了登入的用户码不再是huge/file , 而是butter/fly.
"Now you should apologize..."
Continue to the next level
3年前 评论
Jason990420

第20关 http://www.pythonchallenge.com/pc/hex/idio...

file

[提示]

  • 图片的文字 "PRIVATE PROPERTY BEYOND THIS FENCE" 篱笆之外是私人财产
  • 图片下方文字 "but inspecting it carefully is allowed." 但是允许仔细检查它
  • 标题: go away 走开
  • 图片档名 unreal.jpg

[解] 先试看看几个关键字, private, property, beyond, fence, inspect, allow, away, unreal

  1. 代入关键词, 结果都不是
from Tool import Read_URL
from base64 import b64encode

auth = str.encode("%s:%s" % ('butter', 'fly'))
user_and_pass = b64encode(auth).decode("ascii")
headers = {'Authorization': 'Basic %s' % user_and_pass,
           'Accept': 'application/json'}
table = ['private', 'property', 'beyond', 'fence', 'inspect', 'allow',
         'away', 'unreal']
for name in table:
    url = f'http://www.pythonchallenge.com/pc/hex/{name}.html'
    response, html = Read_URL(url, headers=headers)
    if response != None: print(name)
  1. 图片除了文字, 啥都没有, 所有的提示也都看了一下, 还是没有. 检查一下所有Response Headers, 看到图片载入时的'Content-Range': 'bytes 0-30202/ 2123456789' 内容有问题.
from Tool import Read_URL
from base64 import b64encode
auth = str.encode("%s:%s" % ('butter', 'fly'))
user_and_pass = b64encode(auth).decode("ascii")
headers = {'Authorization': 'Basic %s' % user_and_pass,
           'Accept': 'application/json'}
url = f'http://www.pythonchallenge.com/pc/hex/idiot2.html'
response, html = Read_URL(url, headers=headers)
print(dict(response.headers))
url = f'http://www.pythonchallenge.com/pc/hex/unreal.jpg'
response, html = Read_URL(url, headers=headers, byte=True)
print(dict(response.headers))
  1. 在 Http 1.1中规定 instance-length 指定所选资源的当前长度, 检查一下图片的大小是30203, 表示后面还有一堆数据.
Content-Range = "Content-Range" ":" content-range-spec
       content-range-spec = byte-content-range-spec
       byte-content-range-spec = bytes-unit SP byte-range-resp-spec "/"
                                 ( instance-length | "*" )
       byte-range-resp-spec = (first-byte-pos "-" last-byte-pos) | "*"
       instance-length = 1*DIGIT
  1. 2123456789 - 30203 = 2,123,426,586 代表后面还有约2G的数据, 这不合情理, 难不成要去下载2G的数据... 没办法, 还是试试, 结果是 HTTP 206 Partial Content, 代表方法不对
from Tool import Read_URL
from base64 import b64encode
auth = str.encode("%s:%s" % ('butter', 'fly'))
user_and_pass = b64encode(auth).decode("ascii")
headers = {'Authorization': 'Basic %s' % user_and_pass,
           'Accept': 'application/json',
           'Range': 'bytes=30203-2123456788'}
url = f'http://www.pythonchallenge.com/pc/hex/unreal.jpg'
response, html = Read_URL(url, headers=headers, byte=True)
  1. 在Http 1.1中撷取 paritial content, 只要加上 Range: bytes=from-to, 检查了一下, 内容是分段的, 所以一段一段找过去.
from Tool import Read_URL
from base64 import b64encode
auth = str.encode("%s:%s" % ('butter', 'fly'))
user_and_pass = b64encode(auth).decode("ascii")
headers = {'Authorization': f'Basic {user_and_pass}'}
url = f'http://www.pythonchallenge.com/pc/hex/unreal.jpg'
response, html = Read_URL(url, headers=headers, byte=True)
def next_data(start):
    global response, html
    headers['Range'] = f'bytes={start}-2123456788'
    response, html = Read_URL(url, headers=headers, byte=True)
result = []
while True:
    start = int(response.headers['Content-Range'].split('-')[1].split('/')[0])+1
    next_data(start)
    if html == None:
        break
    result.append(html)
print(result)
"""
[b"Why don't you respect my privacy?\n", b'we can go on in this way for really long time.\n', b'stop this!\n', b'invader! invader!\ n', b'ok, invader. you are inside now. \n']
"""
  1. 换了网址为invader, 出现了Yes! that's you!, 网页源代码也是那几个字, 又没了线索! 再一个byte 再试试往下找, 跳了几个都没有, 从后面试试.
start = 2123456788
prev_data(start)
result = [html]
while True:
    start = int(response.headers['Content-Range'].split(' ')[1].split('-')[0])-1
    next_data(start)
    if html == None:
        break
    result.append(html)
print(result)
"""
[b'esrever ni emankcin wen ruoy si drowssap eht\n',
 b'and it is hiding at 1152983631.\n']
"""
'esrever ni emankcin wen ruoy si drowssap eht'[::-1]
'the password is your new nickname in reverse'
  1. 后面找的字串是全倒过来的, 原来是"密码是你新的昵称倒过来", 而且密码藏在1152983631. pre_data(1152983631) 得到一大串数据, 注意到一开始的两个字PK, 应该是个压缩档
with open('D:/new.zip', 'wb') as f:
    f.write(html)
  1. 打开压缩档, 有两个档案package.pack 和readme.txt, 但是要密码, 试了1152983631, 2123456789 以及invader, 都不对! 对了, 要倒过来, redavni, readme.txt 内容似乎无疑紧要, package.pack 作什么? 再看了一遍readme.txt, 喔原来, 这就是第20的结尾, 并没有告诉你第21关的网址. 所以第20关OK 了! readme.txt 内容如下
Yes! This is really level 21 in here.
And yes, After you solve it, you'll be in level 22!

Now for the level:

* We used to play this game when we were kids
* When I had no idea what to do, I looked backwards.
3年前 评论
Jason990420

第21关 (没有网址, 没有图片)

[提示]

  • We used to play this game when we were kids 我们小时候曾经玩过这个游戏
  • When I had no idea what to do, I looked backwards. 当我不知道该怎么做时, 我往回看看.
  • package.pack

[解] 只有一个非文字的档案 package.pack

  1. 先读档案内容, 这里面好像有个字组一直重复 \x9c\x00, 把它们都去掉, 还是看不出来是什么.
>>> with open('D:/package.pack', 'rb') as f:
>>> data = f.read()
>>> data[:32]
(b'x\x9c\x00\n@\xf5\xbfx\x9c\x00\x07@\xf8\xbfx\x9c\x00\x06@\xf9\xbfx\x9c\x00'
 b'\xff?\x00\xc0x\x9c\x00\xff')
  1. google 找了file header 78 9c, 有一篇说是database ? 还有很多篇说是zlib, 所以先从zlib 开始. 解压缩了6次才不是b'x\x9c' 开头, 而是b'BZh91AY&SY \x91\xe8/+\x00v'
import zlib
new_data = data
count = 0
while new_data[:2] == b'x\x9c':
    count += 1
    new_data = zlib.decompress(new_data)
print(count)
  1. BZ 开头好像又是压缩档, 先用bzip2 检查, 结果又和上面一样一直重复压缩. 直到最后, 发现结果还是看不懂, 不是说往回再看看? 啊? 原来又是语义和字义不同, When I had no idea what to do, I looked backwards. 意思是说数据从后面开始看, 看了还是没有, 反向压缩? 试试...
import bz2
import zlib

with open('D:/package.pack', 'rb') as f:
    unpack = f.read()
count = 0
while True:
    f_char = unpack[:2]
    b_char = unpack[-2:]
    if f_char == b'BZ':
        unpack = bz2.BZ2Decompressor().decompress(unpack)
    elif f_char == b'x\x9c':
        unpack = zlib.decompress(unpack)
    elif b_char == b'ZB':
        unpack = bz2.BZ2Decompressor().decompress(unpack[::-1])
    elif b_char == b'\x9cx':
        unpack = zlib.decompress(unpack[::-1])
    else:
        print(unpack[:50])
        print(unpack[-50:])
        print(unpack[-1:-51:-1])
        break
    count += 1
    print(last_data[:50])
print(count)
  1. 终于出现不一样的b'look at your logs', logs ?? 没有logs 啊, 记录变化? 这里有变化的就是header和压缩方法, 就把这4种方法编号为1,2,3,4 , 记录下来看看. 内容就简化一下.
result = []
while ...
    if ...
        result.append('1')
    elif ...
        result.append('2')
    ...
  1. 再把最后的result 合成一个字串, 一个长长的字串, 其中1,2,3,4各出现的次数为300, 423, 0, 9, 因为4出现的次数最少, 所以把4换成换行, 另外2出现的最多, 把2换成空格.
>>> print(''.join(result).replace('4', '\n').replace('2', ' '))
      111          111      11111111    11111111    1111111111  11111111
   1111111      1111111    111111111   111111111   111111111   111111111
  11     11    11     11   11      11  11      11  11          11      11
 11           11       11  11      11  11      11  11          11      11
 11           11       11  111111111   111111111   11111111    111111111
 11           11       11  11111111    11111111    11111111    11111111 
 11           11       11  11          11          11          11   11 
  11     11    11     11   11          11          11          11    11 
   1111111      1111111    11          11          111111111   11     11 
     111          111      11          11          1111111111  11      11
  1. 将 copper 代入网址, 就看到第22关了
3年前 评论
Jason990420

第22关 http://www.pythonchallenge.com/pc/hex/copp...

file

[提示]

  • level22.jpg 图片
  • 标题 emulate 模拟, 仿真
  • 注解 or maybe white.gif would be more bright 或者 white.gif 更加明亮
  • Status Code 304 Not Modified

[解] white.gif ?

  1. 先改网址, 出来一个好像是黑色的方块200x200, 先存档再检查每一点, 结果是P模式的图片. 而且图素值的总和竟然不是0, 是8. 经检查只有(100, 100)这个点是8, 其他都是0. 当我把图片放大, 看到了一个图点在晃... 检查一下gif 内容, 共有133 个frames
>>> from Tool import Read_File, Flat_List
>>> im = Read_File('D:/white.gif')
>>> lines = []
>>> for y in range(im.height):
... line = []
... for x in range(im.width):
... line.append(im.getpixel((x, y)))
>>> lines.append(line)
>>> total = sum(Flat_List(lines))
8
>>> [(x, y, im.getpixel((x, y))) for x in range(im.width) for y in range(im.height) if im.getpixel((x, y)) ! = 0]
[(100, 100, 8)]
>>> im.n_frames
133
  1. 只好找出每个点的位置, 先假设每一帧是有一个点不是 0. 得到的结果是 x, y in [98, 100, 102]
points = []
for i in range(133):
    im.seek(i)
    point = [(x, y) for x in range(im.width)
             for y in range(im.height) if im.getpixel((x, y)) != 0]
    points.append(point[0])
  1. 133 = 19*7, 试着把X座标, Y座标, max(X座标, Y座标), min(X座标, Y座标) 的值转成字母, 排列成19*7 或7\ *19 矩阵, 看看有没有什么东西? 结果什么也看不出来...

  2. 原始图片是一个摇杆, 如果我们把那移动的的点当作摇杆在动, 那可能就是向上, 向下, 向左, 向右... 试试, 毕竟还有一个暗示是emulate . 画出了一张图, 可好像字的靠在一起来, 把移动的距离拉大一点, 结果还是一样.

x0, y0 = 50, 50
x1, y1 = points[0]
dx = dy = 0
for point in points[1:]:
    x2, y2 = point
    dx += x2-x1 if x2!=x1 else 0
    dy += y2-y1 if y2!=y1 else 0
    x0 += dx
    y0 += dy
    im_new.putpixel((x0, y0), (0, 0, 0))
    x1, y1 = x2, y2
im_new.save('D:/new_move.jpg')

file

  1. 对了, 摇杆的移动应该是和中心点来比较, 而不是和前一个移动来比较, 中心点应该是 (100,100), 结果然竟还是一样.
  2. 这些字是一笔画出来的, 得想办法把它们分开, 检查一下这些移动里最少的几个数据, 可能就是字分开的关键.
result = {}
for point in points:
    if point in result:
        result[point] += 1
    else:
        result[point] = 1
"""
{(100, 100): 5, (100, 102): 31, (102, 102): 10, (102, 100): 28, (102, 98): 9, (100, 98): 14, ( 98, 98): 5, (98, 100): 25, (98, 102): 6}
  1. 上面最少的是 (100, 100), (98, 98), 再来就是 (98, 102), 看起来最可能是 (100, 100)
from PIL import Image
im_new = Image.new('RGB', (250, 50), (255, 255, 255))
x, y = 1, 1
x0, y0 = 100, 100
dx, dy = 0, 0
for point in points:
    x1, y1 = point
    dx = x1 - x0 if x1 != x0 else 0
    dy = y1 - y0 if y1 != y0 else 0
    if dx == 0 and dy == 0:
        dx, dy = 40, 0
    x, y = x + dx, y + dy
    im_new.putpixel((x, y), (0, 0, 0))
im_new.save('D:/new_move.jpg')

file

  1. 把 bonus 代入网址, 第 23 关来了.
3年前 评论
Jason990420

第23关 http://www.pythonchallenge.com/pc/hex/bonu...

file

[提示]

  • 标题: what is this module? 这是什么模组?
  • 图片: bouns.jpg 又来了一头牛…
  • 有三段注解
    • 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. 您欠某人一个道歉吗? 现在是时候告诉他你很抱歉. 请表现出良好的态度, 尽管与此关无关。
    • it can’t find it. this is an undocumented module. 找不到它, 这是一个未记录的模块。
    • 'va gur snpr bs jung?'

[解] this is an undocumented module ? 也许不是如字面上的意思, 这是一个叫作 undocumented 的模组? google 一下, 就看到下面这一项.

Undocumented Modules — Python 3.8.2 documentationdocs.python.org › library › undoc
Undocumented Modules. Here's a quick listing of modules that are currently undocumented, but that should be documented. Feel free to contribute ...
  1. 看了一下内容, 错了, 不是叫undocumented 模组, 而是应该有说明文件, 却没有文件说明的模组列表. 其中两项都是与平台有关的, ntpath 以及posixpath, 都用来建立os.path. 既然与平台有关, 我相信不会出这样的问题, 万一平台不对, 就没得搞了.
  2. 'va gur snpr bs jung?' 可能是某种编码方式, 先字母偏移试试, 找到一句有意义, 但还是不懂什么意思的句子'in the face of what?' 面对什么?
text = 'va gur snpr bs jung?'
table1 = ''.join([chr(i+ord('a')) for i in range(26)])
for i in range(26):
    table2 = table1[i:] + table1[:i]
    trans = ''.maketrans(table1, table2)
    out = text.translate(trans)
    print(out)
"""
va gur snpr bs jung?
wb hvs toqs ct kvoh?
...
in the face of what?
...
ty esp qlnp zq hsle?
uz ftq rmoq ar itmf?
"""
  1. 图片中是一头牛, 我面对着牛 cow? bull ? 牛面对着草 grass ? 面对着地面 ground ? 面对着地板 floor ? 试了一遍网址, No !
  2. this is an undocumented module ? this ? google 一下, 还真有 'this' 模组…
Attributes of Python module `this` - Stack Overflowstackoverflow.com › questions › attributes-of-python-m...
2016518- i and c are simply loop variables, used to build the d dictionary. From the module source code: d = {} for c in (65, 97): for i in range(26): . ..
  1. import this 出现了 The Zen of Python, by Tim Peters, my god !
Help on module this:
NAME
    this
DATA
    c = 97
    d = {'A': 'N', 'B': 'O', 'C': 'P', 'D': 'Q', 'E': 'R', 'F': 'S' , 'G': ...
    i = 25
    s = "Gur Mra bs Clguba, ol Gvz Crgref\n\nOrnhgvshy vf o...bar ubaxvat ...
FILE
    d:\software\python37\lib\this.py
# this.py
s = """Gur Mra bs Clguba, .... """
d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)
print("".join([d.get(c, c) for c in s]))
  1. 这就是一段转码, 所谓的’rot13’, 每个小写字母对应后移13位的字母, 那不就是2中作的事! 试改网址, face ? this ? rot13 ? 都不对.. . 转码还是不对.
  2. 回头再看一下 this, 还有 The Zen of Python, by Tim Peters
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
...
In the face of ambiguity, refuse the temptation to guess.
...
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
  1. 在 The Zen of Python 中看到了这一句 In the face of ambiguity …, 就是这一句 ! 答案就是ambiguity ! 第24关…真伤脑筋 >.<”
3年前 评论
Jason990420

第24关 http://www.pythonchallenge.com/pc/hex/ambi...

file

[提示]

  • 标题: from top to bottom 由上而下
  • 图片: maze.png 迷宫

[解] 破解迷宫? 破解成功又如何? 这迷宫的墙还有颜色变化... 不管先解迷宫再说, 好像也没看到的提示? 由上而下? 这可说不定, 也许是上,下, 左, 右 ?

  1. 先破解迷宫再说, 搞了好几天, 总觉得结果不对, 只有左上角有一个解, 把经过了座标点取出来, 用了所有的方法, 还是不对. 又看了半天, 哎, 原来这迷宫不是走白的地方, 而是走不是白的地方, 而且起点及终点就在右上角及左下角....无语了.
  2. 使用建立树状的方法, 重新解迷宫, 因为迷宫的墙还有颜色变化, 所以将经过的点颜色保存下来
import numpy as np
from Tool import Read_File, Save_File

im = Read_File('D:/maze.png')
array = np.array(im, dtype=np.uint8)
white = (255, 255, 255 ,255)
stop = (1, im.height-1)
x, y = im.width - 2, 0
tree = {}
parent = {(x,y):None}
to_do = [(x, y)]
flag = False
while to_do != []:
    tmp = []
    for x, y in to_do:
        tree[(x, y)] = {'parent':parent[(x, y)], 'child':[]}
        if (x, y) == stop:
            flag = True
            break
        for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
            x1, y1 = x + dx, y + dy
            if (x1, y1) in tree:
                continue
            if 1<=x1<im.width-1 and 1<=y1<im.height and im.getpixel((x1, y1))!=white:
                if (x1, y1) not in tree[(x, y)]['child'] and (x1, y1)!=tree[(x, y)]['parent']:
                    parent[(x1, y1)] = (x, y)
                    tree[(x, y)]['child'].append((x1, y1))
                    tmp.append((x1, y1))
    if flag:
        break
    else:
        to_do = tmp.copy()

new_im = im.copy()
x, y = stop
data = []
while True:
    new_im.putpixel((x, y), (255, 255, 0, 255))
    data.append(im.getpixel((x, y)))
    position = tree[(x, y)]['parent']
    if position:
        x, y = position
    else:
        break
data = data[::-1]
Save_File('D:/new_maze.png', new_im)

file

  1. 看一下数据的内容, 有内容的都是偶数位的红色
>>> data[:100]
[(0, 0, 0, 255), (80, 0, 0, 255), (0, 0, 0, 255), (75, 0, 0, 255),
 (0, 0, 0, 255), (3, 0, 0, 255), (0, 0, 0, 255), (4, 0, 0, 255),
 (0, 0, 0, 255), (20, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255),
 (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255),
 (0, 0, 0, 255), (8, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), ... ]
  1. 数据取偶数位的红色, 再转成位元组, 一看前面的位元组, 正是压缩档.
>>> new_data = np.array(data, dtype=np.uint8)[1::2, 0].tobytes()
>>> new_data[:50]
(b'PK\x03\x04\x14\x00\x00\x00\x08\x00\x88\x9a\xb02\xa5\xb9\xd9\xb2\xe7G'
 b'\x00\x00\xa0L\x00\x00\x08\x00\x15\x00maze.jpgUT\t\x00\x03?\xc8\x88B\xfa'
 b'\xd1\x8a')
  1. 再来在成压缩档, 再解压缩
>>> with open('D:/maze.zip', 'wb') as f:
... f.write(new_data)
  1. 看到两个档案, mybroken.zip 打开是一个 mybroken.gif, "Checksum error, The archive is corrupt", 不管, 先看 maze.jpg
mybroken.zip
maze.jpg
  1. maze.jpg 打开一看, 上图的文字是lake, 代入网址, 终于看到第25关了. 这里还留了一个破碎的图片档, 有需要再来搞它.

file

3年前 评论
Jason990420

第25关 http://www.pythonchallenge.com/pc/hex/lake...

file

[提示]

  • 一张图片 lake1.jpg ? 还有 lake0 ? lake2 ? ...
  • 标题 imagine how they sound 想像他们是怎么发出声音的 ?
  • 注解 can you see the waves? 你看到这些波浪了吗?

[解] 没有lake0.jpg 或lake2.jpg, 或者html, 都不是, 第一个想到的winsound, 可是这个只供window 使用, 应该不是. wave ? .wav ? 那总要有档啊, 输入网址试试.

  1. 输入网址 lake1.wav, 播放了一段杂音, 不管, 先存档, 再看一下内容.
>>> with open('D:/lake1.wav', 'rb') as f:
... data = f.read()
>>> data[:50]
(b'RIFFT*\x00\x00WAVEfmt \x10\x00\x00\x00\x01\x00\x01\x00\x80%\x00\x00'
 b'\x80%\x00\x00\x01\x00\x08\x00data0*\x00\x00\xff\xff\xff\xfe\xfe\xfe')
  1. WAV 档案的前44 (或46) 位元组放置档头(header), 总区块大小T*\x00\x00, 档案格式WAVE, 子区块1标签'fmt ', 子区块1大小\x10\x00\x00\x00, 音讯格式\x01\x00, 声道数量\x01\x00, 取样频率\x80%\x00\x00, 位元组率\x80%\x00\x00, 区块对齐\x01\x00, 位元深度\x08\x00, 子区块2标签'data', 子区块2大小0*\x00\x00, 后面是音讯资料\xff\xff\xff\xfe\xfe\xfe ...

file

  1. 从音讯格式及声道数量来看, 资料应该是 little endian. 看起来格式也没问题.
block_size = 10836
block1_size= 16
sample_rate = 9600
bytes_rate = 9600
block_align = 1
bit_deep = 8
block2_size = 10800
  1. 卽然声音不对, 那就来看看后面的音讯资料, 好像没什么特别的, 再把它转成 Big Endian 试试 ... 结果变成另一种杂音
>>> data1 = list(data[44:])
>>> len(set(list(data1)))
197
>>> data2 = data[:44] + data[45::2] + data[44::2]
>>> with open('D:/new_lake1.wav', 'wb') as f:
... f.write(data2)
  1. 突然想到, lake1.wav, 应该还有其他的 wav, 试了一下, 真的有好多个, 竟然有25个
from Tool import Read_URL
i = 1
all_data = []
while True:
    response, data = Read_URL(
        f'http://www.pythonchallenge.com/pc/hex/lake{i}.wav',
        user='butter', password='fly', byte=True)
    if not data:
        break
    all_data.append(data)
    i += 1
print(len(all_data))
  1. 图片上是一个拼图, 也就是25片, 看来要把这25个声音档的数据拼在一起了, 要我们搞定wav 的格式, 肯定不太合理. 重点是该怎么拼, 拼成什么东西? 卽然是拼图, 那就先拼成图片. 先检查长度, 都是10844-44 (header 长度) = 10800 = $2^3$ x $3^3$ x $5^2$, 不知道单一张拼图的尺寸, 有点麻烦. 先假设RGB 的图片, 10800/3 = 3600, $\sqrt{3600}=60$, 先一个最的因数试试, 60x60.
>>> while True:
... if 10800%i == 0:
... print(i)
... break
... i -= 1
...
100
import numpy as np
from PIL import Image
im = Image.new(mode='RGB', size = (300, 300), color=(0, 0, 0))
for i ,d in enumerate(all_data):
    array = np.array(list(d[44:]), dtype=np.uint8)
    array.resize((60, 60, 3))
    img = Image.fromarray(array, mode='RGB')
    im.paste(img, box=((i%5)*60, (i//5)*60))
im.save('D:/new_lake.jpg')
  1. 没想到就这么简单, 答案就有了, 就是 decent

file

3年前 评论
Jason990420

第26关 http://www.pythonchallenge.com/pc/hex/dece...

file

[提示]

  • 一张图片 decent.jpg 两只猴子
  • 标题 be a man - apologize! 男子汉一点, 道歉 !
  • 注解1 you've got his e-mail 你已经收到他的电邮了.
  • 注解2 Join us at the IRC: irc.freenode.net #pythonchallenge 于 IRC, 加入我们
  • 文字 Hurry up, I'm missing the boat 快点, 我快要错过船班了.

[解] 先上 irc.freenode.net 看看, 连不上网? 电邮? 检查 http header 也没什么...

  1. 先看第24关未解的破碎图片mybroken.gif, 看能不能修复? 用了好几个软件都不能修复, 最后是使用了WinZip, 虽然不完美, 看起来却一点问题也没有. speed ?速度? 加速 ?

file

  1. 已经收到他的电邮了? 回前几关看看, 只有第19关收到了一个电邮, 内容好像也没有什么相关的. 难道要发电邮? leopold.moz@pythonchallenge.com, 我还真的发了一封Email, 等了好久都没有结果, 哈哈哈哈... 隔天早上八点, 收到了回电, 真慢... 十五小时后才回收, 还以为这招没有用.
my broken zip Re: sorry
    Never mind that.
    Have you found my broken zip?
    md5: bbb8b499a0eef99b52c7f13f4e78c24b
    Can you believe what one mistake can lead to?
  1. Can you believe what one mistake can lead to? 一个错误? 有三种可能, 有一位元组多出来了, 有一位元组少了, 有一位元组错了? 难道要一个一个位元组修改?哎, 试试吧. 又搞了半天, 还是不行, 解压缩结果, 看到当然还是那个字speed.

  2. 回头再看看, Hurry up, I'm missing the boat 快点, 我快要错过船班了. 这句不是这么翻译的啊? 是少了boat, 试试speedboat, yes, 没错, 原来第一步后就可以找到答案了.

3年前 评论
Jason990420

第27关 http://www.pythonchallenge.com/pc/hex/spee...

file

[提示]

  • 标题: between the tables 这应该不是桌子, 是表格吧?
  • 一张划船的图片 zigzag.jpg, 图上有摆动的白线, NW ? MN ? zigzag 之字形
  • 注解:
    • did you say gif?
    • oh, and this is NOT a repeat of 14
    • Join us at the IRC: irc.freenode.net #pythonchallenge
  • 一个链接: http://www.pythonchallenge.com/pc/ring/bel...

[解] 链接点进去, 要新的用户名及密码. 表格? 网址改了table[0,s,1, 2]都没有, 上面还有一句did you say gif ?, 把网址改成zigzag.jpg , 出来一张乱图. 第14关是绕一圈成一张新图. 又说不是第14关的重复, 搞不好就是类似的.

file

  1. 先按摆船桨的方向把图重组一遍, 结果还是差不多, 把图放大, 看起来好像都是一个颜色, 只是亮度不一样, 图片的模式是P, 单层, 颜色要对应调色板, 而且只有一帧. 而调色盘的频色RGB 都是一样的值.
>>> from Tool import Read_File
>>> im = Read_File('D:/zigzag.gif')
>>> print(im.size, im.mode, im.n_frames)
(320, 270) P 1
>>>palette = im.getpalette()
>>> print(palette[:50])
[37, 37, 37, 229, 229, 229, 162, 162, 162, 136, 136, 136, 59, 59, 59, 212, 212, 212, 9, 9, 9, 41, 41, 41, 24 , 24, 24, 156, 156, 156, 148, 148, 148, 112, 112, 112, 254, 254, 254, 91, 91, 91, 106, 106, 106, 49, 49, 49, 248, 248 ]
  1. 先把图片真正的颜色值, 转换出来, 也因为三个同色, 所以只要取一个颜色就可以了, 最后的内容也看不出来是什么档案, 再看一下原来的数据是如何?
>>> from Tool import Map
>>> def func(byte):
... return table[byte]
>>> table = palette[::3]
>>> raw = im.tobytes()
>>> data = bytes(Map(func, list(raw)))
>>> data[:50]
(b"\xd0\xcb\x0c\xfe<\x8bHB\xbd\x7f\xb0\xadF\xaa\xcf' ~\x8e\xa4\x0bp\xeb\x9e"
 b'\xe07\xdb\x08\x18L\xb6\x13\x0f1\x89\xca!\xb9\x97yr\xb4\xdd\xf1"\x8c\xa0/'
 b'^\xfc')
>>> raw[:50]
(b"\xd7\xd0\xcb\x0c\xfe<\x8bHB\xbd\x7f\xb0\xadF\xaa\xcf' ~\x8e\xa4\x0bp\xeb"
 b'\x9e\xe07\xdb\x08\x18L\xb6\x13\x0f1\x89\xca!\xb9\x97yr\xb4\xdd\xf1"\x8c\xa0'
 b'/^')
  1. 咦? 这两个数据好像偏移了一位元组, 再比对前后, 后面然对齐了??, 这代表数据有些地方是不一样的, 把这个位元组找出来, 记录它在data的位置.
import numpy as np
im = Read_File('D:/zigzag.gif')
im1 = np.array(im, dtype=np.uint8).flatten()[1:]
im2 = np.array(im.convert(mode='RGB').getchannel(0), dtype=np.uint8).flatten()[:-1]
result = list(np.where(im1!=im2)[0])
new = Image.new(mode='RGB', size=(im.width, im.height))
for i in result:
    new.putpixel((i%(im.width), i//(im.width)), (255,255,255,255))
new.show()

file

  1. 输入 keyword 网址, 结果来一行字符串 be busy with the unkeywords, 关键字? 还有一个东西没有处理, 就是不一样的数据. value2 不知道是什么东西, 但是 value1 应该是一个压缩的资料.
>>> value1 = bytes([im1[i] for i in result])
>>> value2 = bytes([im2[i] for i in result])
>>> value1[:20]
b'BZh91AY&SY\xe0\xaaYF\x00\x17\x9a\x11\x80@'
>>> value2[:20]
b'\x99\xbdQ\x82\xf2\x89S\x04\x15E\x047 \x04\x95\xe4N\x9b\xd5\xa8'
  1. 使用bz2 解压缩value1, 出来一大串字符串, 长度70644, 看了一下前面几个字符串. 除了乱入的../ring/bell.html 以外, 看起来像是Python 的关键字, 还有重复.
>>> string[:500]
(b'../ring/bell.html del assert repeat raise or class is exec return except pri'
 b'nt return switch from exec repeat else not while assert or class class break'
 b' except assert yield finally ../ring/bell.html assert ../ring/bell.html in i'
 b's yield and import break def ../ring/bell.html global repeat if yield pass e'
 b'xec del return def or repeat switch for else or break if global def raise la'
 b'mbda and else for ../ring/bell.html pass ../ring/bell.html elif break else i'
 b's except if class import assert is lambda co')
  1. 先断字再去掉重复的
>>> keyword = set(string.decode('ascii').split(' '))
>>> print(keyword)
{'while', 'class', 'print', 'if', 'repeat', 'for', 'del', 'or', 'and', 'yield', 'continue', 'elif', ' except', 'try', 'return', 'pass', 'finally', 'import', 'break', 'assert', 'raise', 'is', 'exec', '../ring/bell .html', 'from', 'not', 'in', 'def', 'global', 'switch', 'lambda', 'else'}
  1. 还是挺多的, 关键字? 哪个不是? 要查一下 Python 的关键字表, 或者可以使用 keyword 模组
>>> import keyword as key
>>> [item for item in keyword if not key.iskeyword(item)]
['print', 'repeat', 'exec', '../ring/bell.html', 'switch']
  1. 还有4 个词, 如果对 Python 2 熟的人, 就知道 print, exec 也是关键字, 但在 Python 3 就不是了. 试了一下, 用户名repeat, 密码 switch, 这就进到第 28 关了. 上面还有个网址, 试试... 哈, 不用试就是第 28 关的网址.
3年前 评论
Jason990420

第28关 http://www.pythonchallenge.com/pc/ring/bel...

file

[提示]

  • 一张图片 bell.png, 上面好像有斑点的条纹.
  • 标题 many pairs ring-ring
  • 文字 RING-RING-RING, say it out loud

[解] 第一眼看到的就是图片上一堆的长条纹, 放大看了看, 好像只能计算每个长条纹的长度, 再记录其颜色. 另外Ring 也是个第三方库, “Ring从代码的角度显示了一种控制缓存的方法-与存储无关”. 先从长条纹开始吧.

  1. 长条纹的长度及颜色, 图档读进来以, 发现上面的数据没什么规律, 要算长度及颜色, 就放弃了. 找一下R, G, B 三个图层, 个别.show() 了一下, 看到长条纹只在Green 层.
from Tool import Read_File
im = Read_File('D:/bell.png')
r, g, b = im.split()
  1. 实在是看不到什么特别的地方, 只好分析一下数据, 首先画了一下每一点的分布图, 看不出什么, 两点相加也没有, 两点相减, 好像有货
import numpy as np
import matplotlib.pyplot as plt
array = np.array(g, dtype=np.uint8)
x = list(range(im.width-1))
plt.plot(x, array[0,0:-1]+array[0, 1:])
plt.show()

file

  1. 再检查下两点相减的结果, 发现相减结果中, (-42, 175), 以及 (42, 153) 次数明显多了很多.
>>> check = array[0,0:-1].astype(np.int)-array[0, 1:].astype(np.int)
>>> values, counts = np.unique(check, return_counts=True)
>>> print(tuple(zip(values, counts)))
((-155, 1), (-136, 1), (-132, 1), (-128, 1), (-125, 1), (-117, 1), (-112, 1), (-70, 1), (-60, 2), (-59, 1), (-58, 1), (-53, 1), (-50, 1), (-49, 1), ( -48, 1), (-47, 3), (-46, 2), (-44, 4), (-43, 1), (-42, 175), (-41, 2), (- 40, 2), (-39, 1), (-38, 3), (-37, 2), (-36, 1), (-35, 4), (-34, 3), (-33 , 2), (-32, 3), (-30, 5), (-29, 1), (-28, 1), (-27, 1), (-25, 1), (-24, 1), (-22, 2), (-20, 2), (-19, 2), (-18, 2), (-17, 3), (-16, 3), (-15, 3 ), (-14, 1), (-13, 2), (-12, 3), (-11, 2), (-10, 5), (-9, 2), (-8, 6) , (-7, 4), (-6, 4), (-5, 6), (-4, 4), (-3, 5), (-2, 6), (-1, 9), (0, 12), (1, 5), (2, 6), (3, 5), (4, 3), (5, 8), (6, 3), (7, 2), (8 , 7), (9, 3), (10, 1), (11, 3), (12, 5), (13, 2), (14, 3), (15, 5), (16, 6 ), (17, 2), (18, 3), (19, 4), (20, 2), (21, 1), (22, 1), (23, 2), (24, 3), (25, 3), (26, 3), (27, 5), (28, 2), (29, 3), (30, 4), (31, 3), (32, 2), (33 , 1), (34, 1), (35, 4), (36, 2), (37, 4), (38, 3), (39, 2), (40, 4), (41, 4 ), (42, 153), (43, 1), (44, 3), (45, 2), (46, 3), (48, 2), (49, 2), (52, 1), (53, 1), (55, 1), (56, 1), (58, 2), (71, 1), (78, 1), (80, 1) , (85, 2), (86, 1), (91, 1), (94, 1), (97, 1), (107, 1))
  1. 再看了一下, 42, -42 都是出现在偶数位减去后面的数, 负数对我们来说, 目前是没意义的, 所以准备取绝对值, 而且42出现的次数太多, 以资讯理论来说, 表示它们能带的讯息量很低, 先移除不用.
>>> new_array = array.flatten().astype(np.int)
>>> check = np.abs(new_array[::2]-new_array[1::2])
>>> check[:100]
array([42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
       42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
       42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
       42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
       42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
       42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42])
>>> result = check[check!=42]
>>> len(result)
24
>>> bytes(result.astype(np.uint8))
b'whodunnit().split()[0] ?'
  1. whodunnit().split()[0] ? 谁知道? 谁干的? 什么啊?

WhoDunnits是一个致力于神秘的讨论小组,该小组遵循为期一年的围绕单个主题的书籍和电影计划。今年,我们将探讨当代和历史的国际奥秘。全世界都在谋杀!

唐娜·莱昂(Donna Leon)广受赞誉的系列中的第一部影片是Vignesie的Commissario Guido Brunetti。

吉多·布鲁内蒂(Guido Brunetti)是一位虚构的意大利侦探,由美国作家唐娜·莱昂(Donna Leon)创作。他是驻扎在Vignesie的意大利国家警察的特使(侦探总监),他是该城市的本地人。截至2018年,布鲁内蒂(Brunetti)是27部小说的主题:他也是基于这些小说的德国电视电影系列的主题。

>>> 'Guido Brunetti'.split()[0]
'Guido'
  1. 网址输入 guido, 就可以看到第29关了.
3年前 评论
Jason990420

29关 http://www.pythonchallenge.com/pc/ring/gui...

file

[提示]

  • 一张图片whoisit.jpg
  • 标题: silence !

[解] 好像什么都没有, 仔细看了图片, 眼镜, 放大镜, 啤酒瓶, 我直接就想到了...玻璃, glass.

  1. 输入glass, 得到了一字符串yes. this is a glass. 又没了... whoisit ? Tweety Bird ? 在开发人员工具中看到guido.html 竟然有85 行? 可看到的文字也就12 行, 其他都是空格和'\n'. 先查一下每行空格的长度.
from Tool import Read_URL, Map
url = 'http://www.pythonchallenge.com/pc/ring/guido.html'
response, html = Read_URL(url, user='repeat', password='switch')
>>> lines = html.splitlines()
>>> len(lines)
84
>>> length = Map(len, lines)[12:]
>>> buffer = bytes(length)
(b'BZh91AY&SY\xd9\xc2p\x18\x00\x00\x04\x9d\x80`\x80\x00\x00\x80 ./\x9c \x001'
 b'L\x98\x99\x06F\x112hd\x06jUd\xb9\x9e\xc6\x18\xc5\x92RH\xe5Z"\x01\xba\xa7\x80'
 b'\x7f\x8b\xb9"\x9c(Hl\xe18\x0c\x00')
  1. 又看到 BZ 开头, yankeedoodle 扬子面 ?
>>> import bz2
>>> result = bz2.decompress(buffer)
b"Isn't it clear? I am yankeedoodle!"
  1. 网址输入 yankeedoodle, 啊, 这就出来第30关了 !
3年前 评论
Jason990420

第30关 http://www.pythonchallenge.com/pc/ring/yan...

file

[提示]

  • 一张图片 yankeedoodle.jpg
  • 标题: relax you are on 30
  • 文字: The picture is only meant to help you relax
  • 注解: while you look at the csv file

[解] 没看到什么特别, 就注解里提到csv

  1. 网址中 html 改成 csv, 下载了一个 csv 档. 懒得处理, 直接前后加上 [ ], 改存成 json 档.
from Tool import Read_File
table = Read_File('D:/yankeedoodle.json')
>>> max(table), min(table)
(0.99998, 0.12154)
>>> len(table)
7367
  1. 数据看起来就像乱数产生器出来的, 有可能是一张图片, 图片的格式是浮点的也只有 'F' 模式. 先看一下图片可能的寛与高
>>> length = len(table)
>>> factor = [i for i in range(2, length) if length%i==0]
[53, 139]
  1. 先按图片的尺寸为 (53, 139), 试试, 看到一张图.
from PIL import Image
im = Image.new(mode='F', size=(53, 139), color=0)
im.putdata(table, scale=256)
im = im.rotate(-90, expand=True)
im = im.transpose(Image.FLIP_LEFT_RIGHT)

file

  1. 就按上面的方式处理 table
from Tool import Map
with open('D:/yankeedoodle.csv', 'rt') as f:
    data = f.read()
d = Map(str.strip, data.split(','))
d.append('0.00000')
length = len(d)
result = [d[i][5]+d[i+1][5]+d[i+2][6] for i in range(0, length, 3)]
string = bytes(Map(int, result))
  1. 最后的结果, 就是 grandpa, 再来就是第31关.
>>> string
(b'So, you found the hidden message.\nThere is lots of room here for a long '
 b'message, but we only need very little space to say "look at grandpa", so the'
 b' rest is just garbage. \nVTZ.l\'\x7ftf*Om@I"p]#R`cWEBZ40ofSC>OZFkRP0\\)+b?'
 b"Ir)S%Jt3f{ei%n2<FErFx~IzVm JTh =xdx++'de8C5'|>2\\/We;ib(b%d$N<2u(o$*d@.*6"
 ...)
3年前 评论
Jason990420

第31关 http://www.pythonchallenge.com/pc/ring/gra...

file

[提示]

[解] 看不到能作什么, 唯一就是一照片还有 Where am I?

  1. 上浏览器以图搜索, 找到了泰国的第三大岛苏梅岛. 试了半天, 找到了用户名 kohsamui 与密码 thailand.
Rocks Grandmother and Grandfather (Hin Ta / Hin Yai) on Koh Samui, Thailand
  1. 登入后,
    • 一张图片 mandelbrot.gif
    • 标题: UFOs ?
    • 字符串: That was too easy. You are still on 31...
    • window left="0.34" top="0.57" width="0.036" height="0.027" option iterations="128"

file

  1. 又回到原点, 曼德博图? 看来图里又藏了什么东西... 以下是算法

曼德博集合(Mandelbrot set,或译为曼德布洛特复数集合)是一种在复数平面上组成碎形的点的集合,以数学家本华·曼德博的名字命名。曼德博集合与朱利亚集合有些相似的地方,例如使用相同的复二次多项式来进行叠代。

for each pixel (Px, Py) on the screen do
    x0 = scaled x coordinate of pixel (scaled to lie in the Mandelbrot X scale (-2.5, 1))
    y0 = scaled y coordinate of pixel (scaled to lie in the Mandelbrot Y scale (-1, 1))
    x := 0.0
    y := 0.0
    iteration := 0
    max_iteration := 1000
    while (x×x + y×y ≤ 2×2 AND iteration < max_iteration) do
        xtemp := x×x - y×y + x0
        y := 2×x×y + y0
        x := xtemp
        iteration := iteration + 1
    color := palette[iteration]
    plot(Px, Py, color)
  1. 先按window left="0.34" top="0.57" width="0.036" height="0.027" option iterations="128", 画出一张曼德博图, 图的实际大小呢, 就按mandelbrot.gif的大小(640x480, P mode, 1 frame), 因为结果与mandelbrot.gif 的方向上下巅倒, 所以把y 座标给反过来放.
from PIL import Image
from Tool import Read_File
im = Read_File('D:/mandelbrot.gif')
width, height = im.size
new_im = Image.new(mode='P', size=im.size)
new_im.putpalette(im.getpalette(), rawmode="RGB")
max_iteration = 128
for x in range(width):
    for y in range(height):
        x0 = 0.34 + x*0.036/width
        y0 = 0.57 + y*0.027/height
        x1 = 0
        y1 = 0
        iteration = 0
        while (x1**2 + y1**2 <= 4) and (iteration < max_iteration):
            xtemp = x1**2 - y1**2 + x0
            y1 = 2*x1*y1 + y0
            x1 = xtemp
            iteration += 1
        new_im.putpixel((x, height-1-y), iteration-1)
new_im.show()

![image-20200421111303725](D:\ALL\TYPORA Image\image-20200421111303725.png)

  1. 两张图简直是一模一样, 肯定有一些不一样的地方, 把不一样的地方找出来.
>>> import numpy as np
>>> array1 = np.array(im).astype(np.int)
>>> array2 = np.array(new_im).astype(np.int)
>>> different = array1 - array2
>>> values, counts = np.unique(different, return_counts=True)
(array([-16, 0, 16]), array([ 1282, 305521, 397], dtype=int64))
  1. 一样的点数太多不计, 不一样的点数有1679点, 数值差有两种状态, 就把它当作两种不同的颜色, 要画图的寛与长度就从1679来算.
>>> [i for i in range(2, 1679) if 1679%i==0]
[23, 73]
>>> img = Image.new('RGB', (23,73), color=(0, 0, 0))
>>> new_array = different[different!=0].reshape((73, 23))
>>> for x in range(23):
... for y in range(73):
... if new_array[y, x]<0:
... img.putpixel((x, y), (255, 255, 255))
... else:
... img.putpixel((x, y), (0, 0, 0))
>>> img = img.resize((230, 730))
>>> img.show()

file

  1. UFOs ? 外星人留下的讯息? 在网上找到了"一样"的图片, 阿雷西博讯息(Arecibo Message), 试试, 答案是arecibo, 虽然出来的图没有标注是第32关, 但是在网页源代码中的注解you are in level 32.

file

3年前 评论
Jason990420

第32关 http://www.pythonchallenge.com/pc/rock/are...

file

[提示]

  1. 一个可点选格子的图
  2. 两个 Javascript: etch-a-scetch.js, pencil.js
  3. 标题: etch-a-scetch 蚀刻素描
  4. 字符串:
    • you are in level 32
    • Fill in the blanks
    • for warmup.txt

[解] 上面的数字明显是那一列的分开红点数, 左边的数字是那一行的分开红点数, fill in the blank ? 真点选填满, 什么事也没发生..., 蚀刻素描是1960年那个时代最著名的玩具之一, 在全球销售了超过1亿个单位. 奇怪了我就没听说过.

  1. warmup.txt 填入网址, 得到一9x9的格子, 还有每一行列的计数值, 看来是要求数织Nonogram 的解. 要按行列的计数值决定图中的格子位置. 搞了好久才搞定...计算时间0.33秒, 还挺快的. (创建评论失败:回复内容过长 ! 代码后放) file

  2. 出现一个箭头, 试试网址, Which direction? 试了一下, 发现答案是up, 不是left, 应该是x, y 变数弄反了, 这是题目太难解了, 都晕了. 又出来了一串字You want to go up? Let's scale this up then. Now get serious and solve this. 又来一个up.txt, 32x32, 还好我不是人工解题. 可是问题又来了.... 我是预先建一个单行9格的各种组合表, 有512种组合, 这里变成是一行32格的组合表, 代表有$2^{32}$ 的组合, $log(2^{32}) = 9.632$, 也就是有将近10G 的组合, 完了....前面辛苦写出来的程序又不能用了.

end = 'up.txt'
  1. 后面就不再列出新代码. 反正出来一条蛇吧, snake ? 不对, 试了好多蛇的词也都不对, 最后是python, 没想到吧, 到这还没完, 标题here we go, Congrats ! You made it through to the smiling python. "Free" as in "Free speech", not as in "free...

"Free software" means software that respects users' freedom and community. Roughly, it means that the users have the freedom to run, copy, distribute, study, change and improve the software. Thus, "free software" is a matter of liberty, not price. To understand the concept, you should think of *"free" as in "free speech," not as in "free beer"*. We sometimes call it "libre software," borrowing the French or Spanish word for "free" as in freedom, to show we do not mean the software is gratis.

— The Free Software Foundation

  1. 输入 beer, 就看到第33关, 不是啤酒, 一堆的Jack Dianels, 我的最爱, 玉米酿出来的威士忌, 好呛的味道, 久久难以忘怀...可惜能看不能喝.
3年前 评论
Jason990420
from Tool import Read_URL, Map
from datetime import datetime
from itertools import product
import numpy as np

def check_line(line):
    result = []
    count = 0
    for item in line:
        if item:
            count += 1
        else:
            if count != 0:
                result.append(count)
            count = 0
    if count != 0:
        result.append(count)
    return result

def check_rule(x0, y0):
    vlines = [item for item in combination if check_line(item)==h[x0]]
    hlines = [item for item in combination if check_line(item)==v[y0]]
    new_vlines = []
    for line in vlines:
        count = check_line(line)
        if sum(count)+len(count)-1 == height:
            array[:, x0] = line
            checked[:, x0] = True
        else:
            flag = True
            for y in range(height):
                if checked[y, x0] and line[y]!=array[y, x0]:
                    flag = False
                    break
            if flag:
                new_vlines.append(line)
    new_hlines = []
    for line in hlines:
        count = check_line(line)
        if sum(count)+len(count)-1 == width:
            array[y0, :] = line
            checked[y0, :] = True
        else:
            flag = True
            for x in range(width):
                if checked[y0, x] and line[x]!=array[y0, x]:
                    flag = False
                    break
            if flag:
                new_hlines.append(line)
    v_count = len(set([item[y0] for item in new_vlines]))
    h_count = len(set([item[x0] for item in new_hlines]))
    if v_count==1 and h_count==1 and new_vlines[0][y0]==new_hlines[0][x0]:
        array[y0, x0] = new_vlines[0][y0]
        checked[y0, x0] = True
    elif len(new_vlines) == 1 and new_vlines[0][y0] in [item[x0] for item in new_hlines]:
        array[y0, x0] = new_vlines[0][y0]
        checked[y0, x0] = True
    elif len(new_hlines) == 1 and new_hlines[0][x0] in [item[y0] for item in new_vlines]:
        array[y0, x0] = new_hlines[0][x0]
        checked[y0, x0] = True

def pre_check():
    for x in range(width):
        vlines = [item for item in combination if check_line(item)==h[x]]
        if vlines != []:
            v_sum = Map(sum, list(zip(*vlines)))
            for y in range(height):
                if v_sum[y] == len(vlines):
                    array[y, x] = 1
                    checked[y, x] = True
                elif v_sum[y] == 0:
                    array[y, x] = 0
                    checked[y, x] = True
    for y in range(height):
        hlines = [item for item in combination if check_line(item)==v[y]]
        if hlines != []:
            h_sum = Map(sum, list(zip(*hlines)))
            for x in range(width):
                if h_sum[x] == len(hlines):
                    array[y, x] = 1
                    checked[y, x] = True
                elif h_sum[x] == 0:
                    array[y, x] = 0
                    checked[y, x] = True

def check():
    for x in range(width):
        for y in range(height):
            check_rule(x, y)

def Read(end):
    global array
    url = root + end
    response, html = Read_URL(url, user='kohsamui', password='thailand')
    lines = [line for line in html.splitlines() if not(line.startswith('#') or len(line)==0)]
    w, h = size = tuple(Map(int, lines[0].split(' ')))
    horizontal = [Map(int, line.split(' ')) for line in lines[1:1+w]]
    vertical = [Map(int, line.split(' ')) for line in lines[1+w:]]
    return w, h, horizontal, vertical

root = 'http://www.pythonchallenge.com/pc/rock/'
end = 'warmup.txt'
width, height, h, v = Read(end)
now = datetime.now()
checked = np.zeros((height, width)).astype(np.bool)
array = np.zeros((height, width))
v_sum = Map(sum, v)
h_sum = Map(sum, h)
combination = list(map(list, product([0, 1], repeat=width)))
pre_check()
not_finished = True
while np.count_nonzero(checked == 0) != 0 and not_finished:
    check()
if np.count_nonzero(checked == 0) == 0:
    print("solution is")
    print(array)
else:
    print("There's no solution !")
print(datetime.now()-now)

from PIL import Image
new_im = Image.new(size=(width, height), mode='RGB', color=(255, 255, 255))
for x in range(width):
    for y in range(height):
        if array[y, x] == 1:
            new_im.putpixel((x, y), (0, 0 ,0))
new_im = new_im.resize((width*20, height*20), resample=Image.NEAREST)
new_im.show()
3年前 评论
Jason990420

第33关 http://www.pythonchallenge.com/pc/rock/bee...

file

[提示]

  • 一张图片 beer1.jpg
  • 标题: 33 bottles of beer on the wall 墙上有33瓶啤酒
  • 字符串: If you are blinded by the light, remove its power, with its might. Then from the ashes, fair and square, another truth at you will glare. 如果您被灯光蒙蔽了,请用力断开它的电源。然后,在灰烬中,你身上的另一个真理就会正大光明地光亮耀眼。

[解] 33瓶啤酒? beer1.jpg?

  1. beer2.jpg 显示 no, png, 又改了beer2.png, 检查数据, 移位相减, 从单色变成 RGB 都没有结果.

file

>>> from Tool import Read_File
>>> im = Read_File('D:/beer2.png')
>>> im.mode, im.size
('L', (138, 138))
  1. If you are blinded by the light, remove its power, with its might. 这是要移除白点吗? 没 255 的白点啊, 最大就194, 移除?
>>> from Tool import Read_File
>>> im = Read_File('D:/beer2.png')
>>> import numpy as np
>>> array = np.array(im, dtype=np.uint8)
>>> values, counts = np.unique(array, return_counts=True)
>>> values
array([ 1, 2, 7, 8, 13, 14, 19, 20, 25, 26, 31, 32, 37,
        38, 43, 44, 49, 50, 55, 56, 61, 62, 67, 68, 73, 74,
        79, 80, 85, 86, 91, 92, 97, 98, 103, 104, 109, 110, 115,
       116, 121, 122, 127, 128, 133, 134, 139, 140, 145, 146, 151, 152,
       157, 158, 163, 164, 169, 170, 175, 176, 181, 182, 187, 188, 193,
       194], dtype=uint8)
>>> counts
array([1532, 232, 963, 189, 724, 329, 549, 243, 144, 424, 119,
        328, 126, 339, 126, 357, 107, 225, 79, 609, 181, 356,
         70, 298, 23, 164, 26, 354, 47, 341, 139, 257, 104,
        505, 192, 224, 114, 310, 32, 183, 238, 198, 117, 327,
        110, 342, 118, 342, 145, 323, 152, 324, 161, 323, 175,
        317, 183, 317, 171, 337, 198, 318, 241, 283, 1348, 272],
      dtype=int64)
  1. 先移除194, 取寛长为76x247, 247x76, ... 都不对.
>>> new_data = [d for d in list(im.getdata()) if d<194]
>>> [i for i in range(2, len(new_data)) if len(new_data)%i==0]
[2, 4, 13, 19, 26, 38, 52, 76, 247, 361, 494, 722, 988, 1444, 4693, 9386]
>>> import math
>>> math.sqrt(len(new_data))
137.0109484676316
>>> Image.new(mode='L', size=(76, 247)).putdata(new_data).show()
  1. 再移除193的点, 长度正好是132x132, 结果图与原图差不多, 就尺寸变小了
>>> new_data = [d for d in list(im.getdata()) if d<193]
>>> math.sqrt(len(new_data))
  1. 原图尺寸为138x138, 掉去两个最高亮度后, 尺寸变成136x136, 这肯定不是巧合. 里面还是一个X 字. 再缩呢? 缩多少次呢? 33瓶啤酒? 应该是一直到X 不见吧.
array = np.array(im, dtype=np.uint8).flatten()
main = Image.new(mode='L', size=(138*7, 138*5))
for i in range(33):
    values, counts = np.unique(array, return_counts=True)
    array = array[array<values[-2]]
    s = int(math.sqrt(array.shape[0]))
    im_array = array[:s**2].reshape((s, s))
    new_im = Image.fromarray(im_array, mode='L')
    main.paste(new_im, ((i%7)*138, (i//7)*128))
main.show()

file

  1. 上面有些字还有框在, 应该就是哪些字了, gremlins, 这就通关了, 没有了, 没有下一关了. 刚开始还挺好玩的, 到后面就不好玩了, 简直就像是情报员, 把资讯隐藏起来, 等着被解码. 常常会找到没有目标下手. 不如给个料, 指定方法, 你学会某项Python 技能, 自然就能通关. 就这样, 结束了.
3年前 评论
Coolest 3年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!