rust: Move modules to their own files
This commit is contained in:
parent
d6888248c3
commit
46dff81bad
12 changed files with 730 additions and 803 deletions
33
rust/src/day01.rs
Normal file
33
rust/src/day01.rs
Normal file
|
@ -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<usize> = 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
|
||||||
|
}
|
47
rust/src/day02.rs
Normal file
47
rust/src/day02.rs
Normal file
|
@ -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
|
||||||
|
}
|
42
rust/src/day03.rs
Normal file
42
rust/src/day03.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
fn count(rows: &[Vec<char>], 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<Vec<char>> = 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
|
||||||
|
}
|
75
rust/src/day04.rs
Normal file
75
rust/src/day04.rs
Normal file
|
@ -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
|
||||||
|
}
|
51
rust/src/day05.rs
Normal file
51
rust/src/day05.rs
Normal file
|
@ -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
|
||||||
|
}
|
95
rust/src/day08.rs
Normal file
95
rust/src/day08.rs
Normal file
|
@ -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<i32>) {
|
||||||
|
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<i32> {
|
||||||
|
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
|
||||||
|
}
|
58
rust/src/day09.rs
Normal file
58
rust/src/day09.rs
Normal file
|
@ -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<usize> = input.split('\n').flat_map(str::parse).collect();
|
||||||
|
let part1 = part1(&numbers);
|
||||||
|
let part2 = part2(&numbers, part1);
|
||||||
|
|
||||||
|
if print {
|
||||||
|
dbg!(part1, part2);
|
||||||
|
}
|
||||||
|
|
||||||
|
instant.elapsed()
|
||||||
|
}
|
37
rust/src/day15.rs
Normal file
37
rust/src/day15.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
const END: usize = 30_000_000;
|
||||||
|
pub fn run(print: bool) -> Duration {
|
||||||
|
let input: Vec<usize> = "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()
|
||||||
|
}
|
148
rust/src/day16.rs
Normal file
148
rust/src/day16.rs
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
use std::fmt;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
type Ticket = Vec<usize>;
|
||||||
|
|
||||||
|
#[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<Ticket> = 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::<Vec<_>>())
|
||||||
|
.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()
|
||||||
|
}
|
116
rust/src/day19.rs
Normal file
116
rust/src/day19.rs
Normal file
|
@ -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<Vec<usize>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rules = HashMap<usize, Rhs>;
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
826
rust/src/main.rs
826
rust/src/main.rs
|
@ -1,808 +1,27 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
mod day01 {
|
mod nop;
|
||||||
use std::{collections::HashMap, time::Duration, time::Instant};
|
|
||||||
|
mod day01;
|
||||||
pub fn run(print: bool) -> Duration {
|
mod day02;
|
||||||
let file_string = std::fs::read_to_string("inputs/day01").unwrap();
|
mod day03;
|
||||||
let instant = Instant::now();
|
mod day04;
|
||||||
|
mod day05;
|
||||||
let entries: Vec<usize> = file_string.lines().flat_map(|line| line.parse()).collect();
|
use nop as day06;
|
||||||
let mut sums = HashMap::new();
|
use nop as day07;
|
||||||
|
mod day08;
|
||||||
let mut part1 = None;
|
mod day09;
|
||||||
for i in &entries {
|
use nop as day10;
|
||||||
for j in &entries {
|
use nop as day11;
|
||||||
if i + j < 2020 {
|
use nop as day12;
|
||||||
sums.insert(i + j, i * j);
|
use nop as day13;
|
||||||
} else if i + j == 2020 {
|
use nop as day14;
|
||||||
part1 = Some(i * j);
|
mod day15;
|
||||||
}
|
mod day16;
|
||||||
}
|
use nop as day17;
|
||||||
}
|
use nop as day18;
|
||||||
let part1 = part1.unwrap();
|
mod day19;
|
||||||
|
use nop as day20;
|
||||||
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<char>], 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<Vec<char>> = 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<i32>) {
|
|
||||||
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<i32> {
|
|
||||||
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<usize> = 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<usize> = "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<usize>;
|
|
||||||
|
|
||||||
#[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<Ticket> = 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::<Vec<_>>())
|
|
||||||
.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<Vec<usize>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
type Rules = HashMap<usize, Rhs>;
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let days = [
|
let days = [
|
||||||
|
@ -825,6 +44,7 @@ fn main() {
|
||||||
day17::run,
|
day17::run,
|
||||||
day18::run,
|
day18::run,
|
||||||
day19::run,
|
day19::run,
|
||||||
|
day20::run,
|
||||||
];
|
];
|
||||||
|
|
||||||
if let Some(day) = std::env::args().nth(1) {
|
if let Some(day) = std::env::args().nth(1) {
|
||||||
|
|
5
rust/src/nop.rs
Normal file
5
rust/src/nop.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub fn run(_: bool) -> Duration {
|
||||||
|
Duration::new(0, 0)
|
||||||
|
}
|
Loading…
Reference in a new issue