Reorganization
This commit is contained in:
1 parent
02fe9aea97
commit
85ce1c4641
13 files changed
+49
-34
No files matched your search
Generated
+251
@@ -0,0 +1,251 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aoc-runner"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d21ef9204ad206a5a3e918e9920da04e1118ad91ce4f23570be964b9d6b9dfcb"
|
||||
|
||||
[[package]]
|
||||
name = "aoc-runner-derive"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba8b944269d3fee645d281b1335e1797044db497bb02d0098cc3fdb8900069cc"
|
||||
dependencies = [
|
||||
"aoc-runner-internal",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aoc-runner-internal"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "274b0ba7f3669a45ec0aaacf94eb032a749de880ab776091576cca94037c9982"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aoc_2024"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aoc-runner",
|
||||
"aoc-runner-derive",
|
||||
"nom",
|
||||
"rayon",
|
||||
"regex",
|
||||
"topological-sort",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
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.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[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 = "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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.215"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.215"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[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 = "topological-sort"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "aoc_2024"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
aoc-runner = "0.3.0"
|
||||
aoc-runner-derive = "0.3.0"
|
||||
nom = "7.1.3"
|
||||
rayon = "1.10.0"
|
||||
regex = "1.11.1"
|
||||
topological-sort = "0.2.2"
|
||||
@@ -0,0 +1,123 @@
|
||||
# This is a configuration file for the bacon tool
|
||||
#
|
||||
# Complete help on configuration: https://dystroy.org/bacon/config/
|
||||
#
|
||||
# You may check the current default at
|
||||
# https://github.com/Canop/bacon/blob/main/defaults/default-bacon.toml
|
||||
|
||||
default_job = "check"
|
||||
env.CARGO_TERM_COLOR = "always"
|
||||
|
||||
[jobs.check]
|
||||
command = ["cargo", "check"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.check-all]
|
||||
command = ["cargo", "check", "--all-targets"]
|
||||
need_stdout = false
|
||||
|
||||
# Run clippy on the default target
|
||||
[jobs.clippy]
|
||||
command = ["cargo", "clippy"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.aoc]
|
||||
command = ["cargo", "aoc"]
|
||||
need_stdout = true
|
||||
on_change_strategy = "kill_then_restart"
|
||||
|
||||
# Run clippy on all targets
|
||||
# To disable some lints, you may change the job this way:
|
||||
# [jobs.clippy-all]
|
||||
# command = [
|
||||
# "cargo", "clippy",
|
||||
# "--all-targets",
|
||||
# "--",
|
||||
# "-A", "clippy::bool_to_int_with_if",
|
||||
# "-A", "clippy::collapsible_if",
|
||||
# "-A", "clippy::derive_partial_eq_without_eq",
|
||||
# ]
|
||||
# need_stdout = false
|
||||
[jobs.clippy-all]
|
||||
command = ["cargo", "clippy", "--all-targets"]
|
||||
need_stdout = false
|
||||
|
||||
# This job lets you run
|
||||
# - all tests: bacon test
|
||||
# - a specific test: bacon test -- config::test_default_files
|
||||
# - the tests of a package: bacon test -- -- -p config
|
||||
[jobs.test]
|
||||
command = ["cargo", "test"]
|
||||
need_stdout = true
|
||||
|
||||
[jobs.nextest]
|
||||
command = [
|
||||
"cargo",
|
||||
"nextest",
|
||||
"run",
|
||||
"--hide-progress-bar",
|
||||
"--failure-output",
|
||||
"final",
|
||||
]
|
||||
need_stdout = true
|
||||
analyzer = "nextest"
|
||||
|
||||
[jobs.doc]
|
||||
command = ["cargo", "doc", "--no-deps"]
|
||||
need_stdout = false
|
||||
|
||||
# If the doc compiles, then it opens in your browser and bacon switches
|
||||
# to the previous job
|
||||
[jobs.doc-open]
|
||||
command = ["cargo", "doc", "--no-deps", "--open"]
|
||||
need_stdout = false
|
||||
on_success = "back" # so that we don't open the browser at each change
|
||||
|
||||
# You can run your application and have the result displayed in bacon,
|
||||
# if it makes sense for this crate.
|
||||
[jobs.run]
|
||||
command = [
|
||||
"cargo",
|
||||
"run",
|
||||
# put launch parameters for your program behind a `--` separator
|
||||
]
|
||||
need_stdout = true
|
||||
allow_warnings = true
|
||||
background = true
|
||||
|
||||
# Run your long-running application (eg server) and have the result displayed in bacon.
|
||||
# For programs that never stop (eg a server), `background` is set to false
|
||||
# to have the cargo run output immediately displayed instead of waiting for
|
||||
# program's end.
|
||||
# 'on_change_strategy' is set to `kill_then_restart` to have your program restart
|
||||
# on every change (an alternative would be to use the 'F5' key manually in bacon).
|
||||
# If you often use this job, it makes sense to override the 'r' key by adding
|
||||
# a binding `r = job:run-long` at the end of this file .
|
||||
[jobs.run-long]
|
||||
command = [
|
||||
"cargo",
|
||||
"run",
|
||||
# put launch parameters for your program behind a `--` separator
|
||||
]
|
||||
need_stdout = true
|
||||
allow_warnings = true
|
||||
background = false
|
||||
on_change_strategy = "kill_then_restart"
|
||||
|
||||
# This parameterized job runs the example of your choice, as soon
|
||||
# as the code compiles.
|
||||
# Call it as
|
||||
# bacon ex -- my-example
|
||||
[jobs.ex]
|
||||
command = ["cargo", "run", "--example"]
|
||||
need_stdout = true
|
||||
allow_warnings = true
|
||||
|
||||
# You may define here keybindings that would be specific to
|
||||
# a project, for example a shortcut to launch a specific job.
|
||||
# Shortcuts to internal functions (scrolling, toggling, etc.)
|
||||
# should go in your personal global prefs.toml file instead.
|
||||
[keybindings]
|
||||
# alt-m = "job:my-job"
|
||||
c = "job:clippy-all" # comment this to have 'c' run clippy on only the default target
|
||||
a = "job:aoc"
|
||||
@@ -0,0 +1,54 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use nom::character::complete::multispace0;
|
||||
use nom::multi::many1;
|
||||
use nom::{character::complete::u64 as parse_u64, IResult};
|
||||
|
||||
#[aoc_generator(day1)]
|
||||
pub fn generator(input: &str) -> Vec<(u64, u64)> {
|
||||
many1(parse_line)(input).unwrap().1
|
||||
}
|
||||
|
||||
fn parse_line(input: &str) -> IResult<&str, (u64, u64)> {
|
||||
let (input, left) = parse_u64(input)?;
|
||||
let (input, _) = multispace0(input)?;
|
||||
let (input, right) = parse_u64(input)?;
|
||||
let (input, _) = multispace0(input)?;
|
||||
Ok((input, (left, right)))
|
||||
}
|
||||
|
||||
#[aoc(day1, part1)]
|
||||
pub fn part1(input: &[(u64, u64)]) -> u64 {
|
||||
let (mut left, mut right): (Vec<u64>, Vec<u64>) = input.iter().copied().unzip();
|
||||
left.sort();
|
||||
right.sort();
|
||||
left.iter()
|
||||
.zip(right.iter())
|
||||
.map(|(l, r)| (*l).abs_diff(*r))
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[aoc(day1, part2)]
|
||||
pub fn part2(input: &[(u64, u64)]) -> usize {
|
||||
let (mut left, mut right): (Vec<u64>, Vec<u64>) = input.iter().copied().unzip();
|
||||
left.sort();
|
||||
right.sort();
|
||||
|
||||
let mut l;
|
||||
let mut r = 0;
|
||||
let mut score = 0usize;
|
||||
for value in left {
|
||||
l = r;
|
||||
while l < right.len() && right[l] < value {
|
||||
l += 1
|
||||
}
|
||||
r = l;
|
||||
while r < right.len() && right[r] <= value {
|
||||
r += 1
|
||||
}
|
||||
score += (value as usize) * (r - l);
|
||||
if r == right.len() - 1 {
|
||||
break;
|
||||
};
|
||||
}
|
||||
score
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use nom::character::complete::{newline, space0};
|
||||
use nom::multi::{many0, many1};
|
||||
use nom::{character::complete::i64 as parse_usize, IResult};
|
||||
|
||||
#[aoc_generator(day2)]
|
||||
pub fn generator(input: &str) -> Vec<Vec<isize>> {
|
||||
many1(parse_line)(input).unwrap().1
|
||||
}
|
||||
|
||||
fn parse_level(input: &str) -> IResult<&str, isize> {
|
||||
let (input, res) = parse_usize(input)?;
|
||||
let (input, _) = space0(input)?;
|
||||
Ok((input, res as isize))
|
||||
}
|
||||
|
||||
fn parse_line(input: &str) -> IResult<&str, Vec<isize>> {
|
||||
let (input, levels) = many1(parse_level)(input)?;
|
||||
let (input, _) = many0(newline)(input)?;
|
||||
Ok((input, levels))
|
||||
}
|
||||
|
||||
#[aoc(day2, part1)]
|
||||
pub fn part1(input: &[Vec<isize>]) -> usize {
|
||||
input.iter().filter(|vec| is_valid(vec)).count()
|
||||
}
|
||||
|
||||
fn is_valid(input: &[isize]) -> bool {
|
||||
let mut direction = 0;
|
||||
for i in 0..(input.len() - 1) {
|
||||
let diff = input[i + 1] - input[i];
|
||||
if diff == 0 {
|
||||
return false;
|
||||
} else if (1..=3).contains(&diff) {
|
||||
if direction == -1 {
|
||||
return false;
|
||||
}
|
||||
direction = 1;
|
||||
} else if (1..=3).contains(&-diff) {
|
||||
if direction == 1 {
|
||||
return false;
|
||||
}
|
||||
direction = -1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
#[aoc(day2, part2)]
|
||||
pub fn part2(input: &[Vec<isize>]) -> usize {
|
||||
input
|
||||
.iter()
|
||||
.filter(|rec| (0..rec.len()).any(|i| is_valid2(rec, i)))
|
||||
.count()
|
||||
}
|
||||
|
||||
fn is_valid2(input: &[isize], skip: usize) -> bool {
|
||||
let mut direction = 0;
|
||||
for i in 0..(input.len() - 2) {
|
||||
let diff = if skip <= i {
|
||||
input[i + 2] - input[i + 1]
|
||||
} else if skip == i + 1 {
|
||||
input[i + 2] - input[i]
|
||||
} else {
|
||||
input[i + 1] - input[i]
|
||||
};
|
||||
if diff == 0 {
|
||||
return false;
|
||||
} else if (1..=3).contains(&diff) {
|
||||
if direction == -1 {
|
||||
return false;
|
||||
}
|
||||
direction = 1;
|
||||
} else if (1..=3).contains(&-diff) {
|
||||
if direction == 1 {
|
||||
return false;
|
||||
}
|
||||
direction = -1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use regex::Regex;
|
||||
#[aoc_generator(day3)]
|
||||
fn parse(input: &str) -> String {
|
||||
input.to_string()
|
||||
}
|
||||
|
||||
#[aoc(day3, part1)]
|
||||
fn part1(input: &str) -> i64 {
|
||||
Regex::new(r"mul\((?<a>[0-9]{1,3}),(?<b>[0-9]{1,3})\)")
|
||||
.unwrap()
|
||||
.captures_iter(input)
|
||||
.fold(0, |a, c| {
|
||||
a + c.get(1).unwrap().as_str().parse::<i64>().unwrap()
|
||||
* c.get(2).unwrap().as_str().parse::<i64>().unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
#[aoc(day3, part2)]
|
||||
fn part2(input: &str) -> i64 {
|
||||
Regex::new(r"mul\((?<a>[0-9]{1,3}),(?<b>[0-9]{1,3})\)|do\(\)|don't\(\)")
|
||||
.unwrap()
|
||||
.captures_iter(input)
|
||||
.fold((true, 0i64), |a, c| match c.get(0).unwrap().as_str() {
|
||||
"do()" => (true, a.1),
|
||||
"don't()" => (false, a.1),
|
||||
_ => {
|
||||
if a.0 {
|
||||
(
|
||||
a.0,
|
||||
a.1 + c.get(1).unwrap().as_str().parse::<i64>().unwrap()
|
||||
* c.get(2).unwrap().as_str().parse::<i64>().unwrap(),
|
||||
)
|
||||
} else {
|
||||
a
|
||||
}
|
||||
}
|
||||
})
|
||||
.1
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))
|
||||
"
|
||||
)),
|
||||
161
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(
|
||||
part2(&parse(
|
||||
"xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))"
|
||||
)),
|
||||
48
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
#[aoc_generator(day4)]
|
||||
fn parse(input: &str) -> Vec<Vec<char>> {
|
||||
input
|
||||
.split("\n")
|
||||
.map(|line| line.chars().collect())
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[aoc(day4, part1)]
|
||||
fn part1(d: &[Vec<char>]) -> usize {
|
||||
(0..d.len()).fold(0, |z, i| {
|
||||
z + (0..d[0].len())
|
||||
.map(move |j| {
|
||||
(
|
||||
j as i64,
|
||||
i as i64,
|
||||
j > 2,
|
||||
j < d.len() - 3,
|
||||
i > 2,
|
||||
i < d[0].len() - 3,
|
||||
)
|
||||
})
|
||||
.fold(0, |z, p| {
|
||||
z + (-1..=1).fold(0, |z, f| {
|
||||
z + (-1..=1).fold(0, move |z, g| {
|
||||
z + ((f != 0 || g != 0)
|
||||
&& (f == 0 || (f == -1 && p.2) || (f == 1 && p.3))
|
||||
&& (g == 0 || (g == -1 && p.4) || (g == 1 && p.5))
|
||||
&& (0..=3).zip("XMAS".chars()).all(|v| {
|
||||
d[(p.1 + v.0 * g) as usize][(p.0 + v.0 * f) as usize] == v.1
|
||||
})) as usize
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn _part1(input: &[Vec<char>]) -> u64 {
|
||||
let h = input.len();
|
||||
let w = input[0].len();
|
||||
let mut sum = 0;
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
if input[y][x] == 'S' {
|
||||
if x < w - 3
|
||||
&& input[y][x + 1] == 'A'
|
||||
&& input[y][x + 2] == 'M'
|
||||
&& input[y][x + 3] == 'X'
|
||||
{
|
||||
// println!("rev X {} {}", x, y);
|
||||
sum += 1
|
||||
}
|
||||
if y < h - 3
|
||||
&& input[y + 1][x] == 'A'
|
||||
&& input[y + 2][x] == 'M'
|
||||
&& input[y + 3][x] == 'X'
|
||||
{
|
||||
// println!("rev Y {} {}", x, y);
|
||||
sum += 1
|
||||
}
|
||||
if x < w - 3
|
||||
&& y < h - 3
|
||||
&& input[y + 1][x + 1] == 'A'
|
||||
&& input[y + 2][x + 2] == 'M'
|
||||
&& input[y + 3][x + 3] == 'X'
|
||||
{
|
||||
// println!("rdi Y {} {}", x, y);
|
||||
sum += 1
|
||||
}
|
||||
if x >= 3
|
||||
&& y < h - 3
|
||||
&& input[y + 1][x - 1] == 'A'
|
||||
&& input[y + 2][x - 2] == 'M'
|
||||
&& input[y + 3][x - 3] == 'X'
|
||||
{
|
||||
// println!("rdi Y {} {}", x, y);
|
||||
sum += 1
|
||||
}
|
||||
} else if input[y][x] == 'X' {
|
||||
if x < w - 3
|
||||
&& input[y][x + 1] == 'M'
|
||||
&& input[y][x + 2] == 'A'
|
||||
&& input[y][x + 3] == 'S'
|
||||
{
|
||||
// println!(" X {} {}", x, y);
|
||||
sum += 1
|
||||
}
|
||||
if y < h - 3
|
||||
&& input[y + 1][x] == 'M'
|
||||
&& input[y + 2][x] == 'A'
|
||||
&& input[y + 3][x] == 'S'
|
||||
{
|
||||
// println!(" Y {} {}", x, y);
|
||||
sum += 1
|
||||
}
|
||||
if x < w - 3
|
||||
&& y < h - 3
|
||||
&& input[y + 1][x + 1] == 'M'
|
||||
&& input[y + 2][x + 2] == 'A'
|
||||
&& input[y + 3][x + 3] == 'S'
|
||||
{
|
||||
// println!("dia Y {} {}", x, y);
|
||||
sum += 1
|
||||
}
|
||||
if x >= 3
|
||||
&& y < h - 3
|
||||
&& input[y + 1][x - 1] == 'M'
|
||||
&& input[y + 2][x - 2] == 'A'
|
||||
&& input[y + 3][x - 3] == 'S'
|
||||
{
|
||||
// println!("dia Y {} {}", x, y);
|
||||
sum += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
#[aoc(day4, part2)]
|
||||
fn part2(input: &[Vec<char>]) -> u64 {
|
||||
let h = input.len();
|
||||
let w = input[0].len();
|
||||
let mut sum = 0;
|
||||
for y in 1..h - 1 {
|
||||
for x in 1..w - 1 {
|
||||
if input[y][x] == 'A' {
|
||||
let diag1 = (input[y - 1][x - 1] == 'M' && input[y + 1][x + 1] == 'S')
|
||||
|| (input[y - 1][x - 1] == 'S' && input[y + 1][x + 1] == 'M');
|
||||
let diag2 = (input[y - 1][x + 1] == 'M' && input[y + 1][x - 1] == 'S')
|
||||
|| (input[y - 1][x + 1] == 'S' && input[y + 1][x - 1] == 'M');
|
||||
if diag1 && diag2 {
|
||||
sum += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(part1(&parse("MMMSXXMASM\nMSAMXMSMSA\nAMXSXMAAMM\nMSAMASMSMX\nXMASAMXAMM\nXXAMMXXAMA\nSMSMSASXSS\nSAXAMASAAA\nMAMMMXMMMM\nMXMXAXMASX")), 18);
|
||||
assert_eq!(part1(&parse(include_str!("../input/2024/day4.txt"))), 2406);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(part2(&parse(".M.S......\n..A..MSMS.\n.M.S.MAA..\n..A.ASMSM.\n.M.S.M....\n..........\nS.S.S.S.S.\n.A.A.A.A..\nM.M.M.M.M.\n..........")), 9);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use nom::character::complete::u64 as parse_u64;
|
||||
use nom::multi::separated_list1;
|
||||
use nom::IResult;
|
||||
use nom::{
|
||||
bytes::complete::tag, character::complete::newline, multi::separated_list0,
|
||||
sequence::separated_pair,
|
||||
};
|
||||
use topological_sort::TopologicalSort;
|
||||
|
||||
type Input = (Vec<(u64, u64)>, Vec<Vec<u64>>);
|
||||
|
||||
#[aoc_generator(day5)]
|
||||
fn parse(input: &str) -> Input {
|
||||
parse_lines(input).unwrap().1
|
||||
}
|
||||
|
||||
fn parse_lines(input: &str) -> IResult<&str, Input> {
|
||||
let (input, rules) =
|
||||
separated_list0(newline, separated_pair(parse_u64, tag("|"), parse_u64))(input)?;
|
||||
let (input, _) = newline(input)?;
|
||||
let (input, _) = newline(input)?;
|
||||
let (input, records) = separated_list0(newline, separated_list1(tag(","), parse_u64))(input)?;
|
||||
Ok((input, (rules, records)))
|
||||
}
|
||||
|
||||
#[aoc(day5, part1)]
|
||||
fn part1(input: &Input) -> u64 {
|
||||
let rules = input.0.clone();
|
||||
let records = input.1.clone();
|
||||
|
||||
let mut rules_map: HashMap<u64, Vec<u64>> = HashMap::new();
|
||||
rules.iter().for_each(|rule| {
|
||||
if let std::collections::hash_map::Entry::Vacant(e) = rules_map.entry(rule.1) {
|
||||
e.insert(vec![rule.0]);
|
||||
} else {
|
||||
rules_map.get_mut(&rule.1).unwrap().push(rule.0);
|
||||
rules_map.get_mut(&rule.1).unwrap().sort();
|
||||
}
|
||||
});
|
||||
|
||||
let mut correct = 0;
|
||||
records.iter().for_each(|record| {
|
||||
let mut forbidden = vec![];
|
||||
let valid = record.iter().all(|page| {
|
||||
let allowed = !forbidden.contains(page);
|
||||
if rules_map.contains_key(page) {
|
||||
let mut new_forbidden = rules_map.get(page).unwrap().clone();
|
||||
forbidden.append(&mut new_forbidden);
|
||||
}
|
||||
allowed
|
||||
});
|
||||
if valid {
|
||||
correct += record[record.len() >> 1]
|
||||
}
|
||||
});
|
||||
|
||||
correct
|
||||
}
|
||||
|
||||
#[aoc(day5, part2)]
|
||||
fn part2(input: &Input) -> u64 {
|
||||
let rules = input.0.clone();
|
||||
let records = input.1.clone();
|
||||
|
||||
let mut rules_map: HashMap<u64, Vec<u64>> = HashMap::new();
|
||||
rules.iter().for_each(|rule| {
|
||||
if let std::collections::hash_map::Entry::Vacant(e) = rules_map.entry(rule.1) {
|
||||
e.insert(vec![rule.0]);
|
||||
} else {
|
||||
rules_map.get_mut(&rule.1).unwrap().push(rule.0);
|
||||
rules_map.get_mut(&rule.1).unwrap().sort();
|
||||
}
|
||||
});
|
||||
|
||||
let mut correct = 0;
|
||||
records.iter().for_each(|record| {
|
||||
let mut forbidden = vec![];
|
||||
let valid = record.iter().all(|page| {
|
||||
let allowed = !forbidden.contains(page);
|
||||
if rules_map.contains_key(page) {
|
||||
let mut new_forbidden = rules_map.get(page).unwrap().clone();
|
||||
forbidden.append(&mut new_forbidden);
|
||||
}
|
||||
allowed
|
||||
});
|
||||
if !valid {
|
||||
// dbg!(&record);
|
||||
let mut ts = TopologicalSort::<u64>::new();
|
||||
for i in record {
|
||||
ts.insert(*i);
|
||||
if let Some(rules) = rules_map.get(i) {
|
||||
for rule in rules {
|
||||
ts.add_dependency(*rule, *i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let sorted = ts
|
||||
.clone()
|
||||
.filter(|a| record.contains(a))
|
||||
.collect::<Vec<u64>>();
|
||||
correct += sorted[sorted.len() >> 1]
|
||||
}
|
||||
});
|
||||
|
||||
correct
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(part1(&parse("47|53\n97|13\n97|61\n97|47\n75|29\n61|13\n75|53\n29|13\n97|29\n53|29\n61|53\n97|53\n61|29\n47|13\n75|47\n97|75\n47|61\n75|61\n47|29\n75|13\n53|13\n\n75,47,61,53,29\n97,61,53,29,13\n75,29,13\n75,97,47,61,53\n61,13,29\n97,13,75,29,47")), 143);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(part2(&parse("47|53\n97|13\n97|61\n97|47\n75|29\n61|13\n75|53\n29|13\n97|29\n53|29\n61|53\n97|53\n61|29\n47|13\n75|47\n97|75\n47|61\n75|61\n47|29\n75|13\n53|13\n\n75,47,61,53,29\n97,61,53,29,13\n75,29,13\n75,97,47,61,53\n61,13,29\n97,13,75,29,47")), 123);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
|
||||
// type Input = ((usize, usize), Vec<Vec<u8>>);
|
||||
type Input = ((usize, usize), Vec<Vec<u8>>, Vec<Vec<[u8; 5]>>);
|
||||
|
||||
const UP: u8 = 0;
|
||||
const DOWN: u8 = 1;
|
||||
const LEFT: u8 = 2;
|
||||
const RIGHT: u8 = 4;
|
||||
const EMPTY: u8 = 8;
|
||||
const WALL: u8 = 16;
|
||||
const PASSED: u8 = 32;
|
||||
|
||||
const DIR: [(isize, isize); 5] = [(0, -1), (0, 1), (-1, 0), (0, 0), (1, 0)];
|
||||
const NEXT_DIR: [usize; 5] = [RIGHT as usize, LEFT as usize, UP as usize, 0, DOWN as usize];
|
||||
|
||||
#[aoc_generator(day6)]
|
||||
fn parse(input: &str) -> Input {
|
||||
let lines = input.split("\n");
|
||||
let mut pos = (0, 0);
|
||||
let mut stones = VecDeque::new();
|
||||
|
||||
let map: Vec<Vec<u8>> = lines
|
||||
.enumerate()
|
||||
.map(|(y, line)| {
|
||||
line.chars()
|
||||
.enumerate()
|
||||
.map(|(x, c)| match c {
|
||||
'.' => EMPTY,
|
||||
'#' => {
|
||||
stones.push_back((x, y));
|
||||
WALL
|
||||
}
|
||||
'^' => {
|
||||
pos = (x, y);
|
||||
EMPTY
|
||||
}
|
||||
_ => todo!(),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut jumps = vec![vec![[u8::MAX; 5]; map[0].len()]; map.len()];
|
||||
|
||||
while let Some((x, y)) = stones.pop_front() {
|
||||
for dir in NEXT_DIR {
|
||||
let mut d = 1;
|
||||
loop {
|
||||
let pos = (x as isize - d * DIR[dir].0, y as isize - d * DIR[dir].1);
|
||||
if pos.0 < 0
|
||||
|| pos.0 >= map[0].len() as isize
|
||||
|| pos.1 < 0
|
||||
|| pos.1 >= map.len() as isize
|
||||
|| map[pos.1 as usize][pos.0 as usize] == WALL
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
jumps[pos.1 as usize][pos.0 as usize][dir] = (d - 1) as u8;
|
||||
|
||||
d += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(pos, map, jumps)
|
||||
// (pos, map)
|
||||
}
|
||||
|
||||
#[aoc(day6, part1)]
|
||||
fn part1(input: &Input) -> u64 {
|
||||
let h = input.1.len();
|
||||
let w = input.1[0].len();
|
||||
|
||||
let mut map = input.1.clone();
|
||||
|
||||
let mut pos = ((input.0).0 as isize, (input.0).1 as isize);
|
||||
let mut dir = UP as usize;
|
||||
let mut count = 0;
|
||||
|
||||
loop {
|
||||
if map[pos.1 as usize][pos.0 as usize] == EMPTY {
|
||||
count += 1;
|
||||
map[pos.1 as usize][pos.0 as usize] = PASSED;
|
||||
}
|
||||
|
||||
if pos.0 + DIR[dir].0 < 0
|
||||
|| pos.0 + DIR[dir].0 >= w as isize
|
||||
|| pos.1 + DIR[dir].1 < 0
|
||||
|| pos.1 + DIR[dir].1 >= h as isize
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
while map[(pos.1 + DIR[dir].1) as usize][(pos.0 + DIR[dir].0) as usize] == WALL {
|
||||
dir = NEXT_DIR[dir];
|
||||
}
|
||||
|
||||
let next_pos = (pos.0 + DIR[dir].0, pos.1 + DIR[dir].1);
|
||||
pos = next_pos;
|
||||
}
|
||||
|
||||
count
|
||||
}
|
||||
|
||||
#[aoc(day6, part2)]
|
||||
fn part2(input: &Input) -> u64 {
|
||||
let h = input.1.len();
|
||||
let w = input.1[0].len();
|
||||
|
||||
let initial = ((input.0).0 as isize, (input.0).1 as isize);
|
||||
|
||||
let mut map = input.1.clone();
|
||||
let jumps = &input.2;
|
||||
|
||||
let mut pos = initial;
|
||||
let mut dir = UP as usize;
|
||||
let mut count = 0;
|
||||
|
||||
loop {
|
||||
if pos.0 + DIR[dir].0 < 0
|
||||
|| pos.0 + DIR[dir].0 >= w as isize
|
||||
|| pos.1 + DIR[dir].1 < 0
|
||||
|| pos.1 + DIR[dir].1 >= h as isize
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
while map[(pos.1 + DIR[dir].1) as usize][(pos.0 + DIR[dir].0) as usize] == WALL {
|
||||
dir = NEXT_DIR[dir];
|
||||
}
|
||||
|
||||
let next_pos = (pos.0 + DIR[dir].0, pos.1 + DIR[dir].1);
|
||||
|
||||
if next_pos != initial && map[next_pos.1 as usize][next_pos.0 as usize] == EMPTY {
|
||||
map[next_pos.1 as usize][next_pos.0 as usize] = WALL;
|
||||
if check_loop(pos, next_pos, dir, &mut map, jumps) {
|
||||
count += 1;
|
||||
}
|
||||
map[next_pos.1 as usize][next_pos.0 as usize] = PASSED;
|
||||
}
|
||||
|
||||
pos = next_pos;
|
||||
}
|
||||
|
||||
count
|
||||
}
|
||||
|
||||
fn check_loop(
|
||||
mut pos: (isize, isize),
|
||||
stone: (isize, isize),
|
||||
mut dir: usize,
|
||||
map: &mut [Vec<u8>],
|
||||
jumps: &[Vec<[u8; 5]>],
|
||||
) -> bool {
|
||||
let h = map.len();
|
||||
let w = map[0].len();
|
||||
|
||||
let mut changed = Vec::with_capacity(w * h * 4);
|
||||
|
||||
loop {
|
||||
if pos.0 + DIR[dir].0 < 0
|
||||
|| pos.0 + DIR[dir].0 >= w as isize
|
||||
|| pos.1 + DIR[dir].1 < 0
|
||||
|| pos.1 + DIR[dir].1 >= h as isize
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
let mut turn = false;
|
||||
while map[(pos.1 + DIR[dir].1) as usize][(pos.0 + DIR[dir].0) as usize] == WALL {
|
||||
dir = NEXT_DIR[dir];
|
||||
turn = true;
|
||||
}
|
||||
|
||||
let next_pos = if (pos.0 == stone.0) || (pos.1 == stone.1) {
|
||||
(pos.0 + DIR[dir].0, pos.1 + DIR[dir].1)
|
||||
} else {
|
||||
let d = jumps[pos.1 as usize][pos.0 as usize][dir] as isize;
|
||||
if d == u8::MAX as isize {
|
||||
changed.into_iter().for_each(|pos: (isize, isize)| {
|
||||
map[pos.1 as usize][pos.0 as usize] &= !(UP | DOWN | LEFT | RIGHT);
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
(pos.0 + d * DIR[dir].0, pos.1 + d * DIR[dir].1)
|
||||
};
|
||||
|
||||
if turn {
|
||||
if map[next_pos.1 as usize][next_pos.0 as usize] & dir as u8 != 0 {
|
||||
changed.into_iter().for_each(|pos: (isize, isize)| {
|
||||
map[pos.1 as usize][pos.0 as usize] &= !(UP | DOWN | LEFT | RIGHT);
|
||||
});
|
||||
|
||||
return true;
|
||||
} else {
|
||||
map[next_pos.1 as usize][next_pos.0 as usize] |= dir as u8;
|
||||
changed.push(next_pos);
|
||||
}
|
||||
}
|
||||
|
||||
pos = next_pos;
|
||||
}
|
||||
|
||||
changed.into_iter().for_each(|pos: (isize, isize)| {
|
||||
map[pos.1 as usize][pos.0 as usize] &= !(UP | DOWN | LEFT | RIGHT);
|
||||
});
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(part1(&parse("....#.....\n.........#\n..........\n..#.......\n.......#..\n..........\n.#..^.....\n........#.\n#.........\n......#...")), 41);
|
||||
assert_eq!(part1(&parse(include_str!("../input/2024/day6.txt"))), 4977);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(part2(&parse("....#.....\n.........#\n..........\n..#.......\n.......#..\n..........\n.#..^.....\n........#.\n#.........\n......#...")), 6);
|
||||
assert_eq!(part2(&parse(include_str!("../input/2024/day6.txt"))), 1729);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
mod day6;
|
||||
mod day5;
|
||||
mod day4;
|
||||
mod day1;
|
||||
mod day2;
|
||||
mod day3;
|
||||
|
||||
extern crate aoc_runner;
|
||||
|
||||
#[macro_use]
|
||||
extern crate aoc_runner_derive;
|
||||
|
||||
aoc_lib! {year = 2024}
|
||||
Reference in new issue
Block a user