mirror of
https://github.com/javalsai/aoc.git
synced 2026-01-12 17:10:00 +01:00
add: d10, p2 left, not touching python or a CAS crate
This commit is contained in:
154
2025/10/p1.rs
Normal file
154
2025/10/p1.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
#![feature(slice_split_once)]
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// The max indicator is like 10 long, eyeballing it, makes sense so theres only a digit.
|
||||
///
|
||||
/// So use a u16 as a bitfield for that, saves a lot of memory.
|
||||
#[derive(Clone)]
|
||||
struct Machine {
|
||||
indicators: u16,
|
||||
buttons: Box<[u16]>,
|
||||
joltages: Box<[u16]>, // This u16 is unrelated (for now) btw
|
||||
len: u8,
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
fn from_slice(s: &[u8]) -> Self {
|
||||
let (left, right) = s.split_once(|&b| b == b']').unwrap();
|
||||
|
||||
let len = left[1..].len() as u8;
|
||||
let indicators = left[1..]
|
||||
.iter()
|
||||
.rev()
|
||||
.fold(0u16, |acc, &b| (acc << 1) | (b == b'#') as u16);
|
||||
|
||||
let (left, right) = right.split_once(|&b| b == b'{').unwrap();
|
||||
|
||||
let joltages = right[..(right.len() - 1)]
|
||||
.split(|&b| b == b',')
|
||||
.map(|s| s.iter().fold(0, |acc, b| acc * 10 + (b - b'0') as u16))
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice();
|
||||
|
||||
let buttons = left
|
||||
.trim_ascii()
|
||||
.split(|&b| b == b' ')
|
||||
.map(|button| {
|
||||
button[1..(button.len() - 1)]
|
||||
.split(|&b| b == b',')
|
||||
.fold(0, |bitfield, n| {
|
||||
debug_assert_eq!(n.len(), 1);
|
||||
|
||||
let idx = n[0] - b'0';
|
||||
bitfield | (1 << idx)
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice();
|
||||
|
||||
Self {
|
||||
indicators,
|
||||
buttons,
|
||||
joltages,
|
||||
len,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_solution(&self, max: &mut u8, mut solution: u32) {
|
||||
let params = count_bit_population(solution);
|
||||
if params >= *max {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
let mut acc = self.indicators;
|
||||
while solution != 0 {
|
||||
let this_vec = (solution & 1) == 1;
|
||||
solution >>= 1;
|
||||
|
||||
if this_vec {
|
||||
acc ^= self.buttons[i];
|
||||
|
||||
// found a solution
|
||||
if acc == 0 {
|
||||
let rem_params = count_bit_population(solution);
|
||||
*max = params - rem_params;
|
||||
}
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn count_bit_population(mut n: u32) -> u8 {
|
||||
let mut count = 0;
|
||||
while n != 0 {
|
||||
count += 1;
|
||||
n = n & (n - 1);
|
||||
}
|
||||
|
||||
count
|
||||
}
|
||||
|
||||
impl fmt::Debug for Machine {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Machine([{:0width$b}] ",
|
||||
self.indicators,
|
||||
width = self.len as usize
|
||||
)?;
|
||||
|
||||
for button in &self.buttons {
|
||||
write!(f, "{button:0width$b} ", width = self.len as usize)?;
|
||||
}
|
||||
|
||||
write!(f, "{{{:?}}})", self.joltages)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "Rust" fn challenge_usize(buf: &[u8]) -> usize {
|
||||
// Istg this is just solving a ℤ₂ vector system to find the smallest sum of coords that get you 0⃗
|
||||
//
|
||||
// The parameters are also ℤ₂ because applying the same button twice would just undo it. It's
|
||||
// either brute forceable or fancy math O(1), I just know how to do systems by hand, not
|
||||
// computer. But ummmmmmm.
|
||||
//
|
||||
// I = a b₀ + b b₁ + ...
|
||||
// where I: Machine::indicators
|
||||
//
|
||||
// ⎧ a b₀₀ + b b₀₁ + ... = I₀
|
||||
// ⎨ a b₁₀ + b b₁₁ + ... = I₁
|
||||
// ⎩ ...
|
||||
//
|
||||
// ⎛ b₀ ⎞ ⎛ a ⎞ ⎛ I₀ ⎞
|
||||
// ⎜ b₁ ⎟·⎜ b ⎟ = ⎜ I₁ ⎟
|
||||
// ⎝ ... ⎠ ⎝...⎠ ⎝ ... ⎠
|
||||
// A · C = I
|
||||
//
|
||||
// Ofc that'd just be I · A⁻¹, but A is not square and will have multiple solutions.
|
||||
|
||||
let mut presses_count = 0;
|
||||
|
||||
let machines = buf[..(buf.len() - 1)]
|
||||
.split(|&b| b == b'\n')
|
||||
.map(Machine::from_slice);
|
||||
|
||||
for machine in machines {
|
||||
let mut max = machine.buttons.len() as u8;
|
||||
let max_comb_bitf = 1u32 << max;
|
||||
|
||||
let mut vec_bitfield = 1u32; // represents the vectors to try
|
||||
while vec_bitfield < max_comb_bitf {
|
||||
machine.try_solution(&mut max, vec_bitfield);
|
||||
vec_bitfield += 1;
|
||||
}
|
||||
|
||||
presses_count += max as usize;
|
||||
}
|
||||
|
||||
presses_count
|
||||
}
|
||||
340
2025/10/p2.rs
Normal file
340
2025/10/p2.rs
Normal file
@@ -0,0 +1,340 @@
|
||||
#![feature(slice_split_once, exact_div)]
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use crate::eq::Equation;
|
||||
|
||||
pub mod eq {
|
||||
use std::{
|
||||
fmt,
|
||||
// ops::{AddAssign, MulAssign},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Equation {
|
||||
parameters: Box<[i16]>,
|
||||
term: i16,
|
||||
}
|
||||
|
||||
// impl MulAssign<i16> for Equation {
|
||||
// fn mul_assign(&mut self, rhs: i16) {
|
||||
// self.parameters.iter_mut().for_each(|param| *param *= rhs);
|
||||
// self.term *= rhs;
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl AddAssign for Equation {
|
||||
// fn add_assign(&mut self, rhs: Self) {
|
||||
// assert_eq!(self.degree(), rhs.degree());
|
||||
|
||||
// self.parameters
|
||||
// .iter_mut()
|
||||
// .zip(rhs.parameters.iter())
|
||||
// .for_each(|(me, other)| *me += other);
|
||||
// self.term += rhs.term;
|
||||
// }
|
||||
// }
|
||||
|
||||
impl From<(Box<[i16]>, i16)> for Equation {
|
||||
fn from((parameters, term): (Box<[i16]>, i16)) -> Self {
|
||||
Self { parameters, term }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Equation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut prev_was_zero = true;
|
||||
|
||||
for (param, c) in self.parameters.iter().zip(('a'..='z').cycle()) {
|
||||
if !prev_was_zero {
|
||||
write!(f, " + ")?;
|
||||
} else {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
|
||||
prev_was_zero = *param == 0;
|
||||
if !prev_was_zero {
|
||||
write!(f, "{param:>4}{c}")?;
|
||||
} else {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, " = {:>3}", self.term)
|
||||
}
|
||||
}
|
||||
|
||||
impl Equation {
|
||||
pub const fn new(parameters: Box<[i16]>, term: i16) -> Self {
|
||||
Self { parameters, term }
|
||||
}
|
||||
|
||||
pub fn set_n_term(&mut self, n: usize, value: i16) {
|
||||
self.parameters[n] = value;
|
||||
}
|
||||
|
||||
pub fn set_term(&mut self, value: i16) {
|
||||
self.term = value;
|
||||
}
|
||||
|
||||
pub fn zeroed(len: usize) -> Self {
|
||||
Self {
|
||||
parameters: vec![0; len].into_boxed_slice(),
|
||||
term: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn left_padded(&self) -> usize {
|
||||
self.parameters
|
||||
.iter()
|
||||
.position(|&item| item != 0)
|
||||
.unwrap_or(self.degree())
|
||||
}
|
||||
|
||||
pub fn degree(&self) -> usize {
|
||||
self.parameters.len()
|
||||
}
|
||||
|
||||
pub fn eliminate_by(&mut self, other: &Self) -> bool {
|
||||
let term_idx = self.left_padded();
|
||||
let Some(&my_term) = self.parameters.get(term_idx) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Some(&other_term) = other.parameters.get(term_idx) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// self * other_term - other * self_term
|
||||
self.parameters
|
||||
.iter_mut()
|
||||
.zip(other.parameters.iter())
|
||||
.for_each(|(a, &b)| *a = *a * other_term - b * my_term);
|
||||
|
||||
self.term = self.term * other_term - other.term * my_term;
|
||||
true
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.parameters.iter().all(|&v| v == 0)
|
||||
}
|
||||
|
||||
pub fn has_known(&self) -> Option<(usize, i16)> {
|
||||
let (only_idx, &only_val) =
|
||||
self.parameters.iter().enumerate().find(|&(_, &v)| v != 0)?;
|
||||
if self.parameters.iter().enumerate().all(|(i, &v)| {
|
||||
if i == only_idx {
|
||||
return true;
|
||||
}
|
||||
|
||||
v == 0
|
||||
}) {
|
||||
Some((
|
||||
only_idx,
|
||||
self.term
|
||||
.div_exact(only_val)
|
||||
.expect("Equation found a fractional solution"),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pub mod var {
|
||||
// pub enum VarDependency
|
||||
|
||||
// pub enum VarKnowledgeType {
|
||||
// DependantOn()
|
||||
// Known(i16),
|
||||
// Unknown,
|
||||
// }
|
||||
|
||||
// pub struct VarKnowledge {
|
||||
// var_idx: usize,
|
||||
// type: VarKnowledgeType,
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
mod __hide {
|
||||
use std::{cmp::Ordering, iter::Sum, ops::Add};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum EqEvalRes {
|
||||
Correct,
|
||||
Undershoot,
|
||||
Overshoot,
|
||||
}
|
||||
|
||||
impl Add for EqEvalRes {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
use EqEvalRes::*;
|
||||
|
||||
match (self, rhs) {
|
||||
(Correct, x) => x,
|
||||
(_, Overshoot) | (Overshoot, _) => Overshoot,
|
||||
(Undershoot, _) => Undershoot,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sum<Self> for EqEvalRes {
|
||||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||
iter.reduce(|a, b| a + b).unwrap_or(Self::Correct)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ordering> for EqEvalRes {
|
||||
fn from(value: Ordering) -> Self {
|
||||
use EqEvalRes::*;
|
||||
|
||||
match value {
|
||||
Ordering::Less => Undershoot,
|
||||
Ordering::Equal => Correct,
|
||||
Ordering::Greater => Overshoot,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The max indicator is like 10 long, eyeballing it, makes sense so theres only a digit.
|
||||
///
|
||||
/// So use a u16 as a bitfield for that, saves a lot of memory.
|
||||
#[derive(Clone)]
|
||||
struct Machine {
|
||||
indicators: u16,
|
||||
buttons: Box<[u16]>,
|
||||
equations: Box<[Equation]>,
|
||||
len: u8,
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
fn gauss(&mut self) {
|
||||
let mut do_smth = true;
|
||||
while do_smth {
|
||||
do_smth = false;
|
||||
self.equations.sort_by_key(|eq| eq.left_padded());
|
||||
|
||||
for i in 1..self.equations.len() {
|
||||
for j in 0..i {
|
||||
if self.equations[i].left_padded() == self.equations[j].left_padded() {
|
||||
// SAFETY: i != j (0..!=i), so its not the same things we're borrowing
|
||||
do_smth |= unsafe { &mut *std::ptr::from_mut(&mut self.equations[i]) }
|
||||
.eliminate_by(&self.equations[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_slice(s: &[u8]) -> Self {
|
||||
let (left, right) = s.split_once(|&b| b == b']').unwrap();
|
||||
|
||||
let len = left[1..].len() as u8;
|
||||
let indicators = left[1..]
|
||||
.iter()
|
||||
.rev()
|
||||
.fold(0u16, |acc, &b| (acc << 1) | (b == b'#') as u16);
|
||||
|
||||
let (left, right) = right.split_once(|&b| b == b'{').unwrap();
|
||||
|
||||
let buttons = left
|
||||
.trim_ascii()
|
||||
.split(|&b| b == b' ')
|
||||
.map(|button| {
|
||||
button[1..(button.len() - 1)]
|
||||
.split(|&b| b == b',')
|
||||
.fold(0, |bitfield, n| {
|
||||
debug_assert_eq!(n.len(), 1);
|
||||
|
||||
let idx = n[0] - b'0';
|
||||
bitfield | (1 << idx)
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice();
|
||||
|
||||
let joltages = right[..(right.len() - 1)]
|
||||
.split(|&b| b == b',')
|
||||
.map(|s| s.iter().fold(0, |acc, b| acc * 10 + (b - b'0') as i16));
|
||||
|
||||
let mut eqs = Vec::with_capacity(len as usize);
|
||||
for (pos, jolts) in joltages.enumerate() {
|
||||
let mut eq = Equation::zeroed(buttons.len());
|
||||
eq.set_term(jolts);
|
||||
|
||||
for (i, b) in buttons.iter().enumerate() {
|
||||
// println!("{pos} {b:b}");
|
||||
if b & (1 << pos) != 0 {
|
||||
eq.set_n_term(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
eqs.push(eq);
|
||||
}
|
||||
|
||||
Self {
|
||||
indicators,
|
||||
buttons,
|
||||
equations: eqs.into_boxed_slice(),
|
||||
len,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Machine {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Machine([{:0width$b}]",
|
||||
self.indicators,
|
||||
width = self.len as usize
|
||||
)?;
|
||||
|
||||
for button in &self.buttons {
|
||||
write!(f, " {button:0width$b}", width = self.len as usize)?;
|
||||
}
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "Rust" fn challenge_usize(buf: &[u8]) -> usize {
|
||||
let machines = buf[..(buf.len() - 1)]
|
||||
.split(|&b| b == b'\n')
|
||||
.map(Machine::from_slice);
|
||||
|
||||
for (i, mut machine) in machines.enumerate() {
|
||||
println!("{i}: {machine:?}");
|
||||
|
||||
for eq in &machine.equations {
|
||||
println!(" {eq:?} {:?}", eq.has_known());
|
||||
}
|
||||
println!();
|
||||
|
||||
machine.gauss();
|
||||
|
||||
for eq in &machine.equations {
|
||||
println!(" {eq:?} {:?}", eq.has_known());
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
42
|
||||
}
|
||||
|
||||
/// Just an implementation of euler's algorithm
|
||||
fn gcd(mut a: u16, mut b: u16) -> u16 {
|
||||
while b != 0 {
|
||||
(a, b) = (b, a % b)
|
||||
}
|
||||
a
|
||||
}
|
||||
|
||||
fn lcm(a: u16, b: u16) -> u16 {
|
||||
a * b / gcd(a, b)
|
||||
}
|
||||
Reference in New Issue
Block a user