San Diego 2022 | magic3

#San_Diego_CTF_2022

posixがrevしてくれた

magic_arr = [i for i in range(0x30)]

setup_arr1 = [41, 38, 11, 14, 19, 9, 16, 48, 6, 46, 8, 35, 22, 32, 17, 40, 25, 1] 
setup_arr2 = [26, 21, 29, 15, 12, 4, 47, 23, 31, 18, 2]
setup_arr3 = [30, 27, 24, 33, 43, 3]
setup_arr4 = [28, 36, 44, 37, 10, 39, 42, 45, 7, 34, 5]

def swap(arr):
  for i in range(1, len(arr)):
    tmp = magic_arr[arr[i - 1] - 1]
    magic_arr[arr[i - 1] - 1] = magic_arr[arr[i] - 1]
    magic_arr[arr[i] - 1] = tmp

swap(setup_arr1)
swap(setup_arr2)
swap(setup_arr3)
swap(setup_arr4)

def L():
  swap([40, 41, 17, 1])
  swap([37, 44, 20, 4])
  swap([35, 46, 22, 6])
  swap([14, 16, 11, 9])
  swap([12, 15, 13, 10])

def D():
  swap([38, 30, 22, 14])
  swap([39, 31, 23, 15])
  swap([40, 32, 24, 16])
  swap([46, 48, 43, 41])
  swap([44, 47, 45, 42])

def R():
  swap([19, 43, 38, 3])
  swap([21, 45, 36, 5])
  swap([24, 48, 33, 8])
  swap([30, 32, 27, 25])
  swap([28, 31, 29, 26])

def B():
  swap([27, 48, 14, 1])
  swap([29, 47, 12, 2])
  swap([32, 46, 9, 3])
  swap([38, 40, 35, 33])
  swap([36, 39, 37, 34])

def U():
  swap([6, 8, 3, 1])
  swap([4, 7, 5, 2])
  swap([17, 25, 33, 9])
  swap([18, 26, 34, 10])
  swap([19, 27, 35, 11])

def F():
  swap([16, 43, 25, 6])
  swap([13, 42, 28, 7])
  swap([11, 41, 30, 8])
  swap([22, 24, 19, 17])
  swap([20, 23, 21, 18])

input_str = 'llddrr'

for c in input_str:
  match c:
    case 'l':
      L()
    case 'd':
      D()
    case 'r':
      R()
    case 'b':
      B()
    case 'u':
      U()
    case 'f':
      F()

for i in range(0x30):
  assert(magic_arr[i] == i)

これはRubikCube で、お望みのstateになるような操作を求める。

def rubik_normalize(moves, p=0):

    normalized = ""
    while p < len(moves):
        if moves[p] == "(":
            in_paren, p = rubik_normalize(moves, p+1)
        elif moves[p] == ")":
            return normalized, p+1
        elif moves[p] in "LDRBUF":
            in_paren = moves[p]
            p += 1
        else:
            raise ValueError(moves[:p+1])
        
        times = 1
        if p < len(moves) and moves[p] == "^":
            p += 1 # read ^

            save = p
            while p < len(moves) and moves[p]  in  "-1234567890":
                p+=1
            times = int(moves[save:p]) % 4
        if p < len(moves) and moves[p] == "*":
            p += 1
        normalized += in_paren * times
    print(normalized)
    return normalized, p

    

def rubik_convert(moves):
    # converted = ""
    # for move in moves:
    #     converted += move*3
    return moves.lower()

from ptrlib import chunks

state = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]
state2 = [40, 25, 29, 46, 27, 45, 33, 34, 15, 38, 13, 3, 12, 18, 11, 47, 39, 1, 8, 19, 28, 31, 30, 32, 0, 20, 23, 35, 14, 26, 17, 16, 42, 4, 21, 43, 9, 10, 41, 24, 37, 44, 2, 36, 6, 7, 22, 5]
 
state = [x + 1 for x in state]
state2 = [x + 1 for x in state2]

r = RubiksCube(state)
r2 = RubiksCube(state2)

ans = r2.solve(r)
print(ans)
normalized, _ = rubik_normalize(ans)

print(r.move("*".join(list(normalized))) == r2)
print(rubik_convert(normalized))
# d <-> d^-1
# l <-> l^-1