Python Challenge(http://www.pythonchallenge.com/)是一个在线Python解谜游戏,利用编程来解谜,看上去很有趣的样子。当然,鉴于我的Python还只到粗通基本语法的程度,闯关的姿势一定很难看就是了,但重在参与,重在学习嘛,所以这篇博文更多是一种学习过程的记录。Now it's time for the riddles~
第零关
网址:http://www.pythonchallenge.com/pc/def/0.html
第一关
网址:http://www.pythonchallenge.com/pc/def/map.html
initStr = '''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. '''
fnlStr = ''
for i in initStr:
if 97 <= ord(i) <= 122:
i = chr((ord(i)+1)%122+1+(ord(i)+1)/122*96)
fnlStr += i
print fnlStr
运行得到的明文字符串如下:
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.
嗯,第一反应是被打脸了。。。其次学到了一招string.maketrans()。于是立马在shell中输入help()进入帮助,查阅string.maketrans()函数的说明。
import string
initStr = '''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. '''
transtable = string.maketrans('abcdefghijklmnopqrstuvwxyz','cdefghijklmnopqrstuvwxyzab')
fnlStr = string.translate(initStr, transtable)
print fnlStr
可以看到,maketrans+translate的使用,一下子把字符转换的直观性和灵活性带到了新的层次,不用像之前那样构造一个纠结的转换函数chr((ord(i)+2-1)%122+1+(ord(i)+2-1)/122*96)了。现在回到那段明文字符串,和谜底有关的是最后一句话“now apply on the url”。哦,原来需要我们解码的是网址中的map,比起复制粘贴还是手动比较快,ocr,回车!
第二关
网址: http://www.pythonchallenge.com/pc/def/ocr.html图片上是字迹模糊的书页,下面的提示为:recognize the characters. maybe they are in the book, but MAYBE they are in the page source. 便去查看网页源码,果然有玄机:
rare characters,顾名思义,就是和这堆乱码中的大多数成员都不同的字符。根据前面的经验,猜想这个谜题的结果也是一个有意义的英文单词,那么,就试试在这片字符森林中寻找英文字母吧!于是我瞪大了眼睛认真搜索,猛地发现这段乱码长达1000多行,断然放弃,这种dirty work显然更适合计算机。因为字符串很长,为了保持代码的整洁,就将其保存到文件,由程序自动读取。之后的操作很简单,for循环遍历其中所有字符,遇到字母输出就是了。Python代码如下:
strFile = open("ocr.txt",'r') #将源码中的字符串保存到ocr.txt文件
wholeStr = strFile.read() #一次读取ocr.txt的全部内容,并保存到字符串wholeStr中
fnlStr = ''
for i in wholeStr:
if (65 <= ord(i) <= 90) or (97 <= ord(i) <= 122):
fnlStr += i
print fnlStr
运行结果为equality,输入URL试试,解锁成功!
第三关
网址:http://www.pythonchallenge.com/pc/def/equality.html
图片为
提示为:One small letter, surrounded by EXACTLY three big bodyguards on each of its sides. 我们要找的是小写字母,要求是其两边都正好邻接三个大写字母。面临的第一个问题就是需要我们搜寻的字符串在哪里?根据上一关的经验,不妨去看看页面源代码,果不其然,其中又藏着1000+行的字符串,开头部分如下
和上一关一样,我们选择将该字符串保存到一个文件中,然后让程序自动读取。只是,和上一关比起来,这里对目标字符的要求明显要花巧得多。也许,这种类型的问题在Python中有非常简洁的处理方法,只是我对Python还并不熟,便先用自己的方式来解决这个问题——像在C中一样,逐个逐个字符处理。当然,这种操作肯定很啰嗦,不过,过关之后就能看到参考答案,到时再好好学习一下也不错。那么就舍近求远一次吧!Python代码如下:
strFile = open("equality.txt",'r')
wholeStr = strFile.read() #一次读取equality.txt的全部内容,并保存到新建的字符串wholeFile中
countLeft = 0 #紧挨着小写字母左侧的大写字母字符串长度
countRight = 0 #紧挨着小写字母右侧的大写字母字符串长度
left3 = False #当前的小写字母左侧是否毗邻由三个大写字母组成的字符串
leftCap = False #当前字符的左边是否是大写字母
resultTemp = '' #为最终结果做预备的临时字符串
result = '' #最终输出的字符串
for i in wholeStr:
# 如果i是大写字母且其左侧为小写字母
if (not leftCap) and (65 <= ord(i) <= 90):
leftCap = True #针对下一个循环更新leftCap值
# 如果i左侧的小写字母左侧紧挨着三个大写字母,则该小写字母成为我们的候选字符,且我们开始对其右侧的大写字母计数
# 否则忽略该小写字母,将i视为下一个潜在小写字母左侧字符串中的元素
if left3:
countRight += 1
else:
countLeft += 1
# 如果i是大写字母且其左侧也为大写字母
elif leftCap and (65 <= ord(i) <=90):
if left3 and (countRight < 3):
countRight += 1
# 若countRight>3,则中断countRight的记录,但把它作为countLeft继续计数
elif countRight == 3:
countLeft = countRight
countRight += 1 #令countRight=4, 以规避第34行countRight==3的判断
countLeft += 1
resultTemp += '0' #resultTemp中的'0'代表其之前的字符不输出到result
else:
countLeft += 1
# 如果i是小写字母
elif (122 >= ord(i) >= 97):
leftCap = False #针对下一个循环更新leftCap值
if (countLeft == 3) or (countRight == 3):
left3 = True
resultTemp += i #满足left3的要求,则输出到resultTemp
elif left3:
left3 = False
resultTemp += '0' #不满足right3的要求,则向resultTemp输出'0',意义同前
countLeft = 0
countRight = 0
#从resultTemp得到result,即忽略resultTemp中的'0'及其之前的字符
prev='0'
for j in resultTemp:
if (j != '0') and (prev != '0'):
result += prev
prev = j
if (prev != '0') and (countRight == 3):
result += j
print result
运行之后得到的结果是linkedlist,成功过关。不过,正如前面说的,我先来瞅瞅它的官方解答——原来是wiki,可能是世界各地的player自发编辑而成的吧,其内容之详尽,思路之多样,让我大开眼界。其中最简洁的解法用到了Python中的正则表达式模块re:
>>> import re
>>> ''.join(re.findall('[a-z][A-Z]{3}([a-z])[A-Z]{3}[a-z]', wholeStr))
'linkedlist'