diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e33a799..fe14da5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,7 @@ jobs: - day14 - day15 - day16 + - day17 part: [ a, b ] if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 04c58c7..1f17046 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -305,6 +305,16 @@ dependencies = [ "sqrid 0.0.22", ] +[[package]] +name = "day17" +version = "0.1.0" +dependencies = [ + "aoc", + "color-eyre", + "nom", + "sqrid 0.0.23", +] + [[package]] name = "either" version = "1.9.0" @@ -591,6 +601,12 @@ version = "0.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92bfab44888e2fc47178beb71e1ad84bd2a97361a6943019a8602f214ae4c126" +[[package]] +name = "sqrid" +version = "0.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa174f70d3ecc8171ef9784a74bac0689af9ce41b01d00732a132465f985f3a" + [[package]] name = "thread_local" version = "1.1.7" diff --git a/Cargo.toml b/Cargo.toml index 0ca9120..ebeb64c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,5 +19,6 @@ members = [ "day14", "day15", "day16", + "day17", ] diff --git a/day17/Cargo.toml b/day17/Cargo.toml new file mode 100644 index 0000000..0f2ffc2 --- /dev/null +++ b/day17/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "day17" +version = "0.1.0" +edition = "2021" + +[dependencies] +aoc = { path = "../aoc" } +color-eyre = "0.6.2" +nom = "7.1.3" +sqrid = "0.0.23" diff --git a/day17/src/bin/day17a.rs b/day17/src/bin/day17a.rs new file mode 100644 index 0000000..37b2cd8 --- /dev/null +++ b/day17/src/bin/day17a.rs @@ -0,0 +1,78 @@ +// Copyright (C) 2023 Leandro Lisboa Penz +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. + +use day17::*; + +use std::cmp::Reverse; +use std::collections::BinaryHeap; +use std::collections::HashSet; + +pub type Heat = u32; + +#[derive(Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +struct State { + pub pos: Pos, + pub lastdir: Option, + pub dircount: usize, + pub heatacum: u32, +} + +fn process(size: u16, bufin: impl BufRead) -> Result { + let input = parser::parse(bufin)?; + let gheat = Grid::try_from(input)?; + let mut frontier = BinaryHeap::<(Reverse, State)>::default(); + frontier.push((Reverse(0), State::default())); + let mut visited = HashSet::<(Pos, State)>::default(); + let goal = Pos::try_from((size - 1, size - 1)).unwrap(); + while let Some((_priority, st)) = frontier.pop() { + let pos = st.pos; + if visited.contains(&(pos, st)) { + continue; + } + if pos == goal { + return Ok(st.heatacum); + } + for dir in Dir::iter::() { + if st.lastdir == Some(-dir) || (st.lastdir == Some(dir) && st.dircount >= 3) { + // Gets wobbly + continue; + } + if let Ok(newpos) = pos + dir { + let t = newpos.tuple(); + if t.0 >= size || t.1 >= size { + continue; + } + let heatacum = st.heatacum + gheat[newpos]; + let dircount = if Some(dir) == st.lastdir { + st.dircount + 1 + } else { + 1 + }; + let newst = State { + pos: newpos, + lastdir: Some(dir), + dircount, + heatacum, + }; + let dist = Pos::manhattan(&newpos, &goal) as u32; + let priority = Reverse(heatacum + dist); + frontier.push((priority, newst)); + } + } + visited.insert((pos, st)); + } + unreachable!(); +} + +#[test] +fn test() -> Result<()> { + let start = std::time::Instant::now(); + assert_eq!(process(13, EXAMPLE.as_bytes())?, 102); + println!("Elapsed: {}", elapsed(&start)); + Ok(()) +} + +fn main() -> Result<()> { + do_main(|| process(141, stdin().lock())) +} diff --git a/day17/src/lib.rs b/day17/src/lib.rs new file mode 100644 index 0000000..5e247d8 --- /dev/null +++ b/day17/src/lib.rs @@ -0,0 +1,74 @@ +// Copyright (C) 2023 Leandro Lisboa Penz +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. + +pub use aoc::*; + +pub const EXAMPLE: &str = "2413432311323 +3215453535623 +3255245654254 +3446585845452 +4546657867536 +1438598798454 +4457876987766 +3637877979653 +4654967986887 +4564679986453 +1224686865563 +2546548887735 +4322674655533 +"; + +pub type Cell = u32; + +pub mod parser { + use aoc::parser::*; + + use super::*; + + fn cell(input: &str) -> IResult<&str, Cell> { + let (input, val) = character::one_of("0123456789")(input)?; + Ok((input, val.to_digit(10).unwrap())) + } + + fn line(input: &str) -> IResult<&str, Vec> { + let (input, cells) = multi::many1(cell)(input)?; + let (input, _) = character::newline(input)?; + Ok((input, cells)) + } + + pub fn parse(mut bufin: impl BufRead) -> Result>> { + aoc::parse_with!(multi::many1(line), bufin) + } +} + +#[test] +fn test() -> Result<()> { + let input = parser::parse(EXAMPLE.as_bytes())?; + assert_eq!(input.len(), 13); + assert_eq!(input[0].len(), 13); + Ok(()) +} + +pub use sqrid::Dir; +pub type Sqrid = sqrid::sqrid_create!(141, 141, false); +// pub type Sqrid = sqrid::sqrid_create!(13, 13, false); +pub type Pos = sqrid::pos_create!(Sqrid); +pub type Grid = sqrid::grid_create!(Sqrid, u32); + +pub type Griddir = sqrid::grid_create!(Sqrid, String); + +pub fn path_debug(_size: u16, gheat: &Grid, path: &[Dir]) { + let mut gheatacum = Grid::default(); + let mut pos = Pos::TOP_LEFT; + let mut heat = 0; + let mut gdir = Griddir::default(); + for dir in path { + gdir[pos] = dir.name_utf8().to_string(); + pos = (pos + dir).unwrap(); + heat += gheat[pos]; + gheatacum[pos] = heat; + } + eprintln!("{:1}", gdir); + eprintln!("{:>4}", gheatacum); +}