Python Challenge 20의 Url은 다음과 같다

Python challenge 20 : http://www.pythonchallenge.com/pc/hex/idiot2.html


구성

영어로는 표지판에

"이 울타리 뒤쪽으로는 사유재산입니다."

라고 쓰여 있다. 아래 영어는

"그래도 주의깊게 살펴보는 것은 허용됩니다."

라고 적혀있다.

우선 특별한 점이 없으니 파일을 검사해보자. unreal.jpg로 다운로드되는 파일은 헤더 변조도 없어 보이고, 특별히 숨겨놓은 스트링값도 없어 보인다... 으흠; 주석을 볼까

주석도 특별한게 없다. 난항이 될 거 같다.


해결 아이디어

우선 이 문제를 해결하기 위해서는 헤더를 주의 깊게 쳐다볼 필요가 있다.

여러분과 필자가 보고 있는 이 요청정보는 idiot2.html이 아닌 unreal.jpg의 정보라는 것을 기반으로 잡고 가자. 자세히 보면 독특한 response header가 보인다. 바로 content-range라는 응답인데, 이게 전체 크기인 2123456789에서(참 대충도 짓는다. 아마 2의 32 제곱 만들고 싶었던 듯) 0-30202 bytes만을 가져온다.

원래 이런 식이라면 HTTP코드는 206(partial content)이 되어야 한다.(그래야 나머지 데이터도 수신받을 수 있다.) 그러나 304(not modified)로 되었다는 것은 우리한테 모든 정보가 전달되지 않고 강제로 종료시켰다는 의심을 할 수 있다. HTTP코드를 자세하게 모르시는 분들은 아래를 확인하자

https://developer.mozilla.org/ko/docs/Web/HTTP/Status

HTTP 상태 코드 - HTTP | MDN

HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려줍니다. 응답은 5개의 그룹으로 나누어집니다: 정보를 제공하는 응답, 성공적인 응답, 리다이렉트, 클라이언트 에러, 그리고

developer.mozilla.org

일단 30202 뒤쪽으로 해서 콘텐츠를 더 받을 수 있도록 코드를 작성해 보자

### 20_1.py from requests import * if __name__=="__main__" : header = {"Authorization" : "Basic YnV0dGVyOmZseQ==", "user-agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36", "Range": "bytes=30203-/2123456789" } url = "http://www.pythonchallenge.com/pc/hex/unreal.jpg" res = get(url,headers=header) 

아참, 지난 시간까지는 requests 모듈의 get이나 post함수에 auth()라는 파라미터를 주었는데 이번에는 헤더에 base64로 butter:fly값이 인코딩 된 값을 Authorization으로 쓰고 있다.(왜 그러냐고? 모른다. 기존 헤더를 보니 그랬다.)

결과는 다음과 같다.

>>> res.headers {'Content-Type': 'application/octet-stream', 'Content-Transfer-Encoding': 'binary', 'Content-Range': 'bytes 30203-30236/2123456789', 'Transfer-Encoding': 'chunked', 'Date': 'Thu, 03 Jun 2021 14:11:29 GMT', 'Server': 'lighttpd/1.4.35'} >>> res.text "Why don't you respect my privacy?\n"

오호; 다음 값은 30237~ 부터 있는 것이 자명하다. 계속해서 요청해 보면 각각의 범위에서 다음과 같은 값을 찾을 수 있다.

  • 0-30202 : unreal.jpg
  • 30203-30236 : "Why don't you respect my privacy?\n"
  • 30237-30283 : 'we can go on in this way for really long time.\n'
  • 30284-30294 : 'stop this!\n'
  • 30295-30312 : 'invader! invader!\n'
  • 30313-30346 : 'ok, invader. you are inside now. \n'
  • 30347-? : ''

그만 오라고 열심히 설명하다가 30347bytes부터 그만두고 ''만 나온다. 맨 뒤쪽을 검색을 해보면

  • 2123456744-2123456788 : 'esrever ni emankcin wen ruoy si drowssap eht\n'

응? 이상한 언어가 나온다. 우리가 거꾸로 뒤에서부터 탐색을 하는 거 니까, 글자도 거꾸로 나왔다. ㅋㅋㅋㅋ 무슨 콘셉트질인지... 거꾸로 읽어 보자.

'the password is your new nickname in reverse'

와우! 우리의 NICKNAME은? butter? 아니다 아니다 위에서 우리 보고 invader라고 말하지 않는가. 일단 password가 invader의 반대말인 redavni인 것을 알고 넘어가자 우리 보고 invader라고 한 것이 화나니 조금만 더 뒤로 찾아보자

  • 2123456712-2123456743 : 'and it is hiding at 1152983631.\n'

나이스 이제 좀 유의미한 인덱스를 주는 거 같다. 이동해보자

  • 1152983631-1153223363 : some byte file

일단 파일 사이즈가 늘었다. 한번 콘텐츠를 다운로드하여보자

### 20_2.py from requests import * if __name__=="__main__" : header = {"Authorization" : "Basic YnV0dGVyOmZseQ==", "user-agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36", "Range": "bytes=1152983631-/2123456789" } url = "http://www.pythonchallenge.com/pc/hex/unreal.jpg" res = get(url,headers=header) with open("somefile","wb") as f: f.write(res.content)

그래서 다운로드한 somefile를 hxd로 뜯어보자

헤더 시그니처를 보니 ZIP 파일이다. 503B0304는 국 룰이다.

zip으로 변경해서 압축해제를 하려면 비밀번호를 요구한다. 그래 그 비밀번호

Answer PW : redavni

아 조금 독특한건 압축해제한 파일에 readme를 보면 우리는 이미 20번문제는 해결한 상태라고한다. 이제 같이 압축이 풀린 .pack을 해결하는게 22번 문제라고 한다.

Python Challenge 19의 Url은 다음과 같다

Python challenge 19 : http://www.pythonchallenge.com/pc/hex/bin.html


구성

인도 사진이다. 특별한 게 안보이니까 주석을 보자

MIME Type의 e-mail이 보인다. 아래 있는 데이터로 문제를 풀어나가야 될 것 같다.


해결 아이디어 

우선 붙임파일을 base64 decode 해야 될 거 같다. 하는 김에 반환된 바이트 객체를 indian.wav로 바꾸자

### 19.py

import base64

if __name__=="__main__" :

    with open("hex.txt","r") as f:
        data = f.read()
        data = data.replace("\n","")

    data = base64.b64decode(data)

    with open("indian.wav","wb") as f:
        f.write(data)

hex.txt에 붙임파일을 집어넣어두었다. 하여튼 출력된 indian.wav는 다음과 같다.

indian.wav
0.11MB

 

 

중간에 sorry! 라는 말만 나오는 5초짜리 음성이다. 으흠; 여기서 막혔다. 일단 Python Challenge의 성격상 모든 것을 python으로 처리하니, python의 모듈 wave(wav처리 모듈)을 다운로드하여서 여러 가지 해봤는데, 답이 없다...

해결할 수 있는 키노트는 어이없게도 이 사람이 indian이라는 점이다. 

indian >> endian

그니까 endian type을 바꾸면 된다는 것이다.(지금은 LITTLE이다. BIG으로 바꾸고 오라는 거다)

다음은 코드이다. 모듈은 soundfile이라는 모듈을 이용했다.

https://pysoundfile.readthedocs.io/en/latest/#soundfile.SoundFile

 

SoundFile — PySoundFile 0.10.3post1-1-g0394588 documentation

Parameters: file (str or int or file-like object) – The file to open. This can be a file name, a file descriptor or a Python file object (or a similar object with the methods read()/readinto(), write(), seek() and tell()). mode ({'r', 'r+', 'w', 'w+', 'x

pysoundfile.readthedocs.io

### 19_1.py

import soundfile

if __name__=="__main__" :
    ind = soundfile.SoundFile("indian.wav")

    soundfile.write("big.wav",ind.read(),ind.samplerate,ind.subtype,"BIG",ind.format)

근데.... 파일이 깨진다. 결국 구글 신의 힘을 빌려 알아낸것은 BIG endiantype으로 변형할 때는 파라미터가 변경되어야 한다고 한다. 참나.. 이런 건 어떻게 안다는 말인가? 최종적인 코드는 다음과 같다. soundfile에는 framerate에는 마땅한 파라미터가 없어 처음에 가져온 wave를 다시 이용했다.

### 19_2.py

import wave

if __name__=="__main__" :
    with wave.open("indian.wav","rb") as ori :
        new = wave.open("big.wav","wb")
        new.setnchannels(ori.getnchannels())
        new.setsampwidth(ori.getsampwidth()//2)
        new.setframerate(ori.getframerate()*2)
        frames = ori.readframes(ori.getnframes())
        wave.big_endiana = 1
        new.writeframes(frames)
    new.close()

출력은 다음과 같다.

big.wav
0.11MB

 

 

 

idiot으로 들어가 보면 레오파드씨가 우리를 반겨준다.

다음으로 넘어가자 

Answer Url : http://www.pythonchallenge.com/pc/hex/idiot2.html

Python Challenge 18의 Url은 다음과 같다

Python challenge 18 : http://www.pythonchallenge.com/pc/return/balloons.html


구성

Title은 "Can you tell the difference"이다. 번역하면 "차이점을 말할 수 있는가?" 이다.  문제 자체가 조금 번잡한 편에 속하기 때문에, 바로 해결 아이디어로 들어가 보겠다.


해결 아이디어

우선 진짜문제가 담겨 있는 곳은 이곳이 아니다. 차이점은 '밝기', 영어로 하면 'brightness'이다. 일단 진짜 문제의 url인 brightness.html로 이동하자

http://www.pythonchallenge.com/pc/return/brightness.html

구성은 동일하다.

단 주석창에 새로운 힌트가 생겼다.

deltas.gz로 url을 접근하면 gunzip파일을 다운로드 받을 수 있다. 안에는 txt파일인 data가 들어있다 내부는 다음과 같다.

하나의 파일은 아닌것으로 보인다. 제일 위에 줄인 89 50 4e 47은 PNG 파일의 시그니처이다. 중간 \t같은 공백을 기준으로 왼쪽 파일, 오른쪽 파일에 동일한 89 50 4e 47이 있는 것으로 보아 두 파일은 별개의 파일이다. 쪼개서 png를 만든 결과는 다음과 같다..... 이 X발 티스토리 업로드 가 이상하다. 하여간 두 파일을 PNG로 잘 쪼개었다면 축하한다....

 

"필자와 동일하게 문제 잘못 풀고 있는거다"

 

일단 문제를 푸는 희망은 delta라는 gunzip파일의 이름이다. delta라는건 편차라고 흔히 이야기한다. bightness에서 나왔던 difference라는 단어를 같이 구글링 하면 difflib라는 모듈을 찾을 수 있다.

https://docs.python.org/ko/3/library/difflib.html

 

difflib — 델타 계산을 위한 도우미 — Python 3.9.5 문서

difflib — 델타 계산을 위한 도우미 소스 코드: Lib/difflib.py 이 모듈은 시퀀스 비교를 위한 클래스와 함수를 제공합니다. 예를 들어 파일을 비교하는 데 사용할 수 있으며, HTML 및 문맥(context)과 통

docs.python.org

difflib은 두 문자열의 차이점을 표현해주는 모듈로 대충 아래와 같은 동작을 한다.

그러니까 위 delta.gz에 있는 공백 기준 왼쪽과 오른쪽의 차이를 보고 출력을 하는 코드를 만들면 다음과 같은데,

### 18_1.py

import gzip, difflib

if __name__=="__main__" :
    dat = gzip.open("deltas.gz","rb")

    left = list()
    right = list()
    for line in dat :
        left.append(line[:53].decode())
        right.append(line[56:-1].decode())
        

    diff = difflib.Differ().compare(left,right)

    for x in diff :
        print(x)

이것의 출력 결과는 다음과 같다.

  89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00
  
  ...
  
+ 01 50 00 00 00 8f 08 06 00 00 00 ac f7 83 97 00 00 00
  7e 8e f8 2a a1 a1 4b 0c 86 34 2e 31 90 29 8c 16 cd 12
- b7 50 46 d5 db 73 cc d0 7d 03 d3 37 80 52 88 d7 03 76
+ 09 70 48 59 73 00 00 0b 13 00 00 0b 13 01 00 9a 9c 18
  f3 6b be 5a e2 f6 7c 9a 63 78 fe a0 03 5a 55 7b fa 30
- 9f bf 06 92 54 a1 02 81 10 e8 8f 25 20 a3 a1 1a 0b dd
+ 00 00 00 07 74 49 4d 45 07 d5 05 07 0a 37 11 2c 30 95
  a3 3c 20 74 68 c1 a4 48 11 c8 b3 5b f3 aa e5 66 c9 f3
- 39 e8 c6 42 b5 16 ca 1a 3a e6 2a 75 f3 b8 ab f6 c4 84
+ e5 00 00 00 1d 74 45 58 74 43 6f 6d 6d 65 6e 74 00 43
  17 fe e9 ab 81 36 57 f9 54 ed 57 34 4a 29 4d 29 eb e7
+ 72 65 61 74 65 64 20 77 69 74 68 20 54 68 65 20 47 49
  65 1b ae 35 cc 54 ed 2f 79 be 08 f3 78 85 ef 2c 08 54
  a9 e1 50 4d 2c 25 29 e5 96 2c 41 31 42 cb 8d 43 07 a0
+ 4d 50 ef 64 25 6e 00 00 20 00 49 44 41 54 78 da ed 7d
  e3 ae e6 d5 07 87 67 03 3b c6 b4 a4 4a c3 58 24 06 96
  
  ...

그러니까 이 +과 -를 판단해서 왼쪽 파일, 오른쪽 파일, 공통 파일을 만들어서 넣으면 되는 거다.( 참 조금 독특한 점은 기존 delta는 -와 +이 아닌 공백(" ")은 공통적인 문자열을 지칭해서 이번 문제에서도 공통+"+". png와 공통+"-". png 2개를 만들면 된다고 생각을 했는데, "+"과 "-"와 " "으로 3가지를 만들어야 해결이 된다. 헤더도 3개가 따로 있다.)

 

이를 구현한 코드는 다음과 같다. 

### 18_2.py

import gzip, difflib

if __name__=="__main__" :
    dat = gzip.open("deltas.gz","rb")

    left = list()
    right = list()
    for line in dat :
        left.append(line[:53].decode())
        right.append(line[56:-1].decode())
        

    diff = difflib.Differ().compare(left,right)
    
    comm = open("comm.png", "wb")
    left = open("left.png", "wb")
    right = open("right.png", "wb")

    for line in diff :
        bs = bytes([int(x, base=16) for x in line[2:].strip().split(" ") if x])
        if(line[0] == '+'):
            left.write(bs)
        elif(line[0] == '-'):
            right.write(bs)
        else:
            comm.write(bs)

    comm.close()
    left.close()
    right.close()

출력은 다음과 같다.

left.png
right.png
comm.png

공통 사진인 comm.png경로부터 찾아보자 들어가려고 하면 자격증명을 물어본다. 국룰인 huge/file을 해보면 틀리다. butter/fly로 접근을 하면 축하한다. 찾은 거다

Answer Url : http://www.pythonchallenge.com/pc/hex/bin.html


비 고사항인데, 필자는 이거 푸는 게 너무 어려웠다.

difflib모듈은 사용해봤어도 생각도 못하고 있었다. 치열한 전투의 흔적을 남기며 다음 글에서 뵙겠습니다.

<전투의흔적.png>

Python Challenge 17의 Url은 다음과 같다

Python challenge 17 : http://www.pythonchallenge.com/pc/return/romance.html


구성

일단 페이지나, 사진이나, 주석이나 특별한 점은 보이지 않는다. Title인 eat? 이라는 글자를 보아 cookies는 cookies인데, 먹는 쿠키는 아닌 거 같다. 혹시 아래 사진을 기억하는가? 조금 힌트가 될지도 모르겠다.

 


 

해결 아이디어

문제의 아래쪽 사진은 4번문제인 LinkedList의 사진이다.

2021.05.16 - [Python/Python Challenge] - [Python Challenge 4] 무한으로 즐겨요 requests

 

[Python Challenge 4] 무한으로 즐겨요 requests

Python Challenge 4의 url은 다음과 같다. Python challenge 4 : http://www.pythonchallenge.com/pc/def/linkedlist.php follow the chain www.pythonchallenge.com 구성 으흠; 그림이 어떤 걸 의미하는지는 모..

tutoreducto.tistory.com

쿠키를 확인하면 다음과 같은 글을 확인할 수 있다.

오호... 그러니까 이전 문제에서 사용하던 parameter인 nothing대신 busynothing을 사용해 보자고 한다. 바꾸어서 접속해보자(시작번호는 역시 12345이다. 주석에 쓰여있는 대로다)

으흠; 4번에서 왔다면 당장 꺼지라고 써있다. cookies를 확인하자

1. busynothing=44827의 cookies

2. busynothing=45439(44827다음거)의 cookies

오호; cookies가 순서대로 BZ를 이루고 있다. 어디서 많이 본 확장자 아닌가, 바로 bz2모듈을 사용하는 문제였던 8번 문제의 해결방법을 그대로 차용한 듯 하자.

2021.05.19 - [Python/Python Challenge] - [Python Challenge 8] 소난다

 

[Python Challenge 8] 소난다

Python Challenge 8의 url은 다음과 같다. Python challenge 8 : http://www.pythonchallenge.com/pc/def/integrity.html working hard? www.pythonchallenge.com 구성 귀여운 야 울고 있는 꿀벌이 있다. 글자는..

tutoreducto.tistory.com

자 이제 코딩 시간이다.

### 17_1.py

from requests import *
import re
import bz2

if __name__ == "__main__" :
    url = "http://www.pythonchallenge.com/pc/def/linkedlist.php?busynothing="
    nothing = "12345"
    answer = list()
    
    while(True) :
        new_url = url+nothing
        res = get(new_url)
        if(len(res.cookies["info"])==3) :
            answer.append(int(res.cookies['info'][1:],base=16))
        else :
            if(res.cookies['info'] == "+") :
                answer.append(32)
            else :
                answer.append(ord(res.cookies["info"]))
        try :
            nothing = re.findall("busynothing is (\d+)",res.text, re.DOTALL)[-1]
        except IndexError :
            break
        print(res.text)
        
    print(bz2.decompress(bytes(answer)).decode())
    

 중간에 +를 공백으로(정수 32 >> 헥스 0x20 >> " ")로 치환하는 부분이 있다. urlencode에서는 +가 공백을 의미한다. 만약 정말 헥스값으로 "+"를 표현했다면 "+"로 써야 되지만, 그게 아니라 글자로 +가 되어있다면 그건 공백으로 치환해야 한다. 결과는 다음과 같다.

and the next busynothing is 64994
and the next busynothing is 66109
and the next busynothing is 37855
and the next busynothing is 36383
and the next busynothing is 68548
and the next busynothing is 96070
and the next busynothing is 83051
is it the 26th already? call his father and inform him that "the flowers are on their way". he'll understand.

그렇다. 그니까 한국말로 번역하면

 

"어머 벌써 26일이 되었나? 그 사람 아버지한테 연락해서 '꽃은 각자 살아가는 방법이 있다고' 전달해줘 이해하실 거야"

 

이다. 오호.. 이번엔 26일이다. 저번 문제를 기억하는가

2021.05.29 - [Python/Python Challenge] - [Python Challenge 16] 다 내 밑으로 정렬해

 

[Python Challenge 16] 다 내 밑으로 정렬해

Python Challenge 16의 Url은 다음과 같다 Python challenge 16 : http://www.pythonchallenge.com/pc/return/mozart.html 구성 무슨 노이즈가 화면에 껴있다. 일단 사진을 다운로드하여보자 사진의 이름은 moza..

tutoreducto.tistory.com

맞다 문제의 주인공은 mozart였다. 그의 아버지 이름을 검색해 보자

성함은 Leopold 되시나 보다. 전화는 어떻게 걸 수 있을까? 바로 Bert는 나쁜 놈에서 나왔던 xmlrpc서버의 phone메서드를 이용해야 된다.(와 이 정도면 이번 문제는 종합 선물세트다) 

2021.05.19 - [Python/Python Challenge] - [Python Challenge 13] 자니??.... 그냥 전화해봤어...

 

[Python Challenge 13] 자니??.... 그냥 전화해봤어...

Python Challenge 13의 url은 다음과 같다. Python challenge 13 : http://www.pythonchallenge.com/pc/return/disproportional.html 구성 가운데 영어는 다음과 같은 의미이다. "나쁜 놈에게 전화할 것" 상호작..

tutoreducto.tistory.com

자 오랜만에 사용하는 xmlrpc 코드다.

### 17_2.py

import xmlrpc.client as xc

if __name__ == "__main__" :
    url = "http://www.pythonchallenge.com/pc/phonebook.php"
    serv = xc.ServerProxy(url)

    print(serv.phone("Leopold"))

출력은 다음과 같다.

555-VIOLIN

참 길었다. violin으로 접속해 보자.

IC 아직 안 끝났다. Title은 It's me. what do you want?이다. 

그러니까 처음  linkedlist의 cookies를 이용해서 얻어냈던 문자열인 'the flowers are ib their ways"를 전달해야 한다. 여태까지 사용했던 info를 key로 보내보자 코딩 타임이다.

### 17_3.py

from requests import *

if __name__=="__main__" :
    url = "http://www.pythonchallenge.com/pc/stuff/violin.php"
    param = "the flowers are on their way"
    param = param.replace(" ","+")
    cookies = {"info":param}

    res = get(url,cookies=cookies,auth=('hugt','file'))
    print(res.text)

출력에는 다음과 같은 문자열이 쓰여 있었다.

"noh well, don't you dare to forget the balloons."

그렇군 X바 진짜 끝이다.

 

Answer Url :  http://www.pythonchallenge.com/pc/return/balloons.html 


종합 선물세트였다. 어려운 건 없었는데, 젠장 기억력이 좋은 편이 아니어서 쿠소.

Python Challenge 16의 Url은 다음과 같다

Python challenge 16 : http://www.pythonchallenge.com/pc/return/mozart.html


구성

무슨 노이즈가 화면에 껴있다. 일단 사진을 다운로드하여보자

< mozart.gif> 

사진의 이름은  mozart.gif이다. 단서로 보일 만한 점은 웹페이지의 title인

 

"Let Me get this Stright!"

 

이다.

 


해결 아이디어

저 영어가 번역하면, "우리 확실하게 짚고 가자!" 인데. Straight를 직선으로 번역을 하면, 정렬을 해달라는 의미가 된다. 일단 사진의 픽셀을 뜯어보면 신기하게 하나의 값만을 가지는 것을 알 수 있다.(RGBA가 아니다.)

### 16_1.py

from PIL import Image

if __name__=="__main__" :
    with Image.open("mozart.gif") as img :
        for x in range(img.width) :
            print(img.getpixel((x,0)))

출력은 다음과 같은데, 특이하게 중간에 195라는 값이 있다. 아마 중간에 보이는 저 자주색 선 같다.

...

17
60
249
195
195
195
195
195
252
88
11
60
84

...

일단 mozart.gif의 모드를 img.mode로 구해보면 'P'모드라고 한다. 이 P 모드는 8비트의 값을 가진다고 한다. 그래서 0~255 사이의 Value만 픽셀 별로 가진 것이다. 다음은 이 195를 기준으로 정렬하고 mozart_a.gif로 저장하는 코드이다.

### 16_2.py

from PIL import Image

if __name__=="__main__" :
    with Image.open("mozart.gif") as img :
        new_img = Image.new("P",(640,480))
        for y in range(img.height) :
            row_array_img = list()
            start_index = 0
            for x in range(img.width) :
                row_array_img.append(img.getpixel((x,y)))
            start_index = row_array_img.index(195)
            for x in range(img.width) :
                real_x = (start_index+x)%img.width
                new_img.putpixel((x,y),row_array_img[real_x])
        new_img.save("mozart_a.gif","GIF")

찾았다. romance

Answer Url : http://www.pythonchallenge.com/pc/return/romance.html

Python Challenge 15의 Url은 다음과 같다

Python challenge 15 : http://www.pythonchallenge.com/pc/return/uzi.html


구성

구성으로 달력사진이 나와있다. 혹시나 저번 시간 cat까지만 움직인 사람들을 위해서 그 고양이 이름은 uzi이다. uzi.html로 냉큼 오자


해결 아이디어

모듈을 사용하자. 저거 잘 보면 다음달께 보인다. 그니까 2월에 달력을 조금 자세하게 보자

으흠; DeBlur로 복구하자 다음과 같은 툴을 사용했다.

http://yuzhikov.com/projects.html

전혀 도움은 되지 않았다. 그러나 자세하게 사진을 확인해보면 오호; 29일이 있다. 그러니까 윤년이라는 거다 연도를 보면 1XX6년임을 알 수 있다. 

1. 1000~2000사이의 일의 자리가 "6"인 년도

2. 윤년

3. 1월시작이 목요일

이 세가지를 만족하는 년도를 찾아내면 된다. 이런 문제를 위한 완벽한 모듈이 있으니 바로 Calendar 모듈이다. 

2021.05.25 - [Python/Python 모듈탐구] - [Python] - 모듈탐구 calendar - 야 오늘 며칠이냐?

* 맞다 이 문제 풀이를 위해서 모듈탐구를 올렸다.

### 15.py

import calendar


if __name__ == "__main__" :

    ans = list()
    
    ### 1000 ~ 2000 사이 일의자리가 "6"인 순서로 탐색
    for i in range(1006,2000,10) :
        cur_year = i
        
        ### isleap함수는 윤년이면 True 반환
        if(calendar.isleap(cur_year)) :
        	### weekday는 년,월,일 순서로 파라미터를 받아 요일을 반환, 목요일은 3
            if(calendar.weekday(cur_year,1,1)==3) :
                ans.append(cur_year)

    print(ans)

 

출력은 다음과 같다.

[1176, 1356, 1576, 1756, 1976]

주석을 잠깐 보고오자

1. 가장 젊은 사람이 아니라고 하니 1756년달력인가 보다

2. 내일을 위한 꽃을 사라는 것을 보니 달력에 동그라미가 되어있는 26일 다음, 즉 1월 27일을 찾는것이다.

 

그러니까 우리가 찾는 날짜는 1756년 1월 27이다. 이게 어떻게 답일까? 구글링 해보자

WOW mozart 찾았다.

Answer Url : http://www.pythonchallenge.com/pc/return/mozart.html

+ Recent posts