WIP
This commit is contained in:
parent
6f1199f3d2
commit
a24d3134c0
30
2024/fetch-input.sh
Executable file
30
2024/fetch-input.sh
Executable file
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
AOC_YEAR="${AOC_YEAR:-2024}"
|
||||||
|
if [ "$#" -lt 1 ]; then
|
||||||
|
echo "Error: No day provided"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
DAY="$1"
|
||||||
|
f="$HOME/.cache/aoc$AOC_YEAR/$DAY.input"
|
||||||
|
if [ -f "$f" ]; then
|
||||||
|
echo "Skip: File already exists"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
url="https://adventofcode.com/$AOC_YEAR/day/$DAY/input"
|
||||||
|
cookie_file="$HOME/.advent-of-code-session-cookie"
|
||||||
|
if [[ ! -f "$cookie_file" ]]; then
|
||||||
|
echo "Cookie file not found: $cookie_file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cookie="$(cat "$cookie_file")"
|
||||||
|
mkdir -p "$(dirname "$f")"
|
||||||
|
if curl -s --fail-with-body -X GET "$url" -H "Cookie:$cookie" > "$f"; then
|
||||||
|
cat "$f"
|
||||||
|
echo "Downloaded $url to $f - contents have been output to this terminal as well"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "Error: curl failed (are you sure the input is available now?)"
|
||||||
|
rm -f "$f"
|
||||||
|
exit 1
|
||||||
|
fi
|
1
2024/readme.md
Normal file
1
2024/readme.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
1
2024/rust/.envrc
Normal file
1
2024/rust/.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
3
2024/rust/.gitignore
vendored
Normal file
3
2024/rust/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
/target
|
||||||
|
|
||||||
|
/.direnv
|
7
2024/rust/Cargo.lock
generated
Normal file
7
2024/rust/Cargo.lock
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aoc2024"
|
||||||
|
version = "1.0.0"
|
103
2024/rust/Cargo.toml
Normal file
103
2024/rust/Cargo.toml
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
[package]
|
||||||
|
name = "aoc2024"
|
||||||
|
version = "1.0.0"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day1"
|
||||||
|
path = "src/day1.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day2"
|
||||||
|
path = "src/day2.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day3"
|
||||||
|
path = "src/day3.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day4"
|
||||||
|
path = "src/day4.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day5"
|
||||||
|
path = "src/day5.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day6"
|
||||||
|
path = "src/day6.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day7"
|
||||||
|
path = "src/day7.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day8"
|
||||||
|
path = "src/day8.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day9"
|
||||||
|
path = "src/day9.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day10"
|
||||||
|
path = "src/day10.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day11"
|
||||||
|
path = "src/day11.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day12"
|
||||||
|
path = "src/day12.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day13"
|
||||||
|
path = "src/day13.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day14"
|
||||||
|
path = "src/day14.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day15"
|
||||||
|
path = "src/day15.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day16"
|
||||||
|
path = "src/day16.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day17"
|
||||||
|
path = "src/day17.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day18"
|
||||||
|
path = "src/day18.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day19"
|
||||||
|
path = "src/day19.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day20"
|
||||||
|
path = "src/day20.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day21"
|
||||||
|
path = "src/day21.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day22"
|
||||||
|
path = "src/day22.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day23"
|
||||||
|
path = "src/day23.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day24"
|
||||||
|
path = "src/day24.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day25"
|
||||||
|
path = "src/day25.rs"
|
27
2024/rust/flake.lock
Normal file
27
2024/rust/flake.lock
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1732937961,
|
||||||
|
"narHash": "sha256-B5pYT+IVaqcrfOekkwKvx/iToDnuQWzc2oyDxzzBDc4=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "4703b8d2c708e13a8cab03d865f90973536dcdf5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
27
2024/rust/flake.nix
Normal file
27
2024/rust/flake.nix
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
outputs = {
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
}: let
|
||||||
|
supportedSystems = ["x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin"];
|
||||||
|
forEachSupportedSystem = f:
|
||||||
|
nixpkgs.lib.genAttrs supportedSystems (system:
|
||||||
|
f {
|
||||||
|
pkgs = import nixpkgs {inherit system;};
|
||||||
|
});
|
||||||
|
in {
|
||||||
|
devShells = forEachSupportedSystem ({pkgs}: {
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
cargo
|
||||||
|
rustc
|
||||||
|
rustfmt
|
||||||
|
rustPackages.clippy
|
||||||
|
rust-analyzer
|
||||||
|
curl
|
||||||
|
];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
43
2024/rust/readme.md
Normal file
43
2024/rust/readme.md
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# Rust Advent of Code 2024 Solutions
|
||||||
|
|
||||||
|
## Competing
|
||||||
|
|
||||||
|
I compete very lightly. I use [my `at` script][at] like `at 2022-12-02 && ../
|
||||||
|
fetch-input.sh 2` to fetch input as soon as it's available and I use `watchexec
|
||||||
|
-e rs 'clear; cargo test --bin day2 && cargo run --bin day2'` to run my code
|
||||||
|
with tests as I edit them.
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
### Debug
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --bin day1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo test --bin day1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Release Mode
|
||||||
|
|
||||||
|
For speeeeeed!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo build --release --bin day1
|
||||||
|
time ./target/release/day1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Everything
|
||||||
|
|
||||||
|
You can use this `fish` script to build all binaries in release mode and run/
|
||||||
|
time them all:
|
||||||
|
|
||||||
|
```fish
|
||||||
|
cargo build --release --bins
|
||||||
|
time for f in (fd 'day.' target/release/ --type executable --max-depth 1); echo $f; time $f; end
|
||||||
|
```
|
||||||
|
|
||||||
|
[at]: https://git.lyte.dev/lytedev/nix/src/branch/main/modules/home-manager/scripts/common/bin/at
|
146
2024/rust/src/day1.rs
Normal file
146
2024/rust/src/day1.rs
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
mod prelude;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Day1::show(day_input(1), day_input(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Day1 {}
|
||||||
|
impl Day1 {
|
||||||
|
fn calibration_value(line: &str) -> i128 {
|
||||||
|
println!("{line}");
|
||||||
|
let bytes = line.as_bytes();
|
||||||
|
let mut first_digit: Option<u8> = None;
|
||||||
|
let mut last_digit: Option<u8> = None;
|
||||||
|
for i in 0..bytes.len() {
|
||||||
|
let n = bytes.len() - 1 - i;
|
||||||
|
println!("{n} {i}");
|
||||||
|
if first_digit.is_none() && (0x30..=0x39).contains(&bytes[i]) {
|
||||||
|
first_digit = Some(bytes[i] - 0x30);
|
||||||
|
println!("found first {first_digit:?}");
|
||||||
|
}
|
||||||
|
if last_digit.is_none() && (0x30..=0x39).contains(&bytes[n]) {
|
||||||
|
last_digit = Some(bytes[n] - 0x30);
|
||||||
|
println!("found last {last_digit:?}");
|
||||||
|
}
|
||||||
|
if first_digit.is_some() && last_digit.is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("{:?} {:?}", first_digit, last_digit);
|
||||||
|
Into::<i128>::into(first_digit.or(last_digit).unwrap() * 10)
|
||||||
|
+ Into::<i128>::into(last_digit.or(first_digit).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_name(s: &[u8]) -> Option<u8> {
|
||||||
|
for (i, w) in [
|
||||||
|
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
||||||
|
]
|
||||||
|
.map(|s| s.as_bytes())
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
if s.starts_with(w) {
|
||||||
|
return Some(i as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_name_end(s: &[u8]) -> Option<u8> {
|
||||||
|
for (i, w) in [
|
||||||
|
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
||||||
|
]
|
||||||
|
.map(|s| s.as_bytes())
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
if s.ends_with(w) {
|
||||||
|
return Some(i as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calibration_value_words(line: &str) -> i128 {
|
||||||
|
println!("{line}");
|
||||||
|
let bytes = line.as_bytes();
|
||||||
|
let mut first_digit: Option<u8> = None;
|
||||||
|
let mut last_digit: Option<u8> = None;
|
||||||
|
for i in 0..bytes.len() {
|
||||||
|
let n = bytes.len() - 1 - i;
|
||||||
|
println!("NI: {n} {i}");
|
||||||
|
if first_digit.is_none() {
|
||||||
|
if (0x30..=0x39).contains(&bytes[i]) {
|
||||||
|
first_digit = Some(bytes[i] - 0x30);
|
||||||
|
println!("found first {first_digit:?}");
|
||||||
|
} else if let Some(n) = Self::num_name(&bytes[i..]) {
|
||||||
|
first_digit = Some(n);
|
||||||
|
println!("found first text {first_digit:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if last_digit.is_none() {
|
||||||
|
println!("{:?}", &bytes[..=n]);
|
||||||
|
if (0x30..=0x39).contains(&bytes[n]) {
|
||||||
|
last_digit = Some(bytes[n] - 0x30);
|
||||||
|
println!("found last {last_digit:?}");
|
||||||
|
} else if let Some(n) = Self::num_name_end(&bytes[..=n]) {
|
||||||
|
last_digit = Some(n);
|
||||||
|
println!("found last text {last_digit:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if first_digit.is_some() && last_digit.is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("{:?} {:?}", first_digit, last_digit);
|
||||||
|
Into::<i128>::into(first_digit.or(last_digit).unwrap() * 10)
|
||||||
|
+ Into::<i128>::into(last_digit.or(first_digit).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AoCSolution for Day1 {
|
||||||
|
type Input = String;
|
||||||
|
type Solution = i128;
|
||||||
|
|
||||||
|
fn part1(input: Self::Input) -> Self::Solution {
|
||||||
|
input.lines().map(Self::calibration_value).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(input: Self::Input) -> Self::Solution {
|
||||||
|
input.lines().map(Self::calibration_value_words).sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
assert_eq!(
|
||||||
|
Day1::part1(
|
||||||
|
r#"1abc2
|
||||||
|
pqr3stu8vwx
|
||||||
|
a1b2c3d4e5f
|
||||||
|
treb7uchet"#
|
||||||
|
.into()
|
||||||
|
),
|
||||||
|
142
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Day1::part2(
|
||||||
|
r#"two1nine
|
||||||
|
eightwothree
|
||||||
|
abcone2threexyz
|
||||||
|
xtwone3four
|
||||||
|
4nineeightseven2
|
||||||
|
zoneight234
|
||||||
|
7pqrstsixteen"#
|
||||||
|
.into()
|
||||||
|
),
|
||||||
|
281
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
68
2024/rust/src/prelude.rs
Normal file
68
2024/rust/src/prelude.rs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
pub use std::fs::File;
|
||||||
|
pub use std::io::Read;
|
||||||
|
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::{env, io};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum InputFileError {
|
||||||
|
#![allow(dead_code)]
|
||||||
|
EnvVar(env::VarError),
|
||||||
|
Io(io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensures that the input file exists
|
||||||
|
fn ensure_input_file(day: u8) -> Result<PathBuf, InputFileError> {
|
||||||
|
let path = Path::new(&env::var("HOME").map_err(InputFileError::EnvVar)?)
|
||||||
|
.join(format!("./.cache/aoc2024/{0}.input", day));
|
||||||
|
if !path.exists() {
|
||||||
|
eprintln!("Running input downloaded script with day arg {}...", day);
|
||||||
|
std::process::Command::new("sh")
|
||||||
|
.args(["../fetch-input.sh", &day.to_string()])
|
||||||
|
.status()
|
||||||
|
.map_err(InputFileError::Io)?;
|
||||||
|
}
|
||||||
|
Ok(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Useful when you simply want the day's input as a String.
|
||||||
|
pub fn day_input(day: u8) -> String {
|
||||||
|
let mut buffer = String::new();
|
||||||
|
day_input_file(day)
|
||||||
|
.read_to_string(&mut buffer)
|
||||||
|
.expect(&format!("invalid utf8 input for day {}", day));
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Useful when you want the day's input for streaming for maximum performance nonsense
|
||||||
|
pub fn day_input_file(day: u8) -> File {
|
||||||
|
let f = ensure_input_file(day).expect(&format!("Failed to ensure input for day {}", day));
|
||||||
|
File::open(&f).expect(format!("Failed to open file {}", f.display()).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AoCSolution {
|
||||||
|
type Input;
|
||||||
|
type Solution: std::fmt::Debug + std::cmp::PartialEq + std::fmt::Display;
|
||||||
|
|
||||||
|
// Are part1 and part2 solutions _always_ the same?
|
||||||
|
fn part1(input: Self::Input) -> Self::Solution;
|
||||||
|
fn part2(input: Self::Input) -> Self::Solution;
|
||||||
|
|
||||||
|
fn show(i1: Self::Input, i2: Self::Input) {
|
||||||
|
println!("Part 1: {}", Self::part1(i1));
|
||||||
|
println!("Part 2: {}", Self::part2(i2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub trait AoCSolutionTest: AoCSolution {
|
||||||
|
const PART1_TEST_INPUT: Self::Input;
|
||||||
|
const PART2_TEST_INPUT: Self::Input;
|
||||||
|
const PART1_TEST_RESULT: Self::Solution;
|
||||||
|
const PART2_TEST_RESULT: Self::Solution;
|
||||||
|
|
||||||
|
fn assert_valid() {
|
||||||
|
assert_eq!(Self::part1(Self::PART1_TEST_INPUT), Self::PART1_TEST_RESULT);
|
||||||
|
assert_eq!(Self::part2(Self::PART2_TEST_INPUT), Self::PART2_TEST_RESULT);
|
||||||
|
}
|
||||||
|
}
|
32
2024/rust/src/template.rs
Normal file
32
2024/rust/src/template.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
mod prelude;
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Day5::show(day_input(5), day_input(5))
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Day5 {}
|
||||||
|
impl AoCSolution for Day5 {
|
||||||
|
type Input = String;
|
||||||
|
type Solution = usize;
|
||||||
|
|
||||||
|
fn part1(input: Self::Input) -> Self::Solution {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(input: Self::Input) -> Self::Solution {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let input = r#""#;
|
||||||
|
assert_eq!(Day2::part1(input.into()), 1);
|
||||||
|
assert_eq!(Day2::part2(input.into()), 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
This repository is meant to contain all my [Advent of Code][aoc] code throughout
|
This repository is meant to contain all my [Advent of Code][aoc] code throughout
|
||||||
the years I've participated (to whatever extent).
|
the years I've participated (to whatever extent).
|
||||||
|
|
||||||
|
- [2024](./2024)
|
||||||
- [2023](./2023)
|
- [2023](./2023)
|
||||||
- [2022](./2022)
|
- [2022](./2022)
|
||||||
- [2021](./2021)
|
- [2021](./2021)
|
||||||
|
|
Loading…
Reference in a new issue