Compare commits
No commits in common. "263f2389a9dfced4d4ab76f3d42dbce53cfa77b1" and "8adfd753cbb5532dcac1465500f82c6197d09f32" have entirely different histories.
263f2389a9
...
8adfd753cb
34
README.md
34
README.md
@ -1,34 +0,0 @@
|
||||
# Advent of Code 2024
|
||||
|
||||
## Benchmarks
|
||||
|
||||
I am not always optimizing for speed, but here are some benchmarks anyway.
|
||||
|
||||
_Measured with `hyperfine -N --warmup 20 <binary>`. Results include both parts.
|
||||
Input is included in the binary during compilation, but parsed during runtime._
|
||||
|
||||
CPU: Ryzen 5 3600 (6c/12t)
|
||||
|
||||
| Day | Mean [ms] | Min [ms] | Max [ms] |
|
||||
| :------ | ----------: | -------: | -------: |
|
||||
| `day1` | 0.6 ± 0.0 | 0.6 | 1.6 |
|
||||
| `day2` | 1.2 ± 0.0 | 1.2 | 1.5 |
|
||||
| `day3` | 1.5 ± 0.0 | 1.5 | 1.8 |
|
||||
| `day4` | 0.8 ± 0.0 | 0.8 | 1.1 |
|
||||
| `day5` | 1.2 ± 0.0 | 1.1 | 2.0 |
|
||||
| `day6` | 59.0 ± 1.3 | 57.6 | 63.4 |
|
||||
| `day8` | 0.7 ± 0.0 | 0.7 | 0.9 |
|
||||
| `day9` | 474.1 ± 0.6 | 473.1 | 475.4 |
|
||||
| `day10` | 1.6 ± 0.0 | 1.6 | 1.9 |
|
||||
| `day11` | 13.9 ± 0.4 | 13.4 | 15.1 |
|
||||
| `day12` | 53.6 ± 0.4 | 52.6 | 54.8 |
|
||||
| `day13` | 1.2 ± 0.0 | 1.2 | 1.5 |
|
||||
| `day14` | 55.0 ± 0.3 | 54.8 | 57.0 |
|
||||
| `day15` | 8.0 ± 0.2 | 7.9 | 8.7 |
|
||||
| `day16` | 62.7 ± 0.1 | 62.4 | 63.1 |
|
||||
| `day17` | 0.7 ± 0.0 | 0.7 | 1.9 |
|
||||
| `day18` | 106.4 ± 0.3 | 106.0 | 107.7 |
|
||||
| `day19` | 51.9 ± 0.4 | 51.6 | 53.7 |
|
||||
| `day20` | 5.4 ± 0.2 | 5.0 | 6.4 |
|
||||
| `day21` | 1.3 ± 0.0 | 1.2 | 1.5 |
|
||||
| `day22` | 302.3 ± 0.8 | 301.3 | 304.1 |
|
||||
16
day14/Cargo.lock
generated
16
day14/Cargo.lock
generated
@ -16,31 +16,15 @@ name = "day14"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"itertools",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.29.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
|
||||
@ -5,5 +5,4 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
glam = "0.29.2"
|
||||
itertools = "0.13.0"
|
||||
regex = "1.11.1"
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
use glam::IVec2;
|
||||
use itertools::Itertools;
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
@ -77,13 +76,12 @@ fn print_map(robots: &[Robot], dimensions: &IVec2) -> String {
|
||||
}
|
||||
|
||||
fn has_tree(robots: &[Robot]) -> bool {
|
||||
robots.len() == robots.iter().map(|r| r.position).unique().count()
|
||||
// let check: Vec<_> = (0..16).map(|i| IVec2::new(i, 0)).collect();
|
||||
// robots.iter().any(|p| {
|
||||
// check
|
||||
// .iter()
|
||||
// .all(|c| robots.iter().any(|p2| p2.position == p.position + c))
|
||||
// })
|
||||
let check: Vec<_> = (0..16).map(|i| IVec2::new(i, 0)).collect();
|
||||
robots.iter().any(|p| {
|
||||
check
|
||||
.iter()
|
||||
.all(|c| robots.iter().any(|p2| p2.position == p.position + c))
|
||||
})
|
||||
}
|
||||
|
||||
fn part2(robots: &mut [Robot], dimensions: &IVec2) -> i32 {
|
||||
|
||||
16
day15/Cargo.lock
generated
16
day15/Cargo.lock
generated
@ -1,16 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "day15"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"glam",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.29.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
|
||||
@ -1,7 +0,0 @@
|
||||
[package]
|
||||
name = "day15"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
glam = "0.29.2"
|
||||
@ -1,219 +0,0 @@
|
||||
use std::{collections::VecDeque, fmt::Display};
|
||||
|
||||
use glam::IVec2;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
enum Tile {
|
||||
Wall,
|
||||
Robot,
|
||||
Box,
|
||||
BoxL,
|
||||
BoxR,
|
||||
Empty,
|
||||
}
|
||||
use Tile::*;
|
||||
|
||||
impl Display for Tile {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Wall => write!(f, "#"),
|
||||
Robot => write!(f, "@"),
|
||||
Box => write!(f, "O"),
|
||||
BoxL => write!(f, "["),
|
||||
BoxR => write!(f, "]"),
|
||||
Empty => write!(f, "."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
use Direction::*;
|
||||
|
||||
impl Direction {
|
||||
fn as_vec(&self) -> IVec2 {
|
||||
match self {
|
||||
Up => IVec2::new(-1, 0),
|
||||
Down => IVec2::new(1, 0),
|
||||
Left => IVec2::new(0, -1),
|
||||
Right => IVec2::new(0, 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct State {
|
||||
map: Vec<Vec<Tile>>,
|
||||
directions: VecDeque<Direction>,
|
||||
robot_pos: IVec2,
|
||||
}
|
||||
|
||||
impl Display for State {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
self.map
|
||||
.iter()
|
||||
.map(|l| l.iter().map(|t| t.to_string()).collect::<String>() + "\n")
|
||||
.collect::<String>()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn widen(&mut self) {
|
||||
self.map = self
|
||||
.map
|
||||
.iter()
|
||||
.map(|l| {
|
||||
l.iter()
|
||||
.flat_map(|t| match t {
|
||||
Empty => [Empty, Empty],
|
||||
Wall => [Wall, Wall],
|
||||
Box => [BoxL, BoxR],
|
||||
Robot => [Robot, Empty],
|
||||
e => [*e, *e],
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
self.robot_pos *= IVec2::new(1, 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(input: &str) -> State {
|
||||
let (map, directions) = input.split_once("\n\n").unwrap();
|
||||
let mut robot_pos = IVec2::new(0, 0);
|
||||
State {
|
||||
map: map
|
||||
.lines()
|
||||
.enumerate()
|
||||
.map(|(l_idx, l)| {
|
||||
l.chars()
|
||||
.enumerate()
|
||||
.map(|(c_idx, c)| match c {
|
||||
'#' => Wall,
|
||||
'@' => {
|
||||
robot_pos.x = l_idx as i32;
|
||||
robot_pos.y = c_idx as i32;
|
||||
Robot
|
||||
}
|
||||
'O' => Box,
|
||||
_ => Empty,
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect(),
|
||||
directions: directions
|
||||
.replace("\n", "")
|
||||
.chars()
|
||||
.map(|c| match c {
|
||||
'<' => Left,
|
||||
'^' => Up,
|
||||
'>' => Right,
|
||||
'v' => Down,
|
||||
_ => panic!(),
|
||||
})
|
||||
.collect(),
|
||||
robot_pos,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_tile<'a>(map: &'a mut [Vec<Tile>], idx: &IVec2) -> &'a mut Tile {
|
||||
&mut map[idx.x as usize][idx.y as usize]
|
||||
}
|
||||
|
||||
fn can_be_moved(state: &State, pos: &IVec2, direction: &IVec2, to_move: &mut Vec<IVec2>) -> bool {
|
||||
let tile_in_front = state.map[(pos + direction).x as usize][(pos + direction).y as usize];
|
||||
if tile_in_front == Wall {
|
||||
return false;
|
||||
}
|
||||
if tile_in_front == Empty {
|
||||
return true;
|
||||
}
|
||||
|
||||
let to_check_dir = match tile_in_front {
|
||||
BoxL => Right.as_vec(),
|
||||
BoxR => Left.as_vec(),
|
||||
_ => IVec2::new(0, 0),
|
||||
};
|
||||
|
||||
if direction == &Up.as_vec() || direction == &Down.as_vec() {
|
||||
to_move.push(pos + direction);
|
||||
to_move.push(pos + direction + to_check_dir);
|
||||
return can_be_moved(state, &(pos + direction), direction, to_move)
|
||||
&& can_be_moved(state, &(pos + direction + to_check_dir), direction, to_move);
|
||||
}
|
||||
to_move.push(pos + direction);
|
||||
can_be_moved(state, &(pos + direction), direction, to_move)
|
||||
}
|
||||
|
||||
fn do_move(state: &mut State) {
|
||||
let dir = state.directions.pop_front().unwrap().as_vec();
|
||||
let pos_in_front = state.robot_pos + dir;
|
||||
|
||||
if get_tile(&mut state.map, &pos_in_front) == &Wall {
|
||||
return;
|
||||
}
|
||||
|
||||
if get_tile(&mut state.map, &pos_in_front) == &Empty {
|
||||
*get_tile(&mut state.map, &pos_in_front) = Robot;
|
||||
*get_tile(&mut state.map, &state.robot_pos) = Empty;
|
||||
state.robot_pos = pos_in_front;
|
||||
return;
|
||||
}
|
||||
|
||||
let mut to_move = Vec::new();
|
||||
if can_be_moved(state, &state.robot_pos, &dir, &mut to_move) {
|
||||
let mut new_map = state.map.clone();
|
||||
for pos in &to_move {
|
||||
*get_tile(&mut new_map, pos) = Empty;
|
||||
}
|
||||
|
||||
for pos in &to_move {
|
||||
*get_tile(&mut new_map, &(pos + dir)) = *get_tile(&mut state.map, pos);
|
||||
}
|
||||
state.map = new_map;
|
||||
*get_tile(&mut state.map, &pos_in_front) = Robot;
|
||||
*get_tile(&mut state.map, &state.robot_pos) = Empty;
|
||||
state.robot_pos = pos_in_front;
|
||||
}
|
||||
}
|
||||
|
||||
fn get_gps_sum(state: &State) -> usize {
|
||||
state
|
||||
.map
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(l_idx, l)| {
|
||||
l.iter().enumerate().map(move |(t_idx, t)| {
|
||||
if t == &Box || t == &BoxL {
|
||||
100 * l_idx + t_idx
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn part12(state: &mut State) -> usize {
|
||||
while !state.directions.is_empty() {
|
||||
do_move(state);
|
||||
}
|
||||
get_gps_sum(state)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut input = parse(include_str!("../input.txt"));
|
||||
println!("{}", part12(&mut input.clone()));
|
||||
input.widen();
|
||||
println!("{}", part12(&mut input));
|
||||
}
|
||||
126
day16/Cargo.lock
generated
126
day16/Cargo.lock
generated
@ -1,126 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "day16"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"glam",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.29.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.168"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
@ -1,8 +0,0 @@
|
||||
[package]
|
||||
name = "day16"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
ahash = "0.8.11"
|
||||
glam = "0.29.2"
|
||||
@ -1,235 +0,0 @@
|
||||
use std::{cmp::Reverse, collections::BinaryHeap};
|
||||
|
||||
use ahash::{AHashMap, AHashSet};
|
||||
use glam::IVec2;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
enum Tile {
|
||||
Empty,
|
||||
Wall,
|
||||
Start,
|
||||
End,
|
||||
}
|
||||
use Tile::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
enum Direction {
|
||||
North,
|
||||
South,
|
||||
West,
|
||||
East,
|
||||
}
|
||||
use Direction::*;
|
||||
|
||||
impl Direction {
|
||||
fn as_vec(&self) -> IVec2 {
|
||||
match self {
|
||||
North => IVec2::new(-1, 0),
|
||||
South => IVec2::new(1, 0),
|
||||
West => IVec2::new(0, -1),
|
||||
East => IVec2::new(0, 1),
|
||||
}
|
||||
}
|
||||
|
||||
fn turn_left(&self) -> Self {
|
||||
match self {
|
||||
North => West,
|
||||
South => East,
|
||||
West => South,
|
||||
East => North,
|
||||
}
|
||||
}
|
||||
|
||||
fn turn_right(&self) -> Self {
|
||||
match self {
|
||||
North => East,
|
||||
South => West,
|
||||
West => North,
|
||||
East => South,
|
||||
}
|
||||
}
|
||||
|
||||
fn turn_around(&self) -> Self {
|
||||
match self {
|
||||
North => South,
|
||||
South => North,
|
||||
West => East,
|
||||
East => West,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
struct Node {
|
||||
score: u32,
|
||||
direction: Direction,
|
||||
position: IVec2,
|
||||
}
|
||||
|
||||
impl Ord for Node {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.score.cmp(&other.score)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Node {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(input: &str) -> (Vec<Vec<Tile>>, IVec2, IVec2) {
|
||||
let mut start_pos = IVec2::new(0, 0);
|
||||
let mut end_pos = IVec2::new(0, 0);
|
||||
(
|
||||
input
|
||||
.lines()
|
||||
.enumerate()
|
||||
.map(|(l_idx, l)| {
|
||||
l.chars()
|
||||
.enumerate()
|
||||
.map(|(c_idx, c)| match c {
|
||||
'#' => Wall,
|
||||
'S' => {
|
||||
start_pos.x = l_idx as i32;
|
||||
start_pos.y = c_idx as i32;
|
||||
Start
|
||||
}
|
||||
'E' => {
|
||||
end_pos.x = l_idx as i32;
|
||||
end_pos.y = c_idx as i32;
|
||||
End
|
||||
}
|
||||
_ => Empty,
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect(),
|
||||
start_pos,
|
||||
end_pos,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_scores(grid: &[Vec<Tile>], start_pos: &IVec2) -> AHashMap<(IVec2, Direction), u32> {
|
||||
// dijkstra
|
||||
|
||||
let mut to_visit = BinaryHeap::new();
|
||||
let mut visited = AHashMap::new();
|
||||
to_visit.push(Reverse(Node {
|
||||
score: 0,
|
||||
direction: East,
|
||||
position: *start_pos,
|
||||
}));
|
||||
|
||||
while let Some(curr) = to_visit.pop() {
|
||||
let curr = curr.0;
|
||||
|
||||
if visited
|
||||
.get(&(curr.position, curr.direction))
|
||||
.map_or(false, |s| s < &curr.score)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
visited.insert((curr.position, curr.direction), curr.score);
|
||||
|
||||
let pos_in_front = curr.position + curr.direction.as_vec();
|
||||
if grid[pos_in_front.x as usize][pos_in_front.y as usize] != Wall {
|
||||
to_visit.push(Reverse(Node {
|
||||
score: curr.score + 1,
|
||||
direction: curr.direction,
|
||||
position: curr.position + curr.direction.as_vec(),
|
||||
}));
|
||||
}
|
||||
|
||||
to_visit.push(Reverse(Node {
|
||||
score: curr.score + 1000,
|
||||
direction: curr.direction.turn_left(),
|
||||
position: curr.position,
|
||||
}));
|
||||
|
||||
to_visit.push(Reverse(Node {
|
||||
score: curr.score + 1000,
|
||||
direction: curr.direction.turn_right(),
|
||||
position: curr.position,
|
||||
}));
|
||||
}
|
||||
visited
|
||||
}
|
||||
|
||||
fn part1(scores: &AHashMap<(IVec2, Direction), u32>, end_pos: &IVec2) -> u32 {
|
||||
*scores
|
||||
.iter()
|
||||
.filter(|((p, _), _)| p == end_pos)
|
||||
.map(|(_, v)| v)
|
||||
.min()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
struct Visit {
|
||||
position: IVec2,
|
||||
direction: Direction,
|
||||
score: u32,
|
||||
}
|
||||
|
||||
fn part2(scores: &AHashMap<(IVec2, Direction), u32>, end_pos: &IVec2) -> usize {
|
||||
// bfs from end to start along shortest paths
|
||||
|
||||
let shortest_path_len = part1(scores, end_pos);
|
||||
let mut seen: AHashSet<IVec2> = AHashSet::new();
|
||||
let mut to_visit: Vec<Visit> = scores
|
||||
.iter()
|
||||
.filter(|((p, _), val)| val == &&shortest_path_len && p == end_pos)
|
||||
.map(|((p, d), s)| Visit {
|
||||
position: *p,
|
||||
direction: *d,
|
||||
score: *s,
|
||||
})
|
||||
.collect();
|
||||
|
||||
while let Some(curr) = to_visit.pop() {
|
||||
seen.insert(curr.position);
|
||||
|
||||
let pos_behind = curr.position + curr.direction.turn_around().as_vec();
|
||||
if scores
|
||||
.get(&(pos_behind, curr.direction))
|
||||
.map_or(false, |v| v == &(curr.score - 1))
|
||||
{
|
||||
to_visit.push(Visit {
|
||||
position: pos_behind,
|
||||
direction: curr.direction,
|
||||
score: curr.score - 1,
|
||||
});
|
||||
}
|
||||
|
||||
let new_score = curr.score - 1000;
|
||||
if scores
|
||||
.get(&(curr.position, curr.direction.turn_left()))
|
||||
.map_or(false, |v| v == &new_score)
|
||||
{
|
||||
to_visit.push(Visit {
|
||||
position: curr.position,
|
||||
direction: curr.direction.turn_left(),
|
||||
score: new_score,
|
||||
});
|
||||
}
|
||||
if scores
|
||||
.get(&(curr.position, curr.direction.turn_right()))
|
||||
.map_or(false, |v| v == &new_score)
|
||||
{
|
||||
to_visit.push(Visit {
|
||||
position: curr.position,
|
||||
direction: curr.direction.turn_right(),
|
||||
score: new_score,
|
||||
});
|
||||
}
|
||||
}
|
||||
seen.len()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (grid, start_pos, end_pos) = parse(include_str!("../input.txt"));
|
||||
let scores = get_scores(&grid, &start_pos);
|
||||
println!("{}", part1(&scores, &end_pos));
|
||||
println!("{}", part2(&scores, &end_pos));
|
||||
}
|
||||
7
day17/Cargo.lock
generated
7
day17/Cargo.lock
generated
@ -1,7 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "day17"
|
||||
version = "0.1.0"
|
||||
@ -1,6 +0,0 @@
|
||||
[package]
|
||||
name = "day17"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@ -1,127 +0,0 @@
|
||||
#[derive(Debug, Clone)]
|
||||
struct State {
|
||||
reg_a: usize,
|
||||
reg_b: usize,
|
||||
reg_c: usize,
|
||||
prog: Vec<u8>,
|
||||
pc: usize,
|
||||
out: Vec<u8>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn get_combo_operand(&self) -> usize {
|
||||
match self.prog[self.pc + 1] {
|
||||
x @ 0..=3 => x as usize,
|
||||
4 => self.reg_a,
|
||||
5 => self.reg_b,
|
||||
6 => self.reg_c,
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
fn exec_instruction(&mut self) {
|
||||
let literal_op = self.prog[self.pc + 1];
|
||||
match self.prog[self.pc] {
|
||||
0 => self.reg_a >>= self.get_combo_operand(),
|
||||
1 => self.reg_b ^= literal_op as usize,
|
||||
2 => self.reg_b = self.get_combo_operand() & 7,
|
||||
3 => {
|
||||
if self.reg_a != 0 {
|
||||
self.pc = literal_op as usize;
|
||||
return;
|
||||
}
|
||||
}
|
||||
4 => self.reg_b ^= self.reg_c,
|
||||
5 => self.out.push((self.get_combo_operand() & 7) as u8),
|
||||
6 => self.reg_b = self.reg_a >> self.get_combo_operand(),
|
||||
7 => self.reg_c = self.reg_a >> self.get_combo_operand(),
|
||||
_ => panic!("Invalid instruction"),
|
||||
}
|
||||
self.pc += 2;
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(input: &str) -> State {
|
||||
let (registers, program) = input.split_once("\n\n").unwrap();
|
||||
let &[a, b, c] = registers
|
||||
.lines()
|
||||
.map(|l| l.split(' ').last().unwrap().parse().unwrap())
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice()
|
||||
else {
|
||||
panic!()
|
||||
};
|
||||
State {
|
||||
reg_a: a,
|
||||
reg_b: b,
|
||||
reg_c: c,
|
||||
prog: program
|
||||
.split_once(' ')
|
||||
.unwrap()
|
||||
.1
|
||||
.trim()
|
||||
.split(',')
|
||||
.map(|i| i.parse().unwrap())
|
||||
.collect(),
|
||||
pc: 0,
|
||||
out: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn get_output(state: &mut State) -> Vec<u8> {
|
||||
while state.pc < state.prog.len() - 1 {
|
||||
state.exec_instruction();
|
||||
}
|
||||
state.out.clone()
|
||||
}
|
||||
|
||||
fn part1(state: &mut State) -> String {
|
||||
get_output(state)
|
||||
.iter()
|
||||
.map(|n| n.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",")
|
||||
}
|
||||
|
||||
// B <- A % 8
|
||||
// B <- B ^ 3
|
||||
// C <- A >> B
|
||||
// B <- B ^ C
|
||||
// A <- A >> 3
|
||||
// B <- B ^ 5
|
||||
// print B & 7
|
||||
// jmp 0 if A
|
||||
|
||||
fn find_input(state: &State, instructions: &[u8], curr_a: usize) -> Vec<usize> {
|
||||
let mut candidates = vec![];
|
||||
if instructions.is_empty() {
|
||||
return vec![curr_a];
|
||||
}
|
||||
let curr = instructions.last().unwrap();
|
||||
for i in 0..8 {
|
||||
let mut new_state = state.clone();
|
||||
let new_a = (curr_a << 3) + i;
|
||||
new_state.reg_a = new_a;
|
||||
if get_output(&mut new_state)[0] == *curr {
|
||||
candidates.push(new_a);
|
||||
}
|
||||
}
|
||||
let mut results = vec![];
|
||||
for candidate in candidates {
|
||||
results.extend(find_input(
|
||||
state,
|
||||
&instructions[0..instructions.len() - 1],
|
||||
candidate,
|
||||
));
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
fn part2(state: &State) -> usize {
|
||||
*find_input(state, &state.prog, 0).iter().min().unwrap()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let input = parse(include_str!("../input.txt"));
|
||||
println!("{}", part1(&mut input.clone()));
|
||||
println!("{}", part2(&input));
|
||||
}
|
||||
126
day18/Cargo.lock
generated
126
day18/Cargo.lock
generated
@ -1,126 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "day18"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"glam",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.29.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.168"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
@ -1,8 +0,0 @@
|
||||
[package]
|
||||
name = "day18"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
ahash = "0.8.11"
|
||||
glam = "0.29.2"
|
||||
@ -1,149 +0,0 @@
|
||||
use std::{cmp::Reverse, collections::BinaryHeap};
|
||||
|
||||
use ahash::AHashSet;
|
||||
use glam::IVec2;
|
||||
|
||||
enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
use Direction::*;
|
||||
|
||||
impl Direction {
|
||||
fn to_vec(&self) -> IVec2 {
|
||||
match self {
|
||||
Up => IVec2::new(-1, 0),
|
||||
Down => IVec2::new(1, 0),
|
||||
Left => IVec2::new(0, -1),
|
||||
Right => IVec2::new(0, 1),
|
||||
}
|
||||
}
|
||||
fn all() -> Vec<Self> {
|
||||
vec![Up, Down, Left, Right]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
struct Node {
|
||||
score: u32,
|
||||
position: IVec2,
|
||||
}
|
||||
|
||||
impl Ord for Node {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.score.cmp(&other.score)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Node {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
type Distance = (u32, Vec<IVec2>);
|
||||
|
||||
fn parse(input: &str) -> Vec<IVec2> {
|
||||
input
|
||||
.lines()
|
||||
.map(|l| {
|
||||
let (y, x) = l.split_once(',').unwrap();
|
||||
IVec2 {
|
||||
x: x.parse().unwrap(),
|
||||
y: y.parse().unwrap(),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_score<'a>(scores: &'a mut [Vec<Distance>], pos: &IVec2) -> &'a mut Distance {
|
||||
&mut scores[pos.x as usize][pos.y as usize]
|
||||
}
|
||||
|
||||
fn find_path(corrupt_pos: &AHashSet<IVec2>, dimensions: &IVec2) -> Distance {
|
||||
let mut scores =
|
||||
vec![vec![(u32::MAX, vec![]); dimensions.y as usize + 1]; dimensions.x as usize + 1];
|
||||
let mut to_visit = BinaryHeap::new();
|
||||
|
||||
let start_pos = IVec2::new(0, 0);
|
||||
|
||||
get_score(&mut scores, &start_pos).0 = 0;
|
||||
to_visit.push(Reverse(Node {
|
||||
score: 0,
|
||||
position: start_pos,
|
||||
}));
|
||||
|
||||
while let Some(Reverse(curr)) = to_visit.pop() {
|
||||
if curr.position == *dimensions {
|
||||
return (curr.score, get_score(&mut scores, &curr.position).1.clone());
|
||||
}
|
||||
|
||||
if curr.score > get_score(&mut scores, &curr.position).0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
for direction in Direction::all() {
|
||||
let new_pos = curr.position + direction.to_vec();
|
||||
if new_pos.cmplt(start_pos).any()
|
||||
|| new_pos.cmpgt(*dimensions).any()
|
||||
|| corrupt_pos.contains(&new_pos)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let new_score = curr.score + 1;
|
||||
if new_score < get_score(&mut scores, &new_pos).0 {
|
||||
get_score(&mut scores, &new_pos).0 = new_score;
|
||||
|
||||
let mut prev_positions = get_score(&mut scores, &curr.position).1.clone();
|
||||
prev_positions.push(curr.position);
|
||||
get_score(&mut scores, &new_pos).1.extend(prev_positions);
|
||||
|
||||
to_visit.push(Reverse(Node {
|
||||
score: new_score,
|
||||
position: new_pos,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(u32::MAX, vec![])
|
||||
}
|
||||
|
||||
fn part1(positions: &[IVec2], dimensions: &IVec2) -> u32 {
|
||||
find_path(
|
||||
&AHashSet::from_iter(positions[..1024].iter().map(|i| i.to_owned())),
|
||||
dimensions,
|
||||
)
|
||||
.0
|
||||
}
|
||||
|
||||
fn part2(positions: &[IVec2], dimensions: &IVec2) -> String {
|
||||
let mut prev_path = AHashSet::new();
|
||||
for i in 1024..positions.len() {
|
||||
if !prev_path.is_empty() && !prev_path.contains(&positions[i - 1]) {
|
||||
continue;
|
||||
}
|
||||
let (len, path) = find_path(
|
||||
&AHashSet::from_iter(positions[..i].iter().map(|i| i.to_owned())),
|
||||
dimensions,
|
||||
);
|
||||
prev_path = AHashSet::from_iter(path);
|
||||
if len == u32::MAX {
|
||||
return positions
|
||||
.get(i - 1)
|
||||
.map(|p| format!("{},{}", p.y, p.x))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
panic!("not found")
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let input = parse(include_str!("../input.txt"));
|
||||
let size = IVec2::new(70, 70);
|
||||
println!("{}", part1(&input, &size));
|
||||
println!("{}", part2(&input, &size));
|
||||
}
|
||||
119
day19/Cargo.lock
generated
119
day19/Cargo.lock
generated
@ -1,119 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "day19"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
@ -1,7 +0,0 @@
|
||||
[package]
|
||||
name = "day19"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
ahash = "0.8.11"
|
||||
@ -1,88 +0,0 @@
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
enum Color {
|
||||
White,
|
||||
Blue,
|
||||
Black,
|
||||
Red,
|
||||
Green,
|
||||
}
|
||||
use ahash::AHashMap;
|
||||
use Color::*;
|
||||
|
||||
impl From<char> for Color {
|
||||
fn from(value: char) -> Self {
|
||||
match value {
|
||||
'w' => White,
|
||||
'u' => Blue,
|
||||
'b' => Black,
|
||||
'r' => Red,
|
||||
'g' => Green,
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
struct ParsedInput {
|
||||
available_patterns: Vec<Vec<Color>>,
|
||||
designs: Vec<Vec<Color>>,
|
||||
}
|
||||
|
||||
fn parse(input: &str) -> ParsedInput {
|
||||
let (patterns, designs) = input.split_once("\n\n").unwrap();
|
||||
ParsedInput {
|
||||
available_patterns: patterns
|
||||
.trim_end()
|
||||
.split(", ")
|
||||
.map(|p| p.chars().map(|c| c.into()).collect())
|
||||
.collect(),
|
||||
designs: designs
|
||||
.lines()
|
||||
.map(|d| d.chars().map(|c| c.into()).collect())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn count_possible(
|
||||
design: &[Color],
|
||||
available_patterns: &[Vec<Color>],
|
||||
cache: &mut AHashMap<Vec<Color>, usize>,
|
||||
) -> usize {
|
||||
if design.is_empty() {
|
||||
return 1;
|
||||
}
|
||||
if let Some(res) = cache.get(design) {
|
||||
return *res;
|
||||
}
|
||||
let count = available_patterns
|
||||
.iter()
|
||||
.filter(|p| design.starts_with(p))
|
||||
.map(|p| count_possible(&design[p.len()..], available_patterns, cache))
|
||||
.sum();
|
||||
cache.insert(design.to_vec(), count);
|
||||
count
|
||||
}
|
||||
|
||||
fn part1(parsed_input: &ParsedInput) -> usize {
|
||||
let mut cache = AHashMap::new();
|
||||
parsed_input
|
||||
.designs
|
||||
.iter()
|
||||
.filter(|d| count_possible(d, &parsed_input.available_patterns, &mut cache) > 0)
|
||||
.count()
|
||||
}
|
||||
|
||||
fn part2(parsed_input: &ParsedInput) -> usize {
|
||||
let mut cache = AHashMap::new();
|
||||
parsed_input
|
||||
.designs
|
||||
.iter()
|
||||
.map(|d| count_possible(d, &parsed_input.available_patterns, &mut cache))
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let input = parse(include_str!("../input.txt"));
|
||||
println!("{}", part1(&input));
|
||||
println!("{}", part2(&input));
|
||||
}
|
||||
68
day20/Cargo.lock
generated
68
day20/Cargo.lock
generated
@ -1,68 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "day20"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.29.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
@ -1,8 +0,0 @@
|
||||
[package]
|
||||
name = "day20"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
glam = "0.29.2"
|
||||
rayon = "1.10.0"
|
||||
@ -1,139 +0,0 @@
|
||||
use glam::IVec2;
|
||||
use rayon::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
enum Direction {
|
||||
Up,
|
||||
Left,
|
||||
Down,
|
||||
Right,
|
||||
}
|
||||
use Direction::*;
|
||||
|
||||
impl Direction {
|
||||
fn to_vec(self) -> IVec2 {
|
||||
match self {
|
||||
Up => IVec2::new(-1, 0),
|
||||
Left => IVec2::new(0, -1),
|
||||
Down => IVec2::new(1, 0),
|
||||
Right => IVec2::new(0, 1),
|
||||
}
|
||||
}
|
||||
|
||||
fn all() -> Vec<Direction> {
|
||||
vec![Up, Left, Down, Right]
|
||||
}
|
||||
|
||||
fn turn(&self) -> Self {
|
||||
match self {
|
||||
Up => Right,
|
||||
Left => Up,
|
||||
Down => Left,
|
||||
Right => Down,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
struct Tile {
|
||||
tiletype: bool,
|
||||
distance: usize,
|
||||
}
|
||||
|
||||
fn parse(input: &str) -> (Vec<Vec<Tile>>, IVec2, IVec2) {
|
||||
let mut start_pos = IVec2::new(0, 0);
|
||||
let mut end_pos = IVec2::new(0, 0);
|
||||
(
|
||||
input
|
||||
.lines()
|
||||
.enumerate()
|
||||
.map(|(l_idx, l)| {
|
||||
l.chars()
|
||||
.enumerate()
|
||||
.map(|(c_idx, c)| Tile {
|
||||
tiletype: match c {
|
||||
'#' => true,
|
||||
'S' => {
|
||||
start_pos.x = l_idx as i32;
|
||||
start_pos.y = c_idx as i32;
|
||||
false
|
||||
}
|
||||
'E' => {
|
||||
end_pos.x = l_idx as i32;
|
||||
end_pos.y = c_idx as i32;
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
distance: usize::MAX,
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect(),
|
||||
start_pos,
|
||||
end_pos,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_distances(
|
||||
map: &mut [Vec<Tile>],
|
||||
pos: &IVec2,
|
||||
distance: &usize,
|
||||
path: &mut Vec<IVec2>,
|
||||
end_pos: &IVec2,
|
||||
) {
|
||||
let curr_tile = &mut map[pos.x as usize][pos.y as usize];
|
||||
curr_tile.distance = *distance;
|
||||
if pos == end_pos {
|
||||
return;
|
||||
}
|
||||
for direction in Direction::all() {
|
||||
let new_tile_pos = pos + direction.to_vec();
|
||||
let new_tile = map[new_tile_pos.x as usize][new_tile_pos.y as usize];
|
||||
if !new_tile.tiletype && new_tile.distance > distance + 1 {
|
||||
path.push(new_tile_pos);
|
||||
get_distances(map, &new_tile_pos, &(distance + 1), path, end_pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn part12(map: &[Vec<Tile>], path: &[IVec2], max_distance: i32) -> usize {
|
||||
let combinations: Vec<(i32, i32)> = (1..=max_distance)
|
||||
.flat_map(|i| (0..=(max_distance - i)).map(move |j| (i, j)))
|
||||
.collect();
|
||||
path.par_iter()
|
||||
.map(|tile| {
|
||||
Direction::all()
|
||||
.iter()
|
||||
.map(|direction| {
|
||||
combinations
|
||||
.iter()
|
||||
.filter(|(i, j)| {
|
||||
let new_tile_pos =
|
||||
tile + i * direction.to_vec() + j * direction.turn().to_vec();
|
||||
map.get(new_tile_pos.x as usize)
|
||||
.and_then(|l| l.get(new_tile_pos.y as usize))
|
||||
.map_or(false, |t| {
|
||||
t.distance
|
||||
>= map[tile.x as usize][tile.y as usize].distance
|
||||
+ 100
|
||||
+ *i as usize
|
||||
+ *j as usize
|
||||
&& !t.tiletype
|
||||
})
|
||||
})
|
||||
.count()
|
||||
})
|
||||
.sum::<usize>()
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (mut map, start_pos, end_pos) = parse(include_str!("../input.txt"));
|
||||
let mut path = vec![start_pos];
|
||||
get_distances(&mut map, &start_pos, &0, &mut path, &end_pos);
|
||||
println!("{}", part12(&map, &path, 2));
|
||||
println!("{}", part12(&map, &path, 20));
|
||||
}
|
||||
131
day21/Cargo.lock
generated
131
day21/Cargo.lock
generated
@ -1,131 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "day21"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"itertools",
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.29.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
@ -1,9 +0,0 @@
|
||||
[package]
|
||||
name = "day21"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
phf = { version = "0.11.2", features = ["macros"] }
|
||||
glam = "0.29.2"
|
||||
itertools = "0.13.0"
|
||||
@ -1,165 +0,0 @@
|
||||
use std::{collections::HashMap, fmt::Display, iter};
|
||||
|
||||
use glam::IVec2;
|
||||
use itertools::Itertools;
|
||||
use phf::phf_map;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
enum Pad {
|
||||
Keypad,
|
||||
Numpad,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
fn to_vec(self) -> IVec2 {
|
||||
match self {
|
||||
Direction::Up => IVec2::new(-1, 0),
|
||||
Direction::Down => IVec2::new(1, 0),
|
||||
Direction::Left => IVec2::new(0, -1),
|
||||
Direction::Right => IVec2::new(0, 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Direction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Direction::Up => write!(f, "^"),
|
||||
Direction::Down => write!(f, "v"),
|
||||
Direction::Left => write!(f, "<"),
|
||||
Direction::Right => write!(f, ">"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static NUMPAD_POSITIONS: phf::Map<char, IVec2> = phf_map! {
|
||||
'7' => IVec2::new(0, 0),
|
||||
'8' => IVec2::new(0, 1),
|
||||
'9' => IVec2::new(0, 2),
|
||||
'4' => IVec2::new(1, 0),
|
||||
'5' => IVec2::new(1, 1),
|
||||
'6' => IVec2::new(1, 2),
|
||||
'1' => IVec2::new(2, 0),
|
||||
'2' => IVec2::new(2, 1),
|
||||
'3' => IVec2::new(2, 2),
|
||||
'0' => IVec2::new(3, 1),
|
||||
'A' => IVec2::new(3, 2),
|
||||
};
|
||||
|
||||
static KEYPAD_POSITIONS: phf::Map<char, IVec2> = phf_map! {
|
||||
'^' => IVec2::new(0, 1),
|
||||
'A' => IVec2::new(0, 2),
|
||||
'<' => IVec2::new(1, 0),
|
||||
'v' => IVec2::new(1, 1),
|
||||
'>' => IVec2::new(1, 2),
|
||||
};
|
||||
|
||||
fn parse(input: &str) -> Vec<String> {
|
||||
input.lines().map(|l| l.to_string()).collect()
|
||||
}
|
||||
|
||||
fn directions(pos1: &IVec2, pos2: &IVec2) -> Vec<Vec<Direction>> {
|
||||
let diff = pos2 - pos1;
|
||||
let mut path_directions = vec![];
|
||||
if diff.x > 0 {
|
||||
path_directions.extend((0..diff.x).map(|_| Direction::Down));
|
||||
} else {
|
||||
path_directions.extend((0..diff.x.abs()).map(|_| Direction::Up));
|
||||
}
|
||||
if diff.y > 0 {
|
||||
path_directions.extend((0..diff.y).map(|_| Direction::Right));
|
||||
} else {
|
||||
path_directions.extend((0..diff.y.abs()).map(|_| Direction::Left));
|
||||
}
|
||||
let reverse = path_directions.iter().rev().map(|d| d.to_owned()).collect();
|
||||
if reverse == path_directions {
|
||||
vec![path_directions]
|
||||
} else {
|
||||
vec![path_directions, reverse]
|
||||
}
|
||||
}
|
||||
|
||||
fn is_allowed_path(pos1: &IVec2, directions: &[Direction], banned_pos: &IVec2) -> bool {
|
||||
let mut pos = *pos1;
|
||||
for direction in directions {
|
||||
pos += direction.to_vec();
|
||||
if pos == *banned_pos {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn get_paths(pos1: &IVec2, pos2: &IVec2, banned_pos: &IVec2) -> Vec<Vec<Direction>> {
|
||||
directions(pos1, pos2)
|
||||
.iter()
|
||||
.filter(|d| is_allowed_path(pos1, d, banned_pos))
|
||||
.map(|d| d.to_owned())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn min_length(code: &str, pads: &[Pad], cache: &mut HashMap<(String, usize), usize>) -> usize {
|
||||
// credits: RubixDev
|
||||
|
||||
if pads.is_empty() {
|
||||
return code.len();
|
||||
}
|
||||
if let Some(val) = cache.get(&(code.to_string(), pads.len())) {
|
||||
return *val;
|
||||
}
|
||||
let result = std::iter::once('A')
|
||||
.chain(code.chars())
|
||||
.tuple_windows()
|
||||
.map(|(start, end)| {
|
||||
match pads[0] {
|
||||
Pad::Numpad => get_paths(
|
||||
NUMPAD_POSITIONS.get(&start).unwrap(),
|
||||
NUMPAD_POSITIONS.get(&end).unwrap(),
|
||||
&IVec2::new(3, 0),
|
||||
),
|
||||
Pad::Keypad => get_paths(
|
||||
KEYPAD_POSITIONS.get(&start).unwrap(),
|
||||
KEYPAD_POSITIONS.get(&end).unwrap(),
|
||||
&IVec2::new(0, 0),
|
||||
),
|
||||
}
|
||||
.into_iter()
|
||||
.map(|dirs| dirs.iter().map(|d| d.to_string()).collect::<String>() + "A")
|
||||
})
|
||||
.multi_cartesian_product()
|
||||
.map(|combination| {
|
||||
combination
|
||||
.iter()
|
||||
.map(|c| min_length(c, &pads[1..], cache))
|
||||
.sum::<usize>()
|
||||
})
|
||||
.min()
|
||||
.unwrap();
|
||||
cache.insert((code.to_string(), pads.len()), result);
|
||||
result
|
||||
}
|
||||
|
||||
fn part12(input: &[String], keypad_robot_count: usize) -> usize {
|
||||
let mut cache = HashMap::new();
|
||||
let pads: Vec<Pad> = iter::once(Pad::Numpad)
|
||||
.chain((0..keypad_robot_count).map(|_| Pad::Keypad))
|
||||
.collect();
|
||||
input
|
||||
.iter()
|
||||
.map(|s| s[..s.len() - 1].parse::<usize>().unwrap() * min_length(s, &pads, &mut cache))
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let input = parse(include_str!("../input.txt"));
|
||||
println!("{}", part12(&input, 2));
|
||||
println!("{}", part12(&input, 25));
|
||||
}
|
||||
119
day22/Cargo.lock
generated
119
day22/Cargo.lock
generated
@ -1,119 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "day22"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
@ -1,7 +0,0 @@
|
||||
[package]
|
||||
name = "day22"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
ahash = "0.8.11"
|
||||
@ -1,76 +0,0 @@
|
||||
use ahash::{AHashMap, AHashSet};
|
||||
|
||||
fn parse(input: &str) -> Vec<usize> {
|
||||
input.lines().map(|l| l.parse().unwrap()).collect()
|
||||
}
|
||||
|
||||
fn evolve_number(num: &mut usize) {
|
||||
*num ^= *num << 6;
|
||||
*num &= 0xffffff;
|
||||
*num ^= *num >> 5;
|
||||
*num &= 0xffffff;
|
||||
*num ^= *num << 11;
|
||||
*num &= 0xffffff;
|
||||
}
|
||||
|
||||
fn part1(initial_numbers: &[usize]) -> usize {
|
||||
initial_numbers
|
||||
.iter()
|
||||
.map(|num| {
|
||||
let mut res = *num;
|
||||
(0..2000).for_each(|_| evolve_number(&mut res));
|
||||
res
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn part2(numbers: &[usize]) -> isize {
|
||||
let mut sequence_bananas: AHashMap<(i8, i8, i8, i8), Vec<i8>> = AHashMap::new();
|
||||
for number in numbers {
|
||||
let mut costs_and_changes: Vec<(i8, i8)> = vec![];
|
||||
let mut curr_magic = *number;
|
||||
for _ in 0..2000 {
|
||||
let cost = (curr_magic % 10) as i8;
|
||||
costs_and_changes.push((cost, costs_and_changes.last().map_or(0, |l| cost - l.0)));
|
||||
evolve_number(&mut curr_magic);
|
||||
}
|
||||
let sequence_costs = costs_and_changes
|
||||
.windows(4)
|
||||
.skip(1)
|
||||
.map(|w| ((w[0].1, w[1].1, w[2].1, w[3].1), w[3].0));
|
||||
let mut seen_sequences = AHashSet::new();
|
||||
for (k, v) in sequence_costs {
|
||||
if seen_sequences.contains(&k) {
|
||||
continue;
|
||||
}
|
||||
sequence_bananas
|
||||
.entry(k)
|
||||
.and_modify(|v2| v2.push(v))
|
||||
.or_insert(vec![v]);
|
||||
seen_sequences.insert(k);
|
||||
}
|
||||
}
|
||||
sequence_bananas
|
||||
.get(
|
||||
sequence_bananas
|
||||
.iter()
|
||||
.max_by(|a, b| {
|
||||
a.1.iter()
|
||||
.map(|i| *i as isize)
|
||||
.sum::<isize>()
|
||||
.cmp(&b.1.iter().map(|i| *i as isize).sum::<isize>())
|
||||
})
|
||||
.unwrap()
|
||||
.0,
|
||||
)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|i| *i as isize)
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let input = parse(include_str!("../input.txt"));
|
||||
println!("{}", part1(&input));
|
||||
println!("{}", part2(&input));
|
||||
}
|
||||
63
day23/Cargo.lock
generated
63
day23/Cargo.lock
generated
@ -1,63 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "day23"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"petgraph",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.5"
|
||||
source = "git+https://github.com/qoqosz/petgraph?branch=feature%2Fmaximal_cliques#cee17df1398adeae9f4696d3f4caba5662b37d63"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
]
|
||||
@ -1,8 +0,0 @@
|
||||
[package]
|
||||
name = "day23"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
itertools = "0.13.0"
|
||||
petgraph = { git = "https://github.com/qoqosz/petgraph", branch = "feature/maximal_cliques"}
|
||||
@ -1,71 +0,0 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use itertools::Itertools;
|
||||
use petgraph::algo::maximal_cliques;
|
||||
use petgraph::prelude::*;
|
||||
|
||||
fn parse(input: &str) -> Vec<(String, String)> {
|
||||
input
|
||||
.lines()
|
||||
.map(|l| l.split_once("-").unwrap())
|
||||
.map(|(a, b)| (a.to_string(), b.to_string()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn build_graph(edges: Vec<(String, String)>) -> UnGraph<String, ()> {
|
||||
let mut graph = UnGraph::new_undirected();
|
||||
let mut node_indices = HashMap::new();
|
||||
|
||||
for (a, b) in edges {
|
||||
let from_index = *node_indices
|
||||
.entry(a.clone())
|
||||
.or_insert_with(|| graph.add_node(a));
|
||||
let to_index = *node_indices
|
||||
.entry(b.clone())
|
||||
.or_insert_with(|| graph.add_node(b));
|
||||
graph.add_edge(from_index, to_index, ());
|
||||
}
|
||||
|
||||
graph
|
||||
}
|
||||
|
||||
fn part1(graph: &UnGraph<String, ()>) -> usize {
|
||||
let mut cliques3 = HashSet::new();
|
||||
for edge in graph.edge_references() {
|
||||
for &w in graph
|
||||
.neighbors(edge.source())
|
||||
.collect::<HashSet<_>>()
|
||||
.intersection(&graph.neighbors(edge.target()).collect::<HashSet<_>>())
|
||||
{
|
||||
let mut clique3 = [edge.source(), edge.target(), w];
|
||||
clique3.sort_unstable();
|
||||
cliques3.insert(clique3);
|
||||
}
|
||||
}
|
||||
cliques3
|
||||
.iter()
|
||||
.filter(|clique| {
|
||||
clique
|
||||
.iter()
|
||||
.any(|n_idx| graph.node_weight(*n_idx).unwrap().starts_with("t"))
|
||||
})
|
||||
.count()
|
||||
}
|
||||
|
||||
fn part2(graph: &Graph<String, (), Undirected>) -> String {
|
||||
maximal_cliques(graph)
|
||||
.iter()
|
||||
.max_by_key(|x| x.len())
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|n_idx| graph.node_weight(*n_idx).unwrap().to_string())
|
||||
.sorted_unstable()
|
||||
.join(",")
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let input = parse(include_str!("../input.txt"));
|
||||
let graph = build_graph(input);
|
||||
println!("{}", part1(&graph));
|
||||
println!("{}", part2(&graph));
|
||||
}
|
||||
7
day24/Cargo.lock
generated
7
day24/Cargo.lock
generated
@ -1,7 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "day24"
|
||||
version = "0.1.0"
|
||||
@ -1,6 +0,0 @@
|
||||
[package]
|
||||
name = "day24"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@ -1,105 +0,0 @@
|
||||
use std::{cmp::Reverse, collections::HashMap};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
enum Gate {
|
||||
InputGate(bool),
|
||||
CalcGate(CalcGate),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
enum Operation {
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
}
|
||||
|
||||
impl From<&str> for Operation {
|
||||
fn from(value: &str) -> Self {
|
||||
match value {
|
||||
"AND" => Self::And,
|
||||
"OR" => Self::Or,
|
||||
"XOR" => Self::Xor,
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
struct CalcGate {
|
||||
operation: Operation,
|
||||
lhs: String,
|
||||
rhs: String,
|
||||
}
|
||||
|
||||
fn parse(input: &str) -> HashMap<String, Gate> {
|
||||
let mut res = HashMap::new();
|
||||
let (inputs, calcs) = input.split_once("\n\n").unwrap();
|
||||
res.extend(inputs.lines().map(|l| {
|
||||
let (name, val) = l.split_once(": ").unwrap();
|
||||
(name.to_string(), Gate::InputGate(val == "1"))
|
||||
}));
|
||||
res.extend(calcs.lines().map(|l| {
|
||||
let mut iter = l.split(' ');
|
||||
let lhs = iter.next().unwrap();
|
||||
let op = iter.next().unwrap();
|
||||
let rhs = iter.next().unwrap();
|
||||
let name = iter.nth(1).unwrap();
|
||||
(
|
||||
name.to_string(),
|
||||
Gate::CalcGate(CalcGate {
|
||||
operation: Operation::from(op),
|
||||
lhs: lhs.to_string(),
|
||||
rhs: rhs.to_string(),
|
||||
}),
|
||||
)
|
||||
}));
|
||||
res
|
||||
}
|
||||
|
||||
fn get_gate_value(
|
||||
gate: &str,
|
||||
gates: &HashMap<String, Gate>,
|
||||
cache: &mut HashMap<String, bool>,
|
||||
) -> bool {
|
||||
if let Some(v) = cache.get(gate) {
|
||||
return *v;
|
||||
}
|
||||
let res = match gates.get(gate).unwrap() {
|
||||
Gate::InputGate(val) => *val,
|
||||
Gate::CalcGate(calc_gate) => match calc_gate.operation {
|
||||
Operation::And => {
|
||||
get_gate_value(&calc_gate.lhs, gates, cache)
|
||||
& get_gate_value(&calc_gate.rhs, gates, cache)
|
||||
}
|
||||
Operation::Or => {
|
||||
get_gate_value(&calc_gate.lhs, gates, cache)
|
||||
| get_gate_value(&calc_gate.rhs, gates, cache)
|
||||
}
|
||||
Operation::Xor => {
|
||||
get_gate_value(&calc_gate.lhs, gates, cache)
|
||||
^ get_gate_value(&calc_gate.rhs, gates, cache)
|
||||
}
|
||||
},
|
||||
};
|
||||
cache.insert(gate.to_string(), res);
|
||||
res
|
||||
}
|
||||
|
||||
fn part1(gates: &HashMap<String, Gate>) -> usize {
|
||||
let mut output_bits: Vec<_> = gates.keys().filter(|g| g.starts_with("z")).collect();
|
||||
let mut cache = HashMap::new();
|
||||
output_bits.sort_unstable_by_key(|v| Reverse(v.to_string()));
|
||||
let mut res: usize = 0;
|
||||
for bit in output_bits {
|
||||
res <<= 1;
|
||||
if get_gate_value(bit, gates, &mut cache) {
|
||||
res += 1;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let input = parse(include_str!("../input.txt"));
|
||||
println!("{}", part1(&input));
|
||||
}
|
||||
7
day25/Cargo.lock
generated
7
day25/Cargo.lock
generated
@ -1,7 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "day25"
|
||||
version = "0.1.0"
|
||||
@ -1,6 +0,0 @@
|
||||
[package]
|
||||
name = "day25"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@ -1,49 +0,0 @@
|
||||
#[derive(Debug, Clone)]
|
||||
struct ParsedInput {
|
||||
keys: Vec<[u8; 5]>,
|
||||
locks: Vec<[u8; 5]>,
|
||||
}
|
||||
|
||||
fn parse_key(grid: &[Vec<char>]) -> [u8; 5] {
|
||||
let mut res = [0; 5];
|
||||
for (l_idx, line) in grid.iter().skip(1).enumerate().rev().skip(1) {
|
||||
for (c_idx, c) in line.iter().enumerate() {
|
||||
if c == &'#' && res[c_idx] == 0 {
|
||||
res[c_idx] = l_idx as u8 + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn parse(input: &str) -> ParsedInput {
|
||||
let mut keys = vec![];
|
||||
let mut locks = vec![];
|
||||
for block in input.split("\n\n") {
|
||||
let mut grid: Vec<Vec<char>> = block.lines().map(|l| l.chars().collect()).collect();
|
||||
if grid[0][0] == '#' {
|
||||
locks.push(parse_key(&grid));
|
||||
} else {
|
||||
grid.reverse();
|
||||
keys.push(parse_key(&grid));
|
||||
}
|
||||
}
|
||||
ParsedInput { keys, locks }
|
||||
}
|
||||
|
||||
fn overlaps(key: &[u8; 5], lock: &[u8; 5]) -> bool {
|
||||
key.iter().zip(lock.iter()).any(|(k, l)| 5 - k < *l)
|
||||
}
|
||||
|
||||
fn part1(parsed_input: &ParsedInput) -> usize {
|
||||
parsed_input
|
||||
.locks
|
||||
.iter()
|
||||
.map(|l| parsed_input.keys.iter().filter(|k| !overlaps(k, l)).count())
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let input = parse(include_str!("../input.txt"));
|
||||
println!("{}", part1(&input));
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user