1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
/*
 * Copyright (c) 2020 Erik Nordstrøm <erik@nordstroem.no>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

solution_printer!(8, print_solution, input_generator, INPUT, solve_part_1, solve_part_2);

pub const INPUT: &str = include_str!("../input/2019/day8.txt");

/// ### Day 8: Space Image Format
///
/// [https://adventofcode.com/2019/day/8](https://adventofcode.com/2019/day/8)
///
/// The Elves' spirits are lifted when they realize you have an opportunity to
/// reboot one of their Mars rovers, and so they are curious if you would spend
/// a brief sojourn on Mars. You land your ship near the rover.
///
/// When you reach the rover, you discover that it's already in the process of
/// rebooting! It's just waiting for someone to enter a [BIOS](https://en.wikipedia.org/wiki/BIOS) password. The Elf
/// responsible for the rover takes a picture of the password (your puzzle
/// input) and sends it to you via the Digital Sending Network.
///
/// Unfortunately, images sent via the Digital Sending Network aren't encoded
/// with any normal encoding; instead, they're encoded in a special Space Image
/// Format.  None of the Elves seem to remember why this is the case. They send
/// you the instructions to decode it.
///
/// Images are sent as a series of digits that each represent the color of a
/// single pixel.  The digits fill each row of the image left-to-right, then
/// move downward to the next row, filling rows top-to-bottom until every pixel
/// of the image is filled.
///
/// Each image actually consists of a series of identically-sized *layers* that
/// are filled in this way. So, the first digit corresponds to the top-left
/// pixel of the first layer, the second digit corresponds to the pixel to the
/// right of that on the same layer, and so on until the last digit, which
/// corresponds to the bottom-right pixel of the last layer.
///
/// For example, given an image `3` pixels wide and `2` pixels tall, the image data
/// `123456789012` corresponds to the following image layers:
///
/// ```text
/// Layer 1: 123
///          456
///
/// Layer 2: 789
///          012
/// ```
///
/// The image you received is *`25` pixels wide and `6` pixels tall*.
///
/// To make sure the image wasn't corrupted during transmission, the Elves
/// would like you to find the layer that contains the *fewest `0` digits*.  On that
/// layer, what is *the number of `1` digits multiplied by the number of `2` digits?*
///
/// ### Solution
///
/// ⚠️ SPOILER ALERT ⚠️
///
/// ```
/// use codetrotter_aoc_2019_solutions::day_08::{INPUT, input_generator, solve_part_1};
/// assert_eq!(solve_part_1(&input_generator(INPUT)), 2032);
/// ```
pub fn solve_part_1 (input_image_data: &ImageData) -> usize
{
  let mut lowest_amount_of_zeros = usize::max_value();
  let mut answer = 0;

  for layer in input_image_data
  {
    let mut disregard_layer = false;

    let (mut num_zeros_in_layer, mut num_ones_il, mut num_twos_il) = (0,0,0);

    for &pixel_value in layer.iter()
    {
      if pixel_value == 0
      {
        num_zeros_in_layer += 1;
        if num_zeros_in_layer >= lowest_amount_of_zeros
        {
          disregard_layer = true;
          break;
        }
      }
      else if pixel_value == 1
        { num_ones_il += 1; }
      else if pixel_value == 2
        { num_twos_il += 1; }
    }

    if !disregard_layer
    {
      lowest_amount_of_zeros = num_zeros_in_layer;
      answer = num_ones_il * num_twos_il;
    }
  }

  answer
}

pub const IMAGE_WIDTH: usize = 25;
pub const IMAGE_HEIGHT: usize = 6;
pub const PIXELS_PER_LAYER: usize = IMAGE_WIDTH * IMAGE_HEIGHT;
pub type ImageData = Vec<LayerData>;
pub type LayerData = [u8; PIXELS_PER_LAYER];

pub fn input_generator (image_str: &str) -> ImageData
{
  let mut image_str = image_str.trim_end();

  let mut image_data = ImageData::new();

  loop
  {
    let chunk = &image_str[..PIXELS_PER_LAYER];
    image_str = &image_str[PIXELS_PER_LAYER..];

    let mut layer_pixels = [0; PIXELS_PER_LAYER];
    let hurr: Vec<_> = chunk.chars().map(|c| c.to_digit(10).unwrap() as u8).collect();
    layer_pixels.copy_from_slice(&hurr);
    image_data.push(layer_pixels);

    if image_str.len() == 0
      { break; }
  }

  image_data
}

/// ### Day 8, Part Two
///
/// [https://adventofcode.com/2019/day/8#part2](https://adventofcode.com/2019/day/8#part2)
///
/// Now you're ready to decode the image. The image is rendered by stacking the
/// layers and aligning the pixels with the same positions in each layer. The
/// digits indicate the color of the corresponding pixel: `0` is black, `1` is
/// white, and `2` is transparent.
///
/// The layers are rendered with the first layer in front and the last layer in
/// back. So, if a given position has a transparent pixel in the first and
/// second layers, a black pixel in the third layer, and a white pixel in the
/// fourth layer, the final image would have a *black* pixel at that position.
///
/// For example, given an image `2` pixels wide and `2` pixels tall, the image data
/// `0222112222120000` corresponds to the following image layers:
///
/// ```text
/// Layer 1: 02
///          22
///
/// Layer 2: 11
///          22
///
/// Layer 3: 22
///          12
///
/// Layer 4: 00
///          00
/// ```
///
/// Then, the full image can be found by determining the top visible pixel in
/// each position:
///
///   - The top-left pixel is *black* because the top layer is `0`.
///   - The top-right pixel is *white* because the top layer is `2` (transparent),
///     but the second layer is `1`.
///   - The bottom-left pixel is *white* because the top two layers are `2`, but
///     the third layer is `1`.
///   - The bottom-right pixel is *black* because the only visible pixel in that
///     position is `0` (from layer 4).
///
/// So, the final image looks like this:
///
/// ```text
/// 01
/// 10
/// ```
///
/// *What message is produced after decoding your image?*
///
/// ### Solution
///
/// ⚠️ SPOILER ALERT ⚠️
///
/// ```
/// use codetrotter_aoc_2019_solutions::day_08::{INPUT, input_generator, solve_part_2, FlattenedImage, PIXELS_PER_LAYER};
/// let flattened = FlattenedImage
/// {
///   flattened:
///   [
///     0,1,1,0,0,1,1,1,1,0,0,1,1,0,0,1,0,0,1,0,0,1,1,0,0, // "  XXXX    XXXXXXXX    XXXX    XX    XX    XXXX    "
///     1,0,0,1,0,1,0,0,0,0,1,0,0,1,0,1,0,0,1,0,1,0,0,1,0, // "XX    XX  XX        XX    XX  XX    XX  XX    XX  "
///     1,0,0,0,0,1,1,1,0,0,1,0,0,0,0,1,0,0,1,0,1,0,0,0,0, // "XX        XXXXXX    XX        XX    XX  XX        "
///     1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,1,0,1,0,1,1,0, // "XX        XX        XX        XX    XX  XX  XXXX  "
///     1,0,0,1,0,1,0,0,0,0,1,0,0,1,0,1,0,0,1,0,1,0,0,1,0, // "XX    XX  XX        XX    XX  XX    XX  XX    XX  "
///     0,1,1,0,0,1,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,1,1,1,0, // "  XXXX    XX          XXXX      XXXX      XXXXXX  "
///   ],
/// };
/// // XXX: Originally I was planning on decoding the image into a string,
/// //      but unless the whole font is embedded in our input and using
/// //      some kind of masking that we could figure out without too much
/// //      trouble and get the glyphs for all of the possible characters,
/// //      we are not able to decode the pictured string for all of the
/// //      different inputs that the AoC servers is able to give.
/// //      So, unfortunately we are left for now with just outputting
/// //      the flattened image and leaving the reading of the value shown
/// //      in the image to humans running the program.
/// assert_eq!(solve_part_2(&input_generator(INPUT)), flattened);
/// ```
pub fn solve_part_2 (input_image_data: &ImageData) -> FlattenedImage
{
  input_image_data.flatten()
}

pub struct FlattenedImage
{
  pub flattened: LayerData,
}

impl FlattenedImage
{
  fn new () -> Self
  {
    Self
    {
      flattened: [2; PIXELS_PER_LAYER],
    }
  }
}

impl std::fmt::Display for FlattenedImage
{
  fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
  {
    for y in 0..IMAGE_HEIGHT
    {
      for x in 0..IMAGE_WIDTH
      {
        match self.flattened[y * IMAGE_WIDTH + x]
        {
          0 => write!(f, "  ")?,
          1 => write!(f, "XX")?,
          2 => write!(f, "  ")?,
          _ => unreachable!(),
        }
      }
      writeln!(f)?;
    }

    Ok(())
  }
}

impl std::fmt::Debug for FlattenedImage
{
  fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
  {
    for y in 0..IMAGE_HEIGHT
    {
      for x in 0..IMAGE_WIDTH
      {
        write!(f, "{},", self.flattened[y * IMAGE_WIDTH + x])?;
      }
      writeln!(f)?;
    }

    Ok(())
  }
}

impl PartialEq for FlattenedImage
{
  fn eq (&self, other: &Self) -> bool
  {
    for (i, &pixel) in self.flattened.iter().enumerate()
    {
      if pixel != other.flattened[i]
        { return false; }
    }

    true
  }
}

trait Flatten
{
  fn flatten (&self) -> FlattenedImage;
}

impl Flatten for ImageData
{
  fn flatten (&self) -> FlattenedImage
  {
    let mut flattened = FlattenedImage::new();

    let mut pixels_remaining: Vec<_> = (0..PIXELS_PER_LAYER).collect();

    for layer in self
    {
      pixels_remaining.retain(|&i|
      {
        if layer[i] != 2
        {
          flattened.flattened[i] = layer[i];
          return false;
        }
        true
      });

      if pixels_remaining.len() == 0
        { break; }
    }

    flattened
  }
}