add: some days
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
89010123
|
||||
78121874
|
||||
87430965
|
||||
96549874
|
||||
45678903
|
||||
32019012
|
||||
01329801
|
||||
10456732
|
||||
+119
-52
@@ -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<String>) -> SResult<(usize, usize)> {
|
||||
fn solve(lines: Vec<String>) -> 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::<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>;
|
||||
|
||||
#[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<Triple>),
|
||||
@@ -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)));
|
||||
}
|
||||
@@ -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<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> {
|
||||
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<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> {
|
||||
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<String> = input!("d7p1.txt");
|
||||
let got = solve(strings).unwrap();
|
||||
assert_eq!(got, (3749, 0));
|
||||
assert_eq!(got, (3749, 11387));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
............
|
||||
........0...
|
||||
.....0......
|
||||
.......0....
|
||||
....0.......
|
||||
......A.....
|
||||
............
|
||||
............
|
||||
........A...
|
||||
.........A..
|
||||
............
|
||||
............
|
||||
+104
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
2333133121414131402
|
||||
+20
@@ -22,6 +22,16 @@ pub type SResult<T> = Result<T, Box<dyn std::error::Error>>;
|
||||
|
||||
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> {
|
||||
let x = x as i64 + xvel;
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user