generated from janezicmatej/aoc-template
Compare commits
3 Commits
0000030011
...
0000033086
Author | SHA1 | Date |
---|---|---|
Matej Janezic | 0000033086 | |
Matej Janezic | 0000032032 | |
Matej Janezic | 00000310ba |
|
@ -0,0 +1,10 @@
|
||||||
|
.|...\....
|
||||||
|
|.-.\.....
|
||||||
|
.....|-...
|
||||||
|
........|.
|
||||||
|
..........
|
||||||
|
.........\
|
||||||
|
..../.\\..
|
||||||
|
.-.-/..|..
|
||||||
|
.|....-|.\
|
||||||
|
..//.|....
|
|
@ -0,0 +1,13 @@
|
||||||
|
2413432311323
|
||||||
|
3215453535623
|
||||||
|
3255245654254
|
||||||
|
3446585845452
|
||||||
|
4546657867536
|
||||||
|
1438598798454
|
||||||
|
4457876987766
|
||||||
|
3637877979653
|
||||||
|
4654967986887
|
||||||
|
4564679986453
|
||||||
|
1224686865563
|
||||||
|
2546548887735
|
||||||
|
4322674655533
|
|
@ -24,7 +24,7 @@ fn swap<T: Copy>(floor: &mut [Vec<T>], from: (usize, usize), to: (usize, usize))
|
||||||
floor[to.0][to.1] = a;
|
floor[to.0][to.1] = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tilt(floor: &mut Vec<Vec<char>>, tilt: Tilt) {
|
fn tilt(floor: &mut [Vec<char>], tilt: Tilt) {
|
||||||
let (inner, outer) = match tilt {
|
let (inner, outer) = match tilt {
|
||||||
Tilt::North | Tilt::South => (floor[0].len(), floor.len()),
|
Tilt::North | Tilt::South => (floor[0].len(), floor.len()),
|
||||||
Tilt::West | Tilt::East => (floor.len(), floor[0].len()),
|
Tilt::West | Tilt::East => (floor.len(), floor[0].len()),
|
||||||
|
@ -54,7 +54,7 @@ fn tilt(floor: &mut Vec<Vec<char>>, tilt: Tilt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tilt_cycle(floor: &mut Vec<Vec<char>>) {
|
fn tilt_cycle(floor: &mut [Vec<char>]) {
|
||||||
use Tilt::*;
|
use Tilt::*;
|
||||||
tilt(floor, North);
|
tilt(floor, North);
|
||||||
tilt(floor, West);
|
tilt(floor, West);
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
|
enum Direction {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
fn next(&self, (y, x): (usize, usize)) -> (usize, usize) {
|
||||||
|
use Direction::*;
|
||||||
|
match self {
|
||||||
|
Up => (y.checked_sub(1).unwrap_or(usize::MAX), x),
|
||||||
|
Down => (y + 1, x),
|
||||||
|
Left => (y, x.checked_sub(1).unwrap_or(usize::MAX)),
|
||||||
|
Right => (y, x + 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum Mirror {
|
||||||
|
Vertical,
|
||||||
|
Horizontal,
|
||||||
|
EvenSymmetric,
|
||||||
|
OddSymmetric,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ParseMirrorError;
|
||||||
|
|
||||||
|
impl TryFrom<u8> for Mirror {
|
||||||
|
type Error = ParseMirrorError;
|
||||||
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
|
Ok(match value {
|
||||||
|
b'|' => Self::Vertical,
|
||||||
|
b'-' => Self::Horizontal,
|
||||||
|
b'\\' => Self::EvenSymmetric,
|
||||||
|
b'/' => Self::OddSymmetric,
|
||||||
|
_ => return Err(ParseMirrorError),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mirror {
|
||||||
|
fn bounce(&self, d: &Direction) -> (Direction, Option<Direction>) {
|
||||||
|
use Direction::*;
|
||||||
|
use Mirror::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Vertical => match d {
|
||||||
|
Up | Down => (*d, None),
|
||||||
|
Left | Right => (Up, Some(Down)),
|
||||||
|
},
|
||||||
|
Horizontal => match d {
|
||||||
|
Up | Down => (Left, Some(Right)),
|
||||||
|
Left | Right => (*d, None),
|
||||||
|
},
|
||||||
|
EvenSymmetric => (
|
||||||
|
match d {
|
||||||
|
Up => Left,
|
||||||
|
Down => Right,
|
||||||
|
Left => Up,
|
||||||
|
Right => Down,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
OddSymmetric => (
|
||||||
|
match d {
|
||||||
|
Up => Right,
|
||||||
|
Down => Left,
|
||||||
|
Left => Down,
|
||||||
|
Right => Up,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve_with_start(layout: &[Vec<Option<Mirror>>], start: (usize, usize, Direction)) -> usize {
|
||||||
|
let mut queue = vec![start];
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
|
||||||
|
while let Some((y, x, d)) = queue.pop() {
|
||||||
|
let point = layout.get(y).and_then(|row| row.get(x));
|
||||||
|
|
||||||
|
if point.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !visited.insert((y, x, d)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Some(m)) = point {
|
||||||
|
let (new_d, opt_new_d) = m.bounce(&d);
|
||||||
|
let (ny, nx) = new_d.next((y, x));
|
||||||
|
queue.push((ny, nx, new_d));
|
||||||
|
|
||||||
|
if let Some(od) = opt_new_d {
|
||||||
|
let (ony, onx) = od.next((y, x));
|
||||||
|
queue.push((ony, onx, od));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let (ny, nx) = d.next((y, x));
|
||||||
|
queue.push((ny, nx, d));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet::<(usize, usize)>::from_iter(visited.into_iter().map(|(y, x, _)| (y, x))).len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
let layout: Vec<Vec<_>> = input
|
||||||
|
.lines()
|
||||||
|
.map(|x| x.as_bytes().iter().map(|&x| x.try_into().ok()).collect())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Some(solve_with_start(&layout, (0, 0, Direction::Right)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
let layout: Vec<Vec<_>> = input
|
||||||
|
.lines()
|
||||||
|
.map(|x| x.as_bytes().iter().map(|&x| x.try_into().ok()).collect())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let h = layout.len();
|
||||||
|
let w = layout[0].len();
|
||||||
|
|
||||||
|
let mut scores = Vec::with_capacity(2 * (h + w) + 1);
|
||||||
|
|
||||||
|
for hh in 0..h {
|
||||||
|
let left = solve_with_start(&layout, (hh, 0, Direction::Right));
|
||||||
|
let right = solve_with_start(&layout, (hh, w - 1, Direction::Left));
|
||||||
|
|
||||||
|
scores.push(left);
|
||||||
|
scores.push(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
for ww in 0..w {
|
||||||
|
let downward = solve_with_start(&layout, (0, ww, Direction::Down));
|
||||||
|
let upward = solve_with_start(&layout, (h - 1, ww, Direction::Up));
|
||||||
|
|
||||||
|
scores.push(downward);
|
||||||
|
scores.push(upward);
|
||||||
|
}
|
||||||
|
|
||||||
|
scores.into_iter().max()
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(16);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 16)),
|
||||||
|
Some(46)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 16)),
|
||||||
|
Some(51)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
use std::{
|
||||||
|
collections::{BinaryHeap, HashMap},
|
||||||
|
ops::Neg,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
enum Direction {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
const ALL: [Direction; 4] = [
|
||||||
|
Direction::Up,
|
||||||
|
Direction::Down,
|
||||||
|
Direction::Left,
|
||||||
|
Direction::Right,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Neg for Direction {
|
||||||
|
type Output = Self;
|
||||||
|
fn neg(self) -> Self::Output {
|
||||||
|
use Direction::*;
|
||||||
|
match self {
|
||||||
|
Up => Down,
|
||||||
|
Down => Up,
|
||||||
|
Left => Right,
|
||||||
|
Right => Left,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
struct State {
|
||||||
|
heat: usize,
|
||||||
|
position: (usize, usize),
|
||||||
|
direction: Direction,
|
||||||
|
steps: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for State {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for State {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
other
|
||||||
|
.heat
|
||||||
|
.cmp(&self.heat)
|
||||||
|
.then_with(|| self.position.cmp(&other.position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_path(
|
||||||
|
grid: &[Vec<usize>],
|
||||||
|
start: (usize, usize),
|
||||||
|
target: (usize, usize),
|
||||||
|
min_steps: usize,
|
||||||
|
max_steps: usize,
|
||||||
|
) -> Option<usize> {
|
||||||
|
let valid_indexing = |y: usize, x: usize| grid.get(y).and_then(|row| row.get(x)).is_some();
|
||||||
|
|
||||||
|
let mut heap = BinaryHeap::new();
|
||||||
|
heap.push(State {
|
||||||
|
heat: 0,
|
||||||
|
position: (0, 0),
|
||||||
|
direction: Direction::Right,
|
||||||
|
steps: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut heatmap = HashMap::new();
|
||||||
|
heatmap.insert((start, Direction::Down, 0), 0);
|
||||||
|
heatmap.insert((start, Direction::Up, 0), 0);
|
||||||
|
|
||||||
|
while let Some(State {
|
||||||
|
heat,
|
||||||
|
position,
|
||||||
|
direction,
|
||||||
|
steps,
|
||||||
|
}) = heap.pop()
|
||||||
|
{
|
||||||
|
if position == target {
|
||||||
|
return Some(heat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if *heatmap
|
||||||
|
.get(&(position, direction, steps))
|
||||||
|
.unwrap_or(&usize::MAX)
|
||||||
|
< heat
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (y, x) = position;
|
||||||
|
for d in Direction::ALL.iter().filter(|&x| *x != -direction) {
|
||||||
|
if steps < min_steps && *d != direction {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (ny, nx) = match d {
|
||||||
|
Direction::Up => (y.wrapping_sub(1), x),
|
||||||
|
Direction::Down => (y + 1, x),
|
||||||
|
Direction::Left => (y, x.wrapping_sub(1)),
|
||||||
|
Direction::Right => (y, x + 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !valid_indexing(ny, nx) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_steps = if *d == direction { steps + 1 } else { 1 };
|
||||||
|
|
||||||
|
if new_steps > max_steps {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = State {
|
||||||
|
heat: heat + grid[ny][nx],
|
||||||
|
position: (ny, nx),
|
||||||
|
direction: *d,
|
||||||
|
steps: new_steps,
|
||||||
|
};
|
||||||
|
|
||||||
|
if *heatmap
|
||||||
|
.get(&((ny, nx), *d, new_steps))
|
||||||
|
.unwrap_or(&usize::MAX)
|
||||||
|
> state.heat
|
||||||
|
{
|
||||||
|
heatmap.insert(((ny, nx), *d, new_steps), state.heat);
|
||||||
|
heap.push(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
let grid: Vec<Vec<_>> = input
|
||||||
|
.lines()
|
||||||
|
.map(|x| {
|
||||||
|
x.chars()
|
||||||
|
.filter_map(|x| x.to_digit(10).map(|x| x as usize))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let target = (grid.len() - 1, grid[0].len() - 1);
|
||||||
|
|
||||||
|
find_path(&grid, (0, 0), target, 0, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
let grid: Vec<Vec<_>> = input
|
||||||
|
.lines()
|
||||||
|
.map(|x| {
|
||||||
|
x.chars()
|
||||||
|
.filter_map(|x| x.to_digit(10).map(|x| x as usize))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let target = (grid.len() - 1, grid[0].len() - 1);
|
||||||
|
|
||||||
|
find_path(&grid, (0, 0), target, 4, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(17);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(
|
||||||
|
part_one(&aoc::template::read_file("examples", 17)),
|
||||||
|
Some(102)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(
|
||||||
|
part_two(&aoc::template::read_file("examples", 17)),
|
||||||
|
Some(94)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue