Skip to content

Commit

Permalink
feat: Day 23
Browse files Browse the repository at this point in the history
  • Loading branch information
rgoulais committed Dec 23, 2023
1 parent 68d10ed commit 5cd81e5
Show file tree
Hide file tree
Showing 7 changed files with 312 additions and 11 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "advent_of_code"
version = "0.10.0"
authors = ["Felix Spöttel <1682504+fspoettel@users.noreply.github.com>"]
authors = ["Raphael G. <25445272+rgoulais@users.noreply.github.com>"]
edition = "2021"
default-run = "advent_of_code"
publish = false
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
| [Day 20](./src/bin/20.rs) | `2.6ms` | `10.4ms` |
| [Day 21](./src/bin/21.rs) | `140.5ms` | `142.3ms` |
| [Day 22](./src/bin/22.rs) | `174.0ms` | `187.1ms` |
| [Day 23](./src/bin/23.rs) | `1.0ms` | `12.2s` |

**Total: 812.56ms**
**Total: 13013.56ms**
<!--- benchmarking table --->

---
Expand Down
23 changes: 23 additions & 0 deletions data/examples/23.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#.#####################
#.......#########...###
#######.#########.#.###
###.....#.>.>.###.#.###
###v#####.#v#.###.#.###
###.>...#.#.#.....#...#
###v###.#.#.#########.#
###...#.#.#.......#...#
#####.#.#.#######.#.###
#.....#.#.#.......#...#
#.#####.#.#.#########v#
#.#...#...#...###...>.#
#.#.#v#######v###.###v#
#...#.>.#...>.>.#.###.#
#####v#.#.###v#.#.###.#
#.....#...#...#.#.#...#
#.#########.###.#.#.###
#...###...#...#...#.###
###.###.#.###v#####v###
#...#...#.#.>.>.#.>.###
#.###.###.#.###.#.#v###
#.....###...###...#...#
#####################.#
6 changes: 2 additions & 4 deletions src/bin/22.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
advent_of_code::solution!(22);

use std::collections::{HashMap, HashSet, VecDeque};
use crate::utils::Coord;

mod utils;
use advent_of_code::Coord;

#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
struct Brique {
Expand All @@ -16,7 +14,7 @@ struct Mur {
briques: HashMap<usize, Brique>,
briques_en_dessous: HashMap<usize, Vec<usize>>,
briques_au_dessus: HashMap<usize, Vec<usize>>,
max_z: usize,
max_z: isize,
}

impl Mur {
Expand Down
228 changes: 228 additions & 0 deletions src/bin/23.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
advent_of_code::solution!(23);

use std::cmp::Ordering;
use std::collections::{BinaryHeap, VecDeque};
use advent_of_code::{Coord, Direction};

#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
struct Path {
start: Coord,
end: Coord,
distance: usize,
}


struct Maze {
maze: Vec<Vec<char>>,
pathes: Vec<Path>,
slope: bool,
}

const PATHS: char = '.';
const WALL: char = '#';
const SLOPE_NORTH: char = '^';
const SLOPE_SOUTH: char = 'v';
const SLOPE_WEST: char = '<';
const SLOPE_EAST: char = '>';


impl Maze {
pub fn new(slope: bool) -> Self {
Self {
maze: Vec::new(),
pathes: Vec::new(),
slope,
}
}

pub fn read_input(&mut self, input: &str) {
for row in input.lines() {
self.maze.push(row.chars().collect());
}
for i in 0..self.maze.len() {
if self.maze[0][i] == PATHS {
self.find_pathes(Coord(0, i as isize));
}
}
}

pub fn find_pathes(&mut self, start: Coord) {
let mut start_positions = VecDeque::from([start]);
let mut handled_positions = Vec::new();
while let Some(depart) = start_positions.pop_front() {
if handled_positions.contains(&depart) {
continue;
}
handled_positions.push(depart);
for direction in Direction::get_all() {
if let Some(path) = self.follow_path(depart, direction) {
self.pathes.push(path);
if path.end.0 == self.maze.len() as isize - 1 {
continue;
}
start_positions.push_back(path.end);
}
}
}
}

pub fn follow_path(&mut self, start: Coord, direction: Direction) -> Option<Path> {
let mut current_position = start.clone();
let mut current_direction = direction;
let mut distance = 0;
while self.is_valid_plot(current_position, current_direction) {
current_position = current_position.go(current_direction);
distance += 1;
let mut possibilities = Vec::new();
for next_direction in Direction::get_all() {
if next_direction != current_direction.get_opposite() && self.is_valid_plot(current_position, next_direction) {
possibilities.push(next_direction);
}
}
if possibilities.len() == 0 {
return None;
} else if possibilities.len() == 1 {
let next_position = current_position.go(possibilities[0]);
if next_position.0 == self.maze.len() as isize - 1 {
return Some(Path {
start,
end: next_position,
distance: distance + 1,
});
}
current_direction = possibilities[0];
} else {
return Some(Path {
start,
end: current_position,
distance,
});
}
}
None
}

pub fn is_valid_plot(&self, origin: Coord, direction: Direction) -> bool {
let coord = origin.go(direction);
if coord.0 < 0 || coord.1 < 0 {
return false;
}
if coord.0 >= self.maze.len() as isize || coord.1 >= self.maze[0].len() as isize {
return false;
}
let c = self.maze[coord.0 as usize][coord.1 as usize];
if c == WALL {
return false;
}
if !self.slope {
return true;
}
let o = self.maze[origin.0 as usize][origin.1 as usize];
match o {
SLOPE_NORTH => { if direction != Direction::Up { return false; } },
SLOPE_SOUTH => { if direction != Direction::Down { return false; } },
SLOPE_WEST => { if direction != Direction::Left { return false; } },
SLOPE_EAST => { if direction != Direction::Right { return false; } },
_ => {},
}
match direction {
Direction::Up => c != SLOPE_SOUTH,
Direction::Down => c != SLOPE_NORTH,
Direction::Left => c != SLOPE_EAST,
Direction::Right => c != SLOPE_WEST,
}
}

pub fn solve_part1(&self) -> usize {
let mut max_distance = 0;
let mut heap: BinaryHeap<Chemin> = BinaryHeap::new();
heap.push(Chemin::new(self.pathes[0]));

while let Some(chemin) = heap.pop() {
for path in self.pathes.iter().filter(|&x| x.start == chemin.end) {
let mut new_chemin = chemin.clone();
if !new_chemin.add_path(*path) {
continue;
}
if new_chemin.end.0 == self.maze.len() as isize - 1 {
max_distance = std::cmp::max(max_distance, new_chemin.distance);
continue;
}
heap.push(new_chemin);
}
}
max_distance
}
}

#[derive( Clone, Eq, PartialEq)]
struct Chemin {
pathes: Vec<Path>,
distance: usize,
end: Coord,
}

impl Chemin {
fn new(path: Path) -> Self {
Self {
pathes: Vec::from([path]),
distance: path.distance,
end: path.end,
}
}

fn add_path(&mut self, path: Path) -> bool {
if self.pathes.iter().filter(|&x| x.end == path.end || x.start == path.end ).count() > 0 {
return false;
}
self.pathes.push(path);
self.distance += path.distance;
self.end = path.end;
true
}

}

impl Ord for Chemin {
fn cmp(&self, other: &Self) -> Ordering {
self.distance.cmp(&other.distance)
}
}

impl PartialOrd for Chemin {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}


pub fn part_one(input: &str) -> Option<usize> {
let mut maze = Maze::new(true);
maze.read_input(input);
Some(maze.solve_part1())
}

pub fn part_two(input: &str) -> Option<usize> {
let mut maze = Maze::new(false);
maze.read_input(input);
// low 5538
Some(maze.solve_part1())
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_part_one() {
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(94));
}

#[test]
fn test_part_two() {
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(154));
}

}
5 changes: 0 additions & 5 deletions src/bin/utils.rs

This file was deleted.

56 changes: 56 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,57 @@
pub mod template;



#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct Coord (pub isize, pub isize);

impl std::ops::Add for Coord {
type Output = Self;

fn add(self, rhs: Self) -> Self::Output {
Coord(self.0 + rhs.0, self.1 + rhs.1)
}
}


impl Coord {
pub fn go(&self, direction: Direction) -> Self {
match direction {
Direction::Up => Coord(self.0 - 1, self.1),
Direction::Down => Coord(self.0 + 1, self.1),
Direction::Left => Coord(self.0, self.1 - 1),
Direction::Right => Coord(self.0, self.1 + 1),
}
}

pub fn get_four_directions(&self) -> Vec<Self> {
vec![
self.go(Direction::Up),
self.go(Direction::Down),
self.go(Direction::Left),
self.go(Direction::Right),
]
}
}

#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum Direction {
Up,
Down,
Left,
Right,
}

impl Direction {
pub fn get_opposite(&self) -> Self {
match self {
Direction::Up => Direction::Down,
Direction::Down => Direction::Up,
Direction::Left => Direction::Right,
Direction::Right => Direction::Left,
}
}
pub fn get_all() -> Vec<Self> {
vec![Direction::Up, Direction::Down, Direction::Left, Direction::Right]
}
}

0 comments on commit 5cd81e5

Please sign in to comment.