Skip to content

Commit

Permalink
d8 solution.
Browse files Browse the repository at this point in the history
 works on my input, whatever this awful problem statement is supposed to mean.
  • Loading branch information
SOF3 committed Dec 8, 2024
1 parent e3be5ef commit 0948ec1
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 99 deletions.
12 changes: 12 additions & 0 deletions input/d8.sample.input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............
8 changes: 5 additions & 3 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,11 +285,13 @@ fn all_impl(ts: TokenStream) -> syn::Result<TokenStream> {
&format!("day_{day_number}_part_{}_{fn_ident}", part.part_number),
soln_name.span(),
);
let solution_fn_expr = soln_target.fn_expr(day, quote!(aoc2024::all::))?;
let solution_fn_expr =
soln_target.fn_expr(day, quote!(aoc2024::all::))?;
let file_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
.join(format!("input/d{day_number}.private.input.txt"));
let file_path =
file_path.to_str().expect("build path must not contain non-unicode characters");
let file_path = file_path
.to_str()
.expect("build path must not contain non-unicode characters");
let fn_def = quote_spanned! { soln_name.span() =>
fn #fn_ident() {
let input = include_str!(#file_path);
Expand Down
80 changes: 10 additions & 70 deletions src/all.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use std::path::PathBuf;
use std::fmt;
use std::time::Instant;
use std::{env, fmt, fs, io};

use anyhow::Context;
use clap::{Parser, ValueEnum};
use crate::{load_input, Args, JsonString, Parse};

fn call<In: Parse, Out: fmt::Display>(mut f: impl FnMut(In) -> Out, input: &str) -> String {
let start_time = Instant::now();
Expand All @@ -19,72 +17,6 @@ fn call<In: Parse, Out: fmt::Display>(mut f: impl FnMut(In) -> Out, input: &str)
output.to_string()
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum Mode {
Sample,
Private,
}

#[derive(Parser)]
pub struct Args {
mode: Mode,
day: u32,
part: u32,
#[clap(default_value = "")]
variant: String,
}

pub fn load_input(mode: Mode, day: u32) -> anyhow::Result<String> {
let dir = env::var("CARGO_MANIFEST_DIR").context("need cargo run")?;
let path = PathBuf::from(dir).join("input").join(format!(
"d{day}.{}.input.txt",
match mode {
Mode::Sample => "sample",
Mode::Private => "private",
}
));

if let Mode::Private = mode {
let exists =
fs::exists(&path).with_context(|| format!("test {} existence", path.display()))?;
if !exists {
eprintln!("Downloading day {day} input");

let session_cookie = env::var("AOC_SESSION")
.context("private file missing and AOC_SESSION env var missing")?;

let client = reqwest::blocking::Client::new();
let data = client
.get(format!("https://adventofcode.com/2024/day/{day}/input"))
.header("Cookie", format!("session={session_cookie}"))
.send()
.context("request aoc private input")?;
let input = io::read_to_string(data).context("read aoc private input")?;
fs::write(&path, &input).context("write aoc private input to cache")?;
}
}

fs::read_to_string(&path).with_context(|| format!("read file {}", path.display()))
}

pub trait Parse: Clone {
fn parse(input: &str) -> Self;
}

impl Parse for String {
fn parse(input: &str) -> Self { input.to_string() }
}

#[derive(Clone)]
pub struct JsonString(pub String);

impl Parse for JsonString {
fn parse(input: &str) -> Self {
let value = simd_json::json!(input);
Self(simd_json::to_string(&value).unwrap())
}
}

macros::all! {
day 1 {
part 1 {
Expand Down Expand Up @@ -156,4 +88,12 @@ macros::all! {
"reversed" => p2_reversed,
}
}
day 8 {
part 1 {
"naive" => p1_naive,
}
part 2 {
"naive" => p2_naive,
}
}
}
2 changes: 1 addition & 1 deletion src/all/d4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub fn p2_brute(input: String) -> u32 {
[DirectDiagonal::RightUp, DirectDiagonal::LeftDown],
]
.map(|ends| {
let values = ends.map(|direct| grid.get(loc.direct(direct, &grid)?));
let values = ends.map(|direct| grid.get(loc.direct(direct, grid)?));
values == [Some(b'M'), Some(b'S')] || values == [Some(b'S'), Some(b'M')]
});
if matched[0] && matched[1] {
Expand Down
2 changes: 1 addition & 1 deletion src/all/d6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fn p1_ticked<CollectorT: LocCounter>(input: String) -> u32 {
collector.insert(|| loc, || grid.shape.loc_to_index(loc));

'directs: loop {
match loc.direct(direct, &grid) {
match loc.direct(direct, grid) {
None => return collector.count(), // leave map
Some(new_loc) => match grid.get(new_loc).unwrap() {
b'^' | b'.' => {
Expand Down
4 changes: 2 additions & 2 deletions src/all/d7.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fn parse(input: &str) -> impl Iterator<Item = Line> {
})
}

impl<'a> Line<'a> {
impl Line<'_> {
fn operands_rev(&self) -> impl Iterator<Item = Operand> + Clone {
let mut operands = self.operands;
iter::from_fn(move || {
Expand Down Expand Up @@ -141,7 +141,7 @@ fn is_valid_reverse_recurse_p2<'a>(
}
}

fn strip_base10_suffix<'a>(long: u64, suffix: Operand<'a>) -> Option<u64> {
fn strip_base10_suffix(long: u64, suffix: Operand) -> Option<u64> {
let remain = long.wrapping_sub(suffix.value); // works on "my input"
let unit = 10u64.pow(suffix.bytes.len() as u32);
if remain % unit == 0 {
Expand Down
80 changes: 80 additions & 0 deletions src/all/d8.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use rustc_hash::FxHashMap;

use crate::util::{GridLoc, GridView};

fn disjoint_pairs<T>(slice: &[T]) -> impl Iterator<Item = [&T; 2]> {
(0..slice.len()).flat_map(move |left| {
(left + 1..slice.len()).map(move |right| [&slice[left], &slice[right]])
})
}

pub fn p1_naive(input: String) -> u32 {
let grid = GridView::new(input.into_bytes());

let mut freqs = FxHashMap::<u8, Vec<u32>>::default();
for (index, &ch) in grid.input.iter().enumerate() {
if ch != b'.' && ch != b'\n' {
freqs.entry(ch).or_default().push(index as u32);
}
}

let mut matches = 0;
for indices in freqs.values() {
for pair_index in disjoint_pairs(indices) {
let pair_loc =
pair_index.map(|&index| grid.shape.index_to_loc(index as usize).unwrap());
let vector = pair_loc[1] - pair_loc[0];

for loc in [pair_loc[0].add(vector * -1, &grid), pair_loc[1].add(vector, &grid)]
.into_iter()
.flatten()
{
let byte = grid.get(loc).unwrap();
if byte == b'.' {
matches += 1;
}
}
}
}

// turns out it is the number of location-frequency pairs not unique locations???
matches
}

pub fn p2_naive(input: String) -> u32 {
let grid = GridView::new(input.into_bytes());

let mut freqs = FxHashMap::<u8, Vec<u32>>::default();
for (index, &ch) in grid.input.iter().enumerate() {
if ch != b'.' && ch != b'\n' {
freqs.entry(ch).or_default().push(index as u32);
}
}

let mut output = vec![false; grid.input.len()];

for indices in freqs.values() {
for pair_index in disjoint_pairs(indices) {
let pair_loc =
pair_index.map(|&index| grid.shape.index_to_loc(index as usize).unwrap());
let vector = pair_loc[1] - pair_loc[0];

let iter_map_fn_builder = {
let shape = grid.shape;
move |loc: GridLoc, factor: i32| move |step| loc.add(vector * step * factor, shape)
};

for map_fn in
[iter_map_fn_builder(pair_loc[0], -1), iter_map_fn_builder(pair_loc[1], 1)]
{
let mut loc_iter = (0..).map(map_fn);

while let Some(Some(loc)) = loc_iter.next() {
output[grid.shape.loc_to_index(loc) as usize] = true;
}
}
}
}

output.iter().map(|&b| if b { 1 } else { 0 }).sum()
}
74 changes: 73 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,76 @@
use std::path::PathBuf;
use std::{env, fs, io};

use anyhow::Context;
use clap::{Parser, ValueEnum};

pub mod all;
pub use all::{load_input, run, Args, JsonString, Mode, Parse};
pub use all::run;

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum Mode {
Sample,
Private,
}

#[derive(Parser)]
pub struct Args {
mode: Mode,
day: u32,
part: u32,
#[clap(default_value = "")]
variant: String,
}

pub fn load_input(mode: Mode, day: u32) -> anyhow::Result<String> {
let dir = env::var("CARGO_MANIFEST_DIR").context("need cargo run")?;
let path = PathBuf::from(dir).join("input").join(format!(
"d{day}.{}.input.txt",
match mode {
Mode::Sample => "sample",
Mode::Private => "private",
}
));

if let Mode::Private = mode {
let exists =
fs::exists(&path).with_context(|| format!("test {} existence", path.display()))?;
if !exists {
eprintln!("Downloading day {day} input");

let session_cookie = env::var("AOC_SESSION")
.context("private file missing and AOC_SESSION env var missing")?;

let client = reqwest::blocking::Client::new();
let data = client
.get(format!("https://adventofcode.com/2024/day/{day}/input"))
.header("Cookie", format!("session={session_cookie}"))
.send()
.context("request aoc private input")?;
let input = io::read_to_string(data).context("read aoc private input")?;
fs::write(&path, &input).context("write aoc private input to cache")?;
}
}

fs::read_to_string(&path).with_context(|| format!("read file {}", path.display()))
}

pub trait Parse: Clone {
fn parse(input: &str) -> Self;
}

impl Parse for String {
fn parse(input: &str) -> Self { input.to_string() }
}

#[derive(Clone)]
pub struct JsonString(pub String);

impl Parse for JsonString {
fn parse(input: &str) -> Self {
let value = simd_json::json!(input);
Self(simd_json::to_string(&value).unwrap())
}
}

mod util;
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use clap::Parser;

fn main() -> anyhow::Result<()> {
let args = aoc2024::all::Args::parse();
let args = aoc2024::Args::parse();
aoc2024::run(args)
}
Loading

0 comments on commit 0948ec1

Please sign in to comment.