Compare commits

..

19 commits
dev ... master

Author SHA1 Message Date
Daniel Flanagan f70f537f36 Part 1 2024-12-11 10:25:42 -06:00
Daniel Flanagan 3bd6fc2600 Day 10 part 2 2024-12-10 09:43:02 -06:00
Daniel Flanagan b17ad64e2f Day 10 part 1 2024-12-10 09:40:23 -06:00
Daniel Flanagan 772478e4fb Part 2 done 2024-12-09 10:10:00 -06:00
Daniel Flanagan 6f86208041 Day 9 part 1 done 2024-12-09 09:38:47 -06:00
Daniel Flanagan c595088352 Day 8 done 2024-12-08 07:18:20 -06:00
Daniel Flanagan f954cdad24 Day 7 done 2024-12-07 10:14:41 -06:00
Daniel Flanagan 81337c405b Day 7 part 1 2024-12-07 10:10:48 -06:00
Daniel Flanagan e5669fac3b Now it gets ugly 2024-12-06 06:13:52 -06:00
Daniel Flanagan 3d78aaee63 Day 5 done 2024-12-05 11:17:53 -06:00
Daniel Flanagan dc5e3b450f Parsing 2024-12-05 10:04:24 -06:00
Daniel Flanagan 80315133c4 Cleanup 2024-12-04 10:07:31 -06:00
Daniel Flanagan 337508f4e8 Day 4 done 2024-12-04 09:47:17 -06:00
Daniel Flanagan c529a96f30 Add test case for second part 2024-12-03 10:09:20 -06:00
Daniel Flanagan 07b32d8ff8 Add comments 2024-12-03 10:00:08 -06:00
Daniel Flanagan 0c0524cf7b Remove unused struct 2024-12-03 09:46:50 -06:00
Daniel Flanagan ab55683f55 Day 3 done 2024-12-03 09:46:16 -06:00
Daniel Flanagan f5e79284a1 Day 3 part 1 done 2024-12-03 09:37:51 -06:00
Daniel Flanagan 95d88256e0 Starting day 3 2024-12-03 09:14:40 -06:00
12 changed files with 1112 additions and 41 deletions

115
2024/rust/src/day10.rs Normal file
View file

@ -0,0 +1,115 @@
mod prelude;
pub use crate::prelude::*;
fn main() {
let input = day_input(10);
show_answers(part1(&input), part2(&input));
}
fn part1(input: &str) -> usize {
println!("{input}");
let mut sum = 0;
let map: Vec<Vec<u8>> = input
.lines()
.map(|l| l.bytes().map(|b| b - b'0').collect())
.collect();
for (y, row) in map.iter().enumerate() {
for (x, height) in row.iter().enumerate() {
let mut scores = HashSet::new();
if *height == 0 {
capture_scores(&map, *height, x, y, &mut scores);
sum += scores.len();
}
println!("{x} {y} {height}");
}
}
sum
}
fn capture_scores(
map: &Vec<Vec<u8>>,
height: u8,
x: usize,
y: usize,
scores: &mut HashSet<(usize, usize)>,
) {
if height == 9 {
scores.insert((x, y));
println!("found a 9");
}
for (nx, ny) in [
(x + 1, y),
(x.saturating_sub(1), y),
(x, y + 1),
(x, y.saturating_sub(1)),
] {
if nx >= map[0].len() || ny >= map.len() {
continue;
}
let next_height = map[ny][nx];
if next_height.saturating_sub(height) == 1 {
println!("{height} -> {next_height} at {nx},{ny}");
capture_scores(map, next_height, nx, ny, scores)
}
}
}
fn part2(input: &str) -> usize {
println!("{input}");
let mut sum = 0;
let map: Vec<Vec<u8>> = input
.lines()
.map(|l| l.bytes().map(|b| b - b'0').collect())
.collect();
for (y, row) in map.iter().enumerate() {
for (x, height) in row.iter().enumerate() {
if *height == 0 {
sum += find_paths_to_nines(&map, *height, x, y);
}
println!("{x} {y} {height}");
}
}
sum
}
fn find_paths_to_nines(map: &Vec<Vec<u8>>, height: u8, x: usize, y: usize) -> usize {
let mut sum = 0;
if height == 9 {
return 1;
}
for (nx, ny) in [
(x + 1, y),
(x.saturating_sub(1), y),
(x, y + 1),
(x, y.saturating_sub(1)),
] {
if nx >= map[0].len() || ny >= map.len() {
continue;
}
let next_height = map[ny][nx];
if next_height.saturating_sub(height) == 1 {
println!("{height} -> {next_height} at {nx},{ny}");
sum += find_paths_to_nines(map, next_height, nx, ny)
}
}
sum
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let input = r#"89010123
78121874
87430965
96549874
45678903
32019012
01329801
10456732"#;
assert_eq!(part1(input), 36);
assert_eq!(part2(input), 81);
}
}

56
2024/rust/src/day11.rs Normal file
View file

@ -0,0 +1,56 @@
mod prelude;
pub use crate::prelude::*;
fn main() {
let input = day_input(11);
show_answers(part1(&input), part2(&input));
}
fn part1(input: &str) -> usize {
stone_blinks(input, 25)
}
fn stone_blinks(input: &str, count: usize) -> usize {
let mut stones: Vec<usize> = input
.trim()
.split(" ")
.map(|s| s.parse().unwrap())
.collect();
let mut next_stones = vec![];
for blink in 0..count {
println!("{blink} {}", stones.len());
for (_i, s) in stones.iter().enumerate() {
// next_stones.push(*s)
if *s == 0 {
next_stones.push(1);
continue;
}
let ss = s.to_string();
if ss.len() % 2 == 0 {
let mid = ss.len() / 2;
let (s1, s2) = (ss[0..mid].parse().unwrap(), ss[mid..].parse().unwrap());
next_stones.push(s1);
next_stones.push(s2);
continue;
}
next_stones.push(s * 2024)
}
stones = next_stones;
next_stones = vec![];
}
stones.len()
}
fn part2(input: &str) -> usize {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let input = r#"125 17"#;
assert_eq!(part1(input), 55312);
assert_eq!(part2(input), 0);
}
}

View file

@ -24,30 +24,29 @@ impl FromStr for Report {
impl Report { impl Report {
fn is_safe(&self, skip_index: Option<usize>) -> bool { fn is_safe(&self, skip_index: Option<usize>) -> bool {
let mut index_sequence: Box<dyn Iterator<Item = &i64>> = if let Some(i) = skip_index { let mut ordering: Option<Ordering> = None;
Box::new( let skip_index = skip_index.unwrap_or(self.levels.len() + 1);
self.levels[0..i] let max = if skip_index < self.levels.len() {
.iter() self.levels.len() - 2
.chain(self.levels[i + 1..self.levels.len()].iter())
.into_iter(),
)
} else { } else {
Box::new(self.levels.iter()) self.levels.len() - 1
}; };
let a = index_sequence.next().unwrap();
let b = index_sequence.next().unwrap(); for i in 0..max {
let ordering = a.cmp(b); let mut ai = i;
if ordering == Ordering::Equal { let mut bi = ai + 1;
if ai >= skip_index {
ai += 1
};
if bi >= skip_index {
bi += 1
};
let (a, b) = (self.levels[ai], self.levels[bi]);
if !Self::safe_level_pair(a, b, &ordering) {
return false; return false;
} }
if !Self::safe_level_pair(*a, *b, ordering) { if ordering == None {
return false; ordering = Some(a.cmp(&b))
}
println!("{a} {b} {ordering:?} {skip_index:?}");
while let (a, Some(b)) = (b, index_sequence.next()) {
println!("{a} {b} {ordering:?}");
if !Self::safe_level_pair(*a, *b, ordering) {
return false;
} }
} }
@ -55,13 +54,25 @@ impl Report {
} }
fn is_safe_with_any_single_level_removed(&self) -> bool { fn is_safe_with_any_single_level_removed(&self) -> bool {
(0..self.levels.len()) for i in 0..self.levels.len() {
.into_iter() if self.is_safe(Some(i)) {
.any(|i| self.is_safe(Some(i))) return true;
}
}
return false;
} }
fn safe_level_pair(a: i64, b: i64, kind: Ordering) -> bool { fn safe_level_pair(a: i64, b: i64, kind: &Option<Ordering>) -> bool {
kind == a.cmp(&b) && (a - b).abs() <= 3 if a == b {
return false;
}
if (a - b).abs() > 3 {
return false;
}
if kind.map(|o| o == a.cmp(&b)) == Some(false) {
return false;
}
return true;
} }
} }

130
2024/rust/src/day3.rs Normal file
View file

@ -0,0 +1,130 @@
mod prelude;
pub use crate::prelude::*;
fn main() {
let input = day_input(3);
show_answers(sum_muls(&input), sum_muls_with_do_and_dont(&input));
}
fn sum_muls(input: &str) -> usize {
let mut sum = 0;
// this string slice will be how we keep track of what is "left" to parse
// though it would likely be easier to use an index and slice as needed :shrug:
let mut s = &input[..];
// look for the next valid mul operation
while let Some(i) = s.find("mul(") {
// re-slice to the remaining string
s = &s[i + 4..];
// variables to contain our operands
let mut left: Option<usize> = None;
let mut right: Option<usize> = None;
// variable to contain our currently-parsing token, as we will continue
// from here one character at a time
let mut token = &s[0..0];
for c in s.chars() {
match c {
// any digit simply gets "appended" to our token (in reality, we are just increasing the length of the token slice)
'0'..='9' => {
token = &s[0..token.len() + 1];
// the problem indicates that numbers may not be longer than
// 3 digits, so if we encounter one, we break out of this
// loop and head to the next mul operation
if token.len() > 3 {
break;
}
}
// once we hit a comma, we check that we don't already have
// a left operand then parse the number (which we can unwrap
// since we are assuring we are dealing with digits above,
// though we could easily optimize that out with bit of
// arithmetic), advance through our string slice, and reset our
// currently-parsing token
',' => {
if left.is_some() {
break;
}
left = Some(token.parse().unwrap());
s = &s[token.len() + 1..];
token = &s[0..0];
}
// pretty much the same logic for finding a comma, but for the
// right operand
')' => {
if right.is_some() {
break;
}
right = Some(token.parse().unwrap());
s = &s[token.len() + 1..];
token = &s[0..0];
}
_ => break,
}
}
if let (Some(left), Some(right)) = (left, right) {
sum += left * right;
}
}
sum
}
fn sum_muls_with_do_and_dont(input: &str) -> usize {
// the gist of this function is that we look for string segments that are
// between do() and don't() operations and only sum_muls for those NOT
// between a don't() and do()
let mut sum = 0;
let mut s = &input[..];
// since there may be a case where the last do() or don't() is a do(),
// we need to know whether or not to parse the "remainder" after we have
// processed the last don't() segment which may have ended with a do()
let mut mul_rest = true;
while let Some(i) = s.find("don't()") {
// once we find a don't, we know we want to sum_muls for everything up
// to the current position
sum += sum_muls(&s[0..i]);
// then we can advance our slice
s = &s[i..];
mul_rest = false;
if let Some(i) = s.find("do()") {
// if we find a do(), set our mul_rest flag appropriately, advance
// along the slice, and head to the next iteration (or be done in
// the loop)
s = &s[i..];
mul_rest = true;
}
}
// if we "ended" with a d(), we want to sum_muls for the rest of the slice
if mul_rest {
sum += sum_muls(s);
}
sum
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
assert_eq!(
sum_muls("xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))"),
161
);
assert_eq!(
sum_muls_with_do_and_dont(
"xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))"
),
48
);
}
}

126
2024/rust/src/day4.rs Normal file
View file

@ -0,0 +1,126 @@
mod prelude;
pub use crate::prelude::*;
fn main() {
let input = day_input(4);
let grid: Vec<Vec<u8>> = input
.trim()
.lines()
.map(|l| l.trim().bytes().collect())
.collect();
show_answers(count_xmas(&grid), count_mas_x(&grid));
}
fn count_xmas(grid: &Vec<Vec<u8>>) -> usize {
let height = grid.len();
let width = grid[0].len();
let mut counter = 0;
const XMAS: (u8, u8, u8, u8) = (b'X', b'M', b'A', b'S');
const SAMX: (u8, u8, u8, u8) = (b'S', b'A', b'M', b'X');
for (y, line) in grid.iter().enumerate() {
for (x, letter) in line.iter().enumerate() {
// let near_left_edge = x < 3;
let near_top_edge = y < 3;
let near_right_edge = x >= width - 3;
let near_bottom_edge = y >= height - 3;
if !near_right_edge && !near_top_edge {
// north-west
let ray = (
*letter,
grid[y - 1][x + 1],
grid[y - 2][x + 2],
grid[y - 3][x + 3],
);
if ray == XMAS || ray == SAMX {
counter += 1;
}
}
if !near_right_edge {
// west
let ray = (*letter, grid[y][x + 1], grid[y][x + 2], grid[y][x + 3]);
if ray == XMAS || ray == SAMX {
counter += 1;
}
}
if !near_right_edge && !near_bottom_edge {
// south-west
let ray = (
*letter,
grid[y + 1][x + 1],
grid[y + 2][x + 2],
grid[y + 3][x + 3],
);
if ray == XMAS || ray == SAMX {
counter += 1;
}
}
if !near_bottom_edge {
// south
let ray = (*letter, grid[y + 1][x], grid[y + 2][x], grid[y + 3][x]);
if ray == XMAS || ray == SAMX {
counter += 1;
}
}
}
}
counter
}
fn count_mas_x(grid: &Vec<Vec<u8>>) -> usize {
let mut counter = 0;
let (width, height) = (grid[0].len(), grid.len());
for y in 1..height - 1 {
for x in 1..width - 1 {
// 'A' will always be in the middle, so we can easily just look for
// middle 'A' occurences and then check for opposing 'S' and 'M'
// characters
if grid[y][x] != b'A' {
continue;
}
// check opposing cross-characters
let ray1 = (grid[y - 1][x - 1], grid[y + 1][x + 1]);
let ray2 = (grid[y + 1][x - 1], grid[y - 1][x + 1]);
if (ray1 == (b'M', b'S') || ray1 == (b'S', b'M'))
&& (ray2 == (b'M', b'S') || ray2 == (b'S', b'M'))
{
counter += 1;
}
}
}
counter
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let input = r#"MMMSXXMASM
MSAMXMSMSA
AMXSXMAAMM
MSAMASMSMX
XMASAMXAMM
XXAMMXXAMA
SMSMSASXSS
SAXAMASAAA
MAMMMXMMMM
MXMXAXMASX"#;
let grid: Vec<Vec<u8>> = input
.trim()
.lines()
.map(|l| l.trim().bytes().collect())
.collect();
assert_eq!(count_xmas(&grid), 18);
assert_eq!(count_mas_x(&grid), 9);
}
}

133
2024/rust/src/day5.rs Normal file
View file

@ -0,0 +1,133 @@
mod prelude;
pub use crate::prelude::*;
fn main() {
let input = day_input(5);
let update: ManualUpdate = input.parse().unwrap();
show_answers(update.part1(), update.part2());
}
#[derive(Debug)]
struct ManualUpdate {
rules: HashMap<i64, HashSet<i64>>,
updates: Vec<Vec<i64>>,
}
impl FromStr for ManualUpdate {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (rules_string, updates_string) = s.split_once("\n\n").unwrap();
let mut rules: HashMap<i64, HashSet<i64>> = HashMap::new();
let mut updates = vec![];
for rule in rules_string.lines() {
let (left, right) = rule.split_once("|").unwrap();
let (left, right): (i64, i64) = (left.parse()?, right.parse()?);
rules.entry(left).or_default().insert(right);
}
for update in updates_string.lines() {
updates.push(
update
.split(",")
.map(|page| page.parse())
.collect::<Result<Vec<i64>, Self::Err>>()?,
);
}
Ok(Self { rules, updates })
}
}
impl ManualUpdate {
fn part1(&self) -> i64 {
self.updates
.iter()
.filter(|update| self.is_update_valid(update))
.map(Self::middle_page_number)
.sum()
}
fn part2(&self) -> i64 {
self.updates
.iter()
.filter(|update| !self.is_update_valid(update))
.map(|update| self.sort_update(update))
.map(|sorted_update| Self::middle_page_number(&sorted_update))
.sum()
}
fn middle_page_number(update: &Vec<i64>) -> i64 {
update[update.len() / 2]
}
fn is_update_valid(&self, update: &Vec<i64>) -> bool {
// work through pages in an update backwards
for (i, page) in update.iter().rev().enumerate() {
// so we can loop through each previous page
for previous_page in update[0..update.len() - i].iter().rev() {
if let Some(failure_pages) = self.rules.get(page) {
if failure_pages.contains(previous_page) {
return false;
}
}
}
}
return true;
}
fn sort_update(&self, update: &Vec<i64>) -> Vec<i64> {
let mut sorted_update = update.clone();
sorted_update.sort_by(|a, b| {
if let Some(after_pages) = self.rules.get(a) {
if after_pages.contains(b) {
return Ordering::Less;
}
}
return Ordering::Greater;
});
sorted_update
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let manual_update: ManualUpdate = "47|53
97|13
97|61
97|47
75|29
61|13
75|53
29|13
97|29
53|29
61|53
97|53
61|29
47|13
75|47
97|75
47|61
75|61
47|29
75|13
53|13
75,47,61,53,29
97,61,53,29,13
75,29,13
75,97,47,61,53
61,13,29
97,13,75,29,47"
.parse()
.unwrap();
assert_eq!(manual_update.part1(), 143);
assert_eq!(manual_update.part2(), 123);
}
}

193
2024/rust/src/day6.rs Normal file
View file

@ -0,0 +1,193 @@
mod prelude;
pub use crate::prelude::*;
fn main() {
let input = day_input(6);
let mut lab: Lab = input.parse().unwrap();
let mut lab2: Lab = input.parse().unwrap();
show_answers(
lab.num_steps_until_guard_leaves_lab(),
lab2.num_positions_of_new_obstructions_that_cause_guard_loop(),
);
}
#[derive(Default, Debug, PartialEq, Eq, Hash, Clone)]
struct Vec2 {
x: i64,
y: i64,
}
impl Vec2 {
fn new(x: i64, y: i64) -> Self {
Self { x, y }
}
fn add(&self, v: &Vec2) -> Self {
Self::new(self.x + v.x, self.y + v.y)
}
}
impl Display for Vec2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Vec2({}, {})", self.x, self.y)
}
}
#[derive(Debug, Clone)]
struct Obstruction {
pos: Vec2,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct Guard {
pos: Vec2,
vel: Vec2,
}
impl Guard {
fn turn(&mut self) {
// too lazy for trig today I guess
if self.vel.x > 0 && self.vel.y > 0 {
self.vel.x *= -1;
} else if self.vel.x < 0 && self.vel.y > 0 {
self.vel.y *= -1;
} else if self.vel.x < 0 && self.vel.y < 0 {
self.vel.x *= -1;
} else if self.vel.x > 0 && self.vel.y < 0 {
self.vel.y *= -1;
} else if self.vel.y != 0 {
self.vel.x = self.vel.y * -1;
self.vel.y = 0;
} else if self.vel.x != 0 {
self.vel.y = self.vel.x;
self.vel.x = 0;
}
}
}
#[derive(Debug, Clone)]
struct Lab {
obstructions: Vec<Obstruction>,
guard: Guard,
size: Vec2,
}
impl FromStr for Lab {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut size = Vec2::default();
let mut obstructions = vec![];
let mut guard: Option<Guard> = None;
for (y, line) in s.lines().enumerate() {
size.y += 1;
size.x = line.len() as i64; // whatever - still linear ;)
for (x, c) in line.bytes().enumerate() {
match c {
b'#' => obstructions.push(Obstruction {
pos: Vec2::new(x as i64, y as i64),
}),
b'^' => {
guard = Some(Guard {
pos: Vec2::new(x as i64, y as i64),
vel: Vec2::new(0, -1),
})
}
_ => {}
}
}
}
Ok(Lab {
size,
guard: guard.unwrap(),
obstructions,
})
}
}
impl Lab {
fn step(&mut self) {
// move guard
let next_pos = self.guard.pos.add(&self.guard.vel);
self.guard.pos = next_pos;
// after moving, check if we need to turn
// TODO: obstructions should lookup by position instead of this
// broad-phase "check each one each time" slow business
while self.has_obstacle_at(&self.guard.pos.add(&self.guard.vel)) {
self.guard.turn();
}
}
fn contains(&self, pos: &Vec2) -> bool {
(0..self.size.x).contains(&pos.x) && (0..self.size.y).contains(&pos.y)
}
fn num_steps_until_guard_leaves_lab(&mut self) -> usize {
let mut visited = HashSet::new();
visited.insert(self.guard.pos.clone());
while self.contains(&self.guard.pos) {
self.step();
visited.insert(self.guard.pos.clone());
}
visited.len() - 1
}
fn has_obstacle_at(&self, pos: &Vec2) -> bool {
self.obstructions.iter().any(|o| o.pos == *pos)
}
fn num_positions_of_new_obstructions_that_cause_guard_loop(&mut self) -> usize {
let mut positions_with_cycles_detected = 0;
let mut visited = HashSet::new();
visited.insert(self.guard.clone());
// TODO: avoid super brute force
for x in 0..self.size.x {
for y in 0..self.size.y {
println!("Simulating at {x} {y}...");
let mut alternate_visited = visited.clone();
let mut alternate_lab = self.clone();
alternate_lab.obstructions.push(Obstruction {
pos: Vec2::new(x, y),
});
while alternate_lab.contains(&alternate_lab.guard.pos) {
alternate_lab.step();
if alternate_visited.contains(&alternate_lab.guard) {
positions_with_cycles_detected += 1;
break;
}
alternate_visited.insert(alternate_lab.guard.clone());
}
}
}
positions_with_cycles_detected
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let input = "
....#.....
.........#
..........
..#.......
.......#..
..........
.#..^.....
........#.
#.........
......#..."
.trim();
let mut lab: Lab = input.parse().unwrap();
let mut lab2 = lab.clone();
assert_eq!(lab.num_steps_until_guard_leaves_lab(), 41);
assert_eq!(
lab2.num_positions_of_new_obstructions_that_cause_guard_loop(),
6
);
}
}

93
2024/rust/src/day7.rs Normal file
View file

@ -0,0 +1,93 @@
mod prelude;
use std::slice::Windows;
pub use crate::prelude::*;
fn main() {
let input = day_input(7);
show_answers(
sum_possibly_valid_tests(&input, &add_and_mul),
sum_possibly_valid_tests(&input, &add_and_mul_and_concat),
);
}
fn sum_possibly_valid_tests(input: &str, f: &dyn Fn(Vec<usize>, &usize) -> Vec<usize>) -> usize {
let mut sum = 0;
for l in input.lines() {
let (target, operands) = l.split_once(':').unwrap();
let target: usize = target.parse().unwrap();
let operands: Vec<usize> = operands
.trim()
.split(" ")
.map(|s| s.parse().unwrap())
.collect();
if operands
.iter()
.skip(1)
.fold(vec![operands[0]], f)
.iter()
.any(|n| *n == target)
{
sum += target;
}
}
sum
}
fn add_and_mul(lefts: Vec<usize>, right: &usize) -> Vec<usize> {
let mut result = vec![];
for left in lefts {
result.push(left + right);
result.push(left * right);
}
result
}
fn add_and_mul_and_concat(lefts: Vec<usize>, right: &usize) -> Vec<usize> {
let mut result = vec![];
for left in lefts {
result.push(left + right);
result.push(left * right);
result.push(format!("{}{}", left, right).parse().unwrap());
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
assert_eq!(
sum_possibly_valid_tests(
"190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20",
&add_and_mul
),
3749
);
assert_eq!(
sum_possibly_valid_tests(
"190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20",
&add_and_mul_and_concat
),
11387
);
}
}

115
2024/rust/src/day8.rs Normal file
View file

@ -0,0 +1,115 @@
mod prelude;
pub use crate::prelude::*;
fn main() {
let input = day_input(8);
show_answers(part1(&input), part2(&input));
}
fn part1(input: &str) -> usize {
let mut map: HashMap<u8, HashSet<(i64, i64)>> = HashMap::new();
let mut antinodes: HashSet<(i64, i64)> = HashSet::new();
let (width, height) = (
input.lines().count() as i64,
input.lines().next().unwrap().len() as i64,
);
for (y, line) in input.lines().enumerate() {
for (x, c) in line.bytes().enumerate() {
if c == b'.' {
continue;
}
map.entry(c)
.or_insert_with(|| HashSet::new())
.insert((x as i64, y as i64));
}
}
for nodes in map.values() {
println!("{nodes:?}");
for (i, (x1, y1)) in nodes.iter().enumerate() {
for (j, (x2, y2)) in nodes.iter().enumerate() {
if i == j {
continue;
}
let (dx, dy) = (x1 - x2, y1 - y2);
let (a1x, a1y) = (x1 + dx, y1 + dy);
let (a2x, a2y) = (x2 - dx, y2 - dy);
if a1x >= 0 && a1x < width && a1y >= 0 && a1y < height {
antinodes.insert((a1x, a1y));
}
if a2x >= 0 && a2x < width && a2y >= 0 && a2y < height {
antinodes.insert((a2x, a2y));
}
}
}
}
antinodes.len()
}
fn part2(input: &str) -> usize {
let mut map: HashMap<u8, HashSet<(i64, i64)>> = HashMap::new();
let mut antinodes: HashSet<(i64, i64)> = HashSet::new();
let (width, height) = (
input.trim().lines().count() as i64,
input.lines().next().unwrap().trim().len() as i64,
);
for (y, line) in input.lines().enumerate() {
for (x, c) in line.bytes().enumerate() {
if c == b'.' {
continue;
}
map.entry(c)
.or_insert_with(|| HashSet::new())
.insert((x as i64, y as i64));
}
}
for nodes in map.values() {
for (i, (x1, y1)) in nodes.iter().enumerate() {
for (j, (x2, y2)) in nodes.iter().enumerate() {
antinodes.insert((*x1, *y1));
antinodes.insert((*x2, *y2));
if i == j {
continue;
}
let (dx, dy) = (x1 - x2, y1 - y2);
let (mut a1x, mut a1y) = (x1 + dx, y1 + dy);
while a1x >= 0 && a1x < width && a1y >= 0 && a1y < height {
antinodes.insert((a1x, a1y));
println!("{a1x} {a1y} {}", antinodes.len());
a1x += dx;
a1y += dy;
}
let (mut a2x, mut a2y) = (x2 - dx, y2 - dy);
while a2x >= 0 && a2x < width && a2y >= 0 && a2y < height {
antinodes.insert((a2x, a2y));
println!("{a2x} {a2y} {}", antinodes.len());
a2x -= dx;
a2y -= dy;
}
}
}
}
antinodes.len()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let input = "............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............";
assert_eq!(part1(input), 14);
assert_eq!(part2(input), 34);
}
}

101
2024/rust/src/day9.rs Normal file
View file

@ -0,0 +1,101 @@
mod prelude;
pub use crate::prelude::*;
fn main() {
let input = day_input(9);
show_answers(part1(&input), part2(&input));
}
struct Disk {
blocks: Vec<Option<usize>>,
files: Vec<(usize, u8)>,
free_blocks: usize,
}
impl FromStr for Disk {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut blocks: Vec<Option<usize>> = vec![];
let mut files = vec![];
let mut free_blocks: usize = 0;
for (i, len) in s.trim().bytes().map(|len| len - b'0').enumerate() {
// every other iteration is "free space"
let file_id = if i % 2 == 0 {
Some((i / 2) as usize)
} else {
free_blocks += len as usize;
None
};
if file_id.is_some() {
files.push((blocks.len(), len));
}
for _ in 0..len {
blocks.push(file_id);
}
}
Ok(Self {
files,
blocks,
free_blocks,
})
}
}
impl Disk {
fn checksum(&self) -> usize {
self.blocks
.iter()
.enumerate()
.map(|(i, b)| i * b.unwrap_or_default())
.sum()
}
}
fn part1(input: &str) -> usize {
let mut disk: Disk = input.parse().unwrap();
let mut right = disk.blocks.len() - 1;
for left in 0..(right.clone() - disk.free_blocks + 1) {
if disk.blocks[left].is_some() {
continue;
}
while disk.blocks[right].is_none() {
right -= 1;
}
(disk.blocks[left], disk.blocks[right]) = (disk.blocks[right], disk.blocks[left]);
}
disk.checksum()
}
fn part2(input: &str) -> usize {
let mut disk: Disk = input.parse().unwrap();
for (cur_i, len) in disk.files.iter().rev() {
for left in 0..*cur_i {
if disk.blocks[left..(left + (*len as usize))]
.iter()
.all(|f| f.is_none())
{
for i in 0..*len {
let i = i as usize;
(disk.blocks[left + i], disk.blocks[cur_i + i]) =
(disk.blocks[cur_i + i], disk.blocks[left + i])
}
break;
}
}
}
disk.checksum()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let input = r#"2333133121414131402"#;
assert_eq!(part1(input), 1928);
assert_eq!(part2(input), 2858);
}
}

View file

@ -2,6 +2,9 @@ use std::path::{Path, PathBuf};
pub use std::{ pub use std::{
cmp::Ordering, cmp::Ordering,
collections::HashMap, collections::HashMap,
collections::HashSet,
convert::Infallible,
fmt::Display,
fs::File, fs::File,
io::Read, io::Read,
iter::zip, iter::zip,

View file

@ -1,23 +1,18 @@
mod prelude; mod prelude;
use crate::prelude::*; pub use crate::prelude::*;
fn main() { fn main() {
Day5::show(day_input(5), day_input(5)) let input = day_input(DAY);
show_answers(part1(&input), part2(&input));
} }
struct Day5 {} fn part1(input: &str) -> i64 {
impl AoCSolution for Day5 {
type Input = String;
type Solution = usize;
fn part1(input: Self::Input) -> Self::Solution {
0 0
} }
fn part2(input: Self::Input) -> Self::Solution { fn part2(input: &str) -> i64 {
0 0
} }
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -26,7 +21,7 @@ mod tests {
#[test] #[test]
fn test() { fn test() {
let input = r#""#; let input = r#""#;
assert_eq!(Day2::part1(input.into()), 1); assert_eq!(part1(input), 1);
assert_eq!(Day2::part2(input.into()), 0); assert_eq!(part2(input), 0);
} }
} }