diff --git a/2016/Cargo.toml b/2016/Cargo.toml index 656d572..61e74da 100644 --- a/2016/Cargo.toml +++ b/2016/Cargo.toml @@ -5,3 +5,4 @@ authors = ["Bert Peters "] [dependencies] clap = "2.32" +regex = "1.0" diff --git a/2016/inputs/15.txt b/2016/inputs/15.txt new file mode 100644 index 0000000..5200150 --- /dev/null +++ b/2016/inputs/15.txt @@ -0,0 +1,6 @@ +Disc #1 has 13 positions; at time=0, it is at position 11. +Disc #2 has 5 positions; at time=0, it is at position 0. +Disc #3 has 17 positions; at time=0, it is at position 11. +Disc #4 has 3 positions; at time=0, it is at position 0. +Disc #5 has 7 positions; at time=0, it is at position 2. +Disc #6 has 19 positions; at time=0, it is at position 17. diff --git a/2016/src/common.rs b/2016/src/common.rs index ce2ea2f..bd023e5 100644 --- a/2016/src/common.rs +++ b/2016/src/common.rs @@ -29,6 +29,24 @@ pub fn prime_sieve(dest: &mut[bool]) { } } +/// Greatest common divisor +pub fn gcd(a: i32, b: i32) -> i32 { + if a < b { + gcd(b, a) + } else { + if a % b == 0 { + b + } else { + gcd(a % b, b) + } + } +} + +/// Least common multiple +pub fn lcm(a: i32, b: i32) -> i32 { + a * b / gcd(a, b) +} + /// Solution trait /// /// Every day's solution should implement this function so that it can @@ -60,4 +78,15 @@ mod tests { assert_eq!(output, input); } + + #[test] + fn test_gcd() { + assert_eq!(12, gcd(24, 36)); + assert_eq!(1, gcd(1, 7)); + } + + #[test] + fn test_lcm() { + assert_eq!(12, lcm(6, 4)); + } } diff --git a/2016/src/day15.rs b/2016/src/day15.rs new file mode 100644 index 0000000..8eb7643 --- /dev/null +++ b/2016/src/day15.rs @@ -0,0 +1,69 @@ +use std::io; +use std::io::prelude::*; +use regex; +use common; + +#[derive(Default)] +pub struct Day15 { + disks: Vec<(i32, i32)>, +} + +impl Day15 { + pub fn new() -> Day15 { + Default::default() + } + + fn read_disks(&mut self, input: &mut io::Read) { + // Note: this implementation assumes the input is sorted. + let re = regex::Regex::new(r"Disc #(\d+) has (\d+) positions; at time=0, it is at position (\d+).") + .unwrap(); + let reader = io::BufReader::new(input); + for line in reader.lines() { + let contents = line.unwrap(); + let groups = re.captures(&contents).unwrap(); + let disk_size: i32 = groups.get(2).unwrap().as_str().parse().unwrap(); + let start_pos: i32 = groups.get(3).unwrap().as_str().parse().unwrap(); + self.disks.push((disk_size, start_pos)); + } + } + + fn first_pass(&self) -> i32 { + let mut to_wait = 0; + let mut multiplier = 1; + for (i, (size, start)) in self.disks.iter().enumerate() { + while (i as i32 + start + to_wait + 1) % size != 0 { + to_wait += multiplier; + } + multiplier = common::lcm(*size, multiplier); + } + to_wait + } +} + +impl common::Solution for Day15 { + fn part1(&mut self, input: &mut io::Read) -> String { + self.read_disks(input); + format!("{}", self.first_pass()) + } + + fn part2(&mut self, input: &mut io::Read) -> String { + self.read_disks(input); + self.disks.push((11, 0)); + format!("{}", self.first_pass()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use common::Solution; + + const SAMPLE: &str = "Disc #1 has 5 positions; at time=0, it is at position 4.\n\ + Disc #2 has 2 positions; at time=0, it is at position 1."; + + #[test] + fn sample_part1() { + let mut instance = Day15::new(); + assert_eq!("5", instance.part1(&mut SAMPLE.as_bytes())); + } +} diff --git a/2016/src/main.rs b/2016/src/main.rs index d5c4fb3..22a3674 100644 --- a/2016/src/main.rs +++ b/2016/src/main.rs @@ -1,14 +1,17 @@ extern crate clap; +extern crate regex; use clap::{Arg, App}; use std::fs; use std::io; pub mod common; pub mod day1; +pub mod day15; fn get_impl(day: i32) -> Box { match day { 1 => { Box::new(day1::Day1::new()) } + 15 => { Box::new(day15::Day15::new()) } _ => { panic!("Unimplemented day {}", day) }