Day 8!
This commit is contained in:
@@ -7,4 +7,5 @@ edition = "2018"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bytecount = "^0.6.0"
|
||||||
clap = "^2.33.0"
|
clap = "^2.33.0"
|
||||||
|
|||||||
1
inputs/day8
Normal file
1
inputs/day8
Normal file
File diff suppressed because one or more lines are too long
30
src/args.rs
30
src/args.rs
@@ -1,4 +1,5 @@
|
|||||||
use clap::{App,Arg,SubCommand};
|
use clap::{App,Arg,SubCommand};
|
||||||
|
use crate::image::Image;
|
||||||
use crate::machine::Computer;
|
use crate::machine::Computer;
|
||||||
use crate::orbits::UniversalOrbitMap;
|
use crate::orbits::UniversalOrbitMap;
|
||||||
use crate::wiremap::{Wire};
|
use crate::wiremap::{Wire};
|
||||||
@@ -14,6 +15,7 @@ pub enum Command {
|
|||||||
Orbits(UniversalOrbitMap),
|
Orbits(UniversalOrbitMap),
|
||||||
PasswordCrack(u32, u32),
|
PasswordCrack(u32, u32),
|
||||||
Amplify(Computer),
|
Amplify(Computer),
|
||||||
|
Image(Image),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_number(s: String) -> Result<(), String> {
|
fn is_number(s: String) -> Result<(), String> {
|
||||||
@@ -95,6 +97,26 @@ impl Command {
|
|||||||
.required(true)
|
.required(true)
|
||||||
.validator(is_file))
|
.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();
|
.get_matches();
|
||||||
|
|
||||||
if let Some(problem1) = matches.subcommand_matches("fuel") {
|
if let Some(problem1) = matches.subcommand_matches("fuel") {
|
||||||
@@ -152,6 +174,14 @@ impl Command {
|
|||||||
return Command::Amplify(computer);
|
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.");
|
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));
|
||||||
|
}
|
||||||
22
src/main.rs
22
src/main.rs
@@ -1,6 +1,7 @@
|
|||||||
mod args;
|
mod args;
|
||||||
mod endchannel;
|
mod endchannel;
|
||||||
mod fuel;
|
mod fuel;
|
||||||
|
mod image;
|
||||||
mod machine;
|
mod machine;
|
||||||
mod orbits;
|
mod orbits;
|
||||||
mod wiremap;
|
mod wiremap;
|
||||||
@@ -134,5 +135,26 @@ fn main() {
|
|||||||
let (amount_b, settings_b) = computer.find_best_signal(5..10, |x| computer.amplifier(x));
|
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);
|
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