--- /dev/null
+abaccccccccccccccaaaccccaaaaaaaaaaaaaccccccaacccccccccccccccccccccccccccccaaaaaa
+abaaccaacccccccccaaaaaccccaaaaaaaaaaaaaccccaaacccccccccccccccccccccccccccccaaaaa
+abaaccaaacccccccaaaaaacccaaaaaaaaaaaaaacaaaaaaaaccccccccaacccccccccccccccccccaaa
+abcaaaaaaaacccccaaaaaacccaaaaaaaaaaaaaacaaaaaaaacccccccaaaacccccccccccccccccaaaa
+abcaaaaaaaaccccccaaaaaccaaaaaaaaccaaaaaccaaaaaaccccccccaaaaccaaaccccccccccccaaac
+abccaaaaaacccccccaaaaccaaaaaaaaaacaaaacccaaaaaacccccccccakkaaaaaacccccccccccaacc
+abccaaaaaacccccccccccccaaaaaaaaaaccccccccaaaaaaccccccckkkkkkkaaacccccccccccccccc
+abccaaaaaaccccccccccccccccaaaaaaaaaccccccaacaaacccccckkkkkkkkkaccccccaccaaaccccc
+abccaacaaacccccaaccccccccaaacacaaaacaaccccccccccccccakkkoppkkkkicccccaaaaaaccccc
+abccccccccccccaaaccccccccaacccccaaaaaaccccccccccccccjkkooppppkiicccccccaaaaccccc
+abccccccccccaaaaaaaaccccccccccaaaaaaaccccccccccccccjjjooopppppiiiicccccccaaacccc
+abaaacccccccaaaaaaaacccccccaacaaaaaaccccccccccccccjjjjooouuppppiiiiiicccccaacccc
+abaaaccccccccaaaaaaccccccccaaaccaaaaacccccccccccjjjjjooouuuupppiiiiiiiiccccacccc
+abaaaaaacccccaaaaaacccccaaaaaaaaaacaaaccccccccjjjjjjooouuuuuupppppiiiiiicccccccc
+abaaaaaacccccaaaaaacccccaaaaaaaaaacccccccccccjjjjjooooouuxxuupppppqqqijjjccccccc
+abaaaacccccaaaaccaaccccccaaaaaaccccccccccccciijjnooooouuuxxxuuupqqqqqqjjjdddcccc
+abaaaaaccaaaaaaccacccccccaaaaaaccccccccccaaiiiinnootttuuxxxxuuvvvvvqqqjjjdddcccc
+abaaaaaccaaaaaacaaaccaaccaaaaaaccccccccccaaiiinnnntttttuxxxxxvvvvvvqqqjjjdddcccc
+abaaccacccaaaaacaaaaaaaccaaccaaccccccccccaaiiinnnttttxxxxxxxyyyyyvvqqqjjjdddcccc
+abcccccccaaaaacccaaaaaaccccccaaaaacccccccaaiiinnntttxxxxxxxyyyyyvvvqqqjjjddccccc
+SbcccccccaaaaacaaaaaaaaccccccaaaaaccccccccciiinnntttxxxEzzzzyyyyvvqqqjjjdddccccc
+abcccccccccccccaaaaaaaaaccccaaaaaaccccccccciiinnnntttxxxxyyyyyvvvvqqjjjdddcccccc
+abcccccccccccccaaaaaaaaaacccaaaaaacccccccccciiinnnttttxxxyyyyyvvvqqqjjjdddcccccc
+abccccccccccccccccaaaaaaacccaaaaaaccccccccccciiinnnntttwyyywyyyvvrrrkkjdddcccccc
+abcccccccccccccccaaaaaaaaccccaaaccccccccccccciiihnnnttwwwywwyyywvrrrkkkeeccccccc
+abcccccccccccccccaaaaaaaaccccccccccccccccccccchhhmmmsswwwwwwwwwwwvrrkkkeeccccccc
+abcccccccaacccccccacaaacccccccccccccccccccaacchhhhmmsswwwwwswwwwwrrrkkkeeccccccc
+abcccccccaaaccacccccaaacccccccccccccccaaccaaccchhhmmssswwwssrrwwwrrrkkkeeccccccc
+abcccccccaaaaaaacccccccccccaaaccccccccaaaaaaccchhhmmssssssssrrrrrrrrkkkeeaaacccc
+abcccccaaaaaaaaccccccccccccaaaaccccccccaaaaaaachhhmmmssssssllrrrrrrkkkeeeaaacccc
+abccccaaaaaaaaaccccccccccccaaaacccccccccaaaaacchhhmmmmsssllllllllkkkkkeeeaaacccc
+abccccaaaaaaaaaccccccccccccaaacccccccccaaaaacccchhhmmmmmlllllllllkkkkeeeeaaccccc
+abcccccccaaaaaaccccccccccaacccccccaaccaaacaacccchhhmmmmmlllgfflllkkffeeeaaaacccc
+abccccccaaaaaaaccccccccccaaaaaaaaaaaaaccccaacccchhhggmmmggggffffffffffeaaaaacccc
+abccaacccaacccaaaaccaccccaaaaaaaaaaaaacccccccccccgggggggggggffffffffffaacccccccc
+abaaaaccaaaccccaaaaaaccccaaaaaacaaaaaaccccccccccccgggggggggaaaaccffccccccccccccc
+abaaaacccccccccaaaaaaccaaaaaaaaaaaaaacccccccccccccccgggaaaaaaaacccccccccccccccca
+abaaaaacccccccaaaaaaaccaaaaaaaaaaaaaacccccccccccccccccaaacccaaaaccccccccccccccaa
+abaaaaacaaaaccaaaaaaaacaaaaaaaaaaaccccccccccccccccccccaaaccccaaaccccccccccaaacaa
+abaaaaacaaaaccaaaaaaaaaaaaaaaaaaacccccccccccccccccccccccccccccccccccccccccaaaaaa
+abaaacccaaaaccccaaaccccaaaaaaaaaaacccccccccccccccccccccccccccccccccccccccccaaaaa
\ No newline at end of file
--- /dev/null
+#[derive(Debug)]
+pub struct Heightmap {
+ elevations: Vec<Vec<i32>>,
+ start: (usize, usize),
+ end: (usize, usize),
+}
+
+fn char_to_num(c: char) -> i32 {
+ c as i32 - 'a' as i32
+}
+
+pub fn parse(input: &str) -> Heightmap {
+ let mut hm = Heightmap { elevations: Vec::new(), start: (0, 0), end: (0, 0) };
+ for (i, l) in input.lines().enumerate() {
+ let mut row = Vec::new();
+ for (j, c) in l.trim().chars().enumerate() {
+ if c == 'S' {
+ hm.start = (i, j);
+ row.push(char_to_num('a'));
+ } else if c == 'E' {
+ hm.end = (i, j);
+ row.push(char_to_num('z'));
+ } else {
+ row.push(char_to_num(c));
+ }
+ }
+ hm.elevations.push(row);
+ }
+ hm
+}
+
+#[derive(PartialEq)]
+pub enum Path {
+ StartToEnd,
+ EndTo0Elevation,
+}
+
+pub fn nb_steps(hm: &Heightmap, path: Path) -> i32 {
+ let (n, m) = (hm.elevations.len(), hm.elevations[0].len());
+ let mut visited: Vec<Vec<i32>> = Vec::new();
+ for _ in 0..n { visited.push(vec![-1; m]); }
+ let mut positions = vec![hm.end];
+ let mut next_positions = Vec::new();
+ let mut step = -1;
+
+ let neighbors = |i, j| {
+ let mut positions = Vec::new();
+ if i > 0 { positions.push((i - 1, j)); }
+ if i < n - 1 { positions.push((i + 1, j)); }
+ if j > 0 { positions.push((i, j - 1)); }
+ if j < m - 1 { positions.push((i, j + 1)); }
+ positions
+ };
+
+ loop {
+ step = step + 1;
+ for (i, j) in positions.drain(..) {
+ if path == Path::StartToEnd && (i, j) == hm.start || path == Path::EndTo0Elevation && hm.elevations[i][j] == 0 {
+ return step;
+ }
+
+ visited[i][j] = step;
+ for (ni, nj) in neighbors(i, j) {
+ if visited[ni][nj] == -1 && hm.elevations[i][j] - hm.elevations[ni][nj] <= 1 {
+ visited[ni][nj] = step;
+ next_positions.push((ni, nj));
+ }
+ }
+ }
+ std::mem::swap(&mut next_positions, &mut positions);
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ static HEIGHTMAP: &str =
+ "Sabqponm
+ abcryxxl
+ accszExk
+ acctuvwj
+ abdefghi";
+
+ #[test]
+ fn part1() {
+ let heightmap = parse(HEIGHTMAP);
+ assert_eq!(nb_steps(&heightmap, Path::StartToEnd), 31);
+ }
+
+ #[test]
+ fn part2() {
+ let heightmap = parse(HEIGHTMAP);
+ assert_eq!(nb_steps(&heightmap, Path::EndTo0Elevation), 29);
+ }
+}
--- /dev/null
+use std::{fs, io::{BufReader, Seek, SeekFrom}};
+
+use crate::*;
+
+pub fn day01() -> String {
+ let f = fs::File::open("data/day01.input").unwrap();
+ let calories = day01::read_calories(BufReader::new(f));
+ format!("part1: {}, part2: {}", day01::get_most_calories(&calories), day01::get_sum_most_three_calories(&calories))
+}
+
+pub fn day02() -> String {
+ let mut f = fs::File::open("data/day02.input").unwrap();
+ let shapes = day02::read_shapes(BufReader::new(f.try_clone().unwrap()));
+ let _ = f.seek(SeekFrom::Start(0));
+ let shapes_2 = day02::read_shapes_2(BufReader::new(f));
+ format!("part1: {}, part2: {}", day02::get_score(&shapes), day02::get_score(&shapes_2))
+}
+
+pub fn day03() -> String {
+ let rucksacks = day03::parse(&fs::read_to_string("data/day03.input").unwrap());
+ format!("part1: {}, part2: {}", day03::priority_sum(&rucksacks), day03::badge_sum(&rucksacks))
+}
+
+pub fn day04() -> String {
+ let pairs = day04::parse(&fs::read_to_string("data/day04.input").unwrap());
+ format!("part1: {}, part2: {}", day04::number_fully_contain(&pairs), day04::number_overlaps(&pairs))
+}
+
+pub fn day05() -> String {
+ let (mut stacks, moves) = day05::parse(&fs::read_to_string("data/day05.input").unwrap());
+ let mut stacks2 = stacks.clone();
+ day05::apply_moves_by_crate_mover_9000(&mut stacks, &moves);
+ day05::apply_moves_by_crate_mover_9001(&mut stacks2, &moves);
+ format!("part1: {}, part2: {}", day05::get_top_as_string(&stacks), day05::get_top_as_string(&stacks2))
+}
+
+pub fn day06() -> String {
+ let signals = fs::read_to_string("data/day06.input").unwrap();
+ format!("part1: {}, part2: {}", day06::first_marker_pos(&signals, 4), day06::first_marker_pos(&signals, 14))
+}
+
+pub fn day07() -> String {
+ let root = day07::parse(&fs::read_to_string("data/day07.input").unwrap());
+
+ let (root_size, sum_part1, ) = {
+ let mut sizes: Vec<i64> = Vec::new();
+ (root.dir_sizes(|size| size <= 100_000, &mut sizes), sizes.iter().sum::<i64>())
+ };
+
+ let min_part2 = {
+ let to_free = root_size - (70_000_000 - 30_000_000);
+ let mut sizes: Vec<i64> = Vec::new();
+ root.dir_sizes(|size| size >= to_free, &mut sizes);
+ *sizes.iter().min().unwrap()
+ };
+
+ format!("part1: {}, part2: {}", sum_part1, min_part2)
+}
+
+pub fn day08() -> String {
+ let forest = day08::parse(&fs::read_to_string("data/day08.input").unwrap());
+ format!("part1: {}, part2: {}", day08::number_of_visible_trees(&forest), day08::best_scenic_score(&forest))
+}
+
+pub fn day09() -> String {
+ let movements = day09::parse(&fs::read_to_string("data/day09.input").unwrap());
+ format!("part1: {}, part2: {}", day09::nb_positions_visited_by_tail::<2>(&movements), day09::nb_positions_visited_by_tail::<10>(&movements))
+}
+
+pub fn day10() -> String {
+ let instructions = day10::parse(&fs::read_to_string("data/day10.input").unwrap());
+ let mut screen = day10::Screen::new();
+ let sum_signal_strength = screen.draw_screen(&instructions);
+ format!("part1: {}, part2: \n{}", sum_signal_strength, screen.to_ascii())
+}
+
+pub fn day11() -> String {
+ let monkeys = day11::parse(&fs::read_to_string("data/day11.input").unwrap());
+ format!("part1: {}, part2: {}", day11::run(&mut monkeys.clone(), 20, 3), day11::run(&mut monkeys.clone(), 10000, 1))
+}
+
+pub fn day12() -> String {
+ let heightmap = day12::parse(&fs::read_to_string("data/day12.input").unwrap());
+ format!("part1: {}, part2: {}", day12::nb_steps(&heightmap, day12::Path::StartToEnd), day12::nb_steps(&heightmap, day12::Path::EndTo0Elevation))
+}
-use std::{env, fs, time::Instant, io::{BufReader, Seek, SeekFrom}};
+use std::{env, time::Instant};
mod common;
+mod days;
mod day01;
mod day02;
mod day03;
mod day09;
mod day10;
mod day11;
-
-fn day01() -> String {
- let f = fs::File::open("data/day01.input").unwrap();
- let calories = day01::read_calories(BufReader::new(f));
- format!("part1: {}, part2: {}", day01::get_most_calories(&calories), day01::get_sum_most_three_calories(&calories))
-}
-
-fn day02() -> String {
- let mut f = fs::File::open("data/day02.input").unwrap();
- let shapes = day02::read_shapes(BufReader::new(f.try_clone().unwrap()));
- let _ = f.seek(SeekFrom::Start(0));
- let shapes_2 = day02::read_shapes_2(BufReader::new(f));
- format!("part1: {}, part2: {}", day02::get_score(&shapes), day02::get_score(&shapes_2))
-}
-
-fn day03() -> String {
- let rucksacks = day03::parse(&fs::read_to_string("data/day03.input").unwrap());
- format!("part1: {}, part2: {}", day03::priority_sum(&rucksacks), day03::badge_sum(&rucksacks))
-}
-
-fn day04() -> String {
- let pairs = day04::parse(&fs::read_to_string("data/day04.input").unwrap());
- format!("part1: {}, part2: {}", day04::number_fully_contain(&pairs), day04::number_overlaps(&pairs))
-}
-
-fn day05() -> String {
- let (mut stacks, moves) = day05::parse(&fs::read_to_string("data/day05.input").unwrap());
- let mut stacks2 = stacks.clone();
- day05::apply_moves_by_crate_mover_9000(&mut stacks, &moves);
- day05::apply_moves_by_crate_mover_9001(&mut stacks2, &moves);
- format!("part1: {}, part2: {}", day05::get_top_as_string(&stacks), day05::get_top_as_string(&stacks2))
-}
-
-fn day06() -> String {
- let signals = fs::read_to_string("data/day06.input").unwrap();
- format!("part1: {}, part2: {}", day06::first_marker_pos(&signals, 4), day06::first_marker_pos(&signals, 14))
-}
-
-fn day07() -> String {
- let root = day07::parse(&fs::read_to_string("data/day07.input").unwrap());
-
- let (root_size, sum_part1, ) = {
- let mut sizes: Vec<i64> = Vec::new();
- (root.dir_sizes(|size| size <= 100_000, &mut sizes), sizes.iter().sum::<i64>())
- };
-
- let min_part2 = {
- let to_free = root_size - (70_000_000 - 30_000_000);
- let mut sizes: Vec<i64> = Vec::new();
- root.dir_sizes(|size| size >= to_free, &mut sizes);
- *sizes.iter().min().unwrap()
- };
-
- format!("part1: {}, part2: {}", sum_part1, min_part2)
-}
-
-fn day08() -> String {
- let forest = day08::parse(&fs::read_to_string("data/day08.input").unwrap());
- format!("part1: {}, part2: {}", day08::number_of_visible_trees(&forest), day08::best_scenic_score(&forest))
-}
-
-fn day09() -> String {
- let movements = day09::parse(&fs::read_to_string("data/day09.input").unwrap());
- format!("part1: {}, part2: {}", day09::nb_positions_visited_by_tail::<2>(&movements), day09::nb_positions_visited_by_tail::<10>(&movements))
-}
-
-fn day10() -> String {
- let instructions = day10::parse(&fs::read_to_string("data/day10.input").unwrap());
- let mut screen = day10::Screen::new();
- let sum_signal_strength = screen.draw_screen(&instructions);
- format!("part1: {}, part2: \n{}", sum_signal_strength, screen.to_ascii())
-}
-
-fn day11() -> String {
- let monkeys = day11::parse(&fs::read_to_string("data/day11.input").unwrap());
- format!("part1: {}, part2: {}", day11::run(&mut monkeys.clone(), 20, 3), day11::run(&mut monkeys.clone(), 10000, 1))
-}
-
-fn format_micros(t: u128) -> String {
- if t < 10_000 {
- format!("{} μs", t)
- } else if t < 10_000_000u128 {
- format!("{} ms", t / 1_000u128)
- } else {
- format!("{} s", t / 1_000_000u128)
- }
-}
-
-fn do_day(days: &[fn() -> String], day: usize) {
- let now = Instant::now();
- println!("Result of day {:02}: {} (time: {})", day, days[day - 1](), format_micros(now.elapsed().as_micros()));
-}
+mod day12;
fn main() {
println!("https://adventofcode.com/2022");
let days: Vec<fn() -> String> = vec!(
- day01,
- day02,
- day03,
- day04,
- day05,
- day06,
- day07,
- day08,
- day09,
- day10,
- day11,
+ days::day01,
+ days::day02,
+ days::day03,
+ days::day04,
+ days::day05,
+ days::day06,
+ days::day07,
+ days::day08,
+ days::day09,
+ days::day10,
+ days::day11,
+ days::day12,
);
let args: Vec<String> = env::args().skip(1).collect();
}
}
}
+
+fn do_day(days: &[fn() -> String], day: usize) {
+ let now = Instant::now();
+ println!("Result of day {:02}: {} (time: {})", day, days[day - 1](), format_micros(now.elapsed().as_micros()));
+}
+
+fn format_micros(t: u128) -> String {
+ if t < 10_000 {
+ format!("{} μs", t)
+ } else if t < 10_000_000u128 {
+ format!("{} ms", t / 1_000u128)
+ } else {
+ format!("{} s", t / 1_000_000u128)
+ }
+}