Skip to content

Commit

Permalink
d2 (one of the part 2 solutions is wrong)
Browse files Browse the repository at this point in the history
  • Loading branch information
SOF3 committed Dec 2, 2024
1 parent ba80dc4 commit 788f6dd
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 37 deletions.
14 changes: 12 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ anyhow = "1.0.93"
bitvec = "1.0.1"
clap = { version = "4.5.21", features = ["derive"] }
criterion = "0.5.1"
itertools = "0.13.0"
jq-rs = { version = "0.4.1", features = ["bundled"] }
paste = "1.0.15"
reqwest = { version = "0.12.9", features = ["blocking"] }
Expand Down
6 changes: 6 additions & 0 deletions input/d2.sample.input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
7 6 4 2 1
1 2 7 8 9
9 7 6 2 1
1 3 2 4 5
8 6 4 4 1
1 3 6 7 9
73 changes: 41 additions & 32 deletions src/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{env, fmt, fs, io};

use anyhow::Context;
use clap::{Parser, ValueEnum};
use criterion::{BatchSize, Criterion, Bencher};
use criterion::{BatchSize, Bencher, Criterion};

macro_rules! main {
(
Expand All @@ -26,9 +26,7 @@ macro_rules! main {
$(
$part => {
let input = load_input(args.mode, $day)?;

let output = main!(@impl variant, &input, $impls);

let output: String = main!(@impl variant, &input, $impls);
println!("{output}");

Ok(())
Expand All @@ -49,9 +47,12 @@ macro_rules! main {

$($(
{
let mut group = c.benchmark_group(concat!("Day ", $day, " Part ", $part));
main!(@bench group, $day, $impls);
group.finish();
let mut group_rust = c.benchmark_group(concat!("Day ", $day, " Part ", $part, " Rust"));
main!(@bench group_rust, $day, $impls, false);
group_rust.finish();
let mut group_jq = c.benchmark_group(concat!("Day ", $day, " Part ", $part, " JQ"));
main!(@bench group_jq, $day, $impls, true);
group_jq.finish();
}
)*)*
}
Expand All @@ -66,13 +67,15 @@ macro_rules! main {
};
(@bench $group:ident, $day:literal, {
$($name:literal => $fn:expr,)*
}) => {
}, $is_jq:literal) => {
$(
{
let mut f = try_unwrap(move || anyhow::Ok($fn));
$group.bench_function($name, move |b| {
call_benched(b, $day, &mut f);
});
if $name.starts_with("jq") == $is_jq{
$group.bench_function($name, move |b| {
call_benched(b, $day, &mut f);
});
}
}
)*
};
Expand Down Expand Up @@ -108,11 +111,7 @@ fn call<In: Parse, Out: fmt::Display>(mut f: impl FnMut(In) -> Out, input: &str)
fn call_benched<In: Parse, Out: fmt::Display>(b: &mut Bencher, day: u32, f: impl FnMut(In) -> Out) {
let input = load_input(Mode::Private, day).unwrap();
let parsed: In = Parse::parse(&input);
b.iter_batched(
|| parsed.clone(),
f,
BatchSize::LargeInput,
);
b.iter_batched(|| parsed.clone(), f, BatchSize::LargeInput);
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
Expand All @@ -130,22 +129,6 @@ pub struct Args {
variant: String,
}

main! {
day 1 {
part 1 {
"zip" => d1::p1_zip,
"jq" => jq!("d1.jq", "d1q1"),
}
part 2 {
"hash" => d1::p2_hash,
"sorted" => d1::p2_sorted,
"count" => d1::p2_count,
"bitvec" => d1::p2_bitvec,
"jq/hash" => jq!("d1.jq", "d1q2_hash"),
}
}
}

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!(
Expand Down Expand Up @@ -196,3 +179,29 @@ impl Parse for JsonString {
Self(simd_json::to_string(&value).unwrap())
}
}

main! {
day 1 {
part 1 {
"zip" => d1::p1_zip,
"jq" => jq!("d1.jq", "d1q1"),
}
part 2 {
"hash" => d1::p2_hash,
"sorted" => d1::p2_sorted,
"count" => d1::p2_count,
"bitvec" => d1::p2_bitvec,
"jq/hash" => jq!("d1.jq", "d1q2_hash"),
}
}
day 2 {
part 1 {
"windows" => d2::p1_windows,
"first-all" => d2::p1_first_all,
}
part 2 {
"brute" => d2::p2_brute_force,
"vec" => d2::p2_vec,
}
}
}
3 changes: 2 additions & 1 deletion src/all/d1.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{collections::HashMap, fmt, iter};
use std::collections::HashMap;
use std::{fmt, iter};

use bitvec::vec::BitVec;

Expand Down
126 changes: 126 additions & 0 deletions src/all/d2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use std::fmt;

use itertools::Itertools;

use crate::Parse;

#[derive(Clone)]
pub struct Input(Vec<Line>);

#[derive(Clone)]
struct Line(Vec<u32>);

impl Parse for Input {
fn parse(input: &str) -> Self {
Self(
input
.lines()
.map(|line| Line(line.split(' ').map(|s| s.parse::<u32>().unwrap()).collect()))
.collect(),
)
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Direction {
Increase,
Decrease,
Illegal,
}

fn compare(left: u32, right: u32) -> Direction {
if (right + 1..=right + 3).contains(&left) {
Direction::Decrease
} else if (left + 1..=left + 3).contains(&right) {
Direction::Increase
} else {
Direction::Illegal
}
}

fn is_safe_windows(levels: impl Iterator<Item = u32>) -> bool {
let directions = levels.tuple_windows().map(|(left, right)| compare(left, right));
directions.tuple_windows().all(|(left, right)| left != Direction::Illegal && left == right)
}

fn is_safe_first_all(levels: impl Iterator<Item = u32>) -> bool {
let mut directions = levels.tuple_windows().map(|(left, right)| compare(left, right));
let first = directions.next().unwrap();
first != Direction::Illegal && directions.all(|d| d == first)
}

pub fn p1_windows(input: Input) -> impl fmt::Display {
fn is_safe(line: &Line) -> bool { is_safe_windows(line.0.iter().copied()) }

input.0.iter().filter(|line| is_safe(line)).count()
}

pub fn p1_first_all(input: Input) -> impl fmt::Display {
fn is_safe(line: &Line) -> bool { is_safe_first_all(line.0.iter().copied()) }

input.0.iter().filter(|line| is_safe(line)).count()
}

pub fn p2_brute_force(input: Input) -> impl fmt::Display {
fn is_safe(line: &Line) -> bool {
is_safe_first_all(line.0.iter().copied())
|| (0..line.0.len()).any(|skip| {
is_safe_first_all(
line.0[..skip].iter().copied().chain(line.0[skip + 1..].iter().copied()),
)
})
}

input.0.iter().filter(|line| is_safe(line)).count()
}

// TODO: this answer is wrong
pub fn p2_vec(input: Input) -> impl fmt::Display {
fn is_safe_skip(line: &Line, index: usize, dominant: Direction) -> bool {
if let (Some(prev_index), Some(&next_level)) = (index.checked_sub(1), line.0.get(index + 1))
{
if compare(line.0[prev_index], next_level) != dominant {
return false;
}
}

true
}

fn is_safe(line: &Line) -> bool {
let directions: Vec<_> =
line.0.iter().tuple_windows().map(|(&left, &right)| compare(left, right)).collect();

let increase_count = directions.iter().filter(|&&d| d == Direction::Increase).count();
let dominants = if increase_count * 2 > directions.len() {
&[Direction::Increase][..]
} else if increase_count * 2 == directions.len() {
&[Direction::Increase, Direction::Decrease][..]
} else {
&[Direction::Decrease][..]
};

dominants.iter().any(|&dominant| {
let mut violations = directions.iter().enumerate().filter(|(_, &d)| d != dominant);
if let Some((index, _)) = violations.next() {
// line[index] -> line[index+1] violation,
// attempt to skip either line[index] or line[index+1]

if is_safe_skip(line, index, dominant) && is_safe_skip(line, index + 1, dominant) {
let mut next_violation = violations.next();
if next_violation.is_some_and(|(next_index, _)| next_index == index + 1) {
next_violation = violations.next();
}

next_violation.is_none()
} else {
false
}
} else {
true
}
})
}

input.0.iter().filter(|line| is_safe(line)).count()
}
3 changes: 1 addition & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
mod all;
pub use all::bench;
pub use all::run;
use all::Parse;
pub use all::{bench, run};

0 comments on commit 788f6dd

Please sign in to comment.