From 05b61fffb9305a04020d0414bb52aa8cd9efca3b Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 9 Dec 2024 21:18:00 -0800 Subject: [PATCH] add: some days --- src/bin/d10p1.rs | 63 +++++++++++++++++ src/bin/d10p1.txt | 8 +++ src/bin/d7p1.rs | 173 ++++++++++++++++++++++++++++++++-------------- src/bin/d8p1.rs | 76 ++++++++++++++++++++ src/bin/d8p1.txt | 12 ++++ src/bin/d9p1.rs | 104 ++++++++++++++++++++++++++++ src/bin/d9p1.txt | 1 + src/lib.rs | 20 ++++++ 8 files changed, 404 insertions(+), 53 deletions(-) create mode 100644 src/bin/d10p1.rs create mode 100644 src/bin/d10p1.txt create mode 100644 src/bin/d8p1.rs create mode 100644 src/bin/d8p1.txt create mode 100644 src/bin/d9p1.rs create mode 100644 src/bin/d9p1.txt diff --git a/src/bin/d10p1.rs b/src/bin/d10p1.rs new file mode 100644 index 0000000..2b72d5c --- /dev/null +++ b/src/bin/d10p1.rs @@ -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) -> SResult<(usize, usize)> { + // Parse lines into a grid of ints + let width = lines.len(); + let mut grid: Vec> = 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 = input!("d10p1.txt"); + let got = solve(strings).unwrap(); + assert_eq!(got, (36, 0)); + } +} diff --git a/src/bin/d10p1.txt b/src/bin/d10p1.txt new file mode 100644 index 0000000..7bb1248 --- /dev/null +++ b/src/bin/d10p1.txt @@ -0,0 +1,8 @@ +89010123 +78121874 +87430965 +96549874 +45678903 +32019012 +01329801 +10456732 \ No newline at end of file diff --git a/src/bin/d7p1.rs b/src/bin/d7p1.rs index b322685..18436ab 100644 --- a/src/bin/d7p1.rs +++ b/src/bin/d7p1.rs @@ -1,25 +1,73 @@ -use std::{collections::HashSet, str::Chars}; - -use advent_of_code_2024::{make_main, next, Pair, SResult}; +use advent_of_code_2024::{make_main, SResult}; make_main!(); -fn solve(lines: Vec) -> SResult<(usize, usize)> { +fn solve(lines: Vec) -> SResult<(i64, i64)> { let mut total = 0; + let mut total2 = 0; for line in lines { - let (_, (target, trip)) = parse_problem(&line).unwrap(); - for opt in trip.make_opts() { - if opt.eval() == target { - total += target; - } + if let (target, true) = dumb(&line) { + 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::().unwrap(); + let parts: Vec = parts.split_ascii_whitespace().map(|s| s.parse::().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::().unwrap(); + let parts: Vec = parts.split_ascii_whitespace().map(|s| s.parse::().unwrap()).collect(); + let results = dumb_solve2(parts[0], &parts[1..]); + + (value, results.contains(&value)) +} + +fn dumb_solve(total: i64, parts: &[i64]) -> Vec { + 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 { + 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>; -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Debug)] struct Triple { left: Op, right: Op, @@ -31,11 +79,15 @@ impl Triple { if self.oper != Oper::UNKNOWN { return vec![self.clone()]; } - let mut ret = vec!(); - for oper in [Oper::ADD, Oper::MUL] { - for left in self.left.make_opts() { - for right in self.right.make_opts() { - ret.push(Triple{left: left.clone(), right, oper}); + let mut ret = vec![]; + for left in self.left.make_opts() { + for right in self.right.make_opts() { + 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 { Const(i64), Triple(Box), @@ -62,7 +115,7 @@ impl Op { match self { Op::Const(x) => vec![Op::Const(*x)], Op::Triple(t) => { - let mut ret = vec!(); + let mut ret = vec![]; for triple in t.make_opts() { ret.push(Op::Triple(Box::new(triple))); } @@ -70,7 +123,7 @@ impl Op { } } } - + fn eval(&self) -> i64 { match self { Op::Const(x) => *x, @@ -79,7 +132,7 @@ impl Op { } } -#[derive(PartialEq, Clone, Copy)] +#[derive(PartialEq, Clone, Copy, Debug)] enum Oper { UNKNOWN, ADD, @@ -93,47 +146,61 @@ fn parse_problem(s: &str) -> PResult<(i64, Triple)> { Ok((s, (target.eval(), triple))) } -fn take_one(s: &str) -> PResult { - 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 { let (s, _) = take_ws(s)?; - let (s, left) = take_int(s)?; - let (s, _) = take_ws(s)?; - let oper = Oper::UNKNOWN; - let (s, _) = take_ws(s)?; - let (s, right) = match parse_triple(s) { - Ok((s, triple)) => (s, Op::Triple(Box::new(triple))), - Err(_) => take_int(s)?, - }; - let (s, _) = take_ws(s)?; + let (mut s, mut left) = take_int(s)?; + while s.len() > 0 { + let (_s, _) = take_ws(s)?; + let (_s, right) = take_int(_s)?; + left = Op::Triple(Box::new(Triple { + left, + right, + oper: Oper::UNKNOWN, + })); + 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 { + 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 { + 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 i = 0; - let mut chars = s.chars(); - while let Some(c) = chars.by_ref().peekable().next_if(|c| c.is_digit(10)) { + for c in digit.chars() { 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)] @@ -145,6 +212,6 @@ mod tests { fn sample_input() { let strings: Vec = input!("d7p1.txt"); let got = solve(strings).unwrap(); - assert_eq!(got, (3749, 0)); + assert_eq!(got, (3749, 11387)); } } diff --git a/src/bin/d8p1.rs b/src/bin/d8p1.rs new file mode 100644 index 0000000..c802985 --- /dev/null +++ b/src/bin/d8p1.rs @@ -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) -> SResult<(usize, usize)> { + // Convert to a grid of chars for easy searching + // Also record locations of ants and + let mut ants: HashMap>> = HashMap::default(); + let mut grid = vec![]; + let limit = lines.len() as i64; + for (i, line) in lines.into_iter().enumerate() { + let line: Vec = 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> = HashSet::new(); + let mut anodes2: HashSet> = 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 = input!("d8p1.txt"); + let got = solve(strings).unwrap(); + assert_eq!(got, (14, 34)); + } +} diff --git a/src/bin/d8p1.txt b/src/bin/d8p1.txt new file mode 100644 index 0000000..de0f909 --- /dev/null +++ b/src/bin/d8p1.txt @@ -0,0 +1,12 @@ +............ +........0... +.....0...... +.......0.... +....0....... +......A..... +............ +............ +........A... +.........A.. +............ +............ \ No newline at end of file diff --git a/src/bin/d9p1.rs b/src/bin/d9p1.rs new file mode 100644 index 0000000..cccf416 --- /dev/null +++ b/src/bin/d9p1.rs @@ -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) -> SResult<(usize, usize)> { + let line = &lines[0]; + + let mut data: Vec = vec!(); + let mut counts: HashMap = 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 = 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 = input!("d9p1.txt"); + let got = solve(strings).unwrap(); + assert_eq!(got, (1928, 2858)); + } +} diff --git a/src/bin/d9p1.txt b/src/bin/d9p1.txt new file mode 100644 index 0000000..5ff5aae --- /dev/null +++ b/src/bin/d9p1.txt @@ -0,0 +1 @@ +2333133121414131402 \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 5d8d9c6..7f4e45f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,16 @@ pub type SResult = Result>; pub type Pair = (T, T); +pub fn mul_pair(p: Pair, val: T) -> Pair +where T: Copy + std::ops::Mul { + (p.0*val, p.1*val) +} + +pub fn add_pair(p: Pair, val: Pair) -> Pair +where T: Copy + std::ops::Add { + (p.0+val.0, p.1+val.1) +} + pub fn next((x, y): Pair, (xvel, yvel): Pair, width: usize) -> Option { let x = x as i64 + xvel; let y = y as i64 + yvel; @@ -30,4 +40,14 @@ pub fn next((x, y): Pair, (xvel, yvel): Pair, width: usize) -> Option } else { Some((x as usize, y as usize)) } +} + +pub fn nexti64((x, y): Pair, (xvel, yvel): Pair, width: usize) -> Option> { + 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)) + } } \ No newline at end of file