Skip to content

Commit

Permalink
d7
Browse files Browse the repository at this point in the history
  • Loading branch information
SOF3 committed Dec 8, 2024
1 parent 12c5cc7 commit df41f78
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 57 deletions.
9 changes: 9 additions & 0 deletions input/d7.sample.input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20
8 changes: 8 additions & 0 deletions src/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,12 @@ macros::all! {
"brute-fxhash-loc" => p2_brute_fxhash_loc,
}
}
day 7 {
part 1 {
"reversed" => p1_reversed,
}
part 2 {
"reversed" => p2_reversed,
}
}
}
109 changes: 52 additions & 57 deletions src/all/d6.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{collections::HashSet, hash::BuildHasher};
use std::collections::HashSet;
use std::hash::BuildHasher;

use bitvec::vec::BitVec;
use rustc_hash::FxHashSet;
Expand All @@ -24,56 +25,44 @@ fn p1_ticked<CollectorT: LocCounter>(input: String) -> u32 {
'directs: loop {
match loc.direct(direct, &grid) {
None => return collector.count(), // leave map
Some(new_loc) => {
match grid.get(new_loc).unwrap() {
b'^' | b'.' => {
loc = new_loc;
continue 'ticks
}
b'#' => {
direct = direct.clockwise();
continue 'directs
}
_ => unreachable!(),
Some(new_loc) => match grid.get(new_loc).unwrap() {
b'^' | b'.' => {
loc = new_loc;
continue 'ticks;
}
}
b'#' => {
direct = direct.clockwise();
continue 'directs;
}
_ => unreachable!(),
},
}
}
}
}

impl<S: BuildHasher + Default> LocCounter for HashSet<GridLoc, S> {
fn new(capacity: usize) -> Self {
Self::with_capacity_and_hasher(capacity, S::default())
}
fn new(capacity: usize) -> Self { Self::with_capacity_and_hasher(capacity, S::default()) }

fn insert(&mut self, loc: impl FnOnce() -> GridLoc, _: impl FnOnce() -> u32) {
HashSet::insert(self, loc());
}

fn count(&self) -> u32 {
self.len() as u32
}
fn count(&self) -> u32 { self.len() as u32 }
}

impl<S: BuildHasher + Default> LocCounter for HashSet<u32, S> {
fn new(capacity: usize) -> Self {
Self::with_capacity_and_hasher(capacity, S::default())
}
fn new(capacity: usize) -> Self { Self::with_capacity_and_hasher(capacity, S::default()) }

fn insert(&mut self, _: impl FnOnce() -> GridLoc, index: impl FnOnce() -> u32) {
HashSet::insert(self, index());
}

fn count(&self) -> u32 {
self.len() as u32
}
fn count(&self) -> u32 { self.len() as u32 }
}

impl LocCounter for Vec<bool> {
fn new(capacity: usize) -> Self {
vec![false; capacity]
}
fn new(capacity: usize) -> Self { vec![false; capacity] }

fn insert(&mut self, _: impl FnOnce() -> GridLoc, index: impl FnOnce() -> u32) {
self[index() as usize] = true;
Expand All @@ -92,17 +81,13 @@ impl LocCounter for Vec<bool> {
}

impl LocCounter for BitVec {
fn new(capacity: usize) -> Self {
Self::repeat(false, capacity)
}
fn new(capacity: usize) -> Self { Self::repeat(false, capacity) }

fn insert(&mut self, _: impl FnOnce() -> GridLoc, index: impl FnOnce() -> u32) {
self.set(index() as usize, true);
}

fn count(&self) -> u32 {
self.count_ones() as u32
}
fn count(&self) -> u32 { self.count_ones() as u32 }
}

pub fn p1_ticked_fxhash_loc(input: String) -> u32 { p1_ticked::<FxHashSet<GridLoc>>(input) }
Expand All @@ -114,7 +99,12 @@ trait LoopDetector {
fn new(capacity: usize) -> Self;
fn clear(&mut self);

fn insert(&mut self, loc: impl FnOnce() -> GridLoc, index: impl FnOnce() -> u32, direct: DirectTaxicab) -> IsLooped;
fn insert(
&mut self,
loc: impl FnOnce() -> GridLoc,
index: impl FnOnce() -> u32,
direct: DirectTaxicab,
) -> IsLooped;
}

#[derive(PartialEq, Eq)]
Expand All @@ -123,7 +113,11 @@ enum IsLooped {
NewStep,
}

fn is_looping<DetectorT: LoopDetector>(grid: &GridView<impl AsRef<[u8]>>, det: &mut DetectorT, initial: GridLoc) -> bool {
fn is_looping<DetectorT: LoopDetector>(
grid: &GridView<impl AsRef<[u8]>>,
det: &mut DetectorT,
initial: GridLoc,
) -> bool {
det.clear();

let mut loc = initial;
Expand All @@ -137,19 +131,17 @@ fn is_looping<DetectorT: LoopDetector>(grid: &GridView<impl AsRef<[u8]>>, det: &
'directs: loop {
match loc.direct(direct, grid) {
None => return false, // leave map
Some(new_loc) => {
match grid.get(new_loc).unwrap() {
b'^' | b'.' => {
loc = new_loc;
continue 'ticks
}
b'#' => {
direct = direct.clockwise();
continue 'directs
}
_ => unreachable!(),
Some(new_loc) => match grid.get(new_loc).unwrap() {
b'^' | b'.' => {
loc = new_loc;
continue 'ticks;
}
b'#' => {
direct = direct.clockwise();
continue 'directs;
}
}
_ => unreachable!(),
},
}
}
}
Expand Down Expand Up @@ -177,16 +169,17 @@ fn p2_brute<LoopDetectorT: LoopDetector>(input: String) -> u32 {
count
}

impl<S: BuildHasher+Default> LoopDetector for HashSet<(GridLoc, DirectTaxicab), S> {
fn new(capacity: usize) -> Self {
HashSet::with_capacity_and_hasher(capacity, S::default())
}
impl<S: BuildHasher + Default> LoopDetector for HashSet<(GridLoc, DirectTaxicab), S> {
fn new(capacity: usize) -> Self { HashSet::with_capacity_and_hasher(capacity, S::default()) }

fn clear(&mut self) {
HashSet::clear(self)
}
fn clear(&mut self) { HashSet::clear(self) }

fn insert(&mut self, loc: impl FnOnce() -> GridLoc, _: impl FnOnce() -> u32, direct: DirectTaxicab) -> IsLooped {
fn insert(
&mut self,
loc: impl FnOnce() -> GridLoc,
_: impl FnOnce() -> u32,
direct: DirectTaxicab,
) -> IsLooped {
if HashSet::insert(self, (loc(), direct)) {
IsLooped::NewStep
} else {
Expand All @@ -195,4 +188,6 @@ impl<S: BuildHasher+Default> LoopDetector for HashSet<(GridLoc, DirectTaxicab),
}
}

pub fn p2_brute_fxhash_loc(input: String) -> u32 { p2_brute::<FxHashSet<(GridLoc, DirectTaxicab)>>(input) }
pub fn p2_brute_fxhash_loc(input: String) -> u32 {
p2_brute::<FxHashSet<(GridLoc, DirectTaxicab)>>(input)
}
155 changes: 155 additions & 0 deletions src/all/d7.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
use std::iter;

fn fast_parse_once(input: &[u8]) -> Option<(&[u8], u64)> {
let mut buf = input;
let mut output = 0;
while buf.first().is_some_and(u8::is_ascii_digit) {
output *= 10;
let (&first, rest) = buf.split_first().unwrap();
output += u64::from(first - b'0');
buf = rest;
}

if buf.len() != input.len() {
Some((buf, output))
} else {
None
}
}

fn fast_parse_once_reverse(input: &[u8]) -> Option<(&[u8], Operand)> {
let mut buf = input;
let mut output = 0;
let mut unit = 1;
while buf.last().is_some_and(u8::is_ascii_digit) {
let (&last, rest) = buf.split_last().unwrap();
output += u64::from(last - b'0') * unit;
unit *= 10;
buf = rest;
}

if buf.len() != input.len() {
Some((buf, Operand { value: output, bytes: &input[buf.len()..] }))
} else {
None
}
}

#[derive(Clone, Copy)]
struct Operand<'a> {
value: u64,
bytes: &'a [u8],
}

fn parse(input: &str) -> impl Iterator<Item = Line> {
input.lines().map(|line| {
let (operands, result) = fast_parse_once(line.as_bytes()).unwrap();
Line { result, operands }
})
}

impl<'a> Line<'a> {
fn operands_rev(&self) -> impl Iterator<Item = Operand> + Clone {
let mut operands = self.operands;
iter::from_fn(move || {
operands = operands.trim_ascii_end();
let (rest, last) = fast_parse_once_reverse(operands)?;
operands = rest;
Some(last)
})
}
}

struct Line<'a> {
result: u64,
operands: &'a [u8],
}

// Try iterating from the back.
// Break early if unable to reverse addition (negative) or multiplication (not divisible).
fn is_valid_reverse_recurse_p1<'a>(
result: u64,
mut operands: impl Iterator<Item = Operand<'a>> + Clone,
) -> bool {
match operands.next() {
None => result == 0, // empty list
Some(Operand { value: last, .. }) => {
// Try addition
if let Some(intermediate) = result.checked_sub(last) {
if is_valid_reverse_recurse_p1(intermediate, operands.clone()) {
return true;
}
}

// Try multiplication
if result % last == 0 {
// operands are always nonzero
if is_valid_reverse_recurse_p1(result / last, operands) {
return true;
}
}

false
}
}
}

pub fn p1_reversed(input: String) -> u64 {
parse(&input)
.filter(|line| is_valid_reverse_recurse_p1(line.result, line.operands_rev()))
.map(|line| line.result)
.sum()
}

// Try iterating from the back.
// Break early if unable to reverse addition (negative) or multiplication (not divisible).
fn is_valid_reverse_recurse_p2<'a>(
result: u64,
mut operands: impl Iterator<Item = Operand<'a>> + Clone,
) -> bool {
match operands.next() {
None => result == 0, // empty list
Some(last) => {
// Try addition
if let Some(intermediate) = result.checked_sub(last.value) {
if is_valid_reverse_recurse_p2(intermediate, operands.clone()) {
return true;
}
}

// Try concatenation
if let Some(intermediate) = strip_base10_suffix(result, last) {
if is_valid_reverse_recurse_p2(intermediate, operands.clone()) {
return true;
}
}

// Try multiplication
if result % last.value == 0 {
// no operands equal to 0
if is_valid_reverse_recurse_p2(result / last.value, operands) {
return true;
}
}

false
}
}
}

fn strip_base10_suffix<'a>(long: u64, suffix: Operand<'a>) -> Option<u64> {
let remain = long - suffix.value;
let unit = 10u64.pow(suffix.bytes.len() as u32);
if remain % unit == 0 {
Some(remain / unit)
} else {
None
}
}

pub fn p2_reversed(input: String) -> u64 {
parse(&input)
.filter(|line| is_valid_reverse_recurse_p2(line.result, line.operands_rev()))
.map(|line| line.result)
.sum()
}

0 comments on commit df41f78

Please sign in to comment.