use std::fmt;
use std::io;
use std::io::prelude::*;
use std::process;
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[structopt(name = "Day 8: Space Image Format")]
/// Executes an Intcode program on 5 amplifiers, and finds the input that gives the max output
///
/// See https://adventofcode.com/2019/day/8 for details.
struct Opt {
/// Rather than rendering the image, calculate and print its checksum
#[structopt(short = "c", long = "checksum")]
checksum_mode: bool,
#[structopt(short = "w", long = "width")]
width: u32,
#[structopt(short = "h", long = "height")]
height: u32,
}
fn main() {
let opt = Opt::from_args();
let image: Image = {
let mut buffer = String::new();
exit_on_failed_assertion(
io::stdin().read_to_string(&mut buffer),
"Error reading input",
);
Image::from_str(&buffer.trim(), opt.width, opt.height)
};
if opt.checksum_mode {
println!("{}", image.checksum());
} else {
println!("{}", image);
}
}
fn exit_on_failed_assertion(data: Result, message: &str) -> A {
match data {
Ok(data) => data,
Err(e) => {
eprintln!("{}: {}", message, e);
process::exit(1);
}
}
}
#[derive(Debug)]
struct Image {
width: u32,
height: u32,
layers: Vec,
}
impl fmt::Display for Image {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.flatten()
.pixels
.chunks(self.width as usize)
.map(|line| {
line.iter()
.map(|c| write!(f, "{}", if *c == 0 { ' ' } else { '1' }))
.collect::()
.and_then(|_| writeln!(f))
})
.collect()
}
}
impl Image {
fn from_str(s: &str, width: u32, height: u32) -> Image {
Image {
width,
height,
layers: s
.as_bytes()
.chunks((width * height) as usize)
.map(|chunk| ImageLayer::new(chunk))
.collect(),
}
}
fn checksum(&self) -> usize {
self.layers
.iter()
.min_by_key(|layer| layer.pixel_count(0))
.map(|layer| layer.pixel_count(1) * layer.pixel_count(2))
.unwrap_or(0)
}
fn flatten(&self) -> ImageLayer {
self.layers
.iter()
.fold(ImageLayer::empty(self.width, self.height), |acc, next| {
ImageLayer {
pixels: acc
.pixels
.iter()
.zip(next.pixels.iter())
.map(|(a, b)| if *a == 2 { *b } else { *a })
.collect(),
}
})
}
}
#[derive(Debug)]
struct ImageLayer {
pixels: Vec,
}
impl ImageLayer {
fn new(char_pixels: &[u8]) -> ImageLayer {
ImageLayer {
pixels: char_pixels
.iter()
.map(|c| c.overflowing_sub(b'0').0)
.collect(),
}
}
fn empty(width: u32, height: u32) -> ImageLayer {
ImageLayer {
pixels: vec![2; (width * height) as usize],
}
}
fn pixel_count(&self, value: u8) -> usize {
self.pixels.iter().filter(|p| **p == value).count()
}
}