Skip to content

Commit

Permalink
Add solution for day 22
Browse files Browse the repository at this point in the history
  • Loading branch information
komu committed Nov 15, 2023
1 parent b54ef73 commit fc595ce
Show file tree
Hide file tree
Showing 3 changed files with 328 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ a total budget of 100 milliseconds.
| [19](https://adventofcode.com/2022/day/19) | [19.rs](src/bin/19.rs) | 2.73s | 16.44s | 😔 |
| [20](https://adventofcode.com/2022/day/20) | [20.rs](src/bin/20.rs) | 6.72ms | 78.69ms | 😔 |
| [21](https://adventofcode.com/2022/day/21) | [21.rs](src/bin/21.rs) | 325.30µs | 235.40µs | - |
| [22](https://adventofcode.com/2022/day/22) | [22.rs](src/bin/22.rs) | 139.31µs | - | - |

(Totally unscientific numbers from a single run, will improve these in the future.)

Expand Down
313 changes: 313 additions & 0 deletions src/bin/22.rs
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));
}
}
14 changes: 14 additions & 0 deletions src/examples/22.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
...#
.#..
#...
....
...#.......#
........#...
..#....#....
..........#.
...#....
.....#..
.#......
......#.

10R5L5R10L4R5L5

0 comments on commit fc595ce

Please sign in to comment.