Day 8!
This commit is contained in:
30
src/args.rs
30
src/args.rs
@@ -1,4 +1,5 @@
|
||||
use clap::{App,Arg,SubCommand};
|
||||
use crate::image::Image;
|
||||
use crate::machine::Computer;
|
||||
use crate::orbits::UniversalOrbitMap;
|
||||
use crate::wiremap::{Wire};
|
||||
@@ -14,6 +15,7 @@ pub enum Command {
|
||||
Orbits(UniversalOrbitMap),
|
||||
PasswordCrack(u32, u32),
|
||||
Amplify(Computer),
|
||||
Image(Image),
|
||||
}
|
||||
|
||||
fn is_number(s: String) -> Result<(), String> {
|
||||
@@ -95,6 +97,26 @@ impl Command {
|
||||
.required(true)
|
||||
.validator(is_file))
|
||||
)
|
||||
.subcommand(SubCommand::with_name("image")
|
||||
.about("run the given image analysis task")
|
||||
.arg(Arg::with_name("WIDTH")
|
||||
.short("w")
|
||||
.long("width")
|
||||
.help("The width of the image.")
|
||||
.default_value("25")
|
||||
.validator(is_number))
|
||||
.arg(Arg::with_name("HEIGHT")
|
||||
.short("h")
|
||||
.long("height")
|
||||
.help("The height of the image.")
|
||||
.default_value("6")
|
||||
.validator(is_number))
|
||||
.arg(Arg::with_name("IMAGE")
|
||||
.index(1)
|
||||
.help("The image to use.")
|
||||
.required(true)
|
||||
.validator(is_file))
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
if let Some(problem1) = matches.subcommand_matches("fuel") {
|
||||
@@ -151,6 +173,14 @@ impl Command {
|
||||
let computer = Computer::load(problem6.value_of("COMPUTER").unwrap(), 0);
|
||||
return Command::Amplify(computer);
|
||||
}
|
||||
|
||||
if let Some(problem7) = matches.subcommand_matches("image") {
|
||||
let height = usize::from_str_radix(&problem7.value_of("HEIGHT").unwrap(), 10).unwrap();
|
||||
let width = usize::from_str_radix(&problem7.value_of("WIDTH").unwrap(), 10).unwrap();
|
||||
let file_contents = fs::read(problem7.value_of("IMAGE").unwrap()).unwrap();
|
||||
let image_data = str::from_utf8(&file_contents).unwrap();
|
||||
return Command::Image(Image::new(width, height, image_data).unwrap());
|
||||
}
|
||||
|
||||
panic!("Failed to run a reasonable command.");
|
||||
}
|
||||
|
||||
127
src/image.rs
Normal file
127
src/image.rs
Normal file
@@ -0,0 +1,127 @@
|
||||
use bytecount::count;
|
||||
|
||||
const WHITE: char = ' ';
|
||||
const BLACK: char = '\u{2588}';
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ImageParseError {
|
||||
NotEnoughData,
|
||||
IllegalCharacter(char),
|
||||
}
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Image {
|
||||
layers: Vec<Layer>
|
||||
}
|
||||
|
||||
impl Image {
|
||||
pub fn new(width: usize, height: usize, mut s: &str) -> Result<Image,ImageParseError> {
|
||||
let blocksize = height * width;
|
||||
let mut layers = vec![];
|
||||
|
||||
s = s.trim();
|
||||
loop {
|
||||
if s.len() == 0 {
|
||||
return Ok(Image{ layers });
|
||||
}
|
||||
|
||||
if s.len() < blocksize {
|
||||
println!("remaining: |{:?}|", s);
|
||||
return Err(ImageParseError::NotEnoughData);
|
||||
}
|
||||
|
||||
let (start, end) = s.split_at(blocksize);
|
||||
layers.push(Layer::new(width, height, start)?);
|
||||
s = end;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn digits_for_layer(&self, layer: usize, digit: u8) -> usize {
|
||||
if layer >= self.layers.len() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
self.layers[layer].count_digit(digit)
|
||||
}
|
||||
|
||||
pub fn digits_per_layer(&self, digit: u8) -> Vec<usize> {
|
||||
self.layers.iter().map(|l| l.count_digit(digit)).collect()
|
||||
}
|
||||
|
||||
fn get_pixel(&self, x: usize, y: usize) -> u8 {
|
||||
for layer in self.layers.iter() {
|
||||
match layer.data[(layer.width * y) + x] {
|
||||
2 => continue,
|
||||
v => return v,
|
||||
}
|
||||
}
|
||||
panic!("Ran to the end of layers on get_pixel({},{})", x, y);
|
||||
}
|
||||
|
||||
pub fn draw(&self) {
|
||||
assert!(self.layers.len() > 0);
|
||||
let width = self.layers[0].width;
|
||||
let height = self.layers[0].height;
|
||||
|
||||
for y in 0..height {
|
||||
for x in 0..width {
|
||||
let c = match self.get_pixel(x, y) {
|
||||
0 => BLACK,
|
||||
1 => WHITE,
|
||||
2 => panic!("Dropped to final transparent?!"),
|
||||
v => panic!("Unexpected pixel value {}", v),
|
||||
};
|
||||
|
||||
print!("{}", c);
|
||||
}
|
||||
println!("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
struct Layer {
|
||||
width: usize,
|
||||
height: usize,
|
||||
data: Vec<u8>
|
||||
}
|
||||
|
||||
impl Layer {
|
||||
pub fn new(width: usize, height: usize, s: &str) -> Result<Layer,ImageParseError> {
|
||||
let needed_bytes = height * width;
|
||||
let mut data = Vec::with_capacity(needed_bytes);
|
||||
|
||||
if s.len() < needed_bytes {
|
||||
return Err(ImageParseError::NotEnoughData);
|
||||
}
|
||||
|
||||
for c in s.chars().take(needed_bytes) {
|
||||
match c.to_digit(10) {
|
||||
None => return Err(ImageParseError::IllegalCharacter(c)),
|
||||
Some(x) => data.push(x as u8),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Layer{ height, width, data })
|
||||
}
|
||||
|
||||
pub fn count_digit(&self, digit: u8) -> usize {
|
||||
count(&self.data, digit)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn examples() {
|
||||
let example1 = "123456789012";
|
||||
let target1 = Image{ layers: vec![
|
||||
Layer{ height: 2, width: 3, data: vec![1,2,3,4,5,6] },
|
||||
Layer{ height: 2, width: 3, data: vec![7,8,9,0,1,2] },
|
||||
]};
|
||||
assert_eq!(target1, Image::new(3, 2, &example1).unwrap());
|
||||
assert_eq!(1, target1.layers[0].count_digit(1));
|
||||
assert_eq!(1, target1.layers[0].count_digit(4));
|
||||
assert_eq!(0, target1.layers[0].count_digit(9));
|
||||
assert_eq!(1, target1.digits_for_layer(0, 1));
|
||||
assert_eq!(vec![1,1], target1.digits_per_layer(1));
|
||||
assert_eq!(vec![0,1], target1.digits_per_layer(7));
|
||||
}
|
||||
26
src/main.rs
26
src/main.rs
@@ -1,6 +1,7 @@
|
||||
mod args;
|
||||
mod endchannel;
|
||||
mod fuel;
|
||||
mod image;
|
||||
mod machine;
|
||||
mod orbits;
|
||||
mod wiremap;
|
||||
@@ -16,13 +17,13 @@ fn main() {
|
||||
match Command::get() {
|
||||
Command::ComputeFuel(masses) => {
|
||||
let mut total = 0;
|
||||
|
||||
|
||||
for mass in masses {
|
||||
let fuel = calculate_fuel(mass);
|
||||
println!("Mass {}: {} fuel", mass, fuel);
|
||||
total += fuel;
|
||||
}
|
||||
|
||||
|
||||
println!("TOTAL FUEL: {}", total);
|
||||
}
|
||||
|
||||
@@ -134,5 +135,26 @@ fn main() {
|
||||
let (amount_b, settings_b) = computer.find_best_signal(5..10, |x| computer.amplifier(x));
|
||||
println!("Best signal with loopback is {} @ {:?}", amount_b, settings_b);
|
||||
}
|
||||
|
||||
Command::Image(image) => {
|
||||
let zero_byte_counts = image.digits_per_layer(0);
|
||||
let mut lowest_score = usize::max_value();
|
||||
let mut lowest_idx = 0;
|
||||
|
||||
for (idx, layer_count) in zero_byte_counts.iter().enumerate() {
|
||||
if layer_count < &lowest_score {
|
||||
lowest_score = *layer_count;
|
||||
lowest_idx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
println!("Fewest number of zeros at layer {} [{} zeros]", lowest_idx, lowest_score);
|
||||
let one_digits = image.digits_for_layer(lowest_idx, 1);
|
||||
println!("Layer {} has {} one digits.", lowest_idx, one_digits);
|
||||
let two_digits = image.digits_for_layer(lowest_idx, 2);
|
||||
println!("Layer {} has {} two digits.", lowest_idx, two_digits);
|
||||
println!("Multiplied together is {}", one_digits * two_digits);
|
||||
image.draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user