Commited: 50+ воркшопів з ТОПами тестування: M.Bolton, G.Bahmutov, T.King, R.Desyatnikov, J.Bach. Лише за $40 на рік та економія $15 до 20 квітня
×Закрыть

Вызов на Python-игры

После небольшого периода обучения, всегда становится интересно проверить свои знания. Так произошло и со мной, в ходе изучения Python. Немного поискав по просторам Интернета я нашел следующий сайт: www.pythonchallenge.com. После недолгого ознакомления с ним я понял что этот сайт — задачка даже поувлекательней многих компьютерных игр.

Внимание! Прежде чем читать дальше, я настоятельно рекомендую всем попытаться справится с задачами самостоятельно. Опыт и полученное вами удовольствие будут несравненно выше.

Для тех же, кто продолжает читать дальше, я начну повествование о загадках этого сайта и их прохождении. Я обычно буду приводить 2 варианта решения: мой собственный, который сразу приходит в голову и «идеальный», чаще намного более короткий и непонятный.

Уровень 0.

По сути разминочный. Нам предлагается вычислить 2^38 степени. Нет ничего проще:

 

>>> 2**28
274877906944L
Подставив это число вместо имени файла в URL страницы (www.pythonchallenge.com/pc/def/274877906944.html) попадаем на следующий уровень.

Уровень 1.

То что нам предложено сделать на этом уровне — по сути является шифрованием текста с помощью шифра Цезаря, то есть циклической заменой буквы алфавита на другую. Причем проделывать надо это с буквами, не меняя знаки препинания и т.п.

Мое решение было таково:

 

from string import ascii_lowercase as alpha

str = "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."

res =""
for char in str:
if char in alpha:
char = alpha [(alphabet.find(char) + 2) % 26]
res = res + char

print res
Я просто импортировал константу ascii_lowercase из модуля string, содержащую все прописные латинские буквы алфавита, и прошелся по строке, меняя символы на отстоящие от них на 2 позиции «вверх». Деление по модулю на 26 помогло обработать последние буквы алфавита, которые без него «вылетели» бы в неалфавитные символы.

Получилась подсказка, предостеригающая от проделывания подобной операции вручную и рекомендующая применить операцию у имени файла в URL страницы.

С легкостью проделав это получаем ссылку, ведущую нас на следующий уровень.

Более «крутой» вариант решения задачи. На него натолкнуло чтение текста расшифрованной подсказки. Как водится, все нужные вещи уже придуманы до нас. Так модуль string в Python содержит функцию translate, занимающуюся заменой символов строки по словарю (а в новых версия строки имеют еще и метод translate). Словарь же строится функцией maketrans.

 

import string

trans = string.maketrans(string.ascii_lowercase,
string.ascii_lowercase[2:]+string.ascii_lowercase[:2])

str = "g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypcdmp. 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."
print str.translate(trans)
Уровень 2.

Как известно, OCR означает Optical Character Recognition, то есть оптическое распознавание символов. Нам предлагают распознать символы из каши, размещенной в исходнике страницы. Заглянув туда мы видим огромный массив символов, который лучше всего сохранить в файл.

Подсказки советуют найти «редкие» символы. Как определить их редкость? Да проще простого — составить программку для подсчета частоты появления символов. Например такую.

 

text = open('ocr.txt','r').read()

for chr in text:
cnt = text.count(a)
if cnt < 30:
print chr, ":", cnt
Запускаем, ждем... И видим, что редкими являются все символы алфавита, в отличии от собачек, диезов, скобок и прочего мусора.
Остается сущая мелочь — выделить из файла только алфавитные символы. Сделать это весьма просто.

 

text = open('ocr.txt','r').read()

res =""
for char in text:
if char.isalpha():
res = res + char

print res
После выполнения программы получаем слово equality, что с ним делать, надеюсь, уже понятно.

Фильтрацию файла можно проделать также в функциональном стиле:

 

filter(lambda x: x in string.letters, text)
Уровень 3.

Этот уровень встречает нас красивой картинкой со свечками и подсказкой что нам нужна одна маленькая буква, окруженная с обеих сторон 3 большими.

На подсказку о решении наводит заголовок страницы. Необходимо использовать регулярные выражения.

Вот решение «влоб».

 

import re

text = open('equality.txt','r').read()

pattern = re.compile('[a-z][A-Z][A-Z][A-Z]([a-z])[A-Z][A-Z][A-Z][a-z]')

print ''.join(pattern.findall(text))
В принципе, кроме упрощения регулярки (я пока в этом не силен) по-моему тут ловить особо нечего.

Эта программа в результате дает нам следующее ключевое слово: linkedlist

Уровень 4.

Нас приглашают «проследовать» по адресу linkedlist.php.
При щелчке по картинке, нас отправляют по следующему адресу (linkedlist.php?nothing=12345)

Подставив на место параметра nothing число 92512, получаем следующее число.
Особо упорные могут продолжить, но так сидеть придется очень долго.

Первой мыслью было написать переборщик, вызывающий скрипт с параметрами от 1 до, скажем, 100000. Но прикинув требуемый объем времени от этой идеи пришлось отказаться.

Вернувшись к linkedlist.php и заглянув в его исходники, можно найти подсказку про использование urllib, так как перебирать придется долго.

Пишем, как водится, программку.

 

import urllib, re

nothing = '12345'

findnothing = re.compile(r"nothing is (d+)").search

for i in xrange(400):
url = "<a title="Linkification: http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=%s" class="linkification-ext" href="http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=%s">http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=%s</a>" % nothing
page = urllib.urlopen(url).read()
match = findnothing(page)
if match:
nothing = match.group(1)
print "   going to", nothing
else:
break
Программа получает страницу, пытается получить из нее следующее значение nothing.
Если мы получаем что-то необычное, то программа выведет это на экран.

Запустив ее работать, мы обнаружим, что при nothing равном 92118, выводится сообщение.

Yes. Divide by two and keep going.
Разделив 92118 пополам, получаем новое «стартовое» число и запускаем «поиск» снова.

После 10 проходов откроется доступ к следующему уровню. Ключевое слово — peak.

Сначала я пытался обойтись без регулярных выражений, но например число 61066 содержит ловушку.

Это всего лишь первые 4 задачи из более чем 3 десятков. Так что продолжение обязательно следует :-)

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

👍НравитсяПонравилось0
В избранноеВ избранном0
Подписаться на автора
LinkedIn

Похожие статьи




Підписуйтесь: iTunes | Google Podcast | YouTube


3 комментария

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

Я когда-то начал писать про то, как сделать pythonchallenge.com на Haskell: http://users.livejournal.com/_...Завис на 29-м уровне и подзабросил. Надо бы, наверное, закончить.

Привет, cleg, ты таки решил вести программистский блог? Я думал, ты хочешь на своем хосте, а ты готовую систему юзаешь

а я попробовать решил: -) сайт хороший, так что — пробую. может дорасту и до собственного со временем.

По сабжу, интересно, тыкал недавно питон, немного синтаксисом на бейсик похож

чем???: -) от этого я не понимаю...

Привет, cleg, ты таки решил вести программистский блог? Я думал, ты хочешь на своем хосте, а ты готовую систему юзаешь: (По сабжу, интересно, тыкал недавно питон, немного синтаксисом на бейсик похож:)

Подписаться на комментарии