DEV Community

Cover image for Advent of Code 2019 Solution Megathread - Day 8: Space Image Format

Advent of Code 2019 Solution Megathread - Day 8: Space Image Format

Jon Bristow on December 08, 2019

Hooray! A reason to make cool gifs^TM. Day 8 - The Problem In order to finish booting a Elvish Mars Rover, they sent us a picture of th...
Collapse
 
fossheim profile image
Sarah

Such a big contrast with yesterday and especially friday.

Quick and dirty Python solution:

# Get the input
from input import puzzle_input

# Set the width and height
width = 25
height = 6

# Split the input up in layers
layers = []
layer = ""
for index in range(0,len(puzzle_input) + 1):
    if index % (width * height) == 0:
        if layer:
            layers.append(layer)
        layer = ""

    if index < len(puzzle_input):
        layer += puzzle_input[index]

# Loop through the colors
index = 0
colors = ""
while index < len(layers[0]):
    for layer in layers:
        if layer[index] != "2":
            if layer[index] == "0":
                colors += " "
            else:
                colors += "1"
            break
    index += 1

    if index % width == 0:
        colors += "\n"


print(colors)
Collapse
 
robertcram profile image
RobertCram

Hi Sarah,

this one is driving me crazy. I've got exact the same solution you have, but I don't know how to submit it.

I've tried submitting the solution as a string of "0" and "1", but that doesn't work. Same for a string of spaces and "1".

Is there something I'm not seeing?

Any help would be appreciated :)

Collapse
 
fossheim profile image
Sarah

If you print it like in my example, it should display letters. For example

 11    
1  1 
1      
1      
1  1 
 11   

Would be a C. Just type the letters in as a solution :)

Thread Thread
 
robertcram profile image
RobertCram

Thanks Sarah. That did the trick. Glad to be past this one. Lots of catching up to do :)

Thread Thread
 
fossheim profile image
Sarah

Glad I could help :) I’m very far behind as well, have been out sick the past week and a half so didn’t get much done lately. Good thing I have two weeks of vacation coming up.

Collapse
 
jbristow profile image
Jon Bristow • Edited

Part 1:

is the number of 1 digits multiplied by the number of 2 digits

Should be an integer number. Mine was in the 10e4 range.

Part 2

What message is produced after decoding your image?

This answer should be a string of capital letters. (Regex: [A-Z]+) Mine was 5 letters long

You’ll have to use your monkey meat based ocr system unless you’re a maniac who has a python one on hand.

Thread Thread
 
robertcram profile image
RobertCram

Thanks Jon. A bit of squinting helped :)

Collapse
 
rizzu26 profile image
Rizwan

Finally somthing which doesn't involve with opcode :P

Solution in swift

import Cocoa

let width = 25
let tall = 6

let digits = input.compactMap{ Int(String($0))}
let numberOfLayer = digits.count/(width * tall)

var startIndex = 0
let layers = (1...numberOfLayer).map{ layerNumber -> Array<Int> in
    let count = (width * tall) * layerNumber
    let layer = digits[startIndex..<count]
    startIndex = count
    return Array(layer)
}

func partTwo() {
    var result: [Int] = Array.init(repeating: 0, count: width * tall)

    let first = layers.first!

    for (index,value) in first.enumerated() {
        if value == 0 || value == 1 {
            result[index] = value
        }
        else {
            for layer in layers.dropFirst() {
                if (layer[index] == 0 || layer[index] == 1) {
                    result[index] = layer[index]
                    break
                }
            }
        }
    }

    startIndex = 0
    while true {
        let count = width + startIndex
        let value = result[startIndex..<count]
        startIndex = count
        print(value)
        if startIndex >= result.count {
            break
        }
    }
}

func partOne() {
    var low:[Int] = []
    var lowestCount: Int = Int.max
    for layer in layers {
        let dict = layer.reduce(into: [:]) { counts, number in
            counts[number, default: 0] += 1
        }
        let count = dict[0]
        if let count = count {
            if count < lowestCount {
                lowestCount = count
                low = layer
            }
        }
    }
    let dict = low.reduce(into: [:]) { counts, number in
        counts[number, default: 0] += 1
    }
    let result = (dict[1] ?? 0) * (dict[2] ?? 0)
    print("Result is : \(result)")
}

partOne()
partTwo()
Collapse
 
johnnyjayjay profile image
Johnny • Edited

Finally, a problem made for FP again.
First, a small utility function to partition an encoded image into its layers:

; Returns a sequence of the layers in an encoded image starting from the bottom layer. Not lazy
(defn layers [image dimensions]
  (reverse (partition (apply * dimensions) image)))

Part 1:

; For an encoded image (sequence of chars or string) and dimensions in the format [x y],
; finds the layer that has the least '0' pixels and calculates the value required to solve part 1.
(defn corruption-test [image dimensions]
  (let [least-zeros-layer (apply min-key #(% \0) (map frequencies (layers image dimensions)))]
    (* (least-zeros-layer \1) (least-zeros-layer \2))))

Part 2:

; Layers a pixel onto a base pixel. If the new pixel is transparent, returns the base pixel, else the new pixel.
(defn layer [base-pixel new-pixel]
  (if (= \2 new-pixel) base-pixel new-pixel))

; Returns a suited string representation for an encoded pixel.
(defn pixel-to-str [pixel]
  (if (= pixel \0) " " "█"))

; Decodes an encoded image (raw string or sequence of chars).
; Splits the input into layers, reduces them by layering them on top of each other,
; maps each pixel to an according string representation, partitions the result into rows
; (depicted by the provided dimensions) and returns them as a string joined with new lines.
(defn decode [image dimensions]
  (apply str (flatten (interpose "\n" (partition (dimensions 0) (map pixel-to-str (reduce (partial map layer) (layers image dimensions))))))))

(Full code: github.com/jkoenig134/AdventOfCode...)

Collapse
 
smh30 profile image
Stephanie Hope

This one was pretty straightforward for me, aside from a silly mistake at the end which was making my image a mess. I'm much happier about the puzzles now that I've given myself permission to skip any that involve the word 'intcode'!

$width = 25;
$height = 6;

$input = file_get_contents("input8.txt");
$num_layers = strlen($input) / ($width * $height);
$layer_size = strlen($input) / $num_layers;
$layers = str_split($input, $layer_size);

$min = get_fewest_zero($layers);
echo "Part 1: ".substr_count($layers[$min], "1")*substr_count($layers[$min], "2")."\n";

$image = decode_image($layers, $width, $height);
display_image($image);

function display_image($image){
    foreach ($image as $lines){
        foreach ($lines as $pixel){
            if ($pixel=="1"){
                echo "#";
            } else echo " ";
        }
        echo "\n";
    }
}

function decode_image($layers, $width, $height){
    $final_image = array_fill(0, $height, array_fill(0, $width, " "));
    foreach ($layers as $layer){
        $lines = str_split($layer, $width);
        foreach ($lines as $k=>$line){
            $chars = str_split($line);
            foreach ($chars as $m=>$char){
                if ($char != "2"){
                    if ($final_image[$k][$m] == " "){
                        $final_image[$k][$m] = $char;
                    }
                }
            }
        }
    }
    return $final_image;
}

function get_fewest_zero($layers){
$zeros_per_layer = array();
    foreach ($layers as $num=>$layer){
        $zeros_per_layer[$num] = substr_count($layer, "0");
    }
    return min(array_keys($zeros_per_layer, min($zeros_per_layer)));
}
Collapse
 
aspittel profile image
Ali Spittel • Edited

This felt like a big step down in difficulty from graph traversals and CPU madness!

def get_layer(x, y, data):
    start = 0
    while start < len(data):
        yield data[start:start + (x * y)]
        start += x * y


LAYER_HEIGHT = 6
LAYER_WIDTH = 25

with open('input.txt') as _file:
    data = _file.read()
    layers = [layer for layer in get_layer(LAYER_HEIGHT, LAYER_WIDTH, data)]
    least_zeroes = sorted(layers, key=lambda x: x.count("0"))[0]
    print(f'Part 1: {least_zeroes.count("1") * least_zeroes.count("2")}')

    stacked_pixels = ""
    for coordinate in range(LAYER_HEIGHT * LAYER_WIDTH):
        done = False
        starting_idx = 0
        while not done:
            if layers[starting_idx][coordinate] == "2":
                starting_idx += 1
            else:
                done = True
                stacked_pixels += " " if layers[starting_idx][coordinate] == "0" else "1"

    start = 0
    print("Part 2: ")
    while start < len(stacked_pixels):
        print(stacked_pixels[start:start + LAYER_WIDTH])
        start += 25
Collapse
 
neilgall profile image
Neil Gall • Edited

Felt like a Kotlin day today. Pretty simple compared to yesterday.

import java.io.File

typealias Pixel = Char

const val BLACK: Char = '0'
const val WHITE: Char = '1'
const val TRANSPARENT: Char = '2'

data class Size(val width: Int, val height: Int) {
    val pixels: Int = width * height
}

data class Layer(val pixels: List<Pixel>)

data class SpaceImageFormat(val size: Size, val layers: List<Layer>)

fun String.decode(size: Size): SpaceImageFormat =
    SpaceImageFormat(
        size = size,
        layers = trim().asIterable().chunked(size.pixels).map(::Layer)
    )

fun transparentLayer(size: Size): Layer =
    Layer(List<Pixel>(size.pixels) { TRANSPARENT })

fun Layer.countPixels(pixel: Pixel): Int =
    pixels.count { p -> p == pixel }

fun Pair<Pixel, Pixel>.merge() =
    if (first == TRANSPARENT) second else first

fun Layer.merge(next: Layer): Layer =
    Layer(pixels.zip(next.pixels).map { p -> p.merge() })

fun Layer.render(size: Size): String =
    pixels.map { p -> if (p == WHITE) '#' else ' ' }
        .chunked(size.width)
        .map { row -> row.joinToString("") }
        .joinToString("\n")

fun part1(image: SpaceImageFormat): Int {
    val layerWithLeastZeros = image.layers.minBy { layer -> layer.countPixels('0') }!!
    val ones = layerWithLeastZeros.countPixels('1')
    val twos = layerWithLeastZeros.countPixels('2')
    return ones * twos
}

fun part2(image: SpaceImageFormat): String =
    image.layers.fold(transparentLayer(image.size), Layer::merge)
        .render(image.size)

fun main() {
    val image = File("input.txt").readText().decode(Size(25, 6))
    println("Part 1... ${part1(image)}")
    println("Part 2...\n${part2(image)}")
}
Collapse
 
choroba profile image
E. Choroba

Using my native programming language, Perl:

Part 1

#! /usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

my $SIZE = 25 * 6;

open my $in, '<', shift or die $!;

my $min = $SIZE;
my $result;
while ($SIZE == read $in, my $layer, $SIZE) {
    my $zeros = $layer =~ tr/0//;
    if ($zeros < $min) {
        my $ones = $layer =~ tr/1//;
        my $twos = $layer =~ tr/2//;
        $result = $ones * $twos;
        $min = $zeros;
    }
}
say $result;

Part 2

#! /usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

my $WIDTH = 25;
my $SIZE = $WIDTH * 6;

open my $in, '<', shift or die $!;

my $result = '2' x $SIZE;
while ($SIZE == read $in, my $layer, $SIZE) {
    for my $i (0 .. $SIZE - 1) {
        my $front = substr $result, $i, 1;
        next if $front != 2;

        my $back  = substr $layer,  $i, 1;
        substr $result, $i, 1, $back;
    }
}
say $result =~ s/.{$WIDTH}\K/\n/gr =~ tr/01/ #/r;

Both solutions use the transliteration operator tr/// a lot. It's because it not only replaces characters one for one, but also returns the number of given characters in a string.

Collapse
 
mellen profile image
Matt Ellen-Tsivintzeli • Edited

SO much less difficult than yesterday. It took me a while to read the instructions correctly (need to sleep), but then I was away :D


function Pic(width, height, pixels)
{
    this.layers = [];
    let layerPixelCount = width * height;
    for(let pi = 0; pi < pixels.length; pi += layerPixelCount)
    {        
        let layerpixels = pixels.slice(pi, layerPixelCount+pi);
        let layer = new Layer(width, height, layerpixels)
        this.layers.push(layer);
    }
    this.width = width;
    this.height = height;
    this.pixels = pixels;
}

Pic.prototype.crc = function()
{
    let zerocount = Number.MAX_SAFE_INTEGER;
    let result = 0;
    for(let li = 0; li < this.layers.length; li++)
    {
        let layer = this.layers[li];
        let zc = layer.nCount(0);
        if(zc < zerocount)
        {
            zerocount = zc;
            let ones = layer.nCount(1);
            let twos = layer.nCount(2);
            result = ones*twos;
        }
    }
    return result;
};

Pic.prototype.render = function()
{
    let renderedLayer = new Layer(this.width, this.height, this.layers[0].pixels.slice(0, this.layers[0].pixels.length));
    for(let li = 1; li < this.layers.length; li++)
    {
        renderedLayer.replaceTransparent(this.layers[li].pixels);
    }

    console.log(renderedLayer.toString());
}

function Layer(width, height, pixels)
{
    this.width = width;
    this.height = height;
    this.pixels = pixels;
}

Layer.prototype.getRow = function(y)
{
    let start = y * this.width;
    let end = (y+1) * this.width;
    return this.pixels.slice(start, end);
};

Layer.prototype.nCount = function(n)
{
    return this.pixels.reduce((acc, p) => p === n ? (acc+1) : acc, 0);
}

Layer.prototype.replaceTransparent = function(newPixels)
{
    for(let pi = 0; pi < this.pixels.length; pi++)
    {
        if(this.pixels[pi] == 2)
        {
            this.pixels[pi] = newPixels[pi];
        }
    }
}

Layer.prototype.toString = function()
{
    let str = '';
    for(let y = 0; y < this.height; y++)
    {
        str += this.getRow(y).map(p =>
                                  {
                                      let result = '';
                                      switch(p)
                                      {
                                          case 0: result = '\u25af'; break;
                                          case 1: result = '\u25ae'; break;
                                      }
                                      return result;
                                  }).join('');
        str += '\n';
    }
    return str;
};

function day8()
{
    let pixels = document.querySelector('pre').innerHTML.trim().split('').map(s => parseInt(s));
    let pic = new Pic(25, 6, pixels);
    console.log('crc', pic.crc());
    pic.render();
}
Collapse
 
lindakatcodes profile image
Linda Thompson • Edited

Horray, a day I could finish! ;) I've been quite as I'm still struggling to solve part 2 for both day 6 and day 7....taking a break from those for a few days! (So I say, anyways lol)

JavaScript solution below - I used the Chalk library to colorize my text so the image was easier to read, and grabbed a screenshot. A little sad my image didn't spell out anything obvious, like I saw some other people's on the subreddit! My little terminal snowman makes me happy though. lol :)

Also certain this could have been done in fewer passes through the layers, but it was still very fast so not concerned about it.

Day 8 part 2 solution image

const fs = require('fs');
const chalk = require('chalk');

const data = fs.readFileSync('../2019 Solutions/inputs/day08input.txt').toString();

const input = data.split('').map(Number);

// image will be 25 px wide (rows) by 6 px tall (cols)
// so each layer is 25x6 - need to know how many of each digit (0,1,2) exist on ea layer
// find layer with the fewest 0s, then find # of 1s x # of 2s
let width = 25;
let height = 6;

let layers = [];
let zeros = 0;
let ones = 0;
let twos = 0;

let lowestZeros = 0;
let multi = 0;

let currRow = [];
let currLayer = [];
let currCol = 1;

// build out the layers
for (let i = 0; i <= input.length; i++) {
  if (currRow.length < width) {
    currRow.push(input[i]);
  } else if (currRow.length === width) {
    if (currCol < height) {
      currLayer.push(currRow);
      currRow = [];
      currRow.push(input[i]);
      currCol++;
    } else if (currCol === height) {
      currLayer.push(currRow);
      layers.push(currLayer);
      currLayer = [];
      currRow = [];
      currCol = 1;
      currRow.push(input[i]);
    }
  }
}

// part 1 - count the values
layers.forEach(arr => {
  for (let i = 0; i < arr.length; i++) {
    let currArr = arr[i];
    for (let c = 0; c < currArr.length; c++) {
      let val = currArr[c];
      if (val === 0) {
        zeros++;
      } else if (val === 1) {
        ones++;
      } else if (val === 2) {
        twos++;
      }
    }
  }
  if (lowestZeros === 0) {
    lowestZeros = zeros;
      multi = ones * twos;
  } else if (zeros < lowestZeros) {
      lowestZeros = zeros;
      multi = ones * twos;
    }
    ones = 0;
    twos = 0;
    zeros = 0;
});

console.log(`Part 1: ones * twos is ${multi}`);

let finalImage = [];
let singleRow = [];

// part 2 - determine which pixels show up
layers.forEach(layer => {
  for (let y = 0; y < layer.length; y++) {
    for (let x = 0; x < layer[y].length; x++) {
      if (finalImage[y]) {
        singleRow = finalImage[y];
        let data = singleRow[x];
        if (data === 2) {
          if (layer[y][x] === 0) {
            singleRow.splice(x, 1, 0);
          } else if (layer[y][x] === 1) {
            singleRow.splice(x, 1, 1);
          }
        }
        } else {
          singleRow = layer[y];
        } 
      }
    finalImage.splice(y, 1, singleRow);
  }
})

// colorize each pixel to read message
finalImage.forEach(row => {
  for (let h = 0; h < row.length; h++) {
    if (row[h] === 0) {
      row[h] = chalk.black(0);
    } else if (row[h] === 1) {
      row[h] = chalk.white.bgWhite(1);
    } else if (row[h] === 2) {
      row[h] = chalk.hidden(2);
    }
  }
  row.join();
  console.log(`${row}`);
})
Collapse
 
ballpointcarrot profile image
Christopher Kruse

Nice to have an easier problem after 2/5/7. :)

Did it in Ruby this time (I'm a few days behind, so I'm working on catching up):

module Day8
  WIDTH = 25
  HEIGHT = 6

  def self.image_layers(input)
    layer_counts = []
    input.split('').each_slice(WIDTH*HEIGHT) do |layer|
      layer_counts << layer
    end
    layer_counts
  end

  def self.fewest_zeroes(layers)
    frequencies = layers.map do |layer|
      layer.inject(Hash.new(0)) {|hash, n| hash[n] += 1; hash}
    end
    frequencies.sort_by {|l| l['0']}
  end

  def self.merge_layers(layers)
    layers.inject([]) do |top, layer|
      top = layer if top.empty?
      top.zip(layer).map do |p1, p2|
        if ['0','1'].include? p1
          p1
        elsif ['0','1'].include? p2
          p2
        else
          p1
        end
      end
    end
  end

  def self.print_layer(layer)
    layer.each_slice(WIDTH) {|line| puts line.join.gsub('0',' ')}
  end
end

# Execution
layers = Day8.image_layers(File.read('resources/2019-08-input').strip)
lowest = Day8.fewest_zeroes(layers)[0]

puts lowest['1'] * lowest['2']
puts ""
Day8.print_layer(Day8.merge_layers(layers))

Collapse
 
rpalo profile image
Ryan Palo

Woohoo! Had some time to knock together a solution during lunch! It also contains what I feel like is my most Rust-y code I've ever written, where I fold, zip, map, and into_iter all in a few lines. I'm still upset at the obscene hoops I had to go through to read a file and parse each character to a digit. But baby steps...

/// Day 8: Space Image Format
/// 
/// Parse a layered image format being STREAMED THROUGH SPACE

use std::fs;
use std::convert::TryInto;

/// Expects a single line of 0's 1's and 2's
fn parse_input() -> Vec<Vec<usize>> {
    let text = fs::read_to_string("data/day8.txt").unwrap();

    let mut results: Vec<Vec<usize>> = vec![];
    let mut current: Vec<usize> = vec![];
    let mut counter = 0;

    for c in text.chars() {
        current.push(c.to_digit(10).unwrap().try_into().unwrap());
        counter += 1;
        if counter == 25 * 6 {
            counter = 0;
            results.push(current);
            current = vec![];
        }
    }

    results
}

/// To verify the parsing, find the layer with the fewest zeros and return the
/// number of 1's in that layer times the number of 2's.
fn part1(layers: &Vec<Vec<usize>>) -> usize {
    let target_layer: &Vec<usize> = layers.iter().min_by_key(|layer| {
        layer.iter().filter(|x| **x == 0).count()
    }).unwrap();
    let ones = target_layer.iter().filter(|x| **x == 1).count();
    let twos = target_layer.iter().filter(|x| **x == 2).count();

    ones * twos
}

/// Stack the layers top to bottom.  If a layer contains a 2, it's transparent
/// and layers below can be seen.  1's are black.  0's are white.
/// 
/// Show the final compressed image.
fn part2(layers: Vec<Vec<usize>>) {
    let start: Vec<usize> = vec![2; 25*6];
    let result = layers.into_iter().fold(start, |acc, layer| {
        acc.into_iter().zip(layer.into_iter()).map(|(current, new)| {
            if current == 2 {
                new
            } else {
                current
            }
        }).collect()
    });

    for row in 0..6 {
        for col in 0..25 {
            print!("{}", if result[row * 25 + col] == 1 {"#"} else {" "});
        }
        print!("\n");
    }
}

pub fn run() {
    let layers = parse_input();
    println!("Part 1 checksum: {}", part1(&layers));
    part2(layers);
}
Collapse
 
maxart2501 profile image
Massimo Artizzu • Edited

Aw yeah something simple again! Maybe even a little bit too simple? 😄

Anyway, in JavaScript:

const WIDTH = 25;
const HEIGHT = 6;
const layerSize = WIDTH * HEIGHT;

const layers = input.match(new RegExp(`[012]{${layerSize}}`, 'g'));

// Part One
function count(string, needle) {
  return string.split(needle).length - 1;
}
const digitCounts = [0, 1, 2].map(digit => layers.map(layer => count(layer, digit)));
const minZeros = Math.min(...digitCounts[0]);
const minZeroLayerIndex = digitCounts[0].indexOf(minZeros);

console.log(digitCounts[1][minZeroLayerIndex] * digitCounts[2][minZeroLayerIndex]);

// Part Two
const composed = Array.from({ length: layerSize }, (_, index) => {
  return layers.find(layer => layer[index] !== '2')[index];
});

// Pretty print the output 🤪
console.log(
  composed.join('')
    .replace(/0/g, ' ').replace(/1/g, '#')
    .match(new RegExp(`[012]{${WIDTH}}`, 'g'))
    .join('\n')
);

Check my repo for my input.

By the way, here's the language count for day 7 (it could be subject to change as it was a kind of complex challenge):

JavaScript × 3
Python × 2
C × 1
Clojure × 1
Java × 1
Swift × 1

Collapse
 
nordfjord profile image
Einar Norðfjörð

Functional JS style today

const input = require('fs')
  .readFileSync(0)
  .toString()

const WIDTH = 25
const HEIGHT = 6
const layerSize = WIDTH * HEIGHT

const Id = x => ({
  value: x,
  map(fn) {
    return Id(fn(x))
  }
})

const splitEvery = n => list => {
  const result = []
  let idx = 0
  while (idx < list.length) {
    result.push(list.slice(idx, (idx += n)))
  }
  return result
}
const replace = regx => repl => str => str.replace(regx, repl)
const join = w => list => list.join(w)
const map = fn => list => list.map(fn)
const reduce = reducer => initial => list => list.reduce(reducer, initial)
const count = (x, str) => str.match(new RegExp(x, 'g')).length

function part1(input) {
  return Id(input)
    .map(splitEvery(layerSize))
    .map(
      map(x => ({
        0: count(0, x),
        1: count(1, x),
        2: count(2, x),
        x
      }))
    )
    .map(reduce((p, v) => (p[0] < v[0] ? p : v))({ 0: Infinity }))
    .map(l => l[1] * l[2]).value
}

function part2(input) {
  return Id(input)
    .map(splitEvery(layerSize))
    .map(layers =>
      Array.from({ length: layerSize }).map(
        (_, i) => layers.find(l => l[i] !== '2')[i]
      )
    )
    .map(join(''))
    .map(replace(/0/g)(' '))
    .map(replace(/1/g)('#'))
    .map(splitEvery(WIDTH))
    .map(join('\n')).value
}

console.log(part1(input))
console.log(part2(input))
Collapse
 
jbristow profile image
Jon Bristow

This was a nice and simple solution as long as you remembered how to pivot a list of lists. Kotlin's builtin minBy and count functions also simplify things a lot.

import arrow.core.firstOrNone
import arrow.core.getOrElse
import java.nio.file.Files
import java.nio.file.Paths

object Day08 {

    private const val FILENAME = "src/main/resources/day08.txt"
    val fileData: String = Files.readAllLines(Paths.get(FILENAME)).first()

    private fun List<String>.countAllEqualTo(match: Char): Int {
        return sumBy { row -> row.count { pixel -> pixel == match } }
    }

    fun part1(input: String): Int {
        val layerWithLeast0s = input.chunked(25).chunked(6).minBy { layer ->
            layer.countAllEqualTo('0')
        }.orEmpty()

        val num1s = layerWithLeast0s.countAllEqualTo('1')
        val num2s = layerWithLeast0s.countAllEqualTo('2')
        return num1s * num2s
    }

    private fun List<String>.findPixelColors() =
        (this[0].indices).map { i -> map { it[i] } }
            .map(this@Day08::findPixelColor)

    private fun findPixelColor(it: List<Char>) =
        it.dropWhile { c -> c == '2' }
            .firstOrNone()
            .getOrElse { '2' }

    private fun List<List<Char>>.renderImage() =
        joinToString("\n") { row ->
            row.joinToString("") { pixel ->
                when (pixel) {
                    '0' -> '.'
                    '1' -> '#'
                    '2' -> ' '
                    else -> '?'
                }.toString()
            }
        }

    fun part2(input: String) =
        input.chunked(25 * 6)
            .findPixelColors()
            .chunked(25)
            .renderImage()

}

fun main() {
    println(Day08.part1(Day08.fileData))
    println(Day08.part2(Day08.fileData))
}
Collapse
 
jbristow profile image
Jon Bristow • Edited

Here's a version of part1 that does everything in two passes of the list instead of four.

    fun part1(input: String) =
        input.chunked(25 * 6)
            .asSequence()
            .map { layer ->
                layer.groupBy { it }
                    .mapValues { (_, v) -> v.size }
            }.minBy { it.getOrDefault('0', 0) }?.let {
                it.getOrDefault('1', 0) * it.getOrDefault('2', 0)
            } ?: 0
Collapse
 
peter_8000 profile image
Piotr

Another Python hack...

Part 1

from collections import Counter

with open("day08.txt") as file:
    raw = file.read().strip()

chunks = []
for idx in range(0, len(raw), 150):
    chunks.append(raw[idx:idx+150])

zero_counts = [Counter(chunk)["0"] for chunk in chunks]
least_zero_layer = counts.index(sorted(zero_counts)[0])
cnt = Counter(chunks[least_zero_layer])
result = cnt["1"] * cnt["2"]

print("Solution Pt.1:", result)

Part 2

image = []

for idx in range(150):
    if idx!= 0 and idx % 25 == 0:
        image.append("\n")
    layer = 0

    while True:
        pixel = int(chunks[layer][idx])
        if pixel == 0 or pixel == 1:
            image.append(str(pixel))
            break
        layer +=1

print("".join(image).replace("0", " "))
Collapse
 
thibpat profile image
Thibaut Patel

Ascii art is relaxing comparing it IntCode computers <3