day16: problem 1 solution
This commit is contained in:
		
							
								
								
									
										216
									
								
								16/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								16/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,216 @@
 | 
			
		||||
use std::collections::HashSet;
 | 
			
		||||
use std::fmt::Display;
 | 
			
		||||
use std::fs::File;
 | 
			
		||||
use std::io::{BufRead, BufReader, Lines};
 | 
			
		||||
use std::iter::repeat;
 | 
			
		||||
use std::time::Instant;
 | 
			
		||||
 | 
			
		||||
// BOILERPLATE
 | 
			
		||||
type InputIter = Lines<BufReader<File>>;
 | 
			
		||||
 | 
			
		||||
fn get_input() -> InputIter {
 | 
			
		||||
    let f = File::open("input").unwrap();
 | 
			
		||||
    let br = BufReader::new(f);
 | 
			
		||||
    br.lines()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    let start = Instant::now();
 | 
			
		||||
    let ans1 = problem1(get_input());
 | 
			
		||||
    let duration = start.elapsed();
 | 
			
		||||
    println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
 | 
			
		||||
 | 
			
		||||
    let start = Instant::now();
 | 
			
		||||
    let ans2 = problem2(get_input());
 | 
			
		||||
    let duration = start.elapsed();
 | 
			
		||||
    println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PARSE
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
 | 
			
		||||
enum FromDirection {
 | 
			
		||||
    Left,
 | 
			
		||||
    Above,
 | 
			
		||||
    Right,
 | 
			
		||||
    Below,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Display for FromDirection {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            Self::Above => f.write_str("above"),
 | 
			
		||||
            Self::Below => f.write_str("below"),
 | 
			
		||||
            Self::Left => f.write_str("left"),
 | 
			
		||||
            Self::Right => f.write_str("right"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Tile {
 | 
			
		||||
    kind: char,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<char> for Tile {
 | 
			
		||||
    fn from(c: char) -> Self {
 | 
			
		||||
        Tile { kind: c }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Contraption {
 | 
			
		||||
    tiles: Vec<Vec<char>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct VisitState {
 | 
			
		||||
    energized: Vec<Vec<bool>>,
 | 
			
		||||
    visited_rays: HashSet<((i64, i64), FromDirection)>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Contraption {
 | 
			
		||||
    fn cast_ray<'a>(
 | 
			
		||||
        &'a mut self,
 | 
			
		||||
        state: &'a mut VisitState,
 | 
			
		||||
        mut pos: (i64, i64),
 | 
			
		||||
        mut dir: FromDirection,
 | 
			
		||||
    ) -> Vec<((i64, i64), FromDirection)> {
 | 
			
		||||
        let width = state.energized[0].len();
 | 
			
		||||
        let height = state.energized.len();
 | 
			
		||||
        let mut new_rays = Vec::new();
 | 
			
		||||
        while pos.0 >= 0 && pos.1 >= 0 && pos.0 < width as i64 && pos.1 < height as i64 {
 | 
			
		||||
            // visit pos
 | 
			
		||||
            state.energized[pos.1 as usize][pos.0 as usize] = true;
 | 
			
		||||
            if !state.visited_rays.insert((pos, dir)) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            (pos, dir) = match self.tiles[pos.1 as usize][pos.0 as usize] {
 | 
			
		||||
                '.' => match dir {
 | 
			
		||||
                    FromDirection::Left => ((pos.0 + 1, pos.1), dir),
 | 
			
		||||
                    FromDirection::Right => ((pos.0 - 1, pos.1), dir),
 | 
			
		||||
                    FromDirection::Above => ((pos.0, pos.1 + 1), dir),
 | 
			
		||||
                    FromDirection::Below => ((pos.0, pos.1 - 1), dir),
 | 
			
		||||
                },
 | 
			
		||||
                '/' => match dir {
 | 
			
		||||
                    // from left, go up, from below
 | 
			
		||||
                    FromDirection::Left => ((pos.0, pos.1 - 1), FromDirection::Below),
 | 
			
		||||
                    // from up, go left, from the right
 | 
			
		||||
                    FromDirection::Above => ((pos.0 - 1, pos.1), FromDirection::Right),
 | 
			
		||||
                    // from right, go down, from above
 | 
			
		||||
                    FromDirection::Right => ((pos.0, pos.1 + 1), FromDirection::Above),
 | 
			
		||||
                    // from below, go right, from left
 | 
			
		||||
                    FromDirection::Below => ((pos.0 + 1, pos.1), FromDirection::Left),
 | 
			
		||||
                },
 | 
			
		||||
                '\\' => match dir {
 | 
			
		||||
                    FromDirection::Left => ((pos.0, pos.1 + 1), FromDirection::Above),
 | 
			
		||||
                    FromDirection::Above => ((pos.0 + 1, pos.1), FromDirection::Left),
 | 
			
		||||
                    FromDirection::Right => ((pos.0, pos.1 - 1), FromDirection::Below),
 | 
			
		||||
                    FromDirection::Below => ((pos.0 - 1, pos.1), FromDirection::Right),
 | 
			
		||||
                },
 | 
			
		||||
                '-' => match dir {
 | 
			
		||||
                    FromDirection::Left => ((pos.0 + 1, pos.1), dir),
 | 
			
		||||
                    FromDirection::Right => ((pos.0 - 1, pos.1), dir),
 | 
			
		||||
                    FromDirection::Above | FromDirection::Below => {
 | 
			
		||||
                        new_rays.push(((pos.0 + 1, pos.1), FromDirection::Left));
 | 
			
		||||
                        ((pos.0 - 1, pos.1), FromDirection::Right)
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                '|' => match dir {
 | 
			
		||||
                    FromDirection::Above => ((pos.0, pos.1 + 1), dir),
 | 
			
		||||
                    FromDirection::Below => ((pos.0, pos.1 - 1), dir),
 | 
			
		||||
                    FromDirection::Left | FromDirection::Right => {
 | 
			
		||||
                        new_rays.push(((pos.0, pos.1 + 1), FromDirection::Above));
 | 
			
		||||
                        ((pos.0, pos.1 - 1), FromDirection::Below)
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                c => unimplemented!("invalid character {}", c),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        new_rays
 | 
			
		||||
    }
 | 
			
		||||
    fn empty_state(&self) -> VisitState {
 | 
			
		||||
        let mut energized = Vec::new();
 | 
			
		||||
        for _ in 0..self.tiles.len() {
 | 
			
		||||
            energized.push(Vec::from_iter(repeat(false).take(self.tiles[0].len())));
 | 
			
		||||
        }
 | 
			
		||||
        VisitState {
 | 
			
		||||
            energized,
 | 
			
		||||
            visited_rays: HashSet::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: BufRead> From<Lines<T>> for Contraption {
 | 
			
		||||
    fn from(lines: Lines<T>) -> Self {
 | 
			
		||||
        let mut tiles = Vec::new();
 | 
			
		||||
        for line in lines {
 | 
			
		||||
            tiles.push(line.unwrap().chars().map(|c| c.into()).collect());
 | 
			
		||||
        }
 | 
			
		||||
        Contraption { tiles }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn dump_state(state: &Vec<Vec<bool>>) {
 | 
			
		||||
    for line in state {
 | 
			
		||||
        println!(
 | 
			
		||||
            "{}",
 | 
			
		||||
            String::from_iter(line.iter().map(|b| if *b { '#' } else { '.' }))
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PROBLEM 1 solution
 | 
			
		||||
 | 
			
		||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
 | 
			
		||||
    let mut contraption = Contraption::from(input);
 | 
			
		||||
    let mut state = contraption.empty_state();
 | 
			
		||||
 | 
			
		||||
    let mut new_rays = contraption.cast_ray(&mut state, (0, 0), FromDirection::Left);
 | 
			
		||||
    for ray in &new_rays {
 | 
			
		||||
        println!("  {:?}", ray);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    loop {
 | 
			
		||||
        new_rays = new_rays
 | 
			
		||||
            .iter()
 | 
			
		||||
            .flat_map(|(pos, dir)| contraption.cast_ray(&mut state, *pos, *dir))
 | 
			
		||||
            .collect();
 | 
			
		||||
        if new_rays.len() == 0 {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    state.energized.iter().flatten().filter(|c| **c).count() as u64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PROBLEM 2 solution
 | 
			
		||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
 | 
			
		||||
    0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use crate::*;
 | 
			
		||||
    use std::io::Cursor;
 | 
			
		||||
 | 
			
		||||
    const EXAMPLE: &str = &r".|...\....
 | 
			
		||||
|.-.\.....
 | 
			
		||||
.....|-...
 | 
			
		||||
........|.
 | 
			
		||||
..........
 | 
			
		||||
.........\
 | 
			
		||||
..../.\\..
 | 
			
		||||
.-.-/..|..
 | 
			
		||||
.|....-|.\
 | 
			
		||||
..//.|....";
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn problem1_example() {
 | 
			
		||||
        let c = Cursor::new(EXAMPLE);
 | 
			
		||||
        assert_eq!(problem1(c.lines()), 46);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn problem2_example() {
 | 
			
		||||
        let c = Cursor::new(EXAMPLE);
 | 
			
		||||
        assert_eq!(problem2(c.lines()), 51);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user