• 1 Post
  • 108 Comments
Joined 1 year ago
cake
Cake day: June 6th, 2023

help-circle
















  • Rust

    Code
    use std::fs;
    use std::path::PathBuf;
    
    use clap::Parser;
    
    #[derive(Parser)]
    #[command(author, version, about, long_about = None)]
    struct Cli {
        /// A file containing the input
        input_file: PathBuf,
    
        #[arg(short, long)]
        part: Option,
    }
    
    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 (run_part_1, run_part_2) = if let Some(part) = cli.part {
            match part {
                1 => (true, false),
                2 => (false, true),
                _ => unimplemented!(),
            }
        } else {
            (true, true)
        };
        let (it1, it2) = preprocess(&input_text);
    
        if run_part_1 {
            let solution = solve(it1);
            println!("Part 1: {solution}");
        }
        if run_part_2 {
            let solution = solve(it2);
            println!("Part 2: {solution}");
        }
    }
    
    /// Preprocessing for solving
    /// Extracts important information from the input
    /// `part` specifies which part to preprocess for.
    /// Returns a vector for each part containing a direction and amount
    fn preprocess(input: &str) -> (Vec<((isize, isize), isize)>, Vec<((isize, isize), isize)>) {
        let it = input.lines().map(|l| {
            let line: Vec<_> = l.split(' ').collect();
            let direction: char = line[0].chars().nth(0).unwrap();
            let amount: isize = line[1].parse().unwrap();
            let color: &str = &line[2][2..8];
    
            let direction = match direction {
                'R' => (1, 0),
                'L' => (-1, 0),
                'U' => (0, 1),
                'D' => (0, -1),
                _ => unreachable!(),
            };
    
            ((direction, amount), {
                let amount: isize = isize::from_str_radix(&color[..5], 16).unwrap();
                let direction = match &color[5..] {
                    "0" => (1, 0),
                    "1" => (0, -1),
                    "2" => (-1, 0),
                    "3" => (0, 1),
                    _ => unreachable!(),
                };
                (direction, amount)
            })
        });
    
        let it1 = it.clone().map(|(i1, _i2)| i1).collect();
        let it2 = it.map(|(_i1, i2)| i2).collect();
    
        (it1, it2)
    }
    
    /// Finds the area using the shoelace formula
    fn solve(it: Vec<((isize, isize), isize)>) -> isize {
        // Get coordinates from it
        let mut coords: Vec<(isize, isize)> = Vec::with_capacity(it.len() + 1);
    
        // Start at (0, 0)
        coords.push((0, 0)); // Needs to be at both begining and end
        let (mut x, mut y) = (0, 0);
    
        let mut perimeter_length = 0;
    
        // Compute and add the coords
        for (direction, amount) in it {
            let translation = (direction.0 * amount, direction.1 * amount);
            x += translation.0;
            y += translation.1;
    
            coords.push((x, y));
            perimeter_length += amount;
        }
    
        // Should end where it started
        assert_eq!(coords.last().unwrap(), &(0, 0));
    
        // Shoelace formula
        let a = coords
            .iter()
            .zip(coords.iter().skip(1))
            .map(|((x1, y1), (x2, y2))| (x1 * y2) - (x2 * y1))
            .sum::()
            / 2;
    
        // Found by drawing, then trial and error
        // Shoelace area missing 1/2 of perimeter
        // Inside and outside corners cancel out except for one
        a.abs() + perimeter_length / 2 + 1
    }
    
    Yay math!

  • APL

    spoiler
    args ← {1↓⍵/⍨∨\⍵∊⊂'--'} ⎕ARG
    inputs ← ⎕FIO[49]¨ args
    
    words ← 'one' 'two' 'three' 'four' 'five' 'six' 'seven' 'eight' 'nine'
    digits ← '123456789'
    
    part1 ← {↑↑+/{(10×↑⍵)+¯1↑⍵}¨{⍵~0}¨+⊃(⍳9)+.×digits∘.⍷⍵}
    "Part 1: ", part1¨ inputs
    
    part2 ← {↑↑+/{(10×↑⍵)+¯1↑⍵}¨{⍵~0}¨+⊃(⍳9)+.×(words∘.⍷⍵)+digits∘.⍷⍵}
    "Part 2: ", part2¨ inputs
    
    )OFF
    

  • 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