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}.