day(22): add part 1
Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
parent
43c39a3ec0
commit
775ee061d5
2 changed files with 229 additions and 0 deletions
14
samples/day22.txt
Normal file
14
samples/day22.txt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
...#
|
||||||
|
.#..
|
||||||
|
#...
|
||||||
|
....
|
||||||
|
...#.......#
|
||||||
|
........#...
|
||||||
|
..#....#....
|
||||||
|
..........#.
|
||||||
|
...#....
|
||||||
|
.....#..
|
||||||
|
.#......
|
||||||
|
......#.
|
||||||
|
|
||||||
|
10R5L5R10L4R5L5
|
215
src/bin/day22.rs
Normal file
215
src/bin/day22.rs
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
use std::{collections::HashMap, hash::Hash};
|
||||||
|
|
||||||
|
use aoc_2022::*;
|
||||||
|
|
||||||
|
type Input = MonkeyMap;
|
||||||
|
type Output = isize;
|
||||||
|
|
||||||
|
enum Instruction {
|
||||||
|
Move(usize),
|
||||||
|
TurnLeft,
|
||||||
|
TurnRight,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
|
enum Orientation {
|
||||||
|
Horizontal(usize),
|
||||||
|
Vertical(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MonkeyMap {
|
||||||
|
map: Vec<Vec<char>>,
|
||||||
|
boundaries: HashMap<Orientation, (usize, usize)>,
|
||||||
|
instructions: Vec<Instruction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step_with_wrap(position: usize, diff: isize, lower: usize, upper: usize) -> usize {
|
||||||
|
let range_size = (upper - lower) as isize;
|
||||||
|
(lower as isize + (position as isize + diff - lower as isize + range_size) % range_size)
|
||||||
|
as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State<'a> {
|
||||||
|
input: &'a Input,
|
||||||
|
y: usize,
|
||||||
|
x: usize,
|
||||||
|
direction: (isize, isize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> State<'a> {
|
||||||
|
fn new(input: &'a Input) -> State<'a> {
|
||||||
|
Self {
|
||||||
|
input,
|
||||||
|
y: 0,
|
||||||
|
x: input.boundaries[&Orientation::Horizontal(0)].0,
|
||||||
|
direction: (1, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&self) -> (usize, usize) {
|
||||||
|
let h_bound = self.input.boundaries[&Orientation::Horizontal(self.y)];
|
||||||
|
let v_bound = self.input.boundaries[&Orientation::Vertical(self.x)];
|
||||||
|
|
||||||
|
let (dx, dy) = self.direction;
|
||||||
|
|
||||||
|
(
|
||||||
|
step_with_wrap(self.x, dx, h_bound.0, h_bound.1),
|
||||||
|
step_with_wrap(self.y, dy, v_bound.0, v_bound.1),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_blocked(&self) -> bool {
|
||||||
|
let (x, y) = self.next();
|
||||||
|
self.input.map[y][x] == '#'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step(&mut self, steps: usize) {
|
||||||
|
for _ in 0..steps {
|
||||||
|
if self.is_blocked() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(self.x, self.y) = self.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_left(&mut self) {
|
||||||
|
let (x, y) = self.direction;
|
||||||
|
self.direction = (y, -x);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_right(&mut self) {
|
||||||
|
let (x, y) = self.direction;
|
||||||
|
self.direction = (-y, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref DIRECTIONS: HashMap<(isize, isize), usize> =
|
||||||
|
HashMap::from([((1, 0), 0), ((0, 1), 1), ((-1, 0), 2), ((0, -1), 3)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Day22;
|
||||||
|
impl Solution<Input, Output> for Day22 {
|
||||||
|
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
|
||||||
|
let input = file_to_string(pathname);
|
||||||
|
let (map, instructions) = input.split_once("\n\n").unwrap();
|
||||||
|
|
||||||
|
let mut map: Vec<Vec<char>> = map.lines().map(|l| l.chars().collect()).collect();
|
||||||
|
let max_row_length = map.iter().map(|l| l.len()).max().unwrap();
|
||||||
|
map.iter_mut().for_each(|l| l.resize(max_row_length, ' '));
|
||||||
|
|
||||||
|
let mut boundaries = HashMap::new();
|
||||||
|
for row in 0..map.len() {
|
||||||
|
let mut first_non_empty = map[row].iter().enumerate().skip_while(|&(_, &c)| c == ' ');
|
||||||
|
let start = first_non_empty.next().unwrap().0;
|
||||||
|
|
||||||
|
let mut last_non_empty = first_non_empty.skip_while(|&(_, &c)| c != ' ');
|
||||||
|
let end = last_non_empty.next().unwrap_or((map[row].len(), &'_')).0;
|
||||||
|
|
||||||
|
boundaries.insert(Orientation::Horizontal(row), (start, end));
|
||||||
|
}
|
||||||
|
|
||||||
|
for col in 0..map[0].len() {
|
||||||
|
let mut first_non_empty = ColumnIterator::new(&map, col)
|
||||||
|
.enumerate()
|
||||||
|
.skip_while(|&(_, &c)| c == ' ');
|
||||||
|
let start = first_non_empty.next().unwrap().0;
|
||||||
|
|
||||||
|
let mut last_non_empty = first_non_empty.skip_while(|&(_, &c)| c != ' ');
|
||||||
|
let end = last_non_empty.next().unwrap_or((map.len(), &'_')).0;
|
||||||
|
|
||||||
|
boundaries.insert(Orientation::Vertical(col), (start, end));
|
||||||
|
}
|
||||||
|
|
||||||
|
let unparsed_instructions_str = instructions.trim_end();
|
||||||
|
|
||||||
|
let it = unparsed_instructions_str.chars();
|
||||||
|
let mut skip = 0;
|
||||||
|
let mut instructions = Vec::new();
|
||||||
|
while skip < unparsed_instructions_str.len() {
|
||||||
|
let steps: String = it
|
||||||
|
.clone()
|
||||||
|
.skip(skip)
|
||||||
|
.take_while(|c| c.is_digit(10))
|
||||||
|
.collect();
|
||||||
|
if !steps.is_empty() {
|
||||||
|
// found a move instruction
|
||||||
|
instructions.push(Instruction::Move(steps.parse::<usize>().unwrap()));
|
||||||
|
skip += steps.len();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match it.clone().skip(skip).next().unwrap() {
|
||||||
|
'L' => instructions.push(Instruction::TurnLeft),
|
||||||
|
'R' => instructions.push(Instruction::TurnRight),
|
||||||
|
x => panic!("Invalid turn: {}", x),
|
||||||
|
}
|
||||||
|
skip += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
MonkeyMap {
|
||||||
|
map,
|
||||||
|
boundaries,
|
||||||
|
instructions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part_1(input: &Input) -> Output {
|
||||||
|
let final_state = input
|
||||||
|
.instructions
|
||||||
|
.iter()
|
||||||
|
.fold(State::new(input), |mut state, y| {
|
||||||
|
match &y {
|
||||||
|
Instruction::Move(steps) => state.step(*steps),
|
||||||
|
Instruction::TurnLeft => state.turn_left(),
|
||||||
|
Instruction::TurnRight => state.turn_right(),
|
||||||
|
}
|
||||||
|
|
||||||
|
state
|
||||||
|
});
|
||||||
|
|
||||||
|
(1000 * (final_state.y + 1) + 4 * (final_state.x + 1) + DIRECTIONS[&final_state.direction])
|
||||||
|
.try_into()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part_2(_input: &Input) -> Output {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
// Day22::run("sample")
|
||||||
|
Day22::main()
|
||||||
|
}
|
||||||
|
|
||||||
|
test_sample!(day_22, Day22, 6032, 5031);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod day_22_extended {
|
||||||
|
#[test]
|
||||||
|
fn test_expression_positive() {
|
||||||
|
let (l, u) = (4, 9);
|
||||||
|
let d = 1;
|
||||||
|
|
||||||
|
let mut positions = vec![6];
|
||||||
|
for _ in 0..6 {
|
||||||
|
positions.push(l + (positions.last().unwrap() + d - l + (u - l)) % (u - l));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(positions, vec![6, 7, 8, 4, 5, 6, 7]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expression_negative() {
|
||||||
|
let (l, u) = (4, 9);
|
||||||
|
let d = -1;
|
||||||
|
|
||||||
|
let mut positions = vec![6];
|
||||||
|
for _ in 0..6 {
|
||||||
|
positions.push(l + (positions.last().unwrap() + d - l + (u - l)) % (u - l));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(positions, vec![6, 5, 4, 8, 7, 6, 5]);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue