
So the qr code looks broken. Doing a reverse image search on the first image reveals that the image was taken at Cambridge North railway station. The cladding of the building features a pierced design derived from Rule 30. So I thought maybe generate a Rule 30 image, put it over the qr code and do some xoring. First I shrunk the QR code image to 33×33 pixels. As the hints state, that centering is hard, I wrote the following python script which generates a Rule 30 set with the same size as the qr code, shifts it left respectively right, does the xoring, and stores the resulting image in a subfolder.
import sys
MAX_TIME = 33
HALF_SIZE = MAX_TIME
indices = range(-HALF_SIZE, HALF_SIZE+1)
# initial condition
cells = {i: '0' for i in indices}
cells[0] = '1'
# padding on both ends
cells[-HALF_SIZE-1] = '0'
cells[ HALF_SIZE+1] = '0'
new_state = {"111": '0', "110": '0', "101": '0', "000": '0',
"100": '1', "011": '1', "010": '1', "001": '1'}
from PIL import Image
xm = Image.open('hv33.png')
rules = [[0 for _ in range(33)] for i in range(67)]
for time in range(0, MAX_TIME):
x = 0
for i in cells:
if x < 65:
rules[x][time] = (int(cells[i]))
x += 1
# evolve
patterns = {i: cells[i-1] + cells[i] + cells[i+1] for i in
indices}
cells = {i: new_state[patterns[i]] for i in indices}
cells[-HALF_SIZE-1] = '0'
cells[ HALF_SIZE+1] = '0'
for x in range(1, 2 * 66):
im = Image.new('RGB', (33, 33))
pixels = im.load()
for i in range(33):
for j in range(33):
if xm.getpixel((i, j)) == 0:
val = 1
else:
val = 0
if i - 66 + x > 0 and i - 66 + x < len(rules):
val = val ^ int(rules[i - 66 + x][j])
if val == 1:
pixels[i,j] = (0, 0, 0)
else:
pixels[i,j] = (255, 255, 255)
im.save('img/' + str(x) + '.png', 'PNG')
One of the images turns out to be a valid qr code which reveals the flag: HV19{Cha0tic_yet-0rdered}.