Tags: image python csv png pil 

Rating:

Description:

    Can you put this puzzle back together?
    172.31.0.10/puzzletime_7e4148595c3837b0f14598eb0f21b272

The offered file was a gzipped CSV with 48853 lines, of the format:

    Value,Right Edge Id,Bottom Edge Id,Left Edge Id,Top Edge Id
    0x111811,105686801264,887560873655,312990448974,548925778434
    0x384126,650152958814,52199694120,211764317707,433816471245
    0xc3bab1,5909959089,950174968371,749641960137,537615257711
    0x3e4a24,916756480148,216091138945,804727523965,811655369006
    0x3a4d31,426308808667,931973485956,787722656299,55311493582
    0xb6aa92,776257260469,503239175816,128107297285,702022168178
    0x516437,414462949367,364785201606,944310974796,866635322023
    0x1b291a,105359185807,715972077437,104869394726,611561837736

The first column, being a 3-byte hex value, immediately jumped out as RGB.

Grepping around for a random sampling of "Edge Id"s revealed that most edges only appeared twice in the file, a "Right Edge Id" occuring with a matching "Left Edge Id" on another line.

Needing a place to start, we guessed that "0" might be the Edge Id at the border of the image, and a quick grep revealed that to be correct:

    # Edges:

    0x121d15,0,974309486812,473992437188,497944637480
    0x1a251f,569290835136,91408348362,0,738900157213
    0x1d1f1a,404534979934,0,55925662970,84026215997
    0x6e8f98,886611260159,159819253788,530007324828,0

    # Corners:
    0x799394,286014760927,696891703802,0,0
    0x141913,854263618885,0,0,838963856720
    0x272727,0,0,898925785824,245795270114
    0x252e1d,0,420247040560,174936345665,0

By looking for how many edges occurred in a given slot, we can find the size of the image:

    $ gunzip < puzzletime_7e4148595c3837b0f14598eb0f21b272 | grep '^[^,]\+,0,' | wc -l
    207
    $ gunzip < puzzletime_7e4148595c3837b0f14598eb0f21b272 | grep '^[^,]\+,[^,]\+,0,' | wc -l
    236

By writing a program that keeps track of known edges, starting with the top-left edges of `(0,0)`, we were able to look up and render each pixel into our output image:

    import struct
    from PIL import Image, ImageDraw

    size = (207, 236)

    image = Image.new('RGB', size)
    draw = ImageDraw.Draw(image)

    def unpackColor(color):
        (r,g,b) = struct.unpack('BBB', color[2:].decode('hex'))
        return (r,g,b)

    points = dict()
    tops = dict()
    lefts = dict()

    def _color(elem): return elem[0]
    def _right(elem): return elem[1]
    def _bottom(elem): return elem[2]
    def _left(elem): return elem[3]
    def _top(elem): return elem[4]

    # Read all input into `points`, tracking the bottom and left edges
    with open('data.csv') as data:
        data.readline()
        i = 0
        for line in data:
            elem = line.strip().split(',')
            points[(_left(elem),_top(elem))] = elem
            tops.setdefault(_top(elem), []).append(elem)
            lefts.setdefault(_right(elem), []).append(elem)

    wants = dict()
    wants[('0', '0')] = (0,0)

    while wants:
        for idx in wants.keys():
            if idx not in points:
                print "Error:", idx
                del wants[idx]
                continue
            elem = points[idx]

            (x, y) = wants[(_left(elem),_top(elem))]
            print '  ', x, y, elem
            draw.point((x,y), unpackColor(_color(elem)))
            if _bottom(elem) != '0':
                wants[(_left(tops[_bottom(elem)][0]), _bottom(elem))] = (x, y+1)
            if _right(elem) != '0':
                wants[(_right(elem), _top(lefts[_right(elem)][0]))] = (x+1, y)
            del wants[idx]

    image.save('out.png')

    print "Done"

After running the program, we were greeted with a picture of a goat, with the key written across the top.

Answer:

    PuzzleGoatKeyIsBestKey