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