add: some days

This commit is contained in:
Charles
2024-12-09 21:18:00 -08:00
parent 465a50488f
commit 05b61fffb9
8 changed files with 404 additions and 53 deletions
+63
View File
@@ -0,0 +1,63 @@
use std::collections::{HashMap, HashSet, VecDeque};
use advent_of_code_2024::{add_pair, make_main, mul_pair, next, nexti64, Pair, SResult};
make_main!();
fn solve(lines: Vec<String>) -> SResult<(usize, usize)> {
// Parse lines into a grid of ints
let width = lines.len();
let mut grid: Vec<Vec<u32>> = Vec::with_capacity(lines.len());
for line in lines {
grid.push(
line.trim()
.chars()
.map(|c| c.to_digit(10).unwrap())
.collect(),
);
}
let mut starts = HashSet::new();
for (i, row) in grid.iter().enumerate() {
for (j, v) in row.iter().enumerate() {
if *v == 0 {
starts.insert((i, j));
}
}
}
let mut total = 0;
for start in starts.into_iter() {
let mut visited = HashSet::new();
let mut stack = VecDeque::new();
stack.push_back(start);
while stack.len() > 0 {
let cur = stack.pop_front().unwrap();
visited.insert(cur);
if grid[cur.0][cur.1] == 9 {
total += 1;
continue;
}
for vel in [(1, 0), (-1, 0), (0, 1), (0, -1)] {
if let Some(n) = next(cur, vel, width) {
if grid[n.0][n.1] == grid[cur.0][cur.1] + 1 && !visited.contains(&n) {
stack.push_front(n);
}
}
}
}
}
Ok((total, 0))
}
#[cfg(test)]
mod tests {
use advent_of_code_2024::input;
use super::*;
#[test]
fn sample_input() {
let strings: Vec<String> = input!("d10p1.txt");
let got = solve(strings).unwrap();
assert_eq!(got, (36, 0));
}
}
+8
View File
@@ -0,0 +1,8 @@
89010123
78121874
87430965
96549874
45678903
32019012
01329801
10456732
+115 -48
View File
@@ -1,25 +1,73 @@
use std::{collections::HashSet, str::Chars}; use advent_of_code_2024::{make_main, SResult};
use advent_of_code_2024::{make_main, next, Pair, SResult};
make_main!(); make_main!();
fn solve(lines: Vec<String>) -> SResult<(usize, usize)> { fn solve(lines: Vec<String>) -> SResult<(i64, i64)> {
let mut total = 0; let mut total = 0;
let mut total2 = 0;
for line in lines { for line in lines {
let (_, (target, trip)) = parse_problem(&line).unwrap(); if let (target, true) = dumb(&line) {
for opt in trip.make_opts() {
if opt.eval() == target {
total += target; total += target;
} }
if let (target, true) = dumb2(&line) {
//println!("{line}");
total2 += target;
} }
} }
Ok((total as usize, 0)) Ok((total, total2))
}
fn dumb(s: &str) -> (i64, bool) {
let (value, parts) = s.split_once(":").unwrap();
let value = value.parse::<i64>().unwrap();
let parts: Vec<i64> = parts.split_ascii_whitespace().map(|s| s.parse::<i64>().unwrap()).collect();
let results = dumb_solve(parts[0], &parts[1..]);
(value, results.contains(&value))
}
fn dumb2(s: &str) -> (i64, bool) {
let (value, parts) = s.split_once(":").unwrap();
let value = value.parse::<i64>().unwrap();
let parts: Vec<i64> = parts.split_ascii_whitespace().map(|s| s.parse::<i64>().unwrap()).collect();
let results = dumb_solve2(parts[0], &parts[1..]);
(value, results.contains(&value))
}
fn dumb_solve(total: i64, parts: &[i64]) -> Vec<i64> {
if parts.len() == 1 {
return vec![total + parts[0], total * parts[0]];
}
let mut ret = vec!();
ret.append(&mut dumb_solve(total + parts[0], &parts[1..]));
ret.append(&mut dumb_solve(total * parts[0], &parts[1..]));
ret
}
fn dumb_solve2(total: i64, parts: &[i64]) -> Vec<i64> {
if parts.len() == 1 {
return vec![total + parts[0], total * parts[0], concat(total, parts[0])];
}
let mut ret = vec!();
ret.append(&mut dumb_solve2(total + parts[0], &parts[1..]));
ret.append(&mut dumb_solve2(total * parts[0], &parts[1..]));
ret.append(&mut dumb_solve2(concat(total, parts[0]), &parts[1..]));
ret
}
fn concat(mut a: i64, b: i64) -> i64 {
let mut bb = b;
while bb > 0 {
bb /= 10;
a *= 10;
}
a + b
} }
type PResult<'a, S> = Result<(&'a str, S), &'static str>; type PResult<'a, S> = Result<(&'a str, S), &'static str>;
#[derive(PartialEq, Clone)] #[derive(PartialEq, Clone, Debug)]
struct Triple { struct Triple {
left: Op, left: Op,
right: Op, right: Op,
@@ -31,11 +79,15 @@ impl Triple {
if self.oper != Oper::UNKNOWN { if self.oper != Oper::UNKNOWN {
return vec![self.clone()]; return vec![self.clone()];
} }
let mut ret = vec!(); let mut ret = vec![];
for oper in [Oper::ADD, Oper::MUL] {
for left in self.left.make_opts() { for left in self.left.make_opts() {
for right in self.right.make_opts() { for right in self.right.make_opts() {
ret.push(Triple{left: left.clone(), right, oper}); for oper in [Oper::ADD, Oper::MUL] {
ret.push(Triple {
left: left.clone(),
right: right.clone(),
oper,
});
} }
} }
} }
@@ -51,7 +103,8 @@ impl Triple {
} }
} }
#[derive(PartialEq, Clone)] #[derive(PartialEq, Clone, Debug)]
enum Op { enum Op {
Const(i64), Const(i64),
Triple(Box<Triple>), Triple(Box<Triple>),
@@ -62,7 +115,7 @@ impl Op {
match self { match self {
Op::Const(x) => vec![Op::Const(*x)], Op::Const(x) => vec![Op::Const(*x)],
Op::Triple(t) => { Op::Triple(t) => {
let mut ret = vec!(); let mut ret = vec![];
for triple in t.make_opts() { for triple in t.make_opts() {
ret.push(Op::Triple(Box::new(triple))); ret.push(Op::Triple(Box::new(triple)));
} }
@@ -79,7 +132,7 @@ impl Op {
} }
} }
#[derive(PartialEq, Clone, Copy)] #[derive(PartialEq, Clone, Copy, Debug)]
enum Oper { enum Oper {
UNKNOWN, UNKNOWN,
ADD, ADD,
@@ -93,47 +146,61 @@ fn parse_problem(s: &str) -> PResult<(i64, Triple)> {
Ok((s, (target.eval(), triple))) Ok((s, (target.eval(), triple)))
} }
fn take_one(s: &str) -> PResult<char> {
let c = s.chars().next().ok_or("missing char")?;
Ok((s, c))
}
fn take_ws(s: &str) -> PResult<()> {
let mut chars = s,chars();
let mut i = 0;
Ok((s.split_at(i), ()))
}
fn parse_triple(s: &str) -> PResult<Triple> { fn parse_triple(s: &str) -> PResult<Triple> {
let (s, _) = take_ws(s)?; let (s, _) = take_ws(s)?;
let (s, left) = take_int(s)?; let (mut s, mut left) = take_int(s)?;
let (s, _) = take_ws(s)?; while s.len() > 0 {
let oper = Oper::UNKNOWN; let (_s, _) = take_ws(s)?;
let (s, _) = take_ws(s)?; let (_s, right) = take_int(_s)?;
let (s, right) = match parse_triple(s) { left = Op::Triple(Box::new(Triple {
Ok((s, triple)) => (s, Op::Triple(Box::new(triple))), left,
Err(_) => take_int(s)?, right,
}; oper: Oper::UNKNOWN,
let (s, _) = take_ws(s)?; }));
s = _s;
}
Ok((s, Triple{left, right, oper})) if let Op::Triple(x) = left {
Ok((s, *x))
} else {
Err("not even one triple")
}
}
fn take_one(s: &str) -> PResult<char> {
let mut chars = s.chars();
let c = chars.next().ok_or("missing char")?;
Ok((chars.as_str(), c))
}
fn take_while<'a, F: Fn(char) -> bool>(mut s: &'a str, f: F) -> PResult<&'a str> {
let og = s;
let mut mid = 0;
let mut chars = s.chars();
while let Some(c) = chars.next() {
if !f(c) {
break;
}
mid += 1;
}
let (first, rest) = og.split_at(mid);
Ok((rest, first))
}
fn take_ws<'a>(s: &'a str) -> PResult<&'a str> {
take_while(s, |c| c.is_whitespace())
} }
fn take_int(s: &str) -> PResult<Op> { fn take_int(s: &str) -> PResult<Op> {
let (rest, digit) = take_while(s, |c| c.is_digit(10))?;
if digit.len() == 0 {
return Err("no digit");
}
let mut total = 0; let mut total = 0;
let mut i = 0; for c in digit.chars() {
let mut chars = s.chars();
while let Some(c) = chars.by_ref().peekable().next_if(|c| c.is_digit(10)) {
total = total * 10 + c.to_digit(10).unwrap(); total = total * 10 + c.to_digit(10).unwrap();
i += 1;
}
if i == 0 {
panic!("here");
Err("no digits")
} else {
Ok((chars.as_str(), Op::Const(total as i64)))
} }
Ok((rest, Op::Const(total as i64)))
} }
#[cfg(test)] #[cfg(test)]
@@ -145,6 +212,6 @@ mod tests {
fn sample_input() { fn sample_input() {
let strings: Vec<String> = input!("d7p1.txt"); let strings: Vec<String> = input!("d7p1.txt");
let got = solve(strings).unwrap(); let got = solve(strings).unwrap();
assert_eq!(got, (3749, 0)); assert_eq!(got, (3749, 11387));
} }
} }
+76
View File
@@ -0,0 +1,76 @@
use std::collections::{HashMap, HashSet};
use advent_of_code_2024::{add_pair, make_main, mul_pair, next, nexti64, Pair, SResult};
make_main!();
fn solve(lines: Vec<String>) -> SResult<(usize, usize)> {
// Convert to a grid of chars for easy searching
// Also record locations of ants and
let mut ants: HashMap<char, Vec<Pair<i64>>> = HashMap::default();
let mut grid = vec![];
let limit = lines.len() as i64;
for (i, line) in lines.into_iter().enumerate() {
let line: Vec<char> = line.chars().collect();
for (j, c) in line.iter().enumerate() {
if *c == '.' {
continue;
}
ants.entry(*c)
.or_insert_with(|| Vec::new())
.push((i as i64, j as i64));
}
grid.push(line);
}
let mut anodes: HashSet<Pair<i64>> = HashSet::new();
let mut anodes2: HashSet<Pair<i64>> = HashSet::new();
for (_, pairs) in ants {
for (i, p0) in pairs.iter().enumerate() {
for p1 in &pairs[(i+1)..] {
let (p0, p1) = (*p0, *p1);
let vel: (i64, i64) = add_pair(p0, mul_pair(p1, -1));
assert_ne!(vel, (0, 0), "{:?} vs {:?}", p0, p1);
for p in [p0, p1] {
for pp in [add_pair(p, vel), add_pair(p, mul_pair(vel, -1))] {
if pp == p0 || pp == p1 {
continue;
}
if pp.0 < 0 || pp.0 >= limit || pp.1 < 0 || pp.1 >= limit {
continue;
}
anodes.insert(pp);
}
let mut cur_pos = p;
while let Some(pp) = nexti64(cur_pos, vel, limit as usize) {
cur_pos = pp;
anodes2.insert(pp);
}
let mut cur_pos = p;
let vel = mul_pair(vel, -1);
while let Some(pp) = nexti64(cur_pos, vel, limit as usize) {
cur_pos = pp;
anodes2.insert(pp);
}
}
}
}
}
Ok((anodes.len(), anodes2.len()))
}
#[cfg(test)]
mod tests {
use advent_of_code_2024::input;
use super::*;
#[test]
fn sample_input() {
let strings: Vec<String> = input!("d8p1.txt");
let got = solve(strings).unwrap();
assert_eq!(got, (14, 34));
}
}
+12
View File
@@ -0,0 +1,12 @@
............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............
+104
View File
@@ -0,0 +1,104 @@
use std::collections::{HashMap, HashSet};
use advent_of_code_2024::{add_pair, make_main, mul_pair, next, nexti64, Pair, SResult};
make_main!();
fn solve(lines: Vec<String>) -> SResult<(usize, usize)> {
let line = &lines[0];
let mut data: Vec<i16> = vec!();
let mut counts: HashMap<i16, usize> = HashMap::default();
let mut file_index = 0;
for (i, c) in line.chars().enumerate() {
if i % 2 == 0 {
let count = c.to_digit(10).unwrap() as usize;
data.append(&mut vec![file_index; count]);
counts.insert(file_index, count);
file_index += 1;
} else {
data.append(&mut vec![-1; c.to_digit(10).unwrap() as usize]);
}
}
let mut data2 = data.clone();
let mut starti = 0;
let mut endi = data.len()-1;
while endi > starti {
if data[endi] == -1 {
endi -= 1;
} else {
let (data, file) = data.split_at_mut(endi);
let count = move_file(&mut data[starti..], &mut file[..1]);
starti += count;
endi -= 1;
}
}
let mut processed: HashSet<i16> = HashSet::new();
processed.insert(-1);
let mut endi = data.len();
while endi > 1 {
// Go back until we find a start point
while endi > 1 && processed.contains(&data2[endi-1]) {
endi -= 1;
}
if endi == 0 {
break;
}
// Now go back until we are no longer in the same block
let mut fstart = endi-1;
let bid = data2[fstart];
while fstart > 0 && data2[fstart-1] == bid {
fstart -= 1;
}
processed.insert(data2[fstart]);
let (data, file) = data2.split_at_mut(fstart);
move_file(&mut data[..], &mut file[..endi-fstart]);
endi = fstart;
}
Ok((checksum(&data), checksum(&data2)))
}
fn checksum(data: &[i16]) -> usize {
let mut checksum = 0;
for (i, id) in data.iter().enumerate() {
if *id == -1 {
continue;
}
checksum += (*id as usize) * i;
}
checksum
}
fn move_file(data: &mut [i16], file: &mut [i16]) -> usize {
let empty = vec![-1; file.len()];
let mut i = 0;
while i+file.len() <= data.len() {
if &data[i..i+file.len()] == empty {
for j in 0..file.len() {
(data[i+j], file[j]) = (file[j], data[i+j]);
}
i += file.len();
break;
}
i += 1;
}
i
}
#[cfg(test)]
mod tests {
use advent_of_code_2024::input;
use super::*;
#[test]
fn sample_input() {
let strings: Vec<String> = input!("d9p1.txt");
let got = solve(strings).unwrap();
assert_eq!(got, (1928, 2858));
}
}
+1
View File
@@ -0,0 +1 @@
2333133121414131402
+20
View File
@@ -22,6 +22,16 @@ pub type SResult<T> = Result<T, Box<dyn std::error::Error>>;
pub type Pair<T = usize> = (T, T); pub type Pair<T = usize> = (T, T);
pub fn mul_pair<T>(p: Pair<T>, val: T) -> Pair<T>
where T: Copy + std::ops::Mul<Output = T> {
(p.0*val, p.1*val)
}
pub fn add_pair<T>(p: Pair<T>, val: Pair<T>) -> Pair<T>
where T: Copy + std::ops::Add<Output = T> {
(p.0+val.0, p.1+val.1)
}
pub fn next((x, y): Pair, (xvel, yvel): Pair<i64>, width: usize) -> Option<Pair> { pub fn next((x, y): Pair, (xvel, yvel): Pair<i64>, width: usize) -> Option<Pair> {
let x = x as i64 + xvel; let x = x as i64 + xvel;
let y = y as i64 + yvel; let y = y as i64 + yvel;
@@ -31,3 +41,13 @@ pub fn next((x, y): Pair, (xvel, yvel): Pair<i64>, width: usize) -> Option<Pair>
Some((x as usize, y as usize)) Some((x as usize, y as usize))
} }
} }
pub fn nexti64((x, y): Pair<i64>, (xvel, yvel): Pair<i64>, width: usize) -> Option<Pair<i64>> {
let x = x + xvel;
let y = y + yvel;
if x < 0 || y < 0 || x >= width as i64 || y >= width as i64 {
None
} else {
Some((x, y))
}
}