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