day18: problem 1 solution
This commit is contained in:
		
							
								
								
									
										312
									
								
								18/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										312
									
								
								18/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,312 @@
 | 
			
		||||
use itertools::Itertools;
 | 
			
		||||
use std::collections::{HashMap, LinkedList};
 | 
			
		||||
use std::fmt::{Display, Write};
 | 
			
		||||
use std::fs::File;
 | 
			
		||||
use std::io::{BufRead, BufReader, Lines};
 | 
			
		||||
use std::ops::Range;
 | 
			
		||||
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());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DATA
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
 | 
			
		||||
enum Direction {
 | 
			
		||||
    Left,
 | 
			
		||||
    Right,
 | 
			
		||||
    Up,
 | 
			
		||||
    Down,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Direction {
 | 
			
		||||
    const fn all() -> &'static [Self; 4] {
 | 
			
		||||
        &[
 | 
			
		||||
            Direction::Left,
 | 
			
		||||
            Direction::Right,
 | 
			
		||||
            Direction::Up,
 | 
			
		||||
            Direction::Down,
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
    const fn opposite(&self) -> Self {
 | 
			
		||||
        match self {
 | 
			
		||||
            Direction::Left => Direction::Right,
 | 
			
		||||
            Direction::Right => Direction::Left,
 | 
			
		||||
            Direction::Up => Direction::Down,
 | 
			
		||||
            Direction::Down => Direction::Up,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    const fn offset(&self) -> (isize, isize) {
 | 
			
		||||
        match self {
 | 
			
		||||
            Direction::Left => (-1, 0),
 | 
			
		||||
            Direction::Right => (1, 0),
 | 
			
		||||
            Direction::Up => (0, -1),
 | 
			
		||||
            Direction::Down => (0, 1),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&str> for Direction {
 | 
			
		||||
    fn from(s: &str) -> Self {
 | 
			
		||||
        match s {
 | 
			
		||||
            "L" => Direction::Left,
 | 
			
		||||
            "R" => Direction::Right,
 | 
			
		||||
            "U" => Direction::Up,
 | 
			
		||||
            "D" => Direction::Down,
 | 
			
		||||
            s => panic!("{} is not a valid direction", s),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&Direction> for char {
 | 
			
		||||
    fn from(dir: &Direction) -> Self {
 | 
			
		||||
        match dir {
 | 
			
		||||
            Direction::Left => '←',
 | 
			
		||||
            Direction::Right => '→',
 | 
			
		||||
            Direction::Up => '↑',
 | 
			
		||||
            Direction::Down => '↓',
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
struct DigInstruction {
 | 
			
		||||
    dir: Direction,
 | 
			
		||||
    count: usize,
 | 
			
		||||
    color: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&str> for DigInstruction {
 | 
			
		||||
    fn from(s: &str) -> Self {
 | 
			
		||||
        let mut parts = s.split_ascii_whitespace();
 | 
			
		||||
        let (dir, count, color) = (
 | 
			
		||||
            parts.next().unwrap(),
 | 
			
		||||
            parts.next().unwrap(),
 | 
			
		||||
            parts.next().unwrap(),
 | 
			
		||||
        );
 | 
			
		||||
        Self {
 | 
			
		||||
            dir: dir.into(),
 | 
			
		||||
            count: count.parse().unwrap(),
 | 
			
		||||
            color: color.into(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
struct DigPlan {
 | 
			
		||||
    instructions: Vec<DigInstruction>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
struct DigTile {
 | 
			
		||||
    position: Position,
 | 
			
		||||
    depth: usize,
 | 
			
		||||
    color: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for DigTile {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            position: (0, 0),
 | 
			
		||||
            depth: 0,
 | 
			
		||||
            color: "000000".into(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Position = (isize, isize);
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
struct DigHole {
 | 
			
		||||
    tiles_loop: LinkedList<DigTile>,
 | 
			
		||||
    tiles_map: HashMap<Position, DigTile>,
 | 
			
		||||
    x_range: Range<isize>,
 | 
			
		||||
    y_range: Range<isize>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DigHole {
 | 
			
		||||
    fn new() -> Self {
 | 
			
		||||
        DigHole {
 | 
			
		||||
            tiles_loop: LinkedList::new(),
 | 
			
		||||
            tiles_map: HashMap::new(),
 | 
			
		||||
            x_range: 0..0,
 | 
			
		||||
            y_range: 0..0,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn pos_offset(&self, pos: Position, offset: (isize, isize)) -> Position {
 | 
			
		||||
        (pos.0 + offset.0, pos.1 + offset.1)
 | 
			
		||||
    }
 | 
			
		||||
    fn run_plan(&mut self, plan: &DigPlan) {
 | 
			
		||||
        // start position is 1m deep
 | 
			
		||||
        let mut cur_pos = (0, 0);
 | 
			
		||||
        let (mut max_x, mut max_y) = (0, 0);
 | 
			
		||||
 | 
			
		||||
        self.tiles_loop.push_back(DigTile {
 | 
			
		||||
            position: cur_pos,
 | 
			
		||||
            depth: 1,
 | 
			
		||||
            color: "000000".into(),
 | 
			
		||||
        });
 | 
			
		||||
        for i in &plan.instructions {
 | 
			
		||||
            println!("instruction: {:?}", i);
 | 
			
		||||
            for _ in 0..i.count {
 | 
			
		||||
                println!("  cur_pos: {:?}", cur_pos);
 | 
			
		||||
                cur_pos = (cur_pos.0 + i.dir.offset().0, cur_pos.1 + i.dir.offset().1);
 | 
			
		||||
                (max_x, max_y) = (
 | 
			
		||||
                    std::cmp::max(cur_pos.0, max_x),
 | 
			
		||||
                    std::cmp::max(cur_pos.1, max_y),
 | 
			
		||||
                );
 | 
			
		||||
                self.tiles_loop.push_back(DigTile {
 | 
			
		||||
                    position: cur_pos,
 | 
			
		||||
                    depth: 1,
 | 
			
		||||
                    color: i.color.to_owned(),
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // 2d vec dimension is (max_x+1, max_y+1)
 | 
			
		||||
        for tile in &self.tiles_loop {
 | 
			
		||||
            self.x_range.start = std::cmp::min(self.x_range.start, tile.position.0);
 | 
			
		||||
            self.x_range.end = std::cmp::max(self.x_range.end, tile.position.0 + 1);
 | 
			
		||||
            self.y_range.start = std::cmp::min(self.y_range.start, tile.position.1);
 | 
			
		||||
            self.y_range.end = std::cmp::max(self.y_range.end, tile.position.1 + 1);
 | 
			
		||||
            self.tiles_map.insert(tile.position, tile.clone());
 | 
			
		||||
        }
 | 
			
		||||
        println!("{}", self);
 | 
			
		||||
        self.fill_at(self.find_first_inside().unwrap());
 | 
			
		||||
    }
 | 
			
		||||
    fn fill_at(&mut self, pos: Position) {
 | 
			
		||||
        println!("filling at {:?}", pos);
 | 
			
		||||
        let mut to_fill = vec![pos];
 | 
			
		||||
 | 
			
		||||
        while let Some(fill_pos) = to_fill.pop() {
 | 
			
		||||
            if self.tiles_map.contains_key(&fill_pos) {
 | 
			
		||||
                continue;
 | 
			
		||||
            } else {
 | 
			
		||||
                self.tiles_map.insert(
 | 
			
		||||
                    fill_pos,
 | 
			
		||||
                    DigTile {
 | 
			
		||||
                        position: fill_pos,
 | 
			
		||||
                        depth: 1,
 | 
			
		||||
                        color: "000000".to_owned(),
 | 
			
		||||
                    },
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            for new_pos in Direction::all()
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|dir| self.pos_offset(fill_pos, dir.offset()))
 | 
			
		||||
                .filter(|pos| !self.tiles_map.contains_key(&pos))
 | 
			
		||||
            {
 | 
			
		||||
                to_fill.push(new_pos);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn find_first_inside(&self) -> Option<Position> {
 | 
			
		||||
        for y in self.y_range.clone() {
 | 
			
		||||
            let mut inside = false;
 | 
			
		||||
            for x in self.x_range.clone() {
 | 
			
		||||
                if self.tiles_map.contains_key(&(x, y)) && (!self.tiles_map.contains_key(&(x - 1, y)) || !self.tiles_map.contains_key(&(x+1, y)))
 | 
			
		||||
                {
 | 
			
		||||
                    inside = !inside;
 | 
			
		||||
                }
 | 
			
		||||
                if inside && !self.tiles_map.contains_key(&(x, y)) {
 | 
			
		||||
                    return Some((x as isize, y as isize));
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Display for DigHole {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        for y in self.y_range.clone() {
 | 
			
		||||
            for x in self.x_range.clone() {
 | 
			
		||||
                f.write_char(if (x, y) == (133, -267) {
 | 
			
		||||
                    '*'
 | 
			
		||||
                } else if self.tiles_map.contains_key(&(x, y)) {
 | 
			
		||||
                    '#'
 | 
			
		||||
                } else {
 | 
			
		||||
                    '.'
 | 
			
		||||
                })?;
 | 
			
		||||
            }
 | 
			
		||||
            writeln!(f)?;
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: BufRead> From<Lines<T>> for DigPlan {
 | 
			
		||||
    fn from(lines: Lines<T>) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            instructions: lines
 | 
			
		||||
                .map(|line| DigInstruction::from(line.unwrap().as_str()))
 | 
			
		||||
                .collect(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PROBLEM 1 solution
 | 
			
		||||
 | 
			
		||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
 | 
			
		||||
    let plan = DigPlan::from(input);
 | 
			
		||||
    println!("{:?}", plan);
 | 
			
		||||
    let mut dig = DigHole::new();
 | 
			
		||||
    dig.run_plan(&plan);
 | 
			
		||||
    println!("{}", dig);
 | 
			
		||||
    dig.tiles_map.iter().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 6 (#70c710)
 | 
			
		||||
D 5 (#0dc571)
 | 
			
		||||
L 2 (#5713f0)
 | 
			
		||||
D 2 (#d2c081)
 | 
			
		||||
R 2 (#59c680)
 | 
			
		||||
D 2 (#411b91)
 | 
			
		||||
L 5 (#8ceee2)
 | 
			
		||||
U 2 (#caa173)
 | 
			
		||||
L 1 (#1b58a2)
 | 
			
		||||
U 2 (#caa171)
 | 
			
		||||
R 2 (#7807d2)
 | 
			
		||||
U 3 (#a77fa3)
 | 
			
		||||
L 2 (#015232)
 | 
			
		||||
U 2 (#7a21e3)";
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn problem1_example() {
 | 
			
		||||
        let c = Cursor::new(EXAMPLE);
 | 
			
		||||
        assert_eq!(problem1(c.lines()), 62);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn problem2_example() {
 | 
			
		||||
        let c = Cursor::new(EXAMPLE);
 | 
			
		||||
        assert_eq!(problem2(c.lines()), 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user