Python Challenge实录(更新中)

Python Challenge(http://www.pythonchallenge.com/)是一个在线Python解谜游戏,利用编程来解谜,看上去很有趣的样子微笑。当然,鉴于我的Python还只到粗通基本语法的程度,闯关的姿势一定很难看就是了偷笑,但重在参与,重在学习嘛,所以这篇博文更多是一种学习过程的记录。Now it's time for the riddles~

第零关

网址:http://www.pythonchallenge.com/pc/def/0.html



屏幕上显示这样一张图片,下面的提示为“Try to change the URL address.”嗯,作为热身,还是a piece of cake. 根据提示,把图片中的数值算出来,然后把它填入网址中即可。在Python中,**为幂运算符,将2**38输入shell中,得到274877906944,然后将网址改为http://www.pythonchallenge.com/pc/def/ 274877906944.html,回车,就进入了第一关。

第一关

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



屏幕上显示这样一张图片,下面的提示为“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.

根据图片,这里涉及到凯撒密码,将以上字符串中的字母都依字母表顺序顺移两位,应该就能得到明文。密文很撩人,方法很简明,惹得我一时有点手动推算的冲动。不过且慢,好歹这也是一个programming riddle,如果没有点programming,总觉得胜之不武不是?

字符在计算机系统中被编码成数字。最早出现也是现今最通用的编码标准是ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)码,其将包括英文字母、阿拉伯数字、标点符号、特殊符号等在内的128个字符与整数一一对应,参见 ASCII码对照表(勘误:0对应的控制字符是NUL而非NUT)。从表中我们看到,大写字母A-Z分别由十进制数65-90表示,而小写字母a-z分别由十进制数97-122表示。为了便于字符和其对应整数之间的转化,Python提供了两个函数ord和chr,前者返回字符对应的ASCII码整数,后者返回整数在ASCII码表中对应的字符。有了这些知识,我们就可以着手解决这个问题了。大体来说,我们的任务就是将字符串中的每一个字符i替换成chr(ord(i)+2),然而,为了满足“周而复始”的要求,即chr(ord('z')+1)='a',我们需要用到求余运算%, 并且考虑到a-z的整数范围为[97,122],最终对字符i的操作便可表达为chr((ord(i)+2-1)%122+1+(ord(i)+2-1)/122*96),其中(ord(i)+2-1)%122+1保证了ord(i)+2在越过边界122之后会自动从1继续计数,但我们需要其计数的起始值为97,于是加上修正(ord(i)+2-1)/122*96,整数除法的性质保证了该项只在ord(i)+2“越界”时起作用。于是完整的Python代码如下:
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()函数的说明。


意思就是说,给maketrans函数两个同样长度的字符串frm和to,它就会生成一个翻译表字符串,其可以被用作translate函数的第二个参数,指示translate按照该规则来翻译字符串s。于是,第二个版本的代码如下:
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'


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值