Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion aoc_utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use num_traits::{zero, PrimInt};
use num_traits::PrimInt;
use std::collections::{HashMap, HashSet};
use std::fs::read_to_string;
use std::ops::RemAssign;
Expand Down
44 changes: 44 additions & 0 deletions year_2024/input/day10.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
12109832101432101234107652158943210178765892
03078456210145696701218943067654396549456701
54562364345436789874327832107810387630345210
65401875696925210765498017656901234521254321
78078956787814321544567328943217890012189450
69101045698701055432123410812206921983098765
43232132509652566785010569701105435674549056
58943001419143478994321678983210304105678143
67653214328012988765690787894321213289437212
45654301037001089650787096765010034576524301
56789890156121072341256101896654123678915498
43276765243234561212345234987783210569206789
54109854312789870109012345676898765454106543
45610123203650105438721056765609674323287012
54781010154543216521635489832014589210398013
67898543269854107610544376541023008101296323
54987656576765678923455210458782112010387456
23122189983454989012966904349698103465456567
12033078012763210101877813234521098578956798
03944565430887654012109320121034787632347897
87856556021991047121238458945695698961036016
96587432110872338930347567232780087654105125
01498983321265427945656089101091109803234934
32327465456766016859890176232892256712107843
21012334569854105766763245001743343893256765
30503129678945234897854632122654872894349854
45614068798234012656906543213458961783210703
21765878907178723765417891008965450654125612
30854965416069654894328982567872342103054503
48903010325450560761237813450561003276543678
56012321210341981230106504341540214789432189
67329630121212870341012415432634345695321012
78478742198903965494543326998723456786540765
89569653087654654987696547889010567847830874
21052104676501723898587032378765676956921923
32343015685432810767698121459034982349650010
10478723794354903456567030760121061078744567
21569654891263212347450177898267877101233498
32108765430678903038321789783454978715012399
47899834320545676129012876012543269856101087
56938723011230983543903965987650156747801256
40127619654321012652874854107890349832954343
30034508763018723761765543236501212721096501
21065619012349654890101234565432301430787432
1 change: 1 addition & 0 deletions year_2024/input/day11.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5688 62084 2 3248809 179 79 0 172169
5 changes: 5 additions & 0 deletions year_2024/input/day12-example2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
OOOOO
OXOXO
OOOOO
OXOXO
OOOOO
10 changes: 10 additions & 0 deletions year_2024/input/day12-example3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
RRRRIICCFF
RRRRIICCCF
VVRRRCCFFF
VVRCCCJFFF
VVVVCJJCFE
VVIVCCJJEE
VVIIICJJEE
MIIIIIJJEE
MIIISIJEEE
MMMISSJEEE
140 changes: 140 additions & 0 deletions year_2024/input/day12.txt

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions year_2024/input/day9.txt

Large diffs are not rendered by default.

205 changes: 205 additions & 0 deletions year_2024/src/day10.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
use std::collections::{HashMap, HashSet};

pub fn execute() -> String {
let data = aoc_utils::read_lines("input/day10.txt");
let topo = Topography::from_lines(&data);

let part1 = topo.count_all_reachable_peaks();
let part2 = topo.count_all_possible_routes();

format!("{} {}", part1, part2)
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Position(u32, u32);

struct Topography {
size: u32,
altitudes: Vec<u32>,
}
impl Topography {
fn from_lines(lines: &Vec<String>) -> Topography {
let size = lines.len() as u32;

let mut result = Topography {
size,
altitudes: vec![0; (size * size) as usize],
};

for (j, line) in lines.iter().enumerate() {
assert_eq!(line.len(), size as usize);

for (i, c) in line.chars().enumerate() {
let altitude = c.to_digit(10).unwrap();
let position = Position(i as u32, j as u32);
let offset = result.offset(position);
result.altitudes[offset] = altitude;
}
}

result
}

fn altitude(&self, position: Position) -> u32 {
self.altitudes[self.offset(position)]
}

fn offset(&self, position: Position) -> usize {
position.0 as usize * self.size as usize + position.1 as usize
}

fn position(&self, offset: usize) -> Position {
let j = offset % self.size as usize;
let i = offset / self.size as usize;
Position(i as u32, j as u32)
}

fn count_all_reachable_peaks(&self) -> usize {
let heads = self.trailheads();

let head_peaks: Vec<Vec<Position>> = heads
.iter()
.map(|&reachable| self.reachable_peaks(reachable))
.collect();

head_peaks.iter().map(|peaks| peaks.len()).sum()
}

fn reachable_peaks(&self, start: Position) -> Vec<Position> {
assert_eq!(self.altitude(start), 0);

let mut reachable = HashSet::from([start]);
for _ in 0..9 {
reachable = reachable
.iter()
.flat_map(|&reached| self.next_reachable(reached))
.collect();
}
reachable.into_iter().collect()
}

fn next_reachable(&self, from: Position) -> Vec<Position> {
let mut result = vec![];

let offset = self.offset(from);
let altitude = self.altitude(from) + 1;

if from.0 > 0 && self.altitudes[offset - self.size as usize] == altitude {
result.push(Position(from.0 - 1, from.1));
}
if from.0 < self.size - 1 && self.altitudes[offset + self.size as usize] == altitude {
result.push(Position(from.0 + 1, from.1));
}
if from.1 > 0 && self.altitudes[offset - 1] == altitude {
result.push(Position(from.0, from.1 - 1));
}
if from.1 < self.size - 1 && self.altitudes[offset + 1] == altitude {
result.push(Position(from.0, from.1 + 1));
}
result
}

fn count_all_possible_routes(&self) -> usize {
let heads = self.trailheads();

let head_routes: Vec<Vec<(Position, usize)>> = heads
.iter()
.map(|&reachable| self.possible_routes(reachable))
.collect();

head_routes
.iter()
.map(|routes| routes.iter().map(|&(_dest, count)| count).sum::<usize>())
.sum()
}

fn possible_routes(&self, start: Position) -> Vec<(Position, usize)> {
let mut reachable = vec![(start, 1)];
for _ in 0..9 {
let mut next_reachable = HashMap::new();
reachable.iter().for_each(|&(reached, prev_count)| {
let next_positions = self.next_reachable(reached);
for position in next_positions {
next_reachable
.entry(position)
.and_modify(|count| *count += prev_count)
.or_insert(prev_count);
}
});
reachable = next_reachable.into_iter().collect();
}
reachable.into_iter().collect()
}

fn trailheads(&self) -> Vec<Position> {
self.altitudes
.iter()
.enumerate()
.filter_map(|(i, &alt)| (alt == 0).then_some(self.position(i)))
.collect()
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_mine() {
assert_eq!(execute(), "582 1302");
}

#[test]
fn test_from_lines() {
let topo = Topography::from_lines(&_example());

assert_eq!(topo.size, 8);
assert_eq!(topo.altitude(Position(0, 0)), 8);
assert_eq!(topo.altitude(Position(1, 1)), 8);
assert_eq!(topo.altitude(Position(1, 0)), 9);
assert_eq!(topo.altitude(Position(0, 1)), 7);
assert_eq!(topo.altitude(Position(0, 6)), 0);
assert_eq!(topo.altitude(Position(0, 7)), 1);
assert_eq!(topo.altitude(Position(6, 0)), 2);
assert_eq!(topo.altitude(Position(7, 0)), 3);
assert_eq!(topo.altitude(Position(6, 6)), 0);
assert_eq!(topo.altitude(Position(7, 7)), 2);
}

#[test]
fn test_reachable_peaks() {
let topo = Topography::from_lines(&_example());

assert_eq!(topo.reachable_peaks(Position(2, 0)).len(), 5);
assert_eq!(topo.count_all_reachable_peaks(), 36);
}
#[test]
fn test_possible_routes() {
let topo = Topography::from_lines(&_example());

let possible_routes = topo.possible_routes(Position(2, 0));
assert_eq!(possible_routes.len(), 5);
assert_eq!(
possible_routes
.iter()
.map(|&(_dest, count)| count)
.sum::<usize>(),
20
);

assert_eq!(topo.count_all_possible_routes(), 81);
}

fn _example() -> Vec<String> {
vec![
String::from("89010123"),
String::from("78121874"),
String::from("87430965"),
String::from("96549874"),
String::from("45678903"),
String::from("32019012"),
String::from("01329801"),
String::from("10456732"),
]
}
}
Loading
Loading