day18: problem 2 solution
This commit is contained in:
parent
0a9fa8e32f
commit
3bc073f9b8
205
18/src/main.rs
205
18/src/main.rs
@ -37,6 +37,14 @@ enum Direction {
|
||||
Down,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
enum Turn {
|
||||
LeftNinety,
|
||||
RightNinety,
|
||||
OneEighty,
|
||||
None,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
const fn all() -> &'static [Self; 4] {
|
||||
&[
|
||||
@ -62,6 +70,25 @@ impl Direction {
|
||||
Direction::Down => (0, 1),
|
||||
}
|
||||
}
|
||||
fn turn_kind(&self, next_dir: Direction) -> Turn {
|
||||
if *self == next_dir {
|
||||
Turn::None
|
||||
} else if self.opposite() == next_dir {
|
||||
Turn::OneEighty
|
||||
} else {
|
||||
match self {
|
||||
Direction::Left if next_dir == Direction::Up => Turn::RightNinety,
|
||||
Direction::Left if next_dir == Direction::Down => Turn::LeftNinety,
|
||||
Direction::Right if next_dir == Direction::Up => Turn::LeftNinety,
|
||||
Direction::Right if next_dir == Direction::Down => Turn::RightNinety,
|
||||
Direction::Up if next_dir == Direction::Left => Turn::LeftNinety,
|
||||
Direction::Up if next_dir == Direction::Right => Turn::RightNinety,
|
||||
Direction::Down if next_dir == Direction::Right => Turn::LeftNinety,
|
||||
Direction::Down if next_dir == Direction::Left => Turn::RightNinety,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Direction {
|
||||
@ -100,7 +127,13 @@ impl From<&str> for DigInstruction {
|
||||
let (dir, count, color) = (
|
||||
parts.next().unwrap(),
|
||||
parts.next().unwrap(),
|
||||
parts.next().unwrap(),
|
||||
parts
|
||||
.next()
|
||||
.unwrap()
|
||||
.chars()
|
||||
.skip(2)
|
||||
.take(6)
|
||||
.collect::<String>(),
|
||||
);
|
||||
Self {
|
||||
dir: dir.into(),
|
||||
@ -110,24 +143,42 @@ impl From<&str> for DigInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
impl DigInstruction {
|
||||
fn part2_transform(&mut self) {
|
||||
let (distance_s, direction_s) = self.color.split_at(5);
|
||||
self.count = usize::from_str_radix(distance_s, 16).unwrap();
|
||||
self.dir = match direction_s {
|
||||
"0" => Direction::Right,
|
||||
"1" => Direction::Down,
|
||||
"2" => Direction::Left,
|
||||
"3" => Direction::Up,
|
||||
s => panic!("`{}` is not a valid direction code", s)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DigPlan {
|
||||
instructions: Vec<DigInstruction>,
|
||||
}
|
||||
|
||||
impl DigPlan {
|
||||
fn part2_transform(&mut self) {
|
||||
for i in &mut self.instructions {
|
||||
i.part2_transform();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -140,6 +191,12 @@ struct DigHole {
|
||||
tiles_map: HashMap<Position, DigTile>,
|
||||
x_range: Range<isize>,
|
||||
y_range: Range<isize>,
|
||||
area: u64,
|
||||
}
|
||||
|
||||
// determinant of positions p1 and p2
|
||||
fn det(p1: Position, p2: Position) -> i64 {
|
||||
((p1.0 * p2.1) - (p1.1 * p2.0)) as i64
|
||||
}
|
||||
|
||||
impl DigHole {
|
||||
@ -149,88 +206,63 @@ impl DigHole {
|
||||
tiles_map: HashMap::new(),
|
||||
x_range: 0..0,
|
||||
y_range: 0..0,
|
||||
area: 0,
|
||||
}
|
||||
}
|
||||
fn pos_offset(&self, pos: Position, offset: (isize, isize)) -> Position {
|
||||
(pos.0 + offset.0, pos.1 + offset.1)
|
||||
fn pos_offset_n(&self, pos: Position, offset: (isize, isize), n: usize) -> Position {
|
||||
(pos.0 + offset.0 * n as isize, pos.1 + offset.1 * n as isize)
|
||||
}
|
||||
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;
|
||||
let mut move_offset;
|
||||
for (idx, i) in plan.instructions.iter().enumerate() {
|
||||
let prev_instruction = if idx > 0 {
|
||||
&plan.instructions[idx - 1]
|
||||
} 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));
|
||||
&plan.instructions[plan.instructions.len() - 1]
|
||||
};
|
||||
let Some(next_instruction) = plan
|
||||
.instructions
|
||||
.get(idx + 1)
|
||||
.or(Some(&plan.instructions[0]))
|
||||
else {
|
||||
panic!()
|
||||
};
|
||||
let cur_turn = prev_instruction.dir.turn_kind(i.dir);
|
||||
let next_turn = i.dir.turn_kind(next_instruction.dir);
|
||||
// point needs to live on the 'outside' corner of the character. to
|
||||
// achieve this we need to offset the move by the following
|
||||
move_offset = match (cur_turn, next_turn) {
|
||||
(Turn::RightNinety, Turn::RightNinety) => 1,
|
||||
(Turn::RightNinety, Turn::LeftNinety) => 0,
|
||||
(Turn::LeftNinety, Turn::LeftNinety) => -1,
|
||||
(Turn::LeftNinety, Turn::RightNinety) => 0,
|
||||
t => panic!("turn {:?} not allowed here", t),
|
||||
};
|
||||
|
||||
cur_pos = self.pos_offset_n(
|
||||
cur_pos,
|
||||
i.dir.offset(),
|
||||
(i.count as isize + move_offset) as usize,
|
||||
);
|
||||
self.tiles_loop.push_back(DigTile {
|
||||
position: cur_pos,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
|
||||
// Shoelace formula
|
||||
// https://en.wikipedia.org/wiki/Shoelace_formula
|
||||
let double_area: i64 = self
|
||||
.tiles_loop
|
||||
.iter()
|
||||
.tuple_windows()
|
||||
.map(|(a, b)| det(a.position, b.position))
|
||||
.sum();
|
||||
self.area = (double_area / 2).abs() as u64;
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,16 +298,18 @@ impl<T: BufRead> From<Lines<T>> for DigPlan {
|
||||
|
||||
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
|
||||
dig.area
|
||||
}
|
||||
|
||||
// PROBLEM 2 solution
|
||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
||||
0
|
||||
let mut plan = DigPlan::from(input);
|
||||
let mut dig = DigHole::new();
|
||||
plan.part2_transform();
|
||||
dig.run_plan(&plan);
|
||||
dig.area
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -298,6 +332,17 @@ U 3 (#a77fa3)
|
||||
L 2 (#015232)
|
||||
U 2 (#7a21e3)";
|
||||
|
||||
const AREA16_SQUARE: &str = &"R 3 (#000000)
|
||||
D 3 (#000000)
|
||||
L 3 (#000000)
|
||||
U 4 (#000000";
|
||||
|
||||
#[test]
|
||||
fn area16_square() {
|
||||
let c = Cursor::new(AREA16_SQUARE);
|
||||
assert_eq!(problem1(c.lines()), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn problem1_example() {
|
||||
let c = Cursor::new(EXAMPLE);
|
||||
@ -307,6 +352,6 @@ U 2 (#7a21e3)";
|
||||
#[test]
|
||||
fn problem2_example() {
|
||||
let c = Cursor::new(EXAMPLE);
|
||||
assert_eq!(problem2(c.lines()), 0);
|
||||
assert_eq!(problem2(c.lines()), 952408144115);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user