generated from fspoettel/advent-of-code-rust
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
328 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,313 @@ | ||
use std::ops::RangeInclusive; | ||
|
||
pub fn part_one(input: &str) -> Option<u32> { | ||
let map = MonkeyMap::parse(input); | ||
let wrap_strategy = WrapStrategy::Simple { | ||
width: map.width, | ||
height: map.height, | ||
}; | ||
Some(run(map, wrap_strategy)) | ||
} | ||
|
||
pub fn part_two(input: &str) -> Option<u32> { | ||
let map = MonkeyMap::parse(input); | ||
let wraps = example_wrap_definitions(); | ||
Some(run(map, WrapStrategy::Complex(wraps))) | ||
} | ||
|
||
fn run(map: MonkeyMap, wrap_strategy: WrapStrategy) -> u32 { | ||
let mut facing = Facing::Right; | ||
let mut position = map.start_point(); | ||
|
||
for s in &map.path { | ||
match *s { | ||
Instruction::Forward(steps) => { | ||
for _ in 0..steps { | ||
if let Some((new_point, new_facing)) = | ||
map.move_towards(position, facing, &wrap_strategy) | ||
{ | ||
position = new_point; | ||
facing = new_facing; | ||
} else { | ||
break; | ||
} | ||
//map.dump(position); | ||
} | ||
} | ||
Instruction::Turn(t) => facing.turn(t), | ||
} | ||
} | ||
|
||
let row = position.y + 1; | ||
let col = position.x + 1; | ||
1000 * (row as u32) + 4 * (col as u32) + (facing as u32) | ||
} | ||
|
||
type Point = aoc::point::Point<i16>; | ||
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
#[repr(u8)] | ||
enum Facing { | ||
Right, | ||
Down, | ||
Left, | ||
Up, | ||
} | ||
|
||
impl From<u8> for Facing { | ||
fn from(v: u8) -> Self { | ||
match v { | ||
0 => Self::Right, | ||
1 => Self::Down, | ||
2 => Self::Left, | ||
3 => Self::Up, | ||
_ => panic!(), | ||
} | ||
} | ||
} | ||
|
||
impl Facing { | ||
fn turn(&mut self, turn: Turn) { | ||
*self = Facing::from(match turn { | ||
Turn::Left => (*self as u8 + 3) % 4, | ||
Turn::Right => (*self as u8 + 1) % 4, | ||
}); | ||
} | ||
|
||
fn deltas(self) -> (i16, i16) { | ||
match self { | ||
Facing::Right => (1, 0), | ||
Facing::Down => (0, 1), | ||
Facing::Left => (-1, 0), | ||
Facing::Up => (0, -1), | ||
} | ||
} | ||
} | ||
|
||
fn example_wrap_definitions() -> Vec<WrapDefinition> { | ||
let mut result = Vec::new(); | ||
|
||
result.push(WrapDefinition { | ||
x_range: 12..=12, | ||
y_range: 5..=5, | ||
facing: Facing::Right, | ||
target_point: Point { x: 14, y: 8 }, | ||
target_facing: Facing::Down, | ||
}); | ||
result.push(WrapDefinition { | ||
x_range: 10..=10, | ||
y_range: 12..=12, | ||
facing: Facing::Down, | ||
target_point: Point { x: 1, y: 7 }, | ||
target_facing: Facing::Up, | ||
}); | ||
result.push(WrapDefinition { | ||
x_range: 6..=6, | ||
y_range: 3..=3, | ||
facing: Facing::Up, | ||
target_point: Point { x: 8, y: 2 }, | ||
target_facing: Facing::Right, | ||
}); | ||
|
||
result | ||
} | ||
struct WrapDefinition { | ||
x_range: RangeInclusive<i16>, | ||
y_range: RangeInclusive<i16>, | ||
facing: Facing, | ||
target_point: Point, | ||
target_facing: Facing, | ||
} | ||
|
||
enum WrapStrategy { | ||
Simple { width: i16, height: i16 }, | ||
Complex(Vec<WrapDefinition>), | ||
} | ||
|
||
impl WrapStrategy { | ||
fn wrap(&self, mut p: Point, facing: Facing) -> (Point, Facing) { | ||
match self { | ||
&WrapStrategy::Simple { width, height } => { | ||
if p.x < 0 { | ||
p.x = width - 1; | ||
} else if p.x >= width { | ||
p.x = 0; | ||
} else if p.y < 0 { | ||
p.y = height - 1; | ||
} else if p.y >= height { | ||
p.y = 0; | ||
} else { | ||
let (dx, dy) = facing.deltas(); | ||
p = p.towards(dx, dy); | ||
} | ||
return (p, facing); | ||
} | ||
WrapStrategy::Complex(wraps) => { | ||
for wrap in wraps { | ||
if wrap.x_range.contains(&p.x) | ||
&& wrap.y_range.contains(&p.y) | ||
&& wrap.facing == facing | ||
{ | ||
return (wrap.target_point, wrap.target_facing); | ||
} | ||
} | ||
panic!("unregistered wrap at {:?}, {:?}", p, facing); | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Copy)] | ||
enum Turn { | ||
Left, | ||
Right, | ||
} | ||
|
||
#[derive(Debug)] | ||
enum Instruction { | ||
Forward(u16), | ||
Turn(Turn), | ||
} | ||
|
||
impl Instruction { | ||
fn parse_list(suffix: &str) -> Vec<Instruction> { | ||
let mut result = Vec::new(); | ||
|
||
let mut steps = 0; | ||
for c in suffix.chars() { | ||
if c.is_ascii_digit() { | ||
steps = steps * 10 + c.to_digit(10).unwrap() as u16; | ||
} else { | ||
if steps != 0 { | ||
result.push(Instruction::Forward(steps)); | ||
steps = 0; | ||
} | ||
|
||
result.push(Instruction::Turn(match c { | ||
'L' => Turn::Left, | ||
'R' => Turn::Right, | ||
_ => panic!("invalid turn '{c}'"), | ||
})); | ||
} | ||
} | ||
|
||
if steps != 0 { | ||
result.push(Instruction::Forward(steps)); | ||
} | ||
|
||
result | ||
} | ||
} | ||
|
||
struct MonkeyMap<'a> { | ||
grid: Vec<&'a [u8]>, | ||
width: i16, | ||
height: i16, | ||
path: Vec<Instruction>, | ||
} | ||
|
||
impl<'a> MonkeyMap<'a> { | ||
fn parse(s: &'a str) -> Self { | ||
let mut grid = Vec::new(); | ||
|
||
let mut width = 0; | ||
let (prefix, suffix) = s.split_once("\n\n").unwrap(); | ||
for line in prefix.lines() { | ||
width = width.max(line.as_bytes().len() as i16); | ||
grid.push(line.as_bytes()); | ||
} | ||
|
||
let height = grid.len() as i16; | ||
|
||
MonkeyMap { | ||
grid, | ||
width, | ||
height, | ||
path: Instruction::parse_list(suffix.trim_end()), | ||
} | ||
} | ||
|
||
fn start_point(&self) -> Point { | ||
Point { | ||
x: self.grid[0].iter().position(|&c| c == b'.').unwrap() as i16, | ||
y: 0, | ||
} | ||
} | ||
|
||
fn move_towards( | ||
&self, | ||
mut p: Point, | ||
mut facing: Facing, | ||
wrap_strategy: &WrapStrategy, | ||
) -> Option<(Point, Facing)> { | ||
let (dx, dy) = facing.deltas(); | ||
|
||
p = p.towards(dx, dy); | ||
|
||
while self.get(p) == b' ' { | ||
(p, facing) = wrap_strategy.wrap(p, facing); | ||
} | ||
|
||
if self.get(p) == b'.' { | ||
Some((p, facing)) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
fn get(&self, p: Point) -> u8 { | ||
if p.y >= 0 && p.y < self.grid.len() as i16 && p.x >= 0 { | ||
let row = self.grid[p.y as usize]; | ||
if p.x < row.len() as i16 { | ||
return row[p.x as usize]; | ||
} | ||
} | ||
b' ' | ||
} | ||
|
||
fn dump(&self, p: Point) { | ||
for y in 0..self.height { | ||
for x in 0..self.width { | ||
let pt = Point { x, y }; | ||
if pt == p { | ||
print!("@"); | ||
} else { | ||
let v = self.get(pt); | ||
if v == b'.' { | ||
print!("."); | ||
} else if v == b' ' { | ||
print!(" "); | ||
} else { | ||
print!("#"); | ||
} | ||
} | ||
} | ||
println!(); | ||
} | ||
println!(); | ||
println!(); | ||
} | ||
} | ||
|
||
fn main() { | ||
let input = &aoc::read_file("inputs", 22); | ||
aoc::solve!(1, part_one, input); | ||
aoc::solve!(2, part_two, input); | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_part_one() { | ||
let input = aoc::read_file("examples", 22); | ||
assert_eq!(part_one(&input), Some(6032)); | ||
} | ||
|
||
#[test] | ||
fn test_part_two() { | ||
let input = aoc::read_file("examples", 22); | ||
assert_eq!(part_two(&input), Some(5031)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
...# | ||
.#.. | ||
#... | ||
.... | ||
...#.......# | ||
........#... | ||
..#....#.... | ||
..........#. | ||
...#.... | ||
.....#.. | ||
.#...... | ||
......#. | ||
|
||
10R5L5R10L4R5L5 |