Вызов на Python-игры (часть 4)
Продолжение. Начало тут.
Внимание! Прежде чем читать дальше, я настоятельно рекомендую всем попытаться справится с задачами самостоятельно. Опыт и полученное вами удовольствие будут несравненно выше.
Уровень 15
Картинка на уровне содержит календарь, но дата на нем «восстановлению не подлежит». Точнее подлежит, но не вся и не полностью. Заголовок вопрошает «кого?». Внимательное изучение подсказок дает нам несколько ключей к решению.
- Маленький календарь на февраль в правом нижнем углу большого — видно что год високосный.
- В исходнике HTML — «он не самый младший, он второй» и просит «купить цветы на завтра». Видимо у «кого-то» — день рождения 27 числа.
- Выделенная дата — 26 января, понедельник.
Сначала я сделал программку, которая просто выдавала список подходящих годов. (27 января должно было быть вторником)
from datetime import date for year in range(1006, 2000, 10): dat = date(year, 1, 27) if dat.weekday() == 1: print datПосле этого я решил поискать эти даты в Google. С третей же даты мне повезло,
Уровень 16
На этом уровне мы встречаем странную картинку, состоящую из серого шума. Заголовок страницы просит «помочь выровнять их». Выравнивать видимо предполагается розовые черточки, благо их как раз по одной в ряду.
Опять взявшись за PIL, я составил следующую программу.
import Image srcpix = Image.open('mozart.gif').load() dest = Image.new('L', (640, 480), 0) destpix = dest.load() for y in xrange(480): for x in xrange(640): # 195 – розовый цвет, ищем столбец в котором начинается черточка if srcpix[x, y] == 195: # сдвигаем ряд for z in range(640): destpix[z, y] = srcpix[(x+z) % 640, y] dest.show()Получается очень симпатичная, хотя и не цветная картинка со словом romance, проводящим нас на следующий уровнь.
Уровень 17
На этом уровне пришлось попотеть и вспомнить почти все, что встречалось уже раньше.
Для начала, картинка отсылает нас на 4 уровень (те же фигурки с пилой), но печеньица рекомендуют обратить внимание на cookies.
Смотрим, и видим, что нам рекомендуют использовать в этот раз вместо nothing параметь bysunothing. Пробуем, и видим, что на каждой странице имеется cookie с параметром info.
Собираем все печеньица, следуя по ссылкам как на 4 уровне, и получаем 118 байтную строку. Для начала, ее необходимо расшифровать функцией unquote_plus модуля urllib, а потом провести декомпрессию используя модуль bz2.
Получится сообщение, в котором нас просят позвонить его отцу и сказать что цветы уже в пути.
Под отцом, видимо, подразумевается Леопольд Моцарт. Чтобы позвонить, надо узнать номер телефона. А телефонную книгу мы уже использовали в уровне 13. Вызываем XMLRPC сервер и узнаем номер телефона
В итоге у меня получилась следующая программа.
Часть первая, для поиска сообщения аналогична программе с уровня 14.
import urllib2, re, bz2 nothing = '12345' findnothing = re.compile(r"nothing is (d+)").search out = "" for i in xrange(400): url = "<a title="Linkification: http://www.pythonchallenge.com/pc/def/linkedlist.php?busynothing=%s" class="linkification-ext" href="http://www.pythonchallenge.com/pc/def/linkedlist.php?busynothing=%s">http://www.pythonchallenge.com/pc/def/linkedlist.php?busynothing=%s</a>" % nothing req = urllib2.Request(url) res = urllib2.urlopen(req) page = res.read() # если заголовок содержит cookie, читаем его if res.headers.has_key('Set-Cookie'): cookie = res.headers['Set-Cookie'] matches = re.findall('(?si)info=(.*?);', cookie) out += urllib.unquote_plus(matches[0]) print out match = findnothing(page) if match: nothing = match.group(1) print page else: break print bz2.decompress(out) print outЧасть вторая, для разговоров с отцом Моцарта
import urllib message = "the flowers are on their way" url = "<a title="Linkification: http://www.pythonchallenge.com/pc/stuff/violin.php" class="linkification-ext" href="http://www.pythonchallenge.com/pc/stuff/violin.php">http://www.pythonchallenge.com/pc/stuff/violin.php</a>" req = urllib2.Request(url, headers={'Cookie': 'info=' + urllib.quote_plus(message)}) print urllib2.urlopen(req).read()После выполнения второй программы, мы получаем от Моцарта-страршего совет не забыть про воздушные шарики (baloons), что и выводит нас на следующий уровень.
Уровень 18
Перед нами две картинки и вопрос — видим ли мы разницу. Подсказка в исходном коде страницы говорит, что все очевидно. Я попробовал самое очевидное решение — подставить слово яркость (brightness) в URL.
Картинка не изменилась, но поменялся текст подсказки, указывая нам на файл с данными.
Скачав и разархивировав его, я стал думать что же делать дальше.
Заголовок, вопрошающий «можем ли мы указать разницу» натолкнул на идею использовать для сравнения левой и правой половин файла модуль difflib. В этом модуле имеется функция ndiff, сравнивающая два массива строк и возвращающая результат в виде строк, начинающихся с одного из символов: + если строка есть в первом файле, — если она есть во втором и " " если обе строки содержат файл.
Это наталкивало на мысль разделить исходный файл на 3 потока, используя этот признак в качестве ключевого. Так я и сделал, предварительно конвертировав
Программа получилась следующей.
from __future__ import with_statement from difflib import ndiff, restore # преобразование строки 16-ричных чисел в символьные коды def dehex(str): res ='' for char in map(lambda x: chr(int(x, 16)) if x!='' else '', str.split(' ')): res += char return res (lst1, lst2) = ([], []) # читаем файл и формируем список строк левой и правой половин with open('delta.txt', 'r') as file: for line in file: spl = line.split(' ') lst1.append(dehex(spl[0])) lst2.append(dehex(spl[1])) # выводим разницу diff_res = ndiff(lst1, lst2) reslt = [[], [], []] # делим на 3 списка for line in diff_res: if line[0:2] == ' ': reslt[0].append(line[2:]) elif line[0:2] == '- ': reslt[1].append(line[2:]) else: reslt[2].append(line[2:]) # сохраняем for x in xrange(0, 3): f = open('res%d.png' % x, 'wb') f.write(''.join(reslt[x])) f.close()В результате получаем 3 файла, один с сылкой на следующий уровень, и два с логином и паролем для попадания туда.
Уровень 19
В исходнике страницы мы находим сохраненное письмо с вложением. Раскодировать его очень просто с помощью функции b64decode модуля base64.
В итоге мы получим файл indian.wav, но кроме шума и слова sorry в нем ничего не слышно.
В письме говорится, что компьютер Леопольда сломался (out of order), и ему некогда разбираться с этим вложением.
Что делать с файлом, не совсем ясно. Но несколько подсказок проливают на это свет.
- Карта на странице задания инвертирована.
- И страна на карте, и название файла наталкивают на слово «Indian»
- Фраза Леопольда «out of order» также может быть переведена как «не в порядке»
- Его фразы про «молодежь».
Дело остается за малым — «перевернуть» байты в файле. Благо семпл у нас как раз
from base64 import b64decode import wave # открываем файл вложения в письмо instr = open('base64.txt', 'r').read() # декодируем его str2 = b64decode(instr) # открываем файл для чтения и сохраняем его outf = open('indian.wav', 'wb') outf.write(str2) outf.close() # открываем файлы источника и назначения как wave-семплы inw = wave.open('indian.wav', 'r') outw = wave.open('result.wav', 'w') outw.setparams(inw.getparams()) for x in xrange(0, inw.getnframes()): lo = inw.readframes(1) hi = lo[1:2]+lo[0:1] outw.writeframes(hi) inw.close() outw.close()Слушаем его, и последнее слово, которое очень легко разобрать, дает нам URL следующего уровня.
Все про українське ІТ в телеграмі — підписуйтеся на канал редакції DOU
7 коментарів
Підписатись на коментаріВідписатись від коментарів Коментарі можуть залишати тільки користувачі з підтвердженими акаунтами.