Problem
Description: I really like Rubik’s Cubes, so I created a challenge for you. I put the flag on the white tiles and scrambled the cube. Once you solved the cube, you’ll know my secret.
(rev90, solved by 232)
Attachment: rev90.zip
Solution
In this task we were given a text-file containing the scrambling of a Rubik’s cube, as well as the different sides of the cube before it was scrambled.
Scrambling:
F' D' U' L' U' F2 B2 D2 F' U D2 B' U' B2 R2 D2 B' R' U B2 L U R' U' L'
White side:
-------
|{|3| |
| |D|R|
| |W| |
-------
Orange side:
-------
| | | |
| | | |
| | | |
-------
Yellow side:
-------
|}| | |
|3| | |
| | | |
-------
Red side:
-------
|I| | |
| | | |
| | |C|
-------
Green side:
-------
| | | |
| | | |
| | | |
-------
Blue side:
-------
| | | |
| | | |
| | | |
-------
First observation is that the flag is five characters long, since there are
nine characters spread out over the cube, and of these, four are IW{}
.
This makes it really easy to brute-force this challenge, since there are only
120 possible permutations. However, brute-forcing is no fun.
Another way would be to solve this using a physical Rubik’s cube. In the lack of one of those, we decided to go with the programmatic solution.
The implementation is pretty straight forward. First we define a representation of the cube, which naturally is a 6x3x3 array (or list). We define the six different rotations in different functions. Some valuable observations where are:
- Each rotation of a side affects the four adjacent sides
- The meaning of each rotation depends on the starting state of the cube
- The scrambling needs to be done in reverse in order to de-scramble the cube
- Counter-clockwise rotations (marked with prim-sign) are equivalent to three clockwise rotations, and double rotations are reversed with double rotations
Internetwache were kind enough to supply us with the starting state in a tweet. The starting state of the cube was:
Front: green Right: red Up: white Back: blue Left: orange Down: yellow
Having implemented all rotations, and knowing the starting state, we can solve the cube. Using the provided script, the following output was given:
[' ', ' ', ' ']
[' ', ' ', ' ']
[' ', ' ', ' ']
[' ', ' ', ' ']
[' ', ' ', ' ']
[' ', ' ', ' ']
['I', 'W', '{']
['3', 'D', 'R']
['C', '3', '}']
[' ', ' ', ' ']
[' ', ' ', ' ']
[' ', ' ', ' ']
[' ', ' ', ' ']
[' ', ' ', ' ']
[' ', ' ', ' ']
[' ', ' ', ' ']
[' ', ' ', ' ']
[' ', ' ', ' ']
This gives us the flag, IW{3DRC3}.
Code
# The cube
sides = {
'W': [
['{', '3', ' '],
[' ', 'D', 'R'],
[' ', 'W', ' '],
],
'O': [
[' ', ' ', ' '],
[' ', ' ', ' '],
[' ', ' ', ' '],
],
'Y': [
['}', ' ', ' '],
['3', ' ', ' '],
[' ', ' ', ' '],
],
'R': [
['I', ' ', ' '],
[' ', ' ', ' '],
[' ', ' ', 'C'],
],
'G': [
[' ', ' ', ' '],
[' ', ' ', ' '],
[' ', ' ', ' '],
],
'B': [
[' ', ' ', ' '],
[' ', ' ', ' '],
[' ', ' ', ' '],
],
}
# This rotates all squares on one side
def rot(s):
return [[s[2-i][j] for i in range(3)] for j in range(3)]
# These functions rotate adjacent sides
def F(c):
tmp = (c[2][2][0], c[2][2][1], c[2][2][2])
c[2][2][0], c[2][2][1], c[2][2][2] = c[4][2][2], c[4][1][2], c[4][0][2]
c[4][2][2], c[4][1][2], c[4][0][2] = c[5][0][2], c[5][0][1], c[5][0][0]
c[5][0][2], c[5][0][1], c[5][0][0] = c[1][0][0], c[1][1][0], c[1][2][0]
c[1][0][0], c[1][1][0], c[1][2][0] = tmp
c[0] = rot(c[0])
return c
def R(c):
tmp = (c[2][2][2], c[2][1][2], c[2][0][2])
c[2][2][2], c[2][1][2], c[2][0][2] = c[0][2][2], c[0][1][2], c[0][0][2]
c[0][2][2], c[0][1][2], c[0][0][2] = c[5][2][2], c[5][1][2], c[5][0][2]
c[5][2][2], c[5][1][2], c[5][0][2] = c[3][0][0], c[3][1][0], c[3][2][0]
c[3][0][0], c[3][1][0], c[3][2][0] = tmp
c[1] = rot(c[1])
return c
def U(c):
tmp = (c[3][0][2], c[3][0][1], c[3][0][0])
c[3][0][2], c[3][0][1], c[3][0][0] = c[4][0][2], c[4][0][1], c[4][0][0]
c[4][0][2], c[4][0][1], c[4][0][0] = c[0][0][2], c[0][0][1], c[0][0][0]
c[0][0][2], c[0][0][1], c[0][0][0] = c[1][0][2], c[1][0][1], c[1][0][0]
c[1][0][2], c[1][0][1], c[1][0][0] = tmp
c[2] = rot(c[2])
return c
def B(c):
tmp = (c[2][0][2], c[2][0][1], c[2][0][0])
c[2][0][2], c[2][0][1], c[2][0][0] = c[1][2][2], c[1][1][2], c[1][0][2]
c[1][2][2], c[1][1][2], c[1][0][2] = c[5][2][0], c[5][2][1], c[5][2][2]
c[5][2][0], c[5][2][1], c[5][2][2] = c[4][0][0], c[4][1][0], c[4][2][0]
c[4][0][0], c[4][1][0], c[4][2][0] = tmp
c[3] = rot(c[3])
return c
def L(c):
tmp = (c[2][0][0], c[2][1][0], c[2][2][0])
c[2][0][0], c[2][1][0], c[2][2][0] = c[3][2][2], c[3][1][2], c[3][0][2]
c[3][2][2], c[3][1][2], c[3][0][2] = c[5][0][0], c[5][1][0], c[5][2][0]
c[5][0][0], c[5][1][0], c[5][2][0] = c[0][0][0], c[0][1][0], c[0][2][0]
c[0][0][0], c[0][1][0], c[0][2][0] = tmp
c[4] = rot(c[4])
return c
def D(c):
tmp = (c[0][2][0], c[0][2][1], c[0][2][2])
c[0][2][0], c[0][2][1], c[0][2][2] = c[4][2][0], c[4][2][1], c[4][2][2]
c[4][2][0], c[4][2][1], c[4][2][2] = c[3][2][0], c[3][2][1], c[3][2][2]
c[3][2][0], c[3][2][1], c[3][2][2] = c[1][2][0], c[1][2][1], c[1][2][2]
c[1][2][0], c[1][2][1], c[1][2][2] = tmp
c[5] = rot(c[5])
return c
def print_cube(c):
for side in c:
for row in side:
print row
print ""
def solve(c):
# Original:
# F' D' U' L' U' F2 B2 D2 F' U D2 B' U' B2 R2 D2 B' R' U B2 L U R' U' L'
# Reverse:
# L U R U' L' B2 U' R B D2 R2 B2 U B D2 U' F D2 B2 F2 U L U D F
c = L(c) # L
c = U(c) # U
c = R(c) # R
c = U(U(U(c))) # U'
c = L(L(L(c))) # L'
c = B(B(c)) # B2
c = U(U(U(c))) # U'
c = R(c) # R
c = B(c) # B
c = D(D(c)) # D2
c = R(R(c)) # R2
c = B(B(c)) # B2
c = U(c) # U
c = B(c) # B
c = D(D(c)) # D2
c = U(U(U(c))) # U'
c = F(c) # F
c = D(D(c)) # D2
c = B(B(c)) # B2
c = F(F(c)) # F2
c = U(c) # U
c = L(c) # L
c = U(c) # U
c = D(c) # D
c = F(c) # F
if __name__ == '__main__':
# Color of sides in format:
# Front, right, up, back, left, down
starting_pos = "GRWBOY"
c = []
for ch in starting_pos:
c.append(sides[ch])
solve(c)
print_cube(c)