diff --git a/rust/src/day01.rs b/rust/src/day01.rs new file mode 100644 index 0000000..bd35b81 --- /dev/null +++ b/rust/src/day01.rs @@ -0,0 +1,33 @@ +use std::{collections::HashMap, time::Duration, time::Instant}; + +pub fn run(print: bool) -> Duration { + let file_string = std::fs::read_to_string("inputs/day01").unwrap(); + let instant = Instant::now(); + + let entries: Vec = file_string.lines().flat_map(|line| line.parse()).collect(); + let mut sums = HashMap::new(); + + let mut part1 = None; + for i in &entries { + for j in &entries { + if i + j < 2020 { + sums.insert(i + j, i * j); + } else if i + j == 2020 { + part1 = Some(i * j); + } + } + } + let part1 = part1.unwrap(); + + let part2 = entries + .iter() + .find_map(|i| sums.get(&(2020 - i)).map(|mul| i * mul)) + .unwrap(); + + let elapsed = instant.elapsed(); + if print { + dbg!(elapsed, part1, part2); + } + + elapsed +} diff --git a/rust/src/day02.rs b/rust/src/day02.rs new file mode 100644 index 0000000..4f3ca55 --- /dev/null +++ b/rust/src/day02.rs @@ -0,0 +1,47 @@ +use std::time::{Duration, Instant}; + +pub fn run(print: bool) -> Duration { + let file_string = std::fs::read_to_string("inputs/day02").unwrap(); + let instant = Instant::now(); + + let rules: Vec<(usize, usize, char, &str)> = file_string + .lines() + .flat_map(|rule| { + let mut splitc = rule.splitn(2, ' '); + let mut split_range = splitc.next()?.split('-'); + + let min: usize = split_range.next()?.parse().ok()?; + let max: usize = split_range.next()?.parse().ok()?; + + let rest = splitc.next()?; + let c = rest.chars().next()?; + let pw = &rest[3..]; + + Some((min, max, c, pw)) + }) + .collect(); + + let part1 = rules + .iter() + .filter(|(min, max, c, pw)| { + let c = &pw.chars().filter(|n| n == c).count(); + min <= c && c <= max + }) + .count(); + + let part2 = rules + .iter() + .filter(|(min, max, c, pw)| { + let mut chars = pw.chars(); + let a = chars.nth(min - 1) == Some(*c); + let b = chars.nth(max - min - 1) == Some(*c); + a ^ b + }) + .count(); + + let elapsed = instant.elapsed(); + if print { + dbg!(elapsed, part1, part2); + } + elapsed +} diff --git a/rust/src/day03.rs b/rust/src/day03.rs new file mode 100644 index 0000000..bd06199 --- /dev/null +++ b/rust/src/day03.rs @@ -0,0 +1,42 @@ +use std::time::{Duration, Instant}; + +fn count(rows: &[Vec], dx: usize, dy: usize) -> usize { + let mut x = 0; + let mut trees = 0; + for y in (0..rows.len()).step_by(dy) { + let row = &rows[y]; + + if row[x] == '#' { + trees += 1; + } + + x = (x + dx) % row.len(); + } + trees +} + +pub fn run(print: bool) -> Duration { + let file_string = std::fs::read_to_string("inputs/day3").unwrap(); + + let instant = Instant::now(); + + let rows: Vec> = file_string + .lines() + .map(|line| line.chars().collect()) + .collect(); + + let part1 = count(&rows, 3, 1); + + let part2 = part1 + * [(1, 1), (5, 1), (7, 1), (1, 2)] + .iter() + .map(|(dx, dy)| count(&rows, *dx, *dy)) + .fold(1, |acc, element| acc * element); + + let elapsed = instant.elapsed(); + if print { + dbg!(elapsed, part1, part2); + } + + elapsed +} diff --git a/rust/src/day04.rs b/rust/src/day04.rs new file mode 100644 index 0000000..fc24f88 --- /dev/null +++ b/rust/src/day04.rs @@ -0,0 +1,75 @@ +use std::time::{Duration, Instant}; + +fn between(field: &str, lower: i32, higher: i32) -> bool { + let n: i32 = field.parse().unwrap(); + n >= lower && n <= higher +} + +pub fn run(print: bool) -> Duration { + let input = std::fs::read_to_string("inputs/day04").unwrap(); + + let now = Instant::now(); + + let passports: Vec<_> = input + .split("\n\n") + .map(|p| p.replace('\n', " ").trim().to_string()) + .collect(); + + let valid_passports = passports.iter().filter(|p| { + p.contains("byr") + && p.contains("iyr") + && p.contains("eyr") + && p.contains("hgt") + && p.contains("hcl") + && p.contains("ecl") + && p.contains("pid") + }); + + let mut part1 = 0; + + let part2 = valid_passports + .filter(|p| { + part1 += 1; + + let mut valid = true; + for field in p.split(' ') { + let mut parts = field.split(':'); + let label = parts.next().unwrap(); + let value = parts.next().unwrap(); + + valid = valid + && match label { + "byr" => between(value, 1920, 2002), + "iyr" => between(value, 2010, 2020), + "eyr" => between(value, 2020, 2030), + "hgt" => { + let height = &value[..value.len() - 2]; + (value.ends_with("in") && between(height, 59, 76)) + || (value.ends_with("cm") && between(height, 150, 193)) + } + "hcl" => value.starts_with("#"), + "ecl" => { + value == "amb" + || value == "blu" + || value == "brn" + || value == "gry" + || value == "grn" + || value == "hzl" + || value == "oth" + } + "pid" => value.len() == 9, + "cid" => true, + _ => false, + } + } + valid + }) + .count(); + + let elapsed = now.elapsed(); + if print { + dbg!(elapsed, part1, part2); + } + + elapsed +} diff --git a/rust/src/day05.rs b/rust/src/day05.rs new file mode 100644 index 0000000..66dc7e2 --- /dev/null +++ b/rust/src/day05.rs @@ -0,0 +1,51 @@ +use std::time::{Duration, Instant}; + +pub fn run(print: bool) -> Duration { + let input = std::fs::read_to_string("inputs/day05").unwrap(); + let instant = Instant::now(); + + let passes: Vec<_> = input.lines().collect(); + let mut ids: Vec<_> = passes + .iter() + .map(|pass| { + let mut row_min = 0; + let mut row_max = 128; + let mut column_min = 0; + let mut column_max = 8; + + for c in pass.chars() { + match c { + 'F' => row_max = (row_min + row_max) / 2, + 'B' => row_min = (row_min + row_max) / 2, + 'L' => column_max = (column_min + column_max) / 2, + 'R' => column_min = (column_min + column_max) / 2, + _ => (), + }; + } + + row_min * 8 + column_min + }) + .collect(); + + ids.sort(); + + let part1 = ids.iter().max().unwrap(); + + let part2 = ids + .windows(2) + .find_map(|window| { + if window[0] + 2 == window[1] { + Some(window[0] + 1) + } else { + None + } + }) + .unwrap(); + + let elapsed = instant.elapsed(); + if print { + dbg!(elapsed, part1, part2); + } + + elapsed +} diff --git a/rust/src/day08.rs b/rust/src/day08.rs new file mode 100644 index 0000000..0cf4aa5 --- /dev/null +++ b/rust/src/day08.rs @@ -0,0 +1,95 @@ +use std::{ + collections::HashSet, + time::{Duration, Instant}, +}; + +#[derive(Debug, Copy, Clone)] +enum Instruction { + Acc(i32), + Jmp(i32), + Nop, +} + +fn part1(instructions: &[Instruction]) -> (i32, Vec) { + let mut accumulator = 0; + let mut pc = 0; + let mut pc_history = HashSet::new(); + let mut jumps = Vec::new(); + + loop { + match instructions[pc as usize] { + Instruction::Acc(i) => accumulator += i, + Instruction::Jmp(i) => { + let target = pc + i - 1; + if !pc_history.insert(target) { + break (accumulator, jumps); + } + + jumps.push(pc); + pc += i - 1 + } + Instruction::Nop => (), + }; + + pc += 1; + } +} + +fn part2(instructions: &[Instruction], skip: i32) -> Option { + let mut accumulator = 0; + let mut pc = 0; + let mut pc_history = HashSet::new(); + + loop { + if pc != skip { + match instructions.get(pc as usize) { + Some(Instruction::Acc(i)) => accumulator += i, + Some(Instruction::Jmp(i)) => { + let target = pc + i; + if !pc_history.insert(target) { + break None; + } + + pc = target - 1; + } + Some(Instruction::Nop) => (), + None => break Some(accumulator), + }; + } + + pc += 1; + } +} + +pub fn run(print: bool) -> Duration { + let input = std::fs::read_to_string("inputs/day08").unwrap(); + let instant = Instant::now(); + + let instructions: Vec<_> = input + .lines() + .map(|line| { + let operation = &line[0..3]; + let value: i32 = line[4..].parse().unwrap(); + match operation { + "acc" => Instruction::Acc(value), + "jmp" => Instruction::Jmp(value), + "nop" => Instruction::Nop, + _ => unreachable!(operation), + } + }) + .collect(); + + let (part1, jumps) = part1(&instructions); + + let part2 = jumps + .iter() + .rev() + .find_map(|skip_pc| part2(&instructions, *skip_pc)) + .unwrap(); + + let elapsed = instant.elapsed(); + if print { + dbg!(elapsed, part1, part2); + } + elapsed +} diff --git a/rust/src/day09.rs b/rust/src/day09.rs new file mode 100644 index 0000000..0cf1a60 --- /dev/null +++ b/rust/src/day09.rs @@ -0,0 +1,58 @@ +use std::time::{Duration, Instant}; + +fn part1(numbers: &[usize]) -> usize { + 'outer: for i in COUNT..numbers.len() { + for x in i - COUNT..i { + if numbers[x] > numbers[i] { + continue; + } + + for y in x + 1..i { + if numbers[x] + numbers[y] == numbers[i] { + continue 'outer; + } + } + } + + return numbers[i]; + } + + panic!(); +} + +fn part2(numbers: &[usize], part1: usize) -> usize { + let mut lower = 0; + let mut upper = 0; + let mut sum = 0; + + while sum != part1 { + while sum < part1 { + sum += numbers[upper]; + upper += 1; + } + + while sum > part1 { + sum -= numbers[lower]; + lower += 1; + } + } + + let slice = &numbers[lower..upper]; + slice.iter().min().unwrap() + slice.iter().max().unwrap() +} + +const COUNT: usize = 25; +pub fn run(print: bool) -> Duration { + let input = std::fs::read_to_string("../inputs/09").unwrap(); + let instant = Instant::now(); + + let numbers: Vec = input.split('\n').flat_map(str::parse).collect(); + let part1 = part1(&numbers); + let part2 = part2(&numbers, part1); + + if print { + dbg!(part1, part2); + } + + instant.elapsed() +} diff --git a/rust/src/day15.rs b/rust/src/day15.rs new file mode 100644 index 0000000..e8e307c --- /dev/null +++ b/rust/src/day15.rs @@ -0,0 +1,37 @@ +use std::time::{Duration, Instant}; + +const END: usize = 30_000_000; +pub fn run(print: bool) -> Duration { + let input: Vec = "0,1,5,10,3,12,19" + .split(",") + .map(|i| i.parse().unwrap()) + .collect(); + let instant = Instant::now(); + + let mut history = vec![0; END]; + let mut last = input[0]; + for turn in 0..input.len() { + history[last] = turn; + last = input[turn]; + } + + for turn in input.len()..2020 { + let stored = history[last]; + history[last] = turn; + last = if stored == 0 { 0 } else { turn - stored }; + } + if print { + println!("Part 1: {}", last); + } + + for turn in 2020..END { + let stored = history[last]; + history[last] = turn; + last = if stored == 0 { 0 } else { turn - stored }; + } + if print { + println!("Part 2: {}", last); + } + + instant.elapsed() +} diff --git a/rust/src/day16.rs b/rust/src/day16.rs new file mode 100644 index 0000000..76fc4f2 --- /dev/null +++ b/rust/src/day16.rs @@ -0,0 +1,148 @@ +use std::fmt; +use std::time::{Duration, Instant}; + +type Ticket = Vec; + +#[derive(PartialEq, Eq, Hash)] +struct Range { + from: usize, + to: usize, +} + +impl fmt::Debug for Range { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("{} - {}", self.from, self.to)) + } +} +impl Range { + fn from(s: &str) -> Self { + let mut range = s.split("-"); + Self { + from: range.next().unwrap().parse().unwrap(), + to: range.next().unwrap().parse().unwrap(), + } + } + + fn contains(&self, i: usize) -> bool { + i >= self.from && i <= self.to + } +} + +#[derive(Debug, PartialEq, Eq, Hash)] +struct Rule { + name: String, + ranges: (Range, Range), +} + +impl Rule { + fn validate(&self, i: usize) -> bool { + let (a, b) = &self.ranges; + a.contains(i) || b.contains(i) + } +} + +pub fn run(print: bool) -> Duration { + let input = std::fs::read_to_string("../inputs/16").unwrap(); + let instant = Instant::now(); + + let mut lines = input.lines(); + let mut rules = Vec::new(); + while let Some(line) = lines.next() { + let mut colon_split = line.split(": "); + let name = colon_split.next().unwrap().to_string(); + if name.is_empty() { + break; + } + + let mut ranges = colon_split.next().unwrap().split(" or "); + + rules.push(Rule { + name, + ranges: ( + Range::from(ranges.next().unwrap()), + Range::from(ranges.next().unwrap()), + ), + }); + } + + lines.next(); // "your ticket:" + + let your_ticket: Ticket = lines + .next() + .unwrap() + .split(",") + .map(|s| s.parse().unwrap()) + .collect(); + + lines.next(); // "" + lines.next(); // "nearby tickets:" + + let mut nearby_tickets: Vec = Vec::new(); + while let Some(line) = lines.next() { + nearby_tickets.push(line.split(",").map(|s| s.parse().unwrap()).collect()); + } + + let mut part1 = 0; + let mut valid_tickets = Vec::new(); + for ticket in &nearby_tickets { + let mut valid = true; + for &field in ticket { + let rule = rules.iter().find(|rule| rule.validate(field)); + if rule.is_none() { + valid = false; + part1 += field; + } + } + + if valid { + valid_tickets.push(ticket); + } + } + + if print { + dbg!(part1); + } + + let mut field_possibilities: Vec<_> = (0..your_ticket.len()) + .map(|_| (0..rules.len()).collect::>()) + .collect(); + + for ticket in valid_tickets { + for (&field, possibilities) in ticket.iter().zip(field_possibilities.iter_mut()) { + possibilities + .iter() + .position(|&rule_i| !rules[rule_i].validate(field)) + .map(|i| possibilities.remove(i)); + } + } + + let mut part2 = 1; + loop { + let field_possibility = field_possibilities + .iter() + .enumerate() + .find(|(_, fp)| fp.len() == 1); + + let (i, possibility) = match field_possibility { + Some((i, v)) => (i, v[0]), + None => break, + }; + + for fp in field_possibilities.iter_mut() { + fp.iter() + .position(|&i| i == possibility) + .map(|i| fp.remove(i)); + } + + let rule = &rules[possibility].name; + if rule.starts_with("de") { + part2 *= your_ticket[i]; + } + } + + if print { + dbg!(part2); + } + + instant.elapsed() +} diff --git a/rust/src/day19.rs b/rust/src/day19.rs new file mode 100644 index 0000000..d3f1700 --- /dev/null +++ b/rust/src/day19.rs @@ -0,0 +1,116 @@ +use std::collections::BTreeMap as HashMap; +use std::fs::File; +use std::io::{BufWriter, Write}; +use std::time::{Duration, Instant}; + +#[derive(Debug)] +enum Rhs { + Terminal(char), + Variables(Vec>), +} + +type Rules = HashMap; + +fn parse(input: &str) -> Option<(Rules, Vec<&str>)> { + let mut rules = HashMap::new(); + let mut messages = Vec::new(); + let mut parsing_rules = true; + for line in input.lines() { + let trim = line.trim(); + if trim.is_empty() { + parsing_rules = false; + continue; + } + + if parsing_rules { + let mut split = trim.split(": "); + let lhs: usize = split.next()?.parse().ok()?; + + let terms = split.next()?; + let rhs = if terms.starts_with('"') { + Rhs::Terminal(terms.chars().nth(1)?) + } else { + Rhs::Variables( + terms + .split(" | ") + .map(|t| t.split(" ").flat_map(str::parse).collect()) + .collect(), + ) + }; + + rules.insert(lhs, rhs); + } else { + messages.push(trim); + } + } + + Some((rules, messages)) +} + +fn generate(rules: &Rules, messages: &Vec<&str>, filename: &str) -> std::io::Result<()> { + let write_file = File::create(filename)?; + let mut w = BufWriter::new(&write_file); + + writeln!(&mut w, "from typing import Tuple")?; + writeln!(&mut w)?; + + for (i, rule) in rules.iter() { + writeln!(&mut w, "def rule_{}(s):", i)?; + writeln!(&mut w, " if s != '':")?; + + match rule { + Rhs::Terminal(terminal) => { + writeln!(&mut w, " if s[0] == '{}':", terminal)?; + writeln!(&mut w, " yield s[1:]")?; + } + + Rhs::Variables(variables) => { + for term in variables { + for (j, rule) in term.iter().enumerate() { + if j == 0 { + writeln!(&mut w, " v{} = rule_{}(s)", j, rule)?; + } else { + writeln!( + &mut w, + " v{} = (out for inp in v{} for out in rule_{}(inp))", + j, + j - 1, + rule, + )?; + } + } + writeln!(&mut w, " yield from v{}", term.len() - 1)?; + writeln!(&mut w)?; + } + } + } + + writeln!(&mut w)?; + } + writeln!(&mut w, "def validate(s):")?; + writeln!(&mut w, " return any(x == '' for x in rule_0(s))")?; + + writeln!(&mut w)?; + writeln!(&mut w, "if __name__ == '__main__':")?; + writeln!(&mut w, " result = 0")?; + for message in messages { + writeln!(&mut w, " result += validate('{}')", message)?; + } + writeln!(&mut w, " print('Result:', result)")?; + + Ok(()) +} + +pub fn run(_print: bool) -> Duration { + let input = std::fs::read_to_string("../inputs/19").unwrap(); + let instant = Instant::now(); + + let (mut rules, messages) = parse(&input).unwrap(); + generate(&rules, &messages, "/tmp/day19-1.py").unwrap(); + + rules.insert(8, Rhs::Variables(vec![vec![42], vec![42, 8]])); + rules.insert(11, Rhs::Variables(vec![vec![42, 31], vec![42, 11, 31]])); + generate(&rules, &messages, "/tmp/day19-2.py").unwrap(); + + instant.elapsed() +} diff --git a/rust/src/main.rs b/rust/src/main.rs index 2d6e978..ab8477e 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -1,808 +1,27 @@ use std::time::Duration; -mod day01 { - use std::{collections::HashMap, time::Duration, time::Instant}; - - pub fn run(print: bool) -> Duration { - let file_string = std::fs::read_to_string("inputs/day01").unwrap(); - let instant = Instant::now(); - - let entries: Vec = file_string.lines().flat_map(|line| line.parse()).collect(); - let mut sums = HashMap::new(); - - let mut part1 = None; - for i in &entries { - for j in &entries { - if i + j < 2020 { - sums.insert(i + j, i * j); - } else if i + j == 2020 { - part1 = Some(i * j); - } - } - } - let part1 = part1.unwrap(); - - let part2 = entries - .iter() - .find_map(|i| sums.get(&(2020 - i)).map(|mul| i * mul)) - .unwrap(); - - let elapsed = instant.elapsed(); - if print { - dbg!(elapsed, part1, part2); - } - - elapsed - } -} - -mod day02 { - use std::time::{Duration, Instant}; - - pub fn run(print: bool) -> Duration { - let file_string = std::fs::read_to_string("inputs/day02").unwrap(); - let instant = Instant::now(); - - let rules: Vec<(usize, usize, char, &str)> = file_string - .lines() - .flat_map(|rule| { - let mut splitc = rule.splitn(2, ' '); - let mut split_range = splitc.next()?.split('-'); - - let min: usize = split_range.next()?.parse().ok()?; - let max: usize = split_range.next()?.parse().ok()?; - - let rest = splitc.next()?; - let c = rest.chars().next()?; - let pw = &rest[3..]; - - Some((min, max, c, pw)) - }) - .collect(); - - let part1 = rules - .iter() - .filter(|(min, max, c, pw)| { - let c = &pw.chars().filter(|n| n == c).count(); - min <= c && c <= max - }) - .count(); - - let part2 = rules - .iter() - .filter(|(min, max, c, pw)| { - let mut chars = pw.chars(); - let a = chars.nth(min - 1) == Some(*c); - let b = chars.nth(max - min - 1) == Some(*c); - a ^ b - }) - .count(); - - let elapsed = instant.elapsed(); - if print { - dbg!(elapsed, part1, part2); - } - elapsed - } -} - -mod day03 { - use std::time::{Duration, Instant}; - - fn count(rows: &[Vec], dx: usize, dy: usize) -> usize { - let mut x = 0; - let mut trees = 0; - for y in (0..rows.len()).step_by(dy) { - let row = &rows[y]; - - if row[x] == '#' { - trees += 1; - } - - x = (x + dx) % row.len(); - } - trees - } - - pub fn run(print: bool) -> Duration { - let file_string = std::fs::read_to_string("inputs/day3").unwrap(); - - let instant = Instant::now(); - - let rows: Vec> = file_string - .lines() - .map(|line| line.chars().collect()) - .collect(); - - let part1 = count(&rows, 3, 1); - - let part2 = part1 - * [(1, 1), (5, 1), (7, 1), (1, 2)] - .iter() - .map(|(dx, dy)| count(&rows, *dx, *dy)) - .fold(1, |acc, element| acc * element); - - let elapsed = instant.elapsed(); - if print { - dbg!(elapsed, part1, part2); - } - - elapsed - } -} - -mod day04 { - use std::time::{Duration, Instant}; - - fn between(field: &str, lower: i32, higher: i32) -> bool { - let n: i32 = field.parse().unwrap(); - n >= lower && n <= higher - } - - pub fn run(print: bool) -> Duration { - let input = std::fs::read_to_string("inputs/day04").unwrap(); - - let now = Instant::now(); - - let passports: Vec<_> = input - .split("\n\n") - .map(|p| p.replace('\n', " ").trim().to_string()) - .collect(); - - let valid_passports = passports.iter().filter(|p| { - p.contains("byr") - && p.contains("iyr") - && p.contains("eyr") - && p.contains("hgt") - && p.contains("hcl") - && p.contains("ecl") - && p.contains("pid") - }); - - let mut part1 = 0; - - let part2 = valid_passports - .filter(|p| { - part1 += 1; - - let mut valid = true; - for field in p.split(' ') { - let mut parts = field.split(':'); - let label = parts.next().unwrap(); - let value = parts.next().unwrap(); - - valid = valid - && match label { - "byr" => between(value, 1920, 2002), - "iyr" => between(value, 2010, 2020), - "eyr" => between(value, 2020, 2030), - "hgt" => { - let height = &value[..value.len() - 2]; - (value.ends_with("in") && between(height, 59, 76)) - || (value.ends_with("cm") && between(height, 150, 193)) - } - "hcl" => value.starts_with("#"), - "ecl" => { - value == "amb" - || value == "blu" - || value == "brn" - || value == "gry" - || value == "grn" - || value == "hzl" - || value == "oth" - } - "pid" => value.len() == 9, - "cid" => true, - _ => false, - } - } - valid - }) - .count(); - - let elapsed = now.elapsed(); - if print { - dbg!(elapsed, part1, part2); - } - - elapsed - } -} - -mod day05 { - use std::time::{Duration, Instant}; - - pub fn run(print: bool) -> Duration { - let input = std::fs::read_to_string("inputs/day05").unwrap(); - let instant = Instant::now(); - - let passes: Vec<_> = input.lines().collect(); - let mut ids: Vec<_> = passes - .iter() - .map(|pass| { - let mut row_min = 0; - let mut row_max = 128; - let mut column_min = 0; - let mut column_max = 8; - - for c in pass.chars() { - match c { - 'F' => row_max = (row_min + row_max) / 2, - 'B' => row_min = (row_min + row_max) / 2, - 'L' => column_max = (column_min + column_max) / 2, - 'R' => column_min = (column_min + column_max) / 2, - _ => (), - }; - } - - row_min * 8 + column_min - }) - .collect(); - - ids.sort(); - - let part1 = ids.iter().max().unwrap(); - - let part2 = ids - .windows(2) - .find_map(|window| { - if window[0] + 2 == window[1] { - Some(window[0] + 1) - } else { - None - } - }) - .unwrap(); - - let elapsed = instant.elapsed(); - if print { - dbg!(elapsed, part1, part2); - } - - elapsed - } -} - -mod day06 { - use std::time::Duration; - - pub fn run(_: bool) -> Duration { - Duration::new(0, 0) - } -} - -mod day07 { - use std::time::Duration; - - pub fn run(_: bool) -> Duration { - Duration::new(0, 0) - } -} - -mod day08 { - use std::{ - collections::HashSet, - time::{Duration, Instant}, - }; - - #[derive(Debug, Copy, Clone)] - enum Instruction { - Acc(i32), - Jmp(i32), - Nop, - } - - fn part1(instructions: &[Instruction]) -> (i32, Vec) { - let mut accumulator = 0; - let mut pc = 0; - let mut pc_history = HashSet::new(); - let mut jumps = Vec::new(); - - loop { - match instructions[pc as usize] { - Instruction::Acc(i) => accumulator += i, - Instruction::Jmp(i) => { - let target = pc + i - 1; - if !pc_history.insert(target) { - break (accumulator, jumps); - } - - jumps.push(pc); - pc += i - 1 - } - Instruction::Nop => (), - }; - - pc += 1; - } - } - - fn part2(instructions: &[Instruction], skip: i32) -> Option { - let mut accumulator = 0; - let mut pc = 0; - let mut pc_history = HashSet::new(); - - loop { - if pc != skip { - match instructions.get(pc as usize) { - Some(Instruction::Acc(i)) => accumulator += i, - Some(Instruction::Jmp(i)) => { - let target = pc + i; - if !pc_history.insert(target) { - break None; - } - - pc = target - 1; - } - Some(Instruction::Nop) => (), - None => break Some(accumulator), - }; - } - - pc += 1; - } - } - - pub fn run(print: bool) -> Duration { - let input = std::fs::read_to_string("inputs/day08").unwrap(); - let instant = Instant::now(); - - let instructions: Vec<_> = input - .lines() - .map(|line| { - let operation = &line[0..3]; - let value: i32 = line[4..].parse().unwrap(); - match operation { - "acc" => Instruction::Acc(value), - "jmp" => Instruction::Jmp(value), - "nop" => Instruction::Nop, - _ => unreachable!(operation), - } - }) - .collect(); - - let (part1, jumps) = part1(&instructions); - - let part2 = jumps - .iter() - .rev() - .find_map(|skip_pc| part2(&instructions, *skip_pc)) - .unwrap(); - - let elapsed = instant.elapsed(); - if print { - dbg!(elapsed, part1, part2); - } - elapsed - } -} - -mod day09 { - use std::time::{Duration, Instant}; - - fn part1(numbers: &[usize]) -> usize { - 'outer: for i in COUNT..numbers.len() { - for x in i - COUNT..i { - if numbers[x] > numbers[i] { - continue; - } - - for y in x + 1..i { - if numbers[x] + numbers[y] == numbers[i] { - continue 'outer; - } - } - } - - return numbers[i]; - } - - panic!(); - } - - fn part2(numbers: &[usize], part1: usize) -> usize { - let mut lower = 0; - let mut upper = 0; - let mut sum = 0; - - while sum != part1 { - while sum < part1 { - sum += numbers[upper]; - upper += 1; - } - - while sum > part1 { - sum -= numbers[lower]; - lower += 1; - } - } - - let slice = &numbers[lower..upper]; - slice.iter().min().unwrap() + slice.iter().max().unwrap() - } - - const COUNT: usize = 25; - pub fn run(print: bool) -> Duration { - let input = std::fs::read_to_string("../inputs/09").unwrap(); - let instant = Instant::now(); - - let numbers: Vec = input.split('\n').flat_map(str::parse).collect(); - let part1 = part1(&numbers); - let part2 = part2(&numbers, part1); - - if print { - dbg!(part1, part2); - } - - instant.elapsed() - } -} - -mod day10 { - use std::time::Duration; - - pub fn run(_: bool) -> Duration { - Duration::new(0, 0) - } -} - -mod day11 { - use std::time::Duration; - - pub fn run(_: bool) -> Duration { - Duration::new(0, 0) - } -} - -mod day12 { - use std::time::Duration; - - pub fn run(_: bool) -> Duration { - Duration::new(0, 0) - } -} - -mod day13 { - use std::time::Duration; - - pub fn run(_: bool) -> Duration { - Duration::new(0, 0) - } -} - -mod day14 { - use std::time::Duration; - - pub fn run(_: bool) -> Duration { - Duration::new(0, 0) - } -} - -mod day15 { - use std::time::{Duration, Instant}; - - const END: usize = 30_000_000; - pub fn run(print: bool) -> Duration { - let input: Vec = "0,1,5,10,3,12,19" - .split(",") - .map(|i| i.parse().unwrap()) - .collect(); - let instant = Instant::now(); - - let mut history = vec![0; END]; - let mut last = input[0]; - for turn in 0..input.len() { - history[last] = turn; - last = input[turn]; - } - - for turn in input.len()..2020 { - let stored = history[last]; - history[last] = turn; - last = if stored == 0 { 0 } else { turn - stored }; - } - if print { - println!("Part 1: {}", last); - } - - for turn in 2020..END { - let stored = history[last]; - history[last] = turn; - last = if stored == 0 { 0 } else { turn - stored }; - } - if print { - println!("Part 2: {}", last); - } - - instant.elapsed() - } -} - -mod day16 { - use std::fmt; - use std::time::{Duration, Instant}; - - type Ticket = Vec; - - #[derive(PartialEq, Eq, Hash)] - struct Range { - from: usize, - to: usize, - } - - impl fmt::Debug for Range { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_fmt(format_args!("{} - {}", self.from, self.to)) - } - } - impl Range { - fn from(s: &str) -> Self { - let mut range = s.split("-"); - Self { - from: range.next().unwrap().parse().unwrap(), - to: range.next().unwrap().parse().unwrap(), - } - } - - fn contains(&self, i: usize) -> bool { - i >= self.from && i <= self.to - } - } - - #[derive(Debug, PartialEq, Eq, Hash)] - struct Rule { - name: String, - ranges: (Range, Range), - } - - impl Rule { - fn validate(&self, i: usize) -> bool { - let (a, b) = &self.ranges; - a.contains(i) || b.contains(i) - } - } - - pub fn run(print: bool) -> Duration { - let input = std::fs::read_to_string("../inputs/16").unwrap(); - let instant = Instant::now(); - - let mut lines = input.lines(); - let mut rules = Vec::new(); - while let Some(line) = lines.next() { - let mut colon_split = line.split(": "); - let name = colon_split.next().unwrap().to_string(); - if name.is_empty() { - break; - } - - let mut ranges = colon_split.next().unwrap().split(" or "); - - rules.push(Rule { - name, - ranges: ( - Range::from(ranges.next().unwrap()), - Range::from(ranges.next().unwrap()), - ), - }); - } - - lines.next(); // "your ticket:" - - let your_ticket: Ticket = lines - .next() - .unwrap() - .split(",") - .map(|s| s.parse().unwrap()) - .collect(); - - lines.next(); // "" - lines.next(); // "nearby tickets:" - - let mut nearby_tickets: Vec = Vec::new(); - while let Some(line) = lines.next() { - nearby_tickets.push(line.split(",").map(|s| s.parse().unwrap()).collect()); - } - - let mut part1 = 0; - let mut valid_tickets = Vec::new(); - for ticket in &nearby_tickets { - let mut valid = true; - for &field in ticket { - let rule = rules.iter().find(|rule| rule.validate(field)); - if rule.is_none() { - valid = false; - part1 += field; - } - } - - if valid { - valid_tickets.push(ticket); - } - } - - if print { - dbg!(part1); - } - - let mut field_possibilities: Vec<_> = (0..your_ticket.len()) - .map(|_| (0..rules.len()).collect::>()) - .collect(); - - for ticket in valid_tickets { - for (&field, possibilities) in ticket.iter().zip(field_possibilities.iter_mut()) { - possibilities - .iter() - .position(|&rule_i| !rules[rule_i].validate(field)) - .map(|i| possibilities.remove(i)); - } - } - - let mut part2 = 1; - loop { - let field_possibility = field_possibilities - .iter() - .enumerate() - .find(|(_, fp)| fp.len() == 1); - - let (i, possibility) = match field_possibility { - Some((i, v)) => (i, v[0]), - None => break, - }; - - for fp in field_possibilities.iter_mut() { - fp.iter() - .position(|&i| i == possibility) - .map(|i| fp.remove(i)); - } - - let rule = &rules[possibility].name; - if rule.starts_with("de") { - part2 *= your_ticket[i]; - } - } - - if print { - dbg!(part2); - } - - instant.elapsed() - } -} - -mod day17 { - use std::time::Duration; - - pub fn run(_print: bool) -> Duration { - Duration::new(0, 0) - } -} - -mod day18 { - use std::time::Duration; - - pub fn run(_print: bool) -> Duration { - Duration::new(0, 0) - } -} - -mod day19 { - use std::collections::BTreeMap as HashMap; - use std::fs::File; - use std::io::{BufWriter, Write}; - use std::time::{Duration, Instant}; - - #[derive(Debug)] - enum Rhs { - Terminal(char), - Variables(Vec>), - } - - type Rules = HashMap; - - fn parse(input: &str) -> Option<(Rules, Vec<&str>)> { - let mut rules = HashMap::new(); - let mut messages = Vec::new(); - let mut parsing_rules = true; - for line in input.lines() { - let trim = line.trim(); - if trim.is_empty() { - parsing_rules = false; - continue; - } - - if parsing_rules { - let mut split = trim.split(": "); - let lhs: usize = split.next()?.parse().ok()?; - - let terms = split.next()?; - let rhs = if terms.starts_with('"') { - Rhs::Terminal(terms.chars().nth(1)?) - } else { - Rhs::Variables( - terms - .split(" | ") - .map(|t| t.split(" ").flat_map(str::parse).collect()) - .collect(), - ) - }; - - rules.insert(lhs, rhs); - } else { - messages.push(trim); - } - } - - Some((rules, messages)) - } - - fn generate(rules: &Rules, messages: &Vec<&str>, filename: &str) -> std::io::Result<()> { - let write_file = File::create(filename)?; - let mut w = BufWriter::new(&write_file); - - writeln!(&mut w, "from typing import Tuple")?; - writeln!(&mut w)?; - - for (i, rule) in rules.iter() { - writeln!(&mut w, "def rule_{}(s):", i)?; - writeln!(&mut w, " if s != '':")?; - - match rule { - Rhs::Terminal(terminal) => { - writeln!(&mut w, " if s[0] == '{}':", terminal)?; - writeln!(&mut w, " yield s[1:]")?; - } - - Rhs::Variables(variables) => { - for term in variables { - for (j, rule) in term.iter().enumerate() { - if j == 0 { - writeln!(&mut w, " v{} = rule_{}(s)", j, rule)?; - } else { - writeln!( - &mut w, - " v{} = (out for inp in v{} for out in rule_{}(inp))", - j, - j - 1, - rule, - )?; - } - } - writeln!(&mut w, " yield from v{}", term.len() - 1)?; - writeln!(&mut w)?; - } - } - } - - writeln!(&mut w)?; - } - writeln!(&mut w, "def validate(s):")?; - writeln!(&mut w, " return any(x == '' for x in rule_0(s))")?; - - writeln!(&mut w)?; - writeln!(&mut w, "if __name__ == '__main__':")?; - writeln!(&mut w, " result = 0")?; - for message in messages { - writeln!(&mut w, " result += validate('{}')", message)?; - } - writeln!(&mut w, " print('Result:', result)")?; - - Ok(()) - } - - pub fn run(_print: bool) -> Duration { - let input = std::fs::read_to_string("../inputs/19").unwrap(); - let instant = Instant::now(); - - let (mut rules, messages) = parse(&input).unwrap(); - generate(&rules, &messages, "/tmp/day19-1.py").unwrap(); - - rules.insert(8, Rhs::Variables(vec![vec![42], vec![42, 8]])); - rules.insert(11, Rhs::Variables(vec![vec![42, 31], vec![42, 11, 31]])); - generate(&rules, &messages, "/tmp/day19-2.py").unwrap(); - - instant.elapsed() - } -} +mod nop; + +mod day01; +mod day02; +mod day03; +mod day04; +mod day05; +use nop as day06; +use nop as day07; +mod day08; +mod day09; +use nop as day10; +use nop as day11; +use nop as day12; +use nop as day13; +use nop as day14; +mod day15; +mod day16; +use nop as day17; +use nop as day18; +mod day19; +use nop as day20; fn main() { let days = [ @@ -825,6 +44,7 @@ fn main() { day17::run, day18::run, day19::run, + day20::run, ]; if let Some(day) = std::env::args().nth(1) { diff --git a/rust/src/nop.rs b/rust/src/nop.rs new file mode 100644 index 0000000..bd5391c --- /dev/null +++ b/rust/src/nop.rs @@ -0,0 +1,5 @@ +use std::time::Duration; + +pub fn run(_: bool) -> Duration { + Duration::new(0, 0) +}