From 9aff05d29667e3a8fcdf5dfdeccfd013e839b502 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Mon, 12 Nov 2018 13:13:03 +0100 Subject: [PATCH] Rewrite old code into 2018 style. Nice test for my 2018 repo set up. Which turns out needs some tweaking. --- 2016/.gitignore | 5 +- 2016/Cargo.toml | 7 ++ 2016/day-01/sample.txt | 1 - 2016/day-01/solution.rs | 78 ---------------- 2016/{day-01/input.txt => inputs/01.txt} | 0 2016/src/common.rs | 63 +++++++++++++ 2016/src/day1.rs | 112 +++++++++++++++++++++++ 2016/src/main.rs | 65 +++++++++++++ 8 files changed, 249 insertions(+), 82 deletions(-) create mode 100644 2016/Cargo.toml delete mode 100644 2016/day-01/sample.txt delete mode 100644 2016/day-01/solution.rs rename 2016/{day-01/input.txt => inputs/01.txt} (100%) create mode 100644 2016/src/common.rs create mode 100644 2016/src/day1.rs create mode 100644 2016/src/main.rs diff --git a/2016/.gitignore b/2016/.gitignore index 8e73ece..fa8d85a 100644 --- a/2016/.gitignore +++ b/2016/.gitignore @@ -1,3 +1,2 @@ -*/solution -*/target -*/Cargo.lock +Cargo.lock +target diff --git a/2016/Cargo.toml b/2016/Cargo.toml new file mode 100644 index 0000000..656d572 --- /dev/null +++ b/2016/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "aoc-2016" +version = "0.1.0" +authors = ["Bert Peters "] + +[dependencies] +clap = "2.32" diff --git a/2016/day-01/sample.txt b/2016/day-01/sample.txt deleted file mode 100644 index 1d6615f..0000000 --- a/2016/day-01/sample.txt +++ /dev/null @@ -1 +0,0 @@ -R8, R4, R4, R8 diff --git a/2016/day-01/solution.rs b/2016/day-01/solution.rs deleted file mode 100644 index 20cbeed..0000000 --- a/2016/day-01/solution.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::collections::HashSet; -use std::error::Error; -use std::env; -use std::fs::File; -use std::io::prelude::*; -use std::path::Path; -use std::str; - -fn dist(pos: (i32, i32)) -> i32 -{ - let (x, y) = pos; - - return x.abs() + y.abs() -} - -fn walk(pos: (i32, i32), dir: i32) -> (i32, i32) -{ - let backwards = if dir & 2 == 2 { -1 } else { 1 }; - let (x, y) = pos; - - (x + backwards * (1 - (dir & 1)), y + backwards * (dir & 1)) -} - -fn main() { - let args: Vec = env::args().collect(); - let path = Path::new(&args[1]); - let display = path.display(); - - // Open the path in read-only mode, returns `io::Result` - let mut file = match File::open(&path) { - // The `description` method of `io::Error` returns a string that - // describes the error - Err(why) => panic!("couldn't open {}: {}", display, - why.description()), - Ok(file) => file, - }; - - let mut content = String::new(); - match file.read_to_string(&mut content) { - Err(why) => panic!("couldn't open {}: {}", display, - why.description()), - Ok(_) => {}, - }; - - let mut pos = (0, 0); - let mut dir = 0; - let mut found = false; - - let mut positions = HashSet::new(); - positions.insert(pos); - - for instruction in content.split(", ") { - let turn = &instruction[..1]; - let steps_opt: Option = instruction[1..].trim().parse().ok(); - let steps = match steps_opt { - Some(num) => num, - None => panic!("Could note parse number of steps"), - }; - if turn == "R" { - dir += 1; - } else { - dir += 3; - } - dir %= 4; - - for _ in 0..steps { - pos = walk(pos, dir); - if !found && positions.contains(&pos) { - println!("Visited twice at dist {}", dist(pos)) ; - found = true; - } else { - positions.insert(pos); - } - } - } - - println!("Total distance is {}", dist(pos)); -} diff --git a/2016/day-01/input.txt b/2016/inputs/01.txt similarity index 100% rename from 2016/day-01/input.txt rename to 2016/inputs/01.txt diff --git a/2016/src/common.rs b/2016/src/common.rs new file mode 100644 index 0000000..ce2ea2f --- /dev/null +++ b/2016/src/common.rs @@ -0,0 +1,63 @@ +use std::io; + +/// Apply Erathostenes's sieve to the supplied array +/// +/// # Arguments +/// +/// * `dest` - the destination slice to fill with the sieve. This is +/// assumed to be filled with "true" before being handed to this +/// method. +pub fn prime_sieve(dest: &mut[bool]) { + if dest.len() >= 1 { + dest[0] = false; + } + + if dest.len() >= 2 { + dest[1] = false; + } + + let limit = (dest.len() as f64).sqrt() as usize; + + for i in 1..(limit + 1) { + if !dest[i] { + continue + } + + for j in ((i * i)..(dest.len())).step_by(i) { + dest[j] = false; + } + } +} + +/// Solution trait +/// +/// Every day's solution should implement this function so that it can +/// be easily run from the main program. +pub trait Solution { + /// Solve the first part of the day + fn part1(&mut self, input: &mut io::Read) -> String; + + /// Solve the second part of the day + fn part2(&mut self, input: &mut io::Read) -> String; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_prime_sieve() { + let mut input = [true; 10]; + prime_sieve(&mut input); + + let output = [ + false, false, + true, true, + false, true, + false, true, + false, false + ]; + + assert_eq!(output, input); + } +} diff --git a/2016/src/day1.rs b/2016/src/day1.rs new file mode 100644 index 0000000..03f8838 --- /dev/null +++ b/2016/src/day1.rs @@ -0,0 +1,112 @@ +use std::io; +use common; +use std::collections::HashSet; +use std::error::Error; + + +fn dist(pos: (i32, i32)) -> i32 { + let (x, y) = pos; + + return x.abs() + y.abs() +} + +pub struct Day1 { + pos: (i32, i32), + dir: i32, +} + +impl Day1 { + pub fn new() -> Day1 { + Day1{ + pos: (0, 0), + dir: (0), + } + } + + fn walk(&mut self) -> (i32, i32) { + let backwards = if self.dir & 2 == 2 { -1 } else { 1 }; + let (x, y) = self.pos; + + self.pos = (x + backwards * (1 - (self.dir & 1)), y + backwards * (self.dir & 1)); + self.pos + } + + fn turn(&mut self, action: &str) { + if action == "R" { + self.dir += 1; + } else { + self.dir += 3; + } + self.dir %= 4; + } +} + +impl common::Solution for Day1 { + fn part1(&mut self, input: &mut io::Read) -> String { + let mut content = String::new(); + match input.read_to_string(&mut content) { + Err(why) => panic!("couldn't open input: {}", why.description()), + Ok(_) => {}, + }; + + for instruction in content.split(", ") { + let turn = &instruction[..1]; + let steps: i32 = instruction[1..].trim().parse().expect("Invalid number of steps"); + self.turn(turn); + + for _ in 0..steps { + self.walk(); + } + } + + format!("{}", dist(self.pos)) + } + + fn part2(&mut self, input: &mut io::Read) -> String { + let mut content = String::new(); + match input.read_to_string(&mut content) { + Err(why) => panic!("couldn't open input: {}", why.description()), + Ok(_) => {}, + }; + + let mut positions = HashSet::new(); + positions.insert(self.pos); + + for instruction in content.split(", ") { + let turn = &instruction[..1]; + let steps: i32 = instruction[1..].trim().parse().expect("Invalid number of steps"); + self.turn(turn); + + for _ in 0..steps { + let pos = self.walk(); + if positions.contains(&pos) { + return format!("{}", dist(pos)) + } else { + positions.insert(pos); + } + } + } + + panic!("Never visited anything twice!") + } +} + +#[cfg(test)] +mod tests { + use super::Day1; + use common::Solution; + + #[test] + fn sample_part1() { + let mut instance = Day1::new(); + + assert_eq!("8", instance.part1(&mut "R8, R4, R4, R8".as_bytes())) + } + + #[test] + fn sample_part2() { + let mut instance = Day1::new(); + + assert_eq!("4", instance.part2(&mut "R8, R4, R4, R8".as_bytes())) + } +} diff --git a/2016/src/main.rs b/2016/src/main.rs new file mode 100644 index 0000000..d5c4fb3 --- /dev/null +++ b/2016/src/main.rs @@ -0,0 +1,65 @@ +extern crate clap; +use clap::{Arg, App}; +use std::fs; +use std::io; + +pub mod common; +pub mod day1; + +fn get_impl(day: i32) -> Box { + match day { + 1 => { Box::new(day1::Day1::new()) } + _ => { + panic!("Unimplemented day {}", day) + } + } +} + +fn main() { + let matches = App::new("Advent of Code") + .version("2016") + .author("Bert Peters ") + .arg(Arg::with_name("day") + .value_name("DAY") + .help("Number of the day to execute") + .required(true) + .takes_value(true)) + .arg(Arg::with_name("part2") + .short("2") + .help("Whether to run part 2") + .long("part2")) + .arg(Arg::with_name("input") + .short("i") + .long("input") + .help("Optional input file, stdin otherwise") + .takes_value(true)) + .get_matches(); + + let day: i32 = (&matches.value_of("day").unwrap()).parse() + .expect("Invalid int"); + let mut implementation = get_impl(day); + let mut data: Box = match matches.value_of("input") { + Some(filename) => { Box::new(fs::File::open(filename).unwrap()) } + None => { Box::new(io::stdin()) } + }; + + if matches.is_present("part2") { + println!("{}", implementation.part2(&mut data)); + } else { + println!("{}", implementation.part1(&mut data)); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_impl() { + // Verify that we can load all days + let last_implemented = 1; + for d in 1..(last_implemented + 1) { + get_impl(d); + } + } +}