Day 16: The Floor Will Be Lava

Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

FAQ

  • abclop99@beehaw.org
    link
    fedilink
    arrow-up
    1
    ·
    edit-2
    7 months ago
    Rust
    use std::fs;
    use std::path::PathBuf;
    
    use clap::Parser;
    
    use rayon::prelude::*;
    
    #[derive(Parser)]
    #[command(author, version, about, long_about = None)]
    struct Cli {
        input_file: PathBuf,
    }
    
    #[derive(Copy, Clone)]
    enum TileState {
        None,
        Energized(BeamState),
    }
    #[derive(Default, Copy, Clone)]
    struct BeamState {
        up: bool,
        down: bool,
        left: bool,
        right: bool,
    }
    
    fn main() {
        // Parse CLI arguments
        let cli = Cli::parse();
    
        // Read file
        let input_text = fs::read_to_string(&cli.input_file)
            .expect(format!("File \"{}\" not found", cli.input_file.display()).as_str());
    
        let tiles: Vec> = input_text.lines().map(|l| l.chars().collect()).collect();
    
        // Part 1
        let part_1 = test_beam(&tiles, (0, 0), (0, 1));
        println!("Part 1: {}", part_1);
    
        // Part 2
        let part_2: usize = (0..4)
            .into_par_iter()
            .map(|dir| {
                (0..tiles.len())
                    .into_par_iter()
                    .map(move |x| (dir.clone(), x))
            })
            .flatten()
            .map(|(dir, x)| match dir {
                0 => ((0, x), (1, 0)),
                1 => ((x, tiles[0].len() - 1), (0, -1)),
                2 => ((tiles.len() - 1, x), (-1, 0)),
                3 => ((x, 0), (0, 1)),
                _ => unreachable!(),
            })
            .map(|(loc, dir)| test_beam(&tiles, loc, dir))
            .max()
            .unwrap();
        println!("Part 2: {}", part_2);
    }
    
    fn test_beam(
        tiles: &Vec>,
        start_location: (usize, usize),
        start_direction: (i64, i64),
    ) -> usize {
        let mut energized: Vec> =
            vec![vec![TileState::None; tiles[0].len()]; tiles.len()];
    
        continue_beam(
            &mut energized,
            &tiles,
            start_location,
            start_direction,
            true,
            0,
        );
        energized
            .iter()
            .map(|r| {
                r.iter()
                    .filter(|t| matches!(t, TileState::Energized(_)))
                    .count()
            })
            .sum()
    }
    
    fn continue_beam(
        energized: &mut Vec>,
        tiles: &Vec>,
        beam_location: (usize, usize),
        beam_direction: (i64, i64),
        start_hack: bool,
        depth: usize,
    ) {
        assert_ne!(beam_direction, (0, 0));
    
        // Set current tile to energized with the direction
        let current_state = energized[beam_location.0][beam_location.1];
        if !start_hack {
            energized[beam_location.0][beam_location.1] = match current_state {
                TileState::None => TileState::Energized(match beam_direction {
                    (0, 1) => BeamState {
                        right: true,
                        ..BeamState::default()
                    },
                    (0, -1) => BeamState {
                        left: true,
                        ..BeamState::default()
                    },
                    (1, 0) => BeamState {
                        down: true,
                        ..BeamState::default()
                    },
                    (-1, 0) => BeamState {
                        up: true,
                        ..BeamState::default()
                    },
                    _ => unreachable!(),
                }),
                TileState::Energized(state) => TileState::Energized(match beam_direction {
                    (0, 1) => {
                        if state.right {
                            return;
                        }
                        BeamState {
                            right: true,
                            ..state
                        }
                    }
                    (0, -1) => {
                        if state.left {
                            return;
                        }
                        BeamState {
                            left: true,
                            ..state
                        }
                    }
                    (1, 0) => {
                        if state.down {
                            return;
                        }
                        BeamState {
                            down: true,
                            ..state
                        }
                    }
                    (-1, 0) => {
                        if state.up {
                            return;
                        }
                        BeamState { up: true, ..state }
                    }
                    _ => unreachable!(),
                }),
            };
        }
    
        // energized[beam_location.0][beam_location.1] = TileState::Energized(BeamState { up: , down: , left: , right:  });
    
        let next_beam_location = {
            let loc = (
                (beam_location.0 as i64 + beam_direction.0),
                (beam_location.1 as i64 + beam_direction.1),
            );
    
            if start_hack {
                beam_location
            } else if loc.0 < 0
                || loc.0 >= tiles.len() as i64
                || loc.1 < 0
                || loc.1 >= tiles[0].len() as i64
            {
                return;
            } else {
                (loc.0 as usize, loc.1 as usize)
            }
        };
        let next_beam_tile = tiles[next_beam_location.0][next_beam_location.1];
    
        let next_beam_directions: Vec<(i64, i64)> = match next_beam_tile {
            '.' => vec![beam_direction],
            '/' => match beam_direction {
                (0, 1) => vec![(-1, 0)],
                (0, -1) => vec![(1, 0)],
                (1, 0) => vec![(0, -1)],
                (-1, 0) => vec![(0, 1)],
                _ => unreachable!(),
            },
            '\\' => match beam_direction {
                (0, 1) => vec![(1, 0)],
                (0, -1) => vec![(-1, 0)],
                (1, 0) => vec![(0, 1)],
                (-1, 0) => vec![(0, -1)],
                _ => unreachable!(),
            },
            '|' => match beam_direction {
                (0, 1) => vec![(1, 0), (-1, 0)],
                (0, -1) => vec![(1, 0), (-1, 0)],
                (1, 0) => vec![(1, 0)],
                (-1, 0) => vec![(-1, 0)],
                _ => unreachable!(),
            },
            '-' => match beam_direction {
                (0, 1) => vec![(0, 1)],
                (0, -1) => vec![(0, -1)],
                (1, 0) => vec![(0, 1), (0, -1)],
                (-1, 0) => vec![(0, 1), (0, -1)],
                _ => unreachable!(),
            },
            _ => unreachable!(),
        };
    
        for dir in next_beam_directions {
            continue_beam(energized, tiles, next_beam_location, dir, false, depth + 1);
        }
    }
    

    26.28 line-seconds