Python Challenge: 一个解密游戏
Python Challenge是一款解密游戏,有10个关卡,每个关卡都需要破解密码或者找到隐藏的信息。整个游戏旨在锻炼人们的求解能力和编程能力,而且几乎所有的谜题都可以通过Python语言来解决。下面我们来详细介绍一下这个神秘的游戏。
第一关:找到隐藏文件
第一关是从http://www.pythonchallenge.com/第0关开始的,游戏提示是:
\"Hint: try to change the URL address.\"
翻译过来就是:尝试修改URL地址。如果我们尝试在URL的最后输入\"1\",那么我们就会看到某个图片和一个文本信息,这个信息告诉我们:\"you need to change the URL\"
我们再在URL的最后输入\"2\",这时又会看到一个图片和一个文本信息,这个信息告诉我们:\"it's not the first time, don't use the comments to help the one with 'a';\"。
最后我们将URL改为\"http://www.pythonchallenge.com/pc/def/ocr.html\",我们就会看到最终的提示:Find rare characters in the mess below:
这时我们可以通过Python来解决这个问题,代码如下:
``` import urllib2 import re url = 'http://www.pythonchallenge.com/pc/def/ocr.html' page = urllib2.urlopen(url).read() data = re.findall('', page, re.DOTALL)[-1] result = ''.join(re.findall('[A-Za-z]', data)) print result ```运行程序后输出的结果为\"equality\"
第二关:通过图片找规律
第二关的提示是:提示:将URL中的“0”换成“1”,重复阅读页面中的信息。这时我们将URL改为“http://www.pythonchallenge.com/pc/def/274877906944.html”,看到一张图片和一段文本:“yes. find the phone and you can call it with his father's phone number”.
我们先来看这张图片,图片里面有一些黑色的小方块,我们要在这些小方块中找到规律,如果把这些方块排成一行,会发现每个方块都是20 * 20像素的。那么我们可以将这个图片宽度缩小20倍,高度缩小20倍,这样就能更方便地看到每个方块的颜色了。
代码如下:
``` from PIL import Image image = Image.open('c:/temp/maze.png') w, h = image.size maze = [] for y in range(h): row = [] for x in range(w): p = image.getpixel((x, y)) if p[0] + p[1] + p[2] < 255 * 3: row.append(1) else: row.append(0) maze.append(row) print maze ```这时,我们可以将黑色像素点视为墙,白色像素点视为路,那么整个图片就变成了一个迷宫。我们只需要找到从起点到终点的一条路,就能找到答案。迷宫中包含多个峡谷和岛屿,因此可以使用DFS(深度优先搜索)算法来解决这个问题。
代码如下:
``` def dfs(x, y, maze, path): if maze[y][x] == 0: return False path.append((x, y)) if x == len(maze[0]) - 1: return True maze[y][x] = 0 if y > 0 and dfs(x, y - 1, maze, path): return True if y < len(maze) - 1 and dfs(x, y + 1, maze, path): return True if x > 0 and dfs(x - 1, y, maze, path): return True if x < len(maze[0]) - 1 and dfs(x + 1, y, maze, path): return True path.pop() return False path = [] dfs(1, 0, maze, path) print path ```代码运行后会输出一个坐标序列,我们依次加上偏移量(1, 1),再将其作为URL传递给下一步,URL为:\"http://www.pythonchallenge.com/pc/def/peak.html\"。
第三关:学习Pickle模块
第三关的提示是:提示:yes! pickle!
我们可以得到一张图片,图片中包含多个英文单词,看起来毫无头绪。
这时候我们需要了解Python的一个模块:Pickle。这个模块可以将Python数据对象序列化,然后将其写入文件中。我们可以读取pickle文件,将其反序列化,使其返回Python原生数据类型。
我们来看一下下面这段代码:
``` import pickle with open('banner.p', 'rb') as f: banner = pickle.load(f) for line in banner: print ''.join(ch * n for ch, n in line) ```这段代码中,我们首先通过pickle模块将banner.p文件中的字符串序列化,然后将其反序列化回Python对象,得到一个形如((字符, 数量), ...)的元组。接下来,我们一行一行遍历元组,将相应数量的字符连成一行字符串,输出出来。
运行该程序后,我们就能得到一堆ASCII码字符,但是这些字符看起来又不像是密码或者什么其他的信息。但如果将这些字符变得更大,就可能会看出一些规律。我们可以用OpenCV库来放大并归一化结果:
``` import cv2 import numpy as np with open('banner.p', 'rb') as f: banner = pickle.load(f) chars = [] for line in banner: s = ''.join(ch * n for ch, n in line) chars.append(s) max_len = max(len(s) for s in chars) width, height = (max_len, len(chars)) img = np.zeros((height, width, 3), np.uint8) for y, s in enumerate(chars): for x, c in enumerate(s): img[y][x] = (ord(c), ord(c), ord(c)) img = cv2.resize(img, None, fx=10, fy=10) cv2.imshow('Image', img) cv2.waitKey(0) cv2.destroyAllWindows() ```运行该程序,我们会得到一张 ASCII 色块图,其中显示出了\"channel\"。这儿需要您将url中的 \"banner.p\" 更换为 \"channel.p\"。
第三关的答案就藏在这张图片中了。
这样,我们就顺利完成了Python Challenge的三道题目。在消磨业余时间的过程中,锻炼自己的编程能力,不失为一个好方法。当然,Python Challenge还远远不止这些,在后续的挑战中,会逐渐增加难度,出现的问题也会更复杂,但我们可以逐渐掌握更多的Python技能,成为真正的Python专家。