This commit is contained in:
2019-12-17 17:06:09 -08:00
parent ac9012a2c5
commit 9070f6345b
5 changed files with 183 additions and 2 deletions

View File

@@ -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
View 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));
}

View File

@@ -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();
}
}
}