8 votes

Day 8: Space Image Format

Today's problem description: https://adventofcode.com/2019/day/8


Join the Tildes private leaderboard! You can do that on this page, by entering join code 730956-de85ce0c.

Please post your solutions in your own top-level comment. Here's a template you can copy-paste into your comment to format it nicely, with the code collapsed by default inside an expandable section with syntax highlighting (you can replace python with any of the "short names" listed in this page of supported languages):

<details>
<summary>Part 1</summary>

```python
Your code here.
```

</details>

9 comments

  1. [3]
    PapaNachos
    (edited )
    Link
    Today's seems pretty easy. I accidentally read 'most' instead of 'least' so that threw me off a bit during part 1 and explains why I have functionality that's unnecessary Part 1 - Python Literally...

    Today's seems pretty easy. I accidentally read 'most' instead of 'least' so that threw me off a bit during part 1 and explains why I have functionality that's unnecessary

    Part 1 - Python Literally half this code is unnecessary if you just want the solution and nothing else
    width = 25
    height = 6
    layer_len = width * height
    layers = [(input_string[i:i+layer_len]) for i in range(0, len(input_string), layer_len)] 
    most_0s = 0
    most_0s_string = ''
    least_0s = 150
    least_0s_string = ''
    for layer in layers:
        zero_count = 0
        for i in layer: 
            if i == '0': 
                zero_count = zero_count + 1
        if zero_count > most_0s:
            most_0s = zero_count
            most_0s_string = layer
        if zero_count < least_0s:
            least_0s = zero_count
            least_0s_string = layer
            
    ones_count = 0
    twos_count = 0
    for i in least_0s_string:
        if i == '1': 
            ones_count = ones_count + 1
        if i == '2': 
            twos_count = twos_count + 1
            
            
    print(most_0s)
    print(most_0s_string)
    print(least_0s)
    print(least_0s_string)
    print(ones_count)
    print(twos_count)
    
    Part 2 builds directly on Part 1. I didn't change anything in Part 1, so this is just the extra code.
    Part 2 - Python I created a string-stand-in the length of the layer and set it all to 'transparent'. Then I looped through the layers in order and replaced any 'transparent' values with either a space or a black block. Once that was done I took that string and formatted it the way it was supposed to be and printed it out
    def divide_chunks(l, n): 
        # looping till length l 
        for i in range(0, len(l), n):  
            yield l[i:i + n] 
    
    final_result = list(['transparent'] * (width * height))
    for layer in layers:
        for i in range(len(layer)):
            if final_result[i] == 'transparent':
                #print(layer[i])
                if layer[i] is '0':
                    #print('This is black')
                    final_result[i] = '█'
                elif layer[i] is '1':
                    #print('This is white')
                    final_result[i] = ' '
    print(final_result)
    
    
    output_split = list(divide_chunks(final_result, width)) 
    print (output_split) 
    for row in output_split:
        row_merged = ""
        for char in row:
            row_merged = row_merged + char
        print(row_merged)
    
    Non-spoilery Tip Depending on how you make the final image be careful. In my example my 'white' 'pixels' showed up as nothing against the white-background, making my left-most 'L' hard to read, because it just make the image look smaller

    Edit:
    I get that this one is relatively easy, but how the actual fuck did someone get both stars in < 4 minutes. I'm convinced that a lot of the scores on the official leader boards are from people who are cheating

    3 votes
    1. [2]
      Crestwave
      Link Parent
      Wait, you're supposed to actually color the output? I just output mine in ones and zeroes like in the examples, although I did note that it was a bit hard to read (but easily solvable with a sed...

      Wait, you're supposed to actually color the output? I just output mine in ones and zeroes like in the examples, although I did note that it was a bit hard to read (but easily solvable with a sed 's/0/ /g').

      Also, considering my solutions are 17 and 28 lines respectively with blank lines and shebang and all, it seems possible for a hardcore programmer to finish it in 4 minutes, although unlikely, perhaps. Either way, I wouldn't worry about the leaderboards, as I mentioned in a previous thread.

      3 votes
      1. PapaNachos
        (edited )
        Link Parent
        I just think that you're supposed to make the output readable, however that works for you. Remember that 4 minutes also includes reading and understanding the question. It seems more likely to me...

        I just think that you're supposed to make the output readable, however that works for you.

        Remember that 4 minutes also includes reading and understanding the question. It seems more likely to me that that is how long it took them to brute force it

        1 vote
  2. nothis
    (edited )
    Link
    God I love when I can visualize the problem, way more fun! The whole thing only took me 20 minutes, most of that spent not realizing javascript treats text like numbers in pretty much every case...

    God I love when I can visualize the problem, way more fun! The whole thing only took me 20 minutes, most of that spent not realizing javascript treats text like numbers in pretty much every case except when you expect it to. I also inverted white and black to make it more readable.

    Part 1+2
    const checkSum = (pixels, width, height) => {
      let zeroCount = 0;
      let oneCount = 0;
      let twoCount = 0;
      let fewestZeros = Number.MAX_VALUE;
      let checkSum = 0;
    
      const layerCount = pixels.length / (width * height);
      for (let layer = 0; layer < layerCount; layer++) {
        for (let row = 0; row < height; row++) {
          for (let column = 0; column < width; column++) {
            const pixel = pixels[layer * height * width + row * width + column];
            if (pixel === "0") {
              zeroCount++;
            } else if (pixel === "1") {
              oneCount++;
            } else if (pixel === "2") {
              twoCount++;
            }
          }
        }
        if (zeroCount < fewestZeros) {
          fewestZeros = zeroCount;
          checkSum = oneCount * twoCount;
        }
    
        zeroCount = 0;
        oneCount = 0;
        twoCount = 0;
      }
    
      return checkSum;
    };
    
    const drawImage = (pixels, width, height) => {
      const layerCount = pixels.length / (width * height);
      for (let row = 0; row < height; row++) {
        let imageRow = "";
        for (let column = 0; column < width; column++) {
          for (let layer = 0; layer < layerCount; layer++) {
            const pixel = pixels[layer * height * width + row * width + column];
            if (pixel === "2") {
              continue;
            } else {
              content = pixel === "1" ? "#" : " ";
              imageRow += content;
              break;
            }
          }
        }
        console.log(imageRow);
      }
    };
    
    const width = 25;
    const height = 6;
    const pixels = input.split("");
    
    console.log(`checkSum = ${checkSum(pixels, width, height)}`);
    
    drawImage(pixels, width, height);
    
    3 votes
  3. Hvv
    Link
    This one felt much simpler than yesterday's problem and is definitely amenable to quick, ad hoc solutions. C++ solution: Parts 1/2 The code is basically 3 nested for loops: 1 to go through each...

    This one felt much simpler than yesterday's problem and is definitely amenable to quick, ad hoc solutions.

    C++ solution:

    Parts 1/2 The code is basically 3 nested for loops: 1 to go through each layer, 1 to go through each row in each layer, and 1 to go through each column in each row.

    Part 1: Keep a count of all 0s,1s, and 2s per layer and keep a running minimum where when the number of 0s is less than the min, update the min and remember the number of 1s * the number of 2s in that layer.

    Part 2: We're going down layer by layer, so for each pixel in a layer, if that pixel (row/col) is not transparent and our "final image" currently doesn't have anything for that pixel position, stick it in the final image.

    Yes we have to do some interpretation, but the final result is definitely good enough to spell something out.

    Once done, print the product of the min layer and the final image (with black pixels emphasized).

    #include <bits/stdc++.h>
    using namespace std;
    int res[6][25];
    int main() {
    	string s;
    	cin >> s;
    	int sz = s.size()/(25*6);
    	int ma = 99999999,res1 = 0;
    	memset(res,-1,sizeof(res));
    	for(int k=0;k<sz;k++) {
    		int p[3] = {0,0,0};
    		for(int i=0;i<6;i++) {
    			for(int j=0;j<25;j++) {
    				int id = s[j+25*i+150*k];
    				id -= '0';
    				p[id]++;
    				if(res[i][j] == -1 && id != 2) {
    					res[i][j] = id;
    				}
    			}
    		}
    		if(p[0] < ma) {
    			ma = p[0];
    			res1 = p[1]*p[2];
    		}
    	}
    	cout << "Part 1: " << res1 << '\n';
    	cout << "Part 2:\n";
    	for(int i=0;i<6;i++) {
    		for(int j=0;j<25;j++) {
    			if(res[i][j]) {cout << "#";} else {
    				cout << ".";
    			}
    		}
    		cout << '\n';
    	}
    }
    
    2 votes
  4. aymm
    Link
    I was pretty fast (at least for me), but spent 3 hours in part 2, looking for a mistake during decoding. I did finally see that the error was not in the decoding itself, but solely in the output....

    I was pretty fast (at least for me), but spent 3 hours in part 2, looking for a mistake during decoding. I did finally see that the error was not in the decoding itself, but solely in the output. (Off by one error, causing each row to be slightly shifted, making the image completely unreadable).
    Here's the cleaned up swift code:

    Part 1 & 2
    import Foundation
    typealias Layer = [Pixel]
    
    enum Pixel: Character, CustomStringConvertible {
        case black = "0"
        case white = "1"
        case transparent = "2"
        
        var description: String {
            switch self {
            case .transparent:
                return "."
            case .black:
                return " "
            case .white:
                return "*"
            }
        }
    }
    
    func getLayers(pixels: Layer, layerSize size: Int) -> [Layer] {
       return stride(from: 0, to: pixels.count, by: size).map {
           Array(pixels[$0..<($0 + size)])
       }
    }
    
    func validateImage(layers: [Layer]) -> Int {
        let countPixel = {(a: Layer, b: Pixel) -> Int in
            a.filter { $0 == b }.count
        }
        let min = layers.min { countPixel($0, .black) < countPixel($1, .black) }!
        let val = countPixel(min, .white) * countPixel(min, .transparent)
        
        return val
    }
    
    func decode(layers: [Layer]) -> Layer {
        var finalImage = layers.first!
        for layer in layers {
            for (idx, c) in layer.enumerated() {
                if finalImage[idx] == .transparent {
                   finalImage[idx] = c
                }
            }
        }
        return finalImage
    }
    
    func go(input i: String, width: Int, height: Int) {
        let input: Layer = i.filter {$0.isWholeNumber}.map { Pixel(rawValue: $0)! }
        let size = width * height
        
        let layers = getLayers(pixels: Array(input), layerSize: size)
        
        let checksum = validateImage(layers: layers)
        print(checksum)
        
        let finalImage = decode(layers: layers)
        var str = ""
        for (idx, pixel) in finalImage.enumerated() {
            if idx > 0 && idx % width == 0 {
                str += "\n"
            }
            str += pixel.description
        }
        print(str)
    }
    
    guard let fileContents = try? String(contentsOfFile: "input.txt") else {
        fatalError("Cannot open input file")
    }
    
    go(input: fileContents, width: 25, height: 6)
    

    I started treating the pixels as enum almost from the beginning. Most refactoring I did happened in the layer parsing. I never looked much into ArraySlices and general Array handling in Swift, so I had a pretty ugly index fumbling and Array re-creating solution. After some reading into I think I found a nice solution to chunk the input into layers.

    2 votes
  5. Crestwave
    Link
    Okay, thankfully today's challenge was pretty easy, so I have time to work on yesterday's part 2. I almost got them both right on my first try, but forgot a closing parenthesis on part 1, and...

    Okay, thankfully today's challenge was pretty easy, so I have time to work on yesterday's part 2. I almost got them both right on my first try, but forgot a closing parenthesis on part 1, and accidentally used += when I meant to do concatenation on part 2.

    Part 1
    #!/usr/bin/awk -f
    { img = img $0 }
    
    END {
        fewest = 0
        for (i = 1; i <= length(img); ++i) {
            layers[i] = substr(img, 1, 150)
            img = substr(img, 151)
            count = gsub(/0/, "0", layers[i])
            if (count < fewest || ! fewest) {
                j = i
                fewest = count
            }
        }
    
        print gsub(/1/, "1", layers[j]) * gsub(/2/, "2", layers[j])
    }
    
    Part 2
    #!/usr/bin/awk -f
    { img = img $0 }
    
    END {
        width = 25
        height = 6
        len = width * height
        for (i = 1; i <= length(img); ++i) {
            layers[i] = substr(img, 1, len)
            img = substr(img, len+1)
        }
    
        img = ""
        for (i = 1; i <= len && k = 1; ++i) {
            for (;;) {
                j = substr(layers[k], i, 1)
                if (j == 2)
                    k += 1
                else
                    break
            }
    
            img = img j
        }
    
        for (i = 1; i <= length(img); i += width)
            print substr(img, i, width)
    }
    

    Also, was I the only one who had trouble reading the image? It's partially because I was expecting numbers for some reason, but I had to pipe it to sed to color it in order to see the message

    1 vote
  6. Crespyl
    (edited )
    Link
    This was nice and quick, I had fun using a library to pretty-print a colorized version of the output on the console. Day 8, Part 1 - Crystal #!/usr/bin/env crystal require "colorize" INPUT =...

    This was nice and quick, I had fun using a library to pretty-print a colorized version of the output on the console.

    Day 8, Part 1 - Crystal
    #!/usr/bin/env crystal
    require "colorize"
    
    INPUT = ARGV.size > 0 ? ARGV[0] : "day8/input.txt"
    data = File.read(INPUT).strip
    
    WIDTH = 25
    HEIGHT = 6
    
    COLORS = {
      0 => :black,
      1 => :white,
      2 => :transparent
    }
    
    def split_layers(data, width, height)
      data
        .chars
        .in_groups_of(width * height, '-')
        .reduce([] of Array(Int32)) do |layers, layer|
        layers << layer
                 .reject { |c| ! c.number? }
                 .map { |c| c.to_i }
      end
    end
    
    def layer_counts(layers)
      layers.reduce(Array(Hash(Int32,Int32)).new) do |layer_counts, layer|
        layer_counts << (0..2).each_with_object({} of Int32 => Int32) { |i,h| h[i] = layer.count(i) }
      end
    end
    
    def find_layer_with_least(layer_counts, val=0)
      layer_counts.reduce(layer_counts[0]) { |best, cur|
        cur[val] < best[val] ? cur : best
      }
    end
    
    # Part 1
    layers = split_layers(data, WIDTH, HEIGHT)
    counts = layer_counts(layers)
    
    least_zs = find_layer_with_least(counts, 0)
    puts "Part 1: %i" % (least_zs[1] * least_zs[2])
    
    Day 8, Part 2
    # Part 2
    def flatten(layers)
      layers.reverse.reduce(Array(Int32).new(WIDTH*HEIGHT,2)) do |output, layer|
        layer.each_with_index do |v, i|
          output[i] = v unless v == 2
        end
        output
      end
    end
    
    print "Part 2:"
    flattened = flatten(layers)
    flattened.each_with_index do |val, i|
      if i % WIDTH == 0
        print '\n'
      end
    
      color = COLORS[val]
    
      print " ".colorize.back(color)
    end
    print '\n'
    
    1 vote
  7. Gyrfalcon
    Link
    This was a good one, nice change of pace from the debacles with the Intputer. Part 1 & 2, Python My code below has a slightly cleaned up method of output, since I submitted the answer and then...

    This was a good one, nice change of pace from the debacles with the Intputer.

    Part 1 & 2, Python

    My code below has a slightly cleaned up method of output, since I submitted the answer and then realized I was a fool for squinting at a grid of numbers instead of formatting it nicely.

    def read_image(file_name):
    
        with open(file_name, 'r') as fid:
            raw_image = fid.read().strip()
    
        return raw_image
    
    def layerize_image(raw_image, height, width):
    
        raw_image = [int(num) for num in raw_image]
    
        count = 0
        layered_image = []
    
        while count < len(raw_image):
            layer = []
            for i in range(height):
                row = []
                for j in range(width):
                    row.append(raw_image[count])
                    count += 1
    
                layer.append(row)
            layered_image.append(layer)
    
        return layered_image
    
    def count_layer_instances(layer_img, target):
    
        layer_totals = []
    
        for layer in layer_img:
            count = 0
            for row in layer:
                for num in row:
                    if num == target:
                        count += 1
    
            layer_totals.append(count)
    
        return layer_totals
    
    def validate_image(input_file, height, width):
    
        raw_image = read_image(input_file)
    
        layer_img = layerize_image(raw_image, height, width)
    
        zeros = count_layer_instances(layer_img, 0)
    
        target_layer = zeros.index(min(zeros))
    
        ones = count_layer_instances(layer_img, 1)
        twos = count_layer_instances(layer_img, 2)
    
        return ones[target_layer] * twos[target_layer]
    
    def display_image(image):
    
        for row in image:
            row_str = ""
            for num in row:
                if num:
                    row_str += str(num)
                else:
                    row_str += " "
            print(row_str)
    
    def decode_image(input_file, height, width):
    
        raw_image = read_image(input_file)
    
        layer_img = layerize_image(raw_image, height, width)
    
        done_pixels = [[False for i in range(width)] for j in range(height)]
        final_img = [[0 for i in range(width)] for j in range(height)]
    
        for layer in layer_img:
    
            for idx in range(height):
                for jdx in range(width):
    
                    if not done_pixels[idx][jdx]:
                        if layer[idx][jdx] != 2:
                            final_img[idx][jdx] = layer[idx][jdx]
                            done_pixels[idx][jdx] = True
    
        display_image(final_img)
        return final_img
    
    
    input_file = "PuzzleInput.txt"
    layer_width = 25
    layer_height = 6
    
    result = validate_image(input_file, layer_height, layer_width)
    
    print(result)
    
    final_img = decode_image(input_file, layer_height, layer_width)
    
    1 vote