Homeの3問目 (Cipher Map)

py.checkio.org

4掛け4のグリッドのマスにそれぞれ適当な文字を組み込んで、
同じ4掛け4サイズのグリッドの内4マス穴の空いたグリッドをかぶせて穴から覗く文字を取り出す、
90度ずつローテーションさせて合計16文字のパスワードを取り出す。
という暗号化の仕組みを解読するコードを書くという問題ですね。
とりあえず、2次元配列的な感じでやってみることに。
正直無駄な処理だらけではあるけれど、

def recall_password(cipher_grille, ciphered_password):
    result = ''
    grille_2d = [list(cipher_grille[0]),list(cipher_grille[1]),list(cipher_grille[2]),list(cipher_grille[3])]
    password_2d = [list(ciphered_password[0]),list(ciphered_password[1]),list(ciphered_password[2]),list(ciphered_password[3])]
    t = 0
    while t < 4:
        pickedchar = ''
        for i,n in enumerate(grille_2d):
            for j, m in enumerate(n):
                if m == 'X' :
                    pickedchar += password_2d[i][j]
        result += pickedchar
        tmp_grille_2d=
        grille_2d.reverse()
        for a, b, c, d in zip(*grille_2d):
            tmp_grille_2d.append([a, b, c, d])
        grille_2d =

        grille_2d = tmp_grille_2d
        t += 1
    return result

で一旦解けました。
リストをいちいち書くのは少し残念すぎる気がするのでforで回すようにして、
whileで残念な書き方になっていたところをfor ... in range()で置き換え。

def recall_password(cipher_grille, ciphered_password):
    result = ''
    grille_2d, password_2d = ,
    for i, j in zip(cipher_grille,ciphered_password):
        grille_2d.append(i)
        password_2d.append(j)
    for t in range(4):
        pickedchar = ''
        for i,n in enumerate(grille_2d):
            for j, m in enumerate(n):
                if m == 'X' :
                    pickedchar += password_2d[i][j]
        result += pickedchar
        tmp_grille_2d=
        grille_2d.reverse()
        for a, b, c, d in zip(*grille_2d):
            tmp_grille_2d.append([a, b, c, d])
        grille_2d =

        grille_2d = tmp_grille_2d
    return result

そもそも、いちいち配列にする必要ないような気がしたので、
書き換えようとしたものの回転させることができず下記でギブアップ

def recall_password(cipher_grille, ciphered_password):
    result = ''
        for i, j in zip(cipher_grille,ciphered_password):
            for n, m in enumerate(i):
                if m == 'X' :
                    print(j[n])
    return result

最初に位置を出してあとは追ってくって感じで'X'の位置を座標に変換して回すとかどうだろうとか思ったけど、
読み出す順序を考えると結構無駄な処理が増えそうな気もする。。。
ということで、

def recall_password(cipher_grille, ciphered_password):
    result = ''
    grille_2d, password_2d = ,
    for i, j in zip(cipher_grille,ciphered_password):
        grille_2d.append(i)
        password_2d.append(j)
    for t in range(4):
        for i,n in enumerate(grille_2d):
            for j, m in enumerate(n):
                if m == 'X' :
        result += password_2d[i][j]
        tmp_grille_2d=[]
        grille_2d.reverse()
        for a, b, c, d in zip(*grille_2d):
            tmp_grille_2d.append([a, b, c, d])
        grille_2d = tmp_grille_2d
    return result

この辺が一旦落とし所でしょうか。。

さて、他の人の回答を見ると、

def recall_password(grill, cypher):
    password = ""
    for _ in grill: # must be of len 4
        for grill_row, cypher_row in zip(grill, cypher):
            for grill_letter, cypher_letter in zip(grill_row, cypher_row):
                if grill_letter == 'X':
                    password += cypher_letter
        row1, row2, row3, row4 = grill
        grill = tuple(zip(row4, row3, row2, row1)) # rotate
    return password

あー、後半の処理のところを逆に並べてzipするということなんですね。
でも、基本的には

row1, row2, row3, row4 = grill

を発想できませんでした。。
うむむむ。

def recall_password(cipher_grille, ciphered_password):
    result = ''
    for t in range(4):
        for i, j in zip(cipher_grille,ciphered_password):
            for n, m in enumerate(i):
                if m == 'X' :
        result += j[n]
        a, b, c, d = cipher_grille
        cipher_grille = tuple(zip(d, c, b, a))
    return result

なるほどですね。